From 5f28adec3652282f4e750d4cf675b3b056c2f55e Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 28 Dec 2020 13:43:17 +0800 Subject: [PATCH 001/492] add benchmark testing case for package gerror --- errors/gerror/gerror_z_bench_test.go | 79 ++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/errors/gerror/gerror_z_bench_test.go b/errors/gerror/gerror_z_bench_test.go index 6877780d8..679dc5e6b 100644 --- a/errors/gerror/gerror_z_bench_test.go +++ b/errors/gerror/gerror_z_bench_test.go @@ -4,14 +4,87 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package gerror +package gerror_test import ( + "errors" + "github.com/gogf/gf/errors/gerror" "testing" ) -func Benchmark_Stack(b *testing.B) { +var ( + // base error for benchmark testing of Wrap* functions. + baseError = errors.New("test") +) + +func Benchmark_New(b *testing.B) { for i := 0; i < b.N; i++ { - callers() + gerror.New("test") + } +} + +func Benchmark_Newf(b *testing.B) { + for i := 0; i < b.N; i++ { + gerror.Newf("%s", "test") + } +} + +func Benchmark_Wrap(b *testing.B) { + for i := 0; i < b.N; i++ { + gerror.Wrap(baseError, "test") + } +} + +func Benchmark_Wrapf(b *testing.B) { + for i := 0; i < b.N; i++ { + gerror.Wrapf(baseError, "%s", "test") + } +} + +func Benchmark_NewSkip(b *testing.B) { + for i := 0; i < b.N; i++ { + gerror.NewSkip(1, "test") + } +} + +func Benchmark_NewSkipf(b *testing.B) { + for i := 0; i < b.N; i++ { + gerror.NewSkipf(1, "%s", "test") + } +} + +func Benchmark_NewCode(b *testing.B) { + for i := 0; i < b.N; i++ { + gerror.NewCode(500, "test") + } +} + +func Benchmark_NewCodef(b *testing.B) { + for i := 0; i < b.N; i++ { + gerror.NewCodef(500, "%s", "test") + } +} + +func Benchmark_NewCodeSkip(b *testing.B) { + for i := 0; i < b.N; i++ { + gerror.NewCodeSkip(1, 500, "test") + } +} + +func Benchmark_NewCodeSkipf(b *testing.B) { + for i := 0; i < b.N; i++ { + gerror.NewCodeSkipf(1, 500, "%s", "test") + } +} + +func Benchmark_WrapCode(b *testing.B) { + for i := 0; i < b.N; i++ { + gerror.WrapCode(500, baseError, "test") + } +} + +func Benchmark_WrapCodef(b *testing.B) { + for i := 0; i < b.N; i++ { + gerror.WrapCodef(500, baseError, "test") } } From 695d333d2fe5c28c363221e492d591a15259e86c Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 28 Dec 2020 14:59:49 +0800 Subject: [PATCH 002/492] add example cases for package gerror --- errors/gerror/gerror_z_example_test.go | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 errors/gerror/gerror_z_example_test.go diff --git a/errors/gerror/gerror_z_example_test.go b/errors/gerror/gerror_z_example_test.go new file mode 100644 index 000000000..2725ac9ea --- /dev/null +++ b/errors/gerror/gerror_z_example_test.go @@ -0,0 +1,55 @@ +// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gerror_test + +import ( + "errors" + "fmt" + "github.com/gogf/gf/errors/gerror" +) + +func ExampleNewCode() { + err := gerror.NewCode(10000, "My Error") + fmt.Println(err.Error()) + fmt.Println(gerror.Code(err)) + + // Output: + // My Error + // 10000 +} + +func ExampleNewCodef() { + err := gerror.NewCodef(10000, "It's %s", "My Error") + fmt.Println(err.Error()) + fmt.Println(gerror.Code(err)) + + // Output: + // It's My Error + // 10000 +} + +func ExampleWrapCode() { + err1 := errors.New("permission denied") + err2 := gerror.WrapCode(10000, err1, "Custom Error") + fmt.Println(err2.Error()) + fmt.Println(gerror.Code(err2)) + + // Output: + // Custom Error: permission denied + // 10000 +} + +func ExampleWrapCodef() { + err1 := errors.New("permission denied") + err2 := gerror.WrapCodef(10000, err1, "It's %s", "Custom Error") + fmt.Println(err2.Error()) + fmt.Println(gerror.Code(err2)) + + // Output: + // It's Custom Error: permission denied + // 10000 +} From d25a3909d1fd28a91276c1f2aa0f7b9a0e27eccf Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 28 Dec 2020 23:03:13 +0800 Subject: [PATCH 003/492] improve context feature for package gdb --- database/gdb/gdb.go | 6 +++--- database/gdb/gdb_core.go | 28 ++++++++++++++++++------- database/gdb/gdb_z_mysql_method_test.go | 3 +-- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 41846facd..93bea6c13 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -213,9 +213,9 @@ type TableField struct { // Link is a common database function wrapper interface. type Link interface { - Query(sql string, args ...interface{}) (*sql.Rows, error) - Exec(sql string, args ...interface{}) (sql.Result, error) - Prepare(sql string) (*sql.Stmt, error) + QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) + ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) + PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) } // Counter is the type for update count. diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 14cab14f1..a2b6b36a2 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -78,11 +78,15 @@ func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error // DoQuery commits the sql string and its arguments to underlying driver // through given link object and returns the execution result. func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { + ctx := c.DB.GetCtx() + if ctx == nil { + ctx = context.Background() + } sql, args = formatSql(sql, args) sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) if c.DB.GetDebug() { mTime1 := gtime.TimestampMilli() - rows, err = link.Query(sql, args...) + rows, err = link.QueryContext(ctx, sql, args...) mTime2 := gtime.TimestampMilli() s := &Sql{ Sql: sql, @@ -95,7 +99,7 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro } c.writeSqlToLogger(s) } else { - rows, err = link.Query(sql, args...) + rows, err = link.QueryContext(ctx, sql, args...) } if err == nil { return rows, nil @@ -118,12 +122,16 @@ func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err err // DoExec commits the sql string and its arguments to underlying driver // through given link object and returns the execution result. func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) { + ctx := c.DB.GetCtx() + if ctx == nil { + ctx = context.Background() + } sql, args = formatSql(sql, args) sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) if c.DB.GetDebug() { mTime1 := gtime.TimestampMilli() if !c.DB.GetDryRun() { - result, err = link.Exec(sql, args...) + result, err = link.ExecContext(ctx, sql, args...) } else { result = new(SqlResult) } @@ -140,7 +148,7 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re c.writeSqlToLogger(s) } else { if !c.DB.GetDryRun() { - result, err = link.Exec(sql, args...) + result, err = link.ExecContext(ctx, sql, args...) } else { result = new(SqlResult) } @@ -157,8 +165,10 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re // The parameter specifies whether executing the sql on master node, // or else it executes the sql on slave node if master-slave configured. func (c *Core) Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error) { - err := (error)(nil) - link := (Link)(nil) + var ( + err error + link Link + ) if len(execOnMaster) > 0 && execOnMaster[0] { if link, err = c.DB.Master(); err != nil { return nil, err @@ -173,7 +183,11 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error) { // doPrepare calls prepare function on given link object and returns the statement object. func (c *Core) DoPrepare(link Link, sql string) (*sql.Stmt, error) { - return link.Prepare(sql) + ctx := c.DB.GetCtx() + if ctx == nil { + ctx = context.Background() + } + return link.PrepareContext(ctx, sql) } // GetAll queries and returns data records from database. diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index 2b26baa40..461cd2f98 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -1437,8 +1437,7 @@ func Test_DB_UpdateCounter(t *testing.T) { Value: 1, } updateData := g.Map{ - "views": gdbCounter, - "updated_time": gtime.Now().Unix(), + "views": gdbCounter, } result, err := db.Update(tableName, updateData, "id", 1) t.Assert(err, nil) From 4828ddcdd7dc54b81dc45d9d01ddca15d5ead11d Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 29 Dec 2020 00:01:27 +0800 Subject: [PATCH 004/492] add example/unit testing cases for package gdb --- .example/database/gdb/mysql/config.toml | 10 +++++++--- .example/database/gdb/mysql/gdb_ctx.go | 14 +++++++++++++ .example/database/gdb/mysql/gdb_ctx_model.go | 14 +++++++++++++ database/gdb/gdb_z_mysql_method_test.go | 21 ++++++++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 .example/database/gdb/mysql/gdb_ctx.go create mode 100644 .example/database/gdb/mysql/gdb_ctx_model.go diff --git a/.example/database/gdb/mysql/config.toml b/.example/database/gdb/mysql/config.toml index 48a479b62..beea9dcb5 100644 --- a/.example/database/gdb/mysql/config.toml +++ b/.example/database/gdb/mysql/config.toml @@ -1,9 +1,13 @@ # MySQL. [database] - debug = true - link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true" - MaxOpen = 100 + [database.logger] + Level = "all" + Stdout = true + CtxKeys = ["Trace-Id"] + [database.default] + link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + debug = true # Redis. [redis] diff --git a/.example/database/gdb/mysql/gdb_ctx.go b/.example/database/gdb/mysql/gdb_ctx.go new file mode 100644 index 000000000..c296b59ba --- /dev/null +++ b/.example/database/gdb/mysql/gdb_ctx.go @@ -0,0 +1,14 @@ +package main + +import ( + "context" + "github.com/gogf/gf/frame/g" +) + +func main() { + ctx := context.WithValue(context.Background(), "Trace-Id", "123456789") + _, err := g.DB().Ctx(ctx).Query("SELECT 1") + if err != nil { + panic(err) + } +} diff --git a/.example/database/gdb/mysql/gdb_ctx_model.go b/.example/database/gdb/mysql/gdb_ctx_model.go new file mode 100644 index 000000000..ba7b83c02 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_ctx_model.go @@ -0,0 +1,14 @@ +package main + +import ( + "context" + "github.com/gogf/gf/frame/g" +) + +func main() { + ctx := context.WithValue(context.Background(), "Trace-Id", "123456789") + _, err := g.DB().Model("user").Ctx(ctx).All() + if err != nil { + panic(err) + } +} diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index 461cd2f98..be7d19319 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -7,9 +7,11 @@ package gdb_test import ( + "context" "fmt" "github.com/gogf/gf/container/garray" "github.com/gogf/gf/encoding/gparser" + "github.com/gogf/gf/text/gstr" "testing" "time" @@ -1468,3 +1470,22 @@ func Test_DB_UpdateCounter(t *testing.T) { t.Assert(one["views"].Int(), 0) }) } + +func Test_DB_Ctx(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + _, err := db.Ctx(ctx).Query("SELECT SLEEP(10)") + t.Assert(gstr.Contains(err.Error(), "deadline"), true) + }) +} + +func Test_DB_Ctx_Logger(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + defer db.SetDebug(db.GetDebug()) + db.SetDebug(true) + ctx := context.WithValue(context.Background(), "Trace-Id", "123456789") + _, err := db.Ctx(ctx).Query("SELECT 1") + t.Assert(err, nil) + }) +} From 9d25e17fcba0dccfb50c5a469bd6a9c81b7cc61d Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 29 Dec 2020 09:09:12 +0800 Subject: [PATCH 005/492] readme updates --- README.MD | 2 +- README_ZH.MD | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.MD b/README.MD index cdc2e6ac1..ae9422133 100644 --- a/README.MD +++ b/README.MD @@ -33,7 +33,7 @@ golang version >= 1.11 # Architecture
- +
# Packages diff --git a/README_ZH.MD b/README_ZH.MD index 77f472620..7fb5049e5 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -49,7 +49,7 @@ golang版本 >= 1.11 # 架构
- +
# 模块 From bfab4a4952faf0dd64b14e3de7f601613b47179d Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 29 Dec 2020 13:30:15 +0800 Subject: [PATCH 006/492] comment updates for package gdb --- database/gdb/gdb_model_select.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 48ec07c05..80786614d 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -227,10 +227,10 @@ func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) { // // Eg: // user := new(User) -// err := db.Table("user").Where("id", 1).Struct(user) +// err := db.Model("user").Where("id", 1).Struct(user) // // user := (*User)(nil) -// err := db.Table("user").Where("id", 1).Struct(&user) +// err := db.Model("user").Where("id", 1).Struct(&user) func (m *Model) Struct(pointer interface{}, where ...interface{}) error { one, err := m.One(where...) if err != nil { @@ -251,10 +251,10 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error { // // Eg: // users := ([]User)(nil) -// err := db.Table("user").Structs(&users) +// err := db.Model("user").Structs(&users) // // users := ([]*User)(nil) -// err := db.Table("user").Structs(&users) +// err := db.Model("user").Structs(&users) func (m *Model) Structs(pointer interface{}, where ...interface{}) error { all, err := m.All(where...) if err != nil { @@ -275,16 +275,16 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error { // // Eg: // user := new(User) -// err := db.Table("user").Where("id", 1).Struct(user) +// err := db.Model("user").Where("id", 1).Scan(user) // // user := (*User)(nil) -// err := db.Table("user").Where("id", 1).Struct(&user) +// err := db.Model("user").Where("id", 1).Scan(&user) // // users := ([]User)(nil) -// err := db.Table("user").Structs(&users) +// err := db.Model("user").Scan(&users) // // users := ([]*User)(nil) -// err := db.Table("user").Structs(&users) +// err := db.Model("user").Scan(&users) func (m *Model) Scan(pointer interface{}, where ...interface{}) error { t := reflect.TypeOf(pointer) k := t.Kind() From 05703ec3d75dce80cc394ae337f5d665986d2947 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 29 Dec 2020 13:39:40 +0800 Subject: [PATCH 007/492] improve ghttp.RouterGroup.ALLMap --- net/ghttp/ghttp_server_router_group.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/ghttp/ghttp_server_router_group.go b/net/ghttp/ghttp_server_router_group.go index 5c02f3d86..809852dcb 100644 --- a/net/ghttp/ghttp_server_router_group.go +++ b/net/ghttp/ghttp_server_router_group.go @@ -179,12 +179,10 @@ func (g *RouterGroup) ALL(pattern string, object interface{}, params ...interfac } // ALLMap registers http handlers for http methods using map. -func (g *RouterGroup) ALLMap(m map[string]interface{}) *RouterGroup { - var group *RouterGroup +func (g *RouterGroup) ALLMap(m map[string]interface{}) { for pattern, object := range m { - group = g.ALL(pattern, object) + g.ALL(pattern, object) } - return group } // GET registers a http handler to given route pattern and http method: GET. From 662f1ed6b70221eb81d31cb103706ff5d88d6f8e Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 29 Dec 2020 14:01:49 +0800 Subject: [PATCH 008/492] add Query/Exec/Prepare back for gdb.Link --- database/gdb/gdb.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 93bea6c13..8375400b0 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -213,6 +213,9 @@ type TableField struct { // Link is a common database function wrapper interface. type Link interface { + Query(sql string, args ...interface{}) (*sql.Rows, error) + Exec(sql string, args ...interface{}) (sql.Result, error) + Prepare(sql string) (*sql.Stmt, error) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) From 4beeeb92ac9007abddcb9cc92bf07f496551874d Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 29 Dec 2020 14:28:26 +0800 Subject: [PATCH 009/492] improve gerror.Stack --- errors/gerror/gerror.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index c989d7f32..db141ef68 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -199,7 +199,7 @@ func Cause(err error) error { } // Stack returns the stack callers as string. -// It returns an empty string if the does not support stacks. +// It returns the error string directly if the does not support stacks. func Stack(err error) string { if err == nil { return "" @@ -207,7 +207,7 @@ func Stack(err error) string { if e, ok := err.(apiStack); ok { return e.Stack() } - return "" + return err.Error() } // Current creates and returns the current level error. From 51d9f7ff121a9dfb24b112f6c394e3baca3bc96a Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 29 Dec 2020 16:49:15 +0800 Subject: [PATCH 010/492] fix issue in configuration string parsing error when there're special chars(especially '?') in redis password --- database/gredis/gredis_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/gredis/gredis_config.go b/database/gredis/gredis_config.go index 0902c8362..c8d658081 100644 --- a/database/gredis/gredis_config.go +++ b/database/gredis/gredis_config.go @@ -86,7 +86,7 @@ func RemoveConfig(name ...string) { // ConfigFromStr parses and returns config from given str. // Eg: host:port[,db,pass?maxIdle=x&maxActive=x&idleTimeout=x&maxConnLifetime=x] func ConfigFromStr(str string) (config Config, err error) { - array, _ := gregex.MatchString(`([^:]+):*(\d*),{0,1}(\d*),{0,1}(.*)\?(.+)`, str) + array, _ := gregex.MatchString(`([^:]+):*(\d*),{0,1}(\d*),{0,1}(.*)\?(.+?)`, str) if len(array) == 6 { parse, _ := gstr.Parse(array[5]) config = Config{ From dbafe010641460085c6fd54ffc9025fddbbd27b1 Mon Sep 17 00:00:00 2001 From: tiansin Date: Tue, 29 Dec 2020 18:05:00 +0800 Subject: [PATCH 011/492] feat: :art: some time features. --- os/gtime/gtime_time.go | 102 +++++++++++++++++++++++ os/gtime/gtime_z_unit_time_test.go | 128 +++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+) diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 66d9d0797..ff4b516e0 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -320,6 +320,108 @@ func (t *Time) Sub(u *Time) time.Duration { return t.Time.Sub(u.Time) } +// StartOfMinute starting of minute +func (t *Time) StartOfMinute() *Time { + newTime := t.Clone() + newTime.Time = newTime.Time.Truncate(time.Minute) + return newTime +} + +// StartOfHour starting of hour +func (t *Time) StartOfHour() *Time { + y, m, d := t.Date() + newTime := t.Clone() + newTime.Time = time.Date(y, m, d, newTime.Time.Hour(), 0, 0, 0, newTime.Time.Location()) + return newTime +} + +// StartOfDay starting of day +func (t *Time) StartOfDay() *Time { + y, m, d := t.Date() + newTime := t.Clone() + newTime.Time = time.Date(y, m, d, 0, 0, 0, 0, newTime.Time.Location()) + return newTime +} + +// StartOfWeek starting of week +func (t *Time) StartOfWeek() *Time { + weekday := int(t.Weekday()) + return t.StartOfDay().AddDate(0, 0, -weekday) +} + +// StartOfMonth starting of month +func (t *Time) StartOfMonth() *Time { + y, m, _ := t.Date() + newTime := t.Clone() + newTime.Time = time.Date(y, m, 1, 0, 0, 0, 0, newTime.Time.Location()) + return newTime +} + +// StartOfQuarter starting of quarter +func (t *Time) StartOfQuarter() *Time { + month := t.StartOfMonth() + offset := (int(month.Month()) - 1) % 3 + return month.AddDate(0, -offset, 0) +} + +// StartOfHalf starting of half year +func (t *Time) StartOfHalf() *Time { + month := t.StartOfMonth() + offset := (int(month.Month()) - 1) % 6 + return month.AddDate(0, -offset, 0) +} + +// StartOfYear starting of year +func (t *Time) StartOfYear() *Time { + y, _, _ := t.Date() + newTime := t.Clone() + newTime.Time = time.Date(y, time.January, 1, 0, 0, 0, 0, newTime.Time.Location()) + return newTime +} + +// EndOfMinute end of minute +func (t *Time) EndOfMinute() *Time { + return t.StartOfMinute().Add(time.Minute - time.Nanosecond) +} + +// EndOfHour end of hour +func (t *Time) EndOfHour() *Time { + return t.StartOfHour().Add(time.Hour - time.Nanosecond) +} + +// EndOfDay end of day +func (t *Time) EndOfDay() *Time { + y, m, d := t.Date() + newTime := t.Clone() + newTime.Time = time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), newTime.Time.Location()) + return newTime +} + +// EndOfWeek end of week +func (t *Time) EndOfWeek() *Time { + return t.StartOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond) +} + +// EndOfMonth end of month +func (t *Time) EndOfMonth() *Time { + return t.StartOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond) +} + +// EndOfQuarter end of quarter +func (t *Time) EndOfQuarter() *Time { + return t.StartOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond) +} + +// EndOfHalf end of half year +func (t *Time) EndOfHalf() *Time { + return t.StartOfHalf().AddDate(0, 6, 0).Add(-time.Nanosecond) +} + +// EndOfYear end of year +func (t *Time) EndOfYear() *Time { + return t.StartOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond) +} + // MarshalJSON implements the interface MarshalJSON for json.Marshal. func (t *Time) MarshalJSON() ([]byte, error) { return []byte(`"` + t.String() + `"`), nil diff --git a/os/gtime/gtime_z_unit_time_test.go b/os/gtime/gtime_z_unit_time_test.go index edfa6530e..5eb2809bd 100644 --- a/os/gtime/gtime_z_unit_time_test.go +++ b/os/gtime/gtime_z_unit_time_test.go @@ -256,3 +256,131 @@ func Test_Truncate(t *testing.T) { t.Assert(timeTemp.UnixNano(), timeTemp1.Truncate(time.Hour).UnixNano()) }) } + +func Test_StartOfMinute(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-12 18:24:06") + timeTemp1 := timeTemp.StartOfMinute() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:24:00") + }) +} + +func Test_EndOfMinute(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-12 18:24:06") + timeTemp1 := timeTemp.EndOfMinute() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:24:59") + }) +} + +func Test_StartOfHour(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-12 18:24:06") + timeTemp1 := timeTemp.StartOfHour() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:00:00") + }) +} + +func Test_EndOfHour(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-12 18:24:06") + timeTemp1 := timeTemp.EndOfHour() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:59:59") + }) +} + +func Test_StartOfDay(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-12 18:24:06") + timeTemp1 := timeTemp.StartOfDay() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 00:00:00") + }) +} + +func Test_EndOfDay(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-12 18:24:06") + timeTemp1 := timeTemp.EndOfDay() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 23:59:59") + }) +} + +func Test_StartOfWeek(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-12 18:24:06") + timeTemp1 := timeTemp.StartOfWeek() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-06 00:00:00") + }) +} + +func Test_EndOfWeek(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-12 18:24:06") + timeTemp1 := timeTemp.EndOfWeek() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 23:59:59") + }) +} + +func Test_StartOfMonth(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-12 18:24:06") + timeTemp1 := timeTemp.StartOfMonth() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-01 00:00:00") + }) +} + +func Test_EndOfMonth(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-12 18:24:06") + timeTemp1 := timeTemp.EndOfMonth() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59") + }) +} + +func Test_StartOfQuarter(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-06 18:24:06") + timeTemp1 := timeTemp.StartOfQuarter() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-10-01 00:00:00") + }) +} + +func Test_EndOfQuarter(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-06 18:24:06") + timeTemp1 := timeTemp.EndOfQuarter() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59") + }) +} + +func Test_StartOfHalf(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-06 18:24:06") + timeTemp1 := timeTemp.StartOfHalf() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-07-01 00:00:00") + }) +} + +func Test_EndOfHalf(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-06 18:24:06") + timeTemp1 := timeTemp.EndOfHalf() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59") + }) +} + +func Test_StartOfYear(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-06 18:24:06") + timeTemp1 := timeTemp.StartOfYear() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-01-01 00:00:00") + }) +} + +func Test_EndOfYear(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.NewFromStr("2020-12-06 18:24:06") + timeTemp1 := timeTemp.EndOfYear() + t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59") + }) +} From 0d5b93bd07cb1e4d2817036b3cafeef95517016f Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 29 Dec 2020 19:43:25 +0800 Subject: [PATCH 012/492] fix issue in incorrect parsing error message for gview --- os/gview/gview_parse.go | 51 +++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index cd0a632af..8c7a3026b 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -58,8 +58,12 @@ func (view *View) Parse(file string, params ...Params) (result string, err error var tpl interface{} // It caches the file, folder and its content to enhance performance. r := view.fileCacheMap.GetOrSetFuncLock(file, func() interface{} { - var path, folder, content string - var resource *gres.File + var ( + path string + folder string + content string + resource *gres.File + ) // Searching the absolute file path for . path, folder, resource, err = view.searchFile(file) if err != nil { @@ -215,15 +219,14 @@ func (view *View) getTemplate(filePath, folderPath, pattern string) (tpl interfa // Key for template cache. key := fmt.Sprintf("%s_%v", filePath, view.config.Delimiters) result := templates.GetOrSetFuncLock(key, func() interface{} { - // Do not use but the as the parameter for function New, - // because when error occurs the will be printed out for error locating. + tplName := filePath if view.config.AutoEncode { - tpl = htmltpl.New(filePath).Delims( + tpl = htmltpl.New(tplName).Delims( view.config.Delimiters[0], view.config.Delimiters[1], ).Funcs(view.funcMap) } else { - tpl = texttpl.New(filePath).Delims( + tpl = texttpl.New(tplName).Delims( view.config.Delimiters[0], view.config.Delimiters[1], ).Funcs(view.funcMap) @@ -232,16 +235,22 @@ func (view *View) getTemplate(filePath, folderPath, pattern string) (tpl interfa if !gres.IsEmpty() { if files := gres.ScanDirFile(folderPath, pattern, true); len(files) > 0 { var err error - for _, v := range files { - if view.config.AutoEncode { - _, err = tpl.(*htmltpl.Template).New(v.FileInfo().Name()).Parse(string(v.Content())) + if view.config.AutoEncode { + t := tpl.(*htmltpl.Template) + for _, v := range files { + _, err = t.New(v.FileInfo().Name()).Parse(string(v.Content())) if err != nil { - intlog.Error(err) + err = view.formatTemplateObjectCreatingError(v.Name(), tplName, err) + return nil } - } else { - _, err = tpl.(*texttpl.Template).New(v.FileInfo().Name()).Parse(string(v.Content())) + } + } else { + t := tpl.(*texttpl.Template) + for _, v := range files { + _, err = t.New(v.FileInfo().Name()).Parse(string(v.Content())) if err != nil { - intlog.Error(err) + err = view.formatTemplateObjectCreatingError(v.Name(), tplName, err) + return nil } } } @@ -260,16 +269,16 @@ func (view *View) getTemplate(filePath, folderPath, pattern string) (tpl interfa if view.config.AutoEncode { t := tpl.(*htmltpl.Template) for _, file := range files { - _, err = t.Parse(gfile.GetContents(file)) - if err != nil { + if _, err = t.Parse(gfile.GetContents(file)); err != nil { + err = view.formatTemplateObjectCreatingError(file, tplName, err) return nil } } } else { t := tpl.(*texttpl.Template) for _, file := range files { - _, err = t.Parse(gfile.GetContents(file)) - if err != nil { + if _, err = t.Parse(gfile.GetContents(file)); err != nil { + err = view.formatTemplateObjectCreatingError(file, tplName, err) return nil } } @@ -282,6 +291,14 @@ func (view *View) getTemplate(filePath, folderPath, pattern string) (tpl interfa return } +// formatTemplateObjectCreatingError formats the error that creted from creating template object. +func (view *View) formatTemplateObjectCreatingError(filePath, tplName string, err error) error { + if err != nil { + return gerror.NewSkip(1, gstr.Replace(err.Error(), tplName, filePath)) + } + return nil +} + // searchFile returns the found absolute path for and its template folder path. // Note that, the returned is the template folder path, but not the folder of // the returned template file . From 820befa1a0a1279cbe9b155c59fa28e7678b3169 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 29 Dec 2020 20:30:29 +0800 Subject: [PATCH 013/492] improve insert function for package gdb --- database/gdb/gdb_model_insert.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 41562e665..eabe7e1e5 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -106,7 +106,7 @@ func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) { if len(data) > 0 { return m.Data(data...).Insert() } - return m.doInsertWithOption(insertOptionDefault, data...) + return m.doInsertWithOption(insertOptionDefault) } // InsertIgnore does "INSERT IGNORE INTO ..." statement for the model. @@ -116,7 +116,7 @@ func (m *Model) InsertIgnore(data ...interface{}) (result sql.Result, err error) if len(data) > 0 { return m.Data(data...).InsertIgnore() } - return m.doInsertWithOption(insertOptionIgnore, data...) + return m.doInsertWithOption(insertOptionIgnore) } // Replace does "REPLACE INTO ..." statement for the model. @@ -126,7 +126,7 @@ func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) { if len(data) > 0 { return m.Data(data...).Replace() } - return m.doInsertWithOption(insertOptionReplace, data...) + return m.doInsertWithOption(insertOptionReplace) } // Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the model. @@ -139,11 +139,11 @@ func (m *Model) Save(data ...interface{}) (result sql.Result, err error) { if len(data) > 0 { return m.Data(data...).Save() } - return m.doInsertWithOption(insertOptionSave, data...) + return m.doInsertWithOption(insertOptionSave) } // doInsertWithOption inserts data with option parameter. -func (m *Model) doInsertWithOption(option int, data ...interface{}) (result sql.Result, err error) { +func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { defer func() { if err == nil { m.checkAndRemoveCache() From bdf23ef48f52517275bf03841c0232facce552e2 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 30 Dec 2020 12:56:24 +0800 Subject: [PATCH 014/492] fix issue of error code lost in middleware handling for package ghttp --- database/gdb/gdb_core.go | 29 +++++-------- database/gdb/gdb_z_mysql_model_test.go | 55 ++++++++++++++++++------- net/ghttp/ghttp_unit_error_code_test.go | 45 ++++++++++++++++++++ util/gutil/gutil.go | 8 +++- 4 files changed, 101 insertions(+), 36 deletions(-) create mode 100644 net/ghttp/ghttp_unit_error_code_test.go diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index a2b6b36a2..033a9666d 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -48,9 +48,12 @@ func (c *Core) Ctx(ctx context.Context) DB { } // GetCtx returns the context for current DB. -// Note that it might be nil. +// It returns `context.Background()` is there's no context previously set. func (c *Core) GetCtx() context.Context { - return c.ctx + if c.ctx != nil { + return c.ctx + } + return context.Background() } // Master creates and returns a connection from master node if master-slave configured. @@ -78,15 +81,11 @@ func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error // DoQuery commits the sql string and its arguments to underlying driver // through given link object and returns the execution result. func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { - ctx := c.DB.GetCtx() - if ctx == nil { - ctx = context.Background() - } sql, args = formatSql(sql, args) sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) if c.DB.GetDebug() { mTime1 := gtime.TimestampMilli() - rows, err = link.QueryContext(ctx, sql, args...) + rows, err = link.QueryContext(c.DB.GetCtx(), sql, args...) mTime2 := gtime.TimestampMilli() s := &Sql{ Sql: sql, @@ -99,7 +98,7 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro } c.writeSqlToLogger(s) } else { - rows, err = link.QueryContext(ctx, sql, args...) + rows, err = link.QueryContext(c.DB.GetCtx(), sql, args...) } if err == nil { return rows, nil @@ -122,16 +121,12 @@ func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err err // DoExec commits the sql string and its arguments to underlying driver // through given link object and returns the execution result. func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) { - ctx := c.DB.GetCtx() - if ctx == nil { - ctx = context.Background() - } sql, args = formatSql(sql, args) sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) if c.DB.GetDebug() { mTime1 := gtime.TimestampMilli() if !c.DB.GetDryRun() { - result, err = link.ExecContext(ctx, sql, args...) + result, err = link.ExecContext(c.DB.GetCtx(), sql, args...) } else { result = new(SqlResult) } @@ -148,7 +143,7 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re c.writeSqlToLogger(s) } else { if !c.DB.GetDryRun() { - result, err = link.ExecContext(ctx, sql, args...) + result, err = link.ExecContext(c.DB.GetCtx(), sql, args...) } else { result = new(SqlResult) } @@ -183,11 +178,7 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error) { // doPrepare calls prepare function on given link object and returns the statement object. func (c *Core) DoPrepare(link Link, sql string) (*sql.Stmt, error) { - ctx := c.DB.GetCtx() - if ctx == nil { - ctx = context.Background() - } - return link.PrepareContext(ctx, sql) + return link.PrepareContext(c.DB.GetCtx(), sql) } // GetAll queries and returns data records from database. diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 6e24164bd..c52a13919 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -2974,23 +2974,48 @@ func Test_Model_Issue1002(t *testing.T) { t.Assert(v.Int(), 1) }) // where + time.Time arguments, UTC. - t1, _ := time.Parse("2006-01-02 15:04:05", "2020-10-27 19:03:32") - t2, _ := time.Parse("2006-01-02 15:04:05", "2020-10-27 19:03:34") gtest.C(t, func(t *gtest.T) { - v, err := db.Table(table).Fields("id").Where("create_time>? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time if any exception occurs ans passes the exception as an error. func TryCatch(try func(), catch ...func(exception error)) { defer func() { - if e := recover(); e != nil && len(catch) > 0 { - catch[0](fmt.Errorf(`%v`, e)) + if exception := recover(); exception != nil && len(catch) > 0 { + if err, ok := exception.(error); ok { + catch[0](err) + } else { + catch[0](fmt.Errorf(`%v`, exception)) + } } }() try() From 86e70ad55c47afe3fc4c2d418fa8ebaaba3fe7b7 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Wed, 30 Dec 2020 13:18:43 +0800 Subject: [PATCH 015/492] improve package gerror --- errors/gerror/gerror.go | 66 +++++++++++++++- errors/gerror/gerror_z_unit_test.go | 108 +++++++++++++++++++++++++- net/ghttp/ghttp_func.go | 16 ++-- net/ghttp/ghttp_request_middleware.go | 4 +- net/ghttp/ghttp_server_handler.go | 8 +- net/ghttp/ghttp_server_log.go | 2 +- 6 files changed, 189 insertions(+), 15 deletions(-) diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index db141ef68..ce5bc0fc5 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -1,4 +1,4 @@ -// Copyright GoFrame gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -92,7 +92,7 @@ func Wrap(err error, text string) error { error: err, stack: callers(), text: text, - code: -1, + code: Code(err), } } @@ -107,7 +107,37 @@ func Wrapf(err error, format string, args ...interface{}) error { error: err, stack: callers(), text: fmt.Sprintf(format, args...), - code: -1, + code: Code(err), + } +} + +// WrapSkip wraps error with text. +// It returns nil if given err is nil. +// The parameter specifies the stack callers skipped amount. +func WrapSkip(skip int, err error, text string) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(skip), + text: text, + code: Code(err), + } +} + +// WrapSkipf wraps error with text that is formatted with given format and args. +// It returns nil if given err is nil. +// The parameter specifies the stack callers skipped amount. +func WrapSkipf(skip int, err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(skip), + text: fmt.Sprintf(format, args...), + code: Code(err), } } @@ -177,6 +207,36 @@ func WrapCodef(code int, err error, format string, args ...interface{}) error { } } +// WrapCodeSkip wraps error with code and text. +// It returns nil if given err is nil. +// The parameter specifies the stack callers skipped amount. +func WrapCodeSkip(code, skip int, err error, text string) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(skip), + text: text, + code: code, + } +} + +// WrapCodeSkipf wraps error with code and text that is formatted with given format and args. +// It returns nil if given err is nil. +// The parameter specifies the stack callers skipped amount. +func WrapCodeSkipf(code, skip int, err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &Error{ + error: err, + stack: callers(skip), + text: fmt.Sprintf(format, args...), + code: code, + } +} + // Cause returns the error code of current error. // It returns -1 if it has no error code or it does not implements interface Code. func Code(err error) int { diff --git a/errors/gerror/gerror_z_unit_test.go b/errors/gerror/gerror_z_unit_test.go index c7c6e2541..254f5c159 100644 --- a/errors/gerror/gerror_z_unit_test.go +++ b/errors/gerror/gerror_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -26,6 +26,29 @@ func Test_Nil(t *testing.T) { }) } +func Test_New(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := gerror.New("1") + t.AssertNE(err, nil) + t.Assert(err.Error(), "1") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.Newf("%d", 1) + t.AssertNE(err, nil) + t.Assert(err.Error(), "1") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.NewSkip(1, "1") + t.AssertNE(err, nil) + t.Assert(err.Error(), "1") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.NewSkipf(1, "%d", 1) + t.AssertNE(err, nil) + t.Assert(err.Error(), "1") + }) +} + func Test_Wrap(t *testing.T) { gtest.C(t, func(t *gtest.T) { err := errors.New("1") @@ -49,6 +72,75 @@ func Test_Wrap(t *testing.T) { }) } +func Test_Wrapf(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := errors.New("1") + err = gerror.Wrapf(err, "%d", 2) + err = gerror.Wrapf(err, "%d", 3) + t.AssertNE(err, nil) + t.Assert(err.Error(), "3: 2: 1") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.New("1") + err = gerror.Wrapf(err, "%d", 2) + err = gerror.Wrapf(err, "%d", 3) + t.AssertNE(err, nil) + t.Assert(err.Error(), "3: 2: 1") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.New("1") + err = gerror.Wrapf(err, "") + t.AssertNE(err, nil) + t.Assert(err.Error(), "1") + }) +} + +func Test_WrapSkip(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := errors.New("1") + err = gerror.WrapSkip(1, err, "2") + err = gerror.WrapSkip(1, err, "3") + t.AssertNE(err, nil) + t.Assert(err.Error(), "3: 2: 1") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.New("1") + err = gerror.WrapSkip(1, err, "2") + err = gerror.WrapSkip(1, err, "3") + t.AssertNE(err, nil) + t.Assert(err.Error(), "3: 2: 1") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.New("1") + err = gerror.WrapSkip(1, err, "") + t.AssertNE(err, nil) + t.Assert(err.Error(), "1") + }) +} + +func Test_WrapSkipf(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := errors.New("1") + err = gerror.WrapSkipf(1, err, "2") + err = gerror.WrapSkipf(1, err, "3") + t.AssertNE(err, nil) + t.Assert(err.Error(), "3: 2: 1") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.New("1") + err = gerror.WrapSkipf(1, err, "2") + err = gerror.WrapSkipf(1, err, "3") + t.AssertNE(err, nil) + t.Assert(err.Error(), "3: 2: 1") + }) + gtest.C(t, func(t *gtest.T) { + err := gerror.New("1") + err = gerror.WrapSkipf(1, err, "") + t.AssertNE(err, nil) + t.Assert(err.Error(), "1") + }) +} + func Test_Cause(t *testing.T) { gtest.C(t, func(t *gtest.T) { err := errors.New("1") @@ -201,4 +293,18 @@ func Test_Code(t *testing.T) { t.Assert(gerror.Code(err), 1) t.Assert(err.Error(), "3: 2: 1") }) + gtest.C(t, func(t *gtest.T) { + err := errors.New("1") + err = gerror.Wrap(err, "2") + err = gerror.WrapCodeSkip(1, 100, err, "3") + t.Assert(gerror.Code(err), 1) + t.Assert(err.Error(), "3: 2: 1") + }) + gtest.C(t, func(t *gtest.T) { + err := errors.New("1") + err = gerror.Wrap(err, "2") + err = gerror.WrapCodeSkipf(1, 100, err, "%s", "3") + t.Assert(gerror.Code(err), 1) + t.Assert(err.Error(), "3: 2: 1") + }) } diff --git a/net/ghttp/ghttp_func.go b/net/ghttp/ghttp_func.go index 8e6b06acd..3a03bdfea 100644 --- a/net/ghttp/ghttp_func.go +++ b/net/ghttp/ghttp_func.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -69,19 +69,23 @@ func BuildParams(params interface{}, noUrlEncode ...bool) (encodedParamStr strin // niceCallFunc calls function with exception capture logic. func niceCallFunc(f func()) { defer func() { - if e := recover(); e != nil { - switch e { + if exception := recover(); exception != nil { + switch exception { case exceptionExit, exceptionExitAll: return default: - if _, ok := e.(errorStack); ok { + if _, ok := exception.(errorStack); ok { // It's already an error that has stack info. - panic(e) + panic(exception) } else { // Create a new error with stack info. // Note that there's a skip pointing the start stacktrace // of the real error point. - panic(gerror.NewSkipf(1, "%v", e)) + if err, ok := exception.(error); ok { + panic(gerror.Wrap(err, "")) + } else { + panic(gerror.NewSkipf(1, "%v", exception)) + } } } } diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index 13da4129d..6ddcde54e 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -128,7 +128,7 @@ func (m *Middleware) Next() { // Create a new error with stack info. // Note that there's a skip pointing the start stacktrace // of the real error point. - m.request.error = gerror.NewSkip(1, exception.Error()) + m.request.error = gerror.WrapSkip(1, exception, "") } m.request.Response.WriteStatus(http.StatusInternalServerError, exception) loop = false diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index fbe416a8b..8c7e24854 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -65,7 +65,11 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } else { if exception := recover(); exception != nil { request.Response.WriteStatus(http.StatusInternalServerError) - s.handleErrorLog(gerror.Newf("%v", exception), request) + if err, ok := exception.(error); ok { + s.handleErrorLog(gerror.Wrap(err, ""), request) + } else { + s.handleErrorLog(gerror.Newf("%v", exception), request) + } } } // access log handling. diff --git a/net/ghttp/ghttp_server_log.go b/net/ghttp/ghttp_server_log.go index 93e66dcb5..c8f0f5f8f 100644 --- a/net/ghttp/ghttp_server_log.go +++ b/net/ghttp/ghttp_server_log.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, From 036bc03ebf5862d5193094eee0d8c400cc31309b Mon Sep 17 00:00:00 2001 From: jflyfox Date: Wed, 30 Dec 2020 13:27:27 +0800 Subject: [PATCH 016/492] improve error handling for package gconv --- util/gconv/gconv.go | 8 ++++++++ util/gconv/gconv_map.go | 16 ++++++++++++---- util/gconv/gconv_struct.go | 8 ++++++-- util/gconv/gconv_structs.go | 8 ++++++-- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index fc0b5fb82..01b3a64ac 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -21,6 +21,14 @@ import ( "github.com/gogf/gf/encoding/gbinary" ) +type ( + // errorStack is the interface for Stack feature. + errorStack interface { + Error() string + Stack() string + } +) + var ( // Empty strings. emptyStringMap = map[string]struct{}{ diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index 8688507b9..c94a5bf49 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -444,8 +444,12 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s } defer func() { // Catch the panic, especially the reflect operation panics. - if e := recover(); e != nil { - err = gerror.NewSkipf(1, "%v", e) + if exception := recover(); exception != nil { + if e, ok := exception.(errorStack); ok { + err = e + } else { + err = gerror.NewSkipf(1, "%v", exception) + } } }() var ( @@ -544,8 +548,12 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] } defer func() { // Catch the panic, especially the reflect operation panics. - if e := recover(); e != nil { - err = gerror.NewSkipf(1, "%v", e) + if exception := recover(); exception != nil { + if e, ok := exception.(errorStack); ok { + err = e + } else { + err = gerror.NewSkipf(1, "%v", exception) + } } }() var ( diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index a84226e33..401f136a6 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -53,8 +53,12 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str defer func() { // Catch the panic, especially the reflect operation panics. - if e := recover(); e != nil { - err = gerror.NewSkipf(1, "%v", e) + if exception := recover(); exception != nil { + if e, ok := exception.(errorStack); ok { + err = e + } else { + err = gerror.NewSkipf(1, "%v", exception) + } } }() diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index 0372bb86f..a4ce17065 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -45,8 +45,12 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st defer func() { // Catch the panic, especially the reflect operation panics. - if e := recover(); e != nil { - err = gerror.NewSkipf(1, "%v", e) + if exception := recover(); exception != nil { + if e, ok := exception.(errorStack); ok { + err = e + } else { + err = gerror.NewSkipf(1, "%v", exception) + } } }() // If given is JSON, it then uses json.Unmarshal doing the converting. From 020b6bde683249795fac40d2e30c7b5b4c7cc4a4 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Wed, 30 Dec 2020 16:42:50 +0800 Subject: [PATCH 017/492] README updates --- README_ZH.MD | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README_ZH.MD b/README_ZH.MD index 7fb5049e5..d6bac9e19 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -64,8 +64,7 @@ golang版本 >= 1.11 # 性能 - -`Web`组件的性能测试,请参考第三方性能测试评估:https://github.com/the-benchmarker/web-frameworks +大家较为感兴趣的`Web`组件性能测试,请参考第三方性能测试评估:https://github.com/the-benchmarker/web-frameworks # 文档 From 5d01c9fff3486fc1f971fa112e58ab528c9e37b7 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 31 Dec 2020 00:27:42 +0800 Subject: [PATCH 018/492] version updates --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 2a3fc1a63..3bf3e1b03 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.14.6" +const VERSION = "v1.15.0" const AUTHORS = "john" From 8dab319a7fc1a0e2c9a3f55924e82ada2fc073e1 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 2 Jan 2021 00:51:53 +0800 Subject: [PATCH 019/492] README updates --- README.MD | 2 +- README_ZH.MD | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.MD b/README.MD index ae9422133..36d36395f 100644 --- a/README.MD +++ b/README.MD @@ -33,7 +33,7 @@ golang version >= 1.11 # Architecture
- +
# Packages diff --git a/README_ZH.MD b/README_ZH.MD index d6bac9e19..21d875e8b 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -49,7 +49,7 @@ golang版本 >= 1.11 # 架构
- +
# 模块 From 2a9c20bfa2767b59d1a99daf9d884eee348c4191 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 2 Jan 2021 01:24:42 +0800 Subject: [PATCH 020/492] README updates --- README.MD | 2 +- README_ZH.MD | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.MD b/README.MD index 36d36395f..d76587d1b 100644 --- a/README.MD +++ b/README.MD @@ -80,7 +80,7 @@ The `Web` component performance of `GoFrame`, please refer to third-party projec - [XiMaLaYa](https://www.ximalaya.com) - [ZYBang](https://www.zybang.com/) -> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://github.com/gogf/gf/issues/168). +> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://itician.org/pages/viewpage.action?pageId=1114415). # Contributors diff --git a/README_ZH.MD b/README_ZH.MD index 21d875e8b..a5f2cfb87 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -95,7 +95,7 @@ golang版本 >= 1.11 - [喜马拉雅](https://www.ximalaya.com) - [作业帮](https://www.zybang.com/) -> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://github.com/gogf/gf/issues/168) 留言。 +> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://itician.org/pages/viewpage.action?pageId=1114415) 留言。 # 贡献 From 6c08d5fd810668aebd7402211781989168e95164 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 2 Jan 2021 01:53:36 +0800 Subject: [PATCH 021/492] fix issue missing build-in variable Request for template parsing of ghttp.Response --- net/ghttp/ghttp_response_view.go | 1 + net/ghttp/ghttp_unit_template_test.go | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/net/ghttp/ghttp_response_view.go b/net/ghttp/ghttp_response_view.go index bb7abf1af..ca4a14473 100644 --- a/net/ghttp/ghttp_response_view.go +++ b/net/ghttp/ghttp_response_view.go @@ -84,6 +84,7 @@ func (r *Response) buildInVars(params ...map[string]interface{}) map[string]inte gutil.MapMerge(m, map[string]interface{}{ "Form": r.Request.GetFormMap(), "Query": r.Request.GetQueryMap(), + "Request": r.Request.GetMap(), "Cookie": r.Request.Cookie.Map(), "Session": r.Request.Session.Map(), }) diff --git a/net/ghttp/ghttp_unit_template_test.go b/net/ghttp/ghttp_unit_template_test.go index 781443af7..63bf01aef 100644 --- a/net/ghttp/ghttp_unit_template_test.go +++ b/net/ghttp/ghttp_unit_template_test.go @@ -139,6 +139,27 @@ func Test_Template_Layout2(t *testing.T) { }) } +func Test_Template_BuildInVarRequest(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + p, _ := ports.PopRand() + s := g.Server(p) + s.BindHandler("/:table/test", func(r *ghttp.Request) { + err := r.Response.WriteTplContent("{{.Request.table}}") + t.Assert(err, nil) + }) + s.SetDumpRouterMap(false) + s.SetPort(p) + s.Start() + defer s.Shutdown() + time.Sleep(100 * time.Millisecond) + client := g.Client() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + + t.Assert(client.GetContent("/user/test"), "user") + t.Assert(client.GetContent("/order/test"), "order") + }) +} + func Test_Template_XSS(t *testing.T) { gtest.C(t, func(t *gtest.T) { v := gview.New() From 4272ac16c7433030f7655cf68c1e5a6e9c95b450 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 2 Jan 2021 15:12:08 +0800 Subject: [PATCH 022/492] README updates --- README.MD | 2 +- README_ZH.MD | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.MD b/README.MD index d76587d1b..2e7e71125 100644 --- a/README.MD +++ b/README.MD @@ -33,7 +33,7 @@ golang version >= 1.11 # Architecture
- +
# Packages diff --git a/README_ZH.MD b/README_ZH.MD index a5f2cfb87..e9f5028fa 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -49,7 +49,7 @@ golang版本 >= 1.11 # 架构
- +
# 模块 From 361742c4a04def48090d36abae50bdca4be3db8c Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 2 Jan 2021 20:31:25 +0800 Subject: [PATCH 023/492] fix issue in incorrect datetime string argument for oracle --- database/gdb/gdb_driver_oracle.go | 130 +++++++++++---------- database/gdb/gdb_z_oracle_internal_test.go | 2 +- 2 files changed, 71 insertions(+), 61 deletions(-) diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index 1e0ee3c98..af0be9e33 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -16,13 +16,13 @@ import ( "fmt" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" + "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" "reflect" "strconv" "strings" - - "github.com/gogf/gf/text/gregex" + "time" ) // DriverOracle is the driver for oracle database. @@ -65,14 +65,8 @@ func (d *DriverOracle) GetChars() (charLeft string, charRight string) { } // HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverOracle) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) { +func (d *DriverOracle) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}) { var index int - // Convert place holder char '?' to string ":vx". - str, _ := gregex.ReplaceStringFunc("\\?", sql, func(s string) string { - index++ - return fmt.Sprintf(":v%d", index) - }) - str, _ = gregex.ReplaceString("\"", "", str) // Change time string argument wrapping with TO_DATE function. for i, v := range args { if reflect.TypeOf(v).Kind() == reflect.String { @@ -82,7 +76,25 @@ func (d *DriverOracle) HandleSqlBeforeCommit(link Link, sql string, args []inter } } } - return d.parseSql(str), args + // Convert place holder char '?' to string ":vx". + newSql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string { + index++ + return fmt.Sprintf(":v%d", index) + }) + newSql, _ = gregex.ReplaceString("\"", "", newSql) + // Handle string datetime argument. + for i, v := range args { + if reflect.TypeOf(v).Kind() == reflect.String { + valueStr := gconv.String(v) + if gregex.IsMatchString(`^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`, valueStr) { + //args[i] = fmt.Sprintf(`TO_DATE('%s','yyyy-MM-dd HH:MI:SS')`, valueStr) + args[i], _ = time.ParseInLocation("2006-01-02 15:04:05", valueStr, time.Local) + } + } + } + newSql = d.parseSql(newSql) + newArgs = args + return } // parseSql does some replacement of the sql before commits it to underlying driver, @@ -232,20 +244,20 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[ } func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) { - var fields []string - var values []string - var params []interface{} - var dataMap Map - rv := reflect.ValueOf(data) - kind := rv.Kind() + var ( + fields []string + values []string + params []interface{} + dataMap Map + rv = reflect.ValueOf(data) + kind = rv.Kind() + ) if kind == reflect.Ptr { rv = rv.Elem() kind = rv.Kind() } switch kind { - case reflect.Slice: - fallthrough - case reflect.Array: + case reflect.Slice, reflect.Array: return d.DB.DoBatchInsert(link, table, data, option, batch...) case reflect.Map: fallthrough @@ -254,10 +266,11 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio default: return result, gerror.New(fmt.Sprint("unsupported data type:", kind)) } - - indexs := make([]string, 0) - indexMap := make(map[string]string) - indexExists := false + var ( + indexes = make([]string, 0) + indexMap = make(map[string]string) + indexExists = false + ) if option != insertOptionDefault { index, err := d.getTableUniqueIndex(table) if err != nil { @@ -267,20 +280,19 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio if len(index) > 0 { for _, v := range index { for k, _ := range v { - indexs = append(indexs, k) + indexes = append(indexes, k) } indexMap = v indexExists = true break } } - } - - subSqlStr := make([]string, 0) - onStr := make([]string, 0) - updateStr := make([]string, 0) - + var ( + subSqlStr = make([]string, 0) + onStr = make([]string, 0) + updateStr = make([]string, 0) + ) charL, charR := d.DB.GetChars() for k, v := range dataMap { k = strings.ToUpper(k) @@ -290,10 +302,8 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio fields = append(fields, tableAlias1+"."+charL+k+charR) values = append(values, tableAlias2+"."+charL+k+charR) params = append(params, v) - subSqlStr = append(subSqlStr, fmt.Sprintf("%s?%s %s", charL, charR, k)) - - //merge中的on子句中由唯一索引组成,update子句中不含唯一索引 + //m erge中的on子句中由唯一索引组成, update子句中不含唯一索引 if _, ok := indexMap[k]; ok { onStr = append(onStr, fmt.Sprintf("%s.%s = %s.%s ", tableAlias1, k, tableAlias2, k)) } else { @@ -314,20 +324,21 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio if indexExists && option != insertOptionDefault { switch option { - case insertOptionReplace: - fallthrough - case insertOptionSave: + case + insertOptionReplace, + insertOptionSave: tmp := fmt.Sprintf( "MERGE INTO %s %s USING(SELECT %s FROM DUAL) %s ON(%s) WHEN MATCHED THEN UPDATE SET %s WHEN NOT MATCHED THEN INSERT (%s) VALUES(%s)", table, tableAlias1, strings.Join(subSqlStr, ","), tableAlias2, strings.Join(onStr, "AND"), strings.Join(updateStr, ","), strings.Join(fields, ","), strings.Join(values, ","), ) return d.DB.DoExec(link, tmp, params...) + case insertOptionIgnore: return d.DB.DoExec(link, fmt.Sprintf( "INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(%s(%s)) */ INTO %s(%s) VALUES(%s)", - table, strings.Join(indexs, ","), table, strings.Join(fields, ","), strings.Join(values, ","), + table, strings.Join(indexes, ","), table, strings.Join(fields, ","), strings.Join(values, ","), ), params...) } @@ -343,9 +354,11 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio } func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) { - var keys []string - var values []string - var params []interface{} + var ( + keys []string + values []string + params []interface{} + ) listMap := (List)(nil) switch v := list.(type) { case Result: @@ -357,17 +370,16 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{}, case Map: listMap = List{v} default: - rv := reflect.ValueOf(list) - kind := rv.Kind() + var ( + rv = reflect.ValueOf(list) + kind = rv.Kind() + ) if kind == reflect.Ptr { rv = rv.Elem() kind = rv.Kind() } switch kind { - // 如果是slice,那么转换为List类型 - case reflect.Slice: - fallthrough - case reflect.Array: + case reflect.Slice, reflect.Array: listMap = make(List, rv.Len()) for i := 0; i < rv.Len(); i++ { listMap[i] = ConvertDataForTableRecord(rv.Index(i).Interface()) @@ -375,12 +387,11 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{}, case reflect.Map: fallthrough case reflect.Struct: - listMap = List{Map(ConvertDataForTableRecord(list))} + listMap = List{ConvertDataForTableRecord(list)} default: return result, gerror.New(fmt.Sprint("unsupported list type:", kind)) } } - // 判断长度 if len(listMap) < 1 { return result, gerror.New("empty data list") } @@ -389,18 +400,18 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{}, return } } - // 首先获取字段名称及记录长度 + // Retrieve the table fields and length. holders := []string(nil) for k, _ := range listMap[0] { keys = append(keys, k) holders = append(holders, "?") } - batchResult := new(SqlResult) - charL, charR := d.DB.GetChars() - keyStr := charL + strings.Join(keys, charL+","+charR) + charR - valueHolderStr := strings.Join(holders, ",") - - // 当操作类型非insert时调用单笔的insert功能 + var ( + batchResult = new(SqlResult) + charL, charR = d.DB.GetChars() + keyStr = charL + strings.Join(keys, charL+","+charR) + charR + valueHolderStr = strings.Join(holders, ",") + ) if option != insertOptionDefault { for _, v := range listMap { r, err := d.DB.DoInsert(link, table, v, option, 1) @@ -418,13 +429,12 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{}, return batchResult, nil } - // 构造批量写入数据格式(注意map的遍历是无序的) batchNum := defaultBatchNumber if len(batch) > 0 { batchNum = batch[0] } - - intoStr := make([]string, 0) //组装into语句 + // Format "INSERT...INTO..." statement. + intoStr := make([]string, 0) for i := 0; i < len(listMap); i++ { for _, k := range keys { params = append(params, listMap[i][k]) @@ -446,7 +456,7 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{}, intoStr = intoStr[:0] } } - // 处理最后不构成指定批量的数据 + // The leftover data. if len(intoStr) > 0 { r, err := d.DB.DoExec(link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...) if err != nil { diff --git a/database/gdb/gdb_z_oracle_internal_test.go b/database/gdb/gdb_z_oracle_internal_test.go index 1afdee74a..f10895f1e 100644 --- a/database/gdb/gdb_z_oracle_internal_test.go +++ b/database/gdb/gdb_z_oracle_internal_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, From 5629020538b2f8d0ddf68b95e1ee6bb1ea255392 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 2 Jan 2021 21:06:51 +0800 Subject: [PATCH 024/492] fix issue in incorrect datetime string argument for oracle --- database/gdb/gdb_driver_oracle.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index af0be9e33..0a62443de 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -67,15 +67,6 @@ func (d *DriverOracle) GetChars() (charLeft string, charRight string) { // HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. func (d *DriverOracle) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}) { var index int - // Change time string argument wrapping with TO_DATE function. - for i, v := range args { - if reflect.TypeOf(v).Kind() == reflect.String { - valueStr := gconv.String(v) - if gregex.IsMatchString(`^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`, valueStr) { - args[i] = fmt.Sprintf(`TO_DATE('%s','yyyy-MM-dd HH:MI:SS')`, valueStr) - } - } - } // Convert place holder char '?' to string ":vx". newSql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string { index++ From 4991e14dff8d31bfa69f5abe77ea5b4200617123 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 3 Jan 2021 12:05:04 +0800 Subject: [PATCH 025/492] improve error message when failed in Mkdir --- os/gfile/gfile.go | 5 ++--- os/glog/glog_logger_config.go | 3 ++- os/gsession/gsession_storage_file.go | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/os/gfile/gfile.go b/os/gfile/gfile.go index b1be677d8..fa8596060 100644 --- a/os/gfile/gfile.go +++ b/os/gfile/gfile.go @@ -59,10 +59,9 @@ func init() { } // Mkdir creates directories recursively with given . -// The parameter is suggested to be absolute path. +// The parameter is suggested to be an absolute path instead of relative one. func Mkdir(path string) error { - err := os.MkdirAll(path, os.ModePerm) - if err != nil { + if err := os.MkdirAll(path, os.ModePerm); err != nil { return err } return nil diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index f9cbea08c..924a9e2e4 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -9,6 +9,7 @@ package glog import ( "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/util/gconv" @@ -191,7 +192,7 @@ func (l *Logger) SetPath(path string) error { if !gfile.Exists(path) { if err := gfile.Mkdir(path); err != nil { //fmt.Fprintln(os.Stderr, fmt.Sprintf(`[glog] mkdir "%s" failed: %s`, path, err.Error())) - return err + return gerror.Wrapf(err, `Mkdir "%s" failed in Pwd "%s"`, path, gfile.Pwd()) } } l.config.Path = strings.TrimRight(path, gfile.Separator) diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index 5a4a4a247..a256b0eec 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -9,6 +9,7 @@ package gsession import ( "fmt" "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" "os" @@ -55,7 +56,7 @@ func NewStorageFile(path ...string) *StorageFile { } if storagePath != "" { if err := gfile.Mkdir(storagePath); err != nil { - panic(fmt.Sprintf("mkdir '%s' failed: %v", path[0], err)) + panic(gerror.Wrapf(err, `Mkdir "%s" failed in Pwd "%s"`, path, gfile.Pwd())) } } s := &StorageFile{ From d9bd3153eaced2f9e387e725d5c07f30212bcefa Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 3 Jan 2021 23:37:45 +0800 Subject: [PATCH 026/492] improve time parsing for package gtime --- os/gtime/gtime.go | 45 +++++++++++++++++++++------- os/gtime/gtime_time.go | 18 ++++++++++- os/gtime/gtime_z_bench_test.go | 15 +++++++++- os/gtime/gtime_z_unit_basic_test.go | 32 +++++++++++++------- util/gconv/gconv_z_unit_time_test.go | 13 +++++--- 5 files changed, 97 insertions(+), 26 deletions(-) diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index 500b39761..3623fd602 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -45,7 +45,7 @@ const ( // "2018/10/31 - 16:38:46" // "2018-02-09", // "2018.02.09", - TIME_REAGEX_PATTERN1 = `(\d{4}[-/\.]\d{2}[-/\.]\d{2})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` + timeRegexPattern1 = `(\d{4}[-/\.]\d{2}[-/\.]\d{2})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` // Regular expression2(datetime separator supports '-', '/', '.'). // Eg: @@ -53,14 +53,21 @@ const ( // 01/Nov/2018 11:50:28 // 01.Nov.2018 11:50:28 // 01.Nov.2018:11:50:28 - TIME_REAGEX_PATTERN2 = `(\d{1,2}[-/\.][A-Za-z]{3,}[-/\.]\d{4})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` + timeRegexPattern2 = `(\d{1,2}[-/\.][A-Za-z]{3,}[-/\.]\d{4})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` + + // Regular expression3(time). + // Eg: + // 11:50:28 + // 11:50:28.897 + timeRegexPattern3 = `(\d{2}):(\d{2}):(\d{2})\.{0,1}(\d{0,9})` ) var ( // It's more high performance using regular expression // than time.ParseInLocation to parse the datetime string. - timeRegex1, _ = regexp.Compile(TIME_REAGEX_PATTERN1) - timeRegex2, _ = regexp.Compile(TIME_REAGEX_PATTERN2) + timeRegex1, _ = regexp.Compile(timeRegexPattern1) + timeRegex2, _ = regexp.Compile(timeRegexPattern2) + timeRegex3, _ = regexp.Compile(timeRegexPattern3) // Month words to arabic numerals mapping. monthMap = map[string]int{ @@ -247,15 +254,31 @@ func StrToTime(str string, format ...string) (*Time, error) { local = time.Local ) if match = timeRegex1.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { - for k, v := range match { - match[k] = strings.TrimSpace(v) - } + //for k, v := range match { + // match[k] = strings.TrimSpace(v) + //} year, month, day = parseDateStr(match[1]) } else if match = timeRegex2.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { - for k, v := range match { - match[k] = strings.TrimSpace(v) - } + //for k, v := range match { + // match[k] = strings.TrimSpace(v) + //} year, month, day = parseDateStr(match[1]) + } else if match = timeRegex3.FindStringSubmatch(str); len(match) > 0 && match[1] != "" { + //for k, v := range match { + // match[k] = strings.TrimSpace(v) + //} + s := strings.Replace(match[2], ":", "", -1) + if len(s) < 6 { + s += strings.Repeat("0", 6-len(s)) + } + hour, _ = strconv.Atoi(match[1]) + min, _ = strconv.Atoi(match[2]) + sec, _ = strconv.Atoi(match[3]) + nsec, _ = strconv.Atoi(match[4]) + for i := 0; i < 9-len(match[4]); i++ { + nsec *= 10 + } + return NewFromTime(time.Date(0, time.Month(1), 1, hour, min, sec, nsec, local)), nil } else { return nil, errors.New("unsupported time format") } @@ -387,6 +410,8 @@ func ParseTimeFromContent(content string, format ...string) *Time { return NewFromStr(strings.Trim(match[0], "./_- \n\r")) } else if match := timeRegex2.FindStringSubmatch(content); len(match) >= 1 { return NewFromStr(strings.Trim(match[0], "./_- \n\r")) + } else if match := timeRegex3.FindStringSubmatch(content); len(match) >= 1 { + return NewFromStr(strings.Trim(match[0], "./_- \n\r")) } } return nil diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 66d9d0797..9a9e51c4c 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -36,8 +36,24 @@ func New(param ...interface{}) *Time { case *Time: return r case string: + if len(param) > 1 { + switch t := param[1].(type) { + case string: + return NewFromStrFormat(r, t) + case []byte: + return NewFromStrFormat(r, string(t)) + } + } return NewFromStr(r) case []byte: + if len(param) > 1 { + switch t := param[1].(type) { + case string: + return NewFromStrFormat(string(r), t) + case []byte: + return NewFromStrFormat(string(r), string(t)) + } + } return NewFromStr(string(r)) case int: return NewFromTimeStamp(int64(r)) diff --git a/os/gtime/gtime_z_bench_test.go b/os/gtime/gtime_z_bench_test.go index 85408cf10..b6be5aead 100644 --- a/os/gtime/gtime_z_bench_test.go +++ b/os/gtime/gtime_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -8,6 +8,7 @@ package gtime_test import ( "testing" + "time" "github.com/gogf/gf/os/gtime" ) @@ -42,6 +43,18 @@ func Benchmark_StrToTime(b *testing.B) { } } +func Benchmark_StrToTime_Format(b *testing.B) { + for i := 0; i < b.N; i++ { + gtime.StrToTime("2018-02-09 20:46:17.897", "Y-m-d H:i:su") + } +} + +func Benchmark_StrToTime_Layout(b *testing.B) { + for i := 0; i < b.N; i++ { + gtime.StrToTimeLayout("2018-02-09T20:46:17.897Z", time.RFC3339) + } +} + func Benchmark_ParseTimeFromContent(b *testing.B) { for i := 0; i < b.N; i++ { gtime.ParseTimeFromContent("2018-02-09T20:46:17.897Z") diff --git a/os/gtime/gtime_z_unit_basic_test.go b/os/gtime/gtime_z_unit_basic_test.go index d4e0f8fc1..1f2e42fea 100644 --- a/os/gtime/gtime_z_unit_basic_test.go +++ b/os/gtime/gtime_z_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -7,6 +7,7 @@ package gtime_test import ( + "github.com/gogf/gf/frame/g" "testing" "time" @@ -86,6 +87,7 @@ func Test_RFC822(t *testing.T) { func Test_StrToTime(t *testing.T) { gtest.C(t, func(t *gtest.T) { + // Correct datetime string. var testDateTimes = []string{ "2006-01-02 15:04:05", "2006/01/02 15:04:05", @@ -103,13 +105,11 @@ func Test_StrToTime(t *testing.T) { for _, item := range testDateTimes { timeTemp, err := gtime.StrToTime(item) - if err != nil { - t.Error("test fail") - } + t.Assert(err, nil) t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2006-01-02 15:04:05") } - //正常日期列表,时间00:00:00 + // Correct date string,. var testDates = []string{ "2006.01.02", "2006.01.02 00:00", @@ -118,13 +118,25 @@ func Test_StrToTime(t *testing.T) { for _, item := range testDates { timeTemp, err := gtime.StrToTime(item) - if err != nil { - t.Error("test fail") - } + t.Assert(err, nil) t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2006-01-02 00:00:00") } - //测试格式化formatToStdLayout + // Correct time string. + var testTimes = g.MapStrStr{ + "16:12:01": "15:04:05", + "16:12:01.789": "15:04:05.000", + } + + for k, v := range testTimes { + time1, err := gtime.StrToTime(k) + t.Assert(err, nil) + time2, err := time.ParseInLocation(v, k, time.Local) + t.Assert(err, nil) + t.Assert(time1.Time, time2) + } + + // formatToStdLayout var testDateFormats = []string{ "Y-m-d H:i:s", "\\T\\i\\m\\e Y-m-d H:i:s", @@ -149,7 +161,7 @@ func Test_StrToTime(t *testing.T) { t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05.000"), "2007-01-02 15:04:05.000") } - //异常日期列表 + // 异常日期列表 var testDatesFail = []string{ "2006.01", "06..02", diff --git a/util/gconv/gconv_z_unit_time_test.go b/util/gconv/gconv_z_unit_time_test.go index 7f27375a6..335e599e5 100644 --- a/util/gconv/gconv_z_unit_time_test.go +++ b/util/gconv/gconv_z_unit_time_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -17,9 +17,14 @@ import ( func Test_Time(t *testing.T) { gtest.C(t, func(t *gtest.T) { - t1 := "2011-10-10 01:02:03.456" - t.AssertEQ(gconv.GTime(t1), gtime.NewFromStr(t1)) - t.AssertEQ(gconv.Time(t1), gtime.NewFromStr(t1).Time) + s := "2011-10-10 01:02:03.456" + t.AssertEQ(gconv.GTime(s), gtime.NewFromStr(s)) + t.AssertEQ(gconv.Time(s), gtime.NewFromStr(s).Time) t.AssertEQ(gconv.Duration(100), 100*time.Nanosecond) }) + gtest.C(t, func(t *gtest.T) { + s := "01:02:03.456" + t.AssertEQ(gconv.GTime(s), gtime.NewFromStr(s)) + t.AssertEQ(gconv.Time(s), gtime.NewFromStr(s).Time) + }) } From 4d962c5aa54cf267f9e7eaaa6d8ffc657b24236e Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 3 Jan 2021 23:44:44 +0800 Subject: [PATCH 027/492] improve packge gdb for treat int8 as int64 for pgsql --- database/gdb/gdb_core_structure.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database/gdb/gdb_core_structure.go b/database/gdb/gdb_core_structure.go index 27fedb2a1..33dda6e36 100644 --- a/database/gdb/gdb_core_structure.go +++ b/database/gdb/gdb_core_structure.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -56,6 +56,7 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s return gconv.Int(gconv.String(fieldValue)) case + "int8", // For pgsql, int8 = bigint. "big_int", "bigint", "bigserial": From 8a91592839ec4dcca15f5a377ea14bab0b7d32a6 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 4 Jan 2021 00:05:02 +0800 Subject: [PATCH 028/492] fix issue in eq for package gview --- os/gview/gview_buildin.go | 8 ++++---- os/gview/gview_unit_basic_test.go | 13 +++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/os/gview/gview_buildin.go b/os/gview/gview_buildin.go index fc4f8f697..064903897 100644 --- a/os/gview/gview_buildin.go +++ b/os/gview/gview_buildin.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -51,11 +51,11 @@ func (view *View) buildInFuncMaps(value ...interface{}) []map[string]interface{} func (view *View) buildInFuncEq(value interface{}, others ...interface{}) bool { s := gconv.String(value) for _, v := range others { - if strings.Compare(s, gconv.String(v)) != 0 { - return false + if strings.Compare(s, gconv.String(v)) == 0 { + return true } } - return true + return false } // buildInFuncNe implements build-in template function: ne diff --git a/os/gview/gview_unit_basic_test.go b/os/gview/gview_unit_basic_test.go index afca128ed..c46767919 100644 --- a/os/gview/gview_unit_basic_test.go +++ b/os/gview/gview_unit_basic_test.go @@ -175,6 +175,19 @@ func Test_Func(t *testing.T) { t.Assert(err, nil) t.Assert(result, `ILoveGoFrame`) }) + // eq: multiple values. + gtest.C(t, func(t *gtest.T) { + str := `{{eq 1 2 1 3 4 5}}` + result, err := gview.ParseContent(str, nil) + t.Assert(err != nil, false) + t.Assert(result, `true`) + }) + gtest.C(t, func(t *gtest.T) { + str := `{{eq 6 2 1 3 4 5}}` + result, err := gview.ParseContent(str, nil) + t.Assert(err != nil, false) + t.Assert(result, `false`) + }) } func Test_FuncNl2Br(t *testing.T) { From d5493112106db66c1d3dc10c9f6af1dabf4336ba Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 13:04:57 +0800 Subject: [PATCH 029/492] fix issue in uint testing case for package gdb --- database/gdb/gdb_z_mysql_types_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/gdb/gdb_z_mysql_types_test.go b/database/gdb/gdb_z_mysql_types_test.go index 9f34771f4..f261b0e2c 100644 --- a/database/gdb/gdb_z_mysql_types_test.go +++ b/database/gdb/gdb_z_mysql_types_test.go @@ -67,7 +67,7 @@ func Test_Types(t *testing.T) { t.Assert(one["blob"].String(), data["blob"]) t.Assert(one["binary"].String(), data["binary"]) t.Assert(one["date"].String(), data["date"]) - t.Assert(one["time"].String(), data["time"]) + t.Assert(one["time"].String(), `0000-01-01 10:00:01`) t.Assert(one["decimal"].String(), -123.46) t.Assert(one["double"].String(), data["double"]) t.Assert(one["bit"].Int(), data["bit"]) From dd2dfbf58d0820f1fbfee826f4efeb17c9f1dea7 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 13:50:23 +0800 Subject: [PATCH 030/492] fix issue of data race in unit testing case for package gtime --- os/gtime/gtime_z_unit_basic_test.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/os/gtime/gtime_z_unit_basic_test.go b/os/gtime/gtime_z_unit_basic_test.go index 1f2e42fea..941171a95 100644 --- a/os/gtime/gtime_z_unit_basic_test.go +++ b/os/gtime/gtime_z_unit_basic_test.go @@ -15,12 +15,17 @@ import ( "github.com/gogf/gf/test/gtest" ) -func Test_SetTimeZone(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - gtime.SetTimeZone("Asia/Shanghai") - t.Assert(time.Local.String(), "Asia/Shanghai") - }) -} +// DATA RACE! +//time.Now() +// /home/travis/.gimme/versions/go1.11.13.linux.amd64/src/time/time.go:1060 +0xcf +//time.sendTime() +// /home/travis/.gimme/versions/go1.11.13.linux.amd64/src/time/sleep.go:141 +0x44 +//func Test_SetTimeZone(t *testing.T) { +// gtest.C(t, func(t *gtest.T) { +// gtime.SetTimeZone("Asia/Shanghai") +// t.Assert(time.Local.String(), "Asia/Shanghai") +// }) +//} func Test_Nanosecond(t *testing.T) { gtest.C(t, func(t *gtest.T) { From 2eb09efc811da1d34cd280e2ad426cd788af56b2 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 14:03:22 +0800 Subject: [PATCH 031/492] fix issue of data race in unit testing case for package gtime --- os/gtime/gtime_z_unit_basic_test.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/os/gtime/gtime_z_unit_basic_test.go b/os/gtime/gtime_z_unit_basic_test.go index 941171a95..c1f1afef9 100644 --- a/os/gtime/gtime_z_unit_basic_test.go +++ b/os/gtime/gtime_z_unit_basic_test.go @@ -15,17 +15,14 @@ import ( "github.com/gogf/gf/test/gtest" ) -// DATA RACE! -//time.Now() -// /home/travis/.gimme/versions/go1.11.13.linux.amd64/src/time/time.go:1060 +0xcf -//time.sendTime() -// /home/travis/.gimme/versions/go1.11.13.linux.amd64/src/time/sleep.go:141 +0x44 -//func Test_SetTimeZone(t *testing.T) { -// gtest.C(t, func(t *gtest.T) { -// gtime.SetTimeZone("Asia/Shanghai") -// t.Assert(time.Local.String(), "Asia/Shanghai") -// }) -//} +func init() { + gtime.SetTimeZone("Asia/Shanghai") +} +func Test_SetTimeZone(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(time.Local.String(), "Asia/Shanghai") + }) +} func Test_Nanosecond(t *testing.T) { gtest.C(t, func(t *gtest.T) { From 3ab32facccf09cc4a6ff5df7f2b992606290f786 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 14:15:42 +0800 Subject: [PATCH 032/492] improve package gins --- frame/gins/gins_database.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index bcedabf7f..b3a57a6b1 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -8,6 +8,7 @@ package gins import ( "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gutil" @@ -47,7 +48,25 @@ func Database(name ...string) gdb.DB { configMap = Config().GetMap(configNodeKey) } if len(configMap) == 0 && !gdb.IsConfigured() { - panic(fmt.Sprintf(`database init failed: "%s" node not found, is config file or configuration missing?`, configNodeNameDatabase)) + if !Config().Available() { + exampleFileName := "config.example.toml" + if Config().Available(exampleFileName) { + panic(gerror.Newf( + `configuration file "%s" not found, but found "%s", did you miss renaming the configuration example file?`, + Config().GetFileName(), + exampleFileName, + )) + } else { + panic(gerror.Newf( + `configuration file "%s" not found, did you miss the configuration file or the file name setting?`, + Config().GetFileName(), + )) + } + } + panic(gerror.Newf( + `database initialization failed: "%s" node not found, is configuration file or configuration node missing?`, + configNodeNameDatabase, + )) } if len(configMap) == 0 { configMap = make(map[string]interface{}) From b3b1418e11f46caa8832da67825cef158693ea6f Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 14:43:17 +0800 Subject: [PATCH 033/492] improve packge gi18n --- i18n/gi18n/gi18n_manager.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/i18n/gi18n/gi18n_manager.go b/i18n/gi18n/gi18n_manager.go index e7105c5be..6b737df94 100644 --- a/i18n/gi18n/gi18n_manager.go +++ b/i18n/gi18n/gi18n_manager.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -13,8 +13,6 @@ import ( "strings" "sync" - "github.com/gogf/gf/os/glog" - "github.com/gogf/gf/os/gfsnotify" "github.com/gogf/gf/text/gregex" @@ -235,11 +233,11 @@ func (m *Manager) init() { m.data[lang] = make(map[string]string) } if j, err := gjson.LoadContent(file.Content()); err == nil { - for k, v := range j.ToMap() { + for k, v := range j.Map() { m.data[lang][k] = gconv.String(v) } } else { - glog.Errorf("load i18n file '%s' failed: %v", name, err) + intlog.Errorf("load i18n file '%s' failed: %v", name, err) } } } @@ -270,11 +268,11 @@ func (m *Manager) init() { m.data[lang] = make(map[string]string) } if j, err := gjson.LoadContent(gfile.GetBytes(file)); err == nil { - for k, v := range j.ToMap() { + for k, v := range j.Map() { m.data[lang][k] = gconv.String(v) } } else { - glog.Errorf("load i18n file '%s' failed: %v", file, err) + intlog.Errorf("load i18n file '%s' failed: %v", file, err) } } // Monitor changes of i18n files for hot reload feature. From 9d865e4ac6fba102eb071b63daa7d013d468a4dc Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 15:32:55 +0800 Subject: [PATCH 034/492] improve package gtime --- os/gtime/gtime.go | 15 ++++++--------- os/gtime/gtime_z_unit_basic_test.go | 6 ++---- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index 3623fd602..88c38d460 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -14,6 +14,7 @@ import ( "fmt" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/utils" + "os" "regexp" "strconv" "strings" @@ -102,18 +103,14 @@ var ( // The parameter is an area string specifying corresponding time zone, // eg: Asia/Shanghai. // -// Note that the time zone database needed by LoadLocation may not be -// present on all systems, especially non-Unix systems. -// LoadLocation looks in the directory or uncompressed zip file -// named by the ZONEINFO environment variable, if any, then looks in -// known installation locations on Unix systems, -// and finally looks in $GOROOT/lib/time/zoneinfo.zip. +// This should be called before package "time" import. +// Please refer to issue: https://github.com/golang/go/issues/34814 func SetTimeZone(zone string) error { location, err := time.LoadLocation(zone) - if err == nil { - time.Local = location + if err != nil { + return err } - return err + return os.Setenv("TZ", location.String()) } // Timestamp retrieves and returns the timestamp in seconds. diff --git a/os/gtime/gtime_z_unit_basic_test.go b/os/gtime/gtime_z_unit_basic_test.go index c1f1afef9..c4e8b2c26 100644 --- a/os/gtime/gtime_z_unit_basic_test.go +++ b/os/gtime/gtime_z_unit_basic_test.go @@ -15,12 +15,10 @@ import ( "github.com/gogf/gf/test/gtest" ) -func init() { - gtime.SetTimeZone("Asia/Shanghai") -} func Test_SetTimeZone(t *testing.T) { gtest.C(t, func(t *gtest.T) { - t.Assert(time.Local.String(), "Asia/Shanghai") + t.Assert(gtime.SetTimeZone("Asia/Shanghai"), nil) + //t.Assert(time.Local.String(), "Asia/Shanghai") }) } From 1e100ac0ec2e20aaaa0c04ab985add4368007b14 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 15:43:54 +0800 Subject: [PATCH 035/492] improve package gtime --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ee2f52ac1..1ffca7d2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,9 @@ branches: - staging env: -- GF_DEBUG=1 GO111MODULE=on +- TZ=Asia/Shanghai +- GF_DEBUG=1 +- GO111MODULE=on services: - mysql From e4069bdb939153ac9f4439509a1a1e6ff8aef787 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 15:46:51 +0800 Subject: [PATCH 036/492] improve package gtime --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1ffca7d2b..8bcf0f4ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,7 @@ branches: - staging env: -- TZ=Asia/Shanghai -- GF_DEBUG=1 -- GO111MODULE=on +- TZ=Asia/Shanghai GF_DEBUG=1 GO111MODULE=on services: - mysql From a62d2589bca1de7f9254e1d8a18043eaffd0ce66 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 16:33:33 +0800 Subject: [PATCH 037/492] improve time zone feature for package gtime --- os/gtime/gtime_time.go | 23 --------------- os/gtime/gtime_time_zone.go | 58 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 os/gtime/gtime_time_zone.go diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 9a9e51c4c..746a8f5ec 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -235,22 +235,6 @@ func (t *Time) AddStr(duration string) (*Time, error) { } } -// ToLocation converts current time to specified location. -func (t *Time) ToLocation(location *time.Location) *Time { - newTime := t.Clone() - newTime.Time = newTime.Time.In(location) - return newTime -} - -// ToZone converts current time to specified zone like: Asia/Shanghai. -func (t *Time) ToZone(zone string) (*Time, error) { - if l, err := time.LoadLocation(zone); err == nil { - return t.ToLocation(l), nil - } else { - return nil, err - } -} - // UTC converts current time to UTC timezone. func (t *Time) UTC() *Time { newTime := t.Clone() @@ -268,13 +252,6 @@ func (t *Time) RFC822() string { return t.Layout("Mon, 02 Jan 06 15:04 MST") } -// Local converts the time to local timezone. -func (t *Time) Local() *Time { - newTime := t.Clone() - newTime.Time = newTime.Time.Local() - return newTime -} - // AddDate adds year, month and day to the time. func (t *Time) AddDate(years int, months int, days int) *Time { newTime := t.Clone() diff --git a/os/gtime/gtime_time_zone.go b/os/gtime/gtime_time_zone.go new file mode 100644 index 000000000..d7a50eb2c --- /dev/null +++ b/os/gtime/gtime_time_zone.go @@ -0,0 +1,58 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtime + +import ( + "sync" + "time" +) + +var ( + // locationMap is time zone name to its location object. + // Time zone name is like: Asia/Shanghai. + locationMap = make(map[string]*time.Location) + // locationMu is used for concurrent safety for `locationMap`. + locationMu = sync.RWMutex{} +) + +// ToLocation converts current time to specified location. +func (t *Time) ToLocation(location *time.Location) *Time { + newTime := t.Clone() + newTime.Time = newTime.Time.In(location) + return newTime +} + +// ToZone converts current time to specified zone like: Asia/Shanghai. +func (t *Time) ToZone(zone string) (*Time, error) { + if location, err := t.getLocationByZoneName(zone); err == nil { + return t.ToLocation(location), nil + } else { + return nil, err + } +} + +func (t *Time) getLocationByZoneName(name string) (location *time.Location, err error) { + locationMu.RLock() + location = locationMap[name] + locationMu.RUnlock() + if location == nil { + location, err = time.LoadLocation(name) + if err == nil && location != nil { + locationMu.Lock() + locationMap[name] = location + locationMu.Unlock() + } + } + return +} + +// Local converts the time to local timezone. +func (t *Time) Local() *Time { + newTime := t.Clone() + newTime.Time = newTime.Time.Local() + return newTime +} From b6b1bc8813b2b97697e5b381a26c0cb3b6415cba Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 19:22:55 +0800 Subject: [PATCH 038/492] improve time converting for package gconv --- database/gdb/gdb_z_mysql_types_test.go | 27 +++++++++++++++++++++++++- os/gtime/gtime.go | 8 ++++---- os/gtime/gtime_time.go | 5 +++++ util/gconv/gconv_z_unit_time_test.go | 14 +++++++++++++ 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/database/gdb/gdb_z_mysql_types_test.go b/database/gdb/gdb_z_mysql_types_test.go index f261b0e2c..afe03a5d5 100644 --- a/database/gdb/gdb_z_mysql_types_test.go +++ b/database/gdb/gdb_z_mysql_types_test.go @@ -8,6 +8,7 @@ package gdb_test import ( "fmt" + "github.com/gogf/gf/os/gtime" "testing" "github.com/gogf/gf/frame/g" @@ -15,6 +16,7 @@ import ( "github.com/gogf/gf/test/gtest" ) +// All types testing. func Test_Types(t *testing.T) { gtest.C(t, func(t *gtest.T) { if _, err := db.Exec(fmt.Sprintf(` @@ -72,6 +74,29 @@ func Test_Types(t *testing.T) { t.Assert(one["double"].String(), data["double"]) t.Assert(one["bit"].Int(), data["bit"]) t.Assert(one["tinyint"].Bool(), data["tinyint"]) - t.Assert(one["tinyint"].Bool(), data["tinyint"]) + + type T struct { + Id int + Blob []byte + Binary []byte + Date *gtime.Time + Time *gtime.Time + Decimal float64 + Double float64 + Bit int8 + TinyInt bool + } + var obj *T + err = db.Table("types").Struct(&obj) + t.Assert(err, nil) + t.Assert(obj.Id, 1) + t.Assert(obj.Blob, data["blob"]) + t.Assert(obj.Binary, data["binary"]) + t.Assert(obj.Date.Format("Y-m-d"), data["date"]) + t.Assert(obj.Time.String(), `0000-01-01 10:00:01`) + t.Assert(obj.Decimal, -123.46) + t.Assert(obj.Double, data["double"]) + t.Assert(obj.Bit, data["bit"]) + t.Assert(obj.TinyInt, data["tinyint"]) }) } diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index 88c38d460..e03a8715c 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -350,9 +350,9 @@ func StrToTime(str string, format ...string) (*Time, error) { } } } - if year <= 0 { - return nil, errors.New("invalid time string:" + str) - } + //if year <= 0 { + // return nil, errors.New("invalid time string:" + str) + //} return NewFromTime(time.Date(year, time.Month(month), day, hour, min, sec, nsec, local)), nil } @@ -367,7 +367,7 @@ func ConvertZone(strTime string, toZone string, fromZone ...string) (*Time, erro if l, err := time.LoadLocation(fromZone[0]); err != nil { return nil, err } else { - t.Time = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Time.Second(), t.Time.Nanosecond(), l) + t.Time = time.Date(t.Year(), time.Month(t.Month()), t.Day(), t.Hour(), t.Minute(), t.Time.Second(), t.Time.Nanosecond(), l) } } if l, err := time.LoadLocation(toZone); err != nil { diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 746a8f5ec..4584b661e 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -179,6 +179,11 @@ func (t *Time) TimestampNanoStr() string { return strconv.FormatInt(t.TimestampNano(), 10) } +// Month returns the month of the year specified by t. +func (t *Time) Month() int { + return int(t.Time.Month()) +} + // Second returns the second offset within the minute specified by t, // in the range [0, 59]. func (t *Time) Second() int { diff --git a/util/gconv/gconv_z_unit_time_test.go b/util/gconv/gconv_z_unit_time_test.go index 335e599e5..608ad6b67 100644 --- a/util/gconv/gconv_z_unit_time_test.go +++ b/util/gconv/gconv_z_unit_time_test.go @@ -24,6 +24,20 @@ func Test_Time(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { s := "01:02:03.456" + t.AssertEQ(gconv.GTime(s).Hour(), 1) + t.AssertEQ(gconv.GTime(s).Minute(), 2) + t.AssertEQ(gconv.GTime(s).Second(), 3) + t.AssertEQ(gconv.GTime(s), gtime.NewFromStr(s)) + t.AssertEQ(gconv.Time(s), gtime.NewFromStr(s).Time) + }) + gtest.C(t, func(t *gtest.T) { + s := "0000-01-01 01:02:03" + t.AssertEQ(gconv.GTime(s).Year(), 0) + t.AssertEQ(gconv.GTime(s).Month(), 1) + t.AssertEQ(gconv.GTime(s).Day(), 1) + t.AssertEQ(gconv.GTime(s).Hour(), 1) + t.AssertEQ(gconv.GTime(s).Minute(), 2) + t.AssertEQ(gconv.GTime(s).Second(), 3) t.AssertEQ(gconv.GTime(s), gtime.NewFromStr(s)) t.AssertEQ(gconv.Time(s), gtime.NewFromStr(s).Time) }) From 7bc42e6eaa29f8f39bf096659b8ff3d3189febb5 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 19:46:46 +0800 Subject: [PATCH 039/492] improve Fields filtering for package gdb --- database/gdb/gdb_core_structure.go | 7 +------ database/gdb/gdb_z_mysql_model_test.go | 12 +++++++++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/database/gdb/gdb_core_structure.go b/database/gdb/gdb_core_structure.go index 33dda6e36..9458de0ab 100644 --- a/database/gdb/gdb_core_structure.go +++ b/database/gdb/gdb_core_structure.go @@ -7,7 +7,6 @@ package gdb import ( - "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/util/gutil" "strings" "time" @@ -163,15 +162,11 @@ func (c *Core) mappingAndFilterData(schema, table string, data map[string]interf if foundKey != "" { data[foundKey] = dataValue delete(data, dataKey) - } else if !filter { - if schema != "" { - return nil, gerror.Newf(`no column of name "%s" found for table "%s" in schema "%s"`, dataKey, table, schema) - } - return nil, gerror.Newf(`no column of name "%s" found for table "%s"`, dataKey, table) } } } // Data filtering. + // It deletes all key-value pairs that has incorrect field name. if filter { for dataKey, _ := range data { if _, ok := fieldsMap[dataKey]; !ok { diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index c52a13919..697f78382 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -496,6 +496,16 @@ func Test_Model_Update(t *testing.T) { n, _ := result.RowsAffected() t.Assert(n, 1) }) + // Update + Fields(string) + gtest.C(t, func(t *gtest.T) { + result, err := db.Table(table).Fields("passport").Data(g.Map{ + "passport": "user_44", + "none": "none", + }).Where("passport='user_4'").Update() + t.Assert(err, nil) + n, _ := result.RowsAffected() + t.Assert(n, 1) + }) } func Test_Model_Clone(t *testing.T) { From 9fbdb9712bdb9da1009476b881fa0aec67ef9d1f Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 4 Jan 2021 19:50:44 +0800 Subject: [PATCH 040/492] improve package gtime --- os/gtime/gtime.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index e03a8715c..1c6c5a35c 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -10,7 +10,6 @@ package gtime import ( - "errors" "fmt" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/utils" @@ -277,7 +276,7 @@ func StrToTime(str string, format ...string) (*Time, error) { } return NewFromTime(time.Date(0, time.Month(1), 1, hour, min, sec, nsec, local)), nil } else { - return nil, errors.New("unsupported time format") + return nil, gerror.New("unsupported time format") } // Time @@ -350,9 +349,9 @@ func StrToTime(str string, format ...string) (*Time, error) { } } } - //if year <= 0 { - // return nil, errors.New("invalid time string:" + str) - //} + if month <= 0 || day <= 0 { + return nil, gerror.New("invalid time string:" + str) + } return NewFromTime(time.Date(year, time.Month(month), day, hour, min, sec, nsec, local)), nil } From 5c2574da7c0e91cb84824a0af2c9fed8d76fd39b Mon Sep 17 00:00:00 2001 From: jflyfox Date: Wed, 6 Jan 2021 00:39:50 +0800 Subject: [PATCH 041/492] improve package ghttp --- net/ghttp/ghttp_server.go | 17 ++++++++--------- net/ghttp/ghttp_server_config.go | 4 ++-- net/ghttp/ghttp_server_config_static.go | 8 ++++---- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index b1ea3228e..1b17e0c4d 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -8,9 +8,8 @@ package ghttp import ( "bytes" - "errors" - "fmt" "github.com/gogf/gf/debug/gdebug" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "net/http" "os" @@ -125,13 +124,13 @@ func (s *Server) Start() error { // Server can only be run once. if s.Status() == ServerStatusRunning { - return errors.New("[ghttp] server is already running") + return gerror.New("server is already running") } // Logging path setting check. if s.config.LogPath != "" { if err := s.config.Logger.SetPath(s.config.LogPath); err != nil { - return errors.New(fmt.Sprintf("[ghttp] set log path '%s' error: %v", s.config.LogPath, err)) + return gerror.Wrapf(err, `set logging path "%s" failed`, s.config.LogPath) } } // Default session storage. @@ -141,7 +140,7 @@ func (s *Server) Start() error { path = gfile.Join(s.config.SessionPath, s.name) if !gfile.Exists(path) { if err := gfile.Mkdir(path); err != nil { - return errors.New(fmt.Sprintf("[ghttp] mkdir failed for '%s': %v", path, err)) + return gerror.Wrapf(err, `mkdir failed for "%s"`, path) } } } @@ -175,7 +174,7 @@ func (s *Server) Start() error { // If there's no route registered and no static service enabled, // it then returns an error of invalid usage of server. if len(s.routesMap) == 0 && !s.config.FileServerEnabled { - return errors.New(`[ghttp] there's no route set or static feature enabled, did you forget import the router?`) + return gerror.New(`there's no route set or static feature enabled, did you forget import the router?`) } // Start the HTTP server. @@ -196,7 +195,7 @@ func (s *Server) Start() error { if gproc.IsChild() { gtimer.SetTimeout(2*time.Second, func() { if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil { - //glog.Error("[ghttp] server error in process communication:", err) + //glog.Error("server error in process communication:", err) } }) } @@ -320,7 +319,7 @@ func (s *Server) Run() { p.Remove() } } - s.Logger().Printf("[ghttp] %d: all servers shutdown", gproc.Pid()) + s.Logger().Printf("%d: all servers shutdown", gproc.Pid()) } // Wait blocks to wait for all servers done. @@ -338,7 +337,7 @@ func Wait() { } return true }) - glog.Printf("[ghttp] %d: all servers shutdown", gproc.Pid()) + glog.Printf("%d: all servers shutdown", gproc.Pid()) } // startServer starts the underlying server listening. diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 3cc4a1b95..edd63d4ea 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -393,7 +393,7 @@ func (s *Server) EnableHTTPS(certFile, keyFile string, tlsConfig ...*tls.Config) certFileRealPath = certFile } if certFileRealPath == "" { - s.Logger().Fatal(fmt.Sprintf(`[ghttp] EnableHTTPS failed: certFile "%s" does not exist`, certFile)) + s.Logger().Fatal(fmt.Sprintf(`EnableHTTPS failed: certFile "%s" does not exist`, certFile)) } keyFileRealPath := gfile.RealPath(keyFile) if keyFileRealPath == "" { @@ -407,7 +407,7 @@ func (s *Server) EnableHTTPS(certFile, keyFile string, tlsConfig ...*tls.Config) keyFileRealPath = keyFile } if keyFileRealPath == "" { - s.Logger().Fatal(fmt.Sprintf(`[ghttp] EnableHTTPS failed: keyFile "%s" does not exist`, keyFile)) + s.Logger().Fatal(fmt.Sprintf(`EnableHTTPS failed: keyFile "%s" does not exist`, keyFile)) } s.config.HTTPSCertPath = certFileRealPath s.config.HTTPSKeyPath = keyFileRealPath diff --git a/net/ghttp/ghttp_server_config_static.go b/net/ghttp/ghttp_server_config_static.go index 8056acf3a..f4c5164e0 100644 --- a/net/ghttp/ghttp_server_config_static.go +++ b/net/ghttp/ghttp_server_config_static.go @@ -53,12 +53,12 @@ func (s *Server) SetServerRoot(root string) { realPath := root if !gres.Contains(realPath) { if p, err := gfile.Search(root); err != nil { - s.Logger().Fatal(fmt.Sprintf(`[ghttp] SetServerRoot failed: %v`, err)) + s.Logger().Fatal(fmt.Sprintf(`SetServerRoot failed: %v`, err)) } else { realPath = p } } - s.Logger().Debug("[ghttp] SetServerRoot path:", realPath) + s.Logger().Debug("SetServerRoot path:", realPath) s.config.SearchPaths = []string{strings.TrimRight(realPath, gfile.Separator)} s.config.FileServerEnabled = true } @@ -68,7 +68,7 @@ func (s *Server) AddSearchPath(path string) { realPath := path if !gres.Contains(realPath) { if p, err := gfile.Search(path); err != nil { - s.Logger().Fatal(fmt.Sprintf(`[ghttp] AddSearchPath failed: %v`, err)) + s.Logger().Fatal(fmt.Sprintf(`AddSearchPath failed: %v`, err)) } else { realPath = p } @@ -82,7 +82,7 @@ func (s *Server) AddStaticPath(prefix string, path string) { realPath := path if !gres.Contains(realPath) { if p, err := gfile.Search(path); err != nil { - s.Logger().Fatal(fmt.Sprintf(`[ghttp] AddStaticPath failed: %v`, err)) + s.Logger().Fatal(fmt.Sprintf(`AddStaticPath failed: %v`, err)) } else { realPath = p } From b12c909fd6c5cf52c230f151aa7357839019db32 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Wed, 6 Jan 2021 01:00:49 +0800 Subject: [PATCH 042/492] add logging level configuration for package ghttp --- net/ghttp/ghttp_server.go | 6 +-- net/ghttp/ghttp_server_config.go | 52 ++++++++++-------------- net/ghttp/ghttp_server_config_logging.go | 20 ++++++--- net/ghttp/ghttp_unit_config_test.go | 2 +- os/glog/glog_logger_config.go | 2 +- os/gsession/gsession_storage_file.go | 2 +- 6 files changed, 41 insertions(+), 43 deletions(-) diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 1b17e0c4d..770f07d19 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -128,9 +128,9 @@ func (s *Server) Start() error { } // Logging path setting check. - if s.config.LogPath != "" { + if s.config.LogPath != "" && s.config.LogPath != s.config.Logger.GetPath() { if err := s.config.Logger.SetPath(s.config.LogPath); err != nil { - return gerror.Wrapf(err, `set logging path "%s" failed`, s.config.LogPath) + return err } } // Default session storage. diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index edd63d4ea..9a8a7aa9b 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -171,40 +171,21 @@ type ServerConfig struct { // ================================== // Logging. // ================================== - - // Logger specifies the logger for server. - Logger *glog.Logger - - // LogPath specifies the directory for storing logging files. - LogPath string - - // LogStdout specifies whether printing logging content to stdout. - LogStdout bool - - // ErrorStack specifies whether logging stack information when error. - ErrorStack bool - - // ErrorLogEnabled enables error logging content to files. - ErrorLogEnabled bool - - // ErrorLogPattern specifies the error log file pattern like: error-{Ymd}.log - ErrorLogPattern string - - // AccessLogEnabled enables access logging content to files. - AccessLogEnabled bool - - // AccessLogPattern specifies the error log file pattern like: access-{Ymd}.log - AccessLogPattern string + Logger *glog.Logger // Logger specifies the logger for server. + LogPath string // LogPath specifies the directory for storing logging files. + LogLevel string // LogLevel specifies the logging level for logger. + LogStdout bool // LogStdout specifies whether printing logging content to stdout. + ErrorStack bool // ErrorStack specifies whether logging stack information when error. + ErrorLogEnabled bool // ErrorLogEnabled enables error logging content to files. + ErrorLogPattern string // ErrorLogPattern specifies the error log file pattern like: error-{Ymd}.log + AccessLogEnabled bool // AccessLogEnabled enables access logging content to files. + AccessLogPattern string // AccessLogPattern specifies the error log file pattern like: access-{Ymd}.log // ================================== // PProf. // ================================== - - // PProfEnabled enables PProf feature. - PProfEnabled bool - - // PProfPattern specifies the PProf service pattern for router. - PProfPattern string + PProfEnabled bool // PProfEnabled enables PProf feature. + PProfPattern string // PProfPattern specifies the PProf service pattern for router. // ================================== // Other. @@ -267,6 +248,7 @@ func NewConfig() ServerConfig { SessionPath: gsession.DefaultStorageFilePath, SessionCookieOutput: true, Logger: glog.New(), + LogLevel: "all", LogStdout: true, ErrorStack: true, ErrorLogEnabled: true, @@ -334,6 +316,14 @@ func (s *Server) SetConfig(c ServerConfig) error { if c.TLSConfig == nil && c.HTTPSCertPath != "" { s.EnableHTTPS(c.HTTPSCertPath, c.HTTPSKeyPath) } + // Logging. + if s.config.LogPath != "" && s.config.LogPath != s.config.Logger.GetPath() { + if err := s.config.Logger.SetPath(s.config.LogPath); err != nil { + return err + } + } + s.config.Logger.SetLevelStr(s.config.LogLevel) + SetGraceful(c.Graceful) intlog.Printf("SetConfig: %+v", s.config) return nil diff --git a/net/ghttp/ghttp_server_config_logging.go b/net/ghttp/ghttp_server_config_logging.go index 8643b56d4..3ea28f0de 100644 --- a/net/ghttp/ghttp_server_config_logging.go +++ b/net/ghttp/ghttp_server_config_logging.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -6,18 +6,26 @@ package ghttp -import "github.com/gogf/gf/internal/intlog" - // SetLogPath sets the log path for server. // It logs content to file only if the log path is set. -func (s *Server) SetLogPath(path string) { +func (s *Server) SetLogPath(path string) error { if len(path) == 0 { - return + return nil } - intlog.Print("SetLogPath:", path) s.config.LogPath = path s.config.ErrorLogEnabled = true s.config.AccessLogEnabled = true + if s.config.LogPath != "" && s.config.LogPath != s.config.Logger.GetPath() { + if err := s.config.Logger.SetPath(s.config.LogPath); err != nil { + return err + } + } + return nil +} + +// SetLogLevel sets logging level by level string. +func (s *Server) SetLogLevel(level string) { + s.config.LogLevel = level } // SetLogStdout sets whether output the logging content to stdout. diff --git a/net/ghttp/ghttp_unit_config_test.go b/net/ghttp/ghttp_unit_config_test.go index 45a049cfd..29cdc4f00 100644 --- a/net/ghttp/ghttp_unit_config_test.go +++ b/net/ghttp/ghttp_unit_config_test.go @@ -52,7 +52,7 @@ func Test_SetConfigWithMap(t *testing.T) { "AccessLogEnabled": true, "ErrorLogEnabled": true, "PProfEnabled": true, - "LogPath": "/var/log/MyServerLog", + "LogPath": "/tmp/log/MyServerLog", "SessionIdName": "MySessionId", "SessionPath": "/tmp/MySessionStoragePath", "SessionMaxAge": 24 * time.Hour, diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 924a9e2e4..b51b75986 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -192,7 +192,7 @@ func (l *Logger) SetPath(path string) error { if !gfile.Exists(path) { if err := gfile.Mkdir(path); err != nil { //fmt.Fprintln(os.Stderr, fmt.Sprintf(`[glog] mkdir "%s" failed: %s`, path, err.Error())) - return gerror.Wrapf(err, `Mkdir "%s" failed in Pwd "%s"`, path, gfile.Pwd()) + return gerror.Wrapf(err, `Mkdir "%s" failed in PWD "%s"`, path, gfile.Pwd()) } } l.config.Path = strings.TrimRight(path, gfile.Separator) diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index a256b0eec..b97dd820a 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -56,7 +56,7 @@ func NewStorageFile(path ...string) *StorageFile { } if storagePath != "" { if err := gfile.Mkdir(storagePath); err != nil { - panic(gerror.Wrapf(err, `Mkdir "%s" failed in Pwd "%s"`, path, gfile.Pwd())) + panic(gerror.Wrapf(err, `Mkdir "%s" failed in PWD "%s"`, path, gfile.Pwd())) } } s := &StorageFile{ From c6f1ae9426fe909a3547e58493eab73b6a3e468e Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 7 Jan 2021 01:17:03 +0800 Subject: [PATCH 043/492] add file name configuration from command line or environment for package gcfg --- os/gcfg/gcfg.go | 15 ++- os/gcfg/gcfg_instance.go | 4 +- ...unit_test.go => gcfg_z_unit_basic_test.go} | 86 ------------ os/gcfg/gcfg_z_unit_instance_test.go | 127 ++++++++++++++++++ os/gcfg/testdata/envfile/c6.json | 2 + os/gcfg/testdata/envpath/c3.toml | 2 + os/gcfg/testdata/envpath/c4.json | 2 + 7 files changed, 145 insertions(+), 93 deletions(-) rename os/gcfg/{gcfg_z_unit_test.go => gcfg_z_unit_basic_test.go} (81%) create mode 100644 os/gcfg/gcfg_z_unit_instance_test.go create mode 100644 os/gcfg/testdata/envfile/c6.json create mode 100644 os/gcfg/testdata/envpath/c3.toml create mode 100644 os/gcfg/testdata/envpath/c4.json diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index f0c5e0249..fd89a38cf 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -49,6 +49,11 @@ func New(file ...string) *Config { name := DefaultConfigFile if len(file) > 0 { name = file[0] + } else { + // Custom default configuration file name from command line or environment. + if customFile := gcmd.GetWithEnv(fmt.Sprintf("%s.file", cmdEnvKey)).String(); customFile != "" { + name = customFile + } } c := &Config{ name: name, @@ -56,12 +61,12 @@ func New(file ...string) *Config { jsons: gmap.NewStrAnyMap(true), } // Customized dir path from env/cmd. - if envPath := gcmd.GetWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).String(); envPath != "" { - if gfile.Exists(envPath) { - _ = c.SetPath(envPath) + if customPath := gcmd.GetWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).String(); customPath != "" { + if gfile.Exists(customPath) { + _ = c.SetPath(customPath) } else { if errorPrint() { - glog.Errorf("Configuration directory path does not exist: %s", envPath) + glog.Errorf("Configuration directory path does not exist: %s", customPath) } } } else { diff --git a/os/gcfg/gcfg_instance.go b/os/gcfg/gcfg_instance.go index 4f3669374..84999034f 100644 --- a/os/gcfg/gcfg_instance.go +++ b/os/gcfg/gcfg_instance.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -13,7 +13,7 @@ import ( const ( // Default group name for instance usage. - DefaultName = "default" + DefaultName = "config" ) var ( diff --git a/os/gcfg/gcfg_z_unit_test.go b/os/gcfg/gcfg_z_unit_basic_test.go similarity index 81% rename from os/gcfg/gcfg_z_unit_test.go rename to os/gcfg/gcfg_z_unit_basic_test.go index 3fd04683a..7677f4b47 100644 --- a/os/gcfg/gcfg_z_unit_test.go +++ b/os/gcfg/gcfg_z_unit_basic_test.go @@ -12,7 +12,6 @@ import ( "os" "testing" - "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/encoding/gjson" @@ -229,71 +228,6 @@ func Test_SetFileName(t *testing.T) { }) } -func Test_Instance(t *testing.T) { - config := ` -array = [1.0, 2.0, 3.0] -v1 = 1.0 -v2 = "true" -v3 = "off" -v4 = "1.234" - -[redis] - cache = "127.0.0.1:6379,1" - disk = "127.0.0.1:6379,0" - -` - gtest.C(t, func(t *gtest.T) { - path := gcfg.DefaultConfigFile - err := gfile.PutContents(path, config) - t.Assert(err, nil) - defer func() { - t.Assert(gfile.Remove(path), nil) - }() - - c := gcfg.Instance() - t.Assert(c.Get("v1"), 1) - t.AssertEQ(c.GetInt("v1"), 1) - t.AssertEQ(c.GetInt8("v1"), int8(1)) - t.AssertEQ(c.GetInt16("v1"), int16(1)) - t.AssertEQ(c.GetInt32("v1"), int32(1)) - t.AssertEQ(c.GetInt64("v1"), int64(1)) - t.AssertEQ(c.GetUint("v1"), uint(1)) - t.AssertEQ(c.GetUint8("v1"), uint8(1)) - t.AssertEQ(c.GetUint16("v1"), uint16(1)) - t.AssertEQ(c.GetUint32("v1"), uint32(1)) - t.AssertEQ(c.GetUint64("v1"), uint64(1)) - - t.AssertEQ(c.GetVar("v1").String(), "1") - t.AssertEQ(c.GetVar("v1").Bool(), true) - t.AssertEQ(c.GetVar("v2").String(), "true") - t.AssertEQ(c.GetVar("v2").Bool(), true) - - t.AssertEQ(c.GetString("v1"), "1") - t.AssertEQ(c.GetFloat32("v4"), float32(1.234)) - t.AssertEQ(c.GetFloat64("v4"), float64(1.234)) - t.AssertEQ(c.GetString("v2"), "true") - t.AssertEQ(c.GetBool("v2"), true) - t.AssertEQ(c.GetBool("v3"), false) - - t.AssertEQ(c.Contains("v1"), true) - t.AssertEQ(c.Contains("v2"), true) - t.AssertEQ(c.Contains("v3"), true) - t.AssertEQ(c.Contains("v4"), true) - t.AssertEQ(c.Contains("v5"), false) - - t.AssertEQ(c.GetInts("array"), []int{1, 2, 3}) - t.AssertEQ(c.GetStrings("array"), []string{"1", "2", "3"}) - t.AssertEQ(c.GetArray("array"), []interface{}{1, 2, 3}) - t.AssertEQ(c.GetInterfaces("array"), []interface{}{1, 2, 3}) - t.AssertEQ(c.GetMap("redis"), map[string]interface{}{ - "disk": "127.0.0.1:6379,0", - "cache": "127.0.0.1:6379,1", - }) - t.AssertEQ(c.FilePath(), gfile.Pwd()+gfile.Separator+path) - - }) -} - func TestCfg_New(t *testing.T) { gtest.C(t, func(t *gtest.T) { os.Setenv("GF_GCFG_PATH", "config") @@ -446,26 +380,6 @@ func TestCfg_Get(t *testing.T) { }) } -func TestCfg_Instance(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - t.Assert(gcfg.Instance("gf") != nil, true) - }) - gtest.C(t, func(t *gtest.T) { - pwd := gfile.Pwd() - gfile.Chdir(gdebug.TestDataPath()) - defer gfile.Chdir(pwd) - t.Assert(gcfg.Instance("c1") != nil, true) - t.Assert(gcfg.Instance("c1").Get("my-config"), "1") - t.Assert(gcfg.Instance("folder1/c1").Get("my-config"), "2") - }) - gtest.C(t, func(t *gtest.T) { - pwd := gfile.Pwd() - gfile.Chdir(gdebug.TestDataPath("folder1")) - defer gfile.Chdir(pwd) - t.Assert(gcfg.Instance("c2").Get("my-config"), 2) - }) -} - func TestCfg_Config(t *testing.T) { gtest.C(t, func(t *gtest.T) { gcfg.SetContent("gf", "config.yml") diff --git a/os/gcfg/gcfg_z_unit_instance_test.go b/os/gcfg/gcfg_z_unit_instance_test.go new file mode 100644 index 000000000..9043bf434 --- /dev/null +++ b/os/gcfg/gcfg_z_unit_instance_test.go @@ -0,0 +1,127 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".*" -benchmem + +package gcfg + +import ( + "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/debug/gdebug" + "github.com/gogf/gf/os/genv" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/test/gtest" + "testing" +) + +func Test_Instance_Basic(t *testing.T) { + config := ` +array = [1.0, 2.0, 3.0] +v1 = 1.0 +v2 = "true" +v3 = "off" +v4 = "1.234" + +[redis] + cache = "127.0.0.1:6379,1" + disk = "127.0.0.1:6379,0" + +` + gtest.C(t, func(t *gtest.T) { + path := DefaultConfigFile + err := gfile.PutContents(path, config) + t.Assert(err, nil) + defer func() { + t.Assert(gfile.Remove(path), nil) + }() + + c := Instance() + t.Assert(c.Get("v1"), 1) + t.AssertEQ(c.GetInt("v1"), 1) + t.AssertEQ(c.GetInt8("v1"), int8(1)) + t.AssertEQ(c.GetInt16("v1"), int16(1)) + t.AssertEQ(c.GetInt32("v1"), int32(1)) + t.AssertEQ(c.GetInt64("v1"), int64(1)) + t.AssertEQ(c.GetUint("v1"), uint(1)) + t.AssertEQ(c.GetUint8("v1"), uint8(1)) + t.AssertEQ(c.GetUint16("v1"), uint16(1)) + t.AssertEQ(c.GetUint32("v1"), uint32(1)) + t.AssertEQ(c.GetUint64("v1"), uint64(1)) + + t.AssertEQ(c.GetVar("v1").String(), "1") + t.AssertEQ(c.GetVar("v1").Bool(), true) + t.AssertEQ(c.GetVar("v2").String(), "true") + t.AssertEQ(c.GetVar("v2").Bool(), true) + + t.AssertEQ(c.GetString("v1"), "1") + t.AssertEQ(c.GetFloat32("v4"), float32(1.234)) + t.AssertEQ(c.GetFloat64("v4"), float64(1.234)) + t.AssertEQ(c.GetString("v2"), "true") + t.AssertEQ(c.GetBool("v2"), true) + t.AssertEQ(c.GetBool("v3"), false) + + t.AssertEQ(c.Contains("v1"), true) + t.AssertEQ(c.Contains("v2"), true) + t.AssertEQ(c.Contains("v3"), true) + t.AssertEQ(c.Contains("v4"), true) + t.AssertEQ(c.Contains("v5"), false) + + t.AssertEQ(c.GetInts("array"), []int{1, 2, 3}) + t.AssertEQ(c.GetStrings("array"), []string{"1", "2", "3"}) + t.AssertEQ(c.GetArray("array"), []interface{}{1, 2, 3}) + t.AssertEQ(c.GetInterfaces("array"), []interface{}{1, 2, 3}) + t.AssertEQ(c.GetMap("redis"), map[string]interface{}{ + "disk": "127.0.0.1:6379,0", + "cache": "127.0.0.1:6379,1", + }) + t.AssertEQ(c.FilePath(), gfile.Pwd()+gfile.Separator+path) + + }) +} + +func Test_Instance_AutoLocateConfigFile(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(Instance("gf") != nil, true) + }) + // Automatically locate the configuration file with supported file extensions. + gtest.C(t, func(t *gtest.T) { + pwd := gfile.Pwd() + gfile.Chdir(gdebug.TestDataPath()) + defer gfile.Chdir(pwd) + t.Assert(Instance("c1") != nil, true) + t.Assert(Instance("c1").Get("my-config"), "1") + t.Assert(Instance("folder1/c1").Get("my-config"), "2") + }) + // Automatically locate the configuration file with supported file extensions. + gtest.C(t, func(t *gtest.T) { + pwd := gfile.Pwd() + gfile.Chdir(gdebug.TestDataPath("folder1")) + defer gfile.Chdir(pwd) + t.Assert(Instance("c2").Get("my-config"), 2) + }) +} + +func Test_Instance_EnvPath(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + genv.Set("GF_GCFG_PATH", gdebug.TestDataPath("envpath")) + defer genv.Set("GF_GCFG_PATH", "") + t.Assert(Instance("c3") != nil, true) + t.Assert(Instance("c3").Get("my-config"), "3") + t.Assert(Instance("c4").Get("my-config"), "4") + instances = gmap.NewStrAnyMap(true) + }) +} + +func Test_Instance_EnvFile(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + genv.Set("GF_GCFG_PATH", gdebug.TestDataPath("envfile")) + defer genv.Set("GF_GCFG_PATH", "") + genv.Set("GF_GCFG_FILE", "c6.json") + defer genv.Set("GF_GCFG_FILE", "") + t.Assert(Instance().Get("my-config"), "6") + instances = gmap.NewStrAnyMap(true) + }) +} diff --git a/os/gcfg/testdata/envfile/c6.json b/os/gcfg/testdata/envfile/c6.json new file mode 100644 index 000000000..ac49cc62e --- /dev/null +++ b/os/gcfg/testdata/envfile/c6.json @@ -0,0 +1,2 @@ + +{"my-config": 6} \ No newline at end of file diff --git a/os/gcfg/testdata/envpath/c3.toml b/os/gcfg/testdata/envpath/c3.toml new file mode 100644 index 000000000..13fcebc0f --- /dev/null +++ b/os/gcfg/testdata/envpath/c3.toml @@ -0,0 +1,2 @@ + +my-config = "3" \ No newline at end of file diff --git a/os/gcfg/testdata/envpath/c4.json b/os/gcfg/testdata/envpath/c4.json new file mode 100644 index 000000000..9f8a930de --- /dev/null +++ b/os/gcfg/testdata/envpath/c4.json @@ -0,0 +1,2 @@ + +{"my-config": 4} \ No newline at end of file From a9f332fdd6e03c9fbcab042d95abb12cb63b1a23 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 7 Jan 2021 13:00:53 +0800 Subject: [PATCH 044/492] version updates --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 3bf3e1b03..82df3b549 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.15.0" +const VERSION = "v1.15.1" const AUTHORS = "john" From 788ade2db012b64c5662ed3bf81df8887e2cb1cb Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 7 Jan 2021 19:34:46 +0800 Subject: [PATCH 045/492] improve package gres --- os/gres/gres_resource.go | 6 ++++-- os/gres/gres_z_unit_2_test.go | 2 +- os/gres/testdata/data/data.go | 2 +- os/gres/testdata/testdata.go | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/os/gres/gres_resource.go b/os/gres/gres_resource.go index ce410108f..2ed7b2e46 100644 --- a/os/gres/gres_resource.go +++ b/os/gres/gres_resource.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -74,6 +74,7 @@ func (r *Resource) Get(path string) *File { return nil } path = strings.Replace(path, "\\", "/", -1) + path = strings.Replace(path, "//", "/", -1) if path != "/" { for path[len(path)-1] == '/' { path = path[:len(path)-1] @@ -93,6 +94,7 @@ func (r *Resource) Get(path string) *File { func (r *Resource) GetWithIndex(path string, indexFiles []string) *File { // Necessary for double char '/' replacement in prefix. path = strings.Replace(path, "\\", "/", -1) + path = strings.Replace(path, "//", "/", -1) if path != "/" { for path[len(path)-1] == '/' { path = path[:len(path)-1] @@ -114,7 +116,6 @@ func (r *Resource) GetWithIndex(path string, indexFiles []string) *File { // GetContent directly returns the content of . func (r *Resource) GetContent(path string) []byte { - path = strings.Replace(path, "\\", "/", -1) file := r.Get(path) if file != nil { return file.Content() @@ -169,6 +170,7 @@ func (r *Resource) ScanDirFile(path string, pattern string, recursive ...bool) [ // It scans directory recursively if given parameter is true. func (r *Resource) doScanDir(path string, pattern string, recursive bool, onlyFile bool) []*File { path = strings.Replace(path, "\\", "/", -1) + path = strings.Replace(path, "//", "/", -1) if path != "/" { for path[len(path)-1] == '/' { path = path[:len(path)-1] diff --git a/os/gres/gres_z_unit_2_test.go b/os/gres/gres_z_unit_2_test.go index f051f952b..8e7bc3496 100644 --- a/os/gres/gres_z_unit_2_test.go +++ b/os/gres/gres_z_unit_2_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gres/testdata/data/data.go b/os/gres/testdata/data/data.go index 4c5967568..f666a4b8f 100644 --- a/os/gres/testdata/data/data.go +++ b/os/gres/testdata/data/data.go @@ -3,7 +3,7 @@ package data import "github.com/gogf/gf/os/gres" func init() { - if err := gres.Add("H4sIAAAAAAAC/7RaeThU7f9+sq8hSyohYpAxtkiyFLKMfYtSGgwRI4y9BaUsSSlU2l4U6bVNRJJS2bdsSYuiENlHROh3RXLOGEve37frin88931/7vNs53NuQzQlFQegA3RgEOtoCCD/1gB6YOuKs3d0QM38ksC7ujibmVKDVR3b7Q+IVegiUVriZYYZZoZ1NaU1eo0oFKoJpV2FLNUW10aat4VJ6OiViZ9rNzY0NNQqE682B3dlZaWbZF7KvJTJkpaRy2mQMqRAIxDCDok2F+RMKagA+PnTEE1Ld+3ZeTtzAIA9AGBhecxz8lx8JRxxjv+vyoznlJnPKfseqKZIqgwAP2s7I6gymj/KpiU9jI23/jUY+icLl7Xuz2CkracH3tXlf2a+0VyJZnMlmqrLvV3afI55Kv8Hz8BkTuCeOYGStO2nln4GzKQCl3wUAFxyuwErkg7QAztHdymUh6fN9PDmdykHlv8kuSDDf/1H4rEeeCkJvA9+ziUJvTqkNlJCylSrumaLdtWWVX/KLLh/cftaAADbohwMsxzT2HO45DFDKFWm2KfNWcw6qt+YK3dM+r85Jg1zTJq8Y8bzqvOP5KVctmPS045Jwx2bj6mnQihZtmPSK1juTIAeOEptw6GwOPgKbyvXqior25NhZjgEpC7llhPD3Y1VJ46wqtGzzoqz9Uaq8gEA+JfH4IRZiMHv3bYxt/rPJRH3+OQljr7LPRB6b8cTEQT1ZqJdSVanif8lsGqW08iB30sKALBleZzungtx5kn+tHkevHGt3P7XfpnavwnF8zufnQtn1gkAf2rclncYCQAQWZSPZZbP7xBSTR9OOVclwbuw/rZ/9IyXTykU7yjdfJNARTdL1dVSHiEEANi0bCrTPf+PVOSn1S+qFSxExt9DkXaO7igsboVrEYKAOoR1dnZdbIrOzRH3r062HAAA1r8i8HZ1d7YjIRCrQZVrG2WYGTLQQgmus3kiSAmW8MAJ8189cML89sAXM8+DHOP5a2hunebbi1nyTx/tf0M24weMbNqOTGNDP4LtG+JdJcgyubHT2plrenYu2xF3zxU4wglH+O0I9OBfYIHPueF8dJPIxul6/4Zoxg3YDWN2buRlkG4a2K+9j9YuOT9WQymmN44VGLJuHshvT5w8XHF/tKZ0/LrmGM54QzCe2x+cVv2Z1AWbGPOWniVk+GasIcc3YxFhbsJYZc+ZxNCLniLlW9ok0z0rMIl7Hshvk3wgk5uQmV5aZSSCRopVV9bcN5NukDE01qpCVxuUVaJzzMSQ2vXZhoScT4Qy2azS+59+1ZdRNr32/GfMdBnetENiw7EMBtU/JbL6cUUaAAAM/lbdjKX/UZ3o9Gr1n7FebpyVNzZC2YKaf1bcuLButD6JuPlnAB1E3AquF2yzw+0dnbH/uzsGnMYJs9w9cubYL9LZ2VV2zrEmaURkFcUsMUdxhcWvi4boosSsMGJ3z+VsRQveNZ4ZoybFAQCIJV995ihnLhzzF9+8pY5XDWof01AQPVi4sf6GQa9RWwr1n1umsiHqgQQAQHxRYnZSYtM9/+8LKETX97iRftj7i9MiM2vPDOQ6Dxf6ze1SZarl9RgAgOOis5YeKnWF7xHurq54lK2Hxwr2mzWQ4SgPvK8zVmIW6JdRdcbJ0/ujVkXNllpk9b862caScmLJGTSMMYnml74Id9jd6bQU3ZzUEZ3IZenRSPFnToq12HRvBQBILyqeYZbd0QXjgF2BfHYYAMrZ1cFV4gjO4Y9+51jfbQ2HN5QkSUeHCmyPeXlZ3dK+8hS3GkZyxKXUWNa+rFTcXti/ucTdkL35PkdZ1NZ9pgKnFDgIQj+ibMu9om5fG3XX7/et8WwhWjURS/qnjqmMjhak/5jqDM18IMtFpR0EQMAtehoXRgBYH41+PA4Av4dyHgUAPZg3679S6jy6/evsVtn46QUVWB3a06IUMKzrBg66iq77WuqeJanZ5sghck7VA7FrUgT/tG/n2KZJETzDhWDKH0b5ybqJN1d/ZqGOKlfvO41hHeBb82rrxpCE4K2vA4r5gxr/cTI0C5GgY6JJ4KZaU8flMkzpwKq1nZWFK/zcqcKjx479U+Dwj2yxGbcVGs14JyxgE6sYE4V2cJcBtnXCNLny4gnhMVde2QuhDue7xKUjGFomix/Wp93H85/nu6pSE7lddPjJ2eRKvv2tjhyX+c6tPqlmhvlJwzZwQmr4K1O6io7gR4w6SiBYCyuFOSIyYa3AdUV93DvnvcqJN11TkWpjRl+0KGnYSgTi3cTjJXZxpt2OT0mLTwkMFjzvwKaVmyfDMMJS+6RVliZmN7Gu8W025YYL69St139az+58MGlEfuLMF7586ghV4bGfNx9vN95P6WtOPDP5Qf3Ec3qfzaHFtz5a5Nkx9Imx7U46oFE4tXYizj3HP5HzpG4i4cgjtfrghIM/1/7oD8D8vDxw8yvlj0yVPH6OnvT9DdTIR8P9lEC1UPiQ0r+0AxcTGJkVRdzGxL6JZd85ppJjZrXz6N4vIoYfzhlI9YwI+j86IGjiH1Iwue7HFMJcXrkt3FMjM1jT+QHL2R3yd16c3ikQGYRQ6q+oEKb7bnH55L2vYwOBB5Tuv8ui7nRJizkbkC/U0lPpNLRdvvd0vU8vcdR2D7raISxW94vfZ9yZU50x1pctr30FLcYjgnaisYwK6Wynj8danCgI2tRQGttkqXLPUtKMc8M7f5uBUU+b8y/kL7+7RKeU94W4ToyW8VK27Hqcdl7WDT4eNcXaAJdgXh+62gEvV2EsY9rXFwdxcWUUTc14/nZk/J7X3cbBTYnPr4QwVZwUuJLTIyb4Kn+jeBpbkdvJC2HnpHUQzD5JnA14Pc1wZJEG3bVQU/PHr/149vbJ9ga1sXopa77frSKum2o6VVV7Llcdxx5ocBYjmNJeW89jf+9Qpa1tgLmvsVCtaA61cdsTdeWYD5rSzyftg0eq33CzieKth1kOGbbRPa6ofJTw+cJP9Hod98ATBmHGQ3douPdvxXNJeN3r7owM5tETrP8cjSNqD4hm/fhW+8oj6taDmMl1Glrfzoaw7Lrf5aZSMz7AyW20mZb1YTkC6Vij9vwxtUP3kSRdn3PaNM4KL3MMvAXqxowfpfd1mW7nsfhWth8zYOXa6WSVen/k+bBI4ZrwPGbaR8+FV6n1qiicspp6+jz2CgJl20sYWbv/tSn74Ko4nvJwi6IrrdiPpuLD+Na7+eiXg4HdUlKHPqcHq212QIS/9l6PsHqJOJQug+PfhSuKq3YaUC2kPa0jyNw4vvEdy5jAYfvdiOBWWwv5rOH3Zvwq5p1JN7EK3EeuKqjfGYltkNu56ZstY+ID1d7TjYdL8xQ35CoMb6QIfJZ5AJ3SXKGWsEbSHcNfGHSmibn/vNuG2+Zavtm2io1i6cFuKo2WZeZnNsbafDC6sX1z/kRHxMupScPmHt4JVJeyQco9az+HA6E1oT2HWyy6Ca1aV/Gnw7tlR9TqDY9pfStL5nqHn7hwwx/Jzn6w7P4acT4+rYoof+rdSYPvJ53k/D9ZBVs+MZs6EuZd/BZR4ecdf93SAXVc3JddkCkssSb/Ab2Nl8JAccrqj2dar1yd0nxbMpEQ9aNa925JyCX5KI3T9xjOIu/nfQgZuM9XQnB033a/9aBo1fk0jtcnCQRBpSf+1x49zH7zbTD4wz58cgGxrT0gjCr1tIWXn3kDb6vM7VU18ZEYVd7vWUdy5fydK7+91T2uY1UVPrhWszpyawh1HjU93zYBopi7bmyvbHfIpPN1B60WjsicUE0G3s6h7BytY1WHXjxu4q7HDuix+BOHFOS9t0fLeenEuq/1DVTLziLu3r171+DuQaLBv6oZE1QGipWKjXYZGgUmsXQsUa41IuKDLW06PAV6PrmdTvKHh952vNld93Tkx2HsQ2bm/ocTyGJKs1WRhVz7r8o3MXGY5AnzNfEW6w1faXRMOV2a63BiPLv541U5pWiUJP8tqRBb686YvL2anKktL9FPv//EXWWvCy8tPmSXnB5WeO3ZSO16Nfa3Jcx85ng7OU8GjwJThFlx3vUUy/ih8JZI5oh4XbodrnKcLC1d7vu60p5WSClW7sSJ2Zs//c7Itp4aFW+AotZzNfVbTTP8ul7f+5DgBSvNz0qCn1RdvAOPBglkVjUXFbUd0byflGqvWNpXa5X8QvdSu7N23OSn+KuJShYfD/bHOGwyJ7xuVuizYh2YDKFk1XROycmPFLgoLccS38eHpMvh9RnKJQiwb/2WZffudkf5x+YboaFMb473UmLTkJY2yt8dHxx8Oxn3IgezLrV9r+cHLlvBsKuftqUXxf7L+DWZv2lT0fWe3ljdJ6icoLgIAyq0nrj+q4LXpnybmtolT0kdVQ80YY4w6WR6iaHadqL/jMznolDlsQQd7yqh3dbiY6uVHh8vld4XO3R1qGB3RidRK/FqtazSgb5vMoZeTfZj6At1btkUX/M6WsZzx3LxOQxnCHG7enMS95W8D7r95MTPvtbBcR3/Z0n/hG1g1kcM3hG90ZWQW78t6Tpx8Gz6Ue/JS71x7SXlFm2ZNXVxEXbtFXvM85Noz/dkyan14BE7nt84Ss3249arClzkuLHAk+Sea+mvd+xJOvS4a6O+9lgD47O76lMBSacaeQ8UnE2VsnWm5fSzSj0nVXf8VKC+Zb0p3UD/RQHfHWEDurosF9TiON50ZcVUK/Ide7MRMdHrr9GtESfAb8GBV0U37Pfhl5ds3fvC9u2aN4ncyYMdvXnttg2l/NsszPdeLPV700OQaDCO5pHKYZH25u+o1bL1L35+V54BSUBUjVZHpY1+sd/K3U38YOcQWoMYY9mjX0GfuXZS9bRPxBpdqzgB5c2jX9GRlaaBwfGx5dJurCFr2QP4S7ZcK1C/Vfr5uuKpH3fwVo+ZGvbto+wRaVQ0cT9avZ5X6v2z/q1vGVyx6yniCoS5rX3aV+GLhZua1Q0GJN9tdyn7jlX+Mqy1mZgQM1p48WRe62glX2XHgyfNR9uJOrpFGXbCCNai917v/I9m3sJnD+9StqYRKxms5h/BO7blcK7tvyH8SCKNGGEkOKzMOtHiPiI1yUydn1OIbU3LqixPihrYhm1Na3lVffnfyyeNdaw84wnD9yUPbM5yu1eB/Ib2/Ejsl4mJqDDD5eZbbug59t3VvGyY9q4eoqwGlerTcVvE14OqQSVZ+taL9mZrg3Er+2rDtpx0pcwJPLdAc3pNpsKd97qIi7kvHeNdLCedQrZcd2nj3Z/+CrNfO21zgybizHiBBvOLPV2VlM/TUeUZ+0YsIxoUBzSESvN3uOszj7YGjrwtRD5c82/IUaeDj9QdrnrTTsb27jPBEfLG17RqHzE55R1wgecf/536uEyOVOFwbl+8TO1mEeUSj/63wWuKgvyMJviftG5oKOZ9cQvb59L0stnmc7mrR9DVBGv3M0knfRXfK09mCMkGUm8TyBup7d3SXXdN40IqDbK/2eT70fMvLJCItcEH35XWH2crWL/twwe+6/hBGzdPvLPUnZej8o5ZigaJuKjID4Xj5UENJj/VBEKn0o7R8F424fTllH6W9nxUtaivpY2mqDmVPbp4l9hUGjMzhTkLJ67ym9aX13LcXlyuJsf1Ht30ik32YUv4Nyd7pLF4qNEedZWGL9qNp1XGQwyXR3ftuOQrrKTUDqUmi/TvWtkB+owHbm7cx4RXPUHT0cC47mNX6u4vulP5yaf1ojKjMTZjHCyd40PuMQceE2zuhe7vp80zDTf2ehtr3nWysu6zcOY5/JB+phYuW4cQHqSo3Hn6WUPNye8Oo3vDO9TRvJ580fQ1p66bHeyXjMTGEsW1q3nCAzJ1WAq6wmSzK5sjwhlYlRTYimgaNNrRWOLNhqTCa33veIQGDepMLiZms+JCTa4dkN5SJxTZmTN0a4eg1See7ag3wnnHhxD6sSOFnPcyNB1orAIpjt0z1T9CLM8NlJ18/ann05m3VJeL2YYF9mDOBNugCziodzD0yPYNS0eu88gRYW18MOL0KJ3+4nt3NPp4942RmMZi6WMNtdk9IN6tR12FsXyrHZXfa6yMnrXGYxm0N7J8i8rZl80hlxkjVT1Y3MKUXVNHDgS0SR/UjGtSQdM6X0/auEslibcga2J7pU+abf8exsh+mzgEuubM7oGcfuVoXSZk7e3xPc+m2P90/fpvBWQwAXCJeanm+sybD84O6yNxCA95vRWvrnUp5mei+uggHVwEBilOi+aprufJYNj0zDza2frClEFMf6KBZtHeiPCEZ/vCwj1M2ycCC7f6ZCcRuOyJhjR8hf0p3+98TesuvBF+Ur6A+QMf4hRfaIG/X4fe0AbtvTQb/QfqHlRa+Pd99bTno3d4kpsuwBB/MHhn9Qt5ngT50pOe17uf7P7eN8k4WxGm97vpWQDAgyV7+L8qWkHv5tdrIB7rcsQZg8eSM2RI3AhlLlKmZVZaidZONRT5/W3VTKuiUruR8k9z4XGMCBAEAGxc9KWVFUrmjPF19fz9OfHvXl35yMCgbF1xeIwjDusO15+Spq+tV1GJNjPUqa7Zol1ZiUYap6S2E8okszxGRhndhoc9mAm1NZJZn1LS9KvQd1PnGiZCtG/75QEAUouK4SEnxt7VFb+IkooalPacDOIoi9tCGkyLFbWW/hJGVsMhLMZuYQ3lWnq6MxpmejSSWcQjnvgFdYz7n6/fCgBA/b2Omd9wHQQj/KD36lCmI2alImVVmZ8IgUZ3Q0W9rBgYGBjkhK4IKdz16hvDaYQ+vMugHBo9dltIbpzVKCOsuzuq4V3QtRdRptRC5wsaBG6/2Sux4VjIOjntMdFowwRj6QqOKI814eEu0VFEos4aGaJx+CVJ5p2lja92yXYn2cVgrl6IuWkd1AiSqVJwG46FoJ2HC49T/SmyvPrCpigAwPjfTmLpFUxiMl5Jk503JHNkbs3tdR6u4iT5HLNMHnJzgwCfA3M84krNO1bIQ+7Zp6RVV6KrRfW0ZydhXYVIWZWR8ct7qe27Srvahe07gjk+36Lfcru9M5b/8NxncqqxTtpfc1B/0cfDSU6HC8ZxJZ8n+RfCmv4hRcY/z+FhmSy4fZV6vdMfKlf/ByrpBahInpSNim87KRX5bv8s1SInxioKDso5rdC41BpA/+fvEgN//VwkwUUKBE03McOAbs4BkeR85jDI56BmF+bPnQqrwPxUFFwANHu0DibA68/gxVJRpHjQqBAHDI+HAiyeX1qsLmZYXY9JociUR77DPYvRu3MVJSCXNIJXA00EccGqUYUMXyBpRAoGjQIxwMASZ8HgkaLFDKGCGfL9N8AKfJChAuTyQ3Dp0JwP3Ad3yPAF8kOkYNCAD9yHV7Ng8KDQ8n0QowakqSA4N/TbGhOMG0sNyKeCSCGgcRw4RMksBGnsZ54KSMIGDqFIA8ineEghoHEWFhjEvVkIMsGc5aNw0YIFMzfLfx6Bv1GWNS8ZYfMy//fQ+XEaeAnQ5At8ak7BEcjFaUjBoCkXOJg1HVgqOrP82p5CwWAxGbgcaIoFLoeCHiwVkyEFg+ZV4GCuJGBkYjDLr60OCgYLvMDlQDMpnDA5HAxgicALKRY0fALHOkaCNT/Tslhlq2GVfYRikYZXSM4NSLAEfqoKMYJlhFdI8aDBEThe9Hw8MuGU5RdJzQQWDp/ARUGjHtwwUTrzQMiET0jhoOEMONyX+XDz0yKLbUp0sE0pmBmQy3csfFCwwdS8mB1ONt9BigPNVMBxtqwGiwQ4SHGgQQlW+OUBhkOSxyCFgcYe4NeztSxg8YwFKRQ0lsAOgzpPCkWSmljsUdHDHpUiKyAbalju5cafFZALNcALgUYM4Jf5PMhwMqGGxXQwwHRsYwPk8wkkeyKkYwe31BcGMD+fQIoE7ZTBD3YPXrBgv2/5B7sGHyDtscEFQFtg8FIC+MCiPbbFXGWFudoLRZrfQIMLgvav+GCCZPnBshto816fIB0pHhhqITlUch2NefsgpLkEh9TYBJbX2CKFhLZy4JBV5CDJ9SmW/1QIAmCxjhBcGbRrA1c2RgZmWf5BGzRwSIwgWF7zhxQS2muBQ5aRg/xb/zhh/hltBku2bODyoD0Vfpi86wthkWvZkMJC+ydwWDohsPz2zPIPZg8ILGRjoab59QfKQBn4swFA+PW+Av4vAAD//6Jqwe1DNwAA"); err != nil { + if err := gres.Add("H4sIAAAAAAAC/7RaeTiU6/9+sq8hSyohYpAxtkiyFLKMfYtSGgxxGGHsLShlSUqh0nZQpGObiCMplX3LlrQoCpF9RIR+V4T3HWPJ+X27rvjHc9/3536f7f28tyGakooD0AE6MIh1NASQf+sAPbB1xdk7OqBmfkngXV2czUypwZqOnfaHxCp0kSgt8TLDDDPDuprSGr1GFArVhNKuQpZqi2sjzdvCJHT0ysTPtxsbGhpqlYlXm4N7srLSTTIvZV7KZEnLyOU0SBlSoBEIYYdEm4typhRUAPz8aYimpbv+7IKdOQDAHgCwuDzmeXkuvhKOOMf/V2XG88rM55V9D1RTJFUGgJIVwhiqjGZO2bSku4Mj1r8GQ/9k8bI2zA1G2np64F1d/mfmG82XaDZfoqm63NvlzedYoPJ/8AxM5gXumxcoSdt+evlnwEwqcNlHAcBlt5uwIukAPbBzdJdCeXjaTA9vfpdyaOVPkgsy/Nd/JB7rgZeSwPvg512S0KtDaiMlpEy1qmu2aVdtWzNXZsGDSzvXAwDYluRgmOWYxp7HJY8ZQqkyxT5tzlLWUf3GXL1j0v/NMWmYY9LkHTNeUJ1/JC/lih2TnnZMGu7YQkw9FULJih2TXsVyZwL0wFFqBw6FxcFXeFu5VlVZ2b4MM8MhIHU5t5wY7m6sOnGUVY2edVacrTdSlQ8AwL8yBifMYgx+73aMudV/Lom4zycvcexd7qHQ+7ueiCCotxLtSrI6TfwvgzWznEYO/F5SAIBtK+N091yMM0/yp83z4M3r5Q6+9svU/k0ont/57Hw4s04AmKtxR95fSACAyJJ8LLN8fkeQavpwyvkqCd6F9Xf8o2e8fEqheFfp1psEKrpZqq6W8gghAMCWFVOZ7vt/pCI/rX5RrWIhMv4eirRzdEdhcatcixAE1BGss7PrUlN0fo64f3Wy5QAAsP4Rgberu7MdCYFYDapc2yjDzJCBFkpwg80TQUqwjAdOmP/qgRPmtwe+mAUe5BgvXEPz6zTfXsySf/po/xOyGT9gZNN2ZBob+hFs3xDvKUGWyc3d1s5c07NzxY64e67CEU44wm9HoAf/Igt83g3nY1tENk/X+ydEM27AbhizcyMvg3TTwH7tfbR+2fmxFkoxvXGswpANC0B+e+Lk4Yqb05rS8euaYzjjDcF4fn9wWjM3qQu2MOYtP0vI8M1YQ45vxiLC/ISxyp43iaEXPUXKt7xJpvtWYRL3ApDfJvlAJjchM720ykgEjRSrrqx5YCbdIGNorFWFrjYoq0TnmIkhteuzDQk5nwhlslmlDz79qi+jbHrt+c+Y6TK8ZZfEpuMZDKpzJbL6cUUaAAAM/lTdjKX/UZ3o9Gr1n7FebpyVNzZC2YKaf1bcuLButD6JuIVnAB1E3CquF2yzw+0dnbH/uzsGnMYJs9I9cubYL9LZ3VV23rEmaURkDcUsMUdxhcWvi4boksSsMGJ3z5VsRYveNZ4ZoybFAQCIZV995ilnLhwLF9+CpY5XDWof01AQPVy4uf6mQa9RWwr13C1T2RD1UAIAIL4kMTspsem+//cFFKLre8JIP+z9pWmRmbVnB3Kdhwv95nepMtXyegwAwHHJWUsPlbrK9wh3V1c8ytbDYxX7zTrIcJQH3tcZKzEL9MuoOuPk6f1Rq6JmWy2y+h+dbGNJObHkDBrGmETzy1+EO+zudlqKbk3qiE7ksvRopJibk2ItNt3bAQDSS4pnmGV3dME4YFchnx0GgHJ2dXCVOIpzmNPvHOu7o+GvTSVJ0tGhAjtjXl5Rt7SvPM2thpEccSk1lrUvKxW3F/ZvLnE3ZG9+wFEWtf2AqcBpBQ6C0I8o23KvqDvXR931+31rPFuIVk3Ekv6p4yqjowXpP6Y6QzMfynJRaQcBEHCbnsaFEQDWR6MfTwDA76GcRwFAD+bNxq+UOo/u/Dq7VTZ/ekEF1ob2tCgFDOu6gcOuohu+lrpnSWq2OXKInFf1QOyZFME/7ds9tmVSBM9wMZjyh1F+sm7irbWfWaijytX7zmBYB/jWvdq+OSQhePvrgGL+oMa/nQzNQiTomGgSuKnW1XG5DFM6sGrtZGXhCj9/uvDY8eN/Fzj8LVtsxm2FRjPeDQvYwirGRKEd3GWAbZ0wTa68dFJ4zJVX9mKow4UucekIhpbJ4n/r0x7g+S/wXVOpidwpOvzkXHIl38FWR44rfOfXnlIzw/ykYRs4KTX8lSldRUfwI0YdJRCshZXCHBWZsFbguqo+7p3zXuXkm66pSLUxoy9alDRsJQLxbuLxEns40+7Ep6TFpwQGC15wYNPKzZNhGGGpfdIqSxOzl1jX+DabctPFDerWGz9tZHc+nDQiP3H2C18+dYSq8NjPW493Gh+k9DUnnp38oH7yOb3P1tDi2x8t8uwY+sTY9iYd0iicWj8R557jn8h5SjeRcPSRWn1wwuGf63/0B2B+Xhm49ZXyR6ZKHj9HT/rBBmrko+F+SqBaKHxE6R/agUsJjMyKIm5jYt/Esu8eV8kxs9p9bP8XEcMP5w2kekYE/R8dEjTxDymY3PBjCmEur9wW7qmRGazp/JDl3C75uy/O7BaIDEIo9VdUCNN9t7hy6v7XsYHAQ0oP3mVRd7qkxZwLyBdq6al0Gtop33um3qeXOGq7D13tEBar+8XvM+7s6c4Y6yuW17+CFuMRQTvRWEaFdLYzJ2ItThYEbWkojW2yVLlvKWnGuemdv83AqKfNhRfyV95dplPK+0LcIEbLeDlbdiNOOy/rJh+PmmJtgEswrw9d7YCXqzCWMe3ri8O4uDKKpmY8fzsyft/rbuPgpsTnV0OYKk4JXM3pERN8lb9ZPI2tyO3UxbDz0joIZp8kzga8nmY4skiD7nqoqfnj1348+/tke4PaWL2UNd/vVRHXTTWdqqo9n6uOYw80OIcRTGmvreexv3+k0tY2wNzXWKhWNIfauO2JunLMB03p55P2wSPVb7jZRPHWwyxHDNvoHldUPkr4fPEneqOOe+BJgzDjobs03Ae347kkvO53d0YG8+gJ1n+OxhG1B0SzfnyrfeURdfthzOQGDa1v50JY9jzoclOpGR/g5DbaSsv6bzkC6Vij9vwxtUP30SRdn/PaNM4KL3MMvAXqxowfpfd1me7ksfhWdhAzYOXa6WSV+mDk+bBI4brwPGbaR8+F16j1qiictpp6+jz2KgJl20sYWX/wtSn74Jo4nvJwi6KrrdiPpuLD+NZ7+eiXg4HdUlJHPqcHq211QIS/9t6IsHqJOJIug+PfgyuKq3YaUC2kPaMjyNw4vvkdy5jAX/Z7EcGtthbyWcPvzfhVzDuTbmEVuI9eU1C/OxLbILd7yzdbxsSHqr1nGv8qzVPclKswvJki8FnmIXRKc4VawjpJdwx/YdDZJub+C26b7phr+WbbKjaKpQe7qTRalpmf3Rxr88Ho5s6t+RMdES+nJg2be3gnUF3KBin3rf0cDoXWhPb81WLRTWjVuoY/E94tO6JWb3hc61tZMtc7/MTFm/5IdvbDZQ/WifPxaVVE+VPvTRp8P+kk5//JKtjyidnU0TDv4reICj/v+BuWDqgT4r7sgkxhiTX5D+ltvBQGilPWfjzbevXalObbkomEqB/VuvdKQi7LR2mcuc9wDvkg70PIwAO+EoKj+44HrYdFqy6kcbw+RSAIKj3xv/7o3+w33waDPxzAJxcQ29oDwqhSz1h4+Zk38LbK3FlTEx+JUeX9nnU0V87fufLbW90TOlZV4YPrNasjt4dQ51HT8+0QIIq568b2ynaHTDrfcNBq4YjMCdVk4O0cys7ROl515MXjJu567IAeiz9xSEHee2e0nJdOrPt630C17Czi3r179wzuHSQa/KOaMUFloFip2GiXoVFgEkvHEuVaIyI+2NKmw1Og55Pb6ST/19Dbjjd7656O/PgL+y8zc/+/E8hiSrM1kYVcB6/JNzFxmOQJ8zXxFusNX210TDlTmutwcjy7+eM1OaVolCT/bakQW+vOmLz9mpypLS/RT7//xF1jrwsvLT5il5weVnj92UjtRjX2tyXMfOZ4OzlPBo8CU4RZcd6NFMv4ofCWSOaIeF26Xa5ynCwtXe4HutKeVkgpVu7GidmbP/3OyLaRGhVvgKLWczX1W0sz/Lpe3/uI4EUrzc9Kgp9UXbwDjwUJZFY1FxW1HdV8kJRqr1jaV2uV/EL3cruzdtzkp/hriUoWHw/3xzhsMSe8blbos2IdmAyhZNV0TsnJjxS4JC3HEt/Hh6TL4fUZyiUIsG//lmX37k5H+cfmm6GhTG9O9FJi05CWNsrfHR8efjsZ9yIHsyG1fb/nBy5bwbBrn3akF8X+w/g1mb9pS9GNnt5Y3SeonKC4CAMqtJ64/quC16Z8W5raJU9LHVMPNGGOMOlkeomh2nGy/6zM56JQ5bEEHe8qob3W4mNrlR6fKJU+EDt0bahgb0YnUSvxWrWs0qG+bzKGXk32Y+iLdW7ZFF/zOlrGc8dy8TkMZwlxe3pzEg+UvA+68+Tkz77WwXEd/2dJf4dtYtZHDN4VvdmVkFu/I+kGcfBc+jHvycu9ce0l5RZtmTV1cRF27RX7zPOTaC/0ZMmp9eARu57fPEbN9uP2qwpc5LixwJPknuvpr3ftSzryuGuzvvZYA+Oze+pTAUmnG3kPFZxLlbJ1puX0s0o9L1V34nSgvmW9Kd1A/yUB311hA7q6LBfV4jjedGXFVCvyHX+zGTHR66/RrREnwG/BgVdFNxz04ZeXbN3/wvbtujeJ3MmDHb157bYNpfw7LMz3Xyr1e9NDkGgwjuaRymGR9ubvqNWy9S9+fk+eAUlAVI1WR6WNfrHfzt1N/GDnEFqDGGPZp19Bn7l+UvWMT8Q6Xas4AeWto1/RkZWmgcHxseXSbqwh69kD+Eu2XS9Qv136+Ybi6R938VaPmRoOHKDsEWlUNHE/Vr2RV+r9s/7tbxlcsRsp4gqEua192tfgi4WbmtUNBiTf7XQp+45V/jKstZWYEDNaeOlUXutoJV9lx8MnzcfaiTq6RRl2wgjWovde7/yPZd7GZw/vUbamESsZrOYfwTu25XCu778p/EgijRhhJDiszDrR4j4iNclMnZ9TiG1Ny6osT4oa2IFtTWt5VX3lnyunjHWsPOMJww8kD23NcrtfgfyG9vxI7JeJiagww+XmW27qOf7d1bxsmPaeHqKsBpXq03FHxNeDqkElWfr2i/Zma4NxK/tqw7acdKXMCTy3QHN6TabC3fe6iEu5Lx3jXSwnnUK23XBp4z2Y/gpzUDtta4Mm4ux4gQbzi31dlZTP01HlGQdGLCMaFAc0hErzd7nrM4+2Bo68LUT+u+6fkGNOhx+pO1zzpp2M7T1ggiPkja9r1T5qcto74CLP3/679XGZHKnC4dy+eJnarSLKJR79b4PXFQX5GU3wP2nd1FDM++I2ts+l6WWzzedyV4+gawnW7meTTvkqvleezBCSDaTeIZA3Utu7rbvuusbFVBpkf7PJ92MXXlggEeuDD78rrT/BVrBxx4cPfDfwgzZunnhnqbsvR+UdsxQNEnFRkR8Kx8uDGkx+qgmETqUdp+G9YsLpyyn9LO35qGpRX0sbTVFzKnt08R6xqTRmZgpzFk5c5TetL6/luL24XE1O6D265RWb7MOW8E9O9khj8VCjPeoaDV+0G0+rjIcYLo/u+gnJV1hJqV1KTRbp37WyA/QZD93afIAJr3qSpqOBccPHrtS9X3Sn8pPP6EVlRmNsxjhYOseH3GMOPSbY3A892E+bZxpu7PU21rzrVGXdZ+HM8/gh/UwtXLYOITxIUbnzzLOGmlPfHUb3h3eoo3k9+aLpa07fMDvcLxmJjSWKa1fzhAdk6rAUdIXJZlc2R4QzsCopsBXRNGi0o7HEWw1Jhdf73vEIDRrUmVxKzGbFhZpcPyS9rU4osjNn6PYuQatPPDtRb4TzTgwh9GNHCjnvZ2g60FgFUhy/b6p/lFieGyg7+fpTz6ezb6muFLMNC+zDnA22QRdwUO9i6JHtG5aO3OCRI8La+HDE6VE6/aX37mj0ie6bIzGNxdLHG2qze0C8W4+6CmP5djsqv9dYGT1rjccyaG9k+TaVcy+bQ64wRqp6sLiFKbumjhwKaJM+rBnXpIKmdb6RtHmPShJvQdbEzkqfNNv+fYyR/TZxCHTN2b0DOf3K0bpMyNo74/ueTbHPdf36bwdkMAFwmXm55vrMmw/ODusjcQQPeb0Vr651KeZnovroIB1cBAYpzojmqW7kyWDY8sw82tn64pRBTH+igWbR/ojwhGcHwsI9TNsnAgu3+2QnEbjsiYY0fIX9Kd/vfk3rLrwZfkq+gPkDH+I0X2iBv1+H3tAm7f00m/0H6h5WWvj3ffW056N3eJKbLsAQfzh4d/ULeZ4E+dJTnje6n+z93jfJOFsRpve76TkAwMNle/i/KlpF7+bXayAe63LUGYPHkjNkSNwIZS5SpmVWWonWTjUU+f1t1UyrolK7kXKuufA4RgQIAgA2L/nSygolc8b4unr+/pz4Z6+ufGRgULauODzGEYd1h+tPSdPX1quoRJsZ6lTXbNOurEQjjVNS2wllklkeI6OMbsPDHsyE2hrJrE8pafpV6Hup8w0TIdq3/fIAAKklxfCQE2Pv6opfQklFDUp7XgZxlMVtMQ2mxYpay38JI6vhCBZjt7iGci093RkNMz0aySziUU/8ojrG/S/UbwcAoP5cx8xvuA6CEX7Qe20o01GzUpGyqsxPhECje6GiXlYMDAwMckJXhRTuefWN4TRC/73HoBwaPXZHSG6c1SgjrLs7quFd0PUXUabUQhcKGgTuvNkvsel4yAY57THRaMMEY+kKjiiPdeHhLtFRRKLOOhmicfhlSebdpY2v9sh2J9nFYK5djLllHdQIkqlScJuOh6CdhwtPUM0VWV59cUsUAGD8Tyex9ComMRmvpMnOG5I5Mr/m9jsPV3GSfI5ZIQ+5uUGAz4F5HnGl5l2r5CH37FPSqivR1aJ62rOTsK5CpKzKyPjl/dT2PaVd7cL2HcEcn2/Tb7vT3hnL/9f8Z3KqsU7aX3NQf8nHw0lOhwvGcTWfJ/kXw5r+IUXGP8/hYZksuH2Ver3THyrX/gcq6UWoSJ6UjYpvOykV+W7/LNUSJ8YaCg7Kea3QuNQ6QD/3d4mBv34ukeAiBYKmm5hhQLfmgUhyPvMY5HNQswvz526FNWBhKgouAJo92gAT4DU3eKlUFCkeNCrEAcPjoQBL55eWqosZVtdjUigy5ZHvcM9i9O5eQwnIJY3g1UATQVywalQhwxdJGpGCQaNADDCwxFkweKRoKUOoYIZ8/w2wCh9kqAC5/BBcOjTnA/fBHTJ8kfwQKRg04AP34dUsGDwotHIfxKgBaSoIzg39tsYE48ZSA/KpIFIIaBwHDlEyC0Ea+1mgApKwgUMo0gDyKR5SCGichQUGcX8WgkwwZ+UoXLRg0czNyp9H4G+UFc1LRti8zP89dGGcBl4CNPkCn5pTcARycRpSMGjKBQ5mTQeWi86svLanUDBYTAYuB5pigcuhoAfLxWRIwaB5FTiYKwkYmRjMymurg4LBAi9wOdBMCidMDgcDWCbwQooFDZ/AsY6TYC3MtCxV2VpYZR+hWKThFZJzAxIsgZ+qQoxgBeEVUjxocASOF70Qj0w4ZeVFUjOBxcMncFHQqAc3TJTOAhAy4RNSOGg4Aw73ZSHcwrTIUpsSHWxTCmYG5PIdix8UbDA1L2aHk813kOJAMxVwnG1rwRIBDlIcaFCCFX55gOGQ5DFIYaCxB/j1bD0LWDpjQQoFjSWww6AukEKRpCaWelT0sEelyArIhhpWernxZwXkQg3wQqARA/hlPg8ynEyoYSkdDDAdO9gA+XwCyZ4I6djBLfWFASzMJ5AiQTtl8IPdgxcs2u9b+cGuwQdIe2xwAdAWGLyUAD6wZI9tKVdZYa72QpEWNtDggqD9Kz6YIFl+sOIG2oLXJ0hHigeGWkgOlVxHY8E+CGkuwSE1toCVNbZIIaGtHDhkFTlIcn2KlT8VggBYqiMEVwbt2sCVjZGBWZF/0AYNHBIjCFbW/CGFhPZa4JBl5CD/1D9OmH9GW8GyLRu4PGhPhR8m78ZiWORaNqSw0P4JHJZOCKy8PbPyg9kDAgvZWKhpfv2BMlAG/mwAEH69r4D/CwAA//9FB/78QzcAAA=="); err != nil { panic("add binary content to resource manager failed: " + err.Error()) } } diff --git a/os/gres/testdata/testdata.go b/os/gres/testdata/testdata.go index 1b3381164..bf6f3feae 100644 --- a/os/gres/testdata/testdata.go +++ b/os/gres/testdata/testdata.go @@ -3,7 +3,7 @@ package testdata import "github.com/gogf/gf/os/gres" func init() { - if err := gres.Add("H4sIAAAAAAAC/7SaCTTU6xvH3+xZQpYUIWKQMQyRZCnKMpaxRimNNWKEka0FpSxJKRRtF0W61ogkXZV935KUohDZR0Tof8rF/MaMJt1/59TUyfk83/f7Pu9veeaLRlHTcAIGAMAVt5toQPCLGawG9o7Odh4IG1esvaODqQktWNX6JuUgGkXPQPiD5BEcRAi4jacHztXllyQGMGLnCCEJkCb9+y8pnKuL809q93b7gxKVunCElmQ5OsMU3VBbVqvXjEAgWhDa1fAybUltuFlnqJSOXrnkhS4jNBqtVS5ZYwbuyckhW2TrZOtks5Gy8rlNMoZUKBhM1CHR+pK8KRUNAN+//9BqoiHfZgYAsF9W6wYyWl18pByxjv+pTONFmXsXZUrTd535tUweIpn/Ny/RiyJNFkVef3bR9tciiTvo/2Gi0aI+s0V9XwPUlYj1Le1wxgV9to7uMis4ImsgAISHp/UKTgffEsiP33CcnQdORgrnjVu0S0qvAa4Nl5Ix0aqp3aJdvWXVwnoLH1zevg4AwL5sJTZopZ8VFumkycHUqrMcAAAWyp1E/qmTyP/CSSTESSRpJ42WrNcvgp/6N51E/nQSCXVyKVlPNav0d5x0lNmGpcBJYmlrIAC4raP7gjDKt4N9CQRhh/2jHSHgIA7bOTu7Qi9VnRVa1eXlezNM0aNA5gpYNW+a+2cnG86fhv9+GS9Xd2dbojIStYgKbcMMUzQjPWGZG+yeMOIyFLnihPlvXHHC/OuKD2aJK7lGaN832ybdGj+Wht8XUJA6/ibv4Gq2eekF9hIWggCA9SsoOecQpORPgzKN0L5ZNq/x95R1/MF8oZs7rZy5AQCsv+uRu+cKPOIlxfnXI8J7yELf5Et/t34etHGd/IFXvpnai/44H98kthEAwL2CcnP+QG5Z8/2Tn9Hz7EIYC4E/dp8HHq/7ZQ9xLi3kexiurv9Hj1VQ1L8uOXm4Yhd0p3T/uIei59zK+rG9XkWNd/yi8pxWLRyCwk1M+b/uJLJV58wiVXXOtKzFprLMWbSNcQA1S1yVUttM9q7ANn4yqH9t8yY4DFmZ6WXVhmIouERNVe0DU2STLNpIqxpVY1Behco1lYBrN+ags3I/ZJXLZZc9+PBjrRnlP0+s35y9LmObdkjxnshgVFtYLpsvd4QBAMBgZRrnTP5DjeI/z7jf3GbIT7Hxx4SrmNMKzkucEtWN0ieSuHRHWKESf/x1BZuxbikFYYdd7t6QV4EPczdSmz7Kpr54yG284GoCAADB3y7mhKH0khtyf8dTMVixzs7e8guOtUnjYquo5stzllSaywAAxJctz02ivLsnJVezudK0kgXE15xnRogZSQAAjMJ3msXCP88sidO65AqBUwvsmtytKH6oaGPjTYMBw84U2oVHGhU04qEUAEBy2fLrSZc32fufn7VgXZ+Thvqhby//lJpZf244z3msyHfxEleuVtGIAQA4Lit4LUTw/68fieo4YcjVIdOLtJvxtqXZPcZ+BE8yhg6Cx3604pbfqezuSa4yhX1osy3/CBwAILZsVS5o1bkWhBReXPH8/emnu/9QKd1VvvU6gYZhvmBve0W4CABg028WNNn7HxZc7und3dUV90fvQT8ACBsPjxVcVHmWQBAeOB9nO6l53I+VNxgl/7w7a1XWbqmH1/ytk2MkLS+RnEHHFJ1oduWTaLft3R4L8c1J3VGJ3BYezVQLFzuJduu+rQAA5LILYYNqcHTBOKzk/rCeBAbh7OrgKnUU67CwFucYn21NR3hLk5BRIULbo+uualjYV53hUcdIj7uUGcnZl5dJ2ov6tZa6ozlaH3CWR27dbyJ0RpEzS+RbpE3Fscg71yfc9Yd8aj3b8ZYt+NKh2ROqExOF6d9me0IyH8px02gHAuB/ezWdCxMAbI8n3p8EQNBDJZ8KgH7M6w2fqXUe3/nxdKm68cMLGrAmpL9d2X9M1w0cchVf/7nMPVtas9ORU+yCmgds14wY7p/BnZObZsRwjJeCqL8ZFiTrJt5a85GVNrJCY/Ashm1YYO3LrRuDE4K2vvIvEQxs/ssJbRosxcBMl8BDs7aB22WM2oFNazsbK3fYhTNFx0+c+KvQ4S+5ElMeSxSK6W6o/yY2CWYq7aBeA7uOaZPkqsunRCdd+eUuhThc7JVEhjO2z5Q8akx7gBO8KBCrWhuxXXzs6fnkKoEDHY6cVwUurDmtbor5Tsc+fEpm7DNzuqqO8HuMBkIoSMtOBnNUbNpKkfuaxpRX7lvVU697ZyPUJw0/aVHTsZcKxbtJxkvt4kq7E5+SFp8SECR80YFdKy9flnGctf5phxxd9B58Q3NbDjXvpfUaVhs+bOBwPpQ0rjB97pNAAW24mujk91tPthsdoPYxw5+beadx6vlq780hJbffm+fbMg5KsO9JOri7aHbddJx7rl8i12ndxKyjj9UbgxIOfV/3bcgf8/3q8K3P1N8yVfMFOfvTDzTRwh+PDVEDtSLRw8p/0w9fTmBiURJzm5T4IpFz94RqrqnlzuP7Pomh310wkOkfF/Z7fFDY2C+4cGb9t1mYmYJKZ5jn7swgTeeHrOd3KNx9cXanUEQgTHmoslKU4av51dP3P08OBxxUfvAmm7bHJS36vH+BSHt/ldPodoWBs43eA/gJm72oGofQGN1Pvh+x5870RFtdtbj+GbQbjQvbiscwKaaznz0ZY36qMHBTU1lMi4XqfQtpUy7eN37WwxOe1hdfKFx9c4VBOf8Tfr0EPdOVHLkNWO387JsCfOpK9f4uQfzeDPXDx1xF7ZjSPr84hI0rp2ppxQl2weP3vuozCmpJfH4tmLnytNC13H4J4ZcFGyXT2IvdTl8KvYDUgbF4J3E14fQ0w+DFuxmuh5iYPXnly7dvUG4gsJPtmIrm2z2qkrqpJrPV9RfyNLAcAQbnMcIpXfWNfPb3D1fZ2Pib+RiJ1Ivn0hp1PtVQiX6niXw+Yx80XvOah10cZzXGehjdyfCksupxwsdL31EbdNwDThmEGo3epeM5sBXHLXXsfl9PRBCfnnDjxygsXntYPPvbl/qXHpG3H0bPrN+t9eV8MOuuB71uqrVTw1w8hpvp2R5VwOCOterPn9A69B1N0vW+oE3nrFiXa+Al1DBp9Dh9sNdkO5/5l/IDmGFL1x4ny9QH48/HxIrWhuWz0D9+LrpKfUBV8Yzl7D/PY67BEDYDWePrDrwy4RhZFcdXEWZefK3D7r2J5Biu414Bqm4koE9G5vDH9CD1zQ6wsFdeG2CWdbDD6bJYwV3Y4rgap2G1IvqzOsIszVMb37BOCh2x3wML6rAxV8gee2sqqGrWk3TLTpHnaKyixt3xmCb5nZu+2DAlPlQbONt8pCxfiTdPcWwjVcCzzIOolNZK9YS10u4YwaLAcy0sQxfdeO+Yafnk2Cg1S6QHuak2W5SbndsYY/3O8Ob2zQXT3eF1szPo1n7+aUSvikHKfStfh4MhtSH9R9rN+7I6tGJxZ8P65MbVG9EntL6UJ3O/wU1fuukH5+A4VP5graSAgFZlpB/tnqSRtzNO8n4fLIMsnprOHg31KmmDVfp6xd+wcECclPThEGYOTawteLja+pjicEnKmvfnOq7Fzmq2lU4nRH6r0b1XGnxFIXL32fuM5+EP8t8FDz8QKM1ydN/2oOOQePXFNM5Xp7OyhJWf+l1//Cjn9ZeRoHf7ccmF+M4u/1Ca1LPmx3zNmvg7ZO+sqo2PwKjxf80+mifv51z1pU33pI5lddjIOs2aiK3BtPm0qwW2CeEl3HVjBuT6gmecbzhotXNG5IZoMvL3jObkap2oPvziSQtPo92wHqsfflRRwWt7lPwxnRj3dT4B6jnZ+D179uwa2TOCN/hbLWOaxkCpSqnZNmN3oXEMA2uka62Y5Eh7pw5foZ53Xo+TwpHRtu7Xexr+Gf92xO4RC8vQo2l4CbXpqogi7gOxCi3MnMb5ogIt/CV6Y9eaHVPOluU5nJrKaX0fK68chZAWvC0TbGPVE52/T5Mrtb0O9c/X79hYjoawspLDtsnpoUXXn43Xb1DnaCtlETDD2cp7MnoUmsBMS/JvpFjEj4a1R7CEx+sy7HCV52Jt73Xf35v2T6WMUtVOrIS92T9fmdg30CLiDRC0eq4mvmvoxl416nsdFr5kqflRWfiDmotXwPFAoczq1uLizqOaD5JS7ZXKBustk1/oXuly1o6b+RAfm6hs/v7QULTDJrOsV62Kg5ZswzPB1Gyazim5BRFCl5HyrPGDAnCGXH7v0bwsIY6tX7Jt39zprnjfejMkhPn1yQFquzS4hbXKV8eHh9pm4l7kYtandu3zfMdtIxwa+2FbenHM30yfkwVbNhXf6B+I0X2KyA2MCzegQelJ6r8sfGUisKmlS/qMzHGNAGOWcOMe5joMzbZTQ+dkPxaHqEwm6HhVi+yxkpxco/zkZBlyf8xo7GjhnowevFZibI2c8sHBL7LoYy32k6hLDW45VJ/zu9un8ibzcLmM57Lidg3kJu4vfRt45+mp74MdI1M6fs+S/grlZdGHjdwVv9mbkNe4LekGfuR8+nGvmSsDcV2lFeadmbUNceG2XZV7zQqS6C/2Z8ur9+NgO57fPE7L/u32y0psxJSR0NPk/uvpr3bsTTr8pHejvvZkE9Ozexqz/klnmvkPFp5PlbFxpufytUy9INNw8kyAvkWjCcPw0GUhnx2hw7q6rJfU4zhf92ZH1ygJnHi9ETY94Le7b3eckKA5J04N1XTAW1BBumPfC5u2ta8TeZJHugfyu2yaygS3mZvtu1zm+7o/S6rJKIpPJpcV6SXYXa9l41fy/J4CIzwLVj1RE5k28cl+K08f/p2tQ0gtbJJ1r37l6sx1M2pnvcPX6lrGCalsnviMiqgyCQiKj6lAurEFr+PwFyzdcr1Q43bZxxtKZ77dxVk+YW7av5+6X6xZydj9eM0Gfpm3z4a2tjG62m2giisU5bHy7lqFKxFtadUwGJZ+s92l/Kudyqcxrc34hOiJosun8zsmqgSquh8+bT3ehdfRLc6wFYWxFb899sbveOZtXM7YLhUrOonSkRrBcZxjZy7XuqGboo+l0vDhhsJjKmzT7e7jMjMstAW5RXYdadlVFUmRw9vsOtLaX9Zc/fvqaSMdS8/4rLEH0gc3Z7vdr4R/QXm+xw/JRodXmmLzCix4+098dTUrH6O/pwcrr0WkenffEfPxoGlSTUbeftHVamUwZWlfg+7MTVfOnMbxCLWm12Yq3n2rC7ucV+cY72Ix4xS85YZLJ/+B9JeYA9ppm5s0YeemCnezvNjbW0X9PB1RkbF/3CK8SWl4t0hZwQ53fZaJjoDxtiL4o7V/Bx93OvRYwyHWi34mZmC/MTYrf2pth/ZR4zNe/pf4/vLbqY/N5EwVDePxwcnWbxZTKfUYagtaWxzoazgt+LSDt6mE/8Vtu0GXlrpW648Vrh6BsQlW7ueSTvsovVWZyRCRC6DdJpQ/Xj+wpa/h+u5LqXTwoVbjr8cvvjCHw9YFHXpT1niSvXDDtnfvBG7gRqzdPHHOMnfrJhQcs5UMErGREe+KpioCm4y/qwuFzKadoOO/aszlw4V8lvZ8Qq14sL2Trrg1lSOqZJfEbBoLC5UZKxe26ovWp1fyPMe4XY1P6j2+dSwm2Zs94e/cnPHmktFme0QsnUCUG1+HrIcENp/h+knpl3bSMjuUW8zTv2rl+OszHby1cT8zTu0UXXcT0/r3val7PunOFiSf1YvMjMJYT3Ky9kyNukcffJJlfT/kwBB9vkmY0bG2GLPe01UNH0UzL+BG9TO1sDk6WWGBSio9Z5811Z7+6jCxL6xbA8XvKRC1uvbMDdNDQ9IRdjF4Se0avjD/TB3Wwt5QuZyq1vAwRjZlRfZiuqbdXSg7/K2mpKLrg2/4REYMGowvJ+awYUOMrx9EbmkQiejJHb29Q9jyA992xGvR/JOjMP2Y8SKu+xmaDnSWAVQn7pvoH8VX5AXIzbz60P/hXBvN1RL2MaG9mHNB1qhCTtodjP1yg2PIiPUeuWJszQ/HnR6nr7781h2FOtl3czy6uQR5oqk+px/Eu/VrqDJVbLWl8X1lJ6tntfuJLMoLXrFF9Xxda/BVpgg1D1a3UBXX1PGD/p3IQ5pxLaooeucbSRt3qSbxF2ZPb6/yTrMZ2ssUMWQdB0PVntsznDukEqXLDK+/M7X32SzHwlx66LZ/BjMAV1goe4Ode//B2tp5Sx3GEbzBStbUu5QIMtO8d0AGFYMRqrPi+Wob+DIYNz0zi3K2ujRrED2UaKBZvC88LOHZ/tAwD5Ou6YCird45SVnc9ng0nUDRUMrXu5/T+opuhp1WKGR5JwA7IxBS6OfbrTfKq72PbqPfcMPDKnO/wc+e9gKrHZ7mpQsxxh8K2lnzQoEvQaHstOeNvqd7vg7OMM2vCzPw1eQ8AOAhhe+5ODuXo84Y3J+9HM5DSBk0KmmIMBMr1zItq0Jpp6LF/v162VSrskq7mXphlvUkWgwIAwA2Lqube2lJZ4yPqydOZgX6RcjCEDauWBzGEWvnDl1LSpq+tl5lFcoUrVNTu0W7qgoFN0pJ7coql872GJ9gchsb82DJqq+Vzv6QkqZfjbqXujirE6FvG1IAAMgsK0mIvCR7V1fcMnoqaxHai2LwE6xu5JSYlChp/XpEtYySw3YYW/JKKrT0dOeUzA0JpbPxRz1xZNVM+V1s3AoAQKxUzdwnVE2WIW7Ea00I81HTMrHy6swPWQGG90LEj1kyMjIyyotcE1G8d2xwErs75NE9RpWQqMk7IvJTbIYZoX19kU1vAq+/iDShFblY2CR05/U+Kd4TwevltSfFo9AJRshKzkiPtWFhLlGReLzOWlm8UdgVaZadZc0vd8n1JdlGY2IvRd+yCmwGyTQpWN4TwSjnsaKTNAtLrai5tCkSADC1shZHrqDFyfqGJNlPRL2zeDr3OY9VcxF9+fhb1Uj1TBa0NxarSSq37vijaqR6IiWtpgpVI66nPd+iDZVi5dWGRnX3U7t2lfV2idp3B3F+vL16y52unhjBI4vBAZrJHvofHaq/7LbxklfjgnFcyZf3ossTf/4hQ8JRz7Ex2WyooVV6Az+/xl/zxwWRZAoS7aC1qk8XcUEAfK1sDQkL0s4X/Ml7FBNvtdSdVVSc1OQzdvPDzoGdPz7JJO7IIzggCHUixNLE3TxpzjvCRJvAAgmAxIBw0iSSKTFiKmEAbQOEqrgKUJKNIwYShsV4IMD3RECK9BFmuzgguCAqsGzebLmdYITshAg1IJ0MIw9YAwHYQwBEeSboegizW3yQ9dxfAiGTDCNGEoa22CBILhpAPgJGuT+uhBjkCvzJgACW9YcwkQX158sSCJm8FzGSMIoF9ceKFpAPdlHuz6MFDFGKaxFAKre1CJiCAIhSXORVsEMgqnRg2RQX1BTCqBXU53OkOKRSXMRIwlgVFDlIEkkisUX5ak3pwbLpLKg0wtgUVNoNUhxS6SxiJGFACopkYwCUpa8oX63PUiQkZwWVRhiC4oVIKyLFIZGzIiYSpp2gROnVgKIo1XJr5YSs9dpSInFmiuiSSpBkgt4YP5JBkcpMEVMJk0pQ6j5GQHEmivJl15GkQjJPUIGE2SJ+iEBeJkBp5okYSpgGgkKfkoMuDSktt2hWyKKtmcEysSKoNMKUxTqItOSllCUxDmIaYYgHSuNiAb/MDRHTCJM53BDaFRI0ohgQMYwwZwN9JGNZAyiJ9hADCXMw6yHA86SBRGGd5XZiLYSnzAqWy9IQgwjjK1DQHSiIOCyzRBFBIgUKEmADy2VfiEGEwQ8uCCgKCiIRZ6GcRccOfpFUofzhw2uBRRRCofThLA8CIAqhQFdEGAaBPtvPLIGQCKEsp4kNoilgLVguT0J0uyOYrULbu4wEZmmehJhHONOE7luZAPjFfJZy228LAvIzUaggwmEldIFdSyC/K4obIsp8E/j1wBMqjnDSKAI9w2Rh5AaeS155CWaHQhD2NiHwO5PLJbc5gjEgFPyMPJjUUIkYTDh0g4LRwuB3ZoqU75nSZvDrCR5UJeF8DaoymCyMIl8JR2lQ8Dh5MCW+Ek7FoGBbEfA7c7nlfOWF+DpKHkw0YoNKJZx+iUKkaoiC3x2xEcMJJ11QeA4FcCR5L0gNy+a9+L5TFAaWjM5o6X78rypQBVs4ALD+8egD/hcAAP//TL8FiKM6AAA="); err != nil { + if err := gres.Add("H4sIAAAAAAAC/7SaCTTU6xvH3+xZQpYUIWKQMQyRZCnKMpaxRimNNWKEka0FpSxJKRRtF0W61ogkXZV935KUohDZR0Tof8rF/MaMJt1/59TUyfk83/f7Pu9veeaLRlHTcAIGAMAVt5toQPCLGawG9o7Odh4IG1esvaODqQktWNX6JuUgGkXPQPiD5BEcRAi4jacHztXllyQGMGLnCCEJkCb9+y8pnKuL809q93b7gxKVunCElmQ5OsMU3VBbVqvXjEAgWhDa1fAybUltuFlnqJSOXrnkhS4jNBqtVS5ZYwbuyckhW2TrZOtks5Gy8rlNMoZUKBhM1CHR+pK8KRUNAN+//9BqoiHfZgYAsF9W6wYyWl18pByxjv+pTONFmXsXZUrTd535tUweIpn/Ny/RiyJNFkVef3bR9tciiTvo/2Gi0aI+s0V9XwPUlYj1Le1wxgV9to7uMis4ImsgAISHp/UKTgffEsiP33CcnQdORgrnjVu0S0qvAa4Nl5Ix0aqp3aJdvWXVwnoLH1zevg4AwL5sJTZopZ8VFumkycHUqrMcAAAWyp1E/qmTyP/CSSTESSRpJ42WrNcvgp/6N51E/nQSCXVyKVlPNav0d5x0lNmGpcBJYmlrIAC4raP7gjDKt4N9CQRhh/2jHSHgIA7bOTu7Qi9VnRVa1eXlezNM0aNA5gpYNW+a+2cnG86fhv9+GS9Xd2dbojIStYgKbcMMUzQjPWGZG+yeMOIyFLnihPlvXHHC/OuKD2aJK7lGaN832ybdGj+Wht8XUJA6/ibv4Gq2eekF9hIWggCA9SsoOecQpORPgzKN0L5ZNq/x95R1/MF8oZs7rZy5AQCsv+uRu+cKPOIlxfnXI8J7yELf5Et/t34etHGd/IFXvpnai/44H98kthEAwL2CcnP+QG5Z8/2Tn9Hz7EIYC4E/dp8HHq/7ZQ9xLi3kexiurv9Hj1VQ1L8uOXm4Yhd0p3T/uIei59zK+rG9XkWNd/yi8pxWLRyCwk1M+b/uJLJV58wiVXXOtKzFprLMWbSNcQA1S1yVUttM9q7ANn4yqH9t8yY4DFmZ6WXVhmIouERNVe0DU2STLNpIqxpVY1Behco1lYBrN+ags3I/ZJXLZZc9+PBjrRnlP0+s35y9LmObdkjxnshgVFtYLpsvd4QBAMBgZRrnTP5DjeI/z7jf3GbIT7Hxx4SrmNMKzkucEtWN0ieSuHRHWKESf/x1BZuxbikFYYdd7t6QV4EPczdSmz7Kpr54yG284GoCAADB3y7mhKH0khtyf8dTMVixzs7e8guOtUnjYquo5stzllSaywAAxJctz02ivLsnJVezudK0kgXE15xnRogZSQAAjMJ3msXCP88sidO65AqBUwvsmtytKH6oaGPjTYMBw84U2oVHGhU04qEUAEBy2fLrSZc32fufn7VgXZ+Thvqhby//lJpZf244z3msyHfxEleuVtGIAQA4Lit4LUTw/68fieo4YcjVIdOLtJvxtqXZPcZ+BE8yhg6Cx3604pbfqezuSa4yhX1osy3/CBwAILZsVS5o1bkWhBReXPH8/emnu/9QKd1VvvU6gYZhvmBve0W4CABg028WNNn7HxZc7und3dUV90fvQT8ACBsPjxVcVHmWQBAeOB9nO6l53I+VNxgl/7w7a1XWbqmH1/ytk2MkLS+RnEHHFJ1oduWTaLft3R4L8c1J3VGJ3BYezVQLFzuJduu+rQAA5LILYYNqcHTBOKzk/rCeBAbh7OrgKnUU67CwFucYn21NR3hLk5BRIULbo+uualjYV53hUcdIj7uUGcnZl5dJ2ov6tZa6ozlaH3CWR27dbyJ0RpEzS+RbpE3Fscg71yfc9Yd8aj3b8ZYt+NKh2ROqExOF6d9me0IyH8px02gHAuB/ezWdCxMAbI8n3p8EQNBDJZ8KgH7M6w2fqXUe3/nxdKm68cMLGrAmpL9d2X9M1w0cchVf/7nMPVtas9ORU+yCmgds14wY7p/BnZObZsRwjJeCqL8ZFiTrJt5a85GVNrJCY/Ashm1YYO3LrRuDE4K2vvIvEQxs/ssJbRosxcBMl8BDs7aB22WM2oFNazsbK3fYhTNFx0+c+KvQ4S+5ElMeSxSK6W6o/yY2CWYq7aBeA7uOaZPkqsunRCdd+eUuhThc7JVEhjO2z5Q8akx7gBO8KBCrWhuxXXzs6fnkKoEDHY6cVwUurDmtbor5Tsc+fEpm7DNzuqqO8HuMBkIoSMtOBnNUbNpKkfuaxpRX7lvVU697ZyPUJw0/aVHTsZcKxbtJxkvt4kq7E5+SFp8SECR80YFdKy9flnGctf5phxxd9B58Q3NbDjXvpfUaVhs+bOBwPpQ0rjB97pNAAW24mujk91tPthsdoPYxw5+beadx6vlq780hJbffm+fbMg5KsO9JOri7aHbddJx7rl8i12ndxKyjj9UbgxIOfV/3bcgf8/3q8K3P1N8yVfMFOfvTDzTRwh+PDVEDtSLRw8p/0w9fTmBiURJzm5T4IpFz94RqrqnlzuP7Pomh310wkOkfF/Z7fFDY2C+4cGb9t1mYmYJKZ5jn7swgTeeHrOd3KNx9cXanUEQgTHmoslKU4av51dP3P08OBxxUfvAmm7bHJS36vH+BSHt/ldPodoWBs43eA/gJm72oGofQGN1Pvh+x5870RFtdtbj+GbQbjQvbiscwKaaznz0ZY36qMHBTU1lMi4XqfQtpUy7eN37WwxOe1hdfKFx9c4VBOf8Tfr0EPdOVHLkNWO387JsCfOpK9f4uQfzeDPXDx1xF7ZjSPr84hI0rp2ppxQl2weP3vuozCmpJfH4tmLnytNC13H4J4ZcFGyXT2IvdTl8KvYDUgbF4J3E14fQ0w+DFuxmuh5iYPXnly7dvUG4gsJPtmIrm2z2qkrqpJrPV9RfyNLAcAQbnMcIpXfWNfPb3D1fZ2Pib+RiJ1Ivn0hp1PtVQiX6niXw+Yx80XvOah10cZzXGehjdyfCksupxwsdL31EbdNwDThmEGo3epeM5sBXHLXXsfl9PRBCfnnDjxygsXntYPPvbl/qXHpG3H0bPrN+t9eV8MOuuB71uqrVTw1w8hpvp2R5VwOCOterPn9A69B1N0vW+oE3nrFiXa+Al1DBp9Dh9sNdkO5/5l/IDmGFL1x4ny9QH48/HxIrWhuWz0D9+LrpKfUBV8Yzl7D/PY67BEDYDWePrDrwy4RhZFcdXEWZefK3D7r2J5Biu414Bqm4koE9G5vDH9CD1zQ6wsFdeG2CWdbDD6bJYwV3Y4rgap2G1IvqzOsIszVMb37BOCh2x3wML6rAxV8gee2sqqGrWk3TLTpHnaKyixt3xmCb5nZu+2DAlPlQbONt8pCxfiTdPcWwjVcCzzIOolNZK9YS10u4YwaLAcy0sQxfdeO+Yafnk2Cg1S6QHuak2W5SbndsYY/3O8Ob2zQXT3eF1szPo1n7+aUSvikHKfStfh4MhtSH9R9rN+7I6tGJxZ8P65MbVG9EntL6UJ3O/wU1fuukH5+A4VP5graSAgFZlpB/tnqSRtzNO8n4fLIMsnprOHg31KmmDVfp6xd+wcECclPThEGYOTawteLja+pjicEnKmvfnOq7Fzmq2lU4nRH6r0b1XGnxFIXL32fuM5+EP8t8FDz8QKM1ydN/2oOOQePXFNM5Xp7OyhJWf+l1//Cjn9ZeRoHf7ccmF+M4u/1Ca1LPmx3zNmvg7ZO+sqo2PwKjxf80+mifv51z1pU33pI5lddjIOs2aiK3BtPm0qwW2CeEl3HVjBuT6gmecbzhotXNG5IZoMvL3jObkap2oPvziSQtPo92wHqsfflRRwWt7lPwxnRj3dT4B6jnZ+D179uwa2TOCN/hbLWOaxkCpSqnZNmN3oXEMA2uka62Y5Eh7pw5foZ53Xo+TwpHRtu7Xexr+Gf92xO4RC8vQo2l4CbXpqogi7gOxCi3MnMb5ogIt/CV6Y9eaHVPOluU5nJrKaX0fK68chZAWvC0TbGPVE52/T5Mrtb0O9c/X79hYjoawspLDtsnpoUXXn43Xb1DnaCtlETDD2cp7MnoUmsBMS/JvpFjEj4a1R7CEx+sy7HCV52Jt73Xf35v2T6WMUtVOrIS92T9fmdg30CLiDRC0eq4mvmvoxl416nsdFr5kqflRWfiDmotXwPFAoczq1uLizqOaD5JS7ZXKBustk1/oXuly1o6b+RAfm6hs/v7QULTDJrOsV62Kg5ZswzPB1Gyazim5BRFCl5HyrPGDAnCGXH7v0bwsIY6tX7Jt39zprnjfejMkhPn1yQFquzS4hbXKV8eHh9pm4l7kYtandu3zfMdtIxwa+2FbenHM30yfkwVbNhXf6B+I0X2KyA2MCzegQelJ6r8sfGUisKmlS/qMzHGNAGOWcOMe5joMzbZTQ+dkPxaHqEwm6HhVi+yxkpxco/zkZBlyf8xo7GjhnowevFZibI2c8sHBL7LoYy32k6hLDW45VJ/zu9un8ibzcLmM57Lidg3kJu4vfRt45+mp74MdI1M6fs+S/grlZdGHjdwVv9mbkNe4LekGfuR8+nGvmSsDcV2lFeadmbUNceG2XZV7zQqS6C/2Z8ur9+NgO57fPE7L/u32y0psxJSR0NPk/uvpr3bsTTr8pHejvvZkE9Ozexqz/klnmvkPFp5PlbFxpufytUy9INNw8kyAvkWjCcPw0GUhnx2hw7q6rJfU4zhf92ZH1ygJnHi9ETY94Le7b3eckKA5J04N1XTAW1BBumPfC5u2ta8TeZJHugfyu2yaygS3mZvtu1zm+7o/S6rJKIpPJpcV6SXYXa9l41fy/J4CIzwLVj1RE5k28cl+K08f/p2tQ0gtbJJ1r37l6sx1M2pnvcPX6lrGCalsnviMiqgyCQiKj6lAurEFr+PwFyzdcr1Q43bZxxtKZ77dxVk+YW7av5+6X6xZydj9eM0Gfpm3z4a2tjG62m2giisU5bHy7lqFKxFtadUwGJZ+s92l/Kudyqcxrc34hOiJosun8zsmqgSquh8+bT3ehdfRLc6wFYWxFb899sbveOZtXM7YLhUrOonSkRrBcZxjZy7XuqGboo+l0vDhhsJjKmzT7e7jMjMstAW5RXYdadlVFUmRw9vsOtLaX9Zc/fvqaSMdS8/4rLEH0gc3Z7vdr4R/QXm+xw/JRodXmmLzCix4+098dTUrH6O/pwcrr0WkenffEfPxoGlSTUbeftHVamUwZWlfg+7MTVfOnMbxCLWm12Yq3n2rC7ucV+cY72Ix4xS85YZLJ/+B9JeYA9ppm5s0YeemCnezvNjbW0X9PB1RkbF/3CK8SWl4t0hZwQ53fZaJjoDxtiL4o7V/Bx93OvRYwyHWi34mZmC/MTYrf2pth/ZR4zNe/pf4/vLbqY/N5EwVDePxwcnWbxZTKfUYagtaWxzoazgt+LSDt6mE/8Vtu0GXlrpW648Vrh6BsQlW7ueSTvsovVWZyRCRC6DdJpQ/Xj+wpa/h+u5LqXTwoVbjr8cvvjCHw9YFHXpT1niSvXDDtnfvBG7gRqzdPHHOMnfrJhQcs5UMErGREe+KpioCm4y/qwuFzKadoOO/aszlw4V8lvZ8Qq14sL2Trrg1lSOqZJfEbBoLC5UZKxe26ovWp1fyPMe4XY1P6j2+dSwm2Zs94e/cnPHmktFme0QsnUCUG1+HrIcENp/h+knpl3bSMjuUW8zTv2rl+OszHby1cT8zTu0UXXcT0/r3val7PunOFiSf1YvMjMJYT3Ky9kyNukcffJJlfT/kwBB9vkmY0bG2GLPe01UNH0UzL+BG9TO1sDk6WWGBSio9Z5811Z7+6jCxL6xbA8XvKRC1uvbMDdNDQ9IRdjF4Se0avjD/TB3Wwt5QuZyq1vAwRjZlRfZiuqbdXSg7/K2mpKLrg2/4REYMGowvJ+awYUOMrx9EbmkQiejJHb29Q9jyA992xGvR/JOjMP2Y8SKu+xmaDnSWAVQn7pvoH8VX5AXIzbz60P/hXBvN1RL2MaG9mHNB1qhCTtodjP1yg2PIiPUeuWJszQ/HnR6nr7781h2FOtl3czy6uQR5oqk+px/Eu/VrqDJVbLWl8X1lJ6tntfuJLMoLXrFF9Xxda/BVpgg1D1a3UBXX1PGD/p3IQ5pxLaooeucbSRt3qSbxF2ZPb6/yTrMZ2ssUMWQdB0PVntsznDukEqXLDK+/M7X32SzHwlx66LZ/BjMAV1goe4Ode//B2tp5Sx3GEbzBStbUu5QIMtO8d0AGFYMRqrPi+Wob+DIYNz0zi3K2ujRrED2UaKBZvC88LOHZ/tAwD5Ou6YCird45SVnc9ng0nUDRUMrXu5/T+opuhp1WKGR5JwA7IxBS6OfbrTfKq72PbqPfcMPDKnO/wc+e9gKrHZ7mpQsxxh8K2lnzQoEvQaHstOeNvqd7vg7OMM2vCzPw1eQ8AOAhhe+5ODuXo84Y3J+9HM5DSBk0KmmIMBMr1zItq0Jpp6LF/v162VSrskq7mXphlvUkWgwIAwA2Lqube2lJZ4yPqydOZgX6RcjCEDauWBzGEWvnDl1LSpq+tl5lFcoUrVNTu0W7qgoFN0pJ7coql872GJ9gchsb82DJqq+Vzv6QkqZfjbqXujirE6FvG1IAAMgsK0mIvCR7V1fcMnoqaxHai2LwE6xu5JSYlChp/XpEtYySw3YYW/JKKrT0dOeUzA0JpbPxRz1xZNVM+V1s3AoAQKxUzdwnVE2WIW7Ea00I81HTMrHy6swPWQGG90LEj1kyMjIyyotcE1G8d2xwErs75NE9RpWQqMk7IvJTbIYZoX19kU1vAq+/iDShFblY2CR05/U+Kd4TwevltSfFo9AJRshKzkiPtWFhLlGReLzOWlm8UdgVaZadZc0vd8n1JdlGY2IvRd+yCmwGyTQpWN4TwSjnsaKTNAtLrai5tCkSADC1shZHrqDFyfqGJNlPRL2zeDr3OY9VcxF9+fhb1Uj1TBa0NxarSSq37vijaqR6IiWtpgpVI66nPd+iDZVi5dWGRnX3U7t2lfV2idp3B3F+vL16y52unhjBI4vBAZrJHvofHaq/7LbxklfjgnFcyZf3ossTf/4hQ8JRz7Ex2WyooVV6Az+/xl/zxwWRZAoS7aC1qk8XcUEAlC1hRoQFaecL/uTdHRm3WurOKipOavIZu/lh58DOH59kEnfkERwQhDoRYmnibp405x1hok1ggQRAYkA4aRLJlBgxlTCAtgFCVVwFKMnGEQMJw2I8EOB7IiBF+gizXRwQXBAVWDZvttxOMEJ2QoQakE6GkQesgQDsIQCiPBN0PYTZLT7Ieu4vgZBJhhEjCUNbbBAkFw0gHwGj3B9XQgxyBf5kQADL+kOYyIL682UJhEzeixhJGMWC+mNFC8gHuyj359EChijFtQggldtaBExBAEQpLvIq2CEQVTqwbIoLagph1Arq8zlSHFIpLmIkYawKihwkiSSR2KJ8tab0YNl0FlQaYWwKKu0GKQ6pdBYxkjAgBUWyMQDK0leUr9ZnKRKSs4JKIwxB8UKkFZHikMhZERMJ005QovRqQFGUarm1ckLWem0pkTgzRXRJJUgyQW+MH8mgSGWmiKmESSUodR8joDgTRfmy60hSIZknqEDCbBE/RCAvE6A080QMJUwDQaFPyUGXhpSWWzQrZNHWzGCZWBFUGmHKYh1EWvJSypIYBzGNMMQDpXGxgF/mhohphMkcbgjtCgkaUQyIGEaYs4E+krGsAZREe4iBhDmY9RDgedJAorDOcjuxFsJTZgXLZWmIQYTxFSjoDhREHJZZooggkQIFCbCB5bIvxCDC4AcXBBQFBZGIs1DOomMHv0iqUP7w4bXAIgqhUPpwlgcBEIVQoCsiDINAn+1nlkBIhFCW08QG0RSwFiyXJyG63RHMVqHtXUYCszRPQswjnGlC961MAPxiPku57bcFAfmZKFQQ4bASusCuJZDfFcUNEWW+Cfx64AkVRzhpFIGeYbIwcgPPJa+8BLNDIQh7mxD4ncnlktscwRgQCn5GHkxqqEQMJhy6QcFoYfA7M0XK90xpM/j1BA+qknC+BlUZTBZGka+EozQoeJw8mBJfCadiULCtCPidudxyvvJCfB0lDyYasUGlEk6/RCFSNUTB747YiOGEky4oPIcCOJK8F6SGZfNefN8pCgNLRme0dD/+VxWogi0cAFj/ePQB/wsAAP//umpbUqM6AAA="); err != nil { panic("add binary content to resource manager failed: " + err.Error()) } } From c7ce8cf94359946e5ef5f16cc31d62f2fc59414b Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 8 Jan 2021 00:39:52 +0800 Subject: [PATCH 046/492] fix issue in resource searching for package gcfg --- os/gcfg/gcfg.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index fd89a38cf..34f9c2859 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -283,6 +283,10 @@ func (c *Config) FilePath(file ...string) (path string) { } }) } + // Already found? + if path != "" { + return + } // Searching the file system. c.paths.RLockFunc(func(array []string) { for _, prefix := range array { From e141b8e0987c7ac990531903a5563f62bb6aad07 Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 8 Jan 2021 00:58:58 +0800 Subject: [PATCH 047/492] shutdown server gracefully when reveiving TERM signal --- net/ghttp/ghttp_server_admin_process.go | 13 +++++++++---- net/ghttp/ghttp_server_admin_unix.go | 16 ++++++++++++---- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/net/ghttp/ghttp_server_admin_process.go b/net/ghttp/ghttp_server_admin_process.go index 18299e8b6..577a0168d 100644 --- a/net/ghttp/ghttp_server_admin_process.go +++ b/net/ghttp/ghttp_server_admin_process.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -233,8 +233,13 @@ func shutdownWebServers(signal ...string) { } } -// gracefulShutdownWebServers gracefully shuts down all servers. -func gracefulShutdownWebServers() { +// shutdownWebServersGracefully gracefully shuts down all servers. +func shutdownWebServersGracefully(signal ...string) { + if len(signal) > 0 { + glog.Printf("%d: server gracefully shutting down by signal: %s", gproc.Pid(), signal[0]) + } else { + glog.Printf("%d: server gracefully shutting down by api", gproc.Pid()) + } serverMapping.RLockFunc(func(m map[string]interface{}) { for _, v := range m { for _, s := range v.(*Server).servers { @@ -262,7 +267,7 @@ func handleProcessMessage() { if msg := gproc.Receive(adminGProcCommGroup); msg != nil { if bytes.EqualFold(msg.Data, []byte("exit")) { intlog.Printf("%d: process message: exit", gproc.Pid()) - gracefulShutdownWebServers() + shutdownWebServersGracefully() allDoneChan <- struct{}{} intlog.Printf("%d: process message: exit done", gproc.Pid()) return diff --git a/net/ghttp/ghttp_server_admin_unix.go b/net/ghttp/ghttp_server_admin_unix.go index 19fff62af..de96d89e5 100644 --- a/net/ghttp/ghttp_server_admin_unix.go +++ b/net/ghttp/ghttp_server_admin_unix.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright 2017 gf Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -35,14 +35,22 @@ func handleProcessSignal() { sig = <-procSignalChan intlog.Printf(`signal received: %s`, sig.String()) switch sig { - // Stop the servers. - case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGABRT: + // Shutdown the servers. + case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGABRT: shutdownWebServers(sig.String()) return + // Shutdown the servers gracefully. + // Especially from K8S when running server in POD. + case syscall.SIGTERM: + shutdownWebServersGracefully(sig.String()) + return + // Restart the servers. case syscall.SIGUSR1: - restartWebServers(sig.String()) + if err := restartWebServers(sig.String()); err != nil { + intlog.Error(err) + } return default: From 8365ce9d291212568d49d3183f387b42fa0eb1f5 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Fri, 8 Jan 2021 16:24:08 +0800 Subject: [PATCH 048/492] change name *Case to Case* for case functions of package gstr --- text/gstr/gstr_case.go | 85 ++++++++++++++++++++++++------ text/gstr/gstr_z_unit_case_test.go | 38 ++++++------- 2 files changed, 89 insertions(+), 34 deletions(-) diff --git a/text/gstr/gstr_case.go b/text/gstr/gstr_case.go index e472f9055..393dae9ab 100644 --- a/text/gstr/gstr_case.go +++ b/text/gstr/gstr_case.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -6,15 +6,15 @@ // // | Function | Result | // |-----------------------------------|--------------------| -// | SnakeCase(s) | any_kind_of_string | -// | SnakeScreamingCase(s) | ANY_KIND_OF_STRING | -// | KebabCase(s) | any-kind-of-string | -// | KebabScreamingCase(s) | ANY-KIND-OF-STRING | -// | DelimitedCase(s, '.') | any.kind.of.string | -// | DelimitedScreamingCase(s, '.') | ANY.KIND.OF.STRING | -// | CamelCase(s) | AnyKindOfString | -// | CamelLowerCase(s) | anyKindOfString | -// | SnakeFirstUpperCase(RGBCodeMd5) | rgb_code_md5 | +// | CaseSnake(s) | any_kind_of_string | +// | CaseSnakeScreaming(s) | ANY_KIND_OF_STRING | +// | CaseSnakeFirstUpper("RGBCodeMd5") | rgb_code_md5 | +// | CaseKebab(s) | any-kind-of-string | +// | CaseKebabScreaming(s) | ANY-KIND-OF-STRING | +// | CaseDelimited(s, '.') | any.kind.of.string | +// | CaseDelimitedScreaming(s, '.') | ANY.KIND.OF.STRING | +// | CaseCamel(s) | AnyKindOfString | +// | CaseCamelLower(s) | anyKindOfString | package gstr @@ -30,12 +30,24 @@ var ( ) // CamelCase converts a string to CamelCase. +// Deprecated, use CaseCamel instead. func CamelCase(s string) string { + return CaseCamel(s) +} + +// CaseCamel converts a string to CamelCase. +func CaseCamel(s string) string { return toCamelInitCase(s, true) } // CamelLowerCase converts a string to lowerCamelCase. +// Deprecated, use CaseCamelLower instead. func CamelLowerCase(s string) string { + return CaseCamelLower(s) +} + +// CaseCamelLower converts a string to lowerCamelCase. +func CaseCamelLower(s string) string { if s == "" { return s } @@ -46,19 +58,38 @@ func CamelLowerCase(s string) string { } // SnakeCase converts a string to snake_case. +// Deprecated, use CaseSnake instead. func SnakeCase(s string) string { + return CaseSnake(s) +} + +// CaseSnake converts a string to snake_case. +func CaseSnake(s string) string { return DelimitedCase(s, '_') } // SnakeScreamingCase converts a string to SNAKE_CASE_SCREAMING. +// Deprecated, use CaseSnakeScreaming instead. func SnakeScreamingCase(s string) string { + return CaseSnakeScreaming(s) +} + +// CaseSnakeScreaming converts a string to SNAKE_CASE_SCREAMING. +func CaseSnakeScreaming(s string) string { return DelimitedScreamingCase(s, '_', true) } // SnakeFirstUpperCase converts a string from RGBCodeMd5 to rgb_code_md5. // The length of word should not be too long -// TODO for efficiency should change regexp to traversing string in future +// Deprecated, use CaseSnakeFirstUpper instead. func SnakeFirstUpperCase(word string, underscore ...string) string { + return CaseSnakeFirstUpper(word, underscore...) +} + +// CaseSnakeFirstUpper converts a string from RGBCodeMd5 to rgb_code_md5. +// The length of word should not be too long +// TODO for efficiency should change regexp to traversing string in future +func CaseSnakeFirstUpper(word string, underscore ...string) string { replace := "_" if len(underscore) > 0 { replace = underscore[0] @@ -84,23 +115,47 @@ func SnakeFirstUpperCase(word string, underscore ...string) string { return TrimLeft(word, replace) } -// KebabCase converts a string to kebab-case +// KebabCase converts a string to kebab-case. +// Deprecated, use CaseKebab instead. func KebabCase(s string) string { - return DelimitedCase(s, '-') + return CaseKebab(s) +} + +// CaseKebab converts a string to kebab-case +func CaseKebab(s string) string { + return CaseDelimited(s, '-') } // KebabScreamingCase converts a string to KEBAB-CASE-SCREAMING. +// Deprecated, use CaseKebabScreaming instead. func KebabScreamingCase(s string) string { - return DelimitedScreamingCase(s, '-', true) + return CaseKebabScreaming(s) +} + +// CaseKebabScreaming converts a string to KEBAB-CASE-SCREAMING. +func CaseKebabScreaming(s string) string { + return CaseDelimitedScreaming(s, '-', true) } // DelimitedCase converts a string to snake.case.delimited. +// Deprecated, use CaseDelimited instead. func DelimitedCase(s string, del uint8) string { - return DelimitedScreamingCase(s, del, false) + return CaseDelimited(s, del) +} + +// CaseDelimited converts a string to snake.case.delimited. +func CaseDelimited(s string, del uint8) string { + return CaseDelimitedScreaming(s, del, false) } // DelimitedScreamingCase converts a string to DELIMITED.SCREAMING.CASE or delimited.screaming.case. +// Deprecated, use CaseDelimitedScreaming instead. func DelimitedScreamingCase(s string, del uint8, screaming bool) string { + return CaseDelimitedScreaming(s, del, screaming) +} + +// CaseDelimitedScreaming converts a string to DELIMITED.SCREAMING.CASE or delimited.screaming.case. +func CaseDelimitedScreaming(s string, del uint8, screaming bool) string { s = addWordBoundariesToNumbers(s) s = strings.Trim(s, " ") n := "" diff --git a/text/gstr/gstr_z_unit_case_test.go b/text/gstr/gstr_z_unit_case_test.go index cde0ed961..9fe4c3dea 100644 --- a/text/gstr/gstr_z_unit_case_test.go +++ b/text/gstr/gstr_z_unit_case_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -12,7 +12,7 @@ import ( "github.com/gogf/gf/text/gstr" ) -func Test_CamelCase(t *testing.T) { +func Test_CaseCamel(t *testing.T) { cases := [][]string{ {"test_case", "TestCase"}, {"test", "Test"}, @@ -28,14 +28,14 @@ func Test_CamelCase(t *testing.T) { for _, i := range cases { in := i[0] out := i[1] - result := gstr.CamelCase(in) + result := gstr.CaseCamel(in) if result != out { t.Error("'" + result + "' != '" + out + "'") } } } -func Test_CamelLowerCase(t *testing.T) { +func Test_CaseCamelLower(t *testing.T) { cases := [][]string{ {"foo-bar", "fooBar"}, {"TestCase", "testCase"}, @@ -45,14 +45,14 @@ func Test_CamelLowerCase(t *testing.T) { for _, i := range cases { in := i[0] out := i[1] - result := gstr.CamelLowerCase(in) + result := gstr.CaseCamelLower(in) if result != out { t.Error("'" + result + "' != '" + out + "'") } } } -func Test_SnakeCase(t *testing.T) { +func Test_CaseSnake(t *testing.T) { cases := [][]string{ {"testCase", "test_case"}, {"TestCase", "test_case"}, @@ -75,14 +75,14 @@ func Test_SnakeCase(t *testing.T) { for _, i := range cases { in := i[0] out := i[1] - result := gstr.SnakeCase(in) + result := gstr.CaseSnake(in) if result != out { t.Error("'" + in + "'('" + result + "' != '" + out + "')") } } } -func Test_DelimitedCase(t *testing.T) { +func Test_CaseDelimited(t *testing.T) { cases := [][]string{ {"testCase", "test@case"}, {"TestCase", "test@case"}, @@ -106,28 +106,28 @@ func Test_DelimitedCase(t *testing.T) { for _, i := range cases { in := i[0] out := i[1] - result := gstr.DelimitedCase(in, '@') + result := gstr.CaseDelimited(in, '@') if result != out { t.Error("'" + in + "' ('" + result + "' != '" + out + "')") } } } -func Test_SnakeScreamingCase(t *testing.T) { +func Test_CaseSnakeScreaming(t *testing.T) { cases := [][]string{ {"testCase", "TEST_CASE"}, } for _, i := range cases { in := i[0] out := i[1] - result := gstr.SnakeScreamingCase(in) + result := gstr.CaseSnakeScreaming(in) if result != out { t.Error("'" + result + "' != '" + out + "'") } } } -func Test_KebabCase(t *testing.T) { +func Test_CaseKebab(t *testing.T) { cases := [][]string{ {"testCase", "test-case"}, {"optimization1.0.0", "optimization-1-0-0"}, @@ -135,42 +135,42 @@ func Test_KebabCase(t *testing.T) { for _, i := range cases { in := i[0] out := i[1] - result := gstr.KebabCase(in) + result := gstr.CaseKebab(in) if result != out { t.Error("'" + result + "' != '" + out + "'") } } } -func Test_KebabScreamingCase(t *testing.T) { +func Test_CaseKebabScreaming(t *testing.T) { cases := [][]string{ {"testCase", "TEST-CASE"}, } for _, i := range cases { in := i[0] out := i[1] - result := gstr.KebabScreamingCase(in) + result := gstr.CaseKebabScreaming(in) if result != out { t.Error("'" + result + "' != '" + out + "'") } } } -func Test_DelimitedScreamingCase(t *testing.T) { +func Test_CaseDelimitedScreaming(t *testing.T) { cases := [][]string{ {"testCase", "TEST.CASE"}, } for _, i := range cases { in := i[0] out := i[1] - result := gstr.DelimitedScreamingCase(in, '.', true) + result := gstr.CaseDelimitedScreaming(in, '.', true) if result != out { t.Error("'" + result + "' != '" + out + "'") } } } -func TestSnakeFirstUpperCase(t *testing.T) { +func Test_CaseSnakeFirstUpper(t *testing.T) { cases := [][]string{ {"RGBCodeMd5", "rgb_code_md5"}, {"testCase", "test_case"}, @@ -186,7 +186,7 @@ func TestSnakeFirstUpperCase(t *testing.T) { for _, i := range cases { in := i[0] out := i[1] - result := gstr.SnakeFirstUpperCase(in) + result := gstr.CaseSnakeFirstUpper(in) if result != out { t.Error("'" + result + "' != '" + out + "'") } From 92b791eb08444285cb3a9daec48289e7f72a0ef5 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 9 Jan 2021 21:05:47 +0800 Subject: [PATCH 049/492] improve Struct convertion for package gdb; improve IsNil function for package internal/empty --- database/gdb/gdb_func.go | 20 +++++++++--------- database/gdb/gdb_type_record.go | 3 ++- database/gdb/gdb_z_mysql_model_test.go | 12 +++++++++++ database/gdb/gdb_z_mysql_struct_test.go | 5 +++-- frame/g/g_func.go | 7 +++++-- internal/empty/empty.go | 27 ++++++++++++++++++++----- internal/empty/empty_test.go | 21 ++++++++++++++++++- 7 files changed, 74 insertions(+), 21 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 78c9ac47b..5e7c9b7bf 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -48,9 +48,9 @@ type apiMapStrAny interface { } const ( - ORM_TAG_FOR_STRUCT = "orm" - ORM_TAG_FOR_UNIQUE = "unique" - ORM_TAG_FOR_PRIMARY = "primary" + OrmTagForStruct = "orm" + OrmTagForUnique = "unique" + OrmTagForPrimary = "primary" ) var ( @@ -58,7 +58,7 @@ var ( quoteWordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`) // Priority tags for struct converting for orm field mapping. - structTagPriority = append([]string{ORM_TAG_FOR_STRUCT}, gconv.StructTagPriority...) + structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...) ) // ListItemValues retrieves and returns the elements of all item struct/map with key . @@ -315,14 +315,14 @@ func doQuoteString(s, charLeft, charRight string) string { // GetWhereConditionOfStruct returns the where condition sql and arguments by given struct pointer. // This function automatically retrieves primary or unique field and its attribute value as condition. func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interface{}, err error) { - tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT}) + tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct}) if err != nil { return "", nil, err } array := ([]string)(nil) for _, field := range tagField { array = strings.Split(field.TagValue, ",") - if len(array) > 1 && gstr.InArray([]string{ORM_TAG_FOR_UNIQUE, ORM_TAG_FOR_PRIMARY}, array[1]) { + if len(array) > 1 && gstr.InArray([]string{OrmTagForUnique, OrmTagForPrimary}, array[1]) { return array[0], []interface{}{field.Value()}, nil } if len(where) > 0 { @@ -336,14 +336,14 @@ func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interf // GetPrimaryKey retrieves and returns primary key field name from given struct. func GetPrimaryKey(pointer interface{}) (string, error) { - tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT}) + tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct}) if err != nil { return "", err } array := ([]string)(nil) for _, field := range tagField { array = strings.Split(field.TagValue, ",") - if len(array) > 1 && array[1] == ORM_TAG_FOR_PRIMARY { + if len(array) > 1 && array[1] == OrmTagForPrimary { return array[0], nil } } @@ -741,7 +741,7 @@ func FormatSqlWithArgs(sql string, args []interface{}) string { // convertMapToStruct maps the to given struct. // Note that the given parameter should be a pointer to s struct. func convertMapToStruct(data map[string]interface{}, pointer interface{}) error { - tagNameMap, err := structs.TagMapName(pointer, []string{ORM_TAG_FOR_STRUCT}) + tagNameMap, err := structs.TagMapName(pointer, []string{OrmTagForStruct}) if err != nil { return err } diff --git a/database/gdb/gdb_type_record.go b/database/gdb/gdb_type_record.go index 7801e18fc..3db440993 100644 --- a/database/gdb/gdb_type_record.go +++ b/database/gdb/gdb_type_record.go @@ -11,6 +11,7 @@ import ( "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/encoding/gparser" "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/util/gconv" "reflect" ) @@ -47,7 +48,7 @@ func (r Record) GMap() *gmap.StrAnyMap { // Note that it returns sql.ErrNoRows if is empty. func (r Record) Struct(pointer interface{}) error { // If the record is empty, it returns error. - if r.IsEmpty() { + if r.IsEmpty() && !empty.IsNil(pointer, true) { return sql.ErrNoRows } // Special handling for parameter type: reflect.Value diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 697f78382..44ae4d520 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -981,6 +981,18 @@ func Test_Model_Struct(t *testing.T) { err := db.Table(table).Where("id=-1").Struct(user) t.Assert(err, sql.ErrNoRows) }) + gtest.C(t, func(t *gtest.T) { + type User struct { + Id int + Passport string + Password string + NickName string + CreateTime *gtime.Time + } + var user *User + err := db.Table(table).Where("id=-1").Struct(&user) + t.Assert(err, nil) + }) } func Test_Model_Struct_CustomType(t *testing.T) { diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index d11648aa3..d7159c4c5 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -262,7 +262,8 @@ func Test_Struct_Empty(t *testing.T) { one, err := db.Table(table).Where("id=100").One() t.Assert(err, nil) var user *User - t.AssertNE(one.Struct(&user), nil) + t.Assert(one.Struct(&user), nil) + t.Assert(user, nil) }) gtest.C(t, func(t *gtest.T) { diff --git a/frame/g/g_func.go b/frame/g/g_func.go index d99a77ff3..7b1afc730 100644 --- a/frame/g/g_func.go +++ b/frame/g/g_func.go @@ -52,9 +52,12 @@ func TryCatch(try func(), catch ...func(exception error)) { } // IsNil checks whether given is nil. +// Parameter is used for tracing to the source variable if given is type +// of a pinter that also points to a pointer. It returns nil if the source is nil when +// is true. // Note that it might use reflect feature which affects performance a little bit. -func IsNil(value interface{}) bool { - return empty.IsNil(value) +func IsNil(value interface{}, traceSource ...bool) bool { + return empty.IsNil(value, traceSource...) } // IsEmpty checks whether given empty. diff --git a/internal/empty/empty.go b/internal/empty/empty.go index ae0beb57a..2e59ea5a0 100644 --- a/internal/empty/empty.go +++ b/internal/empty/empty.go @@ -1,10 +1,10 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package empty provides functions for checking empty variables. +// Package empty provides functions for checking empty/nil variables. package empty import ( @@ -152,8 +152,11 @@ func IsEmpty(value interface{}) bool { } // IsNil checks whether given is nil. +// Parameter is used for tracing to the source variable if given is type +// of a pinter that also points to a pointer. It returns nil if the source is nil when +// is true. // Note that it might use reflect feature which affects performance a little bit. -func IsNil(value interface{}) bool { +func IsNil(value interface{}, traceSource ...bool) bool { if value == nil { return true } @@ -168,10 +171,24 @@ func IsNil(value interface{}) bool { reflect.Map, reflect.Slice, reflect.Func, - reflect.Ptr, reflect.Interface, reflect.UnsafePointer: - return rv.IsNil() + return !rv.IsValid() || rv.IsNil() + + case reflect.Ptr: + if len(traceSource) > 0 && traceSource[0] { + for rv.Kind() == reflect.Ptr { + rv = rv.Elem() + } + if !rv.IsValid() { + return true + } + if rv.Kind() == reflect.Ptr { + return rv.IsNil() + } + } else { + return !rv.IsValid() || rv.IsNil() + } } return false } diff --git a/internal/empty/empty_test.go b/internal/empty/empty_test.go index 520aa64ee..2a4e6ecbd 100644 --- a/internal/empty/empty_test.go +++ b/internal/empty/empty_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -108,3 +108,22 @@ func TestIsEmpty(t *testing.T) { t.Assert(empty.IsEmpty(tmpF6), false) }) } + +func TestIsNil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(empty.IsNil(nil), true) + }) + gtest.C(t, func(t *gtest.T) { + var i int + t.Assert(empty.IsNil(i), false) + }) + gtest.C(t, func(t *gtest.T) { + var i *int + t.Assert(empty.IsNil(i), true) + }) + gtest.C(t, func(t *gtest.T) { + var i *int + t.Assert(empty.IsNil(&i), false) + t.Assert(empty.IsNil(&i, true), true) + }) +} From bb2dad6d5e262baa7c80d5b2b28598dd410ff048 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 9 Jan 2021 23:02:16 +0800 Subject: [PATCH 050/492] improve package gdb of empty value handling for struct convertion --- database/gdb/gdb_type_record.go | 7 +++++-- database/gdb/gdb_z_mysql_struct_test.go | 13 +++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/database/gdb/gdb_type_record.go b/database/gdb/gdb_type_record.go index 3db440993..13edcf385 100644 --- a/database/gdb/gdb_type_record.go +++ b/database/gdb/gdb_type_record.go @@ -48,8 +48,11 @@ func (r Record) GMap() *gmap.StrAnyMap { // Note that it returns sql.ErrNoRows if is empty. func (r Record) Struct(pointer interface{}) error { // If the record is empty, it returns error. - if r.IsEmpty() && !empty.IsNil(pointer, true) { - return sql.ErrNoRows + if r.IsEmpty() { + if !empty.IsNil(pointer, true) { + return sql.ErrNoRows + } + return nil } // Special handling for parameter type: reflect.Value if _, ok := pointer.(reflect.Value); ok { diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index d7159c4c5..baf5c8f7a 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -7,6 +7,7 @@ package gdb_test import ( + "database/sql" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" @@ -252,10 +253,10 @@ func Test_Struct_Empty(t *testing.T) { } gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).Where("id=100").One() - t.Assert(err, nil) user := new(User) - t.AssertNE(one.Struct(user), nil) + err := db.Table(table).Where("id=100").Struct(user) + t.Assert(err, sql.ErrNoRows) + t.AssertNE(user, nil) }) gtest.C(t, func(t *gtest.T) { @@ -267,10 +268,10 @@ func Test_Struct_Empty(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).Where("id=100").One() - t.Assert(err, nil) var user *User - t.AssertNE(one.Struct(user), nil) + err := db.Table(table).Where("id=100").Struct(&user) + t.Assert(err, nil) + t.Assert(user, nil) }) } From a4152347e51bf5a18af05417fcc8c046444b4d39 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 10 Jan 2021 23:37:20 +0800 Subject: [PATCH 051/492] improve comment for package ghttp --- net/ghttp/ghttp_server_config_static.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ghttp/ghttp_server_config_static.go b/net/ghttp/ghttp_server_config_static.go index f4c5164e0..c71d6faf2 100644 --- a/net/ghttp/ghttp_server_config_static.go +++ b/net/ghttp/ghttp_server_config_static.go @@ -1,10 +1,10 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// 静态文件搜索优先级: Resource > ServerPaths > ServerRoot > SearchPath +// Static Searching Priority: Resource > ServerPaths > ServerRoot > SearchPath package ghttp From 6f5b0c393ee607814fe5a61b0b291e0828027ac1 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 11 Jan 2021 19:30:22 +0800 Subject: [PATCH 052/492] add more unit testing case for package gdb --- database/gdb/gdb_z_mysql_model_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 44ae4d520..a462f3d4b 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -2147,6 +2147,27 @@ func Test_Model_Option_List(t *testing.T) { }) } +func Test_Model_OmitEmpty(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + table := fmt.Sprintf(`table_%s`, gtime.TimestampNanoStr()) + if _, err := db.Exec(fmt.Sprintf(` + CREATE TABLE IF NOT EXISTS %s ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + name varchar(45) NOT NULL, + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + _, err := db.Table(table).OmitEmpty().Data(g.Map{ + "id": 1, + "name": "", + }).Save() + t.AssertNE(err, nil) + }) +} + func Test_Model_Option_Where(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createInitTable() From 8ee3793f8f24c327b8e6fbbd80bd3c953b941f08 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 11 Jan 2021 20:06:09 +0800 Subject: [PATCH 053/492] add SessionCookieMaxAge configuration for ghttp.Server --- .../net/ghttp/server/session/basic/session.go | 1 + net/ghttp/ghttp_server_config.go | 17 +++++---- net/ghttp/ghttp_server_config_session.go | 12 ++++++- net/ghttp/ghttp_server_cookie.go | 36 ++++++++++++------- 4 files changed, 47 insertions(+), 19 deletions(-) diff --git a/.example/net/ghttp/server/session/basic/session.go b/.example/net/ghttp/server/session/basic/session.go index c7a21a684..d7ae0e1ec 100644 --- a/.example/net/ghttp/server/session/basic/session.go +++ b/.example/net/ghttp/server/session/basic/session.go @@ -8,6 +8,7 @@ import ( func main() { s := g.Server() + s.SetSessionCookieMaxAge(0) s.Group("/", func(group *ghttp.RouterGroup) { group.GET("/set", func(r *ghttp.Request) { r.Session.Set("time", gtime.Timestamp()) diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 9a8a7aa9b..715ab0890 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -152,14 +152,11 @@ type ServerConfig struct { // Session. // ================================== - // SessionMaxAge specifies max TTL for session items. - SessionMaxAge time.Duration - // SessionIdName specifies the session id name. SessionIdName string - // SessionCookieOutput specifies whether automatic outputting session id to cookie. - SessionCookieOutput bool + // SessionMaxAge specifies max TTL for session items. + SessionMaxAge time.Duration // SessionPath specifies the session storage directory path for storing session files. // It only makes sense if the session storage is type of file storage. @@ -168,6 +165,13 @@ type ServerConfig struct { // SessionStorage specifies the session storage. SessionStorage gsession.Storage + // SessionCookieMaxAge specifies the cookie ttl for session id. + // It it is set 0, it means it expires along with browser session. + SessionCookieMaxAge time.Duration + + // SessionCookieOutput specifies whether automatic outputting session id to cookie. + SessionCookieOutput bool + // ================================== // Logging. // ================================== @@ -243,10 +247,11 @@ func NewConfig() ServerConfig { CookieMaxAge: time.Hour * 24 * 365, CookiePath: "/", CookieDomain: "", - SessionMaxAge: time.Hour * 24, SessionIdName: "gfsessionid", SessionPath: gsession.DefaultStorageFilePath, + SessionMaxAge: time.Hour * 24, SessionCookieOutput: true, + SessionCookieMaxAge: time.Hour * 24, Logger: glog.New(), LogLevel: "all", LogStdout: true, diff --git a/net/ghttp/ghttp_server_config_session.go b/net/ghttp/ghttp_server_config_session.go index e9f712629..205eae613 100644 --- a/net/ghttp/ghttp_server_config_session.go +++ b/net/ghttp/ghttp_server_config_session.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -32,6 +32,11 @@ func (s *Server) SetSessionCookieOutput(enabled bool) { s.config.SessionCookieOutput = enabled } +// SetSessionCookieMaxAge sets the SessionCookieMaxAge for server. +func (s *Server) SetSessionCookieMaxAge(maxAge time.Duration) { + s.config.SessionCookieMaxAge = maxAge +} + // GetSessionMaxAge returns the SessionMaxAge of server. func (s *Server) GetSessionMaxAge() time.Duration { return s.config.SessionMaxAge @@ -41,3 +46,8 @@ func (s *Server) GetSessionMaxAge() time.Duration { func (s *Server) GetSessionIdName() string { return s.config.SessionIdName } + +// GetSessionCookieMaxAge returns the SessionCookieMaxAge of server. +func (s *Server) GetSessionCookieMaxAge() time.Duration { + return s.config.SessionCookieMaxAge +} diff --git a/net/ghttp/ghttp_server_cookie.go b/net/ghttp/ghttp_server_cookie.go index af7f7473c..47fbfb918 100644 --- a/net/ghttp/ghttp_server_cookie.go +++ b/net/ghttp/ghttp_server_cookie.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -14,9 +14,6 @@ import ( // Cookie for HTTP COOKIE management. type Cookie struct { data map[string]*cookieItem // Underlying cookie items. - path string // The default cookie path. - domain string // The default cookie domain - maxAge time.Duration // The default cookie max age. server *Server // Belonged HTTP server request *Request // Belonged HTTP request. response *Response // Belonged HTTP response. @@ -47,13 +44,10 @@ func (c *Cookie) init() { return } c.data = make(map[string]*cookieItem) - c.path = c.request.Server.GetCookiePath() - c.domain = c.request.Server.GetCookieDomain() - c.maxAge = c.request.Server.GetCookieMaxAge() c.response = c.request.Response // DO NOT ADD ANY DEFAULT COOKIE DOMAIN! - //if c.domain == "" { - // c.domain = c.request.GetHost() + //if c.request.Server.GetCookieDomain() == "" { + // c.request.Server.GetCookieDomain() = c.request.GetHost() //} for _, v := range c.request.Cookies() { c.data[v.Name] = &cookieItem{ @@ -86,7 +80,13 @@ func (c *Cookie) Contains(key string) bool { // Set sets cookie item with default domain, path and expiration age. func (c *Cookie) Set(key, value string) { - c.SetCookie(key, value, c.domain, c.path, c.maxAge) + c.SetCookie( + key, + value, + c.request.Server.GetCookieDomain(), + c.request.Server.GetCookiePath(), + c.request.Server.GetCookieMaxAge(), + ) } // SetCookie sets cookie item given given domain, path and expiration age. @@ -128,7 +128,13 @@ func (c *Cookie) GetSessionId() string { // SetSessionId sets session id in the cookie. func (c *Cookie) SetSessionId(id string) { - c.Set(c.server.GetSessionIdName(), id) + c.SetCookie( + c.server.GetSessionIdName(), + id, + c.request.Server.GetCookieDomain(), + c.request.Server.GetCookiePath(), + c.server.GetSessionCookieMaxAge(), + ) } // Get retrieves and returns the value with specified key. @@ -149,7 +155,13 @@ func (c *Cookie) Get(key string, def ...string) string { // Remove deletes specified key and its value from cookie using default domain and path. // It actually tells the http client that the cookie is expired, do not send it to server next time. func (c *Cookie) Remove(key string) { - c.SetCookie(key, "", c.domain, c.path, -86400) + c.SetCookie( + key, + "", + c.request.Server.GetCookieDomain(), + c.request.Server.GetCookiePath(), + -86400, + ) } // RemoveCookie deletes specified key and its value from cookie using given domain and path. From 2ae32ed2c213053cb7a937d47194aa8ca0895a85 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 11 Jan 2021 20:48:35 +0800 Subject: [PATCH 054/492] add QueryTimeout/ExecTimeout/TranTimeout/PrepareTimeout for package gdb --- database/gdb/gdb_core.go | 30 +++++++++++++++++++++++------- database/gdb/gdb_core_config.go | 9 ++++++--- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 033a9666d..12c0d25c1 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -83,9 +83,13 @@ func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { sql, args = formatSql(sql, args) sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) + ctx := c.DB.GetCtx() + if c.GetConfig().QueryTimeout > 0 { + ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) + } if c.DB.GetDebug() { mTime1 := gtime.TimestampMilli() - rows, err = link.QueryContext(c.DB.GetCtx(), sql, args...) + rows, err = link.QueryContext(ctx, sql, args...) mTime2 := gtime.TimestampMilli() s := &Sql{ Sql: sql, @@ -98,7 +102,7 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro } c.writeSqlToLogger(s) } else { - rows, err = link.QueryContext(c.DB.GetCtx(), sql, args...) + rows, err = link.QueryContext(ctx, sql, args...) } if err == nil { return rows, nil @@ -123,10 +127,14 @@ func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err err func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) { sql, args = formatSql(sql, args) sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) + ctx := c.DB.GetCtx() + if c.GetConfig().ExecTimeout > 0 { + ctx, _ = context.WithTimeout(ctx, c.GetConfig().ExecTimeout) + } if c.DB.GetDebug() { mTime1 := gtime.TimestampMilli() if !c.DB.GetDryRun() { - result, err = link.ExecContext(c.DB.GetCtx(), sql, args...) + result, err = link.ExecContext(ctx, sql, args...) } else { result = new(SqlResult) } @@ -143,7 +151,7 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re c.writeSqlToLogger(s) } else { if !c.DB.GetDryRun() { - result, err = link.ExecContext(c.DB.GetCtx(), sql, args...) + result, err = link.ExecContext(ctx, sql, args...) } else { result = new(SqlResult) } @@ -178,7 +186,11 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error) { // doPrepare calls prepare function on given link object and returns the statement object. func (c *Core) DoPrepare(link Link, sql string) (*sql.Stmt, error) { - return link.PrepareContext(c.DB.GetCtx(), sql) + ctx := c.DB.GetCtx() + if c.GetConfig().QueryTimeout > 0 { + ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) + } + return link.PrepareContext(ctx, sql) } // GetAll queries and returns data records from database. @@ -320,7 +332,11 @@ func (c *Core) Begin() (*TX, error) { if master, err := c.DB.Master(); err != nil { return nil, err } else { - if tx, err := master.Begin(); err == nil { + ctx := c.DB.GetCtx() + if c.GetConfig().TranTimeout > 0 { + ctx, _ = context.WithTimeout(ctx, c.GetConfig().TranTimeout) + } + if tx, err := master.BeginTx(ctx, nil); err == nil { return &TX{ db: c.DB, tx: tx, diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index 85a9ee888..31b8106a1 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -16,8 +16,7 @@ import ( ) const ( - DEFAULT_GROUP_NAME = "default" // Deprecated, use DefaultGroupName instead. - DefaultGroupName = "default" // Default group name. + DefaultGroupName = "default" // Default group name. ) // Config is the configuration management object. @@ -44,6 +43,10 @@ type ConfigNode struct { MaxIdleConnCount int `json:"maxidle"` // (Optional) Max idle connection configuration for underlying connection pool. MaxOpenConnCount int `json:"maxopen"` // (Optional) Max open connection configuration for underlying connection pool. MaxConnLifetime time.Duration `json:"maxlifetime"` // (Optional) Max connection TTL configuration for underlying connection pool. + QueryTimeout time.Duration // (Optional) Max query time for per dql. + ExecTimeout time.Duration // (Optional) Max exec time for dml. + TranTimeout time.Duration // (Optional) Max exec time time for a transaction. + PrepareTimeout time.Duration // (Optional) Max exec time time for prepare operation. CreatedAt string // (Optional) The filed name of table for automatic-filled created datetime. UpdatedAt string // (Optional) The filed name of table for automatic-filled updated datetime. DeletedAt string // (Optional) The filed name of table for automatic-filled updated datetime. From a9aa021914734ea5943e1b516a611e21531d978d Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 12 Jan 2021 00:42:33 +0800 Subject: [PATCH 055/492] add json tag for configuration struct for package gdb/gredis/ghttp/glog/gview --- database/gdb/gdb_core_config.go | 48 +++++++------- database/gredis/gredis.go | 24 +++---- net/ghttp/ghttp_server.go | 4 +- net/ghttp/ghttp_server_config.go | 104 +++++++++++++++---------------- os/glog/glog_logger_config.go | 40 ++++++------ os/gview/gview_config.go | 14 ++--- 6 files changed, 117 insertions(+), 117 deletions(-) diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index 31b8106a1..cbc4e4f9f 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -27,30 +27,30 @@ type ConfigGroup []ConfigNode // ConfigNode is configuration for one node. type ConfigNode struct { - Host string // Host of server, ip or domain like: 127.0.0.1, localhost - Port string // Port, it's commonly 3306. - User string // Authentication username. - Pass string // Authentication password. - Name string // Default used database name. - Type string // Database type: mysql, sqlite, mssql, pgsql, oracle. - Role string // (Optional, "master" in default) Node role, used for master-slave mode: master, slave. - Debug bool // (Optional) Debug mode enables debug information logging and output. - Prefix string // (Optional) Table prefix. - DryRun bool // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements. - Weight int // (Optional) Weight for load balance calculating, it's useless if there's just one node. - Charset string // (Optional, "utf8mb4" in default) Custom charset when operating on database. - LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored. - MaxIdleConnCount int `json:"maxidle"` // (Optional) Max idle connection configuration for underlying connection pool. - MaxOpenConnCount int `json:"maxopen"` // (Optional) Max open connection configuration for underlying connection pool. - MaxConnLifetime time.Duration `json:"maxlifetime"` // (Optional) Max connection TTL configuration for underlying connection pool. - QueryTimeout time.Duration // (Optional) Max query time for per dql. - ExecTimeout time.Duration // (Optional) Max exec time for dml. - TranTimeout time.Duration // (Optional) Max exec time time for a transaction. - PrepareTimeout time.Duration // (Optional) Max exec time time for prepare operation. - CreatedAt string // (Optional) The filed name of table for automatic-filled created datetime. - UpdatedAt string // (Optional) The filed name of table for automatic-filled updated datetime. - DeletedAt string // (Optional) The filed name of table for automatic-filled updated datetime. - TimeMaintainDisabled bool // (Optional) Disable the automatic time maintaining feature. + Host string `json:"host"` // Host of server, ip or domain like: 127.0.0.1, localhost + Port string `json:"port"` // Port, it's commonly 3306. + User string `json:"user"` // Authentication username. + Pass string `json:"pass"` // Authentication password. + Name string `json:"name"` // Default used database name. + Type string `json:"type"` // Database type: mysql, sqlite, mssql, pgsql, oracle. + Role string `json:"role"` // (Optional, "master" in default) Node role, used for master-slave mode: master, slave. + Debug bool `json:"debug"` // (Optional) Debug mode enables debug information logging and output. + Prefix string `json:"prefix"` // (Optional) Table prefix. + DryRun bool `json:"dryRun"` // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements. + Weight int `json:"weight"` // (Optional) Weight for load balance calculating, it's useless if there's just one node. + Charset string `json:"charset"` // (Optional, "utf8mb4" in default) Custom charset when operating on database. + LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored. + MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool. + MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool. + MaxConnLifetime time.Duration `json:"maxLifetime"` // (Optional) Max connection TTL configuration for underlying connection pool. + QueryTimeout time.Duration `json:"queryTimeout"` // (Optional) Max query time for per dql. + ExecTimeout time.Duration `json:"execTimeout"` // (Optional) Max exec time for dml. + TranTimeout time.Duration `json:"tranTimeout"` // (Optional) Max exec time time for a transaction. + PrepareTimeout time.Duration `json:"prepareTimeout"` // (Optional) Max exec time time for prepare operation. + CreatedAt string `json:"createdAt"` // (Optional) The filed name of table for automatic-filled created datetime. + UpdatedAt string `json:"updatedAt"` // (Optional) The filed name of table for automatic-filled updated datetime. + DeletedAt string `json:"deletedAt"` // (Optional) The filed name of table for automatic-filled updated datetime. + TimeMaintainDisabled bool `json:"timeMaintainDisabled"` // (Optional) Disable the automatic time maintaining feature. } // configs is internal used configuration object. diff --git a/database/gredis/gredis.go b/database/gredis/gredis.go index a489c325f..a19b7e26e 100644 --- a/database/gredis/gredis.go +++ b/database/gredis/gredis.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -36,17 +36,17 @@ type Conn struct { // Redis configuration. type Config struct { - Host string - Port int - Db int - Pass string // Password for AUTH. - MaxIdle int // Maximum number of connections allowed to be idle (default is 10) - MaxActive int // Maximum number of connections limit (default is 0 means no limit). - IdleTimeout time.Duration // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0) - MaxConnLifetime time.Duration // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0) - ConnectTimeout time.Duration // Dial connection timeout. - TLS bool // Specifies the config to use when a TLS connection is dialed. - TLSSkipVerify bool // Disables server name verification when connecting over TLS + Host string `json:"host"` + Port int `json:"port"` + Db int `json:"db"` + Pass string `json:"pass"` // Password for AUTH. + MaxIdle int `json:"maxIdle"` // Maximum number of connections allowed to be idle (default is 10) + MaxActive int `json:"maxActive"` // Maximum number of connections limit (default is 0 means no limit). + IdleTimeout time.Duration `json:"idleTimeout"` // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0) + MaxConnLifetime time.Duration `json:"maxConnLifetime"` // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0) + ConnectTimeout time.Duration `json:"connectTimeout"` // Dial connection timeout. + TLS bool `json:"tls"` // Specifies the config to use when a TLS connection is dialed. + TLSSkipVerify bool `json:"tlsSkipVerify"` // Disables server name verification when connecting over TLS } // Pool statistics. diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 770f07d19..0fa1e3445 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -350,7 +350,7 @@ func (s *Server) startServer(fdMap listenerFdMap) { s.config.HTTPSAddr = s.config.Address s.config.Address = "" } else { - s.config.HTTPSAddr = gDEFAULT_HTTPS_ADDR + s.config.HTTPSAddr = defaultHttpsAddr } } httpsEnabled = len(s.config.HTTPSAddr) > 0 @@ -385,7 +385,7 @@ func (s *Server) startServer(fdMap listenerFdMap) { } // HTTP if !httpsEnabled && len(s.config.Address) == 0 { - s.config.Address = gDEFAULT_HTTP_ADDR + s.config.Address = defaultHttpAddr } var array []string if v, ok := fdMap["http"]; ok && len(v) > 0 { diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 715ab0890..760b17b43 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -27,12 +27,12 @@ import ( ) const ( - gDEFAULT_HTTP_ADDR = ":80" // Default listening port for HTTP. - gDEFAULT_HTTPS_ADDR = ":443" // Default listening port for HTTPS. - URI_TYPE_DEFAULT = 0 // Method name to URI converting type, which converts name to its lower case and joins the words using char '-'. - URI_TYPE_FULLNAME = 1 // Method name to URI converting type, which does no converting to the method name. - URI_TYPE_ALLLOWER = 2 // Method name to URI converting type, which converts name to its lower case. - URI_TYPE_CAMEL = 3 // Method name to URI converting type, which converts name to its camel case. + defaultHttpAddr = ":80" // Default listening port for HTTP. + defaultHttpsAddr = ":443" // Default listening port for HTTPS. + URI_TYPE_DEFAULT = 0 // Method name to URI converting type, which converts name to its lower case and joins the words using char '-'. + URI_TYPE_FULLNAME = 1 // Method name to URI converting type, which does no converting to the method name. + URI_TYPE_ALLLOWER = 2 // Method name to URI converting type, which converts name to its lower case. + URI_TYPE_CAMEL = 3 // Method name to URI converting type, which converts name to its camel case. ) // ServerConfig is the HTTP Server configuration manager. @@ -43,16 +43,16 @@ type ServerConfig struct { // Address specifies the server listening address like "port" or ":port", // multiple addresses joined using ','. - Address string + Address string `json:"address"` // HTTPSAddr specifies the HTTPS addresses, multiple addresses joined using char ','. - HTTPSAddr string + HTTPSAddr string `json:"httpsAddr"` // HTTPSCertPath specifies certification file path for HTTPS service. - HTTPSCertPath string + HTTPSCertPath string `json:"httpsCertPath"` // HTTPSKeyPath specifies the key file path for HTTPS service. - HTTPSKeyPath string + HTTPSKeyPath string `json:"httpsKeyPath"` // TLSConfig optionally provides a TLS configuration for use // by ServeTLS and ListenAndServeTLS. Note that this value is @@ -61,10 +61,10 @@ type ServerConfig struct { // tls.Config.SetSessionTicketKeys. To use // SetSessionTicketKeys, use Server.Serve with a TLS Listener // instead. - TLSConfig *tls.Config + TLSConfig *tls.Config `json:"tlsConfig"` // Handler the handler for HTTP request. - Handler http.Handler + Handler http.Handler `json:"-"` // ReadTimeout is the maximum duration for reading the entire // request, including the body. @@ -73,19 +73,19 @@ type ServerConfig struct { // decisions on each request body's acceptable deadline or // upload rate, most users will prefer to use // ReadHeaderTimeout. It is valid to use them both. - ReadTimeout time.Duration + ReadTimeout time.Duration `json:"readTimeout"` // WriteTimeout is the maximum duration before timing out // writes of the response. It is reset whenever a new // request's header is read. Like ReadTimeout, it does not // let Handlers make decisions on a per-request basis. - WriteTimeout time.Duration + WriteTimeout time.Duration `json:"writeTimeout"` // IdleTimeout is the maximum amount of time to wait for the // next request when keep-alives are enabled. If IdleTimeout // is zero, the value of ReadTimeout is used. If both are // zero, there is no timeout. - IdleTimeout time.Duration + IdleTimeout time.Duration `json:"idleTimeout"` // MaxHeaderBytes controls the maximum number of bytes the // server will read parsing the request header's keys and @@ -94,102 +94,102 @@ type ServerConfig struct { // // It can be configured in configuration file using string like: 1m, 10m, 500kb etc. // It's 10240 bytes in default. - MaxHeaderBytes int + MaxHeaderBytes int `json:"maxHeaderBytes"` // KeepAlive enables HTTP keep-alive. - KeepAlive bool + KeepAlive bool `json:"keepAlive"` // ServerAgent specifies the server agent information, which is wrote to // HTTP response header as "Server". - ServerAgent string + ServerAgent string `json:"serverAgent"` // View specifies the default template view object for the server. - View *gview.View + View *gview.View `json:"view"` // ================================== // Static. // ================================== // Rewrites specifies the URI rewrite rules map. - Rewrites map[string]string + Rewrites map[string]string `json:"rewrites"` // IndexFiles specifies the index files for static folder. - IndexFiles []string + IndexFiles []string `json:"indexFiles"` // IndexFolder specifies if listing sub-files when requesting folder. // The server responses HTTP status code 403 if it is false. - IndexFolder bool + IndexFolder bool `json:"indexFolder"` // ServerRoot specifies the root directory for static service. - ServerRoot string + ServerRoot string `json:"serverRoot"` // SearchPaths specifies additional searching directories for static service. - SearchPaths []string + SearchPaths []string `json:"searchPaths"` // StaticPaths specifies URI to directory mapping array. - StaticPaths []staticPathItem + StaticPaths []staticPathItem `json:"staticPaths"` // FileServerEnabled is the global switch for static service. // It is automatically set enabled if any static path is set. - FileServerEnabled bool + FileServerEnabled bool `json:"fileServerEnabled"` // ================================== // Cookie. // ================================== // CookieMaxAge specifies the max TTL for cookie items. - CookieMaxAge time.Duration + CookieMaxAge time.Duration `json:"cookieMaxAge"` // CookiePath specifies cookie path. // It also affects the default storage for session id. - CookiePath string + CookiePath string `json:"cookiePath"` // CookieDomain specifies cookie domain. // It also affects the default storage for session id. - CookieDomain string + CookieDomain string `json:"cookieDomain"` // ================================== // Session. // ================================== // SessionIdName specifies the session id name. - SessionIdName string + SessionIdName string `json:"sessionIdName"` // SessionMaxAge specifies max TTL for session items. - SessionMaxAge time.Duration + SessionMaxAge time.Duration `json:"sessionMaxAge"` // SessionPath specifies the session storage directory path for storing session files. // It only makes sense if the session storage is type of file storage. - SessionPath string + SessionPath string `json:"sessionPath"` // SessionStorage specifies the session storage. - SessionStorage gsession.Storage + SessionStorage gsession.Storage `json:"sessionStorage"` // SessionCookieMaxAge specifies the cookie ttl for session id. // It it is set 0, it means it expires along with browser session. - SessionCookieMaxAge time.Duration + SessionCookieMaxAge time.Duration `json:"sessionCookieMaxAge"` // SessionCookieOutput specifies whether automatic outputting session id to cookie. - SessionCookieOutput bool + SessionCookieOutput bool `json:"sessionCookieOutput"` // ================================== // Logging. // ================================== - Logger *glog.Logger // Logger specifies the logger for server. - LogPath string // LogPath specifies the directory for storing logging files. - LogLevel string // LogLevel specifies the logging level for logger. - LogStdout bool // LogStdout specifies whether printing logging content to stdout. - ErrorStack bool // ErrorStack specifies whether logging stack information when error. - ErrorLogEnabled bool // ErrorLogEnabled enables error logging content to files. - ErrorLogPattern string // ErrorLogPattern specifies the error log file pattern like: error-{Ymd}.log - AccessLogEnabled bool // AccessLogEnabled enables access logging content to files. - AccessLogPattern string // AccessLogPattern specifies the error log file pattern like: access-{Ymd}.log + Logger *glog.Logger `json:"logger"` // Logger specifies the logger for server. + LogPath string `json:"logPath"` // LogPath specifies the directory for storing logging files. + LogLevel string `json:"logLevel"` // LogLevel specifies the logging level for logger. + LogStdout bool `json:"logStdout"` // LogStdout specifies whether printing logging content to stdout. + ErrorStack bool `json:"errorStack"` // ErrorStack specifies whether logging stack information when error. + ErrorLogEnabled bool `json:"errorLogEnabled"` // ErrorLogEnabled enables error logging content to files. + ErrorLogPattern string `json:"errorLogPattern"` // ErrorLogPattern specifies the error log file pattern like: error-{Ymd}.log + AccessLogEnabled bool `json:"accessLogEnabled"` // AccessLogEnabled enables access logging content to files. + AccessLogPattern string `json:"accessLogPattern"` // AccessLogPattern specifies the error log file pattern like: access-{Ymd}.log // ================================== // PProf. // ================================== - PProfEnabled bool // PProfEnabled enables PProf feature. - PProfPattern string // PProfPattern specifies the PProf service pattern for router. + PProfEnabled bool `json:"pprofEnabled"` // PProfEnabled enables PProf feature. + PProfPattern string `json:"pprofPattern"` // PProfPattern specifies the PProf service pattern for router. // ================================== // Other. @@ -198,26 +198,26 @@ type ServerConfig struct { // ClientMaxBodySize specifies the max body size limit in bytes for client request. // It can be configured in configuration file using string like: 1m, 10m, 500kb etc. // It's 8MB in default. - ClientMaxBodySize int64 + ClientMaxBodySize int64 `json:"clientMaxBodySize"` // FormParsingMemory specifies max memory buffer size in bytes which can be used for // parsing multimedia form. // It can be configured in configuration file using string like: 1m, 10m, 500kb etc. // It's 1MB in default. - FormParsingMemory int64 + FormParsingMemory int64 `json:"formParsingMemory"` // NameToUriType specifies the type for converting struct method name to URI when // registering routes. - NameToUriType int + NameToUriType int `json:"nameToUriType"` // RouteOverWrite allows overwrite the route if duplicated. - RouteOverWrite bool + RouteOverWrite bool `json:"routeOverWrite"` // DumpRouterMap specifies whether automatically dumps router map when server starts. - DumpRouterMap bool + DumpRouterMap bool `json:"dumpRouterMap"` // Graceful enables graceful reload feature for all servers of the process. - Graceful bool + Graceful bool `json:"graceful"` } // Deprecated. Use NewConfig instead. diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index b51b75986..dd619d20e 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -21,25 +21,25 @@ import ( // Config is the configuration object for logger. type Config struct { - Writer io.Writer // Customized io.Writer. - Flags int // Extra flags for logging output features. - Path string // Logging directory path. - File string // Format for logging file. - Level int // Output level. - Prefix string // Prefix string for every logging content. - StSkip int // Skip count for stack. - StStatus int // Stack status(1: enabled - default; 0: disabled) - StFilter string // Stack string filter. - CtxKeys []interface{} // Context keys for logging, which is used for value retrieving from context. - HeaderPrint bool `c:"header"` // Print header or not(true in default). - StdoutPrint bool `c:"stdout"` // Output to stdout or not(true in default). - LevelPrefixes map[int]string // Logging level to its prefix string mapping. - RotateSize int64 // Rotate the logging file if its size > 0 in bytes. - RotateExpire time.Duration // Rotate the logging file if its mtime exceeds this duration. - RotateBackupLimit int // Max backup for rotated files, default is 0, means no backups. - RotateBackupExpire time.Duration // Max expire for rotated files, which is 0 in default, means no expiration. - RotateBackupCompress int // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression. - RotateCheckInterval time.Duration // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default. + Writer io.Writer `json:"-"` // Customized io.Writer. + Flags int `json:"flags"` // Extra flags for logging output features. + Path string `json:"path"` // Logging directory path. + File string `json:"file"` // Format for logging file. + Level int `json:"level"` // Output level. + Prefix string `json:"prefix"` // Prefix string for every logging content. + StSkip int `json:"stSkip"` // Skip count for stack. + StStatus int `json:"stStatus"` // Stack status(1: enabled - default; 0: disabled) + StFilter string `json:"stFilter"` // Stack string filter. + CtxKeys []interface{} `json:"ctxKeys"` // Context keys for logging, which is used for value retrieving from context. + HeaderPrint bool `json:"header"` // Print header or not(true in default). + StdoutPrint bool `json:"stdout"` // Output to stdout or not(true in default). + LevelPrefixes map[int]string `json:"levelPrefixes"` // Logging level to its prefix string mapping. + RotateSize int64 `json:"rotateSize"` // Rotate the logging file if its size > 0 in bytes. + RotateExpire time.Duration `json:"rotateExpire"` // Rotate the logging file if its mtime exceeds this duration. + RotateBackupLimit int `json:"rotateBackupLimit"` // Max backup for rotated files, default is 0, means no backups. + RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration. + RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression. + RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default. } // DefaultConfig returns the default configuration for logger. diff --git a/os/gview/gview_config.go b/os/gview/gview_config.go index 2404c2f56..508344a0a 100644 --- a/os/gview/gview_config.go +++ b/os/gview/gview_config.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -21,12 +21,12 @@ import ( // Config is the configuration object for template engine. type Config struct { - Paths []string // Searching array for path, NOT concurrent-safe for performance purpose. - Data map[string]interface{} // Global template variables including configuration. - DefaultFile string // Default template file for parsing. - Delimiters []string // Custom template delimiters. - AutoEncode bool // Automatically encodes and provides safe html output, which is good for avoiding XSS. - I18nManager *gi18n.Manager // I18n manager for the view. + Paths []string `json:"paths"` // Searching array for path, NOT concurrent-safe for performance purpose. + Data map[string]interface{} `json:"data"` // Global template variables including configuration. + DefaultFile string `json:"defaultFile"` // Default template file for parsing. + Delimiters []string `json:"delimiters"` // Custom template delimiters. + AutoEncode bool `json:"autoEncode"` // Automatically encodes and provides safe html output, which is good for avoiding XSS. + I18nManager *gi18n.Manager `json:"-"` // I18n manager for the view. } const ( From 73c68e48a126c0b11094b927f1c1a66eee02e276 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 12 Jan 2021 10:46:39 +0800 Subject: [PATCH 056/492] improve package gvalid --- net/ghttp/ghttp_server_pprof.go | 4 +- util/gvalid/gvalid.go | 133 +++++++++++++++++- util/gvalid/gvalid_validator.go | 31 ++++ ...lid_check.go => gvalid_validator_check.go} | 127 +++-------------- ...k_map.go => gvalid_validator_check_map.go} | 9 +- ...ct.go => gvalid_validator_check_struct.go} | 9 +- ...message.go => gvalid_validator_message.go} | 8 +- ...gth.go => gvalid_validator_rule_length.go} | 10 +- ..._luhn.go => gvalid_validator_rule_luhn.go} | 4 +- ...ange.go => gvalid_validator_rule_range.go} | 14 +- ...d.go => gvalid_validator_rule_required.go} | 4 +- ...o => gvalid_validator_rule_resident_id.go} | 4 +- ...est.go => gvalid_z_unit_basic_all_test.go} | 2 +- ...test.go => gvalid_z_unit_checkmap_test.go} | 2 +- ...t.go => gvalid_z_unit_checkstruct_test.go} | 2 +- ...t.go => gvalid_z_unit_custom_rule_test.go} | 2 +- ...t.go => gvalid_z_unit_customerror_test.go} | 2 +- ...test.go => gvalid_z_unit_internal_test.go} | 2 +- 18 files changed, 217 insertions(+), 152 deletions(-) create mode 100644 util/gvalid/gvalid_validator.go rename util/gvalid/{gvalid_check.go => gvalid_validator_check.go} (74%) rename util/gvalid/{gvalid_check_map.go => gvalid_validator_check_map.go} (93%) rename util/gvalid/{gvalid_check_struct.go => gvalid_validator_check_struct.go} (96%) rename util/gvalid/{gvalid_message.go => gvalid_validator_message.go} (94%) rename util/gvalid/{gvalid_rule_length.go => gvalid_validator_rule_length.go} (80%) rename util/gvalid/{gvalid_rule_luhn.go => gvalid_validator_rule_luhn.go} (82%) rename util/gvalid/{gvalid_rule_range.go => gvalid_validator_rule_range.go} (76%) rename util/gvalid/{gvalid_rule_required.go => gvalid_validator_rule_required.go} (93%) rename util/gvalid/{gvalid_rule_resident_id.go => gvalid_validator_rule_resident_id.go} (93%) rename util/gvalid/{gvalid_unit_basic_all_test.go => gvalid_z_unit_basic_all_test.go} (99%) rename util/gvalid/{gvalid_unit_checkmap_test.go => gvalid_z_unit_checkmap_test.go} (98%) rename util/gvalid/{gvalid_unit_checkstruct_test.go => gvalid_z_unit_checkstruct_test.go} (99%) rename util/gvalid/{gvalid_unit_custom_rule_test.go => gvalid_z_unit_custom_rule_test.go} (98%) rename util/gvalid/{gvalid_unit_customerror_test.go => gvalid_z_unit_customerror_test.go} (96%) rename util/gvalid/{gvalid_unit_internal_test.go => gvalid_z_unit_internal_test.go} (94%) diff --git a/net/ghttp/ghttp_server_pprof.go b/net/ghttp/ghttp_server_pprof.go index 27179030b..2cde7ab6e 100644 --- a/net/ghttp/ghttp_server_pprof.go +++ b/net/ghttp/ghttp_server_pprof.go @@ -18,7 +18,7 @@ import ( type utilPProf struct{} const ( - gDEFAULT_PPROF_PATTERN = "/debug/pprof" + defaultPProfPattern = "/debug/pprof" ) // EnablePProf enables PProf feature for server. @@ -28,7 +28,7 @@ func (s *Server) EnablePProf(pattern ...string) { // EnablePProf enables PProf feature for server of specified domain. func (d *Domain) EnablePProf(pattern ...string) { - p := gDEFAULT_PPROF_PATTERN + p := defaultPProfPattern if len(pattern) > 0 && pattern[0] != "" { p = pattern[0] } diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 59a45dec4..136b37d87 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -8,6 +8,7 @@ package gvalid import ( + "regexp" "strings" "github.com/gogf/gf/text/gregex" @@ -62,6 +63,136 @@ import ( // like: map[field] => string|map[rule]string type CustomMsg = map[string]interface{} +const ( + // regular expression pattern for single validation rule. + singleRulePattern = `^([\w-]+):{0,1}(.*)` + invalidRulesErrKey = "invalid_rules" + invalidParamsErrKey = "invalid_params" + invalidObjectErrKey = "invalid_object" +) + +var ( + // defaultValidator is the default validator for package functions. + defaultValidator = New() + + // all internal error keys. + internalErrKeyMap = map[string]string{ + invalidRulesErrKey: invalidRulesErrKey, + invalidParamsErrKey: invalidParamsErrKey, + invalidObjectErrKey: invalidObjectErrKey, + } + // regular expression object for single rule + // which is compiled just once and of repeatable usage. + ruleRegex, _ = regexp.Compile(singleRulePattern) + + // mustCheckRulesEvenValueEmpty specifies some rules that must be validated + // even the value is empty (nil or empty). + mustCheckRulesEvenValueEmpty = map[string]struct{}{ + "required": {}, + "required-if": {}, + "required-unless": {}, + "required-with": {}, + "required-with-all": {}, + "required-without": {}, + "required-without-all": {}, + //"same": {}, + //"different": {}, + //"in": {}, + //"not-in": {}, + //"regex": {}, + } + // allSupportedRules defines all supported rules that is used for quick checks. + allSupportedRules = map[string]struct{}{ + "required": {}, + "required-if": {}, + "required-unless": {}, + "required-with": {}, + "required-with-all": {}, + "required-without": {}, + "required-without-all": {}, + "date": {}, + "date-format": {}, + "email": {}, + "phone": {}, + "phone-loose": {}, + "telephone": {}, + "passport": {}, + "password": {}, + "password2": {}, + "password3": {}, + "postcode": {}, + "resident-id": {}, + "bank-card": {}, + "qq": {}, + "ip": {}, + "ipv4": {}, + "ipv6": {}, + "mac": {}, + "url": {}, + "domain": {}, + "length": {}, + "min-length": {}, + "max-length": {}, + "between": {}, + "min": {}, + "max": {}, + "json": {}, + "integer": {}, + "float": {}, + "boolean": {}, + "same": {}, + "different": {}, + "in": {}, + "not-in": {}, + "regex": {}, + } + // boolMap defines the boolean values. + boolMap = map[string]struct{}{ + "1": {}, + "true": {}, + "on": {}, + "yes": {}, + "": {}, + "0": {}, + "false": {}, + "off": {}, + "no": {}, + } +) + +// Check checks single value with specified rules. +// It returns nil if successful validation. +// +// The parameter can be any type of variable, which will be converted to string +// for validation. +// The parameter can be one or more rules, multiple rules joined using char '|'. +// The parameter specifies the custom error messages, which can be type of: +// string/map/struct/*struct. +// The optional parameter specifies the extra validation parameters for some rules +// like: required-*、same、different, etc. +func Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error { + return defaultValidator.Check(value, rules, messages, params...) +} + +// CheckMap validates map and returns the error result. It returns nil if with successful validation. +// +// The parameter can be type of []string/map[string]string. It supports sequence in error result +// if is type of []string. +// The optional parameter specifies the custom error messages for specified keys and rules. +func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error { + return defaultValidator.CheckMap(params, rules, messages...) +} + +// CheckStruct validates strcut and returns the error result. +// +// The parameter should be type of struct/*struct. +// The parameter can be type of []string/map[string]string. It supports sequence in error result +// if is type of []string. +// The optional parameter specifies the custom error messages for specified keys and rules. +func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error { + return defaultValidator.CheckStruct(object, rules, messages...) +} + // parseSequenceTag parses one sequence tag to field, rule and error message. // The sequence tag is like: [alias@]rule[...#msg...] func parseSequenceTag(tag string) (field, rule, msg string) { diff --git a/util/gvalid/gvalid_validator.go b/util/gvalid/gvalid_validator.go new file mode 100644 index 000000000..49c8505aa --- /dev/null +++ b/util/gvalid/gvalid_validator.go @@ -0,0 +1,31 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gvalid + +// Validator is the validation manager. +type Validator struct { + i18nLang string // I18n language. +} + +// New creates and returns a new Validator. +func New() *Validator { + return &Validator{} +} + +// Clone creates and returns a new Validator which is a shallow copy of current one. +func (v *Validator) Clone() *Validator { + newValidator := New() + *newValidator = *v + return newValidator +} + +// I18n is a chaining operation function which sets the I18n language for next validation. +func (v *Validator) I18n(language string) *Validator { + newValidator := v.Clone() + newValidator.i18nLang = language + return newValidator +} diff --git a/util/gvalid/gvalid_check.go b/util/gvalid/gvalid_validator_check.go similarity index 74% rename from util/gvalid/gvalid_check.go rename to util/gvalid/gvalid_validator_check.go index 99e5c327f..035276256 100644 --- a/util/gvalid/gvalid_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -14,105 +14,10 @@ import ( "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/util/gconv" - "regexp" "strconv" "strings" ) -const ( - // regular expression pattern for single validation rule. - singleRulePattern = `^([\w-]+):{0,1}(.*)` - invalidRulesErrKey = "invalid_rules" - invalidParamsErrKey = "invalid_params" - invalidObjectErrKey = "invalid_object" -) - -var ( - // all internal error keys. - internalErrKeyMap = map[string]string{ - invalidRulesErrKey: invalidRulesErrKey, - invalidParamsErrKey: invalidParamsErrKey, - invalidObjectErrKey: invalidObjectErrKey, - } - // regular expression object for single rule - // which is compiled just once and of repeatable usage. - ruleRegex, _ = regexp.Compile(singleRulePattern) - - // mustCheckRulesEvenValueEmpty specifies some rules that must be validated - // even the value is empty (nil or empty). - mustCheckRulesEvenValueEmpty = map[string]struct{}{ - "required": {}, - "required-if": {}, - "required-unless": {}, - "required-with": {}, - "required-with-all": {}, - "required-without": {}, - "required-without-all": {}, - //"same": {}, - //"different": {}, - //"in": {}, - //"not-in": {}, - //"regex": {}, - } - // allSupportedRules defines all supported rules that is used for quick checks. - allSupportedRules = map[string]struct{}{ - "required": {}, - "required-if": {}, - "required-unless": {}, - "required-with": {}, - "required-with-all": {}, - "required-without": {}, - "required-without-all": {}, - "date": {}, - "date-format": {}, - "email": {}, - "phone": {}, - "phone-loose": {}, - "telephone": {}, - "passport": {}, - "password": {}, - "password2": {}, - "password3": {}, - "postcode": {}, - "resident-id": {}, - "bank-card": {}, - "qq": {}, - "ip": {}, - "ipv4": {}, - "ipv6": {}, - "mac": {}, - "url": {}, - "domain": {}, - "length": {}, - "min-length": {}, - "max-length": {}, - "between": {}, - "min": {}, - "max": {}, - "json": {}, - "integer": {}, - "float": {}, - "boolean": {}, - "same": {}, - "different": {}, - "in": {}, - "not-in": {}, - "regex": {}, - } - // boolMap defines the boolean values. - boolMap = map[string]struct{}{ - "1": {}, - "true": {}, - "on": {}, - "yes": {}, - "": {}, - "0": {}, - "false": {}, - "off": {}, - "no": {}, - } -) - // Check checks single value with specified rules. // It returns nil if successful validation. // @@ -123,12 +28,12 @@ var ( // string/map/struct/*struct. // The optional parameter specifies the extra validation parameters for some rules // like: required-*、same、different, etc. -func Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error { - return doCheck("", value, rules, messages, params...) +func (v *Validator) Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error { + return v.doCheck("", value, rules, messages, params...) } // doCheck does the really rules validation for single key-value. -func doCheck(key string, value interface{}, rules string, messages interface{}, params ...interface{}) *Error { +func (v *Validator) doCheck(key string, value interface{}, rules string, messages interface{}, params ...interface{}) *Error { // If there's no validation rules, it does nothing and returns quickly. if rules == "" { return nil @@ -196,7 +101,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{}, // It checks custom validation rules with most priority. var ( dataMap map[string]interface{} - message = getErrorMessageByRule(ruleKey, customMsgMap) + message = v.getErrorMessageByRule(ruleKey, customMsgMap) ) if len(params) > 0 { dataMap = gconv.Map(params[0]) @@ -209,7 +114,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{}, } } else { // It checks build-in validation rules if there's no custom rule. - match, err = doCheckBuildInRules(index, value, ruleKey, rulePattern, ruleItems, data, customMsgMap) + match, err = v.doCheckBuildInRules(index, value, ruleKey, rulePattern, ruleItems, data, customMsgMap) if !match && err != nil { errorMsgArray[ruleKey] = err.Error() } @@ -220,7 +125,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{}, // It does nothing if the error message for this rule // is already set in previous validation. if _, ok := errorMsgArray[ruleKey]; !ok { - errorMsgArray[ruleKey] = getErrorMessageByRule(ruleKey, customMsgMap) + errorMsgArray[ruleKey] = v.getErrorMessageByRule(ruleKey, customMsgMap) } } index++ @@ -233,7 +138,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{}, return nil } -func doCheckBuildInRules( +func (v *Validator) doCheckBuildInRules( index int, value interface{}, ruleKey string, @@ -253,7 +158,7 @@ func doCheckBuildInRules( "required-with-all", "required-without", "required-without-all": - match = checkRequired(valueStr, ruleKey, rulePattern, dataMap) + match = v.checkRequired(valueStr, ruleKey, rulePattern, dataMap) // Length rules. // It also supports length of unicode string. @@ -261,7 +166,7 @@ func doCheckBuildInRules( "length", "min-length", "max-length": - if msg := checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { + if msg := v.checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { return match, errors.New(msg) } else { match = true @@ -272,7 +177,7 @@ func doCheckBuildInRules( "min", "max", "between": - if msg := checkRange(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { + if msg := v.checkRange(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { return match, errors.New(msg) } else { match = true @@ -308,7 +213,7 @@ func doCheckBuildInRules( match = true } else { var msg string - msg = getErrorMessageByRule(ruleKey, customMsgMap) + msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":format", rulePattern, -1) return match, errors.New(msg) } @@ -322,7 +227,7 @@ func doCheckBuildInRules( } if !match { var msg string - msg = getErrorMessageByRule(ruleKey, customMsgMap) + msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":field", rulePattern, -1) return match, errors.New(msg) } @@ -337,7 +242,7 @@ func doCheckBuildInRules( } if !match { var msg string - msg = getErrorMessageByRule(ruleKey, customMsgMap) + msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":field", rulePattern, -1) return match, errors.New(msg) } @@ -430,11 +335,11 @@ func doCheckBuildInRules( // 总: // (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$) case "resident-id": - match = checkResidentId(valueStr) + match = v.checkResidentId(valueStr) // Bank card number using LUHN algorithm. case "bank-card": - match = checkLuHn(valueStr) + match = v.checkLuHn(valueStr) // Universal passport format rule: // Starting with letter, containing only numbers or underscores, length between 6 and 18. diff --git a/util/gvalid/gvalid_check_map.go b/util/gvalid/gvalid_validator_check_map.go similarity index 93% rename from util/gvalid/gvalid_check_map.go rename to util/gvalid/gvalid_validator_check_map.go index ca5a754f8..195253743 100644 --- a/util/gvalid/gvalid_check_map.go +++ b/util/gvalid/gvalid_validator_check_map.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -7,9 +7,8 @@ package gvalid import ( - "strings" - "github.com/gogf/gf/util/gconv" + "strings" ) // CheckMap validates map and returns the error result. It returns nil if with successful validation. @@ -17,7 +16,7 @@ import ( // The parameter can be type of []string/map[string]string. It supports sequence in error result // if is type of []string. // The optional parameter specifies the custom error messages for specified keys and rules. -func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error { +func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error { // If there's no validation rules, it does nothing and returns quickly. if params == nil || rules == nil { return nil @@ -96,7 +95,7 @@ func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Err value = v } // It checks each rule and its value in loop. - if e := doCheck(key, value, rule, customMsgs[key], data); e != nil { + if e := v.doCheck(key, value, rule, customMsgs[key], data); e != nil { _, item := e.FirstItem() // =========================================================== // Only in map and struct validations, if value is nil or empty diff --git a/util/gvalid/gvalid_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go similarity index 96% rename from util/gvalid/gvalid_check_struct.go rename to util/gvalid/gvalid_validator_check_struct.go index 69fee0430..9fe1b105d 100644 --- a/util/gvalid/gvalid_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -7,10 +7,9 @@ package gvalid import ( - "strings" - "github.com/gogf/gf/internal/structs" "github.com/gogf/gf/util/gconv" + "strings" ) var ( @@ -24,7 +23,7 @@ var ( // The parameter can be type of []string/map[string]string. It supports sequence in error result // if is type of []string. // The optional parameter specifies the custom error messages for specified keys and rules. -func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error { +func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error { // It here must use structs.TagFields not structs.MapField to ensure error sequence. tagField, err := structs.TagFields(object, structTagPriority) if err != nil { @@ -166,7 +165,7 @@ func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) * value = v } // It checks each rule and its value in loop. - if e := doCheck(key, value, rule, customMessage[key], params); e != nil { + if e := v.doCheck(key, value, rule, customMessage[key], params); e != nil { _, item := e.FirstItem() // =========================================================== // Only in map and struct validations, if value is nil or empty diff --git a/util/gvalid/gvalid_message.go b/util/gvalid/gvalid_validator_message.go similarity index 94% rename from util/gvalid/gvalid_message.go rename to util/gvalid/gvalid_validator_message.go index c3b6e6c2b..56bf17251 100644 --- a/util/gvalid/gvalid_message.go +++ b/util/gvalid/gvalid_validator_message.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -63,18 +63,18 @@ var defaultMessages = map[string]string{ // getErrorMessageByRule retrieves and returns the error message for specified rule. // It firstly retrieves the message from custom message map, and then checks i18n manager, // it returns the default error message if it's not found in custom message map or i18n manager. -func getErrorMessageByRule(ruleKey string, customMsgMap map[string]string) string { +func (v *Validator) getErrorMessageByRule(ruleKey string, customMsgMap map[string]string) string { content := customMsgMap[ruleKey] if content != "" { return content } - content = gi18n.GetContent(fmt.Sprintf(`gf.gvalid.rule.%s`, ruleKey)) + content = gi18n.GetContent(fmt.Sprintf(`gf.gvalid.rule.%s`, ruleKey), v.i18nLang) if content == "" { content = defaultMessages[ruleKey] } // If there's no configured rule message, it uses default one. if content == "" { - content = gi18n.GetContent(`gf.gvalid.rule.__default__`) + content = gi18n.GetContent(`gf.gvalid.rule.__default__`, v.i18nLang) if content == "" { content = defaultMessages["__default__"] } diff --git a/util/gvalid/gvalid_rule_length.go b/util/gvalid/gvalid_validator_rule_length.go similarity index 80% rename from util/gvalid/gvalid_rule_length.go rename to util/gvalid/gvalid_validator_rule_length.go index 7d67284f8..df0f36189 100644 --- a/util/gvalid/gvalid_rule_length.go +++ b/util/gvalid/gvalid_validator_rule_length.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -15,7 +15,7 @@ import ( // checkLength checks using length rules. // The length is calculated using unicode string, which means one chinese character or letter // both has the length of 1. -func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) string { +func (v *Validator) checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) string { var ( msg = "" runeArray = gconv.Runes(value) @@ -39,7 +39,7 @@ func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) } } if valueLen < min || valueLen > max { - msg = getErrorMessageByRule(ruleKey, customMsgMap) + msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1) msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1) return msg @@ -48,14 +48,14 @@ func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) case "min-length": min, err := strconv.Atoi(ruleVal) if valueLen < min || err != nil { - msg = getErrorMessageByRule(ruleKey, customMsgMap) + msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1) } case "max-length": max, err := strconv.Atoi(ruleVal) if valueLen > max || err != nil { - msg = getErrorMessageByRule(ruleKey, customMsgMap) + msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1) } } diff --git a/util/gvalid/gvalid_rule_luhn.go b/util/gvalid/gvalid_validator_rule_luhn.go similarity index 82% rename from util/gvalid/gvalid_rule_luhn.go rename to util/gvalid/gvalid_validator_rule_luhn.go index ed8983dae..d6eff5fd1 100644 --- a/util/gvalid/gvalid_rule_luhn.go +++ b/util/gvalid/gvalid_validator_rule_luhn.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -8,7 +8,7 @@ package gvalid // checkLuHn checks with LUHN algorithm. // It's usually used for bank card number validation. -func checkLuHn(value string) bool { +func (v *Validator) checkLuHn(value string) bool { var ( sum = 0 nDigits = len(value) diff --git a/util/gvalid/gvalid_rule_range.go b/util/gvalid/gvalid_validator_rule_range.go similarity index 76% rename from util/gvalid/gvalid_rule_range.go rename to util/gvalid/gvalid_validator_rule_range.go index 1993f2ac8..7730cd610 100644 --- a/util/gvalid/gvalid_rule_range.go +++ b/util/gvalid/gvalid_validator_rule_range.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -12,7 +12,7 @@ import ( ) // checkRange checks using range rules. -func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) string { +func (v *Validator) checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) string { msg := "" switch ruleKey { // Value range. @@ -30,9 +30,9 @@ func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) max = v } } - v, err := strconv.ParseFloat(value, 10) - if v < min || v > max || err != nil { - msg = getErrorMessageByRule(ruleKey, customMsgMap) + valueF, err := strconv.ParseFloat(value, 10) + if valueF < min || valueF > max || err != nil { + msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1) msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1) } @@ -44,7 +44,7 @@ func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) valueN, err2 = strconv.ParseFloat(value, 10) ) if valueN < min || err1 != nil || err2 != nil { - msg = getErrorMessageByRule(ruleKey, customMsgMap) + msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1) } @@ -55,7 +55,7 @@ func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) valueN, err2 = strconv.ParseFloat(value, 10) ) if valueN > max || err1 != nil || err2 != nil { - msg = getErrorMessageByRule(ruleKey, customMsgMap) + msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1) } diff --git a/util/gvalid/gvalid_rule_required.go b/util/gvalid/gvalid_validator_rule_required.go similarity index 93% rename from util/gvalid/gvalid_rule_required.go rename to util/gvalid/gvalid_validator_rule_required.go index c92456788..0578e3212 100644 --- a/util/gvalid/gvalid_rule_required.go +++ b/util/gvalid/gvalid_validator_rule_required.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -11,7 +11,7 @@ import ( ) // checkRequired checks using required rules. -func checkRequired(value, ruleKey, ruleVal string, params map[string]string) bool { +func (v *Validator) checkRequired(value, ruleKey, ruleVal string, params map[string]string) bool { required := false switch ruleKey { // Required. diff --git a/util/gvalid/gvalid_rule_resident_id.go b/util/gvalid/gvalid_validator_rule_resident_id.go similarity index 93% rename from util/gvalid/gvalid_rule_resident_id.go rename to util/gvalid/gvalid_validator_rule_resident_id.go index b43cc952d..8a83e949b 100644 --- a/util/gvalid/gvalid_rule_resident_id.go +++ b/util/gvalid/gvalid_validator_rule_resident_id.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -32,7 +32,7 @@ import ( // // 总: // (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$) -func checkResidentId(id string) bool { +func (v *Validator) checkResidentId(id string) bool { id = strings.ToUpper(strings.TrimSpace(id)) if len(id) != 18 { return false diff --git a/util/gvalid/gvalid_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go similarity index 99% rename from util/gvalid/gvalid_unit_basic_all_test.go rename to util/gvalid/gvalid_z_unit_basic_all_test.go index b1aea6849..81861aa14 100755 --- a/util/gvalid/gvalid_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gvalid/gvalid_unit_checkmap_test.go b/util/gvalid/gvalid_z_unit_checkmap_test.go similarity index 98% rename from util/gvalid/gvalid_unit_checkmap_test.go rename to util/gvalid/gvalid_z_unit_checkmap_test.go index e29d9104a..4660ebf21 100755 --- a/util/gvalid/gvalid_unit_checkmap_test.go +++ b/util/gvalid/gvalid_z_unit_checkmap_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gvalid/gvalid_unit_checkstruct_test.go b/util/gvalid/gvalid_z_unit_checkstruct_test.go similarity index 99% rename from util/gvalid/gvalid_unit_checkstruct_test.go rename to util/gvalid/gvalid_z_unit_checkstruct_test.go index 3e9f3aae5..4f630acae 100755 --- a/util/gvalid/gvalid_unit_checkstruct_test.go +++ b/util/gvalid/gvalid_z_unit_checkstruct_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gvalid/gvalid_unit_custom_rule_test.go b/util/gvalid/gvalid_z_unit_custom_rule_test.go similarity index 98% rename from util/gvalid/gvalid_unit_custom_rule_test.go rename to util/gvalid/gvalid_z_unit_custom_rule_test.go index b6ec82d69..2fdc36a92 100644 --- a/util/gvalid/gvalid_unit_custom_rule_test.go +++ b/util/gvalid/gvalid_z_unit_custom_rule_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gvalid/gvalid_unit_customerror_test.go b/util/gvalid/gvalid_z_unit_customerror_test.go similarity index 96% rename from util/gvalid/gvalid_unit_customerror_test.go rename to util/gvalid/gvalid_z_unit_customerror_test.go index ef76bb6bc..9f73afa4b 100755 --- a/util/gvalid/gvalid_unit_customerror_test.go +++ b/util/gvalid/gvalid_z_unit_customerror_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gvalid/gvalid_unit_internal_test.go b/util/gvalid/gvalid_z_unit_internal_test.go similarity index 94% rename from util/gvalid/gvalid_unit_internal_test.go rename to util/gvalid/gvalid_z_unit_internal_test.go index 6a68cd6f2..89d38dcd6 100644 --- a/util/gvalid/gvalid_unit_internal_test.go +++ b/util/gvalid/gvalid_z_unit_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, From 14536e9abcc6e063f0d1000d3935f396c6a822be Mon Sep 17 00:00:00 2001 From: eyasliu Date: Tue, 12 Jan 2021 18:08:50 +0800 Subject: [PATCH 057/492] add http client middleware feature --- net/ghttp/ghttp_client_config.go | 1 + net/ghttp/ghttp_client_middleware.go | 72 +++++++++++++++++++++++++++ net/ghttp/ghttp_client_request.go | 60 ++++++++++++++++++----- net/ghttp/ghttp_unit_client_test.go | 73 ++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 net/ghttp/ghttp_client_middleware.go diff --git a/net/ghttp/ghttp_client_config.go b/net/ghttp/ghttp_client_config.go index 207cfe868..2710ecb01 100644 --- a/net/ghttp/ghttp_client_config.go +++ b/net/ghttp/ghttp_client_config.go @@ -33,6 +33,7 @@ type Client struct { browserMode bool // Whether auto saving and sending cookie content. retryCount int // Retry count when request fails. retryInterval time.Duration // Retry interval when request fails. + middlewareHandler []ClientHandlerFunc // Interceptor handlers } // NewClient creates and returns a new HTTP client object. diff --git a/net/ghttp/ghttp_client_middleware.go b/net/ghttp/ghttp_client_middleware.go new file mode 100644 index 000000000..b8f7ede3b --- /dev/null +++ b/net/ghttp/ghttp_client_middleware.go @@ -0,0 +1,72 @@ +package ghttp + +import ( + "github.com/gogf/gf/errors/gerror" + "net/http" +) + +const gfHttpClientMiddlewareKey = "__gfHttpClientMiddlewareKey" + +var gfHttpClientMiddlewareAbort = gerror.New("http request abort") + +// Use Add middleware to client +func (c *Client) Use(handlers ...ClientHandlerFunc) *Client { + newClient := c + if c.parent == nil { + newClient = c.Clone() + } + + newClient.middlewareHandler = append(newClient.middlewareHandler, handlers...) + return newClient +} + +// MiddlewareNext call next middleware +// this is should only be call in ClientHandlerFunc +func (c *Client) MiddlewareNext(req *http.Request) (*ClientResponse, error) { + m, ok := req.Context().Value(gfHttpClientMiddlewareKey).(*clientMiddleware) + if ok { + resp, err := m.Next(c, req) + return resp, err + } + return c.callRequest(req) +} + +// MiddlewareAbort stop call after all middleware, so it will not send http request +// this is should only be call in ClientHandlerFunc +func (c *Client) MiddlewareAbort(req *http.Request) (*ClientResponse, error) { + m := req.Context().Value(gfHttpClientMiddlewareKey).(*clientMiddleware) + m.Abort() + return m.resp, m.err +} + +type ClientHandlerFunc = func(c *Client, r *http.Request) (*ClientResponse, error) + +// clientMiddleware is the plugin for http client request workflow management. +type clientMiddleware struct { + handlers []ClientHandlerFunc // mdl handlers + handlerIndex int // current handler index + abort bool // abort call after handlers + resp *ClientResponse // save resp + err error // save err +} + +// Next call next middleware handler, if abort, +func (m *clientMiddleware) Next(c *Client, req *http.Request) (resp *ClientResponse, err error) { + if m.abort { + return m.resp, m.err + } + if m.handlerIndex < len(m.handlers) { + m.handlerIndex++ + resp, err = m.handlers[m.handlerIndex](c, req) + m.resp = resp + m.err = err + } + return +} + +func (m *clientMiddleware) Abort() { + m.abort = true + if m.err == nil { + m.err = gfHttpClientMiddlewareAbort + } +} diff --git a/net/ghttp/ghttp_client_request.go b/net/ghttp/ghttp_client_request.go index ceb6007ef..02c3849b3 100644 --- a/net/ghttp/ghttp_client_request.go +++ b/net/ghttp/ghttp_client_request.go @@ -8,6 +8,7 @@ package ghttp import ( "bytes" + "context" "errors" "fmt" "github.com/gogf/gf/internal/json" @@ -82,14 +83,8 @@ func (c *Client) Trace(url string, data ...interface{}) (*ClientResponse, error) return c.DoRequest("TRACE", url, data...) } -// DoRequest sends request with given HTTP method and data and returns the response object. -// Note that the response object MUST be closed if it'll be never used. -// -// Note that it uses "multipart/form-data" as its Content-Type if it contains file uploading, -// else it uses "application/x-www-form-urlencoded". It also automatically detects the post -// content for JSON format, and for that it automatically sets the Content-Type as -// "application/json". -func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *ClientResponse, err error) { +// prepareRequest verify params and return http request +func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *http.Request, err error) { method = strings.ToUpper(method) if len(c.prefix) > 0 { url = c.prefix + gstr.Trim(url) @@ -123,7 +118,6 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien param = BuildParams(data[0]) } } - var req *http.Request if method == "GET" { // It appends the parameters to the url if http method is GET. if param != "" { @@ -203,6 +197,8 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien // Context. if c.ctx != nil { req = req.WithContext(c.ctx) + } else { + req = req.WithContext(context.Background()) } // Custom header. if len(c.header) > 0 { @@ -232,6 +228,11 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien if len(c.authUser) > 0 { req.SetBasicAuth(c.authUser, c.authPass) } + return req, nil +} + +// callRequest send http request, return *ClientResponse and error +func (c *Client) callRequest(req *http.Request) (resp *ClientResponse, err error) { resp = &ClientResponse{ request: req, } @@ -250,12 +251,49 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien c.retryCount-- time.Sleep(c.retryInterval) } else { - return resp, err + //return resp, err + break } } else { break } } + return resp, err +} + +// DoRequest sends request with given HTTP method and data and returns the response object. +// Note that the response object MUST be closed if it'll be never used. +// +// Note that it uses "multipart/form-data" as its Content-Type if it contains file uploading, +// else it uses "application/x-www-form-urlencoded". It also automatically detects the post +// content for JSON format, and for that it automatically sets the Content-Type as +// "application/json". +func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *ClientResponse, err error) { + req, err := c.prepareRequest(method, url, data...) + if err != nil { + return nil, err + } + + if len(c.middlewareHandler) > 0 { + mdlHandlers := make([]ClientHandlerFunc, 0, len(c.middlewareHandler) + 1) + mdlHandlers = append(mdlHandlers, c.middlewareHandler...) + + // last call internal handler + mdlHandlers = append(mdlHandlers, func(cli *Client, r *http.Request) (*ClientResponse, error) { + return cli.callRequest(r) + }) + + // call middleware + ctx := context.WithValue(req.Context(), gfHttpClientMiddlewareKey, &clientMiddleware{ + handlers: mdlHandlers, + handlerIndex: -1, + }) + req = req.WithContext(ctx) + resp, err = c.MiddlewareNext(req) + } else { + resp, err = c.callRequest(req) + } + // Auto saving cookie content. if c.browserMode { @@ -268,5 +306,5 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien } } } - return resp, nil + return resp, err } diff --git a/net/ghttp/ghttp_unit_client_test.go b/net/ghttp/ghttp_unit_client_test.go index e48340209..d8137eac0 100644 --- a/net/ghttp/ghttp_unit_client_test.go +++ b/net/ghttp/ghttp_unit_client_test.go @@ -7,11 +7,14 @@ package ghttp_test import ( + "bytes" "context" "fmt" "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/util/guid" + "io/ioutil" + "net/http" "testing" "time" @@ -332,3 +335,73 @@ func Test_Client_File_And_Param(t *testing.T) { t.Assert(c.PostContent("/", data), data["json"].(string)+gfile.GetContents(path)) }) } + +func Test_Client_Middleware(t *testing.T) { + p, _ := ports.PopRand() + s := g.Server(p) + isServerHandler := false + respStr := "test resp str" + s.BindHandler("/", func(r *ghttp.Request) { + isServerHandler = true + r.Response.Write(respStr) + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + + gtest.C(t, func(t *gtest.T) { + str := "" + str2 := "resp body" + c := ghttp.NewClient().SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { + str += "a" + resp, err = c.MiddlewareNext(r) + str += "b" + return + }).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { + str += "c" + resp, err = c.MiddlewareNext(r) + str += "d" + return + }).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { + str += "e" + resp, err = c.MiddlewareNext(r) + resp.Response.Body = ioutil.NopCloser(bytes.NewBufferString(str2)) + str += "f" + return + }) + + resp, err := c.Get("/") + t.Assert(str, "acefdb") + t.Assert(err, nil) + t.Assert(resp.ReadAllString(), str2) + t.Assert(isServerHandler, true) + + // test abort, abort will not send + str3 := "" + c = ghttp.NewClient().SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { + str3 += "a" + resp, err = c.MiddlewareNext(r) + str3 += "b" + return + }).Use(func(c *ghttp.Client, r *http.Request) (*ghttp.ClientResponse, error) { + str3 += "c" + resp, err := c.MiddlewareAbort(r) + str3 += "d" + resp, err = c.MiddlewareNext(r) + str3 += "e" + return resp, err + }).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { + str3 += "f" + resp, err = c.MiddlewareNext(r) + str3 += "g" + return + }) + resp, err = c.Get("/") + t.Assert(str3, "acdeb") + t.Assert(err.Error(), "http request abort") + t.Assert(resp, nil) + }) +} \ No newline at end of file From c07c74bf1b4e3ee9771da4557238840b049f34c8 Mon Sep 17 00:00:00 2001 From: eyasliu Date: Tue, 12 Jan 2021 19:01:33 +0800 Subject: [PATCH 058/492] fix lint --- net/ghttp/ghttp_client_middleware.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/ghttp/ghttp_client_middleware.go b/net/ghttp/ghttp_client_middleware.go index b8f7ede3b..b775f45f6 100644 --- a/net/ghttp/ghttp_client_middleware.go +++ b/net/ghttp/ghttp_client_middleware.go @@ -5,9 +5,9 @@ import ( "net/http" ) -const gfHttpClientMiddlewareKey = "__gfHttpClientMiddlewareKey" +const gfHTTPClientMiddlewareKey = "__gfHttpClientMiddlewareKey" -var gfHttpClientMiddlewareAbort = gerror.New("http request abort") +var gfHTTPClientMiddlewareAbort = gerror.New("http request abort") // Use Add middleware to client func (c *Client) Use(handlers ...ClientHandlerFunc) *Client { @@ -23,7 +23,7 @@ func (c *Client) Use(handlers ...ClientHandlerFunc) *Client { // MiddlewareNext call next middleware // this is should only be call in ClientHandlerFunc func (c *Client) MiddlewareNext(req *http.Request) (*ClientResponse, error) { - m, ok := req.Context().Value(gfHttpClientMiddlewareKey).(*clientMiddleware) + m, ok := req.Context().Value(gfHTTPClientMiddlewareKey).(*clientMiddleware) if ok { resp, err := m.Next(c, req) return resp, err @@ -34,11 +34,12 @@ func (c *Client) MiddlewareNext(req *http.Request) (*ClientResponse, error) { // MiddlewareAbort stop call after all middleware, so it will not send http request // this is should only be call in ClientHandlerFunc func (c *Client) MiddlewareAbort(req *http.Request) (*ClientResponse, error) { - m := req.Context().Value(gfHttpClientMiddlewareKey).(*clientMiddleware) + m := req.Context().Value(gfHTTPClientMiddlewareKey).(*clientMiddleware) m.Abort() return m.resp, m.err } +// ClientHandlerFunc middleware handler func type ClientHandlerFunc = func(c *Client, r *http.Request) (*ClientResponse, error) // clientMiddleware is the plugin for http client request workflow management. @@ -67,6 +68,6 @@ func (m *clientMiddleware) Next(c *Client, req *http.Request) (resp *ClientRespo func (m *clientMiddleware) Abort() { m.abort = true if m.err == nil { - m.err = gfHttpClientMiddlewareAbort + m.err = gfHTTPClientMiddlewareAbort } } From f9905f2bb5cacffcba336904a4d767fa0d835c02 Mon Sep 17 00:00:00 2001 From: eyasliu Date: Tue, 12 Jan 2021 19:09:45 +0800 Subject: [PATCH 059/492] gofmt code --- net/ghttp/ghttp_client_config.go | 22 +++++++++++----------- net/ghttp/ghttp_client_request.go | 5 ++--- net/ghttp/ghttp_unit_client_test.go | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/net/ghttp/ghttp_client_config.go b/net/ghttp/ghttp_client_config.go index 2710ecb01..06e3e7ea7 100644 --- a/net/ghttp/ghttp_client_config.go +++ b/net/ghttp/ghttp_client_config.go @@ -22,17 +22,17 @@ import ( // Client is the HTTP client for HTTP request management. type Client struct { - http.Client // Underlying HTTP Client. - ctx context.Context // Context for each request. - parent *Client // Parent http client, this is used for chaining operations. - header map[string]string // Custom header map. - cookies map[string]string // Custom cookie map. - prefix string // Prefix for request. - authUser string // HTTP basic authentication: user. - authPass string // HTTP basic authentication: pass. - browserMode bool // Whether auto saving and sending cookie content. - retryCount int // Retry count when request fails. - retryInterval time.Duration // Retry interval when request fails. + http.Client // Underlying HTTP Client. + ctx context.Context // Context for each request. + parent *Client // Parent http client, this is used for chaining operations. + header map[string]string // Custom header map. + cookies map[string]string // Custom cookie map. + prefix string // Prefix for request. + authUser string // HTTP basic authentication: user. + authPass string // HTTP basic authentication: pass. + browserMode bool // Whether auto saving and sending cookie content. + retryCount int // Retry count when request fails. + retryInterval time.Duration // Retry interval when request fails. middlewareHandler []ClientHandlerFunc // Interceptor handlers } diff --git a/net/ghttp/ghttp_client_request.go b/net/ghttp/ghttp_client_request.go index 02c3849b3..9706af4db 100644 --- a/net/ghttp/ghttp_client_request.go +++ b/net/ghttp/ghttp_client_request.go @@ -275,7 +275,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien } if len(c.middlewareHandler) > 0 { - mdlHandlers := make([]ClientHandlerFunc, 0, len(c.middlewareHandler) + 1) + mdlHandlers := make([]ClientHandlerFunc, 0, len(c.middlewareHandler)+1) mdlHandlers = append(mdlHandlers, c.middlewareHandler...) // last call internal handler @@ -284,7 +284,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien }) // call middleware - ctx := context.WithValue(req.Context(), gfHttpClientMiddlewareKey, &clientMiddleware{ + ctx := context.WithValue(req.Context(), gfHTTPClientMiddlewareKey, &clientMiddleware{ handlers: mdlHandlers, handlerIndex: -1, }) @@ -294,7 +294,6 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien resp, err = c.callRequest(req) } - // Auto saving cookie content. if c.browserMode { now := time.Now() diff --git a/net/ghttp/ghttp_unit_client_test.go b/net/ghttp/ghttp_unit_client_test.go index d8137eac0..42a3ed5f8 100644 --- a/net/ghttp/ghttp_unit_client_test.go +++ b/net/ghttp/ghttp_unit_client_test.go @@ -404,4 +404,4 @@ func Test_Client_Middleware(t *testing.T) { t.Assert(err.Error(), "http request abort") t.Assert(resp, nil) }) -} \ No newline at end of file +} From 6cf48f94798acbae6a91ff14c16b90beecccb058 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Wed, 13 Jan 2021 10:56:17 +0800 Subject: [PATCH 060/492] improve package gstr --- text/gstr/gstr_case.go | 7 +++---- text/gstr/gstr_z_unit_case_test.go | 14 +++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/text/gstr/gstr_case.go b/text/gstr/gstr_case.go index 393dae9ab..f8f6aa3e0 100644 --- a/text/gstr/gstr_case.go +++ b/text/gstr/gstr_case.go @@ -86,9 +86,8 @@ func SnakeFirstUpperCase(word string, underscore ...string) string { return CaseSnakeFirstUpper(word, underscore...) } -// CaseSnakeFirstUpper converts a string from RGBCodeMd5 to rgb_code_md5. -// The length of word should not be too long -// TODO for efficiency should change regexp to traversing string in future +// CaseSnakeFirstUpper converts a string like "RGBCodeMd5" to "rgb_code_md5". +// TODO for efficiency should change regexp to traversing string in future. func CaseSnakeFirstUpper(word string, underscore ...string) string { replace := "_" if len(underscore) > 0 { @@ -104,7 +103,7 @@ func CaseSnakeFirstUpper(word string, underscore ...string) string { m := firstCamelCaseStart.FindAllStringSubmatch(word, 1) if len(m) > 0 && m[0][1] != "" { w := strings.ToLower(m[0][1]) - w = string(w[:len(w)-1]) + replace + string(w[len(w)-1]) + w = w[:len(w)-1] + replace + string(w[len(w)-1]) word = strings.Replace(word, m[0][1], w, 1) } else { diff --git a/text/gstr/gstr_z_unit_case_test.go b/text/gstr/gstr_z_unit_case_test.go index 9fe4c3dea..a87de35e4 100644 --- a/text/gstr/gstr_z_unit_case_test.go +++ b/text/gstr/gstr_z_unit_case_test.go @@ -7,6 +7,7 @@ package gstr_test import ( + "github.com/gogf/gf/test/gtest" "testing" "github.com/gogf/gf/text/gstr" @@ -182,13 +183,12 @@ func Test_CaseSnakeFirstUpper(t *testing.T) { {"User_ID", "user_id"}, {"user_id", "user_id"}, {"md5", "md5"}, + {"Numbers2And55With000", "numbers2_and55_with000"}, } - for _, i := range cases { - in := i[0] - out := i[1] - result := gstr.CaseSnakeFirstUpper(in) - if result != out { - t.Error("'" + result + "' != '" + out + "'") + gtest.C(t, func(t *gtest.T) { + for _, item := range cases { + t.Assert(gstr.CaseSnakeFirstUpper(item[0]), item[1]) } - } + }) + } From 55a7c01f7346525c45ce14cf77808acd6625e6de Mon Sep 17 00:00:00 2001 From: tiansin Date: Wed, 13 Jan 2021 16:14:21 +0800 Subject: [PATCH 061/492] feat: :ambulance: Add function comment --- os/gtime/gtime_time.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index ff4b516e0..99da2ae6a 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -320,14 +320,14 @@ func (t *Time) Sub(u *Time) time.Duration { return t.Time.Sub(u.Time) } -// StartOfMinute starting of minute +// StartOfMinute Modify to start of current minute, seconds become 0 func (t *Time) StartOfMinute() *Time { newTime := t.Clone() newTime.Time = newTime.Time.Truncate(time.Minute) return newTime } -// StartOfHour starting of hour +// StartOfHour Modify to start of current hour, minutes and seconds become 0 func (t *Time) StartOfHour() *Time { y, m, d := t.Date() newTime := t.Clone() @@ -335,7 +335,7 @@ func (t *Time) StartOfHour() *Time { return newTime } -// StartOfDay starting of day +// StartOfDay Resets the time to 00:00:00 start of day func (t *Time) StartOfDay() *Time { y, m, d := t.Date() newTime := t.Clone() @@ -343,13 +343,13 @@ func (t *Time) StartOfDay() *Time { return newTime } -// StartOfWeek starting of week +// StartOfWeek Resets the date to the first day of week and the time to 00:00:00 func (t *Time) StartOfWeek() *Time { weekday := int(t.Weekday()) return t.StartOfDay().AddDate(0, 0, -weekday) } -// StartOfMonth starting of month +// StartOfMonth Resets the date to the first day of the month and the time to 00:00:00 func (t *Time) StartOfMonth() *Time { y, m, _ := t.Date() newTime := t.Clone() @@ -357,21 +357,21 @@ func (t *Time) StartOfMonth() *Time { return newTime } -// StartOfQuarter starting of quarter +// StartOfQuarter Resets the date to the first day of the quarter and the time to 00:00:00 func (t *Time) StartOfQuarter() *Time { month := t.StartOfMonth() offset := (int(month.Month()) - 1) % 3 return month.AddDate(0, -offset, 0) } -// StartOfHalf starting of half year +// StartOfHalf Resets the date to the first day of the half year and the time to 00:00:00 func (t *Time) StartOfHalf() *Time { month := t.StartOfMonth() offset := (int(month.Month()) - 1) % 6 return month.AddDate(0, -offset, 0) } -// StartOfYear starting of year +// StartOfYear Resets the date to the first day of the year and the time to 00:00:00 func (t *Time) StartOfYear() *Time { y, _, _ := t.Date() newTime := t.Clone() @@ -379,17 +379,17 @@ func (t *Time) StartOfYear() *Time { return newTime } -// EndOfMinute end of minute +// EndOfMinute Modify to end of current minute, seconds become 59 func (t *Time) EndOfMinute() *Time { return t.StartOfMinute().Add(time.Minute - time.Nanosecond) } -// EndOfHour end of hour +// EndOfHour Modify to end of current hour, minutes and seconds become 59 func (t *Time) EndOfHour() *Time { return t.StartOfHour().Add(time.Hour - time.Nanosecond) } -// EndOfDay end of day +// EndOfDay Resets the time to 23:59:59 end of day func (t *Time) EndOfDay() *Time { y, m, d := t.Date() newTime := t.Clone() @@ -397,27 +397,27 @@ func (t *Time) EndOfDay() *Time { return newTime } -// EndOfWeek end of week +// EndOfWeek Resets the date to end of week and time to 23:59:59 func (t *Time) EndOfWeek() *Time { return t.StartOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond) } -// EndOfMonth end of month +// EndOfMonth Resets the date to end of the month and time to 23:59:59 func (t *Time) EndOfMonth() *Time { return t.StartOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond) } -// EndOfQuarter end of quarter +// EndOfQuarter Resets the date to end of the quarter and time to 23:59:59 func (t *Time) EndOfQuarter() *Time { return t.StartOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond) } -// EndOfHalf end of half year +// EndOfHalf Resets the date to the end of the half year and the time to 23:59:59 func (t *Time) EndOfHalf() *Time { return t.StartOfHalf().AddDate(0, 6, 0).Add(-time.Nanosecond) } -// EndOfYear end of year +// EndOfYear Resets the date to end of the year and time to 23:59:59 func (t *Time) EndOfYear() *Time { return t.StartOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond) } From c1b52e0f35317df5c455f144c6a0a754b8ba28b8 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Wed, 13 Jan 2021 20:00:31 +0800 Subject: [PATCH 062/492] add more unit testing cases for package gredis --- database/gredis/gredis_z_example_test.go | 54 +++++++++++++++++++++- database/gredis/gredis_z_unit_test.go | 57 ++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/database/gredis/gredis_z_example_test.go b/database/gredis/gredis_z_example_test.go index 499d31b34..47cb81487 100644 --- a/database/gredis/gredis_z_example_test.go +++ b/database/gredis/gredis_z_example_test.go @@ -10,6 +10,7 @@ import ( "fmt" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/util/gutil" ) func Example_autoMarshalUnmarshalMap() { @@ -101,7 +102,7 @@ func Example_autoMarshalUnmarshalStructSlice() { fmt.Println(users2) } -func Example_hashSet() { +func Example_hSet() { var ( err error result *gvar.Var @@ -124,3 +125,54 @@ func Example_hashSet() { // May Output: // map[id:10000 name:john] } + +func Example_hMSet_Map() { + var ( + key = "user_100" + data = g.Map{ + "name": "gf", + "sex": 0, + "score": 100, + } + ) + _, err := g.Redis().Do("HMSET", append(g.Slice{key}, gutil.MapToSlice(data)...)...) + if err != nil { + g.Log().Fatal(err) + } + v, err := g.Redis().DoVar("HMGET", key, "name") + if err != nil { + g.Log().Fatal(err) + } + fmt.Println(v.Slice()) + + // May Output: + // [gf] +} + +func Example_hMSet_Struct() { + type User struct { + Name string `json:"name"` + Sex int `json:"sex"` + Score int `json:"score"` + } + var ( + key = "user_100" + data = &User{ + Name: "gf", + Sex: 0, + Score: 100, + } + ) + _, err := g.Redis().Do("HMSET", append(g.Slice{key}, gutil.StructToSlice(data)...)...) + if err != nil { + g.Log().Fatal(err) + } + v, err := g.Redis().DoVar("HMGET", key, "name") + if err != nil { + g.Log().Fatal(err) + } + fmt.Println(v.Slice()) + + // May Output: + // ["gf"] +} diff --git a/database/gredis/gredis_z_unit_test.go b/database/gredis/gredis_z_unit_test.go index e13d1934c..dc50185b6 100644 --- a/database/gredis/gredis_z_unit_test.go +++ b/database/gredis/gredis_z_unit_test.go @@ -10,6 +10,7 @@ import ( "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/guid" + "github.com/gogf/gf/util/gutil" "testing" "time" @@ -255,9 +256,11 @@ func Test_HSet(t *testing.T) { func Test_HGetAll1(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var err error - redis := gredis.New(config) - key := guid.S() + var ( + err error + key = guid.S() + redis = gredis.New(config) + ) defer redis.Do("DEL", key) _, err = redis.Do("HSET", key, "id", 100) @@ -296,6 +299,54 @@ func Test_HGetAll2(t *testing.T) { }) } +func Test_HMSet(t *testing.T) { + // map + gtest.C(t, func(t *gtest.T) { + var ( + err error + key = guid.S() + redis = gredis.New(config) + data = g.Map{ + "name": "gf", + "sex": 0, + "score": 100, + } + ) + defer redis.Do("DEL", key) + + _, err = redis.Do("HMSET", append(g.Slice{key}, gutil.MapToSlice(data)...)...) + t.Assert(err, nil) + v, err := redis.DoVar("HMGET", key, "name") + t.Assert(err, nil) + t.Assert(v.Slice(), g.Slice{data["name"]}) + }) + // struct + gtest.C(t, func(t *gtest.T) { + type User struct { + Name string `json:"name"` + Sex int `json:"sex"` + Score int `json:"score"` + } + var ( + err error + key = guid.S() + redis = gredis.New(config) + data = &User{ + Name: "gf", + Sex: 0, + Score: 100, + } + ) + defer redis.Do("DEL", key) + + _, err = redis.Do("HMSET", append(g.Slice{key}, gutil.StructToSlice(data)...)...) + t.Assert(err, nil) + v, err := redis.DoVar("HMGET", key, "name") + t.Assert(err, nil) + t.Assert(v.Slice(), g.Slice{data.Name}) + }) +} + func Test_Auto_Marshal(t *testing.T) { var ( err error From 2d67d31f902b2e3a8032c179ce957ab61635d898 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 13 Jan 2021 23:38:10 +0800 Subject: [PATCH 063/492] improve benckmark for package grand --- util/grand/grand_z_bench_test.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/util/grand/grand_z_bench_test.go b/util/grand/grand_z_bench_test.go index fc3a165ca..dc2361c28 100644 --- a/util/grand/grand_z_bench_test.go +++ b/util/grand/grand_z_bench_test.go @@ -9,11 +9,12 @@ package grand_test import ( - "crypto/rand" - "encoding/binary" - "testing" + cryptoRand "crypto/rand" + mathRand "math/rand" + "encoding/binary" "github.com/gogf/gf/util/grand" + "testing" ) var ( @@ -23,15 +24,21 @@ var ( strForStr = "我爱GoFrame" ) -func Benchmark_Rand_Buffer4(b *testing.B) { +func Benchmark_Math_Rand_Int(b *testing.B) { for i := 0; i < b.N; i++ { - rand.Read(randBuffer4) + mathRand.Int() } } -func Benchmark_Rand_Buffer1024(b *testing.B) { +func Benchmark_CryptoRand_Buffer4(b *testing.B) { for i := 0; i < b.N; i++ { - rand.Read(randBuffer1024) + cryptoRand.Read(randBuffer4) + } +} + +func Benchmark_CryptoRand_Buffer1024(b *testing.B) { + for i := 0; i < b.N; i++ { + cryptoRand.Read(randBuffer1024) } } @@ -101,9 +108,9 @@ func Benchmark_Uint32Converting(b *testing.B) { } } -func Benchmark_Buffer(b *testing.B) { +func Benchmark_CryptoRand_Buffer(b *testing.B) { for i := 0; i < b.N; i++ { - if _, err := rand.Read(buffer); err == nil { + if _, err := cryptoRand.Read(buffer); err == nil { binary.LittleEndian.Uint64(buffer) } } From 9c8a68f742d82407fb850045eaa5858b78b1d2b4 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 14 Jan 2021 00:05:15 +0800 Subject: [PATCH 064/492] add MarshalJSON for package gerror --- errors/gerror/gerror_error.go | 6 ++++++ errors/gerror/gerror_z_unit_test.go | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index dfbd5183f..cfe301486 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -169,6 +169,12 @@ func (err *Error) Next() error { return err.error } +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +// Note that do not use pointer as its receiver here. +func (err *Error) MarshalJSON() ([]byte, error) { + return []byte(`"` + err.Error() + `"`), nil +} + // formatSubStack formats the stack for error. func formatSubStack(st stack, buffer *bytes.Buffer) { index := 1 diff --git a/errors/gerror/gerror_z_unit_test.go b/errors/gerror/gerror_z_unit_test.go index 254f5c159..e60243aa7 100644 --- a/errors/gerror/gerror_z_unit_test.go +++ b/errors/gerror/gerror_z_unit_test.go @@ -9,6 +9,7 @@ package gerror_test import ( "errors" "fmt" + "github.com/gogf/gf/internal/json" "testing" "github.com/gogf/gf/errors/gerror" @@ -308,3 +309,12 @@ func Test_Code(t *testing.T) { t.Assert(err.Error(), "3: 2: 1") }) } + +func Test_Json(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := gerror.Wrap(gerror.New("1"), "2") + b, e := json.Marshal(err) + t.Assert(e, nil) + t.Assert(string(b), `"2: 1"`) + }) +} From bc4d84b60fe8f0d9a62c3191c654d9e9b25a5176 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 14 Jan 2021 00:27:01 +0800 Subject: [PATCH 065/492] improve comment for package gtime --- os/gtime/gtime_time.go | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index a81b736d4..7da67768a 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -318,14 +318,14 @@ func (t *Time) Sub(u *Time) time.Duration { return t.Time.Sub(u.Time) } -// StartOfMinute Modify to start of current minute, seconds become 0 +// StartOfMinute clones and returns a new time of which the seconds is set to 0. func (t *Time) StartOfMinute() *Time { newTime := t.Clone() newTime.Time = newTime.Time.Truncate(time.Minute) return newTime } -// StartOfHour Modify to start of current hour, minutes and seconds become 0 +// StartOfHour clones and returns a new time of which the hour, minutes and seconds are set to 0. func (t *Time) StartOfHour() *Time { y, m, d := t.Date() newTime := t.Clone() @@ -333,7 +333,7 @@ func (t *Time) StartOfHour() *Time { return newTime } -// StartOfDay Resets the time to 00:00:00 start of day +// StartOfDay clones and returns a new time which is the start of day, its time is set to 00:00:00. func (t *Time) StartOfDay() *Time { y, m, d := t.Date() newTime := t.Clone() @@ -341,13 +341,15 @@ func (t *Time) StartOfDay() *Time { return newTime } -// StartOfWeek Resets the date to the first day of week and the time to 00:00:00 +// StartOfWeek clones and returns a new time which is the first day of week and its time is set to +// 00:00:00. func (t *Time) StartOfWeek() *Time { weekday := int(t.Weekday()) return t.StartOfDay().AddDate(0, 0, -weekday) } -// StartOfMonth Resets the date to the first day of the month and the time to 00:00:00 +// StartOfMonth clones and returns a new time which is the first day of the month and its is set to +// 00:00:00 func (t *Time) StartOfMonth() *Time { y, m, _ := t.Date() newTime := t.Clone() @@ -355,21 +357,24 @@ func (t *Time) StartOfMonth() *Time { return newTime } -// StartOfQuarter Resets the date to the first day of the quarter and the time to 00:00:00 +// StartOfQuarter clones and returns a new time which is the first day of the quarter and its time is set +// to 00:00:00. func (t *Time) StartOfQuarter() *Time { month := t.StartOfMonth() offset := (int(month.Month()) - 1) % 3 return month.AddDate(0, -offset, 0) } -// StartOfHalf Resets the date to the first day of the half year and the time to 00:00:00 +// StartOfHalf clones and returns a new time which is the first day of the half year and its time is set +// to 00:00:00. func (t *Time) StartOfHalf() *Time { month := t.StartOfMonth() offset := (int(month.Month()) - 1) % 6 return month.AddDate(0, -offset, 0) } -// StartOfYear Resets the date to the first day of the year and the time to 00:00:00 +// StartOfYear clones and returns a new time which is the first day of the year and its time is set to +// 00:00:00. func (t *Time) StartOfYear() *Time { y, _, _ := t.Date() newTime := t.Clone() @@ -377,17 +382,17 @@ func (t *Time) StartOfYear() *Time { return newTime } -// EndOfMinute Modify to end of current minute, seconds become 59 +// EndOfMinute clones and returns a new time of which the seconds is set to 59. func (t *Time) EndOfMinute() *Time { return t.StartOfMinute().Add(time.Minute - time.Nanosecond) } -// EndOfHour Modify to end of current hour, minutes and seconds become 59 +// EndOfHour clones and returns a new time of which the minutes and seconds are both set to 59. func (t *Time) EndOfHour() *Time { return t.StartOfHour().Add(time.Hour - time.Nanosecond) } -// EndOfDay Resets the time to 23:59:59 end of day +// EndOfDay clones and returns a new time which is the end of day the and its time is set to 23:59:59. func (t *Time) EndOfDay() *Time { y, m, d := t.Date() newTime := t.Clone() @@ -395,27 +400,27 @@ func (t *Time) EndOfDay() *Time { return newTime } -// EndOfWeek Resets the date to end of week and time to 23:59:59 +// EndOfWeek clones and returns a new time which is the end of week and its time is set to 23:59:59. func (t *Time) EndOfWeek() *Time { return t.StartOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond) } -// EndOfMonth Resets the date to end of the month and time to 23:59:59 +// EndOfMonth clones and returns a new time which is the end of the month and its time is set to 23:59:59. func (t *Time) EndOfMonth() *Time { return t.StartOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond) } -// EndOfQuarter Resets the date to end of the quarter and time to 23:59:59 +// EndOfQuarter clones and returns a new time which is end of the quarter and its time is set to 23:59:59. func (t *Time) EndOfQuarter() *Time { return t.StartOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond) } -// EndOfHalf Resets the date to the end of the half year and the time to 23:59:59 +// EndOfHalf clones and returns a new time which is the end of the half year and its time is set to 23:59:59. func (t *Time) EndOfHalf() *Time { return t.StartOfHalf().AddDate(0, 6, 0).Add(-time.Nanosecond) } -// EndOfYear Resets the date to end of the year and time to 23:59:59 +// EndOfYear clones and returns a new time which is the end of the year and its time is set to 23:59:59. func (t *Time) EndOfYear() *Time { return t.StartOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond) } From 1fee3eb5f07acd97e084beedc3fe741d8fe6bdb3 Mon Sep 17 00:00:00 2001 From: eyasliu Date: Fri, 15 Jan 2021 13:58:16 +0800 Subject: [PATCH 066/492] fix review change --- net/ghttp/ghttp_client_middleware.go | 22 ++++++++++++---------- net/ghttp/ghttp_client_request.go | 4 +++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/net/ghttp/ghttp_client_middleware.go b/net/ghttp/ghttp_client_middleware.go index b775f45f6..294336c21 100644 --- a/net/ghttp/ghttp_client_middleware.go +++ b/net/ghttp/ghttp_client_middleware.go @@ -7,8 +7,6 @@ import ( const gfHTTPClientMiddlewareKey = "__gfHttpClientMiddlewareKey" -var gfHTTPClientMiddlewareAbort = gerror.New("http request abort") - // Use Add middleware to client func (c *Client) Use(handlers ...ClientHandlerFunc) *Client { newClient := c @@ -25,7 +23,7 @@ func (c *Client) Use(handlers ...ClientHandlerFunc) *Client { func (c *Client) MiddlewareNext(req *http.Request) (*ClientResponse, error) { m, ok := req.Context().Value(gfHTTPClientMiddlewareKey).(*clientMiddleware) if ok { - resp, err := m.Next(c, req) + resp, err := m.Next(req) return resp, err } return c.callRequest(req) @@ -34,9 +32,12 @@ func (c *Client) MiddlewareNext(req *http.Request) (*ClientResponse, error) { // MiddlewareAbort stop call after all middleware, so it will not send http request // this is should only be call in ClientHandlerFunc func (c *Client) MiddlewareAbort(req *http.Request) (*ClientResponse, error) { - m := req.Context().Value(gfHTTPClientMiddlewareKey).(*clientMiddleware) - m.Abort() - return m.resp, m.err + m, ok := req.Context().Value(gfHTTPClientMiddlewareKey).(*clientMiddleware) + if ok { + m.Abort() + return m.resp, m.err + } + return nil, gerror.New("http request abort") } // ClientHandlerFunc middleware handler func @@ -44,6 +45,7 @@ type ClientHandlerFunc = func(c *Client, r *http.Request) (*ClientResponse, erro // clientMiddleware is the plugin for http client request workflow management. type clientMiddleware struct { + client *Client // http client handlers []ClientHandlerFunc // mdl handlers handlerIndex int // current handler index abort bool // abort call after handlers @@ -52,13 +54,13 @@ type clientMiddleware struct { } // Next call next middleware handler, if abort, -func (m *clientMiddleware) Next(c *Client, req *http.Request) (resp *ClientResponse, err error) { - if m.abort { +func (m *clientMiddleware) Next(req *http.Request) (resp *ClientResponse, err error) { + if m.abort || m.err != nil { return m.resp, m.err } if m.handlerIndex < len(m.handlers) { m.handlerIndex++ - resp, err = m.handlers[m.handlerIndex](c, req) + resp, err = m.handlers[m.handlerIndex](m.client, req) m.resp = resp m.err = err } @@ -68,6 +70,6 @@ func (m *clientMiddleware) Next(c *Client, req *http.Request) (resp *ClientRespo func (m *clientMiddleware) Abort() { m.abort = true if m.err == nil { - m.err = gfHTTPClientMiddlewareAbort + m.err = gerror.New("http request abort") } } diff --git a/net/ghttp/ghttp_client_request.go b/net/ghttp/ghttp_client_request.go index 9706af4db..56942dc47 100644 --- a/net/ghttp/ghttp_client_request.go +++ b/net/ghttp/ghttp_client_request.go @@ -231,7 +231,8 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h return req, nil } -// callRequest send http request, return *ClientResponse and error +// callRequest sends request with give http.Request, and returns the responses object. +// Note that the response object MUST be closed if it'll be never used. func (c *Client) callRequest(req *http.Request) (resp *ClientResponse, err error) { resp = &ClientResponse{ request: req, @@ -285,6 +286,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien // call middleware ctx := context.WithValue(req.Context(), gfHTTPClientMiddlewareKey, &clientMiddleware{ + client: c, handlers: mdlHandlers, handlerIndex: -1, }) From 5b17108f717c9984e19187fd4e9f6a1f33ad11ed Mon Sep 17 00:00:00 2001 From: eyasliu Date: Fri, 15 Jan 2021 18:43:28 +0800 Subject: [PATCH 067/492] trigger CI --- net/ghttp/ghttp_unit_client_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ghttp/ghttp_unit_client_test.go b/net/ghttp/ghttp_unit_client_test.go index 42a3ed5f8..f951c858a 100644 --- a/net/ghttp/ghttp_unit_client_test.go +++ b/net/ghttp/ghttp_unit_client_test.go @@ -340,10 +340,10 @@ func Test_Client_Middleware(t *testing.T) { p, _ := ports.PopRand() s := g.Server(p) isServerHandler := false - respStr := "test resp str" + //respStr := "test resp str" s.BindHandler("/", func(r *ghttp.Request) { isServerHandler = true - r.Response.Write(respStr) + //r.Response.Write(respStr) }) s.SetPort(p) s.SetDumpRouterMap(false) From c20f3f05957c6f0fd4e0e38ec902c587f1ba1ad7 Mon Sep 17 00:00:00 2001 From: eyasliu Date: Sat, 16 Jan 2021 17:50:57 +0800 Subject: [PATCH 068/492] add Wrapf and WrapH to transform net/http.HandlerFunc and net/http.Handler to ghttp.HandlerFunc --- net/ghttp/ghttp_server_util.go | 23 ++++++++ net/ghttp/ghttp_unit_server_util_test.go | 69 ++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 net/ghttp/ghttp_server_util.go create mode 100644 net/ghttp/ghttp_unit_server_util_test.go diff --git a/net/ghttp/ghttp_server_util.go b/net/ghttp/ghttp_server_util.go new file mode 100644 index 000000000..47dd8db44 --- /dev/null +++ b/net/ghttp/ghttp_server_util.go @@ -0,0 +1,23 @@ +// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp + +import "net/http" + +// WrapF is a helper function for wrapping http.HandlerFunc and returns a ghttp middleware. +func WrapF(f http.HandlerFunc) HandlerFunc { + return func(r *Request) { + f(r.Response.Writer, r.Request) + } +} + +// WrapH is a helper function for wrapping http.Handler and returns a ghttp middleware. +func WrapH(h http.Handler) HandlerFunc { + return func(r *Request) { + h.ServeHTTP(r.Response.Writer, r.Request) + } +} diff --git a/net/ghttp/ghttp_unit_server_util_test.go b/net/ghttp/ghttp_unit_server_util_test.go new file mode 100644 index 000000000..3da6ab1df --- /dev/null +++ b/net/ghttp/ghttp_unit_server_util_test.go @@ -0,0 +1,69 @@ +// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp_test + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/test/gtest" +) + +type testWrapStdHTTPStruct struct { + T *gtest.T + text string +} + +func (t *testWrapStdHTTPStruct) ServeHTTP(w http.ResponseWriter, req *http.Request) { + t.T.Assert(req.Method, "POST") + t.T.Assert(req.URL.Path, "/api/wraph") + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(w, t.text) +} +func Test_Server_Wrap_Handler(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + p, _ := ports.PopRand() + s := g.Server(p) + str1 := "hello" + str2 := "hello again" + s.Group("/api", func(group *ghttp.RouterGroup) { + group.GET("/wrapf", ghttp.WrapF(func(w http.ResponseWriter, req *http.Request) { + t.Assert(req.Method, "GET") + t.Assert(req.URL.Path, "/api/wrapf") + w.WriteHeader(http.StatusBadRequest) + fmt.Fprint(w, str1) + })) + + group.POST("/wraph", ghttp.WrapH(&testWrapStdHTTPStruct{t, str2})) + }) + + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + client := g.Client() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d/api", p)) + + response, er1 := client.Get("/wrapf") + defer response.Close() + t.Assert(er1, nil) + t.Assert(response.StatusCode, http.StatusBadRequest) + t.Assert(response.ReadAllString(), str1) + + response2, er2 := client.Post("/wraph") + defer response2.Close() + t.Assert(er2, nil) + t.Assert(response2.StatusCode, http.StatusInternalServerError) + t.Assert(response2.ReadAllString(), str2) + }) +} From 2c5e6b379f523c1b12550516402528026453648e Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 16 Jan 2021 20:20:30 +0800 Subject: [PATCH 069/492] improve package gres --- os/gres/gres_func.go | 12 +++++++----- os/gres/gres_func_zip.go | 13 +++++++------ os/gres/testdata/data/data.go | 2 +- os/gres/testdata/testdata.go | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/os/gres/gres_func.go b/os/gres/gres_func.go index 52d449ab8..4995fbc29 100644 --- a/os/gres/gres_func.go +++ b/os/gres/gres_func.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -20,7 +20,7 @@ import ( ) const ( - gPACKAGE_TEMPLATE = ` + packedGoSouceTemplate = ` package %s import "github.com/gogf/gf/os/gres" @@ -39,8 +39,10 @@ func init() { // // Note that parameter supports multiple paths join with ','. func Pack(srcPaths string, keyPrefix ...string) ([]byte, error) { - buffer := bytes.NewBuffer(nil) - headerPrefix := "" + var ( + buffer = bytes.NewBuffer(nil) + headerPrefix = "" + ) if len(keyPrefix) > 0 && keyPrefix[0] != "" { headerPrefix = keyPrefix[0] } @@ -79,7 +81,7 @@ func PackToGoFile(srcPath, goFilePath, pkgName string, keyPrefix ...string) erro } return gfile.PutContents( goFilePath, - fmt.Sprintf(gstr.TrimLeft(gPACKAGE_TEMPLATE), pkgName, gbase64.EncodeToString(data)), + fmt.Sprintf(gstr.TrimLeft(packedGoSouceTemplate), pkgName, gbase64.EncodeToString(data)), ) } diff --git a/os/gres/gres_func_zip.go b/os/gres/gres_func_zip.go index 22af99258..bc6e0ac50 100644 --- a/os/gres/gres_func_zip.go +++ b/os/gres/gres_func_zip.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -11,6 +11,7 @@ import ( "github.com/gogf/gf/internal/fileinfo" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/text/gregex" "io" "os" "strings" @@ -72,7 +73,7 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix intlog.Printf(`exclude file path: %s`, file) continue } - err := zipFile(file, headerPrefix+gfile.Dir(file[len(path):]), zipWriter) + err = zipFile(file, headerPrefix+gfile.Dir(file[len(path):]), zipWriter) if err != nil { return err } @@ -83,7 +84,7 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix path = headerPrefix for { name = gfile.Basename(path) - err := zipFileVirtual( + err = zipFileVirtual( fileinfo.New(name, 0, os.ModeDir|os.ModePerm, time.Now()), path, zipWriter, ) if err != nil { @@ -136,7 +137,7 @@ func zipFileVirtual(info os.FileInfo, path string, zw *zip.Writer) error { return err } header.Name = path - if _, err := zw.CreateHeader(header); err != nil { + if _, err = zw.CreateHeader(header); err != nil { return err } return nil @@ -148,9 +149,9 @@ func createFileHeader(info os.FileInfo, prefix string) (*zip.FileHeader, error) return nil, err } if len(prefix) > 0 { - prefix = strings.Replace(prefix, `\`, `/`, -1) - prefix = strings.TrimRight(prefix, `/`) header.Name = prefix + `/` + header.Name + header.Name = strings.Replace(header.Name, `\`, `/`, -1) + header.Name, _ = gregex.ReplaceString(`/{2,}`, `/`, header.Name) } return header, nil } diff --git a/os/gres/testdata/data/data.go b/os/gres/testdata/data/data.go index f666a4b8f..099d514e3 100644 --- a/os/gres/testdata/data/data.go +++ b/os/gres/testdata/data/data.go @@ -3,7 +3,7 @@ package data import "github.com/gogf/gf/os/gres" func init() { - if err := gres.Add("H4sIAAAAAAAC/7RaeTiU6/9+sq8hSyohYpAxtkiyFLKMfYtSGgxxGGHsLShlSUqh0nZQpGObiCMplX3LlrQoCpF9RIR+V4T3HWPJ+X27rvjHc9/3536f7f28tyGakooD0AE6MIh1NASQf+sAPbB1xdk7OqBmfkngXV2czUypwZqOnfaHxCp0kSgt8TLDDDPDuprSGr1GFArVhNKuQpZqi2sjzdvCJHT0ysTPtxsbGhpqlYlXm4N7srLSTTIvZV7KZEnLyOU0SBlSoBEIYYdEm4typhRUAPz8aYimpbv+7IKdOQDAHgCwuDzmeXkuvhKOOMf/V2XG88rM55V9D1RTJFUGgJIVwhiqjGZO2bSku4Mj1r8GQ/9k8bI2zA1G2np64F1d/mfmG82XaDZfoqm63NvlzedYoPJ/8AxM5gXumxcoSdt+evlnwEwqcNlHAcBlt5uwIukAPbBzdJdCeXjaTA9vfpdyaOVPkgsy/Nd/JB7rgZeSwPvg512S0KtDaiMlpEy1qmu2aVdtWzNXZsGDSzvXAwDYluRgmOWYxp7HJY8ZQqkyxT5tzlLWUf3GXL1j0v/NMWmYY9LkHTNeUJ1/JC/lih2TnnZMGu7YQkw9FULJih2TXsVyZwL0wFFqBw6FxcFXeFu5VlVZ2b4MM8MhIHU5t5wY7m6sOnGUVY2edVacrTdSlQ8AwL8yBifMYgx+73aMudV/Lom4zycvcexd7qHQ+7ueiCCotxLtSrI6TfwvgzWznEYO/F5SAIBtK+N091yMM0/yp83z4M3r5Q6+9svU/k0ont/57Hw4s04AmKtxR95fSACAyJJ8LLN8fkeQavpwyvkqCd6F9Xf8o2e8fEqheFfp1psEKrpZqq6W8gghAMCWFVOZ7vt/pCI/rX5RrWIhMv4eirRzdEdhcatcixAE1BGss7PrUlN0fo64f3Wy5QAAsP4Rgberu7MdCYFYDapc2yjDzJCBFkpwg80TQUqwjAdOmP/qgRPmtwe+mAUe5BgvXEPz6zTfXsySf/po/xOyGT9gZNN2ZBob+hFs3xDvKUGWyc3d1s5c07NzxY64e67CEU44wm9HoAf/Igt83g3nY1tENk/X+ydEM27AbhizcyMvg3TTwH7tfbR+2fmxFkoxvXGswpANC0B+e+Lk4Yqb05rS8euaYzjjDcF4fn9wWjM3qQu2MOYtP0vI8M1YQ45vxiLC/ISxyp43iaEXPUXKt7xJpvtWYRL3ApDfJvlAJjchM720ykgEjRSrrqx5YCbdIGNorFWFrjYoq0TnmIkhteuzDQk5nwhlslmlDz79qi+jbHrt+c+Y6TK8ZZfEpuMZDKpzJbL6cUUaAAAM/lTdjKX/UZ3o9Gr1n7FebpyVNzZC2YKaf1bcuLButD6JuIVnAB1E3CquF2yzw+0dnbH/uzsGnMYJs9I9cubYL9LZ3VV23rEmaURkDcUsMUdxhcWvi4boksSsMGJ3z5VsRYveNZ4ZoybFAQCIZV995ilnLhwLF9+CpY5XDWof01AQPVy4uf6mQa9RWwr13C1T2RD1UAIAIL4kMTspsem+//cFFKLre8JIP+z9pWmRmbVnB3Kdhwv95nepMtXyegwAwHHJWUsPlbrK9wh3V1c8ytbDYxX7zTrIcJQH3tcZKzEL9MuoOuPk6f1Rq6JmWy2y+h+dbGNJObHkDBrGmETzy1+EO+zudlqKbk3qiE7ksvRopJibk2ItNt3bAQDSS4pnmGV3dME4YFchnx0GgHJ2dXCVOIpzmNPvHOu7o+GvTSVJ0tGhAjtjXl5Rt7SvPM2thpEccSk1lrUvKxW3F/ZvLnE3ZG9+wFEWtf2AqcBpBQ6C0I8o23KvqDvXR931+31rPFuIVk3Ekv6p4yqjowXpP6Y6QzMfynJRaQcBEHCbnsaFEQDWR6MfTwDA76GcRwFAD+bNxq+UOo/u/Dq7VTZ/ekEF1ob2tCgFDOu6gcOuohu+lrpnSWq2OXKInFf1QOyZFME/7ds9tmVSBM9wMZjyh1F+sm7irbWfWaijytX7zmBYB/jWvdq+OSQhePvrgGL+oMa/nQzNQiTomGgSuKnW1XG5DFM6sGrtZGXhCj9/uvDY8eN/Fzj8LVtsxm2FRjPeDQvYwirGRKEd3GWAbZ0wTa68dFJ4zJVX9mKow4UucekIhpbJ4n/r0x7g+S/wXVOpidwpOvzkXHIl38FWR44rfOfXnlIzw/ykYRs4KTX8lSldRUfwI0YdJRCshZXCHBWZsFbguqo+7p3zXuXkm66pSLUxoy9alDRsJQLxbuLxEns40+7Ep6TFpwQGC15wYNPKzZNhGGGpfdIqSxOzl1jX+DabctPFDerWGz9tZHc+nDQiP3H2C18+dYSq8NjPW493Gh+k9DUnnp38oH7yOb3P1tDi2x8t8uwY+sTY9iYd0iicWj8R557jn8h5SjeRcPSRWn1wwuGf63/0B2B+Xhm49ZXyR6ZKHj9HT/rBBmrko+F+SqBaKHxE6R/agUsJjMyKIm5jYt/Esu8eV8kxs9p9bP8XEcMP5w2kekYE/R8dEjTxDymY3PBjCmEur9wW7qmRGazp/JDl3C75uy/O7BaIDEIo9VdUCNN9t7hy6v7XsYHAQ0oP3mVRd7qkxZwLyBdq6al0Gtop33um3qeXOGq7D13tEBar+8XvM+7s6c4Y6yuW17+CFuMRQTvRWEaFdLYzJ2ItThYEbWkojW2yVLlvKWnGuemdv83AqKfNhRfyV95dplPK+0LcIEbLeDlbdiNOOy/rJh+PmmJtgEswrw9d7YCXqzCWMe3ri8O4uDKKpmY8fzsyft/rbuPgpsTnV0OYKk4JXM3pERN8lb9ZPI2tyO3UxbDz0joIZp8kzga8nmY4skiD7nqoqfnj1348+/tke4PaWL2UNd/vVRHXTTWdqqo9n6uOYw80OIcRTGmvreexv3+k0tY2wNzXWKhWNIfauO2JunLMB03p55P2wSPVb7jZRPHWwyxHDNvoHldUPkr4fPEneqOOe+BJgzDjobs03Ae347kkvO53d0YG8+gJ1n+OxhG1B0SzfnyrfeURdfthzOQGDa1v50JY9jzoclOpGR/g5DbaSsv6bzkC6Vij9vwxtUP30SRdn/PaNM4KL3MMvAXqxowfpfd1me7ksfhWdhAzYOXa6WSV+mDk+bBI4brwPGbaR8+F16j1qiictpp6+jz2KgJl20sYWX/wtSn74Jo4nvJwi6KrrdiPpuLD+NZ7+eiXg4HdUlJHPqcHq211QIS/9t6IsHqJOJIug+PfgyuKq3YaUC2kPaMjyNw4vvkdy5jAX/Z7EcGtthbyWcPvzfhVzDuTbmEVuI9eU1C/OxLbILd7yzdbxsSHqr1nGv8qzVPclKswvJki8FnmIXRKc4VawjpJdwx/YdDZJub+C26b7phr+WbbKjaKpQe7qTRalpmf3Rxr88Ho5s6t+RMdES+nJg2be3gnUF3KBin3rf0cDoXWhPb81WLRTWjVuoY/E94tO6JWb3hc61tZMtc7/MTFm/5IdvbDZQ/WifPxaVVE+VPvTRp8P+kk5//JKtjyidnU0TDv4reICj/v+BuWDqgT4r7sgkxhiTX5D+ltvBQGilPWfjzbevXalObbkomEqB/VuvdKQi7LR2mcuc9wDvkg70PIwAO+EoKj+44HrYdFqy6kcbw+RSAIKj3xv/7o3+w33waDPxzAJxcQ29oDwqhSz1h4+Zk38LbK3FlTEx+JUeX9nnU0V87fufLbW90TOlZV4YPrNasjt4dQ51HT8+0QIIq568b2ynaHTDrfcNBq4YjMCdVk4O0cys7ROl515MXjJu567IAeiz9xSEHee2e0nJdOrPt630C17Czi3r179wzuHSQa/KOaMUFloFip2GiXoVFgEkvHEuVaIyI+2NKmw1Og55Pb6ST/19Dbjjd7656O/PgL+y8zc/+/E8hiSrM1kYVcB6/JNzFxmOQJ8zXxFusNX210TDlTmutwcjy7+eM1OaVolCT/bakQW+vOmLz9mpypLS/RT7//xF1jrwsvLT5il5weVnj92UjtRjX2tyXMfOZ4OzlPBo8CU4RZcd6NFMv4ofCWSOaIeF26Xa5ynCwtXe4HutKeVkgpVu7GidmbP/3OyLaRGhVvgKLWczX1W0sz/Lpe3/uI4EUrzc9Kgp9UXbwDjwUJZFY1FxW1HdV8kJRqr1jaV2uV/EL3cruzdtzkp/hriUoWHw/3xzhsMSe8blbos2IdmAyhZNV0TsnJjxS4JC3HEt/Hh6TL4fUZyiUIsG//lmX37k5H+cfmm6GhTG9O9FJi05CWNsrfHR8efjsZ9yIHsyG1fb/nBy5bwbBrn3akF8X+w/g1mb9pS9GNnt5Y3SeonKC4CAMqtJ64/quC16Z8W5raJU9LHVMPNGGOMOlkeomh2nGy/6zM56JQ5bEEHe8qob3W4mNrlR6fKJU+EDt0bahgb0YnUSvxWrWs0qG+bzKGXk32Y+iLdW7ZFF/zOlrGc8dy8TkMZwlxe3pzEg+UvA+68+Tkz77WwXEd/2dJf4dtYtZHDN4VvdmVkFu/I+kGcfBc+jHvycu9ce0l5RZtmTV1cRF27RX7zPOTaC/0ZMmp9eARu57fPEbN9uP2qwpc5LixwJPknuvpr3ftSzryuGuzvvZYA+Oze+pTAUmnG3kPFZxLlbJ1puX0s0o9L1V34nSgvmW9Kd1A/yUB311hA7q6LBfV4jjedGXFVCvyHX+zGTHR66/RrREnwG/BgVdFNxz04ZeXbN3/wvbtujeJ3MmDHb157bYNpfw7LMz3Xyr1e9NDkGgwjuaRymGR9ubvqNWy9S9+fk+eAUlAVI1WR6WNfrHfzt1N/GDnEFqDGGPZp19Bn7l+UvWMT8Q6Xas4AeWto1/RkZWmgcHxseXSbqwh69kD+Eu2XS9Qv136+Ybi6R938VaPmRoOHKDsEWlUNHE/Vr2RV+r9s/7tbxlcsRsp4gqEua192tfgi4WbmtUNBiTf7XQp+45V/jKstZWYEDNaeOlUXutoJV9lx8MnzcfaiTq6RRl2wgjWovde7/yPZd7GZw/vUbamESsZrOYfwTu25XCu778p/EgijRhhJDiszDrR4j4iNclMnZ9TiG1Ny6osT4oa2IFtTWt5VX3lnyunjHWsPOMJww8kD23NcrtfgfyG9vxI7JeJiagww+XmW27qOf7d1bxsmPaeHqKsBpXq03FHxNeDqkElWfr2i/Zma4NxK/tqw7acdKXMCTy3QHN6TabC3fe6iEu5Lx3jXSwnnUK23XBp4z2Y/gpzUDtta4Mm4ux4gQbzi31dlZTP01HlGQdGLCMaFAc0hErzd7nrM4+2Bo68LUT+u+6fkGNOhx+pO1zzpp2M7T1ggiPkja9r1T5qcto74CLP3/679XGZHKnC4dy+eJnarSLKJR79b4PXFQX5GU3wP2nd1FDM++I2ts+l6WWzzedyV4+gawnW7meTTvkqvleezBCSDaTeIZA3Utu7rbvuusbFVBpkf7PJ92MXXlggEeuDD78rrT/BVrBxx4cPfDfwgzZunnhnqbsvR+UdsxQNEnFRkR8Kx8uDGkx+qgmETqUdp+G9YsLpyyn9LO35qGpRX0sbTVFzKnt08R6xqTRmZgpzFk5c5TetL6/luL24XE1O6D265RWb7MOW8E9O9khj8VCjPeoaDV+0G0+rjIcYLo/u+gnJV1hJqV1KTRbp37WyA/QZD93afIAJr3qSpqOBccPHrtS9X3Sn8pPP6EVlRmNsxjhYOseH3GMOPSbY3A892E+bZxpu7PU21rzrVGXdZ+HM8/gh/UwtXLYOITxIUbnzzLOGmlPfHUb3h3eoo3k9+aLpa07fMDvcLxmJjSWKa1fzhAdk6rAUdIXJZlc2R4QzsCopsBXRNGi0o7HEWw1Jhdf73vEIDRrUmVxKzGbFhZpcPyS9rU4osjNn6PYuQatPPDtRb4TzTgwh9GNHCjnvZ2g60FgFUhy/b6p/lFieGyg7+fpTz6ezb6muFLMNC+zDnA22QRdwUO9i6JHtG5aO3OCRI8La+HDE6VE6/aX37mj0ie6bIzGNxdLHG2qze0C8W4+6CmP5djsqv9dYGT1rjccyaG9k+TaVcy+bQ64wRqp6sLiFKbumjhwKaJM+rBnXpIKmdb6RtHmPShJvQdbEzkqfNNv+fYyR/TZxCHTN2b0DOf3K0bpMyNo74/ueTbHPdf36bwdkMAFwmXm55vrMmw/ODusjcQQPeb0Vr651KeZnovroIB1cBAYpzojmqW7kyWDY8sw82tn64pRBTH+igWbR/ojwhGcHwsI9TNsnAgu3+2QnEbjsiYY0fIX9Kd/vfk3rLrwZfkq+gPkDH+I0X2iBv1+H3tAm7f00m/0H6h5WWvj3ffW056N3eJKbLsAQfzh4d/ULeZ4E+dJTnje6n+z93jfJOFsRpve76TkAwMNle/i/KlpF7+bXayAe63LUGYPHkjNkSNwIZS5SpmVWWonWTjUU+f1t1UyrolK7kXKuufA4RgQIAgA2L/nSygolc8b4unr+/pz4Z6+ufGRgULauODzGEYd1h+tPSdPX1quoRJsZ6lTXbNOurEQjjVNS2wllklkeI6OMbsPDHsyE2hrJrE8pafpV6Hup8w0TIdq3/fIAAKklxfCQE2Pv6opfQklFDUp7XgZxlMVtMQ2mxYpay38JI6vhCBZjt7iGci093RkNMz0aySziUU/8ojrG/S/UbwcAoP5cx8xvuA6CEX7Qe20o01GzUpGyqsxPhECje6GiXlYMDAwMckJXhRTuefWN4TRC/73HoBwaPXZHSG6c1SgjrLs7quFd0PUXUabUQhcKGgTuvNkvsel4yAY57THRaMMEY+kKjiiPdeHhLtFRRKLOOhmicfhlSebdpY2v9sh2J9nFYK5djLllHdQIkqlScJuOh6CdhwtPUM0VWV59cUsUAGD8Tyex9ComMRmvpMnOG5I5Mr/m9jsPV3GSfI5ZIQ+5uUGAz4F5HnGl5l2r5CH37FPSqivR1aJ62rOTsK5CpKzKyPjl/dT2PaVd7cL2HcEcn2/Tb7vT3hnL/9f8Z3KqsU7aX3NQf8nHw0lOhwvGcTWfJ/kXw5r+IUXGP8/hYZksuH2Ver3THyrX/gcq6UWoSJ6UjYpvOykV+W7/LNUSJ8YaCg7Kea3QuNQ6QD/3d4mBv34ukeAiBYKmm5hhQLfmgUhyPvMY5HNQswvz526FNWBhKgouAJo92gAT4DU3eKlUFCkeNCrEAcPjoQBL55eWqosZVtdjUigy5ZHvcM9i9O5eQwnIJY3g1UATQVywalQhwxdJGpGCQaNADDCwxFkweKRoKUOoYIZ8/w2wCh9kqAC5/BBcOjTnA/fBHTJ8kfwQKRg04AP34dUsGDwotHIfxKgBaSoIzg39tsYE48ZSA/KpIFIIaBwHDlEyC0Ea+1mgApKwgUMo0gDyKR5SCGichQUGcX8WgkwwZ+UoXLRg0czNyp9H4G+UFc1LRti8zP89dGGcBl4CNPkCn5pTcARycRpSMGjKBQ5mTQeWi86svLanUDBYTAYuB5pigcuhoAfLxWRIwaB5FTiYKwkYmRjMymurg4LBAi9wOdBMCidMDgcDWCbwQooFDZ/AsY6TYC3MtCxV2VpYZR+hWKThFZJzAxIsgZ+qQoxgBeEVUjxocASOF70Qj0w4ZeVFUjOBxcMncFHQqAc3TJTOAhAy4RNSOGg4Aw73ZSHcwrTIUpsSHWxTCmYG5PIdix8UbDA1L2aHk813kOJAMxVwnG1rwRIBDlIcaFCCFX55gOGQ5DFIYaCxB/j1bD0LWDpjQQoFjSWww6AukEKRpCaWelT0sEelyArIhhpWernxZwXkQg3wQqARA/hlPg8ynEyoYSkdDDAdO9gA+XwCyZ4I6djBLfWFASzMJ5AiQTtl8IPdgxcs2u9b+cGuwQdIe2xwAdAWGLyUAD6wZI9tKVdZYa72QpEWNtDggqD9Kz6YIFl+sOIG2oLXJ0hHigeGWkgOlVxHY8E+CGkuwSE1toCVNbZIIaGtHDhkFTlIcn2KlT8VggBYqiMEVwbt2sCVjZGBWZF/0AYNHBIjCFbW/CGFhPZa4JBl5CD/1D9OmH9GW8GyLRu4PGhPhR8m78ZiWORaNqSw0P4JHJZOCKy8PbPyg9kDAgvZWKhpfv2BMlAG/mwAEH69r4D/CwAA//9FB/78QzcAAA=="); err != nil { + if err := gres.Add("H4sIAAAAAAAC/7RaeTiU6/9+sq8hSyohYpAxtkiyFLKMfYtSGgxxGGHsLShlSUqh0nZQpGObiCMplX3LlrQoCpF9ZAv9rkjed4wl5/ftuuIfz33fn/t9tvfz3oZoSioOQAfowCDW0RBA/q0D9MDWFWfv6ICa/SWBd3VxNjOlBms6dtofEqvQRaK0xMsMM8wM62pKa/QaUShUE0q7ClmqLa6NNG8Lk9DRKxM/325saGioVSZebQ7uycpKN8m8lHkpkyUtI5fTIGVIgUYghB0SbS7KmVJQAfDjhyGalu76swt25gAAewDA4vKY5+W5+Eo44hz/X5UZzyszn1c2FqimSKoMgNM2ksZQZTS/lc1IGmumOPxzMPRPFi9rw+/BSFtPD7yry//MfKP5Es3mSzRVl3u7vPkcC1T+D56BybzAffMCJWnbTy//DJhJBS77KAC47HYTViQdoAd2ju5SKA9Pm5nhze9SDq38SXJBhv/8j8RjPfBSEngf/LxLEnp1SG2khJSpVnXNNu2qbWt+l1nw4NLO9QAAtiU5GOY4ZrDnccljhlCqTLPPmLOUdVS/MFfvmPR/c0wa5pg0eceMF1TnH8lLuWLHpGcck4Y7thBTT4VQsmLHpFex3JkAPXCU2oFDYXHwFd5WrlVVVrYvw8xwCEhdzi0nhrsbq04eZVWjZ50TZ+uNVOUDAPCvjMEJsxiD37sd4271n0si7vPJSxx7l3so9P6uJyII6q1Eu5KsThP/y2DNHKeRA7+XFABg28o43T0X48yT/GHzPHjzermDr/0ytX8Riud3PjsfzqwTAH7XuCPvLyQAQGRJPpY5Pr8jSDV9OOV8lQTvwvo7/tGzXj6lULyrdOtNAhXdHFVXS3mEEABgy4qpTPf9P1KRn1Y/qVaxEBl/DUXaObqjsLhVrkUIAuoI1tnZdakpOj9H3L862XIAAFj/iMDb1d3ZjoRArAZVrm2UYWbIQAsluMHmiSAlWMYDJ8x/9cAJ88sDX8wCD3KMF66h+XWaby9myT9ztP8J2awfMLIZOzKNDf0Itm+I95Qgy+TmbmtnrpnZuWJH3D1X4QgnHOGXI9CDf5EFPu+G87EtIptn6v0Tolk3YDeMubmRl0G6aWC/9j5av+z8WAulmNk4VmHIhgUgvzxx8nDF/daa0vHzmmM46w3BeH5/cFrze1IXbGHMW36WkOGbtYYc36xFhPkJY5U9bxJDL3qalG95k0z3rcIk7gUgv0zygUxuQmZ6aZWRCBopVl1Z88BMukHG0FirCl1tUFaJzjETQ2rXZxsScj4RymSzSh98+llfRtnM2vOfNdNleMsuiU3HMxhUf5fI6scVaQAAMPhTdbOW/kd1ojOr1X/WerkJVt7YCGULav45cRPCutH6JOIWngF0EHGruF6wzQ23d3TG/u/uGHAaJ8xK98jZY79IZ3dX2XnHmqQRkTUUc8QcxRUWPy8aoksSs8KI3T1XshUtetd4ZoyaEgcAIJZ99ZmnnL1wLFx8C5Y6XjWofVxDQfRw4eb6mwa9Rm0p1L9vmcqGqIcSAADxJYnZSYlN9/2/L6AQXd8TRvph7y/NiMysPTuQ6zxc6De/S5WpltdjAACOS85aeqjUVb5HuLu64lG2Hh6r2G/WQYajPPC+zliJOaCfRtUZJ8/sj1oVNdtqkdX/6GQbS8qJJWfQMMYkml/+Itxhd7fTUnRrUkd0IpelRyPF7zkp1mLTvR0AIL2keIY5dkcXjAN2FfLZYQAoZ1cHV4mjOIff+p1jfXc0/LWpJEk6OlRgZ8zLK+qW9pWnudUwkiMupcay9mWl4vbC/s0l7obszQ84yqK2HzAVOK3AQRD6HmVb7hV15/qou36/b41nC9GqiVjSP31cZXS0IP37dGdo5kNZLirtIAACbtPTuDACwPpo9OMJAPg9lPMoAOjBvNn4lVLn0Z2fZ7fK5k8vqMDa0J4WpYBhXTdw2FV0w9dS9yxJzTZHDpHzqh6IPVMi+Kd9u8e3TIngGS4GU343yk/WTby19jMLdVS5et8ZDOsA37pX2zeHJARvfx1QzB/U+LeToVmIBB0TTQI31bo6LpdhSgdWrZ2sLFzh508XHjt+/O8Ch79li824rdBoxrthAVtYxZgotIO7DLCtk6bJlZdOCo+78speDHW40CUuHcHQMlX8b33aAzz/Bb5rKjWRO0WHn5xLruQ72OrIcYXv/NpTamaYHzRsAyelhr8ypavoCH7EqKMEgrWwUpijIpPWClxX1Se8c96rnHzTNR2pNm70RYuShq1EIN5NPF5iD2fanfiUtPiUwGDBCw5sWrl5MgwjLLVPWmVpYvYS6xrfZlNuurhB3Xrjp43szoeTRuQnz37hy6eOUBUe/3Hr8U7jg5S+5sSzUx/UTz6n99kaWnz7o0WeHUOfGNvepEMahdPrJ+Pcc/wTOU/pJhKOPlKrD044/GP99/4AzI8rA7e+Un7PVMnj5+hJP9hAjXw03E8JVAuFjyj9QztwKYGRWVHEbVzsm1j23eMqOWZWu4/t/yJi+OG8gVTPiKD/o0OCJv4hBVMbvk8jzOWV28I9NTKDNZ0fspzbJX/3xZndApFBCKX+igphujGLK6fufx0fCDyk9OBdFnWnS1rMuYB8oZaeSqehnfK9Z+p9eomjtvvQ1Q5hsbpf/D7jzp7ujLG+Ynn9K2gxHhG0E41lVEhnO3Mi1uJkQdCWhtLYJkuV+5aSZpyb3vnbDIx62lx4IX/l3WU6pbwvxA1itIyXs2U34rTzsm7y8agp1ga4BPP60NUOeLkKYxnTvr44jIsro2hqxvO3I+P3ve42Dm5KfH41hKnilMDVnB4xwVf5m8XT2IrcTl0MOy+tg2D2SeJswOtphiOLNOiuh5qaP37tx7O/T7Y3qI3VS1nz/V4Vcd1U0+mq2vO56jj2QINzGMGU9tp6Hvv7RyptbQPMfY2FakVzqI3bnqgrx3zQlH4+ZR88Uv2Gm00Ubz3McsSwje5xReWjhM8Xf6A36rgHnjQIMx66S8N9cDueS8LrfndnZDCPnmD952gcUXtANOv7t9pXHlG3H8ZMbdDQ+nYuhGXPgy43lZqJAU5uo620rP+WI5CONWrPH1M7dB9N0vU5r03jrPAyx8BboG7c+FF6X5fpTh6Lb2UHMQNWrp1OVqkPRp4PixSuC89jpn30XHiNWq+Kwmmr6afPY68iULa9hJH1B1+bsg+uieMpD7coutqK/WgqPoxvvZePfjkY2C0ldeRzerDaVgdE+GvvjQirl4gj6TI4/j24orhqpwHVQtozOoLMjROb37GMC/xlvxcR3GprIZ81/N6MX8W8M+kWVoH76DUF9bsjsQ1yu7d8s2VMfKjae6bxr9I8xU25CsObKQKfZR5CpzRXqCWsk3TH8BcGnW1i7r/gtumOuZZvtq1io1h6sJtKo2WZ+dnNsTYfjG7u3Jo/2RHxcnrKsLmHdxLVpWyQct/az+FQaE1oz18tFt2EVq1r+DPh3bIjavWGx7W+lSVzvcNPXrzpj2RnP1z2YJ04H59WRZQ/9d6kwfdTTnL+n6yCLZ+YTR8N8y5+i6jw846/YemAOiHuyy7IFJZYk/+Q3sZLYaA4Ze3Hs61Xr01rvi2ZTIj6Xq17ryTksnyUxpn7DOeQD/I+hAw84CshOLrveNB6WLTqQhrH61MEgqDSE//rj/7NfvNtMPjDAXxyAbGtPSCMKvWMhZefeQNvq8ydNTXxkRhV3rGso7ly/s6V397qntCxqgofXK9ZHbk9hDqPmp5vhwBRzF03tle2O2TK+YaDVgtHZE6oJgNv51B2jtbxqiMvHjdx12MH9Fj8iUMK8t47o+W8dGLd1/sGqmVnEffu3btncO8g0eAf1YxJKgPFSsVGuwyNApNYOpYo1xoR8cGWNh2eAj2f3E4n+b+G3na82Vv3dOT7X9h/mZn7/51EFlOarYks5Dp4Tb6JicMkT5ivibdYb/hqo2PKmdJch5MT2c0fr8kpRaMk+W9Lhdhad8bk7dfkTG15iX469gN3jb0uvLT4iF1yeljh9WcjtRvV2N+WMPOZ4+3kPBk8CkwRZsV5N1Is44fCWyKZI+J16Xa5ynGytHS5H+hKe1ohpVi5Gydmb/50jJFtIzUq3gBFredq6reWZvh1vb73EcGLVpqflQQ/qbp4Bx4LEsisai4qajuq+SAp1V6xtK/WKvmF7uV2Z+24qU/x1xKVLD4e7o9x2GJOeN2s0GfFOjAVQsmq6ZySkx8pcElajiW+jw9Jl8PrM5RLEGDf/i3L7t2djvKPzTdDQ5nenOilxKYhLW2UxxwfHn47FfciB7MhtX2/5wcuW8Gwa592pBfF/sP4NZm/aUvRjZ7eWN0nqJyguAgDKrSeuP6rgtemfFua2iVPSx1TDzRhjjDpZHqJodpxsv+szOeiUOXxBB3vKqG91uLja5UenyiVPhA7dG2oYG9GJ1Er8Vq1rNKhvm8yhl5N9uPoi3Vu2RRf8zpaJnLHc/E5DGcJcXt6cxIPlLwPuvPk5I++1sEJHf9nSX+HbWLWRwzeFb3ZlZBbvyPpBnHwXPox76nLvXHtJeUWbZk1dXERdu0V+8zzk2gv9GTJqfXgEbue3zxGzfb99qsKXOSEscCT5J7r6a937Us68rhrs772eAPjs3vq0wFJpxt5DxWcS5Wydabl9LNKPS9Vd+J0oL5lvSndQP8lAd9dYQO6uiwX1eI43nRlxVQr8h1/sxkx2euv0a0RJ8BvwYFXRTcc9OGXl2zd/8L27bo3idzJgx29ee22DaX8OyzM918q9XvTQ5BoMI7mkcphkfbm76jVsvUvfn5PngFJQFSNVkeljX6x387dTfxg5xBagxhn2adfQZ+5fkr1jE/EOl2rOAHlraNf0ZGVpoHB8bHl0m6sIevZA/hLtl0vUL9d+vmG4unvd/FWj5kaDhyg7BFpVDRxP1a9kVfq/bP+7W8ZXLEbKeIKhLmtfdrX4IuFm5rVDQYk3+10KRvDKn8Z1tpKTIgZLbx0Kq91tJKvsuPhk+Zj7UQd3aIMO2EEa9F7r3f+xzJv47OH9yhb04iVDFbzj+Ad23I41/ffFH4kkUaMMBIcVmadbHEfkZpips7PKcS2pmVVlidFDezAtqa1vKq+8s+VU8Y6Vp7xhOEHkoe2Zrndr0B+Q3t+JPbLxERUmOFy8y039RwfczUvG6a9p4coq0Gl+nTcEfH1oGpQSZa+/aK92dpgwsq+2rAtJ10pcxLPLdCcXpOpcPe9LuJS7kvHeBfLKaeQbTdc2ngPpr/CHNRO29qgiTg7UaDB/GJfVyXl83RUecaBEcuIBsUBDaHS/F3u+syjrYEjbwuR/677J+SY0+FH6g7XvGmnYnsPmOAIeRPrWrWPmpz2DrjI87f/bn1cJkeqcDi3L16mdquIcolH/9vgdUVBfkaT/E9aNzUU8764je1zaXrZbPO53NUj6FqCtfvZpFO+iu+VpzKEZAOpdwjkjdT2buuuu65xMZUG2d9sMnbswgsLJGJ98OF3pfUn2Ao27vjwge8GftDGzRPvLHX35ai8Y5aiQSIuKvJD4UR5UIPJDzWB0Om04zS8V0w4fTmln6U9H1Ut6mtpoylqTmWPLt4jNp3GzExhzsKJq/ym9eW1HLcXl6vJCb1Ht7xik33YEv7JyR5pLB5qtEddo+GLduNplfEQw+XRXT8h+QorKbVLqckifUwrO0Cf8dCtzQeY8KonaToaGDd87Erd+0V3Oj/5jF5UZjTGZpyDpXNiyD3m0GOCzf3Qg/20eabhxl5vY827TlXWfRbOPI8f0s/UwmXrEMKDFJU7zzxrqDk15jC6P7xDHc3ryRdNX3P6htnhfslIbCxRXLuaJzwgU4eloCtMNruyOSKcgVVJga2IpkGjHY0l3mpIKrze945HaNCgzuRSYjYrLtTk+iHpbXVCkZ05Q7d3CVp94tmJeiOcd2IIoR87Ush5P0PTgcYqkOL4fVP9o8Ty3EDZqdefej6dfUt1pZhtWGAf5mywDbqAg3oXQ49s37B05AaPHBHWxocjTo/S6S+9d0ejT3TfHIlpLJY+3lCb3QPi3XrUVRjLt9tR+b3GyuhZazyWQXsjy7epnHvZHHKFMVLVg8UtTNk1deRQQJv0Yc24JhU0rfONpM17VJJ4C7Imd1b6pNn272OM7LeJQ6Brzu4dyOlXjtZlQtbemdj3bJr9d9ev/3ZABhMAl5mXa67Pvvng7LA+EkfwkNdb8epal2J+JqqPDtLBRWCQ4oxonupGngyGLc/Mo52tL04bxPQnGmgW7Y8IT3h2ICzcw7R9MrBwu092EoHLnmhIw1fYnzJ292tad+HN8FPyBcwf+BCn+UIL/P069IY2ae+n2ew/UPew0sK/76unPR+9w5PcdAGG+MPBu6tfyPMkyJee8rzR/WTvWN8U41xFmN4x03MAgIfL9vB/VrSK3s3P10A81uWoMwaPJWfIkLgRylykTMustBKtnWoo8uvbqplWRaV2I+Xv5sLjGBEgCADYvORLKyuUzBnj6+r563Pin7268pGBQdm64vAYRxzWHa4/JU1fW6+iEm1mqFNds027shKNNE5JbSeUSWZ5jIwyug0PezATamsksz6lpOlXoe+lzjdMhGjf9ssDAKSWFMNDToy9qyt+CSUVNSjteRnEURa3xTSYFitqLf8ljKyGI1iM3eIayrX0dGc1zPZoJLOIRz3xi+qY8L9Qvx0AgPpzHbO/4ToIRvhB77WhTEfNSkXKqjI/EQKN7oWKelkxMDAwyAldFVK459U3jtMI/fceg3Jo9PgdIbkJVqOMsO7uqIZ3QddfRJlSC10oaBC482a/xKbjIRvktMdFow0TjKUrOKI81oWHu0RHEYk662SIxuGXJZl3lza+2iPbnWQXg7l2MeaWdVAjSKZKwW06HoJ2Hi48QfW7yPLqi1uiAAATfzqJpVcxicl4JU123pDMkfk1t995uIqT5HPMCnnIzQ0CfA7M84grNe9aJQ+5Z5+SVl2JrhbV056bhHUVImVVRsYv76e27yntahe27wjm+Hybftud9s5Y/r/mP5NTjXfS/pyD+ks+Hk5yOlwwjqv5PMm/GNbMDyky/nkOD8tkwe2r1Oud+VC59j9QSS9CRfKkbFR820mpyHf756iWODHWUHBQzmuFxqXWAfrff5cY+PPnEgkuUiBouokZBnRrHogk5zOPQT4HNbcwf+xWWAMWpqLgAqDZow0wAV6/By+ViiLFg0aFOGB4PBRg6fzSUnUxw+p6TApFpjzyHe45jN7daygBuaQRvBpoIogLVo0qZPgiSSNSMGgUiAEGljgHBo8ULWUIFcyQsV8Aq/BBhgqQyw/BpUNzPnAf3CHDF8kPkYJBAz5wH17NgcGDQiv3QYwakKaC4NzQb2tMMG4sNSCfCiKFgMZx4BAlcxCksZ8FKiAJGziEIg0gn+IhhYDGWVhgEPfnIMgEc1aOwkULFs3crPx5BP5CWdG8ZITNy/xfQxfGaeAlQJMv8Kk5DUcgF6chBYOmXOBg1nRguejMymt7CgWDxWTgcqApFrgcCnqwXEyGFAyaV4GDuZKAkYnBrLy2OigYLPAClwPNpHDC5HAwgGUCL6RY0PAJHOs4CdbCTMtSla2FVfYRikUaXiE5NyDBEvipKsQIVhBeIcWDBkfgeNEL8ciEU1ZeJDUTWDx8AhcFjXpww0TpLAAhEz4hhYOGM+BwXxbCLUyLLLUp0cE2pWBmQC7fsfhBwQZT82JuONl8BykONFMBx9m2FiwR4CDFgQYlWOGXBxgOSR6DFAYae4Bfz9azgKUzFqRQ0FgCOwzqAikUSWpiqUdFD3tUiqyAbKhhpZcbf1ZALtQALwQaMYBf5vMgw8mEGpbSwQDTsYMNkM8nkOyJkI4d3FJfGMDCfAIpErRTBj/YPXjBov2+lR/sGnyAtMcGFwBtgcFLCeADS/bYlnKVFeZqLxRpYQMNLgjav+KDCZLlBytuoC14fYJ0pHhgqIXkUMl1NBbsg5DmEhxSYwtYWWOLFBLayoFDVpGDJNenWPlTIQiApTpCcGXQrg1c2TgZmBX5B23QwCExgmBlzR9SSGivBQ5ZRg7yT/3jhPlntBUs27KBy4P2VPhh8m4shkWuZUMKC+2fwGHphMDK2zMrP5g9ILCQjYWa5ucfKANl4M8GAOHn+wr4vwAAAP//lfLuQkM3AAA="); err != nil { panic("add binary content to resource manager failed: " + err.Error()) } } diff --git a/os/gres/testdata/testdata.go b/os/gres/testdata/testdata.go index bf6f3feae..b228bde1c 100644 --- a/os/gres/testdata/testdata.go +++ b/os/gres/testdata/testdata.go @@ -3,7 +3,7 @@ package testdata import "github.com/gogf/gf/os/gres" func init() { - if err := gres.Add("H4sIAAAAAAAC/7SaCTTU6xvH3+xZQpYUIWKQMQyRZCnKMpaxRimNNWKEka0FpSxJKRRtF0W61ogkXZV935KUohDZR0Tof8rF/MaMJt1/59TUyfk83/f7Pu9veeaLRlHTcAIGAMAVt5toQPCLGawG9o7Odh4IG1esvaODqQktWNX6JuUgGkXPQPiD5BEcRAi4jacHztXllyQGMGLnCCEJkCb9+y8pnKuL809q93b7gxKVunCElmQ5OsMU3VBbVqvXjEAgWhDa1fAybUltuFlnqJSOXrnkhS4jNBqtVS5ZYwbuyckhW2TrZOtks5Gy8rlNMoZUKBhM1CHR+pK8KRUNAN+//9BqoiHfZgYAsF9W6wYyWl18pByxjv+pTONFmXsXZUrTd535tUweIpn/Ny/RiyJNFkVef3bR9tciiTvo/2Gi0aI+s0V9XwPUlYj1Le1wxgV9to7uMis4ImsgAISHp/UKTgffEsiP33CcnQdORgrnjVu0S0qvAa4Nl5Ix0aqp3aJdvWXVwnoLH1zevg4AwL5sJTZopZ8VFumkycHUqrMcAAAWyp1E/qmTyP/CSSTESSRpJ42WrNcvgp/6N51E/nQSCXVyKVlPNav0d5x0lNmGpcBJYmlrIAC4raP7gjDKt4N9CQRhh/2jHSHgIA7bOTu7Qi9VnRVa1eXlezNM0aNA5gpYNW+a+2cnG86fhv9+GS9Xd2dbojIStYgKbcMMUzQjPWGZG+yeMOIyFLnihPlvXHHC/OuKD2aJK7lGaN832ybdGj+Wht8XUJA6/ibv4Gq2eekF9hIWggCA9SsoOecQpORPgzKN0L5ZNq/x95R1/MF8oZs7rZy5AQCsv+uRu+cKPOIlxfnXI8J7yELf5Et/t34etHGd/IFXvpnai/44H98kthEAwL2CcnP+QG5Z8/2Tn9Hz7EIYC4E/dp8HHq/7ZQ9xLi3kexiurv9Hj1VQ1L8uOXm4Yhd0p3T/uIei59zK+rG9XkWNd/yi8pxWLRyCwk1M+b/uJLJV58wiVXXOtKzFprLMWbSNcQA1S1yVUttM9q7ANn4yqH9t8yY4DFmZ6WXVhmIouERNVe0DU2STLNpIqxpVY1Behco1lYBrN+ags3I/ZJXLZZc9+PBjrRnlP0+s35y9LmObdkjxnshgVFtYLpsvd4QBAMBgZRrnTP5DjeI/z7jf3GbIT7Hxx4SrmNMKzkucEtWN0ieSuHRHWKESf/x1BZuxbikFYYdd7t6QV4EPczdSmz7Kpr54yG284GoCAADB3y7mhKH0khtyf8dTMVixzs7e8guOtUnjYquo5stzllSaywAAxJctz02ivLsnJVezudK0kgXE15xnRogZSQAAjMJ3msXCP88sidO65AqBUwvsmtytKH6oaGPjTYMBw84U2oVHGhU04qEUAEBy2fLrSZc32fufn7VgXZ+Thvqhby//lJpZf244z3msyHfxEleuVtGIAQA4Lit4LUTw/68fieo4YcjVIdOLtJvxtqXZPcZ+BE8yhg6Cx3604pbfqezuSa4yhX1osy3/CBwAILZsVS5o1bkWhBReXPH8/emnu/9QKd1VvvU6gYZhvmBve0W4CABg028WNNn7HxZc7und3dUV90fvQT8ACBsPjxVcVHmWQBAeOB9nO6l53I+VNxgl/7w7a1XWbqmH1/ytk2MkLS+RnEHHFJ1oduWTaLft3R4L8c1J3VGJ3BYezVQLFzuJduu+rQAA5LILYYNqcHTBOKzk/rCeBAbh7OrgKnUU67CwFucYn21NR3hLk5BRIULbo+uualjYV53hUcdIj7uUGcnZl5dJ2ov6tZa6ozlaH3CWR27dbyJ0RpEzS+RbpE3Fscg71yfc9Yd8aj3b8ZYt+NKh2ROqExOF6d9me0IyH8px02gHAuB/ezWdCxMAbI8n3p8EQNBDJZ8KgH7M6w2fqXUe3/nxdKm68cMLGrAmpL9d2X9M1w0cchVf/7nMPVtas9ORU+yCmgds14wY7p/BnZObZsRwjJeCqL8ZFiTrJt5a85GVNrJCY/Ashm1YYO3LrRuDE4K2vvIvEQxs/ssJbRosxcBMl8BDs7aB22WM2oFNazsbK3fYhTNFx0+c+KvQ4S+5ElMeSxSK6W6o/yY2CWYq7aBeA7uOaZPkqsunRCdd+eUuhThc7JVEhjO2z5Q8akx7gBO8KBCrWhuxXXzs6fnkKoEDHY6cVwUurDmtbor5Tsc+fEpm7DNzuqqO8HuMBkIoSMtOBnNUbNpKkfuaxpRX7lvVU697ZyPUJw0/aVHTsZcKxbtJxkvt4kq7E5+SFp8SECR80YFdKy9flnGctf5phxxd9B58Q3NbDjXvpfUaVhs+bOBwPpQ0rjB97pNAAW24mujk91tPthsdoPYxw5+beadx6vlq780hJbffm+fbMg5KsO9JOri7aHbddJx7rl8i12ndxKyjj9UbgxIOfV/3bcgf8/3q8K3P1N8yVfMFOfvTDzTRwh+PDVEDtSLRw8p/0w9fTmBiURJzm5T4IpFz94RqrqnlzuP7Pomh310wkOkfF/Z7fFDY2C+4cGb9t1mYmYJKZ5jn7swgTeeHrOd3KNx9cXanUEQgTHmoslKU4av51dP3P08OBxxUfvAmm7bHJS36vH+BSHt/ldPodoWBs43eA/gJm72oGofQGN1Pvh+x5870RFtdtbj+GbQbjQvbiscwKaaznz0ZY36qMHBTU1lMi4XqfQtpUy7eN37WwxOe1hdfKFx9c4VBOf8Tfr0EPdOVHLkNWO387JsCfOpK9f4uQfzeDPXDx1xF7ZjSPr84hI0rp2ppxQl2weP3vuozCmpJfH4tmLnytNC13H4J4ZcFGyXT2IvdTl8KvYDUgbF4J3E14fQ0w+DFuxmuh5iYPXnly7dvUG4gsJPtmIrm2z2qkrqpJrPV9RfyNLAcAQbnMcIpXfWNfPb3D1fZ2Pib+RiJ1Ivn0hp1PtVQiX6niXw+Yx80XvOah10cZzXGehjdyfCksupxwsdL31EbdNwDThmEGo3epeM5sBXHLXXsfl9PRBCfnnDjxygsXntYPPvbl/qXHpG3H0bPrN+t9eV8MOuuB71uqrVTw1w8hpvp2R5VwOCOterPn9A69B1N0vW+oE3nrFiXa+Al1DBp9Dh9sNdkO5/5l/IDmGFL1x4ny9QH48/HxIrWhuWz0D9+LrpKfUBV8Yzl7D/PY67BEDYDWePrDrwy4RhZFcdXEWZefK3D7r2J5Biu414Bqm4koE9G5vDH9CD1zQ6wsFdeG2CWdbDD6bJYwV3Y4rgap2G1IvqzOsIszVMb37BOCh2x3wML6rAxV8gee2sqqGrWk3TLTpHnaKyixt3xmCb5nZu+2DAlPlQbONt8pCxfiTdPcWwjVcCzzIOolNZK9YS10u4YwaLAcy0sQxfdeO+Yafnk2Cg1S6QHuak2W5SbndsYY/3O8Ob2zQXT3eF1szPo1n7+aUSvikHKfStfh4MhtSH9R9rN+7I6tGJxZ8P65MbVG9EntL6UJ3O/wU1fuukH5+A4VP5graSAgFZlpB/tnqSRtzNO8n4fLIMsnprOHg31KmmDVfp6xd+wcECclPThEGYOTawteLja+pjicEnKmvfnOq7Fzmq2lU4nRH6r0b1XGnxFIXL32fuM5+EP8t8FDz8QKM1ydN/2oOOQePXFNM5Xp7OyhJWf+l1//Cjn9ZeRoHf7ccmF+M4u/1Ca1LPmx3zNmvg7ZO+sqo2PwKjxf80+mifv51z1pU33pI5lddjIOs2aiK3BtPm0qwW2CeEl3HVjBuT6gmecbzhotXNG5IZoMvL3jObkap2oPvziSQtPo92wHqsfflRRwWt7lPwxnRj3dT4B6jnZ+D179uwa2TOCN/hbLWOaxkCpSqnZNmN3oXEMA2uka62Y5Eh7pw5foZ53Xo+TwpHRtu7Xexr+Gf92xO4RC8vQo2l4CbXpqogi7gOxCi3MnMb5ogIt/CV6Y9eaHVPOluU5nJrKaX0fK68chZAWvC0TbGPVE52/T5Mrtb0O9c/X79hYjoawspLDtsnpoUXXn43Xb1DnaCtlETDD2cp7MnoUmsBMS/JvpFjEj4a1R7CEx+sy7HCV52Jt73Xf35v2T6WMUtVOrIS92T9fmdg30CLiDRC0eq4mvmvoxl416nsdFr5kqflRWfiDmotXwPFAoczq1uLizqOaD5JS7ZXKBustk1/oXuly1o6b+RAfm6hs/v7QULTDJrOsV62Kg5ZswzPB1Gyazim5BRFCl5HyrPGDAnCGXH7v0bwsIY6tX7Jt39zprnjfejMkhPn1yQFquzS4hbXKV8eHh9pm4l7kYtandu3zfMdtIxwa+2FbenHM30yfkwVbNhXf6B+I0X2KyA2MCzegQelJ6r8sfGUisKmlS/qMzHGNAGOWcOMe5joMzbZTQ+dkPxaHqEwm6HhVi+yxkpxco/zkZBlyf8xo7GjhnowevFZibI2c8sHBL7LoYy32k6hLDW45VJ/zu9un8ibzcLmM57Lidg3kJu4vfRt45+mp74MdI1M6fs+S/grlZdGHjdwVv9mbkNe4LekGfuR8+nGvmSsDcV2lFeadmbUNceG2XZV7zQqS6C/2Z8ur9+NgO57fPE7L/u32y0psxJSR0NPk/uvpr3bsTTr8pHejvvZkE9Ozexqz/klnmvkPFp5PlbFxpufytUy9INNw8kyAvkWjCcPw0GUhnx2hw7q6rJfU4zhf92ZH1ygJnHi9ETY94Le7b3eckKA5J04N1XTAW1BBumPfC5u2ta8TeZJHugfyu2yaygS3mZvtu1zm+7o/S6rJKIpPJpcV6SXYXa9l41fy/J4CIzwLVj1RE5k28cl+K08f/p2tQ0gtbJJ1r37l6sx1M2pnvcPX6lrGCalsnviMiqgyCQiKj6lAurEFr+PwFyzdcr1Q43bZxxtKZ77dxVk+YW7av5+6X6xZydj9eM0Gfpm3z4a2tjG62m2giisU5bHy7lqFKxFtadUwGJZ+s92l/Kudyqcxrc34hOiJosun8zsmqgSquh8+bT3ehdfRLc6wFYWxFb899sbveOZtXM7YLhUrOonSkRrBcZxjZy7XuqGboo+l0vDhhsJjKmzT7e7jMjMstAW5RXYdadlVFUmRw9vsOtLaX9Zc/fvqaSMdS8/4rLEH0gc3Z7vdr4R/QXm+xw/JRodXmmLzCix4+098dTUrH6O/pwcrr0WkenffEfPxoGlSTUbeftHVamUwZWlfg+7MTVfOnMbxCLWm12Yq3n2rC7ucV+cY72Ix4xS85YZLJ/+B9JeYA9ppm5s0YeemCnezvNjbW0X9PB1RkbF/3CK8SWl4t0hZwQ53fZaJjoDxtiL4o7V/Bx93OvRYwyHWi34mZmC/MTYrf2pth/ZR4zNe/pf4/vLbqY/N5EwVDePxwcnWbxZTKfUYagtaWxzoazgt+LSDt6mE/8Vtu0GXlrpW648Vrh6BsQlW7ueSTvsovVWZyRCRC6DdJpQ/Xj+wpa/h+u5LqXTwoVbjr8cvvjCHw9YFHXpT1niSvXDDtnfvBG7gRqzdPHHOMnfrJhQcs5UMErGREe+KpioCm4y/qwuFzKadoOO/aszlw4V8lvZ8Qq14sL2Trrg1lSOqZJfEbBoLC5UZKxe26ovWp1fyPMe4XY1P6j2+dSwm2Zs94e/cnPHmktFme0QsnUCUG1+HrIcENp/h+knpl3bSMjuUW8zTv2rl+OszHby1cT8zTu0UXXcT0/r3val7PunOFiSf1YvMjMJYT3Ky9kyNukcffJJlfT/kwBB9vkmY0bG2GLPe01UNH0UzL+BG9TO1sDk6WWGBSio9Z5811Z7+6jCxL6xbA8XvKRC1uvbMDdNDQ9IRdjF4Se0avjD/TB3Wwt5QuZyq1vAwRjZlRfZiuqbdXSg7/K2mpKLrg2/4REYMGowvJ+awYUOMrx9EbmkQiejJHb29Q9jyA992xGvR/JOjMP2Y8SKu+xmaDnSWAVQn7pvoH8VX5AXIzbz60P/hXBvN1RL2MaG9mHNB1qhCTtodjP1yg2PIiPUeuWJszQ/HnR6nr7781h2FOtl3czy6uQR5oqk+px/Eu/VrqDJVbLWl8X1lJ6tntfuJLMoLXrFF9Xxda/BVpgg1D1a3UBXX1PGD/p3IQ5pxLaooeucbSRt3qSbxF2ZPb6/yTrMZ2ssUMWQdB0PVntsznDukEqXLDK+/M7X32SzHwlx66LZ/BjMAV1goe4Ode//B2tp5Sx3GEbzBStbUu5QIMtO8d0AGFYMRqrPi+Wob+DIYNz0zi3K2ujRrED2UaKBZvC88LOHZ/tAwD5Ou6YCird45SVnc9ng0nUDRUMrXu5/T+opuhp1WKGR5JwA7IxBS6OfbrTfKq72PbqPfcMPDKnO/wc+e9gKrHZ7mpQsxxh8K2lnzQoEvQaHstOeNvqd7vg7OMM2vCzPw1eQ8AOAhhe+5ODuXo84Y3J+9HM5DSBk0KmmIMBMr1zItq0Jpp6LF/v162VSrskq7mXphlvUkWgwIAwA2Lqube2lJZ4yPqydOZgX6RcjCEDauWBzGEWvnDl1LSpq+tl5lFcoUrVNTu0W7qgoFN0pJ7coql872GJ9gchsb82DJqq+Vzv6QkqZfjbqXujirE6FvG1IAAMgsK0mIvCR7V1fcMnoqaxHai2LwE6xu5JSYlChp/XpEtYySw3YYW/JKKrT0dOeUzA0JpbPxRz1xZNVM+V1s3AoAQKxUzdwnVE2WIW7Ea00I81HTMrHy6swPWQGG90LEj1kyMjIyyotcE1G8d2xwErs75NE9RpWQqMk7IvJTbIYZoX19kU1vAq+/iDShFblY2CR05/U+Kd4TwevltSfFo9AJRshKzkiPtWFhLlGReLzOWlm8UdgVaZadZc0vd8n1JdlGY2IvRd+yCmwGyTQpWN4TwSjnsaKTNAtLrai5tCkSADC1shZHrqDFyfqGJNlPRL2zeDr3OY9VcxF9+fhb1Uj1TBa0NxarSSq37vijaqR6IiWtpgpVI66nPd+iDZVi5dWGRnX3U7t2lfV2idp3B3F+vL16y52unhjBI4vBAZrJHvofHaq/7LbxklfjgnFcyZf3ossTf/4hQ8JRz7Ex2WyooVV6Az+/xl/zxwWRZAoS7aC1qk8XcUEAlC1hRoQFaecL/uTdHRm3WurOKipOavIZu/lh58DOH59kEnfkERwQhDoRYmnibp405x1hok1ggQRAYkA4aRLJlBgxlTCAtgFCVVwFKMnGEQMJw2I8EOB7IiBF+gizXRwQXBAVWDZvttxOMEJ2QoQakE6GkQesgQDsIQCiPBN0PYTZLT7Ieu4vgZBJhhEjCUNbbBAkFw0gHwGj3B9XQgxyBf5kQADL+kOYyIL682UJhEzeixhJGMWC+mNFC8gHuyj359EChijFtQggldtaBExBAEQpLvIq2CEQVTqwbIoLagph1Arq8zlSHFIpLmIkYawKihwkiSSR2KJ8tab0YNl0FlQaYWwKKu0GKQ6pdBYxkjAgBUWyMQDK0leUr9ZnKRKSs4JKIwxB8UKkFZHikMhZERMJ005QovRqQFGUarm1ckLWem0pkTgzRXRJJUgyQW+MH8mgSGWmiKmESSUodR8joDgTRfmy60hSIZknqEDCbBE/RCAvE6A080QMJUwDQaFPyUGXhpSWWzQrZNHWzGCZWBFUGmHKYh1EWvJSypIYBzGNMMQDpXGxgF/mhohphMkcbgjtCgkaUQyIGEaYs4E+krGsAZREe4iBhDmY9RDgedJAorDOcjuxFsJTZgXLZWmIQYTxFSjoDhREHJZZooggkQIFCbCB5bIvxCDC4AcXBBQFBZGIs1DOomMHv0iqUP7w4bXAIgqhUPpwlgcBEIVQoCsiDINAn+1nlkBIhFCW08QG0RSwFiyXJyG63RHMVqHtXUYCszRPQswjnGlC961MAPxiPku57bcFAfmZKFQQ4bASusCuJZDfFcUNEWW+Cfx64AkVRzhpFIGeYbIwcgPPJa+8BLNDIQh7mxD4ncnlktscwRgQCn5GHkxqqEQMJhy6QcFoYfA7M0XK90xpM/j1BA+qknC+BlUZTBZGka+EozQoeJw8mBJfCadiULCtCPidudxyvvJCfB0lDyYasUGlEk6/RCFSNUTB747YiOGEky4oPIcCOJK8F6SGZfNefN8pCgNLRme0dD/+VxWogi0cAFj/ePQB/wsAAP//umpbUqM6AAA="); err != nil { + if err := gres.Add("H4sIAAAAAAAC/7SaCTTU6xvH3+xZQpYUIWKQMQyRZCnKMpaxRimNNWKEka0FpSxJKVTaLop0rRFJuir7viUpRSGyj2yh/ykX8xszmnT/nVNTJ+fzfN/v+7y/5ZkvGkVNwwkYAABX3G6hAcEvZrAa2Ds623kgbFyx9o4Opia0YFXL2+SDaBQ9A+EPkkdwECHgNp4eOFeXX5IYwLCdI4QkQJr077+kcK4uzj+pXdvtD0pU6MIRWpJl6HRTdH1NaY1eEwKBaEZoV8FLtSW14WYdoVI6emWSFzqN0Gi0VplktRm4LyeHbJatla2VzULKyuc0yhhSoWAwUYcE60vyplQ0AHz//kOriYZ8qxkAwH5ZrRvIaHXxkXLEOv6nMo0XZe5dlClN33nm1zJ5iGT+37xEL4o0WRR54/lF21+LJO6g/4eJRov6zBb1TQSoKxHrW9rhjAv6bB3dZVZwRNZAAAgPT+sVnA6+JZAfv+E4Ow+cjBTOG7dol5RePVwbLiVjolVds0W7asuqhfUWPLy8fR0AgH3ZSmzQSj8rLNJJk4OpVWc5AAAslDuJ/FMnkf+Fk0iIk0jSThotWa9fBD/1bzqJ/OkkEurkUrKeambJ7zjpKLMNS4GTxNLWQABwW0f3BWGUbwf7EgjCDvtHO0LAQRy2c3Z2hV6qOsq1qsrK9qabokeAzBWwat409y9ONpw/Df/9Ml6u7s62RGUkahDl2obppmhGesIyN9k9YcRlKHLFCfPfuOKE+dcVH8wSV3KM0L5vt026NXwqCX8goCB1/G3uwdVs89Lz7SUsBAEA61dQcs4hSMmfBmUYoX0zbd7g7yvr+IP5Qrd2WjlzAwBYf9cjd88VeMRLivOvR4T3kIW+yZP+bv0iaOM6+QOvfTO0F/1xPr5JbCMAgHsF5eb8gdyy5vsnL737+YUwFgJ/7L70P1n3yx7iXFrI9zBcXf+PHqugqH9dcvJwxS7oTu76cQ9Fz7mV+WN7vQob7vpF5TqtWjgEBZuY8n7dSWSrzplFquqcaZmLTWWZvWgbYz9qlrgqpbaZ7F2BbfxkUP/a5k1wGDIz0kqrDMVQcInqypqHpshGWbSRVhWq2qCsEpVjKgHXbshGZ+Z8zCyTyyp9+PHHWtPLfp5Yvzl7XUY37ZDiPZHOqLawXDZf7ggDAIDByjTOmfyHGsV/nnG/uc2Qn2LjjwlXMacVnJc4JaobpU8kcemOsEIl/vjrCjZj3VIKwg673L0htxwf5m6kNn2UTX3xkNt4wdUEAACCv13MCUPpJTfkwY5nYrAinZ09ZRccaxLHxFZRzZfnLK4wlwEAiC9bnptEeXdPSq5mc6VpJfOJrznPjRAzkgAAGIXvNIuFf55ZEqd1yRUCpxbYOblbUfxQ4caGWwb9hh3JtAuPNCpoxCMpAIDksuXXky5vsvc/P2vBuj4nDfVD313+KTWj7txQrvNooe/iJa5MrbwBAwBwXFbwWojg/18/EtVxwpCrQ6YXaTfjbUuyuo39CJ5kDB0Ej/1oxS2/U9ndk1xlCvvQZlveETgAQGzZqlzQqnMtCCm8uOL5+9NPd/+hUrqnfPtNPA3DfMGetvJwEQDApt8saLL3Pyy43NO7u6sr7o/eg34AEDYeHiu4qPIsgSA8cD7OdlLzuB8rrzdK+nl31qqo2VIHr/5bJ9tIWl4iKZ2OKTrB7Mpn0S7be90W4psTu6ISuC08mqgWLnYSbda9WwEAyGUXwgbV4OiCcVjJ/WE9CQzC2dXBVeoo1mFhLc4xPtsaj/CWJCKjQoS2R9de1bCwrzzDo46RHnMpNZKzLyuVtBf1aylxR3O0POQsi9y630TojCJnpsi3SJvyY5F3b4y76w/61Hi24S2b8SWDsydUx8cL0r7NdodkPJLjptEOBMD/zmo6FyYA2J6MfzgJgKCHSh4VAH2YNxu+UOs8ufvj6VJ148eXNGBNSF+bsv+orhs45Cq+/kupe5a0Zocjp9gFNQ/Yrhkx3D8DOyc3zYjhGC8FUX8zzE/STbi95hMrbWS5xsBZDNuQwNpXWzcGxwdtfe1fLBjY9JcT2jRYioGZLp6HZm09t8sotQOb1nY2Vu6wC2cKj5848VeBw19yxaY8ligU071Q/01sEsxU2kE9Bnbt0yZJlZdPiU668stdCnG42COJDGdsmyl+3JD6ECd4UeC6ak3EdvHRZ+eTKgUOtDtyXhW4sOa0uinmOx370CmZ0S/Maao6wh8wGgihIC07GcxRsWkrRe5rGlNeOe9UT73pmY1QnzT8rEVNx14iFOcmGSe1iyv1blxyalxyQJDwRQd2rdw8WcYx1rpn7XJ00Xvw9U2t2dS8l9ZrWG34uIHD+VDimML0uc8C+bThaqKT328/3W50gNrHDH9u5r3GqRervTeHFN/5YJ5nyzggwb4n8eDuwtl107HuOX4JXKd1EzKPPlFvCIo/9H3dt0F/zPerQ7e/UH/LUM0T5OxLO9BIC38yOkgN1ApFDyv/TT90OZ6JRUnMbVLiq0T2vROqOaaWO4/v+yyGfn/BQKZvTNjvyUFhY7/ggpn132ZhZgoqHWGeuzOCNJ0fsZ7foXDv5dmdQhGBMOXBigpRhgnzq6cffJkcCjio/PBtFm23S2r0ef98kba+SqeR7Qr9Zxu8+/HjNntR1Q6hMbqffT9hz53pjra6anHjC2gzGhO2FY9hUkxjP3syxvxUQeCmxtKYZgvVBxbSply8b/2sh8Y9rS++VLj69gqDct5n/HoJeqYr2XIbsNp5WbcE+NSV6vxdgvi9GeqGjrmK2jGlfnl5CBtbRtXcghPshMftfd1rFNSc8OJaMHPFaaFrOX0Swq/yN0qmshe5nb4UegGpA2PxTuRqxOlphsGLdjPcCDExe/ral2/fgFx/YAfbMRXNd3tUJXVTTGar6i7kamA5AgzOY4STO+sa+OwfHK60sfE38zESqRPPoTXqeKahEv1eE/lixj5orPoND7s4zmqU9TC6g+FpReWT+E+XvqM26LgHnDIINRq5R8dzYCuOW+rYg97uiCA+PeGGT1FYvPaQeNa3r3WvPCLvPIqeWb9b6+v5YNZdD3vcVGumhrh4DDfTsz0uh8Eda9RfPKV16D2aqOt9QZvOWbE2x8BLqH7S6EnaQI/Jdj7zr2UHMEOWrt1OlikPx16MihWuDctjoX/yQnSVer+q4hnL2X9exFyDIWz6M8fWHXhtwjG8KpavPMy86Fq73QcTyVFc+/18VO1wQK+MzOFPaUHqmx1gYa+9NsAsa2GH02SxgruwRbHVTkNqhfRndYRZmqY2vmWdFDpivwcW1G5jrpA1+s5UUNWsO/G2nSLP0euKGvfGYhrld276asOU8Eit/2zTkdI8Jd5cxdGNVAHPMw6iklsq1OPXSrtjBAsDzzWzDF50471rpuWTbaPUJJEW5KbaZFFmdm5jjPV7w1vbN+dPd4XXzs6gW/r4pxE9KgbJD6x8HQ6G1IT0HWkz781s17qOOxvWKzem3oA+ofW1LIn7LW760i0/OAfHobKHayUFBLQqIv1o9yQOv5txkvf7aBlk8cx09mioV3ErrMLXK+6mhQPipKQPhzBzaEJN/qPV1scUh4qT13w4137t+qxma8l0fOS3at37JcFXFCJ3n33AeB7+MO998NBDgZJMR/dtD9sPiVddTOV8fTozU1j5md+NJ4+z33wdDnq/H5dUgO/o9A+lSTlrfszXrJG/Xfbuqpq4CIwa/0TW0Vx5P+fKr626J3Usq8KG12lWR2wNps2jXS2wTQgv4a4b0y/XGzzjfNNBq40zIidEk5G/eyQ7R+tE1eGXT5t5GuyG9Fj98COKCl7bo+SP6cS4r/MJUM/Owu/Zs2fX8J5hvMHfaunTNAZKlUpNtum7C4xjGFgjXWvEJIfbOnT4CvS8c7udFI6MtHa92VP/z9i3I3aPWVgGH0/Di6lNV0UUch+4rtDMzGmcJyrQzF+sN3qtyTH5bGmuw6mp7JYP1+WVoxDSgndkgm2suqPz9mlypbTVov6Z+I69zlEfVlp82DYpLbTwxvOxug3qHK0lLAJmOFt5T0aPAhOYaXHezWSLuJGwtgiW8Dhdhh2u8lysbT3u+3tS/6mQUarciZWwN/tngol9Ay0izgBBq+dq4ruGbvR1g77XYeFLlpqflIU/qrl4BRwPFMqoaikq6jiq+TAxxV6pdKDOMuml7pVOZ+3YmY9x1xOUzT8cGox22GSW+bpFccCSbWgmmJpN0zk5Jz9C6DJSnjVuQADOkMPvPZKbKcSx9WuW7du7XeUfWm6FhDC/OdlPbZcKt7BWmXB8dKh1JvZlDmZ9Suc+z/fcNsKh1z9uSyuK+ZvpS5Jg86aim339MbrPEDmBseEGNCg9Sf1XBa9NBDY1d0qfkTmuEWDMEm7czVyLodl2avCc7KeiEJXJeB2vKpE9VpKTa5SfnixF7o8ZuT5SsCe9G6+VcL1aTvngwFdZ9LFm+0nUpXq3bKoveV1tU7mTubgcxnOZsbv6cxL2l7wLvPvs1PeB9uEpHb/niX+F8rLow4bvid/qic9t2JZ4Ez98Pu2418yV/tjOknLzjoya+thw286KvWb5ifQX+7Lk1ftwsB0vbh2nZf9251UFNmLKSOhZUt+NtNc79iYeftqzUV97spHp+X2NWf/EM038BwvOp8jYONNz+VqmXJCpP3kmQN+iwYRhaPCykM+O0CFdXdZL6rGcb3qyoquVBE682Qib7vfb3bs7VkjQnBOnhmo84C2oIN2+76VN69o3CTxJw139eZ02jaWC28zN9l0u9X3TlynVaBTFJ5PDivQS7KrTsvErfnFfgRGeCasar45MHf9sv5WnF//e1iGkBjbJule/YnXGuhm1s97ha3UtY4VUNo9/QUVUmgQExcWUI93Ygtdx+AuWbLlRoHGn9NNNpTPf7uEsnzI37t9P3SfWpGTsfrx6A7/Mu+eDW1sZXe02UMUWiPJYeXeuwhWLNrdoGAxJv93uUjZhp/J5VGszPj56vPDy6bz28UqByq5Hz1qOd+J1dIvSbUVhbEXvjr31O55xB5c9ukvFik6iZLhacAzn2JHDtW7wlugTqVR8uKHwqArbdJv7mMwMC21+TqFde2pWZXli5NA2u/bUtlfVV/++etpIx9IzLnP0ofTBzVluDyrgX1GeH/CDstHhFabY3HwL3r4TE65mZaP09/VgZTWIFO+uu2I+HjSNqknIOy87W6wMpiztq9EdOWnKGdM4HqGWtJoMxXvvdGGXc2sd41wsZpyCt9x06eA/kPYKc0A7dXOjJuzcVMFulpd7eyqpX6QhytP3j1mENyoN7RYpzd/hrs8y3h4w1loIf7z27+DjToeeaDhc96Kfienfb4zNzJta26591PiMl/8lvr/8dupjMzhTRMN4fHCydZvFVEo8BluD1hYF+hpOCz5r520s5n95x27Apbm2xfpTuatH4PV4K/dziad9lN6pzKSLyAXQbhPKG6vr39Jbf2P3pRQ6+GCL8cTxiy/N4bB1QYfeljacZC/YsO39e4GbuGFrN0+cs8y92nEFxywlgwRsZMT7wqnywEbj7+pCIbOpJ+j4rxpz+XAhn6e+GFcrGmjroCtqSeGIKt4lMZvKwkJlxsqFrfyq9fm1PM8xblfjk3pPbh+LSfJmj/87J3usqXikyR5xnU4gyo2vXdZDApvHcOOk9Cs7aZkdys3maRNa2f76TAdvb9zPjFM7RdfVyLT+Q0/Kns+6s/lJZ/UiM6Iw1pOcrN1TI+7RB59mWj8IOTBIn2cSZnSsNcas53Rl/SfRjAu4Ef0MLWy2TmZYoJJK99nnjTWnJxzG94V1aaD4PQWiVtecuWl6aFA6wi4GL6ldzRfmn6HDWtATKpdd2RIexsimrMheRNe4uxNlh7/dmFh4Y+Atn8iwQb3x5YRsNmyI8Y2DyC31IhHdOSN3dghbfuTbjngjmndyBKYfM1bI9SBd04HOMoDqxAMT/aP48twAuZnXH/s+nmuluVrMPiq0F3MuyBpVwEm7g7FPbmAUGbHeI0eMrenRmNOTtNWX37mjUCd7b41FNxUjTzTWZfeBOLc+DVWm8q22NL6v7WT1rHY/lUV5wcu3qJ6vbQm+yhSh5sHqFqrimjJ20L8DeUgztlkVRe98M3HjLtVE/oKs6e2V3qk2g3uZIgatY2GomnN7hnIGVaJ0meF1d6f2Pp/lWJhLD97xT2cG4AoLZW+wc+8/WFs7b6nDOII3WMnqOpdiQWaaDw7IoCIwTHVWPE9tA18646bnZlHOVpdmDaIHEww0i/aFh8U/3x8a5mHSOR1QuNU7OzGT2x6PphMoHEyeuPcltbfwVthphQKW9wKwMwIhBX6+XXojvNr76Db6DdU/qjT3G/jiaS+w2uFZbpoQY9yhoJ3VLxX44hVKT3ve7H22Z2Jghml+XZj+CZPzAIBHFL7n4uxcjjpjcH/2cjgPIWXQiKQhwkysTMu0tBKlnYIW+/frZVOtikrtJuqFWdbTaDEgDADYuKxu7qUlnTE+rp44mRXoFyELQ9i4YnEYR6ydO3Qtyan62noVlShTtE51zRbtykoU3Cg5pTOzTDrLY2ycyW101IMls65GOutjcqp+Fep+yuKsToS+dVABACCzrCQh8pLsXV1xy+ipqEFoL4rBj7O6kVNiUqyk9esR1TJKDtthbMkrKdfS051TMjcklM7CH/XEkVUz5XexYSsAALFSNXOfUDWZhrhhrzUhzEdNS8XKqjI+ZgYY3g8RP2bJyMjIKC9yTUTx/rGBSezukMf3GVVCoibvishPsRmmh/b2Rja+DbzxMtKEVuRiQaPQ3Tf7pHhPBK+X154Uj0LHGyErOCM91oaFuURF4vE6a2XxRmFXpFl2lja92iXXm2gbjbl+Kfq2VWATSKJJxvKeCEY5jxaepFlYann1pU2RAICplbU4cgUtTtY3JMl+IuqdxdO5z3m0iovoy8ffqkaqZzKhvbFYTVK5ZccfVSPVE8mp1ZWoanE97fkWra8QK6syNKp9kNK5q7SnU9S+K4jz053VW+52dscIHlkMDtBMdtP/6FD9ZbeNl7waF4zjSr68F12e+PMPGRKOeo6OymZBDa3U6//5Nf6aPy6IJFOQaAetVX06iQsCcMZa2oiwIO18wZ+8iRaqQ0vdWUXFSU0+Yzc/7Ozf+eOTTOKOPIIDglAnQixN3M2T5rwjTLQJLJAASAgIJ00imRIjphIG0DZAqIqrACXZOGIgYViMBwL8QASkSB9htosDgguiAsvmzZbbCUbITohQA9LJMPKANRCAPQRAlGeCrocwu8UHWc+DJRAyyTBiJGFoiw2C5KIB5CNglPvjSohBrsCfdAhgWX8IE1lQf74ugZDJexEjCaNYUH+saAH5YBfl/jxewBCluBYBpHJbi4ApCIAoxUVeBTsEokoHlk1xQU0hjFpBfT5HikMqxUWMJIxVQZEDJJEkEluUr9aUHiybzoJKI4xNQaXdJMUhlc4iRhIGpKBINgZAWfqK8tX6LEVCclZQaYQhKF6ItEJSHBI5K2IiYdoJSpReDSiKUi23Vk7IWq8tJRJnpoguqQRJJuiN8RMZFKnMFDGVMKkEpe5jBBRnoihfdi1JKiTzBBVImC3ihwjkZQKUZp6IoYRpICj0GTno0pDScotmhSzamhksEyuCSiNMWayDSEtaSlkS4yCmEYZ4oDQuFvDL3BAxjTCZww2hXSFBI4oBEcMIczbQRzKWNYCSaA8xkDAHsx4CPE8aSBTWWW4n1kJ4yqxguSwNMYgwvgIF3YWCiMMySxQRJFKgIAE2sFz2hRhEGPzggoCioCAScRbKWXTs4BdJFcofPrwWWEQhFEofznIhAKIQCnRFhGEQ6LP9zBIIiRDKcprYIJoC1oLl8iREtzuC2Sq0vUtJYJbmSYh5hDNN6L6VCoBfzGcpt/2OICA/E4UKIhxWQhfYuQTyu6K4IaLMN4FfDzyh4ggnjSLQM0wWRm7gueSVl2B2KARhbxMCvzO5XHKbIxgDQsHPyYNJDZWIwYRDNygYLQx+Z6ZI+Z4pbQa/nuBBVRLO16Aqg8nCKPKVcJQGBY+RB1PiK+FUDAq2FQG/M5dbzldeiK8j5MFEIzaoVMLplyhEqoYo+N0RGzGccNIFhWdTAEeS94LUsGzei+87RWFgyeiMlu7H/6oCVbCFAwDrH48+4H8BAAD//4eDbUOjOgAA"); err != nil { panic("add binary content to resource manager failed: " + err.Error()) } } From aefa54f04b75454c17393ce5e8b3c64c887fb4d8 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 16 Jan 2021 20:37:59 +0800 Subject: [PATCH 070/492] improve package gredis --- os/gsession/gsession_storage_file.go | 10 ++++++---- os/gsession/gsession_storage_redis.go | 12 +++++++----- os/gsession/gsession_storage_redis_hashtable.go | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index b97dd820a..f1df7d8db 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -68,8 +68,10 @@ func NewStorageFile(path ...string) *StorageFile { // Batch updates the TTL for session ids timely. gtimer.AddSingleton(DefaultStorageFileLoopInterval, func() { //intlog.Print("StorageFile.timer start") - var id string - var err error + var ( + id string + err error + ) for { if id = s.updatingIdSet.Pop(); id == "" { break @@ -221,7 +223,7 @@ func (s *StorageFile) SetSession(id string, data *gmap.StrAnyMap, ttl time.Durat // It just adds the session id to the async handling queue. func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error { intlog.Printf("StorageFile.UpdateTTL: %s, %v", id, ttl) - if ttl >= DefaultStorageRedisLoopInterval { + if ttl >= DefaultStorageFileLoopInterval { s.updatingIdSet.Add(id) } return nil diff --git a/os/gsession/gsession_storage_redis.go b/os/gsession/gsession_storage_redis.go index 567fe2036..bd5714e66 100644 --- a/os/gsession/gsession_storage_redis.go +++ b/os/gsession/gsession_storage_redis.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -26,7 +26,7 @@ type StorageRedis struct { var ( // DefaultStorageRedisLoopInterval is the interval updating TTL for session ids // in last duration. - DefaultStorageRedisLoopInterval = time.Minute + DefaultStorageRedisLoopInterval = 10 * time.Second ) // NewStorageRedis creates and returns a redis storage object for session. @@ -45,9 +45,11 @@ func NewStorageRedis(redis *gredis.Redis, prefix ...string) *StorageRedis { // Batch updates the TTL for session ids timely. gtimer.AddSingleton(DefaultStorageRedisLoopInterval, func() { intlog.Print("StorageRedis.timer start") - var id string - var err error - var ttlSeconds int + var ( + id string + err error + ttlSeconds int + ) for { if id, ttlSeconds = s.updatingIdMap.Pop(); id == "" { break diff --git a/os/gsession/gsession_storage_redis_hashtable.go b/os/gsession/gsession_storage_redis_hashtable.go index b7eec2970..15eb825a3 100644 --- a/os/gsession/gsession_storage_redis_hashtable.go +++ b/os/gsession/gsession_storage_redis_hashtable.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, From 91e29e23a4bc6a3d68269095e42fed1d2f754f43 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 16 Jan 2021 22:31:48 +0800 Subject: [PATCH 071/492] improve package gcfg for searching priority --- net/ghttp/ghttp_server_util.go | 6 +++--- net/ghttp/ghttp_unit_server_util_test.go | 2 +- os/gcfg/gcfg.go | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/net/ghttp/ghttp_server_util.go b/net/ghttp/ghttp_server_util.go index 47dd8db44..f9a3ea8e1 100644 --- a/net/ghttp/ghttp_server_util.go +++ b/net/ghttp/ghttp_server_util.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -8,14 +8,14 @@ package ghttp import "net/http" -// WrapF is a helper function for wrapping http.HandlerFunc and returns a ghttp middleware. +// WrapF is a helper function for wrapping http.HandlerFunc and returns a ghttp.HandlerFunc. func WrapF(f http.HandlerFunc) HandlerFunc { return func(r *Request) { f(r.Response.Writer, r.Request) } } -// WrapH is a helper function for wrapping http.Handler and returns a ghttp middleware. +// WrapH is a helper function for wrapping http.Handler and returns a ghttp.HandlerFunc. func WrapH(h http.Handler) HandlerFunc { return func(r *Request) { h.ServeHTTP(r.Response.Writer, r.Request) diff --git a/net/ghttp/ghttp_unit_server_util_test.go b/net/ghttp/ghttp_unit_server_util_test.go index 3da6ab1df..66127e2ad 100644 --- a/net/ghttp/ghttp_unit_server_util_test.go +++ b/net/ghttp/ghttp_unit_server_util_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index 34f9c2859..f9c9c4c91 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -70,16 +70,16 @@ func New(file ...string) *Config { } } } else { - // Dir path of working dir. - _ = c.SetPath(gfile.Pwd()) - // Dir path of binary. - if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { - _ = c.AddPath(selfPath) - } // Dir path of main package. if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) { _ = c.AddPath(mainPath) } + // Dir path of binary. + if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { + _ = c.AddPath(selfPath) + } + // Dir path of working dir. + _ = c.AddPath(gfile.Pwd()) } return c } From c5145dc4f60a9ff591a5bdfaf36bdf0ac8a04deb Mon Sep 17 00:00:00 2001 From: eyasliu Date: Sun, 17 Jan 2021 17:26:53 +0800 Subject: [PATCH 072/492] remove abort, actually the abort is unuse --- net/ghttp/ghttp_client_middleware.go | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/net/ghttp/ghttp_client_middleware.go b/net/ghttp/ghttp_client_middleware.go index 294336c21..c121ef66b 100644 --- a/net/ghttp/ghttp_client_middleware.go +++ b/net/ghttp/ghttp_client_middleware.go @@ -1,7 +1,6 @@ package ghttp import ( - "github.com/gogf/gf/errors/gerror" "net/http" ) @@ -29,17 +28,6 @@ func (c *Client) MiddlewareNext(req *http.Request) (*ClientResponse, error) { return c.callRequest(req) } -// MiddlewareAbort stop call after all middleware, so it will not send http request -// this is should only be call in ClientHandlerFunc -func (c *Client) MiddlewareAbort(req *http.Request) (*ClientResponse, error) { - m, ok := req.Context().Value(gfHTTPClientMiddlewareKey).(*clientMiddleware) - if ok { - m.Abort() - return m.resp, m.err - } - return nil, gerror.New("http request abort") -} - // ClientHandlerFunc middleware handler func type ClientHandlerFunc = func(c *Client, r *http.Request) (*ClientResponse, error) @@ -48,14 +36,13 @@ type clientMiddleware struct { client *Client // http client handlers []ClientHandlerFunc // mdl handlers handlerIndex int // current handler index - abort bool // abort call after handlers resp *ClientResponse // save resp err error // save err } // Next call next middleware handler, if abort, func (m *clientMiddleware) Next(req *http.Request) (resp *ClientResponse, err error) { - if m.abort || m.err != nil { + if m.err != nil { return m.resp, m.err } if m.handlerIndex < len(m.handlers) { @@ -66,10 +53,3 @@ func (m *clientMiddleware) Next(req *http.Request) (resp *ClientResponse, err er } return } - -func (m *clientMiddleware) Abort() { - m.abort = true - if m.err == nil { - m.err = gerror.New("http request abort") - } -} From 76768373374a1b498df616841dcb8ca9b5f65049 Mon Sep 17 00:00:00 2001 From: eyasliu Date: Sun, 17 Jan 2021 18:05:29 +0800 Subject: [PATCH 073/492] fix abort test --- net/ghttp/ghttp_unit_client_test.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/net/ghttp/ghttp_unit_client_test.go b/net/ghttp/ghttp_unit_client_test.go index f951c858a..baf13d09b 100644 --- a/net/ghttp/ghttp_unit_client_test.go +++ b/net/ghttp/ghttp_unit_client_test.go @@ -10,14 +10,16 @@ import ( "bytes" "context" "fmt" - "github.com/gogf/gf/debug/gdebug" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/util/guid" "io/ioutil" "net/http" "testing" "time" + "github.com/gogf/gf/debug/gdebug" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/util/guid" + "github.com/gogf/gf/frame/g" "github.com/gogf/gf/net/ghttp" "github.com/gogf/gf/test/gtest" @@ -340,10 +342,8 @@ func Test_Client_Middleware(t *testing.T) { p, _ := ports.PopRand() s := g.Server(p) isServerHandler := false - //respStr := "test resp str" s.BindHandler("/", func(r *ghttp.Request) { isServerHandler = true - //r.Response.Write(respStr) }) s.SetPort(p) s.SetDumpRouterMap(false) @@ -381,6 +381,7 @@ func Test_Client_Middleware(t *testing.T) { // test abort, abort will not send str3 := "" + abortStr := "abort request" c = ghttp.NewClient().SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { str3 += "a" resp, err = c.MiddlewareNext(r) @@ -388,11 +389,7 @@ func Test_Client_Middleware(t *testing.T) { return }).Use(func(c *ghttp.Client, r *http.Request) (*ghttp.ClientResponse, error) { str3 += "c" - resp, err := c.MiddlewareAbort(r) - str3 += "d" - resp, err = c.MiddlewareNext(r) - str3 += "e" - return resp, err + return nil, gerror.New(abortStr) }).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { str3 += "f" resp, err = c.MiddlewareNext(r) @@ -400,8 +397,8 @@ func Test_Client_Middleware(t *testing.T) { return }) resp, err = c.Get("/") - t.Assert(str3, "acdeb") - t.Assert(err.Error(), "http request abort") + t.Assert(str3, "acb") + t.Assert(err.Error(), abortStr) t.Assert(resp, nil) }) } From 5c9cee7fb9b401b1a54eeb3248cfb03d93249399 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 17 Jan 2021 20:56:38 +0800 Subject: [PATCH 074/492] improve package gconv --- net/ghttp/ghttp_request_param.go | 4 +- os/gtime/gtime_time.go | 9 ++++ util/gconv/gconv_struct.go | 53 +++++++++++-------- .../gconv_z_unit_struct_interface_test.go | 5 ++ 4 files changed, 46 insertions(+), 25 deletions(-) diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index 102de9a1d..a9a122bdf 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -112,11 +112,11 @@ func (r *Request) doParse(pointer interface{}, requestType int) error { if err != nil { return err } - if err := j.GetStructs(".", pointer); err != nil { + if err = j.GetStructs(".", pointer); err != nil { return err } for i := 0; i < reflectVal2.Len(); i++ { - if err := gvalid.CheckStruct(reflectVal2.Index(i), nil); err != nil { + if err = gvalid.CheckStruct(reflectVal2.Index(i), nil); err != nil { return err } } diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 7da67768a..1137a6799 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -443,3 +443,12 @@ func (t *Time) UnmarshalJSON(b []byte) error { t.Time = newTime.Time return nil } + +//// UnmarshalValue is an interface implement which sets any type of value for Time. +//func (t *Time) UnmarshalValue(value interface{}) error { +// vTime := New(value) +// if vTime != nil { +// *t = *vTime +// } +// return gerror.Newf(`invalid time value: %v`, value) +//} diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 401f136a6..ecf6b7f50 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -119,11 +119,9 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str return nil } - // UnmarshalValue. - // Assign value with interface UnmarshalValue. - // Note that only pointer can implement interface UnmarshalValue. - if v, ok := pointerReflectValue.Interface().(apiUnmarshalValue); ok { - return v.UnmarshalValue(params) + // Normal unmarshalling interfaces checks. + if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, params); ok { + return err } // It automatically creates struct object if necessary. @@ -133,8 +131,13 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str e := reflect.New(pointerElemReflectValue.Type().Elem()).Elem() pointerElemReflectValue.Set(e.Addr()) } - if v, ok := pointerElemReflectValue.Interface().(apiUnmarshalValue); ok { - return v.UnmarshalValue(params) + fmt.Println(pointerElemReflectValue.Type()) + //if v, ok := pointerElemReflectValue.Interface().(apiUnmarshalValue); ok { + // return v.UnmarshalValue(params) + //} + // Note that it's `pointerElemReflectValue` here not `pointerReflectValue`. + if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, params); ok { + return err } // Retrieve its element, may be struct at last. pointerElemReflectValue = pointerElemReflectValue.Elem() @@ -286,25 +289,29 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, map } // bindVarToReflectValueWithInterfaceCheck does binding using common interfaces checks. -func bindVarToReflectValueWithInterfaceCheck(structFieldValue reflect.Value, value interface{}) (err error, ok bool) { - if structFieldValue.CanAddr() { - pointer := structFieldValue.Addr().Interface() - if v, ok := pointer.(apiUnmarshalValue); ok { - return v.UnmarshalValue(value), ok +func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value interface{}) (err error, ok bool) { + var pointer interface{} + if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() { + // Not a pointer, but can token address, that makes it can be unmarshalled. + pointer = reflectValue.Addr().Interface() + } else { + pointer = reflectValue.Interface() + } + if v, ok := pointer.(apiUnmarshalValue); ok { + return v.UnmarshalValue(value), ok + } + if v, ok := pointer.(apiUnmarshalText); ok { + if s, ok := value.(string); ok { + return v.UnmarshalText([]byte(s)), ok } - if v, ok := pointer.(apiUnmarshalText); ok { - if s, ok := value.(string); ok { - return v.UnmarshalText([]byte(s)), ok - } - if b, ok := value.([]byte); ok { - return v.UnmarshalText(b), ok - } - } - if v, ok := pointer.(apiSet); ok { - v.Set(value) - return nil, ok + if b, ok := value.([]byte); ok { + return v.UnmarshalText(b), ok } } + if v, ok := pointer.(apiSet); ok { + v.Set(value) + return nil, ok + } return nil, false } diff --git a/util/gconv/gconv_z_unit_struct_interface_test.go b/util/gconv/gconv_z_unit_struct_interface_test.go index 2794ef031..a18678235 100644 --- a/util/gconv/gconv_z_unit_struct_interface_test.go +++ b/util/gconv/gconv_z_unit_struct_interface_test.go @@ -48,6 +48,11 @@ func Test_Struct_UnmarshalValue1(t *testing.T) { err := gconv.Struct(g.Map{"ServiceDate": nil}, st) t.AssertNE(err, nil) }) + gtest.C(t, func(t *gtest.T) { + st := &MyTimeSt{} + err := gconv.Struct(g.Map{"ServiceDate": "error"}, st) + t.AssertNE(err, nil) + }) } type Pkg struct { From 6f968a125eb23684387b721a8b2b0df2ac31ec22 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 17 Jan 2021 21:39:17 +0800 Subject: [PATCH 075/492] improve time converting for package gconv --- os/gtime/gtime_time.go | 19 +++++++++++-------- os/gtime/gtime_z_unit_time_test.go | 2 +- util/gconv/gconv_struct.go | 1 - util/gconv/gconv_z_unit_time_test.go | 19 +++++++++++++++++++ 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 1137a6799..997cf43ad 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -8,6 +8,7 @@ package gtime import ( "bytes" + "github.com/gogf/gf/errors/gerror" "strconv" "time" ) @@ -444,11 +445,13 @@ func (t *Time) UnmarshalJSON(b []byte) error { return nil } -//// UnmarshalValue is an interface implement which sets any type of value for Time. -//func (t *Time) UnmarshalValue(value interface{}) error { -// vTime := New(value) -// if vTime != nil { -// *t = *vTime -// } -// return gerror.Newf(`invalid time value: %v`, value) -//} +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Note that it overwrites the same implementer of `time.Time`. +func (t *Time) UnmarshalText(data []byte) error { + vTime := New(data) + if vTime != nil { + *t = *vTime + return nil + } + return gerror.Newf(`invalid time value: %s`, data) +} diff --git a/os/gtime/gtime_z_unit_time_test.go b/os/gtime/gtime_z_unit_time_test.go index 5eb2809bd..2f3ca95ec 100644 --- a/os/gtime/gtime_z_unit_time_test.go +++ b/os/gtime/gtime_z_unit_time_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index ecf6b7f50..130d87477 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -131,7 +131,6 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str e := reflect.New(pointerElemReflectValue.Type().Elem()).Elem() pointerElemReflectValue.Set(e.Addr()) } - fmt.Println(pointerElemReflectValue.Type()) //if v, ok := pointerElemReflectValue.Interface().(apiUnmarshalValue); ok { // return v.UnmarshalValue(params) //} diff --git a/util/gconv/gconv_z_unit_time_test.go b/util/gconv/gconv_z_unit_time_test.go index 608ad6b67..1d41b04cd 100644 --- a/util/gconv/gconv_z_unit_time_test.go +++ b/util/gconv/gconv_z_unit_time_test.go @@ -7,6 +7,7 @@ package gconv_test import ( + "github.com/gogf/gf/frame/g" "testing" "time" @@ -42,3 +43,21 @@ func Test_Time(t *testing.T) { t.AssertEQ(gconv.Time(s), gtime.NewFromStr(s).Time) }) } + +func Test_Time_Slice_Attribute(t *testing.T) { + type SelectReq struct { + Arr []*gtime.Time + One *gtime.Time + } + gtest.C(t, func(t *gtest.T) { + var s *SelectReq + err := gconv.Struct(g.Map{ + "arr": g.Slice{"2021-01-12 12:34:56", "2021-01-12 12:34:57"}, + "one": "2021-01-12 12:34:58", + }, &s) + t.Assert(err, nil) + t.Assert(s.One, "2021-01-12 12:34:58") + t.Assert(s.Arr[0], "2021-01-12 12:34:56") + t.Assert(s.Arr[1], "2021-01-12 12:34:57") + }) +} From 093034acd1b20feddcc1cebeb9febfd45353ca2d Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 17 Jan 2021 21:46:25 +0800 Subject: [PATCH 076/492] copyright comment update --- .example/database/gdb/driver/driver/driver.go | 2 +- .example/net/gsmtp/gsmtp_sendMail.go | 2 +- container/garray/garray.go | 2 +- container/garray/garray_func.go | 2 +- container/garray/garray_normal_any.go | 2 +- container/garray/garray_normal_int.go | 2 +- container/garray/garray_normal_str.go | 2 +- container/garray/garray_sorted_any.go | 2 +- container/garray/garray_sorted_int.go | 2 +- container/garray/garray_sorted_str.go | 2 +- container/garray/garray_z_example_any_test.go | 2 +- container/garray/garray_z_example_str_test.go | 2 +- container/garray/garray_z_unit_all_basic_test.go | 2 +- container/garray/garray_z_unit_normal_any_array_test.go | 2 +- container/garray/garray_z_unit_normal_int_array_test.go | 2 +- container/garray/garray_z_unit_normal_str_array_test.go | 2 +- container/garray/garray_z_unit_sorted_any_array_test.go | 2 +- container/garray/garray_z_unit_sorted_int_array_test.go | 2 +- container/garray/garray_z_unit_sorted_str_array_test.go | 2 +- container/glist/glist.go | 2 +- container/glist/glist_example_test.go | 2 +- container/glist/glist_z_bench_test.go | 2 +- container/glist/glist_z_unit_test.go | 2 +- container/gmap/gmap.go | 2 +- container/gmap/gmap_hash_any_any_map.go | 2 +- container/gmap/gmap_hash_int_any_map.go | 2 +- container/gmap/gmap_hash_int_int_map.go | 2 +- container/gmap/gmap_hash_int_str_map.go | 2 +- container/gmap/gmap_hash_str_any_map.go | 2 +- container/gmap/gmap_hash_str_int_map.go | 2 +- container/gmap/gmap_hash_str_str_map.go | 2 +- container/gmap/gmap_list_map.go | 2 +- container/gmap/gmap_tree_map.go | 2 +- container/gmap/gmap_z_basic_test.go | 2 +- container/gmap/gmap_z_bench_maps_test.go | 2 +- container/gmap/gmap_z_bench_safe_test.go | 2 +- container/gmap/gmap_z_bench_syncmap_test.go | 2 +- container/gmap/gmap_z_bench_unsafe_test.go | 2 +- container/gmap/gmap_z_example_any_any_test.go | 2 +- container/gmap/gmap_z_unit_any_any_test.go | 2 +- container/gmap/gmap_z_unit_int_any_test.go | 2 +- container/gmap/gmap_z_unit_int_int_test.go | 2 +- container/gmap/gmap_z_unit_int_str_test.go | 2 +- container/gmap/gmap_z_unit_list_map_test.go | 2 +- container/gmap/gmap_z_unit_str_any_test.go | 2 +- container/gmap/gmap_z_unit_str_int_test.go | 2 +- container/gmap/gmap_z_unit_str_str_test.go | 2 +- container/gmap/gmap_z_unit_tree_map_test.go | 2 +- container/gpool/gpool.go | 2 +- container/gpool/gpool_bench_test.go | 2 +- container/gpool/gpool_z_unit_test.go | 2 +- container/gqueue/gqueue.go | 2 +- container/gqueue/gqueue_bench_test.go | 2 +- container/gqueue/gqueue_unit_test.go | 2 +- container/gring/gring.go | 2 +- container/gring/gring_bench_test.go | 2 +- container/gring/gring_unit_test.go | 2 +- container/gset/gset_any_set.go | 2 +- container/gset/gset_int_set.go | 2 +- container/gset/gset_str_set.go | 2 +- container/gset/gset_z_bench_test.go | 2 +- container/gset/gset_z_example_any_test.go | 2 +- container/gset/gset_z_example_int_test.go | 2 +- container/gset/gset_z_example_str_test.go | 2 +- container/gset/gset_z_unit_any_test.go | 2 +- container/gset/gset_z_unit_int_test.go | 2 +- container/gset/gset_z_unit_str_test.go | 2 +- container/gtree/gtree.go | 2 +- container/gtree/gtree_avltree.go | 2 +- container/gtree/gtree_btree.go | 2 +- container/gtree/gtree_redblacktree.go | 2 +- container/gtree/gtree_z_avl_tree_test.go | 2 +- container/gtree/gtree_z_b_tree_test.go | 2 +- container/gtree/gtree_z_redblack_tree_test.go | 2 +- container/gtype/bool.go | 2 +- container/gtype/byte.go | 2 +- container/gtype/bytes.go | 2 +- container/gtype/float32.go | 2 +- container/gtype/float64.go | 2 +- container/gtype/gtype.go | 2 +- container/gtype/int.go | 2 +- container/gtype/int32.go | 2 +- container/gtype/int64.go | 2 +- container/gtype/interface.go | 2 +- container/gtype/string.go | 2 +- container/gtype/uint.go | 2 +- container/gtype/uint32.go | 2 +- container/gtype/uint64.go | 2 +- container/gtype/z_bench_basic_test.go | 2 +- container/gtype/z_bench_json_test.go | 2 +- container/gtype/z_unit_bool_test.go | 2 +- container/gtype/z_unit_byte_test.go | 2 +- container/gtype/z_unit_bytes_test.go | 2 +- container/gtype/z_unit_float32_test.go | 2 +- container/gtype/z_unit_float64_test.go | 2 +- container/gtype/z_unit_int32_test.go | 2 +- container/gtype/z_unit_int64_test.go | 2 +- container/gtype/z_unit_int_test.go | 2 +- container/gtype/z_unit_interface_test.go | 2 +- container/gtype/z_unit_string_test.go | 2 +- container/gtype/z_unit_uint32_test.go | 2 +- container/gtype/z_unit_uint64_test.go | 2 +- container/gtype/z_unit_uint_test.go | 2 +- container/gvar/gvar.go | 2 +- container/gvar/gvar_is.go | 2 +- container/gvar/gvar_list.go | 2 +- container/gvar/gvar_map.go | 2 +- container/gvar/gvar_struct.go | 2 +- container/gvar/gvar_z_bench_obj_test.go | 2 +- container/gvar/gvar_z_bench_ptr_test.go | 2 +- container/gvar/gvar_z_unit_basic_test.go | 2 +- container/gvar/gvar_z_unit_is_test.go | 2 +- container/gvar/gvar_z_unit_json_test.go | 2 +- container/gvar/gvar_z_unit_list_test.go | 2 +- container/gvar/gvar_z_unit_map_test.go | 2 +- container/gvar/gvar_z_unit_maptomap_test.go | 2 +- container/gvar/gvar_z_unit_struct_test.go | 2 +- crypto/gaes/gaes.go | 2 +- crypto/gaes/gaes_test.go | 2 +- crypto/gcrc32/gcrc32.go | 2 +- crypto/gcrc32/gcrc32_test.go | 2 +- crypto/gdes/gdes.go | 2 +- crypto/gdes/gdes_test.go | 2 +- crypto/gmd5/gmd5.go | 2 +- crypto/gmd5/gmd5_test.go | 2 +- crypto/gsha1/gsha1.go | 2 +- crypto/gsha1/gsha1_test.go | 2 +- database/gdb/gdb.go | 2 +- database/gdb/gdb_core_utility.go | 2 +- database/gdb/gdb_driver_mssql.go | 2 +- database/gdb/gdb_driver_mysql.go | 2 +- database/gdb/gdb_driver_pgsql.go | 2 +- database/gdb/gdb_driver_sqlite.go | 2 +- database/gdb/gdb_model.go | 2 +- database/gdb/gdb_model_cache.go | 2 +- database/gdb/gdb_model_condition.go | 2 +- database/gdb/gdb_model_delete.go | 2 +- database/gdb/gdb_model_fields.go | 2 +- database/gdb/gdb_model_insert.go | 2 +- database/gdb/gdb_model_join.go | 2 +- database/gdb/gdb_model_lock.go | 2 +- database/gdb/gdb_model_option.go | 2 +- database/gdb/gdb_model_select.go | 2 +- database/gdb/gdb_model_time.go | 2 +- database/gdb/gdb_model_update.go | 2 +- database/gdb/gdb_model_utility.go | 2 +- database/gdb/gdb_result.go | 2 +- database/gdb/gdb_schema.go | 2 +- database/gdb/gdb_transaction.go | 2 +- database/gdb/gdb_type_record.go | 2 +- database/gdb/gdb_type_record_deprecated.go | 2 +- database/gdb/gdb_type_result.go | 2 +- database/gdb/gdb_type_result_deprecated.go | 2 +- database/gdb/gdb_type_result_scanlist.go | 2 +- database/gdb/gdb_z_driver_test.go | 2 +- database/gdb/gdb_z_example_test.go | 2 +- database/gdb/gdb_z_init_test.go | 2 +- database/gdb/gdb_z_mysql_association_test.go | 2 +- database/gdb/gdb_z_mysql_basic_test.go | 2 +- database/gdb/gdb_z_mysql_ctx_test.go | 2 +- database/gdb/gdb_z_mysql_internal_test.go | 2 +- database/gdb/gdb_z_mysql_method_test.go | 2 +- database/gdb/gdb_z_mysql_raw_test.go | 2 +- database/gdb/gdb_z_mysql_time_maintain_test.go | 2 +- database/gdb/gdb_z_mysql_transaction_test.go | 2 +- database/gdb/gdb_z_mysql_types_test.go | 2 +- database/gredis/gredis_config.go | 2 +- database/gredis/gredis_conn.go | 2 +- database/gredis/gredis_instance.go | 2 +- database/gredis/gredis_z_example_test.go | 2 +- database/gredis/gredis_z_unit_conn_test.go | 2 +- database/gredis/gredis_z_unit_test.go | 2 +- debug/gdebug/gdebug.go | 2 +- debug/gdebug/gdebug_caller.go | 2 +- debug/gdebug/gdebug_grid.go | 2 +- debug/gdebug/gdebug_stack.go | 2 +- debug/gdebug/gdebug_testdata.go | 2 +- debug/gdebug/gdebug_version.go | 2 +- debug/gdebug/gdebug_z_bench_test.go | 2 +- encoding/gbase64/gbase64.go | 2 +- encoding/gbase64/gbase64_test.go | 2 +- encoding/gbinary/gbinary.go | 2 +- encoding/gbinary/gbinary_be.go | 2 +- encoding/gbinary/gbinary_bit.go | 2 +- encoding/gbinary/gbinary_func.go | 2 +- encoding/gbinary/gbinary_le.go | 2 +- encoding/gbinary/gbinary_z_be_test.go | 2 +- encoding/gbinary/gbinary_z_le_test.go | 2 +- encoding/gbinary/gbinary_z_test.go | 2 +- encoding/gcharset/gcharset.go | 2 +- encoding/gcharset/gcharset_test.go | 2 +- encoding/gcompress/gcompress.go | 2 +- encoding/gcompress/gcompress_gzip.go | 2 +- encoding/gcompress/gcompress_z_unit_gzip_test.go | 2 +- encoding/gcompress/gcompress_z_unit_zip_test.go | 2 +- encoding/gcompress/gcompress_z_unit_zlib_test.go | 2 +- encoding/gcompress/gcompress_zip.go | 2 +- encoding/gcompress/gcompress_zlib.go | 2 +- encoding/ghash/ghash.go | 2 +- encoding/ghash/ghash_bench_test.go | 2 +- encoding/ghtml/ghtml.go | 2 +- encoding/ghtml/ghtml_test.go | 2 +- encoding/gini/gini.go | 2 +- encoding/gini/gini_test.go | 2 +- encoding/gjson/gjson.go | 2 +- encoding/gjson/gjson_api.go | 2 +- encoding/gjson/gjson_api_config.go | 2 +- encoding/gjson/gjson_api_encoding.go | 2 +- encoding/gjson/gjson_api_new_load.go | 2 +- encoding/gjson/gjson_deprecated.go | 2 +- encoding/gjson/gjson_implements.go | 2 +- encoding/gjson/gjson_stdlib_json_util.go | 2 +- encoding/gjson/gjson_z_bench_test.go | 2 +- encoding/gjson/gjson_z_example_conversion_test.go | 2 +- encoding/gjson/gjson_z_example_dataset_test.go | 2 +- encoding/gjson/gjson_z_example_load_test.go | 2 +- encoding/gjson/gjson_z_example_new_test.go | 2 +- encoding/gjson/gjson_z_example_pattern_test.go | 2 +- encoding/gjson/gjson_z_unit_basic_test.go | 2 +- encoding/gjson/gjson_z_unit_implements_test.go | 2 +- encoding/gjson/gjson_z_unit_internal_test.go | 2 +- encoding/gjson/gjson_z_unit_json_test.go | 2 +- encoding/gjson/gjson_z_unit_load_test.go | 2 +- encoding/gjson/gjson_z_unit_new_test.go | 2 +- encoding/gjson/gjson_z_unit_set_test.go | 2 +- encoding/gjson/gjson_z_unit_struct_test.go | 2 +- encoding/gparser/gparser.go | 2 +- encoding/gparser/gparser_api_encoding.go | 2 +- encoding/gparser/gparser_api_new_load.go | 2 +- encoding/gparser/gparser_unit_basic_test.go | 2 +- encoding/gparser/gparser_unit_load_test.go | 2 +- encoding/gparser/gparser_unit_new_test.go | 2 +- encoding/gparser/gparser_unit_set_test.go | 2 +- encoding/gtoml/gtoml.go | 2 +- encoding/gtoml/gtoml_test.go | 2 +- encoding/gurl/url.go | 2 +- encoding/gurl/url_test.go | 2 +- encoding/gxml/gxml.go | 2 +- encoding/gxml/gxml_test.go | 2 +- encoding/gyaml/gyaml.go | 2 +- encoding/gyaml/gyaml_test.go | 2 +- errors/gerror/gerror_error.go | 2 +- errors/gerror/gerror_stack.go | 2 +- errors/gerror/gerror_z_bench_test.go | 2 +- errors/gerror/gerror_z_example_test.go | 2 +- frame/g/g.go | 2 +- frame/g/g_func.go | 2 +- frame/g/g_logger.go | 2 +- frame/g/g_object.go | 2 +- frame/g/g_setting.go | 2 +- frame/g/g_z_example_test.go | 2 +- frame/gins/gins.go | 2 +- frame/gins/gins_config.go | 2 +- frame/gins/gins_database.go | 2 +- frame/gins/gins_i18n.go | 2 +- frame/gins/gins_log.go | 2 +- frame/gins/gins_redis.go | 2 +- frame/gins/gins_resource.go | 2 +- frame/gins/gins_server.go | 2 +- frame/gins/gins_view.go | 2 +- frame/gins/gins_z_unit_basic_test.go | 2 +- frame/gins/gins_z_unit_config_test.go | 2 +- frame/gins/gins_z_unit_database_test.go | 2 +- frame/gins/gins_z_unit_redis_test.go | 2 +- frame/gins/gins_z_unit_view_test.go | 2 +- frame/gmvc/controller.go | 2 +- frame/gmvc/model.go | 2 +- frame/gmvc/view.go | 2 +- i18n/gi18n/gi18n.go | 2 +- i18n/gi18n/gi18n_instance.go | 2 +- i18n/gi18n/gi18n_unit_test.go | 2 +- internal/fileinfo/fileinfo.go | 2 +- internal/intlog/intlog.go | 2 +- internal/json/json.go | 2 +- internal/mutex/mutex.go | 2 +- internal/mutex/mutex_z_bench_test.go | 2 +- internal/mutex/mutex_z_unit_test.go | 2 +- internal/rwmutex/rwmutex.go | 2 +- internal/rwmutex/rwmutex_z_bench_test.go | 2 +- internal/rwmutex/rwmutex_z_unit_test.go | 2 +- internal/structs/structs.go | 2 +- internal/structs/structs_map.go | 2 +- internal/structs/structs_tag.go | 2 +- internal/structs/structs_z_bench_test.go | 2 +- internal/structs/structs_z_unit_test.go | 2 +- internal/utils/utils.go | 2 +- internal/utils/utils_array.go | 2 +- internal/utils/utils_io.go | 2 +- internal/utils/utils_str.go | 2 +- internal/utils/utils_z_bench_test.go | 2 +- internal/utils/utils_z_test.go | 2 +- net/ghttp/ghttp.go | 2 +- net/ghttp/ghttp_client_api.go | 2 +- net/ghttp/ghttp_client_bytes.go | 2 +- net/ghttp/ghttp_client_chain.go | 2 +- net/ghttp/ghttp_client_config.go | 2 +- net/ghttp/ghttp_client_content.go | 2 +- net/ghttp/ghttp_client_dump.go | 2 +- net/ghttp/ghttp_client_request.go | 2 +- net/ghttp/ghttp_client_response.go | 2 +- net/ghttp/ghttp_client_var.go | 2 +- net/ghttp/ghttp_controller.go | 2 +- net/ghttp/ghttp_request.go | 2 +- net/ghttp/ghttp_request_auth.go | 2 +- net/ghttp/ghttp_request_param.go | 2 +- net/ghttp/ghttp_request_param_ctx.go | 2 +- net/ghttp/ghttp_request_param_file.go | 2 +- net/ghttp/ghttp_request_param_form.go | 2 +- net/ghttp/ghttp_request_param_page.go | 2 +- net/ghttp/ghttp_request_param_param.go | 2 +- net/ghttp/ghttp_request_param_post.go | 2 +- net/ghttp/ghttp_request_param_query.go | 2 +- net/ghttp/ghttp_request_param_request.go | 2 +- net/ghttp/ghttp_request_param_router.go | 2 +- net/ghttp/ghttp_request_view.go | 2 +- net/ghttp/ghttp_response.go | 2 +- net/ghttp/ghttp_response_cors.go | 2 +- net/ghttp/ghttp_response_view.go | 2 +- net/ghttp/ghttp_response_write.go | 2 +- net/ghttp/ghttp_response_writer.go | 2 +- net/ghttp/ghttp_server_admin.go | 2 +- net/ghttp/ghttp_server_admin_windows.go | 2 +- net/ghttp/ghttp_server_config_cookie.go | 2 +- net/ghttp/ghttp_server_config_mess.go | 2 +- net/ghttp/ghttp_server_config_route.go | 2 +- net/ghttp/ghttp_server_domain.go | 2 +- net/ghttp/ghttp_server_error_logger.go | 2 +- net/ghttp/ghttp_server_graceful.go | 2 +- net/ghttp/ghttp_server_plugin.go | 2 +- net/ghttp/ghttp_server_pprof.go | 2 +- net/ghttp/ghttp_server_router.go | 2 +- net/ghttp/ghttp_server_router_group.go | 2 +- net/ghttp/ghttp_server_router_hook.go | 2 +- net/ghttp/ghttp_server_router_middleware.go | 2 +- net/ghttp/ghttp_server_router_serve.go | 2 +- net/ghttp/ghttp_server_service_controller.go | 2 +- net/ghttp/ghttp_server_service_handler.go | 2 +- net/ghttp/ghttp_server_service_object.go | 2 +- net/ghttp/ghttp_server_session.go | 2 +- net/ghttp/ghttp_server_status.go | 2 +- net/ghttp/ghttp_server_websocket.go | 2 +- net/ghttp/ghttp_unit_client_dump_test.go | 2 +- net/ghttp/ghttp_unit_client_test.go | 2 +- net/ghttp/ghttp_unit_config_test.go | 2 +- net/ghttp/ghttp_unit_context_test.go | 2 +- net/ghttp/ghttp_unit_cookie_test.go | 2 +- net/ghttp/ghttp_unit_error_code_test.go | 2 +- net/ghttp/ghttp_unit_https_test.go | 2 +- net/ghttp/ghttp_unit_init_test.go | 2 +- net/ghttp/ghttp_unit_ip_test.go | 2 +- net/ghttp/ghttp_unit_log_test.go | 2 +- net/ghttp/ghttp_unit_mess_test.go | 2 +- net/ghttp/ghttp_unit_middleware_basic_test.go | 2 +- net/ghttp/ghttp_unit_middleware_cors_test.go | 2 +- net/ghttp/ghttp_unit_pprof_test.go | 2 +- net/ghttp/ghttp_unit_request_ctx_test.go | 2 +- net/ghttp/ghttp_unit_request_file_test.go | 2 +- net/ghttp/ghttp_unit_request_json_test.go | 2 +- net/ghttp/ghttp_unit_request_page_test.go | 2 +- net/ghttp/ghttp_unit_request_struct_test.go | 2 +- net/ghttp/ghttp_unit_request_test.go | 2 +- net/ghttp/ghttp_unit_request_xml_test.go | 2 +- net/ghttp/ghttp_unit_router_basic_test.go | 2 +- net/ghttp/ghttp_unit_router_controller_rest_test.go | 2 +- net/ghttp/ghttp_unit_router_controller_test.go | 2 +- net/ghttp/ghttp_unit_router_domain_basic_test.go | 2 +- net/ghttp/ghttp_unit_router_domain_controller_rest_test.go | 2 +- net/ghttp/ghttp_unit_router_domain_controller_test.go | 2 +- net/ghttp/ghttp_unit_router_domain_object_rest_test.go | 2 +- net/ghttp/ghttp_unit_router_domain_object_test.go | 2 +- net/ghttp/ghttp_unit_router_exit_test.go | 2 +- net/ghttp/ghttp_unit_router_group_group_test.go | 2 +- net/ghttp/ghttp_unit_router_group_hook_test.go | 2 +- net/ghttp/ghttp_unit_router_group_rest_test.go | 2 +- net/ghttp/ghttp_unit_router_group_test.go | 2 +- net/ghttp/ghttp_unit_router_hook_test.go | 2 +- net/ghttp/ghttp_unit_router_names_test.go | 2 +- net/ghttp/ghttp_unit_router_object_rest1_test.go | 2 +- net/ghttp/ghttp_unit_router_object_rest2_test.go | 2 +- net/ghttp/ghttp_unit_router_object_test.go | 2 +- net/ghttp/ghttp_unit_session_test.go | 2 +- net/ghttp/ghttp_unit_static_test.go | 2 +- net/ghttp/ghttp_unit_status_test.go | 2 +- net/ghttp/ghttp_unit_template_test.go | 2 +- net/ghttp/ghttp_unit_websocket_test.go | 2 +- net/ghttp/ghttp_z_bench_test.go | 2 +- net/ghttp/ghttp_z_example_client_test.go | 2 +- net/ghttp/ghttp_z_example_get_test.go | 2 +- net/ghttp/ghttp_z_example_init_test.go | 2 +- net/ghttp/ghttp_z_example_post_test.go | 2 +- net/ghttp/ghttp_z_example_test.go | 2 +- net/gipv4/gipv4.go | 2 +- net/gipv4/gipv4_ip.go | 2 +- net/gipv4/gipv4_lookup.go | 2 +- net/gipv4/gipv4_mac.go | 2 +- net/gipv6/gipv6.go | 2 +- net/gsmtp/gsmtp.go | 2 +- net/gsmtp/gsmtp_test.go | 2 +- net/gtcp/gtcp.go | 2 +- net/gtcp/gtcp_conn.go | 2 +- net/gtcp/gtcp_conn_pkg.go | 2 +- net/gtcp/gtcp_func.go | 2 +- net/gtcp/gtcp_func_pkg.go | 2 +- net/gtcp/gtcp_pool.go | 2 +- net/gtcp/gtcp_pool_pkg.go | 2 +- net/gtcp/gtcp_server.go | 2 +- net/gtcp/gtcp_unit_init_test.go | 2 +- net/gtcp/gtcp_unit_pkg_test.go | 2 +- net/gtcp/gtcp_unit_pool_pkg_test.go | 2 +- net/gtcp/gtcp_unit_pool_test.go | 2 +- net/gudp/gudp.go | 2 +- net/gudp/gudp_conn.go | 2 +- net/gudp/gudp_func.go | 2 +- net/gudp/gudp_server.go | 2 +- net/gudp/gudp_unit_basic_test.go | 2 +- net/gudp/gudp_unit_init_test.go | 2 +- os/gbuild/gbuild.go | 2 +- os/gcache/gcache.go | 2 +- os/gcache/gcache_adapter.go | 2 +- os/gcache/gcache_adapter_memory.go | 2 +- os/gcache/gcache_adapter_memory_item.go | 2 +- os/gcache/gcache_adapter_memory_lru.go | 2 +- os/gcache/gcache_cache.go | 2 +- os/gcache/gcache_z_bench_test.go | 2 +- os/gcache/gcache_z_unit_1_test.go | 2 +- os/gcfg/gcfg_api.go | 2 +- os/gcfg/gcfg_config.go | 2 +- os/gcfg/gcfg_error.go | 2 +- os/gcfg/gcfg_z_example_pattern_test.go | 2 +- os/gcfg/gcfg_z_unit_basic_test.go | 2 +- os/gcmd/gcmd.go | 2 +- os/gcmd/gcmd_handler.go | 2 +- os/gcmd/gcmd_parser.go | 2 +- os/gcmd/gcmd_parser_handler.go | 2 +- os/gcmd/gcmd_scan.go | 2 +- os/gcmd/gcmd_z_unit_default_test.go | 2 +- os/gcmd/gcmd_z_unit_parser_test.go | 2 +- os/gcron/gcron.go | 2 +- os/gcron/gcron_cron.go | 2 +- os/gcron/gcron_entry.go | 2 +- os/gcron/gcron_schedule.go | 2 +- os/gcron/gcron_unit_1_test.go | 2 +- os/gcron/gcron_unit_2_test.go | 2 +- os/gcron/gcron_z_bench_test.go | 2 +- os/gcron/gcron_z_example_1_test.go | 2 +- os/genv/genv.go | 2 +- os/genv/genv_test.go | 2 +- os/gfile/gfile.go | 2 +- os/gfile/gfile_cache.go | 2 +- os/gfile/gfile_contents.go | 2 +- os/gfile/gfile_copy.go | 2 +- os/gfile/gfile_home.go | 2 +- os/gfile/gfile_replace.go | 2 +- os/gfile/gfile_scan.go | 2 +- os/gfile/gfile_search.go | 2 +- os/gfile/gfile_size.go | 2 +- os/gfile/gfile_sort.go | 2 +- os/gfile/gfile_source.go | 2 +- os/gfile/gfile_time.go | 2 +- os/gfile/gfile_z_cache_test.go | 2 +- os/gfile/gfile_z_contents_test.go | 2 +- os/gfile/gfile_z_copy_test.go | 2 +- os/gfile/gfile_z_readline_test.go | 2 +- os/gfile/gfile_z_scan_test.go | 2 +- os/gfile/gfile_z_search_test.go | 2 +- os/gfile/gfile_z_size_test.go | 2 +- os/gfile/gfile_z_test.go | 2 +- os/gfile/gfile_z_time_test.go | 2 +- os/gfpool/gfpool.go | 2 +- os/gfpool/gfpool_file.go | 2 +- os/gfpool/gfpool_pool.go | 2 +- os/gfsnotify/gfsnotify.go | 2 +- os/gfsnotify/gfsnotify_event.go | 2 +- os/gfsnotify/gfsnotify_filefunc.go | 2 +- os/gfsnotify/gfsnotify_watcher.go | 2 +- os/gfsnotify/gfsnotify_watcher_loop.go | 2 +- os/gfsnotify/gfsnotify_z_unit_test.go | 2 +- os/glog/glog.go | 2 +- os/glog/glog_api.go | 2 +- os/glog/glog_chaining.go | 2 +- os/glog/glog_config.go | 2 +- os/glog/glog_instance.go | 2 +- os/glog/glog_logger.go | 2 +- os/glog/glog_logger_api.go | 2 +- os/glog/glog_logger_chaining.go | 2 +- os/glog/glog_logger_level.go | 2 +- os/glog/glog_logger_rotate.go | 2 +- os/glog/glog_logger_writer.go | 2 +- os/glog/glog_z_example_test.go | 2 +- os/glog/glog_z_unit_basic_test.go | 2 +- os/glog/glog_z_unit_chaining_test.go | 2 +- os/glog/glog_z_unit_concurrent_test.go | 2 +- os/glog/glog_z_unit_config_test.go | 2 +- os/glog/glog_z_unit_ctx_test.go | 2 +- os/glog/glog_z_unit_level_test.go | 2 +- os/glog/glog_z_unit_rotate_test.go | 2 +- os/gmlock/gmlock.go | 2 +- os/gmlock/gmlock_locker.go | 2 +- os/gmlock/gmlock_unit_lock_test.go | 2 +- os/gmlock/gmlock_unit_rlock_test.go | 2 +- os/gmlock/gmlock_z_bench_test.go | 2 +- os/gmutex/gmutex.go | 2 +- os/gmutex/gmutex_bench_test.go | 2 +- os/gmutex/gmutex_unit_test.go | 2 +- os/gproc/gproc.go | 2 +- os/gproc/gproc_comm.go | 2 +- os/gproc/gproc_comm_receive.go | 2 +- os/gproc/gproc_comm_send.go | 2 +- os/gproc/gproc_manager.go | 2 +- os/gproc/gproc_process.go | 2 +- os/gres/gres.go | 2 +- os/gres/gres_file.go | 2 +- os/gres/gres_http_file.go | 2 +- os/gres/gres_instance.go | 2 +- os/gres/gres_z_unit_1_test.go | 2 +- os/grpool/grpool.go | 2 +- os/grpool/grpool_bench_1_test.go | 2 +- os/grpool/grpool_bench_2_test.go | 2 +- os/grpool/grpool_unit_test.go | 2 +- os/gsession/gsession.go | 2 +- os/gsession/gsession_manager.go | 2 +- os/gsession/gsession_session.go | 2 +- os/gsession/gsession_storage.go | 2 +- os/gsession/gsession_storage_memory.go | 2 +- os/gsession/gsession_unit_storage_file_test.go | 2 +- os/gsession/gsession_unit_storage_memory_test.go | 2 +- os/gsession/gsession_unit_storage_redis_hashtable_test.go | 2 +- os/gsession/gsession_unit_storage_redis_test.go | 2 +- os/gsession/gsession_unit_test.go | 2 +- os/gspath/gspath.go | 2 +- os/gspath/gspath_cache.go | 2 +- os/gspath/gspath_unit_test.go | 2 +- os/gtime/gtime.go | 2 +- os/gtime/gtime_format.go | 2 +- os/gtime/gtime_time_wrapper.go | 2 +- os/gtime/gtime_z_unit_format_test.go | 2 +- os/gtime/gtime_z_unit_json_test.go | 2 +- os/gtimer/gtimer.go | 2 +- os/gtimer/gtimer_entry.go | 2 +- os/gtimer/gtimer_loop.go | 2 +- os/gtimer/gtimer_timer.go | 2 +- os/gtimer/gtimer_z_bench_test.go | 2 +- os/gtimer/gtimer_z_example_test.go | 2 +- os/gtimer/gtimer_z_unit_0_test.go | 2 +- os/gtimer/gtimer_z_unit_1_test.go | 2 +- os/gtimer/gtimer_z_unit_2_test.go | 2 +- os/gview/gview.go | 2 +- os/gview/gview_error.go | 2 +- os/gview/gview_i18n.go | 2 +- os/gview/gview_instance.go | 2 +- os/gview/gview_parse.go | 2 +- os/gview/gview_unit_basic_test.go | 2 +- os/gview/gview_unit_config_test.go | 2 +- os/gview/gview_unit_encode_test.go | 2 +- os/gview/gview_unit_i18n_test.go | 2 +- test/gtest/gtest.go | 2 +- test/gtest/gtest_t.go | 2 +- test/gtest/gtest_util.go | 2 +- test/gtest/gtest_z_unit_test.go | 2 +- text/gregex/gregex.go | 2 +- text/gregex/gregex_cache.go | 2 +- text/gregex/gregex_z_bench_test.go | 2 +- text/gregex/gregex_z_unit_test.go | 2 +- text/gstr/gstr.go | 2 +- text/gstr/gstr_contain.go | 2 +- text/gstr/gstr_convert.go | 2 +- text/gstr/gstr_domain.go | 2 +- text/gstr/gstr_levenshtein.go | 2 +- text/gstr/gstr_parse.go | 2 +- text/gstr/gstr_pos.go | 2 +- text/gstr/gstr_similartext.go | 2 +- text/gstr/gstr_soundex.go | 2 +- text/gstr/gstr_trim.go | 2 +- text/gstr/gstr_version.go | 2 +- text/gstr/gstr_z_unit_basic_test.go | 2 +- text/gstr/gstr_z_unit_convert_test.go | 2 +- text/gstr/gstr_z_unit_domain_test.go | 2 +- text/gstr/gstr_z_unit_parse_test.go | 2 +- text/gstr/gstr_z_unit_pos_test.go | 2 +- text/gstr/gstr_z_unit_trim_test.go | 2 +- text/gstr/gstr_z_unit_version_test.go | 2 +- util/gconv/gconv.go | 2 +- util/gconv/gconv_interface.go | 2 +- util/gconv/gconv_map.go | 2 +- util/gconv/gconv_scan.go | 2 +- util/gconv/gconv_slice.go | 2 +- util/gconv/gconv_slice_any.go | 2 +- util/gconv/gconv_slice_float.go | 2 +- util/gconv/gconv_slice_int.go | 2 +- util/gconv/gconv_slice_str.go | 2 +- util/gconv/gconv_slice_uint.go | 2 +- util/gconv/gconv_struct.go | 2 +- util/gconv/gconv_structs.go | 2 +- util/gconv/gconv_time.go | 2 +- util/gconv/gconv_unsafe.go | 2 +- util/gconv/gconv_z_bench_bytes_test.go | 2 +- util/gconv/gconv_z_bench_float_test.go | 2 +- util/gconv/gconv_z_bench_int_test.go | 2 +- util/gconv/gconv_z_bench_reflect_test.go | 2 +- util/gconv/gconv_z_bench_str_test.go | 2 +- util/gconv/gconv_z_bench_struct_test.go | 2 +- util/gconv/gconv_z_unit_all_test.go | 2 +- util/gconv/gconv_z_unit_basic_test.go | 2 +- util/gconv/gconv_z_unit_bool_test.go | 2 +- util/gconv/gconv_z_unit_custom_type_test.go | 2 +- util/gconv/gconv_z_unit_map_test.go | 2 +- util/gconv/gconv_z_unit_maptomap_test.go | 2 +- util/gconv/gconv_z_unit_scan_test.go | 2 +- util/gconv/gconv_z_unit_slice_test.go | 2 +- util/gconv/gconv_z_unit_string_test.go | 2 +- util/gconv/gconv_z_unit_struct_interface_test.go | 2 +- util/gconv/gconv_z_unit_struct_slice_test.go | 2 +- util/gconv/gconv_z_unit_struct_test.go | 2 +- util/gconv/gconv_z_unit_unsafe_test.go | 2 +- util/gmode/gmode.go | 2 +- util/gmode/gmode_test.go | 2 +- util/gpage/gpage.go | 2 +- util/gpage/gpage_unit_test.go | 2 +- util/grand/grand.go | 2 +- util/grand/grand_buffer.go | 2 +- util/grand/grand_z_bench_test.go | 2 +- util/grand/grand_z_unit_test.go | 2 +- util/guid/guid.go | 2 +- util/guid/guid_string.go | 2 +- util/guid/guid_z_bench_test.go | 2 +- util/guid/guid_z_unit_test.go | 2 +- util/gutil/gutil.go | 2 +- util/gutil/gutil_comparator.go | 2 +- util/gutil/gutil_dump.go | 2 +- util/gutil/gutil_list.go | 2 +- util/gutil/gutil_map.go | 2 +- util/gutil/gutil_slice.go | 2 +- util/gutil/gutil_struct.go | 2 +- util/gutil/gutil_z_bench_test.go | 2 +- util/gutil/gutil_z_unit_comparator_test.go | 2 +- util/gutil/gutil_z_unit_list_test.go | 2 +- util/gutil/gutil_z_unit_map_test.go | 2 +- util/gutil/gutil_z_unit_slice_test.go | 2 +- util/gutil/gutil_z_unit_struct_test.go | 2 +- util/gutil/gutil_z_unit_test.go | 2 +- util/gvalid/gvalid_custom_rule.go | 2 +- util/gvalid/gvalid_error.go | 2 +- util/gvalid/gvalid_z_example_test.go | 2 +- 643 files changed, 643 insertions(+), 643 deletions(-) diff --git a/.example/database/gdb/driver/driver/driver.go b/.example/database/gdb/driver/driver/driver.go index 97948b80c..5fd86304d 100644 --- a/.example/database/gdb/driver/driver/driver.go +++ b/.example/database/gdb/driver/driver/driver.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/.example/net/gsmtp/gsmtp_sendMail.go b/.example/net/gsmtp/gsmtp_sendMail.go index 8496c43e9..f32cd7f38 100644 --- a/.example/net/gsmtp/gsmtp_sendMail.go +++ b/.example/net/gsmtp/gsmtp_sendMail.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray.go b/container/garray/garray.go index 8cc88e71b..08e9ece89 100644 --- a/container/garray/garray.go +++ b/container/garray/garray.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_func.go b/container/garray/garray_func.go index 572cba444..d3e7b7795 100644 --- a/container/garray/garray_func.go +++ b/container/garray/garray_func.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_normal_any.go b/container/garray/garray_normal_any.go index 4c41cc2d4..3e0324a10 100644 --- a/container/garray/garray_normal_any.go +++ b/container/garray/garray_normal_any.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index 69d4c157a..10b64529d 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_normal_str.go b/container/garray/garray_normal_str.go index 889a19a09..477156a8a 100644 --- a/container/garray/garray_normal_str.go +++ b/container/garray/garray_normal_str.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_sorted_any.go b/container/garray/garray_sorted_any.go index 3122be023..497ea9e67 100644 --- a/container/garray/garray_sorted_any.go +++ b/container/garray/garray_sorted_any.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_sorted_int.go b/container/garray/garray_sorted_int.go index fa44ed534..ab926a63a 100644 --- a/container/garray/garray_sorted_int.go +++ b/container/garray/garray_sorted_int.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_sorted_str.go b/container/garray/garray_sorted_str.go index 762017d72..5450f5252 100644 --- a/container/garray/garray_sorted_str.go +++ b/container/garray/garray_sorted_str.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_z_example_any_test.go b/container/garray/garray_z_example_any_test.go index e7bdcfe4b..f79fe1adc 100644 --- a/container/garray/garray_z_example_any_test.go +++ b/container/garray/garray_z_example_any_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_z_example_str_test.go b/container/garray/garray_z_example_str_test.go index 2d1a522f8..733322c6e 100644 --- a/container/garray/garray_z_example_str_test.go +++ b/container/garray/garray_z_example_str_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_z_unit_all_basic_test.go b/container/garray/garray_z_unit_all_basic_test.go index b9da2fc95..fddebe271 100644 --- a/container/garray/garray_z_unit_all_basic_test.go +++ b/container/garray/garray_z_unit_all_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_z_unit_normal_any_array_test.go b/container/garray/garray_z_unit_normal_any_array_test.go index 19fb8aa8d..b74fc7656 100644 --- a/container/garray/garray_z_unit_normal_any_array_test.go +++ b/container/garray/garray_z_unit_normal_any_array_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_z_unit_normal_int_array_test.go b/container/garray/garray_z_unit_normal_int_array_test.go index f107ba201..862f46901 100644 --- a/container/garray/garray_z_unit_normal_int_array_test.go +++ b/container/garray/garray_z_unit_normal_int_array_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_z_unit_normal_str_array_test.go b/container/garray/garray_z_unit_normal_str_array_test.go index 28777dc28..f4a02744c 100644 --- a/container/garray/garray_z_unit_normal_str_array_test.go +++ b/container/garray/garray_z_unit_normal_str_array_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_z_unit_sorted_any_array_test.go b/container/garray/garray_z_unit_sorted_any_array_test.go index 3cf4cc185..f3ce498ea 100644 --- a/container/garray/garray_z_unit_sorted_any_array_test.go +++ b/container/garray/garray_z_unit_sorted_any_array_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_z_unit_sorted_int_array_test.go b/container/garray/garray_z_unit_sorted_int_array_test.go index 3d7e18490..c4e2e04b7 100644 --- a/container/garray/garray_z_unit_sorted_int_array_test.go +++ b/container/garray/garray_z_unit_sorted_int_array_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/garray/garray_z_unit_sorted_str_array_test.go b/container/garray/garray_z_unit_sorted_str_array_test.go index 4dbabc3c0..5be967e54 100644 --- a/container/garray/garray_z_unit_sorted_str_array_test.go +++ b/container/garray/garray_z_unit_sorted_str_array_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/glist/glist.go b/container/glist/glist.go index 2b4b68bdd..1a3fd22bb 100644 --- a/container/glist/glist.go +++ b/container/glist/glist.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with l file, diff --git a/container/glist/glist_example_test.go b/container/glist/glist_example_test.go index af1118c89..c2a00fd04 100644 --- a/container/glist/glist_example_test.go +++ b/container/glist/glist_example_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/glist/glist_z_bench_test.go b/container/glist/glist_z_bench_test.go index 48889e11e..f9d1b749b 100644 --- a/container/glist/glist_z_bench_test.go +++ b/container/glist/glist_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/glist/glist_z_unit_test.go b/container/glist/glist_z_unit_test.go index 2c04169ac..d1dd57c3e 100644 --- a/container/glist/glist_z_unit_test.go +++ b/container/glist/glist_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gmap/gmap.go b/container/gmap/gmap.go index e3e22c190..b5e887e62 100644 --- a/container/gmap/gmap.go +++ b/container/gmap/gmap.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_hash_any_any_map.go b/container/gmap/gmap_hash_any_any_map.go index 28859ed45..acc10da60 100644 --- a/container/gmap/gmap_hash_any_any_map.go +++ b/container/gmap/gmap_hash_any_any_map.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_hash_int_any_map.go b/container/gmap/gmap_hash_int_any_map.go index 52df0cebc..d04c49a09 100644 --- a/container/gmap/gmap_hash_int_any_map.go +++ b/container/gmap/gmap_hash_int_any_map.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_hash_int_int_map.go b/container/gmap/gmap_hash_int_int_map.go index 4d546661e..4372246e3 100644 --- a/container/gmap/gmap_hash_int_int_map.go +++ b/container/gmap/gmap_hash_int_int_map.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_hash_int_str_map.go b/container/gmap/gmap_hash_int_str_map.go index 8d1f1edfd..e92f8023a 100644 --- a/container/gmap/gmap_hash_int_str_map.go +++ b/container/gmap/gmap_hash_int_str_map.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_hash_str_any_map.go b/container/gmap/gmap_hash_str_any_map.go index 423923e2c..1ee97612e 100644 --- a/container/gmap/gmap_hash_str_any_map.go +++ b/container/gmap/gmap_hash_str_any_map.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_hash_str_int_map.go b/container/gmap/gmap_hash_str_int_map.go index 58f98f017..7a51f6ad9 100644 --- a/container/gmap/gmap_hash_str_int_map.go +++ b/container/gmap/gmap_hash_str_int_map.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_hash_str_str_map.go b/container/gmap/gmap_hash_str_str_map.go index 5565b28a7..04f6c0698 100644 --- a/container/gmap/gmap_hash_str_str_map.go +++ b/container/gmap/gmap_hash_str_str_map.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_list_map.go b/container/gmap/gmap_list_map.go index 823cd7a54..224cb9f63 100644 --- a/container/gmap/gmap_list_map.go +++ b/container/gmap/gmap_list_map.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_tree_map.go b/container/gmap/gmap_tree_map.go index 9bcce1b62..1b9f02783 100644 --- a/container/gmap/gmap_tree_map.go +++ b/container/gmap/gmap_tree_map.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_basic_test.go b/container/gmap/gmap_z_basic_test.go index 534ccc610..5ffcdebf3 100644 --- a/container/gmap/gmap_z_basic_test.go +++ b/container/gmap/gmap_z_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_bench_maps_test.go b/container/gmap/gmap_z_bench_maps_test.go index 54dc6968c..b5e3b22ff 100644 --- a/container/gmap/gmap_z_bench_maps_test.go +++ b/container/gmap/gmap_z_bench_maps_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_bench_safe_test.go b/container/gmap/gmap_z_bench_safe_test.go index 2e84d15ca..46bbc1354 100644 --- a/container/gmap/gmap_z_bench_safe_test.go +++ b/container/gmap/gmap_z_bench_safe_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_bench_syncmap_test.go b/container/gmap/gmap_z_bench_syncmap_test.go index 64c252299..889edbe06 100644 --- a/container/gmap/gmap_z_bench_syncmap_test.go +++ b/container/gmap/gmap_z_bench_syncmap_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_bench_unsafe_test.go b/container/gmap/gmap_z_bench_unsafe_test.go index c63c88a8b..2a6f4853f 100644 --- a/container/gmap/gmap_z_bench_unsafe_test.go +++ b/container/gmap/gmap_z_bench_unsafe_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_example_any_any_test.go b/container/gmap/gmap_z_example_any_any_test.go index 68675b72a..73ad8d233 100644 --- a/container/gmap/gmap_z_example_any_any_test.go +++ b/container/gmap/gmap_z_example_any_any_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_unit_any_any_test.go b/container/gmap/gmap_z_unit_any_any_test.go index 53cc0bfb5..4a06b4e04 100644 --- a/container/gmap/gmap_z_unit_any_any_test.go +++ b/container/gmap/gmap_z_unit_any_any_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_unit_int_any_test.go b/container/gmap/gmap_z_unit_int_any_test.go index cc7f8e57f..c6ac46486 100644 --- a/container/gmap/gmap_z_unit_int_any_test.go +++ b/container/gmap/gmap_z_unit_int_any_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_unit_int_int_test.go b/container/gmap/gmap_z_unit_int_int_test.go index f4763be3a..37b940152 100644 --- a/container/gmap/gmap_z_unit_int_int_test.go +++ b/container/gmap/gmap_z_unit_int_int_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_unit_int_str_test.go b/container/gmap/gmap_z_unit_int_str_test.go index 45f4d0bb9..2b4292ef1 100644 --- a/container/gmap/gmap_z_unit_int_str_test.go +++ b/container/gmap/gmap_z_unit_int_str_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_unit_list_map_test.go b/container/gmap/gmap_z_unit_list_map_test.go index 08f60d7e5..045d673a7 100644 --- a/container/gmap/gmap_z_unit_list_map_test.go +++ b/container/gmap/gmap_z_unit_list_map_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_unit_str_any_test.go b/container/gmap/gmap_z_unit_str_any_test.go index 7f915a90a..22180c901 100644 --- a/container/gmap/gmap_z_unit_str_any_test.go +++ b/container/gmap/gmap_z_unit_str_any_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_unit_str_int_test.go b/container/gmap/gmap_z_unit_str_int_test.go index b806292c2..56ba1141a 100644 --- a/container/gmap/gmap_z_unit_str_int_test.go +++ b/container/gmap/gmap_z_unit_str_int_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_unit_str_str_test.go b/container/gmap/gmap_z_unit_str_str_test.go index 002ae13bc..0dfffd3a5 100644 --- a/container/gmap/gmap_z_unit_str_str_test.go +++ b/container/gmap/gmap_z_unit_str_str_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gmap/gmap_z_unit_tree_map_test.go b/container/gmap/gmap_z_unit_tree_map_test.go index bcdec51b6..e008d9986 100644 --- a/container/gmap/gmap_z_unit_tree_map_test.go +++ b/container/gmap/gmap_z_unit_tree_map_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gpool/gpool.go b/container/gpool/gpool.go index e7a59905d..0ac919139 100644 --- a/container/gpool/gpool.go +++ b/container/gpool/gpool.go @@ -1,4 +1,4 @@ -// Copyright GoFrame gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gpool/gpool_bench_test.go b/container/gpool/gpool_bench_test.go index 4dc1346cf..f43cada2c 100644 --- a/container/gpool/gpool_bench_test.go +++ b/container/gpool/gpool_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gpool/gpool_z_unit_test.go b/container/gpool/gpool_z_unit_test.go index cca547339..b8cfd61fa 100644 --- a/container/gpool/gpool_z_unit_test.go +++ b/container/gpool/gpool_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gqueue/gqueue.go b/container/gqueue/gqueue.go index 1ecfc9231..fe9bde541 100644 --- a/container/gqueue/gqueue.go +++ b/container/gqueue/gqueue.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gqueue/gqueue_bench_test.go b/container/gqueue/gqueue_bench_test.go index cdddbc57e..cf12624e2 100644 --- a/container/gqueue/gqueue_bench_test.go +++ b/container/gqueue/gqueue_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gqueue/gqueue_unit_test.go b/container/gqueue/gqueue_unit_test.go index cc4d75b6b..565028dd7 100644 --- a/container/gqueue/gqueue_unit_test.go +++ b/container/gqueue/gqueue_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gring/gring.go b/container/gring/gring.go index fc75335a5..ac308f56c 100644 --- a/container/gring/gring.go +++ b/container/gring/gring.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gring/gring_bench_test.go b/container/gring/gring_bench_test.go index dd9f69b22..5835af5bc 100644 --- a/container/gring/gring_bench_test.go +++ b/container/gring/gring_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gring/gring_unit_test.go b/container/gring/gring_unit_test.go index 144a0d565..706f273a3 100644 --- a/container/gring/gring_unit_test.go +++ b/container/gring/gring_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gset/gset_any_set.go b/container/gset/gset_any_set.go index 77b339bea..f6ceccfa6 100644 --- a/container/gset/gset_any_set.go +++ b/container/gset/gset_any_set.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gset/gset_int_set.go b/container/gset/gset_int_set.go index 6d006bfa2..18d6ca9e2 100644 --- a/container/gset/gset_int_set.go +++ b/container/gset/gset_int_set.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gset/gset_str_set.go b/container/gset/gset_str_set.go index 23a8dd32f..d800af22e 100644 --- a/container/gset/gset_str_set.go +++ b/container/gset/gset_str_set.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gset/gset_z_bench_test.go b/container/gset/gset_z_bench_test.go index 378cbae83..ed7a1c9cd 100644 --- a/container/gset/gset_z_bench_test.go +++ b/container/gset/gset_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gset/gset_z_example_any_test.go b/container/gset/gset_z_example_any_test.go index 2fa07cd00..f1a08309d 100644 --- a/container/gset/gset_z_example_any_test.go +++ b/container/gset/gset_z_example_any_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gset/gset_z_example_int_test.go b/container/gset/gset_z_example_int_test.go index cc14b4ec8..1118042d5 100644 --- a/container/gset/gset_z_example_int_test.go +++ b/container/gset/gset_z_example_int_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gset/gset_z_example_str_test.go b/container/gset/gset_z_example_str_test.go index f511e2ceb..d0c714bd5 100644 --- a/container/gset/gset_z_example_str_test.go +++ b/container/gset/gset_z_example_str_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gset/gset_z_unit_any_test.go b/container/gset/gset_z_unit_any_test.go index 50fa0417a..d9d6e6479 100644 --- a/container/gset/gset_z_unit_any_test.go +++ b/container/gset/gset_z_unit_any_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gset/gset_z_unit_int_test.go b/container/gset/gset_z_unit_int_test.go index 4ba7ad449..e45fa21d4 100644 --- a/container/gset/gset_z_unit_int_test.go +++ b/container/gset/gset_z_unit_int_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gset/gset_z_unit_str_test.go b/container/gset/gset_z_unit_str_test.go index c283b8cdd..33586cd8e 100644 --- a/container/gset/gset_z_unit_str_test.go +++ b/container/gset/gset_z_unit_str_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtree/gtree.go b/container/gtree/gtree.go index 56f69268d..8fb5c28ad 100644 --- a/container/gtree/gtree.go +++ b/container/gtree/gtree.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtree/gtree_avltree.go b/container/gtree/gtree_avltree.go index eb8e72d05..b5ee031dd 100644 --- a/container/gtree/gtree_avltree.go +++ b/container/gtree/gtree_avltree.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtree/gtree_btree.go b/container/gtree/gtree_btree.go index a8c1bbb3b..45a3068b9 100644 --- a/container/gtree/gtree_btree.go +++ b/container/gtree/gtree_btree.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtree/gtree_redblacktree.go b/container/gtree/gtree_redblacktree.go index f73c9e568..33a8d45dd 100644 --- a/container/gtree/gtree_redblacktree.go +++ b/container/gtree/gtree_redblacktree.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtree/gtree_z_avl_tree_test.go b/container/gtree/gtree_z_avl_tree_test.go index aa5b0b981..32ee443fe 100644 --- a/container/gtree/gtree_z_avl_tree_test.go +++ b/container/gtree/gtree_z_avl_tree_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gtree/gtree_z_b_tree_test.go b/container/gtree/gtree_z_b_tree_test.go index 469226de2..92dedccc2 100644 --- a/container/gtree/gtree_z_b_tree_test.go +++ b/container/gtree/gtree_z_b_tree_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gtree/gtree_z_redblack_tree_test.go b/container/gtree/gtree_z_redblack_tree_test.go index 37dee0c9c..7af80b05b 100644 --- a/container/gtree/gtree_z_redblack_tree_test.go +++ b/container/gtree/gtree_z_redblack_tree_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with gm file, diff --git a/container/gtype/bool.go b/container/gtype/bool.go index f24fc8661..adb49db20 100644 --- a/container/gtype/bool.go +++ b/container/gtype/bool.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/byte.go b/container/gtype/byte.go index 0fdeb0ab4..3b2a8d09b 100644 --- a/container/gtype/byte.go +++ b/container/gtype/byte.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/bytes.go b/container/gtype/bytes.go index 75ae554b4..9574558cb 100644 --- a/container/gtype/bytes.go +++ b/container/gtype/bytes.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/float32.go b/container/gtype/float32.go index 087a35d88..2eaeb8960 100644 --- a/container/gtype/float32.go +++ b/container/gtype/float32.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/float64.go b/container/gtype/float64.go index 749f1b622..ecc80facc 100644 --- a/container/gtype/float64.go +++ b/container/gtype/float64.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/gtype.go b/container/gtype/gtype.go index 795d36beb..b43e7f7d6 100644 --- a/container/gtype/gtype.go +++ b/container/gtype/gtype.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/int.go b/container/gtype/int.go index da649b946..c1b2543f0 100644 --- a/container/gtype/int.go +++ b/container/gtype/int.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/int32.go b/container/gtype/int32.go index 75aba6adb..63d4a5d23 100644 --- a/container/gtype/int32.go +++ b/container/gtype/int32.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/int64.go b/container/gtype/int64.go index 9d0f9448f..b9c356ebd 100644 --- a/container/gtype/int64.go +++ b/container/gtype/int64.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/interface.go b/container/gtype/interface.go index 0c9734e3f..acb26f46c 100644 --- a/container/gtype/interface.go +++ b/container/gtype/interface.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/string.go b/container/gtype/string.go index 58798140d..0e1483b6f 100644 --- a/container/gtype/string.go +++ b/container/gtype/string.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/uint.go b/container/gtype/uint.go index d8802265d..b9203a9af 100644 --- a/container/gtype/uint.go +++ b/container/gtype/uint.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/uint32.go b/container/gtype/uint32.go index 403b7de59..2fbdcb030 100644 --- a/container/gtype/uint32.go +++ b/container/gtype/uint32.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/uint64.go b/container/gtype/uint64.go index d3f8b36eb..580163bb5 100644 --- a/container/gtype/uint64.go +++ b/container/gtype/uint64.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_bench_basic_test.go b/container/gtype/z_bench_basic_test.go index 8a0d6f932..705d04f0c 100644 --- a/container/gtype/z_bench_basic_test.go +++ b/container/gtype/z_bench_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_bench_json_test.go b/container/gtype/z_bench_json_test.go index c4eeefeb5..b2830fae5 100644 --- a/container/gtype/z_bench_json_test.go +++ b/container/gtype/z_bench_json_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_bool_test.go b/container/gtype/z_unit_bool_test.go index 858868140..297f5de85 100644 --- a/container/gtype/z_unit_bool_test.go +++ b/container/gtype/z_unit_bool_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_byte_test.go b/container/gtype/z_unit_byte_test.go index ce171432a..0a6c77df4 100644 --- a/container/gtype/z_unit_byte_test.go +++ b/container/gtype/z_unit_byte_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_bytes_test.go b/container/gtype/z_unit_bytes_test.go index b0be15489..234307ae8 100644 --- a/container/gtype/z_unit_bytes_test.go +++ b/container/gtype/z_unit_bytes_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_float32_test.go b/container/gtype/z_unit_float32_test.go index fb88f01ae..14914ed27 100644 --- a/container/gtype/z_unit_float32_test.go +++ b/container/gtype/z_unit_float32_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_float64_test.go b/container/gtype/z_unit_float64_test.go index 12079cb5c..717e0500a 100644 --- a/container/gtype/z_unit_float64_test.go +++ b/container/gtype/z_unit_float64_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_int32_test.go b/container/gtype/z_unit_int32_test.go index 53c268c7f..c2b7d5690 100644 --- a/container/gtype/z_unit_int32_test.go +++ b/container/gtype/z_unit_int32_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_int64_test.go b/container/gtype/z_unit_int64_test.go index 21110b8bf..85a0bbea8 100644 --- a/container/gtype/z_unit_int64_test.go +++ b/container/gtype/z_unit_int64_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_int_test.go b/container/gtype/z_unit_int_test.go index eccdffbd3..49e44b2f2 100644 --- a/container/gtype/z_unit_int_test.go +++ b/container/gtype/z_unit_int_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_interface_test.go b/container/gtype/z_unit_interface_test.go index 05e124a79..990951b53 100644 --- a/container/gtype/z_unit_interface_test.go +++ b/container/gtype/z_unit_interface_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_string_test.go b/container/gtype/z_unit_string_test.go index bb1362a6a..16132474d 100644 --- a/container/gtype/z_unit_string_test.go +++ b/container/gtype/z_unit_string_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_uint32_test.go b/container/gtype/z_unit_uint32_test.go index 650d4a97c..1e9d0a508 100644 --- a/container/gtype/z_unit_uint32_test.go +++ b/container/gtype/z_unit_uint32_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_uint64_test.go b/container/gtype/z_unit_uint64_test.go index a4e5c02a7..1c97b53c5 100644 --- a/container/gtype/z_unit_uint64_test.go +++ b/container/gtype/z_unit_uint64_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gtype/z_unit_uint_test.go b/container/gtype/z_unit_uint_test.go index 5524031c4..d9237940a 100644 --- a/container/gtype/z_unit_uint_test.go +++ b/container/gtype/z_unit_uint_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar.go b/container/gvar/gvar.go index 3ee988702..1f8fd0e4e 100644 --- a/container/gvar/gvar.go +++ b/container/gvar/gvar.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_is.go b/container/gvar/gvar_is.go index 396b4e40e..a7d2f14c7 100644 --- a/container/gvar/gvar_is.go +++ b/container/gvar/gvar_is.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_list.go b/container/gvar/gvar_list.go index bc6d74188..7e4c28833 100644 --- a/container/gvar/gvar_list.go +++ b/container/gvar/gvar_list.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_map.go b/container/gvar/gvar_map.go index 56bace487..40bb97e9a 100644 --- a/container/gvar/gvar_map.go +++ b/container/gvar/gvar_map.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_struct.go b/container/gvar/gvar_struct.go index d215e4df1..2300a46ab 100644 --- a/container/gvar/gvar_struct.go +++ b/container/gvar/gvar_struct.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_z_bench_obj_test.go b/container/gvar/gvar_z_bench_obj_test.go index 895518338..8053ce174 100644 --- a/container/gvar/gvar_z_bench_obj_test.go +++ b/container/gvar/gvar_z_bench_obj_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_z_bench_ptr_test.go b/container/gvar/gvar_z_bench_ptr_test.go index 619fafc37..815e8cabf 100644 --- a/container/gvar/gvar_z_bench_ptr_test.go +++ b/container/gvar/gvar_z_bench_ptr_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_z_unit_basic_test.go b/container/gvar/gvar_z_unit_basic_test.go index a3ae8cff4..f3feaf3ed 100644 --- a/container/gvar/gvar_z_unit_basic_test.go +++ b/container/gvar/gvar_z_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_z_unit_is_test.go b/container/gvar/gvar_z_unit_is_test.go index c1656e044..d485b2af7 100644 --- a/container/gvar/gvar_z_unit_is_test.go +++ b/container/gvar/gvar_z_unit_is_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_z_unit_json_test.go b/container/gvar/gvar_z_unit_json_test.go index 97d556ca6..0c02c80eb 100644 --- a/container/gvar/gvar_z_unit_json_test.go +++ b/container/gvar/gvar_z_unit_json_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_z_unit_list_test.go b/container/gvar/gvar_z_unit_list_test.go index fba1fd3bb..d62a4d24a 100644 --- a/container/gvar/gvar_z_unit_list_test.go +++ b/container/gvar/gvar_z_unit_list_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_z_unit_map_test.go b/container/gvar/gvar_z_unit_map_test.go index 181344761..30e6c7718 100644 --- a/container/gvar/gvar_z_unit_map_test.go +++ b/container/gvar/gvar_z_unit_map_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_z_unit_maptomap_test.go b/container/gvar/gvar_z_unit_maptomap_test.go index 708d0a3d1..89350f1d7 100644 --- a/container/gvar/gvar_z_unit_maptomap_test.go +++ b/container/gvar/gvar_z_unit_maptomap_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/container/gvar/gvar_z_unit_struct_test.go b/container/gvar/gvar_z_unit_struct_test.go index 64f7f0d40..e65c593a3 100644 --- a/container/gvar/gvar_z_unit_struct_test.go +++ b/container/gvar/gvar_z_unit_struct_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/crypto/gaes/gaes.go b/crypto/gaes/gaes.go index d0ddb9ae7..6e2f84d5a 100644 --- a/crypto/gaes/gaes.go +++ b/crypto/gaes/gaes.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/crypto/gaes/gaes_test.go b/crypto/gaes/gaes_test.go index 58b1fc3c7..eeb94c21a 100644 --- a/crypto/gaes/gaes_test.go +++ b/crypto/gaes/gaes_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/crypto/gcrc32/gcrc32.go b/crypto/gcrc32/gcrc32.go index 5ef54cbca..59b193bd2 100644 --- a/crypto/gcrc32/gcrc32.go +++ b/crypto/gcrc32/gcrc32.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/crypto/gcrc32/gcrc32_test.go b/crypto/gcrc32/gcrc32_test.go index f5682e439..8bd33bf39 100644 --- a/crypto/gcrc32/gcrc32_test.go +++ b/crypto/gcrc32/gcrc32_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/crypto/gdes/gdes.go b/crypto/gdes/gdes.go index 7f1b59e94..55d936dfe 100644 --- a/crypto/gdes/gdes.go +++ b/crypto/gdes/gdes.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/crypto/gdes/gdes_test.go b/crypto/gdes/gdes_test.go index 384480713..c4f05303c 100644 --- a/crypto/gdes/gdes_test.go +++ b/crypto/gdes/gdes_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/crypto/gmd5/gmd5.go b/crypto/gmd5/gmd5.go index f73a2ca27..ab16f1cf7 100644 --- a/crypto/gmd5/gmd5.go +++ b/crypto/gmd5/gmd5.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/crypto/gmd5/gmd5_test.go b/crypto/gmd5/gmd5_test.go index 286308373..a2f3b8e28 100644 --- a/crypto/gmd5/gmd5_test.go +++ b/crypto/gmd5/gmd5_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/crypto/gsha1/gsha1.go b/crypto/gsha1/gsha1.go index caa43d008..7f8b6347f 100644 --- a/crypto/gsha1/gsha1.go +++ b/crypto/gsha1/gsha1.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/crypto/gsha1/gsha1_test.go b/crypto/gsha1/gsha1_test.go index 775a43ac9..8fa22b096 100644 --- a/crypto/gsha1/gsha1_test.go +++ b/crypto/gsha1/gsha1_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 8375400b0..898624713 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_core_utility.go b/database/gdb/gdb_core_utility.go index c86086d99..70d1aec99 100644 --- a/database/gdb/gdb_core_utility.go +++ b/database/gdb/gdb_core_utility.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index b3718e333..c5433e9d3 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index 509152a58..f5a7290ce 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 828439e4b..07a2bd18c 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index b70808b32..5457ecdb4 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 34bdfc3e2..39cbd5213 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_cache.go b/database/gdb/gdb_model_cache.go index 16263c904..f8f4b5578 100644 --- a/database/gdb/gdb_model_cache.go +++ b/database/gdb/gdb_model_cache.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 30f440f84..832508017 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_delete.go b/database/gdb/gdb_model_delete.go index 16cdb84e6..9db70dedc 100644 --- a/database/gdb/gdb_model_delete.go +++ b/database/gdb/gdb_model_delete.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index de331dcef..abf3e76cf 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index eabe7e1e5..89d22e1b0 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_join.go b/database/gdb/gdb_model_join.go index aa7e12a52..1eebb7eb1 100644 --- a/database/gdb/gdb_model_join.go +++ b/database/gdb/gdb_model_join.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_lock.go b/database/gdb/gdb_model_lock.go index 79fdf975b..a207a126e 100644 --- a/database/gdb/gdb_model_lock.go +++ b/database/gdb/gdb_model_lock.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_option.go b/database/gdb/gdb_model_option.go index 4634a75a1..e41e44316 100644 --- a/database/gdb/gdb_model_option.go +++ b/database/gdb/gdb_model_option.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 80786614d..99fe48f8e 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_time.go b/database/gdb/gdb_model_time.go index ad594c230..ce14d831e 100644 --- a/database/gdb/gdb_model_time.go +++ b/database/gdb/gdb_model_time.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index e76b5d386..3d7b2f0a6 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index c2949d64e..9ec5f74eb 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_result.go b/database/gdb/gdb_result.go index f2d8fb444..acfbd3f1c 100644 --- a/database/gdb/gdb_result.go +++ b/database/gdb/gdb_result.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_schema.go b/database/gdb/gdb_schema.go index b1d59ad04..6f520fa81 100644 --- a/database/gdb/gdb_schema.go +++ b/database/gdb/gdb_schema.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_transaction.go index 263ff476f..e4b0de61b 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_transaction.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_type_record.go b/database/gdb/gdb_type_record.go index 13edcf385..f469c413f 100644 --- a/database/gdb/gdb_type_record.go +++ b/database/gdb/gdb_type_record.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_type_record_deprecated.go b/database/gdb/gdb_type_record_deprecated.go index 0a241de40..a21df29c1 100644 --- a/database/gdb/gdb_type_record_deprecated.go +++ b/database/gdb/gdb_type_record_deprecated.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_type_result.go b/database/gdb/gdb_type_result.go index e45ceffdb..92377fadc 100644 --- a/database/gdb/gdb_type_result.go +++ b/database/gdb/gdb_type_result.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_type_result_deprecated.go b/database/gdb/gdb_type_result_deprecated.go index d9a991bab..b09c75971 100644 --- a/database/gdb/gdb_type_result_deprecated.go +++ b/database/gdb/gdb_type_result_deprecated.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index cf3a74f2d..06cb9b8e1 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_driver_test.go b/database/gdb/gdb_z_driver_test.go index 7daef73bb..e9f1c8883 100644 --- a/database/gdb/gdb_z_driver_test.go +++ b/database/gdb/gdb_z_driver_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_example_test.go b/database/gdb/gdb_z_example_test.go index b9f4edfa9..4ede0e233 100644 --- a/database/gdb/gdb_z_example_test.go +++ b/database/gdb/gdb_z_example_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_init_test.go b/database/gdb/gdb_z_init_test.go index b95081a8b..44ad2167e 100644 --- a/database/gdb/gdb_z_init_test.go +++ b/database/gdb/gdb_z_init_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_mysql_association_test.go b/database/gdb/gdb_z_mysql_association_test.go index 4b8df7c2d..856377fea 100644 --- a/database/gdb/gdb_z_mysql_association_test.go +++ b/database/gdb/gdb_z_mysql_association_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_mysql_basic_test.go b/database/gdb/gdb_z_mysql_basic_test.go index e4fee4e42..74a8ecd2c 100644 --- a/database/gdb/gdb_z_mysql_basic_test.go +++ b/database/gdb/gdb_z_mysql_basic_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_mysql_ctx_test.go b/database/gdb/gdb_z_mysql_ctx_test.go index b99347942..6a6a811ab 100644 --- a/database/gdb/gdb_z_mysql_ctx_test.go +++ b/database/gdb/gdb_z_mysql_ctx_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index af07624ae..4e3bf14c5 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index be7d19319..295b0d111 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_mysql_raw_test.go b/database/gdb/gdb_z_mysql_raw_test.go index ac6a29367..d9ec28d11 100644 --- a/database/gdb/gdb_z_mysql_raw_test.go +++ b/database/gdb/gdb_z_mysql_raw_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index 4c4763acb..b3e15d839 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index bbfaaddd4..8bd97cdee 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gdb/gdb_z_mysql_types_test.go b/database/gdb/gdb_z_mysql_types_test.go index afe03a5d5..792af82a2 100644 --- a/database/gdb/gdb_z_mysql_types_test.go +++ b/database/gdb/gdb_z_mysql_types_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gredis/gredis_config.go b/database/gredis/gredis_config.go index c8d658081..49584efb9 100644 --- a/database/gredis/gredis_config.go +++ b/database/gredis/gredis_config.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index 32538f639..0a9f30b5b 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gredis/gredis_instance.go b/database/gredis/gredis_instance.go index 34124dd21..4a5afcd25 100644 --- a/database/gredis/gredis_instance.go +++ b/database/gredis/gredis_instance.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gredis/gredis_z_example_test.go b/database/gredis/gredis_z_example_test.go index 47cb81487..17e4b8f02 100644 --- a/database/gredis/gredis_z_example_test.go +++ b/database/gredis/gredis_z_example_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gredis/gredis_z_unit_conn_test.go b/database/gredis/gredis_z_unit_conn_test.go index ce5157a03..bd93429ec 100644 --- a/database/gredis/gredis_z_unit_conn_test.go +++ b/database/gredis/gredis_z_unit_conn_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/database/gredis/gredis_z_unit_test.go b/database/gredis/gredis_z_unit_test.go index dc50185b6..b995511be 100644 --- a/database/gredis/gredis_z_unit_test.go +++ b/database/gredis/gredis_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/debug/gdebug/gdebug.go b/debug/gdebug/gdebug.go index 4771ee648..7ff8e3db9 100644 --- a/debug/gdebug/gdebug.go +++ b/debug/gdebug/gdebug.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/debug/gdebug/gdebug_caller.go b/debug/gdebug/gdebug_caller.go index d5a55594e..3256d2695 100644 --- a/debug/gdebug/gdebug_caller.go +++ b/debug/gdebug/gdebug_caller.go @@ -1,4 +1,4 @@ -// Copyright 2019-2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/debug/gdebug/gdebug_grid.go b/debug/gdebug/gdebug_grid.go index 4f01fb9cd..f43023d87 100644 --- a/debug/gdebug/gdebug_grid.go +++ b/debug/gdebug/gdebug_grid.go @@ -1,4 +1,4 @@ -// Copyright 2019-2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/debug/gdebug/gdebug_stack.go b/debug/gdebug/gdebug_stack.go index 67ec3e30e..5e6b572fc 100644 --- a/debug/gdebug/gdebug_stack.go +++ b/debug/gdebug/gdebug_stack.go @@ -1,4 +1,4 @@ -// Copyright 2019-2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/debug/gdebug/gdebug_testdata.go b/debug/gdebug/gdebug_testdata.go index f0be40a72..e3d2dadad 100644 --- a/debug/gdebug/gdebug_testdata.go +++ b/debug/gdebug/gdebug_testdata.go @@ -1,4 +1,4 @@ -// Copyright 2019-2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/debug/gdebug/gdebug_version.go b/debug/gdebug/gdebug_version.go index 69a5fc97e..dabfb0c06 100644 --- a/debug/gdebug/gdebug_version.go +++ b/debug/gdebug/gdebug_version.go @@ -1,4 +1,4 @@ -// Copyright 2019-2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/debug/gdebug/gdebug_z_bench_test.go b/debug/gdebug/gdebug_z_bench_test.go index 7e26cdd60..768d53ce2 100644 --- a/debug/gdebug/gdebug_z_bench_test.go +++ b/debug/gdebug/gdebug_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gbase64/gbase64.go b/encoding/gbase64/gbase64.go index 6a2e7b76d..b88373677 100644 --- a/encoding/gbase64/gbase64.go +++ b/encoding/gbase64/gbase64.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gbase64/gbase64_test.go b/encoding/gbase64/gbase64_test.go index c382a239b..5193c7172 100644 --- a/encoding/gbase64/gbase64_test.go +++ b/encoding/gbase64/gbase64_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gbinary/gbinary.go b/encoding/gbinary/gbinary.go index 7081b88dc..5fbc48477 100644 --- a/encoding/gbinary/gbinary.go +++ b/encoding/gbinary/gbinary.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gbinary/gbinary_be.go b/encoding/gbinary/gbinary_be.go index 7d5fcbf40..2313aff1d 100644 --- a/encoding/gbinary/gbinary_be.go +++ b/encoding/gbinary/gbinary_be.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gbinary/gbinary_bit.go b/encoding/gbinary/gbinary_bit.go index 04cdd32bb..536fb4030 100644 --- a/encoding/gbinary/gbinary_bit.go +++ b/encoding/gbinary/gbinary_bit.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gbinary/gbinary_func.go b/encoding/gbinary/gbinary_func.go index 8fd332e98..6e1fba246 100644 --- a/encoding/gbinary/gbinary_func.go +++ b/encoding/gbinary/gbinary_func.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gbinary/gbinary_le.go b/encoding/gbinary/gbinary_le.go index db2cf0439..001208ddf 100644 --- a/encoding/gbinary/gbinary_le.go +++ b/encoding/gbinary/gbinary_le.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gbinary/gbinary_z_be_test.go b/encoding/gbinary/gbinary_z_be_test.go index 509424c62..41cd46772 100644 --- a/encoding/gbinary/gbinary_z_be_test.go +++ b/encoding/gbinary/gbinary_z_be_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gbinary/gbinary_z_le_test.go b/encoding/gbinary/gbinary_z_le_test.go index a25528b69..6609f40d7 100644 --- a/encoding/gbinary/gbinary_z_le_test.go +++ b/encoding/gbinary/gbinary_z_le_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gbinary/gbinary_z_test.go b/encoding/gbinary/gbinary_z_test.go index 6472375ba..349531f2e 100644 --- a/encoding/gbinary/gbinary_z_test.go +++ b/encoding/gbinary/gbinary_z_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gcharset/gcharset.go b/encoding/gcharset/gcharset.go index 3fcae958f..c976fa606 100644 --- a/encoding/gcharset/gcharset.go +++ b/encoding/gcharset/gcharset.go @@ -1,4 +1,4 @@ -// Copyright 2018-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gcharset/gcharset_test.go b/encoding/gcharset/gcharset_test.go index bee6e7b83..2f09f9e84 100644 --- a/encoding/gcharset/gcharset_test.go +++ b/encoding/gcharset/gcharset_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gcompress/gcompress.go b/encoding/gcompress/gcompress.go index 6b78b780c..1b4ca9423 100644 --- a/encoding/gcompress/gcompress.go +++ b/encoding/gcompress/gcompress.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gcompress/gcompress_gzip.go b/encoding/gcompress/gcompress_gzip.go index e6bd5f155..884bb4a1d 100644 --- a/encoding/gcompress/gcompress_gzip.go +++ b/encoding/gcompress/gcompress_gzip.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gcompress/gcompress_z_unit_gzip_test.go b/encoding/gcompress/gcompress_z_unit_gzip_test.go index 77bb4fc95..cb0674b11 100644 --- a/encoding/gcompress/gcompress_z_unit_gzip_test.go +++ b/encoding/gcompress/gcompress_z_unit_gzip_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gcompress/gcompress_z_unit_zip_test.go b/encoding/gcompress/gcompress_z_unit_zip_test.go index 66ec5a515..5a289202b 100644 --- a/encoding/gcompress/gcompress_z_unit_zip_test.go +++ b/encoding/gcompress/gcompress_z_unit_zip_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gcompress/gcompress_z_unit_zlib_test.go b/encoding/gcompress/gcompress_z_unit_zlib_test.go index 5683fd297..02d6330e7 100644 --- a/encoding/gcompress/gcompress_z_unit_zlib_test.go +++ b/encoding/gcompress/gcompress_z_unit_zlib_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gcompress/gcompress_zip.go b/encoding/gcompress/gcompress_zip.go index 34076e413..72991aff9 100644 --- a/encoding/gcompress/gcompress_zip.go +++ b/encoding/gcompress/gcompress_zip.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gcompress/gcompress_zlib.go b/encoding/gcompress/gcompress_zlib.go index 8ffcdaf6b..8d3f74605 100644 --- a/encoding/gcompress/gcompress_zlib.go +++ b/encoding/gcompress/gcompress_zlib.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/ghash/ghash.go b/encoding/ghash/ghash.go index ede93117f..7d69d4fc6 100644 --- a/encoding/ghash/ghash.go +++ b/encoding/ghash/ghash.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/ghash/ghash_bench_test.go b/encoding/ghash/ghash_bench_test.go index bb91711e8..1504a9b34 100644 --- a/encoding/ghash/ghash_bench_test.go +++ b/encoding/ghash/ghash_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/ghtml/ghtml.go b/encoding/ghtml/ghtml.go index 1064ffd2b..da0b798cd 100644 --- a/encoding/ghtml/ghtml.go +++ b/encoding/ghtml/ghtml.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/ghtml/ghtml_test.go b/encoding/ghtml/ghtml_test.go index 9752db19b..fbf62aee9 100644 --- a/encoding/ghtml/ghtml_test.go +++ b/encoding/ghtml/ghtml_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gini/gini.go b/encoding/gini/gini.go index e9c7db20e..8ca3770bb 100644 --- a/encoding/gini/gini.go +++ b/encoding/gini/gini.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gini/gini_test.go b/encoding/gini/gini_test.go index f5339cca2..e373410b5 100644 --- a/encoding/gini/gini_test.go +++ b/encoding/gini/gini_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson.go b/encoding/gjson/gjson.go index 1087422a6..2a16bb9bf 100644 --- a/encoding/gjson/gjson.go +++ b/encoding/gjson/gjson.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_api.go b/encoding/gjson/gjson_api.go index 9a76d5992..3f74ef6ea 100644 --- a/encoding/gjson/gjson_api.go +++ b/encoding/gjson/gjson_api.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_api_config.go b/encoding/gjson/gjson_api_config.go index ff83ed540..1fe53a9ac 100644 --- a/encoding/gjson/gjson_api_config.go +++ b/encoding/gjson/gjson_api_config.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_api_encoding.go b/encoding/gjson/gjson_api_encoding.go index dd8bb5d87..d745c7119 100644 --- a/encoding/gjson/gjson_api_encoding.go +++ b/encoding/gjson/gjson_api_encoding.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index e0b2d52a1..b6a711f17 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_deprecated.go b/encoding/gjson/gjson_deprecated.go index 581f0e653..a373e6823 100644 --- a/encoding/gjson/gjson_deprecated.go +++ b/encoding/gjson/gjson_deprecated.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_implements.go b/encoding/gjson/gjson_implements.go index 8a75e9ae7..c621482e9 100644 --- a/encoding/gjson/gjson_implements.go +++ b/encoding/gjson/gjson_implements.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_stdlib_json_util.go b/encoding/gjson/gjson_stdlib_json_util.go index c7ebc0bd1..be1c57668 100644 --- a/encoding/gjson/gjson_stdlib_json_util.go +++ b/encoding/gjson/gjson_stdlib_json_util.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_bench_test.go b/encoding/gjson/gjson_z_bench_test.go index 2d30159b4..2b94f89f7 100644 --- a/encoding/gjson/gjson_z_bench_test.go +++ b/encoding/gjson/gjson_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_example_conversion_test.go b/encoding/gjson/gjson_z_example_conversion_test.go index 1d067e217..1a103f6a6 100644 --- a/encoding/gjson/gjson_z_example_conversion_test.go +++ b/encoding/gjson/gjson_z_example_conversion_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_example_dataset_test.go b/encoding/gjson/gjson_z_example_dataset_test.go index e335d1369..868052a3f 100644 --- a/encoding/gjson/gjson_z_example_dataset_test.go +++ b/encoding/gjson/gjson_z_example_dataset_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_example_load_test.go b/encoding/gjson/gjson_z_example_load_test.go index c67c2368a..733cf4db9 100644 --- a/encoding/gjson/gjson_z_example_load_test.go +++ b/encoding/gjson/gjson_z_example_load_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_example_new_test.go b/encoding/gjson/gjson_z_example_new_test.go index e735284c9..6b0f2b918 100644 --- a/encoding/gjson/gjson_z_example_new_test.go +++ b/encoding/gjson/gjson_z_example_new_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_example_pattern_test.go b/encoding/gjson/gjson_z_example_pattern_test.go index 9af658333..faa6e09a5 100644 --- a/encoding/gjson/gjson_z_example_pattern_test.go +++ b/encoding/gjson/gjson_z_example_pattern_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_unit_basic_test.go b/encoding/gjson/gjson_z_unit_basic_test.go index 6269c94ff..1c5244556 100644 --- a/encoding/gjson/gjson_z_unit_basic_test.go +++ b/encoding/gjson/gjson_z_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_unit_implements_test.go b/encoding/gjson/gjson_z_unit_implements_test.go index 7f1601098..8264e1625 100644 --- a/encoding/gjson/gjson_z_unit_implements_test.go +++ b/encoding/gjson/gjson_z_unit_implements_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_unit_internal_test.go b/encoding/gjson/gjson_z_unit_internal_test.go index 1fb508956..99b07ad62 100644 --- a/encoding/gjson/gjson_z_unit_internal_test.go +++ b/encoding/gjson/gjson_z_unit_internal_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_unit_json_test.go b/encoding/gjson/gjson_z_unit_json_test.go index e2d113cff..8c2b50feb 100644 --- a/encoding/gjson/gjson_z_unit_json_test.go +++ b/encoding/gjson/gjson_z_unit_json_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_unit_load_test.go b/encoding/gjson/gjson_z_unit_load_test.go index a59137e18..c26a4e258 100644 --- a/encoding/gjson/gjson_z_unit_load_test.go +++ b/encoding/gjson/gjson_z_unit_load_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_unit_new_test.go b/encoding/gjson/gjson_z_unit_new_test.go index a4b64fb1b..c3051ce26 100644 --- a/encoding/gjson/gjson_z_unit_new_test.go +++ b/encoding/gjson/gjson_z_unit_new_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_unit_set_test.go b/encoding/gjson/gjson_z_unit_set_test.go index 353a0e214..1a6dc557d 100644 --- a/encoding/gjson/gjson_z_unit_set_test.go +++ b/encoding/gjson/gjson_z_unit_set_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gjson/gjson_z_unit_struct_test.go b/encoding/gjson/gjson_z_unit_struct_test.go index 82054c894..046acc194 100644 --- a/encoding/gjson/gjson_z_unit_struct_test.go +++ b/encoding/gjson/gjson_z_unit_struct_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gparser/gparser.go b/encoding/gparser/gparser.go index ff7f2738b..89ea36e29 100644 --- a/encoding/gparser/gparser.go +++ b/encoding/gparser/gparser.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gparser/gparser_api_encoding.go b/encoding/gparser/gparser_api_encoding.go index a848161a7..a9b2901a6 100644 --- a/encoding/gparser/gparser_api_encoding.go +++ b/encoding/gparser/gparser_api_encoding.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gparser/gparser_api_new_load.go b/encoding/gparser/gparser_api_new_load.go index d731d1140..5b368858b 100644 --- a/encoding/gparser/gparser_api_new_load.go +++ b/encoding/gparser/gparser_api_new_load.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gparser/gparser_unit_basic_test.go b/encoding/gparser/gparser_unit_basic_test.go index 1eeca7bae..59beb1d69 100644 --- a/encoding/gparser/gparser_unit_basic_test.go +++ b/encoding/gparser/gparser_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gparser/gparser_unit_load_test.go b/encoding/gparser/gparser_unit_load_test.go index a92f50c9f..b2cc024d4 100644 --- a/encoding/gparser/gparser_unit_load_test.go +++ b/encoding/gparser/gparser_unit_load_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gparser/gparser_unit_new_test.go b/encoding/gparser/gparser_unit_new_test.go index 0f8984825..1e9614444 100644 --- a/encoding/gparser/gparser_unit_new_test.go +++ b/encoding/gparser/gparser_unit_new_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gparser/gparser_unit_set_test.go b/encoding/gparser/gparser_unit_set_test.go index 777b496c1..7b07f542d 100644 --- a/encoding/gparser/gparser_unit_set_test.go +++ b/encoding/gparser/gparser_unit_set_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gtoml/gtoml.go b/encoding/gtoml/gtoml.go index 3d4a7887d..80610373d 100644 --- a/encoding/gtoml/gtoml.go +++ b/encoding/gtoml/gtoml.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gtoml/gtoml_test.go b/encoding/gtoml/gtoml_test.go index b9c4ccfd3..1074600b5 100644 --- a/encoding/gtoml/gtoml_test.go +++ b/encoding/gtoml/gtoml_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gurl/url.go b/encoding/gurl/url.go index c1a480519..2e3d2d508 100644 --- a/encoding/gurl/url.go +++ b/encoding/gurl/url.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gurl/url_test.go b/encoding/gurl/url_test.go index 59cba45b3..c992caea3 100644 --- a/encoding/gurl/url_test.go +++ b/encoding/gurl/url_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gxml/gxml.go b/encoding/gxml/gxml.go index df096f31b..cf831bd28 100644 --- a/encoding/gxml/gxml.go +++ b/encoding/gxml/gxml.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gxml/gxml_test.go b/encoding/gxml/gxml_test.go index d9ea1970d..8d9989a6d 100644 --- a/encoding/gxml/gxml_test.go +++ b/encoding/gxml/gxml_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gyaml/gyaml.go b/encoding/gyaml/gyaml.go index 587eba319..95c531b57 100644 --- a/encoding/gyaml/gyaml.go +++ b/encoding/gyaml/gyaml.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/encoding/gyaml/gyaml_test.go b/encoding/gyaml/gyaml_test.go index e63267c73..072349c37 100644 --- a/encoding/gyaml/gyaml_test.go +++ b/encoding/gyaml/gyaml_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index cfe301486..1e7588269 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/errors/gerror/gerror_stack.go b/errors/gerror/gerror_stack.go index d5fbb3ffb..b65cce769 100644 --- a/errors/gerror/gerror_stack.go +++ b/errors/gerror/gerror_stack.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/errors/gerror/gerror_z_bench_test.go b/errors/gerror/gerror_z_bench_test.go index 679dc5e6b..fea2aaf8f 100644 --- a/errors/gerror/gerror_z_bench_test.go +++ b/errors/gerror/gerror_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/errors/gerror/gerror_z_example_test.go b/errors/gerror/gerror_z_example_test.go index 2725ac9ea..7c627dbbe 100644 --- a/errors/gerror/gerror_z_example_test.go +++ b/errors/gerror/gerror_z_example_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/g/g.go b/frame/g/g.go index 72bdd723b..7a188318e 100644 --- a/frame/g/g.go +++ b/frame/g/g.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/g/g_func.go b/frame/g/g_func.go index 7b1afc730..a4d5ba436 100644 --- a/frame/g/g_func.go +++ b/frame/g/g_func.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/g/g_logger.go b/frame/g/g_logger.go index 280162e97..7d0a6c2d1 100644 --- a/frame/g/g_logger.go +++ b/frame/g/g_logger.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/g/g_object.go b/frame/g/g_object.go index 551719ee6..eee9bb8a4 100644 --- a/frame/g/g_object.go +++ b/frame/g/g_object.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/g/g_setting.go b/frame/g/g_setting.go index d989adaf1..a8adff947 100644 --- a/frame/g/g_setting.go +++ b/frame/g/g_setting.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/g/g_z_example_test.go b/frame/g/g_z_example_test.go index 414b41d88..262ebb775 100644 --- a/frame/g/g_z_example_test.go +++ b/frame/g/g_z_example_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins.go b/frame/gins/gins.go index 5e8ff2e51..af822b0f6 100644 --- a/frame/gins/gins.go +++ b/frame/gins/gins.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_config.go b/frame/gins/gins_config.go index b68d74d0a..38bca58b4 100644 --- a/frame/gins/gins_config.go +++ b/frame/gins/gins_config.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index b3a57a6b1..e313e1ec5 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_i18n.go b/frame/gins/gins_i18n.go index cc4cb8c40..71b57b63a 100644 --- a/frame/gins/gins_i18n.go +++ b/frame/gins/gins_i18n.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_log.go b/frame/gins/gins_log.go index 4fb4bef51..3ec6c691c 100644 --- a/frame/gins/gins_log.go +++ b/frame/gins/gins_log.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_redis.go b/frame/gins/gins_redis.go index 3c1ef55a8..12ba495b4 100644 --- a/frame/gins/gins_redis.go +++ b/frame/gins/gins_redis.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_resource.go b/frame/gins/gins_resource.go index 20f235a63..fa97179fb 100644 --- a/frame/gins/gins_resource.go +++ b/frame/gins/gins_resource.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_server.go b/frame/gins/gins_server.go index 9a84c84b9..090835628 100644 --- a/frame/gins/gins_server.go +++ b/frame/gins/gins_server.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_view.go b/frame/gins/gins_view.go index b76b58761..ab49298fe 100644 --- a/frame/gins/gins_view.go +++ b/frame/gins/gins_view.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_z_unit_basic_test.go b/frame/gins/gins_z_unit_basic_test.go index 9ed95fabd..ba9253cc9 100644 --- a/frame/gins/gins_z_unit_basic_test.go +++ b/frame/gins/gins_z_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_z_unit_config_test.go b/frame/gins/gins_z_unit_config_test.go index d65041407..0057b347b 100644 --- a/frame/gins/gins_z_unit_config_test.go +++ b/frame/gins/gins_z_unit_config_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_z_unit_database_test.go b/frame/gins/gins_z_unit_database_test.go index ce85196f9..2693a27e7 100644 --- a/frame/gins/gins_z_unit_database_test.go +++ b/frame/gins/gins_z_unit_database_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_z_unit_redis_test.go b/frame/gins/gins_z_unit_redis_test.go index 3619b1dd5..6ac44f85f 100644 --- a/frame/gins/gins_z_unit_redis_test.go +++ b/frame/gins/gins_z_unit_redis_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gins/gins_z_unit_view_test.go b/frame/gins/gins_z_unit_view_test.go index e03f398dd..bc8f2d409 100644 --- a/frame/gins/gins_z_unit_view_test.go +++ b/frame/gins/gins_z_unit_view_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gmvc/controller.go b/frame/gmvc/controller.go index 678edb3be..39b63f271 100644 --- a/frame/gmvc/controller.go +++ b/frame/gmvc/controller.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gmvc/model.go b/frame/gmvc/model.go index 5eb656d0d..7e5f6a2b3 100644 --- a/frame/gmvc/model.go +++ b/frame/gmvc/model.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/frame/gmvc/view.go b/frame/gmvc/view.go index 73caebdea..99d7d73c2 100644 --- a/frame/gmvc/view.go +++ b/frame/gmvc/view.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/i18n/gi18n/gi18n.go b/i18n/gi18n/gi18n.go index 5cb8aea0b..6fc153d73 100644 --- a/i18n/gi18n/gi18n.go +++ b/i18n/gi18n/gi18n.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/i18n/gi18n/gi18n_instance.go b/i18n/gi18n/gi18n_instance.go index 4007fc1aa..d9abdbde2 100644 --- a/i18n/gi18n/gi18n_instance.go +++ b/i18n/gi18n/gi18n_instance.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/i18n/gi18n/gi18n_unit_test.go b/i18n/gi18n/gi18n_unit_test.go index 00757686f..b0012c1c7 100644 --- a/i18n/gi18n/gi18n_unit_test.go +++ b/i18n/gi18n/gi18n_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/fileinfo/fileinfo.go b/internal/fileinfo/fileinfo.go index 05747b126..d469e3ca7 100644 --- a/internal/fileinfo/fileinfo.go +++ b/internal/fileinfo/fileinfo.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index a001ab54e..c2cc60ad6 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/json/json.go b/internal/json/json.go index 589f7a2f3..1ed12ba79 100644 --- a/internal/json/json.go +++ b/internal/json/json.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/mutex/mutex.go b/internal/mutex/mutex.go index 50eb5b210..9e04dc6bf 100644 --- a/internal/mutex/mutex.go +++ b/internal/mutex/mutex.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/mutex/mutex_z_bench_test.go b/internal/mutex/mutex_z_bench_test.go index f67f1d3d9..90e81199a 100644 --- a/internal/mutex/mutex_z_bench_test.go +++ b/internal/mutex/mutex_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/mutex/mutex_z_unit_test.go b/internal/mutex/mutex_z_unit_test.go index 69c273804..3713d2b5a 100644 --- a/internal/mutex/mutex_z_unit_test.go +++ b/internal/mutex/mutex_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/rwmutex/rwmutex.go b/internal/rwmutex/rwmutex.go index 6bd21e49e..0a6a9615b 100644 --- a/internal/rwmutex/rwmutex.go +++ b/internal/rwmutex/rwmutex.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/rwmutex/rwmutex_z_bench_test.go b/internal/rwmutex/rwmutex_z_bench_test.go index e743c2547..ec745f71e 100644 --- a/internal/rwmutex/rwmutex_z_bench_test.go +++ b/internal/rwmutex/rwmutex_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/rwmutex/rwmutex_z_unit_test.go b/internal/rwmutex/rwmutex_z_unit_test.go index 7f44d4590..336855ccf 100644 --- a/internal/rwmutex/rwmutex_z_unit_test.go +++ b/internal/rwmutex/rwmutex_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/structs/structs.go b/internal/structs/structs.go index ea7455b4a..2fa28f523 100644 --- a/internal/structs/structs.go +++ b/internal/structs/structs.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/structs/structs_map.go b/internal/structs/structs_map.go index 0585a04d9..aa36050f4 100644 --- a/internal/structs/structs_map.go +++ b/internal/structs/structs_map.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/structs/structs_tag.go b/internal/structs/structs_tag.go index 6603c12b5..1a7ac849c 100644 --- a/internal/structs/structs_tag.go +++ b/internal/structs/structs_tag.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/structs/structs_z_bench_test.go b/internal/structs/structs_z_bench_test.go index 6cb7fc909..afdf7ad4b 100644 --- a/internal/structs/structs_z_bench_test.go +++ b/internal/structs/structs_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/structs/structs_z_unit_test.go b/internal/structs/structs_z_unit_test.go index f3bdba4de..5124c6cce 100644 --- a/internal/structs/structs_z_unit_test.go +++ b/internal/structs/structs_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 05d3fa6af..414a90ca0 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/utils/utils_array.go b/internal/utils/utils_array.go index eae9b96b6..b96e039e6 100644 --- a/internal/utils/utils_array.go +++ b/internal/utils/utils_array.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/utils/utils_io.go b/internal/utils/utils_io.go index 45dc34470..1d227bd79 100644 --- a/internal/utils/utils_io.go +++ b/internal/utils/utils_io.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/utils/utils_str.go b/internal/utils/utils_str.go index 1f2ca6ce1..7ca82b7bf 100644 --- a/internal/utils/utils_str.go +++ b/internal/utils/utils_str.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/utils/utils_z_bench_test.go b/internal/utils/utils_z_bench_test.go index 13320b73a..586a642fb 100644 --- a/internal/utils/utils_z_bench_test.go +++ b/internal/utils/utils_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/internal/utils/utils_z_test.go b/internal/utils/utils_z_test.go index a26e86e7a..12a946f80 100644 --- a/internal/utils/utils_z_test.go +++ b/internal/utils/utils_z_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index 2fdfe53c4..41d751fab 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_client_api.go b/net/ghttp/ghttp_client_api.go index 591d47064..335902e6f 100644 --- a/net/ghttp/ghttp_client_api.go +++ b/net/ghttp/ghttp_client_api.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_client_bytes.go b/net/ghttp/ghttp_client_bytes.go index cde71c21f..a7b6b412c 100644 --- a/net/ghttp/ghttp_client_bytes.go +++ b/net/ghttp/ghttp_client_bytes.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_client_chain.go b/net/ghttp/ghttp_client_chain.go index 781ed0e81..e27159015 100644 --- a/net/ghttp/ghttp_client_chain.go +++ b/net/ghttp/ghttp_client_chain.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_client_config.go b/net/ghttp/ghttp_client_config.go index 207cfe868..4a47d4a47 100644 --- a/net/ghttp/ghttp_client_config.go +++ b/net/ghttp/ghttp_client_config.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_client_content.go b/net/ghttp/ghttp_client_content.go index d010e3470..c3cef5a3d 100644 --- a/net/ghttp/ghttp_client_content.go +++ b/net/ghttp/ghttp_client_content.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_client_dump.go b/net/ghttp/ghttp_client_dump.go index beba4f847..bd8c4f875 100644 --- a/net/ghttp/ghttp_client_dump.go +++ b/net/ghttp/ghttp_client_dump.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_client_request.go b/net/ghttp/ghttp_client_request.go index ceb6007ef..5d724cea6 100644 --- a/net/ghttp/ghttp_client_request.go +++ b/net/ghttp/ghttp_client_request.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_client_response.go b/net/ghttp/ghttp_client_response.go index 30995be5b..f7ed30014 100644 --- a/net/ghttp/ghttp_client_response.go +++ b/net/ghttp/ghttp_client_response.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_client_var.go b/net/ghttp/ghttp_client_var.go index 5102462e6..e8b2a00b0 100644 --- a/net/ghttp/ghttp_client_var.go +++ b/net/ghttp/ghttp_client_var.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_controller.go b/net/ghttp/ghttp_controller.go index 376c1427b..1ed2c6701 100644 --- a/net/ghttp/ghttp_controller.go +++ b/net/ghttp/ghttp_controller.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index 9256455fa..3c0e244f6 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_auth.go b/net/ghttp/ghttp_request_auth.go index 82ab1c666..a021692eb 100644 --- a/net/ghttp/ghttp_request_auth.go +++ b/net/ghttp/ghttp_request_auth.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index a9a122bdf..3eef4057a 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_param_ctx.go b/net/ghttp/ghttp_request_param_ctx.go index 0fe2fcda7..395d6c0e6 100644 --- a/net/ghttp/ghttp_request_param_ctx.go +++ b/net/ghttp/ghttp_request_param_ctx.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_param_file.go b/net/ghttp/ghttp_request_param_file.go index 0086b9e3e..c5b803221 100644 --- a/net/ghttp/ghttp_request_param_file.go +++ b/net/ghttp/ghttp_request_param_file.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_param_form.go b/net/ghttp/ghttp_request_param_form.go index ef9f6bf6e..630e9ea74 100644 --- a/net/ghttp/ghttp_request_param_form.go +++ b/net/ghttp/ghttp_request_param_form.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_param_page.go b/net/ghttp/ghttp_request_param_page.go index c9fbe7d03..478ae3ed0 100644 --- a/net/ghttp/ghttp_request_param_page.go +++ b/net/ghttp/ghttp_request_param_page.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_param_param.go b/net/ghttp/ghttp_request_param_param.go index c318ae64b..a965b6d46 100644 --- a/net/ghttp/ghttp_request_param_param.go +++ b/net/ghttp/ghttp_request_param_param.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_param_post.go b/net/ghttp/ghttp_request_param_post.go index f49dd4c4f..b3147b232 100644 --- a/net/ghttp/ghttp_request_param_post.go +++ b/net/ghttp/ghttp_request_param_post.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_param_query.go b/net/ghttp/ghttp_request_param_query.go index 1b84ce5aa..a7257ecbf 100644 --- a/net/ghttp/ghttp_request_param_query.go +++ b/net/ghttp/ghttp_request_param_query.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_param_request.go b/net/ghttp/ghttp_request_param_request.go index ff5396a10..95a2be8e0 100644 --- a/net/ghttp/ghttp_request_param_request.go +++ b/net/ghttp/ghttp_request_param_request.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_param_router.go b/net/ghttp/ghttp_request_param_router.go index 0d387a8ca..1641688b8 100644 --- a/net/ghttp/ghttp_request_param_router.go +++ b/net/ghttp/ghttp_request_param_router.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_request_view.go b/net/ghttp/ghttp_request_view.go index acccea816..74244d4cb 100644 --- a/net/ghttp/ghttp_request_view.go +++ b/net/ghttp/ghttp_request_view.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_response.go b/net/ghttp/ghttp_response.go index 507767421..cc8c11383 100644 --- a/net/ghttp/ghttp_response.go +++ b/net/ghttp/ghttp_response.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_response_cors.go b/net/ghttp/ghttp_response_cors.go index 8bf139a06..8e311ec2d 100644 --- a/net/ghttp/ghttp_response_cors.go +++ b/net/ghttp/ghttp_response_cors.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_response_view.go b/net/ghttp/ghttp_response_view.go index ca4a14473..e3361197c 100644 --- a/net/ghttp/ghttp_response_view.go +++ b/net/ghttp/ghttp_response_view.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_response_write.go b/net/ghttp/ghttp_response_write.go index ab6e5aedf..f8aca67a6 100644 --- a/net/ghttp/ghttp_response_write.go +++ b/net/ghttp/ghttp_response_write.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_response_writer.go b/net/ghttp/ghttp_response_writer.go index c8cc4d6ab..7d87981ff 100644 --- a/net/ghttp/ghttp_response_writer.go +++ b/net/ghttp/ghttp_response_writer.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_admin.go b/net/ghttp/ghttp_server_admin.go index 12ffe619f..ee47fc2b5 100644 --- a/net/ghttp/ghttp_server_admin.go +++ b/net/ghttp/ghttp_server_admin.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_admin_windows.go b/net/ghttp/ghttp_server_admin_windows.go index 7a830d579..55c17751d 100644 --- a/net/ghttp/ghttp_server_admin_windows.go +++ b/net/ghttp/ghttp_server_admin_windows.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_config_cookie.go b/net/ghttp/ghttp_server_config_cookie.go index 21dc2a289..266462028 100644 --- a/net/ghttp/ghttp_server_config_cookie.go +++ b/net/ghttp/ghttp_server_config_cookie.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_config_mess.go b/net/ghttp/ghttp_server_config_mess.go index ffcbc998c..ebf2de3e2 100644 --- a/net/ghttp/ghttp_server_config_mess.go +++ b/net/ghttp/ghttp_server_config_mess.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_config_route.go b/net/ghttp/ghttp_server_config_route.go index a931f5ecf..1fcde0eb5 100644 --- a/net/ghttp/ghttp_server_config_route.go +++ b/net/ghttp/ghttp_server_config_route.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_domain.go b/net/ghttp/ghttp_server_domain.go index cfe2332a1..3fcb066e0 100644 --- a/net/ghttp/ghttp_server_domain.go +++ b/net/ghttp/ghttp_server_domain.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_error_logger.go b/net/ghttp/ghttp_server_error_logger.go index 13f5d21e0..36870e26e 100644 --- a/net/ghttp/ghttp_server_error_logger.go +++ b/net/ghttp/ghttp_server_error_logger.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_graceful.go b/net/ghttp/ghttp_server_graceful.go index 9c53363e5..e4ca2553c 100644 --- a/net/ghttp/ghttp_server_graceful.go +++ b/net/ghttp/ghttp_server_graceful.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_plugin.go b/net/ghttp/ghttp_server_plugin.go index d8d47aaf3..ce97e7f9b 100644 --- a/net/ghttp/ghttp_server_plugin.go +++ b/net/ghttp/ghttp_server_plugin.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_pprof.go b/net/ghttp/ghttp_server_pprof.go index 2cde7ab6e..bcd8588bf 100644 --- a/net/ghttp/ghttp_server_pprof.go +++ b/net/ghttp/ghttp_server_pprof.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index aee073026..8049d01e4 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_router_group.go b/net/ghttp/ghttp_server_router_group.go index 809852dcb..90dbca7bc 100644 --- a/net/ghttp/ghttp_server_router_group.go +++ b/net/ghttp/ghttp_server_router_group.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_router_hook.go b/net/ghttp/ghttp_server_router_hook.go index 46fd63693..4da68ad5e 100644 --- a/net/ghttp/ghttp_server_router_hook.go +++ b/net/ghttp/ghttp_server_router_hook.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_router_middleware.go b/net/ghttp/ghttp_server_router_middleware.go index 10f207e4a..699ccd9d5 100644 --- a/net/ghttp/ghttp_server_router_middleware.go +++ b/net/ghttp/ghttp_server_router_middleware.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_router_serve.go b/net/ghttp/ghttp_server_router_serve.go index bee6e9896..9babf9a14 100644 --- a/net/ghttp/ghttp_server_router_serve.go +++ b/net/ghttp/ghttp_server_router_serve.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_service_controller.go b/net/ghttp/ghttp_server_service_controller.go index 380399c54..94f0940c1 100644 --- a/net/ghttp/ghttp_server_service_controller.go +++ b/net/ghttp/ghttp_server_service_controller.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_service_handler.go b/net/ghttp/ghttp_server_service_handler.go index 0bae88483..c81ee3900 100644 --- a/net/ghttp/ghttp_server_service_handler.go +++ b/net/ghttp/ghttp_server_service_handler.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_service_object.go b/net/ghttp/ghttp_server_service_object.go index 656f32c4f..43b532303 100644 --- a/net/ghttp/ghttp_server_service_object.go +++ b/net/ghttp/ghttp_server_service_object.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_session.go b/net/ghttp/ghttp_server_session.go index debc0389d..a295628b3 100644 --- a/net/ghttp/ghttp_server_session.go +++ b/net/ghttp/ghttp_server_session.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_status.go b/net/ghttp/ghttp_server_status.go index 8b20769e8..7cc627abd 100644 --- a/net/ghttp/ghttp_server_status.go +++ b/net/ghttp/ghttp_server_status.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_server_websocket.go b/net/ghttp/ghttp_server_websocket.go index 07d3a8bca..65c8740ec 100644 --- a/net/ghttp/ghttp_server_websocket.go +++ b/net/ghttp/ghttp_server_websocket.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_client_dump_test.go b/net/ghttp/ghttp_unit_client_dump_test.go index 0bb505759..9852d2c92 100644 --- a/net/ghttp/ghttp_unit_client_dump_test.go +++ b/net/ghttp/ghttp_unit_client_dump_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_client_test.go b/net/ghttp/ghttp_unit_client_test.go index e48340209..21a68c568 100644 --- a/net/ghttp/ghttp_unit_client_test.go +++ b/net/ghttp/ghttp_unit_client_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_config_test.go b/net/ghttp/ghttp_unit_config_test.go index 29cdc4f00..95ba5abfb 100644 --- a/net/ghttp/ghttp_unit_config_test.go +++ b/net/ghttp/ghttp_unit_config_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_context_test.go b/net/ghttp/ghttp_unit_context_test.go index 86be300aa..74897cfcf 100644 --- a/net/ghttp/ghttp_unit_context_test.go +++ b/net/ghttp/ghttp_unit_context_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_cookie_test.go b/net/ghttp/ghttp_unit_cookie_test.go index 83262b739..b9f6c0e92 100644 --- a/net/ghttp/ghttp_unit_cookie_test.go +++ b/net/ghttp/ghttp_unit_cookie_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_error_code_test.go b/net/ghttp/ghttp_unit_error_code_test.go index e0f5933df..c5484afda 100644 --- a/net/ghttp/ghttp_unit_error_code_test.go +++ b/net/ghttp/ghttp_unit_error_code_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_https_test.go b/net/ghttp/ghttp_unit_https_test.go index 25b87995b..3d8c3e417 100644 --- a/net/ghttp/ghttp_unit_https_test.go +++ b/net/ghttp/ghttp_unit_https_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_init_test.go b/net/ghttp/ghttp_unit_init_test.go index d5d02dc33..df18b679a 100644 --- a/net/ghttp/ghttp_unit_init_test.go +++ b/net/ghttp/ghttp_unit_init_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_ip_test.go b/net/ghttp/ghttp_unit_ip_test.go index 6d8f851ee..e1d9ffb34 100644 --- a/net/ghttp/ghttp_unit_ip_test.go +++ b/net/ghttp/ghttp_unit_ip_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_log_test.go b/net/ghttp/ghttp_unit_log_test.go index 28d1ed428..4d9e876f7 100644 --- a/net/ghttp/ghttp_unit_log_test.go +++ b/net/ghttp/ghttp_unit_log_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_mess_test.go b/net/ghttp/ghttp_unit_mess_test.go index 563d61dcf..401620d08 100644 --- a/net/ghttp/ghttp_unit_mess_test.go +++ b/net/ghttp/ghttp_unit_mess_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_middleware_basic_test.go b/net/ghttp/ghttp_unit_middleware_basic_test.go index 8ef8d52e5..c950891cd 100644 --- a/net/ghttp/ghttp_unit_middleware_basic_test.go +++ b/net/ghttp/ghttp_unit_middleware_basic_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_middleware_cors_test.go b/net/ghttp/ghttp_unit_middleware_cors_test.go index 117d51623..71fb4f4ff 100644 --- a/net/ghttp/ghttp_unit_middleware_cors_test.go +++ b/net/ghttp/ghttp_unit_middleware_cors_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_pprof_test.go b/net/ghttp/ghttp_unit_pprof_test.go index 2b3c96b66..be2413216 100644 --- a/net/ghttp/ghttp_unit_pprof_test.go +++ b/net/ghttp/ghttp_unit_pprof_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_request_ctx_test.go b/net/ghttp/ghttp_unit_request_ctx_test.go index f4b31a6c1..92b30aa97 100644 --- a/net/ghttp/ghttp_unit_request_ctx_test.go +++ b/net/ghttp/ghttp_unit_request_ctx_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_request_file_test.go b/net/ghttp/ghttp_unit_request_file_test.go index 03a4b4245..b6b9dd57d 100644 --- a/net/ghttp/ghttp_unit_request_file_test.go +++ b/net/ghttp/ghttp_unit_request_file_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_request_json_test.go b/net/ghttp/ghttp_unit_request_json_test.go index 8965be3ad..656ad6073 100644 --- a/net/ghttp/ghttp_unit_request_json_test.go +++ b/net/ghttp/ghttp_unit_request_json_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_request_page_test.go b/net/ghttp/ghttp_unit_request_page_test.go index 7fdb83562..42c54d44e 100644 --- a/net/ghttp/ghttp_unit_request_page_test.go +++ b/net/ghttp/ghttp_unit_request_page_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_request_struct_test.go b/net/ghttp/ghttp_unit_request_struct_test.go index 009c45672..4bdd503a8 100644 --- a/net/ghttp/ghttp_unit_request_struct_test.go +++ b/net/ghttp/ghttp_unit_request_struct_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_request_test.go b/net/ghttp/ghttp_unit_request_test.go index 47e2798d8..f7b28618a 100644 --- a/net/ghttp/ghttp_unit_request_test.go +++ b/net/ghttp/ghttp_unit_request_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_request_xml_test.go b/net/ghttp/ghttp_unit_request_xml_test.go index 1000be366..37252b9cc 100644 --- a/net/ghttp/ghttp_unit_request_xml_test.go +++ b/net/ghttp/ghttp_unit_request_xml_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_basic_test.go b/net/ghttp/ghttp_unit_router_basic_test.go index 89f033e7e..f15391909 100644 --- a/net/ghttp/ghttp_unit_router_basic_test.go +++ b/net/ghttp/ghttp_unit_router_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_controller_rest_test.go b/net/ghttp/ghttp_unit_router_controller_rest_test.go index fae4aff4c..2aff1a243 100644 --- a/net/ghttp/ghttp_unit_router_controller_rest_test.go +++ b/net/ghttp/ghttp_unit_router_controller_rest_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_controller_test.go b/net/ghttp/ghttp_unit_router_controller_test.go index 2c01a8876..a3104872c 100644 --- a/net/ghttp/ghttp_unit_router_controller_test.go +++ b/net/ghttp/ghttp_unit_router_controller_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_domain_basic_test.go b/net/ghttp/ghttp_unit_router_domain_basic_test.go index eef7df82a..5b597cb6f 100644 --- a/net/ghttp/ghttp_unit_router_domain_basic_test.go +++ b/net/ghttp/ghttp_unit_router_domain_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_domain_controller_rest_test.go b/net/ghttp/ghttp_unit_router_domain_controller_rest_test.go index f01c7abe0..be0cdbf1f 100644 --- a/net/ghttp/ghttp_unit_router_domain_controller_rest_test.go +++ b/net/ghttp/ghttp_unit_router_domain_controller_rest_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_domain_controller_test.go b/net/ghttp/ghttp_unit_router_domain_controller_test.go index e9d3f0191..72983e2d3 100644 --- a/net/ghttp/ghttp_unit_router_domain_controller_test.go +++ b/net/ghttp/ghttp_unit_router_domain_controller_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_domain_object_rest_test.go b/net/ghttp/ghttp_unit_router_domain_object_rest_test.go index 5d4a5cb59..49bf230a6 100644 --- a/net/ghttp/ghttp_unit_router_domain_object_rest_test.go +++ b/net/ghttp/ghttp_unit_router_domain_object_rest_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_domain_object_test.go b/net/ghttp/ghttp_unit_router_domain_object_test.go index 78131fcc6..d390a3bf8 100644 --- a/net/ghttp/ghttp_unit_router_domain_object_test.go +++ b/net/ghttp/ghttp_unit_router_domain_object_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_exit_test.go b/net/ghttp/ghttp_unit_router_exit_test.go index c29f19160..3fb4f83a2 100644 --- a/net/ghttp/ghttp_unit_router_exit_test.go +++ b/net/ghttp/ghttp_unit_router_exit_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_group_group_test.go b/net/ghttp/ghttp_unit_router_group_group_test.go index 886519d17..b743d16a0 100644 --- a/net/ghttp/ghttp_unit_router_group_group_test.go +++ b/net/ghttp/ghttp_unit_router_group_group_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_group_hook_test.go b/net/ghttp/ghttp_unit_router_group_hook_test.go index 30fada8e8..555a37835 100644 --- a/net/ghttp/ghttp_unit_router_group_hook_test.go +++ b/net/ghttp/ghttp_unit_router_group_hook_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_group_rest_test.go b/net/ghttp/ghttp_unit_router_group_rest_test.go index f4ca999eb..c127eb554 100644 --- a/net/ghttp/ghttp_unit_router_group_rest_test.go +++ b/net/ghttp/ghttp_unit_router_group_rest_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_group_test.go b/net/ghttp/ghttp_unit_router_group_test.go index 76763bbf1..619055356 100644 --- a/net/ghttp/ghttp_unit_router_group_test.go +++ b/net/ghttp/ghttp_unit_router_group_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_hook_test.go b/net/ghttp/ghttp_unit_router_hook_test.go index 0bc806a46..7381fe215 100644 --- a/net/ghttp/ghttp_unit_router_hook_test.go +++ b/net/ghttp/ghttp_unit_router_hook_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_names_test.go b/net/ghttp/ghttp_unit_router_names_test.go index f51a2ee7a..bf6f40e8d 100644 --- a/net/ghttp/ghttp_unit_router_names_test.go +++ b/net/ghttp/ghttp_unit_router_names_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_object_rest1_test.go b/net/ghttp/ghttp_unit_router_object_rest1_test.go index 7c8ac17a3..fd57ace76 100644 --- a/net/ghttp/ghttp_unit_router_object_rest1_test.go +++ b/net/ghttp/ghttp_unit_router_object_rest1_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_object_rest2_test.go b/net/ghttp/ghttp_unit_router_object_rest2_test.go index 343b44558..1cac4155b 100644 --- a/net/ghttp/ghttp_unit_router_object_rest2_test.go +++ b/net/ghttp/ghttp_unit_router_object_rest2_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_router_object_test.go b/net/ghttp/ghttp_unit_router_object_test.go index b339f81ce..5a30aefe3 100644 --- a/net/ghttp/ghttp_unit_router_object_test.go +++ b/net/ghttp/ghttp_unit_router_object_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_session_test.go b/net/ghttp/ghttp_unit_session_test.go index f4b8c3d78..67cdc9ecb 100644 --- a/net/ghttp/ghttp_unit_session_test.go +++ b/net/ghttp/ghttp_unit_session_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_static_test.go b/net/ghttp/ghttp_unit_static_test.go index 5f8ce1244..bdd24cc70 100644 --- a/net/ghttp/ghttp_unit_static_test.go +++ b/net/ghttp/ghttp_unit_static_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_status_test.go b/net/ghttp/ghttp_unit_status_test.go index f37a185b8..07cf33f87 100644 --- a/net/ghttp/ghttp_unit_status_test.go +++ b/net/ghttp/ghttp_unit_status_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_template_test.go b/net/ghttp/ghttp_unit_template_test.go index 63bf01aef..d4ce1ec1e 100644 --- a/net/ghttp/ghttp_unit_template_test.go +++ b/net/ghttp/ghttp_unit_template_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_unit_websocket_test.go b/net/ghttp/ghttp_unit_websocket_test.go index 7fd1ecea1..5b06ff589 100644 --- a/net/ghttp/ghttp_unit_websocket_test.go +++ b/net/ghttp/ghttp_unit_websocket_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_z_bench_test.go b/net/ghttp/ghttp_z_bench_test.go index 63deec5a7..e18f46c7c 100644 --- a/net/ghttp/ghttp_z_bench_test.go +++ b/net/ghttp/ghttp_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_z_example_client_test.go b/net/ghttp/ghttp_z_example_client_test.go index b1148d441..d33fc9415 100644 --- a/net/ghttp/ghttp_z_example_client_test.go +++ b/net/ghttp/ghttp_z_example_client_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_z_example_get_test.go b/net/ghttp/ghttp_z_example_get_test.go index 1505b37f1..7448c651e 100644 --- a/net/ghttp/ghttp_z_example_get_test.go +++ b/net/ghttp/ghttp_z_example_get_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_z_example_init_test.go b/net/ghttp/ghttp_z_example_init_test.go index db741774f..cc3c844b9 100644 --- a/net/ghttp/ghttp_z_example_init_test.go +++ b/net/ghttp/ghttp_z_example_init_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_z_example_post_test.go b/net/ghttp/ghttp_z_example_post_test.go index 9f9a69f50..f63c6f259 100644 --- a/net/ghttp/ghttp_z_example_post_test.go +++ b/net/ghttp/ghttp_z_example_post_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/ghttp/ghttp_z_example_test.go b/net/ghttp/ghttp_z_example_test.go index 6d651f8e7..d451723f5 100644 --- a/net/ghttp/ghttp_z_example_test.go +++ b/net/ghttp/ghttp_z_example_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gipv4/gipv4.go b/net/gipv4/gipv4.go index 222be658f..fd49e1479 100644 --- a/net/gipv4/gipv4.go +++ b/net/gipv4/gipv4.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gipv4/gipv4_ip.go b/net/gipv4/gipv4_ip.go index c99bab283..46d2aab8e 100644 --- a/net/gipv4/gipv4_ip.go +++ b/net/gipv4/gipv4_ip.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gipv4/gipv4_lookup.go b/net/gipv4/gipv4_lookup.go index 423a611f1..f6aed4b0f 100644 --- a/net/gipv4/gipv4_lookup.go +++ b/net/gipv4/gipv4_lookup.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gipv4/gipv4_mac.go b/net/gipv4/gipv4_mac.go index a39128fc9..78c6e21cd 100644 --- a/net/gipv4/gipv4_mac.go +++ b/net/gipv4/gipv4_mac.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gipv6/gipv6.go b/net/gipv6/gipv6.go index 20cb3ad41..1905a9676 100644 --- a/net/gipv6/gipv6.go +++ b/net/gipv6/gipv6.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gsmtp/gsmtp.go b/net/gsmtp/gsmtp.go index 0492b465f..37c8dfd5d 100644 --- a/net/gsmtp/gsmtp.go +++ b/net/gsmtp/gsmtp.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gsmtp/gsmtp_test.go b/net/gsmtp/gsmtp_test.go index edae876ea..9688a9fae 100644 --- a/net/gsmtp/gsmtp_test.go +++ b/net/gsmtp/gsmtp_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp.go b/net/gtcp/gtcp.go index b69fdc792..75b58f6eb 100644 --- a/net/gtcp/gtcp.go +++ b/net/gtcp/gtcp.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp_conn.go b/net/gtcp/gtcp_conn.go index 84bbfac25..2a2c8e61b 100644 --- a/net/gtcp/gtcp_conn.go +++ b/net/gtcp/gtcp_conn.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp_conn_pkg.go b/net/gtcp/gtcp_conn_pkg.go index ba75fd374..a13a036f6 100644 --- a/net/gtcp/gtcp_conn_pkg.go +++ b/net/gtcp/gtcp_conn_pkg.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp_func.go b/net/gtcp/gtcp_func.go index e120f5ec8..7643f918b 100644 --- a/net/gtcp/gtcp_func.go +++ b/net/gtcp/gtcp_func.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp_func_pkg.go b/net/gtcp/gtcp_func_pkg.go index 8bcd0aa3d..09cf2fdb9 100644 --- a/net/gtcp/gtcp_func_pkg.go +++ b/net/gtcp/gtcp_func_pkg.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp_pool.go b/net/gtcp/gtcp_pool.go index 815c8a8bf..eda264b38 100644 --- a/net/gtcp/gtcp_pool.go +++ b/net/gtcp/gtcp_pool.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp_pool_pkg.go b/net/gtcp/gtcp_pool_pkg.go index 1f5cd29a7..7412e829c 100644 --- a/net/gtcp/gtcp_pool_pkg.go +++ b/net/gtcp/gtcp_pool_pkg.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp_server.go b/net/gtcp/gtcp_server.go index e56538526..f4ec7363c 100644 --- a/net/gtcp/gtcp_server.go +++ b/net/gtcp/gtcp_server.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp_unit_init_test.go b/net/gtcp/gtcp_unit_init_test.go index 974fa04db..1a3c533da 100644 --- a/net/gtcp/gtcp_unit_init_test.go +++ b/net/gtcp/gtcp_unit_init_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp_unit_pkg_test.go b/net/gtcp/gtcp_unit_pkg_test.go index 89687b800..3f9e15fd8 100644 --- a/net/gtcp/gtcp_unit_pkg_test.go +++ b/net/gtcp/gtcp_unit_pkg_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp_unit_pool_pkg_test.go b/net/gtcp/gtcp_unit_pool_pkg_test.go index 49e7fa407..62ffe21b9 100644 --- a/net/gtcp/gtcp_unit_pool_pkg_test.go +++ b/net/gtcp/gtcp_unit_pool_pkg_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gtcp/gtcp_unit_pool_test.go b/net/gtcp/gtcp_unit_pool_test.go index dbd8cf051..f887494f8 100644 --- a/net/gtcp/gtcp_unit_pool_test.go +++ b/net/gtcp/gtcp_unit_pool_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gudp/gudp.go b/net/gudp/gudp.go index 81a3cf941..8873d5904 100644 --- a/net/gudp/gudp.go +++ b/net/gudp/gudp.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gudp/gudp_conn.go b/net/gudp/gudp_conn.go index d9fdf317f..52aaccfab 100644 --- a/net/gudp/gudp_conn.go +++ b/net/gudp/gudp_conn.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gudp/gudp_func.go b/net/gudp/gudp_func.go index a4d08fe6d..9616871fe 100644 --- a/net/gudp/gudp_func.go +++ b/net/gudp/gudp_func.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gudp/gudp_server.go b/net/gudp/gudp_server.go index 2ecb28a76..db7dca6a4 100644 --- a/net/gudp/gudp_server.go +++ b/net/gudp/gudp_server.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gudp/gudp_unit_basic_test.go b/net/gudp/gudp_unit_basic_test.go index fbe3c26bd..c7bff0215 100644 --- a/net/gudp/gudp_unit_basic_test.go +++ b/net/gudp/gudp_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/net/gudp/gudp_unit_init_test.go b/net/gudp/gudp_unit_init_test.go index bc5815922..df1b0fbf7 100644 --- a/net/gudp/gudp_unit_init_test.go +++ b/net/gudp/gudp_unit_init_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gbuild/gbuild.go b/os/gbuild/gbuild.go index 19b9e1fe5..05102ea60 100644 --- a/os/gbuild/gbuild.go +++ b/os/gbuild/gbuild.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcache/gcache.go b/os/gcache/gcache.go index 80ef28aab..fb2fd09c9 100644 --- a/os/gcache/gcache.go +++ b/os/gcache/gcache.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcache/gcache_adapter.go b/os/gcache/gcache_adapter.go index 1dad55efe..71c22c7c9 100644 --- a/os/gcache/gcache_adapter.go +++ b/os/gcache/gcache_adapter.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcache/gcache_adapter_memory.go b/os/gcache/gcache_adapter_memory.go index 5dd532c61..2de6328f6 100644 --- a/os/gcache/gcache_adapter_memory.go +++ b/os/gcache/gcache_adapter_memory.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcache/gcache_adapter_memory_item.go b/os/gcache/gcache_adapter_memory_item.go index 9865a6d92..44b72a5ef 100644 --- a/os/gcache/gcache_adapter_memory_item.go +++ b/os/gcache/gcache_adapter_memory_item.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcache/gcache_adapter_memory_lru.go b/os/gcache/gcache_adapter_memory_lru.go index b6f6c1b86..455ce6d7d 100644 --- a/os/gcache/gcache_adapter_memory_lru.go +++ b/os/gcache/gcache_adapter_memory_lru.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcache/gcache_cache.go b/os/gcache/gcache_cache.go index 5c80c9a2e..c664c141a 100644 --- a/os/gcache/gcache_cache.go +++ b/os/gcache/gcache_cache.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcache/gcache_z_bench_test.go b/os/gcache/gcache_z_bench_test.go index a3df89941..028c07beb 100644 --- a/os/gcache/gcache_z_bench_test.go +++ b/os/gcache/gcache_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcache/gcache_z_unit_1_test.go b/os/gcache/gcache_z_unit_1_test.go index 459800a91..bf5a0ad81 100644 --- a/os/gcache/gcache_z_unit_1_test.go +++ b/os/gcache/gcache_z_unit_1_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcfg/gcfg_api.go b/os/gcfg/gcfg_api.go index 4675561e9..d14dc3fa5 100644 --- a/os/gcfg/gcfg_api.go +++ b/os/gcfg/gcfg_api.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcfg/gcfg_config.go b/os/gcfg/gcfg_config.go index e9e404834..4d7fabf3d 100644 --- a/os/gcfg/gcfg_config.go +++ b/os/gcfg/gcfg_config.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcfg/gcfg_error.go b/os/gcfg/gcfg_error.go index f3ad32921..469130675 100644 --- a/os/gcfg/gcfg_error.go +++ b/os/gcfg/gcfg_error.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcfg/gcfg_z_example_pattern_test.go b/os/gcfg/gcfg_z_example_pattern_test.go index 9616cd85c..a5ba919d5 100644 --- a/os/gcfg/gcfg_z_example_pattern_test.go +++ b/os/gcfg/gcfg_z_example_pattern_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcfg/gcfg_z_unit_basic_test.go b/os/gcfg/gcfg_z_unit_basic_test.go index 7677f4b47..133f90adc 100644 --- a/os/gcfg/gcfg_z_unit_basic_test.go +++ b/os/gcfg/gcfg_z_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcmd/gcmd.go b/os/gcmd/gcmd.go index a587b1aa7..e37d944fb 100644 --- a/os/gcmd/gcmd.go +++ b/os/gcmd/gcmd.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcmd/gcmd_handler.go b/os/gcmd/gcmd_handler.go index 1fa5096d2..2bcb7a905 100644 --- a/os/gcmd/gcmd_handler.go +++ b/os/gcmd/gcmd_handler.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcmd/gcmd_parser.go b/os/gcmd/gcmd_parser.go index 8bc235d53..f18e5093e 100644 --- a/os/gcmd/gcmd_parser.go +++ b/os/gcmd/gcmd_parser.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcmd/gcmd_parser_handler.go b/os/gcmd/gcmd_parser_handler.go index 21acc11af..a2ab79eee 100644 --- a/os/gcmd/gcmd_parser_handler.go +++ b/os/gcmd/gcmd_parser_handler.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcmd/gcmd_scan.go b/os/gcmd/gcmd_scan.go index 492c668f0..8c59c84e2 100644 --- a/os/gcmd/gcmd_scan.go +++ b/os/gcmd/gcmd_scan.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcmd/gcmd_z_unit_default_test.go b/os/gcmd/gcmd_z_unit_default_test.go index df9f4df16..36d91bf7e 100644 --- a/os/gcmd/gcmd_z_unit_default_test.go +++ b/os/gcmd/gcmd_z_unit_default_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcmd/gcmd_z_unit_parser_test.go b/os/gcmd/gcmd_z_unit_parser_test.go index 4988c64be..0d8a666bc 100644 --- a/os/gcmd/gcmd_z_unit_parser_test.go +++ b/os/gcmd/gcmd_z_unit_parser_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcron/gcron.go b/os/gcron/gcron.go index 23ce47823..8abe47423 100644 --- a/os/gcron/gcron.go +++ b/os/gcron/gcron.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcron/gcron_cron.go b/os/gcron/gcron_cron.go index 0e7ce7337..28e2b8522 100644 --- a/os/gcron/gcron_cron.go +++ b/os/gcron/gcron_cron.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index 5d3f571e7..9b9087639 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcron/gcron_schedule.go b/os/gcron/gcron_schedule.go index a413cf7c1..017875a1e 100644 --- a/os/gcron/gcron_schedule.go +++ b/os/gcron/gcron_schedule.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcron/gcron_unit_1_test.go b/os/gcron/gcron_unit_1_test.go index 01a31353e..4e408e2a3 100644 --- a/os/gcron/gcron_unit_1_test.go +++ b/os/gcron/gcron_unit_1_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcron/gcron_unit_2_test.go b/os/gcron/gcron_unit_2_test.go index 8635bff94..f3c8935e2 100644 --- a/os/gcron/gcron_unit_2_test.go +++ b/os/gcron/gcron_unit_2_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcron/gcron_z_bench_test.go b/os/gcron/gcron_z_bench_test.go index fd9925691..037a9b058 100644 --- a/os/gcron/gcron_z_bench_test.go +++ b/os/gcron/gcron_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gcron/gcron_z_example_1_test.go b/os/gcron/gcron_z_example_1_test.go index e1f636250..d49f2142f 100644 --- a/os/gcron/gcron_z_example_1_test.go +++ b/os/gcron/gcron_z_example_1_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/genv/genv.go b/os/genv/genv.go index 815552e36..0dd2a0a52 100644 --- a/os/genv/genv.go +++ b/os/genv/genv.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/genv/genv_test.go b/os/genv/genv_test.go index 39ca57fae..5ff118464 100644 --- a/os/genv/genv_test.go +++ b/os/genv/genv_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile.go b/os/gfile/gfile.go index fa8596060..b21d20a43 100644 --- a/os/gfile/gfile.go +++ b/os/gfile/gfile.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_cache.go b/os/gfile/gfile_cache.go index 6dee6324a..5489087cb 100644 --- a/os/gfile/gfile_cache.go +++ b/os/gfile/gfile_cache.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_contents.go b/os/gfile/gfile_contents.go index e4a8e48e9..6b3a7c5d6 100644 --- a/os/gfile/gfile_contents.go +++ b/os/gfile/gfile_contents.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_copy.go b/os/gfile/gfile_copy.go index d3b6887a1..32e9f1320 100644 --- a/os/gfile/gfile_copy.go +++ b/os/gfile/gfile_copy.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_home.go b/os/gfile/gfile_home.go index 2842864b4..5e5d42fca 100644 --- a/os/gfile/gfile_home.go +++ b/os/gfile/gfile_home.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_replace.go b/os/gfile/gfile_replace.go index a0664bbdf..90a9fadd4 100644 --- a/os/gfile/gfile_replace.go +++ b/os/gfile/gfile_replace.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_scan.go b/os/gfile/gfile_scan.go index ac6aa1809..e8187b6a7 100644 --- a/os/gfile/gfile_scan.go +++ b/os/gfile/gfile_scan.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_search.go b/os/gfile/gfile_search.go index 2936f280a..08d3d01fc 100644 --- a/os/gfile/gfile_search.go +++ b/os/gfile/gfile_search.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_size.go b/os/gfile/gfile_size.go index 37d08e947..615b9e765 100644 --- a/os/gfile/gfile_size.go +++ b/os/gfile/gfile_size.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_sort.go b/os/gfile/gfile_sort.go index f1a6d4dc8..d4162de93 100644 --- a/os/gfile/gfile_sort.go +++ b/os/gfile/gfile_sort.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_source.go b/os/gfile/gfile_source.go index 8f5192470..3faa85b11 100644 --- a/os/gfile/gfile_source.go +++ b/os/gfile/gfile_source.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_time.go b/os/gfile/gfile_time.go index 80b4b3754..f91fee5b8 100644 --- a/os/gfile/gfile_time.go +++ b/os/gfile/gfile_time.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_z_cache_test.go b/os/gfile/gfile_z_cache_test.go index eb0e30005..6aec14346 100644 --- a/os/gfile/gfile_z_cache_test.go +++ b/os/gfile/gfile_z_cache_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_z_contents_test.go b/os/gfile/gfile_z_contents_test.go index 1b28bbb8f..a141d6d4e 100644 --- a/os/gfile/gfile_z_contents_test.go +++ b/os/gfile/gfile_z_contents_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_z_copy_test.go b/os/gfile/gfile_z_copy_test.go index 484318383..9aceb72df 100644 --- a/os/gfile/gfile_z_copy_test.go +++ b/os/gfile/gfile_z_copy_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_z_readline_test.go b/os/gfile/gfile_z_readline_test.go index 7c92efe05..57899301b 100644 --- a/os/gfile/gfile_z_readline_test.go +++ b/os/gfile/gfile_z_readline_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_z_scan_test.go b/os/gfile/gfile_z_scan_test.go index f334517ef..786f5f5e4 100644 --- a/os/gfile/gfile_z_scan_test.go +++ b/os/gfile/gfile_z_scan_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_z_search_test.go b/os/gfile/gfile_z_search_test.go index cde8d5277..fc40d508d 100644 --- a/os/gfile/gfile_z_search_test.go +++ b/os/gfile/gfile_z_search_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_z_size_test.go b/os/gfile/gfile_z_size_test.go index fe74cb392..481f84ef7 100644 --- a/os/gfile/gfile_z_size_test.go +++ b/os/gfile/gfile_z_size_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_z_test.go b/os/gfile/gfile_z_test.go index 4262601b7..35b10b292 100644 --- a/os/gfile/gfile_z_test.go +++ b/os/gfile/gfile_z_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfile/gfile_z_time_test.go b/os/gfile/gfile_z_time_test.go index 37dfaf87e..b40510539 100644 --- a/os/gfile/gfile_z_time_test.go +++ b/os/gfile/gfile_z_time_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfpool/gfpool.go b/os/gfpool/gfpool.go index db4e7ecd9..cf91b5565 100644 --- a/os/gfpool/gfpool.go +++ b/os/gfpool/gfpool.go @@ -1,4 +1,4 @@ -// Copyright 2017-2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfpool/gfpool_file.go b/os/gfpool/gfpool_file.go index b985722b1..8a734ecde 100644 --- a/os/gfpool/gfpool_file.go +++ b/os/gfpool/gfpool_file.go @@ -1,4 +1,4 @@ -// Copyright 2017-2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfpool/gfpool_pool.go b/os/gfpool/gfpool_pool.go index e83abf6aa..288e18360 100644 --- a/os/gfpool/gfpool_pool.go +++ b/os/gfpool/gfpool_pool.go @@ -1,4 +1,4 @@ -// Copyright 2017-2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfsnotify/gfsnotify.go b/os/gfsnotify/gfsnotify.go index e4a605473..07817a8e5 100644 --- a/os/gfsnotify/gfsnotify.go +++ b/os/gfsnotify/gfsnotify.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfsnotify/gfsnotify_event.go b/os/gfsnotify/gfsnotify_event.go index 4811b6235..f91638ca5 100644 --- a/os/gfsnotify/gfsnotify_event.go +++ b/os/gfsnotify/gfsnotify_event.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // ThIs Source Code Form Is subject to the terms of the MIT License. // If a copy of the MIT was not dIstributed with thIs file, diff --git a/os/gfsnotify/gfsnotify_filefunc.go b/os/gfsnotify/gfsnotify_filefunc.go index 7518ca19f..f1d4daef6 100644 --- a/os/gfsnotify/gfsnotify_filefunc.go +++ b/os/gfsnotify/gfsnotify_filefunc.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // ThIs Source Code Form Is subject to the terms of the MIT License. // If a copy of the MIT was not dIstributed with thIs file, diff --git a/os/gfsnotify/gfsnotify_watcher.go b/os/gfsnotify/gfsnotify_watcher.go index 8b3e07636..421b40a59 100644 --- a/os/gfsnotify/gfsnotify_watcher.go +++ b/os/gfsnotify/gfsnotify_watcher.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfsnotify/gfsnotify_watcher_loop.go b/os/gfsnotify/gfsnotify_watcher_loop.go index c9ada4006..a05c663de 100644 --- a/os/gfsnotify/gfsnotify_watcher_loop.go +++ b/os/gfsnotify/gfsnotify_watcher_loop.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gfsnotify/gfsnotify_z_unit_test.go b/os/gfsnotify/gfsnotify_z_unit_test.go index 341b02050..a1f195dc1 100644 --- a/os/gfsnotify/gfsnotify_z_unit_test.go +++ b/os/gfsnotify/gfsnotify_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog.go b/os/glog/glog.go index 8389fefe7..6adbc3e48 100644 --- a/os/glog/glog.go +++ b/os/glog/glog.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_api.go b/os/glog/glog_api.go index 8cc66a442..830d6f64f 100644 --- a/os/glog/glog_api.go +++ b/os/glog/glog_api.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_chaining.go b/os/glog/glog_chaining.go index b62eb7c74..f55012fa3 100644 --- a/os/glog/glog_chaining.go +++ b/os/glog/glog_chaining.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_config.go b/os/glog/glog_config.go index 9dcab7eba..c38454495 100644 --- a/os/glog/glog_config.go +++ b/os/glog/glog_config.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_instance.go b/os/glog/glog_instance.go index 400cdde9b..f53e3e37d 100644 --- a/os/glog/glog_instance.go +++ b/os/glog/glog_instance.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 4af7bf1f9..288423e7c 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_logger_api.go b/os/glog/glog_logger_api.go index 93aa69cbe..f7769630d 100644 --- a/os/glog/glog_logger_api.go +++ b/os/glog/glog_logger_api.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_logger_chaining.go b/os/glog/glog_logger_chaining.go index 3c0e88eb4..3a5080b62 100644 --- a/os/glog/glog_logger_chaining.go +++ b/os/glog/glog_logger_chaining.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_logger_level.go b/os/glog/glog_logger_level.go index 25e50ec0e..f9b5c28ed 100644 --- a/os/glog/glog_logger_level.go +++ b/os/glog/glog_logger_level.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_logger_rotate.go b/os/glog/glog_logger_rotate.go index a89ecec35..0b1f785b4 100644 --- a/os/glog/glog_logger_rotate.go +++ b/os/glog/glog_logger_rotate.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_logger_writer.go b/os/glog/glog_logger_writer.go index de4f3a853..765834df5 100644 --- a/os/glog/glog_logger_writer.go +++ b/os/glog/glog_logger_writer.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_z_example_test.go b/os/glog/glog_z_example_test.go index 17de0a755..6ac5d5336 100644 --- a/os/glog/glog_z_example_test.go +++ b/os/glog/glog_z_example_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_z_unit_basic_test.go b/os/glog/glog_z_unit_basic_test.go index 0f126ce69..f20d56b86 100644 --- a/os/glog/glog_z_unit_basic_test.go +++ b/os/glog/glog_z_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_z_unit_chaining_test.go b/os/glog/glog_z_unit_chaining_test.go index d6360cce7..131d45aa4 100644 --- a/os/glog/glog_z_unit_chaining_test.go +++ b/os/glog/glog_z_unit_chaining_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_z_unit_concurrent_test.go b/os/glog/glog_z_unit_concurrent_test.go index 1378e2278..1f5275b62 100644 --- a/os/glog/glog_z_unit_concurrent_test.go +++ b/os/glog/glog_z_unit_concurrent_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_z_unit_config_test.go b/os/glog/glog_z_unit_config_test.go index 54b6a7346..6e50b01dd 100644 --- a/os/glog/glog_z_unit_config_test.go +++ b/os/glog/glog_z_unit_config_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_z_unit_ctx_test.go b/os/glog/glog_z_unit_ctx_test.go index 9ac5e0001..dd1f80932 100644 --- a/os/glog/glog_z_unit_ctx_test.go +++ b/os/glog/glog_z_unit_ctx_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_z_unit_level_test.go b/os/glog/glog_z_unit_level_test.go index 3ceefaaf2..d12535895 100644 --- a/os/glog/glog_z_unit_level_test.go +++ b/os/glog/glog_z_unit_level_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/glog/glog_z_unit_rotate_test.go b/os/glog/glog_z_unit_rotate_test.go index fa0581e66..8b8096216 100644 --- a/os/glog/glog_z_unit_rotate_test.go +++ b/os/glog/glog_z_unit_rotate_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gmlock/gmlock.go b/os/gmlock/gmlock.go index a6baa38ee..60a17a08a 100644 --- a/os/gmlock/gmlock.go +++ b/os/gmlock/gmlock.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gmlock/gmlock_locker.go b/os/gmlock/gmlock_locker.go index 5c0138da7..11f195057 100644 --- a/os/gmlock/gmlock_locker.go +++ b/os/gmlock/gmlock_locker.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gmlock/gmlock_unit_lock_test.go b/os/gmlock/gmlock_unit_lock_test.go index eba02d936..16a4d4eab 100644 --- a/os/gmlock/gmlock_unit_lock_test.go +++ b/os/gmlock/gmlock_unit_lock_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gmlock/gmlock_unit_rlock_test.go b/os/gmlock/gmlock_unit_rlock_test.go index 4d01bc930..8a6d3a09b 100644 --- a/os/gmlock/gmlock_unit_rlock_test.go +++ b/os/gmlock/gmlock_unit_rlock_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gmlock/gmlock_z_bench_test.go b/os/gmlock/gmlock_z_bench_test.go index 2ca7d63ab..c87876d2b 100644 --- a/os/gmlock/gmlock_z_bench_test.go +++ b/os/gmlock/gmlock_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gmutex/gmutex.go b/os/gmutex/gmutex.go index ad0254fdf..54acea5d2 100644 --- a/os/gmutex/gmutex.go +++ b/os/gmutex/gmutex.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gmutex/gmutex_bench_test.go b/os/gmutex/gmutex_bench_test.go index b54a7b5cf..13c8af7bc 100644 --- a/os/gmutex/gmutex_bench_test.go +++ b/os/gmutex/gmutex_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gmutex/gmutex_unit_test.go b/os/gmutex/gmutex_unit_test.go index 1f992a8a8..7da5e58dc 100644 --- a/os/gmutex/gmutex_unit_test.go +++ b/os/gmutex/gmutex_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gproc/gproc.go b/os/gproc/gproc.go index bfbee3685..2a3977b96 100644 --- a/os/gproc/gproc.go +++ b/os/gproc/gproc.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gproc/gproc_comm.go b/os/gproc/gproc_comm.go index 779ff6432..ca9911c83 100644 --- a/os/gproc/gproc_comm.go +++ b/os/gproc/gproc_comm.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gproc/gproc_comm_receive.go b/os/gproc/gproc_comm_receive.go index c101fe0f0..b0bd9f39a 100644 --- a/os/gproc/gproc_comm_receive.go +++ b/os/gproc/gproc_comm_receive.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gproc/gproc_comm_send.go b/os/gproc/gproc_comm_send.go index 74b947c5e..25af7c453 100644 --- a/os/gproc/gproc_comm_send.go +++ b/os/gproc/gproc_comm_send.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gproc/gproc_manager.go b/os/gproc/gproc_manager.go index 49e11c055..8f3cea114 100644 --- a/os/gproc/gproc_manager.go +++ b/os/gproc/gproc_manager.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gproc/gproc_process.go b/os/gproc/gproc_process.go index b6062c8e3..5d4990ce0 100644 --- a/os/gproc/gproc_process.go +++ b/os/gproc/gproc_process.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gres/gres.go b/os/gres/gres.go index 636cca352..0c4489a8a 100644 --- a/os/gres/gres.go +++ b/os/gres/gres.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gres/gres_file.go b/os/gres/gres_file.go index dd2f2ccb7..a6c48b6a7 100644 --- a/os/gres/gres_file.go +++ b/os/gres/gres_file.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gres/gres_http_file.go b/os/gres/gres_http_file.go index d56edeca1..62c5f56db 100644 --- a/os/gres/gres_http_file.go +++ b/os/gres/gres_http_file.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gres/gres_instance.go b/os/gres/gres_instance.go index afaefc22b..9971af7f4 100644 --- a/os/gres/gres_instance.go +++ b/os/gres/gres_instance.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gres/gres_z_unit_1_test.go b/os/gres/gres_z_unit_1_test.go index 3cc0d85c3..56ed0ef95 100644 --- a/os/gres/gres_z_unit_1_test.go +++ b/os/gres/gres_z_unit_1_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/grpool/grpool.go b/os/grpool/grpool.go index 48af508ef..136484367 100644 --- a/os/grpool/grpool.go +++ b/os/grpool/grpool.go @@ -1,4 +1,4 @@ -// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/grpool/grpool_bench_1_test.go b/os/grpool/grpool_bench_1_test.go index 0f6eb0f72..cdd111101 100644 --- a/os/grpool/grpool_bench_1_test.go +++ b/os/grpool/grpool_bench_1_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/grpool/grpool_bench_2_test.go b/os/grpool/grpool_bench_2_test.go index c7fae031d..0bc61cebc 100644 --- a/os/grpool/grpool_bench_2_test.go +++ b/os/grpool/grpool_bench_2_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/grpool/grpool_unit_test.go b/os/grpool/grpool_unit_test.go index b4250bd46..7aed8604b 100644 --- a/os/grpool/grpool_unit_test.go +++ b/os/grpool/grpool_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gsession/gsession.go b/os/gsession/gsession.go index 59417c535..4d6ac77ad 100644 --- a/os/gsession/gsession.go +++ b/os/gsession/gsession.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gsession/gsession_manager.go b/os/gsession/gsession_manager.go index 79b096585..dc83ae50d 100644 --- a/os/gsession/gsession_manager.go +++ b/os/gsession/gsession_manager.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gsession/gsession_session.go b/os/gsession/gsession_session.go index dfbff5336..17e764852 100644 --- a/os/gsession/gsession_session.go +++ b/os/gsession/gsession_session.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gsession/gsession_storage.go b/os/gsession/gsession_storage.go index 75d06ddf2..02b72e322 100644 --- a/os/gsession/gsession_storage.go +++ b/os/gsession/gsession_storage.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gsession/gsession_storage_memory.go b/os/gsession/gsession_storage_memory.go index 24786e745..7b41e7ff6 100644 --- a/os/gsession/gsession_storage_memory.go +++ b/os/gsession/gsession_storage_memory.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gsession/gsession_unit_storage_file_test.go b/os/gsession/gsession_unit_storage_file_test.go index 1efcf3cc4..edd872b06 100644 --- a/os/gsession/gsession_unit_storage_file_test.go +++ b/os/gsession/gsession_unit_storage_file_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gsession/gsession_unit_storage_memory_test.go b/os/gsession/gsession_unit_storage_memory_test.go index e551855f0..054e9948d 100644 --- a/os/gsession/gsession_unit_storage_memory_test.go +++ b/os/gsession/gsession_unit_storage_memory_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gsession/gsession_unit_storage_redis_hashtable_test.go b/os/gsession/gsession_unit_storage_redis_hashtable_test.go index f9f6cd3eb..6ce58ef38 100644 --- a/os/gsession/gsession_unit_storage_redis_hashtable_test.go +++ b/os/gsession/gsession_unit_storage_redis_hashtable_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gsession/gsession_unit_storage_redis_test.go b/os/gsession/gsession_unit_storage_redis_test.go index 278c404ea..f0fd9f3b2 100644 --- a/os/gsession/gsession_unit_storage_redis_test.go +++ b/os/gsession/gsession_unit_storage_redis_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gsession/gsession_unit_test.go b/os/gsession/gsession_unit_test.go index 41ef14a66..98ebaea17 100644 --- a/os/gsession/gsession_unit_test.go +++ b/os/gsession/gsession_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gspath/gspath.go b/os/gspath/gspath.go index a2aafccb2..c628968d8 100644 --- a/os/gspath/gspath.go +++ b/os/gspath/gspath.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gspath/gspath_cache.go b/os/gspath/gspath_cache.go index 9e671ce18..324be56c1 100644 --- a/os/gspath/gspath_cache.go +++ b/os/gspath/gspath_cache.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gspath/gspath_unit_test.go b/os/gspath/gspath_unit_test.go index 74b24b2e1..32cc09218 100644 --- a/os/gspath/gspath_unit_test.go +++ b/os/gspath/gspath_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index 1c6c5a35c..3540aa465 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtime/gtime_format.go b/os/gtime/gtime_format.go index ada2efd4d..e1681cc60 100644 --- a/os/gtime/gtime_format.go +++ b/os/gtime/gtime_format.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtime/gtime_time_wrapper.go b/os/gtime/gtime_time_wrapper.go index 66c075b73..96c7855e0 100644 --- a/os/gtime/gtime_time_wrapper.go +++ b/os/gtime/gtime_time_wrapper.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtime/gtime_z_unit_format_test.go b/os/gtime/gtime_z_unit_format_test.go index 80beae8a5..232a72a03 100644 --- a/os/gtime/gtime_z_unit_format_test.go +++ b/os/gtime/gtime_z_unit_format_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtime/gtime_z_unit_json_test.go b/os/gtime/gtime_z_unit_json_test.go index 80654db71..b79fd3230 100644 --- a/os/gtime/gtime_z_unit_json_test.go +++ b/os/gtime/gtime_z_unit_json_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtimer/gtimer.go b/os/gtimer/gtimer.go index 97ff3782b..40c7d6a53 100644 --- a/os/gtimer/gtimer.go +++ b/os/gtimer/gtimer.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go index 0f228ebbf..fab11a27f 100644 --- a/os/gtimer/gtimer_entry.go +++ b/os/gtimer/gtimer_entry.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtimer/gtimer_loop.go b/os/gtimer/gtimer_loop.go index c38c21c50..bb811387d 100644 --- a/os/gtimer/gtimer_loop.go +++ b/os/gtimer/gtimer_loop.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtimer/gtimer_timer.go b/os/gtimer/gtimer_timer.go index 115097c4c..8f4929497 100644 --- a/os/gtimer/gtimer_timer.go +++ b/os/gtimer/gtimer_timer.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtimer/gtimer_z_bench_test.go b/os/gtimer/gtimer_z_bench_test.go index b114ad55a..1f5eea33c 100644 --- a/os/gtimer/gtimer_z_bench_test.go +++ b/os/gtimer/gtimer_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtimer/gtimer_z_example_test.go b/os/gtimer/gtimer_z_example_test.go index 6db68f311..e8cbb543b 100644 --- a/os/gtimer/gtimer_z_example_test.go +++ b/os/gtimer/gtimer_z_example_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtimer/gtimer_z_unit_0_test.go b/os/gtimer/gtimer_z_unit_0_test.go index f69f1d8c9..32f7a986c 100644 --- a/os/gtimer/gtimer_z_unit_0_test.go +++ b/os/gtimer/gtimer_z_unit_0_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtimer/gtimer_z_unit_1_test.go b/os/gtimer/gtimer_z_unit_1_test.go index be75a9cb7..d6da5a8b4 100644 --- a/os/gtimer/gtimer_z_unit_1_test.go +++ b/os/gtimer/gtimer_z_unit_1_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gtimer/gtimer_z_unit_2_test.go b/os/gtimer/gtimer_z_unit_2_test.go index 6006dd125..ba0d2d421 100644 --- a/os/gtimer/gtimer_z_unit_2_test.go +++ b/os/gtimer/gtimer_z_unit_2_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gview/gview.go b/os/gview/gview.go index f421a570b..2350ff900 100644 --- a/os/gview/gview.go +++ b/os/gview/gview.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gview/gview_error.go b/os/gview/gview_error.go index 423fc6d55..6c038e7ab 100644 --- a/os/gview/gview_error.go +++ b/os/gview/gview_error.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gview/gview_i18n.go b/os/gview/gview_i18n.go index 0e5fa1116..df8ca6584 100644 --- a/os/gview/gview_i18n.go +++ b/os/gview/gview_i18n.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gview/gview_instance.go b/os/gview/gview_instance.go index 0e976b911..7781dcd0c 100644 --- a/os/gview/gview_instance.go +++ b/os/gview/gview_instance.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index 8c7a3026b..e02d9dd75 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gview/gview_unit_basic_test.go b/os/gview/gview_unit_basic_test.go index c46767919..53caf1b8c 100644 --- a/os/gview/gview_unit_basic_test.go +++ b/os/gview/gview_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gview/gview_unit_config_test.go b/os/gview/gview_unit_config_test.go index a19d60701..314b4be48 100644 --- a/os/gview/gview_unit_config_test.go +++ b/os/gview/gview_unit_config_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gview/gview_unit_encode_test.go b/os/gview/gview_unit_encode_test.go index d3d9efea6..1aad3616d 100644 --- a/os/gview/gview_unit_encode_test.go +++ b/os/gview/gview_unit_encode_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/os/gview/gview_unit_i18n_test.go b/os/gview/gview_unit_i18n_test.go index 216e1309a..b04ba2bbd 100644 --- a/os/gview/gview_unit_i18n_test.go +++ b/os/gview/gview_unit_i18n_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/test/gtest/gtest.go b/test/gtest/gtest.go index e5464b3b0..b8c0273bb 100644 --- a/test/gtest/gtest.go +++ b/test/gtest/gtest.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/test/gtest/gtest_t.go b/test/gtest/gtest_t.go index 06c677227..a0854a852 100644 --- a/test/gtest/gtest_t.go +++ b/test/gtest/gtest_t.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/test/gtest/gtest_util.go b/test/gtest/gtest_util.go index 701d8250c..8749478be 100644 --- a/test/gtest/gtest_util.go +++ b/test/gtest/gtest_util.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/test/gtest/gtest_z_unit_test.go b/test/gtest/gtest_z_unit_test.go index 0f5b756cd..db74e97f0 100644 --- a/test/gtest/gtest_z_unit_test.go +++ b/test/gtest/gtest_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gregex/gregex.go b/text/gregex/gregex.go index 8ad0292ee..4f77bf356 100644 --- a/text/gregex/gregex.go +++ b/text/gregex/gregex.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gregex/gregex_cache.go b/text/gregex/gregex_cache.go index 1b4913782..ebce7e8e4 100644 --- a/text/gregex/gregex_cache.go +++ b/text/gregex/gregex_cache.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gregex/gregex_z_bench_test.go b/text/gregex/gregex_z_bench_test.go index e4c9f9339..3e51ffc6f 100644 --- a/text/gregex/gregex_z_bench_test.go +++ b/text/gregex/gregex_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gregex/gregex_z_unit_test.go b/text/gregex/gregex_z_unit_test.go index a9b132b45..81ef93706 100644 --- a/text/gregex/gregex_z_unit_test.go +++ b/text/gregex/gregex_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr.go b/text/gstr/gstr.go index 4f8607e17..aa5fe0ca8 100644 --- a/text/gstr/gstr.go +++ b/text/gstr/gstr.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_contain.go b/text/gstr/gstr_contain.go index 73ded148d..0302c9696 100644 --- a/text/gstr/gstr_contain.go +++ b/text/gstr/gstr_contain.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_convert.go b/text/gstr/gstr_convert.go index 357035b65..7efce8c2b 100644 --- a/text/gstr/gstr_convert.go +++ b/text/gstr/gstr_convert.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_domain.go b/text/gstr/gstr_domain.go index 020ed5705..30b57d0dd 100644 --- a/text/gstr/gstr_domain.go +++ b/text/gstr/gstr_domain.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_levenshtein.go b/text/gstr/gstr_levenshtein.go index 4656281a2..489c6eb3b 100644 --- a/text/gstr/gstr_levenshtein.go +++ b/text/gstr/gstr_levenshtein.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_parse.go b/text/gstr/gstr_parse.go index c44c2cbe5..a41267403 100644 --- a/text/gstr/gstr_parse.go +++ b/text/gstr/gstr_parse.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_pos.go b/text/gstr/gstr_pos.go index bf76e756a..6fbb4420a 100644 --- a/text/gstr/gstr_pos.go +++ b/text/gstr/gstr_pos.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_similartext.go b/text/gstr/gstr_similartext.go index 3bd816ef1..efb253739 100644 --- a/text/gstr/gstr_similartext.go +++ b/text/gstr/gstr_similartext.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_soundex.go b/text/gstr/gstr_soundex.go index 117c2dd39..bdba8933e 100644 --- a/text/gstr/gstr_soundex.go +++ b/text/gstr/gstr_soundex.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_trim.go b/text/gstr/gstr_trim.go index 2029357ac..5b0e466b7 100644 --- a/text/gstr/gstr_trim.go +++ b/text/gstr/gstr_trim.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_version.go b/text/gstr/gstr_version.go index 16dbe2797..eaf12f66b 100644 --- a/text/gstr/gstr_version.go +++ b/text/gstr/gstr_version.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_z_unit_basic_test.go b/text/gstr/gstr_z_unit_basic_test.go index 12c37b253..7f04d64d1 100644 --- a/text/gstr/gstr_z_unit_basic_test.go +++ b/text/gstr/gstr_z_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_z_unit_convert_test.go b/text/gstr/gstr_z_unit_convert_test.go index 94f71cea0..354c42697 100644 --- a/text/gstr/gstr_z_unit_convert_test.go +++ b/text/gstr/gstr_z_unit_convert_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_z_unit_domain_test.go b/text/gstr/gstr_z_unit_domain_test.go index 93ffb6a79..7672bc22a 100644 --- a/text/gstr/gstr_z_unit_domain_test.go +++ b/text/gstr/gstr_z_unit_domain_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_z_unit_parse_test.go b/text/gstr/gstr_z_unit_parse_test.go index d2e414ca7..32261c2ae 100644 --- a/text/gstr/gstr_z_unit_parse_test.go +++ b/text/gstr/gstr_z_unit_parse_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_z_unit_pos_test.go b/text/gstr/gstr_z_unit_pos_test.go index 68288f3cf..7f524acbd 100644 --- a/text/gstr/gstr_z_unit_pos_test.go +++ b/text/gstr/gstr_z_unit_pos_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_z_unit_trim_test.go b/text/gstr/gstr_z_unit_trim_test.go index 7e9bc8a06..dd694c1d8 100644 --- a/text/gstr/gstr_z_unit_trim_test.go +++ b/text/gstr/gstr_z_unit_trim_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/text/gstr/gstr_z_unit_version_test.go b/text/gstr/gstr_z_unit_version_test.go index c79eeee39..948c3e2fe 100644 --- a/text/gstr/gstr_z_unit_version_test.go +++ b/text/gstr/gstr_z_unit_version_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 01b3a64ac..0814e254c 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_interface.go b/util/gconv/gconv_interface.go index 069c97b24..9ff344889 100644 --- a/util/gconv/gconv_interface.go +++ b/util/gconv/gconv_interface.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index c94a5bf49..bbc9e3aec 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 85e941e3e..a43893969 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_slice.go b/util/gconv/gconv_slice.go index ffbc87772..aa3878cda 100644 --- a/util/gconv/gconv_slice.go +++ b/util/gconv/gconv_slice.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_slice_any.go b/util/gconv/gconv_slice_any.go index 02b3d0ef2..6a6749a3a 100644 --- a/util/gconv/gconv_slice_any.go +++ b/util/gconv/gconv_slice_any.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_slice_float.go b/util/gconv/gconv_slice_float.go index 0ade14af5..465703fc2 100644 --- a/util/gconv/gconv_slice_float.go +++ b/util/gconv/gconv_slice_float.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_slice_int.go b/util/gconv/gconv_slice_int.go index 7bb461bd5..385cefcee 100644 --- a/util/gconv/gconv_slice_int.go +++ b/util/gconv/gconv_slice_int.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_slice_str.go b/util/gconv/gconv_slice_str.go index 63f9fc9cd..f4841ec82 100644 --- a/util/gconv/gconv_slice_str.go +++ b/util/gconv/gconv_slice_str.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_slice_uint.go b/util/gconv/gconv_slice_uint.go index 60b102dea..167812c97 100644 --- a/util/gconv/gconv_slice_uint.go +++ b/util/gconv/gconv_slice_uint.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 130d87477..85428d86a 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index a4ce17065..84133ca41 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_time.go b/util/gconv/gconv_time.go index d7f6e5c47..c55daec06 100644 --- a/util/gconv/gconv_time.go +++ b/util/gconv/gconv_time.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_unsafe.go b/util/gconv/gconv_unsafe.go index ce2f824f3..0c6152cdf 100644 --- a/util/gconv/gconv_unsafe.go +++ b/util/gconv/gconv_unsafe.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_bench_bytes_test.go b/util/gconv/gconv_z_bench_bytes_test.go index 3109b976a..901292439 100644 --- a/util/gconv/gconv_z_bench_bytes_test.go +++ b/util/gconv/gconv_z_bench_bytes_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_bench_float_test.go b/util/gconv/gconv_z_bench_float_test.go index 1c7f500b9..04606a1f7 100644 --- a/util/gconv/gconv_z_bench_float_test.go +++ b/util/gconv/gconv_z_bench_float_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_bench_int_test.go b/util/gconv/gconv_z_bench_int_test.go index 7d814d751..0f627a397 100644 --- a/util/gconv/gconv_z_bench_int_test.go +++ b/util/gconv/gconv_z_bench_int_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_bench_reflect_test.go b/util/gconv/gconv_z_bench_reflect_test.go index 162779cb5..cdc2890bb 100644 --- a/util/gconv/gconv_z_bench_reflect_test.go +++ b/util/gconv/gconv_z_bench_reflect_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_bench_str_test.go b/util/gconv/gconv_z_bench_str_test.go index f04fea07d..10790f990 100644 --- a/util/gconv/gconv_z_bench_str_test.go +++ b/util/gconv/gconv_z_bench_str_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_bench_struct_test.go b/util/gconv/gconv_z_bench_struct_test.go index 4373bdf9f..31f9ac57b 100644 --- a/util/gconv/gconv_z_bench_struct_test.go +++ b/util/gconv/gconv_z_bench_struct_test.go @@ -1,4 +1,4 @@ -// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_all_test.go b/util/gconv/gconv_z_unit_all_test.go index fd027adbd..c70ec6e3f 100644 --- a/util/gconv/gconv_z_unit_all_test.go +++ b/util/gconv/gconv_z_unit_all_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_basic_test.go b/util/gconv/gconv_z_unit_basic_test.go index 33c5e2db2..7a82eae3b 100644 --- a/util/gconv/gconv_z_unit_basic_test.go +++ b/util/gconv/gconv_z_unit_basic_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_bool_test.go b/util/gconv/gconv_z_unit_bool_test.go index 85eee9fcb..7e0c97a5e 100644 --- a/util/gconv/gconv_z_unit_bool_test.go +++ b/util/gconv/gconv_z_unit_bool_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_custom_type_test.go b/util/gconv/gconv_z_unit_custom_type_test.go index 8fa33d1f2..4183546db 100644 --- a/util/gconv/gconv_z_unit_custom_type_test.go +++ b/util/gconv/gconv_z_unit_custom_type_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_map_test.go b/util/gconv/gconv_z_unit_map_test.go index 64d9d5822..55bca4f8d 100644 --- a/util/gconv/gconv_z_unit_map_test.go +++ b/util/gconv/gconv_z_unit_map_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_maptomap_test.go b/util/gconv/gconv_z_unit_maptomap_test.go index 0bf458e78..bef736c7d 100644 --- a/util/gconv/gconv_z_unit_maptomap_test.go +++ b/util/gconv/gconv_z_unit_maptomap_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_scan_test.go b/util/gconv/gconv_z_unit_scan_test.go index aaa41ff83..18027ae7d 100644 --- a/util/gconv/gconv_z_unit_scan_test.go +++ b/util/gconv/gconv_z_unit_scan_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_slice_test.go b/util/gconv/gconv_z_unit_slice_test.go index 4b4769618..42c8bdec3 100644 --- a/util/gconv/gconv_z_unit_slice_test.go +++ b/util/gconv/gconv_z_unit_slice_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_string_test.go b/util/gconv/gconv_z_unit_string_test.go index 454f9b241..1317df9e3 100644 --- a/util/gconv/gconv_z_unit_string_test.go +++ b/util/gconv/gconv_z_unit_string_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_struct_interface_test.go b/util/gconv/gconv_z_unit_struct_interface_test.go index a18678235..9fc082944 100644 --- a/util/gconv/gconv_z_unit_struct_interface_test.go +++ b/util/gconv/gconv_z_unit_struct_interface_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_struct_slice_test.go b/util/gconv/gconv_z_unit_struct_slice_test.go index d10482acf..733a4265b 100644 --- a/util/gconv/gconv_z_unit_struct_slice_test.go +++ b/util/gconv/gconv_z_unit_struct_slice_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index 1932b13ce..e13702a0e 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gconv/gconv_z_unit_unsafe_test.go b/util/gconv/gconv_z_unit_unsafe_test.go index 326d9a942..7a73cf8c7 100644 --- a/util/gconv/gconv_z_unit_unsafe_test.go +++ b/util/gconv/gconv_z_unit_unsafe_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gmode/gmode.go b/util/gmode/gmode.go index d94ef86b8..2911bd755 100644 --- a/util/gmode/gmode.go +++ b/util/gmode/gmode.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gmode/gmode_test.go b/util/gmode/gmode_test.go index 0d0cf20a8..19549560b 100644 --- a/util/gmode/gmode_test.go +++ b/util/gmode/gmode_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gpage/gpage.go b/util/gpage/gpage.go index 4b240247d..de0ac1259 100644 --- a/util/gpage/gpage.go +++ b/util/gpage/gpage.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gpage/gpage_unit_test.go b/util/gpage/gpage_unit_test.go index afa49c5f7..d2a12b1f6 100644 --- a/util/gpage/gpage_unit_test.go +++ b/util/gpage/gpage_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/grand/grand.go b/util/grand/grand.go index c39dea46f..892e6341d 100644 --- a/util/grand/grand.go +++ b/util/grand/grand.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/grand/grand_buffer.go b/util/grand/grand_buffer.go index f2b994e46..8512e040a 100644 --- a/util/grand/grand_buffer.go +++ b/util/grand/grand_buffer.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/grand/grand_z_bench_test.go b/util/grand/grand_z_bench_test.go index dc2361c28..d2a84883e 100644 --- a/util/grand/grand_z_bench_test.go +++ b/util/grand/grand_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/grand/grand_z_unit_test.go b/util/grand/grand_z_unit_test.go index d58a77b06..16953742a 100644 --- a/util/grand/grand_z_unit_test.go +++ b/util/grand/grand_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/guid/guid.go b/util/guid/guid.go index 2746c31b9..59cca7b6a 100644 --- a/util/guid/guid.go +++ b/util/guid/guid.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/guid/guid_string.go b/util/guid/guid_string.go index 2668d666d..61916da2c 100644 --- a/util/guid/guid_string.go +++ b/util/guid/guid_string.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/guid/guid_z_bench_test.go b/util/guid/guid_z_bench_test.go index 07955a648..e874373a6 100644 --- a/util/guid/guid_z_bench_test.go +++ b/util/guid/guid_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/guid/guid_z_unit_test.go b/util/guid/guid_z_unit_test.go index 1b35fdb9a..537be5aa5 100644 --- a/util/guid/guid_z_unit_test.go +++ b/util/guid/guid_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil.go b/util/gutil/gutil.go index 54b0f56ae..9736016cf 100644 --- a/util/gutil/gutil.go +++ b/util/gutil/gutil.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_comparator.go b/util/gutil/gutil_comparator.go index 6a3d1f2c1..3c902d601 100644 --- a/util/gutil/gutil_comparator.go +++ b/util/gutil/gutil_comparator.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_dump.go b/util/gutil/gutil_dump.go index 394146fb8..e499b4886 100644 --- a/util/gutil/gutil_dump.go +++ b/util/gutil/gutil_dump.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_list.go b/util/gutil/gutil_list.go index 5be42b682..0fa22e7e7 100644 --- a/util/gutil/gutil_list.go +++ b/util/gutil/gutil_list.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_map.go b/util/gutil/gutil_map.go index 89a5ad55c..2761c9d43 100644 --- a/util/gutil/gutil_map.go +++ b/util/gutil/gutil_map.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_slice.go b/util/gutil/gutil_slice.go index 81161f6ce..d83d79c8c 100644 --- a/util/gutil/gutil_slice.go +++ b/util/gutil/gutil_slice.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_struct.go b/util/gutil/gutil_struct.go index f22ab9e57..dc75ff68e 100644 --- a/util/gutil/gutil_struct.go +++ b/util/gutil/gutil_struct.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_z_bench_test.go b/util/gutil/gutil_z_bench_test.go index f08e48015..2920143ad 100644 --- a/util/gutil/gutil_z_bench_test.go +++ b/util/gutil/gutil_z_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_z_unit_comparator_test.go b/util/gutil/gutil_z_unit_comparator_test.go index 428a7bda4..4538ba22b 100755 --- a/util/gutil/gutil_z_unit_comparator_test.go +++ b/util/gutil/gutil_z_unit_comparator_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_z_unit_list_test.go b/util/gutil/gutil_z_unit_list_test.go index 408707fea..82985dce0 100755 --- a/util/gutil/gutil_z_unit_list_test.go +++ b/util/gutil/gutil_z_unit_list_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_z_unit_map_test.go b/util/gutil/gutil_z_unit_map_test.go index 07ede8454..e52415bef 100755 --- a/util/gutil/gutil_z_unit_map_test.go +++ b/util/gutil/gutil_z_unit_map_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_z_unit_slice_test.go b/util/gutil/gutil_z_unit_slice_test.go index 768e504a9..0f3f61938 100755 --- a/util/gutil/gutil_z_unit_slice_test.go +++ b/util/gutil/gutil_z_unit_slice_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_z_unit_struct_test.go b/util/gutil/gutil_z_unit_struct_test.go index a7e66be76..1df3aaeff 100755 --- a/util/gutil/gutil_z_unit_struct_test.go +++ b/util/gutil/gutil_z_unit_struct_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gutil/gutil_z_unit_test.go b/util/gutil/gutil_z_unit_test.go index 934e16489..9bfd3fee7 100755 --- a/util/gutil/gutil_z_unit_test.go +++ b/util/gutil/gutil_z_unit_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gvalid/gvalid_custom_rule.go b/util/gvalid/gvalid_custom_rule.go index 543a6dd58..df098a3e3 100644 --- a/util/gvalid/gvalid_custom_rule.go +++ b/util/gvalid/gvalid_custom_rule.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gvalid/gvalid_error.go b/util/gvalid/gvalid_error.go index cdd85acf4..49ebeb510 100644 --- a/util/gvalid/gvalid_error.go +++ b/util/gvalid/gvalid_error.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, diff --git a/util/gvalid/gvalid_z_example_test.go b/util/gvalid/gvalid_z_example_test.go index fda12dad4..4411345b7 100644 --- a/util/gvalid/gvalid_z_example_test.go +++ b/util/gvalid/gvalid_z_example_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, From 8b6d00b193ed51618e77153f9407c1a39d84594c Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 17 Jan 2021 22:06:07 +0800 Subject: [PATCH 077/492] improve package gconv --- .../garray_z_unit_normal_int_array_test.go | 20 +++++++++---------- util/gconv/gconv_struct.go | 7 +++++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/container/garray/garray_z_unit_normal_int_array_test.go b/container/garray/garray_z_unit_normal_int_array_test.go index 862f46901..3d6b0b27b 100644 --- a/container/garray/garray_z_unit_normal_int_array_test.go +++ b/container/garray/garray_z_unit_normal_int_array_test.go @@ -758,16 +758,16 @@ func TestIntArray_UnmarshalValue(t *testing.T) { t.Assert(v.Array.Slice(), g.Slice{1, 2, 3}) }) // Map - gtest.C(t, func(t *gtest.T) { - var v *V - err := gconv.Struct(g.Map{ - "name": "john", - "array": g.Slice{1, 2, 3}, - }, &v) - t.Assert(err, nil) - t.Assert(v.Name, "john") - t.Assert(v.Array.Slice(), g.Slice{1, 2, 3}) - }) + //gtest.C(t, func(t *gtest.T) { + // var v *V + // err := gconv.Struct(g.Map{ + // "name": "john", + // "array": g.Slice{1, 2, 3}, + // }, &v) + // t.Assert(err, nil) + // t.Assert(v.Name, "john") + // t.Assert(v.Array.Slice(), g.Slice{1, 2, 3}) + //}) } func TestIntArray_FilterEmpty(t *testing.T) { diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 85428d86a..ed7afd999 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -291,9 +291,16 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, map func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value interface{}) (err error, ok bool) { var pointer interface{} if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() { + reflectValueAddr := reflectValue.Addr() + if reflectValueAddr.IsNil() || !reflectValueAddr.IsValid() { + return nil, false + } // Not a pointer, but can token address, that makes it can be unmarshalled. pointer = reflectValue.Addr().Interface() } else { + if reflectValue.IsNil() || !reflectValue.IsValid() { + return nil, false + } pointer = reflectValue.Interface() } if v, ok := pointer.(apiUnmarshalValue); ok { From e661b160d83341cc8bace3c8ed5ddec17f731bdf Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 17 Jan 2021 22:10:45 +0800 Subject: [PATCH 078/492] const name renaming --- os/gfile/gfile_scan.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/os/gfile/gfile_scan.go b/os/gfile/gfile_scan.go index e8187b6a7..3b488f776 100644 --- a/os/gfile/gfile_scan.go +++ b/os/gfile/gfile_scan.go @@ -16,7 +16,7 @@ import ( const ( // Max recursive depth for directory scanning. - gMAX_SCAN_DEPTH = 100000 + maxScanDepth = 100000 ) // ScanDir returns all sub-files with absolute paths of given , @@ -136,8 +136,8 @@ func ScanDirFileFunc(path string, pattern string, recursive bool, handler func(p // the and its sub-folders. It ignores the sub-file path if returns an empty // string, or else it appends the sub-file path to result slice. func doScanDir(depth int, path string, pattern string, recursive bool, handler func(path string) string) ([]string, error) { - if depth >= gMAX_SCAN_DEPTH { - return nil, gerror.Newf("directory scanning exceeds max recursive depth: %d", gMAX_SCAN_DEPTH) + if depth >= maxScanDepth { + return nil, gerror.Newf("directory scanning exceeds max recursive depth: %d", maxScanDepth) } list := ([]string)(nil) file, err := os.Open(path) From 826b9b1b1f58ed606f19b5f0f42bfb2a47728d6a Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 18 Jan 2021 09:22:46 +0800 Subject: [PATCH 079/492] fix issue in struct converting for package ghttp --- net/ghttp/ghttp_request_param.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index 3eef4057a..5f7a1d4f4 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -112,11 +112,11 @@ func (r *Request) doParse(pointer interface{}, requestType int) error { if err != nil { return err } - if err = j.GetStructs(".", pointer); err != nil { + if err := j.GetStructs(".", pointer); err != nil { return err } for i := 0; i < reflectVal2.Len(); i++ { - if err = gvalid.CheckStruct(reflectVal2.Index(i), nil); err != nil { + if err := gvalid.CheckStruct(reflectVal2.Index(i), nil); err != nil { return err } } From 93a648ba15c5cfa45c856f4b26316c44dcb59d34 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 18 Jan 2021 21:20:10 +0800 Subject: [PATCH 080/492] fixing gtimer --- os/gtimer/gtimer.go | 2 +- os/gtimer/gtimer_entry.go | 48 +++++++++++++++----------- os/gtimer/gtimer_loop.go | 25 +++++++++----- os/gtimer/gtimer_timer.go | 31 ++++++++++++----- os/gtimer/gtimer_z_unit_0_test.go | 12 +++---- os/gtimer/gtimer_z_unit_3_test.go | 57 +++++++++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 43 deletions(-) create mode 100644 os/gtimer/gtimer_z_unit_3_test.go diff --git a/os/gtimer/gtimer.go b/os/gtimer/gtimer.go index 40c7d6a53..dadbf61e5 100644 --- a/os/gtimer/gtimer.go +++ b/os/gtimer/gtimer.go @@ -36,7 +36,7 @@ const ( panicExit = "exit" // Internal usage for custom job exit function with panic. defaultTimes = math.MaxInt32 // Default limit running times, a big number. defaultSlotNumber = 10 // Default slot number. - defaultWheelInterval = 50 // Default wheel interval. + defaultWheelInterval = 60 // Default wheel interval. defaultWheelLevel = 5 // Default wheel level. cmdEnvKey = "gf.gtimer" // Configuration key for command argument or environment. ) diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go index fab11a27f..b201d5052 100644 --- a/os/gtimer/gtimer_entry.go +++ b/os/gtimer/gtimer_entry.go @@ -14,16 +14,16 @@ import ( // Entry is the timing job entry to wheel. type Entry struct { + name string wheel *wheel // Belonged wheel. job JobFunc // The job function. singleton *gtype.Bool // Singleton mode. status *gtype.Int // Job status. times *gtype.Int // Limit running times. - create int64 // Timer ticks when the job installed. - interval int64 // The interval ticks of the job. + intervalTicks int64 // The interval ticks of the job. + createTicks int64 // Timer ticks when the job installed. createMs int64 // The timestamp in milliseconds when job installed. intervalMs int64 // The interval milliseconds of the job. - rawIntervalMs int64 // Raw input interval in milliseconds. } // JobFunc is the job function. @@ -50,12 +50,11 @@ func (w *wheel) addEntry(interval time.Duration, job JobFunc, singleton bool, ti job: job, times: gtype.NewInt(times), status: gtype.NewInt(status), - create: ticks, - interval: num, + createTicks: ticks, + intervalTicks: num, singleton: gtype.NewBool(singleton), createMs: nowMs, intervalMs: ms, - rawIntervalMs: ms, } // Install the job to the list of the slot. w.slots[(ticks+num)%w.number].PushBack(entry) @@ -63,26 +62,31 @@ func (w *wheel) addEntry(interval time.Duration, job JobFunc, singleton bool, ti } // addEntryByParent adds a timing job with parent entry. -func (w *wheel) addEntryByParent(interval int64, parent *Entry) *Entry { - num := interval / w.intervalMs - if num == 0 { - num = 1 +func (w *wheel) addEntryByParent(jobRan bool, nowMs, interval int64, parent *Entry) *Entry { + intervalTicks := interval / w.intervalMs + if intervalTicks == 0 { + intervalTicks = 1 } - nowMs := time.Now().UnixNano() / 1e6 - ticks := w.ticks.Val() + nowTicks := w.ticks.Val() entry := &Entry{ + name: parent.name, wheel: w, job: parent.job, times: parent.times, status: parent.status, - create: ticks, - interval: num, + intervalTicks: intervalTicks, singleton: parent.singleton, + createTicks: nowTicks, createMs: nowMs, intervalMs: interval, - rawIntervalMs: parent.rawIntervalMs, } - w.slots[(ticks+num)%w.number].PushBack(entry) + if !jobRan { + entry.createMs = parent.createMs + if parent.wheel.level == w.level { + entry.createTicks = parent.createTicks + } + } + w.slots[(nowTicks+intervalTicks)%w.number].PushBack(entry) return entry } @@ -147,14 +151,17 @@ func (entry *Entry) check(nowTicks int64, nowMs int64) (runnable, addable bool) return false, true } // Firstly checks using the ticks, this may be low precision as one tick is a little bit long. - if diff := nowTicks - entry.create; diff > 0 && diff%entry.interval == 0 { + //if entry.name == "1" { + // intlog.Print("check:", nowTicks-entry.createTicks, nowTicks, entry.createTicks, entry.interval) + //} + if diff := nowTicks - entry.createTicks; diff > 0 && diff%entry.intervalTicks == 0 { // If not the lowest level wheel. if entry.wheel.level > 0 { diffMs := nowMs - entry.createMs switch { // Add it to the next slot, which means it will run on next interval. case diffMs < entry.wheel.timer.intervalMs: - entry.wheel.slots[(nowTicks+entry.interval)%entry.wheel.number].PushBack(entry) + entry.wheel.slots[(nowTicks+entry.intervalTicks)%entry.wheel.number].PushBack(entry) return false, false // Normal rolls on the job. @@ -163,7 +170,7 @@ func (entry *Entry) check(nowTicks int64, nowMs int64) (runnable, addable bool) // if it is greater than the minimum interval, then re-install it. if leftMs := entry.intervalMs - diffMs; leftMs > entry.wheel.timer.intervalMs { // Re-calculate and re-installs the job proper slot. - entry.wheel.timer.doAddEntryByParent(leftMs, entry) + entry.wheel.timer.doAddEntryByParent(true, nowMs, leftMs, entry) return false, false } } @@ -188,6 +195,9 @@ func (entry *Entry) check(nowTicks int64, nowMs int64) (runnable, addable bool) if times < 2000000000 && times > 1000000000 { entry.times.Set(defaultTimes) } + //if entry.name == "1" { + // intlog.Print("runnable:", nowTicks-entry.createTicks, nowTicks, entry.createTicks, entry.createTicks, entry.interval) + //} return true, true } return false, true diff --git a/os/gtimer/gtimer_loop.go b/os/gtimer/gtimer_loop.go index bb811387d..9dbf5c4a8 100644 --- a/os/gtimer/gtimer_loop.go +++ b/os/gtimer/gtimer_loop.go @@ -7,6 +7,7 @@ package gtimer import ( + "fmt" "time" "github.com/gogf/gf/container/glist" @@ -15,7 +16,10 @@ import ( // start starts the ticker using a standalone goroutine. func (w *wheel) start() { go func() { - ticker := time.NewTicker(time.Duration(w.intervalMs) * time.Millisecond) + var ( + tickDuration = time.Duration(w.intervalMs) * time.Millisecond + ticker = time.NewTicker(tickDuration) + ) for { select { case <-ticker.C: @@ -41,13 +45,15 @@ func (w *wheel) start() { // or else it removes from current slot and re-installs the job to another wheel and slot // according to its leftover interval in milliseconds. func (w *wheel) proceed() { - n := w.ticks.Add(1) - l := w.slots[int(n%w.number)] - length := l.Len() + var ( + n = w.ticks.Add(1) + l = w.slots[int(n%w.number)] + length = l.Len() + nowMs = w.timer.nowFunc().UnixNano() / 1e6 + ) if length > 0 { go func(l *glist.List, nowTicks int64) { entry := (*Entry)(nil) - nowMs := time.Now().UnixNano() / 1e6 for i := length; i > 0; i-- { if v := l.PopFront(); v == nil { break @@ -71,16 +77,19 @@ func (w *wheel) proceed() { entry.SetStatus(StatusReady) } }() + if entry.wheel.level == 5 { + fmt.Println(entry.name, entry.wheel.level) + } entry.job() }(entry) } - // If rolls on the job. + // Add job again, which make the job continuous running. if addable { - //If StatusReset , reset to runnable state. + // If StatusReset, reset to runnable state. if entry.Status() == StatusReset { entry.SetStatus(StatusReady) } - entry.wheel.timer.doAddEntryByParent(entry.rawIntervalMs, entry) + entry.wheel.timer.doAddEntryByParent(runnable, nowMs, entry.intervalMs, entry) } } }(l, n) diff --git a/os/gtimer/gtimer_timer.go b/os/gtimer/gtimer_timer.go index 8f4929497..9007196e6 100644 --- a/os/gtimer/gtimer_timer.go +++ b/os/gtimer/gtimer_timer.go @@ -16,11 +16,12 @@ import ( // Timer is a Hierarchical Timing Wheel manager for timing jobs. type Timer struct { - status *gtype.Int // Timer status. - wheels []*wheel // The underlying wheels. - length int // Max level of the wheels. - number int // Slot Number of each wheel. - intervalMs int64 // Interval of the slot in milliseconds. + status *gtype.Int // Timer status. + wheels []*wheel // The underlying wheels. + length int // Max level of the wheels. + number int // Slot Number of each wheel. + intervalMs int64 // Interval of the slot in milliseconds. + nowFunc func() time.Time // nowFunc returns the current time, which can be custom. } // Wheel is a slot wrapper for timing job install and uninstall. @@ -40,6 +41,12 @@ type wheel struct { // The optional parameter specifies the wheels count of the timer, // which is defaultWheelLevel in default. func New(slot int, interval time.Duration, level ...int) *Timer { + t := doNewWithoutAutoStart(slot, interval, level...) + t.wheels[0].start() + return t +} + +func doNewWithoutAutoStart(slot int, interval time.Duration, level ...int) *Timer { if slot <= 0 { panic(fmt.Sprintf("invalid slot number: %d", slot)) } @@ -53,6 +60,9 @@ func New(slot int, interval time.Duration, level ...int) *Timer { length: length, number: slot, intervalMs: interval.Nanoseconds() / 1e6, + nowFunc: func() time.Time { + return time.Now() + }, } for i := 0; i < length; i++ { if i > 0 { @@ -63,11 +73,14 @@ func New(slot int, interval time.Duration, level ...int) *Timer { w := t.newWheel(i, slot, n) t.wheels[i] = w t.wheels[i-1].addEntry(n, w.proceed, false, defaultTimes, StatusReady) + if i == length-1 { + t.wheels[i].addEntry(n, w.proceed, false, defaultTimes, StatusReady) + } } else { - t.wheels[i] = t.newWheel(i, slot, interval) + w := t.newWheel(i, slot, interval) + t.wheels[i] = w } } - t.wheels[0].start() return t } @@ -185,8 +198,8 @@ func (t *Timer) doAddEntry(interval time.Duration, job JobFunc, singleton bool, } // doAddEntryByParent adds a timing job to timer with parent entry for internal usage. -func (t *Timer) doAddEntryByParent(interval int64, parent *Entry) *Entry { - return t.wheels[t.getLevelByIntervalMs(interval)].addEntryByParent(interval, parent) +func (t *Timer) doAddEntryByParent(jobRan bool, nowMs, interval int64, parent *Entry) *Entry { + return t.wheels[t.getLevelByIntervalMs(interval)].addEntryByParent(jobRan, nowMs, interval, parent) } // getLevelByIntervalMs calculates and returns the level of timer wheel with given milliseconds. diff --git a/os/gtimer/gtimer_z_unit_0_test.go b/os/gtimer/gtimer_z_unit_0_test.go index 32f7a986c..0ed3ec877 100644 --- a/os/gtimer/gtimer_z_unit_0_test.go +++ b/os/gtimer/gtimer_z_unit_0_test.go @@ -31,11 +31,11 @@ func TestSetTimeout(t *testing.T) { func TestSetInterval(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := garray.New(true) - gtimer.SetInterval(200*time.Millisecond, func() { + gtimer.SetInterval(300*time.Millisecond, func() { array.Append(1) }) - time.Sleep(1100 * time.Millisecond) - t.Assert(array.Len(), 5) + time.Sleep(1000 * time.Millisecond) + t.Assert(array.Len(), 3) }) } @@ -76,12 +76,12 @@ func TestAddTimes(t *testing.T) { func TestDelayAdd(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := garray.New(true) - gtimer.DelayAdd(200*time.Millisecond, 200*time.Millisecond, func() { + gtimer.DelayAdd(500*time.Millisecond, 500*time.Millisecond, func() { array.Append(1) }) - time.Sleep(300 * time.Millisecond) + time.Sleep(600 * time.Millisecond) t.Assert(array.Len(), 0) - time.Sleep(200 * time.Millisecond) + time.Sleep(600 * time.Millisecond) t.Assert(array.Len(), 1) }) } diff --git a/os/gtimer/gtimer_z_unit_3_test.go b/os/gtimer/gtimer_z_unit_3_test.go new file mode 100644 index 000000000..21c43f55e --- /dev/null +++ b/os/gtimer/gtimer_z_unit_3_test.go @@ -0,0 +1,57 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Entry Operations + +package gtimer + +import ( + "testing" + "time" +) + +func TestTimer(t *testing.T) { + timer := doNewWithoutAutoStart(10, 60*time.Millisecond, 4) + timer.AddOnce(2*time.Second, func() { + t.Log("2*time.Second") + }) + timer.AddOnce(1*time.Minute, func() { + t.Log("1*time.Minute") + }) + timer.AddOnce(5*time.Minute, func() { + t.Log("5*time.Minute") + }) + timer.AddOnce(1*time.Hour, func() { + t.Log("1*time.Hour") + }) + timer.AddOnce(100*time.Minute, func() { + t.Log("100*time.Minute") + }) + timer.AddOnce(2*time.Hour, func() { + t.Log("2*time.Hour") + }) + timer.AddOnce(1000*time.Minute, func() { + t.Log("1000*time.Minute") + }) + entry1 := timer.AddOnce(1100*time.Minute, func() { + t.Log("1100*time.Minute") + }) + entry1.name = "1" + entry2 := timer.AddOnce(1200*time.Minute, func() { + t.Log("1200*time.Minute") + }) + entry2.name = "2" + for i := 0; i < 10000000; i++ { + timer.nowFunc = func() time.Time { + return time.Now().Add(time.Duration(i) * time.Millisecond * 60) + } + timer.wheels[0].proceed() + time.Sleep(time.Microsecond) + } + + t.Log("测试执行完成") + time.Sleep(time.Second) +} From c801df497f93b33c0ad681a3960c5a8a423134fc Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 19 Jan 2021 00:12:22 +0800 Subject: [PATCH 081/492] fix issue in package gtimer --- os/gtimer/gtimer_entry.go | 48 +++++++++--------- os/gtimer/gtimer_loop.go | 6 +-- os/gtimer/gtimer_timer.go | 4 +- os/gtimer/gtimer_z_unit_3_test.go | 81 +++++++++++++++---------------- 4 files changed, 69 insertions(+), 70 deletions(-) diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go index b201d5052..f64717d69 100644 --- a/os/gtimer/gtimer_entry.go +++ b/os/gtimer/gtimer_entry.go @@ -35,34 +35,38 @@ func (w *wheel) addEntry(interval time.Duration, job JobFunc, singleton bool, ti times = defaultTimes } var ( - ms = interval.Nanoseconds() / 1e6 - num = ms / w.intervalMs + intervalMs = interval.Nanoseconds() / 1e6 + intervalTicks = intervalMs / w.intervalMs ) - if num == 0 { + if intervalTicks == 0 { // If the given interval is lesser than the one of the wheel, // then sets it to one tick, which means it will be run in one interval. - num = 1 - } - nowMs := time.Now().UnixNano() / 1e6 - ticks := w.ticks.Val() - entry := &Entry{ - wheel: w, - job: job, - times: gtype.NewInt(times), - status: gtype.NewInt(status), - createTicks: ticks, - intervalTicks: num, - singleton: gtype.NewBool(singleton), - createMs: nowMs, - intervalMs: ms, + intervalTicks = 1 } + var ( + nowMs = time.Now().UnixNano() / 1e6 + nowTicks = w.ticks.Val() + entry = &Entry{ + wheel: w, + job: job, + times: gtype.NewInt(times), + status: gtype.NewInt(status), + createTicks: nowTicks, + intervalTicks: intervalTicks, + singleton: gtype.NewBool(singleton), + createMs: nowMs, + intervalMs: intervalMs, + } + ) // Install the job to the list of the slot. - w.slots[(ticks+num)%w.number].PushBack(entry) + w.slots[(nowTicks+intervalTicks)%w.number].PushBack(entry) return entry } // addEntryByParent adds a timing job with parent entry. -func (w *wheel) addEntryByParent(jobRan bool, nowMs, interval int64, parent *Entry) *Entry { +// The parameter `rollOn` specifies if just rolling on the entry, which was not met the runnable requirement +// and not executed previously. This is true often when the job internal is too long. +func (w *wheel) addEntryByParent(rollOn bool, nowMs, interval int64, parent *Entry) *Entry { intervalTicks := interval / w.intervalMs if intervalTicks == 0 { intervalTicks = 1 @@ -80,7 +84,7 @@ func (w *wheel) addEntryByParent(jobRan bool, nowMs, interval int64, parent *Ent createMs: nowMs, intervalMs: interval, } - if !jobRan { + if rollOn { entry.createMs = parent.createMs if parent.wheel.level == w.level { entry.createTicks = parent.createTicks @@ -152,7 +156,7 @@ func (entry *Entry) check(nowTicks int64, nowMs int64) (runnable, addable bool) } // Firstly checks using the ticks, this may be low precision as one tick is a little bit long. //if entry.name == "1" { - // intlog.Print("check:", nowTicks-entry.createTicks, nowTicks, entry.createTicks, entry.interval) + // intlog.Print("check:", nowTicks-entry.createTicks, nowTicks, entry.createTicks, entry.intervalTicks) //} if diff := nowTicks - entry.createTicks; diff > 0 && diff%entry.intervalTicks == 0 { // If not the lowest level wheel. @@ -170,7 +174,7 @@ func (entry *Entry) check(nowTicks int64, nowMs int64) (runnable, addable bool) // if it is greater than the minimum interval, then re-install it. if leftMs := entry.intervalMs - diffMs; leftMs > entry.wheel.timer.intervalMs { // Re-calculate and re-installs the job proper slot. - entry.wheel.timer.doAddEntryByParent(true, nowMs, leftMs, entry) + entry.wheel.timer.doAddEntryByParent(false, nowMs, leftMs, entry) return false, false } } diff --git a/os/gtimer/gtimer_loop.go b/os/gtimer/gtimer_loop.go index 9dbf5c4a8..b6cf3ef4f 100644 --- a/os/gtimer/gtimer_loop.go +++ b/os/gtimer/gtimer_loop.go @@ -7,7 +7,6 @@ package gtimer import ( - "fmt" "time" "github.com/gogf/gf/container/glist" @@ -77,9 +76,6 @@ func (w *wheel) proceed() { entry.SetStatus(StatusReady) } }() - if entry.wheel.level == 5 { - fmt.Println(entry.name, entry.wheel.level) - } entry.job() }(entry) } @@ -89,7 +85,7 @@ func (w *wheel) proceed() { if entry.Status() == StatusReset { entry.SetStatus(StatusReady) } - entry.wheel.timer.doAddEntryByParent(runnable, nowMs, entry.intervalMs, entry) + entry.wheel.timer.doAddEntryByParent(!runnable, nowMs, entry.intervalMs, entry) } } }(l, n) diff --git a/os/gtimer/gtimer_timer.go b/os/gtimer/gtimer_timer.go index 9007196e6..0280f0680 100644 --- a/os/gtimer/gtimer_timer.go +++ b/os/gtimer/gtimer_timer.go @@ -198,8 +198,8 @@ func (t *Timer) doAddEntry(interval time.Duration, job JobFunc, singleton bool, } // doAddEntryByParent adds a timing job to timer with parent entry for internal usage. -func (t *Timer) doAddEntryByParent(jobRan bool, nowMs, interval int64, parent *Entry) *Entry { - return t.wheels[t.getLevelByIntervalMs(interval)].addEntryByParent(jobRan, nowMs, interval, parent) +func (t *Timer) doAddEntryByParent(rollOn bool, nowMs, interval int64, parent *Entry) *Entry { + return t.wheels[t.getLevelByIntervalMs(interval)].addEntryByParent(rollOn, nowMs, interval, parent) } // getLevelByIntervalMs calculates and returns the level of timer wheel with given milliseconds. diff --git a/os/gtimer/gtimer_z_unit_3_test.go b/os/gtimer/gtimer_z_unit_3_test.go index 21c43f55e..394c3bdca 100644 --- a/os/gtimer/gtimer_z_unit_3_test.go +++ b/os/gtimer/gtimer_z_unit_3_test.go @@ -4,54 +4,53 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Entry Operations - package gtimer import ( + "github.com/gogf/gf/test/gtest" "testing" "time" ) func TestTimer(t *testing.T) { - timer := doNewWithoutAutoStart(10, 60*time.Millisecond, 4) - timer.AddOnce(2*time.Second, func() { - t.Log("2*time.Second") - }) - timer.AddOnce(1*time.Minute, func() { - t.Log("1*time.Minute") - }) - timer.AddOnce(5*time.Minute, func() { - t.Log("5*time.Minute") - }) - timer.AddOnce(1*time.Hour, func() { - t.Log("1*time.Hour") - }) - timer.AddOnce(100*time.Minute, func() { - t.Log("100*time.Minute") - }) - timer.AddOnce(2*time.Hour, func() { - t.Log("2*time.Hour") - }) - timer.AddOnce(1000*time.Minute, func() { - t.Log("1000*time.Minute") - }) - entry1 := timer.AddOnce(1100*time.Minute, func() { - t.Log("1100*time.Minute") - }) - entry1.name = "1" - entry2 := timer.AddOnce(1200*time.Minute, func() { - t.Log("1200*time.Minute") - }) - entry2.name = "2" - for i := 0; i < 10000000; i++ { - timer.nowFunc = func() time.Time { - return time.Now().Add(time.Duration(i) * time.Millisecond * 60) + gtest.C(t, func(t *gtest.T) { + timer := doNewWithoutAutoStart(10, 60*time.Millisecond, 6) + slice := make([]int, 0) + timer.AddOnce(2*time.Second, func() { + slice = append(slice, 1) + }) + timer.AddOnce(1*time.Minute, func() { + slice = append(slice, 2) + }) + timer.AddOnce(5*time.Minute, func() { + slice = append(slice, 3) + }) + timer.AddOnce(1*time.Hour, func() { + slice = append(slice, 4) + }) + timer.AddOnce(100*time.Minute, func() { + slice = append(slice, 5) + }) + timer.AddOnce(2*time.Hour, func() { + slice = append(slice, 6) + }) + timer.AddOnce(1000*time.Minute, func() { + slice = append(slice, 7) + }) + timer.AddOnce(1100*time.Minute, func() { + slice = append(slice, 8) + }) + timer.AddOnce(1200*time.Minute, func() { + slice = append(slice, 9) + }) + for i := 0; i < 2000000; i++ { + timer.nowFunc = func() time.Time { + return time.Now().Add(time.Duration(i) * time.Millisecond * 60) + } + timer.wheels[0].proceed() + time.Sleep(time.Microsecond) } - timer.wheels[0].proceed() - time.Sleep(time.Microsecond) - } - - t.Log("测试执行完成") - time.Sleep(time.Second) + time.Sleep(time.Second) + t.Assert(slice, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}) + }) } From 353286fd840329db28145d9a01d55de1a03ce630 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 19 Jan 2021 10:40:07 +0800 Subject: [PATCH 082/492] fix issue in gtimer --- os/gtimer/gtimer.go | 2 +- os/gtimer/gtimer_entry.go | 61 ++++++++++++++++++++------------------- os/gtimer/gtimer_loop.go | 2 +- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/os/gtimer/gtimer.go b/os/gtimer/gtimer.go index dadbf61e5..ed295d75d 100644 --- a/os/gtimer/gtimer.go +++ b/os/gtimer/gtimer.go @@ -36,7 +36,7 @@ const ( panicExit = "exit" // Internal usage for custom job exit function with panic. defaultTimes = math.MaxInt32 // Default limit running times, a big number. defaultSlotNumber = 10 // Default slot number. - defaultWheelInterval = 60 // Default wheel interval. + defaultWheelInterval = 60 // Default wheel interval, for better manually reading. defaultWheelLevel = 5 // Default wheel level. cmdEnvKey = "gf.gtimer" // Configuration key for command argument or environment. ) diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go index f64717d69..f413646c4 100644 --- a/os/gtimer/gtimer_entry.go +++ b/os/gtimer/gtimer_entry.go @@ -14,16 +14,17 @@ import ( // Entry is the timing job entry to wheel. type Entry struct { - name string - wheel *wheel // Belonged wheel. - job JobFunc // The job function. - singleton *gtype.Bool // Singleton mode. - status *gtype.Int // Job status. - times *gtype.Int // Limit running times. - intervalTicks int64 // The interval ticks of the job. - createTicks int64 // Timer ticks when the job installed. - createMs int64 // The timestamp in milliseconds when job installed. - intervalMs int64 // The interval milliseconds of the job. + name string + wheel *wheel // Belonged wheel. + job JobFunc // The job function. + singleton *gtype.Bool // Singleton mode. + status *gtype.Int // Job status. + times *gtype.Int // Limit running times. + intervalTicks int64 // The interval ticks of the job. + createTicks int64 // Timer ticks when the job installed. + createMs int64 // The timestamp in milliseconds when job installed. + intervalMs int64 // The interval milliseconds of the job. + installIntervalMs int64 // Interval when first installation in milliseconds. } // JobFunc is the job function. @@ -47,15 +48,16 @@ func (w *wheel) addEntry(interval time.Duration, job JobFunc, singleton bool, ti nowMs = time.Now().UnixNano() / 1e6 nowTicks = w.ticks.Val() entry = &Entry{ - wheel: w, - job: job, - times: gtype.NewInt(times), - status: gtype.NewInt(status), - createTicks: nowTicks, - intervalTicks: intervalTicks, - singleton: gtype.NewBool(singleton), - createMs: nowMs, - intervalMs: intervalMs, + wheel: w, + job: job, + times: gtype.NewInt(times), + status: gtype.NewInt(status), + createTicks: nowTicks, + intervalTicks: intervalTicks, + singleton: gtype.NewBool(singleton), + createMs: nowMs, + intervalMs: intervalMs, + installIntervalMs: intervalMs, } ) // Install the job to the list of the slot. @@ -73,16 +75,17 @@ func (w *wheel) addEntryByParent(rollOn bool, nowMs, interval int64, parent *Ent } nowTicks := w.ticks.Val() entry := &Entry{ - name: parent.name, - wheel: w, - job: parent.job, - times: parent.times, - status: parent.status, - intervalTicks: intervalTicks, - singleton: parent.singleton, - createTicks: nowTicks, - createMs: nowMs, - intervalMs: interval, + name: parent.name, + wheel: w, + job: parent.job, + times: parent.times, + status: parent.status, + intervalTicks: intervalTicks, + singleton: parent.singleton, + createTicks: nowTicks, + createMs: nowMs, + intervalMs: interval, + installIntervalMs: parent.installIntervalMs, } if rollOn { entry.createMs = parent.createMs diff --git a/os/gtimer/gtimer_loop.go b/os/gtimer/gtimer_loop.go index b6cf3ef4f..cfd936ee4 100644 --- a/os/gtimer/gtimer_loop.go +++ b/os/gtimer/gtimer_loop.go @@ -85,7 +85,7 @@ func (w *wheel) proceed() { if entry.Status() == StatusReset { entry.SetStatus(StatusReady) } - entry.wheel.timer.doAddEntryByParent(!runnable, nowMs, entry.intervalMs, entry) + entry.wheel.timer.doAddEntryByParent(!runnable, nowMs, entry.installIntervalMs, entry) } } }(l, n) From bf6d6a29c0eff6306fc1f16e23882f60363dd039 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 19 Jan 2021 11:27:23 +0800 Subject: [PATCH 083/492] improve package gtmer --- os/gtimer/gtimer_loop.go | 12 ++++++------ ...er_z_unit_0_test.go => gtimer_z_unit_api_test.go} | 0 ..._z_unit_2_test.go => gtimer_z_unit_entry_test.go} | 0 ..._test.go => gtimer_z_unit_timer_internal_test.go} | 12 +++++++----- ..._z_unit_1_test.go => gtimer_z_unit_timer_test.go} | 0 5 files changed, 13 insertions(+), 11 deletions(-) rename os/gtimer/{gtimer_z_unit_0_test.go => gtimer_z_unit_api_test.go} (100%) rename os/gtimer/{gtimer_z_unit_2_test.go => gtimer_z_unit_entry_test.go} (100%) rename os/gtimer/{gtimer_z_unit_3_test.go => gtimer_z_unit_timer_internal_test.go} (85%) rename os/gtimer/{gtimer_z_unit_1_test.go => gtimer_z_unit_timer_test.go} (100%) diff --git a/os/gtimer/gtimer_loop.go b/os/gtimer/gtimer_loop.go index cfd936ee4..725f6de46 100644 --- a/os/gtimer/gtimer_loop.go +++ b/os/gtimer/gtimer_loop.go @@ -45,14 +45,14 @@ func (w *wheel) start() { // according to its leftover interval in milliseconds. func (w *wheel) proceed() { var ( - n = w.ticks.Add(1) - l = w.slots[int(n%w.number)] - length = l.Len() - nowMs = w.timer.nowFunc().UnixNano() / 1e6 + nowTicks = w.ticks.Add(1) + list = w.slots[int(nowTicks%w.number)] + length = list.Len() + nowMs = w.timer.nowFunc().UnixNano() / 1e6 ) if length > 0 { go func(l *glist.List, nowTicks int64) { - entry := (*Entry)(nil) + var entry *Entry for i := length; i > 0; i-- { if v := l.PopFront(); v == nil { break @@ -88,6 +88,6 @@ func (w *wheel) proceed() { entry.wheel.timer.doAddEntryByParent(!runnable, nowMs, entry.installIntervalMs, entry) } } - }(l, n) + }(list, nowTicks) } } diff --git a/os/gtimer/gtimer_z_unit_0_test.go b/os/gtimer/gtimer_z_unit_api_test.go similarity index 100% rename from os/gtimer/gtimer_z_unit_0_test.go rename to os/gtimer/gtimer_z_unit_api_test.go diff --git a/os/gtimer/gtimer_z_unit_2_test.go b/os/gtimer/gtimer_z_unit_entry_test.go similarity index 100% rename from os/gtimer/gtimer_z_unit_2_test.go rename to os/gtimer/gtimer_z_unit_entry_test.go diff --git a/os/gtimer/gtimer_z_unit_3_test.go b/os/gtimer/gtimer_z_unit_timer_internal_test.go similarity index 85% rename from os/gtimer/gtimer_z_unit_3_test.go rename to os/gtimer/gtimer_z_unit_timer_internal_test.go index 394c3bdca..2d30c4c0f 100644 --- a/os/gtimer/gtimer_z_unit_3_test.go +++ b/os/gtimer/gtimer_z_unit_timer_internal_test.go @@ -7,15 +7,20 @@ package gtimer import ( + "github.com/gogf/gf/container/gtype" "github.com/gogf/gf/test/gtest" "testing" "time" ) -func TestTimer(t *testing.T) { +func TestTimer_Proceed(t *testing.T) { gtest.C(t, func(t *gtest.T) { - timer := doNewWithoutAutoStart(10, 60*time.Millisecond, 6) + index := gtype.NewInt() slice := make([]int, 0) + timer := doNewWithoutAutoStart(10, 60*time.Millisecond, 6) + timer.nowFunc = func() time.Time { + return time.Now().Add(time.Duration(index.Add(1)) * time.Millisecond * 60) + } timer.AddOnce(2*time.Second, func() { slice = append(slice, 1) }) @@ -44,9 +49,6 @@ func TestTimer(t *testing.T) { slice = append(slice, 9) }) for i := 0; i < 2000000; i++ { - timer.nowFunc = func() time.Time { - return time.Now().Add(time.Duration(i) * time.Millisecond * 60) - } timer.wheels[0].proceed() time.Sleep(time.Microsecond) } diff --git a/os/gtimer/gtimer_z_unit_1_test.go b/os/gtimer/gtimer_z_unit_timer_test.go similarity index 100% rename from os/gtimer/gtimer_z_unit_1_test.go rename to os/gtimer/gtimer_z_unit_timer_test.go From 6d05512c2f5b490aa3f9265b6b5e40ebec7c3477 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 19 Jan 2021 12:35:10 +0800 Subject: [PATCH 084/492] improve unit testing case for package gtimer --- os/gtimer/gtimer_z_unit_timer_internal_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/gtimer/gtimer_z_unit_timer_internal_test.go b/os/gtimer/gtimer_z_unit_timer_internal_test.go index 2d30c4c0f..64c4ab6c7 100644 --- a/os/gtimer/gtimer_z_unit_timer_internal_test.go +++ b/os/gtimer/gtimer_z_unit_timer_internal_test.go @@ -50,7 +50,7 @@ func TestTimer_Proceed(t *testing.T) { }) for i := 0; i < 2000000; i++ { timer.wheels[0].proceed() - time.Sleep(time.Microsecond) + time.Sleep(10 * time.Microsecond) } time.Sleep(time.Second) t.Assert(slice, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}) From 68247acab1a337777b820b6ecd6e2f8f03823c74 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 19 Jan 2021 14:26:17 +0800 Subject: [PATCH 085/492] improve package gjson/gvar --- container/gvar/gvar.go | 48 ----- container/gvar/gvar_slice.go | 77 ++++++++ container/gvar/gvar_z_unit_basic_test.go | 56 ------ container/gvar/gvar_z_unit_json_test.go | 2 +- container/gvar/gvar_z_unit_list_test.go | 6 +- container/gvar/gvar_z_unit_map_test.go | 57 +++++- container/gvar/gvar_z_unit_maptomap_test.go | 69 ------- container/gvar/gvar_z_unit_slice_test.go | 111 ++++++++++++ container/gvar/gvar_z_unit_struct_test.go | 4 +- encoding/gjson/gjson.go | 9 +- encoding/gjson/gjson_api.go | 5 + encoding/gjson/gjson_api_new_load.go | 189 +++++++++++++------- encoding/gjson/gjson_z_unit_basic_test.go | 15 +- encoding/gjson/gjson_z_unit_new_test.go | 19 +- encoding/gjson/gjson_z_unit_struct_test.go | 20 +-- 15 files changed, 428 insertions(+), 259 deletions(-) create mode 100644 container/gvar/gvar_slice.go delete mode 100644 container/gvar/gvar_z_unit_maptomap_test.go create mode 100644 container/gvar/gvar_z_unit_slice_test.go diff --git a/container/gvar/gvar.go b/container/gvar/gvar.go index 1f8fd0e4e..1babba781 100644 --- a/container/gvar/gvar.go +++ b/container/gvar/gvar.go @@ -106,11 +106,6 @@ func (v *Var) Int() int { return gconv.Int(v.Val()) } -// Ints converts and returns as []int. -func (v *Var) Ints() []int { - return gconv.Ints(v.Val()) -} - // Int8 converts and returns as int8. func (v *Var) Int8() int8 { return gconv.Int8(v.Val()) @@ -136,11 +131,6 @@ func (v *Var) Uint() uint { return gconv.Uint(v.Val()) } -// Uints converts and returns as []uint. -func (v *Var) Uints() []uint { - return gconv.Uints(v.Val()) -} - // Uint8 converts and returns as uint8. func (v *Var) Uint8() uint8 { return gconv.Uint8(v.Val()) @@ -171,44 +161,6 @@ func (v *Var) Float64() float64 { return gconv.Float64(v.Val()) } -// Floats converts and returns as []float64. -func (v *Var) Floats() []float64 { - return gconv.Floats(v.Val()) -} - -// Strings converts and returns as []string. -func (v *Var) Strings() []string { - return gconv.Strings(v.Val()) -} - -// Interfaces converts and returns as []interfaces{}. -func (v *Var) Interfaces() []interface{} { - return gconv.Interfaces(v.Val()) -} - -// Slice is alias of Interfaces. -func (v *Var) Slice() []interface{} { - return v.Interfaces() -} - -// Array is alias of Interfaces. -func (v *Var) Array() []interface{} { - return v.Interfaces() -} - -// Vars converts and returns as []Var. -func (v *Var) Vars() []*Var { - array := gconv.Interfaces(v.Val()) - if len(array) == 0 { - return nil - } - vars := make([]*Var, len(array)) - for k, v := range array { - vars[k] = New(v) - } - return vars -} - // Time converts and returns as time.Time. // The parameter specifies the format of the time string using gtime, // eg: Y-m-d H:i:s. diff --git a/container/gvar/gvar_slice.go b/container/gvar/gvar_slice.go new file mode 100644 index 000000000..e4c2f5272 --- /dev/null +++ b/container/gvar/gvar_slice.go @@ -0,0 +1,77 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gvar + +import "github.com/gogf/gf/util/gconv" + +// Ints converts and returns as []int. +func (v *Var) Ints() []int { + return gconv.Ints(v.Val()) +} + +// Int64s converts and returns as []int64. +func (v *Var) Int64s() []int64 { + return gconv.Int64s(v.Val()) +} + +// Uints converts and returns as []uint. +func (v *Var) Uints() []uint { + return gconv.Uints(v.Val()) +} + +// Uint64s converts and returns as []uint64. +func (v *Var) Uint64s() []uint64 { + return gconv.Uint64s(v.Val()) +} + +// Floats is alias of Float64s. +func (v *Var) Floats() []float64 { + return gconv.Floats(v.Val()) +} + +// Float32s converts and returns as []float32. +func (v *Var) Float32s() []float32 { + return gconv.Float32s(v.Val()) +} + +// Float64s converts and returns as []float64. +func (v *Var) Float64s() []float64 { + return gconv.Float64s(v.Val()) +} + +// Strings converts and returns as []string. +func (v *Var) Strings() []string { + return gconv.Strings(v.Val()) +} + +// Interfaces converts and returns as []interfaces{}. +func (v *Var) Interfaces() []interface{} { + return gconv.Interfaces(v.Val()) +} + +// Slice is alias of Interfaces. +func (v *Var) Slice() []interface{} { + return v.Interfaces() +} + +// Array is alias of Interfaces. +func (v *Var) Array() []interface{} { + return v.Interfaces() +} + +// Vars converts and returns as []Var. +func (v *Var) Vars() []*Var { + array := gconv.Interfaces(v.Val()) + if len(array) == 0 { + return nil + } + vars := make([]*Var, len(array)) + for k, v := range array { + vars[k] = New(v) + } + return vars +} diff --git a/container/gvar/gvar_z_unit_basic_test.go b/container/gvar/gvar_z_unit_basic_test.go index f3feaf3ed..d46ab188f 100644 --- a/container/gvar/gvar_z_unit_basic_test.go +++ b/container/gvar/gvar_z_unit_basic_test.go @@ -221,62 +221,6 @@ func Test_Float64(t *testing.T) { }) } -func Test_Ints(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var arr = []int{1, 2, 3, 4, 5} - objOne := gvar.New(arr, true) - t.Assert(objOne.Ints()[0], arr[0]) - }) -} -func Test_Floats(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var arr = []float64{1, 2, 3, 4, 5} - objOne := gvar.New(arr, true) - t.Assert(objOne.Floats()[0], arr[0]) - }) -} -func Test_Strings(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var arr = []string{"hello", "world"} - objOne := gvar.New(arr, true) - t.Assert(objOne.Strings()[0], arr[0]) - }) -} - -func Test_Interfaces(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var arr = []int{1, 2, 3, 4, 5} - objOne := gvar.New(arr, true) - t.Assert(objOne.Interfaces(), arr) - }) -} - -func Test_Slice(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var arr = []int{1, 2, 3, 4, 5} - objOne := gvar.New(arr, true) - t.Assert(objOne.Slice(), arr) - }) -} - -func Test_Array(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var arr = []int{1, 2, 3, 4, 5} - objOne := gvar.New(arr, false) - t.Assert(objOne.Array(), arr) - }) -} - -func Test_Vars(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var arr = []int{1, 2, 3, 4, 5} - objOne := gvar.New(arr, false) - t.Assert(len(objOne.Vars()), 5) - t.Assert(objOne.Vars()[0].Int(), 1) - t.Assert(objOne.Vars()[4].Int(), 5) - }) -} - func Test_Time(t *testing.T) { gtest.C(t, func(t *gtest.T) { var timeUnix int64 = 1556242660 diff --git a/container/gvar/gvar_z_unit_json_test.go b/container/gvar/gvar_z_unit_json_test.go index 0c02c80eb..53dc856f3 100644 --- a/container/gvar/gvar_z_unit_json_test.go +++ b/container/gvar/gvar_z_unit_json_test.go @@ -14,7 +14,7 @@ import ( "testing" ) -func Test_Json(t *testing.T) { +func TestVar_Json(t *testing.T) { // Marshal gtest.C(t, func(t *gtest.T) { s := "i love gf" diff --git a/container/gvar/gvar_z_unit_list_test.go b/container/gvar/gvar_z_unit_list_test.go index d62a4d24a..eb2cd2314 100644 --- a/container/gvar/gvar_z_unit_list_test.go +++ b/container/gvar/gvar_z_unit_list_test.go @@ -13,7 +13,7 @@ import ( "testing" ) -func Test_ListItemValues_Map(t *testing.T) { +func TestVar_ListItemValues_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { listMap := g.List{ g.Map{"id": 1, "score": 100}, @@ -34,7 +34,7 @@ func Test_ListItemValues_Map(t *testing.T) { }) } -func Test_ListItemValues_Struct(t *testing.T) { +func TestVar_ListItemValues_Struct(t *testing.T) { gtest.C(t, func(t *gtest.T) { type T struct { Id int @@ -78,7 +78,7 @@ func Test_ListItemValues_Struct(t *testing.T) { }) } -func Test_ListItemValuesUnique(t *testing.T) { +func TestVar_ListItemValuesUnique(t *testing.T) { gtest.C(t, func(t *gtest.T) { listMap := g.List{ g.Map{"id": 1, "score": 100}, diff --git a/container/gvar/gvar_z_unit_map_test.go b/container/gvar/gvar_z_unit_map_test.go index 30e6c7718..d0b96deb6 100644 --- a/container/gvar/gvar_z_unit_map_test.go +++ b/container/gvar/gvar_z_unit_map_test.go @@ -13,7 +13,7 @@ import ( "testing" ) -func Test_Map(t *testing.T) { +func TestVar_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { m := g.Map{ "k1": "v1", @@ -24,3 +24,58 @@ func Test_Map(t *testing.T) { t.Assert(objOne.Map()["k2"], m["k2"]) }) } + +func TestVar_MapToMap(t *testing.T) { + // map[int]int -> map[string]string + // empty original map. + gtest.C(t, func(t *gtest.T) { + m1 := g.MapIntInt{} + m2 := g.MapStrStr{} + t.Assert(gvar.New(m1).MapToMap(&m2), nil) + t.Assert(len(m1), len(m2)) + }) + // map[int]int -> map[string]string + gtest.C(t, func(t *gtest.T) { + m1 := g.MapIntInt{ + 1: 100, + 2: 200, + } + m2 := g.MapStrStr{} + t.Assert(gvar.New(m1).MapToMap(&m2), nil) + t.Assert(m2["1"], m1[1]) + t.Assert(m2["2"], m1[2]) + }) + // map[string]interface{} -> map[string]string + gtest.C(t, func(t *gtest.T) { + m1 := g.Map{ + "k1": "v1", + "k2": "v2", + } + m2 := g.MapStrStr{} + t.Assert(gvar.New(m1).MapToMap(&m2), nil) + t.Assert(m2["k1"], m1["k1"]) + t.Assert(m2["k2"], m1["k2"]) + }) + // map[string]string -> map[string]interface{} + gtest.C(t, func(t *gtest.T) { + m1 := g.MapStrStr{ + "k1": "v1", + "k2": "v2", + } + m2 := g.Map{} + t.Assert(gvar.New(m1).MapToMap(&m2), nil) + t.Assert(m2["k1"], m1["k1"]) + t.Assert(m2["k2"], m1["k2"]) + }) + // map[string]interface{} -> map[interface{}]interface{} + gtest.C(t, func(t *gtest.T) { + m1 := g.MapStrStr{ + "k1": "v1", + "k2": "v2", + } + m2 := g.MapAnyAny{} + t.Assert(gvar.New(m1).MapToMap(&m2), nil) + t.Assert(m2["k1"], m1["k1"]) + t.Assert(m2["k2"], m1["k2"]) + }) +} diff --git a/container/gvar/gvar_z_unit_maptomap_test.go b/container/gvar/gvar_z_unit_maptomap_test.go deleted file mode 100644 index 89350f1d7..000000000 --- a/container/gvar/gvar_z_unit_maptomap_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gvar_test - -import ( - "github.com/gogf/gf/container/gvar" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/test/gtest" - "testing" -) - -func Test_MapToMap(t *testing.T) { - // map[int]int -> map[string]string - // empty original map. - gtest.C(t, func(t *gtest.T) { - m1 := g.MapIntInt{} - m2 := g.MapStrStr{} - t.Assert(gvar.New(m1).MapToMap(&m2), nil) - t.Assert(len(m1), len(m2)) - }) - // map[int]int -> map[string]string - gtest.C(t, func(t *gtest.T) { - m1 := g.MapIntInt{ - 1: 100, - 2: 200, - } - m2 := g.MapStrStr{} - t.Assert(gvar.New(m1).MapToMap(&m2), nil) - t.Assert(m2["1"], m1[1]) - t.Assert(m2["2"], m1[2]) - }) - // map[string]interface{} -> map[string]string - gtest.C(t, func(t *gtest.T) { - m1 := g.Map{ - "k1": "v1", - "k2": "v2", - } - m2 := g.MapStrStr{} - t.Assert(gvar.New(m1).MapToMap(&m2), nil) - t.Assert(m2["k1"], m1["k1"]) - t.Assert(m2["k2"], m1["k2"]) - }) - // map[string]string -> map[string]interface{} - gtest.C(t, func(t *gtest.T) { - m1 := g.MapStrStr{ - "k1": "v1", - "k2": "v2", - } - m2 := g.Map{} - t.Assert(gvar.New(m1).MapToMap(&m2), nil) - t.Assert(m2["k1"], m1["k1"]) - t.Assert(m2["k2"], m1["k2"]) - }) - // map[string]interface{} -> map[interface{}]interface{} - gtest.C(t, func(t *gtest.T) { - m1 := g.MapStrStr{ - "k1": "v1", - "k2": "v2", - } - m2 := g.MapAnyAny{} - t.Assert(gvar.New(m1).MapToMap(&m2), nil) - t.Assert(m2["k1"], m1["k1"]) - t.Assert(m2["k2"], m1["k2"]) - }) -} diff --git a/container/gvar/gvar_z_unit_slice_test.go b/container/gvar/gvar_z_unit_slice_test.go new file mode 100644 index 000000000..585b17bbd --- /dev/null +++ b/container/gvar/gvar_z_unit_slice_test.go @@ -0,0 +1,111 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gvar_test + +import ( + "github.com/gogf/gf/container/gvar" + "github.com/gogf/gf/test/gtest" + "testing" +) + +func TestVar_Ints(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []int{1, 2, 3, 4, 5} + objOne := gvar.New(arr, true) + t.Assert(objOne.Ints()[0], arr[0]) + }) +} + +func TestVar_Uints(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []int{1, 2, 3, 4, 5} + objOne := gvar.New(arr, true) + t.Assert(objOne.Uints()[0], arr[0]) + }) +} + +func TestVar_Int64s(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []int{1, 2, 3, 4, 5} + objOne := gvar.New(arr, true) + t.Assert(objOne.Int64s()[0], arr[0]) + }) +} + +func TestVar_Uint64s(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []int{1, 2, 3, 4, 5} + objOne := gvar.New(arr, true) + t.Assert(objOne.Uint64s()[0], arr[0]) + }) +} + +func TestVar_Floats(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []float64{1, 2, 3, 4, 5} + objOne := gvar.New(arr, true) + t.Assert(objOne.Floats()[0], arr[0]) + }) +} + +func TestVar_Float32s(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []float32{1, 2, 3, 4, 5} + objOne := gvar.New(arr, true) + t.AssertEQ(objOne.Float32s(), arr) + }) +} + +func TestVar_Float64s(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []float64{1, 2, 3, 4, 5} + objOne := gvar.New(arr, true) + t.AssertEQ(objOne.Float64s(), arr) + }) +} + +func TestVar_Strings(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []string{"hello", "world"} + objOne := gvar.New(arr, true) + t.Assert(objOne.Strings()[0], arr[0]) + }) +} + +func TestVar_Interfaces(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []int{1, 2, 3, 4, 5} + objOne := gvar.New(arr, true) + t.Assert(objOne.Interfaces(), arr) + }) +} + +func TestVar_Slice(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []int{1, 2, 3, 4, 5} + objOne := gvar.New(arr, true) + t.Assert(objOne.Slice(), arr) + }) +} + +func TestVar_Array(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []int{1, 2, 3, 4, 5} + objOne := gvar.New(arr, false) + t.Assert(objOne.Array(), arr) + }) +} + +func TestVar_Vars(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var arr = []int{1, 2, 3, 4, 5} + objOne := gvar.New(arr, false) + t.Assert(len(objOne.Vars()), 5) + t.Assert(objOne.Vars()[0].Int(), 1) + t.Assert(objOne.Vars()[4].Int(), 5) + }) +} diff --git a/container/gvar/gvar_z_unit_struct_test.go b/container/gvar/gvar_z_unit_struct_test.go index e65c593a3..2164e5ad6 100644 --- a/container/gvar/gvar_z_unit_struct_test.go +++ b/container/gvar/gvar_z_unit_struct_test.go @@ -14,7 +14,7 @@ import ( "testing" ) -func Test_Struct(t *testing.T) { +func TestVar_Struct(t *testing.T) { gtest.C(t, func(t *gtest.T) { type StTest struct { Test int @@ -42,7 +42,7 @@ func Test_Struct(t *testing.T) { }) } -func Test_Var_Attribute_Struct(t *testing.T) { +func TestVar_Var_Attribute_Struct(t *testing.T) { gtest.C(t, func(t *gtest.T) { type User struct { Uid int diff --git a/encoding/gjson/gjson.go b/encoding/gjson/gjson.go index 2a16bb9bf..df30d9e75 100644 --- a/encoding/gjson/gjson.go +++ b/encoding/gjson/gjson.go @@ -19,7 +19,7 @@ import ( const ( // Separator char for hierarchical data access. - gDEFAULT_SPLIT_CHAR = '.' + defaultSplitChar = '.' ) // The customized JSON struct. @@ -30,6 +30,13 @@ type Json struct { vc bool // Violence Check(false in default), which is used to access data when the hierarchical data key contains separator char. } +// Option for Json object creating. +type Option struct { + Safe bool // Mark this object is for in concurrent-safe usage. + Tags string // Custom priority tags for decoding. + StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64. +} + // setValue sets to by . // Note: // 1. If value is nil and removed is true, means deleting this value; diff --git a/encoding/gjson/gjson_api.go b/encoding/gjson/gjson_api.go index 3f74ef6ea..51787f102 100644 --- a/encoding/gjson/gjson_api.go +++ b/encoding/gjson/gjson_api.go @@ -24,6 +24,11 @@ func (j *Json) Value() interface{} { return *(j.p) } +// Var returns the json value as *gvar.Var. +func (j *Json) Var() *gvar.Var { + return gvar.New(j.Value()) +} + // IsNil checks whether the value pointed by is nil. func (j *Json) IsNil() bool { j.mu.RLock() diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index b6a711f17..8759f2e71 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -42,15 +42,27 @@ func New(data interface{}, safe ...bool) *Json { // The parameter specifies whether using this Json object in concurrent-safe context, which // is false in default. func NewWithTag(data interface{}, tags string, safe ...bool) *Json { - j := (*Json)(nil) + option := Option{ + Tags: tags, + } + if len(safe) > 0 && safe[0] { + option.Safe = true + } + return NewWithOption(data, option) +} + +// NewWithOption creates a Json object with any variable type of , but should be a map +// or slice for data access reason, or it will make no sense. +func NewWithOption(data interface{}, option Option) *Json { + var j *Json switch data.(type) { case string, []byte: - if r, err := LoadContent(gconv.Bytes(data)); err == nil { + if r, err := loadContentWithOption(data, option); err == nil { j = r } else { j = &Json{ p: &data, - c: byte(gDEFAULT_SPLIT_CHAR), + c: byte(defaultSplitChar), vc: false, } } @@ -69,26 +81,26 @@ func NewWithTag(data interface{}, tags string, safe ...bool) *Json { i = gconv.Interfaces(data) j = &Json{ p: &i, - c: byte(gDEFAULT_SPLIT_CHAR), + c: byte(defaultSplitChar), vc: false, } case reflect.Map, reflect.Struct: i := interface{}(nil) - i = gconv.MapDeep(data, tags) + i = gconv.MapDeep(data, option.Tags) j = &Json{ p: &i, - c: byte(gDEFAULT_SPLIT_CHAR), + c: byte(defaultSplitChar), vc: false, } default: j = &Json{ p: &data, - c: byte(gDEFAULT_SPLIT_CHAR), + c: byte(defaultSplitChar), vc: false, } } } - j.mu = rwmutex.New(safe...) + j.mu = rwmutex.New(option.Safe) return j } @@ -99,42 +111,133 @@ func Load(path string, safe ...bool) (*Json, error) { } else { path = p } - return doLoadContent(gfile.Ext(path), gfile.GetBytesWithCache(path), safe...) + option := Option{} + if len(safe) > 0 && safe[0] { + option.Safe = true + } + return doLoadContentWithOption(gfile.Ext(path), gfile.GetBytesWithCache(path), option) } // LoadJson creates a Json object from given JSON format content. func LoadJson(data interface{}, safe ...bool) (*Json, error) { - return doLoadContent("json", gconv.Bytes(data), safe...) + option := Option{} + if len(safe) > 0 && safe[0] { + option.Safe = true + } + return doLoadContentWithOption("json", gconv.Bytes(data), option) } // LoadXml creates a Json object from given XML format content. func LoadXml(data interface{}, safe ...bool) (*Json, error) { - return doLoadContent("xml", gconv.Bytes(data), safe...) + option := Option{} + if len(safe) > 0 && safe[0] { + option.Safe = true + } + return doLoadContentWithOption("xml", gconv.Bytes(data), option) } // LoadIni creates a Json object from given INI format content. func LoadIni(data interface{}, safe ...bool) (*Json, error) { - return doLoadContent("ini", gconv.Bytes(data), safe...) + option := Option{} + if len(safe) > 0 && safe[0] { + option.Safe = true + } + return doLoadContentWithOption("ini", gconv.Bytes(data), option) } // LoadYaml creates a Json object from given YAML format content. func LoadYaml(data interface{}, safe ...bool) (*Json, error) { - return doLoadContent("yaml", gconv.Bytes(data), safe...) + option := Option{} + if len(safe) > 0 && safe[0] { + option.Safe = true + } + return doLoadContentWithOption("yaml", gconv.Bytes(data), option) } // LoadToml creates a Json object from given TOML format content. func LoadToml(data interface{}, safe ...bool) (*Json, error) { - return doLoadContent("toml", gconv.Bytes(data), safe...) + option := Option{} + if len(safe) > 0 && safe[0] { + option.Safe = true + } + return doLoadContentWithOption("toml", gconv.Bytes(data), option) +} + +// LoadContent creates a Json object from given content, it checks the data type of +// automatically, supporting data content type as follows: +// JSON, XML, INI, YAML and TOML. +func LoadContent(data interface{}, safe ...bool) (*Json, error) { + content := gconv.Bytes(data) + if len(content) == 0 { + return New(nil, safe...), nil + } + return LoadContentType(checkDataType(content), content, safe...) +} + +// LoadContentType creates a Json object from given type and content, +// supporting data content type as follows: +// JSON, XML, INI, YAML and TOML. +func LoadContentType(dataType string, data interface{}, safe ...bool) (*Json, error) { + content := gconv.Bytes(data) + if len(content) == 0 { + return New(nil, safe...), nil + } + //ignore UTF8-BOM + if content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF { + content = content[3:] + } + option := Option{} + if len(safe) > 0 && safe[0] { + option.Safe = true + } + return doLoadContentWithOption(dataType, content, option) +} + +// IsValidDataType checks and returns whether given a valid data type for loading. +func IsValidDataType(dataType string) bool { + if dataType == "" { + return false + } + if dataType[0] == '.' { + dataType = dataType[1:] + } + switch dataType { + case "json", "js", "xml", "yaml", "yml", "toml", "ini": + return true + } + return false +} + +func loadContentWithOption(data interface{}, option Option) (*Json, error) { + content := gconv.Bytes(data) + if len(content) == 0 { + return NewWithOption(nil, option), nil + } + return loadContentTypeWithOption(checkDataType(content), content, option) +} + +func loadContentTypeWithOption(dataType string, data interface{}, option Option) (*Json, error) { + content := gconv.Bytes(data) + if len(content) == 0 { + return NewWithOption(nil, option), nil + } + //ignore UTF8-BOM + if content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF { + content = content[3:] + } + return doLoadContentWithOption(dataType, content, option) } // doLoadContent creates a Json object from given content. // It supports data content type as follows: // JSON, XML, INI, YAML and TOML. -func doLoadContent(dataType string, data []byte, safe ...bool) (*Json, error) { - var err error - var result interface{} +func doLoadContentWithOption(dataType string, data []byte, option Option) (*Json, error) { + var ( + err error + result interface{} + ) if len(data) == 0 { - return New(nil, safe...), nil + return NewWithOption(nil, option), nil } if dataType == "" { dataType = checkDataType(data) @@ -167,10 +270,9 @@ func doLoadContent(dataType string, data []byte, safe ...bool) (*Json, error) { return nil, err } decoder := json.NewDecoder(bytes.NewReader(data)) - // Do not use number, it converts float64 to json.Number type, - // which actually a string type. It causes converting issue for other data formats, - // for example: yaml. - //decoder.UseNumber() + if option.StrNumber { + decoder.UseNumber() + } if err := decoder.Decode(&result); err != nil { return nil, err } @@ -178,48 +280,7 @@ func doLoadContent(dataType string, data []byte, safe ...bool) (*Json, error) { case string, []byte: return nil, fmt.Errorf(`json decoding failed for content: %s`, string(data)) } - return New(result, safe...), nil -} - -// LoadContent creates a Json object from given content, it checks the data type of -// automatically, supporting data content type as follows: -// JSON, XML, INI, YAML and TOML. -func LoadContent(data interface{}, safe ...bool) (*Json, error) { - content := gconv.Bytes(data) - if len(content) == 0 { - return New(nil, safe...), nil - } - return LoadContentType(checkDataType(content), content, safe...) -} - -// LoadContentType creates a Json object from given type and content, -// supporting data content type as follows: -// JSON, XML, INI, YAML and TOML. -func LoadContentType(dataType string, data interface{}, safe ...bool) (*Json, error) { - content := gconv.Bytes(data) - if len(content) == 0 { - return New(nil, safe...), nil - } - //ignore UTF8-BOM - if content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF { - content = content[3:] - } - return doLoadContent(dataType, content, safe...) -} - -// IsValidDataType checks and returns whether given a valid data type for loading. -func IsValidDataType(dataType string) bool { - if dataType == "" { - return false - } - if dataType[0] == '.' { - dataType = dataType[1:] - } - switch dataType { - case "json", "js", "xml", "yaml", "yml", "toml", "ini": - return true - } - return false + return NewWithOption(result, option), nil } // checkDataType automatically checks and returns the data type for . diff --git a/encoding/gjson/gjson_z_unit_basic_test.go b/encoding/gjson/gjson_z_unit_basic_test.go index 1c5244556..733adea8e 100644 --- a/encoding/gjson/gjson_z_unit_basic_test.go +++ b/encoding/gjson/gjson_z_unit_basic_test.go @@ -466,7 +466,20 @@ func Test_Basic(t *testing.T) { }) } -func Test_IsNil(t *testing.T) { +func TestJson_Var(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + data := []byte("[9223372036854775807, 9223372036854775806]") + array := gjson.New(data).Var().Array() + t.Assert(array, []uint64{9223372036854776000, 9223372036854776000}) + }) + gtest.C(t, func(t *gtest.T) { + data := []byte("[9223372036854775807, 9223372036854775806]") + array := gjson.NewWithOption(data, gjson.Option{StrNumber: true}).Var().Array() + t.Assert(array, []uint64{9223372036854775807, 9223372036854775806}) + }) +} + +func TestJson_IsNil(t *testing.T) { gtest.C(t, func(t *gtest.T) { j := gjson.New(nil) t.Assert(j.IsNil(), true) diff --git a/encoding/gjson/gjson_z_unit_new_test.go b/encoding/gjson/gjson_z_unit_new_test.go index c3051ce26..b4f911379 100644 --- a/encoding/gjson/gjson_z_unit_new_test.go +++ b/encoding/gjson/gjson_z_unit_new_test.go @@ -13,7 +13,7 @@ import ( "github.com/gogf/gf/test/gtest" ) -func Test_Load_NewWithTag(t *testing.T) { +func Test_NewWithTag(t *testing.T) { type User struct { Age int `xml:"age-xml" json:"age-json"` Name string `xml:"name-xml" json:"name-json"` @@ -48,7 +48,7 @@ func Test_Load_NewWithTag(t *testing.T) { }) } -func Test_Load_New_CustomStruct(t *testing.T) { +func Test_New_CustomStruct(t *testing.T) { type Base struct { Id int } @@ -70,7 +70,7 @@ func Test_Load_New_CustomStruct(t *testing.T) { }) } -func Test_Load_New_HierarchicalStruct(t *testing.T) { +func Test_New_HierarchicalStruct(t *testing.T) { gtest.C(t, func(t *gtest.T) { type Me struct { Name string `json:"name"` @@ -97,3 +97,16 @@ func Test_Load_New_HierarchicalStruct(t *testing.T) { t.Assert(j.MustToJsonString(), `{"children":[{"children":null,"name":"Bean"},{"children":null,"name":"Sam"}],"name":"john","score":100}`) }) } + +func Test_NewWithOption(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + data := []byte("[9223372036854775807, 9223372036854775806]") + array := gjson.New(data).Array() + t.Assert(array, []uint64{9223372036854776000, 9223372036854776000}) + }) + gtest.C(t, func(t *gtest.T) { + data := []byte("[9223372036854775807, 9223372036854775806]") + array := gjson.NewWithOption(data, gjson.Option{StrNumber: true}).Array() + t.Assert(array, []uint64{9223372036854775807, 9223372036854775806}) + }) +} diff --git a/encoding/gjson/gjson_z_unit_struct_test.go b/encoding/gjson/gjson_z_unit_struct_test.go index 046acc194..bd41a366b 100644 --- a/encoding/gjson/gjson_z_unit_struct_test.go +++ b/encoding/gjson/gjson_z_unit_struct_test.go @@ -76,7 +76,7 @@ func Test_GetScanDeep(t *testing.T) { }) } -func Test_ToScan(t *testing.T) { +func Test_Scan1(t *testing.T) { type User struct { Name string Score float64 @@ -84,7 +84,7 @@ func Test_ToScan(t *testing.T) { j := gjson.New(`[{"name":"john", "score":"100"},{"name":"smith", "score":"60"}]`) gtest.C(t, func(t *gtest.T) { var users []User - err := j.ToScan(&users) + err := j.Scan(&users) t.Assert(err, nil) t.Assert(users, []User{ { @@ -99,7 +99,7 @@ func Test_ToScan(t *testing.T) { }) } -func Test_ToScanDeep(t *testing.T) { +func Test_Scan2(t *testing.T) { type User struct { Name string Score float64 @@ -107,7 +107,7 @@ func Test_ToScanDeep(t *testing.T) { j := gjson.New(`[{"name":"john", "score":"100"},{"name":"smith", "score":"60"}]`) gtest.C(t, func(t *gtest.T) { var users []User - err := j.ToScanDeep(&users) + err := j.Scan(&users) t.Assert(err, nil) t.Assert(users, []User{ { @@ -122,7 +122,7 @@ func Test_ToScanDeep(t *testing.T) { }) } -func Test_ToStruct1(t *testing.T) { +func Test_Struct1(t *testing.T) { gtest.C(t, func(t *gtest.T) { type BaseInfoItem struct { IdCardNumber string `db:"id_card_number" json:"idCardNumber" field:"id_card_number"` @@ -198,12 +198,12 @@ func Test_ToStruct1(t *testing.T) { data := new(UserCollectionAddReq) j, err := gjson.LoadJson(jsonContent) t.Assert(err, nil) - err = j.ToStruct(data) + err = j.Struct(data) t.Assert(err, nil) }) } -func Test_ToStruct(t *testing.T) { +func Test_Struct(t *testing.T) { gtest.C(t, func(t *gtest.T) { type Item struct { Title string `json:"title"` @@ -231,7 +231,7 @@ func Test_ToStruct(t *testing.T) { t.Assert(j.GetBool("items"), false) t.Assert(j.GetArray("items"), nil) m := new(M) - err = j.ToStruct(m) + err = j.Struct(m) t.Assert(err, nil) t.AssertNE(m.Me, nil) t.Assert(m.Me["day"], "20009") @@ -239,7 +239,7 @@ func Test_ToStruct(t *testing.T) { }) } -func Test_ToStruct_Complicated(t *testing.T) { +func Test_Struct_Complicated(t *testing.T) { type CertInfo struct { UserRealName string `json:"userRealname,omitempty"` IdentType string `json:"identType,omitempty"` @@ -290,7 +290,7 @@ func Test_ToStruct_Complicated(t *testing.T) { j, err := gjson.LoadContent(jsonContent) t.Assert(err, nil) var response = new(Response) - err = j.ToStruct(response) + err = j.Struct(response) t.Assert(err, nil) t.Assert(len(response.CertList), 3) t.Assert(response.CertList[0].CertID, 2023313) From e1418a2caac699f40c2eba3c47f18e7c4037ea7f Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 19 Jan 2021 16:44:37 +0800 Subject: [PATCH 086/492] fix blocking issue in roration feature for package glog --- os/glog/glog_logger_rotate.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/os/glog/glog_logger_rotate.go b/os/glog/glog_logger_rotate.go index 0b1f785b4..e508b8006 100644 --- a/os/glog/glog_logger_rotate.go +++ b/os/glog/glog_logger_rotate.go @@ -62,8 +62,12 @@ func (l *Logger) doRotateFile(filePath string) error { now = gtime.Now() micro = now.Microsecond() % 1000 ) - for micro < 100 { - micro *= 10 + if micro == 0 { + micro = 101 + } else { + for micro < 100 { + micro *= 10 + } } newFilePath = gfile.Join( dirPath, From fe80881ec9e9f81f029f8fb53bb8584241b275b2 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 19 Jan 2021 19:18:39 +0800 Subject: [PATCH 087/492] improve package ghttp --- net/ghttp/ghttp_request.go | 4 ++-- net/ghttp/ghttp_request_middleware.go | 6 +++--- net/ghttp/ghttp_server_pprof.go | 20 +++++++++++++++++--- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index 3c0e244f6..f340f5859 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -33,7 +33,7 @@ type Request struct { Router *Router // Matched Router for this request. Note that it's not available in HOOK handler. EnterTime int64 // Request starting time in microseconds. LeaveTime int64 // Request ending time in microseconds. - Middleware *Middleware // Middleware manager. + Middleware *middleware // Middleware manager. StaticFile *StaticFile // Static file object for static file serving. context context.Context // Custom context for internal usage purpose. handlers []*handlerParsedItem // All matched handlers containing handler, hook and middleware for this request. @@ -75,7 +75,7 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { request.Cookie = GetCookie(request) request.Session = s.sessionManager.New(request.GetSessionId()) request.Response.Request = request - request.Middleware = &Middleware{ + request.Middleware = &middleware{ request: request, } // Custom session id creating function. diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index 6ddcde54e..caa0e0bb0 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -14,8 +14,8 @@ import ( "github.com/gogf/gf/util/gutil" ) -// Middleware is the plugin for request workflow management. -type Middleware struct { +// middleware is the plugin for request workflow management. +type middleware struct { served bool // Is the request served, which is used for checking response status 404. request *Request // The request object pointer. handlerIndex int // Index number for executing sequence purpose for handler items. @@ -24,7 +24,7 @@ type Middleware struct { // Next calls the next workflow handler. // It's an important function controlling the workflow of the server request execution. -func (m *Middleware) Next() { +func (m *middleware) Next() { var item *handlerParsedItem var loop = true for loop { diff --git a/net/ghttp/ghttp_server_pprof.go b/net/ghttp/ghttp_server_pprof.go index bcd8588bf..dc63a6a74 100644 --- a/net/ghttp/ghttp_server_pprof.go +++ b/net/ghttp/ghttp_server_pprof.go @@ -18,9 +18,18 @@ import ( type utilPProf struct{} const ( - defaultPProfPattern = "/debug/pprof" + defaultPProfServerName = "pprof-server" + defaultPProfPattern = "/debug/pprof" ) +// StartPProfServer starts and runs a new server for pprof. +func StartPProfServer(port int, pattern ...string) { + s := GetServer(defaultPProfServerName) + s.EnablePProf() + s.SetPort(port) + s.Run() +} + // EnablePProf enables PProf feature for server. func (s *Server) EnablePProf(pattern ...string) { s.Domain(defaultDomainName).EnablePProf(pattern...) @@ -56,13 +65,18 @@ func (p *utilPProf) Index(r *Request) { buffer, _ := gview.ParseContent(` - gf ghttp pprof + GoFrame PProf {{$uri := .uri}} profiles:
- {{range .profiles}} + + + + {{end}}
{{.Count}}{{.Name}}{{end}} + {{range .profiles}} +
{{.Count}}{{.Name}}

full goroutine stack dump
From 5f4293a803fa87e5253b01c729841481027d528f Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 19 Jan 2021 19:33:21 +0800 Subject: [PATCH 088/492] make unnecessaryly exported resource private for package ghttp --- net/ghttp/ghttp.go | 2 +- net/ghttp/ghttp_client_api.go | 40 +++++++++++++++++++++++++++++++ net/ghttp/ghttp_request.go | 6 ++--- net/ghttp/ghttp_response.go | 12 +++++----- net/ghttp/ghttp_response_cors.go | 2 +- net/ghttp/ghttp_server.go | 2 +- net/ghttp/ghttp_server_handler.go | 14 +++++------ 7 files changed, 59 insertions(+), 19 deletions(-) diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index 41d751fab..ec486b0ef 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -117,7 +117,7 @@ const ( HookAfterOutput = "HOOK_AFTER_OUTPUT" ServerStatusStopped = 0 ServerStatusRunning = 1 - SupportedHttpMethods = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE" + supportedHttpMethods = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE" defaultServerName = "default" defaultDomainName = "default" defaultMethod = "ALL" diff --git a/net/ghttp/ghttp_client_api.go b/net/ghttp/ghttp_client_api.go index 335902e6f..737f02f61 100644 --- a/net/ghttp/ghttp_client_api.go +++ b/net/ghttp/ghttp_client_api.go @@ -10,234 +10,273 @@ import "github.com/gogf/gf/container/gvar" // Get is a convenience method for sending GET request. // NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Get or NewClient().Get instead. func Get(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("GET", url, data...) } // Put is a convenience method for sending PUT request. // NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Put or NewClient().Put instead. func Put(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("PUT", url, data...) } // Post is a convenience method for sending POST request. // NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Post or NewClient().Post instead. func Post(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("POST", url, data...) } // Delete is a convenience method for sending DELETE request. // NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Delete or NewClient().Delete instead. func Delete(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("DELETE", url, data...) } // Head is a convenience method for sending HEAD request. // NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Head or NewClient().Head instead. func Head(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("HEAD", url, data...) } // Patch is a convenience method for sending PATCH request. // NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Patch or NewClient().Patch instead. func Patch(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("PATCH", url, data...) } // Connect is a convenience method for sending CONNECT request. // NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Connect or NewClient().Connect instead. func Connect(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("CONNECT", url, data...) } // Options is a convenience method for sending OPTIONS request. // NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Options or NewClient().Options instead. func Options(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("OPTIONS", url, data...) } // Trace is a convenience method for sending TRACE request. // NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Trace or NewClient().Trace instead. func Trace(url string, data ...interface{}) (*ClientResponse, error) { return DoRequest("TRACE", url, data...) } // DoRequest is a convenience method for sending custom http method request. // NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().DoRequest or NewClient().DoRequest instead. func DoRequest(method, url string, data ...interface{}) (*ClientResponse, error) { return NewClient().DoRequest(method, url, data...) } // GetContent is a convenience method for sending GET request, which retrieves and returns // the result content and automatically closes response object. +// Deprecated, please use g.Client().GetContent or NewClient().GetContent instead. func GetContent(url string, data ...interface{}) string { return RequestContent("GET", url, data...) } // PutContent is a convenience method for sending PUT request, which retrieves and returns // the result content and automatically closes response object. +// Deprecated, please use g.Client().PutContent or NewClient().PutContent instead. func PutContent(url string, data ...interface{}) string { return RequestContent("PUT", url, data...) } // PostContent is a convenience method for sending POST request, which retrieves and returns // the result content and automatically closes response object. +// Deprecated, please use g.Client().PostContent or NewClient().PostContent instead. func PostContent(url string, data ...interface{}) string { return RequestContent("POST", url, data...) } // DeleteContent is a convenience method for sending DELETE request, which retrieves and returns // the result content and automatically closes response object. +// Deprecated, please use g.Client().DeleteContent or NewClient().DeleteContent instead. func DeleteContent(url string, data ...interface{}) string { return RequestContent("DELETE", url, data...) } // HeadContent is a convenience method for sending HEAD request, which retrieves and returns // the result content and automatically closes response object. +// Deprecated, please use g.Client().HeadContent or NewClient().HeadContent instead. func HeadContent(url string, data ...interface{}) string { return RequestContent("HEAD", url, data...) } // PatchContent is a convenience method for sending PATCH request, which retrieves and returns // the result content and automatically closes response object. +// Deprecated, please use g.Client().PatchContent or NewClient().PatchContent instead. func PatchContent(url string, data ...interface{}) string { return RequestContent("PATCH", url, data...) } // ConnectContent is a convenience method for sending CONNECT request, which retrieves and returns // the result content and automatically closes response object. +// Deprecated, please use g.Client().ConnectContent or NewClient().ConnectContent instead. func ConnectContent(url string, data ...interface{}) string { return RequestContent("CONNECT", url, data...) } // OptionsContent is a convenience method for sending OPTIONS request, which retrieves and returns // the result content and automatically closes response object. +// Deprecated, please use g.Client().OptionsContent or NewClient().OptionsContent instead. func OptionsContent(url string, data ...interface{}) string { return RequestContent("OPTIONS", url, data...) } // TraceContent is a convenience method for sending TRACE request, which retrieves and returns // the result content and automatically closes response object. +// Deprecated, please use g.Client().TraceContent or NewClient().TraceContent instead. func TraceContent(url string, data ...interface{}) string { return RequestContent("TRACE", url, data...) } // RequestContent is a convenience method for sending custom http method request, which // retrieves and returns the result content and automatically closes response object. +// Deprecated, please use g.Client().RequestContent or NewClient().RequestContent instead. func RequestContent(method string, url string, data ...interface{}) string { return NewClient().RequestContent(method, url, data...) } // GetBytes is a convenience method for sending GET request, which retrieves and returns // the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().GetBytes or NewClient().GetBytes instead. func GetBytes(url string, data ...interface{}) []byte { return RequestBytes("GET", url, data...) } // PutBytes is a convenience method for sending PUT request, which retrieves and returns // the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().PutBytes or NewClient().PutBytes instead. func PutBytes(url string, data ...interface{}) []byte { return RequestBytes("PUT", url, data...) } // PostBytes is a convenience method for sending POST request, which retrieves and returns // the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().PostBytes or NewClient().PostBytes instead. func PostBytes(url string, data ...interface{}) []byte { return RequestBytes("POST", url, data...) } // DeleteBytes is a convenience method for sending DELETE request, which retrieves and returns // the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().DeleteBytes or NewClient().DeleteBytes instead. func DeleteBytes(url string, data ...interface{}) []byte { return RequestBytes("DELETE", url, data...) } // HeadBytes is a convenience method for sending HEAD request, which retrieves and returns // the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().HeadBytes or NewClient().HeadBytes instead. func HeadBytes(url string, data ...interface{}) []byte { return RequestBytes("HEAD", url, data...) } // PatchBytes is a convenience method for sending PATCH request, which retrieves and returns // the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().PatchBytes or NewClient().PatchBytes instead. func PatchBytes(url string, data ...interface{}) []byte { return RequestBytes("PATCH", url, data...) } // ConnectBytes is a convenience method for sending CONNECT request, which retrieves and returns // the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().ConnectBytes or NewClient().ConnectBytes instead. func ConnectBytes(url string, data ...interface{}) []byte { return RequestBytes("CONNECT", url, data...) } // OptionsBytes is a convenience method for sending OPTIONS request, which retrieves and returns // the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().OptionsBytes or NewClient().OptionsBytes instead. func OptionsBytes(url string, data ...interface{}) []byte { return RequestBytes("OPTIONS", url, data...) } // TraceBytes is a convenience method for sending TRACE request, which retrieves and returns // the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().TraceBytes or NewClient().TraceBytes instead. func TraceBytes(url string, data ...interface{}) []byte { return RequestBytes("TRACE", url, data...) } // RequestBytes is a convenience method for sending custom http method request, which // retrieves and returns the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().RequestBytes or NewClient().RequestBytes instead. func RequestBytes(method string, url string, data ...interface{}) []byte { return NewClient().RequestBytes(method, url, data...) } // GetVar sends a GET request, retrieves and converts the result content to specified pointer. // The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().GetVar or NewClient().GetVar instead. func GetVar(url string, data ...interface{}) *gvar.Var { return RequestVar("GET", url, data...) } // PutVar sends a PUT request, retrieves and converts the result content to specified pointer. // The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().PutVar or NewClient().PutVar instead. func PutVar(url string, data ...interface{}) *gvar.Var { return RequestVar("PUT", url, data...) } // PostVar sends a POST request, retrieves and converts the result content to specified pointer. // The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().PostVar or NewClient().PostVar instead. func PostVar(url string, data ...interface{}) *gvar.Var { return RequestVar("POST", url, data...) } // DeleteVar sends a DELETE request, retrieves and converts the result content to specified pointer. // The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().DeleteVar or NewClient().DeleteVar instead. func DeleteVar(url string, data ...interface{}) *gvar.Var { return RequestVar("DELETE", url, data...) } // HeadVar sends a HEAD request, retrieves and converts the result content to specified pointer. // The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().HeadVar or NewClient().HeadVar instead. func HeadVar(url string, data ...interface{}) *gvar.Var { return RequestVar("HEAD", url, data...) } // PatchVar sends a PATCH request, retrieves and converts the result content to specified pointer. // The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().PatchVar or NewClient().PatchVar instead. func PatchVar(url string, data ...interface{}) *gvar.Var { return RequestVar("PATCH", url, data...) } // ConnectVar sends a CONNECT request, retrieves and converts the result content to specified pointer. // The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().ConnectVar or NewClient().ConnectVar instead. func ConnectVar(url string, data ...interface{}) *gvar.Var { return RequestVar("CONNECT", url, data...) } // OptionsVar sends a OPTIONS request, retrieves and converts the result content to specified pointer. // The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().OptionsVar or NewClient().OptionsVar instead. func OptionsVar(url string, data ...interface{}) *gvar.Var { return RequestVar("OPTIONS", url, data...) } // TraceVar sends a TRACE request, retrieves and converts the result content to specified pointer. // The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().TraceVar or NewClient().TraceVar instead. func TraceVar(url string, data ...interface{}) *gvar.Var { return RequestVar("TRACE", url, data...) } @@ -245,6 +284,7 @@ func TraceVar(url string, data ...interface{}) *gvar.Var { // RequestVar sends request using given HTTP method and data, retrieves converts the result // to specified pointer. It reads and closes the response object internally automatically. // The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().RequestVar or NewClient().RequestVar instead. func RequestVar(method string, url string, data ...interface{}) *gvar.Var { response, err := DoRequest(method, url, data...) if err != nil { diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index f340f5859..a07e115b5 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -34,7 +34,7 @@ type Request struct { EnterTime int64 // Request starting time in microseconds. LeaveTime int64 // Request ending time in microseconds. Middleware *middleware // Middleware manager. - StaticFile *StaticFile // Static file object for static file serving. + StaticFile *staticFile // Static file object for static file serving. context context.Context // Custom context for internal usage purpose. handlers []*handlerParsedItem // All matched handlers containing handler, hook and middleware for this request. hasHookHandler bool // A bool marking whether there's hook handler in the handlers for performance purpose. @@ -57,8 +57,8 @@ type Request struct { viewParams gview.Params // Custom template view variables for this response. } -// StaticFile is the file struct for static file service. -type StaticFile struct { +// staticFile is the file struct for static file service. +type staticFile struct { File *gres.File // Resource file object. Path string // File path. IsDir bool // Is directory. diff --git a/net/ghttp/ghttp_response.go b/net/ghttp/ghttp_response.go index cc8c11383..a33613957 100644 --- a/net/ghttp/ghttp_response.go +++ b/net/ghttp/ghttp_response.go @@ -41,9 +41,9 @@ func newResponse(s *Server, w http.ResponseWriter) *Response { // ServeFile serves the file to the response. func (r *Response) ServeFile(path string, allowIndex ...bool) { - serveFile := (*StaticFile)(nil) + serveFile := (*staticFile)(nil) if file := gres.Get(path); file != nil { - serveFile = &StaticFile{ + serveFile = &staticFile{ File: file, IsDir: file.FileInfo().IsDir(), } @@ -53,20 +53,20 @@ func (r *Response) ServeFile(path string, allowIndex ...bool) { r.WriteStatus(http.StatusNotFound) return } - serveFile = &StaticFile{Path: path} + serveFile = &staticFile{Path: path} } r.Server.serveFile(r.Request, serveFile, allowIndex...) } // ServeFileDownload serves file downloading to the response. func (r *Response) ServeFileDownload(path string, name ...string) { - serveFile := (*StaticFile)(nil) + serveFile := (*staticFile)(nil) downloadName := "" if len(name) > 0 { downloadName = name[0] } if file := gres.Get(path); file != nil { - serveFile = &StaticFile{ + serveFile = &staticFile{ File: file, IsDir: file.FileInfo().IsDir(), } @@ -79,7 +79,7 @@ func (r *Response) ServeFileDownload(path string, name ...string) { r.WriteStatus(http.StatusNotFound) return } - serveFile = &StaticFile{Path: path} + serveFile = &staticFile{Path: path} if downloadName == "" { downloadName = gfile.Basename(path) } diff --git a/net/ghttp/ghttp_response_cors.go b/net/ghttp/ghttp_response_cors.go index 8e311ec2d..32206ed7b 100644 --- a/net/ghttp/ghttp_response_cors.go +++ b/net/ghttp/ghttp_response_cors.go @@ -45,7 +45,7 @@ func init() { func (r *Response) DefaultCORSOptions() CORSOptions { options := CORSOptions{ AllowOrigin: "*", - AllowMethods: SupportedHttpMethods, + AllowMethods: supportedHttpMethods, AllowCredentials: "true", AllowHeaders: defaultAllowHeaders, MaxAge: 3628800, diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 0fa1e3445..dc4b01ead 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -34,7 +34,7 @@ import ( func init() { // Initialize the methods map. - for _, v := range strings.Split(SupportedHttpMethods, ",") { + for _, v := range strings.Split(supportedHttpMethods, ",") { methodsMap[v] = struct{}{} } } diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index 8c7e24854..5b0331ad4 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -186,7 +186,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // searchStaticFile searches the file with given URI. // It returns a file struct specifying the file information. -func (s *Server) searchStaticFile(uri string) *StaticFile { +func (s *Server) searchStaticFile(uri string) *staticFile { var file *gres.File var path string var dir bool @@ -200,14 +200,14 @@ func (s *Server) searchStaticFile(uri string) *StaticFile { } file = gres.GetWithIndex(item.path+uri[len(item.prefix):], s.config.IndexFiles) if file != nil { - return &StaticFile{ + return &staticFile{ File: file, IsDir: file.FileInfo().IsDir(), } } path, dir = gspath.Search(item.path, uri[len(item.prefix):], s.config.IndexFiles...) if path != "" { - return &StaticFile{ + return &staticFile{ Path: path, IsDir: dir, } @@ -221,13 +221,13 @@ func (s *Server) searchStaticFile(uri string) *StaticFile { for _, p := range s.config.SearchPaths { file = gres.GetWithIndex(p+uri, s.config.IndexFiles) if file != nil { - return &StaticFile{ + return &staticFile{ File: file, IsDir: file.FileInfo().IsDir(), } } if path, dir = gspath.Search(p, uri, s.config.IndexFiles...); path != "" { - return &StaticFile{ + return &staticFile{ Path: path, IsDir: dir, } @@ -237,7 +237,7 @@ func (s *Server) searchStaticFile(uri string) *StaticFile { // Lastly search the resource manager. if len(s.config.StaticPaths) == 0 && len(s.config.SearchPaths) == 0 { if file = gres.GetWithIndex(uri, s.config.IndexFiles); file != nil { - return &StaticFile{ + return &staticFile{ File: file, IsDir: file.FileInfo().IsDir(), } @@ -248,7 +248,7 @@ func (s *Server) searchStaticFile(uri string) *StaticFile { // serveFile serves the static file for client. // The optional parameter specifies if allowing directory listing if is directory. -func (s *Server) serveFile(r *Request, f *StaticFile, allowIndex ...bool) { +func (s *Server) serveFile(r *Request, f *staticFile, allowIndex ...bool) { // Use resource file from memory. if f.File != nil { if f.IsDir { From 333e5b27aacab2b73efa4a4d6db09c727a28ff60 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 19 Jan 2021 21:08:01 +0800 Subject: [PATCH 089/492] add more unit testing case for package gdb --- database/gdb/gdb_z_mysql_model_test.go | 59 ++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index a462f3d4b..1528571b3 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -642,6 +642,65 @@ func Test_Model_All(t *testing.T) { }) } +func Test_Model_Fields(t *testing.T) { + tableName1 := createInitTable() + defer dropTable(tableName1) + + tableName2 := "user_" + gtime.Now().TimestampNanoStr() + if _, err := db.Exec(fmt.Sprintf(` + CREATE TABLE %s ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + name varchar(45) NULL, + age int(10) unsigned, + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableName2, + )); err != nil { + gtest.Assert(err, nil) + } + defer dropTable(tableName2) + + r, err := db.Insert(tableName2, g.Map{ + "id": 1, + "name": "table2_1", + "age": 18, + }) + gtest.Assert(err, nil) + n, _ := r.RowsAffected() + gtest.Assert(n, 1) + + gtest.C(t, func(t *gtest.T) { + all, err := db.Table(tableName1).As("u").Fields("u.passport,u.id").Where("u.id<2").All() + t.Assert(err, nil) + t.Assert(len(all), 1) + t.Assert(len(all[0]), 2) + }) + gtest.C(t, func(t *gtest.T) { + all, err := db.Table(tableName1).As("u1"). + LeftJoin(tableName1, "u2", "u2.id=u1.id"). + Fields("u1.passport,u1.id,u2.id AS u2id"). + Where("u1.id<2"). + All() + t.Assert(err, nil) + t.Assert(len(all), 1) + t.Assert(len(all[0]), 3) + }) + gtest.C(t, func(t *gtest.T) { + all, err := db.Table(tableName1).As("u1"). + LeftJoin(tableName2, "u2", "u2.id=u1.id"). + Fields("u1.passport,u1.id,u2.name,u2.age"). + Where("u1.id<2"). + All() + t.Assert(err, nil) + t.Assert(len(all), 1) + t.Assert(len(all[0]), 4) + t.Assert(all[0]["id"], 1) + t.Assert(all[0]["age"], 18) + t.Assert(all[0]["name"], "table2_1") + t.Assert(all[0]["passport"], "user_1") + }) +} + func Test_Model_FindAll(t *testing.T) { table := createInitTable() defer dropTable(table) From a304ca8f5b78d1493886b78497dc2baa5fa3b1b0 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 20 Jan 2021 00:18:29 +0800 Subject: [PATCH 090/492] improve unit testing case for package gcron --- os/gcron/gcron_unit_1_test.go | 2 +- text/gstr/gstr_case.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/os/gcron/gcron_unit_1_test.go b/os/gcron/gcron_unit_1_test.go index 4e408e2a3..5d471c8c7 100644 --- a/os/gcron/gcron_unit_1_test.go +++ b/os/gcron/gcron_unit_1_test.go @@ -41,7 +41,7 @@ func TestCron_Add_Close(t *testing.T) { t.Assert(cron.Size(), 3) time.Sleep(1200 * time.Millisecond) t.Assert(array.Len(), 2) - time.Sleep(1200 * time.Millisecond) + time.Sleep(1500 * time.Millisecond) t.Assert(array.Len(), 5) cron.Close() time.Sleep(1200 * time.Millisecond) diff --git a/text/gstr/gstr_case.go b/text/gstr/gstr_case.go index f8f6aa3e0..a720beea0 100644 --- a/text/gstr/gstr_case.go +++ b/text/gstr/gstr_case.go @@ -76,7 +76,7 @@ func SnakeScreamingCase(s string) string { // CaseSnakeScreaming converts a string to SNAKE_CASE_SCREAMING. func CaseSnakeScreaming(s string) string { - return DelimitedScreamingCase(s, '_', true) + return CaseDelimitedScreaming(s, '_', true) } // SnakeFirstUpperCase converts a string from RGBCodeMd5 to rgb_code_md5. From 59285709a65e8f00d863232ec9e983a7ea2799fd Mon Sep 17 00:00:00 2001 From: jflyfox Date: Wed, 20 Jan 2021 13:09:59 +0800 Subject: [PATCH 091/492] fix issue in filds filter in join statements for package gdb --- database/gdb/gdb.go | 5 ++ database/gdb/gdb_func.go | 12 +++- database/gdb/gdb_model_utility.go | 4 +- database/gdb/gdb_z_init_test.go | 2 - database/gdb/gdb_z_mysql_model_test.go | 85 ++++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 6 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 898624713..0212a0999 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -271,6 +271,11 @@ var ( // which is a regular field name of table. regularFieldNameRegPattern = `^[\w\.\-\_]+$` + // regularFieldNameWithoutDotRegPattern is similar to regularFieldNameRegPattern but not allows '.'. + // Note that, although some databases allow char '.' in the field name, but it here does not allow '.' + // in the field name as it conflicts with "db.table.field" pattern in SOME situations. + regularFieldNameWithoutDotRegPattern = `^[\w\-\_]+$` + // internalCache is the memory cache for internal usage. internalCache = gcache.New() diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 5e7c9b7bf..2a8878a33 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -467,14 +467,20 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) ( newWhere = db.QuoteString(newWhere) if len(newArgs) > 0 { if utils.IsArray(newArgs[0]) { - // Eg: Where("id", []int{1,2,3}) + // Eg: + // Where("id", []int{1,2,3}) + // Where("user.id", []int{1,2,3}) newWhere += " IN (?)" } else if empty.IsNil(newArgs[0]) { - // Eg: Where("id", nil) + // Eg: + // Where("id", nil) + // Where("user.id", nil) newWhere += " IS NULL" newArgs = nil } else { - // Eg: Where/And/Or("uid", 1) + // Eg: + // Where/And/Or("uid", 1) + // Where/And/Or("user.uid", 1) newWhere += "=?" } } diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 9ec5f74eb..17a484025 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -44,10 +44,12 @@ func (m *Model) mappingAndFilterToTableFields(fields []string) []string { } for _, field := range inputFieldsArray { if _, ok := fieldsKeyMap[field]; !ok { - if !gregex.IsMatchString(regularFieldNameRegPattern, field) { + if !gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, field) { + // Eg: user.id, user.name outputFieldsArray = append(outputFieldsArray, field) continue } else { + // Eg: id, name if foundKey, _ := gutil.MapPossibleItemByKey(fieldsKeyMap, field); foundKey != "" { outputFieldsArray = append(outputFieldsArray, foundKey) } diff --git a/database/gdb/gdb_z_init_test.go b/database/gdb/gdb_z_init_test.go index 44ad2167e..388c410e6 100644 --- a/database/gdb/gdb_z_init_test.go +++ b/database/gdb/gdb_z_init_test.go @@ -72,7 +72,6 @@ func init() { gtest.Error(err) } db.SetSchema(SCHEMA1) - createTable(TABLE) // Prefix db. if r, err := gdb.New("prefix"); err != nil { @@ -87,7 +86,6 @@ func init() { gtest.Error(err) } dbPrefix.SetSchema(SCHEMA1) - createTable(TABLE) } func createTable(table ...string) string { diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 1528571b3..297eb417c 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -3236,3 +3236,88 @@ func Test_Model_Fields_Map_Struct(t *testing.T) { t.Assert(a.XXX_TYPE, 0) }) } + +func Test_Model_Fields_AutoFilterInJoinStatement(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var err error + table1 := "user" + table2 := "score" + table3 := "info" + if _, err := db.Exec(fmt.Sprintf(` + CREATE TABLE IF NOT EXISTS %s ( + id int(11) NOT NULL AUTO_INCREMENT, + name varchar(500) NOT NULL DEFAULT '', + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; + `, table1, + )); err != nil { + t.Assert(err, nil) + } + defer dropTable(table1) + _, err = db.Table(table1).Insert(g.Map{ + "id": 1, + "name": "john", + }) + t.Assert(err, nil) + + if _, err := db.Exec(fmt.Sprintf(` + CREATE TABLE IF NOT EXISTS %s ( + id int(11) NOT NULL AUTO_INCREMENT, + user_id int(11) NOT NULL DEFAULT 0, + number varchar(500) NOT NULL DEFAULT '', + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; + `, table2, + )); err != nil { + t.Assert(err, nil) + } + defer dropTable(table2) + _, err = db.Table(table2).Insert(g.Map{ + "id": 1, + "user_id": 1, + "number": "n", + }) + t.Assert(err, nil) + + if _, err := db.Exec(fmt.Sprintf(` + CREATE TABLE IF NOT EXISTS %s ( + id int(11) NOT NULL AUTO_INCREMENT, + user_id int(11) NOT NULL DEFAULT 0, + description varchar(500) NOT NULL DEFAULT '', + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; + `, table3, + )); err != nil { + t.Assert(err, nil) + } + defer dropTable(table3) + _, err = db.Table(table3).Insert(g.Map{ + "id": 1, + "user_id": 1, + "description": "brief", + }) + t.Assert(err, nil) + + one, err := db.Table("user"). + Where("user.id", 1). + Fields("score.number,user.name"). + LeftJoin("score", "user.id=score.user_id"). + LeftJoin("info", "info.id=info.user_id"). + Order("user.id asc"). + One() + t.Assert(err, nil) + t.Assert(len(one), 2) + t.Assert(one["name"].String(), "john") + t.Assert(one["number"].String(), "n") + + one, err = db.Table("user"). + LeftJoin("score", "user.id=score.user_id"). + LeftJoin("info", "info.id=info.user_id"). + Fields("score.number,user.name"). + One() + t.Assert(err, nil) + t.Assert(len(one), 2) + t.Assert(one["name"].String(), "john") + t.Assert(one["number"].String(), "n") + }) +} From b38a4610eb24b35e61ff53f04bd183d26d90030e Mon Sep 17 00:00:00 2001 From: jflyfox Date: Wed, 20 Jan 2021 14:19:14 +0800 Subject: [PATCH 092/492] improve unit testing case for package gtimer --- os/gtimer/gtimer_z_unit_api_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/os/gtimer/gtimer_z_unit_api_test.go b/os/gtimer/gtimer_z_unit_api_test.go index 0ed3ec877..debc25d9c 100644 --- a/os/gtimer/gtimer_z_unit_api_test.go +++ b/os/gtimer/gtimer_z_unit_api_test.go @@ -102,7 +102,7 @@ func TestDelayAddEntry(t *testing.T) { func TestDelayAddSingleton(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := garray.New(true) - gtimer.DelayAddSingleton(200*time.Millisecond, 200*time.Millisecond, func() { + gtimer.DelayAddSingleton(500*time.Millisecond, 500*time.Millisecond, func() { array.Append(1) time.Sleep(10000 * time.Millisecond) }) @@ -129,12 +129,12 @@ func TestDelayAddOnce(t *testing.T) { func TestDelayAddTimes(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := garray.New(true) - gtimer.DelayAddTimes(200*time.Millisecond, 200*time.Millisecond, 2, func() { + gtimer.DelayAddTimes(500*time.Millisecond, 500*time.Millisecond, 2, func() { array.Append(1) }) time.Sleep(300 * time.Millisecond) t.Assert(array.Len(), 0) - time.Sleep(1000 * time.Millisecond) + time.Sleep(1500 * time.Millisecond) t.Assert(array.Len(), 2) }) } From 0fb32b63e74226417c6c92429a19b4486b18c502 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 21 Jan 2021 21:14:17 +0800 Subject: [PATCH 093/492] add signal handler feature for package gproc; improve package ghttp/g for signal listening feature --- database/gdb/gdb_type_result_scanlist.go | 14 ++-- frame/g/g_func.go | 6 +- net/ghttp/ghttp_server.go | 4 +- net/ghttp/ghttp_server_admin_unix.go | 50 ++++---------- net/ghttp/ghttp_server_admin_windows.go | 4 +- os/gproc/gproc.go | 16 ++--- os/gproc/gproc_process.go | 2 +- os/gproc/gproc_signal.go | 83 ++++++++++++++++++++++++ 8 files changed, 121 insertions(+), 58 deletions(-) create mode 100644 os/gproc/gproc_signal.go diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index 06cb9b8e1..5da062227 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -33,7 +33,7 @@ import ( // ScanList(&users, "UserDetail", "User", "uid:Uid") // ScanList(&users, "UserScores", "User", "uid:Uid") // -// The parameters "User"/"UserDetail"/"UserScores" in the example codes specify the target attribute struct +// The parameters "User/UserDetail/UserScores" in the example codes specify the target attribute struct // that current result will be bound to. // // The "uid" in the example codes is the table field name of the result, and the "Uid" is the relational @@ -236,12 +236,12 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation if len(relationDataMap) > 0 { relationField = relationValue.FieldByName(relationAttrName) if relationField.IsValid() { - v := relationDataMap[gconv.String(relationField.Interface())] - if v == nil { + relationDataItem := relationDataMap[gconv.String(relationField.Interface())] + if relationDataItem == nil { // There's no relational data. continue } - if err = gconv.Struct(v, e); err != nil { + if err = gconv.Struct(relationDataItem, e); err != nil { return err } } else { @@ -249,12 +249,12 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation return fmt.Errorf(`invalid relation: "%s:%s"`, relation[0], relation[1]) } } else { - v := r[i] - if v == nil { + relationDataItem := r[i] + if relationDataItem == nil { // There's no relational data. continue } - if err = gconv.Struct(v, e); err != nil { + if err = gconv.Struct(relationDataItem, e); err != nil { return err } } diff --git a/frame/g/g_func.go b/frame/g/g_func.go index a4d5ba436..e29fbf94a 100644 --- a/frame/g/g_func.go +++ b/frame/g/g_func.go @@ -10,6 +10,7 @@ import ( "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/os/gproc" "github.com/gogf/gf/util/gutil" ) @@ -18,9 +19,12 @@ func NewVar(i interface{}, safe ...bool) *Var { return gvar.New(i, safe...) } -// Wait blocks until all the web servers shutdown. +// Wait blocks until: +// 1. All the web servers shutdown, it does nothing if there's no running web server. +// 2. Shutdown signals received and all registered shutdown handlers done. func Wait() { ghttp.Wait() + gproc.Listen() } // Dump dumps a variable to stdout with more manually readable. diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index dc4b01ead..d56a31ccb 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -64,8 +64,8 @@ func serverProcessInit() { } } - // Signal handler. - go handleProcessSignal() + // Register signal handler. + registerSignalHandler() // Process message handler. // It's enabled only graceful feature is enabled. diff --git a/net/ghttp/ghttp_server_admin_unix.go b/net/ghttp/ghttp_server_admin_unix.go index de96d89e5..3c29a8f1c 100644 --- a/net/ghttp/ghttp_server_admin_unix.go +++ b/net/ghttp/ghttp_server_admin_unix.go @@ -10,50 +10,28 @@ package ghttp import ( "github.com/gogf/gf/internal/intlog" + "github.com/gogf/gf/os/gproc" "os" - "os/signal" "syscall" ) -// procSignalChan is the channel for listening the signal. -var procSignalChan = make(chan os.Signal) - -// handleProcessSignal handles all signal from system. -func handleProcessSignal() { - var sig os.Signal - signal.Notify( - procSignalChan, - syscall.SIGINT, - syscall.SIGQUIT, - syscall.SIGKILL, - syscall.SIGTERM, - syscall.SIGABRT, - syscall.SIGUSR1, - syscall.SIGUSR2, - ) - for { - sig = <-procSignalChan - intlog.Printf(`signal received: %s`, sig.String()) - switch sig { - // Shutdown the servers. - case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGABRT: - shutdownWebServers(sig.String()) - return +// registerSignalHandler handles all signal from system. +func registerSignalHandler() { + gproc.AddSigHandler(func(sig os.Signal) { + // Shutdown the servers with force. + shutdownWebServers(sig.String()) + }, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGABRT) + gproc.AddSigHandler(func(sig os.Signal) { // Shutdown the servers gracefully. // Especially from K8S when running server in POD. - case syscall.SIGTERM: - shutdownWebServersGracefully(sig.String()) - return + shutdownWebServersGracefully(sig.String()) + }, syscall.SIGTERM) + gproc.AddSigHandler(func(sig os.Signal) { // Restart the servers. - case syscall.SIGUSR1: - if err := restartWebServers(sig.String()); err != nil { - intlog.Error(err) - } - return - - default: + if err := restartWebServers(sig.String()); err != nil { + intlog.Error(err) } - } + }, syscall.SIGUSR1) } diff --git a/net/ghttp/ghttp_server_admin_windows.go b/net/ghttp/ghttp_server_admin_windows.go index 55c17751d..005efdf6e 100644 --- a/net/ghttp/ghttp_server_admin_windows.go +++ b/net/ghttp/ghttp_server_admin_windows.go @@ -8,7 +8,7 @@ package ghttp -// handleProcessSignal does nothing on windows platform. -func handleProcessSignal() { +// registerSignalHandler does nothing on windows platform. +func registerSignalHandler() { } diff --git a/os/gproc/gproc.go b/os/gproc/gproc.go index 2a3977b96..33fe32f00 100644 --- a/os/gproc/gproc.go +++ b/os/gproc/gproc.go @@ -21,14 +21,12 @@ import ( ) const ( - gPROC_ENV_KEY_PPID_KEY = "GPROC_PPID" + envKeyPPid = "GPROC_PPID" ) var ( - // processPid is the pid of current process. - processPid = os.Getpid() - // processStartTime is the start time of current process. - processStartTime = time.Now() + processPid = os.Getpid() // processPid is the pid of current process. + processStartTime = time.Now() // processStartTime is the start time of current process. ) // Pid returns the pid of current process. @@ -41,7 +39,7 @@ func PPid() int { if !IsChild() { return Pid() } - ppidValue := os.Getenv(gPROC_ENV_KEY_PPID_KEY) + ppidValue := os.Getenv(envKeyPPid) if ppidValue != "" && ppidValue != "0" { return gconv.Int(ppidValue) } @@ -59,16 +57,16 @@ func PPidOS() int { // IsChild checks and returns whether current process is a child process. // A child process is forked by another gproc process. func IsChild() bool { - ppidValue := os.Getenv(gPROC_ENV_KEY_PPID_KEY) + ppidValue := os.Getenv(envKeyPPid) return ppidValue != "" && ppidValue != "0" } // SetPPid sets custom parent pid for current process. func SetPPid(ppid int) error { if ppid > 0 { - return os.Setenv(gPROC_ENV_KEY_PPID_KEY, gconv.String(ppid)) + return os.Setenv(envKeyPPid, gconv.String(ppid)) } else { - return os.Unsetenv(gPROC_ENV_KEY_PPID_KEY) + return os.Unsetenv(envKeyPPid) } } diff --git a/os/gproc/gproc_process.go b/os/gproc/gproc_process.go index 5d4990ce0..74bf04344 100644 --- a/os/gproc/gproc_process.go +++ b/os/gproc/gproc_process.go @@ -67,7 +67,7 @@ func (p *Process) Start() (int, error) { if p.Process != nil { return p.Pid(), nil } - p.Env = append(p.Env, fmt.Sprintf("%s=%d", gPROC_ENV_KEY_PPID_KEY, p.PPid)) + p.Env = append(p.Env, fmt.Sprintf("%s=%d", envKeyPPid, p.PPid)) if err := p.Cmd.Start(); err == nil { if p.Manager != nil { p.Manager.processes.Set(p.Process.Pid, p) diff --git a/os/gproc/gproc_signal.go b/os/gproc/gproc_signal.go new file mode 100644 index 000000000..8d9231f04 --- /dev/null +++ b/os/gproc/gproc_signal.go @@ -0,0 +1,83 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gproc + +import ( + "os" + "os/signal" + "sync" + "syscall" +) + +// SigHandler defines a function type for signal handling. +type SigHandler func(sig os.Signal) + +var ( + signalHandlerMap = make(map[os.Signal][]SigHandler) + shutdownSignalMap = map[os.Signal]struct{}{ + syscall.SIGINT: {}, + syscall.SIGQUIT: {}, + syscall.SIGKILL: {}, + syscall.SIGTERM: {}, + syscall.SIGABRT: {}, + } +) + +func init() { + for sig, _ := range shutdownSignalMap { + signalHandlerMap[sig] = make([]SigHandler, 0) + } +} + +// AddSigHandler adds custom signal handler for custom one or more signals. +func AddSigHandler(handler SigHandler, signals ...os.Signal) { + for _, sig := range signals { + signalHandlerMap[sig] = append(signalHandlerMap[sig], handler) + } +} + +// AddSigHandlerShutdown adds custom signal handler for shutdown signals: +// syscall.SIGINT, +// syscall.SIGQUIT, +// syscall.SIGKILL, +// syscall.SIGTERM, +// syscall.SIGABRT. +func AddSigHandlerShutdown(handler SigHandler) { + for sig, _ := range shutdownSignalMap { + signalHandlerMap[sig] = append(signalHandlerMap[sig], handler) + } +} + +// ListenSignal blocks and does signal listening and handling. +func Listen() { + signals := make([]os.Signal, 0) + for sig, _ := range signalHandlerMap { + signals = append(signals, sig) + } + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, signals...) + var sig os.Signal + for { + wg := sync.WaitGroup{} + sig = <-sigChan + if handlers, ok := signalHandlerMap[sig]; ok { + for _, handler := range handlers { + wg.Add(1) + go func(handler SigHandler, sig os.Signal) { + defer wg.Done() + handler(sig) + }(handler, sig) + } + } + // If it is shutdown signal, it exits this signal listening. + if _, ok := shutdownSignalMap[sig]; ok { + // Wait until signal handlers done. + wg.Wait() + return + } + } +} From 0aca2f0501ebc487425ba4e34757419d38019649 Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 22 Jan 2021 00:36:28 +0800 Subject: [PATCH 094/492] fix issue in package g; improve package ghttp --- frame/g/g.go | 2 +- net/ghttp/ghttp_server.go | 4 ++- net/ghttp/ghttp_server_admin_unix.go | 52 ++++++++++++++++++++-------- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/frame/g/g.go b/frame/g/g.go index 7a188318e..f97762ade 100644 --- a/frame/g/g.go +++ b/frame/g/g.go @@ -30,7 +30,7 @@ type MapIntBool = map[int]bool // Frequently-used slice type alias. type List = []Map -type ListAnyAny = []Map +type ListAnyAny = []MapAnyAny type ListAnyStr = []MapAnyStr type ListAnyInt = []MapAnyInt type ListStrAny = []MapStrAny diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index d56a31ccb..f2425c665 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -316,7 +316,9 @@ func (s *Server) Run() { if len(s.plugins) > 0 { for _, p := range s.plugins { intlog.Printf(`remove plugin: %s`, p.Name()) - p.Remove() + if err := p.Remove(); err != nil { + intlog.Errorf("%+v", err) + } } } s.Logger().Printf("%d: all servers shutdown", gproc.Pid()) diff --git a/net/ghttp/ghttp_server_admin_unix.go b/net/ghttp/ghttp_server_admin_unix.go index 3c29a8f1c..b4f0cf6b7 100644 --- a/net/ghttp/ghttp_server_admin_unix.go +++ b/net/ghttp/ghttp_server_admin_unix.go @@ -1,4 +1,4 @@ -// Copyright 2017 gf Author(https://goframe.org). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -10,28 +10,50 @@ package ghttp import ( "github.com/gogf/gf/internal/intlog" - "github.com/gogf/gf/os/gproc" "os" + "os/signal" "syscall" ) -// registerSignalHandler handles all signal from system. -func registerSignalHandler() { - gproc.AddSigHandler(func(sig os.Signal) { - // Shutdown the servers with force. - shutdownWebServers(sig.String()) - }, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGABRT) +// procSignalChan is the channel for listening the signal. +var procSignalChan = make(chan os.Signal) + +// handleProcessSignal handles all signal from system. +func handleProcessSignal() { + var sig os.Signal + signal.Notify( + procSignalChan, + syscall.SIGINT, + syscall.SIGQUIT, + syscall.SIGKILL, + syscall.SIGTERM, + syscall.SIGABRT, + syscall.SIGUSR1, + syscall.SIGUSR2, + ) + for { + sig = <-procSignalChan + intlog.Printf(`signal received: %s`, sig.String()) + switch sig { + // Shutdown the servers. + case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGABRT: + shutdownWebServers(sig.String()) + return - gproc.AddSigHandler(func(sig os.Signal) { // Shutdown the servers gracefully. // Especially from K8S when running server in POD. - shutdownWebServersGracefully(sig.String()) - }, syscall.SIGTERM) + case syscall.SIGTERM: + shutdownWebServersGracefully(sig.String()) + return - gproc.AddSigHandler(func(sig os.Signal) { // Restart the servers. - if err := restartWebServers(sig.String()); err != nil { - intlog.Error(err) + case syscall.SIGUSR1: + if err := restartWebServers(sig.String()); err != nil { + intlog.Error(err) + } + return + + default: } - }, syscall.SIGUSR1) + } } From da0669c739b99825b69b49565696e59aca48f0d7 Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 22 Jan 2021 01:06:44 +0800 Subject: [PATCH 095/492] improve middleware feature for ghttp.Client --- net/ghttp/ghttp_client_middleware.go | 61 +++++++-------- net/ghttp/ghttp_client_request.go | 108 ++++++++++++++------------- net/ghttp/ghttp_server.go | 4 +- net/ghttp/ghttp_unit_client_test.go | 67 +++++++++++------ 4 files changed, 128 insertions(+), 112 deletions(-) diff --git a/net/ghttp/ghttp_client_middleware.go b/net/ghttp/ghttp_client_middleware.go index c121ef66b..505d85bf5 100644 --- a/net/ghttp/ghttp_client_middleware.go +++ b/net/ghttp/ghttp_client_middleware.go @@ -4,52 +4,45 @@ import ( "net/http" ) -const gfHTTPClientMiddlewareKey = "__gfHttpClientMiddlewareKey" - -// Use Add middleware to client -func (c *Client) Use(handlers ...ClientHandlerFunc) *Client { - newClient := c - if c.parent == nil { - newClient = c.Clone() - } - - newClient.middlewareHandler = append(newClient.middlewareHandler, handlers...) - return newClient -} - -// MiddlewareNext call next middleware -// this is should only be call in ClientHandlerFunc -func (c *Client) MiddlewareNext(req *http.Request) (*ClientResponse, error) { - m, ok := req.Context().Value(gfHTTPClientMiddlewareKey).(*clientMiddleware) - if ok { - resp, err := m.Next(req) - return resp, err - } - return c.callRequest(req) -} - // ClientHandlerFunc middleware handler func type ClientHandlerFunc = func(c *Client, r *http.Request) (*ClientResponse, error) // clientMiddleware is the plugin for http client request workflow management. type clientMiddleware struct { - client *Client // http client - handlers []ClientHandlerFunc // mdl handlers - handlerIndex int // current handler index - resp *ClientResponse // save resp - err error // save err + client *Client // http client. + handlers []ClientHandlerFunc // mdl handlers. + handlerIndex int // current handler index. + resp *ClientResponse // save resp. + err error // save err. } -// Next call next middleware handler, if abort, +const clientMiddlewareKey = "__clientMiddlewareKey" + +// Use adds one or more middleware handlers to client. +func (c *Client) Use(handlers ...ClientHandlerFunc) *Client { + c.middlewareHandler = append(c.middlewareHandler, handlers...) + return c +} + +// MiddlewareNext calls next middleware. +// This is should only be call in ClientHandlerFunc. +func (c *Client) MiddlewareNext(req *http.Request) (*ClientResponse, error) { + if v := req.Context().Value(clientMiddlewareKey); v != nil { + if m, ok := v.(*clientMiddleware); ok { + return m.Next(req) + } + } + return c.callRequest(req) +} + +// Next calls next middleware handler. func (m *clientMiddleware) Next(req *http.Request) (resp *ClientResponse, err error) { if m.err != nil { return m.resp, m.err } if m.handlerIndex < len(m.handlers) { m.handlerIndex++ - resp, err = m.handlers[m.handlerIndex](m.client, req) - m.resp = resp - m.err = err + m.resp, m.err = m.handlers[m.handlerIndex](m.client, req) } - return + return m.resp, m.err } diff --git a/net/ghttp/ghttp_client_request.go b/net/ghttp/ghttp_client_request.go index 0962c9080..f407c59e6 100644 --- a/net/ghttp/ghttp_client_request.go +++ b/net/ghttp/ghttp_client_request.go @@ -11,6 +11,7 @@ import ( "context" "errors" "fmt" + "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/internal/utils" "io" @@ -83,7 +84,52 @@ func (c *Client) Trace(url string, data ...interface{}) (*ClientResponse, error) return c.DoRequest("TRACE", url, data...) } -// prepareRequest verify params and return http request +// DoRequest sends request with given HTTP method and data and returns the response object. +// Note that the response object MUST be closed if it'll be never used. +// +// Note that it uses "multipart/form-data" as its Content-Type if it contains file uploading, +// else it uses "application/x-www-form-urlencoded". It also automatically detects the post +// content for JSON format, and for that it automatically sets the Content-Type as +// "application/json". +func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *ClientResponse, err error) { + req, err := c.prepareRequest(method, url, data...) + if err != nil { + return nil, err + } + + // Client middleware. + if len(c.middlewareHandler) > 0 { + mdlHandlers := make([]ClientHandlerFunc, 0, len(c.middlewareHandler)+1) + mdlHandlers = append(mdlHandlers, c.middlewareHandler...) + mdlHandlers = append(mdlHandlers, func(cli *Client, r *http.Request) (*ClientResponse, error) { + return cli.callRequest(r) + }) + ctx := context.WithValue(req.Context(), clientMiddlewareKey, &clientMiddleware{ + client: c, + handlers: mdlHandlers, + handlerIndex: -1, + }) + req = req.WithContext(ctx) + resp, err = c.MiddlewareNext(req) + } else { + resp, err = c.callRequest(req) + } + + // Auto saving cookie content. + if c.browserMode && resp != nil { + now := time.Now() + for _, v := range resp.Response.Cookies() { + if !v.Expires.IsZero() && v.Expires.UnixNano() < now.UnixNano() { + delete(c.cookies, v.Name) + } else { + c.cookies[v.Name] = v.Value + } + } + } + return resp, err +} + +// prepareRequest verifies request parameters, builds and returns http request. func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *http.Request, err error) { method = strings.ToUpper(method) if len(c.prefix) > 0 { @@ -145,10 +191,14 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h if file, err := writer.CreateFormFile(array[0], gfile.Basename(path)); err == nil { if f, err := os.Open(path); err == nil { if _, err = io.Copy(file, f); err != nil { - f.Close() + if err := f.Close(); err != nil { + intlog.Errorf(`%+v`, err) + } return nil, err } - f.Close() + if err := f.Close(); err != nil { + intlog.Errorf(`%+v`, err) + } } else { return nil, err } @@ -246,7 +296,9 @@ func (c *Client) callRequest(req *http.Request) (resp *ClientResponse, err error if resp.Response, err = c.Do(req); err != nil { // The response might not be nil when err != nil. if resp.Response != nil { - resp.Response.Body.Close() + if err := resp.Response.Body.Close(); err != nil { + intlog.Errorf(`%+v`, err) + } } if c.retryCount > 0 { c.retryCount-- @@ -261,51 +313,3 @@ func (c *Client) callRequest(req *http.Request) (resp *ClientResponse, err error } return resp, err } - -// DoRequest sends request with given HTTP method and data and returns the response object. -// Note that the response object MUST be closed if it'll be never used. -// -// Note that it uses "multipart/form-data" as its Content-Type if it contains file uploading, -// else it uses "application/x-www-form-urlencoded". It also automatically detects the post -// content for JSON format, and for that it automatically sets the Content-Type as -// "application/json". -func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *ClientResponse, err error) { - req, err := c.prepareRequest(method, url, data...) - if err != nil { - return nil, err - } - - if len(c.middlewareHandler) > 0 { - mdlHandlers := make([]ClientHandlerFunc, 0, len(c.middlewareHandler)+1) - mdlHandlers = append(mdlHandlers, c.middlewareHandler...) - - // last call internal handler - mdlHandlers = append(mdlHandlers, func(cli *Client, r *http.Request) (*ClientResponse, error) { - return cli.callRequest(r) - }) - - // call middleware - ctx := context.WithValue(req.Context(), gfHTTPClientMiddlewareKey, &clientMiddleware{ - client: c, - handlers: mdlHandlers, - handlerIndex: -1, - }) - req = req.WithContext(ctx) - resp, err = c.MiddlewareNext(req) - } else { - resp, err = c.callRequest(req) - } - - // Auto saving cookie content. - if c.browserMode { - now := time.Now() - for _, v := range resp.Response.Cookies() { - if !v.Expires.IsZero() && v.Expires.UnixNano() < now.UnixNano() { - delete(c.cookies, v.Name) - } else { - c.cookies[v.Name] = v.Value - } - } - } - return resp, err -} diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index f2425c665..af9edfd5c 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -64,8 +64,8 @@ func serverProcessInit() { } } - // Register signal handler. - registerSignalHandler() + // Signal handler. + go handleProcessSignal() // Process message handler. // It's enabled only graceful feature is enabled. diff --git a/net/ghttp/ghttp_unit_client_test.go b/net/ghttp/ghttp_unit_client_test.go index bead02afb..468483b2a 100644 --- a/net/ghttp/ghttp_unit_client_test.go +++ b/net/ghttp/ghttp_unit_client_test.go @@ -353,52 +353,71 @@ func Test_Client_Middleware(t *testing.T) { time.Sleep(100 * time.Millisecond) gtest.C(t, func(t *gtest.T) { - str := "" - str2 := "resp body" - c := ghttp.NewClient().SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { - str += "a" + var ( + str1 = "" + str2 = "resp body" + ) + c := g.Client().SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { + str1 += "a" resp, err = c.MiddlewareNext(r) - str += "b" - return - }).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { - str += "c" - resp, err = c.MiddlewareNext(r) - str += "d" - return - }).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { - str += "e" - resp, err = c.MiddlewareNext(r) - resp.Response.Body = ioutil.NopCloser(bytes.NewBufferString(str2)) - str += "f" + if err != nil { + return nil, err + } + str1 += "b" + return + }) + c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { + str1 += "c" + resp, err = c.MiddlewareNext(r) + if err != nil { + return nil, err + } + str1 += "d" + return + }) + c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { + str1 += "e" + resp, err = c.MiddlewareNext(r) + if err != nil { + return nil, err + } + resp.Response.Body = ioutil.NopCloser(bytes.NewBufferString(str2)) + str1 += "f" return }) - resp, err := c.Get("/") - t.Assert(str, "acefdb") + t.Assert(str1, "acefdb") t.Assert(err, nil) t.Assert(resp.ReadAllString(), str2) t.Assert(isServerHandler, true) // test abort, abort will not send - str3 := "" - abortStr := "abort request" - c = ghttp.NewClient().SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { + var ( + str3 = "" + abortStr = "abort request" + ) + + c = g.Client().SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { str3 += "a" resp, err = c.MiddlewareNext(r) str3 += "b" return - }).Use(func(c *ghttp.Client, r *http.Request) (*ghttp.ClientResponse, error) { + }) + c.Use(func(c *ghttp.Client, r *http.Request) (*ghttp.ClientResponse, error) { str3 += "c" return nil, gerror.New(abortStr) - }).Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { + }) + c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { str3 += "f" resp, err = c.MiddlewareNext(r) str3 += "g" return }) resp, err = c.Get("/") + t.Assert(err, abortStr) t.Assert(str3, "acb") - t.Assert(err.Error(), abortStr) t.Assert(resp, nil) }) } From f1ed9b31b6e993c97c7a8ccd20709b304d80317a Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 22 Jan 2021 12:57:21 +0800 Subject: [PATCH 096/492] add g.Listen for automatic signal handling feature --- frame/g/g_func.go | 11 ++++++++--- os/gproc/gproc_signal.go | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/frame/g/g_func.go b/frame/g/g_func.go index e29fbf94a..64b284482 100644 --- a/frame/g/g_func.go +++ b/frame/g/g_func.go @@ -19,11 +19,16 @@ func NewVar(i interface{}, safe ...bool) *Var { return gvar.New(i, safe...) } -// Wait blocks until: -// 1. All the web servers shutdown, it does nothing if there's no running web server. -// 2. Shutdown signals received and all registered shutdown handlers done. +// Wait is an alias of ghttp.Wait, which blocks until all the web servers shutdown. +// It's commonly used in multiple servers situation. func Wait() { ghttp.Wait() +} + +// Listen is an alias of gproc.Listen, which handles the signals received and automatically +// calls registered signal handler functions. +// It blocks until shutdown signals received and all registered shutdown handlers done. +func Listen() { gproc.Listen() } diff --git a/os/gproc/gproc_signal.go b/os/gproc/gproc_signal.go index 8d9231f04..40ad5464b 100644 --- a/os/gproc/gproc_signal.go +++ b/os/gproc/gproc_signal.go @@ -7,6 +7,7 @@ package gproc import ( + "github.com/gogf/gf/internal/intlog" "os" "os/signal" "sync" @@ -64,6 +65,7 @@ func Listen() { for { wg := sync.WaitGroup{} sig = <-sigChan + intlog.Printf(`signal received: %s`, sig.String()) if handlers, ok := signalHandlerMap[sig]; ok { for _, handler := range handlers { wg.Add(1) From e222d92fb5dc78f9c846fd1a49a364dba679635f Mon Sep 17 00:00:00 2001 From: jflyfox Date: Fri, 22 Jan 2021 15:19:11 +0800 Subject: [PATCH 097/492] improve unit testing case for package gcron --- os/gcron/gcron_unit_1_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/gcron/gcron_unit_1_test.go b/os/gcron/gcron_unit_1_test.go index 4e408e2a3..5cb253c11 100644 --- a/os/gcron/gcron_unit_1_test.go +++ b/os/gcron/gcron_unit_1_test.go @@ -41,7 +41,7 @@ func TestCron_Add_Close(t *testing.T) { t.Assert(cron.Size(), 3) time.Sleep(1200 * time.Millisecond) t.Assert(array.Len(), 2) - time.Sleep(1200 * time.Millisecond) + time.Sleep(1400 * time.Millisecond) t.Assert(array.Len(), 5) cron.Close() time.Sleep(1200 * time.Millisecond) From ce640048b855a263b640869cca6f641e958a1426 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Fri, 22 Jan 2021 15:30:39 +0800 Subject: [PATCH 098/492] improve package gcfg --- os/gcfg/gcfg.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index f9c9c4c91..71c203cd0 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -11,6 +11,7 @@ import ( "bytes" "errors" "fmt" + "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gcmd" "github.com/gogf/gf/text/gstr" @@ -178,6 +179,7 @@ func (c *Config) SetPath(path string) error { c.jsons.Clear() c.paths.Clear() c.paths.Append(realPath) + intlog.Print("SetPath:", realPath) return nil } @@ -251,7 +253,7 @@ func (c *Config) AddPath(path string) error { return nil } c.paths.Append(realPath) - //glog.Debug("[gcfg] AddPath:", realPath) + intlog.Print("AddPath:", realPath) return nil } From c27ddb1d79aa176eb5212a3645d6749ea5d98f08 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Fri, 22 Jan 2021 23:04:29 +0800 Subject: [PATCH 099/492] opentelemetry --- .example/net/gtrace/jaeger/main.go | 79 ++++++ go.mod | 11 +- ...ghttp_client_config.go => ghttp_client.go} | 14 + net/ghttp/ghttp_client_request.go | 21 ++ net/gtrace/gtrace_http_client.go | 242 ++++++++++++++++++ os/gcron/gcron_unit_1_test.go | 10 +- os/gcron/gcron_unit_2_test.go | 2 +- 7 files changed, 372 insertions(+), 7 deletions(-) create mode 100644 .example/net/gtrace/jaeger/main.go rename net/ghttp/{ghttp_client_config.go => ghttp_client.go} (94%) create mode 100644 net/gtrace/gtrace_http_client.go diff --git a/.example/net/gtrace/jaeger/main.go b/.example/net/gtrace/jaeger/main.go new file mode 100644 index 000000000..87d32d26e --- /dev/null +++ b/.example/net/gtrace/jaeger/main.go @@ -0,0 +1,79 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Command jaeger is an example program that creates spans +// and uploads to Jaeger. +package main + +import ( + "context" + "fmt" + "github.com/gogf/gf/frame/g" + "go.opentelemetry.io/otel/exporters/trace/jaeger" + "go.opentelemetry.io/otel/trace" + "log" + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/label" + + sdktrace "go.opentelemetry.io/otel/sdk/trace" +) + +// initTracer creates a new trace provider instance and registers it as global trace provider. +func initTracer() func() { + // Create and install Jaeger export pipeline. + flush, err := jaeger.InstallNewPipeline( + jaeger.WithCollectorEndpoint("http://localhost:14268/api/traces"), + jaeger.WithProcess(jaeger.Process{ + ServiceName: "http-trace-demo", + Tags: []label.KeyValue{ + label.String("exporter", "jaeger"), + label.Float64("float", 312.23), + }, + }), + jaeger.WithSDK(&sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), + ) + if err != nil { + log.Fatal(err) + } + return flush +} + +func main() { + ctx := context.Background() + + flush := initTracer() + defer flush() + + ctx, span := otel.Tracer("component-main").Start(ctx, "foo") + defer span.End() + + content := g.Client().Ctx(ctx).Header(g.MapStrStr{ + "test": "123", + "john": "smith", + }).Cookie(g.MapStrStr{ + "cookieKey":"cookieValue", + }).GetContent("http://baidu.com/?q=goframe") + fmt.Println(content) +} + +func bar(ctx context.Context) { + _, span := otel.Tracer("test").Start(ctx, "bar") + defer span.End() + span.AddEvent("Nice operation!", trace.WithAttributes(label.Int("bogons", 100))) + span.SetAttributes(label.String("test2", "123")) + time.Sleep(time.Second * 2) + // Do bar... +} diff --git a/go.mod b/go.mod index 77c4f3f19..9b3716251 100644 --- a/go.mod +++ b/go.mod @@ -10,9 +10,16 @@ require ( github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/websocket v1.4.1 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf + github.com/kr/pretty v0.1.0 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/olekukonko/tablewriter v0.0.1 - golang.org/x/net v0.0.0-20200602114024-627f9648deb9 - golang.org/x/text v0.3.2 + go.opentelemetry.io/otel v0.16.0 + go.opentelemetry.io/otel/exporters/trace/jaeger v0.16.0 + go.opentelemetry.io/otel/sdk v0.16.0 + golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 + golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 // indirect + golang.org/x/text v0.3.4 + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c ) diff --git a/net/ghttp/ghttp_client_config.go b/net/ghttp/ghttp_client.go similarity index 94% rename from net/ghttp/ghttp_client_config.go rename to net/ghttp/ghttp_client.go index b90d57665..089efc7c9 100644 --- a/net/ghttp/ghttp_client_config.go +++ b/net/ghttp/ghttp_client.go @@ -9,6 +9,8 @@ package ghttp import ( "context" "crypto/tls" + "fmt" + "github.com/gogf/gf" "github.com/gogf/gf/text/gstr" "golang.org/x/net/proxy" "net" @@ -24,6 +26,7 @@ import ( type Client struct { http.Client // Underlying HTTP Client. ctx context.Context // Context for each request. + agent string // Client agent. parent *Client // Parent http client, this is used for chaining operations. header map[string]string // Custom header map. cookies map[string]string // Custom cookie map. @@ -36,6 +39,10 @@ type Client struct { middlewareHandler []ClientHandlerFunc // Interceptor handlers } +var ( + defaultClientAgent = fmt.Sprintf(`GoFrameHTTPClient %s`, gf.VERSION) +) + // NewClient creates and returns a new HTTP client object. func NewClient() *Client { return &Client{ @@ -50,6 +57,7 @@ func NewClient() *Client { }, header: make(map[string]string), cookies: make(map[string]string), + agent: defaultClientAgent, } } @@ -90,6 +98,12 @@ func (c *Client) SetHeaderMap(m map[string]string) *Client { return c } +// SetAgent sets the User-Agent header for client. +func (c *Client) SetAgent(agent string) *Client { + c.header["User-Agent"] = agent + return c +} + // SetContentType sets HTTP content type for the client. func (c *Client) SetContentType(contentType string) *Client { c.header["Content-Type"] = contentType diff --git a/net/ghttp/ghttp_client_request.go b/net/ghttp/ghttp_client_request.go index f407c59e6..3d3775922 100644 --- a/net/ghttp/ghttp_client_request.go +++ b/net/ghttp/ghttp_client_request.go @@ -11,9 +11,13 @@ import ( "context" "errors" "fmt" + "github.com/gogf/gf" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/internal/utils" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/trace" "io" "io/ioutil" "mime/multipart" @@ -97,6 +101,19 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien return nil, err } + // Tracing. + tr := otel.GetTracerProvider().Tracer( + "github.com/gogf/gf/net/ghttp.client", + trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), + ) + ctx, span := tr.Start(req.Context(), req.URL.String()) + defer span.End() + // Header (Cookie is in it). + if len(req.Header) > 0 { + span.SetAttributes(label.Any(`http.headers`, req.Header)) + } + req = req.WithContext(ctx) + // Client middleware. if len(c.middlewareHandler) > 0 { mdlHandlers := make([]ClientHandlerFunc, 0, len(c.middlewareHandler)+1) @@ -278,6 +295,10 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h if len(c.authUser) > 0 { req.SetBasicAuth(c.authUser, c.authPass) } + // Client agent. + if c.agent != "" { + req.Header.Set("User-Agent", c.agent) + } return req, nil } diff --git a/net/gtrace/gtrace_http_client.go b/net/gtrace/gtrace_http_client.go new file mode 100644 index 000000000..7bc100b22 --- /dev/null +++ b/net/gtrace/gtrace_http_client.go @@ -0,0 +1,242 @@ +package gtrace + +import ( + "context" + "crypto/tls" + "fmt" + "github.com/gogf/gf" + "net/http/httptrace" + "net/textproto" + "strings" + "sync" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/semconv" + "go.opentelemetry.io/otel/trace" +) + +var ( + HTTPStatus = label.Key("http.status") + HTTPHeaderMIME = label.Key("http.mime") + HTTPRemoteAddr = label.Key("http.remote") + HTTPLocalAddr = label.Key("http.local") +) + +var ( + hookMap = map[string]string{ + "http.dns": "http.getconn", + "http.connect": "http.getconn", + "http.tls": "http.getconn", + } +) + +func parentHook(hook string) string { + if strings.HasPrefix(hook, "http.connect") { + return hookMap["http.connect"] + } + return hookMap[hook] +} + +type clientTracer struct { + context.Context + + tr trace.Tracer + + activeHooks map[string]context.Context + root trace.Span + mtx sync.Mutex +} + +func NewClientTrace(ctx context.Context) *httptrace.ClientTrace { + ct := &clientTracer{ + Context: ctx, + activeHooks: make(map[string]context.Context), + } + + ct.tr = otel.GetTracerProvider().Tracer( + "github.com/gogf/gf/net/ghttp.client", + trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), + ) + + return &httptrace.ClientTrace{ + GetConn: ct.getConn, + GotConn: ct.gotConn, + PutIdleConn: ct.putIdleConn, + GotFirstResponseByte: ct.gotFirstResponseByte, + Got100Continue: ct.got100Continue, + Got1xxResponse: ct.got1xxResponse, + DNSStart: ct.dnsStart, + DNSDone: ct.dnsDone, + ConnectStart: ct.connectStart, + ConnectDone: ct.connectDone, + TLSHandshakeStart: ct.tlsHandshakeStart, + TLSHandshakeDone: ct.tlsHandshakeDone, + WroteHeaderField: ct.wroteHeaderField, + WroteHeaders: ct.wroteHeaders, + Wait100Continue: ct.wait100Continue, + WroteRequest: ct.wroteRequest, + } +} + +func (ct *clientTracer) start(hook, spanName string, attrs ...label.KeyValue) { + ct.mtx.Lock() + defer ct.mtx.Unlock() + + if hookCtx, found := ct.activeHooks[hook]; !found { + var sp trace.Span + ct.activeHooks[hook], sp = ct.tr.Start(ct.getParentContext(hook), spanName, trace.WithAttributes(attrs...), trace.WithSpanKind(trace.SpanKindClient)) + if ct.root == nil { + ct.root = sp + } + } else { + // end was called before start finished, add the start attributes and end the span here + span := trace.SpanFromContext(hookCtx) + span.SetAttributes(attrs...) + span.End() + + delete(ct.activeHooks, hook) + } +} + +func (ct *clientTracer) end(hook string, err error, attrs ...label.KeyValue) { + ct.mtx.Lock() + defer ct.mtx.Unlock() + if ctx, ok := ct.activeHooks[hook]; ok { + span := trace.SpanFromContext(ctx) + if err != nil { + span.SetStatus(codes.Error, err.Error()) + } + span.SetAttributes(attrs...) + span.End() + delete(ct.activeHooks, hook) + } else { + // start is not finished before end is called. + // Start a span here with the ending attributes that will be finished when start finishes. + // Yes, it's backwards. v0v + ctx, span := ct.tr.Start(ct.getParentContext(hook), hook, trace.WithAttributes(attrs...), trace.WithSpanKind(trace.SpanKindClient)) + if err != nil { + span.SetStatus(codes.Error, err.Error()) + } + ct.activeHooks[hook] = ctx + } +} + +func (ct *clientTracer) getParentContext(hook string) context.Context { + ctx, ok := ct.activeHooks[parentHook(hook)] + if !ok { + return ct.Context + } + return ctx +} + +func (ct *clientTracer) span(hook string) trace.Span { + ct.mtx.Lock() + defer ct.mtx.Unlock() + if ctx, ok := ct.activeHooks[hook]; ok { + return trace.SpanFromContext(ctx) + } + return nil +} + +func (ct *clientTracer) getConn(host string) { + ct.start("http.getconn", "http.getconn", semconv.HTTPHostKey.String(host)) +} + +func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) { + ct.end("http.getconn", + nil, + HTTPRemoteAddr.String(info.Conn.RemoteAddr().String()), + HTTPLocalAddr.String(info.Conn.LocalAddr().String()), + ) +} + +func (ct *clientTracer) putIdleConn(err error) { + ct.end("http.receive", err) +} + +func (ct *clientTracer) gotFirstResponseByte() { + ct.start("http.receive", "http.receive") +} + +func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) { + ct.start("http.dns", "http.dns", semconv.HTTPHostKey.String(info.Host)) +} + +func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) { + ct.end("http.dns", info.Err) +} + +func (ct *clientTracer) connectStart(network, addr string) { + ct.start("http.connect."+addr, "http.connect", HTTPRemoteAddr.String(addr)) +} + +func (ct *clientTracer) connectDone(network, addr string, err error) { + ct.end("http.connect."+addr, err) +} + +func (ct *clientTracer) tlsHandshakeStart() { + ct.start("http.tls", "http.tls") +} + +func (ct *clientTracer) tlsHandshakeDone(_ tls.ConnectionState, err error) { + ct.end("http.tls", err) +} + +func (ct *clientTracer) wroteHeaderField(k string, v []string) { + if ct.span("http.headers") == nil { + ct.start("http.headers", "http.headers") + } + ct.root.SetAttributes(label.String("http."+strings.ToLower(k), sliceToString(v))) +} + +func (ct *clientTracer) wroteHeaders() { + if ct.span("http.headers") != nil { + ct.end("http.headers", nil) + } + ct.start("http.send", "http.send") +} + +func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { + if info.Err != nil { + ct.root.SetStatus(codes.Error, info.Err.Error()) + } + ct.end("http.send", info.Err) +} + +func (ct *clientTracer) got100Continue() { + ct.span("http.receive").AddEvent("GOT 100 - Continue") +} + +func (ct *clientTracer) wait100Continue() { + ct.span("http.receive").AddEvent("GOT 100 - Wait") +} + +func (ct *clientTracer) got1xxResponse(code int, header textproto.MIMEHeader) error { + ct.span("http.receive").AddEvent("GOT 1xx", trace.WithAttributes( + HTTPStatus.Int(code), + HTTPHeaderMIME.String(sm2s(header)), + )) + return nil +} + +func sliceToString(value []string) string { + if len(value) == 0 { + return "undefined" + } + return strings.Join(value, ",") +} + +func sm2s(value map[string][]string) string { + var buf strings.Builder + for k, v := range value { + if buf.Len() != 0 { + buf.WriteString(",") + } + buf.WriteString(k) + buf.WriteString("=") + buf.WriteString(sliceToString(v)) + } + return buf.String() +} diff --git a/os/gcron/gcron_unit_1_test.go b/os/gcron/gcron_unit_1_test.go index 5cb253c11..0807f4f4f 100644 --- a/os/gcron/gcron_unit_1_test.go +++ b/os/gcron/gcron_unit_1_test.go @@ -7,6 +7,7 @@ package gcron_test import ( + "github.com/gogf/gf/os/glog" "testing" "time" @@ -20,18 +21,19 @@ func TestCron_Add_Close(t *testing.T) { cron := gcron.New() array := garray.New(true) _, err1 := cron.Add("* * * * * *", func() { - //glog.Println("cron1") + glog.Println("cron1") array.Append(1) }) _, err2 := cron.Add("* * * * * *", func() { - //glog.Println("cron2") + glog.Println("cron2") array.Append(1) }, "test") _, err3 := cron.Add("* * * * * *", func() { + glog.Println("cron3") array.Append(1) }, "test") _, err4 := cron.Add("@every 2s", func() { - //glog.Println("cron3") + glog.Println("cron4") array.Append(1) }) t.Assert(err1, nil) @@ -39,7 +41,7 @@ func TestCron_Add_Close(t *testing.T) { t.AssertNE(err3, nil) t.Assert(err4, nil) t.Assert(cron.Size(), 3) - time.Sleep(1200 * time.Millisecond) + time.Sleep(1300 * time.Millisecond) t.Assert(array.Len(), 2) time.Sleep(1400 * time.Millisecond) t.Assert(array.Len(), 5) diff --git a/os/gcron/gcron_unit_2_test.go b/os/gcron/gcron_unit_2_test.go index f3c8935e2..fc157a929 100644 --- a/os/gcron/gcron_unit_2_test.go +++ b/os/gcron/gcron_unit_2_test.go @@ -47,7 +47,7 @@ func TestCron_Entry_Operations(t *testing.T) { t.Assert(array.Len(), 1) t.Assert(cron.Size(), 1) entry.Stop() - time.Sleep(2000 * time.Millisecond) + time.Sleep(5000 * time.Millisecond) t.Assert(array.Len(), 1) t.Assert(cron.Size(), 1) entry.Start() From 9524263803587648a5e42f80009ac3cd5afb175f Mon Sep 17 00:00:00 2001 From: jflyfox Date: Fri, 22 Jan 2021 23:07:11 +0800 Subject: [PATCH 100/492] improve unit testing cases for package gcron --- os/gcron/gcron_unit_1_test.go | 10 ++++++---- os/gcron/gcron_unit_2_test.go | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/os/gcron/gcron_unit_1_test.go b/os/gcron/gcron_unit_1_test.go index 5cb253c11..0807f4f4f 100644 --- a/os/gcron/gcron_unit_1_test.go +++ b/os/gcron/gcron_unit_1_test.go @@ -7,6 +7,7 @@ package gcron_test import ( + "github.com/gogf/gf/os/glog" "testing" "time" @@ -20,18 +21,19 @@ func TestCron_Add_Close(t *testing.T) { cron := gcron.New() array := garray.New(true) _, err1 := cron.Add("* * * * * *", func() { - //glog.Println("cron1") + glog.Println("cron1") array.Append(1) }) _, err2 := cron.Add("* * * * * *", func() { - //glog.Println("cron2") + glog.Println("cron2") array.Append(1) }, "test") _, err3 := cron.Add("* * * * * *", func() { + glog.Println("cron3") array.Append(1) }, "test") _, err4 := cron.Add("@every 2s", func() { - //glog.Println("cron3") + glog.Println("cron4") array.Append(1) }) t.Assert(err1, nil) @@ -39,7 +41,7 @@ func TestCron_Add_Close(t *testing.T) { t.AssertNE(err3, nil) t.Assert(err4, nil) t.Assert(cron.Size(), 3) - time.Sleep(1200 * time.Millisecond) + time.Sleep(1300 * time.Millisecond) t.Assert(array.Len(), 2) time.Sleep(1400 * time.Millisecond) t.Assert(array.Len(), 5) diff --git a/os/gcron/gcron_unit_2_test.go b/os/gcron/gcron_unit_2_test.go index f3c8935e2..fc157a929 100644 --- a/os/gcron/gcron_unit_2_test.go +++ b/os/gcron/gcron_unit_2_test.go @@ -47,7 +47,7 @@ func TestCron_Entry_Operations(t *testing.T) { t.Assert(array.Len(), 1) t.Assert(cron.Size(), 1) entry.Stop() - time.Sleep(2000 * time.Millisecond) + time.Sleep(5000 * time.Millisecond) t.Assert(array.Len(), 1) t.Assert(cron.Size(), 1) entry.Start() From ab9d7ed5094837cfc6feb519eb06b678c73c592b Mon Sep 17 00:00:00 2001 From: jflyfox Date: Fri, 22 Jan 2021 23:09:42 +0800 Subject: [PATCH 101/492] improve package gcron --- os/gcron/gcron.go | 10 +++++----- os/gcron/gcron_entry.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/os/gcron/gcron.go b/os/gcron/gcron.go index 8abe47423..7f45c4b2c 100644 --- a/os/gcron/gcron.go +++ b/os/gcron/gcron.go @@ -15,11 +15,11 @@ import ( ) const ( - StatusReady = gtimer.StatusReady - StatusRunning = gtimer.StatusRunning - StatusStopped = gtimer.StatusStopped - StatusClosed = gtimer.StatusClosed - gDEFAULT_TIMES = math.MaxInt32 + StatusReady = gtimer.StatusReady + StatusRunning = gtimer.StatusRunning + StatusStopped = gtimer.StatusStopped + StatusClosed = gtimer.StatusClosed + defaultTimes = math.MaxInt32 ) var ( diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index 9b9087639..94ff02f43 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -43,7 +43,7 @@ func (c *Cron) addEntry(pattern string, job func(), singleton bool, name ...stri cron: c, schedule: schedule, jobName: runtime.FuncForPC(reflect.ValueOf(job).Pointer()).Name(), - times: gtype.NewInt(gDEFAULT_TIMES), + times: gtype.NewInt(defaultTimes), Job: job, Time: time.Now(), } @@ -130,7 +130,7 @@ func (entry *Entry) check() { } } if times < 2000000000 && times > 1000000000 { - entry.times.Set(gDEFAULT_TIMES) + entry.times.Set(defaultTimes) } glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s start", entry.Name, entry.schedule.pattern, entry.jobName) defer func() { From f579c724c6bac082ed1dd7470156c1eccbd82f3b Mon Sep 17 00:00:00 2001 From: jflyfox Date: Fri, 22 Jan 2021 23:21:29 +0800 Subject: [PATCH 102/492] fix issue in batch number for package gdb --- .../database/gdb/mysql/gdb_batch_insert.go | 24 +++++++++++++++++++ database/gdb/gdb_core.go | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 .example/database/gdb/mysql/gdb_batch_insert.go diff --git a/.example/database/gdb/mysql/gdb_batch_insert.go b/.example/database/gdb/mysql/gdb_batch_insert.go new file mode 100644 index 000000000..171bb9fb4 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_batch_insert.go @@ -0,0 +1,24 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/frame/g" +) + +func main() { + db := g.DB() + db.SetDebug(true) + list := make(g.List, 0) + for i := 0; i < 100; i++ { + list = append(list, g.Map{ + "name": fmt.Sprintf(`name_%d`, i), + }) + } + r, e := db.Table("user").Data(list).Batch(2).Insert() + if e != nil { + panic(e) + } + if r != nil { + fmt.Println(r.LastInsertId()) + } +} diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 12c0d25c1..5ccdc2516 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -690,7 +690,7 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i } } valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")") - if len(values) == batchNum || (i == listMapLen-1 && len(values) > 0) { + if len(valueHolder) == batchNum || (i == listMapLen-1 && len(valueHolder) > 0) { r, err := c.DB.DoExec( link, fmt.Sprintf( From 2c7a257b5a3b827f3593188ad16b012b5b896af4 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Fri, 22 Jan 2021 23:49:17 +0800 Subject: [PATCH 103/492] remove parseTime parameter for mysql driver for package gdb --- database/gdb/gdb_driver_mysql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index f5a7290ce..6831f5b27 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -42,7 +42,7 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) { } } else { source = fmt.Sprintf( - "%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true", + "%s:%s@tcp(%s:%s)/%s?charset=%s", config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset, ) } From a5f53f158ae5f2096658025e73ae42edafa1cb99 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Sat, 23 Jan 2021 01:01:24 +0800 Subject: [PATCH 104/492] improve unit testing case for package gcron; improve package gdb; --- database/gdb/gdb_model_insert.go | 18 +- .../gdb/gdb_z_mysql_time_maintain_test.go | 278 +++++++++++++++++- os/gcron/gcron_unit_2_test.go | 4 +- 3 files changed, 289 insertions(+), 11 deletions(-) diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 89d22e1b0..4eca381f3 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -164,6 +164,11 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { if m.batch > 0 { batch = m.batch } + newData, err := m.filterDataForInsertOrUpdate(list) + if err != nil { + return nil, err + } + list = newData.(List) // Automatic handling for creating/updating time. if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") { for k, v := range list { @@ -177,10 +182,6 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { list[k] = v } } - newData, err := m.filterDataForInsertOrUpdate(list) - if err != nil { - return nil, err - } return m.db.DoBatchInsert( m.getLink(true), m.tables, @@ -191,6 +192,11 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { } // Single operation. if data, ok := m.data.(Map); ok { + newData, err := m.filterDataForInsertOrUpdate(data) + if err != nil { + return nil, err + } + data = newData.(Map) // Automatic handling for creating/updating time. if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") { gutil.MapDelete(data, fieldNameCreate, fieldNameUpdate, fieldNameDelete) @@ -201,10 +207,6 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { data[fieldNameUpdate] = nowString } } - newData, err := m.filterDataForInsertOrUpdate(data) - if err != nil { - return nil, err - } return m.db.DoInsert( m.getLink(true), m.tables, diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index b3e15d839..12d3f7b86 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -17,8 +17,9 @@ import ( "github.com/gogf/gf/test/gtest" ) +// CreateAt/UpdateAt/DeleteAt func Test_SoftCreateUpdateDeleteTime(t *testing.T) { - table := "time_test_table" + table := "time_test_table_" + gtime.TimestampNanoStr() if _, err := db.Exec(fmt.Sprintf(` CREATE TABLE %s ( id int(11) NOT NULL, @@ -150,6 +151,281 @@ CREATE TABLE %s ( }) } +// CreatedAt/UpdatedAt/DeletedAt +func Test_SoftCreatedUpdatedDeletedTime_Map(t *testing.T) { + table := "time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + id int(11) NOT NULL, + name varchar(45) DEFAULT NULL, + created_at datetime DEFAULT NULL, + updated_at datetime DEFAULT NULL, + deleted_at datetime DEFAULT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := g.Map{ + "id": 1, + "name": "name_1", + } + r, err := db.Table(table).Data(dataInsert).Insert() + t.Assert(err, nil) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInsert, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneInsert["id"].Int(), 1) + t.Assert(oneInsert["name"].String(), "name_1") + t.Assert(oneInsert["deleted_at"].String(), "") + t.AssertGE(oneInsert["created_at"].GTime().Timestamp(), gtime.Timestamp()-2) + t.AssertGE(oneInsert["updated_at"].GTime().Timestamp(), gtime.Timestamp()) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Save + dataSave := g.Map{ + "id": 1, + "name": "name_10", + } + r, err = db.Table(table).Data(dataSave).Save() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 2) + + oneSave, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneSave["id"].Int(), 1) + t.Assert(oneSave["name"].String(), "name_10") + t.Assert(oneSave["deleted_at"].String(), "") + t.Assert(oneSave["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertNE(oneSave["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) + t.AssertGE(oneSave["updated_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Update + dataUpdate := g.Map{ + "name": "name_1000", + } + r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneUpdate, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneUpdate["id"].Int(), 1) + t.Assert(oneUpdate["name"].String(), "name_1000") + t.Assert(oneUpdate["deleted_at"].String(), "") + t.Assert(oneUpdate["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + + // Replace + dataReplace := g.Map{ + "id": 1, + "name": "name_100", + } + r, err = db.Table(table).Data(dataReplace).Replace() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 2) + + oneReplace, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneReplace["id"].Int(), 1) + t.Assert(oneReplace["name"].String(), "name_100") + t.Assert(oneReplace["deleted_at"].String(), "") + t.AssertGE(oneReplace["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertGE(oneReplace["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Delete + r, err = db.Table(table).Delete("id", 1) + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + // Delete Select + one4, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(len(one4), 0) + one5, err := db.Table(table).Unscoped().FindOne(1) + t.Assert(err, nil) + t.Assert(one5["id"].Int(), 1) + t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + // Delete Count + i, err := db.Table(table).FindCount() + t.Assert(err, nil) + t.Assert(i, 0) + i, err = db.Table(table).Unscoped().FindCount() + t.Assert(err, nil) + t.Assert(i, 1) + + // Delete Unscoped + r, err = db.Table(table).Unscoped().Delete("id", 1) + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + one6, err := db.Table(table).Unscoped().FindOne(1) + t.Assert(err, nil) + t.Assert(len(one6), 0) + i, err = db.Table(table).Unscoped().FindCount() + t.Assert(err, nil) + t.Assert(i, 0) + }) +} + +// CreatedAt/UpdatedAt/DeletedAt +func Test_SoftCreatedUpdatedDeletedTime_Struct(t *testing.T) { + table := "time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + id int(11) NOT NULL, + name varchar(45) DEFAULT NULL, + created_at datetime DEFAULT NULL, + updated_at datetime DEFAULT NULL, + deleted_at datetime DEFAULT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + type User struct { + Id int + Name string + CreatedAT *gtime.Time + UpdatedAT *gtime.Time + DeletedAT *gtime.Time + } + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := User{ + Id: 1, + Name: "name_1", + } + r, err := db.Table(table).Data(dataInsert).Insert() + t.Assert(err, nil) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInsert, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneInsert["id"].Int(), 1) + t.Assert(oneInsert["name"].String(), "name_1") + t.Assert(oneInsert["deleted_at"].String(), "") + t.AssertGE(oneInsert["created_at"].GTime().Timestamp(), gtime.Timestamp()-2) + t.AssertGE(oneInsert["updated_at"].GTime().Timestamp(), gtime.Timestamp()) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Save + dataSave := User{ + Id: 1, + Name: "name_10", + } + r, err = db.Table(table).Data(dataSave).OmitEmpty().Save() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 2) + + oneSave, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneSave["id"].Int(), 1) + t.Assert(oneSave["name"].String(), "name_10") + t.Assert(oneSave["deleted_at"].String(), "") + t.Assert(oneSave["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertNE(oneSave["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) + t.AssertGE(oneSave["updated_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Update + dataUpdate := User{ + Name: "name_1000", + } + r, err = db.Table(table).Data(dataUpdate).OmitEmpty().WherePri(1).Update() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneUpdate, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneUpdate["id"].Int(), 1) + t.Assert(oneUpdate["name"].String(), "name_1000") + t.Assert(oneUpdate["deleted_at"].String(), "") + t.Assert(oneUpdate["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + + // Replace + dataReplace := User{ + Id: 1, + Name: "name_100", + } + r, err = db.Table(table).Data(dataReplace).OmitEmpty().Replace() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 2) + + oneReplace, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneReplace["id"].Int(), 1) + t.Assert(oneReplace["name"].String(), "name_100") + t.Assert(oneReplace["deleted_at"].String(), "") + t.AssertGE(oneReplace["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertGE(oneReplace["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Delete + r, err = db.Table(table).Delete("id", 1) + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + // Delete Select + one4, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(len(one4), 0) + one5, err := db.Table(table).Unscoped().FindOne(1) + t.Assert(err, nil) + t.Assert(one5["id"].Int(), 1) + t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + // Delete Count + i, err := db.Table(table).FindCount() + t.Assert(err, nil) + t.Assert(i, 0) + i, err = db.Table(table).Unscoped().FindCount() + t.Assert(err, nil) + t.Assert(i, 1) + + // Delete Unscoped + r, err = db.Table(table).Unscoped().Delete("id", 1) + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + one6, err := db.Table(table).Unscoped().FindOne(1) + t.Assert(err, nil) + t.Assert(len(one6), 0) + i, err = db.Table(table).Unscoped().FindCount() + t.Assert(err, nil) + t.Assert(i, 0) + }) +} + func Test_SoftUpdateTime(t *testing.T) { table := "time_test_table_" + gtime.TimestampNanoStr() if _, err := db.Exec(fmt.Sprintf(` diff --git a/os/gcron/gcron_unit_2_test.go b/os/gcron/gcron_unit_2_test.go index fc157a929..3d0fffc84 100644 --- a/os/gcron/gcron_unit_2_test.go +++ b/os/gcron/gcron_unit_2_test.go @@ -47,12 +47,12 @@ func TestCron_Entry_Operations(t *testing.T) { t.Assert(array.Len(), 1) t.Assert(cron.Size(), 1) entry.Stop() - time.Sleep(5000 * time.Millisecond) + time.Sleep(2000 * time.Millisecond) t.Assert(array.Len(), 1) t.Assert(cron.Size(), 1) entry.Start() glog.Println("start") - time.Sleep(1200 * time.Millisecond) + time.Sleep(1000 * time.Millisecond) t.Assert(array.Len(), 2) t.Assert(cron.Size(), 1) entry.Close() From 2dd2144dcd69625e962f497af7624d183286d95c Mon Sep 17 00:00:00 2001 From: jflyfox Date: Sun, 24 Jan 2021 21:52:34 +0800 Subject: [PATCH 105/492] improve unit testing case for package gdb/gcron --- .../gdb/gdb_z_mysql_time_maintain_test.go | 32 +++++++++---------- os/gcron/gcron_unit_1_test.go | 26 +++++---------- os/gcron/gcron_unit_2_test.go | 22 +++++++------ 3 files changed, 37 insertions(+), 43 deletions(-) diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index 12d3f7b86..34efc5f22 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -51,7 +51,7 @@ CREATE TABLE %s ( t.Assert(oneInsert["name"].String(), "name_1") t.Assert(oneInsert["delete_at"].String(), "") t.AssertGE(oneInsert["create_at"].GTime().Timestamp(), gtime.Timestamp()-2) - t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp()) + t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) // For time asserting purpose. time.Sleep(2 * time.Second) @@ -73,7 +73,7 @@ CREATE TABLE %s ( t.Assert(oneSave["delete_at"].String(), "") t.Assert(oneSave["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) t.AssertNE(oneSave["update_at"].GTime().Timestamp(), oneInsert["update_at"].GTime().Timestamp()) - t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) // For time asserting purpose. time.Sleep(2 * time.Second) @@ -93,7 +93,7 @@ CREATE TABLE %s ( t.Assert(oneUpdate["name"].String(), "name_1000") t.Assert(oneUpdate["delete_at"].String(), "") t.Assert(oneUpdate["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) - t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Replace dataReplace := g.Map{ @@ -128,7 +128,7 @@ CREATE TABLE %s ( one5, err := db.Table(table).Unscoped().FindOne(1) t.Assert(err, nil) t.Assert(one5["id"].Int(), 1) - t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count i, err := db.Table(table).FindCount() t.Assert(err, nil) @@ -185,7 +185,7 @@ CREATE TABLE %s ( t.Assert(oneInsert["name"].String(), "name_1") t.Assert(oneInsert["deleted_at"].String(), "") t.AssertGE(oneInsert["created_at"].GTime().Timestamp(), gtime.Timestamp()-2) - t.AssertGE(oneInsert["updated_at"].GTime().Timestamp(), gtime.Timestamp()) + t.AssertGE(oneInsert["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) // For time asserting purpose. time.Sleep(2 * time.Second) @@ -207,7 +207,7 @@ CREATE TABLE %s ( t.Assert(oneSave["deleted_at"].String(), "") t.Assert(oneSave["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) t.AssertNE(oneSave["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) - t.AssertGE(oneSave["updated_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneSave["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) // For time asserting purpose. time.Sleep(2 * time.Second) @@ -227,7 +227,7 @@ CREATE TABLE %s ( t.Assert(oneUpdate["name"].String(), "name_1000") t.Assert(oneUpdate["deleted_at"].String(), "") t.Assert(oneUpdate["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) - t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Replace dataReplace := g.Map{ @@ -262,7 +262,7 @@ CREATE TABLE %s ( one5, err := db.Table(table).Unscoped().FindOne(1) t.Assert(err, nil) t.Assert(one5["id"].Int(), 1) - t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count i, err := db.Table(table).FindCount() t.Assert(err, nil) @@ -326,7 +326,7 @@ CREATE TABLE %s ( t.Assert(oneInsert["name"].String(), "name_1") t.Assert(oneInsert["deleted_at"].String(), "") t.AssertGE(oneInsert["created_at"].GTime().Timestamp(), gtime.Timestamp()-2) - t.AssertGE(oneInsert["updated_at"].GTime().Timestamp(), gtime.Timestamp()) + t.AssertGE(oneInsert["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) // For time asserting purpose. time.Sleep(2 * time.Second) @@ -348,7 +348,7 @@ CREATE TABLE %s ( t.Assert(oneSave["deleted_at"].String(), "") t.Assert(oneSave["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) t.AssertNE(oneSave["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) - t.AssertGE(oneSave["updated_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneSave["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) // For time asserting purpose. time.Sleep(2 * time.Second) @@ -368,7 +368,7 @@ CREATE TABLE %s ( t.Assert(oneUpdate["name"].String(), "name_1000") t.Assert(oneUpdate["deleted_at"].String(), "") t.Assert(oneUpdate["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) - t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Replace dataReplace := User{ @@ -403,7 +403,7 @@ CREATE TABLE %s ( one5, err := db.Table(table).Unscoped().FindOne(1) t.Assert(err, nil) t.Assert(one5["id"].Int(), 1) - t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count i, err := db.Table(table).FindCount() t.Assert(err, nil) @@ -648,7 +648,7 @@ CREATE TABLE %s ( t.Assert(oneInsert["name"].String(), "name_1") t.Assert(oneInsert["delete_at"].String(), "") t.AssertGE(oneInsert["create_at"].GTime().Timestamp(), gtime.Timestamp()-2) - t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp()) + t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) time.Sleep(2 * time.Second) @@ -672,7 +672,7 @@ CREATE TABLE %s ( t.Assert(oneSave["delete_at"].String(), "") t.Assert(oneSave["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) t.AssertNE(oneSave["update_at"].GTime().Timestamp(), oneInsert["update_at"].GTime().Timestamp()) - t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) time.Sleep(2 * time.Second) @@ -695,7 +695,7 @@ CREATE TABLE %s ( t.Assert(oneUpdate["name"].String(), "name_1000") t.Assert(oneUpdate["delete_at"].String(), "") t.Assert(oneUpdate["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) - t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Replace dataReplace := &Entity{ @@ -732,7 +732,7 @@ CREATE TABLE %s ( one5, err := db.Table(table).Unscoped().FindOne(1) t.Assert(err, nil) t.Assert(one5["id"].Int(), 1) - t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count i, err := db.Table(table).FindCount() t.Assert(err, nil) diff --git a/os/gcron/gcron_unit_1_test.go b/os/gcron/gcron_unit_1_test.go index 0807f4f4f..c8754498b 100644 --- a/os/gcron/gcron_unit_1_test.go +++ b/os/gcron/gcron_unit_1_test.go @@ -7,7 +7,7 @@ package gcron_test import ( - "github.com/gogf/gf/os/glog" + "github.com/gogf/gf/frame/g" "testing" "time" @@ -21,34 +21,24 @@ func TestCron_Add_Close(t *testing.T) { cron := gcron.New() array := garray.New(true) _, err1 := cron.Add("* * * * * *", func() { - glog.Println("cron1") + g.Log().Println("cron1") array.Append(1) }) _, err2 := cron.Add("* * * * * *", func() { - glog.Println("cron2") + g.Log().Println("cron2") array.Append(1) }, "test") - _, err3 := cron.Add("* * * * * *", func() { - glog.Println("cron3") - array.Append(1) - }, "test") - _, err4 := cron.Add("@every 2s", func() { - glog.Println("cron4") - array.Append(1) - }) t.Assert(err1, nil) t.Assert(err2, nil) - t.AssertNE(err3, nil) - t.Assert(err4, nil) - t.Assert(cron.Size(), 3) + t.Assert(cron.Size(), 2) time.Sleep(1300 * time.Millisecond) t.Assert(array.Len(), 2) - time.Sleep(1400 * time.Millisecond) - t.Assert(array.Len(), 5) + time.Sleep(1300 * time.Millisecond) + t.Assert(array.Len(), 4) cron.Close() - time.Sleep(1200 * time.Millisecond) + time.Sleep(1300 * time.Millisecond) fixedLength := array.Len() - time.Sleep(1200 * time.Millisecond) + time.Sleep(1300 * time.Millisecond) t.Assert(array.Len(), fixedLength) }) } diff --git a/os/gcron/gcron_unit_2_test.go b/os/gcron/gcron_unit_2_test.go index 3d0fffc84..4a0e02ace 100644 --- a/os/gcron/gcron_unit_2_test.go +++ b/os/gcron/gcron_unit_2_test.go @@ -7,21 +7,23 @@ package gcron_test import ( + "github.com/gogf/gf/frame/g" "testing" "time" "github.com/gogf/gf/container/garray" "github.com/gogf/gf/os/gcron" - "github.com/gogf/gf/os/glog" "github.com/gogf/gf/test/gtest" ) func TestCron_Entry_Operations(t *testing.T) { gtest.C(t, func(t *gtest.T) { - cron := gcron.New() - array := garray.New(true) + var ( + cron = gcron.New() + array = garray.New(true) + ) cron.DelayAddTimes(500*time.Millisecond, "* * * * * *", 2, func() { - glog.Println("add times") + g.Log().Println("add times") array.Append(1) }) t.Assert(cron.Size(), 0) @@ -34,16 +36,18 @@ func TestCron_Entry_Operations(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { - cron := gcron.New() - array := garray.New(true) + var ( + cron = gcron.New() + array = garray.New(true) + ) entry, err1 := cron.Add("* * * * * *", func() { - glog.Println("add") + g.Log().Println("add") array.Append(1) }) t.Assert(err1, nil) t.Assert(array.Len(), 0) t.Assert(cron.Size(), 1) - time.Sleep(1200 * time.Millisecond) + time.Sleep(1300 * time.Millisecond) t.Assert(array.Len(), 1) t.Assert(cron.Size(), 1) entry.Stop() @@ -51,7 +55,7 @@ func TestCron_Entry_Operations(t *testing.T) { t.Assert(array.Len(), 1) t.Assert(cron.Size(), 1) entry.Start() - glog.Println("start") + g.Log().Println("start") time.Sleep(1000 * time.Millisecond) t.Assert(array.Len(), 2) t.Assert(cron.Size(), 1) From 9b02f5220a10475ca6844af0b0072f6cbe3065a7 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Sun, 24 Jan 2021 22:33:47 +0800 Subject: [PATCH 106/492] imprive uint testing case for package gtimer --- os/gtimer/gtimer_z_unit_timer_internal_test.go | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/os/gtimer/gtimer_z_unit_timer_internal_test.go b/os/gtimer/gtimer_z_unit_timer_internal_test.go index 64c4ab6c7..3953cf4d3 100644 --- a/os/gtimer/gtimer_z_unit_timer_internal_test.go +++ b/os/gtimer/gtimer_z_unit_timer_internal_test.go @@ -39,20 +39,11 @@ func TestTimer_Proceed(t *testing.T) { timer.AddOnce(2*time.Hour, func() { slice = append(slice, 6) }) - timer.AddOnce(1000*time.Minute, func() { - slice = append(slice, 7) - }) - timer.AddOnce(1100*time.Minute, func() { - slice = append(slice, 8) - }) - timer.AddOnce(1200*time.Minute, func() { - slice = append(slice, 9) - }) - for i := 0; i < 2000000; i++ { + for i := 0; i < 500000; i++ { timer.wheels[0].proceed() time.Sleep(10 * time.Microsecond) } time.Sleep(time.Second) - t.Assert(slice, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}) + t.Assert(slice, []int{1, 2, 3, 4, 5, 6}) }) } From 9f2e69a9e6b8eaa95576f7d29f9c3bd954b7ccea Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 25 Jan 2021 00:04:01 +0800 Subject: [PATCH 107/492] improve model relation feature for package gdb --- database/gdb/gdb_type_result_scanlist.go | 150 ++++++++++--------- database/gdb/gdb_z_mysql_association_test.go | 116 ++++++++++++++ 2 files changed, 193 insertions(+), 73 deletions(-) diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index 5da062227..9494ef514 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -8,11 +8,9 @@ package gdb import ( "database/sql" - "fmt" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" - "github.com/gogf/gf/util/gutil" "reflect" ) @@ -42,19 +40,19 @@ import ( // given parameter. // // See the example or unit testing cases for clear understanding for this function. -func (r Result) ScanList(listPointer interface{}, attributeName string, relation ...string) (err error) { +func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) { // Necessary checks for parameters. - if attributeName == "" { - return gerror.New(`attributeName should not be empty`) - } - if len(relation) > 0 { - if len(relation) < 2 { - return gerror.New(`relation name and key should are both necessary`) - } - if relation[0] == "" || relation[1] == "" { - return gerror.New(`relation name and key should not be empty`) - } + if bindToAttrName == "" { + return gerror.New(`bindToAttrName should not be empty`) } + //if len(relation) > 0 { + // if len(relation) < 2 { + // return gerror.New(`relation name and key should are both necessary`) + // } + // if relation[0] == "" || relation[1] == "" { + // return gerror.New(`relation name and key should not be empty`) + // } + //} var ( reflectValue = reflect.ValueOf(listPointer) @@ -65,12 +63,12 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation reflectKind = reflectValue.Kind() } if reflectKind != reflect.Ptr { - return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) + return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) } reflectValue = reflectValue.Elem() reflectKind = reflectValue.Kind() if reflectKind != reflect.Slice && reflectKind != reflect.Array { - return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) + return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) } length := len(r) if length == 0 { @@ -101,59 +99,61 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation // Relation variables. var ( - relationDataMap map[string]Value - relationFieldName string - relationAttrName string + relationKVStr string + relationDataMap map[string]Value + relationFromAttrName string // Eg: relationKV: User, uid:Uid -> User + relationResultFieldName string // Eg: relationKV: uid:Uid -> uid + relationBindToSubAttrName string // Eg: relationKV: uid:Uid -> Uid ) - if len(relation) > 0 { - array := gstr.Split(relation[1], ":") - if len(array) > 1 { + if len(relationKV) > 0 { + if len(relationKV) == 1 { + relationKVStr = relationKV[0] + } else { + relationFromAttrName = relationKV[0] + relationKVStr = relationKV[1] + } + array := gstr.SplitAndTrim(relationKVStr, ":") + if len(array) == 2 { // Defined table field to relation attribute name. // Like: // uid:Uid // uid:UserId - relationFieldName = array[0] - relationAttrName = array[1] + relationResultFieldName = array[0] + relationBindToSubAttrName = array[1] } else { - relationAttrName = relation[1] - // Find the possible map key by given only struct attribute name. - // Like: - // Uid - if k, _ := gutil.MapPossibleItemByKey(r[0].Map(), relation[1]); k != "" { - relationFieldName = k - } + return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`) } - if relationFieldName != "" { - relationDataMap = r.MapKeyValue(relationFieldName) + if relationResultFieldName != "" { + relationDataMap = r.MapKeyValue(relationResultFieldName) } if len(relationDataMap) == 0 { - return fmt.Errorf(`cannot find the relation data map, maybe invalid relation key given: %s`, relation[1]) + return gerror.Newf(`cannot find the relation data map, maybe invalid relation given "%v"`, relationKV) } } // Bind to target attribute. var ( - ok bool - attrValue reflect.Value - attrKind reflect.Kind - attrType reflect.Type - attrField reflect.StructField + ok bool + bindToAttrValue reflect.Value + bindToAttrKind reflect.Kind + bindToAttrType reflect.Type + bindToAttrField reflect.StructField ) if arrayItemType.Kind() == reflect.Ptr { - if attrField, ok = arrayItemType.Elem().FieldByName(attributeName); !ok { - return fmt.Errorf(`invalid field name: %s`, attributeName) + if bindToAttrField, ok = arrayItemType.Elem().FieldByName(bindToAttrName); !ok { + return gerror.Newf(`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, bindToAttrName) } } else { - if attrField, ok = arrayItemType.FieldByName(attributeName); !ok { - return fmt.Errorf(`invalid field name: %s`, attributeName) + if bindToAttrField, ok = arrayItemType.FieldByName(bindToAttrName); !ok { + return gerror.Newf(`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, bindToAttrName) } } - attrType = attrField.Type - attrKind = attrType.Kind() + bindToAttrType = bindToAttrField.Type + bindToAttrKind = bindToAttrType.Kind() // Bind to relation conditions. var ( - relationValue reflect.Value - relationField reflect.Value + relationFromAttrValue reflect.Value + relationFromAttrField reflect.Value ) for i := 0; i < arrayValue.Len(); i++ { arrayElemValue := arrayValue.Index(i) @@ -173,41 +173,45 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation } else { // Like: []Entity } - attrValue = arrayElemValue.FieldByName(attributeName) - if len(relation) > 0 { - relationValue = arrayElemValue.FieldByName(relation[0]) - if relationValue.Kind() == reflect.Ptr { - relationValue = relationValue.Elem() + bindToAttrValue = arrayElemValue.FieldByName(bindToAttrName) + if relationFromAttrName != "" { + // Attribute value of current slice element. + relationFromAttrValue = arrayElemValue.FieldByName(relationFromAttrName) + if relationFromAttrValue.Kind() == reflect.Ptr { + relationFromAttrValue = relationFromAttrValue.Elem() } + } else { + // Current slice element. + relationFromAttrValue = arrayElemValue } - if len(relationDataMap) > 0 && !relationValue.IsValid() { - return fmt.Errorf(`invalid relation: "%s:%s"`, relation[0], relation[1]) + if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() { + return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } - switch attrKind { + switch bindToAttrKind { case reflect.Array, reflect.Slice: if len(relationDataMap) > 0 { - relationField = relationValue.FieldByName(relationAttrName) - if relationField.IsValid() { + relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) + if relationFromAttrField.IsValid() { if err = gconv.Structs( - relationDataMap[gconv.String(relationField.Interface())], - attrValue.Addr(), + relationDataMap[gconv.String(relationFromAttrField.Interface())], + bindToAttrValue.Addr(), ); err != nil { return err } } else { // May be the attribute does not exist yet. - return fmt.Errorf(`invalid relation: "%s:%s"`, relation[0], relation[1]) + return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } } else { - return fmt.Errorf(`relationKey should not be empty as field "%s" is slice`, attributeName) + return gerror.Newf(`relationKey should not be empty as field "%s" is slice`, bindToAttrName) } case reflect.Ptr: - e := reflect.New(attrType.Elem()).Elem() + e := reflect.New(bindToAttrType.Elem()).Elem() if len(relationDataMap) > 0 { - relationField = relationValue.FieldByName(relationAttrName) - if relationField.IsValid() { - v := relationDataMap[gconv.String(relationField.Interface())] + relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) + if relationFromAttrField.IsValid() { + v := relationDataMap[gconv.String(relationFromAttrField.Interface())] if v == nil { // There's no relational data. continue @@ -217,7 +221,7 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation } } else { // May be the attribute does not exist yet. - return fmt.Errorf(`invalid relation: "%s:%s"`, relation[0], relation[1]) + return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } } else { v := r[i] @@ -229,14 +233,14 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation return err } } - attrValue.Set(e.Addr()) + bindToAttrValue.Set(e.Addr()) case reflect.Struct: - e := reflect.New(attrType).Elem() + e := reflect.New(bindToAttrType).Elem() if len(relationDataMap) > 0 { - relationField = relationValue.FieldByName(relationAttrName) - if relationField.IsValid() { - relationDataItem := relationDataMap[gconv.String(relationField.Interface())] + relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) + if relationFromAttrField.IsValid() { + relationDataItem := relationDataMap[gconv.String(relationFromAttrField.Interface())] if relationDataItem == nil { // There's no relational data. continue @@ -246,7 +250,7 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation } } else { // May be the attribute does not exist yet. - return fmt.Errorf(`invalid relation: "%s:%s"`, relation[0], relation[1]) + return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } } else { relationDataItem := r[i] @@ -258,10 +262,10 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation return err } } - attrValue.Set(e) + bindToAttrValue.Set(e) default: - return fmt.Errorf(`unsupport attribute type: %s`, attrKind.String()) + return gerror.Newf(`unsupported attribute type: %s`, bindToAttrKind.String()) } } reflect.ValueOf(listPointer).Elem().Set(arrayValue) diff --git a/database/gdb/gdb_z_mysql_association_test.go b/database/gdb/gdb_z_mysql_association_test.go index 856377fea..06b462017 100644 --- a/database/gdb/gdb_z_mysql_association_test.go +++ b/database/gdb/gdb_z_mysql_association_test.go @@ -472,3 +472,119 @@ CREATE TABLE %s ( t.Assert(users[1].UserScores[4].Score, 5) }) } + +func Test_Table_Relation_EmbedStruct(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampMicroStr() + tableUserDetail = "user_detail_" + gtime.TimestampMicroStr() + tableUserScores = "user_scores_" + gtime.TimestampMicroStr() + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + name varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + address varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + uid int(10) unsigned NOT NULL, + score int(10) unsigned NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + *EntityUser + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + *EntityUser + *EntityUserDetail + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + + gtest.C(t, func(t *gtest.T) { + var ( + err error + scores []*EntityUserScores + ) + // SELECT * FROM `user_scores` + err = db.Table(tableUserScores).Scan(&scores) + t.Assert(err, nil) + + // SELECT * FROM `user_scores` WHERE `uid` IN(1,2,3,4,5) + err = db.Table(tableUser). + Where("uid", gdb.ListItemValuesUnique(&scores, "Uid")). + ScanList(&scores, "EntityUser", "uid:Uid") + t.Assert(err, nil) + + // SELECT * FROM `user_detail` WHERE `uid` IN(1,2,3,4,5) + err = db.Table(tableUserDetail). + Where("uid", gdb.ListItemValuesUnique(&scores, "Uid")). + ScanList(&scores, "EntityUserDetail", "uid:Uid") + t.Assert(err, nil) + + // Assertions. + t.Assert(len(scores), 25) + t.Assert(scores[0].Id, 1) + t.Assert(scores[0].Uid, 1) + t.Assert(scores[0].Name, "name_1") + t.Assert(scores[0].Address, "address_1") + t.Assert(scores[24].Id, 25) + t.Assert(scores[24].Uid, 5) + t.Assert(scores[24].Name, "name_5") + t.Assert(scores[24].Address, "address_5") + }) +} From 3f2ae3ba6207c9fc5df05ec45fc48d92f8e0e136 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Mon, 25 Jan 2021 14:54:38 +0800 Subject: [PATCH 108/492] add tracing feature for ghttp.Client --- .../client/tracing}/jaeger/main.go | 30 +- net/ghttp/ghttp_client.go | 441 +++++++++++------- net/ghttp/ghttp_client_api.go | 295 ------------ net/ghttp/ghttp_client_middleware.go | 48 -- net/ghttp/ghttp_func.go | 51 +- net/ghttp/ghttp_middleware.go | 16 + net/ghttp/ghttp_unit_client_dump_test.go | 4 +- net/ghttp/ghttp_unit_client_test.go | 10 +- net/ghttp/internal/client/client.go | 237 ++++++++++ .../client/client_bytes.go} | 2 +- .../client/client_chain.go} | 23 +- .../client/client_content.go} | 2 +- .../client/client_dump.go} | 14 +- .../internal/client/client_middleware.go | 48 ++ .../client/client_request.go} | 61 +-- .../client/client_response.go} | 20 +- net/ghttp/internal/client/client_tracing.go | 72 +++ .../internal/client/client_tracing_tracer.go | 171 +++++++ .../client/client_var.go} | 2 +- net/ghttp/internal/httputil/httputils.go | 66 +++ net/gtrace/gtrace_http_client.go | 242 ---------- 21 files changed, 963 insertions(+), 892 deletions(-) rename .example/net/{gtrace => ghttp/client/tracing}/jaeger/main.go (74%) delete mode 100644 net/ghttp/ghttp_client_api.go delete mode 100644 net/ghttp/ghttp_client_middleware.go create mode 100644 net/ghttp/ghttp_middleware.go create mode 100644 net/ghttp/internal/client/client.go rename net/ghttp/{ghttp_client_bytes.go => internal/client/client_bytes.go} (99%) rename net/ghttp/{ghttp_client_chain.go => internal/client/client_chain.go} (91%) rename net/ghttp/{ghttp_client_content.go => internal/client/client_content.go} (99%) rename net/ghttp/{ghttp_client_dump.go => internal/client/client_dump.go} (88%) create mode 100644 net/ghttp/internal/client/client_middleware.go rename net/ghttp/{ghttp_client_request.go => internal/client/client_request.go} (83%) rename net/ghttp/{ghttp_client_response.go => internal/client/client_response.go} (74%) create mode 100644 net/ghttp/internal/client/client_tracing.go create mode 100644 net/ghttp/internal/client/client_tracing_tracer.go rename net/ghttp/{ghttp_client_var.go => internal/client/client_var.go} (99%) create mode 100644 net/ghttp/internal/httputil/httputils.go delete mode 100644 net/gtrace/gtrace_http_client.go diff --git a/.example/net/gtrace/jaeger/main.go b/.example/net/ghttp/client/tracing/jaeger/main.go similarity index 74% rename from .example/net/gtrace/jaeger/main.go rename to .example/net/ghttp/client/tracing/jaeger/main.go index 87d32d26e..c845c8307 100644 --- a/.example/net/gtrace/jaeger/main.go +++ b/.example/net/ghttp/client/tracing/jaeger/main.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" "go.opentelemetry.io/otel/exporters/trace/jaeger" "go.opentelemetry.io/otel/trace" "log" @@ -57,16 +58,27 @@ func main() { flush := initTracer() defer flush() - ctx, span := otel.Tracer("component-main").Start(ctx, "foo") + ctx, span := otel.Tracer("test").Start(ctx, "test") defer span.End() - content := g.Client().Ctx(ctx).Header(g.MapStrStr{ - "test": "123", - "john": "smith", - }).Cookie(g.MapStrStr{ - "cookieKey":"cookieValue", - }).GetContent("http://baidu.com/?q=goframe") - fmt.Println(content) + for i := 0; i < 20; i++ { + g.Client().Use(ghttp.MiddlewareClientTracing).Ctx(ctx).Header(g.MapStrStr{ + "test": "123", + "john": "smith", + }).Cookie(g.MapStrStr{ + "cookieKey": "cookieValue", + }).GetContent(fmt.Sprintf("http://baidu.com/?q=test_%d", i)) + } + foo(ctx) +} + +func foo(ctx context.Context) { + ctx, span := otel.Tracer("test").Start(ctx, "foo") + defer span.End() + span.AddEvent("Nice operation!", trace.WithAttributes(label.Int("bogons", 100))) + span.SetAttributes(label.String("test2", "123")) + time.Sleep(time.Second * 1) + bar(ctx) } func bar(ctx context.Context) { @@ -74,6 +86,6 @@ func bar(ctx context.Context) { defer span.End() span.AddEvent("Nice operation!", trace.WithAttributes(label.Int("bogons", 100))) span.SetAttributes(label.String("test2", "123")) - time.Sleep(time.Second * 2) + time.Sleep(time.Second * 1) // Do bar... } diff --git a/net/ghttp/ghttp_client.go b/net/ghttp/ghttp_client.go index 089efc7c9..c88e0ef3d 100644 --- a/net/ghttp/ghttp_client.go +++ b/net/ghttp/ghttp_client.go @@ -7,224 +7,303 @@ package ghttp import ( - "context" - "crypto/tls" - "fmt" - "github.com/gogf/gf" - "github.com/gogf/gf/text/gstr" - "golang.org/x/net/proxy" - "net" - "net/http" - "net/url" - "strings" - "time" - - "github.com/gogf/gf/text/gregex" + "github.com/gogf/gf/container/gvar" + "github.com/gogf/gf/net/ghttp/internal/client" ) -// Client is the HTTP client for HTTP request management. -type Client struct { - http.Client // Underlying HTTP Client. - ctx context.Context // Context for each request. - agent string // Client agent. - parent *Client // Parent http client, this is used for chaining operations. - header map[string]string // Custom header map. - cookies map[string]string // Custom cookie map. - prefix string // Prefix for request. - authUser string // HTTP basic authentication: user. - authPass string // HTTP basic authentication: pass. - browserMode bool // Whether auto saving and sending cookie content. - retryCount int // Retry count when request fails. - retryInterval time.Duration // Retry interval when request fails. - middlewareHandler []ClientHandlerFunc // Interceptor handlers -} - -var ( - defaultClientAgent = fmt.Sprintf(`GoFrameHTTPClient %s`, gf.VERSION) +type ( + Client = client.Client + ClientResponse = client.Response + ClientHandlerFunc = client.HandlerFunc ) -// NewClient creates and returns a new HTTP client object. +// New creates and returns a new HTTP client object. func NewClient() *Client { - return &Client{ - Client: http.Client{ - Transport: &http.Transport{ - // No validation for https certification of the server in default. - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - DisableKeepAlives: true, - }, - }, - header: make(map[string]string), - cookies: make(map[string]string), - agent: defaultClientAgent, - } + return client.New() } -// Clone clones current client and returns a new one. -func (c *Client) Clone() *Client { - newClient := NewClient() - *newClient = *c - newClient.header = make(map[string]string) - newClient.cookies = make(map[string]string) - for k, v := range c.header { - newClient.header[k] = v - } - for k, v := range c.cookies { - newClient.cookies[k] = v - } - return newClient +// Get is a convenience method for sending GET request. +// NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Get or NewClient().Get instead. +func Get(url string, data ...interface{}) (*ClientResponse, error) { + return DoRequest("GET", url, data...) } -// SetBrowserMode enables browser mode of the client. -// When browser mode is enabled, it automatically saves and sends cookie content -// from and to server. -func (c *Client) SetBrowserMode(enabled bool) *Client { - c.browserMode = enabled - return c +// Put is a convenience method for sending PUT request. +// NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Put or NewClient().Put instead. +func Put(url string, data ...interface{}) (*ClientResponse, error) { + return DoRequest("PUT", url, data...) } -// SetHeader sets a custom HTTP header pair for the client. -func (c *Client) SetHeader(key, value string) *Client { - c.header[key] = value - return c +// Post is a convenience method for sending POST request. +// NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Post or NewClient().Post instead. +func Post(url string, data ...interface{}) (*ClientResponse, error) { + return DoRequest("POST", url, data...) } -// SetHeaderMap sets custom HTTP headers with map. -func (c *Client) SetHeaderMap(m map[string]string) *Client { - for k, v := range m { - c.header[k] = v - } - return c +// Delete is a convenience method for sending DELETE request. +// NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Delete or NewClient().Delete instead. +func Delete(url string, data ...interface{}) (*ClientResponse, error) { + return DoRequest("DELETE", url, data...) } -// SetAgent sets the User-Agent header for client. -func (c *Client) SetAgent(agent string) *Client { - c.header["User-Agent"] = agent - return c +// Head is a convenience method for sending HEAD request. +// NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Head or NewClient().Head instead. +func Head(url string, data ...interface{}) (*ClientResponse, error) { + return DoRequest("HEAD", url, data...) } -// SetContentType sets HTTP content type for the client. -func (c *Client) SetContentType(contentType string) *Client { - c.header["Content-Type"] = contentType - return c +// Patch is a convenience method for sending PATCH request. +// NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Patch or NewClient().Patch instead. +func Patch(url string, data ...interface{}) (*ClientResponse, error) { + return DoRequest("PATCH", url, data...) } -// SetHeaderRaw sets custom HTTP header using raw string. -func (c *Client) SetHeaderRaw(headers string) *Client { - for _, line := range gstr.SplitAndTrim(headers, "\n") { - array, _ := gregex.MatchString(`^([\w\-]+):\s*(.+)`, line) - if len(array) >= 3 { - c.header[array[1]] = array[2] - } - } - return c +// Connect is a convenience method for sending CONNECT request. +// NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Connect or NewClient().Connect instead. +func Connect(url string, data ...interface{}) (*ClientResponse, error) { + return DoRequest("CONNECT", url, data...) } -// SetCookie sets a cookie pair for the client. -func (c *Client) SetCookie(key, value string) *Client { - c.cookies[key] = value - return c +// Options is a convenience method for sending OPTIONS request. +// NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Options or NewClient().Options instead. +func Options(url string, data ...interface{}) (*ClientResponse, error) { + return DoRequest("OPTIONS", url, data...) } -// SetCookieMap sets cookie items with map. -func (c *Client) SetCookieMap(m map[string]string) *Client { - for k, v := range m { - c.cookies[k] = v - } - return c +// Trace is a convenience method for sending TRACE request. +// NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().Trace or NewClient().Trace instead. +func Trace(url string, data ...interface{}) (*ClientResponse, error) { + return DoRequest("TRACE", url, data...) } -// SetPrefix sets the request server URL prefix. -func (c *Client) SetPrefix(prefix string) *Client { - c.prefix = prefix - return c +// DoRequest is a convenience method for sending custom http method request. +// NOTE that remembers CLOSING the response object when it'll never be used. +// Deprecated, please use g.Client().DoRequest or NewClient().DoRequest instead. +func DoRequest(method, url string, data ...interface{}) (*ClientResponse, error) { + return client.New().DoRequest(method, url, data...) } -// SetTimeOut sets the request timeout for the client. -func (c *Client) SetTimeout(t time.Duration) *Client { - c.Client.Timeout = t - return c +// GetContent is a convenience method for sending GET request, which retrieves and returns +// the result content and automatically closes response object. +// Deprecated, please use g.Client().GetContent or NewClient().GetContent instead. +func GetContent(url string, data ...interface{}) string { + return RequestContent("GET", url, data...) } -// SetBasicAuth sets HTTP basic authentication information for the client. -func (c *Client) SetBasicAuth(user, pass string) *Client { - c.authUser = user - c.authPass = pass - return c +// PutContent is a convenience method for sending PUT request, which retrieves and returns +// the result content and automatically closes response object. +// Deprecated, please use g.Client().PutContent or NewClient().PutContent instead. +func PutContent(url string, data ...interface{}) string { + return RequestContent("PUT", url, data...) } -// SetCtx sets context for each request of this client. -func (c *Client) SetCtx(ctx context.Context) *Client { - c.ctx = ctx - return c +// PostContent is a convenience method for sending POST request, which retrieves and returns +// the result content and automatically closes response object. +// Deprecated, please use g.Client().PostContent or NewClient().PostContent instead. +func PostContent(url string, data ...interface{}) string { + return RequestContent("POST", url, data...) } -// SetRetry sets retry count and interval. -func (c *Client) SetRetry(retryCount int, retryInterval time.Duration) *Client { - c.retryCount = retryCount - c.retryInterval = retryInterval - return c +// DeleteContent is a convenience method for sending DELETE request, which retrieves and returns +// the result content and automatically closes response object. +// Deprecated, please use g.Client().DeleteContent or NewClient().DeleteContent instead. +func DeleteContent(url string, data ...interface{}) string { + return RequestContent("DELETE", url, data...) } -// SetRedirectLimit limit the number of jumps -func (c *Client) SetRedirectLimit(redirectLimit int) *Client { - c.CheckRedirect = func(req *http.Request, via []*http.Request) error { - if len(via) >= redirectLimit { - return http.ErrUseLastResponse - } - return nil - } - return c +// HeadContent is a convenience method for sending HEAD request, which retrieves and returns +// the result content and automatically closes response object. +// Deprecated, please use g.Client().HeadContent or NewClient().HeadContent instead. +func HeadContent(url string, data ...interface{}) string { + return RequestContent("HEAD", url, data...) } -// SetProxy set proxy for the client. -// This func will do nothing when the parameter `proxyURL` is empty or in wrong pattern. -// The correct pattern is like `http://USER:PASSWORD@IP:PORT` or `socks5://USER:PASSWORD@IP:PORT`. -// Only `http` and `socks5` proxies are supported currently. -func (c *Client) SetProxy(proxyURL string) { - if strings.TrimSpace(proxyURL) == "" { - return - } - _proxy, err := url.Parse(proxyURL) +// PatchContent is a convenience method for sending PATCH request, which retrieves and returns +// the result content and automatically closes response object. +// Deprecated, please use g.Client().PatchContent or NewClient().PatchContent instead. +func PatchContent(url string, data ...interface{}) string { + return RequestContent("PATCH", url, data...) +} + +// ConnectContent is a convenience method for sending CONNECT request, which retrieves and returns +// the result content and automatically closes response object. +// Deprecated, please use g.Client().ConnectContent or NewClient().ConnectContent instead. +func ConnectContent(url string, data ...interface{}) string { + return RequestContent("CONNECT", url, data...) +} + +// OptionsContent is a convenience method for sending OPTIONS request, which retrieves and returns +// the result content and automatically closes response object. +// Deprecated, please use g.Client().OptionsContent or NewClient().OptionsContent instead. +func OptionsContent(url string, data ...interface{}) string { + return RequestContent("OPTIONS", url, data...) +} + +// TraceContent is a convenience method for sending TRACE request, which retrieves and returns +// the result content and automatically closes response object. +// Deprecated, please use g.Client().TraceContent or NewClient().TraceContent instead. +func TraceContent(url string, data ...interface{}) string { + return RequestContent("TRACE", url, data...) +} + +// RequestContent is a convenience method for sending custom http method request, which +// retrieves and returns the result content and automatically closes response object. +// Deprecated, please use g.Client().RequestContent or NewClient().RequestContent instead. +func RequestContent(method string, url string, data ...interface{}) string { + return client.New().RequestContent(method, url, data...) +} + +// GetBytes is a convenience method for sending GET request, which retrieves and returns +// the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().GetBytes or NewClient().GetBytes instead. +func GetBytes(url string, data ...interface{}) []byte { + return RequestBytes("GET", url, data...) +} + +// PutBytes is a convenience method for sending PUT request, which retrieves and returns +// the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().PutBytes or NewClient().PutBytes instead. +func PutBytes(url string, data ...interface{}) []byte { + return RequestBytes("PUT", url, data...) +} + +// PostBytes is a convenience method for sending POST request, which retrieves and returns +// the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().PostBytes or NewClient().PostBytes instead. +func PostBytes(url string, data ...interface{}) []byte { + return RequestBytes("POST", url, data...) +} + +// DeleteBytes is a convenience method for sending DELETE request, which retrieves and returns +// the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().DeleteBytes or NewClient().DeleteBytes instead. +func DeleteBytes(url string, data ...interface{}) []byte { + return RequestBytes("DELETE", url, data...) +} + +// HeadBytes is a convenience method for sending HEAD request, which retrieves and returns +// the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().HeadBytes or NewClient().HeadBytes instead. +func HeadBytes(url string, data ...interface{}) []byte { + return RequestBytes("HEAD", url, data...) +} + +// PatchBytes is a convenience method for sending PATCH request, which retrieves and returns +// the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().PatchBytes or NewClient().PatchBytes instead. +func PatchBytes(url string, data ...interface{}) []byte { + return RequestBytes("PATCH", url, data...) +} + +// ConnectBytes is a convenience method for sending CONNECT request, which retrieves and returns +// the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().ConnectBytes or NewClient().ConnectBytes instead. +func ConnectBytes(url string, data ...interface{}) []byte { + return RequestBytes("CONNECT", url, data...) +} + +// OptionsBytes is a convenience method for sending OPTIONS request, which retrieves and returns +// the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().OptionsBytes or NewClient().OptionsBytes instead. +func OptionsBytes(url string, data ...interface{}) []byte { + return RequestBytes("OPTIONS", url, data...) +} + +// TraceBytes is a convenience method for sending TRACE request, which retrieves and returns +// the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().TraceBytes or NewClient().TraceBytes instead. +func TraceBytes(url string, data ...interface{}) []byte { + return RequestBytes("TRACE", url, data...) +} + +// RequestBytes is a convenience method for sending custom http method request, which +// retrieves and returns the result content as bytes and automatically closes response object. +// Deprecated, please use g.Client().RequestBytes or NewClient().RequestBytes instead. +func RequestBytes(method string, url string, data ...interface{}) []byte { + return client.New().RequestBytes(method, url, data...) +} + +// GetVar sends a GET request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().GetVar or NewClient().GetVar instead. +func GetVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("GET", url, data...) +} + +// PutVar sends a PUT request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().PutVar or NewClient().PutVar instead. +func PutVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("PUT", url, data...) +} + +// PostVar sends a POST request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().PostVar or NewClient().PostVar instead. +func PostVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("POST", url, data...) +} + +// DeleteVar sends a DELETE request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().DeleteVar or NewClient().DeleteVar instead. +func DeleteVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("DELETE", url, data...) +} + +// HeadVar sends a HEAD request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().HeadVar or NewClient().HeadVar instead. +func HeadVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("HEAD", url, data...) +} + +// PatchVar sends a PATCH request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().PatchVar or NewClient().PatchVar instead. +func PatchVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("PATCH", url, data...) +} + +// ConnectVar sends a CONNECT request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().ConnectVar or NewClient().ConnectVar instead. +func ConnectVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("CONNECT", url, data...) +} + +// OptionsVar sends a OPTIONS request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().OptionsVar or NewClient().OptionsVar instead. +func OptionsVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("OPTIONS", url, data...) +} + +// TraceVar sends a TRACE request, retrieves and converts the result content to specified pointer. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().TraceVar or NewClient().TraceVar instead. +func TraceVar(url string, data ...interface{}) *gvar.Var { + return RequestVar("TRACE", url, data...) +} + +// RequestVar sends request using given HTTP method and data, retrieves converts the result +// to specified pointer. It reads and closes the response object internally automatically. +// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et +// Deprecated, please use g.Client().RequestVar or NewClient().RequestVar instead. +func RequestVar(method string, url string, data ...interface{}) *gvar.Var { + response, err := DoRequest(method, url, data...) if err != nil { - return - } - if _proxy.Scheme == "http" { - if _, ok := c.Transport.(*http.Transport); ok { - c.Transport.(*http.Transport).Proxy = http.ProxyURL(_proxy) - } - } else { - var auth = &proxy.Auth{} - user := _proxy.User.Username() - - if user != "" { - auth.User = user - password, hasPassword := _proxy.User.Password() - if hasPassword && password != "" { - auth.Password = password - } - } else { - auth = nil - } - // refer to the source code, error is always nil - dialer, err := proxy.SOCKS5( - "tcp", - _proxy.Host, - auth, - &net.Dialer{ - Timeout: c.Client.Timeout, - KeepAlive: c.Client.Timeout, - }, - ) - if err != nil { - return - } - if _, ok := c.Transport.(*http.Transport); ok { - c.Transport.(*http.Transport).DialContext = func(ctx context.Context, network, addr string) (conn net.Conn, e error) { - return dialer.Dial(network, addr) - } - } - //c.SetTimeout(10*time.Second) + return gvar.New(nil) } + defer response.Close() + return gvar.New(response.ReadAll()) } diff --git a/net/ghttp/ghttp_client_api.go b/net/ghttp/ghttp_client_api.go deleted file mode 100644 index 737f02f61..000000000 --- a/net/ghttp/ghttp_client_api.go +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package ghttp - -import "github.com/gogf/gf/container/gvar" - -// Get is a convenience method for sending GET request. -// NOTE that remembers CLOSING the response object when it'll never be used. -// Deprecated, please use g.Client().Get or NewClient().Get instead. -func Get(url string, data ...interface{}) (*ClientResponse, error) { - return DoRequest("GET", url, data...) -} - -// Put is a convenience method for sending PUT request. -// NOTE that remembers CLOSING the response object when it'll never be used. -// Deprecated, please use g.Client().Put or NewClient().Put instead. -func Put(url string, data ...interface{}) (*ClientResponse, error) { - return DoRequest("PUT", url, data...) -} - -// Post is a convenience method for sending POST request. -// NOTE that remembers CLOSING the response object when it'll never be used. -// Deprecated, please use g.Client().Post or NewClient().Post instead. -func Post(url string, data ...interface{}) (*ClientResponse, error) { - return DoRequest("POST", url, data...) -} - -// Delete is a convenience method for sending DELETE request. -// NOTE that remembers CLOSING the response object when it'll never be used. -// Deprecated, please use g.Client().Delete or NewClient().Delete instead. -func Delete(url string, data ...interface{}) (*ClientResponse, error) { - return DoRequest("DELETE", url, data...) -} - -// Head is a convenience method for sending HEAD request. -// NOTE that remembers CLOSING the response object when it'll never be used. -// Deprecated, please use g.Client().Head or NewClient().Head instead. -func Head(url string, data ...interface{}) (*ClientResponse, error) { - return DoRequest("HEAD", url, data...) -} - -// Patch is a convenience method for sending PATCH request. -// NOTE that remembers CLOSING the response object when it'll never be used. -// Deprecated, please use g.Client().Patch or NewClient().Patch instead. -func Patch(url string, data ...interface{}) (*ClientResponse, error) { - return DoRequest("PATCH", url, data...) -} - -// Connect is a convenience method for sending CONNECT request. -// NOTE that remembers CLOSING the response object when it'll never be used. -// Deprecated, please use g.Client().Connect or NewClient().Connect instead. -func Connect(url string, data ...interface{}) (*ClientResponse, error) { - return DoRequest("CONNECT", url, data...) -} - -// Options is a convenience method for sending OPTIONS request. -// NOTE that remembers CLOSING the response object when it'll never be used. -// Deprecated, please use g.Client().Options or NewClient().Options instead. -func Options(url string, data ...interface{}) (*ClientResponse, error) { - return DoRequest("OPTIONS", url, data...) -} - -// Trace is a convenience method for sending TRACE request. -// NOTE that remembers CLOSING the response object when it'll never be used. -// Deprecated, please use g.Client().Trace or NewClient().Trace instead. -func Trace(url string, data ...interface{}) (*ClientResponse, error) { - return DoRequest("TRACE", url, data...) -} - -// DoRequest is a convenience method for sending custom http method request. -// NOTE that remembers CLOSING the response object when it'll never be used. -// Deprecated, please use g.Client().DoRequest or NewClient().DoRequest instead. -func DoRequest(method, url string, data ...interface{}) (*ClientResponse, error) { - return NewClient().DoRequest(method, url, data...) -} - -// GetContent is a convenience method for sending GET request, which retrieves and returns -// the result content and automatically closes response object. -// Deprecated, please use g.Client().GetContent or NewClient().GetContent instead. -func GetContent(url string, data ...interface{}) string { - return RequestContent("GET", url, data...) -} - -// PutContent is a convenience method for sending PUT request, which retrieves and returns -// the result content and automatically closes response object. -// Deprecated, please use g.Client().PutContent or NewClient().PutContent instead. -func PutContent(url string, data ...interface{}) string { - return RequestContent("PUT", url, data...) -} - -// PostContent is a convenience method for sending POST request, which retrieves and returns -// the result content and automatically closes response object. -// Deprecated, please use g.Client().PostContent or NewClient().PostContent instead. -func PostContent(url string, data ...interface{}) string { - return RequestContent("POST", url, data...) -} - -// DeleteContent is a convenience method for sending DELETE request, which retrieves and returns -// the result content and automatically closes response object. -// Deprecated, please use g.Client().DeleteContent or NewClient().DeleteContent instead. -func DeleteContent(url string, data ...interface{}) string { - return RequestContent("DELETE", url, data...) -} - -// HeadContent is a convenience method for sending HEAD request, which retrieves and returns -// the result content and automatically closes response object. -// Deprecated, please use g.Client().HeadContent or NewClient().HeadContent instead. -func HeadContent(url string, data ...interface{}) string { - return RequestContent("HEAD", url, data...) -} - -// PatchContent is a convenience method for sending PATCH request, which retrieves and returns -// the result content and automatically closes response object. -// Deprecated, please use g.Client().PatchContent or NewClient().PatchContent instead. -func PatchContent(url string, data ...interface{}) string { - return RequestContent("PATCH", url, data...) -} - -// ConnectContent is a convenience method for sending CONNECT request, which retrieves and returns -// the result content and automatically closes response object. -// Deprecated, please use g.Client().ConnectContent or NewClient().ConnectContent instead. -func ConnectContent(url string, data ...interface{}) string { - return RequestContent("CONNECT", url, data...) -} - -// OptionsContent is a convenience method for sending OPTIONS request, which retrieves and returns -// the result content and automatically closes response object. -// Deprecated, please use g.Client().OptionsContent or NewClient().OptionsContent instead. -func OptionsContent(url string, data ...interface{}) string { - return RequestContent("OPTIONS", url, data...) -} - -// TraceContent is a convenience method for sending TRACE request, which retrieves and returns -// the result content and automatically closes response object. -// Deprecated, please use g.Client().TraceContent or NewClient().TraceContent instead. -func TraceContent(url string, data ...interface{}) string { - return RequestContent("TRACE", url, data...) -} - -// RequestContent is a convenience method for sending custom http method request, which -// retrieves and returns the result content and automatically closes response object. -// Deprecated, please use g.Client().RequestContent or NewClient().RequestContent instead. -func RequestContent(method string, url string, data ...interface{}) string { - return NewClient().RequestContent(method, url, data...) -} - -// GetBytes is a convenience method for sending GET request, which retrieves and returns -// the result content as bytes and automatically closes response object. -// Deprecated, please use g.Client().GetBytes or NewClient().GetBytes instead. -func GetBytes(url string, data ...interface{}) []byte { - return RequestBytes("GET", url, data...) -} - -// PutBytes is a convenience method for sending PUT request, which retrieves and returns -// the result content as bytes and automatically closes response object. -// Deprecated, please use g.Client().PutBytes or NewClient().PutBytes instead. -func PutBytes(url string, data ...interface{}) []byte { - return RequestBytes("PUT", url, data...) -} - -// PostBytes is a convenience method for sending POST request, which retrieves and returns -// the result content as bytes and automatically closes response object. -// Deprecated, please use g.Client().PostBytes or NewClient().PostBytes instead. -func PostBytes(url string, data ...interface{}) []byte { - return RequestBytes("POST", url, data...) -} - -// DeleteBytes is a convenience method for sending DELETE request, which retrieves and returns -// the result content as bytes and automatically closes response object. -// Deprecated, please use g.Client().DeleteBytes or NewClient().DeleteBytes instead. -func DeleteBytes(url string, data ...interface{}) []byte { - return RequestBytes("DELETE", url, data...) -} - -// HeadBytes is a convenience method for sending HEAD request, which retrieves and returns -// the result content as bytes and automatically closes response object. -// Deprecated, please use g.Client().HeadBytes or NewClient().HeadBytes instead. -func HeadBytes(url string, data ...interface{}) []byte { - return RequestBytes("HEAD", url, data...) -} - -// PatchBytes is a convenience method for sending PATCH request, which retrieves and returns -// the result content as bytes and automatically closes response object. -// Deprecated, please use g.Client().PatchBytes or NewClient().PatchBytes instead. -func PatchBytes(url string, data ...interface{}) []byte { - return RequestBytes("PATCH", url, data...) -} - -// ConnectBytes is a convenience method for sending CONNECT request, which retrieves and returns -// the result content as bytes and automatically closes response object. -// Deprecated, please use g.Client().ConnectBytes or NewClient().ConnectBytes instead. -func ConnectBytes(url string, data ...interface{}) []byte { - return RequestBytes("CONNECT", url, data...) -} - -// OptionsBytes is a convenience method for sending OPTIONS request, which retrieves and returns -// the result content as bytes and automatically closes response object. -// Deprecated, please use g.Client().OptionsBytes or NewClient().OptionsBytes instead. -func OptionsBytes(url string, data ...interface{}) []byte { - return RequestBytes("OPTIONS", url, data...) -} - -// TraceBytes is a convenience method for sending TRACE request, which retrieves and returns -// the result content as bytes and automatically closes response object. -// Deprecated, please use g.Client().TraceBytes or NewClient().TraceBytes instead. -func TraceBytes(url string, data ...interface{}) []byte { - return RequestBytes("TRACE", url, data...) -} - -// RequestBytes is a convenience method for sending custom http method request, which -// retrieves and returns the result content as bytes and automatically closes response object. -// Deprecated, please use g.Client().RequestBytes or NewClient().RequestBytes instead. -func RequestBytes(method string, url string, data ...interface{}) []byte { - return NewClient().RequestBytes(method, url, data...) -} - -// GetVar sends a GET request, retrieves and converts the result content to specified pointer. -// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et -// Deprecated, please use g.Client().GetVar or NewClient().GetVar instead. -func GetVar(url string, data ...interface{}) *gvar.Var { - return RequestVar("GET", url, data...) -} - -// PutVar sends a PUT request, retrieves and converts the result content to specified pointer. -// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et -// Deprecated, please use g.Client().PutVar or NewClient().PutVar instead. -func PutVar(url string, data ...interface{}) *gvar.Var { - return RequestVar("PUT", url, data...) -} - -// PostVar sends a POST request, retrieves and converts the result content to specified pointer. -// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et -// Deprecated, please use g.Client().PostVar or NewClient().PostVar instead. -func PostVar(url string, data ...interface{}) *gvar.Var { - return RequestVar("POST", url, data...) -} - -// DeleteVar sends a DELETE request, retrieves and converts the result content to specified pointer. -// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et -// Deprecated, please use g.Client().DeleteVar or NewClient().DeleteVar instead. -func DeleteVar(url string, data ...interface{}) *gvar.Var { - return RequestVar("DELETE", url, data...) -} - -// HeadVar sends a HEAD request, retrieves and converts the result content to specified pointer. -// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et -// Deprecated, please use g.Client().HeadVar or NewClient().HeadVar instead. -func HeadVar(url string, data ...interface{}) *gvar.Var { - return RequestVar("HEAD", url, data...) -} - -// PatchVar sends a PATCH request, retrieves and converts the result content to specified pointer. -// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et -// Deprecated, please use g.Client().PatchVar or NewClient().PatchVar instead. -func PatchVar(url string, data ...interface{}) *gvar.Var { - return RequestVar("PATCH", url, data...) -} - -// ConnectVar sends a CONNECT request, retrieves and converts the result content to specified pointer. -// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et -// Deprecated, please use g.Client().ConnectVar or NewClient().ConnectVar instead. -func ConnectVar(url string, data ...interface{}) *gvar.Var { - return RequestVar("CONNECT", url, data...) -} - -// OptionsVar sends a OPTIONS request, retrieves and converts the result content to specified pointer. -// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et -// Deprecated, please use g.Client().OptionsVar or NewClient().OptionsVar instead. -func OptionsVar(url string, data ...interface{}) *gvar.Var { - return RequestVar("OPTIONS", url, data...) -} - -// TraceVar sends a TRACE request, retrieves and converts the result content to specified pointer. -// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et -// Deprecated, please use g.Client().TraceVar or NewClient().TraceVar instead. -func TraceVar(url string, data ...interface{}) *gvar.Var { - return RequestVar("TRACE", url, data...) -} - -// RequestVar sends request using given HTTP method and data, retrieves converts the result -// to specified pointer. It reads and closes the response object internally automatically. -// The parameter can be type of: struct/*struct/**struct/[]struct/[]*struct/*[]struct, et -// Deprecated, please use g.Client().RequestVar or NewClient().RequestVar instead. -func RequestVar(method string, url string, data ...interface{}) *gvar.Var { - response, err := DoRequest(method, url, data...) - if err != nil { - return gvar.New(nil) - } - defer response.Close() - return gvar.New(response.ReadAll()) -} diff --git a/net/ghttp/ghttp_client_middleware.go b/net/ghttp/ghttp_client_middleware.go deleted file mode 100644 index 505d85bf5..000000000 --- a/net/ghttp/ghttp_client_middleware.go +++ /dev/null @@ -1,48 +0,0 @@ -package ghttp - -import ( - "net/http" -) - -// ClientHandlerFunc middleware handler func -type ClientHandlerFunc = func(c *Client, r *http.Request) (*ClientResponse, error) - -// clientMiddleware is the plugin for http client request workflow management. -type clientMiddleware struct { - client *Client // http client. - handlers []ClientHandlerFunc // mdl handlers. - handlerIndex int // current handler index. - resp *ClientResponse // save resp. - err error // save err. -} - -const clientMiddlewareKey = "__clientMiddlewareKey" - -// Use adds one or more middleware handlers to client. -func (c *Client) Use(handlers ...ClientHandlerFunc) *Client { - c.middlewareHandler = append(c.middlewareHandler, handlers...) - return c -} - -// MiddlewareNext calls next middleware. -// This is should only be call in ClientHandlerFunc. -func (c *Client) MiddlewareNext(req *http.Request) (*ClientResponse, error) { - if v := req.Context().Value(clientMiddlewareKey); v != nil { - if m, ok := v.(*clientMiddleware); ok { - return m.Next(req) - } - } - return c.callRequest(req) -} - -// Next calls next middleware handler. -func (m *clientMiddleware) Next(req *http.Request) (resp *ClientResponse, err error) { - if m.err != nil { - return m.resp, m.err - } - if m.handlerIndex < len(m.handlers) { - m.handlerIndex++ - m.resp, m.err = m.handlers[m.handlerIndex](m.client, req) - } - return m.resp, m.err -} diff --git a/net/ghttp/ghttp_func.go b/net/ghttp/ghttp_func.go index 3a03bdfea..7047f0317 100644 --- a/net/ghttp/ghttp_func.go +++ b/net/ghttp/ghttp_func.go @@ -8,15 +8,7 @@ package ghttp import ( "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/text/gstr" - "strings" - - "github.com/gogf/gf/encoding/gurl" - "github.com/gogf/gf/util/gconv" -) - -const ( - fileUploadingKey = "@file:" + "github.com/gogf/gf/net/ghttp/internal/httputil" ) // BuildParams builds the request string for the http client. The can be type of: @@ -24,46 +16,7 @@ const ( // // The optional parameter specifies whether ignore the url encoding for the data. func BuildParams(params interface{}, noUrlEncode ...bool) (encodedParamStr string) { - // If given string/[]byte, converts and returns it directly as string. - switch v := params.(type) { - case string, []byte: - return gconv.String(params) - case []interface{}: - if len(v) > 0 { - params = v[0] - } else { - params = nil - } - } - // Else converts it to map and does the url encoding. - m, urlEncode := gconv.Map(params), true - if len(m) == 0 { - return gconv.String(params) - } - if len(noUrlEncode) == 1 { - urlEncode = !noUrlEncode[0] - } - // If there's file uploading, it ignores the url encoding. - if urlEncode { - for k, v := range m { - if gstr.Contains(k, fileUploadingKey) || gstr.Contains(gconv.String(v), fileUploadingKey) { - urlEncode = false - break - } - } - } - s := "" - for k, v := range m { - if len(encodedParamStr) > 0 { - encodedParamStr += "&" - } - s = gconv.String(v) - if urlEncode && len(s) > 6 && strings.Compare(s[0:6], fileUploadingKey) != 0 { - s = gurl.Encode(s) - } - encodedParamStr += k + "=" + s - } - return + return httputil.BuildParams(params, noUrlEncode...) } // niceCallFunc calls function with exception capture logic. diff --git a/net/ghttp/ghttp_middleware.go b/net/ghttp/ghttp_middleware.go new file mode 100644 index 000000000..9531ca378 --- /dev/null +++ b/net/ghttp/ghttp_middleware.go @@ -0,0 +1,16 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp + +import ( + "github.com/gogf/gf/net/ghttp/internal/client" + "net/http" +) + +func MiddlewareClientTracing(c *Client, r *http.Request) (*ClientResponse, error) { + return client.MiddlewareTracing(c, r) +} diff --git a/net/ghttp/ghttp_unit_client_dump_test.go b/net/ghttp/ghttp_unit_client_dump_test.go index 9852d2c92..e6bd789dc 100644 --- a/net/ghttp/ghttp_unit_client_dump_test.go +++ b/net/ghttp/ghttp_unit_client_dump_test.go @@ -36,7 +36,7 @@ func Test_Client_Request_13_Dump(t *testing.T) { time.Sleep(100 * time.Millisecond) gtest.C(t, func(t *gtest.T) { url := fmt.Sprintf("http://127.0.0.1:%d", p) - client := g.Client().SetPrefix(url).ContentJson() + client := g.Client().SetPrefix(url).ContentJson().SetDump(true) r, err := client.Post("/hello", g.Map{"field": "test_for_request_body"}) t.Assert(err, nil) dumpedText := r.RawRequest() @@ -46,7 +46,7 @@ func Test_Client_Request_13_Dump(t *testing.T) { t.Assert(gstr.Contains(dumpedText2, "test_for_response_body"), true) client2 := g.Client().SetPrefix(url).ContentType("text/html") - r2, err := client2.Post("/hello2", g.Map{"field": "test_for_request_body"}) + r2, err := client2.Dump().Post("/hello2", g.Map{"field": "test_for_request_body"}) t.Assert(err, nil) dumpedText3 := r2.RawRequest() t.Assert(gstr.Contains(dumpedText3, "test_for_request_body"), true) diff --git a/net/ghttp/ghttp_unit_client_test.go b/net/ghttp/ghttp_unit_client_test.go index 468483b2a..5f8c73112 100644 --- a/net/ghttp/ghttp_unit_client_test.go +++ b/net/ghttp/ghttp_unit_client_test.go @@ -360,7 +360,7 @@ func Test_Client_Middleware(t *testing.T) { c := g.Client().SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { str1 += "a" - resp, err = c.MiddlewareNext(r) + resp, err = c.Next(r) if err != nil { return nil, err } @@ -369,7 +369,7 @@ func Test_Client_Middleware(t *testing.T) { }) c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { str1 += "c" - resp, err = c.MiddlewareNext(r) + resp, err = c.Next(r) if err != nil { return nil, err } @@ -378,7 +378,7 @@ func Test_Client_Middleware(t *testing.T) { }) c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { str1 += "e" - resp, err = c.MiddlewareNext(r) + resp, err = c.Next(r) if err != nil { return nil, err } @@ -401,7 +401,7 @@ func Test_Client_Middleware(t *testing.T) { c = g.Client().SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { str3 += "a" - resp, err = c.MiddlewareNext(r) + resp, err = c.Next(r) str3 += "b" return }) @@ -411,7 +411,7 @@ func Test_Client_Middleware(t *testing.T) { }) c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { str3 += "f" - resp, err = c.MiddlewareNext(r) + resp, err = c.Next(r) str3 += "g" return }) diff --git a/net/ghttp/internal/client/client.go b/net/ghttp/internal/client/client.go new file mode 100644 index 000000000..3aba87d94 --- /dev/null +++ b/net/ghttp/internal/client/client.go @@ -0,0 +1,237 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package client + +import ( + "context" + "crypto/tls" + "fmt" + "github.com/gogf/gf" + "github.com/gogf/gf/text/gstr" + "golang.org/x/net/proxy" + "net" + "net/http" + "net/url" + "strings" + "time" + + "github.com/gogf/gf/text/gregex" +) + +// Client is the HTTP client for HTTP request management. +type Client struct { + http.Client // Underlying HTTP Client. + ctx context.Context // Context for each request. + dump bool // Mark this request will be dumped. + agent string // Client agent. + parent *Client // Parent http client, this is used for chaining operations. + header map[string]string // Custom header map. + cookies map[string]string // Custom cookie map. + prefix string // Prefix for request. + authUser string // HTTP basic authentication: user. + authPass string // HTTP basic authentication: pass. + browserMode bool // Whether auto saving and sending cookie content. + retryCount int // Retry count when request fails. + retryInterval time.Duration // Retry interval when request fails. + middlewareHandler []HandlerFunc // Interceptor handlers +} + +var ( + defaultClientAgent = fmt.Sprintf(`GoFrameHTTPClient %s`, gf.VERSION) +) + +// New creates and returns a new HTTP client object. +func New() *Client { + return &Client{ + Client: http.Client{ + Transport: &http.Transport{ + // No validation for https certification of the server in default. + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + DisableKeepAlives: true, + }, + }, + header: make(map[string]string), + cookies: make(map[string]string), + agent: defaultClientAgent, + } +} + +// Clone clones current client and returns a new one. +func (c *Client) Clone() *Client { + newClient := New() + *newClient = *c + newClient.header = make(map[string]string) + newClient.cookies = make(map[string]string) + for k, v := range c.header { + newClient.header[k] = v + } + for k, v := range c.cookies { + newClient.cookies[k] = v + } + return newClient +} + +// SetBrowserMode enables browser mode of the client. +// When browser mode is enabled, it automatically saves and sends cookie content +// from and to server. +func (c *Client) SetBrowserMode(enabled bool) *Client { + c.browserMode = enabled + return c +} + +// SetHeader sets a custom HTTP header pair for the client. +func (c *Client) SetHeader(key, value string) *Client { + c.header[key] = value + return c +} + +// SetHeaderMap sets custom HTTP headers with map. +func (c *Client) SetHeaderMap(m map[string]string) *Client { + for k, v := range m { + c.header[k] = v + } + return c +} + +// SetAgent sets the User-Agent header for client. +func (c *Client) SetAgent(agent string) *Client { + c.header["User-Agent"] = agent + return c +} + +// SetContentType sets HTTP content type for the client. +func (c *Client) SetContentType(contentType string) *Client { + c.header["Content-Type"] = contentType + return c +} + +// SetHeaderRaw sets custom HTTP header using raw string. +func (c *Client) SetHeaderRaw(headers string) *Client { + for _, line := range gstr.SplitAndTrim(headers, "\n") { + array, _ := gregex.MatchString(`^([\w\-]+):\s*(.+)`, line) + if len(array) >= 3 { + c.header[array[1]] = array[2] + } + } + return c +} + +// SetCookie sets a cookie pair for the client. +func (c *Client) SetCookie(key, value string) *Client { + c.cookies[key] = value + return c +} + +// SetDump enables/disables dump feature for this request. +func (c *Client) SetDump(dump bool) *Client { + c.dump = dump + return c +} + +// SetCookieMap sets cookie items with map. +func (c *Client) SetCookieMap(m map[string]string) *Client { + for k, v := range m { + c.cookies[k] = v + } + return c +} + +// SetPrefix sets the request server URL prefix. +func (c *Client) SetPrefix(prefix string) *Client { + c.prefix = prefix + return c +} + +// SetTimeOut sets the request timeout for the client. +func (c *Client) SetTimeout(t time.Duration) *Client { + c.Client.Timeout = t + return c +} + +// SetBasicAuth sets HTTP basic authentication information for the client. +func (c *Client) SetBasicAuth(user, pass string) *Client { + c.authUser = user + c.authPass = pass + return c +} + +// SetCtx sets context for each request of this client. +func (c *Client) SetCtx(ctx context.Context) *Client { + c.ctx = ctx + return c +} + +// SetRetry sets retry count and interval. +func (c *Client) SetRetry(retryCount int, retryInterval time.Duration) *Client { + c.retryCount = retryCount + c.retryInterval = retryInterval + return c +} + +// SetRedirectLimit limit the number of jumps +func (c *Client) SetRedirectLimit(redirectLimit int) *Client { + c.CheckRedirect = func(req *http.Request, via []*http.Request) error { + if len(via) >= redirectLimit { + return http.ErrUseLastResponse + } + return nil + } + return c +} + +// SetProxy set proxy for the client. +// This func will do nothing when the parameter `proxyURL` is empty or in wrong pattern. +// The correct pattern is like `http://USER:PASSWORD@IP:PORT` or `socks5://USER:PASSWORD@IP:PORT`. +// Only `http` and `socks5` proxies are supported currently. +func (c *Client) SetProxy(proxyURL string) { + if strings.TrimSpace(proxyURL) == "" { + return + } + _proxy, err := url.Parse(proxyURL) + if err != nil { + return + } + if _proxy.Scheme == "http" { + if _, ok := c.Transport.(*http.Transport); ok { + c.Transport.(*http.Transport).Proxy = http.ProxyURL(_proxy) + } + } else { + var auth = &proxy.Auth{} + user := _proxy.User.Username() + + if user != "" { + auth.User = user + password, hasPassword := _proxy.User.Password() + if hasPassword && password != "" { + auth.Password = password + } + } else { + auth = nil + } + // refer to the source code, error is always nil + dialer, err := proxy.SOCKS5( + "tcp", + _proxy.Host, + auth, + &net.Dialer{ + Timeout: c.Client.Timeout, + KeepAlive: c.Client.Timeout, + }, + ) + if err != nil { + return + } + if _, ok := c.Transport.(*http.Transport); ok { + c.Transport.(*http.Transport).DialContext = func(ctx context.Context, network, addr string) (conn net.Conn, e error) { + return dialer.Dial(network, addr) + } + } + //c.SetTimeout(10*time.Second) + } +} diff --git a/net/ghttp/ghttp_client_bytes.go b/net/ghttp/internal/client/client_bytes.go similarity index 99% rename from net/ghttp/ghttp_client_bytes.go rename to net/ghttp/internal/client/client_bytes.go index a7b6b412c..41794370c 100644 --- a/net/ghttp/ghttp_client_bytes.go +++ b/net/ghttp/internal/client/client_bytes.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package ghttp +package client // GetBytes sends a GET request, retrieves and returns the result content as bytes. func (c *Client) GetBytes(url string, data ...interface{}) []byte { diff --git a/net/ghttp/ghttp_client_chain.go b/net/ghttp/internal/client/client_chain.go similarity index 91% rename from net/ghttp/ghttp_client_chain.go rename to net/ghttp/internal/client/client_chain.go index e27159015..467d614f4 100644 --- a/net/ghttp/ghttp_client_chain.go +++ b/net/ghttp/internal/client/client_chain.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package ghttp +package client import ( "context" @@ -133,7 +133,22 @@ func (c *Client) Retry(retryCount int, retryInterval time.Duration) *Client { newClient = c.Clone() } newClient.SetRetry(retryCount, retryInterval) - return c + return newClient +} + +// Dump is a chaining function, +// which enables/disables dump feature for this request. +func (c *Client) Dump(dump ...bool) *Client { + newClient := c + if c.parent == nil { + newClient = c.Clone() + } + if len(dump) > 0 { + newClient.SetDump(dump[0]) + } else { + newClient.SetDump(true) + } + return newClient } // Proxy is a chaining function, @@ -147,7 +162,7 @@ func (c *Client) Proxy(proxyURL string) *Client { newClient = c.Clone() } newClient.SetProxy(proxyURL) - return c + return newClient } // RedirectLimit is a chaining function, @@ -158,5 +173,5 @@ func (c *Client) RedirectLimit(redirectLimit int) *Client { newClient = c.Clone() } newClient.SetRedirectLimit(redirectLimit) - return c + return newClient } diff --git a/net/ghttp/ghttp_client_content.go b/net/ghttp/internal/client/client_content.go similarity index 99% rename from net/ghttp/ghttp_client_content.go rename to net/ghttp/internal/client/client_content.go index c3cef5a3d..a64ea73ff 100644 --- a/net/ghttp/ghttp_client_content.go +++ b/net/ghttp/internal/client/client_content.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package ghttp +package client // GetContent is a convenience method for sending GET request, which retrieves and returns // the result content and automatically closes response object. diff --git a/net/ghttp/ghttp_client_dump.go b/net/ghttp/internal/client/client_dump.go similarity index 88% rename from net/ghttp/ghttp_client_dump.go rename to net/ghttp/internal/client/client_dump.go index bd8c4f875..ac082d57c 100644 --- a/net/ghttp/ghttp_client_dump.go +++ b/net/ghttp/internal/client/client_dump.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package ghttp +package client import ( "fmt" @@ -35,8 +35,8 @@ func getResponseBody(res *http.Response) string { } // RawRequest returns the raw content of the request. -func (r *ClientResponse) RawRequest() string { - // ClientResponse can be nil. +func (r *Response) RawRequest() string { + // Response can be nil. if r == nil { return "" } @@ -57,8 +57,8 @@ func (r *ClientResponse) RawRequest() string { } // RawResponse returns the raw content of the response. -func (r *ClientResponse) RawResponse() string { - // ClientResponse can be nil. +func (r *Response) RawResponse() string { + // Response can be nil. if r == nil || r.Response == nil { return "" } @@ -76,11 +76,11 @@ func (r *ClientResponse) RawResponse() string { } // Raw returns the raw text of the request and the response. -func (r *ClientResponse) Raw() string { +func (r *Response) Raw() string { return fmt.Sprintf("%s\n%s", r.RawRequest(), r.RawResponse()) } // RawDump outputs the raw text of the request and the response to stdout. -func (r *ClientResponse) RawDump() { +func (r *Response) RawDump() { fmt.Println(r.Raw()) } diff --git a/net/ghttp/internal/client/client_middleware.go b/net/ghttp/internal/client/client_middleware.go new file mode 100644 index 000000000..d483e9df3 --- /dev/null +++ b/net/ghttp/internal/client/client_middleware.go @@ -0,0 +1,48 @@ +package client + +import ( + "net/http" +) + +// HandlerFunc middleware handler func +type HandlerFunc = func(c *Client, r *http.Request) (*Response, error) + +// clientMiddleware is the plugin for http client request workflow management. +type clientMiddleware struct { + client *Client // http client. + handlers []HandlerFunc // mdl handlers. + handlerIndex int // current handler index. + resp *Response // save resp. + err error // save err. +} + +const clientMiddlewareKey = "__clientMiddlewareKey" + +// Use adds one or more middleware handlers to client. +func (c *Client) Use(handlers ...HandlerFunc) *Client { + c.middlewareHandler = append(c.middlewareHandler, handlers...) + return c +} + +// Next calls next middleware. +// This is should only be call in HandlerFunc. +func (c *Client) Next(req *http.Request) (*Response, error) { + if v := req.Context().Value(clientMiddlewareKey); v != nil { + if m, ok := v.(*clientMiddleware); ok { + return m.Next(req) + } + } + return c.callRequest(req) +} + +// Next calls next middleware handler. +func (m *clientMiddleware) Next(req *http.Request) (resp *Response, err error) { + if m.err != nil { + return m.resp, m.err + } + if m.handlerIndex < len(m.handlers) { + m.handlerIndex++ + m.resp, m.err = m.handlers[m.handlerIndex](m.client, req) + } + return m.resp, m.err +} diff --git a/net/ghttp/ghttp_client_request.go b/net/ghttp/internal/client/client_request.go similarity index 83% rename from net/ghttp/ghttp_client_request.go rename to net/ghttp/internal/client/client_request.go index 3d3775922..74f46675f 100644 --- a/net/ghttp/ghttp_client_request.go +++ b/net/ghttp/internal/client/client_request.go @@ -4,20 +4,17 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package ghttp +package client import ( "bytes" "context" "errors" "fmt" - "github.com/gogf/gf" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/internal/utils" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/label" - "go.opentelemetry.io/otel/trace" + "github.com/gogf/gf/net/ghttp/internal/httputil" "io" "io/ioutil" "mime/multipart" @@ -36,55 +33,55 @@ import ( // Get send GET request and returns the response object. // Note that the response object MUST be closed if it'll be never used. -func (c *Client) Get(url string, data ...interface{}) (*ClientResponse, error) { +func (c *Client) Get(url string, data ...interface{}) (*Response, error) { return c.DoRequest("GET", url, data...) } // Put send PUT request and returns the response object. // Note that the response object MUST be closed if it'll be never used. -func (c *Client) Put(url string, data ...interface{}) (*ClientResponse, error) { +func (c *Client) Put(url string, data ...interface{}) (*Response, error) { return c.DoRequest("PUT", url, data...) } // Post sends request using HTTP method POST and returns the response object. // Note that the response object MUST be closed if it'll be never used. -func (c *Client) Post(url string, data ...interface{}) (*ClientResponse, error) { +func (c *Client) Post(url string, data ...interface{}) (*Response, error) { return c.DoRequest("POST", url, data...) } // Delete send DELETE request and returns the response object. // Note that the response object MUST be closed if it'll be never used. -func (c *Client) Delete(url string, data ...interface{}) (*ClientResponse, error) { +func (c *Client) Delete(url string, data ...interface{}) (*Response, error) { return c.DoRequest("DELETE", url, data...) } // Head send HEAD request and returns the response object. // Note that the response object MUST be closed if it'll be never used. -func (c *Client) Head(url string, data ...interface{}) (*ClientResponse, error) { +func (c *Client) Head(url string, data ...interface{}) (*Response, error) { return c.DoRequest("HEAD", url, data...) } // Patch send PATCH request and returns the response object. // Note that the response object MUST be closed if it'll be never used. -func (c *Client) Patch(url string, data ...interface{}) (*ClientResponse, error) { +func (c *Client) Patch(url string, data ...interface{}) (*Response, error) { return c.DoRequest("PATCH", url, data...) } // Connect send CONNECT request and returns the response object. // Note that the response object MUST be closed if it'll be never used. -func (c *Client) Connect(url string, data ...interface{}) (*ClientResponse, error) { +func (c *Client) Connect(url string, data ...interface{}) (*Response, error) { return c.DoRequest("CONNECT", url, data...) } // Options send OPTIONS request and returns the response object. // Note that the response object MUST be closed if it'll be never used. -func (c *Client) Options(url string, data ...interface{}) (*ClientResponse, error) { +func (c *Client) Options(url string, data ...interface{}) (*Response, error) { return c.DoRequest("OPTIONS", url, data...) } // Trace send TRACE request and returns the response object. // Note that the response object MUST be closed if it'll be never used. -func (c *Client) Trace(url string, data ...interface{}) (*ClientResponse, error) { +func (c *Client) Trace(url string, data ...interface{}) (*Response, error) { return c.DoRequest("TRACE", url, data...) } @@ -95,30 +92,17 @@ func (c *Client) Trace(url string, data ...interface{}) (*ClientResponse, error) // else it uses "application/x-www-form-urlencoded". It also automatically detects the post // content for JSON format, and for that it automatically sets the Content-Type as // "application/json". -func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *ClientResponse, err error) { +func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Response, err error) { req, err := c.prepareRequest(method, url, data...) if err != nil { return nil, err } - // Tracing. - tr := otel.GetTracerProvider().Tracer( - "github.com/gogf/gf/net/ghttp.client", - trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), - ) - ctx, span := tr.Start(req.Context(), req.URL.String()) - defer span.End() - // Header (Cookie is in it). - if len(req.Header) > 0 { - span.SetAttributes(label.Any(`http.headers`, req.Header)) - } - req = req.WithContext(ctx) - // Client middleware. if len(c.middlewareHandler) > 0 { - mdlHandlers := make([]ClientHandlerFunc, 0, len(c.middlewareHandler)+1) + mdlHandlers := make([]HandlerFunc, 0, len(c.middlewareHandler)+1) mdlHandlers = append(mdlHandlers, c.middlewareHandler...) - mdlHandlers = append(mdlHandlers, func(cli *Client, r *http.Request) (*ClientResponse, error) { + mdlHandlers = append(mdlHandlers, func(cli *Client, r *http.Request) (*Response, error) { return cli.callRequest(r) }) ctx := context.WithValue(req.Context(), clientMiddlewareKey, &clientMiddleware{ @@ -127,7 +111,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien handlerIndex: -1, }) req = req.WithContext(ctx) - resp, err = c.MiddlewareNext(req) + resp, err = c.Next(req) } else { resp, err = c.callRequest(req) } @@ -178,7 +162,7 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h } } default: - param = BuildParams(data[0]) + param = httputil.BuildParams(data[0]) } } if method == "GET" { @@ -304,15 +288,18 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h // callRequest sends request with give http.Request, and returns the responses object. // Note that the response object MUST be closed if it'll be never used. -func (c *Client) callRequest(req *http.Request) (resp *ClientResponse, err error) { - resp = &ClientResponse{ +func (c *Client) callRequest(req *http.Request) (resp *Response, err error) { + resp = &Response{ request: req, } + // Dump feature. // The request body can be reused for dumping // raw HTTP request-response procedure. - reqBodyContent, _ := ioutil.ReadAll(req.Body) - resp.requestBody = reqBodyContent - req.Body = utils.NewReadCloser(reqBodyContent, false) + if c.dump { + reqBodyContent, _ := ioutil.ReadAll(req.Body) + resp.requestBody = reqBodyContent + req.Body = utils.NewReadCloser(reqBodyContent, false) + } for { if resp.Response, err = c.Do(req); err != nil { // The response might not be nil when err != nil. diff --git a/net/ghttp/ghttp_client_response.go b/net/ghttp/internal/client/client_response.go similarity index 74% rename from net/ghttp/ghttp_client_response.go rename to net/ghttp/internal/client/client_response.go index f7ed30014..893f4f834 100644 --- a/net/ghttp/ghttp_client_response.go +++ b/net/ghttp/internal/client/client_response.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package ghttp +package client import ( "io/ioutil" @@ -13,16 +13,16 @@ import ( "github.com/gogf/gf/util/gconv" ) -// ClientResponse is the struct for client request response. -type ClientResponse struct { +// Response is the struct for client request response. +type Response struct { *http.Response request *http.Request requestBody []byte cookies map[string]string } -// initCookie initializes the cookie map attribute of ClientResponse. -func (r *ClientResponse) initCookie() { +// initCookie initializes the cookie map attribute of Response. +func (r *Response) initCookie() { if r.cookies == nil { r.cookies = make(map[string]string) for _, v := range r.Cookies() { @@ -32,13 +32,13 @@ func (r *ClientResponse) initCookie() { } // GetCookie retrieves and returns the cookie value of specified . -func (r *ClientResponse) GetCookie(key string) string { +func (r *Response) GetCookie(key string) string { r.initCookie() return r.cookies[key] } // GetCookieMap retrieves and returns a copy of current cookie values map. -func (r *ClientResponse) GetCookieMap() map[string]string { +func (r *Response) GetCookieMap() map[string]string { r.initCookie() m := make(map[string]string, len(r.cookies)) for k, v := range r.cookies { @@ -48,7 +48,7 @@ func (r *ClientResponse) GetCookieMap() map[string]string { } // ReadAll retrieves and returns the response content as []byte. -func (r *ClientResponse) ReadAll() []byte { +func (r *Response) ReadAll() []byte { body, err := ioutil.ReadAll(r.Response.Body) if err != nil { return nil @@ -57,12 +57,12 @@ func (r *ClientResponse) ReadAll() []byte { } // ReadAllString retrieves and returns the response content as string. -func (r *ClientResponse) ReadAllString() string { +func (r *Response) ReadAllString() string { return gconv.UnsafeBytesToStr(r.ReadAll()) } // Close closes the response when it will never be used. -func (r *ClientResponse) Close() error { +func (r *Response) Close() error { if r == nil || r.Response == nil || r.Response.Close { return nil } diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go new file mode 100644 index 000000000..d5c9a91b2 --- /dev/null +++ b/net/ghttp/internal/client/client_tracing.go @@ -0,0 +1,72 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package client + +import ( + "fmt" + "github.com/gogf/gf" + "github.com/gogf/gf/internal/utils" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/trace" + "io/ioutil" + "net/http" + "net/http/httptrace" +) + +const ( + maxContentLogSize = 512 * 1024 // Max log size for request and response body. +) + +// MiddlewareTracing is a client middleware that enables tracing feature using standards of OpenTelemetry. +func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err error) { + tr := otel.GetTracerProvider().Tracer( + "github.com/gogf/gf/net/ghttp.Client", + trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), + ) + ctx, span := tr.Start(r.Context(), r.URL.String()) + defer span.End() + response, err = c.Next( + r.WithContext( + httptrace.WithClientTrace( + ctx, newClientTrace(ctx, span, r), + ), + ), + ) + if err != nil { + span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) + } + var resBodyContent string + if response.ContentLength <= maxContentLogSize { + reqBodyContentBytes, _ := ioutil.ReadAll(response.Body) + resBodyContent = string(reqBodyContentBytes) + response.Body = utils.NewReadCloser(reqBodyContentBytes, false) + } else { + resBodyContent = fmt.Sprintf("[Response Body Too Large For Logging, Max: %d bytes]", maxContentLogSize) + } + if response != nil { + span.AddEvent("http.response", trace.WithAttributes( + label.Any(`http.response.headers`, headerToMap(response.Header)), + label.String(`http.response.body`, resBodyContent), + )) + } + return +} + +// headerToMap coverts request headers to map. +func headerToMap(header http.Header) map[string]interface{} { + m := make(map[string]interface{}) + for k, v := range header { + if len(v) > 1 { + m[k] = v + } else { + m[k] = v[0] + } + } + return m +} diff --git a/net/ghttp/internal/client/client_tracing_tracer.go b/net/ghttp/internal/client/client_tracing_tracer.go new file mode 100644 index 000000000..154eb2473 --- /dev/null +++ b/net/ghttp/internal/client/client_tracing_tracer.go @@ -0,0 +1,171 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package client + +import ( + "context" + "crypto/tls" + "fmt" + "github.com/gogf/gf/internal/utils" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/trace" + "io/ioutil" + "net/http" + "net/http/httptrace" + "net/textproto" + "strings" + "sync" +) + +type clientTracer struct { + context.Context + span trace.Span + request *http.Request + requestBody []byte + headers map[string]interface{} + mtx sync.Mutex +} + +func newClientTrace(ctx context.Context, span trace.Span, request *http.Request) *httptrace.ClientTrace { + ct := &clientTracer{ + Context: ctx, + span: span, + request: request, + headers: make(map[string]interface{}), + } + return &httptrace.ClientTrace{ + GetConn: ct.getConn, + GotConn: ct.gotConn, + PutIdleConn: ct.putIdleConn, + GotFirstResponseByte: ct.gotFirstResponseByte, + Got100Continue: ct.got100Continue, + Got1xxResponse: ct.got1xxResponse, + DNSStart: ct.dnsStart, + DNSDone: ct.dnsDone, + ConnectStart: ct.connectStart, + ConnectDone: ct.connectDone, + TLSHandshakeStart: ct.tlsHandshakeStart, + TLSHandshakeDone: ct.tlsHandshakeDone, + WroteHeaderField: ct.wroteHeaderField, + WroteHeaders: ct.wroteHeaders, + Wait100Continue: ct.wait100Continue, + WroteRequest: ct.wroteRequest, + } +} + +func (ct *clientTracer) getConn(host string) { + +} + +func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) { + ct.span.SetAttributes( + label.String("http.connection.remote", info.Conn.RemoteAddr().String()), + label.String("http.connection.local", info.Conn.LocalAddr().String()), + ) +} + +func (ct *clientTracer) putIdleConn(err error) { + if err != nil { + ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) + } +} + +func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) { + ct.span.SetAttributes( + label.String("http.dns.start", info.Host), + ) +} + +func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) { + var buffer strings.Builder + for _, v := range info.Addrs { + if buffer.Len() != 0 { + buffer.WriteString(",") + } + buffer.WriteString(v.String()) + } + if info.Err != nil { + ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) + } + ct.span.SetAttributes( + label.String("http.dns.done", buffer.String()), + ) +} + +func (ct *clientTracer) connectStart(network, addr string) { + ct.span.SetAttributes( + label.String("http.connect.start", network+"@"+addr), + ) +} + +func (ct *clientTracer) connectDone(network, addr string, err error) { + if err != nil { + ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) + } + ct.span.SetAttributes( + label.String("http.connect.done", network+"@"+addr), + ) +} + +func (ct *clientTracer) tlsHandshakeStart() { + +} + +func (ct *clientTracer) tlsHandshakeDone(_ tls.ConnectionState, err error) { + if err != nil { + ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) + } +} + +func (ct *clientTracer) wroteHeaderField(k string, v []string) { + if len(v) > 1 { + ct.headers[k] = v + } else { + ct.headers[k] = v[0] + } +} + +func (ct *clientTracer) wroteHeaders() { + if ct.request.ContentLength <= maxContentLogSize { + reqBodyContent, _ := ioutil.ReadAll(ct.request.Body) + ct.requestBody = reqBodyContent + ct.request.Body = utils.NewReadCloser(reqBodyContent, false) + } +} + +func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { + if info.Err != nil { + ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) + } + var bodyContent string + if ct.request.ContentLength <= maxContentLogSize { + bodyContent = string(ct.requestBody) + } else { + bodyContent = fmt.Sprintf("[Request Body Too Large For Logging, Max: %d bytes]", maxContentLogSize) + } + ct.span.AddEvent("http.request", trace.WithAttributes( + label.Any(`http.request.headers`, ct.headers), + label.String(`http.request.body`, bodyContent), + )) +} + +func (ct *clientTracer) got100Continue() { + +} + +func (ct *clientTracer) wait100Continue() { + +} + +func (ct *clientTracer) gotFirstResponseByte() { + ct.span.AddEvent("http.request.receive", trace.WithAttributes()) +} + +func (ct *clientTracer) got1xxResponse(code int, header textproto.MIMEHeader) error { + return nil +} diff --git a/net/ghttp/ghttp_client_var.go b/net/ghttp/internal/client/client_var.go similarity index 99% rename from net/ghttp/ghttp_client_var.go rename to net/ghttp/internal/client/client_var.go index e8b2a00b0..1fe9810e1 100644 --- a/net/ghttp/ghttp_client_var.go +++ b/net/ghttp/internal/client/client_var.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package ghttp +package client import ( "github.com/gogf/gf/container/gvar" diff --git a/net/ghttp/internal/httputil/httputils.go b/net/ghttp/internal/httputil/httputils.go new file mode 100644 index 000000000..209acb782 --- /dev/null +++ b/net/ghttp/internal/httputil/httputils.go @@ -0,0 +1,66 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package httputil + +import ( + "github.com/gogf/gf/text/gstr" + "strings" + + "github.com/gogf/gf/encoding/gurl" + "github.com/gogf/gf/util/gconv" +) + +const ( + fileUploadingKey = "@file:" +) + +// BuildParams builds the request string for the http client. The can be type of: +// string/[]byte/map/struct/*struct. +// +// The optional parameter specifies whether ignore the url encoding for the data. +func BuildParams(params interface{}, noUrlEncode ...bool) (encodedParamStr string) { + // If given string/[]byte, converts and returns it directly as string. + switch v := params.(type) { + case string, []byte: + return gconv.String(params) + case []interface{}: + if len(v) > 0 { + params = v[0] + } else { + params = nil + } + } + // Else converts it to map and does the url encoding. + m, urlEncode := gconv.Map(params), true + if len(m) == 0 { + return gconv.String(params) + } + if len(noUrlEncode) == 1 { + urlEncode = !noUrlEncode[0] + } + // If there's file uploading, it ignores the url encoding. + if urlEncode { + for k, v := range m { + if gstr.Contains(k, fileUploadingKey) || gstr.Contains(gconv.String(v), fileUploadingKey) { + urlEncode = false + break + } + } + } + s := "" + for k, v := range m { + if len(encodedParamStr) > 0 { + encodedParamStr += "&" + } + s = gconv.String(v) + if urlEncode && len(s) > 6 && strings.Compare(s[0:6], fileUploadingKey) != 0 { + s = gurl.Encode(s) + } + encodedParamStr += k + "=" + s + } + return +} diff --git a/net/gtrace/gtrace_http_client.go b/net/gtrace/gtrace_http_client.go deleted file mode 100644 index 7bc100b22..000000000 --- a/net/gtrace/gtrace_http_client.go +++ /dev/null @@ -1,242 +0,0 @@ -package gtrace - -import ( - "context" - "crypto/tls" - "fmt" - "github.com/gogf/gf" - "net/http/httptrace" - "net/textproto" - "strings" - "sync" - - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" - "go.opentelemetry.io/otel/semconv" - "go.opentelemetry.io/otel/trace" -) - -var ( - HTTPStatus = label.Key("http.status") - HTTPHeaderMIME = label.Key("http.mime") - HTTPRemoteAddr = label.Key("http.remote") - HTTPLocalAddr = label.Key("http.local") -) - -var ( - hookMap = map[string]string{ - "http.dns": "http.getconn", - "http.connect": "http.getconn", - "http.tls": "http.getconn", - } -) - -func parentHook(hook string) string { - if strings.HasPrefix(hook, "http.connect") { - return hookMap["http.connect"] - } - return hookMap[hook] -} - -type clientTracer struct { - context.Context - - tr trace.Tracer - - activeHooks map[string]context.Context - root trace.Span - mtx sync.Mutex -} - -func NewClientTrace(ctx context.Context) *httptrace.ClientTrace { - ct := &clientTracer{ - Context: ctx, - activeHooks: make(map[string]context.Context), - } - - ct.tr = otel.GetTracerProvider().Tracer( - "github.com/gogf/gf/net/ghttp.client", - trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), - ) - - return &httptrace.ClientTrace{ - GetConn: ct.getConn, - GotConn: ct.gotConn, - PutIdleConn: ct.putIdleConn, - GotFirstResponseByte: ct.gotFirstResponseByte, - Got100Continue: ct.got100Continue, - Got1xxResponse: ct.got1xxResponse, - DNSStart: ct.dnsStart, - DNSDone: ct.dnsDone, - ConnectStart: ct.connectStart, - ConnectDone: ct.connectDone, - TLSHandshakeStart: ct.tlsHandshakeStart, - TLSHandshakeDone: ct.tlsHandshakeDone, - WroteHeaderField: ct.wroteHeaderField, - WroteHeaders: ct.wroteHeaders, - Wait100Continue: ct.wait100Continue, - WroteRequest: ct.wroteRequest, - } -} - -func (ct *clientTracer) start(hook, spanName string, attrs ...label.KeyValue) { - ct.mtx.Lock() - defer ct.mtx.Unlock() - - if hookCtx, found := ct.activeHooks[hook]; !found { - var sp trace.Span - ct.activeHooks[hook], sp = ct.tr.Start(ct.getParentContext(hook), spanName, trace.WithAttributes(attrs...), trace.WithSpanKind(trace.SpanKindClient)) - if ct.root == nil { - ct.root = sp - } - } else { - // end was called before start finished, add the start attributes and end the span here - span := trace.SpanFromContext(hookCtx) - span.SetAttributes(attrs...) - span.End() - - delete(ct.activeHooks, hook) - } -} - -func (ct *clientTracer) end(hook string, err error, attrs ...label.KeyValue) { - ct.mtx.Lock() - defer ct.mtx.Unlock() - if ctx, ok := ct.activeHooks[hook]; ok { - span := trace.SpanFromContext(ctx) - if err != nil { - span.SetStatus(codes.Error, err.Error()) - } - span.SetAttributes(attrs...) - span.End() - delete(ct.activeHooks, hook) - } else { - // start is not finished before end is called. - // Start a span here with the ending attributes that will be finished when start finishes. - // Yes, it's backwards. v0v - ctx, span := ct.tr.Start(ct.getParentContext(hook), hook, trace.WithAttributes(attrs...), trace.WithSpanKind(trace.SpanKindClient)) - if err != nil { - span.SetStatus(codes.Error, err.Error()) - } - ct.activeHooks[hook] = ctx - } -} - -func (ct *clientTracer) getParentContext(hook string) context.Context { - ctx, ok := ct.activeHooks[parentHook(hook)] - if !ok { - return ct.Context - } - return ctx -} - -func (ct *clientTracer) span(hook string) trace.Span { - ct.mtx.Lock() - defer ct.mtx.Unlock() - if ctx, ok := ct.activeHooks[hook]; ok { - return trace.SpanFromContext(ctx) - } - return nil -} - -func (ct *clientTracer) getConn(host string) { - ct.start("http.getconn", "http.getconn", semconv.HTTPHostKey.String(host)) -} - -func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) { - ct.end("http.getconn", - nil, - HTTPRemoteAddr.String(info.Conn.RemoteAddr().String()), - HTTPLocalAddr.String(info.Conn.LocalAddr().String()), - ) -} - -func (ct *clientTracer) putIdleConn(err error) { - ct.end("http.receive", err) -} - -func (ct *clientTracer) gotFirstResponseByte() { - ct.start("http.receive", "http.receive") -} - -func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) { - ct.start("http.dns", "http.dns", semconv.HTTPHostKey.String(info.Host)) -} - -func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) { - ct.end("http.dns", info.Err) -} - -func (ct *clientTracer) connectStart(network, addr string) { - ct.start("http.connect."+addr, "http.connect", HTTPRemoteAddr.String(addr)) -} - -func (ct *clientTracer) connectDone(network, addr string, err error) { - ct.end("http.connect."+addr, err) -} - -func (ct *clientTracer) tlsHandshakeStart() { - ct.start("http.tls", "http.tls") -} - -func (ct *clientTracer) tlsHandshakeDone(_ tls.ConnectionState, err error) { - ct.end("http.tls", err) -} - -func (ct *clientTracer) wroteHeaderField(k string, v []string) { - if ct.span("http.headers") == nil { - ct.start("http.headers", "http.headers") - } - ct.root.SetAttributes(label.String("http."+strings.ToLower(k), sliceToString(v))) -} - -func (ct *clientTracer) wroteHeaders() { - if ct.span("http.headers") != nil { - ct.end("http.headers", nil) - } - ct.start("http.send", "http.send") -} - -func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { - if info.Err != nil { - ct.root.SetStatus(codes.Error, info.Err.Error()) - } - ct.end("http.send", info.Err) -} - -func (ct *clientTracer) got100Continue() { - ct.span("http.receive").AddEvent("GOT 100 - Continue") -} - -func (ct *clientTracer) wait100Continue() { - ct.span("http.receive").AddEvent("GOT 100 - Wait") -} - -func (ct *clientTracer) got1xxResponse(code int, header textproto.MIMEHeader) error { - ct.span("http.receive").AddEvent("GOT 1xx", trace.WithAttributes( - HTTPStatus.Int(code), - HTTPHeaderMIME.String(sm2s(header)), - )) - return nil -} - -func sliceToString(value []string) string { - if len(value) == 0 { - return "undefined" - } - return strings.Join(value, ",") -} - -func sm2s(value map[string][]string) string { - var buf strings.Builder - for k, v := range value { - if buf.Len() != 0 { - buf.WriteString(",") - } - buf.WriteString(k) - buf.WriteString("=") - buf.WriteString(sliceToString(v)) - } - return buf.String() -} From cc1e340585a6223b21810770b38145a84ccc13b0 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Mon, 25 Jan 2021 18:43:47 +0800 Subject: [PATCH 109/492] add tracing middleware for ghttp.Client/Server; add package gtrace --- .../net/ghttp/client/tracing/jaeger/main.go | 91 ------------------- .example/net/gtrace/1.http/client/main.go | 46 ++++++++++ .example/net/gtrace/1.http/server/main.go | 52 +++++++++++ net/ghttp/ghttp_middleware.go | 71 +++++++++++++++ net/ghttp/internal/client/client_tracing.go | 44 ++++----- .../internal/client/client_tracing_tracer.go | 8 +- net/ghttp/internal/httputil/httputils.go | 14 +++ net/gtrace/gtrace.go | 48 ++++++++++ 8 files changed, 257 insertions(+), 117 deletions(-) delete mode 100644 .example/net/ghttp/client/tracing/jaeger/main.go create mode 100644 .example/net/gtrace/1.http/client/main.go create mode 100644 .example/net/gtrace/1.http/server/main.go create mode 100644 net/gtrace/gtrace.go diff --git a/.example/net/ghttp/client/tracing/jaeger/main.go b/.example/net/ghttp/client/tracing/jaeger/main.go deleted file mode 100644 index c845c8307..000000000 --- a/.example/net/ghttp/client/tracing/jaeger/main.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Command jaeger is an example program that creates spans -// and uploads to Jaeger. -package main - -import ( - "context" - "fmt" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" - "go.opentelemetry.io/otel/exporters/trace/jaeger" - "go.opentelemetry.io/otel/trace" - "log" - "time" - - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/label" - - sdktrace "go.opentelemetry.io/otel/sdk/trace" -) - -// initTracer creates a new trace provider instance and registers it as global trace provider. -func initTracer() func() { - // Create and install Jaeger export pipeline. - flush, err := jaeger.InstallNewPipeline( - jaeger.WithCollectorEndpoint("http://localhost:14268/api/traces"), - jaeger.WithProcess(jaeger.Process{ - ServiceName: "http-trace-demo", - Tags: []label.KeyValue{ - label.String("exporter", "jaeger"), - label.Float64("float", 312.23), - }, - }), - jaeger.WithSDK(&sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}), - ) - if err != nil { - log.Fatal(err) - } - return flush -} - -func main() { - ctx := context.Background() - - flush := initTracer() - defer flush() - - ctx, span := otel.Tracer("test").Start(ctx, "test") - defer span.End() - - for i := 0; i < 20; i++ { - g.Client().Use(ghttp.MiddlewareClientTracing).Ctx(ctx).Header(g.MapStrStr{ - "test": "123", - "john": "smith", - }).Cookie(g.MapStrStr{ - "cookieKey": "cookieValue", - }).GetContent(fmt.Sprintf("http://baidu.com/?q=test_%d", i)) - } - foo(ctx) -} - -func foo(ctx context.Context) { - ctx, span := otel.Tracer("test").Start(ctx, "foo") - defer span.End() - span.AddEvent("Nice operation!", trace.WithAttributes(label.Int("bogons", 100))) - span.SetAttributes(label.String("test2", "123")) - time.Sleep(time.Second * 1) - bar(ctx) -} - -func bar(ctx context.Context) { - _, span := otel.Tracer("test").Start(ctx, "bar") - defer span.End() - span.AddEvent("Nice operation!", trace.WithAttributes(label.Int("bogons", 100))) - span.SetAttributes(label.String("test2", "123")) - time.Sleep(time.Second * 1) - // Do bar... -} diff --git a/.example/net/gtrace/1.http/client/main.go b/.example/net/gtrace/1.http/client/main.go new file mode 100644 index 000000000..cae43e329 --- /dev/null +++ b/.example/net/gtrace/1.http/client/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "context" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/net/gtrace" + "go.opentelemetry.io/otel/baggage" + "go.opentelemetry.io/otel/exporters/trace/jaeger" + "go.opentelemetry.io/otel/label" + sdkTrace "go.opentelemetry.io/otel/sdk/trace" +) + +const ( + JaegerEndpoint = "http://localhost:14268/api/traces" + ServiceName = "TracingHttpClient" +) + +// initTracer creates a new trace provider instance and registers it as global trace provider. +func initTracer() func() { + // Create and install Jaeger export pipeline. + flush, err := jaeger.InstallNewPipeline( + jaeger.WithCollectorEndpoint(JaegerEndpoint), + jaeger.WithProcess(jaeger.Process{ + ServiceName: ServiceName, + }), + jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), + ) + if err != nil { + g.Log().Fatal(err) + } + return flush +} + +func main() { + flush := initTracer() + defer flush() + + ctx, span := gtrace.Tracer().Start(context.Background(), "test") + defer span.End() + + ctx = baggage.ContextWithValues(ctx, label.String("name", "john")) + client := g.Client().Use(ghttp.MiddlewareClientTracing) + content := client.Ctx(ctx).GetContent("http://127.0.0.1:8199/hello") + g.Log().Print(content) +} diff --git a/.example/net/gtrace/1.http/server/main.go b/.example/net/gtrace/1.http/server/main.go new file mode 100644 index 000000000..1be9b6fc8 --- /dev/null +++ b/.example/net/gtrace/1.http/server/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/net/gtrace" + "go.opentelemetry.io/otel/baggage" + "go.opentelemetry.io/otel/exporters/trace/jaeger" + sdkTrace "go.opentelemetry.io/otel/sdk/trace" +) + +const ( + JaegerEndpoint = "http://localhost:14268/api/traces" + ServiceName = "TracingHttpServer" +) + +// initTracer creates a new trace provider instance and registers it as global trace provider. +func initTracer() func() { + // Create and install Jaeger export pipeline. + flush, err := jaeger.InstallNewPipeline( + jaeger.WithCollectorEndpoint(JaegerEndpoint), + jaeger.WithProcess(jaeger.Process{ + ServiceName: ServiceName, + }), + jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), + ) + if err != nil { + g.Log().Fatal(err) + } + return flush +} + +func main() { + flush := initTracer() + defer flush() + + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(ghttp.MiddlewareServerTracing) + group.GET("/hello", helloHandler) + }) + s.SetPort(8199) + s.Run() +} + +func helloHandler(r *ghttp.Request) { + ctx, span := gtrace.Tracer().Start(r.Context(), "helloHandler") + defer span.End() + + value := baggage.Value(ctx, "name") + r.Response.Write("hello:", value.AsString()) +} diff --git a/net/ghttp/ghttp_middleware.go b/net/ghttp/ghttp_middleware.go index 9531ca378..3460f46ee 100644 --- a/net/ghttp/ghttp_middleware.go +++ b/net/ghttp/ghttp_middleware.go @@ -7,10 +7,81 @@ package ghttp import ( + "fmt" + "github.com/gogf/gf" + "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/net/ghttp/internal/client" + "github.com/gogf/gf/net/ghttp/internal/httputil" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" + "io/ioutil" "net/http" ) +const ( + tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. +) + +// MiddlewareClientTracing is a client middleware that enables tracing feature using standards of OpenTelemetry. func MiddlewareClientTracing(c *Client, r *http.Request) (*ClientResponse, error) { return client.MiddlewareTracing(c, r) } + +// MiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry. +func MiddlewareServerTracing(r *Request) { + tr := otel.GetTracerProvider().Tracer( + "github.com/gogf/gf/net/ghttp.Server", + trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), + ) + // Tracing content parsing, start root span. + propagator := propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{}, + ) + ctx := propagator.Extract(r.Context(), r.Header) + ctx, span := tr.Start(ctx, r.URL.String()) + defer span.End() + + // Inject tracing context. + r.SetCtx(ctx) + + // Request content logging. + var reqBodyContent string + if r.ContentLength <= tracingMaxContentLogSize { + reqBodyContentBytes, _ := ioutil.ReadAll(r.Body) + r.Body = utils.NewReadCloser(reqBodyContentBytes, false) + reqBodyContent = string(reqBodyContentBytes) + } else { + reqBodyContent = fmt.Sprintf( + "[Request Body Too Large For Logging, Max: %d bytes]", + tracingMaxContentLogSize, + ) + } + span.AddEvent("http.request", trace.WithAttributes( + label.Any(`http.request.headers`, httputil.HeaderToMap(r.Header)), + label.String(`http.request.body`, reqBodyContent), + )) + + // Continue executing. + r.Middleware.Next() + + // Error logging. + if err := r.GetError(); err != nil { + span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) + } + // Response content logging. + var resBodyContent string + if r.Response.BufferLength() <= tracingMaxContentLogSize { + resBodyContent = r.Response.BufferString() + } else { + resBodyContent = fmt.Sprintf("[Response Body Too Large For Logging, Max: %d bytes]", tracingMaxContentLogSize) + } + span.AddEvent("http.response", trace.WithAttributes( + label.Any(`http.response.headers`, httputil.HeaderToMap(r.Response.Header())), + label.String(`http.response.body`, resBodyContent), + )) + return +} diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index d5c9a91b2..546447fa8 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -10,9 +10,11 @@ import ( "fmt" "github.com/gogf/gf" "github.com/gogf/gf/internal/utils" + "github.com/gogf/gf/net/ghttp/internal/httputil" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "io/ioutil" "net/http" @@ -20,7 +22,7 @@ import ( ) const ( - maxContentLogSize = 512 * 1024 // Max log size for request and response body. + tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. ) // MiddlewareTracing is a client middleware that enables tracing feature using standards of OpenTelemetry. @@ -31,6 +33,15 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro ) ctx, span := tr.Start(r.Context(), r.URL.String()) defer span.End() + + // Inject tracing content into http header. + propagator := propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{}, + ) + propagator.Inject(ctx, r.Header) + + // Continue client handler executing. response, err = c.Next( r.WithContext( httptrace.WithClientTrace( @@ -41,32 +52,21 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro if err != nil { span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) } + if response == nil { + return + } var resBodyContent string - if response.ContentLength <= maxContentLogSize { + if response.ContentLength <= tracingMaxContentLogSize { reqBodyContentBytes, _ := ioutil.ReadAll(response.Body) resBodyContent = string(reqBodyContentBytes) response.Body = utils.NewReadCloser(reqBodyContentBytes, false) } else { - resBodyContent = fmt.Sprintf("[Response Body Too Large For Logging, Max: %d bytes]", maxContentLogSize) - } - if response != nil { - span.AddEvent("http.response", trace.WithAttributes( - label.Any(`http.response.headers`, headerToMap(response.Header)), - label.String(`http.response.body`, resBodyContent), - )) + resBodyContent = fmt.Sprintf("[Response Body Too Large For Logging, Max: %d bytes]", tracingMaxContentLogSize) } + + span.AddEvent("http.response", trace.WithAttributes( + label.Any(`http.response.headers`, httputil.HeaderToMap(response.Header)), + label.String(`http.response.body`, resBodyContent), + )) return } - -// headerToMap coverts request headers to map. -func headerToMap(header http.Header) map[string]interface{} { - m := make(map[string]interface{}) - for k, v := range header { - if len(v) > 1 { - m[k] = v - } else { - m[k] = v[0] - } - } - return m -} diff --git a/net/ghttp/internal/client/client_tracing_tracer.go b/net/ghttp/internal/client/client_tracing_tracer.go index 154eb2473..fa2f6366e 100644 --- a/net/ghttp/internal/client/client_tracing_tracer.go +++ b/net/ghttp/internal/client/client_tracing_tracer.go @@ -131,7 +131,7 @@ func (ct *clientTracer) wroteHeaderField(k string, v []string) { } func (ct *clientTracer) wroteHeaders() { - if ct.request.ContentLength <= maxContentLogSize { + if ct.request.ContentLength <= tracingMaxContentLogSize { reqBodyContent, _ := ioutil.ReadAll(ct.request.Body) ct.requestBody = reqBodyContent ct.request.Body = utils.NewReadCloser(reqBodyContent, false) @@ -143,10 +143,10 @@ func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) } var bodyContent string - if ct.request.ContentLength <= maxContentLogSize { + if ct.request.ContentLength <= tracingMaxContentLogSize { bodyContent = string(ct.requestBody) } else { - bodyContent = fmt.Sprintf("[Request Body Too Large For Logging, Max: %d bytes]", maxContentLogSize) + bodyContent = fmt.Sprintf("[Request Body Too Large For Logging, Max: %d bytes]", tracingMaxContentLogSize) } ct.span.AddEvent("http.request", trace.WithAttributes( label.Any(`http.request.headers`, ct.headers), @@ -163,7 +163,7 @@ func (ct *clientTracer) wait100Continue() { } func (ct *clientTracer) gotFirstResponseByte() { - ct.span.AddEvent("http.request.receive", trace.WithAttributes()) + } func (ct *clientTracer) got1xxResponse(code int, header textproto.MIMEHeader) error { diff --git a/net/ghttp/internal/httputil/httputils.go b/net/ghttp/internal/httputil/httputils.go index 209acb782..f7ea4db35 100644 --- a/net/ghttp/internal/httputil/httputils.go +++ b/net/ghttp/internal/httputil/httputils.go @@ -8,6 +8,7 @@ package httputil import ( "github.com/gogf/gf/text/gstr" + "net/http" "strings" "github.com/gogf/gf/encoding/gurl" @@ -64,3 +65,16 @@ func BuildParams(params interface{}, noUrlEncode ...bool) (encodedParamStr strin } return } + +// HeaderToMap coverts request headers to map. +func HeaderToMap(header http.Header) map[string]interface{} { + m := make(map[string]interface{}) + for k, v := range header { + if len(v) > 1 { + m[k] = v + } else { + m[k] = v[0] + } + } + return m +} diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go new file mode 100644 index 000000000..232951004 --- /dev/null +++ b/net/gtrace/gtrace.go @@ -0,0 +1,48 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package gtrace provides convenience wrapping functionality for tracing feature using OpenTelemetry. +package gtrace + +import ( + "context" + "github.com/gogf/gf/container/gvar" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/baggage" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/trace" +) + +// Tracer is a short function for retrieve Tracer. +func Tracer(name ...string) trace.Tracer { + tracerName := "" + if len(name) > 0 { + tracerName = name[0] + } + return otel.Tracer(tracerName) +} + +// SetBaggageValue is a convenient function for adding one key-value pair to baggage. +// Note that it uses label.Any to set the key-value pair. +func SetBaggageValue(ctx context.Context, key string, value interface{}) context.Context { + return baggage.ContextWithValues(ctx, label.Any(key, value)) +} + +// SetBaggageMap is a convenient function for adding map key-value pairs to baggage. +// Note that it uses label.Any to set the key-value pair. +func SetBaggageMap(ctx context.Context, data map[string]interface{}) context.Context { + pairs := make([]label.KeyValue, 0) + for k, v := range data { + pairs = append(pairs, label.Any(k, v)) + } + return baggage.ContextWithValues(ctx, pairs...) +} + +// GetBaggageVar retrieves value and returns a *gvar.Var for specified key from baggage. +func GetBaggageVar(ctx context.Context, key string) *gvar.Var { + value := baggage.Value(ctx, label.Key(key)) + return gvar.New(value.AsInterface()) +} From b89d8d2740ebaeac317abfa9cc41d4291d662ae8 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Mon, 25 Jan 2021 21:17:32 +0800 Subject: [PATCH 110/492] add tracing feature for package gdb --- .example/net/gtrace/2.http+db/config.toml | 20 +++ .example/net/gtrace/2.http+db/main.go | 64 +++++++++ database/gdb/gdb.go | 12 +- database/gdb/gdb_core.go | 168 ++++++++++++++++------ database/gdb/gdb_core_config.go | 1 + database/gdb/gdb_driver_mssql.go | 15 ++ database/gdb/gdb_driver_mysql.go | 15 ++ database/gdb/gdb_driver_oracle.go | 21 ++- database/gdb/gdb_driver_pgsql.go | 15 ++ database/gdb/gdb_driver_sqlite.go | 6 + database/gdb/gdb_statement.go | 163 +++++++++++++++++++++ database/gdb/gdb_transaction.go | 2 +- 12 files changed, 449 insertions(+), 53 deletions(-) create mode 100644 .example/net/gtrace/2.http+db/config.toml create mode 100644 .example/net/gtrace/2.http+db/main.go create mode 100644 database/gdb/gdb_statement.go diff --git a/.example/net/gtrace/2.http+db/config.toml b/.example/net/gtrace/2.http+db/config.toml new file mode 100644 index 000000000..5e0bcca3b --- /dev/null +++ b/.example/net/gtrace/2.http+db/config.toml @@ -0,0 +1,20 @@ + +# MySQL. +[database] + [database.logger] + Level = "all" + Stdout = true + CtxKeys = ["Trace-Id"] + [database.default] + link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + debug = true + +# Redis. +[redis] + default = "127.0.0.1:6379,0" + cache = "127.0.0.1:6379,1" + + + + + diff --git a/.example/net/gtrace/2.http+db/main.go b/.example/net/gtrace/2.http+db/main.go new file mode 100644 index 000000000..1700ca05f --- /dev/null +++ b/.example/net/gtrace/2.http+db/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "go.opentelemetry.io/otel/exporters/trace/jaeger" + sdkTrace "go.opentelemetry.io/otel/sdk/trace" +) + +const ( + JaegerEndpoint = "http://localhost:14268/api/traces" + ServiceName = "TracingHttpServerWithDatabase" +) + +// initTracer creates a new trace provider instance and registers it as global trace provider. +func initTracer() func() { + // Create and install Jaeger export pipeline. + flush, err := jaeger.InstallNewPipeline( + jaeger.WithCollectorEndpoint(JaegerEndpoint), + jaeger.WithProcess(jaeger.Process{ + ServiceName: ServiceName, + }), + jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), + ) + if err != nil { + g.Log().Fatal(err) + } + return flush +} + +func main() { + flush := initTracer() + defer flush() + + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(ghttp.MiddlewareServerTracing) + group.ALL("/user", new(dbTracingApi)) + }) + s.SetPort(8199) + s.Run() +} + +type dbTracingApi struct{} + +func (api *dbTracingApi) Insert(r *ghttp.Request) { + result, err := g.Table("user").Ctx(r.Context()).Insert(g.Map{ + "name": r.GetString("name"), + }) + if err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + id, _ := result.LastInsertId() + r.Response.Write("id:", id) +} + +func (api *dbTracingApi) Query(r *ghttp.Request) { + one, err := g.Table("user").Ctx(r.Context()).FindOne(r.GetInt("id")) + if err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + r.Response.Write("user:", one) +} diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 898624713..aebb0f5fa 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -55,7 +55,7 @@ type DB interface { Query(sql string, args ...interface{}) (*sql.Rows, error) Exec(sql string, args ...interface{}) (sql.Result, error) - Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error) + Prepare(sql string, execOnMaster ...bool) (*Stmt, error) // =========================================================================== // Common APIs for CURD. @@ -80,7 +80,7 @@ type DB interface { DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) - DoPrepare(link Link, sql string) (*sql.Stmt, error) + DoPrepare(link Link, sql string) (*Stmt, error) DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) @@ -154,6 +154,7 @@ type DB interface { Tables(schema ...string) (tables []string, err error) TableFields(table string, schema ...string) (map[string]*TableField, error) HasTable(name string) (bool, error) + FilteredLinkInfo() string // HandleSqlBeforeCommit is a hook function, which deals with the sql string before // it's committed to underlying driver. The parameter specifies the current @@ -191,6 +192,7 @@ type Driver interface { // Sql is the sql recording struct. type Sql struct { Sql string // SQL string(may contain reserved char '?'). + Type string // SQL operation type. Args []interface{} // Arguments for this sql. Format string // Formatted sql which contains arguments in the sql. Error error // Execution result. @@ -216,9 +218,9 @@ type Link interface { Query(sql string, args ...interface{}) (*sql.Rows, error) Exec(sql string, args ...interface{}) (sql.Result, error) Prepare(sql string) (*sql.Stmt, error) - QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) - ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) - PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) + QueryContext(ctx context.Context, sql string, args ...interface{}) (*sql.Rows, error) + ExecContext(ctx context.Context, sql string, args ...interface{}) (sql.Result, error) + PrepareContext(ctx context.Context, sql string) (*sql.Stmt, error) } // Counter is the type for update count. diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 5ccdc2516..933af0d83 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -11,8 +11,13 @@ import ( "context" "database/sql" "fmt" + "github.com/gogf/gf" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/text/gstr" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/trace" "reflect" "strings" @@ -85,24 +90,27 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) ctx := c.DB.GetCtx() if c.GetConfig().QueryTimeout > 0 { - ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) + var cancelFunc context.CancelFunc + ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) + defer cancelFunc() } + + mTime1 := gtime.TimestampMilli() + rows, err = link.QueryContext(ctx, sql, args...) + mTime2 := gtime.TimestampMilli() + sqlObj := &Sql{ + Sql: sql, + Type: "DB.QueryContext", + Args: args, + Format: FormatSqlWithArgs(sql, args), + Error: err, + Start: mTime1, + End: mTime2, + Group: c.DB.GetGroup(), + } + c.addSqlToTracing(ctx, sqlObj) if c.DB.GetDebug() { - mTime1 := gtime.TimestampMilli() - rows, err = link.QueryContext(ctx, sql, args...) - mTime2 := gtime.TimestampMilli() - s := &Sql{ - Sql: sql, - Args: args, - Format: FormatSqlWithArgs(sql, args), - Error: err, - Start: mTime1, - End: mTime2, - Group: c.DB.GetGroup(), - } - c.writeSqlToLogger(s) - } else { - rows, err = link.QueryContext(ctx, sql, args...) + c.writeSqlToLogger(sqlObj) } if err == nil { return rows, nil @@ -112,6 +120,48 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro return nil, err } +func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { + if !c.DB.GetConfig().Tracing { + return + } + + tr := otel.GetTracerProvider().Tracer( + "github.com/gogf/gf/database/gdb", + trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), + ) + ctx, span := tr.Start(ctx, sql.Type) + defer span.End() + if sql.Error != nil { + span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, sql.Error)) + } + labels := make([]label.KeyValue, 0) + labels = append(labels, label.String("db.type", c.DB.GetConfig().Type)) + if c.DB.GetConfig().Host != "" { + labels = append(labels, label.String("db.host", c.DB.GetConfig().Host)) + } + if c.DB.GetConfig().Port != "" { + labels = append(labels, label.String("db.port", c.DB.GetConfig().Port)) + } + if c.DB.GetConfig().Name != "" { + labels = append(labels, label.String("db.name", c.DB.GetConfig().Name)) + } + if c.DB.GetConfig().User != "" { + labels = append(labels, label.String("db.user", c.DB.GetConfig().User)) + } + if filteredLinkInfo := c.DB.FilteredLinkInfo(); filteredLinkInfo != "" { + labels = append(labels, label.String("db.link", c.DB.FilteredLinkInfo())) + } + if group := c.DB.GetGroup(); group != "" { + labels = append(labels, label.String("db.group", group)) + } + span.SetAttributes(labels...) + span.AddEvent("db.execution", trace.WithAttributes( + label.String(`db.execution.sql`, sql.Format), + label.String(`db.execution.cost`, fmt.Sprintf(`%d ms`, sql.End-sql.Start)), + label.String(`db.execution.type`, sql.Type), + )) +} + // Exec commits one query SQL to underlying driver and returns the execution result. // It is most commonly used for data inserting and updating. func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error) { @@ -129,32 +179,31 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) ctx := c.DB.GetCtx() if c.GetConfig().ExecTimeout > 0 { - ctx, _ = context.WithTimeout(ctx, c.GetConfig().ExecTimeout) + var cancelFunc context.CancelFunc + ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout) + defer cancelFunc() } - if c.DB.GetDebug() { - mTime1 := gtime.TimestampMilli() - if !c.DB.GetDryRun() { - result, err = link.ExecContext(ctx, sql, args...) - } else { - result = new(SqlResult) - } - mTime2 := gtime.TimestampMilli() - s := &Sql{ - Sql: sql, - Args: args, - Format: FormatSqlWithArgs(sql, args), - Error: err, - Start: mTime1, - End: mTime2, - Group: c.DB.GetGroup(), - } - c.writeSqlToLogger(s) + + mTime1 := gtime.TimestampMilli() + if !c.DB.GetDryRun() { + result, err = link.ExecContext(ctx, sql, args...) } else { - if !c.DB.GetDryRun() { - result, err = link.ExecContext(ctx, sql, args...) - } else { - result = new(SqlResult) - } + result = new(SqlResult) + } + mTime2 := gtime.TimestampMilli() + sqlObj := &Sql{ + Sql: sql, + Type: "DB.ExecContext", + Args: args, + Format: FormatSqlWithArgs(sql, args), + Error: err, + Start: mTime1, + End: mTime2, + Group: c.DB.GetGroup(), + } + c.addSqlToTracing(ctx, sqlObj) + if c.DB.GetDebug() { + c.writeSqlToLogger(sqlObj) } return result, formatError(err, sql, args...) } @@ -167,7 +216,7 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re // // The parameter specifies whether executing the sql on master node, // or else it executes the sql on slave node if master-slave configured. -func (c *Core) Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error) { +func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) { var ( err error link Link @@ -185,12 +234,37 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error) { } // doPrepare calls prepare function on given link object and returns the statement object. -func (c *Core) DoPrepare(link Link, sql string) (*sql.Stmt, error) { +func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) { ctx := c.DB.GetCtx() - if c.GetConfig().QueryTimeout > 0 { - ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) + if c.GetConfig().PrepareTimeout > 0 { + var cancelFunc context.CancelFunc + ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout) + defer cancelFunc() } - return link.PrepareContext(ctx, sql) + var ( + mTime1 = gtime.TimestampMilli() + stmt, err = link.PrepareContext(ctx, sql) + mTime2 = gtime.TimestampMilli() + sqlObj = &Sql{ + Sql: sql, + Type: "DB.PrepareContext", + Args: nil, + Format: FormatSqlWithArgs(sql, nil), + Error: err, + Start: mTime1, + End: mTime2, + Group: c.DB.GetGroup(), + } + ) + c.addSqlToTracing(ctx, sqlObj) + if c.DB.GetDebug() { + c.writeSqlToLogger(sqlObj) + } + return &Stmt{ + Stmt: stmt, + core: c, + sql: sql, + }, err } // GetAll queries and returns data records from database. @@ -334,7 +408,9 @@ func (c *Core) Begin() (*TX, error) { } else { ctx := c.DB.GetCtx() if c.GetConfig().TranTimeout > 0 { - ctx, _ = context.WithTimeout(ctx, c.GetConfig().TranTimeout) + var cancelFunc context.CancelFunc + ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().TranTimeout) + defer cancelFunc() } if tx, err := master.BeginTx(ctx, nil); err == nil { return &TX{ diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index cbc4e4f9f..461583379 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -51,6 +51,7 @@ type ConfigNode struct { UpdatedAt string `json:"updatedAt"` // (Optional) The filed name of table for automatic-filled updated datetime. DeletedAt string `json:"deletedAt"` // (Optional) The filed name of table for automatic-filled updated datetime. TimeMaintainDisabled bool `json:"timeMaintainDisabled"` // (Optional) Disable the automatic time maintaining feature. + Tracing bool `json:"tracing"` // (Optional) Tracing enable the tracing feature of database. } // configs is internal used configuration object. diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index c5433e9d3..c97387ca8 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -56,6 +56,21 @@ func (d *DriverMssql) Open(config *ConfigNode) (*sql.DB, error) { } } +// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for +// logging or tracing purpose. +func (d *DriverMssql) FilteredLinkInfo() string { + linkInfo := d.GetConfig().LinkInfo + if linkInfo == "" { + return "" + } + s, _ := gregex.ReplaceString( + `(.+);\s*password=(.+);\s*server=(.+)`, + `$1;password=xxx;server=$3`, + d.GetConfig().LinkInfo, + ) + return s +} + // GetChars returns the security char for this type of database. func (d *DriverMssql) GetChars() (charLeft string, charRight string) { return "\"", "\"" diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index 6831f5b27..e2430a1cf 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -54,6 +54,21 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) { } } +// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for +// logging or tracing purpose. +func (d *DriverMysql) FilteredLinkInfo() string { + linkInfo := d.GetConfig().LinkInfo + if linkInfo == "" { + return "" + } + s, _ := gregex.ReplaceString( + `(.+?):(.+)@tcp(.+)`, + `$1:xxx@tcp$3`, + linkInfo, + ) + return s +} + // GetChars returns the security char for this type of database. func (d *DriverMysql) GetChars() (charLeft string, charRight string) { return "`", "`" diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index 0a62443de..b33db9dc7 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -49,7 +49,11 @@ func (d *DriverOracle) Open(config *ConfigNode) (*sql.DB, error) { if config.LinkInfo != "" { source = config.LinkInfo } else { - source = fmt.Sprintf("%s/%s@%s", config.User, config.Pass, config.Name) + // 账号/密码@地址:端口/数据库名称 + source = fmt.Sprintf( + "%s/%s@%s:%s/%s", + config.User, config.Pass, config.Host, config.Port, config.Name, + ) } intlog.Printf("Open: %s", source) if db, err := sql.Open("oci8", source); err == nil { @@ -59,6 +63,21 @@ func (d *DriverOracle) Open(config *ConfigNode) (*sql.DB, error) { } } +// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for +// logging or tracing purpose. +func (d *DriverOracle) FilteredLinkInfo() string { + linkInfo := d.GetConfig().LinkInfo + if linkInfo == "" { + return "" + } + s, _ := gregex.ReplaceString( + `(.+?)\s*/\s*(.+)\s*@\s*(.+)\s*:\s*(\d+)\s*/\s*(.+)`, + `$1/xxx@$3:$4/$5`, + linkInfo, + ) + return s +} + // GetChars returns the security char for this type of database. func (d *DriverOracle) GetChars() (charLeft string, charRight string) { return "\"", "\"" diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 07a2bd18c..64c6c7361 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -54,6 +54,21 @@ func (d *DriverPgsql) Open(config *ConfigNode) (*sql.DB, error) { } } +// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for +// logging or tracing purpose. +func (d *DriverPgsql) FilteredLinkInfo() string { + linkInfo := d.GetConfig().LinkInfo + if linkInfo == "" { + return "" + } + s, _ := gregex.ReplaceString( + `(.+?)\s*password=(.+)\s*host=(.+)`, + `$1 password=xxx host=$3`, + linkInfo, + ) + return s +} + // GetChars returns the security char for this type of database. func (d *DriverPgsql) GetChars() (charLeft string, charRight string) { return "\"", "\"" diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index 5457ecdb4..22d5156c6 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -53,6 +53,12 @@ func (d *DriverSqlite) Open(config *ConfigNode) (*sql.DB, error) { } } +// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for +// logging or tracing purpose. +func (d *DriverSqlite) FilteredLinkInfo() string { + return d.GetConfig().LinkInfo +} + // GetChars returns the security char for this type of database. func (d *DriverSqlite) GetChars() (charLeft string, charRight string) { return "`", "`" diff --git a/database/gdb/gdb_statement.go b/database/gdb/gdb_statement.go new file mode 100644 index 000000000..33e74cbd9 --- /dev/null +++ b/database/gdb/gdb_statement.go @@ -0,0 +1,163 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +import ( + "context" + "database/sql" + "github.com/gogf/gf/os/gtime" +) + +// Stmt is a prepared statement. +// A Stmt is safe for concurrent use by multiple goroutines. +// +// If a Stmt is prepared on a Tx or Conn, it will be bound to a single +// underlying connection forever. If the Tx or Conn closes, the Stmt will +// become unusable and all operations will return an error. +// If a Stmt is prepared on a DB, it will remain usable for the lifetime of the +// DB. When the Stmt needs to execute on a new underlying connection, it will +// prepare itself on the new connection automatically. +type Stmt struct { + *sql.Stmt + core *Core + sql string +} + +// ExecContext executes a prepared statement with the given arguments and +// returns a Result summarizing the effect of the statement. +func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error) { + var cancelFunc context.CancelFunc + ctx, cancelFunc = context.WithCancel(ctx) + defer cancelFunc() + if s.core.DB.GetConfig().ExecTimeout > 0 { + var cancelFuncForTimeout context.CancelFunc + ctx, cancelFuncForTimeout = context.WithTimeout(ctx, s.core.DB.GetConfig().ExecTimeout) + defer cancelFuncForTimeout() + } + var ( + mTime1 = gtime.TimestampMilli() + result, err = s.Stmt.ExecContext(ctx, args...) + mTime2 = gtime.TimestampMilli() + sqlObj = &Sql{ + Sql: s.sql, + Type: "Statement.ExecContext", + Args: args, + Format: FormatSqlWithArgs(s.sql, args), + Error: err, + Start: mTime1, + End: mTime2, + Group: s.core.DB.GetGroup(), + } + ) + s.core.addSqlToTracing(ctx, sqlObj) + if s.core.DB.GetDebug() { + s.core.writeSqlToLogger(sqlObj) + } + return result, err +} + +// QueryContext executes a prepared query statement with the given arguments +// and returns the query results as a *Rows. +func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*sql.Rows, error) { + var cancelFunc context.CancelFunc + ctx, cancelFunc = context.WithCancel(ctx) + defer cancelFunc() + if s.core.DB.GetConfig().QueryTimeout > 0 { + var cancelFuncForTimeout context.CancelFunc + ctx, cancelFuncForTimeout = context.WithTimeout(ctx, s.core.DB.GetConfig().QueryTimeout) + defer cancelFuncForTimeout() + } + var ( + mTime1 = gtime.TimestampMilli() + rows, err = s.Stmt.QueryContext(ctx, args...) + mTime2 = gtime.TimestampMilli() + sqlObj = &Sql{ + Sql: s.sql, + Type: "Statement.QueryContext", + Args: args, + Format: FormatSqlWithArgs(s.sql, args), + Error: err, + Start: mTime1, + End: mTime2, + Group: s.core.DB.GetGroup(), + } + ) + s.core.addSqlToTracing(ctx, sqlObj) + if s.core.DB.GetDebug() { + s.core.writeSqlToLogger(sqlObj) + } + return rows, err +} + +// QueryRowContext executes a prepared query statement with the given arguments. +// If an error occurs during the execution of the statement, that error will +// be returned by a call to Scan on the returned *Row, which is always non-nil. +// If the query selects no rows, the *Row's Scan will return ErrNoRows. +// Otherwise, the *Row's Scan scans the first selected row and discards +// the rest. +func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *sql.Row { + var cancelFunc context.CancelFunc + ctx, cancelFunc = context.WithCancel(ctx) + defer cancelFunc() + if s.core.DB.GetConfig().QueryTimeout > 0 { + var cancelFuncForTimeout context.CancelFunc + ctx, cancelFuncForTimeout = context.WithTimeout(ctx, s.core.DB.GetConfig().QueryTimeout) + defer cancelFuncForTimeout() + } + var ( + mTime1 = gtime.TimestampMilli() + row = s.Stmt.QueryRowContext(ctx, args...) + mTime2 = gtime.TimestampMilli() + sqlObj = &Sql{ + Sql: s.sql, + Type: "Statement.QueryRowContext", + Args: args, + Format: FormatSqlWithArgs(s.sql, args), + Error: nil, + Start: mTime1, + End: mTime2, + Group: s.core.DB.GetGroup(), + } + ) + s.core.addSqlToTracing(ctx, sqlObj) + if s.core.DB.GetDebug() { + s.core.writeSqlToLogger(sqlObj) + } + return row +} + +// Exec executes a prepared statement with the given arguments and +// returns a Result summarizing the effect of the statement. +func (s *Stmt) Exec(args ...interface{}) (sql.Result, error) { + return s.ExecContext(context.Background(), args) +} + +// Query executes a prepared query statement with the given arguments +// and returns the query results as a *Rows. +func (s *Stmt) Query(args ...interface{}) (*sql.Rows, error) { + return s.QueryContext(context.Background(), args...) +} + +// QueryRow executes a prepared query statement with the given arguments. +// If an error occurs during the execution of the statement, that error will +// be returned by a call to Scan on the returned *Row, which is always non-nil. +// If the query selects no rows, the *Row's Scan will return ErrNoRows. +// Otherwise, the *Row's Scan scans the first selected row and discards +// the rest. +// +// Example usage: +// +// var name string +// err := nameByUseridStmt.QueryRow(id).Scan(&name) +func (s *Stmt) QueryRow(args ...interface{}) *sql.Row { + return s.QueryRowContext(context.Background(), args...) +} + +// Close closes the statement. +func (s *Stmt) Close() error { + return s.Stmt.Close() +} diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_transaction.go index e4b0de61b..d59114d2b 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_transaction.go @@ -48,7 +48,7 @@ func (tx *TX) Exec(sql string, args ...interface{}) (sql.Result, error) { // returned statement. // The caller must call the statement's Close method // when the statement is no longer needed. -func (tx *TX) Prepare(sql string) (*sql.Stmt, error) { +func (tx *TX) Prepare(sql string) (*Stmt, error) { return tx.db.DoPrepare(tx.tx, sql) } From 99dd889ff098fb23078f4f3db805186c35774790 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Mon, 25 Jan 2021 21:59:44 +0800 Subject: [PATCH 111/492] add tracing feature for package gredis --- .example/net/gtrace/3.http+redis/config.toml | 10 +++ .example/net/gtrace/3.http+redis/main.go | 63 ++++++++++++++++++ database/gdb/gdb_core_config.go | 2 +- database/gredis/gredis.go | 67 ++++++++++++++------ database/gredis/gredis_config.go | 19 +++--- database/gredis/gredis_conn.go | 43 ++++++++++++- database/gredis/gredis_z_unit_test.go | 8 +-- 7 files changed, 180 insertions(+), 32 deletions(-) create mode 100644 .example/net/gtrace/3.http+redis/config.toml create mode 100644 .example/net/gtrace/3.http+redis/main.go diff --git a/.example/net/gtrace/3.http+redis/config.toml b/.example/net/gtrace/3.http+redis/config.toml new file mode 100644 index 000000000..d77f4195b --- /dev/null +++ b/.example/net/gtrace/3.http+redis/config.toml @@ -0,0 +1,10 @@ + +# Redis. +[redis] + default = "127.0.0.1:6379,0?tracing=1" + cache = "127.0.0.1:6379,1?tracing=1" + + + + + diff --git a/.example/net/gtrace/3.http+redis/main.go b/.example/net/gtrace/3.http+redis/main.go new file mode 100644 index 000000000..9593c22dc --- /dev/null +++ b/.example/net/gtrace/3.http+redis/main.go @@ -0,0 +1,63 @@ +package main + +import ( + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "go.opentelemetry.io/otel/exporters/trace/jaeger" + sdkTrace "go.opentelemetry.io/otel/sdk/trace" +) + +const ( + JaegerEndpoint = "http://localhost:14268/api/traces" + ServiceName = "TracingHttpServerWithRedis" +) + +// initTracer creates a new trace provider instance and registers it as global trace provider. +func initTracer() func() { + // Create and install Jaeger export pipeline. + flush, err := jaeger.InstallNewPipeline( + jaeger.WithCollectorEndpoint(JaegerEndpoint), + jaeger.WithProcess(jaeger.Process{ + ServiceName: ServiceName, + }), + jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), + ) + if err != nil { + g.Log().Fatal(err) + } + return flush +} + +func main() { + flush := initTracer() + defer flush() + + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(ghttp.MiddlewareServerTracing) + group.ALL("/redis", new(redisTracingApi)) + }) + s.SetPort(8199) + s.Run() +} + +type redisTracingApi struct{} + +func (api *redisTracingApi) Set(r *ghttp.Request) { + _, err := g.Redis().Ctx(r.Context()).Do("SET", r.GetString("key"), r.GetString("value")) + if err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + r.Response.Write("ok") +} + +func (api *redisTracingApi) Get(r *ghttp.Request) { + value, err := g.Redis().Ctx(r.Context()).DoVar( + "GET", r.GetString("key"), + ) + if err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + r.Response.Write(value.String()) +} diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index 461583379..42eb59f54 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -51,7 +51,7 @@ type ConfigNode struct { UpdatedAt string `json:"updatedAt"` // (Optional) The filed name of table for automatic-filled updated datetime. DeletedAt string `json:"deletedAt"` // (Optional) The filed name of table for automatic-filled updated datetime. TimeMaintainDisabled bool `json:"timeMaintainDisabled"` // (Optional) Disable the automatic time maintaining feature. - Tracing bool `json:"tracing"` // (Optional) Tracing enable the tracing feature of database. + Tracing bool `json:"tracing"` // (Optional) Tracing enable the tracing feature for database. } // configs is internal used configuration object. diff --git a/database/gredis/gredis.go b/database/gredis/gredis.go index a19b7e26e..f192aa511 100644 --- a/database/gredis/gredis.go +++ b/database/gredis/gredis.go @@ -14,6 +14,7 @@ package gredis import ( + "context" "fmt" "time" @@ -24,14 +25,17 @@ import ( // Redis client. type Redis struct { - pool *redis.Pool // Underlying connection pool. - group string // Configuration group. - config Config // Configuration. + pool *redis.Pool // Underlying connection pool. + group string // Configuration group. + config *Config // Configuration. + ctx context.Context // Context. } // Redis connection. type Conn struct { redis.Conn + ctx context.Context + redis *Redis } // Redis configuration. @@ -46,7 +50,8 @@ type Config struct { MaxConnLifetime time.Duration `json:"maxConnLifetime"` // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0) ConnectTimeout time.Duration `json:"connectTimeout"` // Dial connection timeout. TLS bool `json:"tls"` // Specifies the config to use when a TLS connection is dialed. - TLSSkipVerify bool `json:"tlsSkipVerify"` // Disables server name verification when connecting over TLS + TLSSkipVerify bool `json:"tlsSkipVerify"` // Disables server name verification when connecting over TLS. + Tracing bool `json:"tracing"` // Tracing enable the tracing feature for redis. } // Pool statistics. @@ -55,11 +60,11 @@ type PoolStats struct { } const ( - gDEFAULT_POOL_IDLE_TIMEOUT = 10 * time.Second - gDEFAULT_POOL_CONN_TIMEOUT = 10 * time.Second - gDEFAULT_POOL_MAX_IDLE = 10 - gDEFAULT_POOL_MAX_ACTIVE = 100 - gDEFAULT_POOL_MAX_LIFE_TIME = 30 * time.Second + defaultPoolIdleTimeout = 10 * time.Second + defaultPoolConnTimeout = 10 * time.Second + defaultPoolMaxIdle = 10 + defaultPoolMaxActive = 100 + defaultPoolMaxLifeTime = 30 * time.Second ) var ( @@ -69,25 +74,25 @@ var ( // New creates a redis client object with given configuration. // Redis client maintains a connection pool automatically. -func New(config Config) *Redis { +func New(config *Config) *Redis { // The MaxIdle is the most important attribute of the connection pool. // Only if this attribute is set, the created connections from client // can not exceed the limit of the server. if config.MaxIdle == 0 { - config.MaxIdle = gDEFAULT_POOL_MAX_IDLE + config.MaxIdle = defaultPoolMaxIdle } // This value SHOULD NOT exceed the connection limit of redis server. if config.MaxActive == 0 { - config.MaxActive = gDEFAULT_POOL_MAX_ACTIVE + config.MaxActive = defaultPoolMaxActive } if config.IdleTimeout == 0 { - config.IdleTimeout = gDEFAULT_POOL_IDLE_TIMEOUT + config.IdleTimeout = defaultPoolIdleTimeout } if config.ConnectTimeout == 0 { - config.ConnectTimeout = gDEFAULT_POOL_CONN_TIMEOUT + config.ConnectTimeout = defaultPoolConnTimeout } if config.MaxConnLifetime == 0 { - config.MaxConnLifetime = gDEFAULT_POOL_MAX_LIFE_TIME + config.MaxConnLifetime = defaultPoolMaxLifeTime } return &Redis{ config: config, @@ -158,11 +163,29 @@ func (r *Redis) Close() error { return r.pool.Close() } +// Clone clones and returns a new Redis object, which is a shallow copy of current one. +func (r *Redis) Clone() *Redis { + newRedis := New(r.config) + *newRedis = *r + return newRedis +} + +// Ctx is a channing function which sets the context for next operation. +func (r *Redis) Ctx(ctx context.Context) *Redis { + newRedis := r.Clone() + newRedis.ctx = ctx + return newRedis +} + // Conn returns a raw underlying connection object, // which expose more methods to communicate with server. // **You should call Close function manually if you do not use this connection any further.** func (r *Redis) Conn() *Conn { - return &Conn{r.pool.Get()} + return &Conn{ + Conn: r.pool.Get(), + ctx: r.ctx, + redis: r, + } } // Alias of Conn, see Conn. @@ -209,7 +232,11 @@ func (r *Redis) Stats() *PoolStats { // Do automatically get a connection from pool, and close it when the reply received. // It does not really "close" the connection, but drops it back to the connection pool. func (r *Redis) Do(commandName string, args ...interface{}) (interface{}, error) { - conn := &Conn{r.pool.Get()} + conn := &Conn{ + Conn: r.pool.Get(), + ctx: r.ctx, + redis: r, + } defer conn.Close() return conn.Do(commandName, args...) } @@ -217,7 +244,11 @@ func (r *Redis) Do(commandName string, args ...interface{}) (interface{}, error) // DoWithTimeout sends a command to the server and returns the received reply. // The timeout overrides the read timeout set when dialing the connection. func (r *Redis) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (interface{}, error) { - conn := &Conn{r.pool.Get()} + conn := &Conn{ + Conn: r.pool.Get(), + ctx: r.ctx, + redis: r, + } defer conn.Close() return conn.DoWithTimeout(timeout, commandName, args...) } diff --git a/database/gredis/gredis_config.go b/database/gredis/gredis_config.go index 49584efb9..d8635d0eb 100644 --- a/database/gredis/gredis_config.go +++ b/database/gredis/gredis_config.go @@ -30,7 +30,7 @@ var ( // SetConfig sets the global configuration for specified group. // If is not passed, it sets configuration for the default group name. -func SetConfig(config Config, name ...string) { +func SetConfig(config *Config, name ...string) { group := DefaultGroupName if len(name) > 0 { group = name[0] @@ -59,15 +59,15 @@ func SetConfigByStr(str string, name ...string) error { // GetConfig returns the global configuration with specified group name. // If is not passed, it returns configuration of the default group name. -func GetConfig(name ...string) (config Config, ok bool) { +func GetConfig(name ...string) (config *Config, ok bool) { group := DefaultGroupName if len(name) > 0 { group = name[0] } if v := configs.Get(group); v != nil { - return v.(Config), true + return v.(*Config), true } - return Config{}, false + return &Config{}, false } // RemoveConfig removes the global configuration with specified group. @@ -85,11 +85,11 @@ func RemoveConfig(name ...string) { // ConfigFromStr parses and returns config from given str. // Eg: host:port[,db,pass?maxIdle=x&maxActive=x&idleTimeout=x&maxConnLifetime=x] -func ConfigFromStr(str string) (config Config, err error) { - array, _ := gregex.MatchString(`([^:]+):*(\d*),{0,1}(\d*),{0,1}(.*)\?(.+?)`, str) +func ConfigFromStr(str string) (config *Config, err error) { + array, _ := gregex.MatchString(`^([^:]+):*(\d*),{0,1}(\d*),{0,1}(.*)\?(.+)$`, str) if len(array) == 6 { parse, _ := gstr.Parse(array[5]) - config = Config{ + config = &Config{ Host: array[1], Port: gconv.Int(array[2]), Db: gconv.Int(array[3]), @@ -116,11 +116,14 @@ func ConfigFromStr(str string) (config Config, err error) { if v, ok := parse["skipVerify"]; ok { config.TLSSkipVerify = gconv.Bool(v) } + if v, ok := parse["tracing"]; ok { + config.Tracing = gconv.Bool(v) + } return } array, _ = gregex.MatchString(`([^:]+):*(\d*),{0,1}(\d*),{0,1}(.*)`, str) if len(array) == 5 { - config = Config{ + config = &Config{ Host: array[1], Port: gconv.Int(array[2]), Db: gconv.Int(array[3]), diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index 0a9f30b5b..97247a29d 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -7,11 +7,19 @@ package gredis import ( + "context" "errors" + "fmt" + "github.com/gogf/gf" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/json" + "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" "github.com/gomodule/redigo/redis" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/trace" "reflect" "time" ) @@ -52,7 +60,40 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} } return conn.DoWithTimeout(timeout, commandName, args...) } - return c.Conn.Do(commandName, args...) + timestampMilli1 := gtime.TimestampMilli() + reply, err = c.Conn.Do(commandName, args...) + timestampMilli2 := gtime.TimestampMilli() + // Tracing. + if !c.redis.config.Tracing { + return + } + tr := otel.GetTracerProvider().Tracer( + "github.com/gogf/gf/database/gredis", + trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), + ) + _, span := tr.Start(c.ctx, commandName) + defer span.End() + if err != nil { + span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) + } + span.SetAttributes( + label.String("redis.host", c.redis.config.Host), + label.Int("redis.port", c.redis.config.Port), + label.Int("redis.db", c.redis.config.Db), + ) + jsonBytes, _ := json.Marshal(args) + span.AddEvent("redis.execution", trace.WithAttributes( + label.String(`redis.execution.command`, commandName), + label.String(`redis.execution.cost`, fmt.Sprintf(`%d ms`, timestampMilli2-timestampMilli1)), + label.String(`redis.execution.arguments`, string(jsonBytes)), + )) + return +} + +// Ctx is a channing function which sets the context for next operation. +func (c *Conn) Ctx(ctx context.Context) *Conn { + c.ctx = ctx + return c } // Do sends a command to the server and returns the received reply. diff --git a/database/gredis/gredis_z_unit_test.go b/database/gredis/gredis_z_unit_test.go index b995511be..f3712b862 100644 --- a/database/gredis/gredis_z_unit_test.go +++ b/database/gredis/gredis_z_unit_test.go @@ -22,7 +22,7 @@ import ( ) var ( - config = gredis.Config{ + config = &gredis.Config{ Host: "127.0.0.1", Port: 6379, Db: 1, @@ -139,7 +139,7 @@ func Test_Instance(t *testing.T) { func Test_Error(t *testing.T) { gtest.C(t, func(t *gtest.T) { - config1 := gredis.Config{ + config1 := &gredis.Config{ Host: "127.0.0.2", Port: 6379, Db: 1, @@ -149,7 +149,7 @@ func Test_Error(t *testing.T) { _, err := redis.Do("info") t.AssertNE(err, nil) - config1 = gredis.Config{ + config1 = &gredis.Config{ Host: "127.0.0.1", Port: 6379, Db: 1, @@ -159,7 +159,7 @@ func Test_Error(t *testing.T) { _, err = redis.Do("info") t.AssertNE(err, nil) - config1 = gredis.Config{ + config1 = &gredis.Config{ Host: "127.0.0.1", Port: 6379, Db: 100, From 4c6d9f5eff48ec898dca9593a2bb74336b61c08a Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 26 Jan 2021 01:19:55 +0800 Subject: [PATCH 112/492] improve tracing feture for package glog/gdb/gredis --- .example/net/gtrace/2.http+db/config.toml | 5 +- .example/net/gtrace/2.http+db/main.go | 46 ++++++------ .example/net/gtrace/3.http+redis/config.toml | 4 +- .example/net/gtrace/3.http+redis/main.go | 42 +++++------ .../gtrace/4.http+db+redis+log/config.toml | 19 +++++ .../net/gtrace/4.http+db+redis+log/main.go | 71 +++++++++++++++++++ database/gdb/gdb_core.go | 6 +- database/gdb/gdb_core_config.go | 3 +- database/gdb/gdb_model_select.go | 2 +- database/gdb/gdb_z_mysql_method_test.go | 8 +-- .../gdb/gdb_z_mysql_time_maintain_test.go | 2 +- database/gredis/gredis.go | 1 - database/gredis/gredis_config.go | 26 +------ database/gredis/gredis_conn.go | 13 +++- go.mod | 1 + ...dleware.go => ghttp_middleware_tracing.go} | 0 os/glog/glog_logger.go | 32 ++++++--- 17 files changed, 186 insertions(+), 95 deletions(-) create mode 100644 .example/net/gtrace/4.http+db+redis+log/config.toml create mode 100644 .example/net/gtrace/4.http+db+redis+log/main.go rename net/ghttp/{ghttp_middleware.go => ghttp_middleware_tracing.go} (100%) diff --git a/.example/net/gtrace/2.http+db/config.toml b/.example/net/gtrace/2.http+db/config.toml index 5e0bcca3b..643cb6089 100644 --- a/.example/net/gtrace/2.http+db/config.toml +++ b/.example/net/gtrace/2.http+db/config.toml @@ -2,9 +2,8 @@ # MySQL. [database] [database.logger] - Level = "all" - Stdout = true - CtxKeys = ["Trace-Id"] + level = "all" + stdout = true [database.default] link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" debug = true diff --git a/.example/net/gtrace/2.http+db/main.go b/.example/net/gtrace/2.http+db/main.go index 1700ca05f..744971f1f 100644 --- a/.example/net/gtrace/2.http+db/main.go +++ b/.example/net/gtrace/2.http+db/main.go @@ -8,9 +8,30 @@ import ( sdkTrace "go.opentelemetry.io/otel/sdk/trace" ) +type tracingApi struct{} + +func (api *tracingApi) Insert(r *ghttp.Request) { + result, err := g.Table("user").Ctx(r.Context()).Insert(g.Map{ + "name": r.GetString("name"), + }) + if err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + id, _ := result.LastInsertId() + r.Response.Write("id:", id) +} + +func (api *tracingApi) Query(r *ghttp.Request) { + one, err := g.Table("user").Ctx(r.Context()).FindOne(r.GetInt("id")) + if err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + r.Response.Write("user:", one) +} + const ( JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "TracingHttpServerWithDatabase" + ServiceName = "TracingHttpServerWithDB" ) // initTracer creates a new trace provider instance and registers it as global trace provider. @@ -36,29 +57,8 @@ func main() { s := g.Server() s.Group("/", func(group *ghttp.RouterGroup) { group.Middleware(ghttp.MiddlewareServerTracing) - group.ALL("/user", new(dbTracingApi)) + group.ALL("/user", new(tracingApi)) }) s.SetPort(8199) s.Run() } - -type dbTracingApi struct{} - -func (api *dbTracingApi) Insert(r *ghttp.Request) { - result, err := g.Table("user").Ctx(r.Context()).Insert(g.Map{ - "name": r.GetString("name"), - }) - if err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - id, _ := result.LastInsertId() - r.Response.Write("id:", id) -} - -func (api *dbTracingApi) Query(r *ghttp.Request) { - one, err := g.Table("user").Ctx(r.Context()).FindOne(r.GetInt("id")) - if err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - r.Response.Write("user:", one) -} diff --git a/.example/net/gtrace/3.http+redis/config.toml b/.example/net/gtrace/3.http+redis/config.toml index d77f4195b..a1e7b2d69 100644 --- a/.example/net/gtrace/3.http+redis/config.toml +++ b/.example/net/gtrace/3.http+redis/config.toml @@ -1,8 +1,8 @@ # Redis. [redis] - default = "127.0.0.1:6379,0?tracing=1" - cache = "127.0.0.1:6379,1?tracing=1" + default = "127.0.0.1:6379,0" + cache = "127.0.0.1:6379,1" diff --git a/.example/net/gtrace/3.http+redis/main.go b/.example/net/gtrace/3.http+redis/main.go index 9593c22dc..9de300397 100644 --- a/.example/net/gtrace/3.http+redis/main.go +++ b/.example/net/gtrace/3.http+redis/main.go @@ -8,11 +8,31 @@ import ( sdkTrace "go.opentelemetry.io/otel/sdk/trace" ) +type tracingApi struct{} + const ( JaegerEndpoint = "http://localhost:14268/api/traces" ServiceName = "TracingHttpServerWithRedis" ) +func (api *tracingApi) Set(r *ghttp.Request) { + _, err := g.Redis().Ctx(r.Context()).Do("SET", r.GetString("key"), r.GetString("value")) + if err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + r.Response.Write("ok") +} + +func (api *tracingApi) Get(r *ghttp.Request) { + value, err := g.Redis().Ctx(r.Context()).DoVar( + "GET", r.GetString("key"), + ) + if err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + r.Response.Write(value.String()) +} + // initTracer creates a new trace provider instance and registers it as global trace provider. func initTracer() func() { // Create and install Jaeger export pipeline. @@ -36,28 +56,8 @@ func main() { s := g.Server() s.Group("/", func(group *ghttp.RouterGroup) { group.Middleware(ghttp.MiddlewareServerTracing) - group.ALL("/redis", new(redisTracingApi)) + group.ALL("/redis", new(tracingApi)) }) s.SetPort(8199) s.Run() } - -type redisTracingApi struct{} - -func (api *redisTracingApi) Set(r *ghttp.Request) { - _, err := g.Redis().Ctx(r.Context()).Do("SET", r.GetString("key"), r.GetString("value")) - if err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - r.Response.Write("ok") -} - -func (api *redisTracingApi) Get(r *ghttp.Request) { - value, err := g.Redis().Ctx(r.Context()).DoVar( - "GET", r.GetString("key"), - ) - if err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - r.Response.Write(value.String()) -} diff --git a/.example/net/gtrace/4.http+db+redis+log/config.toml b/.example/net/gtrace/4.http+db+redis+log/config.toml new file mode 100644 index 000000000..451299306 --- /dev/null +++ b/.example/net/gtrace/4.http+db+redis+log/config.toml @@ -0,0 +1,19 @@ + +# MySQL. +[database] + [database.logger] + level = "all" + stdout = true + [database.default] + link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + debug = true + +# Redis. +[redis] + default = "127.0.0.1:6379,0" + cache = "127.0.0.1:6379,1" + + + + + diff --git a/.example/net/gtrace/4.http+db+redis+log/main.go b/.example/net/gtrace/4.http+db+redis+log/main.go new file mode 100644 index 000000000..26fa71ae5 --- /dev/null +++ b/.example/net/gtrace/4.http+db+redis+log/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "github.com/gogf/gcache-adapter/adapter" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "go.opentelemetry.io/otel/exporters/trace/jaeger" + sdkTrace "go.opentelemetry.io/otel/sdk/trace" + "time" +) + +type tracingApi struct{} + +const ( + JaegerEndpoint = "http://localhost:14268/api/traces" + ServiceName = "TracingHttpServerWithDBRedisLog" +) + +// initTracer creates a new trace provider instance and registers it as global trace provider. +func initTracer() func() { + // Create and install Jaeger export pipeline. + flush, err := jaeger.InstallNewPipeline( + jaeger.WithCollectorEndpoint(JaegerEndpoint), + jaeger.WithProcess(jaeger.Process{ + ServiceName: ServiceName, + }), + jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), + ) + if err != nil { + g.Log().Fatal(err) + } + return flush +} + +func (api *tracingApi) Insert(r *ghttp.Request) { + result, err := g.Table("user").Ctx(r.Context()).Insert(g.Map{ + "name": r.GetString("name"), + }) + if err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + id, _ := result.LastInsertId() + r.Response.Write("id:", id) +} + +func (api *tracingApi) Query(r *ghttp.Request) { + one, err := g.Table("user"). + Ctx(r.Context()). + Cache(5 * time.Second). + FindOne(r.GetInt("id")) + if err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + r.Response.Write("user:", one) +} + +func main() { + flush := initTracer() + defer flush() + + g.DB().GetCache().SetAdapter(adapter.NewRedis(g.Redis())) + + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(ghttp.MiddlewareServerTracing) + group.ALL("/user", new(tracingApi)) + }) + s.SetPort(8199) + s.Run() +} diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 933af0d83..1d28ff4eb 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -121,7 +121,11 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro } func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { - if !c.DB.GetConfig().Tracing { + if ctx == nil { + return + } + spanCtx := trace.SpanContextFromContext(ctx) + if traceId := spanCtx.TraceID; !traceId.IsValid() { return } diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index 42eb59f54..9f6a6aa4b 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -51,7 +51,6 @@ type ConfigNode struct { UpdatedAt string `json:"updatedAt"` // (Optional) The filed name of table for automatic-filled updated datetime. DeletedAt string `json:"deletedAt"` // (Optional) The filed name of table for automatic-filled updated datetime. TimeMaintainDisabled bool `json:"timeMaintainDisabled"` // (Optional) Disable the automatic time maintaining feature. - Tracing bool `json:"tracing"` // (Optional) Tracing enable the tracing feature for database. } // configs is internal used configuration object. @@ -205,7 +204,7 @@ func (c *Core) SetDryRun(enabled bool) { // GetDryRun returns the DryRun value. // Deprecated, use GetConfig instead. func (c *Core) GetDryRun() bool { - return c.config.DryRun + return c.config.DryRun || allDryRun } // GetPrefix returns the table prefix string configured. diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 99fe48f8e..5670033d0 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -402,7 +402,7 @@ func (m *Model) FindValue(fieldsAndWhere ...interface{}) (Value, error) { } // FindArray queries and returns data values as slice from database. -// Note that if there're multiple columns in the result, it returns just one column values randomly. +// Note that if there are multiple columns in the result, it returns just one column values randomly. // Also see Model.WherePri and Model.Value. func (m *Model) FindArray(fieldsAndWhere ...interface{}) ([]Value, error) { if len(fieldsAndWhere) >= 2 { diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index 295b0d111..4f45a7f53 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -63,7 +63,7 @@ func Test_DB_Exec(t *testing.T) { func Test_DB_Prepare(t *testing.T) { gtest.C(t, func(t *gtest.T) { - st, err := db.Prepare("SELECT 100") + st, err := db.Prepare("SELECT 1") t.Assert(err, nil) rows, err := st.Query() @@ -71,10 +71,10 @@ func Test_DB_Prepare(t *testing.T) { array, err := rows.Columns() t.Assert(err, nil) - t.Assert(array[0], "100") + t.Assert(array[0], "1") - err = rows.Close() - t.Assert(err, nil) + //err = rows.Close() + //t.Assert(err, nil) }) } diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index 34efc5f22..d37c0cacf 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -368,7 +368,7 @@ CREATE TABLE %s ( t.Assert(oneUpdate["name"].String(), "name_1000") t.Assert(oneUpdate["deleted_at"].String(), "") t.Assert(oneUpdate["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) - t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Timestamp()-4) // Replace dataReplace := User{ diff --git a/database/gredis/gredis.go b/database/gredis/gredis.go index f192aa511..bb20a6973 100644 --- a/database/gredis/gredis.go +++ b/database/gredis/gredis.go @@ -51,7 +51,6 @@ type Config struct { ConnectTimeout time.Duration `json:"connectTimeout"` // Dial connection timeout. TLS bool `json:"tls"` // Specifies the config to use when a TLS connection is dialed. TLSSkipVerify bool `json:"tlsSkipVerify"` // Disables server name verification when connecting over TLS. - Tracing bool `json:"tracing"` // Tracing enable the tracing feature for redis. } // Pool statistics. diff --git a/database/gredis/gredis_config.go b/database/gredis/gredis_config.go index d8635d0eb..c2f2ba42e 100644 --- a/database/gredis/gredis_config.go +++ b/database/gredis/gredis_config.go @@ -7,10 +7,8 @@ package gredis import ( - "github.com/gogf/gf/internal/intlog" - "time" - "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/text/gregex" @@ -98,26 +96,8 @@ func ConfigFromStr(str string) (config *Config, err error) { if config.Port == 0 { config.Port = DefaultRedisPort } - if v, ok := parse["maxIdle"]; ok { - config.MaxIdle = gconv.Int(v) - } - if v, ok := parse["maxActive"]; ok { - config.MaxActive = gconv.Int(v) - } - if v, ok := parse["idleTimeout"]; ok { - config.IdleTimeout = gconv.Duration(v) * time.Second - } - if v, ok := parse["maxConnLifetime"]; ok { - config.MaxConnLifetime = gconv.Duration(v) * time.Second - } - if v, ok := parse["tls"]; ok { - config.TLS = gconv.Bool(v) - } - if v, ok := parse["skipVerify"]; ok { - config.TLSSkipVerify = gconv.Bool(v) - } - if v, ok := parse["tracing"]; ok { - config.Tracing = gconv.Bool(v) + if err = gconv.Struct(parse, config); err != nil { + return nil, err } return } diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index 97247a29d..3f5540359 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -63,15 +63,24 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} timestampMilli1 := gtime.TimestampMilli() reply, err = c.Conn.Do(commandName, args...) timestampMilli2 := gtime.TimestampMilli() + // Tracing. - if !c.redis.config.Tracing { + if c.ctx == nil { + return + } + spanCtx := trace.SpanContextFromContext(c.ctx) + if traceId := spanCtx.TraceID; !traceId.IsValid() { return } tr := otel.GetTracerProvider().Tracer( "github.com/gogf/gf/database/gredis", trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), ) - _, span := tr.Start(c.ctx, commandName) + ctx := c.ctx + if ctx == nil { + ctx = context.Background() + } + _, span := tr.Start(ctx, commandName) defer span.End() if err != nil { span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) diff --git a/go.mod b/go.mod index 9b3716251..efe36c7b9 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 github.com/fsnotify/fsnotify v1.4.9 github.com/go-sql-driver/mysql v1.5.0 + github.com/gogf/gcache-adapter v0.0.3 github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/websocket v1.4.1 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf diff --git a/net/ghttp/ghttp_middleware.go b/net/ghttp/ghttp_middleware_tracing.go similarity index 100% rename from net/ghttp/ghttp_middleware.go rename to net/ghttp/ghttp_middleware_tracing.go diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 288423e7c..47cda36b0 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -15,6 +15,7 @@ import ( "github.com/gogf/gf/os/gfpool" "github.com/gogf/gf/os/gmlock" "github.com/gogf/gf/os/gtimer" + "go.opentelemetry.io/otel/trace" "io" "os" "strings" @@ -161,21 +162,30 @@ func (l *Logger) print(std io.Writer, lead string, values ...interface{}) { tempStr = "" valueStr = "" ) - // Context values. - if l.ctx != nil && len(l.config.CtxKeys) > 0 { - ctxStr := "" - for _, key := range l.config.CtxKeys { - if v := l.ctx.Value(key); v != nil { - if ctxStr != "" { - ctxStr += ", " + + if l.ctx != nil { + // Tracing values. + spanCtx := trace.SpanContextFromContext(l.ctx) + if traceId := spanCtx.TraceID; traceId.IsValid() { + buffer.WriteString(fmt.Sprintf("{TraceID:%s} ", traceId.String())) + } + // Context values. + if len(l.config.CtxKeys) > 0 { + ctxStr := "" + for _, key := range l.config.CtxKeys { + if v := l.ctx.Value(key); v != nil { + if ctxStr != "" { + ctxStr += ", " + } + ctxStr += fmt.Sprintf("%s: %+v", key, v) } - ctxStr += fmt.Sprintf("%s: %+v", key, v) + } + if ctxStr != "" { + buffer.WriteString(fmt.Sprintf("{%s} ", ctxStr)) } } - if ctxStr != "" { - buffer.WriteString(fmt.Sprintf("{%s} ", ctxStr)) - } } + for _, v := range values { tempStr = gconv.String(v) if len(valueStr) > 0 { From 0baa52afee5d8ff4a94d13a0b7dd77e4413a0800 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 26 Jan 2021 11:09:50 +0800 Subject: [PATCH 113/492] improve statement features for package gdb --- database/gdb/gdb.go | 3 + database/gdb/gdb_core.go | 31 ++++- database/gdb/gdb_statement.go | 125 ++++++++----------- database/gdb/gdb_z_mysql_method_test.go | 8 +- database/gdb/gdb_z_mysql_transaction_test.go | 43 +++---- 5 files changed, 109 insertions(+), 101 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index aebb0f5fa..1e3665e2d 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -247,6 +247,9 @@ const ( defaultMaxIdleConnCount = 10 // Max idle connection count in pool. defaultMaxOpenConnCount = 100 // Max open connection count in pool. defaultMaxConnLifeTime = 30 * time.Second // Max life time for per connection in pool in seconds. + ctxTimeoutTypeExec = iota + ctxTimeoutTypeQuery + ctxTimeoutTypePrepare ) var ( diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 1d28ff4eb..797750602 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -61,6 +61,32 @@ func (c *Core) GetCtx() context.Context { return context.Background() } +// GetCtxTimeout returns the context and cancel function for specified timeout type. +func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Context, context.CancelFunc) { + if ctx == nil { + ctx = c.DB.GetCtx() + } else { + ctx = context.WithValue(ctx, "WrappedByGetCtxTimeout", nil) + } + switch timeoutType { + case ctxTimeoutTypeExec: + if c.DB.GetConfig().ExecTimeout > 0 { + return context.WithTimeout(ctx, c.DB.GetConfig().ExecTimeout) + } + case ctxTimeoutTypeQuery: + if c.DB.GetConfig().QueryTimeout > 0 { + return context.WithTimeout(ctx, c.DB.GetConfig().QueryTimeout) + } + case ctxTimeoutTypePrepare: + if c.DB.GetConfig().PrepareTimeout > 0 { + return context.WithTimeout(ctx, c.DB.GetConfig().PrepareTimeout) + } + default: + panic(gerror.Newf("invalid context timeout type: %d", timeoutType)) + } + return ctx, func() {} +} + // Master creates and returns a connection from master node if master-slave configured. // It returns the default connection if master-slave not configured. func (c *Core) Master() (*sql.DB, error) { @@ -241,9 +267,8 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) { func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) { ctx := c.DB.GetCtx() if c.GetConfig().PrepareTimeout > 0 { - var cancelFunc context.CancelFunc - ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout) - defer cancelFunc() + // DO NOT USE cancel function in prepare statement. + ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout) } var ( mTime1 = gtime.TimestampMilli() diff --git a/database/gdb/gdb_statement.go b/database/gdb/gdb_statement.go index 33e74cbd9..d861141fd 100644 --- a/database/gdb/gdb_statement.go +++ b/database/gdb/gdb_statement.go @@ -9,6 +9,7 @@ package gdb import ( "context" "database/sql" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" ) @@ -27,29 +28,47 @@ type Stmt struct { sql string } -// ExecContext executes a prepared statement with the given arguments and -// returns a Result summarizing the effect of the statement. -func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error) { - var cancelFunc context.CancelFunc - ctx, cancelFunc = context.WithCancel(ctx) - defer cancelFunc() - if s.core.DB.GetConfig().ExecTimeout > 0 { - var cancelFuncForTimeout context.CancelFunc - ctx, cancelFuncForTimeout = context.WithTimeout(ctx, s.core.DB.GetConfig().ExecTimeout) +const ( + stmtTypeExecContext = "Statement.ExecContext" + stmtTypeQueryContext = "Statement.QueryContext" + stmtTypeQueryRowContext = "Statement.QueryRowContext" +) + +// doStmtCommit commits statement according to given `stmtType`. +func (s *Stmt) doStmtCommit(stmtType string, ctx context.Context, args ...interface{}) (result interface{}, err error) { + var ( + cancelFuncForTimeout context.CancelFunc + timestampMilli1 = gtime.TimestampMilli() + ) + switch stmtType { + case stmtTypeExecContext: + ctx, cancelFuncForTimeout = s.core.GetCtxTimeout(ctxTimeoutTypeExec, ctx) defer cancelFuncForTimeout() + result, err = s.Stmt.ExecContext(ctx, args...) + + case stmtTypeQueryContext: + ctx, cancelFuncForTimeout = s.core.GetCtxTimeout(ctxTimeoutTypeQuery, ctx) + defer cancelFuncForTimeout() + result, err = s.Stmt.QueryContext(ctx, args...) + + case stmtTypeQueryRowContext: + ctx, cancelFuncForTimeout = s.core.GetCtxTimeout(ctxTimeoutTypeQuery, ctx) + defer cancelFuncForTimeout() + result = s.Stmt.QueryRowContext(ctx, args...) + + default: + panic(gerror.Newf(`invalid stmtType: %s`, stmtType)) } var ( - mTime1 = gtime.TimestampMilli() - result, err = s.Stmt.ExecContext(ctx, args...) - mTime2 = gtime.TimestampMilli() - sqlObj = &Sql{ + timestampMilli2 = gtime.TimestampMilli() + sqlObj = &Sql{ Sql: s.sql, - Type: "Statement.ExecContext", + Type: stmtType, Args: args, Format: FormatSqlWithArgs(s.sql, args), Error: err, - Start: mTime1, - End: mTime2, + Start: timestampMilli1, + End: timestampMilli2, Group: s.core.DB.GetGroup(), } ) @@ -60,37 +79,24 @@ func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result return result, err } +// ExecContext executes a prepared statement with the given arguments and +// returns a Result summarizing the effect of the statement. +func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error) { + result, err := s.doStmtCommit(stmtTypeExecContext, ctx, args...) + if result != nil { + return result.(sql.Result), err + } + return nil, err +} + // QueryContext executes a prepared query statement with the given arguments // and returns the query results as a *Rows. func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*sql.Rows, error) { - var cancelFunc context.CancelFunc - ctx, cancelFunc = context.WithCancel(ctx) - defer cancelFunc() - if s.core.DB.GetConfig().QueryTimeout > 0 { - var cancelFuncForTimeout context.CancelFunc - ctx, cancelFuncForTimeout = context.WithTimeout(ctx, s.core.DB.GetConfig().QueryTimeout) - defer cancelFuncForTimeout() + result, err := s.doStmtCommit(stmtTypeQueryContext, ctx, args...) + if result != nil { + return result.(*sql.Rows), err } - var ( - mTime1 = gtime.TimestampMilli() - rows, err = s.Stmt.QueryContext(ctx, args...) - mTime2 = gtime.TimestampMilli() - sqlObj = &Sql{ - Sql: s.sql, - Type: "Statement.QueryContext", - Args: args, - Format: FormatSqlWithArgs(s.sql, args), - Error: err, - Start: mTime1, - End: mTime2, - Group: s.core.DB.GetGroup(), - } - ) - s.core.addSqlToTracing(ctx, sqlObj) - if s.core.DB.GetDebug() { - s.core.writeSqlToLogger(sqlObj) - } - return rows, err + return nil, err } // QueryRowContext executes a prepared query statement with the given arguments. @@ -100,40 +106,17 @@ func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*sql.Rows // Otherwise, the *Row's Scan scans the first selected row and discards // the rest. func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *sql.Row { - var cancelFunc context.CancelFunc - ctx, cancelFunc = context.WithCancel(ctx) - defer cancelFunc() - if s.core.DB.GetConfig().QueryTimeout > 0 { - var cancelFuncForTimeout context.CancelFunc - ctx, cancelFuncForTimeout = context.WithTimeout(ctx, s.core.DB.GetConfig().QueryTimeout) - defer cancelFuncForTimeout() + result, _ := s.doStmtCommit(stmtTypeQueryRowContext, ctx, args...) + if result != nil { + return result.(*sql.Row) } - var ( - mTime1 = gtime.TimestampMilli() - row = s.Stmt.QueryRowContext(ctx, args...) - mTime2 = gtime.TimestampMilli() - sqlObj = &Sql{ - Sql: s.sql, - Type: "Statement.QueryRowContext", - Args: args, - Format: FormatSqlWithArgs(s.sql, args), - Error: nil, - Start: mTime1, - End: mTime2, - Group: s.core.DB.GetGroup(), - } - ) - s.core.addSqlToTracing(ctx, sqlObj) - if s.core.DB.GetDebug() { - s.core.writeSqlToLogger(sqlObj) - } - return row + return nil } // Exec executes a prepared statement with the given arguments and // returns a Result summarizing the effect of the statement. func (s *Stmt) Exec(args ...interface{}) (sql.Result, error) { - return s.ExecContext(context.Background(), args) + return s.ExecContext(context.Background(), args...) } // Query executes a prepared query statement with the given arguments diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index 4f45a7f53..295b0d111 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -63,7 +63,7 @@ func Test_DB_Exec(t *testing.T) { func Test_DB_Prepare(t *testing.T) { gtest.C(t, func(t *gtest.T) { - st, err := db.Prepare("SELECT 1") + st, err := db.Prepare("SELECT 100") t.Assert(err, nil) rows, err := st.Query() @@ -71,10 +71,10 @@ func Test_DB_Prepare(t *testing.T) { array, err := rows.Columns() t.Assert(err, nil) - t.Assert(array[0], "1") + t.Assert(array[0], "100") - //err = rows.Close() - //t.Assert(err, nil) + err = rows.Close() + t.Assert(err, nil) }) } diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index 8bd97cdee..be10ab716 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -88,29 +88,26 @@ func Test_TX_Rollback(t *testing.T) { } func Test_TX_Prepare(t *testing.T) { - tx, err := db.Begin() - if err != nil { - gtest.Error(err) - } - st, err := tx.Prepare("SELECT 100") - if err != nil { - gtest.Error(err) - } - rows, err := st.Query() - if err != nil { - gtest.Error(err) - } - array, err := rows.Columns() - if err != nil { - gtest.Error(err) - } - gtest.Assert(array[0], "100") - if err := rows.Close(); err != nil { - gtest.Error(err) - } - if err := tx.Commit(); err != nil { - gtest.Error(err) - } + gtest.C(t, func(t *gtest.T) { + tx, err := db.Begin() + t.Assert(err, nil) + + st, err := tx.Prepare("SELECT 100") + t.Assert(err, nil) + + rows, err := st.Query() + t.Assert(err, nil) + + array, err := rows.Columns() + t.Assert(err, nil) + t.Assert(array[0], "100") + + rows.Close() + t.Assert(err, nil) + + tx.Commit() + t.Assert(err, nil) + }) } func Test_TX_Insert(t *testing.T) { From 796774efe45810c344fa5efd04c8196314cf19e6 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 26 Jan 2021 13:33:24 +0800 Subject: [PATCH 114/492] improve package gcache --- os/gcache/gcache_adapter.go | 2 +- os/gcache/gcache_adapter_memory.go | 248 +++++------------- os/gcache/gcache_adapter_memory_data.go | 199 ++++++++++++++ .../gcache_adapter_memory_expire_sets.go | 51 ++++ .../gcache_adapter_memory_expire_times.go | 41 +++ 5 files changed, 352 insertions(+), 189 deletions(-) create mode 100644 os/gcache/gcache_adapter_memory_data.go create mode 100644 os/gcache/gcache_adapter_memory_expire_sets.go create mode 100644 os/gcache/gcache_adapter_memory_expire_times.go diff --git a/os/gcache/gcache_adapter.go b/os/gcache/gcache_adapter.go index 71c22c7c9..03b9f63bc 100644 --- a/os/gcache/gcache_adapter.go +++ b/os/gcache/gcache_adapter.go @@ -36,7 +36,7 @@ type Adapter interface { SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) // Get retrieves and returns the associated value of given . - // It returns nil if it does not exist or its value is nil. + // It returns nil if it does not exist, its value is nil or it's expired. Get(key interface{}) (interface{}, error) // GetOrSet retrieves and returns the value of , or sets - pair and diff --git a/os/gcache/gcache_adapter_memory.go b/os/gcache/gcache_adapter_memory.go index 2de6328f6..5bad1bb3f 100644 --- a/os/gcache/gcache_adapter_memory.go +++ b/os/gcache/gcache_adapter_memory.go @@ -8,7 +8,6 @@ package gcache import ( "math" - "sync" "time" "github.com/gogf/gf/container/glist" @@ -20,43 +19,18 @@ import ( // Internal cache object. type adapterMemory struct { - // dataMu ensures the concurrent safety of underlying data map. - dataMu sync.RWMutex - - // expireTimeMu ensures the concurrent safety of expireTimes map. - expireTimeMu sync.RWMutex - - // expireSetMu ensures the concurrent safety of expireSets map. - expireSetMu sync.RWMutex - // cap limits the size of the cache pool. // If the size of the cache exceeds the cap, // the cache expiration process performs according to the LRU algorithm. // It is 0 in default which means no limits. - cap int - - // data is the underlying cache data which is stored in a hash table. - data map[interface{}]adapterMemoryItem - - // expireTimes is the expiring key to its timestamp mapping, - // which is used for quick indexing and deleting. - expireTimes map[interface{}]int64 - - // expireSets is the expiring timestamp to its key set mapping, - // which is used for quick indexing and deleting. - expireSets map[int64]*gset.Set - - // lru is the LRU manager, which is enabled when attribute cap > 0. - lru *adapterMemoryLru - - // lruGetList is the LRU history according with Get function. - lruGetList *glist.List - - // eventList is the asynchronous event list for internal data synchronization. - eventList *glist.List - - // closed controls the cache closed or not. - closed *gtype.Bool + cap int + data *adapterMemoryData // data is the underlying cache data which is stored in a hash table. + expireTimes *adapterMemoryExpireTimes // expireTimes is the expiring key to its timestamp mapping, which is used for quick indexing and deleting. + expireSets *adapterMemoryExpireSets // expireSets is the expiring timestamp to its key set mapping, which is used for quick indexing and deleting. + lru *adapterMemoryLru // lru is the LRU manager, which is enabled when attribute cap > 0. + lruGetList *glist.List // lruGetList is the LRU history according with Get function. + eventList *glist.List // eventList is the asynchronous event list for internal data synchronization. + closed *gtype.Bool // closed controls the cache closed or not. } // Internal cache item. @@ -72,18 +46,18 @@ type adapterMemoryEvent struct { } const ( - // gDEFAULT_MAX_EXPIRE is the default expire time for no expiring items. + // defaultMaxExpire is the default expire time for no expiring items. // It equals to math.MaxInt64/1000000. - gDEFAULT_MAX_EXPIRE = 9223372036854 + defaultMaxExpire = 9223372036854 ) // newAdapterMemory creates and returns a new memory cache object. func newAdapterMemory(lruCap ...int) *adapterMemory { c := &adapterMemory{ + data: newAdapterMemoryData(), lruGetList: glist.New(true), - data: make(map[interface{}]adapterMemoryItem), - expireTimes: make(map[interface{}]int64), - expireSets: make(map[int64]*gset.Set), + expireTimes: newAdapterMemoryExpireTimes(), + expireSets: newAdapterMemoryExpireSets(), eventList: glist.New(true), closed: gtype.NewBool(), } @@ -100,12 +74,10 @@ func newAdapterMemory(lruCap ...int) *adapterMemory { // It deletes the if < 0. func (c *adapterMemory) Set(key interface{}, value interface{}, duration time.Duration) error { expireTime := c.getInternalExpire(duration) - c.dataMu.Lock() - c.data[key] = adapterMemoryItem{ + c.data.Set(key, adapterMemoryItem{ v: value, e: expireTime, - } - c.dataMu.Unlock() + }) c.eventList.PushBack(&adapterMemoryEvent{ k: key, e: expireTime, @@ -119,16 +91,7 @@ func (c *adapterMemory) Set(key interface{}, value interface{}, duration time.Du // It deletes the if given is nil. // It does nothing if does not exist in the cache. func (c *adapterMemory) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { - c.dataMu.Lock() - defer c.dataMu.Unlock() - if item, ok := c.data[key]; ok { - c.data[key] = adapterMemoryItem{ - v: value, - e: item.e, - } - return item.v, true, nil - } - return nil, false, nil + return c.data.Update(key, value) } // UpdateExpire updates the expiration of and returns the old expiration duration value. @@ -137,20 +100,17 @@ func (c *adapterMemory) Update(key interface{}, value interface{}) (oldValue int // It deletes the if < 0. func (c *adapterMemory) UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { newExpireTime := c.getInternalExpire(duration) - c.dataMu.Lock() - defer c.dataMu.Unlock() - if item, ok := c.data[key]; ok { - c.data[key] = adapterMemoryItem{ - v: item.v, - e: newExpireTime, - } + oldDuration, err = c.data.UpdateExpire(key, newExpireTime) + if err != nil { + return + } + if oldDuration != -1 { c.eventList.PushBack(&adapterMemoryEvent{ k: key, e: newExpireTime, }) - return time.Duration(item.e-gtime.TimestampMilli()) * time.Millisecond, nil } - return -1, nil + return } // GetExpire retrieves and returns the expiration of in the cache. @@ -158,9 +118,7 @@ func (c *adapterMemory) UpdateExpire(key interface{}, duration time.Duration) (o // It returns 0 if the does not expire. // It returns -1 if the does not exist in the cache. func (c *adapterMemory) GetExpire(key interface{}) (time.Duration, error) { - c.dataMu.RLock() - defer c.dataMu.RUnlock() - if item, ok := c.data[key]; ok { + if item, ok := c.data.Get(key); ok { return time.Duration(item.e-gtime.TimestampMilli()) * time.Millisecond, nil } return -1, nil @@ -194,14 +152,14 @@ func (c *adapterMemory) SetIfNotExist(key interface{}, value interface{}, durati // It does not expire if == 0. // It deletes the keys of if < 0 or given is nil. func (c *adapterMemory) Sets(data map[interface{}]interface{}, duration time.Duration) error { - expireTime := c.getInternalExpire(duration) - for k, v := range data { - c.dataMu.Lock() - c.data[k] = adapterMemoryItem{ - v: v, - e: expireTime, - } - c.dataMu.Unlock() + var ( + expireTime = c.getInternalExpire(duration) + err = c.data.Sets(data, expireTime) + ) + if err != nil { + return err + } + for k, _ := range data { c.eventList.PushBack(&adapterMemoryEvent{ k: k, e: expireTime, @@ -213,9 +171,7 @@ func (c *adapterMemory) Sets(data map[interface{}]interface{}, duration time.Dur // Get retrieves and returns the associated value of given . // It returns nil if it does not exist or its value is nil. func (c *adapterMemory) Get(key interface{}) (interface{}, error) { - c.dataMu.RLock() - item, ok := c.data[key] - c.dataMu.RUnlock() + item, ok := c.data.Get(key) if ok && !item.IsExpired() { // Adding to LRU history if LRU feature is enabled. if c.cap > 0 { @@ -304,76 +260,44 @@ func (c *adapterMemory) Contains(key interface{}) (bool, error) { // Remove deletes the one or more keys from cache, and returns its value. // If multiple keys are given, it returns the value of the deleted last item. func (c *adapterMemory) Remove(keys ...interface{}) (value interface{}, err error) { - c.dataMu.Lock() - defer c.dataMu.Unlock() - for _, key := range keys { - item, ok := c.data[key] - if ok { - value = item.v - delete(c.data, key) - c.eventList.PushBack(&adapterMemoryEvent{ - k: key, - e: gtime.TimestampMilli() - 1000, - }) - } + var removedKeys []interface{} + removedKeys, value, err = c.data.Remove(keys...) + if err != nil { + return } - return value, nil + for _, key := range removedKeys { + c.eventList.PushBack(&adapterMemoryEvent{ + k: key, + e: gtime.TimestampMilli() - 1000000, + }) + } + return } // Data returns a copy of all key-value pairs in the cache as map type. func (c *adapterMemory) Data() (map[interface{}]interface{}, error) { - m := make(map[interface{}]interface{}) - c.dataMu.RLock() - for k, v := range c.data { - if !v.IsExpired() { - m[k] = v.v - } - } - c.dataMu.RUnlock() - return m, nil + return c.data.Data() } // Keys returns all keys in the cache as slice. func (c *adapterMemory) Keys() ([]interface{}, error) { - keys := make([]interface{}, 0) - c.dataMu.RLock() - for k, v := range c.data { - if !v.IsExpired() { - keys = append(keys, k) - } - } - c.dataMu.RUnlock() - return keys, nil + return c.data.Keys() } // Values returns all values in the cache as slice. func (c *adapterMemory) Values() ([]interface{}, error) { - values := make([]interface{}, 0) - c.dataMu.RLock() - for _, v := range c.data { - if !v.IsExpired() { - values = append(values, v.v) - } - } - c.dataMu.RUnlock() - return values, nil + return c.data.Values() } // Size returns the size of the cache. func (c *adapterMemory) Size() (size int, err error) { - c.dataMu.RLock() - size = len(c.data) - c.dataMu.RUnlock() - return size, nil + return c.data.Size() } // Clear clears all data of the cache. // Note that this function is sensitive and should be carefully used. func (c *adapterMemory) Clear() error { - c.dataMu.Lock() - defer c.dataMu.Unlock() - c.data = make(map[interface{}]adapterMemoryItem) - return nil + return c.data.Clear() } // Close closes the cache. @@ -394,33 +318,17 @@ func (c *adapterMemory) Close() error { // // It doubly checks the whether exists in the cache using mutex writing lock // before setting it to the cache. -func (c *adapterMemory) doSetWithLockCheck(key interface{}, value interface{}, duration time.Duration) (interface{}, error) { +func (c *adapterMemory) doSetWithLockCheck(key interface{}, value interface{}, duration time.Duration) (result interface{}, err error) { expireTimestamp := c.getInternalExpire(duration) - c.dataMu.Lock() - defer c.dataMu.Unlock() - if v, ok := c.data[key]; ok && !v.IsExpired() { - return v.v, nil - } - if f, ok := value.(func() (interface{}, error)); ok { - v, err := f() - if err != nil { - return nil, err - } - if v == nil { - return nil, nil - } else { - value = v - } - } - c.data[key] = adapterMemoryItem{v: value, e: expireTimestamp} + result, err = c.data.SetWithLock(key, value, expireTimestamp) c.eventList.PushBack(&adapterMemoryEvent{k: key, e: expireTimestamp}) - return value, nil + return } // getInternalExpire converts and returns the expire time with given expired duration in milliseconds. func (c *adapterMemory) getInternalExpire(duration time.Duration) int64 { if duration == 0 { - return gDEFAULT_MAX_EXPIRE + return defaultMaxExpire } else { return gtime.TimestampMilli() + duration.Nanoseconds()/1000000 } @@ -431,30 +339,6 @@ func (c *adapterMemory) makeExpireKey(expire int64) int64 { return int64(math.Ceil(float64(expire/1000)+1) * 1000) } -// getExpireSet returns the expire set for given in seconds. -func (c *adapterMemory) getExpireSet(expire int64) (expireSet *gset.Set) { - c.expireSetMu.RLock() - expireSet, _ = c.expireSets[expire] - c.expireSetMu.RUnlock() - return -} - -// getOrNewExpireSet returns the expire set for given in seconds. -// It creates and returns a new set for if it does not exist. -func (c *adapterMemory) getOrNewExpireSet(expire int64) (expireSet *gset.Set) { - if expireSet = c.getExpireSet(expire); expireSet == nil { - expireSet = gset.New(true) - c.expireSetMu.Lock() - if es, ok := c.expireSets[expire]; ok { - expireSet = es - } else { - c.expireSets[expire] = expireSet - } - c.expireSetMu.Unlock() - } - return -} - // syncEventAndClearExpired does the asynchronous task loop: // 1. Asynchronously process the data in the event list, // and synchronize the results to the and properties. @@ -479,20 +363,16 @@ func (c *adapterMemory) syncEventAndClearExpired() { } event = v.(*adapterMemoryEvent) // Fetching the old expire set. - c.expireTimeMu.RLock() - oldExpireTime = c.expireTimes[event.k] - c.expireTimeMu.RUnlock() + oldExpireTime = c.expireTimes.Get(event.k) // Calculating the new expire set. newExpireTime = c.makeExpireKey(event.e) if newExpireTime != oldExpireTime { - c.getOrNewExpireSet(newExpireTime).Add(event.k) + c.expireSets.GetOrNew(newExpireTime).Add(event.k) if oldExpireTime != 0 { - c.getOrNewExpireSet(oldExpireTime).Remove(event.k) + c.expireSets.GetOrNew(oldExpireTime).Remove(event.k) } // Updating the expire time for . - c.expireTimeMu.Lock() - c.expireTimes[event.k] = newExpireTime - c.expireTimeMu.Unlock() + c.expireTimes.Set(event.k, newExpireTime) } // Adding the key the LRU history by writing operations. if c.cap > 0 { @@ -518,16 +398,14 @@ func (c *adapterMemory) syncEventAndClearExpired() { eks = []int64{ek - 1000, ek - 2000, ek - 3000, ek - 4000, ek - 5000} ) for _, expireTime := range eks { - if expireSet = c.getExpireSet(expireTime); expireSet != nil { + if expireSet = c.expireSets.Get(expireTime); expireSet != nil { // Iterating the set to delete all keys in it. expireSet.Iterator(func(key interface{}) bool { c.clearByKey(key) return true }) // Deleting the set after all of its keys are deleted. - c.expireSetMu.Lock() - delete(c.expireSets, expireTime) - c.expireSetMu.Unlock() + c.expireSets.Delete(expireTime) } } } @@ -535,17 +413,11 @@ func (c *adapterMemory) syncEventAndClearExpired() { // clearByKey deletes the key-value pair with given . // The parameter specifies whether doing this deleting forcibly. func (c *adapterMemory) clearByKey(key interface{}, force ...bool) { - c.dataMu.Lock() // Doubly check before really deleting it from cache. - if item, ok := c.data[key]; (ok && item.IsExpired()) || (len(force) > 0 && force[0]) { - delete(c.data, key) - } - c.dataMu.Unlock() + c.data.DeleteWithDoubleCheck(key, force...) // Deleting its expire time from . - c.expireTimeMu.Lock() - delete(c.expireTimes, key) - c.expireTimeMu.Unlock() + c.expireTimes.Delete(key) // Deleting it from LRU. if c.cap > 0 { diff --git a/os/gcache/gcache_adapter_memory_data.go b/os/gcache/gcache_adapter_memory_data.go new file mode 100644 index 000000000..839922e41 --- /dev/null +++ b/os/gcache/gcache_adapter_memory_data.go @@ -0,0 +1,199 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcache + +import ( + "github.com/gogf/gf/os/gtime" + "sync" + "time" +) + +type adapterMemoryData struct { + mu sync.RWMutex // dataMu ensures the concurrent safety of underlying data map. + data map[interface{}]adapterMemoryItem // data is the underlying cache data which is stored in a hash table. +} + +func newAdapterMemoryData() *adapterMemoryData { + return &adapterMemoryData{ + data: make(map[interface{}]adapterMemoryItem), + } +} + +// Update updates the value of without changing its expiration and returns the old value. +// The returned value is false if the does not exist in the cache. +// +// It deletes the if given is nil. +// It does nothing if does not exist in the cache. +func (d *adapterMemoryData) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { + d.mu.Lock() + defer d.mu.Unlock() + if item, ok := d.data[key]; ok { + d.data[key] = adapterMemoryItem{ + v: value, + e: item.e, + } + return item.v, true, nil + } + return nil, false, nil +} + +// UpdateExpire updates the expiration of and returns the old expiration duration value. +// +// It returns -1 and does nothing if the does not exist in the cache. +// It deletes the if < 0. +func (d *adapterMemoryData) UpdateExpire(key interface{}, expireTime int64) (oldDuration time.Duration, err error) { + d.mu.Lock() + defer d.mu.Unlock() + if item, ok := d.data[key]; ok { + d.data[key] = adapterMemoryItem{ + v: item.v, + e: expireTime, + } + return time.Duration(item.e-gtime.TimestampMilli()) * time.Millisecond, nil + } + return -1, nil +} + +// Remove deletes the one or more keys from cache, and returns its value. +// If multiple keys are given, it returns the value of the deleted last item. +func (d *adapterMemoryData) Remove(keys ...interface{}) (removedKeys []interface{}, value interface{}, err error) { + d.mu.Lock() + defer d.mu.Unlock() + removedKeys = make([]interface{}, 0) + for _, key := range keys { + item, ok := d.data[key] + if ok { + value = item.v + delete(d.data, key) + removedKeys = append(removedKeys, key) + } + } + return removedKeys, value, nil +} + +// Data returns a copy of all key-value pairs in the cache as map type. +func (d *adapterMemoryData) Data() (map[interface{}]interface{}, error) { + d.mu.RLock() + m := make(map[interface{}]interface{}, len(d.data)) + for k, v := range d.data { + if !v.IsExpired() { + m[k] = v.v + } + } + d.mu.RUnlock() + return m, nil +} + +// Keys returns all keys in the cache as slice. +func (d *adapterMemoryData) Keys() ([]interface{}, error) { + d.mu.RLock() + var ( + index = 0 + keys = make([]interface{}, len(d.data)) + ) + for k, v := range d.data { + if !v.IsExpired() { + keys[index] = k + index++ + } + } + d.mu.RUnlock() + return keys, nil +} + +// Values returns all values in the cache as slice. +func (d *adapterMemoryData) Values() ([]interface{}, error) { + d.mu.RLock() + var ( + index = 0 + values = make([]interface{}, len(d.data)) + ) + for _, v := range d.data { + if !v.IsExpired() { + values[index] = v.v + index++ + } + } + d.mu.RUnlock() + return values, nil +} + +// Size returns the size of the cache. +func (d *adapterMemoryData) Size() (size int, err error) { + d.mu.RLock() + size = len(d.data) + d.mu.RUnlock() + return size, nil +} + +// Clear clears all data of the cache. +// Note that this function is sensitive and should be carefully used. +func (d *adapterMemoryData) Clear() error { + d.mu.Lock() + defer d.mu.Unlock() + d.data = make(map[interface{}]adapterMemoryItem) + return nil +} + +func (d *adapterMemoryData) Get(key interface{}) (item adapterMemoryItem, ok bool) { + d.mu.RLock() + item, ok = d.data[key] + d.mu.RUnlock() + return +} + +func (d *adapterMemoryData) Set(key interface{}, value adapterMemoryItem) { + d.mu.Lock() + d.data[key] = value + d.mu.Unlock() +} + +// Sets batch sets cache with key-value pairs by , which is expired after . +// +// It does not expire if == 0. +// It deletes the keys of if < 0 or given is nil. +func (d *adapterMemoryData) Sets(data map[interface{}]interface{}, expireTime int64) error { + d.mu.Lock() + for k, v := range data { + d.data[k] = adapterMemoryItem{ + v: v, + e: expireTime, + } + } + d.mu.Unlock() + return nil +} + +func (d *adapterMemoryData) SetWithLock(key interface{}, value interface{}, expireTimestamp int64) (interface{}, error) { + d.mu.Lock() + defer d.mu.Unlock() + if v, ok := d.data[key]; ok && !v.IsExpired() { + return v.v, nil + } + if f, ok := value.(func() (interface{}, error)); ok { + v, err := f() + if err != nil { + return nil, err + } + if v == nil { + return nil, nil + } else { + value = v + } + } + d.data[key] = adapterMemoryItem{v: value, e: expireTimestamp} + return value, nil +} + +func (d *adapterMemoryData) DeleteWithDoubleCheck(key interface{}, force ...bool) { + d.mu.Lock() + // Doubly check before really deleting it from cache. + if item, ok := d.data[key]; (ok && item.IsExpired()) || (len(force) > 0 && force[0]) { + delete(d.data, key) + } + d.mu.Unlock() +} diff --git a/os/gcache/gcache_adapter_memory_expire_sets.go b/os/gcache/gcache_adapter_memory_expire_sets.go new file mode 100644 index 000000000..f041f65ac --- /dev/null +++ b/os/gcache/gcache_adapter_memory_expire_sets.go @@ -0,0 +1,51 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcache + +import ( + "github.com/gogf/gf/container/gset" + "sync" +) + +type adapterMemoryExpireSets struct { + mu sync.RWMutex // expireSetMu ensures the concurrent safety of expireSets map. + expireSets map[int64]*gset.Set // expireSets is the expiring timestamp to its key set mapping, which is used for quick indexing and deleting. +} + +func newAdapterMemoryExpireSets() *adapterMemoryExpireSets { + return &adapterMemoryExpireSets{ + expireSets: make(map[int64]*gset.Set), + } +} + +func (d *adapterMemoryExpireSets) Get(key int64) (result *gset.Set) { + d.mu.RLock() + result = d.expireSets[key] + d.mu.RUnlock() + return +} + +func (d *adapterMemoryExpireSets) GetOrNew(key int64) (result *gset.Set) { + if result = d.Get(key); result != nil { + return + } + d.mu.Lock() + if es, ok := d.expireSets[key]; ok { + result = es + } else { + result = gset.New(true) + d.expireSets[key] = result + } + d.mu.Unlock() + return +} + +func (d *adapterMemoryExpireSets) Delete(key int64) { + d.mu.Lock() + delete(d.expireSets, key) + d.mu.Unlock() +} diff --git a/os/gcache/gcache_adapter_memory_expire_times.go b/os/gcache/gcache_adapter_memory_expire_times.go new file mode 100644 index 000000000..af3d4b419 --- /dev/null +++ b/os/gcache/gcache_adapter_memory_expire_times.go @@ -0,0 +1,41 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcache + +import ( + "sync" +) + +type adapterMemoryExpireTimes struct { + mu sync.RWMutex // expireTimeMu ensures the concurrent safety of expireTimes map. + expireTimes map[interface{}]int64 // expireTimes is the expiring key to its timestamp mapping, which is used for quick indexing and deleting. +} + +func newAdapterMemoryExpireTimes() *adapterMemoryExpireTimes { + return &adapterMemoryExpireTimes{ + expireTimes: make(map[interface{}]int64), + } +} + +func (d *adapterMemoryExpireTimes) Get(key interface{}) (value int64) { + d.mu.RLock() + value = d.expireTimes[key] + d.mu.RUnlock() + return +} + +func (d *adapterMemoryExpireTimes) Set(key interface{}, value int64) { + d.mu.Lock() + d.expireTimes[key] = value + d.mu.Unlock() +} + +func (d *adapterMemoryExpireTimes) Delete(key interface{}) { + d.mu.Lock() + delete(d.expireTimes, key) + d.mu.Unlock() +} From c01b520ba64f2e3749b57fd0646a9c154483c760 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 26 Jan 2021 14:11:36 +0800 Subject: [PATCH 115/492] improve package gcache --- os/gcache/gcache.go | 7 + os/gcache/gcache_adapter.go | 37 ++--- os/gcache/gcache_adapter_memory.go | 47 +++--- os/gcache/gcache_cache.go | 32 +++- os/gcache/gcache_cache_adapter.go | 150 ++++++++++++++++++ ..._1_test.go => gcache_z_unit_basic_test.go} | 12 ++ 6 files changed, 241 insertions(+), 44 deletions(-) create mode 100644 os/gcache/gcache_cache_adapter.go rename os/gcache/{gcache_z_unit_1_test.go => gcache_z_unit_basic_test.go} (97%) diff --git a/os/gcache/gcache.go b/os/gcache/gcache.go index fb2fd09c9..6bfb36d48 100644 --- a/os/gcache/gcache.go +++ b/os/gcache/gcache.go @@ -9,6 +9,7 @@ package gcache import ( + "context" "github.com/gogf/gf/container/gvar" "time" ) @@ -16,6 +17,12 @@ import ( // Default cache object. var defaultCache = New() +// Ctx is a chaining function, which shallowly clones current object and sets the context +// for next operation. +func Ctx(ctx context.Context) *Cache { + return defaultCache.Ctx(ctx) +} + // Set sets cache with - pair, which is expired after . // It does not expire if == 0. func Set(key interface{}, value interface{}, duration time.Duration) { diff --git a/os/gcache/gcache_adapter.go b/os/gcache/gcache_adapter.go index 03b9f63bc..f12c32570 100644 --- a/os/gcache/gcache_adapter.go +++ b/os/gcache/gcache_adapter.go @@ -7,6 +7,7 @@ package gcache import ( + "context" "time" ) @@ -16,13 +17,13 @@ type Adapter interface { // // It does not expire if == 0. // It deletes the if < 0. - Set(key interface{}, value interface{}, duration time.Duration) error + Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error // Sets batch sets cache with key-value pairs by , which is expired after . // // It does not expire if == 0. // It deletes the keys of if < 0 or given is nil. - Sets(data map[interface{}]interface{}, duration time.Duration) error + Sets(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error // SetIfNotExist sets cache with - pair which is expired after // if does not exist in the cache. It returns true the dose not exist in the @@ -33,11 +34,11 @@ type Adapter interface { // // It does not expire if == 0. // It deletes the if < 0 or given is nil. - SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) + SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (bool, error) // Get retrieves and returns the associated value of given . // It returns nil if it does not exist, its value is nil or it's expired. - Get(key interface{}) (interface{}, error) + Get(ctx context.Context, key interface{}) (interface{}, error) // GetOrSet retrieves and returns the value of , or sets - pair and // returns if does not exist in the cache. The key-value pair expires @@ -46,7 +47,7 @@ type Adapter interface { // It does not expire if == 0. // It deletes the if < 0 or given is nil, but it does nothing // if is a function and the function result is nil. - GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error) + GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (interface{}, error) // GetOrSetFunc retrieves and returns the value of , or sets with result of // function and returns its result if does not exist in the cache. The key-value @@ -55,7 +56,7 @@ type Adapter interface { // It does not expire if == 0. // It deletes the if < 0 or given is nil, but it does nothing // if is a function and the function result is nil. - GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) + GetOrSetFunc(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) // GetOrSetFuncLock retrieves and returns the value of , or sets with result of // function and returns its result if does not exist in the cache. The key-value @@ -66,52 +67,52 @@ type Adapter interface { // // Note that the function should be executed within writing mutex lock for concurrent // safety purpose. - GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) + GetOrSetFuncLock(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) // Contains returns true if exists in the cache, or else returns false. - Contains(key interface{}) (bool, error) + Contains(ctx context.Context, key interface{}) (bool, error) // GetExpire retrieves and returns the expiration of in the cache. // // It returns 0 if the does not expire. // It returns -1 if the does not exist in the cache. - GetExpire(key interface{}) (time.Duration, error) + GetExpire(ctx context.Context, key interface{}) (time.Duration, error) // Remove deletes one or more keys from cache, and returns its value. // If multiple keys are given, it returns the value of the last deleted item. - Remove(keys ...interface{}) (value interface{}, err error) + Remove(ctx context.Context, keys ...interface{}) (value interface{}, err error) // Update updates the value of without changing its expiration and returns the old value. // The returned value is false if the does not exist in the cache. // // It deletes the if given is nil. // It does nothing if does not exist in the cache. - Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) + Update(ctx context.Context, key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) // UpdateExpire updates the expiration of and returns the old expiration duration value. // // It returns -1 and does nothing if the does not exist in the cache. // It deletes the if < 0. - UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) + UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error) // Size returns the number of items in the cache. - Size() (size int, err error) + Size(ctx context.Context) (size int, err error) // Data returns a copy of all key-value pairs in the cache as map type. // Note that this function may leads lots of memory usage, you can implement this function // if necessary. - Data() (map[interface{}]interface{}, error) + Data(ctx context.Context) (map[interface{}]interface{}, error) // Keys returns all keys in the cache as slice. - Keys() ([]interface{}, error) + Keys(ctx context.Context) ([]interface{}, error) // Values returns all values in the cache as slice. - Values() ([]interface{}, error) + Values(ctx context.Context) ([]interface{}, error) // Clear clears all data of the cache. // Note that this function is sensitive and should be carefully used. - Clear() error + Clear(ctx context.Context) error // Close closes the cache if necessary. - Close() error + Close(ctx context.Context) error } diff --git a/os/gcache/gcache_adapter_memory.go b/os/gcache/gcache_adapter_memory.go index 5bad1bb3f..cc8dba5b2 100644 --- a/os/gcache/gcache_adapter_memory.go +++ b/os/gcache/gcache_adapter_memory.go @@ -7,6 +7,7 @@ package gcache import ( + "context" "math" "time" @@ -72,7 +73,7 @@ func newAdapterMemory(lruCap ...int) *adapterMemory { // // It does not expire if == 0. // It deletes the if < 0. -func (c *adapterMemory) Set(key interface{}, value interface{}, duration time.Duration) error { +func (c *adapterMemory) Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error { expireTime := c.getInternalExpire(duration) c.data.Set(key, adapterMemoryItem{ v: value, @@ -90,7 +91,7 @@ func (c *adapterMemory) Set(key interface{}, value interface{}, duration time.Du // // It deletes the if given is nil. // It does nothing if does not exist in the cache. -func (c *adapterMemory) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { +func (c *adapterMemory) Update(ctx context.Context, key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { return c.data.Update(key, value) } @@ -98,7 +99,7 @@ func (c *adapterMemory) Update(key interface{}, value interface{}) (oldValue int // // It returns -1 and does nothing if the does not exist in the cache. // It deletes the if < 0. -func (c *adapterMemory) UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { +func (c *adapterMemory) UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { newExpireTime := c.getInternalExpire(duration) oldDuration, err = c.data.UpdateExpire(key, newExpireTime) if err != nil { @@ -117,7 +118,7 @@ func (c *adapterMemory) UpdateExpire(key interface{}, duration time.Duration) (o // // It returns 0 if the does not expire. // It returns -1 if the does not exist in the cache. -func (c *adapterMemory) GetExpire(key interface{}) (time.Duration, error) { +func (c *adapterMemory) GetExpire(ctx context.Context, key interface{}) (time.Duration, error) { if item, ok := c.data.Get(key); ok { return time.Duration(item.e-gtime.TimestampMilli()) * time.Millisecond, nil } @@ -132,8 +133,8 @@ func (c *adapterMemory) GetExpire(key interface{}) (time.Duration, error) { // // It does not expire if == 0. // It deletes the if < 0 or given is nil. -func (c *adapterMemory) SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) { - isContained, err := c.Contains(key) +func (c *adapterMemory) SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (bool, error) { + isContained, err := c.Contains(ctx, key) if err != nil { return false, err } @@ -151,7 +152,7 @@ func (c *adapterMemory) SetIfNotExist(key interface{}, value interface{}, durati // // It does not expire if == 0. // It deletes the keys of if < 0 or given is nil. -func (c *adapterMemory) Sets(data map[interface{}]interface{}, duration time.Duration) error { +func (c *adapterMemory) Sets(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error { var ( expireTime = c.getInternalExpire(duration) err = c.data.Sets(data, expireTime) @@ -170,7 +171,7 @@ func (c *adapterMemory) Sets(data map[interface{}]interface{}, duration time.Dur // Get retrieves and returns the associated value of given . // It returns nil if it does not exist or its value is nil. -func (c *adapterMemory) Get(key interface{}) (interface{}, error) { +func (c *adapterMemory) Get(ctx context.Context, key interface{}) (interface{}, error) { item, ok := c.data.Get(key) if ok && !item.IsExpired() { // Adding to LRU history if LRU feature is enabled. @@ -189,8 +190,8 @@ func (c *adapterMemory) Get(key interface{}) (interface{}, error) { // It does not expire if == 0. // It deletes the if < 0 or given is nil, but it does nothing // if is a function and the function result is nil. -func (c *adapterMemory) GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error) { - v, err := c.Get(key) +func (c *adapterMemory) GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (interface{}, error) { + v, err := c.Get(ctx, key) if err != nil { return nil, err } @@ -208,8 +209,8 @@ func (c *adapterMemory) GetOrSet(key interface{}, value interface{}, duration ti // It does not expire if == 0. // It deletes the if < 0 or given is nil, but it does nothing // if is a function and the function result is nil. -func (c *adapterMemory) GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { - v, err := c.Get(key) +func (c *adapterMemory) GetOrSetFunc(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { + v, err := c.Get(ctx, key) if err != nil { return nil, err } @@ -236,8 +237,8 @@ func (c *adapterMemory) GetOrSetFunc(key interface{}, f func() (interface{}, err // // Note that the function should be executed within writing mutex lock for concurrent // safety purpose. -func (c *adapterMemory) GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { - v, err := c.Get(key) +func (c *adapterMemory) GetOrSetFuncLock(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { + v, err := c.Get(ctx, key) if err != nil { return nil, err } @@ -249,8 +250,8 @@ func (c *adapterMemory) GetOrSetFuncLock(key interface{}, f func() (interface{}, } // Contains returns true if exists in the cache, or else returns false. -func (c *adapterMemory) Contains(key interface{}) (bool, error) { - v, err := c.Get(key) +func (c *adapterMemory) Contains(ctx context.Context, key interface{}) (bool, error) { + v, err := c.Get(ctx, key) if err != nil { return false, err } @@ -259,7 +260,7 @@ func (c *adapterMemory) Contains(key interface{}) (bool, error) { // Remove deletes the one or more keys from cache, and returns its value. // If multiple keys are given, it returns the value of the deleted last item. -func (c *adapterMemory) Remove(keys ...interface{}) (value interface{}, err error) { +func (c *adapterMemory) Remove(ctx context.Context, keys ...interface{}) (value interface{}, err error) { var removedKeys []interface{} removedKeys, value, err = c.data.Remove(keys...) if err != nil { @@ -275,33 +276,33 @@ func (c *adapterMemory) Remove(keys ...interface{}) (value interface{}, err erro } // Data returns a copy of all key-value pairs in the cache as map type. -func (c *adapterMemory) Data() (map[interface{}]interface{}, error) { +func (c *adapterMemory) Data(ctx context.Context) (map[interface{}]interface{}, error) { return c.data.Data() } // Keys returns all keys in the cache as slice. -func (c *adapterMemory) Keys() ([]interface{}, error) { +func (c *adapterMemory) Keys(ctx context.Context) ([]interface{}, error) { return c.data.Keys() } // Values returns all values in the cache as slice. -func (c *adapterMemory) Values() ([]interface{}, error) { +func (c *adapterMemory) Values(ctx context.Context) ([]interface{}, error) { return c.data.Values() } // Size returns the size of the cache. -func (c *adapterMemory) Size() (size int, err error) { +func (c *adapterMemory) Size(ctx context.Context) (size int, err error) { return c.data.Size() } // Clear clears all data of the cache. // Note that this function is sensitive and should be carefully used. -func (c *adapterMemory) Clear() error { +func (c *adapterMemory) Clear(ctx context.Context) error { return c.data.Clear() } // Close closes the cache. -func (c *adapterMemory) Close() error { +func (c *adapterMemory) Close(ctx context.Context) error { if c.cap > 0 { c.lru.Close() } diff --git a/os/gcache/gcache_cache.go b/os/gcache/gcache_cache.go index c664c141a..54f5ea626 100644 --- a/os/gcache/gcache_cache.go +++ b/os/gcache/gcache_cache.go @@ -7,6 +7,7 @@ package gcache import ( + "context" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/os/gtimer" "github.com/gogf/gf/util/gconv" @@ -15,7 +16,8 @@ import ( // Cache struct. type Cache struct { - Adapter // Adapter for cache features. + adapter Adapter // Adapter for cache features. + ctx context.Context // Context for operations. } // New creates and returns a new cache object using default memory adapter. @@ -23,7 +25,7 @@ type Cache struct { func New(lruCap ...int) *Cache { memAdapter := newAdapterMemory(lruCap...) c := &Cache{ - Adapter: memAdapter, + adapter: memAdapter, } // Here may be a "timer leak" if adapter is manually changed from memory adapter. // Do not worry about this, as adapter is less changed and it dose nothing if it's not used. @@ -31,11 +33,27 @@ func New(lruCap ...int) *Cache { return c } +// Clone returns a shallow copy of current object. +func (c *Cache) Clone() *Cache { + return &Cache{ + adapter: c.adapter, + ctx: c.ctx, + } +} + +// Ctx is a chaining function, which shallowly clones current object and sets the context +// for next operation. +func (c *Cache) Ctx(ctx context.Context) *Cache { + newCache := c.Clone() + newCache.ctx = ctx + return newCache +} + // SetAdapter changes the adapter for this cache. // Be very note that, this setting function is not concurrent-safe, which means you should not call // this setting function concurrently in multiple goroutines. func (c *Cache) SetAdapter(adapter Adapter) { - c.Adapter = adapter + c.adapter = adapter } // GetVar retrieves and returns the value of as gvar.Var. @@ -59,3 +77,11 @@ func (c *Cache) KeyStrings() ([]string, error) { } return gconv.Strings(keys), nil } + +// KeyStrings returns all keys in the cache as string slice. +func (c *Cache) getCtx() context.Context { + if c.ctx == nil { + return context.Background() + } + return c.ctx +} diff --git a/os/gcache/gcache_cache_adapter.go b/os/gcache/gcache_cache_adapter.go new file mode 100644 index 000000000..0f4f50852 --- /dev/null +++ b/os/gcache/gcache_cache_adapter.go @@ -0,0 +1,150 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcache + +import ( + "time" +) + +// Set sets cache with - pair, which is expired after . +// +// It does not expire if == 0. +// It deletes the if < 0. +func (c *Cache) Set(key interface{}, value interface{}, duration time.Duration) error { + return c.adapter.Set(c.getCtx(), key, value, duration) +} + +// Sets batch sets cache with key-value pairs by , which is expired after . +// +// It does not expire if == 0. +// It deletes the keys of if < 0 or given is nil. +func (c *Cache) Sets(data map[interface{}]interface{}, duration time.Duration) error { + return c.adapter.Sets(c.getCtx(), data, duration) +} + +// SetIfNotExist sets cache with - pair which is expired after +// if does not exist in the cache. It returns true the dose not exist in the +// cache and it sets successfully to the cache, or else it returns false. +// +// The parameter can be type of , but it dose nothing if its +// result is nil. +// +// It does not expire if == 0. +// It deletes the if < 0 or given is nil. +func (c *Cache) SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) { + return c.adapter.SetIfNotExist(c.getCtx(), key, value, duration) +} + +// Get retrieves and returns the associated value of given . +// It returns nil if it does not exist, its value is nil or it's expired. +func (c *Cache) Get(key interface{}) (interface{}, error) { + return c.adapter.Get(c.getCtx(), key) +} + +// GetOrSet retrieves and returns the value of , or sets - pair and +// returns if does not exist in the cache. The key-value pair expires +// after . +// +// It does not expire if == 0. +// It deletes the if < 0 or given is nil, but it does nothing +// if is a function and the function result is nil. +func (c *Cache) GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error) { + return c.adapter.GetOrSet(c.getCtx(), key, value, duration) +} + +// GetOrSetFunc retrieves and returns the value of , or sets with result of +// function and returns its result if does not exist in the cache. The key-value +// pair expires after . +// +// It does not expire if == 0. +// It deletes the if < 0 or given is nil, but it does nothing +// if is a function and the function result is nil. +func (c *Cache) GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { + return c.adapter.GetOrSetFunc(c.getCtx(), key, f, duration) +} + +// GetOrSetFuncLock retrieves and returns the value of , or sets with result of +// function and returns its result if does not exist in the cache. The key-value +// pair expires after . +// +// It does not expire if == 0. +// It does nothing if function returns nil. +// +// Note that the function should be executed within writing mutex lock for concurrent +// safety purpose. +func (c *Cache) GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { + return c.adapter.GetOrSetFuncLock(c.getCtx(), key, f, duration) +} + +// Contains returns true if exists in the cache, or else returns false. +func (c *Cache) Contains(key interface{}) (bool, error) { + return c.adapter.Contains(c.getCtx(), key) +} + +// GetExpire retrieves and returns the expiration of in the cache. +// +// It returns 0 if the does not expire. +// It returns -1 if the does not exist in the cache. +func (c *Cache) GetExpire(key interface{}) (time.Duration, error) { + return c.adapter.GetExpire(c.getCtx(), key) +} + +// Remove deletes one or more keys from cache, and returns its value. +// If multiple keys are given, it returns the value of the last deleted item. +func (c *Cache) Remove(keys ...interface{}) (value interface{}, err error) { + return c.adapter.Remove(c.getCtx(), keys...) +} + +// Update updates the value of without changing its expiration and returns the old value. +// The returned value is false if the does not exist in the cache. +// +// It deletes the if given is nil. +// It does nothing if does not exist in the cache. +func (c *Cache) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { + return c.adapter.Update(c.getCtx(), key, value) +} + +// UpdateExpire updates the expiration of and returns the old expiration duration value. +// +// It returns -1 and does nothing if the does not exist in the cache. +// It deletes the if < 0. +func (c *Cache) UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { + return c.adapter.UpdateExpire(c.getCtx(), key, duration) +} + +// Size returns the number of items in the cache. +func (c *Cache) Size() (size int, err error) { + return c.adapter.Size(c.getCtx()) +} + +// Data returns a copy of all key-value pairs in the cache as map type. +// Note that this function may leads lots of memory usage, you can implement this function +// if necessary. +func (c *Cache) Data() (map[interface{}]interface{}, error) { + return c.adapter.Data(c.getCtx()) +} + +// Keys returns all keys in the cache as slice. +func (c *Cache) Keys() ([]interface{}, error) { + return c.adapter.Keys(c.getCtx()) +} + +// Values returns all values in the cache as slice. +func (c *Cache) Values() ([]interface{}, error) { + return c.adapter.Values(c.getCtx()) +} + +// Clear clears all data of the cache. +// Note that this function is sensitive and should be carefully used. +func (c *Cache) Clear() error { + return c.adapter.Clear(c.getCtx()) +} + +// Close closes the cache if necessary. +func (c *Cache) Close() error { + return c.adapter.Close(c.getCtx()) +} diff --git a/os/gcache/gcache_z_unit_1_test.go b/os/gcache/gcache_z_unit_basic_test.go similarity index 97% rename from os/gcache/gcache_z_unit_1_test.go rename to os/gcache/gcache_z_unit_basic_test.go index bf5a0ad81..9ea748cd3 100644 --- a/os/gcache/gcache_z_unit_1_test.go +++ b/os/gcache/gcache_z_unit_basic_test.go @@ -9,6 +9,7 @@ package gcache_test import ( + "context" "github.com/gogf/gf/util/guid" "math" "testing" @@ -413,3 +414,14 @@ func TestCache_Basic(t *testing.T) { } }) } + +func TestCache_Ctx(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + cache := gcache.New() + cache.Ctx(context.Background()).Sets(g.MapAnyAny{1: 11, 2: 22}, 0) + b, _ := cache.Contains(1) + t.Assert(b, true) + v, _ := cache.Get(1) + t.Assert(v, 11) + }) +} From 52c17dfce0889f0c43382497158cfed27a084505 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 26 Jan 2021 14:33:35 +0800 Subject: [PATCH 116/492] add example for tracing feature --- database/gdb/gdb_model_cache.go | 2 +- database/gdb/gdb_model_select.go | 11 ++++++++--- go.mod | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/database/gdb/gdb_model_cache.go b/database/gdb/gdb_model_cache.go index f8f4b5578..376e02c55 100644 --- a/database/gdb/gdb_model_cache.go +++ b/database/gdb/gdb_model_cache.go @@ -38,6 +38,6 @@ func (m *Model) Cache(duration time.Duration, name ...string) *Model { // cache feature is enabled. func (m *Model) checkAndRemoveCache() { if m.cacheEnabled && m.cacheDuration < 0 && len(m.cacheName) > 0 { - m.db.GetCache().Remove(m.cacheName) + m.db.GetCache().Ctx(m.db.GetCtx()).Remove(m.cacheName) } } diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 5670033d0..e14438826 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -10,6 +10,7 @@ import ( "fmt" "github.com/gogf/gf/container/gset" "github.com/gogf/gf/container/gvar" + "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" @@ -435,7 +436,7 @@ func (m *Model) FindScan(pointer interface{}, where ...interface{}) error { // doGetAllBySql does the select statement on the database. func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, err error) { cacheKey := "" - cacheObj := m.db.GetCache() + cacheObj := m.db.GetCache().Ctx(m.db.GetCtx()) // Retrieve from cache. if m.cacheEnabled && m.tx == nil { cacheKey = m.cacheName @@ -461,9 +462,13 @@ func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, e // Cache the result. if cacheKey != "" && err == nil { if m.cacheDuration < 0 { - cacheObj.Remove(cacheKey) + if _, err := cacheObj.Remove(cacheKey); err != nil { + intlog.Error(err) + } } else { - cacheObj.Set(cacheKey, result, m.cacheDuration) + if err := cacheObj.Set(cacheKey, result, m.cacheDuration); err != nil { + intlog.Error(err) + } } } return result, err diff --git a/go.mod b/go.mod index efe36c7b9..4222f512a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 github.com/fsnotify/fsnotify v1.4.9 github.com/go-sql-driver/mysql v1.5.0 - github.com/gogf/gcache-adapter v0.0.3 + github.com/gogf/gcache-adapter v0.0.4-0.20210126062229-c84b9cefa528 github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/websocket v1.4.1 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf From c72c7dbe9a75215e8166a5e352a4d7a3f0d41c31 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 26 Jan 2021 16:06:20 +0800 Subject: [PATCH 117/492] improve tracing feature --- .example/net/gtrace/1.http/client/main.go | 2 +- .example/net/gtrace/1.http/server/main.go | 2 +- .example/net/gtrace/2.http+db/main.go | 42 +++++------ .example/net/gtrace/3.http+redis/main.go | 2 +- .../gtrace/4.http+db+redis+log/client/main.go | 71 +++++++++++++++++++ .../{ => server}/config.toml | 0 .../4.http+db+redis+log/{ => server}/main.go | 63 ++++++++++++++-- database/gdb/gdb_core.go | 51 ------------- database/gdb/gdb_core_tracing.go | 65 +++++++++++++++++ database/gredis/gredis_conn.go | 2 +- net/ghttp/internal/client/client_tracing.go | 2 +- .../internal/client/client_tracing_tracer.go | 11 +-- net/gtrace/gtrace.go | 10 +++ 13 files changed, 235 insertions(+), 88 deletions(-) create mode 100644 .example/net/gtrace/4.http+db+redis+log/client/main.go rename .example/net/gtrace/4.http+db+redis+log/{ => server}/config.toml (100%) rename .example/net/gtrace/4.http+db+redis+log/{ => server}/main.go (50%) create mode 100644 database/gdb/gdb_core_tracing.go diff --git a/.example/net/gtrace/1.http/client/main.go b/.example/net/gtrace/1.http/client/main.go index cae43e329..d10eed73d 100644 --- a/.example/net/gtrace/1.http/client/main.go +++ b/.example/net/gtrace/1.http/client/main.go @@ -13,7 +13,7 @@ import ( const ( JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "TracingHttpClient" + ServiceName = "tracing-http-client" ) // initTracer creates a new trace provider instance and registers it as global trace provider. diff --git a/.example/net/gtrace/1.http/server/main.go b/.example/net/gtrace/1.http/server/main.go index 1be9b6fc8..7c6a2687c 100644 --- a/.example/net/gtrace/1.http/server/main.go +++ b/.example/net/gtrace/1.http/server/main.go @@ -11,7 +11,7 @@ import ( const ( JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "TracingHttpServer" + ServiceName = "tracing-http-server" ) // initTracer creates a new trace provider instance and registers it as global trace provider. diff --git a/.example/net/gtrace/2.http+db/main.go b/.example/net/gtrace/2.http+db/main.go index 744971f1f..c85061135 100644 --- a/.example/net/gtrace/2.http+db/main.go +++ b/.example/net/gtrace/2.http+db/main.go @@ -10,6 +10,27 @@ import ( type tracingApi struct{} +const ( + JaegerEndpoint = "http://localhost:14268/api/traces" + ServiceName = "tracing-demo-db" +) + +// initTracer creates a new trace provider instance and registers it as global trace provider. +func initTracer() func() { + // Create and install Jaeger export pipeline. + flush, err := jaeger.InstallNewPipeline( + jaeger.WithCollectorEndpoint(JaegerEndpoint), + jaeger.WithProcess(jaeger.Process{ + ServiceName: ServiceName, + }), + jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), + ) + if err != nil { + g.Log().Fatal(err) + } + return flush +} + func (api *tracingApi) Insert(r *ghttp.Request) { result, err := g.Table("user").Ctx(r.Context()).Insert(g.Map{ "name": r.GetString("name"), @@ -29,27 +50,6 @@ func (api *tracingApi) Query(r *ghttp.Request) { r.Response.Write("user:", one) } -const ( - JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "TracingHttpServerWithDB" -) - -// initTracer creates a new trace provider instance and registers it as global trace provider. -func initTracer() func() { - // Create and install Jaeger export pipeline. - flush, err := jaeger.InstallNewPipeline( - jaeger.WithCollectorEndpoint(JaegerEndpoint), - jaeger.WithProcess(jaeger.Process{ - ServiceName: ServiceName, - }), - jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), - ) - if err != nil { - g.Log().Fatal(err) - } - return flush -} - func main() { flush := initTracer() defer flush() diff --git a/.example/net/gtrace/3.http+redis/main.go b/.example/net/gtrace/3.http+redis/main.go index 9de300397..35788087f 100644 --- a/.example/net/gtrace/3.http+redis/main.go +++ b/.example/net/gtrace/3.http+redis/main.go @@ -12,7 +12,7 @@ type tracingApi struct{} const ( JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "TracingHttpServerWithRedis" + ServiceName = "tracing-demo-redis" ) func (api *tracingApi) Set(r *ghttp.Request) { diff --git a/.example/net/gtrace/4.http+db+redis+log/client/main.go b/.example/net/gtrace/4.http+db+redis+log/client/main.go new file mode 100644 index 000000000..019509cda --- /dev/null +++ b/.example/net/gtrace/4.http+db+redis+log/client/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "context" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/net/gtrace" + "go.opentelemetry.io/otel/exporters/trace/jaeger" + sdkTrace "go.opentelemetry.io/otel/sdk/trace" +) + +const ( + JaegerEndpoint = "http://localhost:14268/api/traces" + ServiceName = "tracing-http-client" +) + +// initTracer creates a new trace provider instance and registers it as global trace provider. +func initTracer() func() { + // Create and install Jaeger export pipeline. + flush, err := jaeger.InstallNewPipeline( + jaeger.WithCollectorEndpoint(JaegerEndpoint), + jaeger.WithProcess(jaeger.Process{ + ServiceName: ServiceName, + }), + jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), + ) + if err != nil { + g.Log().Fatal(err) + } + return flush +} + +func main() { + flush := initTracer() + defer flush() + + ctx, span := gtrace.Tracer().Start(context.Background(), "root") + defer span.End() + + client := g.Client().Use(ghttp.MiddlewareClientTracing) + //client := g.Client() + // Add user info. + idStr := client.Ctx(ctx).PostContent( + "http://127.0.0.1:8199/user/insert", + g.Map{ + "name": "john", + }, + ) + if idStr == "" { + g.Log().Ctx(ctx).Fatal("retrieve empty id string") + } + g.Log().Ctx(ctx).Print("insert:", idStr) + + // Query user info. + userJson := client.Ctx(ctx).GetContent( + "http://127.0.0.1:8199/user/query", + g.Map{ + "id": idStr, + }, + ) + g.Log().Ctx(ctx).Print("query:", idStr, userJson) + + // Delete user info. + deleteResult := client.Ctx(ctx).PostContent( + "http://127.0.0.1:8199/user/delete", + g.Map{ + "id": idStr, + }, + ) + g.Log().Ctx(ctx).Print("delete:", idStr, deleteResult) +} diff --git a/.example/net/gtrace/4.http+db+redis+log/config.toml b/.example/net/gtrace/4.http+db+redis+log/server/config.toml similarity index 100% rename from .example/net/gtrace/4.http+db+redis+log/config.toml rename to .example/net/gtrace/4.http+db+redis+log/server/config.toml diff --git a/.example/net/gtrace/4.http+db+redis+log/main.go b/.example/net/gtrace/4.http+db+redis+log/server/main.go similarity index 50% rename from .example/net/gtrace/4.http+db+redis+log/main.go rename to .example/net/gtrace/4.http+db+redis+log/server/main.go index 26fa71ae5..4b244fb4c 100644 --- a/.example/net/gtrace/4.http+db+redis+log/main.go +++ b/.example/net/gtrace/4.http+db+redis+log/server/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "github.com/gogf/gcache-adapter/adapter" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/frame/g" @@ -14,7 +15,7 @@ type tracingApi struct{} const ( JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "TracingHttpServerWithDBRedisLog" + ServiceName = "tracing-http-server" ) // initTracer creates a new trace provider instance and registers it as global trace provider. @@ -33,26 +34,76 @@ func initTracer() func() { return flush } +type userApiInsert struct { + Name string `v:"required#Please input user name."` +} + +// Insert is a route handler for inserting user info into dtabase. func (api *tracingApi) Insert(r *ghttp.Request) { + var ( + dataReq *userApiInsert + ) + if err := r.Parse(&dataReq); err != nil { + r.Response.WriteExit(gerror.Current(err)) + } result, err := g.Table("user").Ctx(r.Context()).Insert(g.Map{ - "name": r.GetString("name"), + "name": dataReq.Name, }) if err != nil { r.Response.WriteExit(gerror.Current(err)) } id, _ := result.LastInsertId() - r.Response.Write("id:", id) + r.Response.Write(id) } +type userApiQuery struct { + Id int `v:"min:1#User id is required for querying."` +} + +// Query is a route handler for querying user info. It firstly retrieves the info from redis, +// if there's nothing in the redis, it then does db select. func (api *tracingApi) Query(r *ghttp.Request) { + var ( + dataReq *userApiQuery + ) + if err := r.Parse(&dataReq); err != nil { + r.Response.WriteExit(gerror.Current(err)) + } one, err := g.Table("user"). Ctx(r.Context()). - Cache(5 * time.Second). - FindOne(r.GetInt("id")) + Cache(5*time.Second, api.userCacheKey(dataReq.Id)). + FindOne(dataReq.Id) if err != nil { r.Response.WriteExit(gerror.Current(err)) } - r.Response.Write("user:", one) + r.Response.WriteJson(one) +} + +type userApiDelete struct { + Id int `v:"min:1#User id is required for deleting."` +} + +// Delete is a route handler for deleting specified user info. +func (api *tracingApi) Delete(r *ghttp.Request) { + var ( + dataReq *userApiDelete + ) + if err := r.Parse(&dataReq); err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + _, err := g.Table("user"). + Ctx(r.Context()). + Cache(-1, api.userCacheKey(dataReq.Id)). + WherePri(dataReq.Id). + Delete() + if err != nil { + r.Response.WriteExit(gerror.Current(err)) + } + r.Response.Write("ok") +} + +func (api *tracingApi) userCacheKey(id int) string { + return fmt.Sprintf(`userInfo:%d`, id) } func main() { diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 797750602..08c21c245 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -11,13 +11,8 @@ import ( "context" "database/sql" "fmt" - "github.com/gogf/gf" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/text/gstr" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" - "go.opentelemetry.io/otel/trace" "reflect" "strings" @@ -146,52 +141,6 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro return nil, err } -func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { - if ctx == nil { - return - } - spanCtx := trace.SpanContextFromContext(ctx) - if traceId := spanCtx.TraceID; !traceId.IsValid() { - return - } - - tr := otel.GetTracerProvider().Tracer( - "github.com/gogf/gf/database/gdb", - trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), - ) - ctx, span := tr.Start(ctx, sql.Type) - defer span.End() - if sql.Error != nil { - span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, sql.Error)) - } - labels := make([]label.KeyValue, 0) - labels = append(labels, label.String("db.type", c.DB.GetConfig().Type)) - if c.DB.GetConfig().Host != "" { - labels = append(labels, label.String("db.host", c.DB.GetConfig().Host)) - } - if c.DB.GetConfig().Port != "" { - labels = append(labels, label.String("db.port", c.DB.GetConfig().Port)) - } - if c.DB.GetConfig().Name != "" { - labels = append(labels, label.String("db.name", c.DB.GetConfig().Name)) - } - if c.DB.GetConfig().User != "" { - labels = append(labels, label.String("db.user", c.DB.GetConfig().User)) - } - if filteredLinkInfo := c.DB.FilteredLinkInfo(); filteredLinkInfo != "" { - labels = append(labels, label.String("db.link", c.DB.FilteredLinkInfo())) - } - if group := c.DB.GetGroup(); group != "" { - labels = append(labels, label.String("db.group", group)) - } - span.SetAttributes(labels...) - span.AddEvent("db.execution", trace.WithAttributes( - label.String(`db.execution.sql`, sql.Format), - label.String(`db.execution.cost`, fmt.Sprintf(`%d ms`, sql.End-sql.Start)), - label.String(`db.execution.type`, sql.Type), - )) -} - // Exec commits one query SQL to underlying driver and returns the execution result. // It is most commonly used for data inserting and updating. func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error) { diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go new file mode 100644 index 000000000..b012f8159 --- /dev/null +++ b/database/gdb/gdb_core_tracing.go @@ -0,0 +1,65 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. +// + +package gdb + +import ( + "context" + "fmt" + "github.com/gogf/gf" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/trace" +) + +// addSqlToTracing adds sql information to tracer if it's enabled. +func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { + if ctx == nil { + return + } + spanCtx := trace.SpanContextFromContext(ctx) + if traceId := spanCtx.TraceID; !traceId.IsValid() { + return + } + + tr := otel.GetTracerProvider().Tracer( + "github.com/gogf/gf/database/gdb", + trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), + ) + ctx, span := tr.Start(ctx, sql.Type) + defer span.End() + if sql.Error != nil { + span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, sql.Error)) + } + labels := make([]label.KeyValue, 0) + labels = append(labels, label.String("db.type", c.DB.GetConfig().Type)) + if c.DB.GetConfig().Host != "" { + labels = append(labels, label.String("db.host", c.DB.GetConfig().Host)) + } + if c.DB.GetConfig().Port != "" { + labels = append(labels, label.String("db.port", c.DB.GetConfig().Port)) + } + if c.DB.GetConfig().Name != "" { + labels = append(labels, label.String("db.name", c.DB.GetConfig().Name)) + } + if c.DB.GetConfig().User != "" { + labels = append(labels, label.String("db.user", c.DB.GetConfig().User)) + } + if filteredLinkInfo := c.DB.FilteredLinkInfo(); filteredLinkInfo != "" { + labels = append(labels, label.String("db.link", c.DB.FilteredLinkInfo())) + } + if group := c.DB.GetGroup(); group != "" { + labels = append(labels, label.String("db.group", group)) + } + span.SetAttributes(labels...) + span.AddEvent("db.execution", trace.WithAttributes( + label.String(`db.execution.sql`, sql.Format), + label.String(`db.execution.cost`, fmt.Sprintf(`%d ms`, sql.End-sql.Start)), + label.String(`db.execution.type`, sql.Type), + )) +} diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index 3f5540359..bd852ec7b 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -80,7 +80,7 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} if ctx == nil { ctx = context.Background() } - _, span := tr.Start(ctx, commandName) + _, span := tr.Start(ctx, "Redis."+commandName) defer span.End() if err != nil { span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index 546447fa8..ffe35141d 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -52,7 +52,7 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro if err != nil { span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) } - if response == nil { + if response == nil || response.Response == nil { return } var resBodyContent string diff --git a/net/ghttp/internal/client/client_tracing_tracer.go b/net/ghttp/internal/client/client_tracing_tracer.go index fa2f6366e..bf833c069 100644 --- a/net/ghttp/internal/client/client_tracing_tracer.go +++ b/net/ghttp/internal/client/client_tracing_tracer.go @@ -38,6 +38,11 @@ func newClientTrace(ctx context.Context, span trace.Span, request *http.Request) request: request, headers: make(map[string]interface{}), } + if ct.request.ContentLength <= tracingMaxContentLogSize { + reqBodyContent, _ := ioutil.ReadAll(ct.request.Body) + ct.requestBody = reqBodyContent + ct.request.Body = utils.NewReadCloser(reqBodyContent, false) + } return &httptrace.ClientTrace{ GetConn: ct.getConn, GotConn: ct.gotConn, @@ -131,11 +136,7 @@ func (ct *clientTracer) wroteHeaderField(k string, v []string) { } func (ct *clientTracer) wroteHeaders() { - if ct.request.ContentLength <= tracingMaxContentLogSize { - reqBodyContent, _ := ioutil.ReadAll(ct.request.Body) - ct.requestBody = reqBodyContent - ct.request.Body = utils.NewReadCloser(reqBodyContent, false) - } + } func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index 232951004..ea30b0e9e 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -25,6 +25,16 @@ func Tracer(name ...string) trace.Tracer { return otel.Tracer(tracerName) } +// GetTraceId retrieves and returns TraceId from context. +func GetTraceId(ctx context.Context) string { + return trace.SpanContextFromContext(ctx).TraceID.String() +} + +// GetSpanId retrieves and returns SpanId from context. +func GetSpanId(ctx context.Context) string { + return trace.SpanContextFromContext(ctx).SpanID.String() +} + // SetBaggageValue is a convenient function for adding one key-value pair to baggage. // Note that it uses label.Any to set the key-value pair. func SetBaggageValue(ctx context.Context, key string, value interface{}) context.Context { From e3be3bac92b8376a99efb02a81a3a9ac5f475d23 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 26 Jan 2021 16:50:16 +0800 Subject: [PATCH 118/492] add tracing example for grpc --- .example/net/gtrace/1.http/client/main.go | 17 +- .../gtrace/4.http+db+redis+log/client/main.go | 15 +- .../gtrace/5.grpc+db+redis+log/client/main.go | 80 + .../protobuf/user/user.pb.go | 1336 +++++++++++++++++ .../protocol/user/user.proto | 39 + .../5.grpc+db+redis+log/server/config.toml | 19 + .../gtrace/5.grpc+db+redis+log/server/main.go | 118 ++ go.mod | 4 + 8 files changed, 1616 insertions(+), 12 deletions(-) create mode 100644 .example/net/gtrace/5.grpc+db+redis+log/client/main.go create mode 100644 .example/net/gtrace/5.grpc+db+redis+log/protobuf/user/user.pb.go create mode 100644 .example/net/gtrace/5.grpc+db+redis+log/protocol/user/user.proto create mode 100644 .example/net/gtrace/5.grpc+db+redis+log/server/config.toml create mode 100644 .example/net/gtrace/5.grpc+db+redis+log/server/main.go diff --git a/.example/net/gtrace/1.http/client/main.go b/.example/net/gtrace/1.http/client/main.go index d10eed73d..e22e606d1 100644 --- a/.example/net/gtrace/1.http/client/main.go +++ b/.example/net/gtrace/1.http/client/main.go @@ -32,15 +32,20 @@ func initTracer() func() { return flush } -func main() { - flush := initTracer() - defer flush() - - ctx, span := gtrace.Tracer().Start(context.Background(), "test") +func StartRequests() { + ctx, span := gtrace.Tracer().Start(context.Background(), "StartRequests") defer span.End() ctx = baggage.ContextWithValues(ctx, label.String("name", "john")) client := g.Client().Use(ghttp.MiddlewareClientTracing) + content := client.Ctx(ctx).GetContent("http://127.0.0.1:8199/hello") - g.Log().Print(content) + g.Log().Ctx(ctx).Print(content) +} + +func main() { + flush := initTracer() + defer flush() + + StartRequests() } diff --git a/.example/net/gtrace/4.http+db+redis+log/client/main.go b/.example/net/gtrace/4.http+db+redis+log/client/main.go index 019509cda..353a58bcf 100644 --- a/.example/net/gtrace/4.http+db+redis+log/client/main.go +++ b/.example/net/gtrace/4.http+db+redis+log/client/main.go @@ -30,15 +30,11 @@ func initTracer() func() { return flush } -func main() { - flush := initTracer() - defer flush() - - ctx, span := gtrace.Tracer().Start(context.Background(), "root") +func StartRequests() { + ctx, span := gtrace.Tracer().Start(context.Background(), "StartRequests") defer span.End() client := g.Client().Use(ghttp.MiddlewareClientTracing) - //client := g.Client() // Add user info. idStr := client.Ctx(ctx).PostContent( "http://127.0.0.1:8199/user/insert", @@ -69,3 +65,10 @@ func main() { ) g.Log().Ctx(ctx).Print("delete:", idStr, deleteResult) } + +func main() { + flush := initTracer() + defer flush() + + StartRequests() +} diff --git a/.example/net/gtrace/5.grpc+db+redis+log/client/main.go b/.example/net/gtrace/5.grpc+db+redis+log/client/main.go new file mode 100644 index 000000000..3da3dc5f5 --- /dev/null +++ b/.example/net/gtrace/5.grpc+db+redis+log/client/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "context" + "github.com/gogf/gf/.example/net/gtrace/5.grpc+db+redis+log/protobuf/user" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/gtrace" + "go.opentelemetry.io/otel/exporters/trace/jaeger" + sdkTrace "go.opentelemetry.io/otel/sdk/trace" + "google.golang.org/grpc" +) + +const ( + JaegerEndpoint = "http://localhost:14268/api/traces" + ServiceName = "tracing-grpc-client" +) + +// initTracer creates a new trace provider instance and registers it as global trace provider. +func initTracer() func() { + // Create and install Jaeger export pipeline. + flush, err := jaeger.InstallNewPipeline( + jaeger.WithCollectorEndpoint(JaegerEndpoint), + jaeger.WithProcess(jaeger.Process{ + ServiceName: ServiceName, + }), + jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), + ) + if err != nil { + g.Log().Fatal(err) + } + return flush +} + +func StartRequests() { + ctx, span := gtrace.Tracer().Start(context.Background(), "StartRequests") + defer span.End() + + conn, err := grpc.Dial(":8000", grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + g.Log().Fatalf("did not connect: %v", err) + } + defer conn.Close() + + client := user.NewUserClient(conn) + + // Insert. + insertRes, err := client.Insert(ctx, &user.InsertReq{ + Name: "john", + }) + if err != nil { + g.Log().Ctx(ctx).Fatalf(`%+v`, err) + } + g.Log().Ctx(ctx).Println("insert:", insertRes.Id) + + // Query. + queryRes, err := client.Query(ctx, &user.QueryReq{ + Id: insertRes.Id, + }) + if err != nil { + g.Log().Ctx(ctx).Fatalf(`%+v`, err) + } + g.Log().Ctx(ctx).Println("query:", queryRes) + + // Delete. + _, err = client.Delete(ctx, &user.DeleteReq{ + Id: insertRes.Id, + }) + if err != nil { + g.Log().Ctx(ctx).Fatalf(`%+v`, err) + } + g.Log().Ctx(ctx).Println("delete:", "ok") + +} + +func main() { + flush := initTracer() + defer flush() + + StartRequests() +} diff --git a/.example/net/gtrace/5.grpc+db+redis+log/protobuf/user/user.pb.go b/.example/net/gtrace/5.grpc+db+redis+log/protobuf/user/user.pb.go new file mode 100644 index 000000000..89fcfd05d --- /dev/null +++ b/.example/net/gtrace/5.grpc+db+redis+log/protobuf/user/user.pb.go @@ -0,0 +1,1336 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: protocol/user.proto + +package user + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type InsertReq struct { + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty" v:"required#Please input user name."` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InsertReq) Reset() { *m = InsertReq{} } +func (m *InsertReq) String() string { return proto.CompactTextString(m) } +func (*InsertReq) ProtoMessage() {} +func (*InsertReq) Descriptor() ([]byte, []int) { + return fileDescriptor_fe658c3c35e8c4bc, []int{0} +} +func (m *InsertReq) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *InsertReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_InsertReq.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *InsertReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_InsertReq.Merge(m, src) +} +func (m *InsertReq) XXX_Size() int { + return m.Size() +} +func (m *InsertReq) XXX_DiscardUnknown() { + xxx_messageInfo_InsertReq.DiscardUnknown(m) +} + +var xxx_messageInfo_InsertReq proto.InternalMessageInfo + +func (m *InsertReq) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type InsertRes struct { + Id int32 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InsertRes) Reset() { *m = InsertRes{} } +func (m *InsertRes) String() string { return proto.CompactTextString(m) } +func (*InsertRes) ProtoMessage() {} +func (*InsertRes) Descriptor() ([]byte, []int) { + return fileDescriptor_fe658c3c35e8c4bc, []int{1} +} +func (m *InsertRes) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *InsertRes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_InsertRes.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *InsertRes) XXX_Merge(src proto.Message) { + xxx_messageInfo_InsertRes.Merge(m, src) +} +func (m *InsertRes) XXX_Size() int { + return m.Size() +} +func (m *InsertRes) XXX_DiscardUnknown() { + xxx_messageInfo_InsertRes.DiscardUnknown(m) +} + +var xxx_messageInfo_InsertRes proto.InternalMessageInfo + +func (m *InsertRes) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +type QueryReq struct { + Id int32 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty" v:"min:1#User id is required for querying."` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *QueryReq) Reset() { *m = QueryReq{} } +func (m *QueryReq) String() string { return proto.CompactTextString(m) } +func (*QueryReq) ProtoMessage() {} +func (*QueryReq) Descriptor() ([]byte, []int) { + return fileDescriptor_fe658c3c35e8c4bc, []int{2} +} +func (m *QueryReq) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryReq.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryReq.Merge(m, src) +} +func (m *QueryReq) XXX_Size() int { + return m.Size() +} +func (m *QueryReq) XXX_DiscardUnknown() { + xxx_messageInfo_QueryReq.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryReq proto.InternalMessageInfo + +func (m *QueryReq) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +type QueryRes struct { + Id int32 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *QueryRes) Reset() { *m = QueryRes{} } +func (m *QueryRes) String() string { return proto.CompactTextString(m) } +func (*QueryRes) ProtoMessage() {} +func (*QueryRes) Descriptor() ([]byte, []int) { + return fileDescriptor_fe658c3c35e8c4bc, []int{3} +} +func (m *QueryRes) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryRes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryRes.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryRes) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryRes.Merge(m, src) +} +func (m *QueryRes) XXX_Size() int { + return m.Size() +} +func (m *QueryRes) XXX_DiscardUnknown() { + xxx_messageInfo_QueryRes.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryRes proto.InternalMessageInfo + +func (m *QueryRes) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +func (m *QueryRes) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +type DeleteReq struct { + Id int32 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty" v:"required#User id is required for deleting."` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteReq) Reset() { *m = DeleteReq{} } +func (m *DeleteReq) String() string { return proto.CompactTextString(m) } +func (*DeleteReq) ProtoMessage() {} +func (*DeleteReq) Descriptor() ([]byte, []int) { + return fileDescriptor_fe658c3c35e8c4bc, []int{4} +} +func (m *DeleteReq) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteReq.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteReq) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteReq.Merge(m, src) +} +func (m *DeleteReq) XXX_Size() int { + return m.Size() +} +func (m *DeleteReq) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteReq.DiscardUnknown(m) +} + +var xxx_messageInfo_DeleteReq proto.InternalMessageInfo + +func (m *DeleteReq) GetId() int32 { + if m != nil { + return m.Id + } + return 0 +} + +type DeleteRes struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteRes) Reset() { *m = DeleteRes{} } +func (m *DeleteRes) String() string { return proto.CompactTextString(m) } +func (*DeleteRes) ProtoMessage() {} +func (*DeleteRes) Descriptor() ([]byte, []int) { + return fileDescriptor_fe658c3c35e8c4bc, []int{5} +} +func (m *DeleteRes) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteRes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteRes.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteRes) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteRes.Merge(m, src) +} +func (m *DeleteRes) XXX_Size() int { + return m.Size() +} +func (m *DeleteRes) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteRes.DiscardUnknown(m) +} + +var xxx_messageInfo_DeleteRes proto.InternalMessageInfo + +func init() { + proto.RegisterType((*InsertReq)(nil), "demos.InsertReq") + proto.RegisterType((*InsertRes)(nil), "demos.InsertRes") + proto.RegisterType((*QueryReq)(nil), "demos.QueryReq") + proto.RegisterType((*QueryRes)(nil), "demos.QueryRes") + proto.RegisterType((*DeleteReq)(nil), "demos.DeleteReq") + proto.RegisterType((*DeleteRes)(nil), "demos.DeleteRes") +} + +func init() { proto.RegisterFile("protocol/user.proto", fileDescriptor_fe658c3c35e8c4bc) } + +var fileDescriptor_fe658c3c35e8c4bc = []byte{ + // 358 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xcb, 0x4a, 0xc3, 0x40, + 0x14, 0x6d, 0x4a, 0x5b, 0xec, 0x88, 0x0f, 0xc6, 0x8d, 0x44, 0x48, 0x86, 0xa9, 0x8b, 0x42, 0x71, + 0x82, 0x75, 0x57, 0x04, 0x21, 0xb8, 0x09, 0x82, 0x8f, 0x82, 0x1b, 0x77, 0x6d, 0x73, 0x1b, 0x07, + 0x9a, 0x4c, 0x3b, 0x93, 0x14, 0xfc, 0x0e, 0x37, 0x7e, 0x92, 0x4b, 0xbf, 0xa0, 0x48, 0xfd, 0x83, + 0x7e, 0x81, 0xcc, 0xb4, 0x4d, 0x4a, 0xc4, 0x5d, 0xce, 0xbd, 0x39, 0xe7, 0xdc, 0x73, 0xef, 0xa0, + 0x93, 0xa9, 0x14, 0xa9, 0x18, 0x89, 0x89, 0x97, 0x29, 0x90, 0xcc, 0x20, 0x5c, 0x0f, 0x21, 0x16, + 0xca, 0xbe, 0x88, 0x78, 0xfa, 0x9a, 0x0d, 0xd9, 0x48, 0xc4, 0x5e, 0x24, 0x22, 0xe1, 0x99, 0xee, + 0x30, 0x1b, 0x1b, 0x64, 0x80, 0xf9, 0x5a, 0xb3, 0x68, 0x80, 0x9a, 0x41, 0xa2, 0x40, 0xa6, 0x7d, + 0x98, 0xe1, 0x6b, 0x54, 0xbb, 0x1f, 0xc4, 0x70, 0x6a, 0x11, 0xab, 0xdd, 0xf4, 0xdb, 0xab, 0x85, + 0x7b, 0x3e, 0xef, 0x51, 0x09, 0xb3, 0x8c, 0x4b, 0x08, 0x5b, 0x8f, 0x13, 0x18, 0x28, 0x20, 0x3c, + 0x99, 0x66, 0x29, 0xd1, 0xce, 0x24, 0x19, 0xc4, 0xc0, 0x68, 0xdf, 0xb0, 0xe8, 0x59, 0x21, 0xa5, + 0xf0, 0x21, 0xaa, 0x06, 0xa1, 0x11, 0xaa, 0xf7, 0xab, 0x41, 0x48, 0xef, 0xd0, 0xde, 0x53, 0x06, + 0xf2, 0x4d, 0xdb, 0xdc, 0x14, 0x3d, 0xdf, 0x5b, 0x2d, 0xdc, 0xce, 0xbc, 0x47, 0x63, 0x9e, 0xf4, + 0x2e, 0x5b, 0xcf, 0x5a, 0x94, 0x87, 0x84, 0x2b, 0xb2, 0x75, 0x25, 0x63, 0x21, 0xc9, 0x4c, 0x53, + 0x79, 0x12, 0x31, 0x6a, 0xc4, 0x58, 0x2e, 0xf6, 0xc7, 0x08, 0xe3, 0x4d, 0x86, 0xaa, 0xce, 0xb0, + 0x99, 0xec, 0x01, 0x35, 0x6f, 0x61, 0x02, 0x29, 0x68, 0x77, 0x7f, 0xc7, 0xbd, 0xbb, 0x5a, 0xb8, + 0x6c, 0x37, 0xe2, 0x7f, 0x03, 0x84, 0x9a, 0x5e, 0x0c, 0xb0, 0x5f, 0x08, 0xaa, 0xee, 0xbb, 0x85, + 0x6a, 0x9a, 0x87, 0x19, 0x6a, 0xac, 0x17, 0x80, 0x8f, 0x99, 0x39, 0x06, 0xcb, 0x57, 0x6b, 0x97, + 0x2b, 0x8a, 0x56, 0x70, 0x07, 0xd5, 0x4d, 0x0c, 0x7c, 0xb4, 0x69, 0x6e, 0x37, 0x64, 0x97, 0x0a, + 0xfa, 0x67, 0x86, 0x1a, 0x6b, 0xcb, 0x5c, 0x3c, 0x8f, 0x64, 0x97, 0x2b, 0x8a, 0x56, 0x7c, 0xf7, + 0x73, 0xe9, 0x58, 0x5f, 0x4b, 0xc7, 0xfa, 0x5e, 0x3a, 0xd6, 0xc7, 0x8f, 0x53, 0x79, 0x39, 0xc8, + 0x5f, 0x82, 0xbe, 0xdd, 0xb0, 0x61, 0xe0, 0xd5, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xec, 0x23, + 0x28, 0xf9, 0x4d, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// UserClient is the client API for User service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type UserClient interface { + Insert(ctx context.Context, in *InsertReq, opts ...grpc.CallOption) (*InsertRes, error) + Query(ctx context.Context, in *QueryReq, opts ...grpc.CallOption) (*QueryRes, error) + Delete(ctx context.Context, in *DeleteReq, opts ...grpc.CallOption) (*DeleteRes, error) +} + +type userClient struct { + cc *grpc.ClientConn +} + +func NewUserClient(cc *grpc.ClientConn) UserClient { + return &userClient{cc} +} + +func (c *userClient) Insert(ctx context.Context, in *InsertReq, opts ...grpc.CallOption) (*InsertRes, error) { + out := new(InsertRes) + err := c.cc.Invoke(ctx, "/demos.User/Insert", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userClient) Query(ctx context.Context, in *QueryReq, opts ...grpc.CallOption) (*QueryRes, error) { + out := new(QueryRes) + err := c.cc.Invoke(ctx, "/demos.User/Query", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userClient) Delete(ctx context.Context, in *DeleteReq, opts ...grpc.CallOption) (*DeleteRes, error) { + out := new(DeleteRes) + err := c.cc.Invoke(ctx, "/demos.User/Delete", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// UserServer is the server API for User service. +type UserServer interface { + Insert(context.Context, *InsertReq) (*InsertRes, error) + Query(context.Context, *QueryReq) (*QueryRes, error) + Delete(context.Context, *DeleteReq) (*DeleteRes, error) +} + +// UnimplementedUserServer can be embedded to have forward compatible implementations. +type UnimplementedUserServer struct { +} + +func (*UnimplementedUserServer) Insert(ctx context.Context, req *InsertReq) (*InsertRes, error) { + return nil, status.Errorf(codes.Unimplemented, "method Insert not implemented") +} +func (*UnimplementedUserServer) Query(ctx context.Context, req *QueryReq) (*QueryRes, error) { + return nil, status.Errorf(codes.Unimplemented, "method Query not implemented") +} +func (*UnimplementedUserServer) Delete(ctx context.Context, req *DeleteReq) (*DeleteRes, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") +} + +func RegisterUserServer(s *grpc.Server, srv UserServer) { + s.RegisterService(&_User_serviceDesc, srv) +} + +func _User_Insert_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InsertReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServer).Insert(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/demos.User/Insert", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServer).Insert(ctx, req.(*InsertReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _User_Query_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServer).Query(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/demos.User/Query", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServer).Query(ctx, req.(*QueryReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _User_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServer).Delete(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/demos.User/Delete", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServer).Delete(ctx, req.(*DeleteReq)) + } + return interceptor(ctx, in, info, handler) +} + +var _User_serviceDesc = grpc.ServiceDesc{ + ServiceName: "demos.User", + HandlerType: (*UserServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Insert", + Handler: _User_Insert_Handler, + }, + { + MethodName: "Query", + Handler: _User_Query_Handler, + }, + { + MethodName: "Delete", + Handler: _User_Delete_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "protocol/user.proto", +} + +func (m *InsertReq) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *InsertReq) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *InsertReq) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUser(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *InsertRes) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *InsertRes) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *InsertRes) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Id != 0 { + i = encodeVarintUser(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryReq) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryReq) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryReq) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Id != 0 { + i = encodeVarintUser(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryRes) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryRes) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryRes) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintUser(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if m.Id != 0 { + i = encodeVarintUser(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *DeleteReq) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DeleteReq) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteReq) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Id != 0 { + i = encodeVarintUser(dAtA, i, uint64(m.Id)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *DeleteRes) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DeleteRes) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteRes) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + return len(dAtA) - i, nil +} + +func encodeVarintUser(dAtA []byte, offset int, v uint64) int { + offset -= sovUser(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *InsertReq) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUser(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *InsertRes) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUser(uint64(m.Id)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *QueryReq) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUser(uint64(m.Id)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *QueryRes) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUser(uint64(m.Id)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovUser(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *DeleteReq) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Id != 0 { + n += 1 + sovUser(uint64(m.Id)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *DeleteRes) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovUser(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozUser(x uint64) (n int) { + return sovUser(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *InsertReq) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: InsertReq: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: InsertReq: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthUser + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUser + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUser(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUser + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *InsertRes) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: InsertRes: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: InsertRes: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipUser(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUser + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryReq) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryReq: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryReq: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipUser(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUser + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryRes) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryRes: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryRes: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthUser + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthUser + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipUser(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUser + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DeleteReq) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeleteReq: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeleteReq: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + m.Id = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Id |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipUser(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUser + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DeleteRes) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeleteRes: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeleteRes: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipUser(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthUser + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipUser(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowUser + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowUser + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowUser + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthUser + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupUser + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthUser + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthUser = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowUser = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupUser = fmt.Errorf("proto: unexpected end of group") +) diff --git a/.example/net/gtrace/5.grpc+db+redis+log/protocol/user/user.proto b/.example/net/gtrace/5.grpc+db+redis+log/protocol/user/user.proto new file mode 100644 index 000000000..dc7bd7afc --- /dev/null +++ b/.example/net/gtrace/5.grpc+db+redis+log/protocol/user/user.proto @@ -0,0 +1,39 @@ +// protoc --gofast_out=plugins=grpc:. protocol/*.proto -I/Users/john/Workspace/Go/GOPATH/src + +syntax = "proto3"; + +package demos; + +option go_package = "protobuf/user"; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +// User service for tracing demo. +service User { + rpc Insert(InsertReq) returns (InsertRes) {} + rpc Query(QueryReq) returns (QueryRes) {} + rpc Delete(DeleteReq) returns (DeleteRes) {} +} + +message InsertReq { + string Name = 1 [(gogoproto.moretags) = 'v:"required#Please input user name."']; +} + +message InsertRes { + int32 Id = 1; +} + +message QueryReq { + int32 Id = 1 [(gogoproto.moretags) = 'v:"min:1#User id is required for querying."']; +} + +message QueryRes { + int32 Id = 1; + string Name = 2; +} + +message DeleteReq { + int32 Id = 1 [(gogoproto.moretags) = 'v:"required#User id is required for deleting."']; +} + +message DeleteRes {} \ No newline at end of file diff --git a/.example/net/gtrace/5.grpc+db+redis+log/server/config.toml b/.example/net/gtrace/5.grpc+db+redis+log/server/config.toml new file mode 100644 index 000000000..451299306 --- /dev/null +++ b/.example/net/gtrace/5.grpc+db+redis+log/server/config.toml @@ -0,0 +1,19 @@ + +# MySQL. +[database] + [database.logger] + level = "all" + stdout = true + [database.default] + link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + debug = true + +# Redis. +[redis] + default = "127.0.0.1:6379,0" + cache = "127.0.0.1:6379,1" + + + + + diff --git a/.example/net/gtrace/5.grpc+db+redis+log/server/main.go b/.example/net/gtrace/5.grpc+db+redis+log/server/main.go new file mode 100644 index 000000000..b2e254b11 --- /dev/null +++ b/.example/net/gtrace/5.grpc+db+redis+log/server/main.go @@ -0,0 +1,118 @@ +package main + +import ( + "context" + "fmt" + "github.com/gogf/gcache-adapter/adapter" + "github.com/gogf/gf/.example/net/gtrace/5.grpc+db+redis+log/protobuf/user" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/util/gvalid" + "go.opentelemetry.io/otel/exporters/trace/jaeger" + sdkTrace "go.opentelemetry.io/otel/sdk/trace" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "net" + "time" +) + +type server struct{} + +const ( + JaegerEndpoint = "http://localhost:14268/s/traces" + ServiceName = "tracing-grpc-server" +) + +// initTracer creates a new trace provider instance and registers it as global trace provider. +func initTracer() func() { + // Create and install Jaeger export pipeline. + flush, err := jaeger.InstallNewPipeline( + jaeger.WithCollectorEndpoint(JaegerEndpoint), + jaeger.WithProcess(jaeger.Process{ + ServiceName: ServiceName, + }), + jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), + ) + if err != nil { + g.Log().Fatal(err) + } + return flush +} + +// Common validation unary interpreter. +func UnaryValidate(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + // It does nothing if there's no validation tag in the struct definition. + if err := gvalid.CheckStruct(req, nil); err != nil { + return nil, gerror.NewCode( + int(codes.InvalidArgument), + gerror.Current(err).Error(), + ) + } + return handler(ctx, req) +} + +// Insert is a route handler for inserting user info into dtabase. +func (s *server) Insert(ctx context.Context, req *user.InsertReq) (*user.InsertRes, error) { + res := user.InsertRes{} + result, err := g.Table("user").Ctx(ctx).Insert(g.Map{ + "name": req.Name, + }) + if err != nil { + return nil, err + } + id, _ := result.LastInsertId() + res.Id = int32(id) + return &res, nil +} + +// Query is a route handler for querying user info. It firstly retrieves the info from redis, +// if there's nothing in the redis, it then does db select. +func (s *server) Query(ctx context.Context, req *user.QueryReq) (*user.QueryRes, error) { + res := user.QueryRes{} + err := g.Table("user"). + Ctx(ctx). + Cache(5*time.Second, s.userCacheKey(req.Id)). + WherePri(req.Id). + Scan(&res) + if err != nil { + return nil, err + } + return &res, nil +} + +// Delete is a route handler for deleting specified user info. +func (s *server) Delete(ctx context.Context, req *user.DeleteReq) (*user.DeleteRes, error) { + res := user.DeleteRes{} + _, err := g.Table("user"). + Ctx(ctx). + Cache(-1, s.userCacheKey(req.Id)). + WherePri(req.Id). + Delete() + if err != nil { + return nil, err + } + return &res, nil +} + +func (s *server) userCacheKey(id int32) string { + return fmt.Sprintf(`userInfo:%d`, id) +} + +func main() { + flush := initTracer() + defer flush() + + g.DB().GetCache().SetAdapter(adapter.NewRedis(g.Redis())) + + listen, err := net.Listen("tcp", ":8000") + if err != nil { + g.Log().Fatalf("failed to listen: %v", err) + } + s := grpc.NewServer( + grpc.ChainUnaryInterceptor(UnaryValidate), + ) + user.RegisterUserServer(s, &server{}) + if err := s.Serve(listen); err != nil { + g.Log().Fatalf("failed to serve: %v", err) + } +} diff --git a/go.mod b/go.mod index 4222f512a..e81adda63 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,12 @@ go 1.11 require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.4.9 github.com/go-sql-driver/mysql v1.5.0 github.com/gogf/gcache-adapter v0.0.4-0.20210126062229-c84b9cefa528 + github.com/gogo/protobuf v1.3.2 + github.com/golang/protobuf v1.4.3 github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/websocket v1.4.1 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf @@ -21,6 +24,7 @@ require ( golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 // indirect golang.org/x/text v0.3.4 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/grpc v1.35.0 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c ) From 24e2c7926e39f29f2c8e4847f46d9836582b06e2 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 26 Jan 2021 16:53:39 +0800 Subject: [PATCH 119/492] go mod tidy --- go.mod | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/go.mod b/go.mod index e81adda63..40e157c86 100644 --- a/go.mod +++ b/go.mod @@ -5,26 +5,14 @@ go 1.11 require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 - github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.4.9 github.com/go-sql-driver/mysql v1.5.0 - github.com/gogf/gcache-adapter v0.0.4-0.20210126062229-c84b9cefa528 - github.com/gogo/protobuf v1.3.2 - github.com/golang/protobuf v1.4.3 github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/websocket v1.4.1 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf - github.com/kr/pretty v0.1.0 // indirect - github.com/mattn/go-runewidth v0.0.9 // indirect github.com/olekukonko/tablewriter v0.0.1 go.opentelemetry.io/otel v0.16.0 - go.opentelemetry.io/otel/exporters/trace/jaeger v0.16.0 - go.opentelemetry.io/otel/sdk v0.16.0 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 - golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 // indirect golang.org/x/text v0.3.4 - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/grpc v1.35.0 - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c ) From 6bad31f1bee8fafb3c074cae53321b60509faa14 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 26 Jan 2021 20:07:40 +0800 Subject: [PATCH 120/492] move tracing examples to new repo. --- .example/net/gtrace/1.http/client/main.go | 51 - .example/net/gtrace/1.http/server/main.go | 52 - .example/net/gtrace/2.http+db/config.toml | 19 - .example/net/gtrace/2.http+db/main.go | 64 - .example/net/gtrace/3.http+redis/config.toml | 10 - .example/net/gtrace/3.http+redis/main.go | 63 - .../gtrace/4.http+db+redis+log/client/main.go | 74 - .../4.http+db+redis+log/server/config.toml | 19 - .../gtrace/4.http+db+redis+log/server/main.go | 122 -- .../gtrace/5.grpc+db+redis+log/client/main.go | 80 - .../protobuf/user/user.pb.go | 1336 ----------------- .../protocol/user/user.proto | 39 - .../5.grpc+db+redis+log/server/config.toml | 19 - .../gtrace/5.grpc+db+redis+log/server/main.go | 118 -- 14 files changed, 2066 deletions(-) delete mode 100644 .example/net/gtrace/1.http/client/main.go delete mode 100644 .example/net/gtrace/1.http/server/main.go delete mode 100644 .example/net/gtrace/2.http+db/config.toml delete mode 100644 .example/net/gtrace/2.http+db/main.go delete mode 100644 .example/net/gtrace/3.http+redis/config.toml delete mode 100644 .example/net/gtrace/3.http+redis/main.go delete mode 100644 .example/net/gtrace/4.http+db+redis+log/client/main.go delete mode 100644 .example/net/gtrace/4.http+db+redis+log/server/config.toml delete mode 100644 .example/net/gtrace/4.http+db+redis+log/server/main.go delete mode 100644 .example/net/gtrace/5.grpc+db+redis+log/client/main.go delete mode 100644 .example/net/gtrace/5.grpc+db+redis+log/protobuf/user/user.pb.go delete mode 100644 .example/net/gtrace/5.grpc+db+redis+log/protocol/user/user.proto delete mode 100644 .example/net/gtrace/5.grpc+db+redis+log/server/config.toml delete mode 100644 .example/net/gtrace/5.grpc+db+redis+log/server/main.go diff --git a/.example/net/gtrace/1.http/client/main.go b/.example/net/gtrace/1.http/client/main.go deleted file mode 100644 index e22e606d1..000000000 --- a/.example/net/gtrace/1.http/client/main.go +++ /dev/null @@ -1,51 +0,0 @@ -package main - -import ( - "context" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" - "github.com/gogf/gf/net/gtrace" - "go.opentelemetry.io/otel/baggage" - "go.opentelemetry.io/otel/exporters/trace/jaeger" - "go.opentelemetry.io/otel/label" - sdkTrace "go.opentelemetry.io/otel/sdk/trace" -) - -const ( - JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "tracing-http-client" -) - -// initTracer creates a new trace provider instance and registers it as global trace provider. -func initTracer() func() { - // Create and install Jaeger export pipeline. - flush, err := jaeger.InstallNewPipeline( - jaeger.WithCollectorEndpoint(JaegerEndpoint), - jaeger.WithProcess(jaeger.Process{ - ServiceName: ServiceName, - }), - jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), - ) - if err != nil { - g.Log().Fatal(err) - } - return flush -} - -func StartRequests() { - ctx, span := gtrace.Tracer().Start(context.Background(), "StartRequests") - defer span.End() - - ctx = baggage.ContextWithValues(ctx, label.String("name", "john")) - client := g.Client().Use(ghttp.MiddlewareClientTracing) - - content := client.Ctx(ctx).GetContent("http://127.0.0.1:8199/hello") - g.Log().Ctx(ctx).Print(content) -} - -func main() { - flush := initTracer() - defer flush() - - StartRequests() -} diff --git a/.example/net/gtrace/1.http/server/main.go b/.example/net/gtrace/1.http/server/main.go deleted file mode 100644 index 7c6a2687c..000000000 --- a/.example/net/gtrace/1.http/server/main.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" - "github.com/gogf/gf/net/gtrace" - "go.opentelemetry.io/otel/baggage" - "go.opentelemetry.io/otel/exporters/trace/jaeger" - sdkTrace "go.opentelemetry.io/otel/sdk/trace" -) - -const ( - JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "tracing-http-server" -) - -// initTracer creates a new trace provider instance and registers it as global trace provider. -func initTracer() func() { - // Create and install Jaeger export pipeline. - flush, err := jaeger.InstallNewPipeline( - jaeger.WithCollectorEndpoint(JaegerEndpoint), - jaeger.WithProcess(jaeger.Process{ - ServiceName: ServiceName, - }), - jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), - ) - if err != nil { - g.Log().Fatal(err) - } - return flush -} - -func main() { - flush := initTracer() - defer flush() - - s := g.Server() - s.Group("/", func(group *ghttp.RouterGroup) { - group.Middleware(ghttp.MiddlewareServerTracing) - group.GET("/hello", helloHandler) - }) - s.SetPort(8199) - s.Run() -} - -func helloHandler(r *ghttp.Request) { - ctx, span := gtrace.Tracer().Start(r.Context(), "helloHandler") - defer span.End() - - value := baggage.Value(ctx, "name") - r.Response.Write("hello:", value.AsString()) -} diff --git a/.example/net/gtrace/2.http+db/config.toml b/.example/net/gtrace/2.http+db/config.toml deleted file mode 100644 index 643cb6089..000000000 --- a/.example/net/gtrace/2.http+db/config.toml +++ /dev/null @@ -1,19 +0,0 @@ - -# MySQL. -[database] - [database.logger] - level = "all" - stdout = true - [database.default] - link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" - debug = true - -# Redis. -[redis] - default = "127.0.0.1:6379,0" - cache = "127.0.0.1:6379,1" - - - - - diff --git a/.example/net/gtrace/2.http+db/main.go b/.example/net/gtrace/2.http+db/main.go deleted file mode 100644 index c85061135..000000000 --- a/.example/net/gtrace/2.http+db/main.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" - "go.opentelemetry.io/otel/exporters/trace/jaeger" - sdkTrace "go.opentelemetry.io/otel/sdk/trace" -) - -type tracingApi struct{} - -const ( - JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "tracing-demo-db" -) - -// initTracer creates a new trace provider instance and registers it as global trace provider. -func initTracer() func() { - // Create and install Jaeger export pipeline. - flush, err := jaeger.InstallNewPipeline( - jaeger.WithCollectorEndpoint(JaegerEndpoint), - jaeger.WithProcess(jaeger.Process{ - ServiceName: ServiceName, - }), - jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), - ) - if err != nil { - g.Log().Fatal(err) - } - return flush -} - -func (api *tracingApi) Insert(r *ghttp.Request) { - result, err := g.Table("user").Ctx(r.Context()).Insert(g.Map{ - "name": r.GetString("name"), - }) - if err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - id, _ := result.LastInsertId() - r.Response.Write("id:", id) -} - -func (api *tracingApi) Query(r *ghttp.Request) { - one, err := g.Table("user").Ctx(r.Context()).FindOne(r.GetInt("id")) - if err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - r.Response.Write("user:", one) -} - -func main() { - flush := initTracer() - defer flush() - - s := g.Server() - s.Group("/", func(group *ghttp.RouterGroup) { - group.Middleware(ghttp.MiddlewareServerTracing) - group.ALL("/user", new(tracingApi)) - }) - s.SetPort(8199) - s.Run() -} diff --git a/.example/net/gtrace/3.http+redis/config.toml b/.example/net/gtrace/3.http+redis/config.toml deleted file mode 100644 index a1e7b2d69..000000000 --- a/.example/net/gtrace/3.http+redis/config.toml +++ /dev/null @@ -1,10 +0,0 @@ - -# Redis. -[redis] - default = "127.0.0.1:6379,0" - cache = "127.0.0.1:6379,1" - - - - - diff --git a/.example/net/gtrace/3.http+redis/main.go b/.example/net/gtrace/3.http+redis/main.go deleted file mode 100644 index 35788087f..000000000 --- a/.example/net/gtrace/3.http+redis/main.go +++ /dev/null @@ -1,63 +0,0 @@ -package main - -import ( - "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" - "go.opentelemetry.io/otel/exporters/trace/jaeger" - sdkTrace "go.opentelemetry.io/otel/sdk/trace" -) - -type tracingApi struct{} - -const ( - JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "tracing-demo-redis" -) - -func (api *tracingApi) Set(r *ghttp.Request) { - _, err := g.Redis().Ctx(r.Context()).Do("SET", r.GetString("key"), r.GetString("value")) - if err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - r.Response.Write("ok") -} - -func (api *tracingApi) Get(r *ghttp.Request) { - value, err := g.Redis().Ctx(r.Context()).DoVar( - "GET", r.GetString("key"), - ) - if err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - r.Response.Write(value.String()) -} - -// initTracer creates a new trace provider instance and registers it as global trace provider. -func initTracer() func() { - // Create and install Jaeger export pipeline. - flush, err := jaeger.InstallNewPipeline( - jaeger.WithCollectorEndpoint(JaegerEndpoint), - jaeger.WithProcess(jaeger.Process{ - ServiceName: ServiceName, - }), - jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), - ) - if err != nil { - g.Log().Fatal(err) - } - return flush -} - -func main() { - flush := initTracer() - defer flush() - - s := g.Server() - s.Group("/", func(group *ghttp.RouterGroup) { - group.Middleware(ghttp.MiddlewareServerTracing) - group.ALL("/redis", new(tracingApi)) - }) - s.SetPort(8199) - s.Run() -} diff --git a/.example/net/gtrace/4.http+db+redis+log/client/main.go b/.example/net/gtrace/4.http+db+redis+log/client/main.go deleted file mode 100644 index 353a58bcf..000000000 --- a/.example/net/gtrace/4.http+db+redis+log/client/main.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "context" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" - "github.com/gogf/gf/net/gtrace" - "go.opentelemetry.io/otel/exporters/trace/jaeger" - sdkTrace "go.opentelemetry.io/otel/sdk/trace" -) - -const ( - JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "tracing-http-client" -) - -// initTracer creates a new trace provider instance and registers it as global trace provider. -func initTracer() func() { - // Create and install Jaeger export pipeline. - flush, err := jaeger.InstallNewPipeline( - jaeger.WithCollectorEndpoint(JaegerEndpoint), - jaeger.WithProcess(jaeger.Process{ - ServiceName: ServiceName, - }), - jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), - ) - if err != nil { - g.Log().Fatal(err) - } - return flush -} - -func StartRequests() { - ctx, span := gtrace.Tracer().Start(context.Background(), "StartRequests") - defer span.End() - - client := g.Client().Use(ghttp.MiddlewareClientTracing) - // Add user info. - idStr := client.Ctx(ctx).PostContent( - "http://127.0.0.1:8199/user/insert", - g.Map{ - "name": "john", - }, - ) - if idStr == "" { - g.Log().Ctx(ctx).Fatal("retrieve empty id string") - } - g.Log().Ctx(ctx).Print("insert:", idStr) - - // Query user info. - userJson := client.Ctx(ctx).GetContent( - "http://127.0.0.1:8199/user/query", - g.Map{ - "id": idStr, - }, - ) - g.Log().Ctx(ctx).Print("query:", idStr, userJson) - - // Delete user info. - deleteResult := client.Ctx(ctx).PostContent( - "http://127.0.0.1:8199/user/delete", - g.Map{ - "id": idStr, - }, - ) - g.Log().Ctx(ctx).Print("delete:", idStr, deleteResult) -} - -func main() { - flush := initTracer() - defer flush() - - StartRequests() -} diff --git a/.example/net/gtrace/4.http+db+redis+log/server/config.toml b/.example/net/gtrace/4.http+db+redis+log/server/config.toml deleted file mode 100644 index 451299306..000000000 --- a/.example/net/gtrace/4.http+db+redis+log/server/config.toml +++ /dev/null @@ -1,19 +0,0 @@ - -# MySQL. -[database] - [database.logger] - level = "all" - stdout = true - [database.default] - link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" - debug = true - -# Redis. -[redis] - default = "127.0.0.1:6379,0" - cache = "127.0.0.1:6379,1" - - - - - diff --git a/.example/net/gtrace/4.http+db+redis+log/server/main.go b/.example/net/gtrace/4.http+db+redis+log/server/main.go deleted file mode 100644 index 4b244fb4c..000000000 --- a/.example/net/gtrace/4.http+db+redis+log/server/main.go +++ /dev/null @@ -1,122 +0,0 @@ -package main - -import ( - "fmt" - "github.com/gogf/gcache-adapter/adapter" - "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" - "go.opentelemetry.io/otel/exporters/trace/jaeger" - sdkTrace "go.opentelemetry.io/otel/sdk/trace" - "time" -) - -type tracingApi struct{} - -const ( - JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "tracing-http-server" -) - -// initTracer creates a new trace provider instance and registers it as global trace provider. -func initTracer() func() { - // Create and install Jaeger export pipeline. - flush, err := jaeger.InstallNewPipeline( - jaeger.WithCollectorEndpoint(JaegerEndpoint), - jaeger.WithProcess(jaeger.Process{ - ServiceName: ServiceName, - }), - jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), - ) - if err != nil { - g.Log().Fatal(err) - } - return flush -} - -type userApiInsert struct { - Name string `v:"required#Please input user name."` -} - -// Insert is a route handler for inserting user info into dtabase. -func (api *tracingApi) Insert(r *ghttp.Request) { - var ( - dataReq *userApiInsert - ) - if err := r.Parse(&dataReq); err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - result, err := g.Table("user").Ctx(r.Context()).Insert(g.Map{ - "name": dataReq.Name, - }) - if err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - id, _ := result.LastInsertId() - r.Response.Write(id) -} - -type userApiQuery struct { - Id int `v:"min:1#User id is required for querying."` -} - -// Query is a route handler for querying user info. It firstly retrieves the info from redis, -// if there's nothing in the redis, it then does db select. -func (api *tracingApi) Query(r *ghttp.Request) { - var ( - dataReq *userApiQuery - ) - if err := r.Parse(&dataReq); err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - one, err := g.Table("user"). - Ctx(r.Context()). - Cache(5*time.Second, api.userCacheKey(dataReq.Id)). - FindOne(dataReq.Id) - if err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - r.Response.WriteJson(one) -} - -type userApiDelete struct { - Id int `v:"min:1#User id is required for deleting."` -} - -// Delete is a route handler for deleting specified user info. -func (api *tracingApi) Delete(r *ghttp.Request) { - var ( - dataReq *userApiDelete - ) - if err := r.Parse(&dataReq); err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - _, err := g.Table("user"). - Ctx(r.Context()). - Cache(-1, api.userCacheKey(dataReq.Id)). - WherePri(dataReq.Id). - Delete() - if err != nil { - r.Response.WriteExit(gerror.Current(err)) - } - r.Response.Write("ok") -} - -func (api *tracingApi) userCacheKey(id int) string { - return fmt.Sprintf(`userInfo:%d`, id) -} - -func main() { - flush := initTracer() - defer flush() - - g.DB().GetCache().SetAdapter(adapter.NewRedis(g.Redis())) - - s := g.Server() - s.Group("/", func(group *ghttp.RouterGroup) { - group.Middleware(ghttp.MiddlewareServerTracing) - group.ALL("/user", new(tracingApi)) - }) - s.SetPort(8199) - s.Run() -} diff --git a/.example/net/gtrace/5.grpc+db+redis+log/client/main.go b/.example/net/gtrace/5.grpc+db+redis+log/client/main.go deleted file mode 100644 index 3da3dc5f5..000000000 --- a/.example/net/gtrace/5.grpc+db+redis+log/client/main.go +++ /dev/null @@ -1,80 +0,0 @@ -package main - -import ( - "context" - "github.com/gogf/gf/.example/net/gtrace/5.grpc+db+redis+log/protobuf/user" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/gtrace" - "go.opentelemetry.io/otel/exporters/trace/jaeger" - sdkTrace "go.opentelemetry.io/otel/sdk/trace" - "google.golang.org/grpc" -) - -const ( - JaegerEndpoint = "http://localhost:14268/api/traces" - ServiceName = "tracing-grpc-client" -) - -// initTracer creates a new trace provider instance and registers it as global trace provider. -func initTracer() func() { - // Create and install Jaeger export pipeline. - flush, err := jaeger.InstallNewPipeline( - jaeger.WithCollectorEndpoint(JaegerEndpoint), - jaeger.WithProcess(jaeger.Process{ - ServiceName: ServiceName, - }), - jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), - ) - if err != nil { - g.Log().Fatal(err) - } - return flush -} - -func StartRequests() { - ctx, span := gtrace.Tracer().Start(context.Background(), "StartRequests") - defer span.End() - - conn, err := grpc.Dial(":8000", grpc.WithInsecure(), grpc.WithBlock()) - if err != nil { - g.Log().Fatalf("did not connect: %v", err) - } - defer conn.Close() - - client := user.NewUserClient(conn) - - // Insert. - insertRes, err := client.Insert(ctx, &user.InsertReq{ - Name: "john", - }) - if err != nil { - g.Log().Ctx(ctx).Fatalf(`%+v`, err) - } - g.Log().Ctx(ctx).Println("insert:", insertRes.Id) - - // Query. - queryRes, err := client.Query(ctx, &user.QueryReq{ - Id: insertRes.Id, - }) - if err != nil { - g.Log().Ctx(ctx).Fatalf(`%+v`, err) - } - g.Log().Ctx(ctx).Println("query:", queryRes) - - // Delete. - _, err = client.Delete(ctx, &user.DeleteReq{ - Id: insertRes.Id, - }) - if err != nil { - g.Log().Ctx(ctx).Fatalf(`%+v`, err) - } - g.Log().Ctx(ctx).Println("delete:", "ok") - -} - -func main() { - flush := initTracer() - defer flush() - - StartRequests() -} diff --git a/.example/net/gtrace/5.grpc+db+redis+log/protobuf/user/user.pb.go b/.example/net/gtrace/5.grpc+db+redis+log/protobuf/user/user.pb.go deleted file mode 100644 index 89fcfd05d..000000000 --- a/.example/net/gtrace/5.grpc+db+redis+log/protobuf/user/user.pb.go +++ /dev/null @@ -1,1336 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: protocol/user.proto - -package user - -import ( - context "context" - fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/golang/protobuf/proto" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - io "io" - math "math" - math_bits "math/bits" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type InsertReq struct { - Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty" v:"required#Please input user name."` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *InsertReq) Reset() { *m = InsertReq{} } -func (m *InsertReq) String() string { return proto.CompactTextString(m) } -func (*InsertReq) ProtoMessage() {} -func (*InsertReq) Descriptor() ([]byte, []int) { - return fileDescriptor_fe658c3c35e8c4bc, []int{0} -} -func (m *InsertReq) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *InsertReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_InsertReq.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *InsertReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_InsertReq.Merge(m, src) -} -func (m *InsertReq) XXX_Size() int { - return m.Size() -} -func (m *InsertReq) XXX_DiscardUnknown() { - xxx_messageInfo_InsertReq.DiscardUnknown(m) -} - -var xxx_messageInfo_InsertReq proto.InternalMessageInfo - -func (m *InsertReq) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -type InsertRes struct { - Id int32 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *InsertRes) Reset() { *m = InsertRes{} } -func (m *InsertRes) String() string { return proto.CompactTextString(m) } -func (*InsertRes) ProtoMessage() {} -func (*InsertRes) Descriptor() ([]byte, []int) { - return fileDescriptor_fe658c3c35e8c4bc, []int{1} -} -func (m *InsertRes) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *InsertRes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_InsertRes.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *InsertRes) XXX_Merge(src proto.Message) { - xxx_messageInfo_InsertRes.Merge(m, src) -} -func (m *InsertRes) XXX_Size() int { - return m.Size() -} -func (m *InsertRes) XXX_DiscardUnknown() { - xxx_messageInfo_InsertRes.DiscardUnknown(m) -} - -var xxx_messageInfo_InsertRes proto.InternalMessageInfo - -func (m *InsertRes) GetId() int32 { - if m != nil { - return m.Id - } - return 0 -} - -type QueryReq struct { - Id int32 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty" v:"min:1#User id is required for querying."` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *QueryReq) Reset() { *m = QueryReq{} } -func (m *QueryReq) String() string { return proto.CompactTextString(m) } -func (*QueryReq) ProtoMessage() {} -func (*QueryReq) Descriptor() ([]byte, []int) { - return fileDescriptor_fe658c3c35e8c4bc, []int{2} -} -func (m *QueryReq) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QueryReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QueryReq.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *QueryReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryReq.Merge(m, src) -} -func (m *QueryReq) XXX_Size() int { - return m.Size() -} -func (m *QueryReq) XXX_DiscardUnknown() { - xxx_messageInfo_QueryReq.DiscardUnknown(m) -} - -var xxx_messageInfo_QueryReq proto.InternalMessageInfo - -func (m *QueryReq) GetId() int32 { - if m != nil { - return m.Id - } - return 0 -} - -type QueryRes struct { - Id int32 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *QueryRes) Reset() { *m = QueryRes{} } -func (m *QueryRes) String() string { return proto.CompactTextString(m) } -func (*QueryRes) ProtoMessage() {} -func (*QueryRes) Descriptor() ([]byte, []int) { - return fileDescriptor_fe658c3c35e8c4bc, []int{3} -} -func (m *QueryRes) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QueryRes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QueryRes.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *QueryRes) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryRes.Merge(m, src) -} -func (m *QueryRes) XXX_Size() int { - return m.Size() -} -func (m *QueryRes) XXX_DiscardUnknown() { - xxx_messageInfo_QueryRes.DiscardUnknown(m) -} - -var xxx_messageInfo_QueryRes proto.InternalMessageInfo - -func (m *QueryRes) GetId() int32 { - if m != nil { - return m.Id - } - return 0 -} - -func (m *QueryRes) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -type DeleteReq struct { - Id int32 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty" v:"required#User id is required for deleting."` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DeleteReq) Reset() { *m = DeleteReq{} } -func (m *DeleteReq) String() string { return proto.CompactTextString(m) } -func (*DeleteReq) ProtoMessage() {} -func (*DeleteReq) Descriptor() ([]byte, []int) { - return fileDescriptor_fe658c3c35e8c4bc, []int{4} -} -func (m *DeleteReq) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *DeleteReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_DeleteReq.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *DeleteReq) XXX_Merge(src proto.Message) { - xxx_messageInfo_DeleteReq.Merge(m, src) -} -func (m *DeleteReq) XXX_Size() int { - return m.Size() -} -func (m *DeleteReq) XXX_DiscardUnknown() { - xxx_messageInfo_DeleteReq.DiscardUnknown(m) -} - -var xxx_messageInfo_DeleteReq proto.InternalMessageInfo - -func (m *DeleteReq) GetId() int32 { - if m != nil { - return m.Id - } - return 0 -} - -type DeleteRes struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DeleteRes) Reset() { *m = DeleteRes{} } -func (m *DeleteRes) String() string { return proto.CompactTextString(m) } -func (*DeleteRes) ProtoMessage() {} -func (*DeleteRes) Descriptor() ([]byte, []int) { - return fileDescriptor_fe658c3c35e8c4bc, []int{5} -} -func (m *DeleteRes) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *DeleteRes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_DeleteRes.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *DeleteRes) XXX_Merge(src proto.Message) { - xxx_messageInfo_DeleteRes.Merge(m, src) -} -func (m *DeleteRes) XXX_Size() int { - return m.Size() -} -func (m *DeleteRes) XXX_DiscardUnknown() { - xxx_messageInfo_DeleteRes.DiscardUnknown(m) -} - -var xxx_messageInfo_DeleteRes proto.InternalMessageInfo - -func init() { - proto.RegisterType((*InsertReq)(nil), "demos.InsertReq") - proto.RegisterType((*InsertRes)(nil), "demos.InsertRes") - proto.RegisterType((*QueryReq)(nil), "demos.QueryReq") - proto.RegisterType((*QueryRes)(nil), "demos.QueryRes") - proto.RegisterType((*DeleteReq)(nil), "demos.DeleteReq") - proto.RegisterType((*DeleteRes)(nil), "demos.DeleteRes") -} - -func init() { proto.RegisterFile("protocol/user.proto", fileDescriptor_fe658c3c35e8c4bc) } - -var fileDescriptor_fe658c3c35e8c4bc = []byte{ - // 358 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xcb, 0x4a, 0xc3, 0x40, - 0x14, 0x6d, 0x4a, 0x5b, 0xec, 0x88, 0x0f, 0xc6, 0x8d, 0x44, 0x48, 0x86, 0xa9, 0x8b, 0x42, 0x71, - 0x82, 0x75, 0x57, 0x04, 0x21, 0xb8, 0x09, 0x82, 0x8f, 0x82, 0x1b, 0x77, 0x6d, 0x73, 0x1b, 0x07, - 0x9a, 0x4c, 0x3b, 0x93, 0x14, 0xfc, 0x0e, 0x37, 0x7e, 0x92, 0x4b, 0xbf, 0xa0, 0x48, 0xfd, 0x83, - 0x7e, 0x81, 0xcc, 0xb4, 0x4d, 0x4a, 0xc4, 0x5d, 0xce, 0xbd, 0x39, 0xe7, 0xdc, 0x73, 0xef, 0xa0, - 0x93, 0xa9, 0x14, 0xa9, 0x18, 0x89, 0x89, 0x97, 0x29, 0x90, 0xcc, 0x20, 0x5c, 0x0f, 0x21, 0x16, - 0xca, 0xbe, 0x88, 0x78, 0xfa, 0x9a, 0x0d, 0xd9, 0x48, 0xc4, 0x5e, 0x24, 0x22, 0xe1, 0x99, 0xee, - 0x30, 0x1b, 0x1b, 0x64, 0x80, 0xf9, 0x5a, 0xb3, 0x68, 0x80, 0x9a, 0x41, 0xa2, 0x40, 0xa6, 0x7d, - 0x98, 0xe1, 0x6b, 0x54, 0xbb, 0x1f, 0xc4, 0x70, 0x6a, 0x11, 0xab, 0xdd, 0xf4, 0xdb, 0xab, 0x85, - 0x7b, 0x3e, 0xef, 0x51, 0x09, 0xb3, 0x8c, 0x4b, 0x08, 0x5b, 0x8f, 0x13, 0x18, 0x28, 0x20, 0x3c, - 0x99, 0x66, 0x29, 0xd1, 0xce, 0x24, 0x19, 0xc4, 0xc0, 0x68, 0xdf, 0xb0, 0xe8, 0x59, 0x21, 0xa5, - 0xf0, 0x21, 0xaa, 0x06, 0xa1, 0x11, 0xaa, 0xf7, 0xab, 0x41, 0x48, 0xef, 0xd0, 0xde, 0x53, 0x06, - 0xf2, 0x4d, 0xdb, 0xdc, 0x14, 0x3d, 0xdf, 0x5b, 0x2d, 0xdc, 0xce, 0xbc, 0x47, 0x63, 0x9e, 0xf4, - 0x2e, 0x5b, 0xcf, 0x5a, 0x94, 0x87, 0x84, 0x2b, 0xb2, 0x75, 0x25, 0x63, 0x21, 0xc9, 0x4c, 0x53, - 0x79, 0x12, 0x31, 0x6a, 0xc4, 0x58, 0x2e, 0xf6, 0xc7, 0x08, 0xe3, 0x4d, 0x86, 0xaa, 0xce, 0xb0, - 0x99, 0xec, 0x01, 0x35, 0x6f, 0x61, 0x02, 0x29, 0x68, 0x77, 0x7f, 0xc7, 0xbd, 0xbb, 0x5a, 0xb8, - 0x6c, 0x37, 0xe2, 0x7f, 0x03, 0x84, 0x9a, 0x5e, 0x0c, 0xb0, 0x5f, 0x08, 0xaa, 0xee, 0xbb, 0x85, - 0x6a, 0x9a, 0x87, 0x19, 0x6a, 0xac, 0x17, 0x80, 0x8f, 0x99, 0x39, 0x06, 0xcb, 0x57, 0x6b, 0x97, - 0x2b, 0x8a, 0x56, 0x70, 0x07, 0xd5, 0x4d, 0x0c, 0x7c, 0xb4, 0x69, 0x6e, 0x37, 0x64, 0x97, 0x0a, - 0xfa, 0x67, 0x86, 0x1a, 0x6b, 0xcb, 0x5c, 0x3c, 0x8f, 0x64, 0x97, 0x2b, 0x8a, 0x56, 0x7c, 0xf7, - 0x73, 0xe9, 0x58, 0x5f, 0x4b, 0xc7, 0xfa, 0x5e, 0x3a, 0xd6, 0xc7, 0x8f, 0x53, 0x79, 0x39, 0xc8, - 0x5f, 0x82, 0xbe, 0xdd, 0xb0, 0x61, 0xe0, 0xd5, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xec, 0x23, - 0x28, 0xf9, 0x4d, 0x02, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// UserClient is the client API for User service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type UserClient interface { - Insert(ctx context.Context, in *InsertReq, opts ...grpc.CallOption) (*InsertRes, error) - Query(ctx context.Context, in *QueryReq, opts ...grpc.CallOption) (*QueryRes, error) - Delete(ctx context.Context, in *DeleteReq, opts ...grpc.CallOption) (*DeleteRes, error) -} - -type userClient struct { - cc *grpc.ClientConn -} - -func NewUserClient(cc *grpc.ClientConn) UserClient { - return &userClient{cc} -} - -func (c *userClient) Insert(ctx context.Context, in *InsertReq, opts ...grpc.CallOption) (*InsertRes, error) { - out := new(InsertRes) - err := c.cc.Invoke(ctx, "/demos.User/Insert", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *userClient) Query(ctx context.Context, in *QueryReq, opts ...grpc.CallOption) (*QueryRes, error) { - out := new(QueryRes) - err := c.cc.Invoke(ctx, "/demos.User/Query", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *userClient) Delete(ctx context.Context, in *DeleteReq, opts ...grpc.CallOption) (*DeleteRes, error) { - out := new(DeleteRes) - err := c.cc.Invoke(ctx, "/demos.User/Delete", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// UserServer is the server API for User service. -type UserServer interface { - Insert(context.Context, *InsertReq) (*InsertRes, error) - Query(context.Context, *QueryReq) (*QueryRes, error) - Delete(context.Context, *DeleteReq) (*DeleteRes, error) -} - -// UnimplementedUserServer can be embedded to have forward compatible implementations. -type UnimplementedUserServer struct { -} - -func (*UnimplementedUserServer) Insert(ctx context.Context, req *InsertReq) (*InsertRes, error) { - return nil, status.Errorf(codes.Unimplemented, "method Insert not implemented") -} -func (*UnimplementedUserServer) Query(ctx context.Context, req *QueryReq) (*QueryRes, error) { - return nil, status.Errorf(codes.Unimplemented, "method Query not implemented") -} -func (*UnimplementedUserServer) Delete(ctx context.Context, req *DeleteReq) (*DeleteRes, error) { - return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") -} - -func RegisterUserServer(s *grpc.Server, srv UserServer) { - s.RegisterService(&_User_serviceDesc, srv) -} - -func _User_Insert_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(InsertReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(UserServer).Insert(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/demos.User/Insert", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(UserServer).Insert(ctx, req.(*InsertReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _User_Query_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(UserServer).Query(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/demos.User/Query", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(UserServer).Query(ctx, req.(*QueryReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _User_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(UserServer).Delete(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/demos.User/Delete", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(UserServer).Delete(ctx, req.(*DeleteReq)) - } - return interceptor(ctx, in, info, handler) -} - -var _User_serviceDesc = grpc.ServiceDesc{ - ServiceName: "demos.User", - HandlerType: (*UserServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Insert", - Handler: _User_Insert_Handler, - }, - { - MethodName: "Query", - Handler: _User_Query_Handler, - }, - { - MethodName: "Delete", - Handler: _User_Delete_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "protocol/user.proto", -} - -func (m *InsertReq) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *InsertReq) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *InsertReq) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if len(m.Name) > 0 { - i -= len(m.Name) - copy(dAtA[i:], m.Name) - i = encodeVarintUser(dAtA, i, uint64(len(m.Name))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *InsertRes) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *InsertRes) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *InsertRes) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if m.Id != 0 { - i = encodeVarintUser(dAtA, i, uint64(m.Id)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *QueryReq) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *QueryReq) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QueryReq) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if m.Id != 0 { - i = encodeVarintUser(dAtA, i, uint64(m.Id)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *QueryRes) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *QueryRes) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QueryRes) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if len(m.Name) > 0 { - i -= len(m.Name) - copy(dAtA[i:], m.Name) - i = encodeVarintUser(dAtA, i, uint64(len(m.Name))) - i-- - dAtA[i] = 0x12 - } - if m.Id != 0 { - i = encodeVarintUser(dAtA, i, uint64(m.Id)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *DeleteReq) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *DeleteReq) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *DeleteReq) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if m.Id != 0 { - i = encodeVarintUser(dAtA, i, uint64(m.Id)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *DeleteRes) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *DeleteRes) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *DeleteRes) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - return len(dAtA) - i, nil -} - -func encodeVarintUser(dAtA []byte, offset int, v uint64) int { - offset -= sovUser(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *InsertReq) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovUser(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *InsertRes) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Id != 0 { - n += 1 + sovUser(uint64(m.Id)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *QueryReq) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Id != 0 { - n += 1 + sovUser(uint64(m.Id)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *QueryRes) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Id != 0 { - n += 1 + sovUser(uint64(m.Id)) - } - l = len(m.Name) - if l > 0 { - n += 1 + l + sovUser(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *DeleteReq) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Id != 0 { - n += 1 + sovUser(uint64(m.Id)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *DeleteRes) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func sovUser(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozUser(x uint64) (n int) { - return sovUser(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *InsertReq) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: InsertReq: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: InsertReq: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthUser - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthUser - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipUser(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthUser - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *InsertRes) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: InsertRes: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: InsertRes: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - m.Id = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Id |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipUser(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthUser - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *QueryReq) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: QueryReq: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryReq: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - m.Id = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Id |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipUser(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthUser - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *QueryRes) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: QueryRes: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryRes: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - m.Id = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Id |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthUser - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthUser - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipUser(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthUser - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *DeleteReq) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: DeleteReq: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: DeleteReq: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - m.Id = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Id |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipUser(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthUser - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *DeleteRes) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowUser - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: DeleteRes: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: DeleteRes: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipUser(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthUser - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipUser(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowUser - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowUser - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowUser - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthUser - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupUser - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthUser - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthUser = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowUser = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupUser = fmt.Errorf("proto: unexpected end of group") -) diff --git a/.example/net/gtrace/5.grpc+db+redis+log/protocol/user/user.proto b/.example/net/gtrace/5.grpc+db+redis+log/protocol/user/user.proto deleted file mode 100644 index dc7bd7afc..000000000 --- a/.example/net/gtrace/5.grpc+db+redis+log/protocol/user/user.proto +++ /dev/null @@ -1,39 +0,0 @@ -// protoc --gofast_out=plugins=grpc:. protocol/*.proto -I/Users/john/Workspace/Go/GOPATH/src - -syntax = "proto3"; - -package demos; - -option go_package = "protobuf/user"; - -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -// User service for tracing demo. -service User { - rpc Insert(InsertReq) returns (InsertRes) {} - rpc Query(QueryReq) returns (QueryRes) {} - rpc Delete(DeleteReq) returns (DeleteRes) {} -} - -message InsertReq { - string Name = 1 [(gogoproto.moretags) = 'v:"required#Please input user name."']; -} - -message InsertRes { - int32 Id = 1; -} - -message QueryReq { - int32 Id = 1 [(gogoproto.moretags) = 'v:"min:1#User id is required for querying."']; -} - -message QueryRes { - int32 Id = 1; - string Name = 2; -} - -message DeleteReq { - int32 Id = 1 [(gogoproto.moretags) = 'v:"required#User id is required for deleting."']; -} - -message DeleteRes {} \ No newline at end of file diff --git a/.example/net/gtrace/5.grpc+db+redis+log/server/config.toml b/.example/net/gtrace/5.grpc+db+redis+log/server/config.toml deleted file mode 100644 index 451299306..000000000 --- a/.example/net/gtrace/5.grpc+db+redis+log/server/config.toml +++ /dev/null @@ -1,19 +0,0 @@ - -# MySQL. -[database] - [database.logger] - level = "all" - stdout = true - [database.default] - link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" - debug = true - -# Redis. -[redis] - default = "127.0.0.1:6379,0" - cache = "127.0.0.1:6379,1" - - - - - diff --git a/.example/net/gtrace/5.grpc+db+redis+log/server/main.go b/.example/net/gtrace/5.grpc+db+redis+log/server/main.go deleted file mode 100644 index b2e254b11..000000000 --- a/.example/net/gtrace/5.grpc+db+redis+log/server/main.go +++ /dev/null @@ -1,118 +0,0 @@ -package main - -import ( - "context" - "fmt" - "github.com/gogf/gcache-adapter/adapter" - "github.com/gogf/gf/.example/net/gtrace/5.grpc+db+redis+log/protobuf/user" - "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/util/gvalid" - "go.opentelemetry.io/otel/exporters/trace/jaeger" - sdkTrace "go.opentelemetry.io/otel/sdk/trace" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "net" - "time" -) - -type server struct{} - -const ( - JaegerEndpoint = "http://localhost:14268/s/traces" - ServiceName = "tracing-grpc-server" -) - -// initTracer creates a new trace provider instance and registers it as global trace provider. -func initTracer() func() { - // Create and install Jaeger export pipeline. - flush, err := jaeger.InstallNewPipeline( - jaeger.WithCollectorEndpoint(JaegerEndpoint), - jaeger.WithProcess(jaeger.Process{ - ServiceName: ServiceName, - }), - jaeger.WithSDK(&sdkTrace.Config{DefaultSampler: sdkTrace.AlwaysSample()}), - ) - if err != nil { - g.Log().Fatal(err) - } - return flush -} - -// Common validation unary interpreter. -func UnaryValidate(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { - // It does nothing if there's no validation tag in the struct definition. - if err := gvalid.CheckStruct(req, nil); err != nil { - return nil, gerror.NewCode( - int(codes.InvalidArgument), - gerror.Current(err).Error(), - ) - } - return handler(ctx, req) -} - -// Insert is a route handler for inserting user info into dtabase. -func (s *server) Insert(ctx context.Context, req *user.InsertReq) (*user.InsertRes, error) { - res := user.InsertRes{} - result, err := g.Table("user").Ctx(ctx).Insert(g.Map{ - "name": req.Name, - }) - if err != nil { - return nil, err - } - id, _ := result.LastInsertId() - res.Id = int32(id) - return &res, nil -} - -// Query is a route handler for querying user info. It firstly retrieves the info from redis, -// if there's nothing in the redis, it then does db select. -func (s *server) Query(ctx context.Context, req *user.QueryReq) (*user.QueryRes, error) { - res := user.QueryRes{} - err := g.Table("user"). - Ctx(ctx). - Cache(5*time.Second, s.userCacheKey(req.Id)). - WherePri(req.Id). - Scan(&res) - if err != nil { - return nil, err - } - return &res, nil -} - -// Delete is a route handler for deleting specified user info. -func (s *server) Delete(ctx context.Context, req *user.DeleteReq) (*user.DeleteRes, error) { - res := user.DeleteRes{} - _, err := g.Table("user"). - Ctx(ctx). - Cache(-1, s.userCacheKey(req.Id)). - WherePri(req.Id). - Delete() - if err != nil { - return nil, err - } - return &res, nil -} - -func (s *server) userCacheKey(id int32) string { - return fmt.Sprintf(`userInfo:%d`, id) -} - -func main() { - flush := initTracer() - defer flush() - - g.DB().GetCache().SetAdapter(adapter.NewRedis(g.Redis())) - - listen, err := net.Listen("tcp", ":8000") - if err != nil { - g.Log().Fatalf("failed to listen: %v", err) - } - s := grpc.NewServer( - grpc.ChainUnaryInterceptor(UnaryValidate), - ) - user.RegisterUserServer(s, &server{}) - if err := s.Serve(listen); err != nil { - g.Log().Fatalf("failed to serve: %v", err) - } -} From 94adc50487951565990b04a053e23c256af93945 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 26 Jan 2021 21:20:06 +0800 Subject: [PATCH 121/492] version updates --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 82df3b549..a8db58433 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.15.1" +const VERSION = "v1.15.2" const AUTHORS = "john" From 9cc5d7a691350c6dcab835ee80c0f981bd3c5009 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 27 Jan 2021 00:20:23 +0800 Subject: [PATCH 122/492] improve package gdebug/gfile --- debug/gdebug/gdebug_caller.go | 12 +++++----- debug/gdebug/gdebug_stack.go | 4 ++-- internal/intlog/intlog.go | 8 +++---- net/ghttp/ghttp_server_router.go | 4 ++-- net/ghttp/ghttp_server_router_group.go | 2 +- os/gfile/gfile_source.go | 31 +++++++++----------------- 6 files changed, 26 insertions(+), 35 deletions(-) diff --git a/debug/gdebug/gdebug_caller.go b/debug/gdebug/gdebug_caller.go index 3256d2695..a70ffda6a 100644 --- a/debug/gdebug/gdebug_caller.go +++ b/debug/gdebug/gdebug_caller.go @@ -17,8 +17,8 @@ import ( ) const ( - gMAX_DEPTH = 1000 - gFILTER_KEY = "/debug/gdebug/gdebug" + maxCallerDepth = 1000 + stackFilterKey = "/debug/gdebug/gdebug" ) var ( @@ -60,7 +60,7 @@ func CallerWithFilter(filter string, skip ...int) (function string, path string, ok := true pc, file, line, start := callerFromIndex([]string{filter}) if start != -1 { - for i := start + number; i < gMAX_DEPTH; i++ { + for i := start + number; i < maxCallerDepth; i++ { if i != start { pc, file, line, ok = runtime.Caller(i) } @@ -68,7 +68,7 @@ func CallerWithFilter(filter string, skip ...int) (function string, path string, if filter != "" && strings.Contains(file, filter) { continue } - if strings.Contains(file, gFILTER_KEY) { + if strings.Contains(file, stackFilterKey) { continue } function := "" @@ -92,7 +92,7 @@ func CallerWithFilter(filter string, skip ...int) (function string, path string, // VERY NOTE THAT, the returned index value should be as the caller's start point. func callerFromIndex(filters []string) (pc uintptr, file string, line int, index int) { var filtered, ok bool - for index = 0; index < gMAX_DEPTH; index++ { + for index = 0; index < maxCallerDepth; index++ { if pc, file, line, ok = runtime.Caller(index); ok { filtered = false for _, filter := range filters { @@ -104,7 +104,7 @@ func callerFromIndex(filters []string) (pc uintptr, file string, line int, index if filtered { continue } - if strings.Contains(file, gFILTER_KEY) { + if strings.Contains(file, stackFilterKey) { continue } if index > 0 { diff --git a/debug/gdebug/gdebug_stack.go b/debug/gdebug/gdebug_stack.go index 5e6b572fc..af61b9591 100644 --- a/debug/gdebug/gdebug_stack.go +++ b/debug/gdebug/gdebug_stack.go @@ -53,7 +53,7 @@ func StackWithFilters(filters []string, skip ...int) string { ok = true pc, file, line, start = callerFromIndex(filters) ) - for i := start + number; i < gMAX_DEPTH; i++ { + for i := start + number; i < maxCallerDepth; i++ { if i != start { pc, file, line, ok = runtime.Caller(i) } @@ -79,7 +79,7 @@ func StackWithFilters(filters []string, skip ...int) string { if filtered { continue } - if strings.Contains(file, gFILTER_KEY) { + if strings.Contains(file, stackFilterKey) { continue } if fn := runtime.FuncForPC(pc); fn == nil { diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index c2cc60ad6..4c6ec3031 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -16,7 +16,7 @@ import ( ) const ( - gFILTER_KEY = "/internal/intlog" + stackFilterKey = "/internal/intlog" ) var ( @@ -71,7 +71,7 @@ func Error(v ...interface{}) { return } array := append([]interface{}{now(), "[INTE]", file()}, v...) - array = append(array, "\n"+gdebug.StackWithFilter(gFILTER_KEY)) + array = append(array, "\n"+gdebug.StackWithFilter(stackFilterKey)) fmt.Println(array...) } @@ -82,7 +82,7 @@ func Errorf(format string, v ...interface{}) { } fmt.Printf( now()+" [INTE] "+file()+" "+format+"\n%s\n", - append(v, gdebug.StackWithFilter(gFILTER_KEY))..., + append(v, gdebug.StackWithFilter(stackFilterKey))..., ) } @@ -93,6 +93,6 @@ func now() string { // file returns caller file name along with its line number. func file() string { - _, p, l := gdebug.CallerWithFilter(gFILTER_KEY) + _, p, l := gdebug.CallerWithFilter(stackFilterKey) return fmt.Sprintf(`%s:%d`, filepath.Base(p), l) } diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index 8049d01e4..cc7d883b3 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -20,7 +20,7 @@ import ( ) const ( - gFILTER_KEY = "/net/ghttp/ghttp" + stackFilterKey = "/net/ghttp/ghttp" ) var ( @@ -68,7 +68,7 @@ func (s *Server) parsePattern(pattern string) (domain, method, path string, err func (s *Server) setHandler(pattern string, handler *handlerItem) { handler.itemId = handlerIdGenerator.Add(1) if handler.source == "" { - _, file, line := gdebug.CallerWithFilter(gFILTER_KEY) + _, file, line := gdebug.CallerWithFilter(stackFilterKey) handler.source = fmt.Sprintf(`%s:%d`, file, line) } domain, method, uri, err := s.parsePattern(pattern) diff --git a/net/ghttp/ghttp_server_router_group.go b/net/ghttp/ghttp_server_router_group.go index 90dbca7bc..3b4d01d0b 100644 --- a/net/ghttp/ghttp_server_router_group.go +++ b/net/ghttp/ghttp_server_router_group.go @@ -248,7 +248,7 @@ func (g *RouterGroup) Middleware(handlers ...HandlerFunc) *RouterGroup { // preBindToLocalArray adds the route registering parameters to internal variable array for lazily registering feature. func (g *RouterGroup) preBindToLocalArray(bindType string, pattern string, object interface{}, params ...interface{}) *RouterGroup { - _, file, line := gdebug.CallerWithFilter(gFILTER_KEY) + _, file, line := gdebug.CallerWithFilter(stackFilterKey) preBindItems = append(preBindItems, &preBindItem{ group: g, bindType: bindType, diff --git a/os/gfile/gfile_source.go b/os/gfile/gfile_source.go index 3faa85b11..efe93840b 100644 --- a/os/gfile/gfile_source.go +++ b/os/gfile/gfile_source.go @@ -7,7 +7,7 @@ package gfile import ( - "os" + "github.com/gogf/gf/text/gstr" "runtime" "strings" @@ -36,7 +36,7 @@ func init() { // Note2: When the method is called for the first time, if it is in an asynchronous goroutine, // the method may not get the main package path. func MainPkgPath() string { - // Only for source development environments. + // It is only for source development environments. if goRootForFilter == "" { return "" } @@ -44,20 +44,23 @@ func MainPkgPath() string { if path != "" { return path } - lastFile := "" for i := 1; i < 10000; i++ { - if _, file, _, ok := runtime.Caller(i); ok { + if pc, file, _, ok := runtime.Caller(i); ok { if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter { continue } - if gregex.IsMatchString(`/github.com/[^/]+/gf/`, file) && - !gregex.IsMatchString(`/github.com/[^/]+/gf/\.example/`, file) { - continue + // Check if it is called in package initialization function, + // in which it here cannot retrieve main package path, + // it so just returns that can make next check. + if fn := runtime.FuncForPC(pc); fn != nil { + array := gstr.Split(fn.Name(), ".") + if array[0] != "main" { + continue + } } if Ext(file) != ".go" { continue } - lastFile = file if gregex.IsMatchString(`package\s+main`, GetContents(file)) { mainPkgPath.Set(Dir(file)) return Dir(file) @@ -66,17 +69,5 @@ func MainPkgPath() string { break } } - if lastFile != "" { - for path = Dir(lastFile); len(path) > 1 && Exists(path) && path[len(path)-1] != os.PathSeparator; { - files, _ := ScanDir(path, "*.go") - for _, v := range files { - if gregex.IsMatchString(`package\s+main`, GetContents(v)) { - mainPkgPath.Set(path) - return path - } - } - path = Dir(path) - } - } return "" } From f5ba665c59821fad954298c7e8a9c8c546e9d2f4 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 27 Jan 2021 00:55:45 +0800 Subject: [PATCH 123/492] improve package gcfg --- os/gcfg/gcfg.go | 134 ++++++++++++++++++++++------------------- os/gcfg/gcfg_api.go | 2 +- os/gcfg/gcfg_config.go | 6 +- os/gcfg/gcfg_error.go | 6 +- 4 files changed, 78 insertions(+), 70 deletions(-) diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index 71c203cd0..dec2ad8fe 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -33,10 +33,10 @@ const ( // Configuration struct. type Config struct { - name string // Default configuration file name. - paths *garray.StrArray // Searching path array. - jsons *gmap.StrAnyMap // The pared JSON objects for configuration files. - vc bool // Whether do violence check in value index searching. It affects the performance when set true(false in default). + defaultName string // Default configuration file name. + searchPaths *garray.StrArray // Searching path array. + jsonMap *gmap.StrAnyMap // The pared JSON objects for configuration files. + violenceCheck bool // Whether do violence check in value index searching. It affects the performance when set true(false in default). } var ( @@ -57,9 +57,9 @@ func New(file ...string) *Config { } } c := &Config{ - name: name, - paths: garray.NewStrArray(true), - jsons: gmap.NewStrAnyMap(true), + defaultName: name, + searchPaths: garray.NewStrArray(true), + jsonMap: gmap.NewStrAnyMap(true), } // Customized dir path from env/cmd. if customPath := gcmd.GetWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).String(); customPath != "" { @@ -85,27 +85,42 @@ func New(file ...string) *Config { return c } +func (c *Config) getSearchPaths() []string { + var ( + searchPaths = c.searchPaths.Slice() + mainPkgPath = gfile.MainPkgPath() + ) + if mainPkgPath != "" { + if !gstr.InArray(searchPaths, mainPkgPath) { + searchPaths = append([]string{mainPkgPath}, searchPaths...) + } + } + return searchPaths +} + // filePath returns the absolute configuration file path for the given filename by . func (c *Config) filePath(file ...string) (path string) { - name := c.name + name := c.defaultName if len(file) > 0 { name = file[0] } path = c.FilePath(name) if path == "" { - buffer := bytes.NewBuffer(nil) - if c.paths.Len() > 0 { + var ( + searchPaths = c.getSearchPaths() + buffer = bytes.NewBuffer(nil) + ) + + if len(searchPaths) > 0 { buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name)) - c.paths.RLockFunc(func(array []string) { - index := 1 - for _, v := range array { - v = gstr.TrimRight(v, `\/`) - buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) - index++ - buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) - index++ - } - }) + index := 1 + for _, v := range searchPaths { + v = gstr.TrimRight(v, `\/`) + buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) + index++ + buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) + index++ + } } else { buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path set/add", name)) } @@ -132,7 +147,7 @@ func (c *Config) SetPath(path string) error { realPath = gfile.RealPath(path) if realPath == "" { // Relative path. - c.paths.RLockFunc(func(array []string) { + c.searchPaths.RLockFunc(func(array []string) { for _, v := range array { if path, _ := gspath.Search(v, path); path != "" { realPath = path @@ -148,9 +163,9 @@ func (c *Config) SetPath(path string) error { // Path not exist. if realPath == "" { buffer := bytes.NewBuffer(nil) - if c.paths.Len() > 0 { + if c.searchPaths.Len() > 0 { buffer.WriteString(fmt.Sprintf("[gcfg] SetPath failed: cannot find directory \"%s\" in following paths:", path)) - c.paths.RLockFunc(func(array []string) { + c.searchPaths.RLockFunc(func(array []string) { for k, v := range array { buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) } @@ -173,12 +188,12 @@ func (c *Config) SetPath(path string) error { return err } // Repeated path check. - if c.paths.Search(realPath) != -1 { + if c.searchPaths.Search(realPath) != -1 { return nil } - c.jsons.Clear() - c.paths.Clear() - c.paths.Append(realPath) + c.jsonMap.Clear() + c.searchPaths.Clear() + c.searchPaths.Append(realPath) intlog.Print("SetPath:", realPath) return nil } @@ -190,7 +205,7 @@ func (c *Config) SetPath(path string) error { // Note that, turning on this feature is quite expensive, and it is not recommended // to allow separators in the key names. It is best to avoid this on the application side. func (c *Config) SetViolenceCheck(check bool) { - c.vc = check + c.violenceCheck = check c.Clear() } @@ -210,7 +225,7 @@ func (c *Config) AddPath(path string) error { realPath = gfile.RealPath(path) if realPath == "" { // Relative path. - c.paths.RLockFunc(func(array []string) { + c.searchPaths.RLockFunc(func(array []string) { for _, v := range array { if path, _ := gspath.Search(v, path); path != "" { realPath = path @@ -225,9 +240,9 @@ func (c *Config) AddPath(path string) error { } if realPath == "" { buffer := bytes.NewBuffer(nil) - if c.paths.Len() > 0 { + if c.searchPaths.Len() > 0 { buffer.WriteString(fmt.Sprintf("[gcfg] AddPath failed: cannot find directory \"%s\" in following paths:", path)) - c.paths.RLockFunc(func(array []string) { + c.searchPaths.RLockFunc(func(array []string) { for k, v := range array { buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) } @@ -249,10 +264,10 @@ func (c *Config) AddPath(path string) error { return err } // Repeated path check. - if c.paths.Search(realPath) != -1 { + if c.searchPaths.Search(realPath) != -1 { return nil } - c.paths.Append(realPath) + c.searchPaths.Append(realPath) intlog.Print("AddPath:", realPath) return nil } @@ -262,10 +277,11 @@ func (c *Config) AddPath(path string) error { // If the specified configuration file does not exist, // an empty string is returned. func (c *Config) FilePath(file ...string) (path string) { - name := c.name + name := c.defaultName if len(file) > 0 { name = file[0] } + searchPaths := c.getSearchPaths() // Searching resource manager. if !gres.IsEmpty() { for _, v := range resourceTryFiles { @@ -274,45 +290,37 @@ func (c *Config) FilePath(file ...string) (path string) { return } } - c.paths.RLockFunc(func(array []string) { - for _, prefix := range array { - for _, v := range resourceTryFiles { - if file := gres.Get(prefix + v + name); file != nil { - path = file.Name() - return - } + for _, prefix := range searchPaths { + for _, v := range resourceTryFiles { + if file := gres.Get(prefix + v + name); file != nil { + path = file.Name() + return } } - }) - } - // Already found? - if path != "" { - return + } } // Searching the file system. - c.paths.RLockFunc(func(array []string) { - for _, prefix := range array { - prefix = gstr.TrimRight(prefix, `\/`) - if path, _ = gspath.Search(prefix, name); path != "" { - return - } - if path, _ = gspath.Search(prefix+gfile.Separator+"config", name); path != "" { - return - } + for _, prefix := range searchPaths { + prefix = gstr.TrimRight(prefix, `\/`) + if path, _ = gspath.Search(prefix, name); path != "" { + return } - }) + if path, _ = gspath.Search(prefix+gfile.Separator+"config", name); path != "" { + return + } + } return } // SetFileName sets the default configuration file name. func (c *Config) SetFileName(name string) *Config { - c.name = name + c.defaultName = name return c } // GetFileName returns the default configuration file name. func (c *Config) GetFileName() string { - return c.name + return c.defaultName } // Available checks and returns whether configuration of given is available. @@ -321,7 +329,7 @@ func (c *Config) Available(file ...string) bool { if len(file) > 0 && file[0] != "" { name = file[0] } else { - name = c.name + name = c.defaultName } if c.FilePath(name) != "" { return true @@ -339,9 +347,9 @@ func (c *Config) getJson(file ...string) *gjson.Json { if len(file) > 0 && file[0] != "" { name = file[0] } else { - name = c.name + name = c.defaultName } - r := c.jsons.GetOrSetFuncLock(name, func() interface{} { + r := c.jsonMap.GetOrSetFuncLock(name, func() interface{} { var ( content = "" filePath = "" @@ -372,12 +380,12 @@ func (c *Config) getJson(file ...string) *gjson.Json { j, err = gjson.LoadContent(content, true) } if err == nil { - j.SetViolenceCheck(c.vc) + j.SetViolenceCheck(c.violenceCheck) // Add monitor for this configuration file, // any changes of this file will refresh its cache in Config object. if filePath != "" && !gres.Contains(filePath) { _, err = gfsnotify.Add(filePath, func(event *gfsnotify.Event) { - c.jsons.Remove(name) + c.jsonMap.Remove(name) }) if err != nil && errorPrint() { glog.Error(err) diff --git a/os/gcfg/gcfg_api.go b/os/gcfg/gcfg_api.go index d14dc3fa5..75f80a4d0 100644 --- a/os/gcfg/gcfg_api.go +++ b/os/gcfg/gcfg_api.go @@ -456,7 +456,7 @@ func (c *Config) ToMapToMapsDeep(pointer interface{}, mapping ...map[string]stri // Clear removes all parsed configuration files content cache, // which will force reload configuration content from file. func (c *Config) Clear() { - c.jsons.Clear() + c.jsonMap.Clear() } // Dump prints current Json object with more manually readable. diff --git a/os/gcfg/gcfg_config.go b/os/gcfg/gcfg_config.go index 4d7fabf3d..dbf8aef64 100644 --- a/os/gcfg/gcfg_config.go +++ b/os/gcfg/gcfg_config.go @@ -27,7 +27,7 @@ func SetContent(content string, file ...string) { instances.LockFunc(func(m map[string]interface{}) { if configs.Contains(name) { for _, v := range m { - v.(*Config).jsons.Remove(name) + v.(*Config).jsonMap.Remove(name) } } configs.Set(name, content) @@ -55,7 +55,7 @@ func RemoveContent(file ...string) { instances.LockFunc(func(m map[string]interface{}) { if configs.Contains(name) { for _, v := range m { - v.(*Config).jsons.Remove(name) + v.(*Config).jsonMap.Remove(name) } configs.Remove(name) } @@ -70,7 +70,7 @@ func ClearContent() { // Clear cache for all instances. instances.LockFunc(func(m map[string]interface{}) { for _, v := range m { - v.(*Config).jsons.Clear() + v.(*Config).jsonMap.Clear() } }) diff --git a/os/gcfg/gcfg_error.go b/os/gcfg/gcfg_error.go index 469130675..0a43d87d4 100644 --- a/os/gcfg/gcfg_error.go +++ b/os/gcfg/gcfg_error.go @@ -11,12 +11,12 @@ import ( ) const ( - // gERROR_PRINT_KEY is used to specify the key controlling error printing to stdout. + // errorPrintKey is used to specify the key controlling error printing to stdout. // This error is designed not to be returned by functions. - gERROR_PRINT_KEY = "gf.gcfg.errorprint" + errorPrintKey = "gf.gcfg.errorprint" ) // errorPrint checks whether printing error to stdout. func errorPrint() bool { - return gcmd.GetWithEnv(gERROR_PRINT_KEY, true).Bool() + return gcmd.GetWithEnv(errorPrintKey, true).Bool() } From f2fe0849882aa4d078d7c6b074bdbbbb0badaf2f Mon Sep 17 00:00:00 2001 From: jianchenma Date: Wed, 27 Jan 2021 13:28:12 +0800 Subject: [PATCH 124/492] improve tracing feature --- database/gdb/gdb_core_tracing.go | 7 ++----- database/gredis/gredis_conn.go | 7 ++----- net/gtrace/gtrace.go | 34 ++++++++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index b012f8159..a1757abf6 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -11,6 +11,7 @@ import ( "context" "fmt" "github.com/gogf/gf" + "github.com/gogf/gf/net/gtrace" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/label" @@ -19,11 +20,7 @@ import ( // addSqlToTracing adds sql information to tracer if it's enabled. func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { - if ctx == nil { - return - } - spanCtx := trace.SpanContextFromContext(ctx) - if traceId := spanCtx.TraceID; !traceId.IsValid() { + if gtrace.IsActivated(ctx) { return } diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index bd852ec7b..a133cd0ef 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -13,6 +13,7 @@ import ( "github.com/gogf/gf" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/json" + "github.com/gogf/gf/net/gtrace" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" "github.com/gomodule/redigo/redis" @@ -65,11 +66,7 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} timestampMilli2 := gtime.TimestampMilli() // Tracing. - if c.ctx == nil { - return - } - spanCtx := trace.SpanContextFromContext(c.ctx) - if traceId := spanCtx.TraceID; !traceId.IsValid() { + if gtrace.IsActivated(c.ctx) { return } tr := otel.GetTracerProvider().Tracer( diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index ea30b0e9e..21f3ecd62 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -16,6 +16,11 @@ import ( "go.opentelemetry.io/otel/trace" ) +// IsActivated checks and returns if tracing feature is activated. +func IsActivated(ctx context.Context) bool { + return GetTraceId(ctx) != "" +} + // Tracer is a short function for retrieve Tracer. func Tracer(name ...string) trace.Tracer { tracerName := "" @@ -26,24 +31,46 @@ func Tracer(name ...string) trace.Tracer { } // GetTraceId retrieves and returns TraceId from context. +// It returns an empty string is tracing feature is not activated. func GetTraceId(ctx context.Context) string { - return trace.SpanContextFromContext(ctx).TraceID.String() + if ctx == nil { + return "" + } + traceId := trace.SpanContextFromContext(ctx).TraceID + if traceId.IsValid() { + return traceId.String() + } + return "" } // GetSpanId retrieves and returns SpanId from context. +// It returns an empty string is tracing feature is not activated. func GetSpanId(ctx context.Context) string { - return trace.SpanContextFromContext(ctx).SpanID.String() + if ctx == nil { + return "" + } + spanId := trace.SpanContextFromContext(ctx).SpanID + if spanId.IsValid() { + return spanId.String() + } + return "" } // SetBaggageValue is a convenient function for adding one key-value pair to baggage. // Note that it uses label.Any to set the key-value pair. func SetBaggageValue(ctx context.Context, key string, value interface{}) context.Context { + if ctx == nil { + ctx = context.Background() + } return baggage.ContextWithValues(ctx, label.Any(key, value)) } // SetBaggageMap is a convenient function for adding map key-value pairs to baggage. // Note that it uses label.Any to set the key-value pair. func SetBaggageMap(ctx context.Context, data map[string]interface{}) context.Context { + if ctx == nil { + ctx = context.Background() + } pairs := make([]label.KeyValue, 0) for k, v := range data { pairs = append(pairs, label.Any(k, v)) @@ -53,6 +80,9 @@ func SetBaggageMap(ctx context.Context, data map[string]interface{}) context.Con // GetBaggageVar retrieves value and returns a *gvar.Var for specified key from baggage. func GetBaggageVar(ctx context.Context, key string) *gvar.Var { + if ctx == nil { + return gvar.New(nil) + } value := baggage.Value(ctx, label.Key(key)) return gvar.New(value.AsInterface()) } From 8f6f17c341720f73281656df883c741955554de6 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 27 Jan 2021 19:15:14 +0800 Subject: [PATCH 125/492] add common tracing labels --- database/gdb/gdb_core_tracing.go | 7 +++++-- database/gredis/gredis_conn.go | 1 + net/ghttp/ghttp_middleware_tracing.go | 3 +++ net/ghttp/internal/client/client_tracing.go | 3 +++ net/gtrace/gtrace.go | 16 ++++++++++++++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index a1757abf6..484c76838 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -23,18 +23,21 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { if gtrace.IsActivated(ctx) { return } - tr := otel.GetTracerProvider().Tracer( "github.com/gogf/gf/database/gdb", trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), ) ctx, span := tr.Start(ctx, sql.Type) defer span.End() + if sql.Error != nil { span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, sql.Error)) } labels := make([]label.KeyValue, 0) - labels = append(labels, label.String("db.type", c.DB.GetConfig().Type)) + labels = append(labels, gtrace.CommonLabels()...) + labels = append(labels, + label.String("db.type", c.DB.GetConfig().Type), + ) if c.DB.GetConfig().Host != "" { labels = append(labels, label.String("db.host", c.DB.GetConfig().Host)) } diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index a133cd0ef..aaafd44c5 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -82,6 +82,7 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} if err != nil { span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) } + span.SetAttributes(gtrace.CommonLabels()...) span.SetAttributes( label.String("redis.host", c.redis.config.Host), label.Int("redis.port", c.redis.config.Port), diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index 3460f46ee..afb8d38a4 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -12,6 +12,7 @@ import ( "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/net/ghttp/internal/client" "github.com/gogf/gf/net/ghttp/internal/httputil" + "github.com/gogf/gf/net/gtrace" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/label" @@ -45,6 +46,8 @@ func MiddlewareServerTracing(r *Request) { ctx, span := tr.Start(ctx, r.URL.String()) defer span.End() + span.SetAttributes(gtrace.CommonLabels()...) + // Inject tracing context. r.SetCtx(ctx) diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index ffe35141d..516f0396f 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -11,6 +11,7 @@ import ( "github.com/gogf/gf" "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/net/ghttp/internal/httputil" + "github.com/gogf/gf/net/gtrace" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/label" @@ -34,6 +35,8 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro ctx, span := tr.Start(r.Context(), r.URL.String()) defer span.End() + span.SetAttributes(gtrace.CommonLabels()...) + // Inject tracing content into http header. propagator := propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index 21f3ecd62..b4dac689d 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -10,10 +10,17 @@ package gtrace import ( "context" "github.com/gogf/gf/container/gvar" + "github.com/gogf/gf/net/gipv4" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" + "os" +) + +var ( + intranetIps, _ = gipv4.GetIntranetIpArray() + hostname, _ = os.Hostname() ) // IsActivated checks and returns if tracing feature is activated. @@ -21,6 +28,15 @@ func IsActivated(ctx context.Context) bool { return GetTraceId(ctx) != "" } +// CommonLabels returns common used attribute labels: +// ip.intranet, hostname. +func CommonLabels() []label.KeyValue { + return []label.KeyValue{ + label.Array(`ip.intranet`, intranetIps), + label.String(`hostname`, hostname), + } +} + // Tracer is a short function for retrieve Tracer. func Tracer(name ...string) trace.Tracer { tracerName := "" From 152c472bc215665746e284b71f55e7f7584f4ba5 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 27 Jan 2021 19:24:04 +0800 Subject: [PATCH 126/492] add common tracing labels --- net/gtrace/gtrace.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index b4dac689d..c7964afdf 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -11,6 +11,7 @@ import ( "context" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/net/gipv4" + "github.com/gogf/gf/text/gstr" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/label" @@ -32,7 +33,7 @@ func IsActivated(ctx context.Context) bool { // ip.intranet, hostname. func CommonLabels() []label.KeyValue { return []label.KeyValue{ - label.Array(`ip.intranet`, intranetIps), + label.String(`ip.intranet`, gstr.Join(intranetIps, ",")), label.String(`hostname`, hostname), } } From 2c15aad0e7c421a892dd914e3fd0b9319b22f048 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 27 Jan 2021 19:50:32 +0800 Subject: [PATCH 127/492] add common tracing labels --- database/gdb/gdb_core_tracing.go | 4 ++-- database/gredis/gredis_conn.go | 4 ++-- net/ghttp/ghttp_middleware_tracing.go | 9 ++++++--- net/ghttp/internal/client/client_tracing.go | 7 +++++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index 484c76838..b0d3b0c1a 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -20,14 +20,14 @@ import ( // addSqlToTracing adds sql information to tracer if it's enabled. func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { - if gtrace.IsActivated(ctx) { + if !gtrace.IsActivated(ctx) { return } tr := otel.GetTracerProvider().Tracer( "github.com/gogf/gf/database/gdb", trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), ) - ctx, span := tr.Start(ctx, sql.Type) + ctx, span := tr.Start(ctx, sql.Type, trace.WithSpanKind(trace.SpanKindInternal)) defer span.End() if sql.Error != nil { diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index aaafd44c5..1162599ac 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -66,7 +66,7 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} timestampMilli2 := gtime.TimestampMilli() // Tracing. - if gtrace.IsActivated(c.ctx) { + if !gtrace.IsActivated(c.ctx) { return } tr := otel.GetTracerProvider().Tracer( @@ -77,7 +77,7 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} if ctx == nil { ctx = context.Background() } - _, span := tr.Start(ctx, "Redis."+commandName) + _, span := tr.Start(ctx, "Redis."+commandName, trace.WithSpanKind(trace.SpanKindInternal)) defer span.End() if err != nil { span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index afb8d38a4..8cc26cd3f 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -43,7 +43,7 @@ func MiddlewareServerTracing(r *Request) { propagation.Baggage{}, ) ctx := propagator.Extract(r.Context(), r.Header) - ctx, span := tr.Start(ctx, r.URL.String()) + ctx, span := tr.Start(ctx, r.URL.String(), trace.WithSpanKind(trace.SpanKindServer)) defer span.End() span.SetAttributes(gtrace.CommonLabels()...) @@ -59,7 +59,7 @@ func MiddlewareServerTracing(r *Request) { reqBodyContent = string(reqBodyContentBytes) } else { reqBodyContent = fmt.Sprintf( - "[Request Body Too Large For Logging, Max: %d bytes]", + "[Request Body Too Large For Tracing, Max: %d bytes]", tracingMaxContentLogSize, ) } @@ -80,7 +80,10 @@ func MiddlewareServerTracing(r *Request) { if r.Response.BufferLength() <= tracingMaxContentLogSize { resBodyContent = r.Response.BufferString() } else { - resBodyContent = fmt.Sprintf("[Response Body Too Large For Logging, Max: %d bytes]", tracingMaxContentLogSize) + resBodyContent = fmt.Sprintf( + "[Response Body Too Large For Tracing, Max: %d bytes]", + tracingMaxContentLogSize, + ) } span.AddEvent("http.response", trace.WithAttributes( label.Any(`http.response.headers`, httputil.HeaderToMap(r.Response.Header())), diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index 516f0396f..274bfce2f 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -32,7 +32,7 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro "github.com/gogf/gf/net/ghttp.Client", trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), ) - ctx, span := tr.Start(r.Context(), r.URL.String()) + ctx, span := tr.Start(r.Context(), r.URL.String(), trace.WithSpanKind(trace.SpanKindClient)) defer span.End() span.SetAttributes(gtrace.CommonLabels()...) @@ -64,7 +64,10 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro resBodyContent = string(reqBodyContentBytes) response.Body = utils.NewReadCloser(reqBodyContentBytes, false) } else { - resBodyContent = fmt.Sprintf("[Response Body Too Large For Logging, Max: %d bytes]", tracingMaxContentLogSize) + resBodyContent = fmt.Sprintf( + "[Response Body Too Large For Tracing, Max: %d bytes]", + tracingMaxContentLogSize, + ) } span.AddEvent("http.response", trace.WithAttributes( From 6e4e1abf1e2d9de9fd919742e93d263d7d419aba Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 27 Jan 2021 20:25:57 +0800 Subject: [PATCH 128/492] improve tracing by adding constants for attribute and event names --- database/gdb/gdb_core_tracing.go | 36 ++++++---- database/gredis/gredis_conn.go | 40 ++--------- database/gredis/gredis_conn_tracing.go | 66 +++++++++++++++++++ net/ghttp/ghttp_middleware_tracing.go | 20 ++++-- net/ghttp/internal/client/client_tracing.go | 20 ++++-- .../internal/client/client_tracing_tracer.go | 23 ++++--- 6 files changed, 140 insertions(+), 65 deletions(-) create mode 100644 database/gredis/gredis_conn_tracing.go diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index b0d3b0c1a..a538c2e39 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -18,6 +18,20 @@ import ( "go.opentelemetry.io/otel/trace" ) +const ( + tracingAttrDbType = "db.type" + tracingAttrDbHost = "db.host" + tracingAttrDbPort = "db.port" + tracingAttrDbName = "db.name" + tracingAttrDbUser = "db.user" + tracingAttrDbLink = "db.link" + tracingAttrDbGroup = "db.group" + tracingEventDbExecution = "db.execution" + tracingEventDbExecutionSql = "db.execution.sql" + tracingEventDbExecutionCost = "db.execution.cost" + tracingEventDbExecutionType = "db.execution.type" +) + // addSqlToTracing adds sql information to tracer if it's enabled. func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { if !gtrace.IsActivated(ctx) { @@ -36,30 +50,30 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { labels := make([]label.KeyValue, 0) labels = append(labels, gtrace.CommonLabels()...) labels = append(labels, - label.String("db.type", c.DB.GetConfig().Type), + label.String(tracingAttrDbType, c.DB.GetConfig().Type), ) if c.DB.GetConfig().Host != "" { - labels = append(labels, label.String("db.host", c.DB.GetConfig().Host)) + labels = append(labels, label.String(tracingAttrDbHost, c.DB.GetConfig().Host)) } if c.DB.GetConfig().Port != "" { - labels = append(labels, label.String("db.port", c.DB.GetConfig().Port)) + labels = append(labels, label.String(tracingAttrDbPort, c.DB.GetConfig().Port)) } if c.DB.GetConfig().Name != "" { - labels = append(labels, label.String("db.name", c.DB.GetConfig().Name)) + labels = append(labels, label.String(tracingAttrDbName, c.DB.GetConfig().Name)) } if c.DB.GetConfig().User != "" { - labels = append(labels, label.String("db.user", c.DB.GetConfig().User)) + labels = append(labels, label.String(tracingAttrDbUser, c.DB.GetConfig().User)) } if filteredLinkInfo := c.DB.FilteredLinkInfo(); filteredLinkInfo != "" { - labels = append(labels, label.String("db.link", c.DB.FilteredLinkInfo())) + labels = append(labels, label.String(tracingAttrDbLink, c.DB.FilteredLinkInfo())) } if group := c.DB.GetGroup(); group != "" { - labels = append(labels, label.String("db.group", group)) + labels = append(labels, label.String(tracingAttrDbGroup, group)) } span.SetAttributes(labels...) - span.AddEvent("db.execution", trace.WithAttributes( - label.String(`db.execution.sql`, sql.Format), - label.String(`db.execution.cost`, fmt.Sprintf(`%d ms`, sql.End-sql.Start)), - label.String(`db.execution.type`, sql.Type), + span.AddEvent(tracingEventDbExecution, trace.WithAttributes( + label.String(tracingEventDbExecutionSql, sql.Format), + label.String(tracingEventDbExecutionCost, fmt.Sprintf(`%d ms`, sql.End-sql.Start)), + label.String(tracingEventDbExecutionType, sql.Type), )) } diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index 1162599ac..26cc1ad6a 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -9,18 +9,12 @@ package gredis import ( "context" "errors" - "fmt" - "github.com/gogf/gf" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/net/gtrace" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" "github.com/gomodule/redigo/redis" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" - "go.opentelemetry.io/otel/trace" "reflect" "time" ) @@ -66,34 +60,14 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} timestampMilli2 := gtime.TimestampMilli() // Tracing. - if !gtrace.IsActivated(c.ctx) { - return + if gtrace.IsActivated(c.ctx) { + c.addTracingItem(&tracingItem{ + err: err, + commandName: commandName, + arguments: args, + costMilli: timestampMilli2 - timestampMilli1, + }) } - tr := otel.GetTracerProvider().Tracer( - "github.com/gogf/gf/database/gredis", - trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), - ) - ctx := c.ctx - if ctx == nil { - ctx = context.Background() - } - _, span := tr.Start(ctx, "Redis."+commandName, trace.WithSpanKind(trace.SpanKindInternal)) - defer span.End() - if err != nil { - span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) - } - span.SetAttributes(gtrace.CommonLabels()...) - span.SetAttributes( - label.String("redis.host", c.redis.config.Host), - label.Int("redis.port", c.redis.config.Port), - label.Int("redis.db", c.redis.config.Db), - ) - jsonBytes, _ := json.Marshal(args) - span.AddEvent("redis.execution", trace.WithAttributes( - label.String(`redis.execution.command`, commandName), - label.String(`redis.execution.cost`, fmt.Sprintf(`%d ms`, timestampMilli2-timestampMilli1)), - label.String(`redis.execution.arguments`, string(jsonBytes)), - )) return } diff --git a/database/gredis/gredis_conn_tracing.go b/database/gredis/gredis_conn_tracing.go new file mode 100644 index 000000000..d8406ff5e --- /dev/null +++ b/database/gredis/gredis_conn_tracing.go @@ -0,0 +1,66 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gredis + +import ( + "context" + "fmt" + "github.com/gogf/gf" + "github.com/gogf/gf/internal/json" + "github.com/gogf/gf/net/gtrace" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/trace" +) + +// tracingItem holds the information for redis tracing. +type tracingItem struct { + err error + commandName string + arguments []interface{} + costMilli int64 +} + +const ( + tracingAttrRedisHost = "redis.host" + tracingAttrRedisPort = "redis.port" + tracingAttrRedisDb = "redis.db" + tracingEventRedisExecution = "redis.execution" + tracingEventRedisExecutionCommand = "redis.execution.command" + tracingEventRedisExecutionCost = "redis.execution.cost" + tracingEventRedisExecutionArguments = "redis.execution.arguments" +) + +// addTracingItem checks and adds redis tracing information to OpenTelemetry. +func (c *Conn) addTracingItem(item *tracingItem) { + tr := otel.GetTracerProvider().Tracer( + "github.com/gogf/gf/database/gredis", + trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), + ) + ctx := c.ctx + if ctx == nil { + ctx = context.Background() + } + _, span := tr.Start(ctx, "Redis."+item.commandName, trace.WithSpanKind(trace.SpanKindInternal)) + defer span.End() + if item.err != nil { + span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, item.err)) + } + span.SetAttributes(gtrace.CommonLabels()...) + span.SetAttributes( + label.String(tracingAttrRedisHost, c.redis.config.Host), + label.Int(tracingAttrRedisPort, c.redis.config.Port), + label.Int(tracingAttrRedisDb, c.redis.config.Db), + ) + jsonBytes, _ := json.Marshal(item.arguments) + span.AddEvent(tracingEventRedisExecution, trace.WithAttributes( + label.String(tracingEventRedisExecutionCommand, item.commandName), + label.String(tracingEventRedisExecutionCost, fmt.Sprintf(`%d ms`, item.costMilli)), + label.String(tracingEventRedisExecutionArguments, string(jsonBytes)), + )) +} diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index 8cc26cd3f..5ff2c2e49 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -23,7 +23,13 @@ import ( ) const ( - tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. + tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. + tracingEventHttpRequest = "http.request" + tracingEventHttpRequestHeaders = "http.request.headers" + tracingEventHttpRequestBody = "http.request.body" + tracingEventHttpResponse = "http.response" + tracingEventHttpResponseHeaders = "http.response.headers" + tracingEventHttpResponseBody = "http.response.body" ) // MiddlewareClientTracing is a client middleware that enables tracing feature using standards of OpenTelemetry. @@ -63,9 +69,9 @@ func MiddlewareServerTracing(r *Request) { tracingMaxContentLogSize, ) } - span.AddEvent("http.request", trace.WithAttributes( - label.Any(`http.request.headers`, httputil.HeaderToMap(r.Header)), - label.String(`http.request.body`, reqBodyContent), + span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( + label.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)), + label.String(tracingEventHttpRequestBody, reqBodyContent), )) // Continue executing. @@ -85,9 +91,9 @@ func MiddlewareServerTracing(r *Request) { tracingMaxContentLogSize, ) } - span.AddEvent("http.response", trace.WithAttributes( - label.Any(`http.response.headers`, httputil.HeaderToMap(r.Response.Header())), - label.String(`http.response.body`, resBodyContent), + span.AddEvent(tracingEventHttpResponse, trace.WithAttributes( + label.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(r.Response.Header())), + label.String(tracingEventHttpResponseBody, resBodyContent), )) return } diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index 274bfce2f..c7b8c2393 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -23,7 +23,19 @@ import ( ) const ( - tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. + tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. + tracingAttrHttpAddressRemote = "http.address.remote" + tracingAttrHttpAddressLocal = "http.address.local" + tracingAttrHttpDnsStart = "http.dns.start" + tracingAttrHttpDnsDone = "http.dns.done" + tracingAttrHttpConnectStart = "http.connect.start" + tracingAttrHttpConnectDone = "http.connect.done" + tracingEventHttpRequest = "http.request" + tracingEventHttpRequestHeaders = "http.request.headers" + tracingEventHttpRequestBody = "http.request.body" + tracingEventHttpResponse = "http.response" + tracingEventHttpResponseHeaders = "http.response.headers" + tracingEventHttpResponseBody = "http.response.body" ) // MiddlewareTracing is a client middleware that enables tracing feature using standards of OpenTelemetry. @@ -70,9 +82,9 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro ) } - span.AddEvent("http.response", trace.WithAttributes( - label.Any(`http.response.headers`, httputil.HeaderToMap(response.Header)), - label.String(`http.response.body`, resBodyContent), + span.AddEvent(tracingEventHttpResponse, trace.WithAttributes( + label.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(response.Header)), + label.String(tracingEventHttpResponseBody, resBodyContent), )) return } diff --git a/net/ghttp/internal/client/client_tracing_tracer.go b/net/ghttp/internal/client/client_tracing_tracer.go index bf833c069..9279f5593 100644 --- a/net/ghttp/internal/client/client_tracing_tracer.go +++ b/net/ghttp/internal/client/client_tracing_tracer.go @@ -69,8 +69,8 @@ func (ct *clientTracer) getConn(host string) { func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) { ct.span.SetAttributes( - label.String("http.connection.remote", info.Conn.RemoteAddr().String()), - label.String("http.connection.local", info.Conn.LocalAddr().String()), + label.String(tracingAttrHttpAddressRemote, info.Conn.RemoteAddr().String()), + label.String(tracingAttrHttpAddressLocal, info.Conn.LocalAddr().String()), ) } @@ -82,7 +82,7 @@ func (ct *clientTracer) putIdleConn(err error) { func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) { ct.span.SetAttributes( - label.String("http.dns.start", info.Host), + label.String(tracingAttrHttpDnsStart, info.Host), ) } @@ -98,13 +98,13 @@ func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) { ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) } ct.span.SetAttributes( - label.String("http.dns.done", buffer.String()), + label.String(tracingAttrHttpDnsDone, buffer.String()), ) } func (ct *clientTracer) connectStart(network, addr string) { ct.span.SetAttributes( - label.String("http.connect.start", network+"@"+addr), + label.String(tracingAttrHttpConnectStart, network+"@"+addr), ) } @@ -113,7 +113,7 @@ func (ct *clientTracer) connectDone(network, addr string, err error) { ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) } ct.span.SetAttributes( - label.String("http.connect.done", network+"@"+addr), + label.String(tracingAttrHttpConnectDone, network+"@"+addr), ) } @@ -147,11 +147,14 @@ func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { if ct.request.ContentLength <= tracingMaxContentLogSize { bodyContent = string(ct.requestBody) } else { - bodyContent = fmt.Sprintf("[Request Body Too Large For Logging, Max: %d bytes]", tracingMaxContentLogSize) + bodyContent = fmt.Sprintf( + "[Request Body Too Large For Logging, Max: %d bytes]", + tracingMaxContentLogSize, + ) } - ct.span.AddEvent("http.request", trace.WithAttributes( - label.Any(`http.request.headers`, ct.headers), - label.String(`http.request.body`, bodyContent), + ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( + label.Any(tracingEventHttpRequestHeaders, ct.headers), + label.String(tracingEventHttpRequestBody, bodyContent), )) } From 6307af1096ac0e462a093d48d24a90ded3aa8700 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 27 Jan 2021 21:02:39 +0800 Subject: [PATCH 129/492] travis updates --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8bcf0f4ec..96d41718a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ +os: linux +arch: arm64-graviton2 + language: go go: From 69e1628a0dcff238d28ec21f6e8524bf427b7b37 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 27 Jan 2021 23:08:43 +0800 Subject: [PATCH 130/492] improve unit testing case for package gtimer --- os/gtimer/gtimer_z_unit_timer_internal_test.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/os/gtimer/gtimer_z_unit_timer_internal_test.go b/os/gtimer/gtimer_z_unit_timer_internal_test.go index 3953cf4d3..5d144fb9a 100644 --- a/os/gtimer/gtimer_z_unit_timer_internal_test.go +++ b/os/gtimer/gtimer_z_unit_timer_internal_test.go @@ -7,6 +7,7 @@ package gtimer import ( + "github.com/gogf/gf/container/garray" "github.com/gogf/gf/container/gtype" "github.com/gogf/gf/test/gtest" "testing" @@ -16,34 +17,34 @@ import ( func TestTimer_Proceed(t *testing.T) { gtest.C(t, func(t *gtest.T) { index := gtype.NewInt() - slice := make([]int, 0) + array := garray.New(true) timer := doNewWithoutAutoStart(10, 60*time.Millisecond, 6) timer.nowFunc = func() time.Time { return time.Now().Add(time.Duration(index.Add(1)) * time.Millisecond * 60) } timer.AddOnce(2*time.Second, func() { - slice = append(slice, 1) + array.Append(1) }) timer.AddOnce(1*time.Minute, func() { - slice = append(slice, 2) + array.Append(2) }) timer.AddOnce(5*time.Minute, func() { - slice = append(slice, 3) + array.Append(3) }) timer.AddOnce(1*time.Hour, func() { - slice = append(slice, 4) + array.Append(4) }) timer.AddOnce(100*time.Minute, func() { - slice = append(slice, 5) + array.Append(5) }) timer.AddOnce(2*time.Hour, func() { - slice = append(slice, 6) + array.Append(6) }) for i := 0; i < 500000; i++ { timer.wheels[0].proceed() time.Sleep(10 * time.Microsecond) } time.Sleep(time.Second) - t.Assert(slice, []int{1, 2, 3, 4, 5, 6}) + t.Assert(array.Slice(), []int{1, 2, 3, 4, 5, 6}) }) } From a2b322a31bb2bdbab1228d2dbcd67071b7bb554c Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 27 Jan 2021 23:37:04 +0800 Subject: [PATCH 131/492] improve gtime.ParseTimeFromContent --- .../server/router/duplicated/duplicated.go | 20 +++++++++++++++++++ os/gtime/gtime_format.go | 1 + os/gtime/gtime_z_unit_basic_test.go | 5 +++++ 3 files changed, 26 insertions(+) create mode 100644 .example/net/ghttp/server/router/duplicated/duplicated.go diff --git a/.example/net/ghttp/server/router/duplicated/duplicated.go b/.example/net/ghttp/server/router/duplicated/duplicated.go new file mode 100644 index 000000000..9402a2a4d --- /dev/null +++ b/.example/net/ghttp/server/router/duplicated/duplicated.go @@ -0,0 +1,20 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" +) + +func main() { + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.ALL("/test", func(r *ghttp.Request) { + r.Response.Writeln(1) + }) + group.ALL("/test", func(r *ghttp.Request) { + r.Response.Writeln(2) + }) + }) + s.SetPort(8199) + s.Run() +} diff --git a/os/gtime/gtime_format.go b/os/gtime/gtime_format.go index e1681cc60..9b87c12b5 100644 --- a/os/gtime/gtime_format.go +++ b/os/gtime/gtime_format.go @@ -259,6 +259,7 @@ func formatToRegexPattern(format string) string { s := gregex.Quote(formatToStdLayout(format)) s, _ = gregex.ReplaceString(`[0-9]`, `[0-9]`, s) s, _ = gregex.ReplaceString(`[A-Za-z]`, `[A-Za-z]`, s) + s, _ = gregex.ReplaceString(`\s+`, `\s+`, s) return s } diff --git a/os/gtime/gtime_z_unit_basic_test.go b/os/gtime/gtime_z_unit_basic_test.go index c4e8b2c26..34ef93642 100644 --- a/os/gtime/gtime_z_unit_basic_test.go +++ b/os/gtime/gtime_z_unit_basic_test.go @@ -275,6 +275,11 @@ func Test_ParseTimeFromContent(t *testing.T) { t.Error("test fail") } }) + + gtest.C(t, func(t *gtest.T) { + timeStr := "2021-1-27 9:10:24" + t.Assert(gtime.ParseTimeFromContent(timeStr, "Y-n-d g:i:s").String(), "2021-01-27 09:10:24") + }) } func Test_FuncCost(t *testing.T) { From 9fb6227461617f95a791cf1c15b5ecd2ba25f01d Mon Sep 17 00:00:00 2001 From: jianchenma Date: Thu, 28 Jan 2021 10:08:52 +0800 Subject: [PATCH 132/492] fix issue in signal handler for windows --- net/ghttp/ghttp_server_admin_windows.go | 2 +- version.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ghttp/ghttp_server_admin_windows.go b/net/ghttp/ghttp_server_admin_windows.go index 005efdf6e..bf914ad14 100644 --- a/net/ghttp/ghttp_server_admin_windows.go +++ b/net/ghttp/ghttp_server_admin_windows.go @@ -9,6 +9,6 @@ package ghttp // registerSignalHandler does nothing on windows platform. -func registerSignalHandler() { +func handleProcessSignal() { } diff --git a/version.go b/version.go index a8db58433..14f5713de 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.15.2" +const VERSION = "v1.15.3" const AUTHORS = "john" From 2451b40d3ebaad3dd028d4ab7fdc4dfa81448313 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Thu, 28 Jan 2021 13:11:09 +0800 Subject: [PATCH 133/492] improve package gtrace --- net/gtrace/gtrace.go | 46 ++++++++-------------- net/gtrace/gtrace_baggage.go | 75 ++++++++++++++++++++++++++++++++++++ net/gtrace/gtrace_span.go | 24 ++++++++++++ net/gtrace/gtrace_tracer.go | 27 +++++++++++++ 4 files changed, 142 insertions(+), 30 deletions(-) create mode 100644 net/gtrace/gtrace_baggage.go create mode 100644 net/gtrace/gtrace_span.go create mode 100644 net/gtrace/gtrace_tracer.go diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index c7964afdf..460ce4df0 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -9,16 +9,20 @@ package gtrace import ( "context" + "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/net/gipv4" "github.com/gogf/gf/text/gstr" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" "os" ) +const ( + tracingCommonKeyIpIntranet = `ip.intranet` + tracingCommonKeyIpHostname = `hostname` +) + var ( intranetIps, _ = gipv4.GetIntranetIpArray() hostname, _ = os.Hostname() @@ -33,20 +37,11 @@ func IsActivated(ctx context.Context) bool { // ip.intranet, hostname. func CommonLabels() []label.KeyValue { return []label.KeyValue{ - label.String(`ip.intranet`, gstr.Join(intranetIps, ",")), - label.String(`hostname`, hostname), + label.String(tracingCommonKeyIpIntranet, gstr.Join(intranetIps, ",")), + label.String(tracingCommonKeyIpHostname, hostname), } } -// Tracer is a short function for retrieve Tracer. -func Tracer(name ...string) trace.Tracer { - tracerName := "" - if len(name) > 0 { - tracerName = name[0] - } - return otel.Tracer(tracerName) -} - // GetTraceId retrieves and returns TraceId from context. // It returns an empty string is tracing feature is not activated. func GetTraceId(ctx context.Context) string { @@ -76,30 +71,21 @@ func GetSpanId(ctx context.Context) string { // SetBaggageValue is a convenient function for adding one key-value pair to baggage. // Note that it uses label.Any to set the key-value pair. func SetBaggageValue(ctx context.Context, key string, value interface{}) context.Context { - if ctx == nil { - ctx = context.Background() - } - return baggage.ContextWithValues(ctx, label.Any(key, value)) + return NewBaggage(ctx).SetValue(key, value) } // SetBaggageMap is a convenient function for adding map key-value pairs to baggage. // Note that it uses label.Any to set the key-value pair. func SetBaggageMap(ctx context.Context, data map[string]interface{}) context.Context { - if ctx == nil { - ctx = context.Background() - } - pairs := make([]label.KeyValue, 0) - for k, v := range data { - pairs = append(pairs, label.Any(k, v)) - } - return baggage.ContextWithValues(ctx, pairs...) + return NewBaggage(ctx).SetMap(data) +} + +// GetBaggageMap retrieves and returns the baggage values as map. +func GetBaggageMap(ctx context.Context) *gmap.StrAnyMap { + return NewBaggage(ctx).GetMap() } // GetBaggageVar retrieves value and returns a *gvar.Var for specified key from baggage. func GetBaggageVar(ctx context.Context, key string) *gvar.Var { - if ctx == nil { - return gvar.New(nil) - } - value := baggage.Value(ctx, label.Key(key)) - return gvar.New(value.AsInterface()) + return NewBaggage(ctx).GetVar(key) } diff --git a/net/gtrace/gtrace_baggage.go b/net/gtrace/gtrace_baggage.go new file mode 100644 index 000000000..f06aaa9c7 --- /dev/null +++ b/net/gtrace/gtrace_baggage.go @@ -0,0 +1,75 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtrace + +import ( + "context" + "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/container/gvar" + "go.opentelemetry.io/otel/baggage" + "go.opentelemetry.io/otel/label" +) + +// Baggage holds the data through all tracing spans. +type Baggage struct { + ctx context.Context +} + +// NewBaggage creates and returns a new Baggage object from given tracing context. +func NewBaggage(ctx context.Context) *Baggage { + if ctx == nil { + ctx = context.Background() + } + return &Baggage{ + ctx: ctx, + } +} + +// Ctx returns the context that Baggage holds. +func (b *Baggage) Ctx() context.Context { + return b.ctx +} + +// SetValue is a convenient function for adding one key-value pair to baggage. +// Note that it uses label.Any to set the key-value pair. +func (b *Baggage) SetValue(key string, value interface{}) context.Context { + b.ctx = baggage.ContextWithValues(b.ctx, label.Any(key, value)) + return b.ctx +} + +// SetMap is a convenient function for adding map key-value pairs to baggage. +// Note that it uses label.Any to set the key-value pair. +func (b *Baggage) SetMap(data map[string]interface{}) context.Context { + pairs := make([]label.KeyValue, 0) + for k, v := range data { + pairs = append(pairs, label.Any(k, v)) + } + b.ctx = baggage.ContextWithValues(b.ctx, pairs...) + return b.ctx +} + +// GetMap retrieves and returns the baggage values as map. +func (b *Baggage) GetMap() *gmap.StrAnyMap { + m := gmap.NewStrAnyMap() + set := baggage.Set(b.ctx) + if length := set.Len(); length > 0 { + if length == 0 { + return m + } + inter := set.Iter() + for inter.Next() { + m.Set(string(inter.Label().Key), inter.Label().Value.AsInterface()) + } + } + return m +} + +// GetVar retrieves value and returns a *gvar.Var for specified key from baggage. +func (b *Baggage) GetVar(key string) *gvar.Var { + value := baggage.Value(b.ctx, label.Key(key)) + return gvar.New(value.AsInterface()) +} diff --git a/net/gtrace/gtrace_span.go b/net/gtrace/gtrace_span.go new file mode 100644 index 000000000..f9163a60f --- /dev/null +++ b/net/gtrace/gtrace_span.go @@ -0,0 +1,24 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtrace + +import ( + "context" + "go.opentelemetry.io/otel/trace" +) + +type Span struct { + trace.Span +} + +// NewSpan creates a span using default tracer. +func NewSpan(ctx context.Context, spanName string, opts ...trace.SpanOption) (context.Context, *Span) { + ctx, span := NewTracer().Start(ctx, spanName, opts...) + return ctx, &Span{ + Span: span, + } +} diff --git a/net/gtrace/gtrace_tracer.go b/net/gtrace/gtrace_tracer.go new file mode 100644 index 000000000..8394fcebf --- /dev/null +++ b/net/gtrace/gtrace_tracer.go @@ -0,0 +1,27 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtrace + +import ( + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" +) + +type Tracer struct { + trace.Tracer +} + +// Tracer is a short function for retrieving Tracer. +func NewTracer(name ...string) *Tracer { + tracerName := "" + if len(name) > 0 { + tracerName = name[0] + } + return &Tracer{ + Tracer: otel.Tracer(tracerName), + } +} From 2734903886a7537194522cd4011ca369c027b001 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Thu, 28 Jan 2021 13:51:23 +0800 Subject: [PATCH 134/492] tracing baggage --- net/ghttp/ghttp_middleware_tracing.go | 2 ++ net/ghttp/internal/client/client_tracing.go | 1 + net/ghttp/internal/client/client_tracing_tracer.go | 2 ++ 3 files changed, 5 insertions(+) diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index 5ff2c2e49..67cbcfa73 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -26,6 +26,7 @@ const ( tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. tracingEventHttpRequest = "http.request" tracingEventHttpRequestHeaders = "http.request.headers" + tracingEventHttpRequestBaggage = "http.request.baggage" tracingEventHttpRequestBody = "http.request.body" tracingEventHttpResponse = "http.response" tracingEventHttpResponseHeaders = "http.response.headers" @@ -71,6 +72,7 @@ func MiddlewareServerTracing(r *Request) { } span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( label.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)), + label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx).Map()), label.String(tracingEventHttpRequestBody, reqBodyContent), )) diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index c7b8c2393..25c71b674 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -32,6 +32,7 @@ const ( tracingAttrHttpConnectDone = "http.connect.done" tracingEventHttpRequest = "http.request" tracingEventHttpRequestHeaders = "http.request.headers" + tracingEventHttpRequestBaggage = "http.request.baggage" tracingEventHttpRequestBody = "http.request.body" tracingEventHttpResponse = "http.response" tracingEventHttpResponseHeaders = "http.response.headers" diff --git a/net/ghttp/internal/client/client_tracing_tracer.go b/net/ghttp/internal/client/client_tracing_tracer.go index 9279f5593..b97b4c971 100644 --- a/net/ghttp/internal/client/client_tracing_tracer.go +++ b/net/ghttp/internal/client/client_tracing_tracer.go @@ -11,6 +11,7 @@ import ( "crypto/tls" "fmt" "github.com/gogf/gf/internal/utils" + "github.com/gogf/gf/net/gtrace" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" @@ -154,6 +155,7 @@ func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { } ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( label.Any(tracingEventHttpRequestHeaders, ct.headers), + label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context).Map()), label.String(tracingEventHttpRequestBody, bodyContent), )) } From 80248e9a6e373a879034ca687ee7f5d657ae8e68 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Thu, 28 Jan 2021 14:09:13 +0800 Subject: [PATCH 135/492] improve tracing --- container/gtree/gtree.go | 1 - net/ghttp/ghttp_middleware_tracing.go | 2 +- net/ghttp/internal/client/client_tracing_tracer.go | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/container/gtree/gtree.go b/container/gtree/gtree.go index 8fb5c28ad..2cb7b25ee 100644 --- a/container/gtree/gtree.go +++ b/container/gtree/gtree.go @@ -7,5 +7,4 @@ // Package gtree provides concurrent-safe/unsafe tree containers. // // Some implements are from: https://github.com/emirpasic/gods -// Thanks! package gtree diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index 67cbcfa73..b0cb846e5 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -72,7 +72,7 @@ func MiddlewareServerTracing(r *Request) { } span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( label.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)), - label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx).Map()), + label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx)), label.String(tracingEventHttpRequestBody, reqBodyContent), )) diff --git a/net/ghttp/internal/client/client_tracing_tracer.go b/net/ghttp/internal/client/client_tracing_tracer.go index b97b4c971..694db4db2 100644 --- a/net/ghttp/internal/client/client_tracing_tracer.go +++ b/net/ghttp/internal/client/client_tracing_tracer.go @@ -155,7 +155,7 @@ func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { } ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( label.Any(tracingEventHttpRequestHeaders, ct.headers), - label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context).Map()), + label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context)), label.String(tracingEventHttpRequestBody, bodyContent), )) } From 8bd24724e7a73a45a8eb07c58711679db9896f46 Mon Sep 17 00:00:00 2001 From: tangjoin <396344656@qq.com> Date: Sat, 30 Jan 2021 22:10:06 +0800 Subject: [PATCH 136/492] Update client_request.go --- net/ghttp/internal/client/client_request.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ghttp/internal/client/client_request.go b/net/ghttp/internal/client/client_request.go index 74f46675f..f74ec6042 100644 --- a/net/ghttp/internal/client/client_request.go +++ b/net/ghttp/internal/client/client_request.go @@ -251,6 +251,10 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h } else { req = req.WithContext(context.Background()) } + // Client agent. + if c.agent != "" { + req.Header.Set("User-Agent", c.agent) + } // Custom header. if len(c.header) > 0 { for k, v := range c.header { @@ -279,10 +283,6 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h if len(c.authUser) > 0 { req.SetBasicAuth(c.authUser, c.authPass) } - // Client agent. - if c.agent != "" { - req.Header.Set("User-Agent", c.agent) - } return req, nil } From 3e33d66ab4ca7e26e28d5f0bcecbabafa08e06e0 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 30 Jan 2021 23:05:02 +0800 Subject: [PATCH 137/492] fix issue https://github.com/gogf/gf/issues/1148 --- net/ghttp/ghttp_unit_client_test.go | 20 ++++++++++++++++++++ net/ghttp/internal/client/client.go | 8 ++++---- net/ghttp/internal/client/client_request.go | 4 ---- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/net/ghttp/ghttp_unit_client_test.go b/net/ghttp/ghttp_unit_client_test.go index 5f8c73112..0845b8be7 100644 --- a/net/ghttp/ghttp_unit_client_test.go +++ b/net/ghttp/ghttp_unit_client_test.go @@ -421,3 +421,23 @@ func Test_Client_Middleware(t *testing.T) { t.Assert(resp, nil) }) } + +func Test_Client_Agent(t *testing.T) { + p, _ := ports.PopRand() + s := g.Server(p) + s.BindHandler("/", func(r *ghttp.Request) { + r.Response.Write(r.UserAgent()) + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + + gtest.C(t, func(t *gtest.T) { + c := g.Client().SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + c.SetAgent("test") + t.Assert(c.GetContent("/"), "test") + }) +} diff --git a/net/ghttp/internal/client/client.go b/net/ghttp/internal/client/client.go index 3aba87d94..6c2b292b1 100644 --- a/net/ghttp/internal/client/client.go +++ b/net/ghttp/internal/client/client.go @@ -27,7 +27,6 @@ type Client struct { http.Client // Underlying HTTP Client. ctx context.Context // Context for each request. dump bool // Mark this request will be dumped. - agent string // Client agent. parent *Client // Parent http client, this is used for chaining operations. header map[string]string // Custom header map. cookies map[string]string // Custom cookie map. @@ -46,7 +45,7 @@ var ( // New creates and returns a new HTTP client object. func New() *Client { - return &Client{ + client := &Client{ Client: http.Client{ Transport: &http.Transport{ // No validation for https certification of the server in default. @@ -58,11 +57,12 @@ func New() *Client { }, header: make(map[string]string), cookies: make(map[string]string), - agent: defaultClientAgent, } + client.header["User-Agent"] = defaultClientAgent + return client } -// Clone clones current client and returns a new one. +// Clone deeply clones current client and returns a new one. func (c *Client) Clone() *Client { newClient := New() *newClient = *c diff --git a/net/ghttp/internal/client/client_request.go b/net/ghttp/internal/client/client_request.go index f74ec6042..03673a66d 100644 --- a/net/ghttp/internal/client/client_request.go +++ b/net/ghttp/internal/client/client_request.go @@ -251,10 +251,6 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h } else { req = req.WithContext(context.Background()) } - // Client agent. - if c.agent != "" { - req.Header.Set("User-Agent", c.agent) - } // Custom header. if len(c.header) > 0 { for k, v := range c.header { From 28f83d3d324477734fb8a122ae5b5ef2895e8236 Mon Sep 17 00:00:00 2001 From: gouguoyin <245629560@qq.com> Date: Sun, 31 Jan 2021 15:40:27 +0800 Subject: [PATCH 138/492] formatMonthDaySuffixMap() misjudged suffix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when day is 21,abbreviated as 21st,suffix is st ,is not th when day is 22,abbreviated as 22nd,suffix is nd ,is not th when day is 23,abbreviated as 23rd,suffix is rd ,is not th when day is 31,abbreviated as 31st,suffix is st ,is not th --- os/gtime/gtime_format.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/os/gtime/gtime_format.go b/os/gtime/gtime_format.go index 9b87c12b5..a0c563332 100644 --- a/os/gtime/gtime_format.go +++ b/os/gtime/gtime_format.go @@ -266,11 +266,11 @@ func formatToRegexPattern(format string) string { // formatMonthDaySuffixMap returns the short english word for current day. func formatMonthDaySuffixMap(day string) string { switch day { - case "01": + case "01", "21", "31": return "st" - case "02": + case "02", "22": return "nd" - case "03": + case "03", "23": return "rd" default: return "th" From e6b4662ec2433423a20c8e67238e4d70b31f2f40 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 1 Feb 2021 17:10:50 +0800 Subject: [PATCH 139/492] improve tracing feature --- database/gdb/gdb_core_tracing.go | 6 ++---- database/gredis/gredis_conn_tracing.go | 6 ++---- net/ghttp/ghttp_middleware_tracing.go | 6 ++---- net/ghttp/internal/client/client_tracing.go | 6 ++---- net/gtrace/gtrace.go | 22 +++++++++++++-------- 5 files changed, 22 insertions(+), 24 deletions(-) diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index a538c2e39..4ad920f1d 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -19,6 +19,7 @@ import ( ) const ( + tracingInstrumentName = "github.com/gogf/gf/database/gdb" tracingAttrDbType = "db.type" tracingAttrDbHost = "db.host" tracingAttrDbPort = "db.port" @@ -37,10 +38,7 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { if !gtrace.IsActivated(ctx) { return } - tr := otel.GetTracerProvider().Tracer( - "github.com/gogf/gf/database/gdb", - trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), - ) + tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) ctx, span := tr.Start(ctx, sql.Type, trace.WithSpanKind(trace.SpanKindInternal)) defer span.End() diff --git a/database/gredis/gredis_conn_tracing.go b/database/gredis/gredis_conn_tracing.go index d8406ff5e..1e6990839 100644 --- a/database/gredis/gredis_conn_tracing.go +++ b/database/gredis/gredis_conn_tracing.go @@ -27,6 +27,7 @@ type tracingItem struct { } const ( + tracingInstrumentName = "github.com/gogf/gf/database/gredis" tracingAttrRedisHost = "redis.host" tracingAttrRedisPort = "redis.port" tracingAttrRedisDb = "redis.db" @@ -38,10 +39,7 @@ const ( // addTracingItem checks and adds redis tracing information to OpenTelemetry. func (c *Conn) addTracingItem(item *tracingItem) { - tr := otel.GetTracerProvider().Tracer( - "github.com/gogf/gf/database/gredis", - trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), - ) + tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) ctx := c.ctx if ctx == nil { ctx = context.Background() diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index 5ff2c2e49..c93b18248 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -24,6 +24,7 @@ import ( const ( tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. + tracingInstrumentName = "github.com/gogf/gf/net/ghttp.Server" tracingEventHttpRequest = "http.request" tracingEventHttpRequestHeaders = "http.request.headers" tracingEventHttpRequestBody = "http.request.body" @@ -39,10 +40,7 @@ func MiddlewareClientTracing(c *Client, r *http.Request) (*ClientResponse, error // MiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry. func MiddlewareServerTracing(r *Request) { - tr := otel.GetTracerProvider().Tracer( - "github.com/gogf/gf/net/ghttp.Server", - trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), - ) + tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) // Tracing content parsing, start root span. propagator := propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index c7b8c2393..4cedc4d2c 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -24,6 +24,7 @@ import ( const ( tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. + tracingInstrumentName = "github.com/gogf/gf/net/ghttp.Client" tracingAttrHttpAddressRemote = "http.address.remote" tracingAttrHttpAddressLocal = "http.address.local" tracingAttrHttpDnsStart = "http.dns.start" @@ -40,10 +41,7 @@ const ( // MiddlewareTracing is a client middleware that enables tracing feature using standards of OpenTelemetry. func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err error) { - tr := otel.GetTracerProvider().Tracer( - "github.com/gogf/gf/net/ghttp.Client", - trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)), - ) + tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) ctx, span := tr.Start(r.Context(), r.URL.String(), trace.WithSpanKind(trace.SpanKindClient)) defer span.End() diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index c7964afdf..d58d6bff7 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -11,33 +11,39 @@ import ( "context" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/net/gipv4" - "github.com/gogf/gf/text/gstr" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" "os" + "strings" +) + +const ( + commonLabelHostname = "hostname" + commonLabelIpIntranet = "ip.intranet" ) var ( intranetIps, _ = gipv4.GetIntranetIpArray() + intranetIpStr = strings.Join(intranetIps, ",") hostname, _ = os.Hostname() ) -// IsActivated checks and returns if tracing feature is activated. -func IsActivated(ctx context.Context) bool { - return GetTraceId(ctx) != "" -} - // CommonLabels returns common used attribute labels: // ip.intranet, hostname. func CommonLabels() []label.KeyValue { return []label.KeyValue{ - label.String(`ip.intranet`, gstr.Join(intranetIps, ",")), - label.String(`hostname`, hostname), + label.String(commonLabelHostname, hostname), + label.String(commonLabelIpIntranet, intranetIpStr), } } +// IsActivated checks and returns if tracing feature is activated. +func IsActivated(ctx context.Context) bool { + return GetTraceId(ctx) != "" +} + // Tracer is a short function for retrieve Tracer. func Tracer(name ...string) trace.Tracer { tracerName := "" From ce40d139e7f9bcc34689d1020b0e2a9ba36df441 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 1 Feb 2021 21:51:42 +0800 Subject: [PATCH 140/492] fix issue of nil response handling for ghttp.Client --- go.mod | 1 + net/ghttp/internal/client/client_dump.go | 2 +- net/ghttp/internal/client/client_request.go | 2 +- net/ghttp/internal/client/client_response.go | 4 ++++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 40e157c86..6268b8843 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/websocket v1.4.1 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf + github.com/mattn/go-runewidth v0.0.10 // indirect github.com/olekukonko/tablewriter v0.0.1 go.opentelemetry.io/otel v0.16.0 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 diff --git a/net/ghttp/internal/client/client_dump.go b/net/ghttp/internal/client/client_dump.go index ac082d57c..9ac718a65 100644 --- a/net/ghttp/internal/client/client_dump.go +++ b/net/ghttp/internal/client/client_dump.go @@ -58,7 +58,7 @@ func (r *Response) RawRequest() string { // RawResponse returns the raw content of the response. func (r *Response) RawResponse() string { - // Response can be nil. + // Response might be nil. if r == nil || r.Response == nil { return "" } diff --git a/net/ghttp/internal/client/client_request.go b/net/ghttp/internal/client/client_request.go index 03673a66d..e12ddf501 100644 --- a/net/ghttp/internal/client/client_request.go +++ b/net/ghttp/internal/client/client_request.go @@ -117,7 +117,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Respo } // Auto saving cookie content. - if c.browserMode && resp != nil { + if c.browserMode && resp != nil && resp.Response != nil { now := time.Now() for _, v := range resp.Response.Cookies() { if !v.Expires.IsZero() && v.Expires.UnixNano() < now.UnixNano() { diff --git a/net/ghttp/internal/client/client_response.go b/net/ghttp/internal/client/client_response.go index 893f4f834..2cf06cddd 100644 --- a/net/ghttp/internal/client/client_response.go +++ b/net/ghttp/internal/client/client_response.go @@ -49,6 +49,10 @@ func (r *Response) GetCookieMap() map[string]string { // ReadAll retrieves and returns the response content as []byte. func (r *Response) ReadAll() []byte { + // Response might be nil. + if r == nil || r.Response == nil { + return []byte{} + } body, err := ioutil.ReadAll(r.Response.Body) if err != nil { return nil From 813841bb6819f836ed58ad24a406216a1d38edc5 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 2 Feb 2021 15:16:09 +0800 Subject: [PATCH 141/492] rename GetWithEnv to GetOptWithEnv for packge gcmd --- database/gdb/gdb.go | 2 +- internal/intlog/intlog.go | 2 +- os/gcfg/gcfg.go | 4 ++-- os/gcfg/gcfg_error.go | 2 +- os/gcmd/gcmd.go | 10 ++++++++-- os/gcmd/gcmd_z_unit_default_test.go | 4 ++-- os/gfile/gfile_cache.go | 2 +- os/glog/glog.go | 2 +- os/gtimer/gtimer.go | 6 +++--- os/gview/gview.go | 2 +- os/gview/gview_error.go | 2 +- util/gmode/gmode.go | 2 +- 12 files changed, 23 insertions(+), 17 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 671aa338e..8932517c2 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -291,7 +291,7 @@ var ( func init() { // allDryRun is initialized from environment or command options. - allDryRun = gcmd.GetWithEnv("gf.gdb.dryrun", false).Bool() + allDryRun = gcmd.GetOptWithEnv("gf.gdb.dryrun", false).Bool() } // Register registers custom database driver to gdb. diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index 4c6ec3031..98f12322c 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -26,7 +26,7 @@ var ( func init() { // Debugging configured. - if !gcmd.GetWithEnv("GF_DEBUG").IsEmpty() { + if !gcmd.GetOptWithEnv("GF_DEBUG").IsEmpty() { isGFDebug = true return } diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index dec2ad8fe..b994a31cf 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -52,7 +52,7 @@ func New(file ...string) *Config { name = file[0] } else { // Custom default configuration file name from command line or environment. - if customFile := gcmd.GetWithEnv(fmt.Sprintf("%s.file", cmdEnvKey)).String(); customFile != "" { + if customFile := gcmd.GetOptWithEnv(fmt.Sprintf("%s.file", cmdEnvKey)).String(); customFile != "" { name = customFile } } @@ -62,7 +62,7 @@ func New(file ...string) *Config { jsonMap: gmap.NewStrAnyMap(true), } // Customized dir path from env/cmd. - if customPath := gcmd.GetWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).String(); customPath != "" { + if customPath := gcmd.GetOptWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).String(); customPath != "" { if gfile.Exists(customPath) { _ = c.SetPath(customPath) } else { diff --git a/os/gcfg/gcfg_error.go b/os/gcfg/gcfg_error.go index 0a43d87d4..fd7df93b3 100644 --- a/os/gcfg/gcfg_error.go +++ b/os/gcfg/gcfg_error.go @@ -18,5 +18,5 @@ const ( // errorPrint checks whether printing error to stdout. func errorPrint() bool { - return gcmd.GetWithEnv(errorPrintKey, true).Bool() + return gcmd.GetOptWithEnv(errorPrintKey, true).Bool() } diff --git a/os/gcmd/gcmd.go b/os/gcmd/gcmd.go index e37d944fb..d3cf5e65d 100644 --- a/os/gcmd/gcmd.go +++ b/os/gcmd/gcmd.go @@ -117,14 +117,20 @@ func GetArgAll() []string { return defaultParsedArgs } -// GetWithEnv returns the command line argument of the specified . +// GetWithEnv is alias of GetOptWithEnv. +// Deprecated. +func GetWithEnv(key string, def ...interface{}) *gvar.Var { + return GetOptWithEnv(key, def...) +} + +// GetOptWithEnv returns the command line argument of the specified . // If the argument does not exist, then it returns the environment variable with specified . // It returns the default value if none of them exists. // // Fetching Rules: // 1. Command line arguments are in lowercase format, eg: gf..; // 2. Environment arguments are in uppercase format, eg: GF__; -func GetWithEnv(key string, def ...interface{}) *gvar.Var { +func GetOptWithEnv(key string, def ...interface{}) *gvar.Var { value := interface{}(nil) if len(def) > 0 { value = def[0] diff --git a/os/gcmd/gcmd_z_unit_default_test.go b/os/gcmd/gcmd_z_unit_default_test.go index 36d91bf7e..173273c0d 100644 --- a/os/gcmd/gcmd_z_unit_default_test.go +++ b/os/gcmd/gcmd_z_unit_default_test.go @@ -59,12 +59,12 @@ func Test_GetWithEnv(t *testing.T) { gtest.C(t, func(t *gtest.T) { genv.Set("TEST", "1") defer genv.Remove("TEST") - t.Assert(gcmd.GetWithEnv("test"), 1) + t.Assert(gcmd.GetOptWithEnv("test"), 1) }) gtest.C(t, func(t *gtest.T) { genv.Set("TEST", "1") defer genv.Remove("TEST") gcmd.Init("-test", "2") - t.Assert(gcmd.GetWithEnv("test"), 2) + t.Assert(gcmd.GetOptWithEnv("test"), 2) }) } diff --git a/os/gfile/gfile_cache.go b/os/gfile/gfile_cache.go index 5489087cb..758d85495 100644 --- a/os/gfile/gfile_cache.go +++ b/os/gfile/gfile_cache.go @@ -20,7 +20,7 @@ const ( var ( // Default expire time for file content caching. - cacheExpire = gcmd.GetWithEnv("gf.gfile.cache", gDEFAULT_CACHE_EXPIRE).Duration() + cacheExpire = gcmd.GetOptWithEnv("gf.gfile.cache", gDEFAULT_CACHE_EXPIRE).Duration() // internalCache is the memory cache for internal usage. internalCache = gcache.New() diff --git a/os/glog/glog.go b/os/glog/glog.go index 6adbc3e48..089f853f7 100644 --- a/os/glog/glog.go +++ b/os/glog/glog.go @@ -26,7 +26,7 @@ var ( ) func init() { - defaultDebug = gcmd.GetWithEnv("gf.glog.debug", true).Bool() + defaultDebug = gcmd.GetOptWithEnv("gf.glog.debug", true).Bool() SetDebug(defaultDebug) } diff --git a/os/gtimer/gtimer.go b/os/gtimer/gtimer.go index ed295d75d..80840682c 100644 --- a/os/gtimer/gtimer.go +++ b/os/gtimer/gtimer.go @@ -42,9 +42,9 @@ const ( ) var ( - defaultSlots = gcmd.GetWithEnv(fmt.Sprintf("%s.slots", cmdEnvKey), defaultSlotNumber).Int() - defaultLevel = gcmd.GetWithEnv(fmt.Sprintf("%s.level", cmdEnvKey), defaultWheelLevel).Int() - defaultInterval = gcmd.GetWithEnv(fmt.Sprintf("%s.interval", cmdEnvKey), defaultWheelInterval).Duration() * time.Millisecond + defaultSlots = gcmd.GetOptWithEnv(fmt.Sprintf("%s.slots", cmdEnvKey), defaultSlotNumber).Int() + defaultLevel = gcmd.GetOptWithEnv(fmt.Sprintf("%s.level", cmdEnvKey), defaultWheelLevel).Int() + defaultInterval = gcmd.GetOptWithEnv(fmt.Sprintf("%s.interval", cmdEnvKey), defaultWheelInterval).Duration() * time.Millisecond defaultTimer = New(defaultSlots, defaultInterval, defaultLevel) ) diff --git a/os/gview/gview.go b/os/gview/gview.go index 2350ff900..54813b9ab 100644 --- a/os/gview/gview.go +++ b/os/gview/gview.go @@ -71,7 +71,7 @@ func New(path ...string) *View { } } else { // Customized dir path from env/cmd. - if envPath := gcmd.GetWithEnv("gf.gview.path").String(); envPath != "" { + if envPath := gcmd.GetOptWithEnv("gf.gview.path").String(); envPath != "" { if gfile.Exists(envPath) { if err := view.SetPath(envPath); err != nil { intlog.Error(err) diff --git a/os/gview/gview_error.go b/os/gview/gview_error.go index 6c038e7ab..4c38fafe5 100644 --- a/os/gview/gview_error.go +++ b/os/gview/gview_error.go @@ -18,5 +18,5 @@ const ( // errorPrint checks whether printing error to stdout. func errorPrint() bool { - return gcmd.GetWithEnv(gERROR_PRINT_KEY, true).Bool() + return gcmd.GetOptWithEnv(gERROR_PRINT_KEY, true).Bool() } diff --git a/util/gmode/gmode.go b/util/gmode/gmode.go index 2911bd755..9b43dacaf 100644 --- a/util/gmode/gmode.go +++ b/util/gmode/gmode.go @@ -57,7 +57,7 @@ func SetProduct() { func Mode() string { // If current mode is not set, do this auto check. if currentMode == NOT_SET { - if v := gcmd.GetWithEnv(cmdEnvKey).String(); v != "" { + if v := gcmd.GetOptWithEnv(cmdEnvKey).String(); v != "" { // Mode configured from command argument of environment. currentMode = v } else { From 40bdc76af1914b1a60aa794afb23d2400885ee57 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Wed, 3 Feb 2021 15:14:07 +0800 Subject: [PATCH 142/492] improve package gtrace --- net/ghttp/ghttp_middleware_tracing.go | 8 +-- net/ghttp/internal/client/client_tracing.go | 7 +-- net/gtrace/gtrace.go | 11 ++++ net/gtrace/gtrace_carrier.go | 51 +++++++++++++++++ net/gtrace/gtrace_unit_carrier_test.go | 63 +++++++++++++++++++++ 5 files changed, 127 insertions(+), 13 deletions(-) create mode 100644 net/gtrace/gtrace_carrier.go create mode 100644 net/gtrace/gtrace_unit_carrier_test.go diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index 1422e736e..f8b19122b 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -16,7 +16,6 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/label" - "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "io/ioutil" "net/http" @@ -42,12 +41,7 @@ func MiddlewareClientTracing(c *Client, r *http.Request) (*ClientResponse, error // MiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry. func MiddlewareServerTracing(r *Request) { tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) - // Tracing content parsing, start root span. - propagator := propagation.NewCompositeTextMapPropagator( - propagation.TraceContext{}, - propagation.Baggage{}, - ) - ctx := propagator.Extract(r.Context(), r.Header) + ctx := gtrace.DefaultTextMapPropagator().Extract(r.Context(), r.Header) ctx, span := tr.Start(ctx, r.URL.String(), trace.WithSpanKind(trace.SpanKindServer)) defer span.End() diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index 9d034e5ea..5f7e914bb 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -15,7 +15,6 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/label" - "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "io/ioutil" "net/http" @@ -49,11 +48,7 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro span.SetAttributes(gtrace.CommonLabels()...) // Inject tracing content into http header. - propagator := propagation.NewCompositeTextMapPropagator( - propagation.TraceContext{}, - propagation.Baggage{}, - ) - propagator.Inject(ctx, r.Header) + gtrace.DefaultTextMapPropagator().Inject(ctx, r.Header) // Continue client handler executing. response, err = c.Next( diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index 0ffb82d76..aaa1b3e68 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -13,6 +13,7 @@ import ( "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/net/gipv4" "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "os" "strings" @@ -27,6 +28,11 @@ var ( intranetIps, _ = gipv4.GetIntranetIpArray() intranetIpStr = strings.Join(intranetIps, ",") hostname, _ = os.Hostname() + // defaultTextMapPropagator is the default propagator for context propagation between peers. + defaultTextMapPropagator = propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{}, + ) ) // CommonLabels returns common used attribute labels: @@ -43,6 +49,11 @@ func IsActivated(ctx context.Context) bool { return GetTraceId(ctx) != "" } +// DefaultTextMapPropagator returns the default propagator for context propagation between peers. +func DefaultTextMapPropagator() propagation.TextMapPropagator { + return defaultTextMapPropagator +} + // GetTraceId retrieves and returns TraceId from context. // It returns an empty string is tracing feature is not activated. func GetTraceId(ctx context.Context) string { diff --git a/net/gtrace/gtrace_carrier.go b/net/gtrace/gtrace_carrier.go new file mode 100644 index 000000000..6cc55f857 --- /dev/null +++ b/net/gtrace/gtrace_carrier.go @@ -0,0 +1,51 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtrace + +import ( + "github.com/gogf/gf/internal/json" + "github.com/gogf/gf/util/gconv" +) + +type Carrier struct { + data map[string]interface{} +} + +func NewCarrier(data ...map[string]interface{}) *Carrier { + carrier := &Carrier{} + if len(data) > 0 && data[0] != nil { + carrier.data = data[0] + } else { + carrier.data = make(map[string]interface{}) + } + return carrier +} + +func (c *Carrier) Get(k string) string { + return gconv.String(c.data[k]) +} + +func (c *Carrier) Set(k, v string) { + c.data[k] = v +} + +func (c *Carrier) MustMarshal() []byte { + b, err := json.Marshal(c.data) + if err != nil { + panic(err) + } + return b +} + +func (c *Carrier) String() string { + return string(c.MustMarshal()) +} + +func (c *Carrier) UnmarshalJSON(b []byte) error { + carrier := NewCarrier(nil) + return json.Unmarshal(b, carrier.data) +} diff --git a/net/gtrace/gtrace_unit_carrier_test.go b/net/gtrace/gtrace_unit_carrier_test.go new file mode 100644 index 000000000..81080815c --- /dev/null +++ b/net/gtrace/gtrace_unit_carrier_test.go @@ -0,0 +1,63 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtrace_test + +import ( + "context" + "github.com/gogf/gf/net/gtrace" + "github.com/gogf/gf/test/gtest" + "go.opentelemetry.io/otel/oteltest" + "go.opentelemetry.io/otel/trace" + "testing" +) + +const ( + traceIDStr = "4bf92f3577b34da6a3ce929d0e0e4736" + spanIDStr = "00f067aa0ba902b7" +) + +var ( + traceID = mustTraceIDFromHex(traceIDStr) + spanID = mustSpanIDFromHex(spanIDStr) +) + +func mustTraceIDFromHex(s string) (t trace.TraceID) { + var err error + t, err = trace.TraceIDFromHex(s) + if err != nil { + panic(err) + } + return +} + +func mustSpanIDFromHex(s string) (t trace.SpanID) { + var err error + t, err = trace.SpanIDFromHex(s) + if err != nil { + panic(err) + } + return +} + +func TestNewCarrier(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + ctx := trace.ContextWithRemoteSpanContext(context.Background(), trace.SpanContext{ + TraceID: traceID, + SpanID: spanID, + TraceFlags: trace.FlagsSampled, + }) + ctx, _ = oteltest.DefaultTracer().Start(ctx, "inject") + carrier1 := gtrace.NewCarrier() + gtrace.DefaultTextMapPropagator().Inject(ctx, carrier1) + t.Assert(carrier1.String(), `{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000002-01","tracestate":""}`) + + ctx = gtrace.DefaultTextMapPropagator().Extract(ctx, carrier1) + gotSc := trace.RemoteSpanContextFromContext(ctx) + t.Assert(gotSc.TraceID.String(), traceID.String()) + t.Assert(gotSc.SpanID.String(), "0000000000000002") + }) +} From 6135085d619dc918fd26994c7ad285d042d9c26a Mon Sep 17 00:00:00 2001 From: jianchenma Date: Wed, 3 Feb 2021 15:27:41 +0800 Subject: [PATCH 143/492] improve package gtrace --- net/ghttp/ghttp_middleware_tracing.go | 2 +- net/ghttp/internal/client/client_tracing.go | 2 +- net/gtrace/gtrace.go | 17 +++++++++++++++-- net/gtrace/gtrace_unit_carrier_test.go | 5 +++-- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index f8b19122b..22b4cdc2b 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -41,7 +41,7 @@ func MiddlewareClientTracing(c *Client, r *http.Request) (*ClientResponse, error // MiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry. func MiddlewareServerTracing(r *Request) { tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) - ctx := gtrace.DefaultTextMapPropagator().Extract(r.Context(), r.Header) + ctx := otel.GetTextMapPropagator().Extract(r.Context(), r.Header) ctx, span := tr.Start(ctx, r.URL.String(), trace.WithSpanKind(trace.SpanKindServer)) defer span.End() diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index 5f7e914bb..244ea05b9 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -48,7 +48,7 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro span.SetAttributes(gtrace.CommonLabels()...) // Inject tracing content into http header. - gtrace.DefaultTextMapPropagator().Inject(ctx, r.Header) + otel.GetTextMapPropagator().Inject(ctx, r.Header) // Continue client handler executing. response, err = c.Next( diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index aaa1b3e68..a166ddcb0 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -12,6 +12,7 @@ import ( "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/net/gipv4" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" @@ -35,6 +36,10 @@ var ( ) ) +func init() { + CheckSetDefaultTextMapPropagator() +} + // CommonLabels returns common used attribute labels: // ip.intranet, hostname. func CommonLabels() []label.KeyValue { @@ -49,8 +54,16 @@ func IsActivated(ctx context.Context) bool { return GetTraceId(ctx) != "" } -// DefaultTextMapPropagator returns the default propagator for context propagation between peers. -func DefaultTextMapPropagator() propagation.TextMapPropagator { +// CheckSetDefaultTextMapPropagator sets the default TextMapPropagator if it is not set previously. +func CheckSetDefaultTextMapPropagator() { + p := otel.GetTextMapPropagator() + if len(p.Fields()) == 0 { + otel.SetTextMapPropagator(GetDefaultTextMapPropagator()) + } +} + +// GetDefaultTextMapPropagator returns the default propagator for context propagation between peers. +func GetDefaultTextMapPropagator() propagation.TextMapPropagator { return defaultTextMapPropagator } diff --git a/net/gtrace/gtrace_unit_carrier_test.go b/net/gtrace/gtrace_unit_carrier_test.go index 81080815c..85720a3ba 100644 --- a/net/gtrace/gtrace_unit_carrier_test.go +++ b/net/gtrace/gtrace_unit_carrier_test.go @@ -10,6 +10,7 @@ import ( "context" "github.com/gogf/gf/net/gtrace" "github.com/gogf/gf/test/gtest" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/oteltest" "go.opentelemetry.io/otel/trace" "testing" @@ -52,10 +53,10 @@ func TestNewCarrier(t *testing.T) { }) ctx, _ = oteltest.DefaultTracer().Start(ctx, "inject") carrier1 := gtrace.NewCarrier() - gtrace.DefaultTextMapPropagator().Inject(ctx, carrier1) + otel.GetTextMapPropagator().Inject(ctx, carrier1) t.Assert(carrier1.String(), `{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000002-01","tracestate":""}`) - ctx = gtrace.DefaultTextMapPropagator().Extract(ctx, carrier1) + ctx = otel.GetTextMapPropagator().Extract(ctx, carrier1) gotSc := trace.RemoteSpanContextFromContext(ctx) t.Assert(gotSc.TraceID.String(), traceID.String()) t.Assert(gotSc.SpanID.String(), "0000000000000002") From ae559b57de34c17c46983159cf308156598fdeba Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 3 Feb 2021 22:46:59 +0800 Subject: [PATCH 144/492] fix dryrun configuration for package gdb --- database/gdb/gdb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 8932517c2..8a49a6f25 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -490,7 +490,7 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error if node.Debug { c.DB.SetDebug(node.Debug) } - if node.Debug { + if node.DryRun { c.DB.SetDryRun(node.DryRun) } return From 3b2bae612863aae2eb5fa8f8161540d658b7ccd6 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 3 Feb 2021 23:11:17 +0800 Subject: [PATCH 145/492] improve soft deletion for package gdb --- database/gdb/gdb_model_select.go | 28 +------------ database/gdb/gdb_model_utility.go | 18 +++++++- .../gdb/gdb_z_mysql_time_maintain_test.go | 42 +++++++++++++++++++ 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index e14438826..3f4ad8723 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -45,19 +45,7 @@ func (m *Model) doGetAll(limit1 bool, where ...interface{}) (Result, error) { if len(where) > 0 { return m.Where(where[0], where[1:]...).All() } - var ( - softDeletingCondition = m.getConditionForSoftDeleting() - conditionWhere, conditionExtra, conditionArgs = m.formatCondition(limit1, false) - ) - if !m.unscoped && softDeletingCondition != "" { - if conditionWhere == "" { - conditionWhere = " WHERE " - } else { - conditionWhere += " AND " - } - conditionWhere += softDeletingCondition - } - + conditionWhere, conditionExtra, conditionArgs := m.formatCondition(limit1, false) // DO NOT quote the m.fields where, in case of fields like: // DISTINCT t.user_id uid return m.doGetAllBySql( @@ -343,19 +331,7 @@ func (m *Model) Count(where ...interface{}) (int, error) { // DISTINCT t.user_id uid countFields = fmt.Sprintf(`COUNT(%s)`, m.fields) } - var ( - softDeletingCondition = m.getConditionForSoftDeleting() - conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false, true) - ) - if !m.unscoped && softDeletingCondition != "" { - if conditionWhere == "" { - conditionWhere = " WHERE " - } else { - conditionWhere += " AND " - } - conditionWhere += softDeletingCondition - } - + conditionWhere, conditionExtra, conditionArgs := m.formatCondition(false, true) s := fmt.Sprintf("SELECT %s FROM %s%s", countFields, m.tables, conditionWhere+conditionExtra) if len(m.groupBy) > 0 { s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s) diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 17a484025..e3ad16442 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -248,12 +248,24 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh } } } - if conditionWhere != "" { - conditionWhere = " WHERE " + conditionWhere + // Soft deletion. + softDeletingCondition := m.getConditionForSoftDeleting() + if !m.unscoped && softDeletingCondition != "" { + if conditionWhere == "" { + conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition) + } else { + conditionWhere = fmt.Sprintf(` WHERE (%s) AND %s`, conditionWhere, softDeletingCondition) + } + } else { + if conditionWhere != "" { + conditionWhere = " WHERE " + conditionWhere + } } + // GROUP BY. if m.groupBy != "" { conditionExtra += " GROUP BY " + m.groupBy } + // HAVING. if len(m.having) > 0 { havingStr, havingArgs := formatWhere( m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OPTION_OMITEMPTY > 0, @@ -263,9 +275,11 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh conditionArgs = append(conditionArgs, havingArgs...) } } + // ORDER BY. if m.orderBy != "" { conditionExtra += " ORDER BY " + m.orderBy } + // LIMIT. if !isCountStatement { if m.limit != 0 { if m.start >= 0 { diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index d37c0cacf..4bb71398a 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -605,6 +605,48 @@ CREATE TABLE %s ( }) } +func Test_SoftDelete_WhereAndOr(t *testing.T) { + table := "time_test_table" + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + id int(11) NOT NULL, + name varchar(45) DEFAULT NULL, + create_at datetime DEFAULT NULL, + update_at datetime DEFAULT NULL, + delete_at datetime DEFAULT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + //db.SetDebug(true) + // Add datas. + gtest.C(t, func(t *gtest.T) { + for i := 1; i <= 10; i++ { + data := g.Map{ + "id": i, + "name": fmt.Sprintf("name_%d", i), + } + r, err := db.Table(table).Data(data).Insert() + t.Assert(err, nil) + n, _ := r.RowsAffected() + t.Assert(n, 1) + } + }) + gtest.C(t, func(t *gtest.T) { + ids := g.SliceInt{1, 3, 5} + r, err := db.Table(table).Where("id", ids).Delete() + t.Assert(err, nil) + n, _ := r.RowsAffected() + t.Assert(n, 3) + + count, err := db.Table(table).Where("id", 1).Or("id", 3).Count() + t.Assert(err, nil) + t.Assert(count, 0) + }) +} + func Test_CreateUpdateTime_Struct(t *testing.T) { table := "time_test_table" if _, err := db.Exec(fmt.Sprintf(` From fefde4c290ab0989009ab11708342b2de3921f96 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 4 Feb 2021 00:10:13 +0800 Subject: [PATCH 146/492] improve stack feature for package gdebug/gerror --- debug/gdebug/gdebug_stack.go | 6 ++ errors/gerror/gerror_error.go | 26 +++++-- internal/command/command.go | 127 ++++++++++++++++++++++++++++++++++ internal/intlog/intlog.go | 8 +-- internal/utils/utils_debug.go | 37 ++++++++++ os/gcmd/gcmd.go | 87 +++++------------------ 6 files changed, 211 insertions(+), 80 deletions(-) create mode 100644 internal/command/command.go create mode 100644 internal/utils/utils_debug.go diff --git a/debug/gdebug/gdebug_stack.go b/debug/gdebug/gdebug_stack.go index af61b9591..09ac951cd 100644 --- a/debug/gdebug/gdebug_stack.go +++ b/debug/gdebug/gdebug_stack.go @@ -9,6 +9,7 @@ package gdebug import ( "bytes" "fmt" + "github.com/gogf/gf/internal/utils" "runtime" "strings" ) @@ -82,6 +83,11 @@ func StackWithFilters(filters []string, skip ...int) string { if strings.Contains(file, stackFilterKey) { continue } + if !utils.IsDebugEnabled() { + if strings.Contains(file, utils.StackFilterKeyForGoFrame) { + continue + } + } if fn := runtime.FuncForPC(pc); fn == nil { name = "unknown" } else { diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index 1e7588269..01d593134 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -10,6 +10,7 @@ import ( "bytes" "errors" "fmt" + "github.com/gogf/gf/internal/utils" "io" "runtime" "strings" @@ -24,7 +25,8 @@ type Error struct { } const ( - stackFilterKey = "/errors/gerror/gerror" + // Filtering key for current error module paths. + stackFilterKeyLocal = "/errors/gerror/gerror" ) var ( @@ -182,20 +184,34 @@ func formatSubStack(st stack, buffer *bytes.Buffer) { for _, p := range st { if fn := runtime.FuncForPC(p - 1); fn != nil { file, line := fn.FileLine(p - 1) - if strings.Contains(file, stackFilterKey) { - continue + // Custom filtering. + if !utils.IsDebugEnabled() { + if strings.Contains(file, utils.StackFilterKeyForGoFrame) { + continue + } + } else { + if strings.Contains(file, stackFilterKeyLocal) { + continue + } } // Avoid stack string like "" if strings.Contains(file, "<") { continue } - if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter { + // Ignore GO ROOT paths. + if goRootForFilter != "" && + len(file) >= len(goRootForFilter) && + file[0:len(goRootForFilter)] == goRootForFilter { continue } + // Graceful indent. if index > 9 { space = " " } - buffer.WriteString(fmt.Sprintf(" %d).%s%s\n \t%s:%d\n", index, space, fn.Name(), file, line)) + buffer.WriteString(fmt.Sprintf( + " %d).%s%s\n \t%s:%d\n", + index, space, fn.Name(), file, line, + )) index++ } } diff --git a/internal/command/command.go b/internal/command/command.go new file mode 100644 index 000000000..56f2a9d55 --- /dev/null +++ b/internal/command/command.go @@ -0,0 +1,127 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. +// + +// Package command provides console operations, like options/arguments reading. +package command + +import ( + "os" + "regexp" + "strings" +) + +var ( + defaultParsedArgs = make([]string, 0) + defaultParsedOptions = make(map[string]string) + argumentRegex = regexp.MustCompile(`^\-{1,2}([\w\?\.\-]+)(=){0,1}(.*)$`) +) + +// Custom initialization. +func Init(args ...string) { + if len(args) == 0 { + if len(defaultParsedArgs) == 0 && len(defaultParsedOptions) == 0 { + args = os.Args + } else { + return + } + } else { + defaultParsedArgs = make([]string, 0) + defaultParsedOptions = make(map[string]string) + } + // Parsing os.Args with default algorithm. + for i := 0; i < len(args); { + array := argumentRegex.FindStringSubmatch(args[i]) + if len(array) > 2 { + if array[2] == "=" { + defaultParsedOptions[array[1]] = array[3] + } else if i < len(args)-1 { + if len(args[i+1]) > 0 && args[i+1][0] == '-' { + // Eg: gf gen -d -n 1 + defaultParsedOptions[array[1]] = array[3] + } else { + // Eg: gf gen -n 2 + defaultParsedOptions[array[1]] = args[i+1] + i += 2 + continue + } + } else { + // Eg: gf gen -h + defaultParsedOptions[array[1]] = array[3] + } + } else { + defaultParsedArgs = append(defaultParsedArgs, args[i]) + } + i++ + } +} + +// GetOpt returns the option value named . +func GetOpt(name string, def ...string) string { + Init() + if v, ok := defaultParsedOptions[name]; ok { + return v + } + if len(def) > 0 { + return def[0] + } + return "" +} + +// GetOptAll returns all parsed options. +func GetOptAll() map[string]string { + Init() + return defaultParsedOptions +} + +// ContainsOpt checks whether option named exist in the arguments. +func ContainsOpt(name string) bool { + Init() + _, ok := defaultParsedOptions[name] + return ok +} + +// GetArg returns the argument at . +func GetArg(index int, def ...string) string { + Init() + if index < len(defaultParsedArgs) { + return defaultParsedArgs[index] + } + if len(def) > 0 { + return def[0] + } + return "" +} + +// GetArgAll returns all parsed arguments. +func GetArgAll() []string { + Init() + return defaultParsedArgs +} + +// GetOptWithEnv returns the command line argument of the specified . +// If the argument does not exist, then it returns the environment variable with specified . +// It returns the default value if none of them exists. +// +// Fetching Rules: +// 1. Command line arguments are in lowercase format, eg: gf..; +// 2. Environment arguments are in uppercase format, eg: GF__; +func GetOptWithEnv(key string, def ...string) string { + cmdKey := strings.ToLower(strings.Replace(key, "_", ".", -1)) + if ContainsOpt(cmdKey) { + return GetOpt(cmdKey) + } else { + envKey := strings.ToUpper(strings.Replace(key, ".", "_", -1)) + if r, ok := os.LookupEnv(envKey); ok { + return r + } else { + if len(def) > 0 { + return def[0] + } + } + } + return "" +} diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index 98f12322c..d746d5862 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -10,7 +10,7 @@ package intlog import ( "fmt" "github.com/gogf/gf/debug/gdebug" - "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/internal/utils" "path/filepath" "time" ) @@ -25,11 +25,7 @@ var ( ) func init() { - // Debugging configured. - if !gcmd.GetOptWithEnv("GF_DEBUG").IsEmpty() { - isGFDebug = true - return - } + isGFDebug = utils.IsDebugEnabled() } // SetEnabled enables/disables the internal logging manually. diff --git a/internal/utils/utils_debug.go b/internal/utils/utils_debug.go new file mode 100644 index 000000000..c03d30dd4 --- /dev/null +++ b/internal/utils/utils_debug.go @@ -0,0 +1,37 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package utils + +import ( + "github.com/gogf/gf/internal/command" +) + +const ( + debugKey = "gf.debug" // Debug key for checking if in debug mode. + StackFilterKeyForGoFrame = "/github.com/gogf/gf/" // Stack filtering key for all GoFrame module paths. +) + +var ( + // isDebugEnabled marks whether GoFrame debug mode is enabled. + isDebugEnabled = false +) + +func init() { + // Debugging configured. + value := command.GetOptWithEnv(debugKey) + if value == "" || value == "0" || value == "false" { + isDebugEnabled = false + } else { + isDebugEnabled = true + } +} + +// IsDebugEnabled checks and returns whether debug mode is enabled. +// The debug mode is enabled when command argument "gf.debug" or environment "GF_DEBUG" is passed. +func IsDebugEnabled() bool { + return isDebugEnabled +} diff --git a/os/gcmd/gcmd.go b/os/gcmd/gcmd.go index d3cf5e65d..777a881ac 100644 --- a/os/gcmd/gcmd.go +++ b/os/gcmd/gcmd.go @@ -9,69 +9,25 @@ package gcmd import ( + "github.com/gogf/gf/container/gvar" + "github.com/gogf/gf/internal/command" "os" "strings" - - "github.com/gogf/gf/container/gvar" - - "github.com/gogf/gf/text/gregex" ) var ( - defaultParsedArgs = make([]string, 0) - defaultParsedOptions = make(map[string]string) defaultCommandFuncMap = make(map[string]func()) ) // Custom initialization. func Init(args ...string) { - if len(args) == 0 { - if len(defaultParsedArgs) == 0 && len(defaultParsedOptions) == 0 { - args = os.Args - } else { - return - } - } else { - defaultParsedArgs = make([]string, 0) - defaultParsedOptions = make(map[string]string) - } - // Parsing os.Args with default algorithm. - for i := 0; i < len(args); { - array, _ := gregex.MatchString(`^\-{1,2}([\w\?\.\-]+)(=){0,1}(.*)$`, args[i]) - if len(array) > 2 { - if array[2] == "=" { - defaultParsedOptions[array[1]] = array[3] - } else if i < len(args)-1 { - if len(args[i+1]) > 0 && args[i+1][0] == '-' { - // Eg: gf gen -d -n 1 - defaultParsedOptions[array[1]] = array[3] - } else { - // Eg: gf gen -n 2 - defaultParsedOptions[array[1]] = args[i+1] - i += 2 - continue - } - } else { - // Eg: gf gen -h - defaultParsedOptions[array[1]] = array[3] - } - } else { - defaultParsedArgs = append(defaultParsedArgs, args[i]) - } - i++ - } + command.Init(args...) } // GetOpt returns the option value named . func GetOpt(name string, def ...string) string { Init() - if v, ok := defaultParsedOptions[name]; ok { - return v - } - if len(def) > 0 { - return def[0] - } - return "" + return command.GetOpt(name, def...) } // GetOptVar returns the option value named as gvar.Var. @@ -83,26 +39,19 @@ func GetOptVar(name string, def ...string) *gvar.Var { // GetOptAll returns all parsed options. func GetOptAll() map[string]string { Init() - return defaultParsedOptions + return command.GetOptAll() } // ContainsOpt checks whether option named exist in the arguments. -func ContainsOpt(name string, def ...string) bool { +func ContainsOpt(name string) bool { Init() - _, ok := defaultParsedOptions[name] - return ok + return command.ContainsOpt(name) } // GetArg returns the argument at . func GetArg(index int, def ...string) string { Init() - if index < len(defaultParsedArgs) { - return defaultParsedArgs[index] - } - if len(def) > 0 { - return def[0] - } - return "" + return command.GetArg(index, def...) } // GetArgVar returns the argument at as gvar.Var. @@ -114,7 +63,7 @@ func GetArgVar(index int, def ...string) *gvar.Var { // GetArgAll returns all parsed arguments. func GetArgAll() []string { Init() - return defaultParsedArgs + return command.GetArgAll() } // GetWithEnv is alias of GetOptWithEnv. @@ -131,20 +80,20 @@ func GetWithEnv(key string, def ...interface{}) *gvar.Var { // 1. Command line arguments are in lowercase format, eg: gf..; // 2. Environment arguments are in uppercase format, eg: GF__; func GetOptWithEnv(key string, def ...interface{}) *gvar.Var { - value := interface{}(nil) - if len(def) > 0 { - value = def[0] - } cmdKey := strings.ToLower(strings.Replace(key, "_", ".", -1)) - if v := GetOpt(cmdKey); v != "" { - value = v + if ContainsOpt(cmdKey) { + return gvar.New(GetOpt(cmdKey)) } else { envKey := strings.ToUpper(strings.Replace(key, ".", "_", -1)) - if v := os.Getenv(envKey); v != "" { - value = v + if r, ok := os.LookupEnv(envKey); ok { + return gvar.New(r) + } else { + if len(def) > 0 { + return gvar.New(def[0]) + } } } - return gvar.New(value) + return gvar.New(nil) } // BuildOptions builds the options as string. From 72251b880a4984c94ae641aa94fa25f5b02f851c Mon Sep 17 00:00:00 2001 From: jianchenma Date: Fri, 5 Feb 2021 14:44:20 +0800 Subject: [PATCH 147/492] improve package gconv for slice converting --- database/gdb/gdb_core_tracing.go | 5 +- util/gconv/gconv.go | 310 +++++++++++++------------- util/gconv/gconv_slice.go | 8 +- util/gconv/gconv_slice_any.go | 18 +- util/gconv/gconv_slice_float.go | 96 +++++--- util/gconv/gconv_slice_int.go | 126 ++++++++--- util/gconv/gconv_slice_str.go | 44 ++-- util/gconv/gconv_slice_uint.go | 132 +++++++---- util/gconv/gconv_time.go | 22 +- util/gconv/gconv_z_unit_all_test.go | 60 ++--- util/gconv/gconv_z_unit_bool_test.go | 4 +- util/gconv/gconv_z_unit_slice_test.go | 8 + 12 files changed, 496 insertions(+), 337 deletions(-) diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index 4ad920f1d..1ccfae3c7 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -38,7 +38,10 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { if !gtrace.IsActivated(ctx) { return } - tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) + tr := otel.GetTracerProvider().Tracer( + tracingInstrumentName, + trace.WithInstrumentationVersion(gf.VERSION), + ) ctx, span := tr.Start(ctx, sql.Type, trace.WithSpanKind(trace.SpanKindInternal)) defer span.End() diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 0814e254c..75a16afc9 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -48,281 +48,281 @@ var ( // Convert converts the variable to the type , the type is specified by string. // The optional parameter is used for additional necessary parameter for this conversion. // It supports common types conversion as its conversion based on type name string. -func Convert(i interface{}, t string, params ...interface{}) interface{} { +func Convert(any interface{}, t string, params ...interface{}) interface{} { switch t { case "int": - return Int(i) + return Int(any) case "*int": - if _, ok := i.(*int); ok { - return i + if _, ok := any.(*int); ok { + return any } - v := Int(i) + v := Int(any) return &v case "int8": - return Int8(i) + return Int8(any) case "*int8": - if _, ok := i.(*int8); ok { - return i + if _, ok := any.(*int8); ok { + return any } - v := Int8(i) + v := Int8(any) return &v case "int16": - return Int16(i) + return Int16(any) case "*int16": - if _, ok := i.(*int16); ok { - return i + if _, ok := any.(*int16); ok { + return any } - v := Int16(i) + v := Int16(any) return &v case "int32": - return Int32(i) + return Int32(any) case "*int32": - if _, ok := i.(*int32); ok { - return i + if _, ok := any.(*int32); ok { + return any } - v := Int32(i) + v := Int32(any) return &v case "int64": - return Int64(i) + return Int64(any) case "*int64": - if _, ok := i.(*int64); ok { - return i + if _, ok := any.(*int64); ok { + return any } - v := Int64(i) + v := Int64(any) return &v case "uint": - return Uint(i) + return Uint(any) case "*uint": - if _, ok := i.(*uint); ok { - return i + if _, ok := any.(*uint); ok { + return any } - v := Uint(i) + v := Uint(any) return &v case "uint8": - return Uint8(i) + return Uint8(any) case "*uint8": - if _, ok := i.(*uint8); ok { - return i + if _, ok := any.(*uint8); ok { + return any } - v := Uint8(i) + v := Uint8(any) return &v case "uint16": - return Uint16(i) + return Uint16(any) case "*uint16": - if _, ok := i.(*uint16); ok { - return i + if _, ok := any.(*uint16); ok { + return any } - v := Uint16(i) + v := Uint16(any) return &v case "uint32": - return Uint32(i) + return Uint32(any) case "*uint32": - if _, ok := i.(*uint32); ok { - return i + if _, ok := any.(*uint32); ok { + return any } - v := Uint32(i) + v := Uint32(any) return &v case "uint64": - return Uint64(i) + return Uint64(any) case "*uint64": - if _, ok := i.(*uint64); ok { - return i + if _, ok := any.(*uint64); ok { + return any } - v := Uint64(i) + v := Uint64(any) return &v case "float32": - return Float32(i) + return Float32(any) case "*float32": - if _, ok := i.(*float32); ok { - return i + if _, ok := any.(*float32); ok { + return any } - v := Float32(i) + v := Float32(any) return &v case "float64": - return Float64(i) + return Float64(any) case "*float64": - if _, ok := i.(*float64); ok { - return i + if _, ok := any.(*float64); ok { + return any } - v := Float64(i) + v := Float64(any) return &v case "bool": - return Bool(i) + return Bool(any) case "*bool": - if _, ok := i.(*bool); ok { - return i + if _, ok := any.(*bool); ok { + return any } - v := Bool(i) + v := Bool(any) return &v case "string": - return String(i) + return String(any) case "*string": - if _, ok := i.(*string); ok { - return i + if _, ok := any.(*string); ok { + return any } - v := String(i) + v := String(any) return &v case "[]byte": - return Bytes(i) + return Bytes(any) case "[]int": - return Ints(i) + return Ints(any) case "[]int32": - return Int32s(i) + return Int32s(any) case "[]int64": - return Int64s(i) + return Int64s(any) case "[]uint": - return Uints(i) + return Uints(any) case "[]uint32": - return Uint32s(i) + return Uint32s(any) case "[]uint64": - return Uint64s(i) + return Uint64s(any) case "[]float32": - return Float32s(i) + return Float32s(any) case "[]float64": - return Float64s(i) + return Float64s(any) case "[]string": - return Strings(i) + return Strings(any) case "Time", "time.Time": if len(params) > 0 { - return Time(i, String(params[0])) + return Time(any, String(params[0])) } - return Time(i) + return Time(any) case "*time.Time": var v interface{} if len(params) > 0 { - v = Time(i, String(params[0])) + v = Time(any, String(params[0])) } else { - if _, ok := i.(*time.Time); ok { - return i + if _, ok := any.(*time.Time); ok { + return any } - v = Time(i) + v = Time(any) } return &v case "GTime", "gtime.Time": if len(params) > 0 { - if v := GTime(i, String(params[0])); v != nil { + if v := GTime(any, String(params[0])); v != nil { return *v } else { return *gtime.New() } } - if v := GTime(i); v != nil { + if v := GTime(any); v != nil { return *v } else { return *gtime.New() } case "*gtime.Time": if len(params) > 0 { - if v := GTime(i, String(params[0])); v != nil { + if v := GTime(any, String(params[0])); v != nil { return v } else { return gtime.New() } } - if v := GTime(i); v != nil { + if v := GTime(any); v != nil { return v } else { return gtime.New() } case "Duration", "time.Duration": - return Duration(i) + return Duration(any) case "*time.Duration": - if _, ok := i.(*time.Duration); ok { - return i + if _, ok := any.(*time.Duration); ok { + return any } - v := Duration(i) + v := Duration(any) return &v case "map[string]string": - return MapStrStr(i) + return MapStrStr(any) case "map[string]interface{}": - return Map(i) + return Map(any) case "[]map[string]interface{}": - return Maps(i) + return Maps(any) //case "gvar.Var": // // TODO remove reflect usage to create gvar.Var, considering using unsafe pointer // rv := reflect.New(intstore.ReflectTypeVarImp) // ri := rv.Interface() // if v, ok := ri.(apiSet); ok { - // v.Set(i) + // v.Set(any) // } else if v, ok := ri.(apiUnmarshalValue); ok { - // v.UnmarshalValue(i) + // v.UnmarshalValue(any) // } else { - // rv.Set(reflect.ValueOf(i)) + // rv.Set(reflect.ValueOf(any)) // } // return ri default: - return i + return any } } // Byte converts to byte. -func Byte(i interface{}) byte { - if v, ok := i.(byte); ok { +func Byte(any interface{}) byte { + if v, ok := any.(byte); ok { return v } - return Uint8(i) + return Uint8(any) } // Bytes converts to []byte. -func Bytes(i interface{}) []byte { - if i == nil { +func Bytes(any interface{}) []byte { + if any == nil { return nil } - switch value := i.(type) { + switch value := any.(type) { case string: return []byte(value) case []byte: return value default: - return gbinary.Encode(i) + return gbinary.Encode(any) } } // Rune converts to rune. -func Rune(i interface{}) rune { - if v, ok := i.(rune); ok { +func Rune(any interface{}) rune { + if v, ok := any.(rune); ok { return v } - return rune(Int32(i)) + return rune(Int32(any)) } // Runes converts to []rune. -func Runes(i interface{}) []rune { - if v, ok := i.([]rune); ok { +func Runes(any interface{}) []rune { + if v, ok := any.([]rune); ok { return v } - return []rune(String(i)) + return []rune(String(any)) } // String converts to string. // It's most common used converting function. -func String(i interface{}) string { - if i == nil { +func String(any interface{}) string { + if any == nil { return "" } - switch value := i.(type) { + switch value := any.(type) { case int: return strconv.Itoa(value) case int8: @@ -421,11 +421,11 @@ func String(i interface{}) string { // Bool converts to bool. // It returns false if is: false, "", 0, "false", "off", "no", empty slice/map. -func Bool(i interface{}) bool { - if i == nil { +func Bool(any interface{}) bool { + if any == nil { return false } - switch value := i.(type) { + switch value := any.(type) { case bool: return value case []byte: @@ -439,7 +439,7 @@ func Bool(i interface{}) bool { } return true default: - rv := reflect.ValueOf(i) + rv := reflect.ValueOf(any) switch rv.Kind() { case reflect.Ptr: return !rv.IsNil() @@ -452,7 +452,7 @@ func Bool(i interface{}) bool { case reflect.Struct: return true default: - s := strings.ToLower(String(i)) + s := strings.ToLower(String(any)) if _, ok := emptyStringMap[s]; ok { return false } @@ -462,55 +462,55 @@ func Bool(i interface{}) bool { } // Int converts to int. -func Int(i interface{}) int { - if i == nil { +func Int(any interface{}) int { + if any == nil { return 0 } - if v, ok := i.(int); ok { + if v, ok := any.(int); ok { return v } - return int(Int64(i)) + return int(Int64(any)) } // Int8 converts to int8. -func Int8(i interface{}) int8 { - if i == nil { +func Int8(any interface{}) int8 { + if any == nil { return 0 } - if v, ok := i.(int8); ok { + if v, ok := any.(int8); ok { return v } - return int8(Int64(i)) + return int8(Int64(any)) } // Int16 converts to int16. -func Int16(i interface{}) int16 { - if i == nil { +func Int16(any interface{}) int16 { + if any == nil { return 0 } - if v, ok := i.(int16); ok { + if v, ok := any.(int16); ok { return v } - return int16(Int64(i)) + return int16(Int64(any)) } // Int32 converts to int32. -func Int32(i interface{}) int32 { - if i == nil { +func Int32(any interface{}) int32 { + if any == nil { return 0 } - if v, ok := i.(int32); ok { + if v, ok := any.(int32); ok { return v } - return int32(Int64(i)) + return int32(Int64(any)) } // Int64 converts to int64. -func Int64(i interface{}) int64 { - if i == nil { +func Int64(any interface{}) int64 { + if any == nil { return 0 } - switch value := i.(type) { + switch value := any.(type) { case int: return int64(value) case int8: @@ -584,55 +584,55 @@ func Int64(i interface{}) int64 { } // Uint converts to uint. -func Uint(i interface{}) uint { - if i == nil { +func Uint(any interface{}) uint { + if any == nil { return 0 } - if v, ok := i.(uint); ok { + if v, ok := any.(uint); ok { return v } - return uint(Uint64(i)) + return uint(Uint64(any)) } // Uint8 converts to uint8. -func Uint8(i interface{}) uint8 { - if i == nil { +func Uint8(any interface{}) uint8 { + if any == nil { return 0 } - if v, ok := i.(uint8); ok { + if v, ok := any.(uint8); ok { return v } - return uint8(Uint64(i)) + return uint8(Uint64(any)) } // Uint16 converts to uint16. -func Uint16(i interface{}) uint16 { - if i == nil { +func Uint16(any interface{}) uint16 { + if any == nil { return 0 } - if v, ok := i.(uint16); ok { + if v, ok := any.(uint16); ok { return v } - return uint16(Uint64(i)) + return uint16(Uint64(any)) } // Uint32 converts to uint32. -func Uint32(i interface{}) uint32 { - if i == nil { +func Uint32(any interface{}) uint32 { + if any == nil { return 0 } - if v, ok := i.(uint32); ok { + if v, ok := any.(uint32); ok { return v } - return uint32(Uint64(i)) + return uint32(Uint64(any)) } // Uint64 converts to uint64. -func Uint64(i interface{}) uint64 { - if i == nil { +func Uint64(any interface{}) uint64 { + if any == nil { return 0 } - switch value := i.(type) { + switch value := any.(type) { case int: return uint64(value) case int8: @@ -688,11 +688,11 @@ func Uint64(i interface{}) uint64 { } // Float32 converts to float32. -func Float32(i interface{}) float32 { - if i == nil { +func Float32(any interface{}) float32 { + if any == nil { return 0 } - switch value := i.(type) { + switch value := any.(type) { case float32: return value case float64: @@ -700,17 +700,17 @@ func Float32(i interface{}) float32 { case []byte: return gbinary.DecodeToFloat32(value) default: - v, _ := strconv.ParseFloat(String(i), 64) + v, _ := strconv.ParseFloat(String(any), 64) return float32(v) } } // Float64 converts to float64. -func Float64(i interface{}) float64 { - if i == nil { +func Float64(any interface{}) float64 { + if any == nil { return 0 } - switch value := i.(type) { + switch value := any.(type) { case float32: return float64(value) case float64: @@ -718,7 +718,7 @@ func Float64(i interface{}) float64 { case []byte: return gbinary.DecodeToFloat64(value) default: - v, _ := strconv.ParseFloat(String(i), 64) + v, _ := strconv.ParseFloat(String(any), 64) return v } } diff --git a/util/gconv/gconv_slice.go b/util/gconv/gconv_slice.go index aa3878cda..265dd4d3a 100644 --- a/util/gconv/gconv_slice.go +++ b/util/gconv/gconv_slice.go @@ -11,13 +11,13 @@ import ( ) // SliceMap is alias of Maps. -func SliceMap(i interface{}) []map[string]interface{} { - return Maps(i) +func SliceMap(any interface{}) []map[string]interface{} { + return Maps(any) } // SliceMapDeep is alias of MapsDeep. -func SliceMapDeep(i interface{}) []map[string]interface{} { - return MapsDeep(i) +func SliceMapDeep(any interface{}) []map[string]interface{} { + return MapsDeep(any) } // SliceStruct is alias of Structs. diff --git a/util/gconv/gconv_slice_any.go b/util/gconv/gconv_slice_any.go index 6a6749a3a..865d21452 100644 --- a/util/gconv/gconv_slice_any.go +++ b/util/gconv/gconv_slice_any.go @@ -11,22 +11,22 @@ import ( ) // SliceAny is alias of Interfaces. -func SliceAny(i interface{}) []interface{} { - return Interfaces(i) +func SliceAny(any interface{}) []interface{} { + return Interfaces(any) } // Interfaces converts to []interface{}. -func Interfaces(i interface{}) []interface{} { - if i == nil { +func Interfaces(any interface{}) []interface{} { + if any == nil { return nil } - if r, ok := i.([]interface{}); ok { + if r, ok := any.([]interface{}); ok { return r - } else if r, ok := i.(apiInterfaces); ok { + } else if r, ok := any.(apiInterfaces); ok { return r.Interfaces() } else { var array []interface{} - switch value := i.(type) { + switch value := any.(type) { case []string: array = make([]interface{}, len(value)) for k, v := range value { @@ -99,7 +99,7 @@ func Interfaces(i interface{}) []interface{} { default: // Finally we use reflection. var ( - reflectValue = reflect.ValueOf(i) + reflectValue = reflect.ValueOf(any) reflectKind = reflectValue.Kind() ) for reflectKind == reflect.Ptr { @@ -130,7 +130,7 @@ func Interfaces(i interface{}) []interface{} { // array = append(array, v) // } default: - return []interface{}{i} + return []interface{}{any} } } return array diff --git a/util/gconv/gconv_slice_float.go b/util/gconv/gconv_slice_float.go index 465703fc2..56c1f7f4b 100644 --- a/util/gconv/gconv_slice_float.go +++ b/util/gconv/gconv_slice_float.go @@ -9,32 +9,32 @@ package gconv import "reflect" // SliceFloat is alias of Floats. -func SliceFloat(i interface{}) []float64 { - return Floats(i) +func SliceFloat(any interface{}) []float64 { + return Floats(any) } // SliceFloat32 is alias of Float32s. -func SliceFloat32(i interface{}) []float32 { - return Float32s(i) +func SliceFloat32(any interface{}) []float32 { + return Float32s(any) } // SliceFloat64 is alias of Float64s. -func SliceFloat64(i interface{}) []float64 { - return Floats(i) +func SliceFloat64(any interface{}) []float64 { + return Floats(any) } // Floats converts to []float64. -func Floats(i interface{}) []float64 { - return Float64s(i) +func Floats(any interface{}) []float64 { + return Float64s(any) } // Float32s converts to []float32. -func Float32s(i interface{}) []float32 { - if i == nil { +func Float32s(any interface{}) []float32 { + if any == nil { return nil } var array []float32 - switch value := i.(type) { + switch value := any.(type) { case string: if value == "" { return []float32{} @@ -112,35 +112,49 @@ func Float32s(i interface{}) []float32 { array[k] = Float32(v) } default: - if v, ok := i.(apiFloats); ok { + if v, ok := any.(apiFloats); ok { return Float32s(v.Floats()) } - if v, ok := i.(apiInterfaces); ok { + if v, ok := any.(apiInterfaces); ok { return Float32s(v.Interfaces()) } - // Use reflect feature at last. - rv := reflect.ValueOf(i) - switch rv.Kind() { + // Not a common type, it then uses reflection for conversion. + var reflectValue reflect.Value + if v, ok := value.(reflect.Value); ok { + reflectValue = v + } else { + reflectValue = reflect.ValueOf(value) + } + reflectKind := reflectValue.Kind() + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { case reflect.Slice, reflect.Array: - length := rv.Len() - array = make([]float32, length) - for n := 0; n < length; n++ { - array[n] = Float32(rv.Index(n).Interface()) + var ( + length = reflectValue.Len() + slice = make([]float32, length) + ) + for i := 0; i < length; i++ { + slice[i] = Float32(reflectValue.Index(i).Interface()) } + return slice + default: - return []float32{Float32(i)} + return []float32{Float32(any)} } } return array } // Float64s converts to []float64. -func Float64s(i interface{}) []float64 { - if i == nil { +func Float64s(any interface{}) []float64 { + if any == nil { return nil } var array []float64 - switch value := i.(type) { + switch value := any.(type) { case string: if value == "" { return []float64{} @@ -218,23 +232,37 @@ func Float64s(i interface{}) []float64 { array[k] = Float64(v) } default: - if v, ok := i.(apiFloats); ok { + if v, ok := any.(apiFloats); ok { return v.Floats() } - if v, ok := i.(apiInterfaces); ok { + if v, ok := any.(apiInterfaces); ok { return Floats(v.Interfaces()) } - // Use reflect feature at last. - rv := reflect.ValueOf(i) - switch rv.Kind() { + // Not a common type, it then uses reflection for conversion. + var reflectValue reflect.Value + if v, ok := value.(reflect.Value); ok { + reflectValue = v + } else { + reflectValue = reflect.ValueOf(value) + } + reflectKind := reflectValue.Kind() + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { case reflect.Slice, reflect.Array: - length := rv.Len() - array = make([]float64, length) - for n := 0; n < length; n++ { - array[n] = Float64(rv.Index(n).Interface()) + var ( + length = reflectValue.Len() + slice = make([]float64, length) + ) + for i := 0; i < length; i++ { + slice[i] = Float64(reflectValue.Index(i).Interface()) } + return slice + default: - return []float64{Float64(i)} + return []float64{Float64(any)} } } return array diff --git a/util/gconv/gconv_slice_int.go b/util/gconv/gconv_slice_int.go index 385cefcee..0fa2e680c 100644 --- a/util/gconv/gconv_slice_int.go +++ b/util/gconv/gconv_slice_int.go @@ -9,27 +9,27 @@ package gconv import "reflect" // SliceInt is alias of Ints. -func SliceInt(i interface{}) []int { - return Ints(i) +func SliceInt(any interface{}) []int { + return Ints(any) } // SliceInt32 is alias of Int32s. -func SliceInt32(i interface{}) []int32 { - return Int32s(i) +func SliceInt32(any interface{}) []int32 { + return Int32s(any) } // SliceInt is alias of Int64s. -func SliceInt64(i interface{}) []int64 { - return Int64s(i) +func SliceInt64(any interface{}) []int64 { + return Int64s(any) } // Ints converts to []int. -func Ints(i interface{}) []int { - if i == nil { +func Ints(any interface{}) []int { + if any == nil { return nil } var array []int - switch value := i.(type) { + switch value := any.(type) { case string: if value == "" { return []int{} @@ -117,35 +117,49 @@ func Ints(i interface{}) []int { array[k] = Int(v) } default: - if v, ok := i.(apiInts); ok { + if v, ok := any.(apiInts); ok { return v.Ints() } - if v, ok := i.(apiInterfaces); ok { + if v, ok := any.(apiInterfaces); ok { return Ints(v.Interfaces()) } - // Use reflect feature at last. - rv := reflect.ValueOf(i) - switch rv.Kind() { + // Not a common type, it then uses reflection for conversion. + var reflectValue reflect.Value + if v, ok := value.(reflect.Value); ok { + reflectValue = v + } else { + reflectValue = reflect.ValueOf(value) + } + reflectKind := reflectValue.Kind() + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { case reflect.Slice, reflect.Array: - length := rv.Len() - array = make([]int, length) - for n := 0; n < length; n++ { - array[n] = Int(rv.Index(n).Interface()) + var ( + length = reflectValue.Len() + slice = make([]int, length) + ) + for i := 0; i < length; i++ { + slice[i] = Int(reflectValue.Index(i).Interface()) } + return slice + default: - return []int{Int(i)} + return []int{Int(any)} } } return array } // Int32s converts to []int32. -func Int32s(i interface{}) []int32 { - if i == nil { +func Int32s(any interface{}) []int32 { + if any == nil { return nil } var array []int32 - switch value := i.(type) { + switch value := any.(type) { case string: if value == "" { return []int32{} @@ -233,24 +247,49 @@ func Int32s(i interface{}) []int32 { array[k] = Int32(v) } default: - if v, ok := i.(apiInts); ok { + if v, ok := any.(apiInts); ok { return Int32s(v.Ints()) } - if v, ok := i.(apiInterfaces); ok { + if v, ok := any.(apiInterfaces); ok { return Int32s(v.Interfaces()) } - return []int32{Int32(i)} + // Not a common type, it then uses reflection for conversion. + var reflectValue reflect.Value + if v, ok := value.(reflect.Value); ok { + reflectValue = v + } else { + reflectValue = reflect.ValueOf(value) + } + reflectKind := reflectValue.Kind() + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { + case reflect.Slice, reflect.Array: + var ( + length = reflectValue.Len() + slice = make([]int32, length) + ) + for i := 0; i < length; i++ { + slice[i] = Int32(reflectValue.Index(i).Interface()) + } + return slice + + default: + return []int32{Int32(any)} + } } return array } // Int64s converts to []int64. -func Int64s(i interface{}) []int64 { - if i == nil { +func Int64s(any interface{}) []int64 { + if any == nil { return nil } var array []int64 - switch value := i.(type) { + switch value := any.(type) { case string: if value == "" { return []int64{} @@ -338,13 +377,38 @@ func Int64s(i interface{}) []int64 { array[k] = Int64(v) } default: - if v, ok := i.(apiInts); ok { + if v, ok := any.(apiInts); ok { return Int64s(v.Ints()) } - if v, ok := i.(apiInterfaces); ok { + if v, ok := any.(apiInterfaces); ok { return Int64s(v.Interfaces()) } - return []int64{Int64(i)} + // Not a common type, it then uses reflection for conversion. + var reflectValue reflect.Value + if v, ok := value.(reflect.Value); ok { + reflectValue = v + } else { + reflectValue = reflect.ValueOf(value) + } + reflectKind := reflectValue.Kind() + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { + case reflect.Slice, reflect.Array: + var ( + length = reflectValue.Len() + slice = make([]int64, length) + ) + for i := 0; i < length; i++ { + slice[i] = Int64(reflectValue.Index(i).Interface()) + } + return slice + + default: + return []int64{Int64(any)} + } } return array } diff --git a/util/gconv/gconv_slice_str.go b/util/gconv/gconv_slice_str.go index f4841ec82..02c24f288 100644 --- a/util/gconv/gconv_slice_str.go +++ b/util/gconv/gconv_slice_str.go @@ -9,17 +9,17 @@ package gconv import "reflect" // SliceStr is alias of Strings. -func SliceStr(i interface{}) []string { - return Strings(i) +func SliceStr(any interface{}) []string { + return Strings(any) } // Strings converts to []string. -func Strings(i interface{}) []string { - if i == nil { +func Strings(any interface{}) []string { + if any == nil { return nil } var array []string - switch value := i.(type) { + switch value := any.(type) { case []int: array = make([]string, len(value)) for k, v := range value { @@ -98,23 +98,37 @@ func Strings(i interface{}) []string { array[k] = String(v) } default: - if v, ok := i.(apiStrings); ok { + if v, ok := any.(apiStrings); ok { return v.Strings() } - if v, ok := i.(apiInterfaces); ok { + if v, ok := any.(apiInterfaces); ok { return Strings(v.Interfaces()) } - // Use reflect feature at last. - rv := reflect.ValueOf(i) - switch rv.Kind() { + // Not a common type, it then uses reflection for conversion. + var reflectValue reflect.Value + if v, ok := value.(reflect.Value); ok { + reflectValue = v + } else { + reflectValue = reflect.ValueOf(value) + } + reflectKind := reflectValue.Kind() + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { case reflect.Slice, reflect.Array: - length := rv.Len() - array = make([]string, length) - for n := 0; n < length; n++ { - array[n] = String(rv.Index(n).Interface()) + var ( + length = reflectValue.Len() + slice = make([]string, length) + ) + for i := 0; i < length; i++ { + slice[i] = String(reflectValue.Index(i).Interface()) } + return slice + default: - return []string{String(i)} + return []string{String(any)} } } return array diff --git a/util/gconv/gconv_slice_uint.go b/util/gconv/gconv_slice_uint.go index 167812c97..8573ffd5b 100644 --- a/util/gconv/gconv_slice_uint.go +++ b/util/gconv/gconv_slice_uint.go @@ -9,28 +9,28 @@ package gconv import "reflect" // SliceUint is alias of Uints. -func SliceUint(i interface{}) []uint { - return Uints(i) +func SliceUint(any interface{}) []uint { + return Uints(any) } // SliceUint32 is alias of Uint32s. -func SliceUint32(i interface{}) []uint32 { - return Uint32s(i) +func SliceUint32(any interface{}) []uint32 { + return Uint32s(any) } // SliceUint64 is alias of Uint64s. -func SliceUint64(i interface{}) []uint64 { - return Uint64s(i) +func SliceUint64(any interface{}) []uint64 { + return Uint64s(any) } // Uints converts to []uint. -func Uints(i interface{}) []uint { - if i == nil { +func Uints(any interface{}) []uint { + if any == nil { return nil } var array []uint - switch value := i.(type) { + switch value := any.(type) { case string: if value == "" { return []uint{} @@ -113,35 +113,49 @@ func Uints(i interface{}) []uint { array[k] = Uint(v) } default: - if v, ok := i.(apiUints); ok { + if v, ok := any.(apiUints); ok { return v.Uints() } - if v, ok := i.(apiInterfaces); ok { + if v, ok := any.(apiInterfaces); ok { return Uints(v.Interfaces()) } - // Use reflect feature at last. - rv := reflect.ValueOf(i) - switch rv.Kind() { + // Not a common type, it then uses reflection for conversion. + var reflectValue reflect.Value + if v, ok := value.(reflect.Value); ok { + reflectValue = v + } else { + reflectValue = reflect.ValueOf(value) + } + reflectKind := reflectValue.Kind() + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { case reflect.Slice, reflect.Array: - length := rv.Len() - array = make([]uint, length) - for n := 0; n < length; n++ { - array[n] = Uint(rv.Index(n).Interface()) + var ( + length = reflectValue.Len() + slice = make([]uint, length) + ) + for i := 0; i < length; i++ { + slice[i] = Uint(reflectValue.Index(i).Interface()) } + return slice + default: - return []uint{Uint(i)} + return []uint{Uint(any)} } } return array } // Uint32s converts to []uint32. -func Uint32s(i interface{}) []uint32 { - if i == nil { +func Uint32s(any interface{}) []uint32 { + if any == nil { return nil } var array []uint32 - switch value := i.(type) { + switch value := any.(type) { case string: if value == "" { return []uint32{} @@ -224,35 +238,49 @@ func Uint32s(i interface{}) []uint32 { array[k] = Uint32(v) } default: - if v, ok := i.(apiUints); ok { + if v, ok := any.(apiUints); ok { return Uint32s(v.Uints()) } - if v, ok := i.(apiInterfaces); ok { + if v, ok := any.(apiInterfaces); ok { return Uint32s(v.Interfaces()) } - // Use reflect feature at last. - rv := reflect.ValueOf(i) - switch rv.Kind() { + // Not a common type, it then uses reflection for conversion. + var reflectValue reflect.Value + if v, ok := value.(reflect.Value); ok { + reflectValue = v + } else { + reflectValue = reflect.ValueOf(value) + } + reflectKind := reflectValue.Kind() + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { case reflect.Slice, reflect.Array: - length := rv.Len() - array = make([]uint32, length) - for n := 0; n < length; n++ { - array[n] = Uint32(rv.Index(n).Interface()) + var ( + length = reflectValue.Len() + slice = make([]uint32, length) + ) + for i := 0; i < length; i++ { + slice[i] = Uint32(reflectValue.Index(i).Interface()) } + return slice + default: - return []uint32{Uint32(i)} + return []uint32{Uint32(any)} } } return array } // Uint64s converts to []uint64. -func Uint64s(i interface{}) []uint64 { - if i == nil { +func Uint64s(any interface{}) []uint64 { + if any == nil { return nil } var array []uint64 - switch value := i.(type) { + switch value := any.(type) { case string: if value == "" { return []uint64{} @@ -335,23 +363,37 @@ func Uint64s(i interface{}) []uint64 { array[k] = Uint64(v) } default: - if v, ok := i.(apiUints); ok { + if v, ok := any.(apiUints); ok { return Uint64s(v.Uints()) } - if v, ok := i.(apiInterfaces); ok { + if v, ok := any.(apiInterfaces); ok { return Uint64s(v.Interfaces()) } - // Use reflect feature at last. - rv := reflect.ValueOf(i) - switch rv.Kind() { + // Not a common type, it then uses reflection for conversion. + var reflectValue reflect.Value + if v, ok := value.(reflect.Value); ok { + reflectValue = v + } else { + reflectValue = reflect.ValueOf(value) + } + reflectKind := reflectValue.Kind() + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { case reflect.Slice, reflect.Array: - length := rv.Len() - array = make([]uint64, length) - for n := 0; n < length; n++ { - array[n] = Uint64(rv.Index(n).Interface()) + var ( + length = reflectValue.Len() + slice = make([]uint64, length) + ) + for i := 0; i < length; i++ { + slice[i] = Uint64(reflectValue.Index(i).Interface()) } + return slice + default: - return []uint64{Uint64(i)} + return []uint64{Uint64(any)} } } return array diff --git a/util/gconv/gconv_time.go b/util/gconv/gconv_time.go index c55daec06..866cd3513 100644 --- a/util/gconv/gconv_time.go +++ b/util/gconv/gconv_time.go @@ -14,14 +14,14 @@ import ( ) // Time converts to time.Time. -func Time(i interface{}, format ...string) time.Time { +func Time(any interface{}, format ...string) time.Time { // It's already this type. if len(format) == 0 { - if v, ok := i.(time.Time); ok { + if v, ok := any.(time.Time); ok { return v } } - if t := GTime(i, format...); t != nil { + if t := GTime(any, format...); t != nil { return t.Time } return time.Time{} @@ -30,34 +30,34 @@ func Time(i interface{}, format ...string) time.Time { // Duration converts to time.Duration. // If is string, then it uses time.ParseDuration to convert it. // If is numeric, then it converts as nanoseconds. -func Duration(i interface{}) time.Duration { +func Duration(any interface{}) time.Duration { // It's already this type. - if v, ok := i.(time.Duration); ok { + if v, ok := any.(time.Duration); ok { return v } - s := String(i) + s := String(any) if !utils.IsNumeric(s) { d, _ := gtime.ParseDuration(s) return d } - return time.Duration(Int64(i)) + return time.Duration(Int64(any)) } // GTime converts to *gtime.Time. // The parameter can be used to specify the format of . // If no given, it converts using gtime.NewFromTimeStamp if is numeric, // or using gtime.StrToTime if is string. -func GTime(i interface{}, format ...string) *gtime.Time { - if i == nil { +func GTime(any interface{}, format ...string) *gtime.Time { + if any == nil { return nil } // It's already this type. if len(format) == 0 { - if v, ok := i.(*gtime.Time); ok { + if v, ok := any.(*gtime.Time); ok { return v } } - s := String(i) + s := String(any) if len(s) == 0 { return gtime.New() } diff --git a/util/gconv/gconv_z_unit_all_test.go b/util/gconv/gconv_z_unit_all_test.go index c70ec6e3f..2441e8e6b 100644 --- a/util/gconv/gconv_z_unit_all_test.go +++ b/util/gconv/gconv_z_unit_all_test.go @@ -38,8 +38,8 @@ func (s1 S1) Error() string { func Test_Bool_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.AssertEQ(gconv.Bool(i), false) + var any interface{} = nil + t.AssertEQ(gconv.Bool(any), false) t.AssertEQ(gconv.Bool(false), false) t.AssertEQ(gconv.Bool(nil), false) t.AssertEQ(gconv.Bool(0), false) @@ -72,8 +72,8 @@ func Test_Bool_All(t *testing.T) { func Test_Int_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.AssertEQ(gconv.Int(i), 0) + var any interface{} = nil + t.AssertEQ(gconv.Int(any), 0) t.AssertEQ(gconv.Int(false), 0) t.AssertEQ(gconv.Int(nil), 0) t.Assert(gconv.Int(nil), 0) @@ -107,8 +107,8 @@ func Test_Int_All(t *testing.T) { func Test_Int8_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.Assert(gconv.Int8(i), int8(0)) + var any interface{} = nil + t.Assert(gconv.Int8(any), int8(0)) t.AssertEQ(gconv.Int8(false), int8(0)) t.AssertEQ(gconv.Int8(nil), int8(0)) t.AssertEQ(gconv.Int8(0), int8(0)) @@ -141,8 +141,8 @@ func Test_Int8_All(t *testing.T) { func Test_Int16_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.Assert(gconv.Int16(i), int16(0)) + var any interface{} = nil + t.Assert(gconv.Int16(any), int16(0)) t.AssertEQ(gconv.Int16(false), int16(0)) t.AssertEQ(gconv.Int16(nil), int16(0)) t.AssertEQ(gconv.Int16(0), int16(0)) @@ -175,8 +175,8 @@ func Test_Int16_All(t *testing.T) { func Test_Int32_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.Assert(gconv.Int32(i), int32(0)) + var any interface{} = nil + t.Assert(gconv.Int32(any), int32(0)) t.AssertEQ(gconv.Int32(false), int32(0)) t.AssertEQ(gconv.Int32(nil), int32(0)) t.AssertEQ(gconv.Int32(0), int32(0)) @@ -209,11 +209,11 @@ func Test_Int32_All(t *testing.T) { func Test_Int64_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil + var any interface{} = nil t.AssertEQ(gconv.Int64("0x00e"), int64(14)) t.Assert(gconv.Int64("022"), int64(18)) - t.Assert(gconv.Int64(i), int64(0)) + t.Assert(gconv.Int64(any), int64(0)) t.Assert(gconv.Int64(true), 1) t.Assert(gconv.Int64("1"), int64(1)) t.Assert(gconv.Int64("0"), int64(0)) @@ -263,8 +263,8 @@ func Test_Int64_All(t *testing.T) { func Test_Uint_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.AssertEQ(gconv.Uint(i), uint(0)) + var any interface{} = nil + t.AssertEQ(gconv.Uint(any), uint(0)) t.AssertEQ(gconv.Uint(false), uint(0)) t.AssertEQ(gconv.Uint(nil), uint(0)) t.Assert(gconv.Uint(nil), uint(0)) @@ -298,8 +298,8 @@ func Test_Uint_All(t *testing.T) { func Test_Uint8_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.Assert(gconv.Uint8(i), uint8(0)) + var any interface{} = nil + t.Assert(gconv.Uint8(any), uint8(0)) t.AssertEQ(gconv.Uint8(uint8(1)), uint8(1)) t.AssertEQ(gconv.Uint8(false), uint8(0)) t.AssertEQ(gconv.Uint8(nil), uint8(0)) @@ -333,8 +333,8 @@ func Test_Uint8_All(t *testing.T) { func Test_Uint16_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.Assert(gconv.Uint16(i), uint16(0)) + var any interface{} = nil + t.Assert(gconv.Uint16(any), uint16(0)) t.AssertEQ(gconv.Uint16(uint16(1)), uint16(1)) t.AssertEQ(gconv.Uint16(false), uint16(0)) t.AssertEQ(gconv.Uint16(nil), uint16(0)) @@ -368,8 +368,8 @@ func Test_Uint16_All(t *testing.T) { func Test_Uint32_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.Assert(gconv.Uint32(i), uint32(0)) + var any interface{} = nil + t.Assert(gconv.Uint32(any), uint32(0)) t.AssertEQ(gconv.Uint32(uint32(1)), uint32(1)) t.AssertEQ(gconv.Uint32(false), uint32(0)) t.AssertEQ(gconv.Uint32(nil), uint32(0)) @@ -403,11 +403,11 @@ func Test_Uint32_All(t *testing.T) { func Test_Uint64_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil + var any interface{} = nil t.AssertEQ(gconv.Uint64("0x00e"), uint64(14)) t.Assert(gconv.Uint64("022"), uint64(18)) - t.AssertEQ(gconv.Uint64(i), uint64(0)) + t.AssertEQ(gconv.Uint64(any), uint64(0)) t.AssertEQ(gconv.Uint64(true), uint64(1)) t.Assert(gconv.Uint64("1"), int64(1)) t.Assert(gconv.Uint64("0"), uint64(0)) @@ -457,8 +457,8 @@ func Test_Uint64_All(t *testing.T) { func Test_Float32_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.Assert(gconv.Float32(i), float32(0)) + var any interface{} = nil + t.Assert(gconv.Float32(any), float32(0)) t.AssertEQ(gconv.Float32(false), float32(0)) t.AssertEQ(gconv.Float32(nil), float32(0)) t.AssertEQ(gconv.Float32(0), float32(0)) @@ -491,8 +491,8 @@ func Test_Float32_All(t *testing.T) { func Test_Float64_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.Assert(gconv.Float64(i), float64(0)) + var any interface{} = nil + t.Assert(gconv.Float64(any), float64(0)) t.AssertEQ(gconv.Float64(false), float64(0)) t.AssertEQ(gconv.Float64(nil), float64(0)) t.AssertEQ(gconv.Float64(0), float64(0)) @@ -527,8 +527,8 @@ func Test_String_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { var s []rune t.AssertEQ(gconv.String(s), "") - var i interface{} = nil - t.AssertEQ(gconv.String(i), "") + var any interface{} = nil + t.AssertEQ(gconv.String(any), "") t.AssertEQ(gconv.String("1"), "1") t.AssertEQ(gconv.String("0"), string("0")) t.Assert(gconv.String("X"), string("X")) @@ -615,8 +615,8 @@ func Test_Byte_All(t *testing.T) { func Test_Convert_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.AssertEQ(gconv.Convert(i, "string"), "") + var any interface{} = nil + t.AssertEQ(gconv.Convert(any, "string"), "") t.AssertEQ(gconv.Convert("1", "string"), "1") t.Assert(gconv.Convert(int64(1), "int64"), int64(1)) t.Assert(gconv.Convert(int(0), "int"), int(0)) diff --git a/util/gconv/gconv_z_unit_bool_test.go b/util/gconv/gconv_z_unit_bool_test.go index 7e0c97a5e..cfe2b143e 100644 --- a/util/gconv/gconv_z_unit_bool_test.go +++ b/util/gconv/gconv_z_unit_bool_test.go @@ -18,8 +18,8 @@ type boolStruct struct { func Test_Bool(t *testing.T) { gtest.C(t, func(t *gtest.T) { - var i interface{} = nil - t.AssertEQ(gconv.Bool(i), false) + var any interface{} = nil + t.AssertEQ(gconv.Bool(any), false) t.AssertEQ(gconv.Bool(false), false) t.AssertEQ(gconv.Bool(nil), false) t.AssertEQ(gconv.Bool(0), false) diff --git a/util/gconv/gconv_z_unit_slice_test.go b/util/gconv/gconv_z_unit_slice_test.go index 42c8bdec3..062b6fc17 100644 --- a/util/gconv/gconv_z_unit_slice_test.go +++ b/util/gconv/gconv_z_unit_slice_test.go @@ -7,6 +7,7 @@ package gconv_test import ( + "github.com/gogf/gf/container/gvar" "testing" "github.com/gogf/gf/frame/g" @@ -23,6 +24,13 @@ func Test_Slice(t *testing.T) { t.AssertEQ(gconv.Floats(value), []float64{123.456}) t.AssertEQ(gconv.Interfaces(value), []interface{}{123.456}) }) + gtest.C(t, func(t *gtest.T) { + s := []*gvar.Var{ + gvar.New(1), + gvar.New(2), + } + t.AssertEQ(gconv.SliceInt64(s), []int64{1, 2}) + }) } func Test_Slice_Empty(t *testing.T) { From 8ae0bd148b950a98f018b98613e4d930e01e573d Mon Sep 17 00:00:00 2001 From: jianchenma Date: Fri, 5 Feb 2021 17:42:05 +0800 Subject: [PATCH 148/492] improve ScanList for package gdb; improve Structs for package gconv --- database/gdb/gdb_type_result_scanlist.go | 19 +- database/gdb/gdb_z_mysql_internal_test.go | 320 ++++++++++++++++++ .../gdb/gdb_z_mysql_time_maintain_test.go | 6 +- util/gconv/gconv_structs.go | 43 ++- 4 files changed, 363 insertions(+), 25 deletions(-) diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index 9494ef514..c74815389 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -207,7 +207,12 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio } case reflect.Ptr: - e := reflect.New(bindToAttrType.Elem()).Elem() + var element reflect.Value + if bindToAttrValue.IsNil() { + element = reflect.New(bindToAttrType.Elem()).Elem() + } else { + element = bindToAttrValue.Elem() + } if len(relationDataMap) > 0 { relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) if relationFromAttrField.IsValid() { @@ -216,7 +221,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // There's no relational data. continue } - if err = gconv.Struct(v, e); err != nil { + if err = gconv.Struct(v, element); err != nil { return err } } else { @@ -229,14 +234,13 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // There's no relational data. continue } - if err = gconv.Struct(v, e); err != nil { + if err = gconv.Struct(v, element); err != nil { return err } } - bindToAttrValue.Set(e.Addr()) + bindToAttrValue.Set(element.Addr()) case reflect.Struct: - e := reflect.New(bindToAttrType).Elem() if len(relationDataMap) > 0 { relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) if relationFromAttrField.IsValid() { @@ -245,7 +249,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // There's no relational data. continue } - if err = gconv.Struct(relationDataItem, e); err != nil { + if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil { return err } } else { @@ -258,11 +262,10 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // There's no relational data. continue } - if err = gconv.Struct(relationDataItem, e); err != nil { + if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil { return err } } - bindToAttrValue.Set(e) default: return gerror.Newf(`unsupported attribute type: %s`, bindToAttrKind.String()) diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 4e3bf14c5..45946fc31 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -336,3 +336,323 @@ func TestResult_Structs1(t *testing.T) { t.Assert(array[1].Name, "smith") }) } + +// https://github.com/gogf/gf/issues/1159 +func Test_ScanList_NoRecreate_PtrAttribute(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type S1 struct { + Id int + Name string + Age int + Score int + } + type S3 struct { + One *S1 + } + var ( + s []*S3 + err error + ) + r1 := Result{ + Record{ + "id": gvar.New(1), + "name": gvar.New("john"), + "age": gvar.New(16), + }, + Record{ + "id": gvar.New(2), + "name": gvar.New("smith"), + "age": gvar.New(18), + }, + } + err = r1.ScanList(&s, "One") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + + r2 := Result{ + Record{ + "id": gvar.New(1), + "age": gvar.New(20), + }, + Record{ + "id": gvar.New(2), + "age": gvar.New(21), + }, + } + err = r2.ScanList(&s, "One", "One", "id:Id") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 20) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 21) + }) +} + +// https://github.com/gogf/gf/issues/1159 +func Test_ScanList_NoRecreate_StructAttribute(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type S1 struct { + Id int + Name string + Age int + Score int + } + type S3 struct { + One S1 + } + var ( + s []*S3 + err error + ) + r1 := Result{ + Record{ + "id": gvar.New(1), + "name": gvar.New("john"), + "age": gvar.New(16), + }, + Record{ + "id": gvar.New(2), + "name": gvar.New("smith"), + "age": gvar.New(18), + }, + } + err = r1.ScanList(&s, "One") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + + r2 := Result{ + Record{ + "id": gvar.New(1), + "age": gvar.New(20), + }, + Record{ + "id": gvar.New(2), + "age": gvar.New(21), + }, + } + err = r2.ScanList(&s, "One", "One", "id:Id") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 20) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 21) + }) +} + +// https://github.com/gogf/gf/issues/1159 +func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type S1 struct { + Id int + Name string + Age int + Score int + } + type S2 struct { + Id int + Pid int + Name string + Age int + Score int + } + type S3 struct { + One *S1 + Many []*S2 + } + var ( + s []*S3 + err error + ) + r1 := Result{ + Record{ + "id": gvar.New(1), + "name": gvar.New("john"), + "age": gvar.New(16), + }, + Record{ + "id": gvar.New(2), + "name": gvar.New("smith"), + "age": gvar.New(18), + }, + } + err = r1.ScanList(&s, "One") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + + r2 := Result{ + Record{ + "id": gvar.New(100), + "pid": gvar.New(1), + "age": gvar.New(30), + "name": gvar.New("john"), + }, + Record{ + "id": gvar.New(200), + "pid": gvar.New(1), + "age": gvar.New(31), + "name": gvar.New("smith"), + }, + } + err = r2.ScanList(&s, "Many", "One", "pid:Id") + //fmt.Printf("%+v", err) + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(len(s[0].Many), 2) + t.Assert(s[0].Many[0].Name, "john") + t.Assert(s[0].Many[0].Age, 30) + t.Assert(s[0].Many[1].Name, "smith") + t.Assert(s[0].Many[1].Age, 31) + + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + t.Assert(len(s[1].Many), 0) + + r3 := Result{ + Record{ + "id": gvar.New(100), + "pid": gvar.New(1), + "age": gvar.New(40), + }, + Record{ + "id": gvar.New(200), + "pid": gvar.New(1), + "age": gvar.New(41), + }, + } + err = r3.ScanList(&s, "Many", "One", "pid:Id") + //fmt.Printf("%+v", err) + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(len(s[0].Many), 2) + t.Assert(s[0].Many[0].Name, "john") + t.Assert(s[0].Many[0].Age, 40) + t.Assert(s[0].Many[1].Name, "smith") + t.Assert(s[0].Many[1].Age, 41) + + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + t.Assert(len(s[1].Many), 0) + }) +} + +// https://github.com/gogf/gf/issues/1159 +func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type S1 struct { + Id int + Name string + Age int + Score int + } + type S2 struct { + Id int + Pid int + Name string + Age int + Score int + } + type S3 struct { + One S1 + Many []S2 + } + var ( + s []S3 + err error + ) + r1 := Result{ + Record{ + "id": gvar.New(1), + "name": gvar.New("john"), + "age": gvar.New(16), + }, + Record{ + "id": gvar.New(2), + "name": gvar.New("smith"), + "age": gvar.New(18), + }, + } + err = r1.ScanList(&s, "One") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + + r2 := Result{ + Record{ + "id": gvar.New(100), + "pid": gvar.New(1), + "age": gvar.New(30), + "name": gvar.New("john"), + }, + Record{ + "id": gvar.New(200), + "pid": gvar.New(1), + "age": gvar.New(31), + "name": gvar.New("smith"), + }, + } + err = r2.ScanList(&s, "Many", "One", "pid:Id") + //fmt.Printf("%+v", err) + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(len(s[0].Many), 2) + t.Assert(s[0].Many[0].Name, "john") + t.Assert(s[0].Many[0].Age, 30) + t.Assert(s[0].Many[1].Name, "smith") + t.Assert(s[0].Many[1].Age, 31) + + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + t.Assert(len(s[1].Many), 0) + + r3 := Result{ + Record{ + "id": gvar.New(100), + "pid": gvar.New(1), + "age": gvar.New(40), + }, + Record{ + "id": gvar.New(200), + "pid": gvar.New(1), + "age": gvar.New(41), + }, + } + err = r3.ScanList(&s, "Many", "One", "pid:Id") + //fmt.Printf("%+v", err) + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(len(s[0].Many), 2) + t.Assert(s[0].Many[0].Name, "john") + t.Assert(s[0].Many[0].Age, 40) + t.Assert(s[0].Many[1].Name, "smith") + t.Assert(s[0].Many[1].Age, 41) + + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + t.Assert(len(s[1].Many), 0) + }) +} diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index 4bb71398a..74bce2b97 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -467,7 +467,7 @@ CREATE TABLE %s ( } func Test_SoftDelete(t *testing.T) { - table := "time_test_table" + table := "time_test_table_" + gtime.TimestampNanoStr() if _, err := db.Exec(fmt.Sprintf(` CREATE TABLE %s ( id int(11) NOT NULL, @@ -606,7 +606,7 @@ CREATE TABLE %s ( } func Test_SoftDelete_WhereAndOr(t *testing.T) { - table := "time_test_table" + table := "time_test_table_" + gtime.TimestampNanoStr() if _, err := db.Exec(fmt.Sprintf(` CREATE TABLE %s ( id int(11) NOT NULL, @@ -648,7 +648,7 @@ CREATE TABLE %s ( } func Test_CreateUpdateTime_Struct(t *testing.T) { - table := "time_test_table" + table := "time_test_table_" + gtime.TimestampNanoStr() if _, err := db.Exec(fmt.Sprintf(` CREATE TABLE %s ( id int(11) NOT NULL, diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index 84133ca41..0eccae8aa 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -91,27 +91,42 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st return nil } var ( - array = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsMaps), len(paramsMaps)) - itemType = array.Index(0).Type() + reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsMaps), len(paramsMaps)) + itemType = reflectElemArray.Index(0).Type() + itemTypeKind = itemType.Kind() + pointerRvElem = pointerRv.Elem() + pointerRvLength = pointerRvElem.Len() ) - for i := 0; i < len(paramsMaps); i++ { - if itemType.Kind() == reflect.Ptr { - // Slice element is type pointer. - e := reflect.New(itemType.Elem()).Elem() - if err = Struct(paramsMaps[i], e, mapping...); err != nil { + if itemTypeKind == reflect.Ptr { + // Pointer element. + for i := 0; i < len(paramsMaps); i++ { + var tempReflectValue reflect.Value + if i < pointerRvLength { + tempReflectValue = pointerRvElem.Index(i).Elem() + } else { + tempReflectValue = reflect.New(itemType.Elem()).Elem() + } + if err = Struct(paramsMaps[i], tempReflectValue, mapping...); err != nil { return err } - array.Index(i).Set(e.Addr()) - } else { - // Slice element is not type of pointer. - e := reflect.New(itemType).Elem() - if err = Struct(paramsMaps[i], e, mapping...); err != nil { + reflectElemArray.Index(i).Set(tempReflectValue.Addr()) + } + } else { + // Struct element. + for i := 0; i < len(paramsMaps); i++ { + var tempReflectValue reflect.Value + if i < pointerRvLength { + tempReflectValue = pointerRvElem.Index(i) + } else { + tempReflectValue = reflect.New(itemType).Elem() + } + if err = Struct(paramsMaps[i], tempReflectValue, mapping...); err != nil { return err } - array.Index(i).Set(e) + reflectElemArray.Index(i).Set(tempReflectValue) } } - pointerRv.Elem().Set(array) + pointerRv.Elem().Set(reflectElemArray) return nil } From 3bb909ba4f161c14e7fd9b231ad7b18daff114ab Mon Sep 17 00:00:00 2001 From: john Date: Sat, 6 Feb 2021 11:42:58 +0800 Subject: [PATCH 149/492] improve comment for package gspath --- os/gspath/gspath.go | 44 +++++++++++++++++------------------ os/gspath/gspath_unit_test.go | 5 ++++ 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/os/gspath/gspath.go b/os/gspath/gspath.go index c628968d8..d17ce2658 100644 --- a/os/gspath/gspath.go +++ b/os/gspath/gspath.go @@ -59,7 +59,7 @@ func New(path string, cache bool) *SPath { } // Get creates and returns a instance of searching manager for given path. -// The parameter specifies whether using cache feature for this manager. +// The parameter `cache` specifies whether using cache feature for this manager. // If cache feature is enabled, it asynchronously and recursively scans the path // and updates all sub files/folders to the cache using package gfsnotify. func Get(root string, cache bool) *SPath { @@ -71,24 +71,24 @@ func Get(root string, cache bool) *SPath { }).(*SPath) } -// Search searches file under path . -// The parameter should be a absolute path. It will not automatically -// convert to absolute path for performance reason. -// The optional parameter specifies the searching index files when the result is a directory. -// For example, if the result is a directory, and is [index.html, main.html], it will also -// search [index.html, main.html] under . It returns the absolute file path if any of them found, -// or else it returns . +// Search searches file `name` under path `root`. +// The parameter `root` should be a absolute path. It will not automatically +// convert `root` to absolute path for performance reason. +// The optional parameter `indexFiles` specifies the searching index files when the result is a directory. +// For example, if the result `filePath` is a directory, and `indexFiles` is [index.html, main.html], it will also +// search [index.html, main.html] under `filePath`. It returns the absolute file path if any of them found, +// or else it returns `filePath`. func Search(root string, name string, indexFiles ...string) (filePath string, isDir bool) { return Get(root, false).Search(name, indexFiles...) } -// SearchWithCache searches file under path with cache feature enabled. -// The parameter should be a absolute path. It will not automatically -// convert to absolute path for performance reason. -// The optional parameter specifies the searching index files when the result is a directory. -// For example, if the result is a directory, and is [index.html, main.html], it will also -// search [index.html, main.html] under . It returns the absolute file path if any of them found, -// or else it returns . +// SearchWithCache searches file `name` under path `root` with cache feature enabled. +// The parameter `root` should be a absolute path. It will not automatically +// convert `root` to absolute path for performance reason. +// The optional parameter `indexFiles` specifies the searching index files when the result is a directory. +// For example, if the result `filePath` is a directory, and `indexFiles` is [index.html, main.html], it will also +// search [index.html, main.html] under `filePath`. It returns the absolute file path if any of them found, +// or else it returns `filePath`. func SearchWithCache(root string, name string, indexFiles ...string) (filePath string, isDir bool) { return Get(root, true).Search(name, indexFiles...) } @@ -156,11 +156,11 @@ func (sp *SPath) Add(path string) (realPath string, err error) { } } -// Search searches file in the manager. -// The optional parameter specifies the searching index files when the result is a directory. -// For example, if the result is a directory, and is [index.html, main.html], it will also -// search [index.html, main.html] under . It returns the absolute file path if any of them found, -// or else it returns . +// Search searches file `name` in the manager. +// The optional parameter `indexFiles` specifies the searching index files when the result is a directory. +// For example, if the result `filePath` is a directory, and `indexFiles` is [index.html, main.html], it will also +// search [index.html, main.html] under `filePath`. It returns the absolute file path if any of them found, +// or else it returns `filePath`. func (sp *SPath) Search(name string, indexFiles ...string) (filePath string, isDir bool) { // No cache enabled. if sp.cache == nil { @@ -213,8 +213,8 @@ func (sp *SPath) Search(name string, indexFiles ...string) (filePath string, isD return } -// Remove deletes the from cache files of the manager. -// The parameter can be either a absolute path or just a relative file name. +// Remove deletes the `path` from cache files of the manager. +// The parameter `path` can be either a absolute path or just a relative file name. func (sp *SPath) Remove(path string) { if sp.cache == nil { return diff --git a/os/gspath/gspath_unit_test.go b/os/gspath/gspath_unit_test.go index 32cc09218..94b741337 100644 --- a/os/gspath/gspath_unit_test.go +++ b/os/gspath/gspath_unit_test.go @@ -90,22 +90,27 @@ func TestSPath_Basic(t *testing.T) { realPath, err = gsp.Add("gf_tmp1") t.Assert(err != nil, false) t.Assert(realPath, gfile.Join(root, "gf_tmp1")) + realPath, err = gsp.Add("gf_tmp3") t.Assert(err != nil, true) t.Assert(realPath, "") + gsp.Remove(gfile.Join(root, "gf_tmp")) gsp.Remove(gfile.Join(root, "gf_tmp1")) gsp.Remove(gfile.Join(root, "gf_tmp3")) t.Assert(gsp.Size(), 3) t.Assert(len(gsp.Paths()), 3) + gsp.AllPaths() gsp.Set(root) fp, isDir = gsp.Search("gf_tmp") t.Assert(fp, gfile.Join(root, "gf_tmp")) t.Assert(isDir, true) + fp, isDir = gsp.Search("gf_tmp", "gf.txt") t.Assert(fp, gfile.Join(root, "gf_tmp", "gf.txt")) t.Assert(isDir, false) + fp, isDir = gsp.Search("/", "gf.txt") t.Assert(fp, pwd) t.Assert(isDir, true) From 2344701f228ac2c19ae772a9be0d49ef3f948c98 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Sun, 7 Feb 2021 10:31:59 +0800 Subject: [PATCH 150/492] README updates --- README.MD | 4 ++-- README_ZH.MD | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.MD b/README.MD index 2e7e71125..20a588755 100644 --- a/README.MD +++ b/README.MD @@ -98,8 +98,8 @@ We appreciate any kind of sponsorship for `GF` development. If you've got some i # Thanks -JetBrains -Atlassian +JetBrains +Atlassian diff --git a/README_ZH.MD b/README_ZH.MD index e9f5028fa..f53b254bb 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -113,5 +113,6 @@ golang版本 >= 1.11 赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 微信 `389961817` / 邮件 `john@goframe.org`。 # 感谢 -JetBrains -Atlassian +JetBrains +Atlassian + From f4314c318e2880ee86377c6f23525afb2c9cbd97 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Sun, 7 Feb 2021 10:40:02 +0800 Subject: [PATCH 151/492] up --- .../gdb/gdb_z_mysql_association_with_test.go | 316 ++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 database/gdb/gdb_z_mysql_association_with_test.go diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go new file mode 100644 index 000000000..1540b32d5 --- /dev/null +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -0,0 +1,316 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb_test + +//func Test_Table_Relation_With(t *testing.T) { +// var ( +// tableUser = "user_" + gtime.TimestampMicroStr() +// tableUserDetail = "user_detail_" + gtime.TimestampMicroStr() +// tableUserScores = "user_scores_" + gtime.TimestampMicroStr() +// ) +// if _, err := db.Exec(fmt.Sprintf(` +//CREATE TABLE %s ( +// uid int(10) unsigned NOT NULL AUTO_INCREMENT, +// name varchar(45) NOT NULL, +// PRIMARY KEY (uid) +//) ENGINE=InnoDB DEFAULT CHARSET=utf8; +// `, tableUser)); err != nil { +// gtest.Error(err) +// } +// defer dropTable(tableUser) +// +// if _, err := db.Exec(fmt.Sprintf(` +//CREATE TABLE %s ( +// uid int(10) unsigned NOT NULL AUTO_INCREMENT, +// address varchar(45) NOT NULL, +// PRIMARY KEY (uid) +//) ENGINE=InnoDB DEFAULT CHARSET=utf8; +// `, tableUserDetail)); err != nil { +// gtest.Error(err) +// } +// defer dropTable(tableUserDetail) +// +// if _, err := db.Exec(fmt.Sprintf(` +//CREATE TABLE %s ( +// id int(10) unsigned NOT NULL AUTO_INCREMENT, +// uid int(10) unsigned NOT NULL, +// score int(10) unsigned NOT NULL, +// PRIMARY KEY (id) +//) ENGINE=InnoDB DEFAULT CHARSET=utf8; +// `, tableUserScores)); err != nil { +// gtest.Error(err) +// } +// defer dropTable(tableUserScores) +// +// type UserDetail struct { +// Uid int `json:"uid"` +// Address string `json:"address"` +// } +// type UserScores struct { +// Id int `json:"id"` +// Uid int `json:"uid"` +// Score int `json:"score"` +// } +// +// type User struct { +// Id int `json:"id"` +// Name string `json:"name"` +// UserDetail *UserDetail `orm:"with:uid=uid"` +// UserScores []*UserScores `orm:"with:uid=id"` +// } +// +// // Initialize the data. +// var err error +// for i := 1; i <= 5; i++ { +// // User. +// _, err = db.Insert(tableUser, g.Map{ +// "id": i, +// "name": fmt.Sprintf(`name_%d`, i), +// }) +// gtest.Assert(err, nil) +// // Detail. +// _, err = db.Insert(tableUserDetail, g.Map{ +// "uid": i, +// "address": fmt.Sprintf(`address_%d`, i), +// }) +// gtest.Assert(err, nil) +// // Scores. +// for j := 1; j <= 5; j++ { +// _, err = db.Insert(tableUserScores, g.Map{ +// "uid": i, +// "score": j, +// }) +// gtest.Assert(err, nil) +// } +// } +// // MapKeyValue. +// gtest.C(t, func(t *gtest.T) { +// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() +// t.Assert(err, nil) +// t.Assert(all.Len(), 2) +// t.Assert(len(all.MapKeyValue("uid")), 2) +// t.Assert(all.MapKeyValue("uid")["3"].Map()["uid"], 3) +// t.Assert(all.MapKeyValue("uid")["4"].Map()["uid"], 4) +// all, err = db.Table(tableUserScores).Where("uid", g.Slice{3, 4}).Order("id asc").All() +// t.Assert(err, nil) +// t.Assert(all.Len(), 10) +// t.Assert(len(all.MapKeyValue("uid")), 2) +// t.Assert(len(all.MapKeyValue("uid")["3"].Slice()), 5) +// t.Assert(len(all.MapKeyValue("uid")["4"].Slice()), 5) +// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["uid"], 3) +// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["score"], 1) +// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["uid"], 3) +// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["score"], 5) +// }) +// // Result ScanList with struct elements and pointer attributes. +// gtest.C(t, func(t *gtest.T) { +// var users []Entity +// // User +// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() +// t.Assert(err, nil) +// err = all.ScanList(&users, "User") +// t.Assert(err, nil) +// t.Assert(len(users), 2) +// t.Assert(users[0].User, &EntityUser{3, "name_3"}) +// t.Assert(users[1].User, &EntityUser{4, "name_4"}) +// // Detail +// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() +// gtest.Assert(err, nil) +// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") +// t.Assert(err, nil) +// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) +// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) +// // Scores +// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() +// gtest.Assert(err, nil) +// err = all.ScanList(&users, "UserScores", "User", "uid:Uid") +// t.Assert(err, nil) +// t.Assert(len(users[0].UserScores), 5) +// t.Assert(len(users[1].UserScores), 5) +// t.Assert(users[0].UserScores[0].Uid, 3) +// t.Assert(users[0].UserScores[0].Score, 1) +// t.Assert(users[0].UserScores[4].Score, 5) +// t.Assert(users[1].UserScores[0].Uid, 4) +// t.Assert(users[1].UserScores[0].Score, 1) +// t.Assert(users[1].UserScores[4].Score, 5) +// }) +// +// // Result ScanList with pointer elements and pointer attributes. +// gtest.C(t, func(t *gtest.T) { +// var users []*Entity +// // User +// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() +// t.Assert(err, nil) +// err = all.ScanList(&users, "User") +// t.Assert(err, nil) +// t.Assert(len(users), 2) +// t.Assert(users[0].User, &EntityUser{3, "name_3"}) +// t.Assert(users[1].User, &EntityUser{4, "name_4"}) +// // Detail +// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() +// gtest.Assert(err, nil) +// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") +// t.Assert(err, nil) +// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) +// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) +// // Scores +// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() +// gtest.Assert(err, nil) +// err = all.ScanList(&users, "UserScores", "User", "uid:Uid") +// t.Assert(err, nil) +// t.Assert(len(users[0].UserScores), 5) +// t.Assert(len(users[1].UserScores), 5) +// t.Assert(users[0].UserScores[0].Uid, 3) +// t.Assert(users[0].UserScores[0].Score, 1) +// t.Assert(users[0].UserScores[4].Score, 5) +// t.Assert(users[1].UserScores[0].Uid, 4) +// t.Assert(users[1].UserScores[0].Score, 1) +// t.Assert(users[1].UserScores[4].Score, 5) +// }) +// +// // Result ScanList with struct elements and struct attributes. +// gtest.C(t, func(t *gtest.T) { +// type EntityUser struct { +// Uid int `json:"uid"` +// Name string `json:"name"` +// } +// type EntityUserDetail struct { +// Uid int `json:"uid"` +// Address string `json:"address"` +// } +// type EntityUserScores struct { +// Id int `json:"id"` +// Uid int `json:"uid"` +// Score int `json:"score"` +// } +// type Entity struct { +// User EntityUser +// UserDetail EntityUserDetail +// UserScores []EntityUserScores +// } +// var users []Entity +// // User +// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() +// t.Assert(err, nil) +// err = all.ScanList(&users, "User") +// t.Assert(err, nil) +// t.Assert(len(users), 2) +// t.Assert(users[0].User, &EntityUser{3, "name_3"}) +// t.Assert(users[1].User, &EntityUser{4, "name_4"}) +// // Detail +// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() +// gtest.Assert(err, nil) +// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") +// t.Assert(err, nil) +// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) +// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) +// // Scores +// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() +// gtest.Assert(err, nil) +// err = all.ScanList(&users, "UserScores", "User", "uid:Uid") +// t.Assert(err, nil) +// t.Assert(len(users[0].UserScores), 5) +// t.Assert(len(users[1].UserScores), 5) +// t.Assert(users[0].UserScores[0].Uid, 3) +// t.Assert(users[0].UserScores[0].Score, 1) +// t.Assert(users[0].UserScores[4].Score, 5) +// t.Assert(users[1].UserScores[0].Uid, 4) +// t.Assert(users[1].UserScores[0].Score, 1) +// t.Assert(users[1].UserScores[4].Score, 5) +// }) +// +// // Result ScanList with pointer elements and struct attributes. +// gtest.C(t, func(t *gtest.T) { +// type EntityUser struct { +// Uid int `json:"uid"` +// Name string `json:"name"` +// } +// type EntityUserDetail struct { +// Uid int `json:"uid"` +// Address string `json:"address"` +// } +// type EntityUserScores struct { +// Id int `json:"id"` +// Uid int `json:"uid"` +// Score int `json:"score"` +// } +// type Entity struct { +// User EntityUser +// UserDetail EntityUserDetail +// UserScores []EntityUserScores +// } +// var users []*Entity +// +// // User +// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() +// t.Assert(err, nil) +// err = all.ScanList(&users, "User") +// t.Assert(err, nil) +// t.Assert(len(users), 2) +// t.Assert(users[0].User, &EntityUser{3, "name_3"}) +// t.Assert(users[1].User, &EntityUser{4, "name_4"}) +// // Detail +// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() +// gtest.Assert(err, nil) +// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") +// t.Assert(err, nil) +// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) +// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) +// // Scores +// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() +// gtest.Assert(err, nil) +// err = all.ScanList(&users, "UserScores", "User", "uid:Uid") +// t.Assert(err, nil) +// t.Assert(len(users[0].UserScores), 5) +// t.Assert(len(users[1].UserScores), 5) +// t.Assert(users[0].UserScores[0].Uid, 3) +// t.Assert(users[0].UserScores[0].Score, 1) +// t.Assert(users[0].UserScores[4].Score, 5) +// t.Assert(users[1].UserScores[0].Uid, 4) +// t.Assert(users[1].UserScores[0].Score, 1) +// t.Assert(users[1].UserScores[4].Score, 5) +// }) +// +// // Model ScanList with pointer elements and pointer attributes. +// gtest.C(t, func(t *gtest.T) { +// var users []*Entity +// // User +// err := db.Table(tableUser). +// Where("uid", g.Slice{3, 4}). +// Order("uid asc"). +// ScanList(&users, "User") +// t.Assert(err, nil) +// // Detail +// err = db.Table(tableUserDetail). +// Where("uid", gdb.ListItemValues(users, "User", "Uid")). +// Order("uid asc"). +// ScanList(&users, "UserDetail", "User", "uid:Uid") +// gtest.Assert(err, nil) +// // Scores +// err = db.Table(tableUserScores). +// Where("uid", gdb.ListItemValues(users, "User", "Uid")). +// Order("id asc"). +// ScanList(&users, "UserScores", "User", "uid:Uid") +// t.Assert(err, nil) +// +// t.Assert(len(users), 2) +// t.Assert(users[0].User, &EntityUser{3, "name_3"}) +// t.Assert(users[1].User, &EntityUser{4, "name_4"}) +// +// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) +// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) +// +// t.Assert(len(users[0].UserScores), 5) +// t.Assert(len(users[1].UserScores), 5) +// t.Assert(users[0].UserScores[0].Uid, 3) +// t.Assert(users[0].UserScores[0].Score, 1) +// t.Assert(users[0].UserScores[4].Score, 5) +// t.Assert(users[1].UserScores[0].Uid, 4) +// t.Assert(users[1].UserScores[0].Score, 1) +// t.Assert(users[1].UserScores[4].Score, 5) +// }) +//} From 397e11e124c03d63dcf811d30788ea3b86a769f8 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Sun, 7 Feb 2021 14:39:32 +0800 Subject: [PATCH 152/492] improve package gdb for association feature --- database/gdb/gdb_type_result_scanlist.go | 50 +- database/gdb/gdb_z_driver_test.go | 2 +- database/gdb/gdb_z_init_test.go | 4 +- database/gdb/gdb_z_mysql_association_test.go | 693 ++++++++++++++++-- database/gdb/gdb_z_mysql_basic_test.go | 2 +- database/gdb/gdb_z_mysql_ctx_test.go | 2 +- database/gdb/gdb_z_mysql_internal_test.go | 24 +- database/gdb/gdb_z_mysql_method_test.go | 150 ++-- database/gdb/gdb_z_mysql_model_test.go | 662 ++++++++--------- database/gdb/gdb_z_mysql_raw_test.go | 8 +- database/gdb/gdb_z_mysql_struct_test.go | 60 +- .../gdb/gdb_z_mysql_time_maintain_test.go | 164 ++--- database/gdb/gdb_z_mysql_transaction_test.go | 14 +- database/gdb/gdb_z_mysql_types_test.go | 6 +- test/gtest/gtest_t.go | 37 +- test/gtest/gtest_util.go | 91 +-- 16 files changed, 1278 insertions(+), 691 deletions(-) diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index c74815389..b850ec9d1 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -11,6 +11,7 @@ import ( "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/gutil" "reflect" ) @@ -41,6 +42,9 @@ import ( // // See the example or unit testing cases for clear understanding for this function. func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) { + if r.IsEmpty() { + return nil + } // Necessary checks for parameters. if bindToAttrName == "" { return gerror.New(`bindToAttrName should not be empty`) @@ -112,7 +116,13 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio relationFromAttrName = relationKV[0] relationKVStr = relationKV[1] } - array := gstr.SplitAndTrim(relationKVStr, ":") + // The relation key string of table filed name and attribute name + // can be joined with char '=' or ':'. + array := gstr.SplitAndTrim(relationKVStr, "=") + if len(array) == 1 { + // Compatible with old splitting char ':'. + array = gstr.SplitAndTrim(relationKVStr, ":") + } if len(array) == 2 { // Defined table field to relation attribute name. // Like: @@ -120,6 +130,15 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // uid:UserId relationResultFieldName = array[0] relationBindToSubAttrName = array[1] + if key, _ := gutil.MapPossibleItemByKey(r[0].Map(), relationResultFieldName); key == "" { + return gerror.Newf( + `cannot find possible related table field name "%s" from given relation key "%s"`, + relationResultFieldName, + relationKVStr, + ) + } else { + relationResultFieldName = key + } } else { return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`) } @@ -152,8 +171,9 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // Bind to relation conditions. var ( - relationFromAttrValue reflect.Value - relationFromAttrField reflect.Value + relationFromAttrValue reflect.Value + relationFromAttrField reflect.Value + relationBindToSubAttrNameChecked bool ) for i := 0; i < arrayValue.Len(); i++ { arrayElemValue := arrayValue.Index(i) @@ -187,6 +207,30 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() { return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } + // Check and find possible bind to attribute name. + if relationKVStr != "" && !relationBindToSubAttrNameChecked { + relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) + if !relationFromAttrField.IsValid() { + var ( + relationFromAttrType = relationFromAttrValue.Type() + filedMap = make(map[string]interface{}) + ) + for i := 0; i < relationFromAttrType.NumField(); i++ { + filedMap[relationFromAttrType.Field(i).Name] = struct{}{} + } + if key, _ := gutil.MapPossibleItemByKey(filedMap, relationBindToSubAttrName); key == "" { + return gerror.Newf( + `cannot find possible related attribute name "%s" from given relation key "%s"`, + relationBindToSubAttrName, + relationKVStr, + ) + } else { + relationBindToSubAttrName = key + + } + } + relationBindToSubAttrNameChecked = true + } switch bindToAttrKind { case reflect.Array, reflect.Slice: if len(relationDataMap) > 0 { diff --git a/database/gdb/gdb_z_driver_test.go b/database/gdb/gdb_z_driver_test.go index e9f1c8883..3d2d10154 100644 --- a/database/gdb/gdb_z_driver_test.go +++ b/database/gdb/gdb_z_driver_test.go @@ -67,7 +67,7 @@ func Test_Custom_Driver(t *testing.T) { t.Assert(latestSqlString.Val(), "") sqlString := "select 10000" value, err := g.DB("driver-test").GetValue(sqlString) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value, 10000) t.Assert(latestSqlString.Val(), sqlString) }) diff --git a/database/gdb/gdb_z_init_test.go b/database/gdb/gdb_z_init_test.go index 388c410e6..6208c9577 100644 --- a/database/gdb/gdb_z_init_test.go +++ b/database/gdb/gdb_z_init_test.go @@ -38,7 +38,7 @@ func init() { "name": true, "type": true, }, false) - gtest.Assert(err, nil) + gtest.AssertNil(err) configNode = gdb.ConfigNode{ Host: "127.0.0.1", Port: "3306", @@ -195,7 +195,7 @@ func createInitTableWithDb(db gdb.DB, table ...string) (name string) { } result, err := db.BatchInsert(name, array.Slice()) - gtest.Assert(err, nil) + gtest.AssertNil(err) n, e := result.RowsAffected() gtest.Assert(e, nil) diff --git a/database/gdb/gdb_z_mysql_association_test.go b/database/gdb/gdb_z_mysql_association_test.go index 06b462017..4dbbf7db3 100644 --- a/database/gdb/gdb_z_mysql_association_test.go +++ b/database/gdb/gdb_z_mysql_association_test.go @@ -108,24 +108,24 @@ CREATE TABLE %s ( }) return err }) - t.Assert(err, nil) + t.AssertNil(err) }) // Data check. gtest.C(t, func(t *gtest.T) { r, err := db.Table(tableUser).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(r.Len(), 1) t.Assert(r[0]["uid"].Int(), 1) t.Assert(r[0]["name"].String(), "john") r, err = db.Table(tableUserDetail).Where("uid", r[0]["uid"].Int()).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(r.Len(), 1) t.Assert(r[0]["uid"].Int(), 1) t.Assert(r[0]["address"].String(), `Beijing DongZhiMen #66`) r, err = db.Table(tableUserScores).Where("uid", r[0]["uid"].Int()).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(r.Len(), 2) t.Assert(r[0]["uid"].Int(), 1) t.Assert(r[1]["uid"].Int(), 1) @@ -137,15 +137,15 @@ CREATE TABLE %s ( var user Entity // SELECT * FROM `user` WHERE `name`='john' err := db.Table(tableUser).Scan(&user.User, "name", "john") - t.Assert(err, nil) + t.AssertNil(err) // SELECT * FROM `user_detail` WHERE `uid`=1 err = db.Table(tableUserDetail).Scan(&user.UserDetail, "uid", user.User.Uid) - t.Assert(err, nil) + t.AssertNil(err) // SELECT * FROM `user_scores` WHERE `uid`=1 err = db.Table(tableUserScores).Scan(&user.UserScores, "uid", user.User.Uid) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.User, EntityUser{ Uid: 1, @@ -222,39 +222,42 @@ CREATE TABLE %s ( } // Initialize the data. - var err error - for i := 1; i <= 5; i++ { - // User. - _, err = db.Insert(tableUser, g.Map{ - "uid": i, - "name": fmt.Sprintf(`name_%d`, i), - }) - gtest.Assert(err, nil) - // Detail. - _, err = db.Insert(tableUserDetail, g.Map{ - "uid": i, - "address": fmt.Sprintf(`address_%d`, i), - }) - gtest.Assert(err, nil) - // Scores. - for j := 1; j <= 5; j++ { - _, err = db.Insert(tableUserScores, g.Map{ - "uid": i, - "score": j, + gtest.C(t, func(t *gtest.T) { + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), }) - gtest.Assert(err, nil) + t.AssertNil(err) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + t.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + t.AssertNil(err) + } } - } + }) + // MapKeyValue. gtest.C(t, func(t *gtest.T) { all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(all.Len(), 2) t.Assert(len(all.MapKeyValue("uid")), 2) t.Assert(all.MapKeyValue("uid")["3"].Map()["uid"], 3) t.Assert(all.MapKeyValue("uid")["4"].Map()["uid"], 4) all, err = db.Table(tableUserScores).Where("uid", g.Slice{3, 4}).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(all.Len(), 10) t.Assert(len(all.MapKeyValue("uid")), 2) t.Assert(len(all.MapKeyValue("uid")["3"].Slice()), 5) @@ -269,24 +272,24 @@ CREATE TABLE %s ( var users []Entity // User all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() - t.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "User") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users), 2) t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() - gtest.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() - gtest.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "uid:Uid") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users[0].UserScores), 5) t.Assert(len(users[1].UserScores), 5) t.Assert(users[0].UserScores[0].Uid, 3) @@ -302,24 +305,24 @@ CREATE TABLE %s ( var users []*Entity // User all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() - t.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "User") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users), 2) t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() - gtest.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() - gtest.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "uid:Uid") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users[0].UserScores), 5) t.Assert(len(users[1].UserScores), 5) t.Assert(users[0].UserScores[0].Uid, 3) @@ -353,24 +356,24 @@ CREATE TABLE %s ( var users []Entity // User all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() - t.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "User") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users), 2) t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() - gtest.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() - gtest.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "uid:Uid") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users[0].UserScores), 5) t.Assert(len(users[1].UserScores), 5) t.Assert(users[0].UserScores[0].Uid, 3) @@ -405,24 +408,24 @@ CREATE TABLE %s ( // User all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() - t.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "User") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users), 2) t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() - gtest.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() - gtest.Assert(err, nil) + t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "uid:Uid") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users[0].UserScores), 5) t.Assert(len(users[1].UserScores), 5) t.Assert(users[0].UserScores[0].Uid, 3) @@ -441,19 +444,19 @@ CREATE TABLE %s ( Where("uid", g.Slice{3, 4}). Order("uid asc"). ScanList(&users, "User") - t.Assert(err, nil) + t.AssertNil(err) // Detail err = db.Table(tableUserDetail). Where("uid", gdb.ListItemValues(users, "User", "Uid")). Order("uid asc"). ScanList(&users, "UserDetail", "User", "uid:Uid") - gtest.Assert(err, nil) + t.AssertNil(err) // Scores err = db.Table(tableUserScores). Where("uid", gdb.ListItemValues(users, "User", "Uid")). Order("id asc"). ScanList(&users, "UserScores", "User", "uid:Uid") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users), 2) t.Assert(users[0].User, &EntityUser{3, "name_3"}) @@ -473,6 +476,532 @@ CREATE TABLE %s ( }) } +func Test_Table_Relation_Many_RelationKeyCaseInsensitive(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampMicroStr() + tableUserDetail = "user_detail_" + gtime.TimestampMicroStr() + tableUserScores = "user_scores_" + gtime.TimestampMicroStr() + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + name varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + address varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + uid int(10) unsigned NOT NULL, + score int(10) unsigned NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + // Initialize the data. + gtest.C(t, func(t *gtest.T) { + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + t.AssertNil(err) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + t.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + t.AssertNil(err) + } + } + }) + + // MapKeyValue. + gtest.C(t, func(t *gtest.T) { + all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + t.Assert(all.Len(), 2) + t.Assert(len(all.MapKeyValue("uid")), 2) + t.Assert(all.MapKeyValue("uid")["3"].Map()["uid"], 3) + t.Assert(all.MapKeyValue("uid")["4"].Map()["uid"], 4) + all, err = db.Table(tableUserScores).Where("uid", g.Slice{3, 4}).Order("id asc").All() + t.AssertNil(err) + t.Assert(all.Len(), 10) + t.Assert(len(all.MapKeyValue("uid")), 2) + t.Assert(len(all.MapKeyValue("uid")["3"].Slice()), 5) + t.Assert(len(all.MapKeyValue("uid")["4"].Slice()), 5) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["uid"], 3) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["score"], 1) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["uid"], 3) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["score"], 5) + }) + // Result ScanList with struct elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []Entity + // User + all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid:uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "uid:uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // Result ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "Uid:UID") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "Uid:UID") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // Result ScanList with struct elements and struct attributes. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User EntityUser + UserDetail EntityUserDetail + UserScores []EntityUserScores + } + var users []Entity + // User + all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid:UId") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "UId:Uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // Result ScanList with pointer elements and struct attributes. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User EntityUser + UserDetail EntityUserDetail + UserScores []EntityUserScores + } + var users []*Entity + + // User + all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "UID:Uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // Model ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + err := db.Table(tableUser). + Where("uid", g.Slice{3, 4}). + Order("uid asc"). + ScanList(&users, "User") + t.AssertNil(err) + // Detail + err = db.Table(tableUserDetail). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("uid asc"). + ScanList(&users, "UserDetail", "User", "uid:Uid") + t.AssertNil(err) + // Scores + err = db.Table(tableUserScores). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("id asc"). + ScanList(&users, "UserScores", "User", "uid:Uid") + t.AssertNil(err) + + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_EmptyData(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampMicroStr() + tableUserDetail = "user_detail_" + gtime.TimestampMicroStr() + tableUserScores = "user_scores_" + gtime.TimestampMicroStr() + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + name varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + address varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + uid int(10) unsigned NOT NULL, + score int(10) unsigned NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + // Result ScanList with struct elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + db.SetDebug(true) + var users []Entity + // User + all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 0) + // Detail + all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid:uid") + t.AssertNil(err) + + // Scores + all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "uid:uid") + t.AssertNil(err) + }) + return + // Result ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 0) + + // Detail + all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "Uid:UID") + t.AssertNil(err) + + // Scores + all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "Uid:UID") + t.AssertNil(err) + }) + + // Result ScanList with struct elements and struct attributes. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User EntityUser + UserDetail EntityUserDetail + UserScores []EntityUserScores + } + var users []Entity + // User + all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + + // Detail + all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid:UId") + t.AssertNil(err) + + // Scores + all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "UId:Uid") + t.AssertNil(err) + }) + + // Result ScanList with pointer elements and struct attributes. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User EntityUser + UserDetail EntityUserDetail + UserScores []EntityUserScores + } + var users []*Entity + + // User + all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 0) + // Detail + all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") + t.AssertNil(err) + + // Scores + all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "UID:Uid") + t.AssertNil(err) + }) + + // Model ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + err := db.Table(tableUser). + Where("uid", g.Slice{3, 4}). + Order("uid asc"). + ScanList(&users, "User") + t.AssertNil(err) + // Detail + err = db.Table(tableUserDetail). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("uid asc"). + ScanList(&users, "UserDetail", "User", "uid:Uid") + t.AssertNil(err) + // Scores + err = db.Table(tableUserScores). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("id asc"). + ScanList(&users, "UserScores", "User", "uid:Uid") + t.AssertNil(err) + + t.Assert(len(users), 0) + }) +} + func Test_Table_Relation_EmbedStruct(t *testing.T) { var ( tableUser = "user_" + gtime.TimestampMicroStr() @@ -531,29 +1060,31 @@ CREATE TABLE %s ( } // Initialize the data. - var err error - for i := 1; i <= 5; i++ { - // User. - _, err = db.Insert(tableUser, g.Map{ - "uid": i, - "name": fmt.Sprintf(`name_%d`, i), - }) - gtest.Assert(err, nil) - // Detail. - _, err = db.Insert(tableUserDetail, g.Map{ - "uid": i, - "address": fmt.Sprintf(`address_%d`, i), - }) - gtest.Assert(err, nil) - // Scores. - for j := 1; j <= 5; j++ { - _, err = db.Insert(tableUserScores, g.Map{ - "uid": i, - "score": j, + gtest.C(t, func(t *gtest.T) { + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), }) - gtest.Assert(err, nil) + t.AssertNil(err) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + t.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + t.AssertNil(err) + } } - } + }) gtest.C(t, func(t *gtest.T) { var ( @@ -562,19 +1093,19 @@ CREATE TABLE %s ( ) // SELECT * FROM `user_scores` err = db.Table(tableUserScores).Scan(&scores) - t.Assert(err, nil) + t.AssertNil(err) // SELECT * FROM `user_scores` WHERE `uid` IN(1,2,3,4,5) err = db.Table(tableUser). Where("uid", gdb.ListItemValuesUnique(&scores, "Uid")). ScanList(&scores, "EntityUser", "uid:Uid") - t.Assert(err, nil) + t.AssertNil(err) // SELECT * FROM `user_detail` WHERE `uid` IN(1,2,3,4,5) err = db.Table(tableUserDetail). Where("uid", gdb.ListItemValuesUnique(&scores, "Uid")). ScanList(&scores, "EntityUserDetail", "uid:Uid") - t.Assert(err, nil) + t.AssertNil(err) // Assertions. t.Assert(len(scores), 25) diff --git a/database/gdb/gdb_z_mysql_basic_test.go b/database/gdb/gdb_z_mysql_basic_test.go index 74a8ecd2c..b4a1a0083 100644 --- a/database/gdb/gdb_z_mysql_basic_test.go +++ b/database/gdb/gdb_z_mysql_basic_test.go @@ -19,7 +19,7 @@ func Test_Instance(t *testing.T) { t.AssertNE(err, nil) db, err := gdb.Instance() - t.Assert(err, nil) + t.AssertNil(err) err1 := db.PingMaster() err2 := db.PingSlave() diff --git a/database/gdb/gdb_z_mysql_ctx_test.go b/database/gdb/gdb_z_mysql_ctx_test.go index 6a6a811ab..33eb23125 100644 --- a/database/gdb/gdb_z_mysql_ctx_test.go +++ b/database/gdb/gdb_z_mysql_ctx_test.go @@ -17,7 +17,7 @@ import ( func Test_Ctx(t *testing.T) { gtest.C(t, func(t *gtest.T) { db, err := gdb.Instance() - t.Assert(err, nil) + t.AssertNil(err) err1 := db.PingMaster() err2 := db.PingSlave() diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 45946fc31..a606b3215 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -32,7 +32,7 @@ func init() { "name": true, "type": true, }, false) - gtest.Assert(err, nil) + gtest.AssertNil(err) configNode = ConfigNode{ Host: "127.0.0.1", Port: "3306", @@ -329,7 +329,7 @@ func TestResult_Structs1(t *testing.T) { } array := make([]*B, 2) err := r.Structs(&array) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(array[0].Id, 0) t.Assert(array[1].Id, 0) t.Assert(array[0].Name, "john") @@ -366,7 +366,7 @@ func Test_ScanList_NoRecreate_PtrAttribute(t *testing.T) { }, } err = r1.ScanList(&s, "One") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(s), 2) t.Assert(s[0].One.Name, "john") t.Assert(s[0].One.Age, 16) @@ -384,7 +384,7 @@ func Test_ScanList_NoRecreate_PtrAttribute(t *testing.T) { }, } err = r2.ScanList(&s, "One", "One", "id:Id") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(s), 2) t.Assert(s[0].One.Name, "john") t.Assert(s[0].One.Age, 20) @@ -422,7 +422,7 @@ func Test_ScanList_NoRecreate_StructAttribute(t *testing.T) { }, } err = r1.ScanList(&s, "One") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(s), 2) t.Assert(s[0].One.Name, "john") t.Assert(s[0].One.Age, 16) @@ -440,7 +440,7 @@ func Test_ScanList_NoRecreate_StructAttribute(t *testing.T) { }, } err = r2.ScanList(&s, "One", "One", "id:Id") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(s), 2) t.Assert(s[0].One.Name, "john") t.Assert(s[0].One.Age, 20) @@ -486,7 +486,7 @@ func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) { }, } err = r1.ScanList(&s, "One") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(s), 2) t.Assert(s[0].One.Name, "john") t.Assert(s[0].One.Age, 16) @@ -509,7 +509,7 @@ func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) { } err = r2.ScanList(&s, "Many", "One", "pid:Id") //fmt.Printf("%+v", err) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(s), 2) t.Assert(s[0].One.Name, "john") t.Assert(s[0].One.Age, 16) @@ -537,7 +537,7 @@ func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) { } err = r3.ScanList(&s, "Many", "One", "pid:Id") //fmt.Printf("%+v", err) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(s), 2) t.Assert(s[0].One.Name, "john") t.Assert(s[0].One.Age, 16) @@ -590,7 +590,7 @@ func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) { }, } err = r1.ScanList(&s, "One") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(s), 2) t.Assert(s[0].One.Name, "john") t.Assert(s[0].One.Age, 16) @@ -613,7 +613,7 @@ func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) { } err = r2.ScanList(&s, "Many", "One", "pid:Id") //fmt.Printf("%+v", err) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(s), 2) t.Assert(s[0].One.Name, "john") t.Assert(s[0].One.Age, 16) @@ -641,7 +641,7 @@ func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) { } err = r3.ScanList(&s, "Many", "One", "pid:Id") //fmt.Printf("%+v", err) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(s), 2) t.Assert(s[0].One.Name, "john") t.Assert(s[0].One.Age, 16) diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index 295b0d111..56b928a6a 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -36,13 +36,13 @@ func Test_DB_Ping(t *testing.T) { func Test_DB_Query(t *testing.T) { gtest.C(t, func(t *gtest.T) { _, err := db.Query("SELECT ?", 1) - t.Assert(err, nil) + t.AssertNil(err) _, err = db.Query("SELECT ?+?", 1, 2) - t.Assert(err, nil) + t.AssertNil(err) _, err = db.Query("SELECT ?+?", g.Slice{1, 2}) - t.Assert(err, nil) + t.AssertNil(err) _, err = db.Query("ERROR") t.AssertNE(err, nil) @@ -53,7 +53,7 @@ func Test_DB_Query(t *testing.T) { func Test_DB_Exec(t *testing.T) { gtest.C(t, func(t *gtest.T) { _, err := db.Exec("SELECT ?", 1) - t.Assert(err, nil) + t.AssertNil(err) _, err = db.Exec("ERROR") t.AssertNE(err, nil) @@ -64,17 +64,17 @@ func Test_DB_Exec(t *testing.T) { func Test_DB_Prepare(t *testing.T) { gtest.C(t, func(t *gtest.T) { st, err := db.Prepare("SELECT 100") - t.Assert(err, nil) + t.AssertNil(err) rows, err := st.Query() - t.Assert(err, nil) + t.AssertNil(err) array, err := rows.Columns() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(array[0], "100") err = rows.Close() - t.Assert(err, nil) + t.AssertNil(err) }) } @@ -90,7 +90,7 @@ func Test_DB_Insert(t *testing.T) { "nickname": "T1", "create_time": gtime.Now().String(), }) - t.Assert(err, nil) + t.AssertNil(err) // normal map result, err := db.Insert(table, g.Map{ @@ -100,7 +100,7 @@ func Test_DB_Insert(t *testing.T) { "nickname": "name_2", "create_time": gtime.Now().String(), }) - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) @@ -120,12 +120,12 @@ func Test_DB_Insert(t *testing.T) { Nickname: "name_3", CreateTime: timeStr, }) - t.Assert(err, nil) + t.AssertNil(err) n, _ = result.RowsAffected() t.Assert(n, 1) one, err := db.Table(table).Where("id", 3).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["id"].Int(), 3) t.Assert(one["passport"].String(), "user_3") @@ -142,12 +142,12 @@ func Test_DB_Insert(t *testing.T) { Nickname: "name_4", CreateTime: timeStr, }) - t.Assert(err, nil) + t.AssertNil(err) n, _ = result.RowsAffected() t.Assert(n, 1) one, err = db.Table(table).Where("id", 4).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["id"].Int(), 4) t.Assert(one["passport"].String(), "t4") t.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad") @@ -172,12 +172,12 @@ func Test_DB_Insert(t *testing.T) { "create_time": timeStr, }, }) - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) one, err = db.Table(table).Where("id", 200).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["id"].Int(), 200) t.Assert(one["passport"].String(), "t200") t.Assert(one["password"].String(), "25d55ad283aa400af464c76d71qw07ad") @@ -204,10 +204,10 @@ func Test_DB_Insert_WithStructAndSliceAttribute(t *testing.T) { "create_time": gtime.Now().String(), } _, err := db.Insert(table, data) - t.Assert(err, nil) + t.AssertNil(err) one, err := db.GetOne(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], data["passport"]) t.Assert(one["create_time"], data["create_time"]) t.Assert(one["nickname"], gparser.MustToJson(data["nickname"])) @@ -234,10 +234,10 @@ func Test_DB_Insert_KeyFieldNameMapping(t *testing.T) { CreateTime: "2020-10-10 12:00:01", } _, err := db.Insert(table, data) - t.Assert(err, nil) + t.AssertNil(err) one, err := db.GetOne(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], data.Passport) t.Assert(one["create_time"], data.CreateTime) t.Assert(one["nickname"], data.Nickname) @@ -264,10 +264,10 @@ func Test_DB_Upadte_KeyFieldNameMapping(t *testing.T) { CreateTime: "2020-10-10 12:00:01", } _, err := db.Update(table, data, "id=1") - t.Assert(err, nil) + t.AssertNil(err) one, err := db.GetOne(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], data.Passport) t.Assert(one["create_time"], data.CreateTime) t.Assert(one["nickname"], data.Nickname) @@ -320,7 +320,7 @@ func Test_DB_InsertIgnore(t *testing.T) { "nickname": "T1", "create_time": gtime.Now().String(), }) - t.Assert(err, nil) + t.AssertNil(err) }) } @@ -344,7 +344,7 @@ func Test_DB_BatchInsert(t *testing.T) { "create_time": gtime.Now().String(), }, }, 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 2) @@ -372,7 +372,7 @@ func Test_DB_BatchInsert(t *testing.T) { "create_time": gtime.Now().String(), }, }, 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 2) }) @@ -388,7 +388,7 @@ func Test_DB_BatchInsert(t *testing.T) { "nickname": "T1", "create_time": gtime.Now().String(), }) - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) }) @@ -416,7 +416,7 @@ func Test_DB_BatchInsert_Struct(t *testing.T) { CreateTime: gtime.Now(), } result, err := db.BatchInsert(table, user) - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) }) @@ -435,10 +435,10 @@ func Test_DB_Save(t *testing.T) { "nickname": "T11", "create_time": timeStr, }) - t.Assert(err, nil) + t.AssertNil(err) one, err := db.Table(table).Where("id", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["id"].Int(), 1) t.Assert(one["passport"].String(), "t1") t.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad") @@ -460,10 +460,10 @@ func Test_DB_Replace(t *testing.T) { "nickname": "T11", "create_time": timeStr, }) - t.Assert(err, nil) + t.AssertNil(err) one, err := db.Table(table).Where("id", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["id"].Int(), 1) t.Assert(one["passport"].String(), "t1") t.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad") @@ -478,12 +478,12 @@ func Test_DB_Update(t *testing.T) { gtest.C(t, func(t *gtest.T) { result, err := db.Update(table, "password='987654321'", "id=3") - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) one, err := db.Table(table).Where("id", 3).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["id"].Int(), 3) t.Assert(one["passport"].String(), "user_3") t.Assert(one["password"].String(), "987654321") @@ -497,19 +497,19 @@ func Test_DB_GetAll(t *testing.T) { gtest.C(t, func(t *gtest.T) { result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 1) }) gtest.C(t, func(t *gtest.T) { result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), g.Slice{1}) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 1) }) gtest.C(t, func(t *gtest.T) { result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id in(?)", table), g.Slice{1, 2, 3}) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) t.Assert(result[1]["id"].Int(), 2) @@ -517,7 +517,7 @@ func Test_DB_GetAll(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id in(?,?,?)", table), g.Slice{1, 2, 3}) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) t.Assert(result[1]["id"].Int(), 2) @@ -525,7 +525,7 @@ func Test_DB_GetAll(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id in(?,?,?)", table), g.Slice{1, 2, 3}...) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) t.Assert(result[1]["id"].Int(), 2) @@ -533,7 +533,7 @@ func Test_DB_GetAll(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id>=? AND id <=?", table), g.Slice{1, 3}) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) t.Assert(result[1]["id"].Int(), 2) @@ -546,7 +546,7 @@ func Test_DB_GetOne(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { record, err := db.GetOne(fmt.Sprintf("SELECT * FROM %s WHERE passport=?", table), "user_1") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(record["nickname"].String(), "name_1") }) } @@ -556,7 +556,7 @@ func Test_DB_GetValue(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { value, err := db.GetValue(fmt.Sprintf("SELECT id FROM %s WHERE passport=?", table), "user_3") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.Int(), 3) }) } @@ -566,7 +566,7 @@ func Test_DB_GetCount(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { count, err := db.GetCount(fmt.Sprintf("SELECT * FROM %s", table)) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, SIZE) }) } @@ -584,7 +584,7 @@ func Test_DB_GetStruct(t *testing.T) { } user := new(User) err := db.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.NickName, "name_3") }) gtest.C(t, func(t *gtest.T) { @@ -597,7 +597,7 @@ func Test_DB_GetStruct(t *testing.T) { } user := new(User) err := db.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.NickName, "name_3") }) } @@ -615,7 +615,7 @@ func Test_DB_GetStructs(t *testing.T) { } var users []User err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users), SIZE-1) t.Assert(users[0].Id, 2) t.Assert(users[1].Id, 3) @@ -635,7 +635,7 @@ func Test_DB_GetStructs(t *testing.T) { } var users []User err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users), SIZE-1) t.Assert(users[0].Id, 2) t.Assert(users[1].Id, 3) @@ -659,7 +659,7 @@ func Test_DB_GetScan(t *testing.T) { } user := new(User) err := db.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.NickName, "name_3") }) gtest.C(t, func(t *gtest.T) { @@ -672,7 +672,7 @@ func Test_DB_GetScan(t *testing.T) { } user := new(User) err := db.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.NickName, "name_3") }) @@ -686,7 +686,7 @@ func Test_DB_GetScan(t *testing.T) { } var users []User err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users), SIZE-1) t.Assert(users[0].Id, 2) t.Assert(users[1].Id, 3) @@ -706,7 +706,7 @@ func Test_DB_GetScan(t *testing.T) { } var users []User err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users), SIZE-1) t.Assert(users[0].Id, 2) t.Assert(users[1].Id, 3) @@ -722,7 +722,7 @@ func Test_DB_Delete(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { result, err := db.Delete(table, 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, SIZE) }) @@ -746,7 +746,7 @@ func Test_DB_Time(t *testing.T) { n, _ := result.RowsAffected() t.Assert(n, 1) value, err := db.GetValue(fmt.Sprintf("select `passport` from `%s` where id=?", table), 200) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.String(), "t200") }) @@ -765,13 +765,13 @@ func Test_DB_Time(t *testing.T) { n, _ := result.RowsAffected() t.Assert(n, 1) value, err := db.GetValue(fmt.Sprintf("select `passport` from `%s` where id=?", table), 300) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.String(), "t300") }) gtest.C(t, func(t *gtest.T) { result, err := db.Delete(table, 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 2) }) @@ -781,7 +781,7 @@ func Test_DB_ToJson(t *testing.T) { table := createInitTable() defer dropTable(table) _, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1) - gtest.Assert(err, nil) + gtest.AssertNil(err) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Fields("*").Where("id =? ", 1).Select() @@ -855,7 +855,7 @@ func Test_DB_ToXml(t *testing.T) { table := createInitTable() defer dropTable(table) _, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1) - gtest.Assert(err, nil) + gtest.AssertNil(err) gtest.C(t, func(t *gtest.T) { record, err := db.Table(table).Fields("*").Where("id = ?", 1).One() @@ -921,7 +921,7 @@ func Test_DB_ToStringMap(t *testing.T) { table := createInitTable() defer dropTable(table) _, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1) - gtest.Assert(err, nil) + gtest.AssertNil(err) gtest.C(t, func(t *gtest.T) { id := "1" result, err := db.Table(table).Fields("*").Where("id = ?", 1).Select() @@ -957,7 +957,7 @@ func Test_DB_ToIntMap(t *testing.T) { table := createInitTable() defer dropTable(table) _, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1) - gtest.Assert(err, nil) + gtest.AssertNil(err) gtest.C(t, func(t *gtest.T) { id := 1 @@ -993,7 +993,7 @@ func Test_DB_ToUintMap(t *testing.T) { table := createInitTable() defer dropTable(table) _, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1) - gtest.Assert(err, nil) + gtest.AssertNil(err) gtest.C(t, func(t *gtest.T) { id := 1 @@ -1030,7 +1030,7 @@ func Test_DB_ToStringRecord(t *testing.T) { table := createInitTable() defer dropTable(table) _, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1) - gtest.Assert(err, nil) + gtest.AssertNil(err) gtest.C(t, func(t *gtest.T) { id := 1 @@ -1068,7 +1068,7 @@ func Test_DB_ToIntRecord(t *testing.T) { table := createInitTable() defer dropTable(table) _, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1) - gtest.Assert(err, nil) + gtest.AssertNil(err) gtest.C(t, func(t *gtest.T) { id := 1 @@ -1105,7 +1105,7 @@ func Test_DB_ToUintRecord(t *testing.T) { table := createInitTable() defer dropTable(table) _, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1) - gtest.Assert(err, nil) + gtest.AssertNil(err) gtest.C(t, func(t *gtest.T) { id := 1 @@ -1205,7 +1205,7 @@ func Test_DB_Prefix(t *testing.T) { "nickname": fmt.Sprintf(`name_%d`, id), "create_time": gtime.NewFromStr("2018-10-24 10:00:00").String(), }) - t.Assert(err, nil) + t.AssertNil(err) n, e := result.RowsAffected() t.Assert(e, nil) @@ -1221,7 +1221,7 @@ func Test_DB_Prefix(t *testing.T) { "nickname": fmt.Sprintf(`name_%d`, id), "create_time": gtime.NewFromStr("2018-10-24 10:00:01").String(), }) - t.Assert(err, nil) + t.AssertNil(err) n, e := result.RowsAffected() t.Assert(e, nil) @@ -1237,7 +1237,7 @@ func Test_DB_Prefix(t *testing.T) { "nickname": fmt.Sprintf(`name_%d`, id), "create_time": gtime.NewFromStr("2018-10-24 10:00:02").String(), }) - t.Assert(err, nil) + t.AssertNil(err) n, e := result.RowsAffected() t.Assert(e, nil) @@ -1253,7 +1253,7 @@ func Test_DB_Prefix(t *testing.T) { "nickname": fmt.Sprintf(`name_%d`, id), "create_time": gtime.NewFromStr("2018-10-24 10:00:03").String(), }, "id=?", id) - t.Assert(err, nil) + t.AssertNil(err) n, e := result.RowsAffected() t.Assert(e, nil) @@ -1263,7 +1263,7 @@ func Test_DB_Prefix(t *testing.T) { gtest.C(t, func(t *gtest.T) { id := 10000 result, err := db.Delete(name, "id=?", id) - t.Assert(err, nil) + t.AssertNil(err) n, e := result.RowsAffected() t.Assert(e, nil) @@ -1283,7 +1283,7 @@ func Test_DB_Prefix(t *testing.T) { } result, err := db.BatchInsert(name, array.Slice()) - t.Assert(err, nil) + t.AssertNil(err) n, e := result.RowsAffected() t.Assert(e, nil) @@ -1403,7 +1403,7 @@ func Test_Empty_Slice_Argument(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { result, err := db.GetAll(fmt.Sprintf(`select * from %s where id in(?)`, table), g.Slice{}) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 0) }) } @@ -1430,7 +1430,7 @@ func Test_DB_UpdateCounter(t *testing.T) { "updated_time": 0, } _, err = db.Insert(tableName, insertData) - t.Assert(err, nil) + t.AssertNil(err) }) gtest.C(t, func(t *gtest.T) { @@ -1442,11 +1442,11 @@ func Test_DB_UpdateCounter(t *testing.T) { "views": gdbCounter, } result, err := db.Update(tableName, updateData, "id", 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) one, err := db.Table(tableName).Where("id", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["id"].Int(), 1) t.Assert(one["views"].Int(), 1) }) @@ -1461,11 +1461,11 @@ func Test_DB_UpdateCounter(t *testing.T) { "updated_time": gtime.Now().Unix(), } result, err := db.Update(tableName, updateData, "id", 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) one, err := db.Table(tableName).Where("id", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["id"].Int(), 1) t.Assert(one["views"].Int(), 0) }) @@ -1486,6 +1486,6 @@ func Test_DB_Ctx_Logger(t *testing.T) { db.SetDebug(true) ctx := context.WithValue(context.Background(), "Trace-Id", "123456789") _, err := db.Ctx(ctx).Query("SELECT 1") - t.Assert(err, nil) + t.AssertNil(err) }) } diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 297eb417c..df40dd8d3 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -38,7 +38,7 @@ func Test_Model_Insert(t *testing.T) { "nickname": "name_1", "create_time": gtime.Now().String(), }).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.LastInsertId() t.Assert(n, 1) @@ -50,7 +50,7 @@ func Test_Model_Insert(t *testing.T) { "nickname": "name_2", "create_time": gtime.Now().String(), }).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ = result.RowsAffected() t.Assert(n, 1) @@ -70,11 +70,11 @@ func Test_Model_Insert(t *testing.T) { Password: "25d55ad283aa400af464c76d713c07ad", Nickname: "name_3", }).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ = result.RowsAffected() t.Assert(n, 1) value, err := db.Table(table).Fields("passport").Where("id=3").Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.String(), "t3") result, err = db.Table(table).Filter().Data(&User{ @@ -85,15 +85,15 @@ func Test_Model_Insert(t *testing.T) { Nickname: "T4", CreateTime: gtime.Now(), }).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ = result.RowsAffected() t.Assert(n, 1) value, err = db.Table(table).Fields("passport").Where("id=4").Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.String(), "t4") result, err = db.Table(table).Where("id>?", 1).Delete() - t.Assert(err, nil) + t.AssertNil(err) n, _ = result.RowsAffected() t.Assert(n, 3) }) @@ -114,7 +114,7 @@ func Test_Model_Insert_Filter(t *testing.T) { "create_time": gtime.Now().String(), } result, err := db.Table(table).Filter().Data(data).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.LastInsertId() t.Assert(n, 1) @@ -144,7 +144,7 @@ func Test_Model_Insert_Filter(t *testing.T) { } result, err := db.Table(table).Filter().Data(data).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.LastInsertId() t.Assert(n, 2) @@ -170,10 +170,10 @@ func Test_Model_Insert_WithStructAndSliceAttribute(t *testing.T) { "create_time": gtime.Now().String(), } _, err := db.Table(table).Data(data).Insert() - t.Assert(err, nil) + t.AssertNil(err) one, err := db.Table(table).One("id", 1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], data["passport"]) t.Assert(one["create_time"], data["create_time"]) t.Assert(one["nickname"], gparser.MustToJson(data["nickname"])) @@ -200,10 +200,10 @@ func Test_Model_Insert_KeyFieldNameMapping(t *testing.T) { CreateTime: "2020-10-10 12:00:01", } _, err := db.Model(table).Data(data).Insert() - t.Assert(err, nil) + t.AssertNil(err) one, err := db.Model(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], data.Passport) t.Assert(one["create_time"], data.CreateTime) t.Assert(one["nickname"], data.Nickname) @@ -230,10 +230,10 @@ func Test_Model_Update_KeyFieldNameMapping(t *testing.T) { CreateTime: "2020-10-10 12:00:01", } _, err := db.Model(table).Data(data).WherePri(1).Update() - t.Assert(err, nil) + t.AssertNil(err) one, err := db.Model(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], data.Passport) t.Assert(one["create_time"], data.CreateTime) t.Assert(one["nickname"], data.Nickname) @@ -277,10 +277,10 @@ func Test_Model_Insert_Time(t *testing.T) { "create_time": "2020-10-10 20:09:18.334", } _, err := db.Table(table).Data(data).Insert() - t.Assert(err, nil) + t.AssertNil(err) one, err := db.Table(table).One("id", 1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], data["passport"]) t.Assert(one["create_time"], "2020-10-10 20:09:18") t.Assert(one["nickname"], data["nickname"]) @@ -305,7 +305,7 @@ func Test_Model_BatchInsertWithArrayStruct(t *testing.T) { } result, err := user.Filter().Data(array).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.LastInsertId() t.Assert(n, SIZE) }) @@ -334,7 +334,7 @@ func Test_Model_InsertIgnore(t *testing.T) { "nickname": "name_1", "create_time": gtime.Now().String(), }).InsertIgnore() - t.Assert(err, nil) + t.AssertNil(err) }) } @@ -391,7 +391,7 @@ func Test_Model_Batch(t *testing.T) { table := createInitTable() defer dropTable(table) result, err := db.Table(table).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), SIZE) for _, v := range result { v["nickname"].Set(v["nickname"].String() + v["id"].String()) @@ -408,7 +408,7 @@ func Test_Model_Batch(t *testing.T) { table := createInitTable() defer dropTable(table) result, err := db.Table(table).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), SIZE) for _, v := range result { v["nickname"].Set(v["nickname"].String() + v["id"].String()) @@ -433,7 +433,7 @@ func Test_Model_Replace(t *testing.T) { "nickname": "T11", "create_time": "2018-10-24 10:00:00", }).Replace() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) }) @@ -450,7 +450,7 @@ func Test_Model_Save(t *testing.T) { "nickname": "T111", "create_time": "2018-10-24 10:00:00", }).Save() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) }) @@ -462,29 +462,29 @@ func Test_Model_Update(t *testing.T) { // UPDATE...LIMIT gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Data("nickname", "T100").Where(1).Order("id desc").Limit(2).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 2) v1, err := db.Table(table).Fields("nickname").Where("id", 10).Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v1.String(), "T100") v2, err := db.Table(table).Fields("nickname").Where("id", 8).Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v2.String(), "name_8") }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Data("passport", "user_22").Where("passport=?", "user_2").Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Data("passport", "user_2").Where("passport='user_22'").Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) }) @@ -492,7 +492,7 @@ func Test_Model_Update(t *testing.T) { // Update + Data(string) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Data("passport='user_33'").Where("passport='user_3'").Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) }) @@ -502,7 +502,7 @@ func Test_Model_Update(t *testing.T) { "passport": "user_44", "none": "none", }).Where("passport='user_4'").Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) }) @@ -515,13 +515,13 @@ func Test_Model_Clone(t *testing.T) { gtest.C(t, func(t *gtest.T) { md := db.Table(table).Where("id IN(?)", g.Slice{1, 3}) count, err := md.Count() - t.Assert(err, nil) + t.AssertNil(err) record, err := md.Order("id DESC").One() - t.Assert(err, nil) + t.AssertNil(err) result, err := md.Order("id ASC").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 2) t.Assert(record["id"].Int(), 3) @@ -538,50 +538,50 @@ func Test_Model_Safe(t *testing.T) { gtest.C(t, func(t *gtest.T) { md := db.Table(table).Safe(false).Where("id IN(?)", g.Slice{1, 3}) count, err := md.Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 2) md.And("id = ?", 1) count, err = md.Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 1) }) gtest.C(t, func(t *gtest.T) { md := db.Table(table).Safe(true).Where("id IN(?)", g.Slice{1, 3}) count, err := md.Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 2) md.And("id = ?", 1) count, err = md.Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 2) }) gtest.C(t, func(t *gtest.T) { md := db.Table(table).Safe().Where("id IN(?)", g.Slice{1, 3}) count, err := md.Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 2) md.And("id = ?", 1) count, err = md.Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 2) }) gtest.C(t, func(t *gtest.T) { md1 := db.Table(table).Safe() md2 := md1.Where("id in (?)", g.Slice{1, 3}) count, err := md2.Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 2) all, err := md2.All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 2) all, err = md2.Page(1, 10).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 2) }) @@ -595,33 +595,33 @@ func Test_Model_Safe(t *testing.T) { // 1,3 count, err := md2.Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 2) all, err := md2.Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 2) t.Assert(all[0]["id"].Int(), 1) t.Assert(all[1]["id"].Int(), 3) all, err = md2.Page(1, 10).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 2) // 4,5,6 count, err = md3.Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 3) all, err = md3.Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 3) t.Assert(all[0]["id"].Int(), 4) t.Assert(all[1]["id"].Int(), 5) t.Assert(all[2]["id"].Int(), 6) all, err = md3.Page(1, 10).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 3) }) } @@ -632,13 +632,13 @@ func Test_Model_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), SIZE) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id<0").All() t.Assert(result, nil) - t.Assert(err, nil) + t.AssertNil(err) }) } @@ -656,7 +656,7 @@ func Test_Model_Fields(t *testing.T) { ) ENGINE=InnoDB DEFAULT CHARSET=utf8; `, tableName2, )); err != nil { - gtest.Assert(err, nil) + gtest.AssertNil(err) } defer dropTable(tableName2) @@ -665,13 +665,13 @@ func Test_Model_Fields(t *testing.T) { "name": "table2_1", "age": 18, }) - gtest.Assert(err, nil) + gtest.AssertNil(err) n, _ := r.RowsAffected() gtest.Assert(n, 1) gtest.C(t, func(t *gtest.T) { all, err := db.Table(tableName1).As("u").Fields("u.passport,u.id").Where("u.id<2").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 1) t.Assert(len(all[0]), 2) }) @@ -681,7 +681,7 @@ func Test_Model_Fields(t *testing.T) { Fields("u1.passport,u1.id,u2.id AS u2id"). Where("u1.id<2"). All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 1) t.Assert(len(all[0]), 3) }) @@ -691,7 +691,7 @@ func Test_Model_Fields(t *testing.T) { Fields("u1.passport,u1.id,u2.name,u2.age"). Where("u1.id<2"). All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 1) t.Assert(len(all[0]), 4) t.Assert(all[0]["id"], 1) @@ -707,21 +707,21 @@ func Test_Model_FindAll(t *testing.T) { gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).FindAll(5) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 5) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Order("id asc").FindAll("id", 8) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 8) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Order("id asc").FindAll(g.Slice{3, 9}) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 2) t.Assert(result[0]["id"].Int(), 3) t.Assert(result[1]["id"].Int(), 9) @@ -729,13 +729,13 @@ func Test_Model_FindAll(t *testing.T) { gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).FindAll() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), SIZE) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id<0").FindAll() t.Assert(result, nil) - t.Assert(err, nil) + t.AssertNil(err) }) } @@ -745,26 +745,26 @@ func Test_Model_FindAll_GTime(t *testing.T) { gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).FindAll("create_time < ?", gtime.NewFromStr("2000-01-01 00:00:00")) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 0) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).FindAll("create_time > ?", gtime.NewFromStr("2000-01-01 00:00:00")) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), SIZE) }) gtest.C(t, func(t *gtest.T) { v := g.NewVar("2000-01-01 00:00:00") result, err := db.Table(table).FindAll("create_time < ?", v) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 0) }) gtest.C(t, func(t *gtest.T) { v := g.NewVar("2000-01-01 00:00:00") result, err := db.Table(table).FindAll("create_time > ?", v) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), SIZE) }) } @@ -774,13 +774,13 @@ func Test_Model_One(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { record, err := db.Table(table).Where("id", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(record["nickname"].String(), "name_1") }) gtest.C(t, func(t *gtest.T) { record, err := db.Table(table).Where("id", 0).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(record, nil) }) } @@ -791,31 +791,31 @@ func Test_Model_FindOne(t *testing.T) { gtest.C(t, func(t *gtest.T) { record, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(record["nickname"].String(), "name_1") }) gtest.C(t, func(t *gtest.T) { record, err := db.Table(table).FindOne(3) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(record["nickname"].String(), "name_3") }) gtest.C(t, func(t *gtest.T) { record, err := db.Table(table).Where("id", 1).FindOne() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(record["nickname"].String(), "name_1") }) gtest.C(t, func(t *gtest.T) { record, err := db.Table(table).FindOne("id", 9) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(record["nickname"].String(), "name_9") }) gtest.C(t, func(t *gtest.T) { record, err := db.Table(table).Where("id", 0).FindOne() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(record, nil) }) } @@ -826,13 +826,13 @@ func Test_Model_Value(t *testing.T) { gtest.C(t, func(t *gtest.T) { value, err := db.Table(table).Fields("nickname").Where("id", 1).Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.String(), "name_1") }) gtest.C(t, func(t *gtest.T) { value, err := db.Table(table).Fields("nickname").Where("id", 0).Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value, nil) }) } @@ -843,28 +843,28 @@ func Test_Model_Array(t *testing.T) { gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).Where("id", g.Slice{1, 2, 3}).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(all.Array("id"), g.Slice{1, 2, 3}) t.Assert(all.Array("nickname"), g.Slice{"name_1", "name_2", "name_3"}) }) gtest.C(t, func(t *gtest.T) { array, err := db.Table(table).Fields("nickname").Where("id", g.Slice{1, 2, 3}).Array() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(array, g.Slice{"name_1", "name_2", "name_3"}) }) gtest.C(t, func(t *gtest.T) { array, err := db.Table(table).Array("nickname", "id", g.Slice{1, 2, 3}) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(array, g.Slice{"name_1", "name_2", "name_3"}) }) gtest.C(t, func(t *gtest.T) { array, err := db.Table(table).FindArray("nickname", "id", g.Slice{1, 2, 3}) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(array, g.Slice{"name_1", "name_2", "name_3"}) }) gtest.C(t, func(t *gtest.T) { array, err := db.Table(table).FindArray("nickname", g.Slice{1, 2, 3}) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(array, g.Slice{"name_1", "name_2", "name_3"}) }) } @@ -875,25 +875,25 @@ func Test_Model_FindValue(t *testing.T) { gtest.C(t, func(t *gtest.T) { value, err := db.Table(table).FindValue("nickname", 1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.String(), "name_1") }) gtest.C(t, func(t *gtest.T) { value, err := db.Table(table).Order("id desc").FindValue("nickname") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.String(), "name_10") }) gtest.C(t, func(t *gtest.T) { value, err := db.Table(table).Fields("nickname").Where("id", 1).FindValue() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.String(), "name_1") }) gtest.C(t, func(t *gtest.T) { value, err := db.Table(table).Fields("nickname").Where("id", 0).FindValue() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value, nil) }) } @@ -903,33 +903,33 @@ func Test_Model_Count(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { count, err := db.Table(table).Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, SIZE) }) gtest.C(t, func(t *gtest.T) { count, err := db.Table(table).FieldsEx("id").Where("id>8").Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 2) }) gtest.C(t, func(t *gtest.T) { count, err := db.Table(table).Fields("distinct id,nickname").Where("id>8").Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 2) }) // COUNT...LIMIT... gtest.C(t, func(t *gtest.T) { count, err := db.Table(table).Page(1, 2).Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, SIZE) }) //gtest.C(t, func(t *gtest.T) { // count, err := db.Table(table).Fields("id myid").Where("id>8").Count() - // t.Assert(err, nil) + // t.AssertNil(err) // t.Assert(count, 2) //}) //gtest.C(t, func(t *gtest.T) { // count, err := db.Table(table).As("u1").LeftJoin(table, "u2", "u2.id=u1.id").Fields("u2.id u2id").Where("u1.id>8").Count() - // t.Assert(err, nil) + // t.AssertNil(err) // t.Assert(count, 2) //}) } @@ -939,17 +939,17 @@ func Test_Model_FindCount(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { count, err := db.Table(table).FindCount(g.Slice{1, 3}) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 2) }) gtest.C(t, func(t *gtest.T) { count, err := db.Table(table).FindCount(g.Slice{1, 300000}) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 1) }) gtest.C(t, func(t *gtest.T) { count, err := db.Table(table).FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, SIZE) }) } @@ -959,7 +959,7 @@ func Test_Model_Select(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Select() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), SIZE) }) } @@ -977,7 +977,7 @@ func Test_Model_Struct(t *testing.T) { } user := new(User) err := db.Table(table).Where("id=1").Struct(user) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") }) @@ -991,7 +991,7 @@ func Test_Model_Struct(t *testing.T) { } user := new(User) err := db.Table(table).Where("id=1").Struct(user) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") }) @@ -1006,7 +1006,7 @@ func Test_Model_Struct(t *testing.T) { } user := (*User)(nil) err := db.Table(table).Where("id=1").Struct(&user) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") }) @@ -1050,7 +1050,7 @@ func Test_Model_Struct(t *testing.T) { } var user *User err := db.Table(table).Where("id=-1").Struct(&user) - t.Assert(err, nil) + t.AssertNil(err) }) } @@ -1070,7 +1070,7 @@ func Test_Model_Struct_CustomType(t *testing.T) { } user := new(User) err := db.Table(table).Where("id=1").Struct(user) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") }) @@ -1159,7 +1159,7 @@ func Test_Model_Structs(t *testing.T) { } var users []*User err := db.Table(table).Where("id<0").Structs(&users) - t.Assert(err, nil) + t.AssertNil(err) }) } @@ -1205,7 +1205,7 @@ func Test_Model_Scan(t *testing.T) { } user := new(User) err := db.Table(table).Where("id=1").Scan(user) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") }) @@ -1219,7 +1219,7 @@ func Test_Model_Scan(t *testing.T) { } user := new(User) err := db.Table(table).Where("id=1").Scan(user) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") }) @@ -1233,7 +1233,7 @@ func Test_Model_Scan(t *testing.T) { } var users []User err := db.Table(table).Order("id asc").Scan(&users) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users), SIZE) t.Assert(users[0].Id, 1) t.Assert(users[1].Id, 2) @@ -1253,7 +1253,7 @@ func Test_Model_Scan(t *testing.T) { } var users []*User err := db.Table(table).Order("id asc").Scan(&users) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(users), SIZE) t.Assert(users[0].Id, 1) t.Assert(users[1].Id, 2) @@ -1289,7 +1289,7 @@ func Test_Model_OrderBy(t *testing.T) { gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Order("id DESC").Select() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), SIZE) t.Assert(result[0]["nickname"].String(), fmt.Sprintf("name_%d", SIZE)) }) @@ -1301,7 +1301,7 @@ func Test_Model_GroupBy(t *testing.T) { gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).GroupBy("id").Select() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), SIZE) t.Assert(result[0]["nickname"].String(), "name_1") }) @@ -1312,7 +1312,7 @@ func Test_Model_Data(t *testing.T) { table := createInitTable() defer dropTable(table) result, err := db.Table(table).Data("nickname=?", "test").Where("id=?", 3).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) }) @@ -1329,7 +1329,7 @@ func Test_Model_Data(t *testing.T) { }) } result, err := db.Table(table).Data(users).Batch(2).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 10) }) @@ -1346,7 +1346,7 @@ func Test_Model_Data(t *testing.T) { }) } result, err := db.Table(table).Data(users).Batch(2).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 10) }) @@ -1359,7 +1359,7 @@ func Test_Model_Where(t *testing.T) { // string gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id=? and nickname=?", 3, "name_3").One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) @@ -1367,13 +1367,13 @@ func Test_Model_Where(t *testing.T) { // slice gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(g.Slice{"id", 3}).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(g.Slice{"id", 3, "nickname", "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) @@ -1381,7 +1381,7 @@ func Test_Model_Where(t *testing.T) { // slice parameter gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id=? and nickname=?", g.Slice{3, "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) @@ -1390,7 +1390,7 @@ func Test_Model_Where(t *testing.T) { result, err := db.Table(table).Where(g.Map{ "passport like": "user_1%", }).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 2) t.Assert(result[0].GMap().Get("id"), 1) t.Assert(result[1].GMap().Get("id"), 10) @@ -1401,118 +1401,118 @@ func Test_Model_Where(t *testing.T) { "id": g.Slice{1, 2, 3}, "passport": g.Slice{"user_2", "user_3"}, }).And("id=? and nickname=?", g.Slice{3, "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id=3", g.Slice{}).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id=?", g.Slice{3}).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id", 3).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id", 3).Where("nickname", "name_3").One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id", 3).And("nickname", "name_3").One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id", 30).Or("nickname", "name_3").One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id", 30).Or("nickname", "name_3").And("id>?", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id", 30).Or("nickname", "name_3").And("id>", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // slice gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id=? AND nickname=?", g.Slice{3, "name_3"}...).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id=? AND nickname=?", g.Slice{3, "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("passport like ? and nickname like ?", g.Slice{"user_3", "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // map gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(g.Map{"id": 3, "nickname": "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // map key operator gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(g.Map{"id>": 1, "id<": 3}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // gmap.Map gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(gmap.NewFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // gmap.Map key operator gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(gmap.NewFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // list map gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(gmap.NewListMapFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // list map key operator gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(gmap.NewListMapFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // tree map gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // tree map key operator gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id>": 1, "id<": 3})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) @@ -1527,7 +1527,7 @@ func Test_Model_Where(t *testing.T) { "id": g.Slice{1, 2, 3}, } result, err := db.Table(table).Where(conditions).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) }) @@ -1542,7 +1542,7 @@ func Test_Model_Where(t *testing.T) { "id in(?)": g.Slice{1, 2, 3}, } result, err := db.Table(table).Where(conditions).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) }) @@ -1553,17 +1553,17 @@ func Test_Model_Where(t *testing.T) { Nickname string `gconv:"nickname"` } result, err := db.Table(table).Where(User{3, "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) result, err = db.Table(table).Where(&User{3, "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // slice single gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("id IN(?)", g.Slice{1, 3}).Order("id ASC").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 2) t.Assert(result[0]["id"].Int(), 1) t.Assert(result[1]["id"].Int(), 3) @@ -1571,7 +1571,7 @@ func Test_Model_Where(t *testing.T) { // slice + string gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("nickname=? AND id IN(?)", "name_3", g.Slice{1, 3}).Order("id ASC").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 3) }) @@ -1581,7 +1581,7 @@ func Test_Model_Where(t *testing.T) { "id": g.Slice{1, 3}, "nickname": "name_3", }).Order("id ASC").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 3) }) @@ -1595,7 +1595,7 @@ func Test_Model_Where(t *testing.T) { Ids: []int{1, 3}, Nickname: "name_3", }).Order("id ASC").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 3) }) @@ -1608,12 +1608,12 @@ func Test_Model_Where_ISNULL_1(t *testing.T) { gtest.C(t, func(t *gtest.T) { //db.SetDebug(true) result, err := db.Table(table).Data("nickname", nil).Where("id", 2).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) one, err := db.Table(table).Where("nickname", nil).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one.IsEmpty(), false) t.Assert(one["id"], 2) }) @@ -1634,7 +1634,7 @@ func Test_Model_Where_ISNULL_2(t *testing.T) { "id": g.Slice{1, 2, 3}, } result, err := db.Table(table).WherePri(conditions).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) }) @@ -1648,7 +1648,7 @@ func Test_Model_Where_OmitEmpty(t *testing.T) { "id < 4": "", } result, err := db.Table(table).WherePri(conditions).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) }) @@ -1657,7 +1657,7 @@ func Test_Model_Where_OmitEmpty(t *testing.T) { "id < 4": "", } result, err := db.Table(table).WherePri(conditions).OmitEmpty().Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) }) @@ -1669,12 +1669,12 @@ func Test_Model_Where_GTime(t *testing.T) { gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("create_time>?", gtime.NewFromStr("2010-09-01")).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 10) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where("create_time>?", *gtime.NewFromStr("2010-09-01")).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 10) }) } @@ -1686,13 +1686,13 @@ func Test_Model_WherePri(t *testing.T) { // primary key gtest.C(t, func(t *gtest.T) { one, err := db.Table(table).WherePri(3).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertNE(one, nil) t.Assert(one["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).WherePri(g.Slice{3, 9}).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 2) t.Assert(all[0]["id"].Int(), 3) t.Assert(all[1]["id"].Int(), 9) @@ -1701,14 +1701,14 @@ func Test_Model_WherePri(t *testing.T) { // string gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id=? and nickname=?", 3, "name_3").One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) // slice parameter gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id=? and nickname=?", g.Slice{3, "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) @@ -1717,7 +1717,7 @@ func Test_Model_WherePri(t *testing.T) { result, err := db.Table(table).WherePri(g.Map{ "passport like": "user_1%", }).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 2) t.Assert(result[0].GMap().Get("id"), 1) t.Assert(result[1].GMap().Get("id"), 10) @@ -1728,7 +1728,7 @@ func Test_Model_WherePri(t *testing.T) { "id": g.Slice{1, 2, 3}, "passport": g.Slice{"user_2", "user_3"}, }).And("id=? and nickname=?", g.Slice{3, "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) @@ -1737,118 +1737,118 @@ func Test_Model_WherePri(t *testing.T) { "id": g.Slice{1, 2, 3}, "passport": g.Slice{"user_2", "user_3"}, }).Or("nickname=?", g.Slice{"name_4"}).And("id", 3).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 2) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id=3", g.Slice{}).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id=?", g.Slice{3}).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id", 3).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id", 3).WherePri("nickname", "name_3").One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id", 3).And("nickname", "name_3").One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id", 30).Or("nickname", "name_3").One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id", 30).Or("nickname", "name_3").And("id>?", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id", 30).Or("nickname", "name_3").And("id>", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // slice gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id=? AND nickname=?", g.Slice{3, "name_3"}...).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id=? AND nickname=?", g.Slice{3, "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("passport like ? and nickname like ?", g.Slice{"user_3", "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // map gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri(g.Map{"id": 3, "nickname": "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // map key operator gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri(g.Map{"id>": 1, "id<": 3}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // gmap.Map gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri(gmap.NewFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // gmap.Map key operator gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri(gmap.NewFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // list map gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri(gmap.NewListMapFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // list map key operator gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri(gmap.NewListMapFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // tree map gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // tree map key operator gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id>": 1, "id<": 3})).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) @@ -1863,7 +1863,7 @@ func Test_Model_WherePri(t *testing.T) { "id": g.Slice{1, 2, 3}, } result, err := db.Table(table).WherePri(conditions).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) }) @@ -1878,7 +1878,7 @@ func Test_Model_WherePri(t *testing.T) { "id in(?)": g.Slice{1, 2, 3}, } result, err := db.Table(table).WherePri(conditions).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) }) @@ -1889,17 +1889,17 @@ func Test_Model_WherePri(t *testing.T) { Nickname string `gconv:"nickname"` } result, err := db.Table(table).WherePri(User{3, "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) result, err = db.Table(table).WherePri(&User{3, "name_3"}).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // slice single gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("id IN(?)", g.Slice{1, 3}).Order("id ASC").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 2) t.Assert(result[0]["id"].Int(), 1) t.Assert(result[1]["id"].Int(), 3) @@ -1907,7 +1907,7 @@ func Test_Model_WherePri(t *testing.T) { // slice + string gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).WherePri("nickname=? AND id IN(?)", "name_3", g.Slice{1, 3}).Order("id ASC").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 3) }) @@ -1917,7 +1917,7 @@ func Test_Model_WherePri(t *testing.T) { "id": g.Slice{1, 3}, "nickname": "name_3", }).Order("id ASC").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 3) }) @@ -1931,7 +1931,7 @@ func Test_Model_WherePri(t *testing.T) { Ids: []int{1, 3}, Nickname: "name_3", }).Order("id ASC").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 3) }) @@ -1944,14 +1944,14 @@ func Test_Model_Delete(t *testing.T) { // DELETE...LIMIT gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(1).Limit(2).Delete() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 2) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Where(1).Delete() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, SIZE-2) }) @@ -1962,7 +1962,7 @@ func Test_Model_Offset(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Limit(2).Offset(5).Order("id").Select() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 2) t.Assert(result[0]["id"], 6) t.Assert(result[1]["id"], 7) @@ -1974,7 +1974,7 @@ func Test_Model_Page(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).Page(3, 3).Order("id").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"], 7) t.Assert(result[1]["id"], 8) @@ -1983,7 +1983,7 @@ func Test_Model_Page(t *testing.T) { model := db.Table(table).Safe().Order("id") all, err := model.Page(3, 3).All() count, err := model.Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 3) t.Assert(all[0]["id"], "7") t.Assert(count, SIZE) @@ -2001,11 +2001,11 @@ func Test_Model_Option_Map(t *testing.T) { "password": "1", "nickname": "1", }).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) one, err := db.Table(table).Where("id", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertNE(one["password"].String(), "1") t.AssertNE(one["nickname"].String(), "1") t.Assert(one["passport"].String(), "1") @@ -2019,11 +2019,11 @@ func Test_Model_Option_Map(t *testing.T) { "password": 0, "nickname": "1", }).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) one, err := db.Table(table).Where("id", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertNE(one["passport"].String(), "0") t.AssertNE(one["password"].String(), "0") t.Assert(one["nickname"].String(), "1") @@ -2039,9 +2039,9 @@ func Test_Model_Option_Map(t *testing.T) { "password": 0, "nickname": "1", }).Replace() - t.Assert(err, nil) + t.AssertNil(err) one, err := db.Table(table).Where("id", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertNE(one["passport"].String(), "0") t.AssertNE(one["password"].String(), "0") t.Assert(one["nickname"].String(), "1") @@ -2057,11 +2057,11 @@ func Test_Model_Option_Map(t *testing.T) { "password": "1", "nickname": "1", }).Save() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) one, err := db.Table(table).Where("id", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertNE(one["password"].String(), "1") t.AssertNE(one["nickname"].String(), "1") t.Assert(one["passport"].String(), "1") @@ -2075,9 +2075,9 @@ func Test_Model_Option_Map(t *testing.T) { "password": 0, "nickname": "1", }).Save() - t.Assert(err, nil) + t.AssertNil(err) one, err := db.Table(table).Where("id", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertNE(one["passport"].String(), "0") t.AssertNE(one["password"].String(), "0") t.Assert(one["nickname"].String(), "1") @@ -2088,9 +2088,9 @@ func Test_Model_Option_Map(t *testing.T) { "password": 0, "nickname": "1", }).Save() - t.Assert(err, nil) + t.AssertNil(err) one, err = db.Table(table).Where("id", 1).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"].String(), "0") t.Assert(one["password"].String(), "0") t.Assert(one["nickname"].String(), "1") @@ -2102,7 +2102,7 @@ func Test_Model_Option_Map(t *testing.T) { defer dropTable(table) r, err := db.Table(table).Data(g.Map{"nickname": ""}).Where("id", 1).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) @@ -2110,7 +2110,7 @@ func Test_Model_Option_Map(t *testing.T) { t.AssertNE(err, nil) r, err = db.Table(table).OmitEmpty().Data(g.Map{"nickname": "", "password": "123"}).Where("id", 3).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) @@ -2123,12 +2123,12 @@ func Test_Model_Option_Map(t *testing.T) { "passport": "123", "password": "456", }).Where("id", 5).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) one, err := db.Table(table).Where("id", 5).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["password"], "456") t.AssertNE(one["passport"].String(), "") t.AssertNE(one["passport"].String(), "123") @@ -2153,11 +2153,11 @@ func Test_Model_Option_List(t *testing.T) { "nickname": "2", }, }).Save() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 2) list, err := db.Table(table).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(list), 2) t.Assert(list[0]["id"].String(), "1") t.Assert(list[0]["nickname"].String(), "") @@ -2187,11 +2187,11 @@ func Test_Model_Option_List(t *testing.T) { "nickname": "2", }, }).Save() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 2) list, err := db.Table(table).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(list), 2) t.Assert(list[0]["id"].String(), "1") t.Assert(list[0]["nickname"].String(), "") @@ -2232,7 +2232,7 @@ func Test_Model_Option_Where(t *testing.T) { table := createInitTable() defer dropTable(table) r, err := db.Table(table).OmitEmpty().Data("nickname", 1).Where(g.Map{"id": 0, "passport": ""}).And(1).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, SIZE) }) @@ -2240,12 +2240,12 @@ func Test_Model_Option_Where(t *testing.T) { table := createInitTable() defer dropTable(table) r, err := db.Table(table).OmitEmpty().Data("nickname", 1).Where(g.Map{"id": 1, "passport": ""}).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) v, err := db.Table(table).Where("id", 1).Fields("nickname").Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "1") }) } @@ -2260,7 +2260,7 @@ func Test_Model_Where_MultiSliceArguments(t *testing.T) { "nickname": g.Slice{"name_2", "name_4"}, "id >= 4": nil, }).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(r), 1) t.Assert(r[0]["id"], 4) }) @@ -2270,7 +2270,7 @@ func Test_Model_Where_MultiSliceArguments(t *testing.T) { "id": g.Slice{1, 2, 3}, "passport": g.Slice{"user_2", "user_3"}, }).Or("nickname=?", g.Slice{"name_4"}).And("id", 3).One() - t.Assert(err, nil) + t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 2) }) @@ -2282,7 +2282,7 @@ func Test_Model_FieldsEx(t *testing.T) { // Select. gtest.C(t, func(t *gtest.T) { r, err := db.Table(table).FieldsEx("create_time, id").Where("id in (?)", g.Slice{1, 2}).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(r), 2) t.Assert(len(r[0]), 3) t.Assert(r[0]["id"], "") @@ -2299,12 +2299,12 @@ func Test_Model_FieldsEx(t *testing.T) { // Update. gtest.C(t, func(t *gtest.T) { r, err := db.Table(table).FieldsEx("password").Data(g.Map{"nickname": "123", "password": "456"}).Where("id", 3).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) one, err := db.Table(table).Where("id", 3).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["nickname"], "123") t.AssertNE(one["password"], "456") }) @@ -2319,7 +2319,7 @@ func Test_Model_FieldsEx_WithReservedWords(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { _, err := db.Table(table).FieldsEx("content").One() - t.Assert(err, nil) + t.AssertNil(err) }) } @@ -2351,7 +2351,7 @@ func Test_Model_Prefix(t *testing.T) { // Select. gtest.C(t, func(t *gtest.T) { r, err := db.Table(table).Where("id in (?)", g.Slice{1, 2}).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(r), 2) t.Assert(r[0]["id"], "1") t.Assert(r[1]["id"], "2") @@ -2359,7 +2359,7 @@ func Test_Model_Prefix(t *testing.T) { // Select with alias. gtest.C(t, func(t *gtest.T) { r, err := db.Table(table+" as u").Where("u.id in (?)", g.Slice{1, 2}).Order("u.id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(r), 2) t.Assert(r[0]["id"], "1") t.Assert(r[1]["id"], "2") @@ -2367,14 +2367,14 @@ func Test_Model_Prefix(t *testing.T) { // Select with alias and join statement. gtest.C(t, func(t *gtest.T) { r, err := db.Table(table+" as u1").LeftJoin(table+" as u2", "u2.id=u1.id").Where("u1.id in (?)", g.Slice{1, 2}).Order("u1.id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(r), 2) t.Assert(r[0]["id"], "1") t.Assert(r[1]["id"], "2") }) gtest.C(t, func(t *gtest.T) { r, err := db.Table(table).As("u1").LeftJoin(table+" as u2", "u2.id=u1.id").Where("u1.id in (?)", g.Slice{1, 2}).Order("u1.id asc").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(r), 2) t.Assert(r[0]["id"], "1") t.Assert(r[1]["id"], "2") @@ -2401,40 +2401,40 @@ func Test_Model_Schema1(t *testing.T) { gtest.C(t, func(t *gtest.T) { db.SetSchema(SCHEMA1) r, err := db.Table(table).Update(g.Map{"nickname": "name_100"}, "id=1") - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) v, err := db.Table(table).Value("nickname", "id=1") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_100") db.SetSchema(SCHEMA2) v, err = db.Table(table).Value("nickname", "id=1") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_1") }) // Model. gtest.C(t, func(t *gtest.T) { v, err := db.Table(table).Schema(SCHEMA1).Value("nickname", "id=2") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_2") r, err := db.Table(table).Schema(SCHEMA1).Update(g.Map{"nickname": "name_200"}, "id=2") - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) v, err = db.Table(table).Schema(SCHEMA1).Value("nickname", "id=2") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_200") v, err = db.Table(table).Schema(SCHEMA2).Value("nickname", "id=2") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_2") v, err = db.Table(table).Schema(SCHEMA1).Value("nickname", "id=2") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_200") }) // Model. @@ -2448,14 +2448,14 @@ func Test_Model_Schema1(t *testing.T) { "create_time": gtime.NewFromStr("2018-10-24 10:00:00").String(), "none-exist-field": 1, }) - t.Assert(err, nil) + t.AssertNil(err) v, err := db.Table(table).Schema(SCHEMA1).Value("nickname", "id=?", i) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_1000") v, err = db.Table(table).Schema(SCHEMA2).Value("nickname", "id=?", i) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "") }) } @@ -2479,24 +2479,24 @@ func Test_Model_Schema2(t *testing.T) { // Schema. gtest.C(t, func(t *gtest.T) { v, err := db.Schema(SCHEMA1).Table(table).Value("nickname", "id=2") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_2") r, err := db.Schema(SCHEMA1).Table(table).Update(g.Map{"nickname": "name_200"}, "id=2") - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) v, err = db.Schema(SCHEMA1).Table(table).Value("nickname", "id=2") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_200") v, err = db.Schema(SCHEMA2).Table(table).Value("nickname", "id=2") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_2") v, err = db.Schema(SCHEMA1).Table(table).Value("nickname", "id=2") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_200") }) // Schema. @@ -2510,14 +2510,14 @@ func Test_Model_Schema2(t *testing.T) { "create_time": gtime.NewFromStr("2018-10-24 10:00:00").String(), "none-exist-field": 1, }) - t.Assert(err, nil) + t.AssertNil(err) v, err := db.Schema(SCHEMA1).Table(table).Value("nickname", "id=?", i) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "name_1000") v, err = db.Schema(SCHEMA2).Table(table).Value("nickname", "id=?", i) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.String(), "") }) } @@ -2539,9 +2539,9 @@ func Test_Model_FieldsExStruct(t *testing.T) { NickName: "333", } r, err := db.Table(table).FieldsEx("create_time, password").OmitEmpty().Data(user).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, err := r.RowsAffected() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(n, 1) }) gtest.C(t, func(t *gtest.T) { @@ -2565,9 +2565,9 @@ func Test_Model_FieldsExStruct(t *testing.T) { Batch(2). Data(users). Insert() - t.Assert(err, nil) + t.AssertNil(err) n, err := r.RowsAffected() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(n, 10) }) } @@ -2589,9 +2589,9 @@ func Test_Model_OmitEmpty_Time(t *testing.T) { Time: time.Time{}, } r, err := db.Table(table).OmitEmpty().Data(user).WherePri(1).Update() - t.Assert(err, nil) + t.AssertNil(err) n, err := r.RowsAffected() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(n, 1) }) } @@ -2601,7 +2601,7 @@ func Test_Result_Chunk(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { r, err := db.Table(table).Order("id asc").All() - t.Assert(err, nil) + t.AssertNil(err) chunks := r.Chunk(3) t.Assert(len(chunks), 4) t.Assert(chunks[0][0]["id"].Int(), 1) @@ -2619,14 +2619,14 @@ func Test_Model_DryRun(t *testing.T) { gtest.C(t, func(t *gtest.T) { one, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["id"], 1) }) gtest.C(t, func(t *gtest.T) { r, err := db.Table(table).Data("passport", "port_1").WherePri(1).Update() - t.Assert(err, nil) + t.AssertNil(err) n, err := r.RowsAffected() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(n, 0) }) } @@ -2637,7 +2637,7 @@ func Test_Model_Join_SubQuery(t *testing.T) { gtest.C(t, func(t *gtest.T) { subQuery := fmt.Sprintf("select * from `%s`", table) r, err := db.Table(table, "t1").Fields("t2.id").LeftJoin(subQuery, "t2", "t2.id=t1.id").Array() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(r), SIZE) t.Assert(r[0], "1") t.Assert(r[SIZE-1], SIZE) @@ -2650,95 +2650,95 @@ func Test_Model_Cache(t *testing.T) { gtest.C(t, func(t *gtest.T) { one, err := db.Table(table).Cache(time.Second, "test1").FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], "user_1") r, err := db.Table(table).Data("passport", "user_100").WherePri(1).Update() - t.Assert(err, nil) + t.AssertNil(err) n, err := r.RowsAffected() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(n, 1) one, err = db.Table(table).Cache(time.Second, "test1").FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], "user_1") time.Sleep(time.Second * 2) one, err = db.Table(table).Cache(time.Second, "test1").FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], "user_100") }) gtest.C(t, func(t *gtest.T) { one, err := db.Table(table).Cache(time.Second, "test2").FindOne(2) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], "user_2") r, err := db.Table(table).Data("passport", "user_200").Cache(-1, "test2").WherePri(2).Update() - t.Assert(err, nil) + t.AssertNil(err) n, err := r.RowsAffected() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(n, 1) one, err = db.Table(table).Cache(time.Second, "test2").FindOne(2) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], "user_200") }) // transaction. gtest.C(t, func(t *gtest.T) { // make cache for id 3 one, err := db.Table(table).Cache(time.Second, "test3").FindOne(3) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], "user_3") r, err := db.Table(table).Data("passport", "user_300").Cache(time.Second, "test3").WherePri(3).Update() - t.Assert(err, nil) + t.AssertNil(err) n, err := r.RowsAffected() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(n, 1) err = db.Transaction(func(tx *gdb.TX) error { one, err := tx.Table(table).Cache(time.Second, "test3").FindOne(3) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], "user_300") return nil }) - t.Assert(err, nil) + t.AssertNil(err) one, err = db.Table(table).Cache(time.Second, "test3").FindOne(3) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], "user_3") }) gtest.C(t, func(t *gtest.T) { // make cache for id 4 one, err := db.Table(table).Cache(time.Second, "test4").FindOne(4) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], "user_4") r, err := db.Table(table).Data("passport", "user_400").Cache(time.Second, "test3").WherePri(4).Update() - t.Assert(err, nil) + t.AssertNil(err) n, err := r.RowsAffected() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(n, 1) err = db.Transaction(func(tx *gdb.TX) error { // Cache feature disabled. one, err := tx.Table(table).Cache(time.Second, "test4").FindOne(4) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], "user_400") // Update the cache. r, err := tx.Table(table).Data("passport", "user_4000"). Cache(-1, "test4").WherePri(4).Update() - t.Assert(err, nil) + t.AssertNil(err) n, err := r.RowsAffected() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(n, 1) return nil }) - t.Assert(err, nil) + t.AssertNil(err) // Read from db. one, err = db.Table(table).Cache(time.Second, "test4").FindOne(4) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["passport"], "user_4000") }) } @@ -2749,22 +2749,22 @@ func Test_Model_Having(t *testing.T) { gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).Where("id > 1").Having("id > 8").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 2) }) gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).Where("id > 1").Having("id > ?", 8).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 2) }) gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).Where("id > ?", 1).Having("id > ?", 8).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 2) }) gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).Where("id > ?", 1).Having("id", 8).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 1) }) } @@ -2775,7 +2775,7 @@ func Test_Model_Distinct(t *testing.T) { gtest.C(t, func(t *gtest.T) { all, err := db.Table(table, "t").Fields("distinct t.id").Where("id > 1").Having("id > 8").All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 2) }) } @@ -2786,12 +2786,12 @@ func Test_Model_Min_Max(t *testing.T) { gtest.C(t, func(t *gtest.T) { value, err := db.Table(table, "t").Fields("min(t.id)").Where("id > 1").Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.Int(), 2) }) gtest.C(t, func(t *gtest.T) { value, err := db.Table(table, "t").Fields("max(t.id)").Where("id > 1").Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.Int(), 10) }) } @@ -2802,13 +2802,13 @@ func Test_Model_Fields_AutoMapping(t *testing.T) { gtest.C(t, func(t *gtest.T) { value, err := db.Table(table).Fields("ID").Where("id", 2).Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.Int(), 2) }) gtest.C(t, func(t *gtest.T) { value, err := db.Table(table).Fields("NICK_NAME").Where("id", 2).Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.String(), "name_2") }) // Map @@ -2817,7 +2817,7 @@ func Test_Model_Fields_AutoMapping(t *testing.T) { "ID": 1, "NICK_NAME": 1, }).Where("id", 2).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one), 2) t.Assert(one["id"], 2) t.Assert(one["nickname"], "name_2") @@ -2832,7 +2832,7 @@ func Test_Model_Fields_AutoMapping(t *testing.T) { ID: 0, NICKNAME: 0, }).Where("id", 2).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one), 2) t.Assert(one["id"], 2) t.Assert(one["nickname"], "name_2") @@ -2851,13 +2851,13 @@ func Test_Model_FieldsEx_AutoMapping(t *testing.T) { gtest.C(t, func(t *gtest.T) { value, err := db.Table(table).FieldsEx("Passport, Password, NickName, CreateTime").Where("id", 2).Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.Int(), 2) }) gtest.C(t, func(t *gtest.T) { value, err := db.Table(table).FieldsEx("ID, Passport, Password, CreateTime").Where("id", 2).Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(value.String(), "name_2") }) // Map @@ -2867,7 +2867,7 @@ func Test_Model_FieldsEx_AutoMapping(t *testing.T) { "Password": 1, "CreateTime": 1, }).Where("id", 2).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one), 2) t.Assert(one["id"], 2) t.Assert(one["nickname"], "name_2") @@ -2884,7 +2884,7 @@ func Test_Model_FieldsEx_AutoMapping(t *testing.T) { Password: 0, CreateTime: 0, }).Where("id", 2).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one), 2) t.Assert(one["id"], 2) t.Assert(one["nickname"], "name_2") @@ -2905,21 +2905,21 @@ func Test_Model_Fields_Struct(t *testing.T) { } gtest.C(t, func(t *gtest.T) { one, err := db.Table(table).Fields(A{}).Where("id", 2).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one), 2) t.Assert(one["passport"], "user_2") t.Assert(one["password"], "pass_2") }) gtest.C(t, func(t *gtest.T) { one, err := db.Table(table).Fields(&A{}).Where("id", 2).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one), 2) t.Assert(one["passport"], "user_2") t.Assert(one["password"], "pass_2") }) gtest.C(t, func(t *gtest.T) { one, err := db.Table(table).Fields(B{}).Where("id", 2).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one), 3) t.Assert(one["passport"], "user_2") t.Assert(one["password"], "pass_2") @@ -2927,7 +2927,7 @@ func Test_Model_Fields_Struct(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { one, err := db.Table(table).Fields(&B{}).Where("id", 2).One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one), 3) t.Assert(one["passport"], "user_2") t.Assert(one["password"], "pass_2") @@ -2948,15 +2948,15 @@ func Test_Model_NullField(t *testing.T) { "passport": nil, } result, err := db.Table(table).Data(data).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) one, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) var user *User err = one.Struct(&user) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(user.Id, data["id"]) t.Assert(user.Passport, data["passport"]) }) @@ -2967,12 +2967,12 @@ func Test_Model_Empty_Slice_Argument(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { result, err := db.Model(table).Where(`id`, g.Slice{}).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 0) }) gtest.C(t, func(t *gtest.T) { result, err := db.Model(table).Where(`id in(?)`, g.Slice{}).All() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(result), 0) }) } @@ -2984,13 +2984,13 @@ func Test_Model_HasTable(t *testing.T) { gtest.C(t, func(t *gtest.T) { result, err := db.HasTable(table) t.Assert(result, true) - t.Assert(err, nil) + t.AssertNil(err) }) gtest.C(t, func(t *gtest.T) { result, err := db.HasTable("table12321") t.Assert(result, false) - t.Assert(err, nil) + t.AssertNil(err) }) } @@ -3001,13 +3001,13 @@ func Test_Model_HasField(t *testing.T) { gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).HasField("id") t.Assert(result, true) - t.Assert(err, nil) + t.AssertNil(err) }) gtest.C(t, func(t *gtest.T) { result, err := db.Table(table).HasField("id123") t.Assert(result, false) - t.Assert(err, nil) + t.AssertNil(err) }) } @@ -3023,56 +3023,56 @@ func Test_Model_Issue1002(t *testing.T) { "nickname": "name_2", "create_time": "2020-10-27 19:03:33", }).Insert() - gtest.Assert(err, nil) + gtest.AssertNil(err) n, _ := result.RowsAffected() gtest.Assert(n, 1) // where + string. gtest.C(t, func(t *gtest.T) { v, err := db.Table(table).Fields("id").Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").Value() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.Int(), 1) }) gtest.C(t, func(t *gtest.T) { v, err := db.Table(table).Fields("id").Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").FindValue() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.Int(), 1) }) gtest.C(t, func(t *gtest.T) { v, err := db.Table(table).Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").FindValue("id") - t.Assert(err, nil) + t.AssertNil(err) t.Assert(v.Int(), 1) }) // where + string arguments. gtest.C(t, func(t *gtest.T) { v, err := db.Table(table).Fields("id").Where("create_time>? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time100").All() - t.Assert(err, nil) + t.AssertNil(err) users := make([]User, 0) t.Assert(all.Structs(&users), nil) }) gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).Where("id>100").All() - t.Assert(err, nil) + t.AssertNil(err) users := make([]User, 10) t.AssertNE(all.Structs(&users), nil) }) gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).Where("id>100").All() - t.Assert(err, nil) + t.AssertNil(err) var users []User t.Assert(all.Structs(&users), nil) }) gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).Where("id>100").All() - t.Assert(err, nil) + t.AssertNil(err) users := make([]*User, 0) t.Assert(all.Structs(&users), nil) }) gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).Where("id>100").All() - t.Assert(err, nil) + t.AssertNil(err) users := make([]*User, 10) t.Assert(all.Structs(&users), nil) }) gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).Where("id>100").All() - t.Assert(err, nil) + t.AssertNil(err) var users []*User t.Assert(all.Structs(&users), nil) }) @@ -349,13 +349,13 @@ func Test_Model_Scan_CustomType(t *testing.T) { gtest.C(t, func(t *gtest.T) { st := new(MyTimeSt) err := db.Table(table).Fields("create_time").Scan(st) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(st.CreateTime.String(), "2018-10-24 10:00:00") }) gtest.C(t, func(t *gtest.T) { var stSlice []*MyTimeSt err := db.Table(table).Fields("create_time").Scan(&stSlice) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(stSlice), SIZE) t.Assert(stSlice[0].CreateTime.String(), "2018-10-24 10:00:00") t.Assert(stSlice[9].CreateTime.String(), "2018-10-24 10:00:00") diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index 74bce2b97..3133b4c1d 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -41,12 +41,12 @@ CREATE TABLE %s ( "name": "name_1", } r, err := db.Table(table).Data(dataInsert).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) oneInsert, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneInsert["id"].Int(), 1) t.Assert(oneInsert["name"].String(), "name_1") t.Assert(oneInsert["delete_at"].String(), "") @@ -62,12 +62,12 @@ CREATE TABLE %s ( "name": "name_10", } r, err = db.Table(table).Data(dataSave).Save() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) oneSave, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneSave["id"].Int(), 1) t.Assert(oneSave["name"].String(), "name_10") t.Assert(oneSave["delete_at"].String(), "") @@ -83,12 +83,12 @@ CREATE TABLE %s ( "name": "name_1000", } r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) oneUpdate, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneUpdate["id"].Int(), 1) t.Assert(oneUpdate["name"].String(), "name_1000") t.Assert(oneUpdate["delete_at"].String(), "") @@ -101,12 +101,12 @@ CREATE TABLE %s ( "name": "name_100", } r, err = db.Table(table).Data(dataReplace).Replace() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) oneReplace, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneReplace["id"].Int(), 1) t.Assert(oneReplace["name"].String(), "name_100") t.Assert(oneReplace["delete_at"].String(), "") @@ -118,35 +118,35 @@ CREATE TABLE %s ( // Delete r, err = db.Table(table).Delete("id", 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) // Delete Select one4, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one4), 0) one5, err := db.Table(table).Unscoped().FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one5["id"].Int(), 1) t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count i, err := db.Table(table).FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 0) i, err = db.Table(table).Unscoped().FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 1) // Delete Unscoped r, err = db.Table(table).Unscoped().Delete("id", 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) one6, err := db.Table(table).Unscoped().FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one6), 0) i, err = db.Table(table).Unscoped().FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 0) }) } @@ -175,12 +175,12 @@ CREATE TABLE %s ( "name": "name_1", } r, err := db.Table(table).Data(dataInsert).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) oneInsert, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneInsert["id"].Int(), 1) t.Assert(oneInsert["name"].String(), "name_1") t.Assert(oneInsert["deleted_at"].String(), "") @@ -196,12 +196,12 @@ CREATE TABLE %s ( "name": "name_10", } r, err = db.Table(table).Data(dataSave).Save() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) oneSave, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneSave["id"].Int(), 1) t.Assert(oneSave["name"].String(), "name_10") t.Assert(oneSave["deleted_at"].String(), "") @@ -217,12 +217,12 @@ CREATE TABLE %s ( "name": "name_1000", } r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) oneUpdate, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneUpdate["id"].Int(), 1) t.Assert(oneUpdate["name"].String(), "name_1000") t.Assert(oneUpdate["deleted_at"].String(), "") @@ -235,12 +235,12 @@ CREATE TABLE %s ( "name": "name_100", } r, err = db.Table(table).Data(dataReplace).Replace() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) oneReplace, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneReplace["id"].Int(), 1) t.Assert(oneReplace["name"].String(), "name_100") t.Assert(oneReplace["deleted_at"].String(), "") @@ -252,35 +252,35 @@ CREATE TABLE %s ( // Delete r, err = db.Table(table).Delete("id", 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) // Delete Select one4, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one4), 0) one5, err := db.Table(table).Unscoped().FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one5["id"].Int(), 1) t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count i, err := db.Table(table).FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 0) i, err = db.Table(table).Unscoped().FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 1) // Delete Unscoped r, err = db.Table(table).Unscoped().Delete("id", 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) one6, err := db.Table(table).Unscoped().FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one6), 0) i, err = db.Table(table).Unscoped().FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 0) }) } @@ -316,12 +316,12 @@ CREATE TABLE %s ( Name: "name_1", } r, err := db.Table(table).Data(dataInsert).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) oneInsert, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneInsert["id"].Int(), 1) t.Assert(oneInsert["name"].String(), "name_1") t.Assert(oneInsert["deleted_at"].String(), "") @@ -337,12 +337,12 @@ CREATE TABLE %s ( Name: "name_10", } r, err = db.Table(table).Data(dataSave).OmitEmpty().Save() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) oneSave, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneSave["id"].Int(), 1) t.Assert(oneSave["name"].String(), "name_10") t.Assert(oneSave["deleted_at"].String(), "") @@ -358,12 +358,12 @@ CREATE TABLE %s ( Name: "name_1000", } r, err = db.Table(table).Data(dataUpdate).OmitEmpty().WherePri(1).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) oneUpdate, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneUpdate["id"].Int(), 1) t.Assert(oneUpdate["name"].String(), "name_1000") t.Assert(oneUpdate["deleted_at"].String(), "") @@ -376,12 +376,12 @@ CREATE TABLE %s ( Name: "name_100", } r, err = db.Table(table).Data(dataReplace).OmitEmpty().Replace() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) oneReplace, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneReplace["id"].Int(), 1) t.Assert(oneReplace["name"].String(), "name_100") t.Assert(oneReplace["deleted_at"].String(), "") @@ -393,35 +393,35 @@ CREATE TABLE %s ( // Delete r, err = db.Table(table).Delete("id", 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) // Delete Select one4, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one4), 0) one5, err := db.Table(table).Unscoped().FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one5["id"].Int(), 1) t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count i, err := db.Table(table).FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 0) i, err = db.Table(table).Unscoped().FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 1) // Delete Unscoped r, err = db.Table(table).Unscoped().Delete("id", 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) one6, err := db.Table(table).Unscoped().FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one6), 0) i, err = db.Table(table).Unscoped().FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 0) }) } @@ -449,18 +449,18 @@ CREATE TABLE %s ( "num": 10, } r, err := db.Table(table).Data(dataInsert).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) oneInsert, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneInsert["id"].Int(), 1) t.Assert(oneInsert["num"].Int(), 10) // Update. r, err = db.Table(table).Data("num=num+1").Where("id=?", 1).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) }) @@ -489,21 +489,21 @@ CREATE TABLE %s ( "name": fmt.Sprintf("name_%d", i), } r, err := db.Table(table).Data(data).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) } }) gtest.C(t, func(t *gtest.T) { one, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.AssertNE(one["create_at"].String(), "") t.AssertNE(one["update_at"].String(), "") t.Assert(one["delete_at"].String(), "") }) gtest.C(t, func(t *gtest.T) { one, err := db.Table(table).FindOne(10) - t.Assert(err, nil) + t.AssertNil(err) t.AssertNE(one["create_at"].String(), "") t.AssertNE(one["update_at"].String(), "") t.Assert(one["delete_at"].String(), "") @@ -511,16 +511,16 @@ CREATE TABLE %s ( gtest.C(t, func(t *gtest.T) { ids := g.SliceInt{1, 3, 5} r, err := db.Table(table).Where("id", ids).Delete() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 3) count, err := db.Table(table).FindCount(ids) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 0) all, err := db.Table(table).Unscoped().FindAll(ids) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(all), 3) t.AssertNE(all[0]["create_at"].String(), "") t.AssertNE(all[0]["update_at"].String(), "") @@ -572,7 +572,7 @@ CREATE TABLE %s ( "name": "name_1", } r, err := db.Table(table1).Data(dataInsert1).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) @@ -581,26 +581,26 @@ CREATE TABLE %s ( "name": "name_2", } r, err = db.Table(table2).Data(dataInsert2).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) one, err := db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["name"], "name_1") // Soft deleting. r, err = db.Table(table1).Delete() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) one, err = db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one.IsEmpty(), true) one, err = db.Table(table2, "t2").LeftJoin(table1, "t1", "t2.id=t1.id").Fields("t2.name").FindOne() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one.IsEmpty(), true) }) } @@ -629,7 +629,7 @@ CREATE TABLE %s ( "name": fmt.Sprintf("name_%d", i), } r, err := db.Table(table).Data(data).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) } @@ -637,12 +637,12 @@ CREATE TABLE %s ( gtest.C(t, func(t *gtest.T) { ids := g.SliceInt{1, 3, 5} r, err := db.Table(table).Where("id", ids).Delete() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 3) count, err := db.Table(table).Where("id", 1).Or("id", 3).Count() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(count, 0) }) } @@ -680,12 +680,12 @@ CREATE TABLE %s ( DeleteAt: nil, } r, err := db.Table(table).Data(dataInsert).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) oneInsert, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneInsert["id"].Int(), 1) t.Assert(oneInsert["name"].String(), "name_1") t.Assert(oneInsert["delete_at"].String(), "") @@ -703,12 +703,12 @@ CREATE TABLE %s ( DeleteAt: nil, } r, err = db.Table(table).Data(dataSave).Save() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) oneSave, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneSave["id"].Int(), 1) t.Assert(oneSave["name"].String(), "name_10") t.Assert(oneSave["delete_at"].String(), "") @@ -727,12 +727,12 @@ CREATE TABLE %s ( DeleteAt: nil, } r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) oneUpdate, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneUpdate["id"].Int(), 1) t.Assert(oneUpdate["name"].String(), "name_1000") t.Assert(oneUpdate["delete_at"].String(), "") @@ -748,12 +748,12 @@ CREATE TABLE %s ( DeleteAt: nil, } r, err = db.Table(table).Data(dataReplace).Replace() - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) oneReplace, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(oneReplace["id"].Int(), 1) t.Assert(oneReplace["name"].String(), "name_100") t.Assert(oneReplace["delete_at"].String(), "") @@ -764,35 +764,35 @@ CREATE TABLE %s ( // Delete r, err = db.Table(table).Delete("id", 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) // Delete Select one4, err := db.Table(table).FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one4), 0) one5, err := db.Table(table).Unscoped().FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one5["id"].Int(), 1) t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count i, err := db.Table(table).FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 0) i, err = db.Table(table).Unscoped().FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 1) // Delete Unscoped r, err = db.Table(table).Unscoped().Delete("id", 1) - t.Assert(err, nil) + t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) one6, err := db.Table(table).Unscoped().FindOne(1) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(one6), 0) i, err = db.Table(table).Unscoped().FindCount() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(i, 0) }) } diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index be10ab716..38ea6f4a3 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -90,23 +90,23 @@ func Test_TX_Rollback(t *testing.T) { func Test_TX_Prepare(t *testing.T) { gtest.C(t, func(t *gtest.T) { tx, err := db.Begin() - t.Assert(err, nil) + t.AssertNil(err) st, err := tx.Prepare("SELECT 100") - t.Assert(err, nil) + t.AssertNil(err) rows, err := st.Query() - t.Assert(err, nil) + t.AssertNil(err) array, err := rows.Columns() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(array[0], "100") rows.Close() - t.Assert(err, nil) + t.AssertNil(err) tx.Commit() - t.Assert(err, nil) + t.AssertNil(err) }) } @@ -752,7 +752,7 @@ func Test_Transaction(t *testing.T) { } return nil }) - t.Assert(err, nil) + t.AssertNil(err) if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil { gtest.Error(err) diff --git a/database/gdb/gdb_z_mysql_types_test.go b/database/gdb/gdb_z_mysql_types_test.go index 792af82a2..bf0b1efb4 100644 --- a/database/gdb/gdb_z_mysql_types_test.go +++ b/database/gdb/gdb_z_mysql_types_test.go @@ -59,12 +59,12 @@ func Test_Types(t *testing.T) { "bool": false, } r, err := db.Table("types").Data(data).Insert() - t.Assert(err, nil) + t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) one, err := db.Table("types").One() - t.Assert(err, nil) + t.AssertNil(err) t.Assert(one["id"].Int(), 1) t.Assert(one["blob"].String(), data["blob"]) t.Assert(one["binary"].String(), data["binary"]) @@ -88,7 +88,7 @@ func Test_Types(t *testing.T) { } var obj *T err = db.Table("types").Struct(&obj) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(obj.Id, 1) t.Assert(obj.Blob, data["blob"]) t.Assert(obj.Binary, data["binary"]) diff --git a/test/gtest/gtest_t.go b/test/gtest/gtest_t.go index a0854a852..4ce3dae8e 100644 --- a/test/gtest/gtest_t.go +++ b/test/gtest/gtest_t.go @@ -15,74 +15,79 @@ type T struct { *testing.T } -// Assert checks and EQUAL. +// Assert checks `value` and `expect` EQUAL. func (t *T) Assert(value, expect interface{}) { Assert(value, expect) } -// AssertEQ checks and EQUAL, including their TYPES. +// AssertEQ checks `value` and `expect` EQUAL, including their TYPES. func (t *T) AssertEQ(value, expect interface{}) { AssertEQ(value, expect) } -// AssertNE checks and NOT EQUAL. +// AssertNE checks `value` and `expect` NOT EQUAL. func (t *T) AssertNE(value, expect interface{}) { AssertNE(value, expect) } -// AssertNQ checks and NOT EQUAL, including their TYPES. +// AssertNQ checks `value` and `expect` NOT EQUAL, including their TYPES. func (t *T) AssertNQ(value, expect interface{}) { AssertNQ(value, expect) } -// AssertGT checks is GREATER THAN . +// AssertGT checks `value` is GREATER THAN `expect`. // Notice that, only string, integer and float types can be compared by AssertGT, // others are invalid. func (t *T) AssertGT(value, expect interface{}) { AssertGT(value, expect) } -// AssertGE checks is GREATER OR EQUAL THAN . +// AssertGE checks `value` is GREATER OR EQUAL THAN `expect`. // Notice that, only string, integer and float types can be compared by AssertGTE, // others are invalid. func (t *T) AssertGE(value, expect interface{}) { AssertGE(value, expect) } -// AssertLT checks is LESS EQUAL THAN . +// AssertLT checks `value` is LESS EQUAL THAN `expect`. // Notice that, only string, integer and float types can be compared by AssertLT, // others are invalid. func (t *T) AssertLT(value, expect interface{}) { AssertLT(value, expect) } -// AssertLE checks is LESS OR EQUAL THAN . +// AssertLE checks `value` is LESS OR EQUAL THAN `expect`. // Notice that, only string, integer and float types can be compared by AssertLTE, // others are invalid. func (t *T) AssertLE(value, expect interface{}) { AssertLE(value, expect) } -// AssertIN checks is IN . -// The should be a slice, -// but the can be a slice or a basic type variable. +// AssertIN checks `value` is IN `expect`. +// The `expect` should be a slice, +// but the `value` can be a slice or a basic type variable. func (t *T) AssertIN(value, expect interface{}) { AssertIN(value, expect) } -// AssertNI checks is NOT IN . -// The should be a slice, -// but the can be a slice or a basic type variable. +// AssertNI checks `value` is NOT IN `expect`. +// The `expect` should be a slice, +// but the `value` can be a slice or a basic type variable. func (t *T) AssertNI(value, expect interface{}) { AssertNI(value, expect) } -// Error panics with given . +// AssertNil asserts `value` is nil. +func (t *T) AssertNil(value interface{}) { + AssertNil(value) +} + +// Error panics with given `message`. func (t *T) Error(message ...interface{}) { Error(message...) } -// Fatal prints to stderr and exit the process. +// Fatal prints `message` to stderr and exit the process. func (t *T) Fatal(message ...interface{}) { Fatal(message...) } diff --git a/test/gtest/gtest_util.go b/test/gtest/gtest_util.go index 8749478be..60b90d8de 100644 --- a/test/gtest/gtest_util.go +++ b/test/gtest/gtest_util.go @@ -8,6 +8,7 @@ package gtest import ( "fmt" + "github.com/gogf/gf/internal/empty" "os" "reflect" "testing" @@ -18,16 +19,16 @@ import ( ) const ( - gPATH_FILTER_KEY = "/test/gtest/gtest" + pathFilterKey = "/test/gtest/gtest" ) // C creates an unit testing case. -// The parameter is the pointer to testing.T of stdlib (*testing.T). -// The parameter is the closure function for unit testing case. +// The parameter `t` is the pointer to testing.T of stdlib (*testing.T). +// The parameter `f` is the closure function for unit testing case. func C(t *testing.T, f func(t *T)) { defer func() { if err := recover(); err != nil { - fmt.Fprintf(os.Stderr, "%v\n%s", err, gdebug.StackWithFilter(gPATH_FILTER_KEY)) + fmt.Fprintf(os.Stderr, "%v\n%s", err, gdebug.StackWithFilter(pathFilterKey)) t.Fail() } }() @@ -35,23 +36,23 @@ func C(t *testing.T, f func(t *T)) { } // Case creates an unit testing case. -// The parameter is the pointer to testing.T of stdlib (*testing.T). -// The parameter is the closure function for unit testing case. +// The parameter `t` is the pointer to testing.T of stdlib (*testing.T). +// The parameter `f` is the closure function for unit testing case. // Deprecated. func Case(t *testing.T, f func()) { defer func() { if err := recover(); err != nil { - fmt.Fprintf(os.Stderr, "%v\n%s", err, gdebug.StackWithFilter(gPATH_FILTER_KEY)) + fmt.Fprintf(os.Stderr, "%v\n%s", err, gdebug.StackWithFilter(pathFilterKey)) t.Fail() } }() f() } -// Assert checks and EQUAL. +// Assert checks `value` and `expect` EQUAL. func Assert(value, expect interface{}) { rvExpect := reflect.ValueOf(expect) - if isNil(value) { + if empty.IsNil(value) { value = nil } if rvExpect.Kind() == reflect.Map { @@ -69,11 +70,11 @@ func Assert(value, expect interface{}) { } } -// AssertEQ checks and EQUAL, including their TYPES. +// AssertEQ checks `value` and `expect` EQUAL, including their TYPES. func AssertEQ(value, expect interface{}) { // Value assert. rvExpect := reflect.ValueOf(expect) - if isNil(value) { + if empty.IsNil(value) { value = nil } if rvExpect.Kind() == reflect.Map { @@ -95,10 +96,10 @@ func AssertEQ(value, expect interface{}) { } } -// AssertNE checks and NOT EQUAL. +// AssertNE checks `value` and `expect` NOT EQUAL. func AssertNE(value, expect interface{}) { rvExpect := reflect.ValueOf(expect) - if isNil(value) { + if empty.IsNil(value) { value = nil } if rvExpect.Kind() == reflect.Map { @@ -116,7 +117,7 @@ func AssertNE(value, expect interface{}) { } } -// AssertNQ checks and NOT EQUAL, including their TYPES. +// AssertNQ checks `value` and `expect` NOT EQUAL, including their TYPES. func AssertNQ(value, expect interface{}) { // Type assert. t1 := reflect.TypeOf(value) @@ -133,7 +134,7 @@ func AssertNQ(value, expect interface{}) { AssertNE(value, expect) } -// AssertGT checks is GREATER THAN . +// AssertGT checks `value` is GREATER THAN `expect`. // Notice that, only string, integer and float types can be compared by AssertGT, // others are invalid. func AssertGT(value, expect interface{}) { @@ -156,7 +157,7 @@ func AssertGT(value, expect interface{}) { } } -// AssertGE checks is GREATER OR EQUAL THAN . +// AssertGE checks `value` is GREATER OR EQUAL THAN `expect`. // Notice that, only string, integer and float types can be compared by AssertGTE, // others are invalid. func AssertGE(value, expect interface{}) { @@ -183,7 +184,7 @@ func AssertGE(value, expect interface{}) { } } -// AssertLT checks is LESS EQUAL THAN . +// AssertLT checks `value` is LESS EQUAL THAN `expect`. // Notice that, only string, integer and float types can be compared by AssertLT, // others are invalid. func AssertLT(value, expect interface{}) { @@ -206,7 +207,7 @@ func AssertLT(value, expect interface{}) { } } -// AssertLE checks is LESS OR EQUAL THAN . +// AssertLE checks `value` is LESS OR EQUAL THAN `expect`. // Notice that, only string, integer and float types can be compared by AssertLTE, // others are invalid. func AssertLE(value, expect interface{}) { @@ -229,13 +230,15 @@ func AssertLE(value, expect interface{}) { } } -// AssertIN checks is IN . -// The should be a slice, -// but the can be a slice or a basic type variable. +// AssertIN checks `value` is IN `expect`. +// The `expect` should be a slice, +// but the `value` can be a slice or a basic type variable. // TODO map support. func AssertIN(value, expect interface{}) { - passed := true - expectKind := reflect.ValueOf(expect).Kind() + var ( + passed = true + expectKind = reflect.ValueOf(expect).Kind() + ) switch expectKind { case reflect.Slice, reflect.Array: expectSlice := gconv.Strings(expect) @@ -260,13 +263,15 @@ func AssertIN(value, expect interface{}) { } } -// AssertNI checks is NOT IN . -// The should be a slice, -// but the can be a slice or a basic type variable. +// AssertNI checks `value` is NOT IN `expect`. +// The `expect` should be a slice, +// but the `value` can be a slice or a basic type variable. // TODO map support. func AssertNI(value, expect interface{}) { - passed := true - expectKind := reflect.ValueOf(expect).Kind() + var ( + passed = true + expectKind = reflect.ValueOf(expect).Kind() + ) switch expectKind { case reflect.Slice, reflect.Array: for _, v1 := range gconv.Strings(value) { @@ -290,22 +295,24 @@ func AssertNI(value, expect interface{}) { } } -// Error panics with given . +// Error panics with given `message`. func Error(message ...interface{}) { panic(fmt.Sprintf("[ERROR] %s", fmt.Sprint(message...))) } -// Fatal prints to stderr and exit the process. +// Fatal prints `message` to stderr and exit the process. func Fatal(message ...interface{}) { - fmt.Fprintf(os.Stderr, "[FATAL] %s\n%s", fmt.Sprint(message...), gdebug.StackWithFilter(gPATH_FILTER_KEY)) + fmt.Fprintf(os.Stderr, "[FATAL] %s\n%s", fmt.Sprint(message...), gdebug.StackWithFilter(pathFilterKey)) os.Exit(1) } // compareMap compares two maps, returns nil if they are equal, or else returns error. func compareMap(value, expect interface{}) error { - rvValue := reflect.ValueOf(value) - rvExpect := reflect.ValueOf(expect) - if isNil(value) { + var ( + rvValue = reflect.ValueOf(value) + rvExpect = reflect.ValueOf(expect) + ) + if empty.IsNil(value) { value = nil } if rvExpect.Kind() == reflect.Map { @@ -340,13 +347,13 @@ func compareMap(value, expect interface{}) error { return nil } -// isNil checks whether is nil. -func isNil(value interface{}) bool { - rv := reflect.ValueOf(value) - switch rv.Kind() { - case reflect.Slice, reflect.Array, reflect.Map, reflect.Ptr, reflect.Func: - return rv.IsNil() - default: - return value == nil +// AssertNil asserts `value` is nil. +func AssertNil(value interface{}) { + if empty.IsNil(value) { + return } + if err, ok := value.(error); ok { + panic(fmt.Sprintf(`%+v`, err)) + } + AssertNE(value, nil) } From 7a5d86b44d01b811f3e38b0a338ab88b6b72e741 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Sun, 7 Feb 2021 15:34:36 +0800 Subject: [PATCH 153/492] README updates --- README.MD | 10 +++++----- README_ZH.MD | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.MD b/README.MD index 20a588755..97ccc9d92 100644 --- a/README.MD +++ b/README.MD @@ -1,7 +1,7 @@ # GoFrame [![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) -[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) +[![Build Status](https://travis-ci.com/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) [![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf) [![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master) [![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf) @@ -33,7 +33,7 @@ golang version >= 1.11 # Architecture
- +
# Packages @@ -80,7 +80,7 @@ The `Web` component performance of `GoFrame`, please refer to third-party projec - [XiMaLaYa](https://www.ximalaya.com) - [ZYBang](https://www.zybang.com/) -> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://itician.org/pages/viewpage.action?pageId=1114415). +> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://goframe.org/pages/viewpage.action?pageId=1114415). # Contributors @@ -90,7 +90,7 @@ This project exists thanks to all the people who contribute. [[Contributors](htt # Donators -If you love `GF`, why not [buy developer a cup of coffee](https://itician.org/pages/viewpage.action?pageId=1115633)? +If you love `GF`, why not [buy developer a cup of coffee](https://goframe.org/pages/viewpage.action?pageId=1115633)? # Sponsors We appreciate any kind of sponsorship for `GF` development. If you've got some interesting, please contact WeChat `389961817` / Email `john@goframe.org`. @@ -98,7 +98,7 @@ We appreciate any kind of sponsorship for `GF` development. If you've got some i # Thanks -JetBrains +JetBrains Atlassian diff --git a/README_ZH.MD b/README_ZH.MD index f53b254bb..9fdcdf486 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -1,6 +1,6 @@ # GoFrame [![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) -[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) +[![Build Status](https://travis-ci.com/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) [![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf) [![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master) [![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf) @@ -49,7 +49,7 @@ golang版本 >= 1.11 # 架构
- +
# 模块 @@ -95,7 +95,7 @@ golang版本 >= 1.11 - [喜马拉雅](https://www.ximalaya.com) - [作业帮](https://www.zybang.com/) -> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://itician.org/pages/viewpage.action?pageId=1114415) 留言。 +> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://goframe.org/pages/viewpage.action?pageId=1114415) 留言。 # 贡献 @@ -105,7 +105,7 @@ golang版本 >= 1.11 # 捐赠 -如果您喜欢`GF`,要不给开发者 [来杯咖啡](https://itician.org/pages/viewpage.action?pageId=1115633) 吧! +如果您喜欢`GF`,要不给开发者 [来杯咖啡](https://goframe.org/pages/viewpage.action?pageId=1115633) 吧! 请在捐赠时备注您的`github`/`gitee`账号名称。 # 赞助 @@ -113,6 +113,6 @@ golang版本 >= 1.11 赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 微信 `389961817` / 邮件 `john@goframe.org`。 # 感谢 -JetBrains +JetBrains Atlassian From b4d5335e43c7eb1556380e8fac86d0d139914720 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 7 Feb 2021 21:23:09 +0800 Subject: [PATCH 154/492] add package gmeta for embedded struct of meta data feature --- database/gdb/gdb.go | 1 + database/gdb/gdb_func.go | 44 +- database/gdb/gdb_model.go | 62 +-- database/gdb/gdb_model_option.go | 8 +- database/gdb/gdb_model_utility.go | 10 +- database/gdb/gdb_model_with.go | 17 + database/gdb/gdb_type_result_scanlist.go | 1 - .../gdb/gdb_z_mysql_association_with_test.go | 408 +++++------------- database/gdb/gdb_z_mysql_model_test.go | 8 +- frame/g/g_object.go | 16 +- internal/structs/structs.go | 5 + internal/structs/structs_type.go | 55 +++ internal/structs/structs_z_unit_test.go | 36 ++ text/gstr/gstr.go | 9 + text/gstr/gstr_z_unit_basic_test.go | 8 + util/gmeta/gmeta.go | 94 ++++ util/gmeta/gmeta_test.go | 78 ++++ 17 files changed, 499 insertions(+), 361 deletions(-) create mode 100644 database/gdb/gdb_model_with.go create mode 100644 internal/structs/structs_type.go create mode 100644 util/gmeta/gmeta.go create mode 100644 util/gmeta/gmeta_test.go diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 8a49a6f25..371840a82 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -35,6 +35,7 @@ type DB interface { // The DB interface is designed not only for // relational databases but also for NoSQL databases in the future. The name // "Table" is not proper for that purpose any more. + // Deprecated, use Model instead. Table(table ...string) *Model Model(table ...string) *Model Schema(schema string) *Schema diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 2a8878a33..2b9076a3f 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -14,6 +14,7 @@ import ( "github.com/gogf/gf/internal/json" "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/util/gmeta" "github.com/gogf/gf/util/gutil" "reflect" "regexp" @@ -47,10 +48,16 @@ type apiMapStrAny interface { MapStrAny() map[string]interface{} } +// apiTableName is the interface for retrieving table name fro struct. +type apiTableName interface { + TableName() string +} + const ( - OrmTagForStruct = "orm" - OrmTagForUnique = "unique" - OrmTagForPrimary = "primary" + OrmTagForStruct = "orm" + OrmTagForUnique = "unique" + OrmTagForPrimary = "primary" + metaDataNameForTable = "table" ) var ( @@ -61,6 +68,26 @@ var ( structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...) ) +// getTableNameFromObject retrieves and returns the table name from struct object. +func getTableNameFromObject(object interface{}) string { + if r, ok := object.(apiTableName); ok { + // Use the interface value. + return r.TableName() + } else if table := gmeta.Get(object, metaDataNameForTable); !table.IsEmpty() { + // User meta data tag "table". + return table.String() + } else { + // Use the struct name of snake case. + if t, err := structs.StructType(object); err != nil { + panic(err) + } else { + return gstr.CaseSnakeFirstUpper( + gstr.StrEx(t.String(), "."), + ) + } + } +} + // ListItemValues retrieves and returns the elements of all item struct/map with key . // Note that the parameter should be type of slice which contains elements of map or struct, // or else it returns an empty slice. @@ -752,9 +779,16 @@ func convertMapToStruct(data map[string]interface{}, pointer interface{}) error return err } // It retrieves and returns the mapping between orm tag and the struct attribute name. - mapping := make(map[string]string) + var ( + mapping = make(map[string]string) + tagFieldName string + ) for tag, attr := range tagNameMap { - mapping[strings.Split(tag, ",")[0]] = attr + tagFieldName = strings.Split(tag, ",")[0] + if !gregex.IsMatchString(regularFieldNameRegPattern, tagFieldName) { + continue + } + mapping[tagFieldName] = attr } return gconv.Struct(data, pointer, mapping) } diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 39cbd5213..48bea10b0 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -25,6 +25,7 @@ type Model struct { tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud". fields string // Operation fields, multiple fields joined using char ','. fieldsEx string // Excluded operation fields, multiple fields joined using char ','. + withArray []interface{} // Arguments for With feature. extraArgs []interface{} // Extra custom arguments for sql. whereHolder []*whereHolder // Condition strings for where operation. groupBy string // Used for "group by" statement. @@ -53,9 +54,13 @@ type whereHolder struct { } const ( - OPTION_OMITEMPTY = 1 + // Deprecated, use OptionOmitEmpty instead. + OPTION_OMITEMPTY = 1 + // Deprecated, use OptionAllowEmpty instead.. OPTION_ALLOWEMPTY = 2 + OptionOmitEmpty = 1 + OptionAllowEmpty = 2 linkTypeMaster = 1 linkTypeSlave = 2 whereHolderWhere = 1 @@ -63,15 +68,22 @@ const ( whereHolderOr = 3 ) -// Table creates and returns a new ORM model from given schema. -// The parameter can be more than one table names, and also alias name, like: -// 1. Table names: -// Table("user") -// Table("user u") -// Table("user, user_detail") -// Table("user u, user_detail ud") -// 2. Table name with alias: Table("user", "u") +// Table is alias of Core.Model. +// See Core.Model. +// Deprecated, use Model instead. func (c *Core) Table(table ...string) *Model { + return c.DB.Model(table...) +} + +// Model creates and returns a new ORM model from given schema. +// The parameter
can be more than one table names, and also alias name, like: +// 1. Model names: +// Model("user") +// Model("user u") +// Model("user, user_detail") +// Model("user u, user_detail ud") +// 2. Model name with alias: Model("user", "u") +func (c *Core) Model(table ...string) *Model { tables := "" if len(table) > 1 { tables = fmt.Sprintf( @@ -79,8 +91,6 @@ func (c *Core) Table(table ...string) *Model { ) } else if len(table) == 1 { tables = c.DB.QuotePrefixTableName(table[0]) - } else { - panic("table cannot be empty") } return &Model{ db: c.DB, @@ -89,31 +99,25 @@ func (c *Core) Table(table ...string) *Model { fields: "*", start: -1, offset: -1, - option: OPTION_ALLOWEMPTY, + option: OptionAllowEmpty, } } -// Model is alias of Core.Table. -// See Core.Table. -func (c *Core) Model(table ...string) *Model { - return c.DB.Table(table...) +// Table is alias of tx.Model. +// Deprecated, use Model instead. +func (tx *TX) Table(table ...string) *Model { + return tx.Model(table...) } -// Table acts like Core.Table except it operates on transaction. -// See Core.Table. -func (tx *TX) Table(table ...string) *Model { - model := tx.db.Table(table...) +// Model acts like Core.Model except it operates on transaction. +// See Core.Model. +func (tx *TX) Model(table ...string) *Model { + model := tx.db.Model(table...) model.db = tx.db model.tx = tx return model } -// Model is alias of tx.Table. -// See tx.Table. -func (tx *TX) Model(table ...string) *Model { - return tx.Table(table...) -} - // Ctx sets the context for current operation. func (m *Model) Ctx(ctx context.Context) *Model { if ctx == nil { @@ -175,7 +179,7 @@ func (m *Model) Clone() *Model { newModel = m.db.Table(m.tablesInit) } *newModel = *m - // Deep copy slice attributes. + // Shallow copy slice attributes. if n := len(m.extraArgs); n > 0 { newModel.extraArgs = make([]interface{}, n) copy(newModel.extraArgs, m.extraArgs) @@ -184,6 +188,10 @@ func (m *Model) Clone() *Model { newModel.whereHolder = make([]*whereHolder, n) copy(newModel.whereHolder, m.whereHolder) } + if n := len(m.withArray); n > 0 { + newModel.withArray = make([]interface{}, n) + copy(newModel.withArray, m.withArray) + } return newModel } diff --git a/database/gdb/gdb_model_option.go b/database/gdb/gdb_model_option.go index e41e44316..21b0acf1d 100644 --- a/database/gdb/gdb_model_option.go +++ b/database/gdb/gdb_model_option.go @@ -13,15 +13,15 @@ func (m *Model) Option(option int) *Model { return model } -// OptionOmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers +// OptionOmitEmpty sets OptionOmitEmpty option for the model, which automatically filers // the data and where attributes for empty values. // Deprecated, use OmitEmpty instead. func (m *Model) OptionOmitEmpty() *Model { - return m.Option(OPTION_OMITEMPTY) + return m.Option(OptionOmitEmpty) } -// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers +// OmitEmpty sets OptionOmitEmpty option for the model, which automatically filers // the data and where attributes for empty values. func (m *Model) OmitEmpty() *Model { - return m.Option(OPTION_OMITEMPTY) + return m.Option(OptionOmitEmpty) } diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index e3ad16442..2b93696ff 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -92,7 +92,7 @@ func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEm return nil, err } // Remove key-value pairs of which the value is empty. - if allowOmitEmpty && m.option&OPTION_OMITEMPTY > 0 { + if allowOmitEmpty && m.option&OptionOmitEmpty > 0 { tempMap := make(Map, len(data)) for k, v := range data { if empty.IsEmpty(v) { @@ -206,7 +206,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh case whereHolderWhere: if conditionWhere == "" { newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0, + m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, ) if len(newWhere) > 0 { conditionWhere = newWhere @@ -218,7 +218,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh case whereHolderAnd: newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0, + m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, ) if len(newWhere) > 0 { if len(conditionWhere) == 0 { @@ -233,7 +233,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh case whereHolderOr: newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0, + m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, ) if len(newWhere) > 0 { if len(conditionWhere) == 0 { @@ -268,7 +268,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh // HAVING. if len(m.having) > 0 { havingStr, havingArgs := formatWhere( - m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OPTION_OMITEMPTY > 0, + m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0, ) if len(havingStr) > 0 { conditionExtra += " HAVING " + havingStr diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go new file mode 100644 index 000000000..ec6764585 --- /dev/null +++ b/database/gdb/gdb_model_with.go @@ -0,0 +1,17 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +func (m *Model) With(structAttrPointer interface{}) *Model { + model := m.getModel() + if m.tables == "" { + m.tables = getTableNameFromObject(structAttrPointer) + return model + } + model.withArray = append(model.withArray, structAttrPointer) + return model +} diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index b850ec9d1..07578ffa0 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -226,7 +226,6 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio ) } else { relationBindToSubAttrName = key - } } relationBindToSubAttrNameChecked = true diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index 1540b32d5..216e83942 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -6,311 +6,103 @@ package gdb_test -//func Test_Table_Relation_With(t *testing.T) { -// var ( -// tableUser = "user_" + gtime.TimestampMicroStr() -// tableUserDetail = "user_detail_" + gtime.TimestampMicroStr() -// tableUserScores = "user_scores_" + gtime.TimestampMicroStr() -// ) -// if _, err := db.Exec(fmt.Sprintf(` -//CREATE TABLE %s ( -// uid int(10) unsigned NOT NULL AUTO_INCREMENT, -// name varchar(45) NOT NULL, -// PRIMARY KEY (uid) -//) ENGINE=InnoDB DEFAULT CHARSET=utf8; -// `, tableUser)); err != nil { -// gtest.Error(err) -// } -// defer dropTable(tableUser) -// -// if _, err := db.Exec(fmt.Sprintf(` -//CREATE TABLE %s ( -// uid int(10) unsigned NOT NULL AUTO_INCREMENT, -// address varchar(45) NOT NULL, -// PRIMARY KEY (uid) -//) ENGINE=InnoDB DEFAULT CHARSET=utf8; -// `, tableUserDetail)); err != nil { -// gtest.Error(err) -// } -// defer dropTable(tableUserDetail) -// -// if _, err := db.Exec(fmt.Sprintf(` -//CREATE TABLE %s ( -// id int(10) unsigned NOT NULL AUTO_INCREMENT, -// uid int(10) unsigned NOT NULL, -// score int(10) unsigned NOT NULL, -// PRIMARY KEY (id) -//) ENGINE=InnoDB DEFAULT CHARSET=utf8; -// `, tableUserScores)); err != nil { -// gtest.Error(err) -// } -// defer dropTable(tableUserScores) -// -// type UserDetail struct { -// Uid int `json:"uid"` -// Address string `json:"address"` -// } -// type UserScores struct { -// Id int `json:"id"` -// Uid int `json:"uid"` -// Score int `json:"score"` -// } -// -// type User struct { -// Id int `json:"id"` -// Name string `json:"name"` -// UserDetail *UserDetail `orm:"with:uid=uid"` -// UserScores []*UserScores `orm:"with:uid=id"` -// } -// -// // Initialize the data. -// var err error -// for i := 1; i <= 5; i++ { -// // User. -// _, err = db.Insert(tableUser, g.Map{ -// "id": i, -// "name": fmt.Sprintf(`name_%d`, i), -// }) -// gtest.Assert(err, nil) -// // Detail. -// _, err = db.Insert(tableUserDetail, g.Map{ -// "uid": i, -// "address": fmt.Sprintf(`address_%d`, i), -// }) -// gtest.Assert(err, nil) -// // Scores. -// for j := 1; j <= 5; j++ { -// _, err = db.Insert(tableUserScores, g.Map{ -// "uid": i, -// "score": j, -// }) -// gtest.Assert(err, nil) -// } -// } -// // MapKeyValue. -// gtest.C(t, func(t *gtest.T) { -// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() -// t.Assert(err, nil) -// t.Assert(all.Len(), 2) -// t.Assert(len(all.MapKeyValue("uid")), 2) -// t.Assert(all.MapKeyValue("uid")["3"].Map()["uid"], 3) -// t.Assert(all.MapKeyValue("uid")["4"].Map()["uid"], 4) -// all, err = db.Table(tableUserScores).Where("uid", g.Slice{3, 4}).Order("id asc").All() -// t.Assert(err, nil) -// t.Assert(all.Len(), 10) -// t.Assert(len(all.MapKeyValue("uid")), 2) -// t.Assert(len(all.MapKeyValue("uid")["3"].Slice()), 5) -// t.Assert(len(all.MapKeyValue("uid")["4"].Slice()), 5) -// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["uid"], 3) -// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["score"], 1) -// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["uid"], 3) -// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["score"], 5) -// }) -// // Result ScanList with struct elements and pointer attributes. -// gtest.C(t, func(t *gtest.T) { -// var users []Entity -// // User -// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() -// t.Assert(err, nil) -// err = all.ScanList(&users, "User") -// t.Assert(err, nil) -// t.Assert(len(users), 2) -// t.Assert(users[0].User, &EntityUser{3, "name_3"}) -// t.Assert(users[1].User, &EntityUser{4, "name_4"}) -// // Detail -// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() -// gtest.Assert(err, nil) -// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") -// t.Assert(err, nil) -// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) -// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) -// // Scores -// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() -// gtest.Assert(err, nil) -// err = all.ScanList(&users, "UserScores", "User", "uid:Uid") -// t.Assert(err, nil) -// t.Assert(len(users[0].UserScores), 5) -// t.Assert(len(users[1].UserScores), 5) -// t.Assert(users[0].UserScores[0].Uid, 3) -// t.Assert(users[0].UserScores[0].Score, 1) -// t.Assert(users[0].UserScores[4].Score, 5) -// t.Assert(users[1].UserScores[0].Uid, 4) -// t.Assert(users[1].UserScores[0].Score, 1) -// t.Assert(users[1].UserScores[4].Score, 5) -// }) -// -// // Result ScanList with pointer elements and pointer attributes. -// gtest.C(t, func(t *gtest.T) { -// var users []*Entity -// // User -// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() -// t.Assert(err, nil) -// err = all.ScanList(&users, "User") -// t.Assert(err, nil) -// t.Assert(len(users), 2) -// t.Assert(users[0].User, &EntityUser{3, "name_3"}) -// t.Assert(users[1].User, &EntityUser{4, "name_4"}) -// // Detail -// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() -// gtest.Assert(err, nil) -// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") -// t.Assert(err, nil) -// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) -// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) -// // Scores -// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() -// gtest.Assert(err, nil) -// err = all.ScanList(&users, "UserScores", "User", "uid:Uid") -// t.Assert(err, nil) -// t.Assert(len(users[0].UserScores), 5) -// t.Assert(len(users[1].UserScores), 5) -// t.Assert(users[0].UserScores[0].Uid, 3) -// t.Assert(users[0].UserScores[0].Score, 1) -// t.Assert(users[0].UserScores[4].Score, 5) -// t.Assert(users[1].UserScores[0].Uid, 4) -// t.Assert(users[1].UserScores[0].Score, 1) -// t.Assert(users[1].UserScores[4].Score, 5) -// }) -// -// // Result ScanList with struct elements and struct attributes. -// gtest.C(t, func(t *gtest.T) { -// type EntityUser struct { -// Uid int `json:"uid"` -// Name string `json:"name"` -// } -// type EntityUserDetail struct { -// Uid int `json:"uid"` -// Address string `json:"address"` -// } -// type EntityUserScores struct { -// Id int `json:"id"` -// Uid int `json:"uid"` -// Score int `json:"score"` -// } -// type Entity struct { -// User EntityUser -// UserDetail EntityUserDetail -// UserScores []EntityUserScores -// } -// var users []Entity -// // User -// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() -// t.Assert(err, nil) -// err = all.ScanList(&users, "User") -// t.Assert(err, nil) -// t.Assert(len(users), 2) -// t.Assert(users[0].User, &EntityUser{3, "name_3"}) -// t.Assert(users[1].User, &EntityUser{4, "name_4"}) -// // Detail -// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() -// gtest.Assert(err, nil) -// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") -// t.Assert(err, nil) -// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) -// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) -// // Scores -// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() -// gtest.Assert(err, nil) -// err = all.ScanList(&users, "UserScores", "User", "uid:Uid") -// t.Assert(err, nil) -// t.Assert(len(users[0].UserScores), 5) -// t.Assert(len(users[1].UserScores), 5) -// t.Assert(users[0].UserScores[0].Uid, 3) -// t.Assert(users[0].UserScores[0].Score, 1) -// t.Assert(users[0].UserScores[4].Score, 5) -// t.Assert(users[1].UserScores[0].Uid, 4) -// t.Assert(users[1].UserScores[0].Score, 1) -// t.Assert(users[1].UserScores[4].Score, 5) -// }) -// -// // Result ScanList with pointer elements and struct attributes. -// gtest.C(t, func(t *gtest.T) { -// type EntityUser struct { -// Uid int `json:"uid"` -// Name string `json:"name"` -// } -// type EntityUserDetail struct { -// Uid int `json:"uid"` -// Address string `json:"address"` -// } -// type EntityUserScores struct { -// Id int `json:"id"` -// Uid int `json:"uid"` -// Score int `json:"score"` -// } -// type Entity struct { -// User EntityUser -// UserDetail EntityUserDetail -// UserScores []EntityUserScores -// } -// var users []*Entity -// -// // User -// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() -// t.Assert(err, nil) -// err = all.ScanList(&users, "User") -// t.Assert(err, nil) -// t.Assert(len(users), 2) -// t.Assert(users[0].User, &EntityUser{3, "name_3"}) -// t.Assert(users[1].User, &EntityUser{4, "name_4"}) -// // Detail -// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() -// gtest.Assert(err, nil) -// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") -// t.Assert(err, nil) -// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) -// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) -// // Scores -// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() -// gtest.Assert(err, nil) -// err = all.ScanList(&users, "UserScores", "User", "uid:Uid") -// t.Assert(err, nil) -// t.Assert(len(users[0].UserScores), 5) -// t.Assert(len(users[1].UserScores), 5) -// t.Assert(users[0].UserScores[0].Uid, 3) -// t.Assert(users[0].UserScores[0].Score, 1) -// t.Assert(users[0].UserScores[4].Score, 5) -// t.Assert(users[1].UserScores[0].Uid, 4) -// t.Assert(users[1].UserScores[0].Score, 1) -// t.Assert(users[1].UserScores[4].Score, 5) -// }) -// -// // Model ScanList with pointer elements and pointer attributes. -// gtest.C(t, func(t *gtest.T) { -// var users []*Entity -// // User -// err := db.Table(tableUser). -// Where("uid", g.Slice{3, 4}). -// Order("uid asc"). -// ScanList(&users, "User") -// t.Assert(err, nil) -// // Detail -// err = db.Table(tableUserDetail). -// Where("uid", gdb.ListItemValues(users, "User", "Uid")). -// Order("uid asc"). -// ScanList(&users, "UserDetail", "User", "uid:Uid") -// gtest.Assert(err, nil) -// // Scores -// err = db.Table(tableUserScores). -// Where("uid", gdb.ListItemValues(users, "User", "Uid")). -// Order("id asc"). -// ScanList(&users, "UserScores", "User", "uid:Uid") -// t.Assert(err, nil) -// -// t.Assert(len(users), 2) -// t.Assert(users[0].User, &EntityUser{3, "name_3"}) -// t.Assert(users[1].User, &EntityUser{4, "name_4"}) -// -// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) -// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) -// -// t.Assert(len(users[0].UserScores), 5) -// t.Assert(len(users[1].UserScores), 5) -// t.Assert(users[0].UserScores[0].Uid, 3) -// t.Assert(users[0].UserScores[0].Score, 1) -// t.Assert(users[0].UserScores[4].Score, 5) -// t.Assert(users[1].UserScores[0].Uid, 4) -// t.Assert(users[1].UserScores[0].Score, 1) -// t.Assert(users[1].UserScores[4].Score, 5) -// }) -//} +import ( + "fmt" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/test/gtest" + "github.com/gogf/gf/util/gmeta" + "testing" +) + +func Test_Table_Relation_With(t *testing.T) { + var ( + tableUser = "user_with" + tableUserDetail = "user_detail_with" + tableUserScores = "user_scores_with" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +uid int(10) unsigned NOT NULL AUTO_INCREMENT, +address varchar(45) NOT NULL, +PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +uid int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `table:"user_detail_with"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `table:"user_scores_with"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `table:"user_with"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id"` + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model().With(&user).Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + }) +} diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index df40dd8d3..a1f14f511 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -2013,7 +2013,7 @@ func Test_Model_Option_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - r, err := db.Table(table).Option(gdb.OPTION_OMITEMPTY).Data(g.Map{ + r, err := db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ "id": 1, "passport": 0, "password": 0, @@ -2033,7 +2033,7 @@ func Test_Model_Option_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createInitTable() defer dropTable(table) - _, err := db.Table(table).Option(gdb.OPTION_OMITEMPTY).Data(g.Map{ + _, err := db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ "id": 1, "passport": 0, "password": 0, @@ -2069,7 +2069,7 @@ func Test_Model_Option_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - _, err := db.Table(table).Option(gdb.OPTION_OMITEMPTY).Data(g.Map{ + _, err := db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ "id": 1, "passport": 0, "password": 0, @@ -2106,7 +2106,7 @@ func Test_Model_Option_Map(t *testing.T) { n, _ := r.RowsAffected() t.Assert(n, 1) - _, err = db.Table(table).Option(gdb.OPTION_OMITEMPTY).Data(g.Map{"nickname": ""}).Where("id", 2).Update() + _, err = db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{"nickname": ""}).Where("id", 2).Update() t.AssertNE(err, nil) r, err = db.Table(table).OmitEmpty().Data(g.Map{"nickname": "", "password": "123"}).Where("id", 3).Update() diff --git a/frame/g/g_object.go b/frame/g/g_object.go index eee9bb8a4..6cfacac4b 100644 --- a/frame/g/g_object.go +++ b/frame/g/g_object.go @@ -92,15 +92,17 @@ func DB(name ...string) gdb.DB { } // Table is alias of Model. -func Table(tables string, db ...string) *gdb.Model { - return DB(db...).Table(tables) +// The database component is designed not only for +// relational databases but also for NoSQL databases in the future. The name +// "Table" is not proper for that purpose any more. +// Deprecated, use Model instead. +func Table(tables ...string) *gdb.Model { + return DB().Table(tables...) } -// Model creates and returns a model from specified database or default database configuration. -// The optional parameter specifies the configuration group name of the database, -// which is "default" in default. -func Model(tables string, db ...string) *gdb.Model { - return DB(db...).Model(tables) +// Model creates and returns a model based on configuration of default database group. +func Model(tables ...string) *gdb.Model { + return DB().Model(tables...) } // Redis returns an instance of redis client with specified configuration group name. diff --git a/internal/structs/structs.go b/internal/structs/structs.go index 2fa28f523..5ff9190db 100644 --- a/internal/structs/structs.go +++ b/internal/structs/structs.go @@ -13,6 +13,11 @@ import ( "reflect" ) +// Type wraps reflect.Type for additional features. +type Type struct { + reflect.Type +} + // Field contains information of a struct field . type Field struct { value reflect.Value diff --git a/internal/structs/structs_type.go b/internal/structs/structs_type.go new file mode 100644 index 000000000..cee57b2f7 --- /dev/null +++ b/internal/structs/structs_type.go @@ -0,0 +1,55 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package structs + +import ( + "github.com/gogf/gf/errors/gerror" + "reflect" +) + +// StructType retrieves and returns the Type of specified struct/*struct. +func StructType(structOrPointer interface{}) (*Type, error) { + var ( + reflectValue reflect.Value + reflectKind reflect.Kind + reflectType reflect.Type + ) + if rv, ok := structOrPointer.(reflect.Value); ok { + reflectValue = rv + } else { + reflectValue = reflect.ValueOf(structOrPointer) + } + reflectKind = reflectValue.Kind() + for reflectKind == reflect.Ptr { + if !reflectValue.IsValid() || reflectValue.IsNil() { + // If pointer is type of *struct and nil, then automatically create a temporary struct. + reflectValue = reflect.New(reflectValue.Type().Elem()).Elem() + reflectKind = reflectValue.Kind() + } else { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + } + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + if reflectKind != reflect.Struct { + return nil, gerror.Newf( + `invalid parameter kind "%s", kind of "struct" is required`, + reflectKind, + ) + } + reflectType = reflectValue.Type() + return &Type{ + Type: reflectType, + }, nil +} + +func (t *Type) Signature() string { + return t.PkgPath() + "/" + t.String() +} diff --git a/internal/structs/structs_z_unit_test.go b/internal/structs/structs_z_unit_test.go index 5124c6cce..f694aecdf 100644 --- a/internal/structs/structs_z_unit_test.go +++ b/internal/structs/structs_z_unit_test.go @@ -124,3 +124,39 @@ func Test_MapField(t *testing.T) { t.Assert(ok, true) }) } + +func Test_StructType(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type B struct { + Name string + } + type A struct { + B + } + r, err := structs.StructType(new(A)) + t.AssertNil(err) + t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.A`) + }) + gtest.C(t, func(t *gtest.T) { + type B struct { + Name string + } + type A struct { + B + } + r, err := structs.StructType(new(A).B) + t.AssertNil(err) + t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`) + }) + // Error. + gtest.C(t, func(t *gtest.T) { + type B struct { + Name string + } + type A struct { + *B + } + _, err := structs.StructType(new(A).B) + t.AssertNE(err, nil) + }) +} diff --git a/text/gstr/gstr.go b/text/gstr/gstr.go index aa5fe0ca8..2758b6091 100644 --- a/text/gstr/gstr.go +++ b/text/gstr/gstr.go @@ -472,6 +472,15 @@ func Str(haystack string, needle string) string { return haystack[idx+len([]byte(needle))-1:] } +// StrEx returns part of string starting from and excluding +// the first occurrence of to the end of . +func StrEx(haystack string, needle string) string { + if s := Str(haystack, needle); s != "" { + return s[1:] + } + return "" +} + // Shuffle randomly shuffles a string. // It considers parameter as unicode string. func Shuffle(str string) string { diff --git a/text/gstr/gstr_z_unit_basic_test.go b/text/gstr/gstr_z_unit_basic_test.go index 7f04d64d1..76cb2eadc 100644 --- a/text/gstr/gstr_z_unit_basic_test.go +++ b/text/gstr/gstr_z_unit_basic_test.go @@ -295,6 +295,14 @@ func Test_Str(t *testing.T) { }) } +func Test_StrEx(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gstr.StrEx("name@example.com", "@"), "example.com") + t.Assert(gstr.StrEx("name@example.com", ""), "") + t.Assert(gstr.StrEx("name@example.com", "z"), "") + }) +} + func Test_Shuffle(t *testing.T) { gtest.C(t, func(t *gtest.T) { t.Assert(len(gstr.Shuffle("123456")), 6) diff --git a/util/gmeta/gmeta.go b/util/gmeta/gmeta.go new file mode 100644 index 000000000..2a2c357a3 --- /dev/null +++ b/util/gmeta/gmeta.go @@ -0,0 +1,94 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package gmeta provides embedded meta data feature for struct. +package gmeta + +import ( + "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/container/gvar" + "github.com/gogf/gf/internal/structs" + "strconv" +) + +// Meta is used as an embedded attribute for struct to enabled meta data feature. +type Meta struct{} + +const ( + metaAttributeName = "Meta" +) + +var ( + metaDataCacheMap = gmap.NewStrAnyMap(true) +) + +// Data retrieves and returns all meta data from `object`. +// It automatically parses the tag string from "Mata" attribute as its meta data. +func Data(object interface{}) map[string]interface{} { + reflectType, err := structs.StructType(object) + if err != nil { + panic(err) + } + return metaDataCacheMap.GetOrSetFuncLock(reflectType.Signature(), func() interface{} { + if field, ok := reflectType.FieldByName(metaAttributeName); ok { + var ( + key string + tag = field.Tag + data = make(map[string]interface{}) + ) + for tag != "" { + // Skip leading space. + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + break + } + // Scan to colon. A space, a quote or a control character is a syntax error. + // Strictly speaking, control chars include the range [0x7f, 0x9f], not just + // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters + // as it is simpler to inspect the tag's bytes than the tag's runes. + i = 0 + for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { + i++ + } + if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + break + } + key = string(tag[:i]) + tag = tag[i+1:] + + // Scan quoted string to find value. + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + break + } + quotedValue := string(tag[:i+1]) + tag = tag[i+1:] + value, err := strconv.Unquote(quotedValue) + if err != nil { + panic(err) + } + data[key] = value + } + return data + } + return map[string]interface{}{} + }).(map[string]interface{}) +} + +// Get retrieves and returns specified meta data by `key` from `object`. +func Get(object interface{}, key string) *gvar.Var { + return gvar.New(Data(object)[key]) +} diff --git a/util/gmeta/gmeta_test.go b/util/gmeta/gmeta_test.go new file mode 100644 index 000000000..2186d5414 --- /dev/null +++ b/util/gmeta/gmeta_test.go @@ -0,0 +1,78 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmeta_test + +import ( + "github.com/gogf/gf/internal/json" + "github.com/gogf/gf/util/gmeta" + "testing" + + "github.com/gogf/gf/test/gtest" +) + +type A struct { + gmeta.Meta `tag:"123" orm:"456"` + Id int + Name string +} + +var ( + a1 A + a2 *A +) + +func Benchmark_Data_Struct(b *testing.B) { + for i := 0; i < b.N; i++ { + gmeta.Data(a1) + } +} + +func Benchmark_Data_Pointer1(b *testing.B) { + for i := 0; i < b.N; i++ { + gmeta.Data(a2) + } +} + +func Benchmark_Data_Pointer2(b *testing.B) { + for i := 0; i < b.N; i++ { + gmeta.Data(&a2) + } +} + +func Benchmark_Data_Get_Struct(b *testing.B) { + for i := 0; i < b.N; i++ { + gmeta.Get(a1, "tag") + } +} + +func Benchmark_Data_Get_Pointer1(b *testing.B) { + for i := 0; i < b.N; i++ { + gmeta.Get(a2, "tag") + } +} + +func Benchmark_Data_Get_Pointer2(b *testing.B) { + for i := 0; i < b.N; i++ { + gmeta.Get(&a2, "tag") + } +} + +func TestMeta_Basic(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + a := &A{ + Id: 100, + Name: "john", + } + t.Assert(len(gmeta.Data(a)), 2) + t.Assert(gmeta.Get(a, "tag").String(), "123") + t.Assert(gmeta.Get(a, "orm").String(), "456") + + b, err := json.Marshal(a) + t.AssertNil(err) + t.Assert(b, `{"Id":100,"Name":"john"}`) + }) +} From acf47f3907ede0bd4a941eb63cd5a920a1d0ad3b Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 8 Feb 2021 17:57:21 +0800 Subject: [PATCH 155/492] orm with feature --- database/gdb/gdb.go | 42 ++-- database/gdb/gdb_core.go | 206 +++++++++--------- database/gdb/gdb_core_config.go | 2 +- database/gdb/gdb_core_structure.go | 8 +- database/gdb/gdb_core_tracing.go | 24 +- database/gdb/gdb_core_utility.go | 16 +- database/gdb/gdb_driver_mssql.go | 10 +- database/gdb/gdb_driver_mysql.go | 10 +- database/gdb/gdb_driver_oracle.go | 33 ++- database/gdb/gdb_driver_pgsql.go | 10 +- database/gdb/gdb_driver_sqlite.go | 10 +- database/gdb/gdb_func.go | 87 ++++---- database/gdb/gdb_model.go | 42 ++-- database/gdb/gdb_model_cache.go | 12 +- database/gdb/gdb_model_condition.go | 10 +- database/gdb/gdb_model_delete.go | 2 +- database/gdb/gdb_model_fields.go | 12 +- database/gdb/gdb_model_insert.go | 10 +- database/gdb/gdb_model_join.go | 8 +- database/gdb/gdb_model_select.go | 60 ++--- database/gdb/gdb_model_update.go | 2 +- database/gdb/gdb_model_utility.go | 12 +- database/gdb/gdb_model_with.go | 80 ++++++- database/gdb/gdb_schema.go | 4 +- database/gdb/gdb_statement.go | 4 +- database/gdb/gdb_transaction.go | 44 ++-- database/gdb/gdb_type_record.go | 16 +- database/gdb/gdb_type_result.go | 30 +-- database/gdb/gdb_type_result_scanlist.go | 6 +- .../gdb/gdb_z_mysql_association_with_test.go | 22 +- frame/g/g.go | 86 ++++---- frame/g/g_object.go | 14 +- internal/command/command.go | 12 +- internal/empty/empty.go | 10 +- internal/intlog/intlog.go | 16 +- internal/mutex/mutex.go | 2 +- internal/rwmutex/rwmutex.go | 4 +- internal/structs/structs.go | 35 +-- .../{structs_map.go => structs_field.go} | 39 +++- internal/structs/structs_tag.go | 71 +++++- internal/structs/structs_type.go | 43 +++- internal/structs/structs_z_unit_test.go | 91 +++++++- internal/utils/utils_str.go | 4 +- text/gstr/gstr.go | 41 +++- text/gstr/gstr_pos.go | 5 +- text/gstr/gstr_trim.go | 49 ++++- text/gstr/gstr_z_unit_basic_test.go | 16 ++ text/gstr/gstr_z_unit_trim_test.go | 14 ++ util/gmeta/gmeta.go | 54 +---- util/gvalid/gvalid_validator_check_struct.go | 10 +- 50 files changed, 890 insertions(+), 560 deletions(-) rename internal/structs/{structs_map.go => structs_field.go} (52%) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 371840a82..98f0cb75c 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -37,9 +37,23 @@ type DB interface { // "Table" is not proper for that purpose any more. // Deprecated, use Model instead. Table(table ...string) *Model + + // Model creates and returns a new ORM model from given schema. + // The parameter `table` can be more than one table names, and also alias name, like: + // 1. Model names: + // Model("user") + // Model("user u") + // Model("user, user_detail") + // Model("user u, user_detail ud") + // 2. Model name with alias: Model("user", "u") Model(table ...string) *Model + + // Schema creates and returns a schema. Schema(schema string) *Schema + // With creates and returns an ORM model based on meta data of given object. + With(object interface{}) *Model + // Open creates a raw connection object for database with given node configuration. // Note that it is not recommended using the this function manually. Open(config *ConfigNode) (*sql.DB, error) @@ -158,9 +172,9 @@ type DB interface { FilteredLinkInfo() string // HandleSqlBeforeCommit is a hook function, which deals with the sql string before - // it's committed to underlying driver. The parameter specifies the current - // database connection operation object. You can modify the sql string and its - // arguments as you wish before they're committed to driver. + // it's committed to underlying driver. The parameter `link` specifies the current + // database connection operation object. You can modify the sql string `sql` and its + // arguments `args` as you wish before they're committed to driver. HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) // =========================================================================== @@ -174,14 +188,14 @@ type DB interface { // Core is the base struct for database management. type Core struct { - DB DB // DB interface object. + db DB // DB interface object. + ctx context.Context // Context for chaining operation only. group string // Configuration group name. debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime. cache *gcache.Cache // Cache manager, SQL result cache only. schema *gtype.String // Custom schema for this object. logger *glog.Logger // Logger. config *ConfigNode // Current config node. - ctx context.Context // Context for chaining operation only. } // Driver is the interface for integrating sql drivers into package gdb. @@ -302,7 +316,7 @@ func Register(name string, driver Driver) error { } // New creates and returns an ORM object with global configurations. -// The parameter specifies the configuration group name, +// The parameter `name` specifies the configuration group name, // which is DefaultGroupName in default. func New(group ...string) (db DB, err error) { groupName := configs.group @@ -326,11 +340,11 @@ func New(group ...string) (db DB, err error) { config: node, } if v, ok := driverMap[node.Type]; ok { - c.DB, err = v.New(c, node) + c.db, err = v.New(c, node) if err != nil { return nil, err } - return c.DB, nil + return c.db, nil } else { return nil, gerror.New(fmt.Sprintf(`unsupported database type "%s"`, node.Type)) } @@ -343,7 +357,7 @@ func New(group ...string) (db DB, err error) { } // Instance returns an instance for DB operations. -// The parameter specifies the configuration group name, +// The parameter `name` specifies the configuration group name, // which is DefaultGroupName in default. func Instance(name ...string) (db DB, err error) { group := configs.group @@ -363,7 +377,7 @@ func Instance(name ...string) (db DB, err error) { // getConfigNodeByGroup calculates and returns a configuration node of given group. It // calculates the value internally using weight algorithm for load balance. // -// The parameter specifies whether retrieving a master node, or else a slave node +// The parameter `master` specifies whether retrieving a master node, or else a slave node // if master-slave configured. func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) { if list, ok := configs.config[group]; ok { @@ -432,7 +446,7 @@ func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode { } // getSqlDb retrieves and returns a underlying database connection object. -// The parameter specifies whether retrieves master node connection if +// The parameter `master` specifies whether retrieves master node connection if // master-slave nodes are configured. func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error) { // Load balance. @@ -457,7 +471,7 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error } // Cache the underlying connection pool object by node. v, _ := internalCache.GetOrSetFuncLock(node.String(), func() (interface{}, error) { - sqlDb, err = c.DB.Open(node) + sqlDb, err = c.db.Open(node) if err != nil { intlog.Printf("DB open failed: %v, %+v", err, node) return nil, err @@ -489,10 +503,10 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error sqlDb = v.(*sql.DB) } if node.Debug { - c.DB.SetDebug(node.Debug) + c.db.SetDebug(node.Debug) } if node.DryRun { - c.DB.SetDryRun(node.DryRun) + c.db.SetDryRun(node.DryRun) } return } diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 08c21c245..d26606226 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -30,21 +30,21 @@ import ( // a global or package variable for long using. func (c *Core) Ctx(ctx context.Context) DB { if ctx == nil { - return c.DB + return c.db } var ( err error newCore = &Core{} - configNode = c.DB.GetConfig() + configNode = c.db.GetConfig() ) *newCore = *c newCore.ctx = ctx - newCore.DB, err = driverMap[configNode.Type].New(newCore, configNode) + newCore.db, err = driverMap[configNode.Type].New(newCore, configNode) // Seldom error, just log it. if err != nil { - c.DB.GetLogger().Ctx(ctx).Error(err) + c.db.GetLogger().Ctx(ctx).Error(err) } - return newCore.DB + return newCore.db } // GetCtx returns the context for current DB. @@ -59,22 +59,22 @@ func (c *Core) GetCtx() context.Context { // GetCtxTimeout returns the context and cancel function for specified timeout type. func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Context, context.CancelFunc) { if ctx == nil { - ctx = c.DB.GetCtx() + ctx = c.db.GetCtx() } else { ctx = context.WithValue(ctx, "WrappedByGetCtxTimeout", nil) } switch timeoutType { case ctxTimeoutTypeExec: - if c.DB.GetConfig().ExecTimeout > 0 { - return context.WithTimeout(ctx, c.DB.GetConfig().ExecTimeout) + if c.db.GetConfig().ExecTimeout > 0 { + return context.WithTimeout(ctx, c.db.GetConfig().ExecTimeout) } case ctxTimeoutTypeQuery: - if c.DB.GetConfig().QueryTimeout > 0 { - return context.WithTimeout(ctx, c.DB.GetConfig().QueryTimeout) + if c.db.GetConfig().QueryTimeout > 0 { + return context.WithTimeout(ctx, c.db.GetConfig().QueryTimeout) } case ctxTimeoutTypePrepare: - if c.DB.GetConfig().PrepareTimeout > 0 { - return context.WithTimeout(ctx, c.DB.GetConfig().PrepareTimeout) + if c.db.GetConfig().PrepareTimeout > 0 { + return context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout) } default: panic(gerror.Newf("invalid context timeout type: %d", timeoutType)) @@ -97,19 +97,19 @@ func (c *Core) Slave() (*sql.DB, error) { // Query commits one query SQL to underlying driver and returns the execution result. // It is most commonly used for data querying. func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) { - link, err := c.DB.Slave() + link, err := c.db.Slave() if err != nil { return nil, err } - return c.DB.DoQuery(link, sql, args...) + return c.db.DoQuery(link, sql, args...) } // DoQuery commits the sql string and its arguments to underlying driver // through given link object and returns the execution result. func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { sql, args = formatSql(sql, args) - sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) - ctx := c.DB.GetCtx() + sql, args = c.db.HandleSqlBeforeCommit(link, sql, args) + ctx := c.db.GetCtx() if c.GetConfig().QueryTimeout > 0 { var cancelFunc context.CancelFunc ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) @@ -127,10 +127,10 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro Error: err, Start: mTime1, End: mTime2, - Group: c.DB.GetGroup(), + Group: c.db.GetGroup(), } c.addSqlToTracing(ctx, sqlObj) - if c.DB.GetDebug() { + if c.db.GetDebug() { c.writeSqlToLogger(sqlObj) } if err == nil { @@ -144,19 +144,19 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro // Exec commits one query SQL to underlying driver and returns the execution result. // It is most commonly used for data inserting and updating. func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error) { - link, err := c.DB.Master() + link, err := c.db.Master() if err != nil { return nil, err } - return c.DB.DoExec(link, sql, args...) + return c.db.DoExec(link, sql, args...) } // DoExec commits the sql string and its arguments to underlying driver // through given link object and returns the execution result. func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) { sql, args = formatSql(sql, args) - sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) - ctx := c.DB.GetCtx() + sql, args = c.db.HandleSqlBeforeCommit(link, sql, args) + ctx := c.db.GetCtx() if c.GetConfig().ExecTimeout > 0 { var cancelFunc context.CancelFunc ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout) @@ -164,7 +164,7 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re } mTime1 := gtime.TimestampMilli() - if !c.DB.GetDryRun() { + if !c.db.GetDryRun() { result, err = link.ExecContext(ctx, sql, args...) } else { result = new(SqlResult) @@ -178,10 +178,10 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re Error: err, Start: mTime1, End: mTime2, - Group: c.DB.GetGroup(), + Group: c.db.GetGroup(), } c.addSqlToTracing(ctx, sqlObj) - if c.DB.GetDebug() { + if c.db.GetDebug() { c.writeSqlToLogger(sqlObj) } return result, formatError(err, sql, args...) @@ -193,7 +193,7 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re // The caller must call the statement's Close method // when the statement is no longer needed. // -// The parameter specifies whether executing the sql on master node, +// The parameter `execOnMaster` specifies whether executing the sql on master node, // or else it executes the sql on slave node if master-slave configured. func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) { var ( @@ -201,20 +201,20 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) { link Link ) if len(execOnMaster) > 0 && execOnMaster[0] { - if link, err = c.DB.Master(); err != nil { + if link, err = c.db.Master(); err != nil { return nil, err } } else { - if link, err = c.DB.Slave(); err != nil { + if link, err = c.db.Slave(); err != nil { return nil, err } } - return c.DB.DoPrepare(link, sql) + return c.db.DoPrepare(link, sql) } // doPrepare calls prepare function on given link object and returns the statement object. func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) { - ctx := c.DB.GetCtx() + ctx := c.db.GetCtx() if c.GetConfig().PrepareTimeout > 0 { // DO NOT USE cancel function in prepare statement. ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout) @@ -231,11 +231,11 @@ func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) { Error: err, Start: mTime1, End: mTime2, - Group: c.DB.GetGroup(), + Group: c.db.GetGroup(), } ) c.addSqlToTracing(ctx, sqlObj) - if c.DB.GetDebug() { + if c.db.GetDebug() { c.writeSqlToLogger(sqlObj) } return &Stmt{ @@ -247,28 +247,28 @@ func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) { // GetAll queries and returns data records from database. func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) { - return c.DB.DoGetAll(nil, sql, args...) + return c.db.DoGetAll(nil, sql, args...) } // DoGetAll queries and returns data records from database. func (c *Core) DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) { if link == nil { - link, err = c.DB.Slave() + link, err = c.db.Slave() if err != nil { return nil, err } } - rows, err := c.DB.DoQuery(link, sql, args...) + rows, err := c.db.DoQuery(link, sql, args...) if err != nil || rows == nil { return nil, err } defer rows.Close() - return c.DB.convertRowsToResult(rows) + return c.db.convertRowsToResult(rows) } // GetOne queries and returns one record from database. func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) { - list, err := c.DB.GetAll(sql, args...) + list, err := c.db.GetAll(sql, args...) if err != nil { return nil, err } @@ -281,7 +281,7 @@ func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) { // GetArray queries and returns data values as slice from database. // Note that if there're multiple columns in the result, it returns just one column values randomly. func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) { - all, err := c.DB.DoGetAll(nil, sql, args...) + all, err := c.db.DoGetAll(nil, sql, args...) if err != nil { return nil, err } @@ -289,9 +289,9 @@ func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) { } // GetStruct queries one record from database and converts it to given struct. -// The parameter should be a pointer to struct. +// The parameter `pointer` should be a pointer to struct. func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) error { - one, err := c.DB.GetOne(sql, args...) + one, err := c.db.GetOne(sql, args...) if err != nil { return err } @@ -299,9 +299,9 @@ func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) e } // GetStructs queries records from database and converts them to given struct. -// The parameter should be type of struct slice: []struct/[]*struct. +// The parameter `pointer` should be type of struct slice: []struct/[]*struct. func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{}) error { - all, err := c.DB.GetAll(sql, args...) + all, err := c.db.GetAll(sql, args...) if err != nil { return err } @@ -311,8 +311,8 @@ func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{}) // GetScan queries one or more records from database and converts them to given struct or // struct array. // -// If parameter is type of struct pointer, it calls GetStruct internally for -// the conversion. If parameter is type of slice, it calls GetStructs internally +// If parameter `pointer` is type of struct pointer, it calls GetStruct internally for +// the conversion. If parameter `pointer` is type of slice, it calls GetStructs internally // for conversion. func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) error { t := reflect.TypeOf(pointer) @@ -323,9 +323,9 @@ func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) err k = t.Elem().Kind() switch k { case reflect.Array, reflect.Slice: - return c.DB.GetStructs(pointer, sql, args...) + return c.db.GetStructs(pointer, sql, args...) case reflect.Struct: - return c.DB.GetStruct(pointer, sql, args...) + return c.db.GetStruct(pointer, sql, args...) } return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k) } @@ -334,7 +334,7 @@ func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) err // The sql should queries only one field from database, or else it returns only one // field of the result. func (c *Core) GetValue(sql string, args ...interface{}) (Value, error) { - one, err := c.DB.GetOne(sql, args...) + one, err := c.db.GetOne(sql, args...) if err != nil { return gvar.New(nil), err } @@ -351,7 +351,7 @@ func (c *Core) GetCount(sql string, args ...interface{}) (int, error) { if !gregex.IsMatchString(`(?i)SELECT\s+COUNT\(.+\)\s+FROM`, sql) { sql, _ = gregex.ReplaceString(`(?i)(SELECT)\s+(.+)\s+(FROM)`, `$1 COUNT($2) $3`, sql) } - value, err := c.DB.GetValue(sql, args...) + value, err := c.db.GetValue(sql, args...) if err != nil { return 0, err } @@ -360,7 +360,7 @@ func (c *Core) GetCount(sql string, args ...interface{}) (int, error) { // PingMaster pings the master node to check authentication or keeps the connection alive. func (c *Core) PingMaster() error { - if master, err := c.DB.Master(); err != nil { + if master, err := c.db.Master(); err != nil { return err } else { return master.Ping() @@ -369,7 +369,7 @@ func (c *Core) PingMaster() error { // PingSlave pings the slave node to check authentication or keeps the connection alive. func (c *Core) PingSlave() error { - if slave, err := c.DB.Slave(); err != nil { + if slave, err := c.db.Slave(); err != nil { return err } else { return slave.Ping() @@ -381,10 +381,10 @@ func (c *Core) PingSlave() error { // if you no longer use the transaction. Commit or Rollback functions will also // close the transaction automatically. func (c *Core) Begin() (*TX, error) { - if master, err := c.DB.Master(); err != nil { + if master, err := c.db.Master(); err != nil { return nil, err } else { - ctx := c.DB.GetCtx() + ctx := c.db.GetCtx() if c.GetConfig().TranTimeout > 0 { var cancelFunc context.CancelFunc ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().TranTimeout) @@ -392,7 +392,7 @@ func (c *Core) Begin() (*TX, error) { } if tx, err := master.BeginTx(ctx, nil); err == nil { return &TX{ - db: c.DB, + db: c.db, tx: tx, master: master, }, nil @@ -402,16 +402,16 @@ func (c *Core) Begin() (*TX, error) { } } -// Transaction wraps the transaction logic using function . -// It rollbacks the transaction and returns the error from function if +// Transaction wraps the transaction logic using function `f`. +// It rollbacks the transaction and returns the error from function `f` if // it returns non-nil error. It commits the transaction and returns nil if -// function returns nil. +// function `f` returns nil. // -// Note that, you should not Commit or Rollback the transaction in function +// Note that, you should not Commit or Rollback the transaction in function `f` // as it is automatically handled by this function. func (c *Core) Transaction(f func(tx *TX) error) (err error) { var tx *TX - tx, err = c.DB.Begin() + tx, err = c.db.Begin() if err != nil { return err } @@ -438,12 +438,12 @@ func (c *Core) Transaction(f func(tx *TX) error) (err error) { // Insert does "INSERT INTO ..." statement for the table. // If there's already one unique record of the data in the table, it returns error. // -// The parameter can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // Eg: // Data(g.Map{"uid": 10000, "name":"john"}) // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) // -// The parameter specifies the batch operation count when given data is slice. +// The parameter `batch` specifies the batch operation count when given data is slice. func (c *Core) Insert(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return c.Model(table).Data(data).Batch(batch[0]).Insert() @@ -454,12 +454,12 @@ func (c *Core) Insert(table string, data interface{}, batch ...int) (sql.Result, // InsertIgnore does "INSERT IGNORE INTO ..." statement for the table. // If there's already one unique record of the data in the table, it ignores the inserting. // -// The parameter can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // Eg: // Data(g.Map{"uid": 10000, "name":"john"}) // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) // -// The parameter specifies the batch operation count when given data is slice. +// The parameter `batch` specifies the batch operation count when given data is slice. func (c *Core) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return c.Model(table).Data(data).Batch(batch[0]).InsertIgnore() @@ -471,14 +471,14 @@ func (c *Core) InsertIgnore(table string, data interface{}, batch ...int) (sql.R // If there's already one unique record of the data in the table, it deletes the record // and inserts a new one. // -// The parameter can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // Eg: // Data(g.Map{"uid": 10000, "name":"john"}) // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) // -// The parameter can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // If given data is type of slice, it then does batch replacing, and the optional parameter -// specifies the batch operation count. +// `batch` specifies the batch operation count. func (c *Core) Replace(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return c.Model(table).Data(data).Batch(batch[0]).Replace() @@ -490,13 +490,13 @@ func (c *Core) Replace(table string, data interface{}, batch ...int) (sql.Result // It updates the record if there's primary or unique index in the saving data, // or else it inserts a new record into the table. // -// The parameter can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // Eg: // Data(g.Map{"uid": 10000, "name":"john"}) // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) // // If given data is type of slice, it then does batch saving, and the optional parameter -// specifies the batch operation count. +// `batch` specifies the batch operation count. func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return c.Model(table).Data(data).Batch(batch[0]).Save() @@ -506,18 +506,18 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e // doInsert inserts or updates data for given table. // This function is usually used for custom interface definition, you do not need call it manually. -// The parameter can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // Eg: // Data(g.Map{"uid": 10000, "name":"john"}) // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) // -// The parameter
can be more than one table names, and also alias name, like: +// The parameter `table` can be more than one table names, and also alias name, like: // 1. Model names: // Model("user") // Model("user u") @@ -87,13 +84,13 @@ func (c *Core) Model(table ...string) *Model { tables := "" if len(table) > 1 { tables = fmt.Sprintf( - `%s AS %s`, c.DB.QuotePrefixTableName(table[0]), c.DB.QuoteWord(table[1]), + `%s AS %s`, c.db.QuotePrefixTableName(table[0]), c.db.QuoteWord(table[1]), ) } else if len(table) == 1 { - tables = c.DB.QuotePrefixTableName(table[0]) + tables = c.db.QuotePrefixTableName(table[0]) } return &Model{ - db: c.DB, + db: c.db, tablesInit: tables, tables: tables, fields: "*", @@ -103,6 +100,11 @@ func (c *Core) Model(table ...string) *Model { } } +// With creates and returns an ORM model based on meta data of given object. +func (c *Core) With(object interface{}) *Model { + return c.db.Model().With(object) +} + // Table is alias of tx.Model. // Deprecated, use Model instead. func (tx *TX) Table(table ...string) *Model { @@ -118,6 +120,12 @@ func (tx *TX) Model(table ...string) *Model { return model } +// With acts like Core.With except it operates on transaction. +// See Core.With. +func (tx *TX) With(object interface{}) *Model { + return tx.Model().With(object) +} + // Ctx sets the context for current operation. func (m *Model) Ctx(ctx context.Context) *Model { if ctx == nil { diff --git a/database/gdb/gdb_model_cache.go b/database/gdb/gdb_model_cache.go index 376e02c55..0529bf5f6 100644 --- a/database/gdb/gdb_model_cache.go +++ b/database/gdb/gdb_model_cache.go @@ -14,13 +14,13 @@ import ( // if there's another same sql request, it just reads and returns the result from cache, it // but not committed and executed into the database. // -// If the parameter < 0, which means it clear the cache with given . -// If the parameter = 0, which means it never expires. -// If the parameter > 0, which means it expires after . +// If the parameter `duration` < 0, which means it clear the cache with given `name`. +// If the parameter `duration` = 0, which means it never expires. +// If the parameter `duration` > 0, which means it expires after `duration`. // -// The optional parameter is used to bind a name to the cache, which means you can -// later control the cache like changing the or clearing the cache with specified -// . +// The optional parameter `name` is used to bind a name to the cache, which means you can +// later control the cache like changing the `duration` or clearing the cache with specified +// `name`. // // Note that, the cache feature is disabled if the model is performing select statement // on a transaction. diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 832508017..e95c284b6 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -10,7 +10,7 @@ import ( "strings" ) -// Where sets the condition statement for the model. The parameter can be type of +// Where sets the condition statement for the model. The parameter `where` can be type of // string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times, // multiple conditions will be joined into where statement using "AND". // Eg: @@ -45,9 +45,9 @@ func (m *Model) Having(having interface{}, args ...interface{}) *Model { return model } -// WherePri does the same logic as Model.Where except that if the parameter +// WherePri does the same logic as Model.Where except that if the parameter `where` // is a single condition like int/string/float/slice, it treats the condition as the primary -// key value. That is, if primary key is "id" and given parameter as "123", the +// key value. That is, if primary key is "id" and given `where` parameter as "123", the // WherePri function treats the condition as "id=123", but Model.Where treats the condition // as string "123". func (m *Model) WherePri(where interface{}, args ...interface{}) *Model { @@ -115,7 +115,7 @@ func (m *Model) OrderBy(orderBy string) *Model { } // Limit sets the "LIMIT" statement for the model. -// The parameter can be either one or two number, if passed two number is passed, +// The parameter `limit` can be either one or two number, if passed two number is passed, // it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]" // statement. func (m *Model) Limit(limit ...int) *Model { @@ -139,7 +139,7 @@ func (m *Model) Offset(offset int) *Model { } // Page sets the paging number for the model. -// The parameter is started from 1 for paging. +// The parameter `page` is started from 1 for paging. // Note that, it differs that the Limit function starts from 0 for "LIMIT" statement. func (m *Model) Page(page, limit int) *Model { model := m.getModel() diff --git a/database/gdb/gdb_model_delete.go b/database/gdb/gdb_model_delete.go index 9db70dedc..4c56cdcd4 100644 --- a/database/gdb/gdb_model_delete.go +++ b/database/gdb/gdb_model_delete.go @@ -15,7 +15,7 @@ import ( ) // Delete does "DELETE FROM ... " statement for the model. -// The optional parameter is the same as the parameter of Model.Where function, +// The optional parameter `where` is the same as the parameter of Model.Where function, // see Model.Where. func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) { if len(where) > 0 { diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index abf3e76cf..6ec438a67 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -26,7 +26,7 @@ func (m *Model) Filter() *Model { } // Fields sets the operation fields of the model, multiple fields joined using char ','. -// The parameter can be type of string/map/*map/struct/*struct. +// The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct. func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model { length := len(fieldNamesOrMapStruct) if length == 0 { @@ -56,7 +56,7 @@ func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model { // FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','. // Note that this function supports only single table operations. -// The parameter can be type of string/map/*map/struct/*struct. +// The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct. func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model { length := len(fieldNamesOrMapStruct) if length == 0 { @@ -88,7 +88,7 @@ func (m *Model) FieldsStr(prefix ...string) string { } // FieldsStr retrieves and returns all fields from the table, joined with char ','. -// The optional parameter specifies the prefix for each field, eg: FieldsStr("u."). +// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsStr("u."). func (m *Model) GetFieldsStr(prefix ...string) string { prefixStr := "" if len(prefix) > 0 { @@ -122,10 +122,10 @@ func (m *Model) FieldsExStr(fields string, prefix ...string) string { return m.GetFieldsExStr(fields, prefix...) } -// FieldsExStr retrieves and returns fields which are not in parameter from the table, +// FieldsExStr retrieves and returns fields which are not in parameter `fields` from the table, // joined with char ','. -// The parameter specifies the fields that are excluded. -// The optional parameter specifies the prefix for each field, eg: FieldsExStr("id", "u."). +// The parameter `fields` specifies the fields that are excluded. +// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsExStr("id", "u."). func (m *Model) GetFieldsExStr(fields string, prefix ...string) string { prefixStr := "" if len(prefix) > 0 { diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 4eca381f3..b0959d304 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -24,7 +24,7 @@ func (m *Model) Batch(batch int) *Model { } // Data sets the operation data for the model. -// The parameter can be type of string/map/gmap/slice/struct/*struct, etc. +// The parameter `data` can be type of string/map/gmap/slice/struct/*struct, etc. // Note that, it uses shallow value copying for `data` if `data` is type of map/slice // to avoid changing it inside function. // Eg: @@ -100,7 +100,7 @@ func (m *Model) Data(data ...interface{}) *Model { } // Insert does "INSERT INTO ..." statement for the model. -// The optional parameter is the same as the parameter of Model.Data function, +// The optional parameter `data` is the same as the parameter of Model.Data function, // see Model.Data. func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) { if len(data) > 0 { @@ -110,7 +110,7 @@ func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) { } // InsertIgnore does "INSERT IGNORE INTO ..." statement for the model. -// The optional parameter is the same as the parameter of Model.Data function, +// The optional parameter `data` is the same as the parameter of Model.Data function, // see Model.Data. func (m *Model) InsertIgnore(data ...interface{}) (result sql.Result, err error) { if len(data) > 0 { @@ -120,7 +120,7 @@ func (m *Model) InsertIgnore(data ...interface{}) (result sql.Result, err error) } // Replace does "REPLACE INTO ..." statement for the model. -// The optional parameter is the same as the parameter of Model.Data function, +// The optional parameter `data` is the same as the parameter of Model.Data function, // see Model.Data. func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) { if len(data) > 0 { @@ -130,7 +130,7 @@ func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) { } // Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the model. -// The optional parameter is the same as the parameter of Model.Data function, +// The optional parameter `data` is the same as the parameter of Model.Data function, // see Model.Data. // // It updates the record if there's primary or unique index in the saving data, diff --git a/database/gdb/gdb_model_join.go b/database/gdb/gdb_model_join.go index 1eebb7eb1..847c11abd 100644 --- a/database/gdb/gdb_model_join.go +++ b/database/gdb/gdb_model_join.go @@ -23,7 +23,7 @@ func isSubQuery(s string) bool { } // LeftJoin does "LEFT JOIN ... ON ..." statement on the model. -// The parameter
can be joined table and its joined condition, +// The parameter `table` can be joined table and its joined condition, // and also with its alias name, like: // Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid") // Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid") @@ -33,7 +33,7 @@ func (m *Model) LeftJoin(table ...string) *Model { } // RightJoin does "RIGHT JOIN ... ON ..." statement on the model. -// The parameter
can be joined table and its joined condition, +// The parameter `table` can be joined table and its joined condition, // and also with its alias name, like: // Table("user").RightJoin("user_detail", "user_detail.uid=user.uid") // Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid") @@ -43,7 +43,7 @@ func (m *Model) RightJoin(table ...string) *Model { } // InnerJoin does "INNER JOIN ... ON ..." statement on the model. -// The parameter
can be joined table and its joined condition, +// The parameter `table` can be joined table and its joined condition, // and also with its alias name, like: // Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid") // Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") @@ -53,7 +53,7 @@ func (m *Model) InnerJoin(table ...string) *Model { } // doJoin does "LEFT/RIGHT/INNER JOIN ... ON ..." statement on the model. -// The parameter
can be joined table and its joined condition, +// The parameter `table` can be joined table and its joined condition, // and also with its alias name, like: // Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid") // Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 3f4ad8723..ed392b6c7 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -28,7 +28,7 @@ func (m *Model) Select(where ...interface{}) (Result, error) { // It retrieves the records from table and returns the result as slice type. // It returns nil if there's no record retrieved with the given conditions from table. // -// The optional parameter is the same as the parameter of Model.Where function, +// The optional parameter `where` is the same as the parameter of Model.Where function, // see Model.Where. func (m *Model) All(where ...interface{}) (Result, error) { return m.doGetAll(false, where...) @@ -38,8 +38,8 @@ func (m *Model) All(where ...interface{}) (Result, error) { // It retrieves the records from table and returns the result as slice type. // It returns nil if there's no record retrieved with the given conditions from table. // -// The parameter specifies whether limits querying only one record if m.limit is not set. -// The optional parameter is the same as the parameter of Model.Where function, +// The parameter `limit1` specifies whether limits querying only one record if m.limit is not set. +// The optional parameter `where` is the same as the parameter of Model.Where function, // see Model.Where. func (m *Model) doGetAll(limit1 bool, where ...interface{}) (Result, error) { if len(where) > 0 { @@ -139,7 +139,7 @@ func (m *Model) Chunk(limit int, callback func(result Result, err error) bool) { // One retrieves one record from table and returns the result as map type. // It returns nil if there's no record retrieved with the given conditions from table. // -// The optional parameter is the same as the parameter of Model.Where function, +// The optional parameter `where` is the same as the parameter of Model.Where function, // see Model.Where. func (m *Model) One(where ...interface{}) (Record, error) { if len(where) > 0 { @@ -158,7 +158,7 @@ func (m *Model) One(where ...interface{}) (Record, error) { // Value retrieves a specified record value from table and returns the result as interface type. // It returns nil if there's no record found with the given conditions from table. // -// If the optional parameter is given, the fieldsAndWhere[0] is the selected fields +// If the optional parameter `fieldsAndWhere` is given, the fieldsAndWhere[0] is the selected fields // and fieldsAndWhere[1:] is treated as where condition fields. // Also see Model.Fields and Model.Where functions. func (m *Model) Value(fieldsAndWhere ...interface{}) (Value, error) { @@ -184,7 +184,7 @@ func (m *Model) Value(fieldsAndWhere ...interface{}) (Value, error) { // Array queries and returns data values as slice from database. // Note that if there're multiple columns in the result, it returns just one column values randomly. // -// If the optional parameter is given, the fieldsAndWhere[0] is the selected fields +// If the optional parameter `fieldsAndWhere` is given, the fieldsAndWhere[0] is the selected fields // and fieldsAndWhere[1:] is treated as where condition fields. // Also see Model.Fields and Model.Where functions. func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) { @@ -205,14 +205,14 @@ func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) { } // Struct retrieves one record from table and converts it into given struct. -// The parameter should be type of *struct/**struct. If type **struct is given, +// The parameter `pointer` should be type of *struct/**struct. If type **struct is given, // it can create the struct internally during converting. // -// The optional parameter is the same as the parameter of Model.Where function, +// The optional parameter `where` is the same as the parameter of Model.Where function, // see Model.Where. // // Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions -// from table and is not nil. +// from table and `pointer` is not nil. // // Eg: // user := new(User) @@ -225,18 +225,21 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error { if err != nil { return err } - return one.Struct(pointer) + if err = one.Struct(pointer); err != nil { + return err + } + return m.doWithScan(pointer) } // Structs retrieves records from table and converts them into given struct slice. -// The parameter should be type of *[]struct/*[]*struct. It can create and fill the struct +// The parameter `pointer` should be type of *[]struct/*[]*struct. It can create and fill the struct // slice internally during converting. // -// The optional parameter is the same as the parameter of Model.Where function, +// The optional parameter `where` is the same as the parameter of Model.Where function, // see Model.Where. // // Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions -// from table and is not empty. +// from table and `pointer` is not empty. // // Eg: // users := ([]User)(nil) @@ -252,11 +255,11 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error { return all.Structs(pointer) } -// Scan automatically calls Struct or Structs function according to the type of parameter . -// It calls function Struct if is type of *struct/**struct. -// It calls function Structs if is type of *[]struct/*[]*struct. +// Scan automatically calls Struct or Structs function according to the type of parameter `pointer`. +// It calls function Struct if `pointer` is type of *struct/**struct. +// It calls function Structs if `pointer` is type of *[]struct/*[]*struct. // -// The optional parameter is the same as the parameter of Model.Where function, +// The optional parameter `where` is the same as the parameter of Model.Where function, // see Model.Where. // // Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions @@ -275,21 +278,20 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error { // users := ([]*User)(nil) // err := db.Model("user").Scan(&users) func (m *Model) Scan(pointer interface{}, where ...interface{}) error { - t := reflect.TypeOf(pointer) - k := t.Kind() - if k != reflect.Ptr { - return fmt.Errorf("params should be type of pointer, but got: %v", k) + var reflectType reflect.Type + if v, ok := pointer.(reflect.Value); ok { + reflectType = v.Type() + } else { + reflectType = reflect.TypeOf(pointer) } - switch t.Elem().Kind() { - case reflect.Array, reflect.Slice: + if gstr.Contains(reflectType.String(), "[]") { return m.Structs(pointer, where...) - default: - return m.Struct(pointer, where...) } + return m.Struct(pointer, where...) } -// ScanList converts to struct slice which contains other complex struct attributes. -// Note that the parameter should be type of *[]struct/*[]*struct. +// ScanList converts `r` to struct slice which contains other complex struct attributes. +// Note that the parameter `listPointer` should be type of *[]struct/*[]*struct. // Usage example: // // type Entity struct { @@ -307,7 +309,7 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error { // The parameters "User"/"UserDetail"/"UserScores" in the example codes specify the target attribute struct // that current result will be bound to. // The "uid" in the example codes is the table field name of the result, and the "Uid" is the relational -// struct attribute name. It automatically calculates the HasOne/HasMany relationship with given +// struct attribute name. It automatically calculates the HasOne/HasMany relationship with given `relation` // parameter. // See the example or unit testing cases for clear understanding for this function. func (m *Model) ScanList(listPointer interface{}, attributeName string, relation ...string) (err error) { @@ -319,7 +321,7 @@ func (m *Model) ScanList(listPointer interface{}, attributeName string, relation } // Count does "SELECT COUNT(x) FROM ..." statement for the model. -// The optional parameter is the same as the parameter of Model.Where function, +// The optional parameter `where` is the same as the parameter of Model.Where function, // see Model.Where. func (m *Model) Count(where ...interface{}) (int, error) { if len(where) > 0 { diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index 3d7b2f0a6..b1f15ff62 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -19,7 +19,7 @@ import ( // Update does "UPDATE ... " statement for the model. // -// If the optional parameter is given, the dataAndWhere[0] is the updated data field, +// If the optional parameter `dataAndWhere` is given, the dataAndWhere[0] is the updated data field, // and dataAndWhere[1:] is treated as where condition fields. // Also see Model.Data and Model.Where functions. func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err error) { diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 2b93696ff..716db7a30 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -18,7 +18,7 @@ import ( "time" ) -// getModel creates and returns a cloned model of current model if is true, or else it returns +// getModel creates and returns a cloned model of current model if `safe` is true, or else it returns // the current model. func (m *Model) getModel() *Model { if !m.safe { @@ -147,8 +147,8 @@ func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEm return data, nil } -// getLink returns the underlying database link object with configured attribute. -// The parameter specifies whether using the master node if master-slave configured. +// getLink returns the underlying database link object with configured `linkType` attribute. +// The parameter `master` specifies whether using the master node if master-slave configured. func (m *Model) getLink(master bool) Link { if m.tx != nil { return m.tx.tx @@ -196,9 +196,9 @@ func (m *Model) getPrimaryKey() string { } // formatCondition formats where arguments of the model and returns a new condition sql and its arguments. -// Note that this function does not change any attribute value of the . +// Note that this function does not change any attribute value of the `m`. // -// The parameter specifies whether limits querying only one record if m.limit is not set. +// The parameter `limit1` specifies whether limits querying only one record if m.limit is not set. func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) { if len(m.whereHolder) > 0 { for _, v := range m.whereHolder { @@ -302,7 +302,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh return } -// mergeArguments creates and returns new arguments by merging and given . +// mergeArguments creates and returns new arguments by merging and given `args`. func (m *Model) mergeArguments(args []interface{}) []interface{} { if len(m.extraArgs) > 0 { newArgs := make([]interface{}, len(m.extraArgs)+len(args)) diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index ec6764585..b31036c75 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -6,12 +6,90 @@ package gdb +import ( + "fmt" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/structs" + "github.com/gogf/gf/internal/utils" + "github.com/gogf/gf/text/gregex" + "github.com/gogf/gf/text/gstr" +) + func (m *Model) With(structAttrPointer interface{}) *Model { model := m.getModel() if m.tables == "" { - m.tables = getTableNameFromObject(structAttrPointer) + m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(structAttrPointer)) return model } model.withArray = append(model.withArray, structAttrPointer) return model } + +func (m *Model) doWithScan(pointer interface{}) error { + if len(m.withArray) == 0 { + return nil + } + fieldMap, err := structs.FieldMap(pointer, nil) + if err != nil { + return err + } + for _, withItem := range m.withArray { + withItemReflectValueType, err := structs.StructType(withItem) + if err != nil { + return err + } + withItemReflectValueTypeStr := gstr.TrimAll(withItemReflectValueType.String(), "*[]") + for _, fieldValue := range fieldMap { + var ( + fieldType = fieldValue.Type() + fieldTypeStr = gstr.TrimAll(fieldType.String(), "*[]") + ) + if gstr.Compare(fieldTypeStr, withItemReflectValueTypeStr) == 0 { + var ( + withTag string + ormTag = fieldValue.Tag(OrmTagForStruct) + match, _ = gregex.MatchString( + fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith), + ormTag, + ) + ) + if len(match) > 1 { + withTag = match[1] + } + if withTag == "" { + continue + } + array := gstr.SplitAndTrim(withTag, "=") + if len(array) != 2 { + return gerror.Newf(`invalid with tag "%s"`, withTag) + } + var ( + relatedFieldName = array[0] + relatedAttrName = array[1] + relatedFieldValue interface{} + ) + // Find the value of related attribute from `pointer`. + for attributeName, attributeValue := range fieldMap { + if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) { + relatedFieldValue = attributeValue.Value.Interface() + break + } + } + if relatedFieldValue == nil { + return gerror.Newf( + `cannot find the related value for attribute name "%s" of with tag "%s"`, + relatedAttrName, withTag, + ) + } + err = m.db.With(fieldValue.Value). + Fields(withItemReflectValueType.FieldKeys()). + Where(relatedFieldName, relatedFieldValue). + Scan(fieldValue.Value) + if err != nil { + return err + } + } + } + } + return nil +} diff --git a/database/gdb/gdb_schema.go b/database/gdb/gdb_schema.go index 6f520fa81..c7f5fb2a7 100644 --- a/database/gdb/gdb_schema.go +++ b/database/gdb/gdb_schema.go @@ -16,7 +16,7 @@ type Schema struct { // Schema creates and returns a schema. func (c *Core) Schema(schema string) *Schema { return &Schema{ - db: c.DB, + db: c.db, schema: schema, } } @@ -31,7 +31,7 @@ func (tx *TX) Schema(schema string) *Schema { } // Table creates and returns a new ORM model. -// The parameter can be more than one table names, like : +// The parameter `tables` can be more than one table names, like : // "user", "user u", "user, user_detail", "user u, user_detail ud" func (s *Schema) Table(table string) *Model { var m *Model diff --git a/database/gdb/gdb_statement.go b/database/gdb/gdb_statement.go index d861141fd..e856ba0b0 100644 --- a/database/gdb/gdb_statement.go +++ b/database/gdb/gdb_statement.go @@ -69,11 +69,11 @@ func (s *Stmt) doStmtCommit(stmtType string, ctx context.Context, args ...interf Error: err, Start: timestampMilli1, End: timestampMilli2, - Group: s.core.DB.GetGroup(), + Group: s.core.db.GetGroup(), } ) s.core.addSqlToTracing(ctx, sqlObj) - if s.core.DB.GetDebug() { + if s.core.db.GetDebug() { s.core.writeSqlToLogger(sqlObj) } return result, err diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_transaction.go index d59114d2b..5f4a35f79 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_transaction.go @@ -75,7 +75,7 @@ func (tx *TX) GetOne(sql string, args ...interface{}) (Record, error) { } // GetStruct queries one record from database and converts it to given struct. -// The parameter should be a pointer to struct. +// The parameter `pointer` should be a pointer to struct. func (tx *TX) GetStruct(obj interface{}, sql string, args ...interface{}) error { one, err := tx.GetOne(sql, args...) if err != nil { @@ -85,7 +85,7 @@ func (tx *TX) GetStruct(obj interface{}, sql string, args ...interface{}) error } // GetStructs queries records from database and converts them to given struct. -// The parameter should be type of struct slice: []struct/[]*struct. +// The parameter `pointer` should be type of struct slice: []struct/[]*struct. func (tx *TX) GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error { all, err := tx.GetAll(sql, args...) if err != nil { @@ -97,8 +97,8 @@ func (tx *TX) GetStructs(objPointerSlice interface{}, sql string, args ...interf // GetScan queries one or more records from database and converts them to given struct or // struct array. // -// If parameter is type of struct pointer, it calls GetStruct internally for -// the conversion. If parameter is type of slice, it calls GetStructs internally +// If parameter `pointer` is type of struct pointer, it calls GetStruct internally for +// the conversion. If parameter `pointer` is type of slice, it calls GetStructs internally // for conversion. func (tx *TX) GetScan(objPointer interface{}, sql string, args ...interface{}) error { t := reflect.TypeOf(objPointer) @@ -146,12 +146,12 @@ func (tx *TX) GetCount(sql string, args ...interface{}) (int, error) { // Insert does "INSERT INTO ..." statement for the table. // If there's already one unique record of the data in the table, it returns error. // -// The parameter can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // Eg: // Data(g.Map{"uid": 10000, "name":"john"}) // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) // -// The parameter specifies the batch operation count when given data is slice. +// The parameter `batch` specifies the batch operation count when given data is slice. func (tx *TX) Insert(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return tx.Model(table).Data(data).Batch(batch[0]).Insert() @@ -162,12 +162,12 @@ func (tx *TX) Insert(table string, data interface{}, batch ...int) (sql.Result, // InsertIgnore does "INSERT IGNORE INTO ..." statement for the table. // If there's already one unique record of the data in the table, it ignores the inserting. // -// The parameter can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // Eg: // Data(g.Map{"uid": 10000, "name":"john"}) // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) // -// The parameter specifies the batch operation count when given data is slice. +// The parameter `batch` specifies the batch operation count when given data is slice. func (tx *TX) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return tx.Model(table).Data(data).Batch(batch[0]).InsertIgnore() @@ -179,14 +179,14 @@ func (tx *TX) InsertIgnore(table string, data interface{}, batch ...int) (sql.Re // If there's already one unique record of the data in the table, it deletes the record // and inserts a new one. // -// The parameter can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // Eg: // Data(g.Map{"uid": 10000, "name":"john"}) // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) // -// The parameter can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // If given data is type of slice, it then does batch replacing, and the optional parameter -// specifies the batch operation count. +// `batch` specifies the batch operation count. func (tx *TX) Replace(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return tx.Model(table).Data(data).Batch(batch[0]).Replace() @@ -198,13 +198,13 @@ func (tx *TX) Replace(table string, data interface{}, batch ...int) (sql.Result, // It updates the record if there's primary or unique index in the saving data, // or else it inserts a new record into the table. // -// The parameter can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // Eg: // Data(g.Map{"uid": 10000, "name":"john"}) // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) // // If given data is type of slice, it then does batch saving, and the optional parameter -// specifies the batch operation count. +// `batch` specifies the batch operation count. func (tx *TX) Save(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return tx.Model(table).Data(data).Batch(batch[0]).Save() @@ -213,7 +213,7 @@ func (tx *TX) Save(table string, data interface{}, batch ...int) (sql.Result, er } // BatchInsert batch inserts data. -// The parameter must be type of slice of map or struct. +// The parameter `list` must be type of slice of map or struct. func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return tx.Model(table).Data(list).Batch(batch[0]).Insert() @@ -222,7 +222,7 @@ func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Res } // BatchInsert batch inserts data with ignore option. -// The parameter must be type of slice of map or struct. +// The parameter `list` must be type of slice of map or struct. func (tx *TX) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return tx.Model(table).Data(list).Batch(batch[0]).InsertIgnore() @@ -231,7 +231,7 @@ func (tx *TX) BatchInsertIgnore(table string, list interface{}, batch ...int) (s } // BatchReplace batch replaces data. -// The parameter must be type of slice of map or struct. +// The parameter `list` must be type of slice of map or struct. func (tx *TX) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return tx.Model(table).Data(list).Batch(batch[0]).Replace() @@ -240,7 +240,7 @@ func (tx *TX) BatchReplace(table string, list interface{}, batch ...int) (sql.Re } // BatchSave batch replaces data. -// The parameter must be type of slice of map or struct. +// The parameter `list` must be type of slice of map or struct. func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { return tx.Model(table).Data(list).Batch(batch[0]).Save() @@ -250,11 +250,11 @@ func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Resul // Update does "UPDATE ... " statement for the table. // -// The parameter can be type of string/map/gmap/struct/*struct, etc. +// The parameter `data` can be type of string/map/gmap/struct/*struct, etc. // Eg: "uid=10000", "uid", 10000, g.Map{"uid": 10000, "name":"john"} // -// The parameter can be type of string/map/gmap/slice/struct/*struct, etc. -// It is commonly used with parameter . +// The parameter `condition` can be type of string/map/gmap/slice/struct/*struct, etc. +// It is commonly used with parameter `args`. // Eg: // "uid=10000", // "uid", 10000 @@ -268,8 +268,8 @@ func (tx *TX) Update(table string, data interface{}, condition interface{}, args // Delete does "DELETE FROM ... " statement for the table. // -// The parameter can be type of string/map/gmap/slice/struct/*struct, etc. -// It is commonly used with parameter . +// The parameter `condition` can be type of string/map/gmap/slice/struct/*struct, etc. +// It is commonly used with parameter `args`. // Eg: // "uid=10000", // "uid", 10000 diff --git a/database/gdb/gdb_type_record.go b/database/gdb/gdb_type_record.go index f469c413f..7f9c4ffb6 100644 --- a/database/gdb/gdb_type_record.go +++ b/database/gdb/gdb_type_record.go @@ -16,19 +16,19 @@ import ( "reflect" ) -// Json converts to JSON format content. +// Json converts `r` to JSON format content. func (r Record) Json() string { content, _ := gparser.VarToJson(r.Map()) return gconv.UnsafeBytesToStr(content) } -// Xml converts to XML format content. +// Xml converts `r` to XML format content. func (r Record) Xml(rootTag ...string) string { content, _ := gparser.VarToXml(r.Map(), rootTag...) return gconv.UnsafeBytesToStr(content) } -// Map converts to map[string]interface{}. +// Map converts `r` to map[string]interface{}. func (r Record) Map() Map { m := make(map[string]interface{}) for k, v := range r { @@ -37,15 +37,15 @@ func (r Record) Map() Map { return m } -// GMap converts to a gmap. +// GMap converts `r` to a gmap. func (r Record) GMap() *gmap.StrAnyMap { return gmap.NewStrAnyMapFrom(r.Map()) } -// Struct converts to a struct. -// Note that the parameter should be type of *struct/**struct. +// Struct converts `r` to a struct. +// Note that the parameter `pointer` should be type of *struct/**struct. // -// Note that it returns sql.ErrNoRows if is empty. +// Note that it returns sql.ErrNoRows if `r` is empty. func (r Record) Struct(pointer interface{}) error { // If the record is empty, it returns error. if r.IsEmpty() { @@ -76,7 +76,7 @@ func (r Record) Struct(pointer interface{}) error { return convertMapToStruct(r.Map(), pointer) } -// IsEmpty checks and returns whether is empty. +// IsEmpty checks and returns whether `r` is empty. func (r Record) IsEmpty() bool { return len(r) == 0 } diff --git a/database/gdb/gdb_type_result.go b/database/gdb/gdb_type_result.go index 92377fadc..e832e206c 100644 --- a/database/gdb/gdb_type_result.go +++ b/database/gdb/gdb_type_result.go @@ -16,7 +16,7 @@ import ( "github.com/gogf/gf/encoding/gparser" ) -// IsEmpty checks and returns whether is empty. +// IsEmpty checks and returns whether `r` is empty. func (r Result) IsEmpty() bool { return r.Len() == 0 } @@ -32,7 +32,7 @@ func (r Result) Size() int { } // Chunk splits an Result into multiple Results, -// the size of each array is determined by . +// the size of each array is determined by `size`. // The last chunk may contain less than size elements. func (r Result) Chunk(size int) []Result { if size < 1 { @@ -52,19 +52,19 @@ func (r Result) Chunk(size int) []Result { return n } -// Json converts to JSON format content. +// Json converts `r` to JSON format content. func (r Result) Json() string { content, _ := gparser.VarToJson(r.List()) return string(content) } -// Xml converts to XML format content. +// Xml converts `r` to XML format content. func (r Result) Xml(rootTag ...string) string { content, _ := gparser.VarToXml(r.List(), rootTag...) return string(content) } -// List converts to a List. +// List converts `r` to a List. func (r Result) List() List { list := make(List, len(r)) for k, v := range r { @@ -74,7 +74,7 @@ func (r Result) List() List { } // Array retrieves and returns specified column values as slice. -// The parameter is optional is the column field is only one. +// The parameter `field` is optional is the column field is only one. func (r Result) Array(field ...string) []Value { array := make([]Value, len(r)) if len(r) == 0 { @@ -95,7 +95,7 @@ func (r Result) Array(field ...string) []Value { return array } -// MapKeyValue converts to a map[string]Value of which key is specified by . +// MapKeyValue converts `r` to a map[string]Value of which key is specified by `key`. // Note that the item value may be type of slice. func (r Result) MapKeyValue(key string) map[string]Value { var ( @@ -123,7 +123,7 @@ func (r Result) MapKeyValue(key string) map[string]Value { return m } -// MapKeyStr converts to a map[string]Map of which key is specified by . +// MapKeyStr converts `r` to a map[string]Map of which key is specified by `key`. func (r Result) MapKeyStr(key string) map[string]Map { m := make(map[string]Map) for _, item := range r { @@ -134,7 +134,7 @@ func (r Result) MapKeyStr(key string) map[string]Map { return m } -// MapKeyInt converts to a map[int]Map of which key is specified by . +// MapKeyInt converts `r` to a map[int]Map of which key is specified by `key`. func (r Result) MapKeyInt(key string) map[int]Map { m := make(map[int]Map) for _, item := range r { @@ -145,7 +145,7 @@ func (r Result) MapKeyInt(key string) map[int]Map { return m } -// MapKeyUint converts to a map[uint]Map of which key is specified by . +// MapKeyUint converts `r` to a map[uint]Map of which key is specified by `key`. func (r Result) MapKeyUint(key string) map[uint]Map { m := make(map[uint]Map) for _, item := range r { @@ -156,7 +156,7 @@ func (r Result) MapKeyUint(key string) map[uint]Map { return m } -// RecordKeyInt converts to a map[int]Record of which key is specified by . +// RecordKeyInt converts `r` to a map[int]Record of which key is specified by `key`. func (r Result) RecordKeyStr(key string) map[string]Record { m := make(map[string]Record) for _, item := range r { @@ -167,7 +167,7 @@ func (r Result) RecordKeyStr(key string) map[string]Record { return m } -// RecordKeyInt converts to a map[int]Record of which key is specified by . +// RecordKeyInt converts `r` to a map[int]Record of which key is specified by `key`. func (r Result) RecordKeyInt(key string) map[int]Record { m := make(map[int]Record) for _, item := range r { @@ -178,7 +178,7 @@ func (r Result) RecordKeyInt(key string) map[int]Record { return m } -// RecordKeyUint converts to a map[uint]Record of which key is specified by . +// RecordKeyUint converts `r` to a map[uint]Record of which key is specified by `key`. func (r Result) RecordKeyUint(key string) map[uint]Record { m := make(map[uint]Record) for _, item := range r { @@ -189,8 +189,8 @@ func (r Result) RecordKeyUint(key string) map[uint]Record { return m } -// Structs converts to struct slice. -// Note that the parameter should be type of *[]struct/*[]*struct. +// Structs converts `r` to struct slice. +// Note that the parameter `pointer` should be type of *[]struct/*[]*struct. func (r Result) Structs(pointer interface{}) (err error) { var ( reflectValue = reflect.ValueOf(pointer) diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index 07578ffa0..b18f23891 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -15,8 +15,8 @@ import ( "reflect" ) -// ScanList converts to struct slice which contains other complex struct attributes. -// Note that the parameter should be type of *[]struct/*[]*struct. +// ScanList converts `r` to struct slice which contains other complex struct attributes. +// Note that the parameter `listPointer` should be type of *[]struct/*[]*struct. // Usage example: // // type Entity struct { @@ -38,7 +38,7 @@ import ( // The "uid" in the example codes is the table field name of the result, and the "Uid" is the relational // struct attribute name - not the attribute name of the bound to target. In the example codes, it's attribute // name "Uid" of "User" of entity "Entity". It automatically calculates the HasOne/HasMany relationship with -// given parameter. +// given `relation` parameter. // // See the example or unit testing cases for clear understanding for this function. func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) { diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index 216e83942..cbeeed29d 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -16,9 +16,9 @@ import ( func Test_Table_Relation_With(t *testing.T) { var ( - tableUser = "user_with" - tableUserDetail = "user_detail_with" - tableUserScores = "user_scores_with" + tableUser = "user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" ) if _, err := db.Exec(fmt.Sprintf(` CREATE TABLE IF NOT EXISTS %s ( @@ -55,20 +55,20 @@ PRIMARY KEY (id) defer dropTable(tableUserScores) type UserDetail struct { - gmeta.Meta `table:"user_detail_with"` + gmeta.Meta `orm:"table:user_detail"` Uid int `json:"uid"` Address string `json:"address"` } type UserScores struct { - gmeta.Meta `table:"user_scores_with"` + gmeta.Meta `orm:"table:user_scores"` Id int `json:"id"` Uid int `json:"uid"` Score int `json:"score"` } type User struct { - gmeta.Meta `table:"user_with"` + gmeta.Meta `orm:"table:user"` Id int `json:"id"` Name string `json:"name"` UserDetail *UserDetail `orm:"with:uid=id"` @@ -101,8 +101,16 @@ PRIMARY KEY (id) } gtest.C(t, func(t *gtest.T) { var user *User - err := db.Model().With(&user).Where("id", 3).Scan(&user) + db.SetDebug(true) + err := db.With(User{}). + With(User{}.UserDetail). + With(User{}.UserScores). + Where("id", 3). + Scan(&user) t.AssertNil(err) t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) }) } diff --git a/frame/g/g.go b/frame/g/g.go index f97762ade..2138cbfcf 100644 --- a/frame/g/g.go +++ b/frame/g/g.go @@ -6,51 +6,57 @@ package g -import ( - "github.com/gogf/gf/container/gvar" -) +import "github.com/gogf/gf/container/gvar" // Var is a universal variable interface, like generics. type Var = gvar.Var -// Frequently-used map type alias. -type Map = map[string]interface{} -type MapAnyAny = map[interface{}]interface{} -type MapAnyStr = map[interface{}]string -type MapAnyInt = map[interface{}]int -type MapStrAny = map[string]interface{} -type MapStrStr = map[string]string -type MapStrInt = map[string]int -type MapIntAny = map[int]interface{} -type MapIntStr = map[int]string -type MapIntInt = map[int]int -type MapAnyBool = map[interface{}]bool -type MapStrBool = map[string]bool -type MapIntBool = map[int]bool +// Frequently-used map alias. +type ( + Map = map[string]interface{} + MapAnyAny = map[interface{}]interface{} + MapAnyStr = map[interface{}]string + MapAnyInt = map[interface{}]int + MapStrAny = map[string]interface{} + MapStrStr = map[string]string + MapStrInt = map[string]int + MapIntAny = map[int]interface{} + MapIntStr = map[int]string + MapIntInt = map[int]int + MapAnyBool = map[interface{}]bool + MapStrBool = map[string]bool + MapIntBool = map[int]bool +) -// Frequently-used slice type alias. -type List = []Map -type ListAnyAny = []MapAnyAny -type ListAnyStr = []MapAnyStr -type ListAnyInt = []MapAnyInt -type ListStrAny = []MapStrAny -type ListStrStr = []MapStrStr -type ListStrInt = []MapStrInt -type ListIntAny = []MapIntAny -type ListIntStr = []MapIntStr -type ListIntInt = []MapIntInt -type ListAnyBool = []MapAnyBool -type ListStrBool = []MapStrBool -type ListIntBool = []MapIntBool +// Frequently-used slice alias. +type ( + List = []Map + ListAnyAny = []MapAnyAny + ListAnyStr = []MapAnyStr + ListAnyInt = []MapAnyInt + ListStrAny = []MapStrAny + ListStrStr = []MapStrStr + ListStrInt = []MapStrInt + ListIntAny = []MapIntAny + ListIntStr = []MapIntStr + ListIntInt = []MapIntInt + ListAnyBool = []MapAnyBool + ListStrBool = []MapStrBool + ListIntBool = []MapIntBool +) -// Frequently-used slice type alias. -type Slice = []interface{} -type SliceAny = []interface{} -type SliceStr = []string -type SliceInt = []int +// Frequently-used slice alias. +type ( + Slice = []interface{} + SliceAny = []interface{} + SliceStr = []string + SliceInt = []int +) // Array is alias of Slice. -type Array = []interface{} -type ArrayAny = []interface{} -type ArrayStr = []string -type ArrayInt = []int +type ( + Array = []interface{} + ArrayAny = []interface{} + ArrayStr = []string + ArrayInt = []int +) diff --git a/frame/g/g_object.go b/frame/g/g_object.go index 6cfacac4b..6e0dceb64 100644 --- a/frame/g/g_object.go +++ b/frame/g/g_object.go @@ -80,13 +80,14 @@ func Log(name ...string) *glog.Logger { return gins.Log(name...) } -// Database returns an instance of database ORM object with specified configuration group name. +// Database is alias of DB. +// See DB. +// Deprecated, use DB instead. func Database(name ...string) gdb.DB { return gins.Database(name...) } -// DB is alias of Database. -// See Database. +// DB returns an instance of database ORM object with specified configuration group name. func DB(name ...string) gdb.DB { return gins.Database(name...) } @@ -97,7 +98,7 @@ func DB(name ...string) gdb.DB { // "Table" is not proper for that purpose any more. // Deprecated, use Model instead. func Table(tables ...string) *gdb.Model { - return DB().Table(tables...) + return DB().Model(tables...) } // Model creates and returns a model based on configuration of default database group. @@ -105,6 +106,11 @@ func Model(tables ...string) *gdb.Model { return DB().Model(tables...) } +// With creates and returns an ORM model based on meta data of given object. +func With(object interface{}) *gdb.Model { + return DB().With(object) +} + // Redis returns an instance of redis client with specified configuration group name. func Redis(name ...string) *gredis.Redis { return gins.Redis(name...) diff --git a/internal/command/command.go b/internal/command/command.go index 56f2a9d55..824f83ca3 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -59,7 +59,7 @@ func Init(args ...string) { } } -// GetOpt returns the option value named . +// GetOpt returns the option value named `name`. func GetOpt(name string, def ...string) string { Init() if v, ok := defaultParsedOptions[name]; ok { @@ -77,14 +77,14 @@ func GetOptAll() map[string]string { return defaultParsedOptions } -// ContainsOpt checks whether option named exist in the arguments. +// ContainsOpt checks whether option named `name` exist in the arguments. func ContainsOpt(name string) bool { Init() _, ok := defaultParsedOptions[name] return ok } -// GetArg returns the argument at . +// GetArg returns the argument at `index`. func GetArg(index int, def ...string) string { Init() if index < len(defaultParsedArgs) { @@ -102,9 +102,9 @@ func GetArgAll() []string { return defaultParsedArgs } -// GetOptWithEnv returns the command line argument of the specified . -// If the argument does not exist, then it returns the environment variable with specified . -// It returns the default value if none of them exists. +// GetOptWithEnv returns the command line argument of the specified `key`. +// If the argument does not exist, then it returns the environment variable with specified `key`. +// It returns the default value `def` if none of them exists. // // Fetching Rules: // 1. Command line arguments are in lowercase format, eg: gf..; diff --git a/internal/empty/empty.go b/internal/empty/empty.go index 2e59ea5a0..234378aca 100644 --- a/internal/empty/empty.go +++ b/internal/empty/empty.go @@ -26,8 +26,8 @@ type apiMapStrAny interface { MapStrAny() map[string]interface{} } -// IsEmpty checks whether given empty. -// It returns true if is in: 0, nil, false, "", len(slice/map/chan) == 0, +// IsEmpty checks whether given `value` empty. +// It returns true if `value` is in: 0, nil, false, "", len(slice/map/chan) == 0, // or else it returns false. func IsEmpty(value interface{}) bool { if value == nil { @@ -151,9 +151,9 @@ func IsEmpty(value interface{}) bool { return false } -// IsNil checks whether given is nil. -// Parameter is used for tracing to the source variable if given is type -// of a pinter that also points to a pointer. It returns nil if the source is nil when +// IsNil checks whether given `value` is nil. +// Parameter `traceSource` is used for tracing to the source variable if given `value` is type +// of a pinter that also points to a pointer. It returns nil if the source is nil when `traceSource` // is true. // Note that it might use reflect feature which affects performance a little bit. func IsNil(value interface{}, traceSource ...bool) bool { diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index d746d5862..4edf5c2ab 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -31,7 +31,7 @@ func init() { // SetEnabled enables/disables the internal logging manually. // Note that this function is not concurrent safe, be aware of the DATA RACE. func SetEnabled(enabled bool) { - // If they're the same, it does not write the but only reading operation. + // If they're the same, it does not write the `isGFDebug` but only reading operation. if isGFDebug != enabled { isGFDebug = enabled } @@ -42,8 +42,8 @@ func IsEnabled() bool { return isGFDebug } -// Print prints with newline using fmt.Println. -// The parameter can be multiple variables. +// Print prints `v` with newline using fmt.Println. +// The parameter `v` can be multiple variables. func Print(v ...interface{}) { if !isGFDebug { return @@ -51,8 +51,8 @@ func Print(v ...interface{}) { fmt.Println(append([]interface{}{now(), "[INTE]", file()}, v...)...) } -// Printf prints with format using fmt.Printf. -// The parameter can be multiple variables. +// Printf prints `v` with format `format` using fmt.Printf. +// The parameter `v` can be multiple variables. func Printf(format string, v ...interface{}) { if !isGFDebug { return @@ -60,8 +60,8 @@ func Printf(format string, v ...interface{}) { fmt.Printf(now()+" [INTE] "+file()+" "+format+"\n", v...) } -// Error prints with newline using fmt.Println. -// The parameter can be multiple variables. +// Error prints `v` with newline using fmt.Println. +// The parameter `v` can be multiple variables. func Error(v ...interface{}) { if !isGFDebug { return @@ -71,7 +71,7 @@ func Error(v ...interface{}) { fmt.Println(array...) } -// Errorf prints with format using fmt.Printf. +// Errorf prints `v` with format `format` using fmt.Printf. func Errorf(format string, v ...interface{}) { if !isGFDebug { return diff --git a/internal/mutex/mutex.go b/internal/mutex/mutex.go index 9e04dc6bf..ccb32d037 100644 --- a/internal/mutex/mutex.go +++ b/internal/mutex/mutex.go @@ -16,7 +16,7 @@ type Mutex struct { } // New creates and returns a new *Mutex. -// The parameter is used to specify whether using this mutex in concurrent-safety, +// The parameter `safe` is used to specify whether using this mutex in concurrent-safety, // which is false in default. func New(safe ...bool) *Mutex { mu := new(Mutex) diff --git a/internal/rwmutex/rwmutex.go b/internal/rwmutex/rwmutex.go index 0a6a9615b..0d16fb7d3 100644 --- a/internal/rwmutex/rwmutex.go +++ b/internal/rwmutex/rwmutex.go @@ -17,7 +17,7 @@ type RWMutex struct { } // New creates and returns a new *RWMutex. -// The parameter is used to specify whether using this mutex in concurrent safety, +// The parameter `safe` is used to specify whether using this mutex in concurrent safety, // which is false in default. func New(safe ...bool) *RWMutex { mu := Create(safe...) @@ -25,7 +25,7 @@ func New(safe ...bool) *RWMutex { } // Create creates and returns a new RWMutex object. -// The parameter is used to specify whether using this mutex in concurrent safety, +// The parameter `safe` is used to specify whether using this mutex in concurrent safety, // which is false in default. func Create(safe ...bool) RWMutex { mu := RWMutex{} diff --git a/internal/structs/structs.go b/internal/structs/structs.go index 5ff9190db..d6204497c 100644 --- a/internal/structs/structs.go +++ b/internal/structs/structs.go @@ -20,36 +20,7 @@ type Type struct { // Field contains information of a struct field . type Field struct { - value reflect.Value - field reflect.StructField - // Retrieved tag value. There might be more than one tags in the field, - // but only one can be retrieved according to calling function rules. - TagValue string -} - -// Tag returns the value associated with key in the tag string. If there is no -// such key in the tag, Tag returns the empty string. -func (f *Field) Tag(key string) string { - return f.field.Tag.Get(key) -} - -// Value returns the underlying value of the field. It panics if the field -// is not exported. -func (f *Field) Value() interface{} { - return f.value.Interface() -} - -// IsEmbedded returns true if the given field is an anonymous field (embedded) -func (f *Field) IsEmbedded() bool { - return f.field.Anonymous -} - -// IsExported returns true if the given field is exported. -func (f *Field) IsExported() bool { - return f.field.PkgPath == "" -} - -// Name returns the name of the given field -func (f *Field) Name() string { - return f.field.Name + Value reflect.Value // The underlying value of the field. + Field reflect.StructField // The underlying field of the field. + TagValue string // Retrieved tag value. There might be more than one tags in the field, but only one can be retrieved according to calling function rules. } diff --git a/internal/structs/structs_map.go b/internal/structs/structs_field.go similarity index 52% rename from internal/structs/structs_map.go rename to internal/structs/structs_field.go index aa36050f4..8d3e0afe8 100644 --- a/internal/structs/structs_map.go +++ b/internal/structs/structs_field.go @@ -6,14 +6,43 @@ package structs -// MapField retrieves struct field as map[name/tag]*Field from , and returns the map. +// Tag returns the value associated with key in the tag string. If there is no +// such key in the tag, Tag returns the empty string. +func (f *Field) Tag(key string) string { + return f.Field.Tag.Get(key) +} + +// IsEmbedded returns true if the given field is an anonymous field (embedded) +func (f *Field) IsEmbedded() bool { + return f.Field.Anonymous +} + +// IsExported returns true if the given field is exported. +func (f *Field) IsExported() bool { + return f.Field.PkgPath == "" +} + +// Name returns the name of the given field +func (f *Field) Name() string { + return f.Field.Name +} + +// Type returns the type of the given field +func (f *Field) Type() Type { + return Type{ + Type: f.Field.Type, + } +} + +// FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`. // -// The parameter should be type of struct/*struct. +// The parameter `pointer` should be type of struct/*struct. // -// The parameter specifies the priority tag array for retrieving from high to low. +// The parameter `priority` specifies the priority tag array for retrieving from high to low. +// If it's given `nil`, it returns map[name]*Field, of which the `name` is attribute name. // // Note that it only retrieves the exported attributes with first letter up-case from struct. -func MapField(pointer interface{}, priority []string) (map[string]*Field, error) { +func FieldMap(pointer interface{}, priority []string) (map[string]*Field, error) { fields, err := getFieldValues(pointer) if err != nil { return nil, err @@ -40,7 +69,7 @@ func MapField(pointer interface{}, priority []string) (map[string]*Field, error) mapField[tagValue] = tempField } else { if field.IsEmbedded() { - m, err := MapField(field.value, priority) + m, err := FieldMap(field.Value, priority) if err != nil { return nil, err } diff --git a/internal/structs/structs_tag.go b/internal/structs/structs_tag.go index 1a7ac849c..2746cd29c 100644 --- a/internal/structs/structs_tag.go +++ b/internal/structs/structs_tag.go @@ -9,20 +9,73 @@ package structs import ( "errors" "reflect" + "strconv" ) -// TagFields retrieves struct tags as []*Field from , and returns it. +// ParseTag parses tag string into map. +func ParseTag(tag string) map[string]string { + var ( + key string + data = make(map[string]string) + ) + for tag != "" { + // Skip leading space. + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + break + } + // Scan to colon. A space, a quote or a control character is a syntax error. + // Strictly speaking, control chars include the range [0x7f, 0x9f], not just + // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters + // as it is simpler to inspect the tag's bytes than the tag's runes. + i = 0 + for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { + i++ + } + if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + break + } + key = tag[:i] + tag = tag[i+1:] + + // Scan quoted string to find value. + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + break + } + quotedValue := string(tag[:i+1]) + tag = tag[i+1:] + value, err := strconv.Unquote(quotedValue) + if err != nil { + panic(err) + } + data[key] = value + } + return data +} + +// TagFields retrieves and returns struct tags as []*Field from `pointer`. // -// The parameter should be type of struct/*struct. +// The parameter `pointer` should be type of struct/*struct. // // Note that it only retrieves the exported attributes with first letter up-case from struct. func TagFields(pointer interface{}, priority []string) ([]*Field, error) { return getFieldValuesByTagPriority(pointer, priority, map[string]struct{}{}) } -// TagMapName retrieves struct tags as map[tag]attribute from , and returns it. +// TagMapName retrieves and returns struct tags as map[tag]attribute from `pointer`. // -// The parameter should be type of struct/*struct. +// The parameter `pointer` should be type of struct/*struct. // // Note that it only retrieves the exported attributes with first letter up-case from struct. func TagMapName(pointer interface{}, priority []string) (map[string]string, error) { @@ -37,9 +90,9 @@ func TagMapName(pointer interface{}, priority []string) (map[string]string, erro return tagMap, nil } -// TagMapField retrieves struct tags as map[tag]*Field from , and returns it. +// TagMapField retrieves struct tags as map[tag]*Field from `pointer`, and returns it. // -// The parameter should be type of struct/*struct. +// The parameter `pointer` should be type of struct/*struct. // // Note that it only retrieves the exported attributes with first letter up-case from struct. func TagMapField(pointer interface{}, priority []string) (map[string]*Field, error) { @@ -88,8 +141,8 @@ func getFieldValues(value interface{}) ([]*Field, error) { ) for i := 0; i < length; i++ { fields[i] = &Field{ - value: reflectValue.Field(i), - field: structType.Field(i), + Value: reflectValue.Field(i), + Field: structType.Field(i), } } return fields, nil @@ -127,7 +180,7 @@ func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap } // If this is an embedded attribute, it retrieves the tags recursively. if field.IsEmbedded() { - if subTagFields, err := getFieldValuesByTagPriority(field.value, priority, tagMap); err != nil { + if subTagFields, err := getFieldValuesByTagPriority(field.Value, priority, tagMap); err != nil { return nil, err } else { tagFields = append(tagFields, subTagFields...) diff --git a/internal/structs/structs_type.go b/internal/structs/structs_type.go index cee57b2f7..283eb0498 100644 --- a/internal/structs/structs_type.go +++ b/internal/structs/structs_type.go @@ -11,36 +11,45 @@ import ( "reflect" ) -// StructType retrieves and returns the Type of specified struct/*struct. -func StructType(structOrPointer interface{}) (*Type, error) { +// StructType retrieves and returns the struct Type of specified struct/*struct. +func StructType(object interface{}) (*Type, error) { var ( reflectValue reflect.Value reflectKind reflect.Kind reflectType reflect.Type ) - if rv, ok := structOrPointer.(reflect.Value); ok { + if rv, ok := object.(reflect.Value); ok { reflectValue = rv } else { - reflectValue = reflect.ValueOf(structOrPointer) + reflectValue = reflect.ValueOf(object) } reflectKind = reflectValue.Kind() - for reflectKind == reflect.Ptr { - if !reflectValue.IsValid() || reflectValue.IsNil() { - // If pointer is type of *struct and nil, then automatically create a temporary struct. + for { + switch reflectKind { + case reflect.Ptr: + if !reflectValue.IsValid() || reflectValue.IsNil() { + // If pointer is type of *struct and nil, then automatically create a temporary struct. + reflectValue = reflect.New(reflectValue.Type().Elem()).Elem() + reflectKind = reflectValue.Kind() + } else { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + case reflect.Array, reflect.Slice: reflectValue = reflect.New(reflectValue.Type().Elem()).Elem() reflectKind = reflectValue.Kind() - } else { - reflectValue = reflectValue.Elem() - reflectKind = reflectValue.Kind() + default: + goto exitLoop } } +exitLoop: for reflectKind == reflect.Ptr { reflectValue = reflectValue.Elem() reflectKind = reflectValue.Kind() } if reflectKind != reflect.Struct { return nil, gerror.Newf( - `invalid parameter kind "%s", kind of "struct" is required`, + `invalid object kind "%s", kind of "struct" is required`, reflectKind, ) } @@ -50,6 +59,16 @@ func StructType(structOrPointer interface{}) (*Type, error) { }, nil } -func (t *Type) Signature() string { +// Signature returns an unique string as this type. +func (t Type) Signature() string { return t.PkgPath() + "/" + t.String() } + +// FieldKeys returns the keys of current struct/map. +func (t Type) FieldKeys() []string { + keys := make([]string, t.NumField()) + for i := 0; i < t.NumField(); i++ { + keys[i] = t.Field(i).Name + } + return keys +} diff --git a/internal/structs/structs_z_unit_test.go b/internal/structs/structs_z_unit_test.go index f694aecdf..c4f31d1a2 100644 --- a/internal/structs/structs_z_unit_test.go +++ b/internal/structs/structs_z_unit_test.go @@ -102,7 +102,7 @@ func Test_StructOfNilPointer(t *testing.T) { }) } -func Test_MapField(t *testing.T) { +func Test_FieldMap(t *testing.T) { gtest.C(t, func(t *gtest.T) { type User struct { Id int @@ -110,7 +110,7 @@ func Test_MapField(t *testing.T) { Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"` } var user *User - m, _ := structs.MapField(user, []string{"params"}) + m, _ := structs.FieldMap(user, []string{"params"}) t.Assert(len(m), 3) _, ok := m["Id"] t.Assert(ok, true) @@ -123,6 +123,26 @@ func Test_MapField(t *testing.T) { _, ok = m["pass"] t.Assert(ok, true) }) + gtest.C(t, func(t *gtest.T) { + type User struct { + Id int + Name string `params:"name"` + Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"` + } + var user *User + m, _ := structs.FieldMap(user, nil) + t.Assert(len(m), 3) + _, ok := m["Id"] + t.Assert(ok, true) + _, ok = m["Name"] + t.Assert(ok, true) + _, ok = m["name"] + t.Assert(ok, false) + _, ok = m["Pass"] + t.Assert(ok, true) + _, ok = m["pass"] + t.Assert(ok, false) + }) } func Test_StructType(t *testing.T) { @@ -148,7 +168,6 @@ func Test_StructType(t *testing.T) { t.AssertNil(err) t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`) }) - // Error. gtest.C(t, func(t *gtest.T) { type B struct { Name string @@ -156,7 +175,71 @@ func Test_StructType(t *testing.T) { type A struct { *B } - _, err := structs.StructType(new(A).B) + r, err := structs.StructType(new(A).B) + t.AssertNil(err) + t.Assert(r.String(), `structs_test.B`) + }) + // Error. + gtest.C(t, func(t *gtest.T) { + type B struct { + Name string + } + type A struct { + *B + Id int + } + _, err := structs.StructType(new(A).Id) t.AssertNE(err, nil) }) } + +func Test_StructTypeBySlice(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type B struct { + Name string + } + type A struct { + Array []*B + } + r, err := structs.StructType(new(A).Array) + t.AssertNil(err) + t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`) + }) + gtest.C(t, func(t *gtest.T) { + type B struct { + Name string + } + type A struct { + Array []B + } + r, err := structs.StructType(new(A).Array) + t.AssertNil(err) + t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`) + }) + gtest.C(t, func(t *gtest.T) { + type B struct { + Name string + } + type A struct { + Array *[]B + } + r, err := structs.StructType(new(A).Array) + t.AssertNil(err) + t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`) + }) +} + +func TestType_FieldKeys(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type B struct { + Id int + Name string + } + type A struct { + Array []*B + } + r, err := structs.StructType(new(A).Array) + t.AssertNil(err) + t.Assert(r.FieldKeys(), g.Slice{"Id", "Name"}) + }) +} diff --git a/internal/utils/utils_str.go b/internal/utils/utils_str.go index 7ca82b7bf..bd61f0ac9 100644 --- a/internal/utils/utils_str.go +++ b/internal/utils/utils_str.go @@ -67,7 +67,7 @@ func UcFirst(s string) string { return s } -// ReplaceByMap returns a copy of , +// ReplaceByMap returns a copy of `origin`, // which is replaced by a map in unordered way, case-sensitively. func ReplaceByMap(origin string, replaces map[string]string) string { for k, v := range replaces { @@ -87,7 +87,7 @@ func RemoveSymbols(s string) string { return string(b) } -// EqualFoldWithoutChars checks string and equal case-insensitively, +// EqualFoldWithoutChars checks string `s1` and `s2` equal case-insensitively, // with/without chars '-'/'_'/'.'/' '. func EqualFoldWithoutChars(s1, s2 string) bool { return strings.EqualFold(RemoveSymbols(s1), RemoveSymbols(s2)) diff --git a/text/gstr/gstr.go b/text/gstr/gstr.go index 2758b6091..e1805c713 100644 --- a/text/gstr/gstr.go +++ b/text/gstr/gstr.go @@ -23,6 +23,11 @@ import ( "github.com/gogf/gf/util/grand" ) +const ( + // NotFoundIndex is the position index for string not found in searching functions. + NotFoundIndex = -1 +) + // Replace returns a copy of the string // in which string replaced by case-sensitively. func Replace(origin, search, replace string, count ...int) string { @@ -43,8 +48,10 @@ func ReplaceI(origin, search, replace string, count ...int) string { if n == 0 { return origin } - length := len(search) - searchLower := strings.ToLower(search) + var ( + length = len(search) + searchLower = strings.ToLower(search) + ) for { originLower := strings.ToLower(origin) if pos := strings.Index(originLower, searchLower); pos != -1 { @@ -465,11 +472,11 @@ func Str(haystack string, needle string) string { if needle == "" { return "" } - idx := strings.Index(haystack, needle) - if idx == -1 { + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex { return "" } - return haystack[idx+len([]byte(needle))-1:] + return haystack[pos+len([]byte(needle))-1:] } // StrEx returns part of string starting from and excluding @@ -481,6 +488,26 @@ func StrEx(haystack string, needle string) string { return "" } +// StrTill returns part of string ending to and including +// the first occurrence of from the start of . +func StrTill(haystack string, needle string) string { + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex || pos == 0 { + return "" + } + return haystack[:pos+1] +} + +// StrTillEx returns part of string ending to and excluding +// the first occurrence of from the start of . +func StrTillEx(haystack string, needle string) string { + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex || pos == 0 { + return "" + } + return haystack[:pos] +} + // Shuffle randomly shuffles a string. // It considers parameter as unicode string. func Shuffle(str string) string { @@ -687,10 +714,10 @@ func SearchArray(a []string, s string) int { return i } } - return -1 + return NotFoundIndex } // InArray checks whether string in slice . func InArray(a []string, s string) bool { - return SearchArray(a, s) != -1 + return SearchArray(a, s) != NotFoundIndex } diff --git a/text/gstr/gstr_pos.go b/text/gstr/gstr_pos.go index 6fbb4420a..6009cd7cb 100644 --- a/text/gstr/gstr_pos.go +++ b/text/gstr/gstr_pos.go @@ -20,13 +20,12 @@ func Pos(haystack, needle string, startOffset ...int) int { if length == 0 || offset > length || -offset > length { return -1 } - if offset < 0 { offset += length } pos := strings.Index(haystack[offset:], needle) - if pos == -1 { - return -1 + if pos == NotFoundIndex { + return NotFoundIndex } return pos + offset } diff --git a/text/gstr/gstr_trim.go b/text/gstr/gstr_trim.go index 5b0e466b7..a2cdecf81 100644 --- a/text/gstr/gstr_trim.go +++ b/text/gstr/gstr_trim.go @@ -28,11 +28,11 @@ var ( // Trim strips whitespace (or other characters) from the beginning and end of a string. // The optional parameter specifies the additional stripped characters. func Trim(str string, characterMask ...string) string { - if len(characterMask) == 0 { - return strings.Trim(str, defaultTrimChars) - } else { - return strings.Trim(str, defaultTrimChars+characterMask[0]) + trimChars := defaultTrimChars + if len(characterMask) > 0 { + trimChars += characterMask[0] } + return strings.Trim(str, trimChars) } // TrimStr strips all of the given string from the beginning and end of a string. @@ -43,11 +43,11 @@ func TrimStr(str string, cut string, count ...int) string { // TrimLeft strips whitespace (or other characters) from the beginning of a string. func TrimLeft(str string, characterMask ...string) string { - if len(characterMask) == 0 { - return strings.TrimLeft(str, defaultTrimChars) - } else { - return strings.TrimLeft(str, defaultTrimChars+characterMask[0]) + trimChars := defaultTrimChars + if len(characterMask) > 0 { + trimChars += characterMask[0] } + return strings.TrimLeft(str, trimChars) } // TrimLeftStr strips all of the given string from the beginning of a string. @@ -69,11 +69,11 @@ func TrimLeftStr(str string, cut string, count ...int) string { // TrimRight strips whitespace (or other characters) from the end of a string. func TrimRight(str string, characterMask ...string) string { - if len(characterMask) == 0 { - return strings.TrimRight(str, defaultTrimChars) - } else { - return strings.TrimRight(str, defaultTrimChars+characterMask[0]) + trimChars := defaultTrimChars + if len(characterMask) > 0 { + trimChars += characterMask[0] } + return strings.TrimRight(str, trimChars) } // TrimRightStr strips all of the given string from the end of a string. @@ -94,3 +94,28 @@ func TrimRightStr(str string, cut string, count ...int) string { } return str } + +// TrimAll trims all characters in string `str`. +func TrimAll(str string, characterMask ...string) string { + trimChars := defaultTrimChars + if len(characterMask) > 0 { + trimChars += characterMask[0] + } + var ( + filtered bool + slice = make([]rune, 0, len(str)) + ) + for _, char := range str { + filtered = false + for _, trimChar := range trimChars { + if char == trimChar { + filtered = true + break + } + } + if !filtered { + slice = append(slice, char) + } + } + return string(slice) +} diff --git a/text/gstr/gstr_z_unit_basic_test.go b/text/gstr/gstr_z_unit_basic_test.go index 76cb2eadc..6e3934dc3 100644 --- a/text/gstr/gstr_z_unit_basic_test.go +++ b/text/gstr/gstr_z_unit_basic_test.go @@ -303,6 +303,22 @@ func Test_StrEx(t *testing.T) { }) } +func Test_StrTill(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gstr.StrTill("name@example.com", "@"), "name@") + t.Assert(gstr.StrTill("name@example.com", ""), "") + t.Assert(gstr.StrTill("name@example.com", "z"), "") + }) +} + +func Test_StrTillEx(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gstr.StrTillEx("name@example.com", "@"), "name") + t.Assert(gstr.StrTillEx("name@example.com", ""), "") + t.Assert(gstr.StrTillEx("name@example.com", "z"), "") + }) +} + func Test_Shuffle(t *testing.T) { gtest.C(t, func(t *gtest.T) { t.Assert(len(gstr.Shuffle("123456")), 6) diff --git a/text/gstr/gstr_z_unit_trim_test.go b/text/gstr/gstr_z_unit_trim_test.go index dd694c1d8..8415ae731 100644 --- a/text/gstr/gstr_z_unit_trim_test.go +++ b/text/gstr/gstr_z_unit_trim_test.go @@ -81,3 +81,17 @@ func Test_TrimLeftStr(t *testing.T) { t.Assert(gstr.TrimLeftStr("我爱中国人", "我爱中国"), "人") }) } + +func Test_TrimAll(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gstr.TrimAll("gogo我go\n爱gogo\n", "go"), "我爱") + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(gstr.TrimAll("gogo\n我go爱gogo", "go"), "我爱") + t.Assert(gstr.TrimAll("gogo\n我go爱gogo\n", "go"), "我爱") + t.Assert(gstr.TrimAll("gogo\n我go\n爱gogo", "go"), "我爱") + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(gstr.TrimAll("啊我爱\n啊中国\n人啊", "啊"), "我爱中国人") + }) +} diff --git a/util/gmeta/gmeta.go b/util/gmeta/gmeta.go index 2a2c357a3..4969537c2 100644 --- a/util/gmeta/gmeta.go +++ b/util/gmeta/gmeta.go @@ -11,22 +11,23 @@ import ( "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/structs" - "strconv" ) // Meta is used as an embedded attribute for struct to enabled meta data feature. type Meta struct{} const ( + // metaAttributeName is the attribute name of meta data in struct. metaAttributeName = "Meta" ) var ( + // metaDataCacheMap is a cache map for struct type to enhance the performance. metaDataCacheMap = gmap.NewStrAnyMap(true) ) // Data retrieves and returns all meta data from `object`. -// It automatically parses the tag string from "Mata" attribute as its meta data. +// It automatically parses and caches the tag string from "Mata" attribute as its meta data. func Data(object interface{}) map[string]interface{} { reflectType, err := structs.StructType(object) if err != nil { @@ -35,52 +36,11 @@ func Data(object interface{}) map[string]interface{} { return metaDataCacheMap.GetOrSetFuncLock(reflectType.Signature(), func() interface{} { if field, ok := reflectType.FieldByName(metaAttributeName); ok { var ( - key string - tag = field.Tag - data = make(map[string]interface{}) + tags = structs.ParseTag(string(field.Tag)) + data = make(map[string]interface{}, len(tags)) ) - for tag != "" { - // Skip leading space. - i := 0 - for i < len(tag) && tag[i] == ' ' { - i++ - } - tag = tag[i:] - if tag == "" { - break - } - // Scan to colon. A space, a quote or a control character is a syntax error. - // Strictly speaking, control chars include the range [0x7f, 0x9f], not just - // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters - // as it is simpler to inspect the tag's bytes than the tag's runes. - i = 0 - for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { - i++ - } - if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { - break - } - key = string(tag[:i]) - tag = tag[i+1:] - - // Scan quoted string to find value. - i = 1 - for i < len(tag) && tag[i] != '"' { - if tag[i] == '\\' { - i++ - } - i++ - } - if i >= len(tag) { - break - } - quotedValue := string(tag[:i+1]) - tag = tag[i+1:] - value, err := strconv.Unquote(quotedValue) - if err != nil { - panic(err) - } - data[key] = value + for k, v := range tags { + data[k] = v } return data } diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 9fe1b105d..580defc75 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -24,7 +24,7 @@ var ( // if is type of []string. // The optional parameter specifies the custom error messages for specified keys and rules. func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error { - // It here must use structs.TagFields not structs.MapField to ensure error sequence. + // It here must use structs.TagFields not structs.FieldMap to ensure error sequence. tagField, err := structs.TagFields(object, structTagPriority) if err != nil { return newErrorStr("invalid_object", err.Error()) @@ -85,13 +85,13 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages return nil } // Checks and extends the parameters map with struct alias tag. - mapField, err := structs.MapField(object, aliasNameTagPriority) + mapField, err := structs.FieldMap(object, aliasNameTagPriority) if err != nil { return newErrorStr("invalid_object", err.Error()) } for nameOrTag, field := range mapField { - params[nameOrTag] = field.Value() - params[field.Name()] = field.Value() + params[nameOrTag] = field.Value.Interface() + params[field.Name()] = field.Value.Interface() } for _, field := range tagField { fieldName := field.Name() @@ -105,7 +105,7 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages } // It here extends the params map using alias names. if _, ok := params[name]; !ok { - params[name] = field.Value() + params[name] = field.Value.Interface() } if _, ok := checkRules[name]; !ok { if _, ok := checkRules[fieldName]; ok { From a3fa10d820221fc45952e9c381b5be3b0f3899b6 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 9 Feb 2021 18:00:43 +0800 Subject: [PATCH 156/492] great! completed 'with' feature for package gdb --- database/gdb/gdb_func.go | 22 - database/gdb/gdb_model.go | 1 + database/gdb/gdb_model_select.go | 7 +- database/gdb/gdb_model_with.go | 179 +++++- database/gdb/gdb_type_record.go | 23 +- database/gdb/gdb_type_result.go | 53 +- .../gdb/gdb_z_mysql_association_with_test.go | 535 +++++++++++++++++- database/gdb/gdb_z_mysql_method_test.go | 2 +- database/gdb/gdb_z_mysql_struct_test.go | 2 +- internal/structs/structs_tag.go | 35 +- internal/structs/structs_type.go | 1 + internal/utils/utils_str.go | 39 ++ text/gstr/gstr_trim.go | 28 +- util/gconv/gconv_map.go | 40 +- util/gconv/gconv_struct.go | 68 ++- util/gconv/gconv_structs.go | 29 +- ...v_z_unit_struct_marshal_unmarshal_test.go} | 0 util/gconv/gconv_z_unit_struct_tag_test.go | 77 +++ util/gconv/gconv_z_unit_struct_test.go | 90 ++- ...e_test.go => gconv_z_unit_structs_test.go} | 178 +++--- util/gconv/gconv_z_unit_time_test.go | 2 +- 21 files changed, 1128 insertions(+), 283 deletions(-) rename util/gconv/{gconv_z_unit_struct_interface_test.go => gconv_z_unit_struct_marshal_unmarshal_test.go} (100%) create mode 100644 util/gconv/gconv_z_unit_struct_tag_test.go rename util/gconv/{gconv_z_unit_struct_slice_test.go => gconv_z_unit_structs_test.go} (58%) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index f36e17395..162f08e8c 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -783,25 +783,3 @@ func FormatSqlWithArgs(sql string, args []interface{}) string { }) return newQuery } - -// convertMapToStruct maps the `data` to given struct. -// Note that the given parameter `pointer` should be a pointer to s struct. -func convertMapToStruct(data map[string]interface{}, pointer interface{}) error { - tagNameMap, err := structs.TagMapName(pointer, []string{OrmTagForStruct}) - if err != nil { - return err - } - // It retrieves and returns the mapping between orm tag and the struct attribute name. - var ( - mapping = make(map[string]string) - tagFieldName string - ) - for tag, attr := range tagNameMap { - tagFieldName = strings.Split(tag, ",")[0] - if !gregex.IsMatchString(regularFieldNameRegPattern, tagFieldName) { - continue - } - mapping[tagFieldName] = attr - } - return gconv.Struct(data, pointer, mapping) -} diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 99333dede..04f0bbd25 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -26,6 +26,7 @@ type Model struct { fields string // Operation fields, multiple fields joined using char ','. fieldsEx string // Excluded operation fields, multiple fields joined using char ','. withArray []interface{} // Arguments for With feature. + withAll bool // Enable model association operations on all objects that have "with" tag in the struct. extraArgs []interface{} // Extra custom arguments for sql. whereHolder []*whereHolder // Condition strings for where operation. groupBy string // Used for "group by" statement. diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index ed392b6c7..1cd5139fa 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -228,7 +228,7 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error { if err = one.Struct(pointer); err != nil { return err } - return m.doWithScan(pointer) + return m.doWithScanStruct(pointer) } // Structs retrieves records from table and converts them into given struct slice. @@ -252,7 +252,10 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error { if err != nil { return err } - return all.Structs(pointer) + if err = all.Structs(pointer); err != nil { + return err + } + return m.doWithScanStructs(pointer) } // Scan automatically calls Struct or Structs function according to the type of parameter `pointer`. diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index b31036c75..8ead1401c 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -13,27 +13,89 @@ import ( "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" + "reflect" ) -func (m *Model) With(structAttrPointer interface{}) *Model { +// With creates and returns an ORM model based on meta data of given object. +// It also enables model association operations feature on given `object`. +// It can be called multiple times to add one or more objects to model and enable +// their mode association operations feature. +// For example, if given struct definition: +// type User struct { +// gmeta.Meta `orm:"table:user"` +// Id int `json:"id"` +// Name string `json:"name"` +// UserDetail *UserDetail `orm:"with:uid=id"` +// UserScores []*UserScores `orm:"with:uid=id"` +// } +// We can enable model association operations on attribute `UserDetail` and `UserScores` by: +// db.With(User{}.UserDetail).With(User{}.UserDetail).Scan(xxx) +// Or: +// db.With(UserDetail{}).With(UserDetail{}).Scan(xxx) +func (m *Model) With(object interface{}) *Model { model := m.getModel() if m.tables == "" { - m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(structAttrPointer)) + m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(object)) return model } - model.withArray = append(model.withArray, structAttrPointer) + model.withArray = append(model.withArray, object) return model } -func (m *Model) doWithScan(pointer interface{}) error { - if len(m.withArray) == 0 { +// WithAll enables model association operations on all objects that have "with" tag in the struct. +func (m *Model) WithAll() *Model { + model := m.getModel() + model.withAll = true + return model +} + +// getWithTagObjectArrayFrom retrieves and returns object array that have "with" tag in the struct. +func (m *Model) getWithTagObjectArrayFrom(pointer interface{}) ([]interface{}, error) { + fieldMap, err := structs.FieldMap(pointer, nil) + if err != nil { + return nil, err + } + withTagObjectArray := make([]interface{}, 0) + for _, fieldValue := range fieldMap { + var ( + withTag string + ormTag = fieldValue.Tag(OrmTagForStruct) + match, _ = gregex.MatchString( + fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith), + ormTag, + ) + ) + if len(match) > 1 { + withTag = match[1] + } + if withTag == "" { + continue + } + withTagObjectArray = append(withTagObjectArray, fieldValue.Value.Interface()) + } + return withTagObjectArray, nil +} + +// doWithScanStruct handles model association operations feature for single struct. +func (m *Model) doWithScanStruct(pointer interface{}) error { + var ( + err error + withArray = m.withArray + ) + if m.withAll { + withArray, err = m.getWithTagObjectArrayFrom(pointer) + if err != nil { + return err + } + } + if len(withArray) == 0 { return nil } fieldMap, err := structs.FieldMap(pointer, nil) if err != nil { return err } - for _, withItem := range m.withArray { + for withIndex, withItem := range withArray { withItemReflectValueType, err := structs.StructType(withItem) if err != nil { return err @@ -81,10 +143,109 @@ func (m *Model) doWithScan(pointer interface{}) error { relatedAttrName, withTag, ) } - err = m.db.With(fieldValue.Value). - Fields(withItemReflectValueType.FieldKeys()). + bindToReflectValue := fieldValue.Value + switch bindToReflectValue.Kind() { + case reflect.Array, reflect.Slice: + if bindToReflectValue.CanAddr() { + bindToReflectValue = bindToReflectValue.Addr() + } + } + model := m.db.With(fieldValue.Value) + for i, v := range withArray { + if i == withIndex { + continue + } + model = model.With(v) + } + err = model.Fields(withItemReflectValueType.FieldKeys()). Where(relatedFieldName, relatedFieldValue). - Scan(fieldValue.Value) + Scan(bindToReflectValue) + if err != nil { + return err + } + } + } + } + return nil +} + +// doWithScanStructs handles model association operations feature for struct slice. +func (m *Model) doWithScanStructs(pointer interface{}) error { + var ( + err error + withArray = m.withArray + ) + if m.withAll { + withArray, err = m.getWithTagObjectArrayFrom(pointer) + if err != nil { + return err + } + } + if len(withArray) == 0 { + return nil + } + fieldMap, err := structs.FieldMap(pointer, nil) + if err != nil { + return err + } + for withIndex, withItem := range withArray { + withItemReflectValueType, err := structs.StructType(withItem) + if err != nil { + return err + } + withItemReflectValueTypeStr := gstr.TrimAll(withItemReflectValueType.String(), "*[]") + for fieldName, fieldValue := range fieldMap { + var ( + fieldType = fieldValue.Type() + fieldTypeStr = gstr.TrimAll(fieldType.String(), "*[]") + ) + if gstr.Compare(fieldTypeStr, withItemReflectValueTypeStr) == 0 { + var ( + withTag string + ormTag = fieldValue.Tag(OrmTagForStruct) + match, _ = gregex.MatchString( + fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith), + ormTag, + ) + ) + if len(match) > 1 { + withTag = match[1] + } + if withTag == "" { + continue + } + array := gstr.SplitAndTrim(withTag, "=") + if len(array) != 2 { + return gerror.Newf(`invalid with tag "%s"`, withTag) + } + var ( + relatedFieldName = array[0] + relatedAttrName = array[1] + relatedFieldValue interface{} + ) + // Find the value slice of related attribute from `pointer`. + for attributeName, _ := range fieldMap { + if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) { + relatedFieldValue = ListItemValuesUnique(pointer, attributeName) + break + } + } + if relatedFieldValue == nil { + return gerror.Newf( + `cannot find the related value for attribute name "%s" of with tag "%s"`, + relatedAttrName, withTag, + ) + } + model := m.db.With(fieldValue.Value) + for i, v := range withArray { + if i == withIndex { + continue + } + model = model.With(v) + } + err = model.Fields(withItemReflectValueType.FieldKeys()). + Where(relatedFieldName, relatedFieldValue). + ScanList(pointer, fieldName, withTag) if err != nil { return err } diff --git a/database/gdb/gdb_type_record.go b/database/gdb/gdb_type_record.go index 7f9c4ffb6..3685dd296 100644 --- a/database/gdb/gdb_type_record.go +++ b/database/gdb/gdb_type_record.go @@ -10,10 +10,8 @@ import ( "database/sql" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/encoding/gparser" - "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/util/gconv" - "reflect" ) // Json converts `r` to JSON format content. @@ -54,26 +52,7 @@ func (r Record) Struct(pointer interface{}) error { } return nil } - // Special handling for parameter type: reflect.Value - if _, ok := pointer.(reflect.Value); ok { - return convertMapToStruct(r.Map(), pointer) - } - var ( - reflectValue = reflect.ValueOf(pointer) - reflectKind = reflectValue.Kind() - ) - if reflectKind != reflect.Ptr { - return gerror.New("parameter should be type of *struct/**struct") - } - reflectValue = reflectValue.Elem() - reflectKind = reflectValue.Kind() - if reflectKind == reflect.Invalid { - return gerror.New("parameter is an invalid pointer, maybe nil") - } - if reflectKind != reflect.Ptr && reflectKind != reflect.Struct { - return gerror.New("parameter should be type of *struct/**struct") - } - return convertMapToStruct(r.Map(), pointer) + return gconv.StructTag(r.Map(), pointer, OrmTagForStruct) } // IsEmpty checks and returns whether `r` is empty. diff --git a/database/gdb/gdb_type_result.go b/database/gdb/gdb_type_result.go index e832e206c..776cb0657 100644 --- a/database/gdb/gdb_type_result.go +++ b/database/gdb/gdb_type_result.go @@ -7,13 +7,10 @@ package gdb import ( - "database/sql" - "fmt" "github.com/gogf/gf/container/gvar" - "math" - "reflect" - "github.com/gogf/gf/encoding/gparser" + "github.com/gogf/gf/util/gconv" + "math" ) // IsEmpty checks and returns whether `r` is empty. @@ -192,49 +189,5 @@ func (r Result) RecordKeyUint(key string) map[uint]Record { // Structs converts `r` to struct slice. // Note that the parameter `pointer` should be type of *[]struct/*[]*struct. func (r Result) Structs(pointer interface{}) (err error) { - var ( - reflectValue = reflect.ValueOf(pointer) - reflectKind = reflectValue.Kind() - ) - if reflectKind != reflect.Ptr { - return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) - } - reflectValue = reflectValue.Elem() - reflectKind = reflectValue.Kind() - if reflectKind != reflect.Slice && reflectKind != reflect.Array { - return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) - } - length := len(r) - if length == 0 { - // The pointed slice is not empty. - if reflectValue.Len() > 0 { - // It here checks if it has struct item, which is already initialized. - // It then returns error to warn the developer its empty and no conversion. - if v := reflectValue.Index(0); v.Kind() != reflect.Ptr { - return sql.ErrNoRows - } - } - // Do nothing for empty struct slice. - return nil - } - var ( - reflectType = reflect.TypeOf(pointer) - array = reflect.MakeSlice(reflectType.Elem(), length, length) - itemType = array.Index(0).Type() - itemKind = itemType.Kind() - ) - for i := 0; i < length; i++ { - var elem reflect.Value - if itemKind == reflect.Ptr { - elem = reflect.New(itemType.Elem()) - } else { - elem = reflect.New(itemType).Elem() - } - if err = r[i].Struct(elem); err != nil { - return fmt.Errorf(`slice element conversion failed: %s`, err.Error()) - } - array.Index(i).Set(elem) - } - reflect.ValueOf(pointer).Elem().Set(array) - return nil + return gconv.StructsTag(r, pointer, OrmTagForStruct) } diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index cbeeed29d..f4633f50b 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -14,7 +14,7 @@ import ( "testing" ) -func Test_Table_Relation_With(t *testing.T) { +func Test_Table_Relation_With_Scan(t *testing.T) { var ( tableUser = "user" tableUserDetail = "user_detail" @@ -26,7 +26,7 @@ id int(10) unsigned NOT NULL AUTO_INCREMENT, name varchar(45) NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - `, tableUser)); err != nil { + `, tableUser)); err != nil { gtest.Error(err) } defer dropTable(tableUser) @@ -37,7 +37,7 @@ uid int(10) unsigned NOT NULL AUTO_INCREMENT, address varchar(45) NOT NULL, PRIMARY KEY (uid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - `, tableUserDetail)); err != nil { + `, tableUserDetail)); err != nil { gtest.Error(err) } defer dropTable(tableUserDetail) @@ -49,7 +49,7 @@ uid int(10) unsigned NOT NULL, score int(10) unsigned NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - `, tableUserScores)); err != nil { + `, tableUserScores)); err != nil { gtest.Error(err) } defer dropTable(tableUserScores) @@ -101,7 +101,6 @@ PRIMARY KEY (id) } gtest.C(t, func(t *gtest.T) { var user *User - db.SetDebug(true) err := db.With(User{}). With(User{}.UserDetail). With(User{}.UserScores). @@ -112,5 +111,531 @@ PRIMARY KEY (id) t.AssertNE(user.UserDetail, nil) t.Assert(user.UserDetail.Uid, 3) t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var user User + err := db.With(user). + With(user.UserDetail). + With(user.UserScores). + Where("id", 4). + Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.With(User{}). + With(UserDetail{}). + With(UserScores{}). + Where("id", 4). + Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) + // With part attribute: UserDetail. + gtest.C(t, func(t *gtest.T) { + var user User + err := db.With(user). + With(user.UserDetail). + Where("id", 4). + Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 0) + }) + // With part attribute: UserScores. + gtest.C(t, func(t *gtest.T) { + var user User + err := db.With(user). + With(user.UserScores). + Where("id", 4). + Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.Assert(user.UserDetail, nil) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_With_ScanList(t *testing.T) { + var ( + tableUser = "user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +uid int(10) unsigned NOT NULL AUTO_INCREMENT, +address varchar(45) NOT NULL, +PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +uid int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:user"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id"` + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.With(User{}). + With(User{}.UserDetail). + With(User{}.UserScores). + Where("id", []int{3, 4}). + Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.AssertNE(users[0].UserDetail, nil) + t.Assert(users[0].UserDetail.Uid, 3) + t.Assert(users[0].UserDetail.Address, "address_3") + t.Assert(len(users[0].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Uid, 3) + t.Assert(users[0].UserScores[4].Score, 5) + + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.AssertNE(users[1].UserDetail, nil) + t.Assert(users[1].UserDetail.Uid, 4) + t.Assert(users[1].UserDetail.Address, "address_4") + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Uid, 4) + t.Assert(users[1].UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var users []User + err := db.With(User{}). + With(User{}.UserDetail). + With(User{}.UserScores). + Where("id", []int{3, 4}). + Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.AssertNE(users[0].UserDetail, nil) + t.Assert(users[0].UserDetail.Uid, 3) + t.Assert(users[0].UserDetail.Address, "address_3") + t.Assert(len(users[0].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Uid, 3) + t.Assert(users[0].UserScores[4].Score, 5) + + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.AssertNE(users[1].UserDetail, nil) + t.Assert(users[1].UserDetail.Uid, 4) + t.Assert(users[1].UserDetail.Address, "address_4") + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Uid, 4) + t.Assert(users[1].UserScores[4].Score, 5) + }) + // With part attribute: UserDetail. + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.With(User{}). + With(User{}.UserDetail). + Where("id", []int{3, 4}). + Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.AssertNE(users[0].UserDetail, nil) + t.Assert(users[0].UserDetail.Uid, 3) + t.Assert(users[0].UserDetail.Address, "address_3") + t.Assert(len(users[0].UserScores), 0) + + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.AssertNE(users[1].UserDetail, nil) + t.Assert(users[1].UserDetail.Uid, 4) + t.Assert(users[1].UserDetail.Address, "address_4") + t.Assert(len(users[1].UserScores), 0) + }) + // With part attribute: UserScores. + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.With(User{}). + With(User{}.UserScores). + Where("id", []int{3, 4}). + Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.Assert(users[0].UserDetail, nil) + t.Assert(len(users[0].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Uid, 3) + t.Assert(users[0].UserScores[4].Score, 5) + + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.Assert(users[1].UserDetail, nil) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Uid, 4) + t.Assert(users[1].UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_WithAll_Scan(t *testing.T) { + var ( + tableUser = "user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +uid int(10) unsigned NOT NULL AUTO_INCREMENT, +address varchar(45) NOT NULL, +PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +uid int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:user"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id"` + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_WithAll_ScanList(t *testing.T) { + var ( + tableUser = "user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +uid int(10) unsigned NOT NULL AUTO_INCREMENT, +address varchar(45) NOT NULL, +PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +uid int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:user"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id"` + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.AssertNE(users[0].UserDetail, nil) + t.Assert(users[0].UserDetail.Uid, 3) + t.Assert(users[0].UserDetail.Address, "address_3") + t.Assert(len(users[0].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Uid, 3) + t.Assert(users[0].UserScores[4].Score, 5) + + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.AssertNE(users[1].UserDetail, nil) + t.Assert(users[1].UserDetail.Uid, 4) + t.Assert(users[1].UserDetail.Address, "address_4") + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Uid, 4) + t.Assert(users[1].UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var users []User + err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.AssertNE(users[0].UserDetail, nil) + t.Assert(users[0].UserDetail.Uid, 3) + t.Assert(users[0].UserDetail.Address, "address_3") + t.Assert(len(users[0].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Uid, 3) + t.Assert(users[0].UserScores[4].Score, 5) + + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.AssertNE(users[1].UserDetail, nil) + t.Assert(users[1].UserDetail.Uid, 4) + t.Assert(users[1].UserDetail.Address, "address_4") + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Uid, 4) + t.Assert(users[1].UserScores[4].Score, 5) }) } diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index 56b928a6a..74b348686 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -821,7 +821,7 @@ func Test_DB_ToJson(t *testing.T) { result = nil err = result.Structs(&users) - t.AssertNE(err, nil) + t.AssertNil(err) }) gtest.C(t, func(t *gtest.T) { diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index 8e47a1f0a..4face358d 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -296,7 +296,7 @@ func Test_Structs_Empty(t *testing.T) { all, err := db.Table(table).Where("id>100").All() t.AssertNil(err) users := make([]User, 10) - t.AssertNE(all.Structs(&users), nil) + t.Assert(all.Structs(&users), nil) }) gtest.C(t, func(t *gtest.T) { all, err := db.Table(table).Where("id>100").All() diff --git a/internal/structs/structs_tag.go b/internal/structs/structs_tag.go index 2746cd29c..a167e07e4 100644 --- a/internal/structs/structs_tag.go +++ b/internal/structs/structs_tag.go @@ -91,12 +91,11 @@ func TagMapName(pointer interface{}, priority []string) (map[string]string, erro } // TagMapField retrieves struct tags as map[tag]*Field from `pointer`, and returns it. -// -// The parameter `pointer` should be type of struct/*struct. +// The parameter `object` should be either type of struct/*struct/[]struct/[]*struct. // // Note that it only retrieves the exported attributes with first letter up-case from struct. -func TagMapField(pointer interface{}, priority []string) (map[string]*Field, error) { - fields, err := TagFields(pointer, priority) +func TagMapField(object interface{}, priority []string) (map[string]*Field, error) { + fields, err := TagFields(object, priority) if err != nil { return nil, err } @@ -120,19 +119,31 @@ func getFieldValues(value interface{}) ([]*Field, error) { reflectValue = reflect.ValueOf(value) reflectKind = reflectValue.Kind() } - - for reflectKind == reflect.Ptr { - if !reflectValue.IsValid() || reflectValue.IsNil() { - // If pointer is type of *struct and nil, then automatically create a temporary struct. + for { + switch reflectKind { + case reflect.Ptr: + if !reflectValue.IsValid() || reflectValue.IsNil() { + // If pointer is type of *struct and nil, then automatically create a temporary struct. + reflectValue = reflect.New(reflectValue.Type().Elem()).Elem() + reflectKind = reflectValue.Kind() + } else { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + case reflect.Array, reflect.Slice: reflectValue = reflect.New(reflectValue.Type().Elem()).Elem() reflectKind = reflectValue.Kind() - } else { - reflectValue = reflectValue.Elem() - reflectKind = reflectValue.Kind() + default: + goto exitLoop } } +exitLoop: + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } if reflectKind != reflect.Struct { - return nil, errors.New("given value should be type of struct/*struct") + return nil, errors.New("given value should be either type of struct/*struct/[]struct/[]*struct") } var ( structType = reflectValue.Type() diff --git a/internal/structs/structs_type.go b/internal/structs/structs_type.go index 283eb0498..19e20ad40 100644 --- a/internal/structs/structs_type.go +++ b/internal/structs/structs_type.go @@ -12,6 +12,7 @@ import ( ) // StructType retrieves and returns the struct Type of specified struct/*struct. +// The parameter `object` should be either type of struct/*struct/[]struct/[]*struct. func StructType(object interface{}) (*Type, error) { var ( reflectValue reflect.Value diff --git a/internal/utils/utils_str.go b/internal/utils/utils_str.go index bd61f0ac9..01688e9da 100644 --- a/internal/utils/utils_str.go +++ b/internal/utils/utils_str.go @@ -10,6 +10,21 @@ import ( "strings" ) +var ( + // DefaultTrimChars are the characters which are stripped by Trim* functions in default. + DefaultTrimChars = string([]byte{ + '\t', // Tab. + '\v', // Vertical tab. + '\n', // New line (line feed). + '\r', // Carriage return. + '\f', // New page. + ' ', // Ordinary space. + 0x00, // NUL-byte. + 0x85, // Delete. + 0xA0, // Non-breaking space. + }) +) + // IsLetterUpper checks whether the given byte b is in upper case. func IsLetterUpper(b byte) bool { if b >= byte('A') && b <= byte('Z') { @@ -92,3 +107,27 @@ func RemoveSymbols(s string) string { func EqualFoldWithoutChars(s1, s2 string) bool { return strings.EqualFold(RemoveSymbols(s1), RemoveSymbols(s2)) } + +// SplitAndTrim splits string by a string to an array, +// and calls Trim to every element of this array. It ignores the elements +// which are empty after Trim. +func SplitAndTrim(str, delimiter string, characterMask ...string) []string { + array := make([]string, 0) + for _, v := range strings.Split(str, delimiter) { + v = Trim(v, characterMask...) + if v != "" { + array = append(array, v) + } + } + return array +} + +// Trim strips whitespace (or other characters) from the beginning and end of a string. +// The optional parameter specifies the additional stripped characters. +func Trim(str string, characterMask ...string) string { + trimChars := DefaultTrimChars + if len(characterMask) > 0 { + trimChars += characterMask[0] + } + return strings.Trim(str, trimChars) +} diff --git a/text/gstr/gstr_trim.go b/text/gstr/gstr_trim.go index a2cdecf81..e7883e986 100644 --- a/text/gstr/gstr_trim.go +++ b/text/gstr/gstr_trim.go @@ -7,32 +7,14 @@ package gstr import ( + "github.com/gogf/gf/internal/utils" "strings" ) -var ( - // defaultTrimChars are the characters which are stripped by Trim* functions in default. - defaultTrimChars = string([]byte{ - '\t', // Tab. - '\v', // Vertical tab. - '\n', // New line (line feed). - '\r', // Carriage return. - '\f', // New page. - ' ', // Ordinary space. - 0x00, // NUL-byte. - 0x85, // Delete. - 0xA0, // Non-breaking space. - }) -) - // Trim strips whitespace (or other characters) from the beginning and end of a string. // The optional parameter specifies the additional stripped characters. func Trim(str string, characterMask ...string) string { - trimChars := defaultTrimChars - if len(characterMask) > 0 { - trimChars += characterMask[0] - } - return strings.Trim(str, trimChars) + return utils.Trim(str, characterMask...) } // TrimStr strips all of the given string from the beginning and end of a string. @@ -43,7 +25,7 @@ func TrimStr(str string, cut string, count ...int) string { // TrimLeft strips whitespace (or other characters) from the beginning of a string. func TrimLeft(str string, characterMask ...string) string { - trimChars := defaultTrimChars + trimChars := utils.DefaultTrimChars if len(characterMask) > 0 { trimChars += characterMask[0] } @@ -69,7 +51,7 @@ func TrimLeftStr(str string, cut string, count ...int) string { // TrimRight strips whitespace (or other characters) from the end of a string. func TrimRight(str string, characterMask ...string) string { - trimChars := defaultTrimChars + trimChars := utils.DefaultTrimChars if len(characterMask) > 0 { trimChars += characterMask[0] } @@ -97,7 +79,7 @@ func TrimRightStr(str string, cut string, count ...int) string { // TrimAll trims all characters in string `str`. func TrimAll(str string, characterMask ...string) string { - trimChars := defaultTrimChars + trimChars := utils.DefaultTrimChars if len(characterMask) > 0 { trimChars += characterMask[0] } diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index bbc9e3aec..cb7631af0 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -232,7 +232,7 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b rvField reflect.Value dataMap = make(map[string]interface{}) // result map. reflectType = reflectValue.Type() // attribute value type. - name = "" // name may be the tag name or the struct attribute name. + mapKey = "" // mapKey may be the tag name or the struct attribute name. ) for i := 0; i < reflectValue.NumField(); i++ { rtField = reflectType.Field(i) @@ -242,32 +242,32 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b if !utils.IsLetterUpper(fieldName[0]) { continue } - name = "" + mapKey = "" fieldTag := rtField.Tag for _, tag := range tags { - if name = fieldTag.Get(tag); name != "" { + if mapKey = fieldTag.Get(tag); mapKey != "" { break } } - if name == "" { - name = fieldName + if mapKey == "" { + mapKey = fieldName } else { // Support json tag feature: -, omitempty - name = strings.TrimSpace(name) - if name == "-" { + mapKey = strings.TrimSpace(mapKey) + if mapKey == "-" { continue } - array := strings.Split(name, ",") + array := strings.Split(mapKey, ",") if len(array) > 1 { switch strings.TrimSpace(array[1]) { case "omitempty": if empty.IsEmpty(rvField.Interface()) { continue } else { - name = strings.TrimSpace(array[0]) + mapKey = strings.TrimSpace(array[0]) } default: - name = strings.TrimSpace(array[0]) + mapKey = strings.TrimSpace(array[0]) } } } @@ -284,7 +284,7 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b switch rvAttrKind { case reflect.Struct: var ( - hasNoTag = name == fieldName + hasNoTag = mapKey == fieldName rvAttrInterface = rvAttrField.Interface() ) if hasNoTag && rtField.Anonymous { @@ -296,41 +296,41 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b dataMap[k] = v } } else { - dataMap[name] = rvAttrInterface + dataMap[mapKey] = rvAttrInterface } } else if !hasNoTag && rtField.Anonymous { // It means this attribute field has desired tag. - dataMap[name] = doMapConvertForMapOrStructValue(false, rvAttrInterface, true, tags...) + dataMap[mapKey] = doMapConvertForMapOrStructValue(false, rvAttrInterface, true, tags...) } else { - dataMap[name] = doMapConvertForMapOrStructValue(false, rvAttrInterface, false, tags...) + dataMap[mapKey] = doMapConvertForMapOrStructValue(false, rvAttrInterface, false, tags...) } // The struct attribute is type of slice. case reflect.Array, reflect.Slice: length := rvField.Len() if length == 0 { - dataMap[name] = rvField.Interface() + dataMap[mapKey] = rvField.Interface() break } array := make([]interface{}, length) for i := 0; i < length; i++ { array[i] = doMapConvertForMapOrStructValue(false, rvField.Index(i), recursive, tags...) } - dataMap[name] = array + dataMap[mapKey] = array default: if rvField.IsValid() { - dataMap[name] = reflectValue.Field(i).Interface() + dataMap[mapKey] = reflectValue.Field(i).Interface() } else { - dataMap[name] = nil + dataMap[mapKey] = nil } } } else { // No recursive map value converting if rvField.IsValid() { - dataMap[name] = reflectValue.Field(i).Interface() + dataMap[mapKey] = reflectValue.Field(i).Interface() } else { - dataMap[name] = nil + dataMap[mapKey] = nil } } } diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index ed7afd999..7e6b955a8 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -32,17 +32,32 @@ import ( // in mapping procedure to do the matching. // It ignores the map key, if it does not match. func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - return doStruct(params, pointer, mapping...) + var keyToAttributeNameMapping map[string]string + if len(mapping) > 0 { + keyToAttributeNameMapping = mapping[0] + } + return doStruct(params, pointer, keyToAttributeNameMapping, "") +} + +// StructTag acts as Struct but also with support for priority tag feature, which retrieves the +// specified tags for `params` key-value items to struct attribute names mapping. +// The parameter `priorityTag` supports multiple tags that can be joined with char ','. +func StructTag(params interface{}, pointer interface{}, priorityTag string) (err error) { + return doStruct(params, pointer, nil, priorityTag) } // StructDeep do Struct function recursively. // Deprecated, use Struct instead. func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error { - return doStruct(params, pointer, mapping...) + var keyToAttributeNameMapping map[string]string + if len(mapping) > 0 { + keyToAttributeNameMapping = mapping[0] + } + return doStruct(params, pointer, keyToAttributeNameMapping, "") } // doStruct is the core internal converting function for any data to struct. -func doStruct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { +func doStruct(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) { if params == nil { // If is nil, no conversion. return nil @@ -179,7 +194,7 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str continue } } - if err = doStruct(paramsMap, elemFieldValue, mapping...); err != nil { + if err = doStruct(paramsMap, elemFieldValue, mapping, priorityTag); err != nil { return err } } else { @@ -193,13 +208,26 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str // The key of the tagMap is the attribute name of the struct, // and the value is its replaced tag name for later comparison to improve performance. - tagMap := make(map[string]string) - tagToNameMap, err := structs.TagMapName(pointerElemReflectValue, StructTagPriority) + var ( + tagMap = make(map[string]string) + priorityTagArray []string + ) + if priorityTag != "" { + priorityTagArray = append(utils.SplitAndTrim(priorityTag, ","), StructTagPriority...) + } else { + priorityTagArray = StructTagPriority + } + tagToNameMap, err := structs.TagMapName(pointerElemReflectValue, priorityTagArray) if err != nil { return err } - for k, v := range tagToNameMap { - tagMap[v] = utils.RemoveSymbols(k) + for tagName, attributeName := range tagToNameMap { + // If there's something else in the tag string, + // it uses the first part which is split using char ','. + // Eg: + // orm:"id, priority" + // orm:"name, with:uid=id" + tagMap[attributeName] = utils.RemoveSymbols(strings.Split(tagName, ",")[0]) } var ( @@ -209,8 +237,8 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str for mapK, mapV := range paramsMap { attrName = "" // It firstly checks the passed mapping rules. - if len(mapping) > 0 && len(mapping[0]) > 0 { - if passedAttrKey, ok := mapping[0][mapK]; ok { + if len(mapping) > 0 { + if passedAttrKey, ok := mapping[mapK]; ok { attrName = passedAttrKey } } @@ -254,7 +282,7 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str } // Mark it done. doneMap[attrName] = struct{}{} - if err := bindVarToStructAttr(pointerElemReflectValue, attrName, mapV, mapping...); err != nil { + if err := bindVarToStructAttr(pointerElemReflectValue, attrName, mapV, mapping, priorityTag); err != nil { return err } } @@ -262,7 +290,7 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str } // bindVarToStructAttr sets value to struct object attribute by name. -func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, mapping ...map[string]string) (err error) { +func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, mapping map[string]string, priorityTag string) (err error) { structFieldValue := elem.FieldByName(name) if !structFieldValue.IsValid() { return nil @@ -273,7 +301,7 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, map } defer func() { if e := recover(); e != nil { - if err = bindVarToReflectValue(structFieldValue, value, mapping...); err != nil { + if err = bindVarToReflectValue(structFieldValue, value, mapping, priorityTag); err != nil { err = gerror.Wrapf(err, `error binding value to attribute "%s"`, name) } } @@ -322,7 +350,7 @@ func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value i } // bindVarToReflectValue sets to reflect value object . -func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, mapping ...map[string]string) (err error) { +func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, mapping map[string]string, priorityTag string) (err error) { if err, ok := bindVarToReflectValueWithInterfaceCheck(structFieldValue, value); ok { return err } @@ -342,7 +370,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma switch kind { case reflect.Struct: // Recursively converting for struct attribute. - if err := doStruct(value, structFieldValue); err != nil { + if err := doStruct(value, structFieldValue, nil, ""); err != nil { // Note there's reflect conversion mechanism here. structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type())) } @@ -359,14 +387,14 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma for i := 0; i < v.Len(); i++ { if t.Kind() == reflect.Ptr { e := reflect.New(t.Elem()).Elem() - if err := doStruct(v.Index(i).Interface(), e); err != nil { + if err := doStruct(v.Index(i).Interface(), e, nil, ""); err != nil { // Note there's reflect conversion mechanism here. e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t)) } a.Index(i).Set(e.Addr()) } else { e := reflect.New(t).Elem() - if err := doStruct(v.Index(i).Interface(), e); err != nil { + if err := doStruct(v.Index(i).Interface(), e, nil, ""); err != nil { // Note there's reflect conversion mechanism here. e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t)) } @@ -379,14 +407,14 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma t := a.Index(0).Type() if t.Kind() == reflect.Ptr { e := reflect.New(t.Elem()).Elem() - if err := doStruct(value, e); err != nil { + if err := doStruct(value, e, nil, ""); err != nil { // Note there's reflect conversion mechanism here. e.Set(reflect.ValueOf(value).Convert(t)) } a.Index(0).Set(e.Addr()) } else { e := reflect.New(t).Elem() - if err := doStruct(value, e); err != nil { + if err := doStruct(value, e, nil, ""); err != nil { // Note there's reflect conversion mechanism here. e.Set(reflect.ValueOf(value).Convert(t)) } @@ -402,7 +430,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma return err } elem := item.Elem() - if err = bindVarToReflectValue(elem, value, mapping...); err == nil { + if err = bindVarToReflectValue(elem, value, mapping, priorityTag); err == nil { structFieldValue.Set(elem.Addr()) } diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index 0eccae8aa..a7c294abc 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -14,13 +14,28 @@ import ( // Structs converts any slice to given struct slice. func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - return doStructs(params, pointer, mapping...) + var keyToAttributeNameMapping map[string]string + if len(mapping) > 0 { + keyToAttributeNameMapping = mapping[0] + } + return doStructs(params, pointer, keyToAttributeNameMapping, "") +} + +// StructsTag acts as Structs but also with support for priority tag feature, which retrieves the +// specified tags for `params` key-value items to struct attribute names mapping. +// The parameter `priorityTag` supports multiple tags that can be joined with char ','. +func StructsTag(params interface{}, pointer interface{}, priorityTag string) (err error) { + return doStructs(params, pointer, nil, priorityTag) } // StructsDeep converts any slice to given struct slice recursively. // Deprecated, use Structs instead. func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - return doStructs(params, pointer, mapping...) + var keyToAttributeNameMapping map[string]string + if len(mapping) > 0 { + keyToAttributeNameMapping = mapping[0] + } + return doStructs(params, pointer, keyToAttributeNameMapping, "") } // doStructs converts any slice to given struct slice. @@ -30,7 +45,7 @@ func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string] // The parameter should be type of pointer to slice of struct. // Note that if is a pointer to another pointer of type of slice of struct, // it will create the struct/pointer internally. -func doStructs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { +func doStructs(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) { if params == nil { // If is nil, no conversion. return nil @@ -102,11 +117,13 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st for i := 0; i < len(paramsMaps); i++ { var tempReflectValue reflect.Value if i < pointerRvLength { + // Might be nil. tempReflectValue = pointerRvElem.Index(i).Elem() - } else { + } + if !tempReflectValue.IsValid() { tempReflectValue = reflect.New(itemType.Elem()).Elem() } - if err = Struct(paramsMaps[i], tempReflectValue, mapping...); err != nil { + if err = doStruct(paramsMaps[i], tempReflectValue, mapping, priorityTag); err != nil { return err } reflectElemArray.Index(i).Set(tempReflectValue.Addr()) @@ -120,7 +137,7 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st } else { tempReflectValue = reflect.New(itemType).Elem() } - if err = Struct(paramsMaps[i], tempReflectValue, mapping...); err != nil { + if err = doStruct(paramsMaps[i], tempReflectValue, mapping, priorityTag); err != nil { return err } reflectElemArray.Index(i).Set(tempReflectValue) diff --git a/util/gconv/gconv_z_unit_struct_interface_test.go b/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go similarity index 100% rename from util/gconv/gconv_z_unit_struct_interface_test.go rename to util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go diff --git a/util/gconv/gconv_z_unit_struct_tag_test.go b/util/gconv/gconv_z_unit_struct_tag_test.go new file mode 100644 index 000000000..daa06d477 --- /dev/null +++ b/util/gconv/gconv_z_unit_struct_tag_test.go @@ -0,0 +1,77 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gconv_test + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/test/gtest" + "github.com/gogf/gf/util/gconv" + "testing" +) + +func Test_StructTag(t *testing.T) { + type User struct { + Uid int + Name string + Pass1 string `orm:"password1"` + Pass2 string `orm:"password2"` + } + gtest.C(t, func(t *gtest.T) { + user := new(User) + params1 := g.Map{ + "uid": 1, + "Name": "john", + "password1": "123", + "password2": "456", + } + if err := gconv.Struct(params1, user); err != nil { + t.Error(err) + } + t.Assert(user, &User{ + Uid: 1, + Name: "john", + Pass1: "", + Pass2: "", + }) + }) + gtest.C(t, func(t *gtest.T) { + user := new(User) + params1 := g.Map{ + "uid": 1, + "Name": "john", + "password1": "123", + "password2": "456", + } + if err := gconv.StructTag(params1, user, "orm"); err != nil { + t.Error(err) + } + t.Assert(user, &User{ + Uid: 1, + Name: "john", + Pass1: "123", + Pass2: "456", + }) + }) + gtest.C(t, func(t *gtest.T) { + user := new(User) + params2 := g.Map{ + "uid": 2, + "name": "smith", + "password1": "111", + "password2": "222", + } + if err := gconv.StructTag(params2, user, "orm"); err != nil { + t.Error(err) + } + t.Assert(user, &User{ + Uid: 2, + Name: "smith", + Pass1: "111", + Pass2: "222", + }) + }) +} diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index e13702a0e..6c08a6d21 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -7,14 +7,13 @@ package gconv_test import ( - "github.com/gogf/gf/internal/json" - "testing" - "time" - "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/internal/json" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" "github.com/gogf/gf/util/gconv" + "testing" + "time" ) func Test_Struct_Basic1(t *testing.T) { @@ -904,6 +903,89 @@ func Test_Struct_Embedded(t *testing.T) { }) } +func Test_Struct_Slice(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type User struct { + Scores []int + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + t.Assert(err, nil) + t.Assert(user.Scores, array) + }) + gtest.C(t, func(t *gtest.T) { + type User struct { + Scores []int32 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + t.Assert(err, nil) + t.Assert(user.Scores, array) + }) + gtest.C(t, func(t *gtest.T) { + type User struct { + Scores []int64 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + t.Assert(err, nil) + t.Assert(user.Scores, array) + }) + gtest.C(t, func(t *gtest.T) { + type User struct { + Scores []uint + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + t.Assert(err, nil) + t.Assert(user.Scores, array) + }) + gtest.C(t, func(t *gtest.T) { + type User struct { + Scores []uint32 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + t.Assert(err, nil) + t.Assert(user.Scores, array) + }) + gtest.C(t, func(t *gtest.T) { + type User struct { + Scores []uint64 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + t.Assert(err, nil) + t.Assert(user.Scores, array) + }) + gtest.C(t, func(t *gtest.T) { + type User struct { + Scores []float32 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + t.Assert(err, nil) + t.Assert(user.Scores, array) + }) + gtest.C(t, func(t *gtest.T) { + type User struct { + Scores []float64 + } + user := new(User) + array := g.Slice{1, 2, 3} + err := gconv.Struct(g.Map{"scores": array}, user) + t.Assert(err, nil) + t.Assert(user.Scores, array) + }) +} + func Test_Struct_To_Struct(t *testing.T) { var TestA struct { Id int `p:"id"` diff --git a/util/gconv/gconv_z_unit_struct_slice_test.go b/util/gconv/gconv_z_unit_structs_test.go similarity index 58% rename from util/gconv/gconv_z_unit_struct_slice_test.go rename to util/gconv/gconv_z_unit_structs_test.go index 733a4265b..6279b002d 100644 --- a/util/gconv/gconv_z_unit_struct_slice_test.go +++ b/util/gconv/gconv_z_unit_structs_test.go @@ -14,90 +14,7 @@ import ( "github.com/gogf/gf/util/gconv" ) -func Test_Struct_Slice(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - type User struct { - Scores []int - } - user := new(User) - array := g.Slice{1, 2, 3} - err := gconv.Struct(g.Map{"scores": array}, user) - t.Assert(err, nil) - t.Assert(user.Scores, array) - }) - gtest.C(t, func(t *gtest.T) { - type User struct { - Scores []int32 - } - user := new(User) - array := g.Slice{1, 2, 3} - err := gconv.Struct(g.Map{"scores": array}, user) - t.Assert(err, nil) - t.Assert(user.Scores, array) - }) - gtest.C(t, func(t *gtest.T) { - type User struct { - Scores []int64 - } - user := new(User) - array := g.Slice{1, 2, 3} - err := gconv.Struct(g.Map{"scores": array}, user) - t.Assert(err, nil) - t.Assert(user.Scores, array) - }) - gtest.C(t, func(t *gtest.T) { - type User struct { - Scores []uint - } - user := new(User) - array := g.Slice{1, 2, 3} - err := gconv.Struct(g.Map{"scores": array}, user) - t.Assert(err, nil) - t.Assert(user.Scores, array) - }) - gtest.C(t, func(t *gtest.T) { - type User struct { - Scores []uint32 - } - user := new(User) - array := g.Slice{1, 2, 3} - err := gconv.Struct(g.Map{"scores": array}, user) - t.Assert(err, nil) - t.Assert(user.Scores, array) - }) - gtest.C(t, func(t *gtest.T) { - type User struct { - Scores []uint64 - } - user := new(User) - array := g.Slice{1, 2, 3} - err := gconv.Struct(g.Map{"scores": array}, user) - t.Assert(err, nil) - t.Assert(user.Scores, array) - }) - gtest.C(t, func(t *gtest.T) { - type User struct { - Scores []float32 - } - user := new(User) - array := g.Slice{1, 2, 3} - err := gconv.Struct(g.Map{"scores": array}, user) - t.Assert(err, nil) - t.Assert(user.Scores, array) - }) - gtest.C(t, func(t *gtest.T) { - type User struct { - Scores []float64 - } - user := new(User) - array := g.Slice{1, 2, 3} - err := gconv.Struct(g.Map{"scores": array}, user) - t.Assert(err, nil) - t.Assert(user.Scores, array) - }) -} - -func Test_Struct_SliceWithTag(t *testing.T) { +func Test_Structs_WithTag(t *testing.T) { type User struct { Uid int `json:"id"` NickName string `json:"name"` @@ -144,6 +61,97 @@ func Test_Struct_SliceWithTag(t *testing.T) { }) } +func Test_Structs_WithoutTag(t *testing.T) { + type User struct { + Uid int + NickName string + } + gtest.C(t, func(t *gtest.T) { + var users []User + params := g.Slice{ + g.Map{ + "uid": 1, + "nick-name": "name1", + }, + g.Map{ + "uid": 2, + "nick-name": "name2", + }, + } + err := gconv.Structs(params, &users) + t.Assert(err, nil) + t.Assert(len(users), 2) + t.Assert(users[0].Uid, 1) + t.Assert(users[0].NickName, "name1") + t.Assert(users[1].Uid, 2) + t.Assert(users[1].NickName, "name2") + }) + gtest.C(t, func(t *gtest.T) { + var users []*User + params := g.Slice{ + g.Map{ + "uid": 1, + "nick-name": "name1", + }, + g.Map{ + "uid": 2, + "nick-name": "name2", + }, + } + err := gconv.Structs(params, &users) + t.Assert(err, nil) + t.Assert(len(users), 2) + t.Assert(users[0].Uid, 1) + t.Assert(users[0].NickName, "name1") + t.Assert(users[1].Uid, 2) + t.Assert(users[1].NickName, "name2") + }) +} + +func Test_Structs_SliceParameter(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type User struct { + Uid int + NickName string + } + var users []User + params := g.Slice{ + g.Map{ + "uid": 1, + "nick-name": "name1", + }, + g.Map{ + "uid": 2, + "nick-name": "name2", + }, + } + err := gconv.Structs(params, users) + t.AssertNE(err, nil) + }) + gtest.C(t, func(t *gtest.T) { + type User struct { + Uid int + NickName string + } + type A struct { + Users []User + } + var a A + params := g.Slice{ + g.Map{ + "uid": 1, + "nick-name": "name1", + }, + g.Map{ + "uid": 2, + "nick-name": "name2", + }, + } + err := gconv.Structs(params, a.Users) + t.AssertNE(err, nil) + }) +} + func Test_Structs_DirectReflectSet(t *testing.T) { type A struct { Id int @@ -175,7 +183,7 @@ func Test_Structs_DirectReflectSet(t *testing.T) { }) } -func Test_Structs_SliceIntAttribute(t *testing.T) { +func Test_Structs_IntSliceAttribute(t *testing.T) { type A struct { Id []int } diff --git a/util/gconv/gconv_z_unit_time_test.go b/util/gconv/gconv_z_unit_time_test.go index 1d41b04cd..99d3e0115 100644 --- a/util/gconv/gconv_z_unit_time_test.go +++ b/util/gconv/gconv_z_unit_time_test.go @@ -55,7 +55,7 @@ func Test_Time_Slice_Attribute(t *testing.T) { "arr": g.Slice{"2021-01-12 12:34:56", "2021-01-12 12:34:57"}, "one": "2021-01-12 12:34:58", }, &s) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(s.One, "2021-01-12 12:34:58") t.Assert(s.Arr[0], "2021-01-12 12:34:56") t.Assert(s.Arr[1], "2021-01-12 12:34:57") From 5adde275fc2834e3264c194ab0d6f14f34cf2019 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 14 Feb 2021 22:00:56 +0800 Subject: [PATCH 157/492] add switch variable for internal type tracing components --- database/gdb/gdb_core_tracing.go | 13 ++++++++++++- database/gredis/gredis_conn.go | 15 ++++++--------- database/gredis/gredis_conn_tracing.go | 19 ++++++++++++++++++- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index 08209931c..678ad8558 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -12,6 +12,7 @@ import ( "fmt" "github.com/gogf/gf" "github.com/gogf/gf/net/gtrace" + "github.com/gogf/gf/os/gcmd" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/label" @@ -33,9 +34,19 @@ const ( tracingEventDbExecutionType = "db.execution.type" ) +var ( + // tracingInternal enables tracing for internal type spans. + // It's true in default. + tracingInternal = true +) + +func init() { + tracingInternal = gcmd.GetOptWithEnv("gf.tracing.internal", true).Bool() +} + // addSqlToTracing adds sql information to tracer if it's enabled. func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { - if !gtrace.IsActivated(ctx) { + if !tracingInternal || !gtrace.IsActivated(ctx) { return } tr := otel.GetTracerProvider().Tracer( diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index 26cc1ad6a..f33d4528e 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -11,7 +11,6 @@ import ( "errors" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/json" - "github.com/gogf/gf/net/gtrace" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" "github.com/gomodule/redigo/redis" @@ -60,14 +59,12 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} timestampMilli2 := gtime.TimestampMilli() // Tracing. - if gtrace.IsActivated(c.ctx) { - c.addTracingItem(&tracingItem{ - err: err, - commandName: commandName, - arguments: args, - costMilli: timestampMilli2 - timestampMilli1, - }) - } + c.addTracingItem(&tracingItem{ + err: err, + commandName: commandName, + arguments: args, + costMilli: timestampMilli2 - timestampMilli1, + }) return } diff --git a/database/gredis/gredis_conn_tracing.go b/database/gredis/gredis_conn_tracing.go index 1e6990839..2e4c794fa 100644 --- a/database/gredis/gredis_conn_tracing.go +++ b/database/gredis/gredis_conn_tracing.go @@ -12,6 +12,7 @@ import ( "github.com/gogf/gf" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/net/gtrace" + "github.com/gogf/gf/os/gcmd" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/label" @@ -37,9 +38,25 @@ const ( tracingEventRedisExecutionArguments = "redis.execution.arguments" ) +var ( + // tracingInternal enables tracing for internal type spans. + // It's true in default. + tracingInternal = true +) + +func init() { + tracingInternal = gcmd.GetOptWithEnv("gf.tracing.internal", true).Bool() +} + // addTracingItem checks and adds redis tracing information to OpenTelemetry. func (c *Conn) addTracingItem(item *tracingItem) { - tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) + if !tracingInternal || !gtrace.IsActivated(c.ctx) { + return + } + tr := otel.GetTracerProvider().Tracer( + tracingInstrumentName, + trace.WithInstrumentationVersion(gf.VERSION), + ) ctx := c.ctx if ctx == nil { ctx = context.Background() From 285ad36e7dd833621ffd2c964b414261317f1218 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 21 Feb 2021 22:24:51 +0800 Subject: [PATCH 158/492] add short datetime string parsing support for package gtime --- os/gtime/gtime.go | 2 +- os/gtime/gtime_z_unit_time_test.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index 3540aa465..13b341d36 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -45,7 +45,7 @@ const ( // "2018/10/31 - 16:38:46" // "2018-02-09", // "2018.02.09", - timeRegexPattern1 = `(\d{4}[-/\.]\d{2}[-/\.]\d{2})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` + timeRegexPattern1 = `(\d{4}[-/\.]\d{1,2}[-/\.]\d{1,2})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)` // Regular expression2(datetime separator supports '-', '/', '.'). // Eg: diff --git a/os/gtime/gtime_z_unit_time_test.go b/os/gtime/gtime_z_unit_time_test.go index 2f3ca95ec..9dbd6f97a 100644 --- a/os/gtime/gtime_z_unit_time_test.go +++ b/os/gtime/gtime_z_unit_time_test.go @@ -42,6 +42,12 @@ func Test_New(t *testing.T) { timeTemp := gtime.New(timeNow.TimestampMicro()) t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), timeNow.Time.Format("2006-01-02 15:04:05")) }) + // short datetime. + gtest.C(t, func(t *gtest.T) { + timeTemp := gtime.New("2021-2-9 08:01:21") + t.Assert(timeTemp.Format("Y-m-d H:i:s"), "2021-02-09 08:01:21") + t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2021-02-09 08:01:21") + }) } func Test_Nil(t *testing.T) { From 54b629561e580443c2cb23bc5d5ffbccb166053b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=AD=E5=B4=87=E5=B2=B3?= Date: Mon, 22 Feb 2021 11:20:10 +0800 Subject: [PATCH 159/492] style: code style --- os/genv/genv.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/genv/genv.go b/os/genv/genv.go index 0dd2a0a52..0b0e6a8e4 100644 --- a/os/genv/genv.go +++ b/os/genv/genv.go @@ -11,8 +11,8 @@ import ( "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/os/gcmd" "os" + "strings" ) -import "strings" // All returns a copy of strings representing the environment, // in the form "key=value". From 2c34d96b9df5f23fa490d99b8b1bd94c75e275a5 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 23 Feb 2021 22:00:11 +0800 Subject: [PATCH 160/492] add tls configuration for ghttp.Client --- container/gvar/gvar_z_unit_basic_test.go | 55 +++++++++++++++++++-- net/ghttp/internal/client/client.go | 55 +++++++++++++++++++-- net/ghttp/internal/client/client_request.go | 4 +- 3 files changed, 103 insertions(+), 11 deletions(-) diff --git a/container/gvar/gvar_z_unit_basic_test.go b/container/gvar/gvar_z_unit_basic_test.go index d46ab188f..fd20aeeb5 100644 --- a/container/gvar/gvar_z_unit_basic_test.go +++ b/container/gvar/gvar_z_unit_basic_test.go @@ -245,12 +245,57 @@ func Test_Duration(t *testing.T) { }) } -func Test_UnmarshalValue(t *testing.T) { - type V struct { - Name string - Var *gvar.Var - } +func Test_UnmarshalJson(t *testing.T) { gtest.C(t, func(t *gtest.T) { + type V struct { + Name string + Var *gvar.Var + } + var v *V + err := gconv.Struct(map[string]interface{}{ + "name": "john", + "var": "v", + }, &v) + t.Assert(err, nil) + t.Assert(v.Name, "john") + t.Assert(v.Var.String(), "v") + }) + gtest.C(t, func(t *gtest.T) { + type V struct { + Name string + Var gvar.Var + } + var v *V + err := gconv.Struct(map[string]interface{}{ + "name": "john", + "var": "v", + }, &v) + t.Assert(err, nil) + t.Assert(v.Name, "john") + t.Assert(v.Var.String(), "v") + }) +} + +func Test_UnmarshalValue(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type V struct { + Name string + Var *gvar.Var + } + var v *V + err := gconv.Struct(map[string]interface{}{ + "name": "john", + "var": "v", + }, &v) + t.Assert(err, nil) + t.Assert(v.Name, "john") + t.Assert(v.Var.String(), "v") + }) + gtest.C(t, func(t *gtest.T) { + type V struct { + Name string + Var gvar.Var + } var v *V err := gconv.Struct(map[string]interface{}{ "name": "john", diff --git a/net/ghttp/internal/client/client.go b/net/ghttp/internal/client/client.go index 6c2b292b1..7e6366e2d 100644 --- a/net/ghttp/internal/client/client.go +++ b/net/ghttp/internal/client/client.go @@ -8,9 +8,12 @@ package client import ( "context" + "crypto/rand" "crypto/tls" "fmt" "github.com/gogf/gf" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/text/gstr" "golang.org/x/net/proxy" "net" @@ -198,8 +201,8 @@ func (c *Client) SetProxy(proxyURL string) { return } if _proxy.Scheme == "http" { - if _, ok := c.Transport.(*http.Transport); ok { - c.Transport.(*http.Transport).Proxy = http.ProxyURL(_proxy) + if v, ok := c.Transport.(*http.Transport); ok { + v.Proxy = http.ProxyURL(_proxy) } } else { var auth = &proxy.Auth{} @@ -227,11 +230,55 @@ func (c *Client) SetProxy(proxyURL string) { if err != nil { return } - if _, ok := c.Transport.(*http.Transport); ok { - c.Transport.(*http.Transport).DialContext = func(ctx context.Context, network, addr string) (conn net.Conn, e error) { + if v, ok := c.Transport.(*http.Transport); ok { + v.DialContext = func(ctx context.Context, network, addr string) (conn net.Conn, e error) { return dialer.Dial(network, addr) } } //c.SetTimeout(10*time.Second) } } + +// SetTlsKeyCrt sets the certificate and key file for TLS configuration of client. +func (c *Client) SetTLSKeyCrt(crtFile, keyFile string) error { + tlsConfig, err := LoadKeyCrt(crtFile, keyFile) + if err != nil { + return err + } + if v, ok := c.Transport.(*http.Transport); ok { + tlsConfig.InsecureSkipVerify = true + v.TLSClientConfig = tlsConfig + return nil + } + return gerror.New(`cannot set TLSClientConfig for custom Transport of the client`) +} + +// SetTlsConfig sets the TLS configuration of client. +func (c *Client) SetTLSConfig(tlsConfig *tls.Config) error { + if v, ok := c.Transport.(*http.Transport); ok { + v.TLSClientConfig = tlsConfig + return nil + } + return gerror.New(`cannot set TLSClientConfig for custom Transport of the client`) +} + +// LoadKeyCrt creates and returns a TLS configuration object with given certificate and key files. +func LoadKeyCrt(crtFile, keyFile string) (*tls.Config, error) { + crtPath, err := gfile.Search(crtFile) + if err != nil { + return nil, err + } + keyPath, err := gfile.Search(keyFile) + if err != nil { + return nil, err + } + crt, err := tls.LoadX509KeyPair(crtPath, keyPath) + if err != nil { + return nil, err + } + tlsConfig := &tls.Config{} + tlsConfig.Certificates = []tls.Certificate{crt} + tlsConfig.Time = time.Now + tlsConfig.Rand = rand.Reader + return tlsConfig, nil +} diff --git a/net/ghttp/internal/client/client_request.go b/net/ghttp/internal/client/client_request.go index e12ddf501..cc8da7504 100644 --- a/net/ghttp/internal/client/client_request.go +++ b/net/ghttp/internal/client/client_request.go @@ -258,8 +258,8 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h } } // It's necessary set the req.Host if you want to custom the host value of the request. - // It uses the "Host" value from header if it's not set in the request. - if host := req.Header.Get("Host"); host != "" && req.Host == "" { + // It uses the "Host" value from header if it's not empty. + if host := req.Header.Get("Host"); host != "" { req.Host = host } // Custom Cookie. From 0238cdd5ec6e8ddc3eb4b7139d03881f1ac82d2a Mon Sep 17 00:00:00 2001 From: develop1024 <656562721@qq.com> Date: Tue, 23 Feb 2021 23:07:28 +0800 Subject: [PATCH 161/492] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E9=94=99=E8=AF=AF-os/gbuild/GetVar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/gbuild/gbuild.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/gbuild/gbuild.go b/os/gbuild/gbuild.go index 19b9e1fe5..315b1d6d6 100644 --- a/os/gbuild/gbuild.go +++ b/os/gbuild/gbuild.go @@ -59,7 +59,7 @@ func Get(name string, def ...interface{}) interface{} { return nil } -// Get retrieves and returns the build-in binary variable of given name as gvar.Var. +// GetVar retrieves and returns the build-in binary variable of given name as gvar.Var. func GetVar(name string, def ...interface{}) *gvar.Var { return gvar.New(Get(name, def...)) } From 5db4bbc186db31a15ffd5c4be3b843fd2c7cb256 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 24 Feb 2021 01:07:09 +0800 Subject: [PATCH 162/492] fix issue 1162 --- net/ghttp/internal/client/client_response.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/ghttp/internal/client/client_response.go b/net/ghttp/internal/client/client_response.go index 2cf06cddd..324115ebf 100644 --- a/net/ghttp/internal/client/client_response.go +++ b/net/ghttp/internal/client/client_response.go @@ -25,8 +25,11 @@ type Response struct { func (r *Response) initCookie() { if r.cookies == nil { r.cookies = make(map[string]string) - for _, v := range r.Cookies() { - r.cookies[v.Name] = v.Value + // Response might be nil. + if r != nil && r.Response != nil { + for _, v := range r.Cookies() { + r.cookies[v.Name] = v.Value + } } } } From d330afdd36568ed69d146e2f4bb1de4e48aebe3a Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 24 Feb 2021 01:20:06 +0800 Subject: [PATCH 163/492] add required* rules checks for map/slice --- util/gvalid/gvalid_validator_check.go | 2 +- util/gvalid/gvalid_validator_rule_required.go | 27 +++++++++++++------ util/gvalid/gvalid_z_example_test.go | 8 +++--- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index 035276256..674f5d761 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -158,7 +158,7 @@ func (v *Validator) doCheckBuildInRules( "required-with-all", "required-without", "required-without-all": - match = v.checkRequired(valueStr, ruleKey, rulePattern, dataMap) + match = v.checkRequired(value, ruleKey, rulePattern, dataMap) // Length rules. // It also supports length of unicode string. diff --git a/util/gvalid/gvalid_validator_rule_required.go b/util/gvalid/gvalid_validator_rule_required.go index 0578e3212..162c97a57 100644 --- a/util/gvalid/gvalid_validator_rule_required.go +++ b/util/gvalid/gvalid_validator_rule_required.go @@ -7,11 +7,14 @@ package gvalid import ( + "github.com/gogf/gf/util/gconv" + "reflect" "strings" ) // checkRequired checks using required rules. -func (v *Validator) checkRequired(value, ruleKey, ruleVal string, params map[string]string) bool { +// It also supports require checks for `value` of type: slice, map. +func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string, params map[string]string) bool { required := false switch ruleKey { // Required. @@ -22,7 +25,7 @@ func (v *Validator) checkRequired(value, ruleKey, ruleVal string, params map[str // Example: required-if: id,1,age,18 case "required-if": required = false - array := strings.Split(ruleVal, ",") + array := strings.Split(rulePattern, ",") // It supports multiple field and value pairs. if len(array)%2 == 0 { for i := 0; i < len(array); { @@ -42,7 +45,7 @@ func (v *Validator) checkRequired(value, ruleKey, ruleVal string, params map[str // Example: required-unless: id,1,age,18 case "required-unless": required = true - array := strings.Split(ruleVal, ",") + array := strings.Split(rulePattern, ",") // It supports multiple field and value pairs. if len(array)%2 == 0 { for i := 0; i < len(array); { @@ -62,7 +65,7 @@ func (v *Validator) checkRequired(value, ruleKey, ruleVal string, params map[str // Example: required-with:id,name case "required-with": required = false - array := strings.Split(ruleVal, ",") + array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { if params[array[i]] != "" { required = true @@ -74,7 +77,7 @@ func (v *Validator) checkRequired(value, ruleKey, ruleVal string, params map[str // Example: required-with:id,name case "required-with-all": required = true - array := strings.Split(ruleVal, ",") + array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { if params[array[i]] == "" { required = false @@ -86,7 +89,7 @@ func (v *Validator) checkRequired(value, ruleKey, ruleVal string, params map[str // Example: required-with:id,name case "required-without": required = false - array := strings.Split(ruleVal, ",") + array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { if params[array[i]] == "" { required = true @@ -98,7 +101,7 @@ func (v *Validator) checkRequired(value, ruleKey, ruleVal string, params map[str // Example: required-with:id,name case "required-without-all": required = true - array := strings.Split(ruleVal, ",") + array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { if params[array[i]] != "" { required = false @@ -107,7 +110,15 @@ func (v *Validator) checkRequired(value, ruleKey, ruleVal string, params map[str } } if required { - return !(value == "") + reflectValue := reflect.ValueOf(value) + for reflectValue.Kind() == reflect.Ptr { + reflectValue = reflectValue.Elem() + } + switch reflectValue.Kind() { + case reflect.String, reflect.Map, reflect.Array, reflect.Slice: + return reflectValue.Len() != 0 + } + return gconv.String(value) != "" } else { return true } diff --git a/util/gvalid/gvalid_z_example_test.go b/util/gvalid/gvalid_z_example_test.go index 4411345b7..2fa6cedfa 100644 --- a/util/gvalid/gvalid_z_example_test.go +++ b/util/gvalid/gvalid_z_example_test.go @@ -173,13 +173,13 @@ func ExampleRegisterRule_OverwriteRequired() { return nil }) fmt.Println(gvalid.Check("", "required", "It's required")) - fmt.Println(gvalid.Check([]string{}, "required", "It's required")) - fmt.Println(gvalid.Check(map[string]int{}, "required", "It's required")) + fmt.Println(gvalid.Check(0, "required", "It's required")) + fmt.Println(gvalid.Check(false, "required", "It's required")) gvalid.DeleteRule(rule) fmt.Println("rule deleted") fmt.Println(gvalid.Check("", "required", "It's required")) - fmt.Println(gvalid.Check([]string{}, "required", "It's required")) - fmt.Println(gvalid.Check(map[string]int{}, "required", "It's required")) + fmt.Println(gvalid.Check(0, "required", "It's required")) + fmt.Println(gvalid.Check(false, "required", "It's required")) // Output: // It's required // It's required From eb6763b0fd928255ec17c84f0e4a5b2f9db73df1 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Fri, 26 Feb 2021 13:57:47 +0800 Subject: [PATCH 164/492] fix issue of failing configuration for default configuration file for package gcfg --- os/gcfg/gcfg.go | 2 +- os/gcfg/gcfg_instance.go | 13 +++++++++---- os/gcfg/gcfg_z_unit_instance_test.go | 18 +++++++++++++++--- os/gcfg/testdata/default/config.json | 1 + os/gcfg/testdata/default/config.toml | 1 + 5 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 os/gcfg/testdata/default/config.json create mode 100644 os/gcfg/testdata/default/config.toml diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index b994a31cf..1256a6fa3 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -40,7 +40,7 @@ type Config struct { } var ( - supportedFileTypes = []string{"toml", "yaml", "json", "ini", "xml"} + supportedFileTypes = []string{"toml", "yaml", "yml", "json", "ini", "xml"} resourceTryFiles = []string{"", "/", "config/", "config", "/config", "/config/"} ) diff --git a/os/gcfg/gcfg_instance.go b/os/gcfg/gcfg_instance.go index 84999034f..9eeb07905 100644 --- a/os/gcfg/gcfg_instance.go +++ b/os/gcfg/gcfg_instance.go @@ -32,10 +32,15 @@ func Instance(name ...string) *Config { } return instances.GetOrSetFuncLock(key, func() interface{} { c := New() - for _, fileType := range supportedFileTypes { - if file := fmt.Sprintf(`%s.%s`, key, fileType); c.Available(file) { - c.SetFileName(file) - break + // If it's not using default configuration or its configuration file is not available, + // it searches the possible configuration file according to the name and all supported + // file types. + if key != DefaultName || !c.Available() { + for _, fileType := range supportedFileTypes { + if file := fmt.Sprintf(`%s.%s`, key, fileType); c.Available(file) { + c.SetFileName(file) + break + } } } return c diff --git a/os/gcfg/gcfg_z_unit_instance_test.go b/os/gcfg/gcfg_z_unit_instance_test.go index 9043bf434..2cc5e3ab1 100644 --- a/os/gcfg/gcfg_z_unit_instance_test.go +++ b/os/gcfg/gcfg_z_unit_instance_test.go @@ -78,7 +78,6 @@ v4 = "1.234" "cache": "127.0.0.1:6379,1", }) t.AssertEQ(c.FilePath(), gfile.Pwd()+gfile.Separator+path) - }) } @@ -89,7 +88,7 @@ func Test_Instance_AutoLocateConfigFile(t *testing.T) { // Automatically locate the configuration file with supported file extensions. gtest.C(t, func(t *gtest.T) { pwd := gfile.Pwd() - gfile.Chdir(gdebug.TestDataPath()) + t.AssertNil(gfile.Chdir(gdebug.TestDataPath())) defer gfile.Chdir(pwd) t.Assert(Instance("c1") != nil, true) t.Assert(Instance("c1").Get("my-config"), "1") @@ -98,10 +97,23 @@ func Test_Instance_AutoLocateConfigFile(t *testing.T) { // Automatically locate the configuration file with supported file extensions. gtest.C(t, func(t *gtest.T) { pwd := gfile.Pwd() - gfile.Chdir(gdebug.TestDataPath("folder1")) + t.AssertNil(gfile.Chdir(gdebug.TestDataPath("folder1"))) defer gfile.Chdir(pwd) t.Assert(Instance("c2").Get("my-config"), 2) }) + // Default configuration file. + gtest.C(t, func(t *gtest.T) { + instances.Clear() + pwd := gfile.Pwd() + t.AssertNil(gfile.Chdir(gdebug.TestDataPath("default"))) + defer gfile.Chdir(pwd) + t.Assert(Instance().Get("my-config"), 1) + + instances.Clear() + t.AssertNil(genv.Set("GF_GCFG_FILE", "config.json")) + defer genv.Set("GF_GCFG_FILE", "") + t.Assert(Instance().Get("my-config"), 2) + }) } func Test_Instance_EnvPath(t *testing.T) { diff --git a/os/gcfg/testdata/default/config.json b/os/gcfg/testdata/default/config.json new file mode 100644 index 000000000..ea5047901 --- /dev/null +++ b/os/gcfg/testdata/default/config.json @@ -0,0 +1 @@ +{"my-config": 2} \ No newline at end of file diff --git a/os/gcfg/testdata/default/config.toml b/os/gcfg/testdata/default/config.toml new file mode 100644 index 000000000..ec4ea12a8 --- /dev/null +++ b/os/gcfg/testdata/default/config.toml @@ -0,0 +1 @@ +my-config = "1" \ No newline at end of file From d2bd37962eddcd4298e4c2b1f46c2967bf53fd3e Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 27 Feb 2021 23:58:36 +0800 Subject: [PATCH 165/492] add unit testing case of UnmarshalValue for struct converting of querying result; improve package gdb --- database/gdb/gdb_model.go | 4 +- database/gdb/gdb_schema.go | 4 +- database/gdb/gdb_z_driver_test.go | 4 +- database/gdb/gdb_z_init_test.go | 41 +- database/gdb/gdb_z_mysql_association_test.go | 117 ++- database/gdb/gdb_z_mysql_internal_test.go | 30 +- database/gdb/gdb_z_mysql_method_test.go | 76 +- database/gdb/gdb_z_mysql_model_test.go | 710 +++++++++--------- database/gdb/gdb_z_mysql_raw_test.go | 8 +- database/gdb/gdb_z_mysql_struct_test.go | 101 ++- .../gdb/gdb_z_mysql_time_maintain_test.go | 164 ++-- database/gdb/gdb_z_mysql_transaction_test.go | 42 +- database/gdb/gdb_z_mysql_types_test.go | 6 +- 13 files changed, 676 insertions(+), 631 deletions(-) diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 04f0bbd25..4b109534f 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -183,9 +183,9 @@ func (m *Model) Schema(schema string) *Model { func (m *Model) Clone() *Model { newModel := (*Model)(nil) if m.tx != nil { - newModel = m.tx.Table(m.tablesInit) + newModel = m.tx.Model(m.tablesInit) } else { - newModel = m.db.Table(m.tablesInit) + newModel = m.db.Model(m.tablesInit) } *newModel = *m // Shallow copy slice attributes. diff --git a/database/gdb/gdb_schema.go b/database/gdb/gdb_schema.go index c7f5fb2a7..7794eada4 100644 --- a/database/gdb/gdb_schema.go +++ b/database/gdb/gdb_schema.go @@ -36,9 +36,9 @@ func (tx *TX) Schema(schema string) *Schema { func (s *Schema) Table(table string) *Model { var m *Model if s.tx != nil { - m = s.tx.Table(table) + m = s.tx.Model(table) } else { - m = s.db.Table(table) + m = s.db.Model(table) } // Do not change the schema of the original db, // it here creates a new db and changes its schema. diff --git a/database/gdb/gdb_z_driver_test.go b/database/gdb/gdb_z_driver_test.go index 3d2d10154..80686d203 100644 --- a/database/gdb/gdb_z_driver_test.go +++ b/database/gdb/gdb_z_driver_test.go @@ -56,8 +56,8 @@ func Test_Custom_Driver(t *testing.T) { gdb.AddConfigNode("driver-test", gdb.ConfigNode{ Host: "127.0.0.1", Port: "3306", - User: USER, - Pass: PASS, + User: TestDbUser, + Pass: TestDbPass, Name: "test", Type: customDriverName, Role: "master", diff --git a/database/gdb/gdb_z_init_test.go b/database/gdb/gdb_z_init_test.go index 6208c9577..fe9fcc47a 100644 --- a/database/gdb/gdb_z_init_test.go +++ b/database/gdb/gdb_z_init_test.go @@ -18,13 +18,14 @@ import ( ) const ( - SIZE = 10 - TABLE = "user" - SCHEMA1 = "test1" - SCHEMA2 = "test2" - PREFIX1 = "gf_" - USER = "root" - PASS = "12345678" + TableSize = 10 + TableName = "user" + TestSchema1 = "test1" + TestSchema2 = "test2" + TableNamePrefix1 = "gf_" + TestDbUser = "root" + TestDbPass = "12345678" + CreateTime = "2018-10-24 10:00:00" ) var ( @@ -42,8 +43,8 @@ func init() { configNode = gdb.ConfigNode{ Host: "127.0.0.1", Port: "3306", - User: USER, - Pass: PASS, + User: TestDbUser, + Pass: TestDbPass, Name: parser.GetOpt("name", ""), Type: parser.GetOpt("type", "mysql"), Role: "master", @@ -54,7 +55,7 @@ func init() { MaxConnLifetime: 600, } nodePrefix := configNode - nodePrefix.Prefix = PREFIX1 + nodePrefix.Prefix = TableNamePrefix1 gdb.AddConfigNode("test", configNode) gdb.AddConfigNode("prefix", nodePrefix) gdb.AddConfigNode(gdb.DefaultGroupName, configNode) @@ -65,13 +66,13 @@ func init() { db = r } schemaTemplate := "CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET UTF8" - if _, err := db.Exec(fmt.Sprintf(schemaTemplate, SCHEMA1)); err != nil { + if _, err := db.Exec(fmt.Sprintf(schemaTemplate, TestSchema1)); err != nil { gtest.Error(err) } - if _, err := db.Exec(fmt.Sprintf(schemaTemplate, SCHEMA2)); err != nil { + if _, err := db.Exec(fmt.Sprintf(schemaTemplate, TestSchema2)); err != nil { gtest.Error(err) } - db.SetSchema(SCHEMA1) + db.SetSchema(TestSchema1) // Prefix db. if r, err := gdb.New("prefix"); err != nil { @@ -79,13 +80,13 @@ func init() { } else { dbPrefix = r } - if _, err := dbPrefix.Exec(fmt.Sprintf(schemaTemplate, SCHEMA1)); err != nil { + if _, err := dbPrefix.Exec(fmt.Sprintf(schemaTemplate, TestSchema1)); err != nil { gtest.Error(err) } - if _, err := dbPrefix.Exec(fmt.Sprintf(schemaTemplate, SCHEMA2)); err != nil { + if _, err := dbPrefix.Exec(fmt.Sprintf(schemaTemplate, TestSchema2)); err != nil { gtest.Error(err) } - dbPrefix.SetSchema(SCHEMA1) + dbPrefix.SetSchema(TestSchema1) } func createTable(table ...string) string { @@ -104,7 +105,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) { if len(table) > 0 { name = table[0] } else { - name = fmt.Sprintf(`%s_%d`, TABLE, gtime.TimestampNano()) + name = fmt.Sprintf(`%s_%d`, TableName, gtime.TimestampNano()) } dropTableWithDb(db, name) @@ -184,13 +185,13 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) { func createInitTableWithDb(db gdb.DB, table ...string) (name string) { name = createTableWithDb(db, table...) array := garray.New(true) - for i := 1; i <= SIZE; i++ { + for i := 1; i <= TableSize; i++ { array.Append(g.Map{ "id": i, "passport": fmt.Sprintf(`user_%d`, i), "password": fmt.Sprintf(`pass_%d`, i), "nickname": fmt.Sprintf(`name_%d`, i), - "create_time": gtime.NewFromStr("2018-10-24 10:00:00").String(), + "create_time": gtime.NewFromStr(CreateTime).String(), }) } @@ -199,7 +200,7 @@ func createInitTableWithDb(db gdb.DB, table ...string) (name string) { n, e := result.RowsAffected() gtest.Assert(e, nil) - gtest.Assert(n, SIZE) + gtest.Assert(n, TableSize) return } diff --git a/database/gdb/gdb_z_mysql_association_test.go b/database/gdb/gdb_z_mysql_association_test.go index 4dbbf7db3..b853a8662 100644 --- a/database/gdb/gdb_z_mysql_association_test.go +++ b/database/gdb/gdb_z_mysql_association_test.go @@ -112,19 +112,19 @@ CREATE TABLE %s ( }) // Data check. gtest.C(t, func(t *gtest.T) { - r, err := db.Table(tableUser).All() + r, err := db.Model(tableUser).All() t.AssertNil(err) t.Assert(r.Len(), 1) t.Assert(r[0]["uid"].Int(), 1) t.Assert(r[0]["name"].String(), "john") - r, err = db.Table(tableUserDetail).Where("uid", r[0]["uid"].Int()).All() + r, err = db.Model(tableUserDetail).Where("uid", r[0]["uid"].Int()).All() t.AssertNil(err) t.Assert(r.Len(), 1) t.Assert(r[0]["uid"].Int(), 1) t.Assert(r[0]["address"].String(), `Beijing DongZhiMen #66`) - r, err = db.Table(tableUserScores).Where("uid", r[0]["uid"].Int()).All() + r, err = db.Model(tableUserScores).Where("uid", r[0]["uid"].Int()).All() t.AssertNil(err) t.Assert(r.Len(), 2) t.Assert(r[0]["uid"].Int(), 1) @@ -136,15 +136,15 @@ CREATE TABLE %s ( gtest.C(t, func(t *gtest.T) { var user Entity // SELECT * FROM `user` WHERE `name`='john' - err := db.Table(tableUser).Scan(&user.User, "name", "john") + err := db.Model(tableUser).Scan(&user.User, "name", "john") t.AssertNil(err) // SELECT * FROM `user_detail` WHERE `uid`=1 - err = db.Table(tableUserDetail).Scan(&user.UserDetail, "uid", user.User.Uid) + err = db.Model(tableUserDetail).Scan(&user.UserDetail, "uid", user.User.Uid) t.AssertNil(err) // SELECT * FROM `user_scores` WHERE `uid`=1 - err = db.Table(tableUserScores).Scan(&user.UserScores, "uid", user.User.Uid) + err = db.Model(tableUserScores).Scan(&user.UserScores, "uid", user.User.Uid) t.AssertNil(err) t.Assert(user.User, EntityUser{ @@ -250,13 +250,13 @@ CREATE TABLE %s ( // MapKeyValue. gtest.C(t, func(t *gtest.T) { - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) t.Assert(all.Len(), 2) t.Assert(len(all.MapKeyValue("uid")), 2) t.Assert(all.MapKeyValue("uid")["3"].Map()["uid"], 3) t.Assert(all.MapKeyValue("uid")["4"].Map()["uid"], 4) - all, err = db.Table(tableUserScores).Where("uid", g.Slice{3, 4}).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", g.Slice{3, 4}).Order("id asc").All() t.AssertNil(err) t.Assert(all.Len(), 10) t.Assert(len(all.MapKeyValue("uid")), 2) @@ -271,7 +271,7 @@ CREATE TABLE %s ( gtest.C(t, func(t *gtest.T) { var users []Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) @@ -279,14 +279,14 @@ CREATE TABLE %s ( t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "uid:Uid") t.AssertNil(err) @@ -304,7 +304,7 @@ CREATE TABLE %s ( gtest.C(t, func(t *gtest.T) { var users []*Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) @@ -312,14 +312,14 @@ CREATE TABLE %s ( t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "uid:Uid") t.AssertNil(err) @@ -355,7 +355,7 @@ CREATE TABLE %s ( } var users []Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) @@ -363,14 +363,14 @@ CREATE TABLE %s ( t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "uid:Uid") t.AssertNil(err) @@ -407,7 +407,7 @@ CREATE TABLE %s ( var users []*Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) @@ -415,14 +415,14 @@ CREATE TABLE %s ( t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "uid:Uid") t.AssertNil(err) @@ -440,19 +440,19 @@ CREATE TABLE %s ( gtest.C(t, func(t *gtest.T) { var users []*Entity // User - err := db.Table(tableUser). + err := db.Model(tableUser). Where("uid", g.Slice{3, 4}). Order("uid asc"). ScanList(&users, "User") t.AssertNil(err) // Detail - err = db.Table(tableUserDetail). + err = db.Model(tableUserDetail). Where("uid", gdb.ListItemValues(users, "User", "Uid")). Order("uid asc"). ScanList(&users, "UserDetail", "User", "uid:Uid") t.AssertNil(err) // Scores - err = db.Table(tableUserScores). + err = db.Model(tableUserScores). Where("uid", gdb.ListItemValues(users, "User", "Uid")). Order("id asc"). ScanList(&users, "UserScores", "User", "uid:Uid") @@ -564,13 +564,13 @@ CREATE TABLE %s ( // MapKeyValue. gtest.C(t, func(t *gtest.T) { - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) t.Assert(all.Len(), 2) t.Assert(len(all.MapKeyValue("uid")), 2) t.Assert(all.MapKeyValue("uid")["3"].Map()["uid"], 3) t.Assert(all.MapKeyValue("uid")["4"].Map()["uid"], 4) - all, err = db.Table(tableUserScores).Where("uid", g.Slice{3, 4}).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", g.Slice{3, 4}).Order("id asc").All() t.AssertNil(err) t.Assert(all.Len(), 10) t.Assert(len(all.MapKeyValue("uid")), 2) @@ -585,7 +585,7 @@ CREATE TABLE %s ( gtest.C(t, func(t *gtest.T) { var users []Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) @@ -593,14 +593,14 @@ CREATE TABLE %s ( t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:uid") t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "uid:uid") t.AssertNil(err) @@ -618,7 +618,7 @@ CREATE TABLE %s ( gtest.C(t, func(t *gtest.T) { var users []*Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) @@ -626,14 +626,14 @@ CREATE TABLE %s ( t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "Uid:UID") t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "Uid:UID") t.AssertNil(err) @@ -669,7 +669,7 @@ CREATE TABLE %s ( } var users []Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) @@ -677,14 +677,14 @@ CREATE TABLE %s ( t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:UId") t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "UId:Uid") t.AssertNil(err) @@ -721,7 +721,7 @@ CREATE TABLE %s ( var users []*Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) @@ -729,14 +729,14 @@ CREATE TABLE %s ( t.Assert(users[0].User, &EntityUser{3, "name_3"}) t.Assert(users[1].User, &EntityUser{4, "name_4"}) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") t.AssertNil(err) t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "UID:Uid") t.AssertNil(err) @@ -754,19 +754,19 @@ CREATE TABLE %s ( gtest.C(t, func(t *gtest.T) { var users []*Entity // User - err := db.Table(tableUser). + err := db.Model(tableUser). Where("uid", g.Slice{3, 4}). Order("uid asc"). ScanList(&users, "User") t.AssertNil(err) // Detail - err = db.Table(tableUserDetail). + err = db.Model(tableUserDetail). Where("uid", gdb.ListItemValues(users, "User", "Uid")). Order("uid asc"). ScanList(&users, "UserDetail", "User", "uid:Uid") t.AssertNil(err) // Scores - err = db.Table(tableUserScores). + err = db.Model(tableUserScores). Where("uid", gdb.ListItemValues(users, "User", "Uid")). Order("id asc"). ScanList(&users, "UserScores", "User", "uid:Uid") @@ -851,22 +851,21 @@ CREATE TABLE %s ( // Result ScanList with struct elements and pointer attributes. gtest.C(t, func(t *gtest.T) { - db.SetDebug(true) var users []Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) t.Assert(len(users), 0) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:uid") t.AssertNil(err) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "uid:uid") t.AssertNil(err) @@ -876,20 +875,20 @@ CREATE TABLE %s ( gtest.C(t, func(t *gtest.T) { var users []*Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) t.Assert(len(users), 0) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "Uid:UID") t.AssertNil(err) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "Uid:UID") t.AssertNil(err) @@ -917,19 +916,19 @@ CREATE TABLE %s ( } var users []Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:UId") t.AssertNil(err) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "UId:Uid") t.AssertNil(err) @@ -958,19 +957,19 @@ CREATE TABLE %s ( var users []*Entity // User - all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "User") t.AssertNil(err) t.Assert(len(users), 0) // Detail - all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") t.AssertNil(err) // Scores - all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() t.AssertNil(err) err = all.ScanList(&users, "UserScores", "User", "UID:Uid") t.AssertNil(err) @@ -980,19 +979,19 @@ CREATE TABLE %s ( gtest.C(t, func(t *gtest.T) { var users []*Entity // User - err := db.Table(tableUser). + err := db.Model(tableUser). Where("uid", g.Slice{3, 4}). Order("uid asc"). ScanList(&users, "User") t.AssertNil(err) // Detail - err = db.Table(tableUserDetail). + err = db.Model(tableUserDetail). Where("uid", gdb.ListItemValues(users, "User", "Uid")). Order("uid asc"). ScanList(&users, "UserDetail", "User", "uid:Uid") t.AssertNil(err) // Scores - err = db.Table(tableUserScores). + err = db.Model(tableUserScores). Where("uid", gdb.ListItemValues(users, "User", "Uid")). Order("id asc"). ScanList(&users, "UserScores", "User", "uid:Uid") @@ -1092,17 +1091,17 @@ CREATE TABLE %s ( scores []*EntityUserScores ) // SELECT * FROM `user_scores` - err = db.Table(tableUserScores).Scan(&scores) + err = db.Model(tableUserScores).Scan(&scores) t.AssertNil(err) // SELECT * FROM `user_scores` WHERE `uid` IN(1,2,3,4,5) - err = db.Table(tableUser). + err = db.Model(tableUser). Where("uid", gdb.ListItemValuesUnique(&scores, "Uid")). ScanList(&scores, "EntityUser", "uid:Uid") t.AssertNil(err) // SELECT * FROM `user_detail` WHERE `uid` IN(1,2,3,4,5) - err = db.Table(tableUserDetail). + err = db.Model(tableUserDetail). Where("uid", gdb.ListItemValuesUnique(&scores, "Uid")). ScanList(&scores, "EntityUserDetail", "uid:Uid") t.AssertNil(err) diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index a606b3215..736a1c1f0 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -17,9 +17,9 @@ import ( ) const ( - SCHEMA = "test_internal" - USER = "root" - PASS = "12345678" + SCHEMA = "test_internal" + TestDbUser = "root" + TestDbPass = "12345678" ) var ( @@ -36,8 +36,8 @@ func init() { configNode = ConfigNode{ Host: "127.0.0.1", Port: "3306", - User: USER, - Pass: PASS, + User: TestDbUser, + Pass: TestDbPass, Name: parser.GetOpt("name", ""), Type: parser.GetOpt("type", "mysql"), Role: "master", @@ -204,7 +204,7 @@ CREATE TABLE %s ( defer dropTable(table2) gtest.C(t, func(t *gtest.T) { - model := db.Table(table1) + model := db.Model(table1) gtest.Assert(model.getSoftFieldNameCreated(table2), "createat") gtest.Assert(model.getSoftFieldNameUpdated(table2), "updateat") gtest.Assert(model.getSoftFieldNameDeleted(table2), "deleteat") @@ -243,48 +243,48 @@ CREATE TABLE %s ( defer dropTable(table2) gtest.C(t, func(t *gtest.T) { - model := db.Table(table1) + model := db.Model(table1) t.Assert(model.getConditionForSoftDeleting(), "`delete_at` IS NULL") }) gtest.C(t, func(t *gtest.T) { - model := db.Table(fmt.Sprintf(`%s as t`, table1)) + model := db.Model(fmt.Sprintf(`%s as t`, table1)) t.Assert(model.getConditionForSoftDeleting(), "`delete_at` IS NULL") }) gtest.C(t, func(t *gtest.T) { - model := db.Table(fmt.Sprintf(`%s, %s`, table1, table2)) + model := db.Model(fmt.Sprintf(`%s, %s`, table1, table2)) t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf( "`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL", table1, table2, )) }) gtest.C(t, func(t *gtest.T) { - model := db.Table(fmt.Sprintf(`%s t1, %s as t2`, table1, table2)) + model := db.Model(fmt.Sprintf(`%s t1, %s as t2`, table1, table2)) t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL") }) gtest.C(t, func(t *gtest.T) { - model := db.Table(fmt.Sprintf(`%s as t1, %s as t2`, table1, table2)) + model := db.Model(fmt.Sprintf(`%s as t1, %s as t2`, table1, table2)) t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL") }) gtest.C(t, func(t *gtest.T) { - model := db.Table(fmt.Sprintf(`%s as t1`, table1)).LeftJoin(table2+" t2", "t2.id2=t1.id1") + model := db.Model(fmt.Sprintf(`%s as t1`, table1)).LeftJoin(table2+" t2", "t2.id2=t1.id1") t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL") }) gtest.C(t, func(t *gtest.T) { - model := db.Table(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1") + model := db.Model(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1") t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf( "`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL", table1, table2, )) }) gtest.C(t, func(t *gtest.T) { - model := db.Table(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1").RightJoin(table2, "t2.id2=t1.id1") + model := db.Model(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1").RightJoin(table2, "t2.id2=t1.id1") t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf( "`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL AND `%s`.`deleteat` IS NULL", table1, table2, table2, )) }) gtest.C(t, func(t *gtest.T) { - model := db.Table(table1+" as t1").LeftJoin(table2+" as t2", "t2.id2=t1.id1").RightJoin(table2+" as t3 ", "t2.id2=t1.id1") + model := db.Model(table1+" as t1").LeftJoin(table2+" as t2", "t2.id2=t1.id1").RightJoin(table2+" as t3 ", "t2.id2=t1.id1") t.Assert( model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL AND `t3`.`deleteat` IS NULL", diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index 74b348686..9db3b9f6c 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -124,7 +124,7 @@ func Test_DB_Insert(t *testing.T) { n, _ = result.RowsAffected() t.Assert(n, 1) - one, err := db.Table(table).Where("id", 3).One() + one, err := db.Model(table).Where("id", 3).One() t.AssertNil(err) t.Assert(one["id"].Int(), 3) @@ -146,7 +146,7 @@ func Test_DB_Insert(t *testing.T) { n, _ = result.RowsAffected() t.Assert(n, 1) - one, err = db.Table(table).Where("id", 4).One() + one, err = db.Model(table).Where("id", 4).One() t.AssertNil(err) t.Assert(one["id"].Int(), 4) t.Assert(one["passport"].String(), "t4") @@ -176,7 +176,7 @@ func Test_DB_Insert(t *testing.T) { n, _ = r.RowsAffected() t.Assert(n, 2) - one, err = db.Table(table).Where("id", 200).One() + one, err = db.Model(table).Where("id", 200).One() t.AssertNil(err) t.Assert(one["id"].Int(), 200) t.Assert(one["passport"].String(), "t200") @@ -437,7 +437,7 @@ func Test_DB_Save(t *testing.T) { }) t.AssertNil(err) - one, err := db.Table(table).Where("id", 1).One() + one, err := db.Model(table).Where("id", 1).One() t.AssertNil(err) t.Assert(one["id"].Int(), 1) t.Assert(one["passport"].String(), "t1") @@ -462,7 +462,7 @@ func Test_DB_Replace(t *testing.T) { }) t.AssertNil(err) - one, err := db.Table(table).Where("id", 1).One() + one, err := db.Model(table).Where("id", 1).One() t.AssertNil(err) t.Assert(one["id"].Int(), 1) t.Assert(one["passport"].String(), "t1") @@ -482,7 +482,7 @@ func Test_DB_Update(t *testing.T) { n, _ := result.RowsAffected() t.Assert(n, 1) - one, err := db.Table(table).Where("id", 3).One() + one, err := db.Model(table).Where("id", 3).One() t.AssertNil(err) t.Assert(one["id"].Int(), 3) t.Assert(one["passport"].String(), "user_3") @@ -567,7 +567,7 @@ func Test_DB_GetCount(t *testing.T) { gtest.C(t, func(t *gtest.T) { count, err := db.GetCount(fmt.Sprintf("SELECT * FROM %s", table)) t.AssertNil(err) - t.Assert(count, SIZE) + t.Assert(count, TableSize) }) } @@ -616,7 +616,7 @@ func Test_DB_GetStructs(t *testing.T) { var users []User err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) t.AssertNil(err) - t.Assert(len(users), SIZE-1) + t.Assert(len(users), TableSize-1) t.Assert(users[0].Id, 2) t.Assert(users[1].Id, 3) t.Assert(users[2].Id, 4) @@ -636,7 +636,7 @@ func Test_DB_GetStructs(t *testing.T) { var users []User err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) t.AssertNil(err) - t.Assert(len(users), SIZE-1) + t.Assert(len(users), TableSize-1) t.Assert(users[0].Id, 2) t.Assert(users[1].Id, 3) t.Assert(users[2].Id, 4) @@ -687,7 +687,7 @@ func Test_DB_GetScan(t *testing.T) { var users []User err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) t.AssertNil(err) - t.Assert(len(users), SIZE-1) + t.Assert(len(users), TableSize-1) t.Assert(users[0].Id, 2) t.Assert(users[1].Id, 3) t.Assert(users[2].Id, 4) @@ -707,7 +707,7 @@ func Test_DB_GetScan(t *testing.T) { var users []User err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) t.AssertNil(err) - t.Assert(len(users), SIZE-1) + t.Assert(len(users), TableSize-1) t.Assert(users[0].Id, 2) t.Assert(users[1].Id, 3) t.Assert(users[2].Id, 4) @@ -724,7 +724,7 @@ func Test_DB_Delete(t *testing.T) { result, err := db.Delete(table, 1) t.AssertNil(err) n, _ := result.RowsAffected() - t.Assert(n, SIZE) + t.Assert(n, TableSize) }) } @@ -784,7 +784,7 @@ func Test_DB_ToJson(t *testing.T) { gtest.AssertNil(err) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Fields("*").Where("id =? ", 1).Select() + result, err := db.Model(table).Fields("*").Where("id =? ", 1).Select() if err != nil { gtest.Fatal(err) } @@ -825,7 +825,7 @@ func Test_DB_ToJson(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Fields("*").Where("id =? ", 1).One() + result, err := db.Model(table).Fields("*").Where("id =? ", 1).One() if err != nil { gtest.Fatal(err) } @@ -858,7 +858,7 @@ func Test_DB_ToXml(t *testing.T) { gtest.AssertNil(err) gtest.C(t, func(t *gtest.T) { - record, err := db.Table(table).Fields("*").Where("id = ?", 1).One() + record, err := db.Model(table).Fields("*").Where("id = ?", 1).One() if err != nil { gtest.Fatal(err) } @@ -924,7 +924,7 @@ func Test_DB_ToStringMap(t *testing.T) { gtest.AssertNil(err) gtest.C(t, func(t *gtest.T) { id := "1" - result, err := db.Table(table).Fields("*").Where("id = ?", 1).Select() + result, err := db.Model(table).Fields("*").Where("id = ?", 1).Select() if err != nil { gtest.Fatal(err) } @@ -961,7 +961,7 @@ func Test_DB_ToIntMap(t *testing.T) { gtest.C(t, func(t *gtest.T) { id := 1 - result, err := db.Table(table).Fields("*").Where("id = ?", id).Select() + result, err := db.Model(table).Fields("*").Where("id = ?", id).Select() if err != nil { gtest.Fatal(err) } @@ -997,7 +997,7 @@ func Test_DB_ToUintMap(t *testing.T) { gtest.C(t, func(t *gtest.T) { id := 1 - result, err := db.Table(table).Fields("*").Where("id = ?", id).Select() + result, err := db.Model(table).Fields("*").Where("id = ?", id).Select() if err != nil { gtest.Fatal(err) } @@ -1035,7 +1035,7 @@ func Test_DB_ToStringRecord(t *testing.T) { gtest.C(t, func(t *gtest.T) { id := 1 ids := "1" - result, err := db.Table(table).Fields("*").Where("id = ?", id).Select() + result, err := db.Model(table).Fields("*").Where("id = ?", id).Select() if err != nil { gtest.Fatal(err) } @@ -1072,7 +1072,7 @@ func Test_DB_ToIntRecord(t *testing.T) { gtest.C(t, func(t *gtest.T) { id := 1 - result, err := db.Table(table).Fields("*").Where("id = ?", id).Select() + result, err := db.Model(table).Fields("*").Where("id = ?", id).Select() if err != nil { gtest.Fatal(err) } @@ -1109,7 +1109,7 @@ func Test_DB_ToUintRecord(t *testing.T) { gtest.C(t, func(t *gtest.T) { id := 1 - result, err := db.Table(table).Fields("*").Where("id = ?", id).Select() + result, err := db.Model(table).Fields("*").Where("id = ?", id).Select() if err != nil { gtest.Fatal(err) } @@ -1169,7 +1169,7 @@ func Test_DB_TableField(t *testing.T) { "field_varchar": "abc", "field_varbinary": "aaa", } - res, err := db.Table(name).Data(data).Insert() + res, err := db.Model(name).Data(data).Insert() if err != nil { gtest.Fatal(err) } @@ -1181,7 +1181,7 @@ func Test_DB_TableField(t *testing.T) { gtest.Assert(n, 1) } - result, err := db.Table(name).Fields("*").Where("field_int = ?", 2).Select() + result, err := db.Model(name).Fields("*").Where("field_int = ?", 2).Select() if err != nil { gtest.Fatal(err) } @@ -1191,8 +1191,8 @@ func Test_DB_TableField(t *testing.T) { func Test_DB_Prefix(t *testing.T) { db := dbPrefix - name := fmt.Sprintf(`%s_%d`, TABLE, gtime.TimestampNano()) - table := PREFIX1 + name + name := fmt.Sprintf(`%s_%d`, TableName, gtime.TimestampNano()) + table := TableNamePrefix1 + name createTableWithDb(db, table) defer dropTable(table) @@ -1272,7 +1272,7 @@ func Test_DB_Prefix(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := garray.New(true) - for i := 1; i <= SIZE; i++ { + for i := 1; i <= TableSize; i++ { array.Append(g.Map{ "id": i, "passport": fmt.Sprintf(`user_%d`, i), @@ -1287,7 +1287,7 @@ func Test_DB_Prefix(t *testing.T) { n, e := result.RowsAffected() t.Assert(e, nil) - t.Assert(n, SIZE) + t.Assert(n, TableSize) }) } @@ -1300,7 +1300,7 @@ func Test_Model_InnerJoin(t *testing.T) { defer dropTable(table1) defer dropTable(table2) - res, err := db.Table(table1).Where("id > ?", 5).Delete() + res, err := db.Model(table1).Where("id > ?", 5).Delete() if err != nil { t.Fatal(err) } @@ -1312,14 +1312,14 @@ func Test_Model_InnerJoin(t *testing.T) { t.Assert(n, 5) - result, err := db.Table(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").OrderBy("u1.id").Select() + result, err := db.Model(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").OrderBy("u1.id").Select() if err != nil { t.Fatal(err) } t.Assert(len(result), 5) - result, err = db.Table(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > ?", 1).OrderBy("u1.id").Select() + result, err = db.Model(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > ?", 1).OrderBy("u1.id").Select() if err != nil { t.Fatal(err) } @@ -1336,7 +1336,7 @@ func Test_Model_LeftJoin(t *testing.T) { defer dropTable(table1) defer dropTable(table2) - res, err := db.Table(table2).Where("id > ?", 3).Delete() + res, err := db.Model(table2).Where("id > ?", 3).Delete() if err != nil { t.Fatal(err) } @@ -1348,14 +1348,14 @@ func Test_Model_LeftJoin(t *testing.T) { t.Assert(n, 7) } - result, err := db.Table(table1+" u1").LeftJoin(table2+" u2", "u1.id = u2.id").Select() + result, err := db.Model(table1+" u1").LeftJoin(table2+" u2", "u1.id = u2.id").Select() if err != nil { t.Fatal(err) } t.Assert(len(result), 10) - result, err = db.Table(table1+" u1").LeftJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > ? ", 2).Select() + result, err = db.Model(table1+" u1").LeftJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > ? ", 2).Select() if err != nil { t.Fatal(err) } @@ -1372,7 +1372,7 @@ func Test_Model_RightJoin(t *testing.T) { defer dropTable(table1) defer dropTable(table2) - res, err := db.Table(table1).Where("id > ?", 3).Delete() + res, err := db.Model(table1).Where("id > ?", 3).Delete() if err != nil { t.Fatal(err) } @@ -1384,13 +1384,13 @@ func Test_Model_RightJoin(t *testing.T) { t.Assert(n, 7) - result, err := db.Table(table1+" u1").RightJoin(table2+" u2", "u1.id = u2.id").Select() + result, err := db.Model(table1+" u1").RightJoin(table2+" u2", "u1.id = u2.id").Select() if err != nil { t.Fatal(err) } t.Assert(len(result), 10) - result, err = db.Table(table1+" u1").RightJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > 2").Select() + result, err = db.Model(table1+" u1").RightJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > 2").Select() if err != nil { t.Fatal(err) } @@ -1445,7 +1445,7 @@ func Test_DB_UpdateCounter(t *testing.T) { t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) - one, err := db.Table(tableName).Where("id", 1).One() + one, err := db.Model(tableName).Where("id", 1).One() t.AssertNil(err) t.Assert(one["id"].Int(), 1) t.Assert(one["views"].Int(), 1) @@ -1464,7 +1464,7 @@ func Test_DB_UpdateCounter(t *testing.T) { t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) - one, err := db.Table(tableName).Where("id", 1).One() + one, err := db.Model(tableName).Where("id", 1).One() t.AssertNil(err) t.Assert(one["id"].Int(), 1) t.Assert(one["views"].Int(), 0) diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index a1f14f511..0eb1b454a 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -29,7 +29,7 @@ func Test_Model_Insert(t *testing.T) { table := createTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - user := db.Table(table) + user := db.Model(table) result, err := user.Filter().Data(g.Map{ "id": 1, "uid": 1, @@ -42,7 +42,7 @@ func Test_Model_Insert(t *testing.T) { n, _ := result.LastInsertId() t.Assert(n, 1) - result, err = db.Table(table).Filter().Data(g.Map{ + result, err = db.Model(table).Filter().Data(g.Map{ "id": "2", "uid": "2", "passport": "t2", @@ -63,7 +63,7 @@ func Test_Model_Insert(t *testing.T) { CreateTime *gtime.Time `json:"create_time"` } // Model inserting. - result, err = db.Table(table).Filter().Data(User{ + result, err = db.Model(table).Filter().Data(User{ Id: 3, Uid: 3, Passport: "t3", @@ -73,11 +73,11 @@ func Test_Model_Insert(t *testing.T) { t.AssertNil(err) n, _ = result.RowsAffected() t.Assert(n, 1) - value, err := db.Table(table).Fields("passport").Where("id=3").Value() + value, err := db.Model(table).Fields("passport").Where("id=3").Value() t.AssertNil(err) t.Assert(value.String(), "t3") - result, err = db.Table(table).Filter().Data(&User{ + result, err = db.Model(table).Filter().Data(&User{ Id: 4, Uid: 4, Passport: "t4", @@ -88,11 +88,11 @@ func Test_Model_Insert(t *testing.T) { t.AssertNil(err) n, _ = result.RowsAffected() t.Assert(n, 1) - value, err = db.Table(table).Fields("passport").Where("id=4").Value() + value, err = db.Model(table).Fields("passport").Where("id=4").Value() t.AssertNil(err) t.Assert(value.String(), "t4") - result, err = db.Table(table).Where("id>?", 1).Delete() + result, err = db.Model(table).Where("id>?", 1).Delete() t.AssertNil(err) n, _ = result.RowsAffected() t.Assert(n, 3) @@ -113,7 +113,7 @@ func Test_Model_Insert_Filter(t *testing.T) { "nickname": "name_1", "create_time": gtime.Now().String(), } - result, err := db.Table(table).Filter().Data(data).Insert() + result, err := db.Model(table).Filter().Data(data).Insert() t.AssertNil(err) n, _ := result.LastInsertId() t.Assert(n, 1) @@ -143,7 +143,7 @@ func Test_Model_Insert_Filter(t *testing.T) { }, } - result, err := db.Table(table).Filter().Data(data).Insert() + result, err := db.Model(table).Filter().Data(data).Insert() t.AssertNil(err) n, _ := result.LastInsertId() t.Assert(n, 2) @@ -169,10 +169,10 @@ func Test_Model_Insert_WithStructAndSliceAttribute(t *testing.T) { "nickname": []string{"A", "B", "C"}, "create_time": gtime.Now().String(), } - _, err := db.Table(table).Data(data).Insert() + _, err := db.Model(table).Data(data).Insert() t.AssertNil(err) - one, err := db.Table(table).One("id", 1) + one, err := db.Model(table).One("id", 1) t.AssertNil(err) t.Assert(one["passport"], data["passport"]) t.Assert(one["create_time"], data["create_time"]) @@ -276,10 +276,10 @@ func Test_Model_Insert_Time(t *testing.T) { "nickname": "n1", "create_time": "2020-10-10 20:09:18.334", } - _, err := db.Table(table).Data(data).Insert() + _, err := db.Model(table).Data(data).Insert() t.AssertNil(err) - one, err := db.Table(table).One("id", 1) + one, err := db.Model(table).One("id", 1) t.AssertNil(err) t.Assert(one["passport"], data["passport"]) t.Assert(one["create_time"], "2020-10-10 20:09:18") @@ -291,9 +291,9 @@ func Test_Model_BatchInsertWithArrayStruct(t *testing.T) { table := createTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - user := db.Table(table) + user := db.Model(table) array := garray.New() - for i := 1; i <= SIZE; i++ { + for i := 1; i <= TableSize; i++ { array.Append(g.Map{ "id": i, "uid": i, @@ -307,7 +307,7 @@ func Test_Model_BatchInsertWithArrayStruct(t *testing.T) { result, err := user.Filter().Data(array).Insert() t.AssertNil(err) n, _ := result.LastInsertId() - t.Assert(n, SIZE) + t.Assert(n, TableSize) }) } @@ -315,7 +315,7 @@ func Test_Model_InsertIgnore(t *testing.T) { table := createInitTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - _, err := db.Table(table).Filter().Data(g.Map{ + _, err := db.Model(table).Filter().Data(g.Map{ "id": 1, "uid": 1, "passport": "t1", @@ -326,7 +326,7 @@ func Test_Model_InsertIgnore(t *testing.T) { t.AssertNE(err, nil) }) gtest.C(t, func(t *gtest.T) { - _, err := db.Table(table).Filter().Data(g.Map{ + _, err := db.Model(table).Filter().Data(g.Map{ "id": 1, "uid": 1, "passport": "t1", @@ -343,7 +343,7 @@ func Test_Model_Batch(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - result, err := db.Table(table).Filter().Data(g.List{ + result, err := db.Model(table).Filter().Data(g.List{ { "id": 2, "uid": 2, @@ -372,7 +372,7 @@ func Test_Model_Batch(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - result, err := db.Table(table).Data(g.List{ + result, err := db.Model(table).Data(g.List{ {"passport": "t1"}, {"passport": "t2"}, {"passport": "t3"}, @@ -390,34 +390,34 @@ func Test_Model_Batch(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createInitTable() defer dropTable(table) - result, err := db.Table(table).All() + result, err := db.Model(table).All() t.AssertNil(err) - t.Assert(len(result), SIZE) + t.Assert(len(result), TableSize) for _, v := range result { v["nickname"].Set(v["nickname"].String() + v["id"].String()) } - r, e := db.Table(table).Data(result).Save() + r, e := db.Model(table).Data(result).Save() t.Assert(e, nil) n, e := r.RowsAffected() t.Assert(e, nil) - t.Assert(n, SIZE*2) + t.Assert(n, TableSize*2) }) // batch replace gtest.C(t, func(t *gtest.T) { table := createInitTable() defer dropTable(table) - result, err := db.Table(table).All() + result, err := db.Model(table).All() t.AssertNil(err) - t.Assert(len(result), SIZE) + t.Assert(len(result), TableSize) for _, v := range result { v["nickname"].Set(v["nickname"].String() + v["id"].String()) } - r, e := db.Table(table).Data(result).Replace() + r, e := db.Model(table).Data(result).Replace() t.Assert(e, nil) n, e := r.RowsAffected() t.Assert(e, nil) - t.Assert(n, SIZE*2) + t.Assert(n, TableSize*2) }) } @@ -426,7 +426,7 @@ func Test_Model_Replace(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Data(g.Map{ + result, err := db.Model(table).Data(g.Map{ "id": 1, "passport": "t11", "password": "25d55ad283aa400af464c76d713c07ad", @@ -443,7 +443,7 @@ func Test_Model_Save(t *testing.T) { table := createTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Data(g.Map{ + result, err := db.Model(table).Data(g.Map{ "id": 1, "passport": "t111", "password": "25d55ad283aa400af464c76d713c07ad", @@ -461,29 +461,29 @@ func Test_Model_Update(t *testing.T) { defer dropTable(table) // UPDATE...LIMIT gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Data("nickname", "T100").Where(1).Order("id desc").Limit(2).Update() + result, err := db.Model(table).Data("nickname", "T100").Where(1).Order("id desc").Limit(2).Update() t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 2) - v1, err := db.Table(table).Fields("nickname").Where("id", 10).Value() + v1, err := db.Model(table).Fields("nickname").Where("id", 10).Value() t.AssertNil(err) t.Assert(v1.String(), "T100") - v2, err := db.Table(table).Fields("nickname").Where("id", 8).Value() + v2, err := db.Model(table).Fields("nickname").Where("id", 8).Value() t.AssertNil(err) t.Assert(v2.String(), "name_8") }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Data("passport", "user_22").Where("passport=?", "user_2").Update() + result, err := db.Model(table).Data("passport", "user_22").Where("passport=?", "user_2").Update() t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Data("passport", "user_2").Where("passport='user_22'").Update() + result, err := db.Model(table).Data("passport", "user_2").Where("passport='user_22'").Update() t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) @@ -491,14 +491,14 @@ func Test_Model_Update(t *testing.T) { // Update + Data(string) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Data("passport='user_33'").Where("passport='user_3'").Update() + result, err := db.Model(table).Data("passport='user_33'").Where("passport='user_3'").Update() t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) }) // Update + Fields(string) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Fields("passport").Data(g.Map{ + result, err := db.Model(table).Fields("passport").Data(g.Map{ "passport": "user_44", "none": "none", }).Where("passport='user_4'").Update() @@ -513,7 +513,7 @@ func Test_Model_Clone(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - md := db.Table(table).Where("id IN(?)", g.Slice{1, 3}) + md := db.Model(table).Where("id IN(?)", g.Slice{1, 3}) count, err := md.Count() t.AssertNil(err) @@ -536,7 +536,7 @@ func Test_Model_Safe(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - md := db.Table(table).Safe(false).Where("id IN(?)", g.Slice{1, 3}) + md := db.Model(table).Safe(false).Where("id IN(?)", g.Slice{1, 3}) count, err := md.Count() t.AssertNil(err) t.Assert(count, 2) @@ -547,7 +547,7 @@ func Test_Model_Safe(t *testing.T) { t.Assert(count, 1) }) gtest.C(t, func(t *gtest.T) { - md := db.Table(table).Safe(true).Where("id IN(?)", g.Slice{1, 3}) + md := db.Model(table).Safe(true).Where("id IN(?)", g.Slice{1, 3}) count, err := md.Count() t.AssertNil(err) t.Assert(count, 2) @@ -559,7 +559,7 @@ func Test_Model_Safe(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { - md := db.Table(table).Safe().Where("id IN(?)", g.Slice{1, 3}) + md := db.Model(table).Safe().Where("id IN(?)", g.Slice{1, 3}) count, err := md.Count() t.AssertNil(err) t.Assert(count, 2) @@ -570,7 +570,7 @@ func Test_Model_Safe(t *testing.T) { t.Assert(count, 2) }) gtest.C(t, func(t *gtest.T) { - md1 := db.Table(table).Safe() + md1 := db.Model(table).Safe() md2 := md1.Where("id in (?)", g.Slice{1, 3}) count, err := md2.Count() t.AssertNil(err) @@ -589,7 +589,7 @@ func Test_Model_Safe(t *testing.T) { table := createInitTable() defer dropTable(table) - md1 := db.Table(table).Where("id>", 0).Safe() + md1 := db.Model(table).Where("id>", 0).Safe() md2 := md1.Where("id in (?)", g.Slice{1, 3}) md3 := md1.Where("id in (?)", g.Slice{4, 5, 6}) @@ -631,12 +631,12 @@ func Test_Model_All(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).All() + result, err := db.Model(table).All() t.AssertNil(err) - t.Assert(len(result), SIZE) + t.Assert(len(result), TableSize) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id<0").All() + result, err := db.Model(table).Where("id<0").All() t.Assert(result, nil) t.AssertNil(err) }) @@ -670,13 +670,13 @@ func Test_Model_Fields(t *testing.T) { gtest.Assert(n, 1) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(tableName1).As("u").Fields("u.passport,u.id").Where("u.id<2").All() + all, err := db.Model(tableName1).As("u").Fields("u.passport,u.id").Where("u.id<2").All() t.AssertNil(err) t.Assert(len(all), 1) t.Assert(len(all[0]), 2) }) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(tableName1).As("u1"). + all, err := db.Model(tableName1).As("u1"). LeftJoin(tableName1, "u2", "u2.id=u1.id"). Fields("u1.passport,u1.id,u2.id AS u2id"). Where("u1.id<2"). @@ -686,7 +686,7 @@ func Test_Model_Fields(t *testing.T) { t.Assert(len(all[0]), 3) }) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(tableName1).As("u1"). + all, err := db.Model(tableName1).As("u1"). LeftJoin(tableName2, "u2", "u2.id=u1.id"). Fields("u1.passport,u1.id,u2.name,u2.age"). Where("u1.id<2"). @@ -706,21 +706,21 @@ func Test_Model_FindAll(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).FindAll(5) + result, err := db.Model(table).FindAll(5) t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 5) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Order("id asc").FindAll("id", 8) + result, err := db.Model(table).Order("id asc").FindAll("id", 8) t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 8) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Order("id asc").FindAll(g.Slice{3, 9}) + result, err := db.Model(table).Order("id asc").FindAll(g.Slice{3, 9}) t.AssertNil(err) t.Assert(len(result), 2) t.Assert(result[0]["id"].Int(), 3) @@ -728,12 +728,12 @@ func Test_Model_FindAll(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).FindAll() + result, err := db.Model(table).FindAll() t.AssertNil(err) - t.Assert(len(result), SIZE) + t.Assert(len(result), TableSize) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id<0").FindAll() + result, err := db.Model(table).Where("id<0").FindAll() t.Assert(result, nil) t.AssertNil(err) }) @@ -744,28 +744,28 @@ func Test_Model_FindAll_GTime(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).FindAll("create_time < ?", gtime.NewFromStr("2000-01-01 00:00:00")) + result, err := db.Model(table).FindAll("create_time < ?", gtime.NewFromStr("2000-01-01 00:00:00")) t.AssertNil(err) t.Assert(len(result), 0) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).FindAll("create_time > ?", gtime.NewFromStr("2000-01-01 00:00:00")) + result, err := db.Model(table).FindAll("create_time > ?", gtime.NewFromStr("2000-01-01 00:00:00")) t.AssertNil(err) - t.Assert(len(result), SIZE) + t.Assert(len(result), TableSize) }) gtest.C(t, func(t *gtest.T) { v := g.NewVar("2000-01-01 00:00:00") - result, err := db.Table(table).FindAll("create_time < ?", v) + result, err := db.Model(table).FindAll("create_time < ?", v) t.AssertNil(err) t.Assert(len(result), 0) }) gtest.C(t, func(t *gtest.T) { v := g.NewVar("2000-01-01 00:00:00") - result, err := db.Table(table).FindAll("create_time > ?", v) + result, err := db.Model(table).FindAll("create_time > ?", v) t.AssertNil(err) - t.Assert(len(result), SIZE) + t.Assert(len(result), TableSize) }) } @@ -773,13 +773,13 @@ func Test_Model_One(t *testing.T) { table := createInitTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - record, err := db.Table(table).Where("id", 1).One() + record, err := db.Model(table).Where("id", 1).One() t.AssertNil(err) t.Assert(record["nickname"].String(), "name_1") }) gtest.C(t, func(t *gtest.T) { - record, err := db.Table(table).Where("id", 0).One() + record, err := db.Model(table).Where("id", 0).One() t.AssertNil(err) t.Assert(record, nil) }) @@ -790,31 +790,31 @@ func Test_Model_FindOne(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - record, err := db.Table(table).FindOne(1) + record, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(record["nickname"].String(), "name_1") }) gtest.C(t, func(t *gtest.T) { - record, err := db.Table(table).FindOne(3) + record, err := db.Model(table).FindOne(3) t.AssertNil(err) t.Assert(record["nickname"].String(), "name_3") }) gtest.C(t, func(t *gtest.T) { - record, err := db.Table(table).Where("id", 1).FindOne() + record, err := db.Model(table).Where("id", 1).FindOne() t.AssertNil(err) t.Assert(record["nickname"].String(), "name_1") }) gtest.C(t, func(t *gtest.T) { - record, err := db.Table(table).FindOne("id", 9) + record, err := db.Model(table).FindOne("id", 9) t.AssertNil(err) t.Assert(record["nickname"].String(), "name_9") }) gtest.C(t, func(t *gtest.T) { - record, err := db.Table(table).Where("id", 0).FindOne() + record, err := db.Model(table).Where("id", 0).FindOne() t.AssertNil(err) t.Assert(record, nil) }) @@ -825,13 +825,13 @@ func Test_Model_Value(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table).Fields("nickname").Where("id", 1).Value() + value, err := db.Model(table).Fields("nickname").Where("id", 1).Value() t.AssertNil(err) t.Assert(value.String(), "name_1") }) gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table).Fields("nickname").Where("id", 0).Value() + value, err := db.Model(table).Fields("nickname").Where("id", 0).Value() t.AssertNil(err) t.Assert(value, nil) }) @@ -842,28 +842,28 @@ func Test_Model_Array(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table).Where("id", g.Slice{1, 2, 3}).All() + all, err := db.Model(table).Where("id", g.Slice{1, 2, 3}).All() t.AssertNil(err) t.Assert(all.Array("id"), g.Slice{1, 2, 3}) t.Assert(all.Array("nickname"), g.Slice{"name_1", "name_2", "name_3"}) }) gtest.C(t, func(t *gtest.T) { - array, err := db.Table(table).Fields("nickname").Where("id", g.Slice{1, 2, 3}).Array() + array, err := db.Model(table).Fields("nickname").Where("id", g.Slice{1, 2, 3}).Array() t.AssertNil(err) t.Assert(array, g.Slice{"name_1", "name_2", "name_3"}) }) gtest.C(t, func(t *gtest.T) { - array, err := db.Table(table).Array("nickname", "id", g.Slice{1, 2, 3}) + array, err := db.Model(table).Array("nickname", "id", g.Slice{1, 2, 3}) t.AssertNil(err) t.Assert(array, g.Slice{"name_1", "name_2", "name_3"}) }) gtest.C(t, func(t *gtest.T) { - array, err := db.Table(table).FindArray("nickname", "id", g.Slice{1, 2, 3}) + array, err := db.Model(table).FindArray("nickname", "id", g.Slice{1, 2, 3}) t.AssertNil(err) t.Assert(array, g.Slice{"name_1", "name_2", "name_3"}) }) gtest.C(t, func(t *gtest.T) { - array, err := db.Table(table).FindArray("nickname", g.Slice{1, 2, 3}) + array, err := db.Model(table).FindArray("nickname", g.Slice{1, 2, 3}) t.AssertNil(err) t.Assert(array, g.Slice{"name_1", "name_2", "name_3"}) }) @@ -874,25 +874,25 @@ func Test_Model_FindValue(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table).FindValue("nickname", 1) + value, err := db.Model(table).FindValue("nickname", 1) t.AssertNil(err) t.Assert(value.String(), "name_1") }) gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table).Order("id desc").FindValue("nickname") + value, err := db.Model(table).Order("id desc").FindValue("nickname") t.AssertNil(err) t.Assert(value.String(), "name_10") }) gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table).Fields("nickname").Where("id", 1).FindValue() + value, err := db.Model(table).Fields("nickname").Where("id", 1).FindValue() t.AssertNil(err) t.Assert(value.String(), "name_1") }) gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table).Fields("nickname").Where("id", 0).FindValue() + value, err := db.Model(table).Fields("nickname").Where("id", 0).FindValue() t.AssertNil(err) t.Assert(value, nil) }) @@ -902,33 +902,33 @@ func Test_Model_Count(t *testing.T) { table := createInitTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - count, err := db.Table(table).Count() + count, err := db.Model(table).Count() t.AssertNil(err) - t.Assert(count, SIZE) + t.Assert(count, TableSize) }) gtest.C(t, func(t *gtest.T) { - count, err := db.Table(table).FieldsEx("id").Where("id>8").Count() + count, err := db.Model(table).FieldsEx("id").Where("id>8").Count() t.AssertNil(err) t.Assert(count, 2) }) gtest.C(t, func(t *gtest.T) { - count, err := db.Table(table).Fields("distinct id,nickname").Where("id>8").Count() + count, err := db.Model(table).Fields("distinct id,nickname").Where("id>8").Count() t.AssertNil(err) t.Assert(count, 2) }) // COUNT...LIMIT... gtest.C(t, func(t *gtest.T) { - count, err := db.Table(table).Page(1, 2).Count() + count, err := db.Model(table).Page(1, 2).Count() t.AssertNil(err) - t.Assert(count, SIZE) + t.Assert(count, TableSize) }) //gtest.C(t, func(t *gtest.T) { - // count, err := db.Table(table).Fields("id myid").Where("id>8").Count() + // count, err := db.Model(table).Fields("id myid").Where("id>8").Count() // t.AssertNil(err) // t.Assert(count, 2) //}) //gtest.C(t, func(t *gtest.T) { - // count, err := db.Table(table).As("u1").LeftJoin(table, "u2", "u2.id=u1.id").Fields("u2.id u2id").Where("u1.id>8").Count() + // count, err := db.Model(table).As("u1").LeftJoin(table, "u2", "u2.id=u1.id").Fields("u2.id u2id").Where("u1.id>8").Count() // t.AssertNil(err) // t.Assert(count, 2) //}) @@ -938,19 +938,19 @@ func Test_Model_FindCount(t *testing.T) { table := createInitTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - count, err := db.Table(table).FindCount(g.Slice{1, 3}) + count, err := db.Model(table).FindCount(g.Slice{1, 3}) t.AssertNil(err) t.Assert(count, 2) }) gtest.C(t, func(t *gtest.T) { - count, err := db.Table(table).FindCount(g.Slice{1, 300000}) + count, err := db.Model(table).FindCount(g.Slice{1, 300000}) t.AssertNil(err) t.Assert(count, 1) }) gtest.C(t, func(t *gtest.T) { - count, err := db.Table(table).FindCount() + count, err := db.Model(table).FindCount() t.AssertNil(err) - t.Assert(count, SIZE) + t.Assert(count, TableSize) }) } @@ -958,9 +958,9 @@ func Test_Model_Select(t *testing.T) { table := createInitTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Select() + result, err := db.Model(table).Select() t.AssertNil(err) - t.Assert(len(result), SIZE) + t.Assert(len(result), TableSize) }) } @@ -976,7 +976,7 @@ func Test_Model_Struct(t *testing.T) { CreateTime gtime.Time } user := new(User) - err := db.Table(table).Where("id=1").Struct(user) + err := db.Model(table).Where("id=1").Struct(user) t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") @@ -990,7 +990,7 @@ func Test_Model_Struct(t *testing.T) { CreateTime *gtime.Time } user := new(User) - err := db.Table(table).Where("id=1").Struct(user) + err := db.Model(table).Where("id=1").Struct(user) t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") @@ -1005,7 +1005,7 @@ func Test_Model_Struct(t *testing.T) { CreateTime *gtime.Time } user := (*User)(nil) - err := db.Table(table).Where("id=1").Struct(&user) + err := db.Model(table).Where("id=1").Struct(&user) t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") @@ -1020,7 +1020,7 @@ func Test_Model_Struct(t *testing.T) { CreateTime *gtime.Time } user := (*User)(nil) - err := db.Table(table).Where("id=1").Scan(&user) + err := db.Model(table).Where("id=1").Scan(&user) if err != nil { gtest.Error(err) } @@ -1037,7 +1037,7 @@ func Test_Model_Struct(t *testing.T) { CreateTime *gtime.Time } user := new(User) - err := db.Table(table).Where("id=-1").Struct(user) + err := db.Model(table).Where("id=-1").Struct(user) t.Assert(err, sql.ErrNoRows) }) gtest.C(t, func(t *gtest.T) { @@ -1049,7 +1049,7 @@ func Test_Model_Struct(t *testing.T) { CreateTime *gtime.Time } var user *User - err := db.Table(table).Where("id=-1").Struct(&user) + err := db.Model(table).Where("id=-1").Struct(&user) t.AssertNil(err) }) } @@ -1069,7 +1069,7 @@ func Test_Model_Struct_CustomType(t *testing.T) { CreateTime gtime.Time } user := new(User) - err := db.Table(table).Where("id=1").Struct(user) + err := db.Model(table).Where("id=1").Struct(user) t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") @@ -1089,11 +1089,11 @@ func Test_Model_Structs(t *testing.T) { CreateTime gtime.Time } var users []User - err := db.Table(table).Order("id asc").Structs(&users) + err := db.Model(table).Order("id asc").Structs(&users) if err != nil { gtest.Error(err) } - t.Assert(len(users), SIZE) + t.Assert(len(users), TableSize) t.Assert(users[0].Id, 1) t.Assert(users[1].Id, 2) t.Assert(users[2].Id, 3) @@ -1112,11 +1112,11 @@ func Test_Model_Structs(t *testing.T) { CreateTime *gtime.Time } var users []*User - err := db.Table(table).Order("id asc").Structs(&users) + err := db.Model(table).Order("id asc").Structs(&users) if err != nil { gtest.Error(err) } - t.Assert(len(users), SIZE) + t.Assert(len(users), TableSize) t.Assert(users[0].Id, 1) t.Assert(users[1].Id, 2) t.Assert(users[2].Id, 3) @@ -1135,11 +1135,11 @@ func Test_Model_Structs(t *testing.T) { CreateTime *gtime.Time } var users []*User - err := db.Table(table).Order("id asc").Scan(&users) + err := db.Model(table).Order("id asc").Scan(&users) if err != nil { gtest.Error(err) } - t.Assert(len(users), SIZE) + t.Assert(len(users), TableSize) t.Assert(users[0].Id, 1) t.Assert(users[1].Id, 2) t.Assert(users[2].Id, 3) @@ -1158,7 +1158,7 @@ func Test_Model_Structs(t *testing.T) { CreateTime *gtime.Time } var users []*User - err := db.Table(table).Where("id<0").Structs(&users) + err := db.Model(table).Where("id<0").Structs(&users) t.AssertNil(err) }) } @@ -1176,11 +1176,11 @@ func Test_Model_StructsWithJsonTag(t *testing.T) { Time gtime.Time `json:"create_time"` } var users []User - err := db.Table(table).Order("id asc").Structs(&users) + err := db.Model(table).Order("id asc").Structs(&users) if err != nil { gtest.Error(err) } - t.Assert(len(users), SIZE) + t.Assert(len(users), TableSize) t.Assert(users[0].Uid, 1) t.Assert(users[1].Uid, 2) t.Assert(users[2].Uid, 3) @@ -1204,7 +1204,7 @@ func Test_Model_Scan(t *testing.T) { CreateTime gtime.Time } user := new(User) - err := db.Table(table).Where("id=1").Scan(user) + err := db.Model(table).Where("id=1").Scan(user) t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") @@ -1218,7 +1218,7 @@ func Test_Model_Scan(t *testing.T) { CreateTime *gtime.Time } user := new(User) - err := db.Table(table).Where("id=1").Scan(user) + err := db.Model(table).Where("id=1").Scan(user) t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") @@ -1232,9 +1232,9 @@ func Test_Model_Scan(t *testing.T) { CreateTime gtime.Time } var users []User - err := db.Table(table).Order("id asc").Scan(&users) + err := db.Model(table).Order("id asc").Scan(&users) t.AssertNil(err) - t.Assert(len(users), SIZE) + t.Assert(len(users), TableSize) t.Assert(users[0].Id, 1) t.Assert(users[1].Id, 2) t.Assert(users[2].Id, 3) @@ -1252,9 +1252,9 @@ func Test_Model_Scan(t *testing.T) { CreateTime *gtime.Time } var users []*User - err := db.Table(table).Order("id asc").Scan(&users) + err := db.Model(table).Order("id asc").Scan(&users) t.AssertNil(err) - t.Assert(len(users), SIZE) + t.Assert(len(users), TableSize) t.Assert(users[0].Id, 1) t.Assert(users[1].Id, 2) t.Assert(users[2].Id, 3) @@ -1276,8 +1276,8 @@ func Test_Model_Scan(t *testing.T) { user = new(User) users = new([]*User) ) - err1 := db.Table(table).Where("id < 0").Scan(user) - err2 := db.Table(table).Where("id < 0").Scan(users) + err1 := db.Model(table).Where("id < 0").Scan(user) + err2 := db.Model(table).Where("id < 0").Scan(users) t.Assert(err1, sql.ErrNoRows) t.Assert(err2, nil) }) @@ -1288,10 +1288,10 @@ func Test_Model_OrderBy(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Order("id DESC").Select() + result, err := db.Model(table).Order("id DESC").Select() t.AssertNil(err) - t.Assert(len(result), SIZE) - t.Assert(result[0]["nickname"].String(), fmt.Sprintf("name_%d", SIZE)) + t.Assert(len(result), TableSize) + t.Assert(result[0]["nickname"].String(), fmt.Sprintf("name_%d", TableSize)) }) } @@ -1300,9 +1300,9 @@ func Test_Model_GroupBy(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).GroupBy("id").Select() + result, err := db.Model(table).GroupBy("id").Select() t.AssertNil(err) - t.Assert(len(result), SIZE) + t.Assert(len(result), TableSize) t.Assert(result[0]["nickname"].String(), "name_1") }) } @@ -1311,7 +1311,7 @@ func Test_Model_Data(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createInitTable() defer dropTable(table) - result, err := db.Table(table).Data("nickname=?", "test").Where("id=?", 3).Update() + result, err := db.Model(table).Data("nickname=?", "test").Where("id=?", 3).Update() t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) @@ -1328,7 +1328,7 @@ func Test_Model_Data(t *testing.T) { "nickname": fmt.Sprintf(`nickname_%d`, i), }) } - result, err := db.Table(table).Data(users).Batch(2).Insert() + result, err := db.Model(table).Data(users).Batch(2).Insert() t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 10) @@ -1345,7 +1345,7 @@ func Test_Model_Data(t *testing.T) { "nickname": fmt.Sprintf(`nickname_%d`, i), }) } - result, err := db.Table(table).Data(users).Batch(2).Insert() + result, err := db.Model(table).Data(users).Batch(2).Insert() t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 10) @@ -1358,7 +1358,7 @@ func Test_Model_Where(t *testing.T) { // string gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id=? and nickname=?", 3, "name_3").One() + result, err := db.Model(table).Where("id=? and nickname=?", 3, "name_3").One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) @@ -1366,13 +1366,13 @@ func Test_Model_Where(t *testing.T) { // slice gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(g.Slice{"id", 3}).One() + result, err := db.Model(table).Where(g.Slice{"id", 3}).One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(g.Slice{"id", 3, "nickname", "name_3"}).One() + result, err := db.Model(table).Where(g.Slice{"id", 3, "nickname", "name_3"}).One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) @@ -1380,14 +1380,14 @@ func Test_Model_Where(t *testing.T) { // slice parameter gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id=? and nickname=?", g.Slice{3, "name_3"}).One() + result, err := db.Model(table).Where("id=? and nickname=?", g.Slice{3, "name_3"}).One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) // map like gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(g.Map{ + result, err := db.Model(table).Where(g.Map{ "passport like": "user_1%", }).Order("id asc").All() t.AssertNil(err) @@ -1397,7 +1397,7 @@ func Test_Model_Where(t *testing.T) { }) // map + slice parameter gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(g.Map{ + result, err := db.Model(table).Where(g.Map{ "id": g.Slice{1, 2, 3}, "passport": g.Slice{"user_2", "user_3"}, }).And("id=? and nickname=?", g.Slice{3, "name_3"}).One() @@ -1406,112 +1406,112 @@ func Test_Model_Where(t *testing.T) { t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id=3", g.Slice{}).One() + result, err := db.Model(table).Where("id=3", g.Slice{}).One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id=?", g.Slice{3}).One() + result, err := db.Model(table).Where("id=?", g.Slice{3}).One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id", 3).One() + result, err := db.Model(table).Where("id", 3).One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id", 3).Where("nickname", "name_3").One() + result, err := db.Model(table).Where("id", 3).Where("nickname", "name_3").One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id", 3).And("nickname", "name_3").One() + result, err := db.Model(table).Where("id", 3).And("nickname", "name_3").One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id", 30).Or("nickname", "name_3").One() + result, err := db.Model(table).Where("id", 30).Or("nickname", "name_3").One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id", 30).Or("nickname", "name_3").And("id>?", 1).One() + result, err := db.Model(table).Where("id", 30).Or("nickname", "name_3").And("id>?", 1).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id", 30).Or("nickname", "name_3").And("id>", 1).One() + result, err := db.Model(table).Where("id", 30).Or("nickname", "name_3").And("id>", 1).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // slice gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id=? AND nickname=?", g.Slice{3, "name_3"}...).One() + result, err := db.Model(table).Where("id=? AND nickname=?", g.Slice{3, "name_3"}...).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id=? AND nickname=?", g.Slice{3, "name_3"}).One() + result, err := db.Model(table).Where("id=? AND nickname=?", g.Slice{3, "name_3"}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("passport like ? and nickname like ?", g.Slice{"user_3", "name_3"}).One() + result, err := db.Model(table).Where("passport like ? and nickname like ?", g.Slice{"user_3", "name_3"}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // map gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(g.Map{"id": 3, "nickname": "name_3"}).One() + result, err := db.Model(table).Where(g.Map{"id": 3, "nickname": "name_3"}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // map key operator gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(g.Map{"id>": 1, "id<": 3}).One() + result, err := db.Model(table).Where(g.Map{"id>": 1, "id<": 3}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // gmap.Map gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(gmap.NewFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() + result, err := db.Model(table).Where(gmap.NewFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // gmap.Map key operator gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(gmap.NewFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() + result, err := db.Model(table).Where(gmap.NewFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // list map gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(gmap.NewListMapFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() + result, err := db.Model(table).Where(gmap.NewListMapFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // list map key operator gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(gmap.NewListMapFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() + result, err := db.Model(table).Where(gmap.NewListMapFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // tree map gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() + result, err := db.Model(table).Where(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // tree map key operator gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id>": 1, "id<": 3})).One() + result, err := db.Model(table).Where(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id>": 1, "id<": 3})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) @@ -1526,7 +1526,7 @@ func Test_Model_Where(t *testing.T) { "create_time > 0": nil, "id": g.Slice{1, 2, 3}, } - result, err := db.Table(table).Where(conditions).Order("id asc").All() + result, err := db.Model(table).Where(conditions).Order("id asc").All() t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) @@ -1541,7 +1541,7 @@ func Test_Model_Where(t *testing.T) { "create_time > ?": 0, "id in(?)": g.Slice{1, 2, 3}, } - result, err := db.Table(table).Where(conditions).Order("id asc").All() + result, err := db.Model(table).Where(conditions).Order("id asc").All() t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) @@ -1552,17 +1552,17 @@ func Test_Model_Where(t *testing.T) { Id int `json:"id"` Nickname string `gconv:"nickname"` } - result, err := db.Table(table).Where(User{3, "name_3"}).One() + result, err := db.Model(table).Where(User{3, "name_3"}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) - result, err = db.Table(table).Where(&User{3, "name_3"}).One() + result, err = db.Model(table).Where(&User{3, "name_3"}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // slice single gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("id IN(?)", g.Slice{1, 3}).Order("id ASC").All() + result, err := db.Model(table).Where("id IN(?)", g.Slice{1, 3}).Order("id ASC").All() t.AssertNil(err) t.Assert(len(result), 2) t.Assert(result[0]["id"].Int(), 1) @@ -1570,14 +1570,14 @@ func Test_Model_Where(t *testing.T) { }) // slice + string gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("nickname=? AND id IN(?)", "name_3", g.Slice{1, 3}).Order("id ASC").All() + result, err := db.Model(table).Where("nickname=? AND id IN(?)", "name_3", g.Slice{1, 3}).Order("id ASC").All() t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 3) }) // slice + map gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(g.Map{ + result, err := db.Model(table).Where(g.Map{ "id": g.Slice{1, 3}, "nickname": "name_3", }).Order("id ASC").All() @@ -1591,7 +1591,7 @@ func Test_Model_Where(t *testing.T) { Ids []int `json:"id"` Nickname string `gconv:"nickname"` } - result, err := db.Table(table).Where(User{ + result, err := db.Model(table).Where(User{ Ids: []int{1, 3}, Nickname: "name_3", }).Order("id ASC").All() @@ -1607,12 +1607,12 @@ func Test_Model_Where_ISNULL_1(t *testing.T) { gtest.C(t, func(t *gtest.T) { //db.SetDebug(true) - result, err := db.Table(table).Data("nickname", nil).Where("id", 2).Update() + result, err := db.Model(table).Data("nickname", nil).Where("id", 2).Update() t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) - one, err := db.Table(table).Where("nickname", nil).One() + one, err := db.Model(table).Where("nickname", nil).One() t.AssertNil(err) t.Assert(one.IsEmpty(), false) t.Assert(one["id"], 2) @@ -1633,7 +1633,7 @@ func Test_Model_Where_ISNULL_2(t *testing.T) { "create_time > 0": nil, "id": g.Slice{1, 2, 3}, } - result, err := db.Table(table).WherePri(conditions).Order("id asc").All() + result, err := db.Model(table).WherePri(conditions).Order("id asc").All() t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) @@ -1647,7 +1647,7 @@ func Test_Model_Where_OmitEmpty(t *testing.T) { conditions := g.Map{ "id < 4": "", } - result, err := db.Table(table).WherePri(conditions).Order("id asc").All() + result, err := db.Model(table).WherePri(conditions).Order("id asc").All() t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) @@ -1656,7 +1656,7 @@ func Test_Model_Where_OmitEmpty(t *testing.T) { conditions := g.Map{ "id < 4": "", } - result, err := db.Table(table).WherePri(conditions).OmitEmpty().Order("id asc").All() + result, err := db.Model(table).WherePri(conditions).OmitEmpty().Order("id asc").All() t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) @@ -1668,12 +1668,12 @@ func Test_Model_Where_GTime(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("create_time>?", gtime.NewFromStr("2010-09-01")).All() + result, err := db.Model(table).Where("create_time>?", gtime.NewFromStr("2010-09-01")).All() t.AssertNil(err) t.Assert(len(result), 10) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where("create_time>?", *gtime.NewFromStr("2010-09-01")).All() + result, err := db.Model(table).Where("create_time>?", *gtime.NewFromStr("2010-09-01")).All() t.AssertNil(err) t.Assert(len(result), 10) }) @@ -1685,13 +1685,13 @@ func Test_Model_WherePri(t *testing.T) { // primary key gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).WherePri(3).One() + one, err := db.Model(table).WherePri(3).One() t.AssertNil(err) t.AssertNE(one, nil) t.Assert(one["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table).WherePri(g.Slice{3, 9}).Order("id asc").All() + all, err := db.Model(table).WherePri(g.Slice{3, 9}).Order("id asc").All() t.AssertNil(err) t.Assert(len(all), 2) t.Assert(all[0]["id"].Int(), 3) @@ -1700,21 +1700,21 @@ func Test_Model_WherePri(t *testing.T) { // string gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id=? and nickname=?", 3, "name_3").One() + result, err := db.Model(table).WherePri("id=? and nickname=?", 3, "name_3").One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) // slice parameter gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id=? and nickname=?", g.Slice{3, "name_3"}).One() + result, err := db.Model(table).WherePri("id=? and nickname=?", g.Slice{3, "name_3"}).One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) // map like gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(g.Map{ + result, err := db.Model(table).WherePri(g.Map{ "passport like": "user_1%", }).Order("id asc").All() t.AssertNil(err) @@ -1724,7 +1724,7 @@ func Test_Model_WherePri(t *testing.T) { }) // map + slice parameter gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(g.Map{ + result, err := db.Model(table).WherePri(g.Map{ "id": g.Slice{1, 2, 3}, "passport": g.Slice{"user_2", "user_3"}, }).And("id=? and nickname=?", g.Slice{3, "name_3"}).One() @@ -1733,7 +1733,7 @@ func Test_Model_WherePri(t *testing.T) { t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(g.Map{ + result, err := db.Model(table).WherePri(g.Map{ "id": g.Slice{1, 2, 3}, "passport": g.Slice{"user_2", "user_3"}, }).Or("nickname=?", g.Slice{"name_4"}).And("id", 3).One() @@ -1742,112 +1742,112 @@ func Test_Model_WherePri(t *testing.T) { t.Assert(result["id"].Int(), 2) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id=3", g.Slice{}).One() + result, err := db.Model(table).WherePri("id=3", g.Slice{}).One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id=?", g.Slice{3}).One() + result, err := db.Model(table).WherePri("id=?", g.Slice{3}).One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id", 3).One() + result, err := db.Model(table).WherePri("id", 3).One() t.AssertNil(err) t.AssertGT(len(result), 0) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id", 3).WherePri("nickname", "name_3").One() + result, err := db.Model(table).WherePri("id", 3).WherePri("nickname", "name_3").One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id", 3).And("nickname", "name_3").One() + result, err := db.Model(table).WherePri("id", 3).And("nickname", "name_3").One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id", 30).Or("nickname", "name_3").One() + result, err := db.Model(table).WherePri("id", 30).Or("nickname", "name_3").One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id", 30).Or("nickname", "name_3").And("id>?", 1).One() + result, err := db.Model(table).WherePri("id", 30).Or("nickname", "name_3").And("id>?", 1).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id", 30).Or("nickname", "name_3").And("id>", 1).One() + result, err := db.Model(table).WherePri("id", 30).Or("nickname", "name_3").And("id>", 1).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // slice gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id=? AND nickname=?", g.Slice{3, "name_3"}...).One() + result, err := db.Model(table).WherePri("id=? AND nickname=?", g.Slice{3, "name_3"}...).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id=? AND nickname=?", g.Slice{3, "name_3"}).One() + result, err := db.Model(table).WherePri("id=? AND nickname=?", g.Slice{3, "name_3"}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("passport like ? and nickname like ?", g.Slice{"user_3", "name_3"}).One() + result, err := db.Model(table).WherePri("passport like ? and nickname like ?", g.Slice{"user_3", "name_3"}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // map gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(g.Map{"id": 3, "nickname": "name_3"}).One() + result, err := db.Model(table).WherePri(g.Map{"id": 3, "nickname": "name_3"}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // map key operator gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(g.Map{"id>": 1, "id<": 3}).One() + result, err := db.Model(table).WherePri(g.Map{"id>": 1, "id<": 3}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // gmap.Map gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(gmap.NewFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() + result, err := db.Model(table).WherePri(gmap.NewFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // gmap.Map key operator gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(gmap.NewFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() + result, err := db.Model(table).WherePri(gmap.NewFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // list map gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(gmap.NewListMapFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() + result, err := db.Model(table).WherePri(gmap.NewListMapFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // list map key operator gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(gmap.NewListMapFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() + result, err := db.Model(table).WherePri(gmap.NewListMapFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) // tree map gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() + result, err := db.Model(table).WherePri(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id": 3, "nickname": "name_3"})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // tree map key operator gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id>": 1, "id<": 3})).One() + result, err := db.Model(table).WherePri(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id>": 1, "id<": 3})).One() t.AssertNil(err) t.Assert(result["id"].Int(), 2) }) @@ -1862,7 +1862,7 @@ func Test_Model_WherePri(t *testing.T) { "create_time > 0": nil, "id": g.Slice{1, 2, 3}, } - result, err := db.Table(table).WherePri(conditions).Order("id asc").All() + result, err := db.Model(table).WherePri(conditions).Order("id asc").All() t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) @@ -1877,7 +1877,7 @@ func Test_Model_WherePri(t *testing.T) { "create_time > ?": 0, "id in(?)": g.Slice{1, 2, 3}, } - result, err := db.Table(table).WherePri(conditions).Order("id asc").All() + result, err := db.Model(table).WherePri(conditions).Order("id asc").All() t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) @@ -1888,17 +1888,17 @@ func Test_Model_WherePri(t *testing.T) { Id int `json:"id"` Nickname string `gconv:"nickname"` } - result, err := db.Table(table).WherePri(User{3, "name_3"}).One() + result, err := db.Model(table).WherePri(User{3, "name_3"}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) - result, err = db.Table(table).WherePri(&User{3, "name_3"}).One() + result, err = db.Model(table).WherePri(&User{3, "name_3"}).One() t.AssertNil(err) t.Assert(result["id"].Int(), 3) }) // slice single gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("id IN(?)", g.Slice{1, 3}).Order("id ASC").All() + result, err := db.Model(table).WherePri("id IN(?)", g.Slice{1, 3}).Order("id ASC").All() t.AssertNil(err) t.Assert(len(result), 2) t.Assert(result[0]["id"].Int(), 1) @@ -1906,14 +1906,14 @@ func Test_Model_WherePri(t *testing.T) { }) // slice + string gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri("nickname=? AND id IN(?)", "name_3", g.Slice{1, 3}).Order("id ASC").All() + result, err := db.Model(table).WherePri("nickname=? AND id IN(?)", "name_3", g.Slice{1, 3}).Order("id ASC").All() t.AssertNil(err) t.Assert(len(result), 1) t.Assert(result[0]["id"].Int(), 3) }) // slice + map gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).WherePri(g.Map{ + result, err := db.Model(table).WherePri(g.Map{ "id": g.Slice{1, 3}, "nickname": "name_3", }).Order("id ASC").All() @@ -1927,7 +1927,7 @@ func Test_Model_WherePri(t *testing.T) { Ids []int `json:"id"` Nickname string `gconv:"nickname"` } - result, err := db.Table(table).WherePri(User{ + result, err := db.Model(table).WherePri(User{ Ids: []int{1, 3}, Nickname: "name_3", }).Order("id ASC").All() @@ -1943,17 +1943,17 @@ func Test_Model_Delete(t *testing.T) { // DELETE...LIMIT gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(1).Limit(2).Delete() + result, err := db.Model(table).Where(1).Limit(2).Delete() t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 2) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(1).Delete() + result, err := db.Model(table).Where(1).Delete() t.AssertNil(err) n, _ := result.RowsAffected() - t.Assert(n, SIZE-2) + t.Assert(n, TableSize-2) }) } @@ -1961,7 +1961,7 @@ func Test_Model_Offset(t *testing.T) { table := createInitTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Limit(2).Offset(5).Order("id").Select() + result, err := db.Model(table).Limit(2).Offset(5).Order("id").Select() t.AssertNil(err) t.Assert(len(result), 2) t.Assert(result[0]["id"], 6) @@ -1973,20 +1973,20 @@ func Test_Model_Page(t *testing.T) { table := createInitTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Page(3, 3).Order("id").All() + result, err := db.Model(table).Page(3, 3).Order("id").All() t.AssertNil(err) t.Assert(len(result), 3) t.Assert(result[0]["id"], 7) t.Assert(result[1]["id"], 8) }) gtest.C(t, func(t *gtest.T) { - model := db.Table(table).Safe().Order("id") + model := db.Model(table).Safe().Order("id") all, err := model.Page(3, 3).All() count, err := model.Count() t.AssertNil(err) t.Assert(len(all), 3) t.Assert(all[0]["id"], "7") - t.Assert(count, SIZE) + t.Assert(count, TableSize) }) } @@ -1995,7 +1995,7 @@ func Test_Model_Option_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - r, err := db.Table(table).Fields("id, passport").Data(g.Map{ + r, err := db.Model(table).Fields("id, passport").Data(g.Map{ "id": 1, "passport": "1", "password": "1", @@ -2004,7 +2004,7 @@ func Test_Model_Option_Map(t *testing.T) { t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - one, err := db.Table(table).Where("id", 1).One() + one, err := db.Model(table).Where("id", 1).One() t.AssertNil(err) t.AssertNE(one["password"].String(), "1") t.AssertNE(one["nickname"].String(), "1") @@ -2013,7 +2013,7 @@ func Test_Model_Option_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - r, err := db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ + r, err := db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ "id": 1, "passport": 0, "password": 0, @@ -2022,7 +2022,7 @@ func Test_Model_Option_Map(t *testing.T) { t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - one, err := db.Table(table).Where("id", 1).One() + one, err := db.Model(table).Where("id", 1).One() t.AssertNil(err) t.AssertNE(one["passport"].String(), "0") t.AssertNE(one["password"].String(), "0") @@ -2033,14 +2033,14 @@ func Test_Model_Option_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createInitTable() defer dropTable(table) - _, err := db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ + _, err := db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ "id": 1, "passport": 0, "password": 0, "nickname": "1", }).Replace() t.AssertNil(err) - one, err := db.Table(table).Where("id", 1).One() + one, err := db.Model(table).Where("id", 1).One() t.AssertNil(err) t.AssertNE(one["passport"].String(), "0") t.AssertNE(one["password"].String(), "0") @@ -2051,7 +2051,7 @@ func Test_Model_Option_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - r, err := db.Table(table).Fields("id, passport").Data(g.Map{ + r, err := db.Model(table).Fields("id, passport").Data(g.Map{ "id": 1, "passport": "1", "password": "1", @@ -2060,7 +2060,7 @@ func Test_Model_Option_Map(t *testing.T) { t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - one, err := db.Table(table).Where("id", 1).One() + one, err := db.Model(table).Where("id", 1).One() t.AssertNil(err) t.AssertNE(one["password"].String(), "1") t.AssertNE(one["nickname"].String(), "1") @@ -2069,27 +2069,27 @@ func Test_Model_Option_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - _, err := db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ + _, err := db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ "id": 1, "passport": 0, "password": 0, "nickname": "1", }).Save() t.AssertNil(err) - one, err := db.Table(table).Where("id", 1).One() + one, err := db.Model(table).Where("id", 1).One() t.AssertNil(err) t.AssertNE(one["passport"].String(), "0") t.AssertNE(one["password"].String(), "0") t.Assert(one["nickname"].String(), "1") - _, err = db.Table(table).Data(g.Map{ + _, err = db.Model(table).Data(g.Map{ "id": 1, "passport": 0, "password": 0, "nickname": "1", }).Save() t.AssertNil(err) - one, err = db.Table(table).Where("id", 1).One() + one, err = db.Model(table).Where("id", 1).One() t.AssertNil(err) t.Assert(one["passport"].String(), "0") t.Assert(one["password"].String(), "0") @@ -2101,23 +2101,23 @@ func Test_Model_Option_Map(t *testing.T) { table := createInitTable() defer dropTable(table) - r, err := db.Table(table).Data(g.Map{"nickname": ""}).Where("id", 1).Update() + r, err := db.Model(table).Data(g.Map{"nickname": ""}).Where("id", 1).Update() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - _, err = db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{"nickname": ""}).Where("id", 2).Update() + _, err = db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{"nickname": ""}).Where("id", 2).Update() t.AssertNE(err, nil) - r, err = db.Table(table).OmitEmpty().Data(g.Map{"nickname": "", "password": "123"}).Where("id", 3).Update() + r, err = db.Model(table).OmitEmpty().Data(g.Map{"nickname": "", "password": "123"}).Where("id", 3).Update() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) - _, err = db.Table(table).OmitEmpty().Fields("nickname").Data(g.Map{"nickname": "", "password": "123"}).Where("id", 4).Update() + _, err = db.Model(table).OmitEmpty().Fields("nickname").Data(g.Map{"nickname": "", "password": "123"}).Where("id", 4).Update() t.AssertNE(err, nil) - r, err = db.Table(table).OmitEmpty(). + r, err = db.Model(table).OmitEmpty(). Fields("password").Data(g.Map{ "nickname": "", "passport": "123", @@ -2127,7 +2127,7 @@ func Test_Model_Option_Map(t *testing.T) { n, _ = r.RowsAffected() t.Assert(n, 1) - one, err := db.Table(table).Where("id", 5).One() + one, err := db.Model(table).Where("id", 5).One() t.AssertNil(err) t.Assert(one["password"], "456") t.AssertNE(one["passport"].String(), "") @@ -2139,7 +2139,7 @@ func Test_Model_Option_List(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - r, err := db.Table(table).Fields("id, password").Data(g.List{ + r, err := db.Model(table).Fields("id, password").Data(g.List{ g.Map{ "id": 1, "passport": "1", @@ -2156,7 +2156,7 @@ func Test_Model_Option_List(t *testing.T) { t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 2) - list, err := db.Table(table).Order("id asc").All() + list, err := db.Model(table).Order("id asc").All() t.AssertNil(err) t.Assert(len(list), 2) t.Assert(list[0]["id"].String(), "1") @@ -2173,7 +2173,7 @@ func Test_Model_Option_List(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - r, err := db.Table(table).OmitEmpty().Fields("id, password").Data(g.List{ + r, err := db.Model(table).OmitEmpty().Fields("id, password").Data(g.List{ g.Map{ "id": 1, "passport": "1", @@ -2190,7 +2190,7 @@ func Test_Model_Option_List(t *testing.T) { t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 2) - list, err := db.Table(table).Order("id asc").All() + list, err := db.Model(table).Order("id asc").All() t.AssertNil(err) t.Assert(len(list), 2) t.Assert(list[0]["id"].String(), "1") @@ -2219,7 +2219,7 @@ func Test_Model_OmitEmpty(t *testing.T) { gtest.Error(err) } defer dropTable(table) - _, err := db.Table(table).OmitEmpty().Data(g.Map{ + _, err := db.Model(table).OmitEmpty().Data(g.Map{ "id": 1, "name": "", }).Save() @@ -2231,20 +2231,20 @@ func Test_Model_Option_Where(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createInitTable() defer dropTable(table) - r, err := db.Table(table).OmitEmpty().Data("nickname", 1).Where(g.Map{"id": 0, "passport": ""}).And(1).Update() + r, err := db.Model(table).OmitEmpty().Data("nickname", 1).Where(g.Map{"id": 0, "passport": ""}).And(1).Update() t.AssertNil(err) n, _ := r.RowsAffected() - t.Assert(n, SIZE) + t.Assert(n, TableSize) }) gtest.C(t, func(t *gtest.T) { table := createInitTable() defer dropTable(table) - r, err := db.Table(table).OmitEmpty().Data("nickname", 1).Where(g.Map{"id": 1, "passport": ""}).Update() + r, err := db.Model(table).OmitEmpty().Data("nickname", 1).Where(g.Map{"id": 1, "passport": ""}).Update() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - v, err := db.Table(table).Where("id", 1).Fields("nickname").Value() + v, err := db.Model(table).Where("id", 1).Fields("nickname").Value() t.AssertNil(err) t.Assert(v.String(), "1") }) @@ -2254,7 +2254,7 @@ func Test_Model_Where_MultiSliceArguments(t *testing.T) { table := createInitTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - r, err := db.Table(table).Where(g.Map{ + r, err := db.Model(table).Where(g.Map{ "id": g.Slice{1, 2, 3, 4}, "passport": g.Slice{"user_2", "user_3", "user_4"}, "nickname": g.Slice{"name_2", "name_4"}, @@ -2266,7 +2266,7 @@ func Test_Model_Where_MultiSliceArguments(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).Where(g.Map{ + result, err := db.Model(table).Where(g.Map{ "id": g.Slice{1, 2, 3}, "passport": g.Slice{"user_2", "user_3"}, }).Or("nickname=?", g.Slice{"name_4"}).And("id", 3).One() @@ -2281,7 +2281,7 @@ func Test_Model_FieldsEx(t *testing.T) { defer dropTable(table) // Select. gtest.C(t, func(t *gtest.T) { - r, err := db.Table(table).FieldsEx("create_time, id").Where("id in (?)", g.Slice{1, 2}).Order("id asc").All() + r, err := db.Model(table).FieldsEx("create_time, id").Where("id in (?)", g.Slice{1, 2}).Order("id asc").All() t.AssertNil(err) t.Assert(len(r), 2) t.Assert(len(r[0]), 3) @@ -2298,12 +2298,12 @@ func Test_Model_FieldsEx(t *testing.T) { }) // Update. gtest.C(t, func(t *gtest.T) { - r, err := db.Table(table).FieldsEx("password").Data(g.Map{"nickname": "123", "password": "456"}).Where("id", 3).Update() + r, err := db.Model(table).FieldsEx("password").Data(g.Map{"nickname": "123", "password": "456"}).Where("id", 3).Update() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - one, err := db.Table(table).Where("id", 3).One() + one, err := db.Model(table).Where("id", 3).One() t.AssertNil(err) t.Assert(one["nickname"], "123") t.AssertNE(one["password"], "456") @@ -2318,7 +2318,7 @@ func Test_Model_FieldsEx_WithReservedWords(t *testing.T) { } defer dropTable(table) gtest.C(t, func(t *gtest.T) { - _, err := db.Table(table).FieldsEx("content").One() + _, err := db.Model(table).FieldsEx("content").One() t.AssertNil(err) }) } @@ -2328,8 +2328,8 @@ func Test_Model_FieldsStr(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - t.Assert(db.Table(table).FieldsStr(), "`id`,`passport`,`password`,`nickname`,`create_time`") - t.Assert(db.Table(table).FieldsStr("a."), "`a`.`id`,`a`.`passport`,`a`.`password`,`a`.`nickname`,`a`.`create_time`") + t.Assert(db.Model(table).FieldsStr(), "`id`,`passport`,`password`,`nickname`,`create_time`") + t.Assert(db.Model(table).FieldsStr("a."), "`a`.`id`,`a`.`passport`,`a`.`password`,`a`.`nickname`,`a`.`create_time`") }) } @@ -2338,19 +2338,19 @@ func Test_Model_FieldsExStr(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - t.Assert(db.Table(table).FieldsExStr("create_time,nickname"), "`id`,`passport`,`password`") - t.Assert(db.Table(table).FieldsExStr("create_time,nickname", "a."), "`a`.`id`,`a`.`passport`,`a`.`password`") + t.Assert(db.Model(table).FieldsExStr("create_time,nickname"), "`id`,`passport`,`password`") + t.Assert(db.Model(table).FieldsExStr("create_time,nickname", "a."), "`a`.`id`,`a`.`passport`,`a`.`password`") }) } func Test_Model_Prefix(t *testing.T) { db := dbPrefix - table := fmt.Sprintf(`%s_%d`, TABLE, gtime.TimestampNano()) - createInitTableWithDb(db, PREFIX1+table) - defer dropTable(PREFIX1 + table) + table := fmt.Sprintf(`%s_%d`, TableName, gtime.TimestampNano()) + createInitTableWithDb(db, TableNamePrefix1+table) + defer dropTable(TableNamePrefix1 + table) // Select. gtest.C(t, func(t *gtest.T) { - r, err := db.Table(table).Where("id in (?)", g.Slice{1, 2}).Order("id asc").All() + r, err := db.Model(table).Where("id in (?)", g.Slice{1, 2}).Order("id asc").All() t.AssertNil(err) t.Assert(len(r), 2) t.Assert(r[0]["id"], "1") @@ -2358,7 +2358,7 @@ func Test_Model_Prefix(t *testing.T) { }) // Select with alias. gtest.C(t, func(t *gtest.T) { - r, err := db.Table(table+" as u").Where("u.id in (?)", g.Slice{1, 2}).Order("u.id asc").All() + r, err := db.Model(table+" as u").Where("u.id in (?)", g.Slice{1, 2}).Order("u.id asc").All() t.AssertNil(err) t.Assert(len(r), 2) t.Assert(r[0]["id"], "1") @@ -2366,14 +2366,14 @@ func Test_Model_Prefix(t *testing.T) { }) // Select with alias and join statement. gtest.C(t, func(t *gtest.T) { - r, err := db.Table(table+" as u1").LeftJoin(table+" as u2", "u2.id=u1.id").Where("u1.id in (?)", g.Slice{1, 2}).Order("u1.id asc").All() + r, err := db.Model(table+" as u1").LeftJoin(table+" as u2", "u2.id=u1.id").Where("u1.id in (?)", g.Slice{1, 2}).Order("u1.id asc").All() t.AssertNil(err) t.Assert(len(r), 2) t.Assert(r[0]["id"], "1") t.Assert(r[1]["id"], "2") }) gtest.C(t, func(t *gtest.T) { - r, err := db.Table(table).As("u1").LeftJoin(table+" as u2", "u2.id=u1.id").Where("u1.id in (?)", g.Slice{1, 2}).Order("u1.id asc").All() + r, err := db.Model(table).As("u1").LeftJoin(table+" as u2", "u2.id=u1.id").Where("u1.id in (?)", g.Slice{1, 2}).Order("u1.id asc").All() t.AssertNil(err) t.Assert(len(r), 2) t.Assert(r[0]["id"], "1") @@ -2384,63 +2384,63 @@ func Test_Model_Prefix(t *testing.T) { func Test_Model_Schema1(t *testing.T) { //db.SetDebug(true) - db.SetSchema(SCHEMA1) - table := fmt.Sprintf(`%s_%s`, TABLE, gtime.TimestampNanoStr()) + db.SetSchema(TestSchema1) + table := fmt.Sprintf(`%s_%s`, TableName, gtime.TimestampNanoStr()) createInitTableWithDb(db, table) - db.SetSchema(SCHEMA2) + db.SetSchema(TestSchema2) createInitTableWithDb(db, table) defer func() { - db.SetSchema(SCHEMA1) + db.SetSchema(TestSchema1) dropTableWithDb(db, table) - db.SetSchema(SCHEMA2) + db.SetSchema(TestSchema2) dropTableWithDb(db, table) - db.SetSchema(SCHEMA1) + db.SetSchema(TestSchema1) }() // Method. gtest.C(t, func(t *gtest.T) { - db.SetSchema(SCHEMA1) - r, err := db.Table(table).Update(g.Map{"nickname": "name_100"}, "id=1") + db.SetSchema(TestSchema1) + r, err := db.Model(table).Update(g.Map{"nickname": "name_100"}, "id=1") t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - v, err := db.Table(table).Value("nickname", "id=1") + v, err := db.Model(table).Value("nickname", "id=1") t.AssertNil(err) t.Assert(v.String(), "name_100") - db.SetSchema(SCHEMA2) - v, err = db.Table(table).Value("nickname", "id=1") + db.SetSchema(TestSchema2) + v, err = db.Model(table).Value("nickname", "id=1") t.AssertNil(err) t.Assert(v.String(), "name_1") }) // Model. gtest.C(t, func(t *gtest.T) { - v, err := db.Table(table).Schema(SCHEMA1).Value("nickname", "id=2") + v, err := db.Model(table).Schema(TestSchema1).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_2") - r, err := db.Table(table).Schema(SCHEMA1).Update(g.Map{"nickname": "name_200"}, "id=2") + r, err := db.Model(table).Schema(TestSchema1).Update(g.Map{"nickname": "name_200"}, "id=2") t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - v, err = db.Table(table).Schema(SCHEMA1).Value("nickname", "id=2") + v, err = db.Model(table).Schema(TestSchema1).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_200") - v, err = db.Table(table).Schema(SCHEMA2).Value("nickname", "id=2") + v, err = db.Model(table).Schema(TestSchema2).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_2") - v, err = db.Table(table).Schema(SCHEMA1).Value("nickname", "id=2") + v, err = db.Model(table).Schema(TestSchema1).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_200") }) // Model. gtest.C(t, func(t *gtest.T) { i := 1000 - _, err := db.Table(table).Schema(SCHEMA1).Filter().Insert(g.Map{ + _, err := db.Model(table).Schema(TestSchema1).Filter().Insert(g.Map{ "id": i, "passport": fmt.Sprintf(`user_%d`, i), "password": fmt.Sprintf(`pass_%d`, i), @@ -2450,11 +2450,11 @@ func Test_Model_Schema1(t *testing.T) { }) t.AssertNil(err) - v, err := db.Table(table).Schema(SCHEMA1).Value("nickname", "id=?", i) + v, err := db.Model(table).Schema(TestSchema1).Value("nickname", "id=?", i) t.AssertNil(err) t.Assert(v.String(), "name_1000") - v, err = db.Table(table).Schema(SCHEMA2).Value("nickname", "id=?", i) + v, err = db.Model(table).Schema(TestSchema2).Value("nickname", "id=?", i) t.AssertNil(err) t.Assert(v.String(), "") }) @@ -2463,46 +2463,46 @@ func Test_Model_Schema1(t *testing.T) { func Test_Model_Schema2(t *testing.T) { //db.SetDebug(true) - db.SetSchema(SCHEMA1) - table := fmt.Sprintf(`%s_%s`, TABLE, gtime.TimestampNanoStr()) + db.SetSchema(TestSchema1) + table := fmt.Sprintf(`%s_%s`, TableName, gtime.TimestampNanoStr()) createInitTableWithDb(db, table) - db.SetSchema(SCHEMA2) + db.SetSchema(TestSchema2) createInitTableWithDb(db, table) defer func() { - db.SetSchema(SCHEMA1) + db.SetSchema(TestSchema1) dropTableWithDb(db, table) - db.SetSchema(SCHEMA2) + db.SetSchema(TestSchema2) dropTableWithDb(db, table) - db.SetSchema(SCHEMA1) + db.SetSchema(TestSchema1) }() // Schema. gtest.C(t, func(t *gtest.T) { - v, err := db.Schema(SCHEMA1).Table(table).Value("nickname", "id=2") + v, err := db.Schema(TestSchema1).Table(table).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_2") - r, err := db.Schema(SCHEMA1).Table(table).Update(g.Map{"nickname": "name_200"}, "id=2") + r, err := db.Schema(TestSchema1).Table(table).Update(g.Map{"nickname": "name_200"}, "id=2") t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - v, err = db.Schema(SCHEMA1).Table(table).Value("nickname", "id=2") + v, err = db.Schema(TestSchema1).Table(table).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_200") - v, err = db.Schema(SCHEMA2).Table(table).Value("nickname", "id=2") + v, err = db.Schema(TestSchema2).Table(table).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_2") - v, err = db.Schema(SCHEMA1).Table(table).Value("nickname", "id=2") + v, err = db.Schema(TestSchema1).Table(table).Value("nickname", "id=2") t.AssertNil(err) t.Assert(v.String(), "name_200") }) // Schema. gtest.C(t, func(t *gtest.T) { i := 1000 - _, err := db.Schema(SCHEMA1).Table(table).Filter().Insert(g.Map{ + _, err := db.Schema(TestSchema1).Table(table).Filter().Insert(g.Map{ "id": i, "passport": fmt.Sprintf(`user_%d`, i), "password": fmt.Sprintf(`pass_%d`, i), @@ -2512,11 +2512,11 @@ func Test_Model_Schema2(t *testing.T) { }) t.AssertNil(err) - v, err := db.Schema(SCHEMA1).Table(table).Value("nickname", "id=?", i) + v, err := db.Schema(TestSchema1).Table(table).Value("nickname", "id=?", i) t.AssertNil(err) t.Assert(v.String(), "name_1000") - v, err = db.Schema(SCHEMA2).Table(table).Value("nickname", "id=?", i) + v, err = db.Schema(TestSchema2).Table(table).Value("nickname", "id=?", i) t.AssertNil(err) t.Assert(v.String(), "") }) @@ -2538,7 +2538,7 @@ func Test_Model_FieldsExStruct(t *testing.T) { Password: "222", NickName: "333", } - r, err := db.Table(table).FieldsEx("create_time, password").OmitEmpty().Data(user).Insert() + r, err := db.Model(table).FieldsEx("create_time, password").OmitEmpty().Data(user).Insert() t.AssertNil(err) n, err := r.RowsAffected() t.AssertNil(err) @@ -2560,7 +2560,7 @@ func Test_Model_FieldsExStruct(t *testing.T) { NickName: fmt.Sprintf(`nickname_%d`, i), }) } - r, err := db.Table(table).FieldsEx("create_time, password"). + r, err := db.Model(table).FieldsEx("create_time, password"). OmitEmpty(). Batch(2). Data(users). @@ -2588,7 +2588,7 @@ func Test_Model_OmitEmpty_Time(t *testing.T) { Password: "222", Time: time.Time{}, } - r, err := db.Table(table).OmitEmpty().Data(user).WherePri(1).Update() + r, err := db.Model(table).OmitEmpty().Data(user).WherePri(1).Update() t.AssertNil(err) n, err := r.RowsAffected() t.AssertNil(err) @@ -2600,7 +2600,7 @@ func Test_Result_Chunk(t *testing.T) { table := createInitTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - r, err := db.Table(table).Order("id asc").All() + r, err := db.Model(table).Order("id asc").All() t.AssertNil(err) chunks := r.Chunk(3) t.Assert(len(chunks), 4) @@ -2618,12 +2618,12 @@ func Test_Model_DryRun(t *testing.T) { defer db.SetDryRun(false) gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).FindOne(1) + one, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(one["id"], 1) }) gtest.C(t, func(t *gtest.T) { - r, err := db.Table(table).Data("passport", "port_1").WherePri(1).Update() + r, err := db.Model(table).Data("passport", "port_1").WherePri(1).Update() t.AssertNil(err) n, err := r.RowsAffected() t.AssertNil(err) @@ -2636,11 +2636,11 @@ func Test_Model_Join_SubQuery(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { subQuery := fmt.Sprintf("select * from `%s`", table) - r, err := db.Table(table, "t1").Fields("t2.id").LeftJoin(subQuery, "t2", "t2.id=t1.id").Array() + r, err := db.Model(table, "t1").Fields("t2.id").LeftJoin(subQuery, "t2", "t2.id=t1.id").Array() t.AssertNil(err) - t.Assert(len(r), SIZE) + t.Assert(len(r), TableSize) t.Assert(r[0], "1") - t.Assert(r[SIZE-1], SIZE) + t.Assert(r[TableSize-1], TableSize) }) } @@ -2649,49 +2649,49 @@ func Test_Model_Cache(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).Cache(time.Second, "test1").FindOne(1) + one, err := db.Model(table).Cache(time.Second, "test1").FindOne(1) t.AssertNil(err) t.Assert(one["passport"], "user_1") - r, err := db.Table(table).Data("passport", "user_100").WherePri(1).Update() + r, err := db.Model(table).Data("passport", "user_100").WherePri(1).Update() t.AssertNil(err) n, err := r.RowsAffected() t.AssertNil(err) t.Assert(n, 1) - one, err = db.Table(table).Cache(time.Second, "test1").FindOne(1) + one, err = db.Model(table).Cache(time.Second, "test1").FindOne(1) t.AssertNil(err) t.Assert(one["passport"], "user_1") time.Sleep(time.Second * 2) - one, err = db.Table(table).Cache(time.Second, "test1").FindOne(1) + one, err = db.Model(table).Cache(time.Second, "test1").FindOne(1) t.AssertNil(err) t.Assert(one["passport"], "user_100") }) gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).Cache(time.Second, "test2").FindOne(2) + one, err := db.Model(table).Cache(time.Second, "test2").FindOne(2) t.AssertNil(err) t.Assert(one["passport"], "user_2") - r, err := db.Table(table).Data("passport", "user_200").Cache(-1, "test2").WherePri(2).Update() + r, err := db.Model(table).Data("passport", "user_200").Cache(-1, "test2").WherePri(2).Update() t.AssertNil(err) n, err := r.RowsAffected() t.AssertNil(err) t.Assert(n, 1) - one, err = db.Table(table).Cache(time.Second, "test2").FindOne(2) + one, err = db.Model(table).Cache(time.Second, "test2").FindOne(2) t.AssertNil(err) t.Assert(one["passport"], "user_200") }) // transaction. gtest.C(t, func(t *gtest.T) { // make cache for id 3 - one, err := db.Table(table).Cache(time.Second, "test3").FindOne(3) + one, err := db.Model(table).Cache(time.Second, "test3").FindOne(3) t.AssertNil(err) t.Assert(one["passport"], "user_3") - r, err := db.Table(table).Data("passport", "user_300").Cache(time.Second, "test3").WherePri(3).Update() + r, err := db.Model(table).Data("passport", "user_300").Cache(time.Second, "test3").WherePri(3).Update() t.AssertNil(err) n, err := r.RowsAffected() t.AssertNil(err) @@ -2705,17 +2705,17 @@ func Test_Model_Cache(t *testing.T) { }) t.AssertNil(err) - one, err = db.Table(table).Cache(time.Second, "test3").FindOne(3) + one, err = db.Model(table).Cache(time.Second, "test3").FindOne(3) t.AssertNil(err) t.Assert(one["passport"], "user_3") }) gtest.C(t, func(t *gtest.T) { // make cache for id 4 - one, err := db.Table(table).Cache(time.Second, "test4").FindOne(4) + one, err := db.Model(table).Cache(time.Second, "test4").FindOne(4) t.AssertNil(err) t.Assert(one["passport"], "user_4") - r, err := db.Table(table).Data("passport", "user_400").Cache(time.Second, "test3").WherePri(4).Update() + r, err := db.Model(table).Data("passport", "user_400").Cache(time.Second, "test3").WherePri(4).Update() t.AssertNil(err) n, err := r.RowsAffected() t.AssertNil(err) @@ -2737,7 +2737,7 @@ func Test_Model_Cache(t *testing.T) { }) t.AssertNil(err) // Read from db. - one, err = db.Table(table).Cache(time.Second, "test4").FindOne(4) + one, err = db.Model(table).Cache(time.Second, "test4").FindOne(4) t.AssertNil(err) t.Assert(one["passport"], "user_4000") }) @@ -2748,22 +2748,22 @@ func Test_Model_Having(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table).Where("id > 1").Having("id > 8").All() + all, err := db.Model(table).Where("id > 1").Having("id > 8").All() t.AssertNil(err) t.Assert(len(all), 2) }) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table).Where("id > 1").Having("id > ?", 8).All() + all, err := db.Model(table).Where("id > 1").Having("id > ?", 8).All() t.AssertNil(err) t.Assert(len(all), 2) }) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table).Where("id > ?", 1).Having("id > ?", 8).All() + all, err := db.Model(table).Where("id > ?", 1).Having("id > ?", 8).All() t.AssertNil(err) t.Assert(len(all), 2) }) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table).Where("id > ?", 1).Having("id", 8).All() + all, err := db.Model(table).Where("id > ?", 1).Having("id", 8).All() t.AssertNil(err) t.Assert(len(all), 1) }) @@ -2774,7 +2774,7 @@ func Test_Model_Distinct(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table, "t").Fields("distinct t.id").Where("id > 1").Having("id > 8").All() + all, err := db.Model(table, "t").Fields("distinct t.id").Where("id > 1").Having("id > 8").All() t.AssertNil(err) t.Assert(len(all), 2) }) @@ -2785,12 +2785,12 @@ func Test_Model_Min_Max(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table, "t").Fields("min(t.id)").Where("id > 1").Value() + value, err := db.Model(table, "t").Fields("min(t.id)").Where("id > 1").Value() t.AssertNil(err) t.Assert(value.Int(), 2) }) gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table, "t").Fields("max(t.id)").Where("id > 1").Value() + value, err := db.Model(table, "t").Fields("max(t.id)").Where("id > 1").Value() t.AssertNil(err) t.Assert(value.Int(), 10) }) @@ -2801,19 +2801,19 @@ func Test_Model_Fields_AutoMapping(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table).Fields("ID").Where("id", 2).Value() + value, err := db.Model(table).Fields("ID").Where("id", 2).Value() t.AssertNil(err) t.Assert(value.Int(), 2) }) gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table).Fields("NICK_NAME").Where("id", 2).Value() + value, err := db.Model(table).Fields("NICK_NAME").Where("id", 2).Value() t.AssertNil(err) t.Assert(value.String(), "name_2") }) // Map gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).Fields(g.Map{ + one, err := db.Model(table).Fields(g.Map{ "ID": 1, "NICK_NAME": 1, }).Where("id", 2).One() @@ -2828,7 +2828,7 @@ func Test_Model_Fields_AutoMapping(t *testing.T) { ID int NICKNAME int } - one, err := db.Table(table).Fields(&T{ + one, err := db.Model(table).Fields(&T{ ID: 0, NICKNAME: 0, }).Where("id", 2).One() @@ -2850,19 +2850,19 @@ func Test_Model_FieldsEx_AutoMapping(t *testing.T) { // "create_time": gtime.NewFromStr("2018-10-24 10:00:00").String(), gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table).FieldsEx("Passport, Password, NickName, CreateTime").Where("id", 2).Value() + value, err := db.Model(table).FieldsEx("Passport, Password, NickName, CreateTime").Where("id", 2).Value() t.AssertNil(err) t.Assert(value.Int(), 2) }) gtest.C(t, func(t *gtest.T) { - value, err := db.Table(table).FieldsEx("ID, Passport, Password, CreateTime").Where("id", 2).Value() + value, err := db.Model(table).FieldsEx("ID, Passport, Password, CreateTime").Where("id", 2).Value() t.AssertNil(err) t.Assert(value.String(), "name_2") }) // Map gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).FieldsEx(g.Map{ + one, err := db.Model(table).FieldsEx(g.Map{ "Passport": 1, "Password": 1, "CreateTime": 1, @@ -2879,7 +2879,7 @@ func Test_Model_FieldsEx_AutoMapping(t *testing.T) { Password int CreateTime int } - one, err := db.Table(table).FieldsEx(&T{ + one, err := db.Model(table).FieldsEx(&T{ Passport: 0, Password: 0, CreateTime: 0, @@ -2904,21 +2904,21 @@ func Test_Model_Fields_Struct(t *testing.T) { NickName string } gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).Fields(A{}).Where("id", 2).One() + one, err := db.Model(table).Fields(A{}).Where("id", 2).One() t.AssertNil(err) t.Assert(len(one), 2) t.Assert(one["passport"], "user_2") t.Assert(one["password"], "pass_2") }) gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).Fields(&A{}).Where("id", 2).One() + one, err := db.Model(table).Fields(&A{}).Where("id", 2).One() t.AssertNil(err) t.Assert(len(one), 2) t.Assert(one["passport"], "user_2") t.Assert(one["password"], "pass_2") }) gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).Fields(B{}).Where("id", 2).One() + one, err := db.Model(table).Fields(B{}).Where("id", 2).One() t.AssertNil(err) t.Assert(len(one), 3) t.Assert(one["passport"], "user_2") @@ -2926,7 +2926,7 @@ func Test_Model_Fields_Struct(t *testing.T) { t.Assert(one["nickname"], "name_2") }) gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).Fields(&B{}).Where("id", 2).One() + one, err := db.Model(table).Fields(&B{}).Where("id", 2).One() t.AssertNil(err) t.Assert(len(one), 3) t.Assert(one["passport"], "user_2") @@ -2947,11 +2947,11 @@ func Test_Model_NullField(t *testing.T) { "id": 1, "passport": nil, } - result, err := db.Table(table).Data(data).Insert() + result, err := db.Model(table).Data(data).Insert() t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) - one, err := db.Table(table).FindOne(1) + one, err := db.Model(table).FindOne(1) t.AssertNil(err) var user *User @@ -2999,13 +2999,13 @@ func Test_Model_HasField(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).HasField("id") + result, err := db.Model(table).HasField("id") t.Assert(result, true) t.AssertNil(err) }) gtest.C(t, func(t *gtest.T) { - result, err := db.Table(table).HasField("id123") + result, err := db.Model(table).HasField("id123") t.Assert(result, false) t.AssertNil(err) }) @@ -3016,7 +3016,7 @@ func Test_Model_Issue1002(t *testing.T) { table := createTable() defer dropTable(table) - result, err := db.Table(table).Data(g.Map{ + result, err := db.Model(table).Data(g.Map{ "id": 1, "passport": "port_1", "password": "pass_1", @@ -3029,49 +3029,49 @@ func Test_Model_Issue1002(t *testing.T) { // where + string. gtest.C(t, func(t *gtest.T) { - v, err := db.Table(table).Fields("id").Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").Value() + v, err := db.Model(table).Fields("id").Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").Value() t.AssertNil(err) t.Assert(v.Int(), 1) }) gtest.C(t, func(t *gtest.T) { - v, err := db.Table(table).Fields("id").Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").FindValue() + v, err := db.Model(table).Fields("id").Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").FindValue() t.AssertNil(err) t.Assert(v.Int(), 1) }) gtest.C(t, func(t *gtest.T) { - v, err := db.Table(table).Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").FindValue("id") + v, err := db.Model(table).Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").FindValue("id") t.AssertNil(err) t.Assert(v.Int(), 1) }) // where + string arguments. gtest.C(t, func(t *gtest.T) { - v, err := db.Table(table).Fields("id").Where("create_time>? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time? and create_time100").All() + all, err := db.Model(table).Where("id>100").All() t.AssertNil(err) users := make([]User, 0) t.Assert(all.Structs(&users), nil) }) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table).Where("id>100").All() + all, err := db.Model(table).Where("id>100").All() t.AssertNil(err) users := make([]User, 10) t.Assert(all.Structs(&users), nil) }) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table).Where("id>100").All() + all, err := db.Model(table).Where("id>100").All() t.AssertNil(err) var users []User t.Assert(all.Structs(&users), nil) }) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table).Where("id>100").All() + all, err := db.Model(table).Where("id>100").All() t.AssertNil(err) users := make([]*User, 0) t.Assert(all.Structs(&users), nil) }) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table).Where("id>100").All() + all, err := db.Model(table).Where("id>100").All() t.AssertNil(err) users := make([]*User, 10) t.Assert(all.Structs(&users), nil) }) gtest.C(t, func(t *gtest.T) { - all, err := db.Table(table).Where("id>100").All() + all, err := db.Model(table).Where("id>100").All() t.AssertNil(err) var users []*User t.Assert(all.Structs(&users), nil) @@ -343,21 +344,65 @@ func (st *MyTimeSt) UnmarshalValue(v interface{}) error { return nil } -func Test_Model_Scan_CustomType(t *testing.T) { +func Test_Model_Scan_CustomType_Time(t *testing.T) { table := createInitTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { st := new(MyTimeSt) - err := db.Table(table).Fields("create_time").Scan(st) + err := db.Model(table).Fields("create_time").Scan(st) t.AssertNil(err) t.Assert(st.CreateTime.String(), "2018-10-24 10:00:00") }) gtest.C(t, func(t *gtest.T) { var stSlice []*MyTimeSt - err := db.Table(table).Fields("create_time").Scan(&stSlice) + err := db.Model(table).Fields("create_time").Scan(&stSlice) t.AssertNil(err) - t.Assert(len(stSlice), SIZE) + t.Assert(len(stSlice), TableSize) t.Assert(stSlice[0].CreateTime.String(), "2018-10-24 10:00:00") t.Assert(stSlice[9].CreateTime.String(), "2018-10-24 10:00:00") }) } + +type User struct { + Id int + Passport string + Password string + Nickname string + CreateTime *gtime.Time +} + +func (user *User) UnmarshalValue(value interface{}) error { + switch result := value.(type) { + case map[string]interface{}: + user.Id = result["id"].(gdb.Value).Int() + user.Passport = result["passport"].(gdb.Value).String() + user.Password = "" + user.Nickname = result["nickname"].(gdb.Value).String() + user.CreateTime = result["create_time"].(gdb.Value).GTime() + return nil + default: + return gconv.Struct(value, user) + } +} + +func Test_Model_Scan_UnmarshalValue(t *testing.T) { + table := createInitTable() + defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(table).Order("id asc").Scan(&users) + t.AssertNil(err) + t.Assert(len(users), TableSize) + t.Assert(users[0].Id, 1) + t.Assert(users[0].Passport, "user_1") + t.Assert(users[0].Password, "") + t.Assert(users[0].Nickname, "name_1") + t.Assert(users[0].CreateTime.String(), CreateTime) + + t.Assert(users[9].Id, 10) + t.Assert(users[9].Passport, "user_10") + t.Assert(users[9].Password, "") + t.Assert(users[9].Nickname, "name_10") + t.Assert(users[9].CreateTime.String(), CreateTime) + }) +} diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index 3133b4c1d..120a4cd55 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -40,12 +40,12 @@ CREATE TABLE %s ( "id": 1, "name": "name_1", } - r, err := db.Table(table).Data(dataInsert).Insert() + r, err := db.Model(table).Data(dataInsert).Insert() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - oneInsert, err := db.Table(table).FindOne(1) + oneInsert, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneInsert["id"].Int(), 1) t.Assert(oneInsert["name"].String(), "name_1") @@ -61,12 +61,12 @@ CREATE TABLE %s ( "id": 1, "name": "name_10", } - r, err = db.Table(table).Data(dataSave).Save() + r, err = db.Model(table).Data(dataSave).Save() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) - oneSave, err := db.Table(table).FindOne(1) + oneSave, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneSave["id"].Int(), 1) t.Assert(oneSave["name"].String(), "name_10") @@ -82,12 +82,12 @@ CREATE TABLE %s ( dataUpdate := g.Map{ "name": "name_1000", } - r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update() + r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) - oneUpdate, err := db.Table(table).FindOne(1) + oneUpdate, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneUpdate["id"].Int(), 1) t.Assert(oneUpdate["name"].String(), "name_1000") @@ -100,12 +100,12 @@ CREATE TABLE %s ( "id": 1, "name": "name_100", } - r, err = db.Table(table).Data(dataReplace).Replace() + r, err = db.Model(table).Data(dataReplace).Replace() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) - oneReplace, err := db.Table(table).FindOne(1) + oneReplace, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneReplace["id"].Int(), 1) t.Assert(oneReplace["name"].String(), "name_100") @@ -117,35 +117,35 @@ CREATE TABLE %s ( time.Sleep(2 * time.Second) // Delete - r, err = db.Table(table).Delete("id", 1) + r, err = db.Model(table).Delete("id", 1) t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) // Delete Select - one4, err := db.Table(table).FindOne(1) + one4, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(len(one4), 0) - one5, err := db.Table(table).Unscoped().FindOne(1) + one5, err := db.Model(table).Unscoped().FindOne(1) t.AssertNil(err) t.Assert(one5["id"].Int(), 1) t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count - i, err := db.Table(table).FindCount() + i, err := db.Model(table).FindCount() t.AssertNil(err) t.Assert(i, 0) - i, err = db.Table(table).Unscoped().FindCount() + i, err = db.Model(table).Unscoped().FindCount() t.AssertNil(err) t.Assert(i, 1) // Delete Unscoped - r, err = db.Table(table).Unscoped().Delete("id", 1) + r, err = db.Model(table).Unscoped().Delete("id", 1) t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) - one6, err := db.Table(table).Unscoped().FindOne(1) + one6, err := db.Model(table).Unscoped().FindOne(1) t.AssertNil(err) t.Assert(len(one6), 0) - i, err = db.Table(table).Unscoped().FindCount() + i, err = db.Model(table).Unscoped().FindCount() t.AssertNil(err) t.Assert(i, 0) }) @@ -174,12 +174,12 @@ CREATE TABLE %s ( "id": 1, "name": "name_1", } - r, err := db.Table(table).Data(dataInsert).Insert() + r, err := db.Model(table).Data(dataInsert).Insert() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - oneInsert, err := db.Table(table).FindOne(1) + oneInsert, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneInsert["id"].Int(), 1) t.Assert(oneInsert["name"].String(), "name_1") @@ -195,12 +195,12 @@ CREATE TABLE %s ( "id": 1, "name": "name_10", } - r, err = db.Table(table).Data(dataSave).Save() + r, err = db.Model(table).Data(dataSave).Save() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) - oneSave, err := db.Table(table).FindOne(1) + oneSave, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneSave["id"].Int(), 1) t.Assert(oneSave["name"].String(), "name_10") @@ -216,12 +216,12 @@ CREATE TABLE %s ( dataUpdate := g.Map{ "name": "name_1000", } - r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update() + r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) - oneUpdate, err := db.Table(table).FindOne(1) + oneUpdate, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneUpdate["id"].Int(), 1) t.Assert(oneUpdate["name"].String(), "name_1000") @@ -234,12 +234,12 @@ CREATE TABLE %s ( "id": 1, "name": "name_100", } - r, err = db.Table(table).Data(dataReplace).Replace() + r, err = db.Model(table).Data(dataReplace).Replace() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) - oneReplace, err := db.Table(table).FindOne(1) + oneReplace, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneReplace["id"].Int(), 1) t.Assert(oneReplace["name"].String(), "name_100") @@ -251,35 +251,35 @@ CREATE TABLE %s ( time.Sleep(2 * time.Second) // Delete - r, err = db.Table(table).Delete("id", 1) + r, err = db.Model(table).Delete("id", 1) t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) // Delete Select - one4, err := db.Table(table).FindOne(1) + one4, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(len(one4), 0) - one5, err := db.Table(table).Unscoped().FindOne(1) + one5, err := db.Model(table).Unscoped().FindOne(1) t.AssertNil(err) t.Assert(one5["id"].Int(), 1) t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count - i, err := db.Table(table).FindCount() + i, err := db.Model(table).FindCount() t.AssertNil(err) t.Assert(i, 0) - i, err = db.Table(table).Unscoped().FindCount() + i, err = db.Model(table).Unscoped().FindCount() t.AssertNil(err) t.Assert(i, 1) // Delete Unscoped - r, err = db.Table(table).Unscoped().Delete("id", 1) + r, err = db.Model(table).Unscoped().Delete("id", 1) t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) - one6, err := db.Table(table).Unscoped().FindOne(1) + one6, err := db.Model(table).Unscoped().FindOne(1) t.AssertNil(err) t.Assert(len(one6), 0) - i, err = db.Table(table).Unscoped().FindCount() + i, err = db.Model(table).Unscoped().FindCount() t.AssertNil(err) t.Assert(i, 0) }) @@ -315,12 +315,12 @@ CREATE TABLE %s ( Id: 1, Name: "name_1", } - r, err := db.Table(table).Data(dataInsert).Insert() + r, err := db.Model(table).Data(dataInsert).Insert() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - oneInsert, err := db.Table(table).FindOne(1) + oneInsert, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneInsert["id"].Int(), 1) t.Assert(oneInsert["name"].String(), "name_1") @@ -336,12 +336,12 @@ CREATE TABLE %s ( Id: 1, Name: "name_10", } - r, err = db.Table(table).Data(dataSave).OmitEmpty().Save() + r, err = db.Model(table).Data(dataSave).OmitEmpty().Save() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) - oneSave, err := db.Table(table).FindOne(1) + oneSave, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneSave["id"].Int(), 1) t.Assert(oneSave["name"].String(), "name_10") @@ -357,12 +357,12 @@ CREATE TABLE %s ( dataUpdate := User{ Name: "name_1000", } - r, err = db.Table(table).Data(dataUpdate).OmitEmpty().WherePri(1).Update() + r, err = db.Model(table).Data(dataUpdate).OmitEmpty().WherePri(1).Update() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) - oneUpdate, err := db.Table(table).FindOne(1) + oneUpdate, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneUpdate["id"].Int(), 1) t.Assert(oneUpdate["name"].String(), "name_1000") @@ -375,12 +375,12 @@ CREATE TABLE %s ( Id: 1, Name: "name_100", } - r, err = db.Table(table).Data(dataReplace).OmitEmpty().Replace() + r, err = db.Model(table).Data(dataReplace).OmitEmpty().Replace() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) - oneReplace, err := db.Table(table).FindOne(1) + oneReplace, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneReplace["id"].Int(), 1) t.Assert(oneReplace["name"].String(), "name_100") @@ -392,35 +392,35 @@ CREATE TABLE %s ( time.Sleep(2 * time.Second) // Delete - r, err = db.Table(table).Delete("id", 1) + r, err = db.Model(table).Delete("id", 1) t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) // Delete Select - one4, err := db.Table(table).FindOne(1) + one4, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(len(one4), 0) - one5, err := db.Table(table).Unscoped().FindOne(1) + one5, err := db.Model(table).Unscoped().FindOne(1) t.AssertNil(err) t.Assert(one5["id"].Int(), 1) t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count - i, err := db.Table(table).FindCount() + i, err := db.Model(table).FindCount() t.AssertNil(err) t.Assert(i, 0) - i, err = db.Table(table).Unscoped().FindCount() + i, err = db.Model(table).Unscoped().FindCount() t.AssertNil(err) t.Assert(i, 1) // Delete Unscoped - r, err = db.Table(table).Unscoped().Delete("id", 1) + r, err = db.Model(table).Unscoped().Delete("id", 1) t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) - one6, err := db.Table(table).Unscoped().FindOne(1) + one6, err := db.Model(table).Unscoped().FindOne(1) t.AssertNil(err) t.Assert(len(one6), 0) - i, err = db.Table(table).Unscoped().FindCount() + i, err = db.Model(table).Unscoped().FindCount() t.AssertNil(err) t.Assert(i, 0) }) @@ -448,18 +448,18 @@ CREATE TABLE %s ( "id": 1, "num": 10, } - r, err := db.Table(table).Data(dataInsert).Insert() + r, err := db.Model(table).Data(dataInsert).Insert() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - oneInsert, err := db.Table(table).FindOne(1) + oneInsert, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneInsert["id"].Int(), 1) t.Assert(oneInsert["num"].Int(), 10) // Update. - r, err = db.Table(table).Data("num=num+1").Where("id=?", 1).Update() + r, err = db.Model(table).Data("num=num+1").Where("id=?", 1).Update() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) @@ -488,21 +488,21 @@ CREATE TABLE %s ( "id": i, "name": fmt.Sprintf("name_%d", i), } - r, err := db.Table(table).Data(data).Insert() + r, err := db.Model(table).Data(data).Insert() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) } }) gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).FindOne(1) + one, err := db.Model(table).FindOne(1) t.AssertNil(err) t.AssertNE(one["create_at"].String(), "") t.AssertNE(one["update_at"].String(), "") t.Assert(one["delete_at"].String(), "") }) gtest.C(t, func(t *gtest.T) { - one, err := db.Table(table).FindOne(10) + one, err := db.Model(table).FindOne(10) t.AssertNil(err) t.AssertNE(one["create_at"].String(), "") t.AssertNE(one["update_at"].String(), "") @@ -510,16 +510,16 @@ CREATE TABLE %s ( }) gtest.C(t, func(t *gtest.T) { ids := g.SliceInt{1, 3, 5} - r, err := db.Table(table).Where("id", ids).Delete() + r, err := db.Model(table).Where("id", ids).Delete() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 3) - count, err := db.Table(table).FindCount(ids) + count, err := db.Model(table).FindCount(ids) t.AssertNil(err) t.Assert(count, 0) - all, err := db.Table(table).Unscoped().FindAll(ids) + all, err := db.Model(table).Unscoped().FindAll(ids) t.AssertNil(err) t.Assert(len(all), 3) t.AssertNE(all[0]["create_at"].String(), "") @@ -571,7 +571,7 @@ CREATE TABLE %s ( "id": 1, "name": "name_1", } - r, err := db.Table(table1).Data(dataInsert1).Insert() + r, err := db.Model(table1).Data(dataInsert1).Insert() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) @@ -580,26 +580,26 @@ CREATE TABLE %s ( "id": 1, "name": "name_2", } - r, err = db.Table(table2).Data(dataInsert2).Insert() + r, err = db.Model(table2).Data(dataInsert2).Insert() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) - one, err := db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne() + one, err := db.Model(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne() t.AssertNil(err) t.Assert(one["name"], "name_1") // Soft deleting. - r, err = db.Table(table1).Delete() + r, err = db.Model(table1).Delete() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) - one, err = db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne() + one, err = db.Model(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne() t.AssertNil(err) t.Assert(one.IsEmpty(), true) - one, err = db.Table(table2, "t2").LeftJoin(table1, "t1", "t2.id=t1.id").Fields("t2.name").FindOne() + one, err = db.Model(table2, "t2").LeftJoin(table1, "t1", "t2.id=t1.id").Fields("t2.name").FindOne() t.AssertNil(err) t.Assert(one.IsEmpty(), true) }) @@ -628,7 +628,7 @@ CREATE TABLE %s ( "id": i, "name": fmt.Sprintf("name_%d", i), } - r, err := db.Table(table).Data(data).Insert() + r, err := db.Model(table).Data(data).Insert() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) @@ -636,12 +636,12 @@ CREATE TABLE %s ( }) gtest.C(t, func(t *gtest.T) { ids := g.SliceInt{1, 3, 5} - r, err := db.Table(table).Where("id", ids).Delete() + r, err := db.Model(table).Where("id", ids).Delete() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 3) - count, err := db.Table(table).Where("id", 1).Or("id", 3).Count() + count, err := db.Model(table).Where("id", 1).Or("id", 3).Count() t.AssertNil(err) t.Assert(count, 0) }) @@ -679,12 +679,12 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err := db.Table(table).Data(dataInsert).Insert() + r, err := db.Model(table).Data(dataInsert).Insert() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - oneInsert, err := db.Table(table).FindOne(1) + oneInsert, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneInsert["id"].Int(), 1) t.Assert(oneInsert["name"].String(), "name_1") @@ -702,12 +702,12 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err = db.Table(table).Data(dataSave).Save() + r, err = db.Model(table).Data(dataSave).Save() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) - oneSave, err := db.Table(table).FindOne(1) + oneSave, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneSave["id"].Int(), 1) t.Assert(oneSave["name"].String(), "name_10") @@ -726,12 +726,12 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update() + r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) - oneUpdate, err := db.Table(table).FindOne(1) + oneUpdate, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneUpdate["id"].Int(), 1) t.Assert(oneUpdate["name"].String(), "name_1000") @@ -747,12 +747,12 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err = db.Table(table).Data(dataReplace).Replace() + r, err = db.Model(table).Data(dataReplace).Replace() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) - oneReplace, err := db.Table(table).FindOne(1) + oneReplace, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(oneReplace["id"].Int(), 1) t.Assert(oneReplace["name"].String(), "name_100") @@ -763,35 +763,35 @@ CREATE TABLE %s ( time.Sleep(2 * time.Second) // Delete - r, err = db.Table(table).Delete("id", 1) + r, err = db.Model(table).Delete("id", 1) t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) // Delete Select - one4, err := db.Table(table).FindOne(1) + one4, err := db.Model(table).FindOne(1) t.AssertNil(err) t.Assert(len(one4), 0) - one5, err := db.Table(table).Unscoped().FindOne(1) + one5, err := db.Model(table).Unscoped().FindOne(1) t.AssertNil(err) t.Assert(one5["id"].Int(), 1) t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count - i, err := db.Table(table).FindCount() + i, err := db.Model(table).FindCount() t.AssertNil(err) t.Assert(i, 0) - i, err = db.Table(table).Unscoped().FindCount() + i, err = db.Model(table).Unscoped().FindCount() t.AssertNil(err) t.Assert(i, 1) // Delete Unscoped - r, err = db.Table(table).Unscoped().Delete("id", 1) + r, err = db.Model(table).Unscoped().Delete("id", 1) t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) - one6, err := db.Table(table).Unscoped().FindOne(1) + one6, err := db.Model(table).Unscoped().FindOne(1) t.AssertNil(err) t.Assert(len(one6), 0) - i, err = db.Table(table).Unscoped().FindCount() + i, err = db.Model(table).Unscoped().FindCount() t.AssertNil(err) t.Assert(i, 0) }) diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index 38ea6f4a3..96b3dd978 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -183,7 +183,7 @@ func Test_TX_BatchInsert(t *testing.T) { if err := tx.Commit(); err != nil { gtest.Error(err) } - if n, err := db.Table(table).Count(); err != nil { + if n, err := db.Model(table).Count(); err != nil { gtest.Error(err) } else { t.Assert(n, 2) @@ -221,12 +221,12 @@ func Test_TX_BatchReplace(t *testing.T) { if err := tx.Commit(); err != nil { gtest.Error(err) } - if n, err := db.Table(table).Count(); err != nil { + if n, err := db.Model(table).Count(); err != nil { gtest.Error(err) } else { - t.Assert(n, SIZE) + t.Assert(n, TableSize) } - if value, err := db.Table(table).Fields("password").Where("id", 2).Value(); err != nil { + if value, err := db.Model(table).Fields("password").Where("id", 2).Value(); err != nil { gtest.Error(err) } else { t.Assert(value.String(), "PASS_2") @@ -258,13 +258,13 @@ func Test_TX_BatchSave(t *testing.T) { gtest.Error(err) } - if n, err := db.Table(table).Count(); err != nil { + if n, err := db.Model(table).Count(); err != nil { gtest.Error(err) } else { - t.Assert(n, SIZE) + t.Assert(n, TableSize) } - if value, err := db.Table(table).Fields("password").Where("id", 4).Value(); err != nil { + if value, err := db.Model(table).Fields("password").Where("id", 4).Value(); err != nil { gtest.Error(err) } else { t.Assert(value.String(), "PASS_4") @@ -293,7 +293,7 @@ func Test_TX_Replace(t *testing.T) { if err := tx.Rollback(); err != nil { gtest.Error(err) } - if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil { + if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil { gtest.Error(err) } else { t.Assert(value.String(), "name_1") @@ -322,7 +322,7 @@ func Test_TX_Save(t *testing.T) { if err := tx.Commit(); err != nil { gtest.Error(err) } - if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil { + if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil { gtest.Error(err) } else { t.Assert(value.String(), "NAME_1") @@ -351,7 +351,7 @@ func Test_TX_Update(t *testing.T) { _, err = tx.Table(table).Fields("create_time").Where("id", 3).Value() t.AssertNE(err, nil) - if value, err := db.Table(table).Fields("create_time").Where("id", 3).Value(); err != nil { + if value, err := db.Model(table).Fields("create_time").Where("id", 3).Value(); err != nil { gtest.Error(err) } else { t.Assert(value.String(), "2019-10-24 10:00:00") @@ -435,7 +435,7 @@ func Test_TX_GetCount(t *testing.T) { if count, err := tx.GetCount("SELECT * FROM " + table); err != nil { gtest.Error(err) } else { - t.Assert(count, SIZE) + t.Assert(count, TableSize) } if err := tx.Commit(); err != nil { gtest.Error(err) @@ -513,7 +513,7 @@ func Test_TX_GetStructs(t *testing.T) { if err := tx.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil { gtest.Error(err) } - t.Assert(len(users), SIZE) + t.Assert(len(users), TableSize) t.Assert(users[0].Id, 1) t.Assert(users[1].Id, 2) t.Assert(users[2].Id, 3) @@ -542,7 +542,7 @@ func Test_TX_GetStructs(t *testing.T) { if err := tx.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil { gtest.Error(err) } - t.Assert(len(users), SIZE) + t.Assert(len(users), TableSize) t.Assert(users[0].Id, 1) t.Assert(users[1].Id, 2) t.Assert(users[2].Id, 3) @@ -621,7 +621,7 @@ func Test_TX_GetScan(t *testing.T) { if err := tx.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil { gtest.Error(err) } - t.Assert(len(users), SIZE) + t.Assert(len(users), TableSize) t.Assert(users[0].Id, 1) t.Assert(users[1].Id, 2) t.Assert(users[2].Id, 3) @@ -650,7 +650,7 @@ func Test_TX_GetScan(t *testing.T) { if err := tx.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil { gtest.Error(err) } - t.Assert(len(users), SIZE) + t.Assert(len(users), TableSize) t.Assert(users[0].Id, 1) t.Assert(users[1].Id, 2) t.Assert(users[2].Id, 3) @@ -679,7 +679,7 @@ func Test_TX_Delete(t *testing.T) { if err := tx.Commit(); err != nil { gtest.Error(err) } - if n, err := db.Table(table).Count(); err != nil { + if n, err := db.Model(table).Count(); err != nil { gtest.Error(err) } else { t.Assert(n, 0) @@ -704,10 +704,10 @@ func Test_TX_Delete(t *testing.T) { if err := tx.Rollback(); err != nil { gtest.Error(err) } - if n, err := db.Table(table).Count(); err != nil { + if n, err := db.Model(table).Count(); err != nil { gtest.Error(err) } else { - t.Assert(n, SIZE) + t.Assert(n, TableSize) t.AssertNE(n, 0) } }) @@ -732,7 +732,7 @@ func Test_Transaction(t *testing.T) { }) t.AssertNE(err, nil) - if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil { + if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil { gtest.Error(err) } else { t.Assert(value.String(), "name_1") @@ -754,7 +754,7 @@ func Test_Transaction(t *testing.T) { }) t.AssertNil(err) - if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil { + if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil { gtest.Error(err) } else { t.Assert(value.String(), "NAME_1") @@ -782,7 +782,7 @@ func Test_Transaction_Panic(t *testing.T) { }) t.AssertNE(err, nil) - if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil { + if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil { gtest.Error(err) } else { t.Assert(value.String(), "name_1") diff --git a/database/gdb/gdb_z_mysql_types_test.go b/database/gdb/gdb_z_mysql_types_test.go index bf0b1efb4..e26d763b9 100644 --- a/database/gdb/gdb_z_mysql_types_test.go +++ b/database/gdb/gdb_z_mysql_types_test.go @@ -58,12 +58,12 @@ func Test_Types(t *testing.T) { "tinyint": true, "bool": false, } - r, err := db.Table("types").Data(data).Insert() + r, err := db.Model("types").Data(data).Insert() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) - one, err := db.Table("types").One() + one, err := db.Model("types").One() t.AssertNil(err) t.Assert(one["id"].Int(), 1) t.Assert(one["blob"].String(), data["blob"]) @@ -87,7 +87,7 @@ func Test_Types(t *testing.T) { TinyInt bool } var obj *T - err = db.Table("types").Struct(&obj) + err = db.Model("types").Struct(&obj) t.AssertNil(err) t.Assert(obj.Id, 1) t.Assert(obj.Blob, data["blob"]) From 0e6cddb54775866769c6bda6a15e5cb8afb7d7f0 Mon Sep 17 00:00:00 2001 From: turc Date: Mon, 1 Mar 2021 01:43:53 +0800 Subject: [PATCH 166/492] =?UTF-8?q?=E5=8E=BB=E9=99=A4cancelFunc=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E9=80=80=E5=87=BA=E4=B9=8B=E5=89=8D=E5=85=B3=E9=97=AD?= =?UTF-8?q?mysqlConn=E5=AF=BC=E8=87=B4=E6=95=B0=E6=8D=AE=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E4=B8=8D=E5=AE=8C=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/gdb/gdb_core.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index d26606226..7f2a6a1b5 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -111,9 +111,7 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro sql, args = c.db.HandleSqlBeforeCommit(link, sql, args) ctx := c.db.GetCtx() if c.GetConfig().QueryTimeout > 0 { - var cancelFunc context.CancelFunc - ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) - defer cancelFunc() + ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) } mTime1 := gtime.TimestampMilli() From 4b4cc5ebb9828bdd48c2300f1cb561becd8a074e Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 1 Mar 2021 17:02:07 +0800 Subject: [PATCH 167/492] improve package gcfg for main package path searching --- os/gcfg/gcfg.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index 1256a6fa3..2c26abdf6 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -85,7 +85,16 @@ func New(file ...string) *Config { return c } +// getSearchPaths is used for checking and using `MainPkgPath` purpose. +// As the `MainPkgPath` is retrieved only by main goroutine, +// it should be checked multiple times when used in configuration file searching. +// It only makes sense in source development environment. func (c *Config) getSearchPaths() []string { + // Custom configuration directory path. + if !gcmd.GetOptWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).IsEmpty() { + return c.searchPaths.Slice() + } + var ( searchPaths = c.searchPaths.Slice() mainPkgPath = gfile.MainPkgPath() From ffc88eaaa77a633a0a970faec9a851bdbcd2c67e Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 1 Mar 2021 17:05:44 +0800 Subject: [PATCH 168/492] improve package gcfg for main package path searching --- os/gcfg/gcfg.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index 2c26abdf6..423b382df 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -73,14 +73,22 @@ func New(file ...string) *Config { } else { // Dir path of main package. if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) { - _ = c.AddPath(mainPath) + if err := c.AddPath(mainPath); err != nil { + intlog.Error(err) + } } + + // Dir path of working dir. + if err := c.AddPath(gfile.Pwd()); err != nil { + intlog.Error(err) + } + // Dir path of binary. if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { - _ = c.AddPath(selfPath) + if err := c.AddPath(selfPath); err != nil { + intlog.Error(err) + } } - // Dir path of working dir. - _ = c.AddPath(gfile.Pwd()) } return c } From 04e42d2175bd5f0965ee280e34804f292ab6d78a Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 1 Mar 2021 17:15:18 +0800 Subject: [PATCH 169/492] improve package internal/intlog --- internal/intlog/intlog.go | 5 ----- os/gcfg/gcfg.go | 13 ++++++------- os/gfsnotify/gfsnotify_watcher.go | 3 ++- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index 4edf5c2ab..f2675ce91 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -37,11 +37,6 @@ func SetEnabled(enabled bool) { } } -// IsEnabled checks and returns whether current process is in GF development. -func IsEnabled() bool { - return isGFDebug -} - // Print prints `v` with newline using fmt.Println. // The parameter `v` can be multiple variables. func Print(v ...interface{}) { diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index 423b382df..ff3f7a34d 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -409,13 +409,12 @@ func (c *Config) getJson(file ...string) *gjson.Json { } } return j - } else { - if errorPrint() { - if filePath != "" { - glog.Criticalf(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error()) - } else { - glog.Criticalf(`[gcfg] Load configuration failed: %s`, err.Error()) - } + } + if errorPrint() { + if filePath != "" { + glog.Criticalf(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error()) + } else { + glog.Criticalf(`[gcfg] Load configuration failed: %s`, err.Error()) } } return nil diff --git a/os/gfsnotify/gfsnotify_watcher.go b/os/gfsnotify/gfsnotify_watcher.go index 421b40a59..0088bfaad 100644 --- a/os/gfsnotify/gfsnotify_watcher.go +++ b/os/gfsnotify/gfsnotify_watcher.go @@ -47,7 +47,8 @@ func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), re if err := w.watcher.Add(subPath); err != nil { intlog.Error(err) } else { - intlog.Printf("watcher adds monitor for: %s", subPath) + // It uses `Errorf` as it needs trace stack information here. + intlog.Errorf("watcher adds monitor for: %s", subPath) } } } From d0f649b3282009c0c9432997a2d03c54fcdc0a90 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 1 Mar 2021 17:39:13 +0800 Subject: [PATCH 170/492] improve package internal/intlog --- os/gfsnotify/gfsnotify_watcher.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/os/gfsnotify/gfsnotify_watcher.go b/os/gfsnotify/gfsnotify_watcher.go index 0088bfaad..52c342305 100644 --- a/os/gfsnotify/gfsnotify_watcher.go +++ b/os/gfsnotify/gfsnotify_watcher.go @@ -95,7 +95,8 @@ func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event if err := w.watcher.Add(path); err != nil { intlog.Error(err) } else { - intlog.Printf("watcher adds monitor for: %s", path) + // It uses `Errorf` as it needs trace stack information here. + intlog.Errorf("watcher adds monitor for: %s", path) } // Add the callback to global callback map. callbackIdMap.Set(callback.Id, callback) From bd13de2b39d7e2d633a35b33ef37184552595f68 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 1 Mar 2021 17:50:02 +0800 Subject: [PATCH 171/492] improve package internal/intlog --- os/gfsnotify/gfsnotify_watcher.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/os/gfsnotify/gfsnotify_watcher.go b/os/gfsnotify/gfsnotify_watcher.go index 52c342305..421b40a59 100644 --- a/os/gfsnotify/gfsnotify_watcher.go +++ b/os/gfsnotify/gfsnotify_watcher.go @@ -47,8 +47,7 @@ func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), re if err := w.watcher.Add(subPath); err != nil { intlog.Error(err) } else { - // It uses `Errorf` as it needs trace stack information here. - intlog.Errorf("watcher adds monitor for: %s", subPath) + intlog.Printf("watcher adds monitor for: %s", subPath) } } } @@ -95,8 +94,7 @@ func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event if err := w.watcher.Add(path); err != nil { intlog.Error(err) } else { - // It uses `Errorf` as it needs trace stack information here. - intlog.Errorf("watcher adds monitor for: %s", path) + intlog.Printf("watcher adds monitor for: %s", path) } // Add the callback to global callback map. callbackIdMap.Set(callback.Id, callback) From 204fea395c577cd0684a41037d47fb6aa77155ca Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 1 Mar 2021 20:49:09 +0800 Subject: [PATCH 172/492] add struct object support in group router registering for package ghttp --- net/ghttp/ghttp_server_service_object.go | 63 ++++++++++++++++++------ 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/net/ghttp/ghttp_server_service_object.go b/net/ghttp/ghttp_server_service_object.go index 43b532303..d86bf24e2 100644 --- a/net/ghttp/ghttp_server_service_object.go +++ b/net/ghttp/ghttp_server_service_object.go @@ -58,7 +58,8 @@ func (s *Server) doBindObject( methodMap[strings.TrimSpace(v)] = true } } - // 当pattern中的method为all时,去掉该method,以便于后续方法判断 + // If the `method` in `pattern` is `defaultMethod`, + // it removes for convenience for next statement control. domain, method, path, err := s.parsePattern(pattern) if err != nil { s.Logger().Fatal(err) @@ -67,11 +68,21 @@ func (s *Server) doBindObject( if strings.EqualFold(method, defaultMethod) { pattern = s.serveHandlerKey("", path, domain) } - m := make(map[string]*handlerItem) - v := reflect.ValueOf(object) - t := v.Type() - initFunc := (func(*Request))(nil) - shutFunc := (func(*Request))(nil) + var ( + m = make(map[string]*handlerItem) + v = reflect.ValueOf(object) + t = v.Type() + initFunc func(*Request) + shutFunc func(*Request) + ) + // If given `object` is not pointer, it then creates a temporary one, + // of which the value is `v`. + if v.Kind() == reflect.Struct { + newValue := reflect.New(t) + newValue.Elem().Set(v) + v = newValue + t = v.Type() + } structName := t.Elem().Name() if v.MethodByName("Init").IsValid() { initFunc = v.MethodByName("Init").Interface().(func(*Request)) @@ -149,9 +160,21 @@ func (s *Server) doBindObjectMethod( pattern string, object interface{}, method string, middleware []HandlerFunc, source string, ) { - m := make(map[string]*handlerItem) - v := reflect.ValueOf(object) - t := v.Type() + var ( + m = make(map[string]*handlerItem) + v = reflect.ValueOf(object) + t = v.Type() + initFunc func(*Request) + shutFunc func(*Request) + ) + // If given `object` is not pointer, it then creates a temporary one, + // of which the value is `v`. + if v.Kind() == reflect.Struct { + newValue := reflect.New(t) + newValue.Elem().Set(v) + v = newValue + t = v.Type() + } structName := t.Elem().Name() methodName := strings.TrimSpace(method) methodValue := v.MethodByName(methodName) @@ -159,8 +182,6 @@ func (s *Server) doBindObjectMethod( s.Logger().Fatal("invalid method name: " + methodName) return } - initFunc := (func(*Request))(nil) - shutFunc := (func(*Request))(nil) if v.MethodByName("Init").IsValid() { initFunc = v.MethodByName("Init").Interface().(func(*Request)) } @@ -199,11 +220,21 @@ func (s *Server) doBindObjectRest( pattern string, object interface{}, middleware []HandlerFunc, source string, ) { - m := make(map[string]*handlerItem) - v := reflect.ValueOf(object) - t := v.Type() - initFunc := (func(*Request))(nil) - shutFunc := (func(*Request))(nil) + var ( + m = make(map[string]*handlerItem) + v = reflect.ValueOf(object) + t = v.Type() + initFunc func(*Request) + shutFunc func(*Request) + ) + // If given `object` is not pointer, it then creates a temporary one, + // of which the value is `v`. + if v.Kind() == reflect.Struct { + newValue := reflect.New(t) + newValue.Elem().Set(v) + v = newValue + t = v.Type() + } structName := t.Elem().Name() if v.MethodByName("Init").IsValid() { initFunc = v.MethodByName("Init").Interface().(func(*Request)) From 4c78ce6e3f2b6a18e7ca947bb0c0b467292c47ee Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 2 Mar 2021 23:27:50 +0800 Subject: [PATCH 173/492] fix issue #1187 --- database/gdb/gdb_type_result_scanlist.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index b18f23891..521c32b04 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -272,6 +272,10 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } } else { + if i >= len(r) { + // There's no relational data. + continue + } v := r[i] if v == nil { // There's no relational data. @@ -300,6 +304,10 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } } else { + if i >= len(r) { + // There's no relational data. + continue + } relationDataItem := r[i] if relationDataItem == nil { // There's no relational data. From e5ca4e788e6cf9e5a6feef61163fcf85b2ad3b85 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Wed, 3 Mar 2021 14:29:01 +0800 Subject: [PATCH 174/492] fix issue #1190 --- os/gcfg/gcfg.go | 90 ++++++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 54 deletions(-) diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index ff3f7a34d..ef672f429 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -71,6 +71,11 @@ func New(file ...string) *Config { } } } else { + // Dir path of working dir. + if err := c.AddPath(gfile.Pwd()); err != nil { + intlog.Error(err) + } + // Dir path of main package. if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) { if err := c.AddPath(mainPath); err != nil { @@ -78,11 +83,6 @@ func New(file ...string) *Config { } } - // Dir path of working dir. - if err := c.AddPath(gfile.Pwd()); err != nil { - intlog.Error(err) - } - // Dir path of binary. if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { if err := c.AddPath(selfPath); err != nil { @@ -93,28 +93,6 @@ func New(file ...string) *Config { return c } -// getSearchPaths is used for checking and using `MainPkgPath` purpose. -// As the `MainPkgPath` is retrieved only by main goroutine, -// it should be checked multiple times when used in configuration file searching. -// It only makes sense in source development environment. -func (c *Config) getSearchPaths() []string { - // Custom configuration directory path. - if !gcmd.GetOptWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).IsEmpty() { - return c.searchPaths.Slice() - } - - var ( - searchPaths = c.searchPaths.Slice() - mainPkgPath = gfile.MainPkgPath() - ) - if mainPkgPath != "" { - if !gstr.InArray(searchPaths, mainPkgPath) { - searchPaths = append([]string{mainPkgPath}, searchPaths...) - } - } - return searchPaths -} - // filePath returns the absolute configuration file path for the given filename by . func (c *Config) filePath(file ...string) (path string) { name := c.defaultName @@ -124,20 +102,21 @@ func (c *Config) filePath(file ...string) (path string) { path = c.FilePath(name) if path == "" { var ( - searchPaths = c.getSearchPaths() - buffer = bytes.NewBuffer(nil) + buffer = bytes.NewBuffer(nil) ) - - if len(searchPaths) > 0 { + if c.searchPaths.Len() > 0 { buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name)) - index := 1 - for _, v := range searchPaths { - v = gstr.TrimRight(v, `\/`) - buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) - index++ - buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) - index++ - } + c.searchPaths.RLockFunc(func(array []string) { + index := 1 + for _, v := range array { + v = gstr.TrimRight(v, `\/`) + buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) + index++ + buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) + index++ + } + }) + } else { buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path set/add", name)) } @@ -298,7 +277,6 @@ func (c *Config) FilePath(file ...string) (path string) { if len(file) > 0 { name = file[0] } - searchPaths := c.getSearchPaths() // Searching resource manager. if !gres.IsEmpty() { for _, v := range resourceTryFiles { @@ -307,25 +285,29 @@ func (c *Config) FilePath(file ...string) (path string) { return } } - for _, prefix := range searchPaths { - for _, v := range resourceTryFiles { - if file := gres.Get(prefix + v + name); file != nil { - path = file.Name() - return + c.searchPaths.RLockFunc(func(array []string) { + for _, prefix := range array { + for _, v := range resourceTryFiles { + if file := gres.Get(prefix + v + name); file != nil { + path = file.Name() + return + } } } - } + }) } // Searching the file system. - for _, prefix := range searchPaths { - prefix = gstr.TrimRight(prefix, `\/`) - if path, _ = gspath.Search(prefix, name); path != "" { - return + c.searchPaths.RLockFunc(func(array []string) { + for _, prefix := range array { + prefix = gstr.TrimRight(prefix, `\/`) + if path, _ = gspath.Search(prefix, name); path != "" { + return + } + if path, _ = gspath.Search(prefix+gfile.Separator+"config", name); path != "" { + return + } } - if path, _ = gspath.Search(prefix+gfile.Separator+"config", name); path != "" { - return - } - } + }) return } From ba74e0beb2a8da4e02ae261a1a0072730bf11100 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 4 Mar 2021 00:07:06 +0800 Subject: [PATCH 175/492] improve ScanList of the same replation names for package gdb --- database/gdb/gdb_type_result_scanlist.go | 4 + database/gdb/gdb_z_mysql_association_test.go | 303 ++++++++++++++++++- 2 files changed, 303 insertions(+), 4 deletions(-) diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index 521c32b04..9eed29c13 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -123,6 +123,10 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // Compatible with old splitting char ':'. array = gstr.SplitAndTrim(relationKVStr, ":") } + if len(array) == 1 { + // The relation names are the same. + array = []string{relationKVStr, relationKVStr} + } if len(array) == 2 { // Defined table field to relation attribute name. // Like: diff --git a/database/gdb/gdb_z_mysql_association_test.go b/database/gdb/gdb_z_mysql_association_test.go index b853a8662..4a7c9d535 100644 --- a/database/gdb/gdb_z_mysql_association_test.go +++ b/database/gdb/gdb_z_mysql_association_test.go @@ -85,7 +85,7 @@ CREATE TABLE %s ( var err error gtest.C(t, func(t *gtest.T) { err = db.Transaction(func(tx *gdb.TX) error { - r, err := tx.Table(tableUser).Save(EntityUser{ + r, err := tx.Model(tableUser).Save(EntityUser{ Name: "john", }) if err != nil { @@ -95,14 +95,14 @@ CREATE TABLE %s ( if err != nil { return err } - _, err = tx.Table(tableUserDetail).Save(EntityUserDetail{ + _, err = tx.Model(tableUserDetail).Save(EntityUserDetail{ Uid: int(uid), Address: "Beijing DongZhiMen #66", }) if err != nil { return err } - _, err = tx.Table(tableUserScores).Save(g.Slice{ + _, err = tx.Model(tableUserScores).Save(g.Slice{ EntityUserScores{Uid: int(uid), Score: 100, Course: "math"}, EntityUserScores{Uid: int(uid), Score: 99, Course: "physics"}, }) @@ -790,6 +790,301 @@ CREATE TABLE %s ( }) } +func Test_Table_Relation_Many_TheSameRelationNames(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampMicroStr() + tableUserDetail = "user_detail_" + gtime.TimestampMicroStr() + tableUserScores = "user_scores_" + gtime.TimestampMicroStr() + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + name varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + address varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + uid int(10) unsigned NOT NULL, + score int(10) unsigned NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + // Initialize the data. + gtest.C(t, func(t *gtest.T) { + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + t.AssertNil(err) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + t.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + t.AssertNil(err) + } + } + }) + + // Result ScanList with struct elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []Entity + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // Result ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "Uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "UID") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // Result ScanList with struct elements and struct attributes. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User EntityUser + UserDetail EntityUserDetail + UserScores []EntityUserScores + } + var users []Entity + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "UId") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "Uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // Result ScanList with pointer elements and struct attributes. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User EntityUser + UserDetail EntityUserDetail + UserScores []EntityUserScores + } + var users []*Entity + + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "UID") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // Model ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + err := db.Model(tableUser). + Where("uid", g.Slice{3, 4}). + Order("uid asc"). + ScanList(&users, "User") + t.AssertNil(err) + // Detail + err = db.Model(tableUserDetail). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("uid asc"). + ScanList(&users, "UserDetail", "User", "uid") + t.AssertNil(err) + // Scores + err = db.Model(tableUserScores). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("id asc"). + ScanList(&users, "UserScores", "User", "uid") + t.AssertNil(err) + + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) +} + func Test_Table_Relation_EmptyData(t *testing.T) { var ( tableUser = "user_" + gtime.TimestampMicroStr() @@ -1001,7 +1296,7 @@ CREATE TABLE %s ( }) } -func Test_Table_Relation_EmbedStruct(t *testing.T) { +func Test_Table_Relation_EmbeddedStruct(t *testing.T) { var ( tableUser = "user_" + gtime.TimestampMicroStr() tableUserDetail = "user_detail_" + gtime.TimestampMicroStr() From ae0cc5a4b63573af95109feacc6faba692f7bb48 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 4 Mar 2021 20:45:05 +0800 Subject: [PATCH 176/492] add more unit testing case for ScanList feature --- database/gdb/gdb_z_mysql_association_test.go | 255 +++++++++++++++++++ 1 file changed, 255 insertions(+) diff --git a/database/gdb/gdb_z_mysql_association_test.go b/database/gdb/gdb_z_mysql_association_test.go index 4a7c9d535..4aa3b11b8 100644 --- a/database/gdb/gdb_z_mysql_association_test.go +++ b/database/gdb/gdb_z_mysql_association_test.go @@ -1296,6 +1296,261 @@ CREATE TABLE %s ( }) } +func Test_Table_Relation_NoneEqualDataSize(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampMicroStr() + tableUserDetail = "user_detail_" + gtime.TimestampMicroStr() + tableUserScores = "user_scores_" + gtime.TimestampMicroStr() + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + name varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + address varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + uid int(10) unsigned NOT NULL, + score int(10) unsigned NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + // Initialize the data. + gtest.C(t, func(t *gtest.T) { + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + t.AssertNil(err) + // Detail. + //_, err = db.Insert(tableUserDetail, g.Map{ + // "uid": i, + // "address": fmt.Sprintf(`address_%d`, i), + //}) + //t.AssertNil(err) + // Scores. + //for j := 1; j <= 5; j++ { + // _, err = db.Insert(tableUserScores, g.Map{ + // "uid": i, + // "score": j, + // }) + // t.AssertNil(err) + //} + } + }) + + // Result ScanList with struct elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []Entity + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, nil) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 0) + }) + + // Result ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "Uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, nil) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "UID") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 0) + }) + + // Result ScanList with struct elements and struct attributes. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User EntityUser + UserDetail EntityUserDetail + UserScores []EntityUserScores + } + var users []Entity + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "UId") + t.AssertNil(err) + t.Assert(users[0].UserDetail, EntityUserDetail{}) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "Uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 0) + }) + + // Result ScanList with pointer elements and struct attributes. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User EntityUser + UserDetail EntityUserDetail + UserScores []EntityUserScores + } + var users []*Entity + + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, EntityUserDetail{}) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "UID") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 0) + }) + + // Model ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + err := db.Model(tableUser). + Where("uid", g.Slice{3, 4}). + Order("uid asc"). + ScanList(&users, "User") + t.AssertNil(err) + // Detail + err = db.Model(tableUserDetail). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("uid asc"). + ScanList(&users, "UserDetail", "User", "uid") + t.AssertNil(err) + // Scores + err = db.Model(tableUserScores). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("id asc"). + ScanList(&users, "UserScores", "User", "uid") + t.AssertNil(err) + + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + + t.Assert(users[0].UserDetail, nil) + + t.Assert(len(users[0].UserScores), 0) + }) +} + func Test_Table_Relation_EmbeddedStruct(t *testing.T) { var ( tableUser = "user_" + gtime.TimestampMicroStr() From 19bfc48dcaccbd16291a3fe3e9337b94346081a0 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 4 Mar 2021 22:49:11 +0800 Subject: [PATCH 177/492] version updates --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 14f5713de..b22017481 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.15.3" +const VERSION = "v1.15.4" const AUTHORS = "john" From 0e58b6e95ba211fcde27954a68cbf4acadbb6bc9 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 8 Mar 2021 23:27:48 +0800 Subject: [PATCH 178/492] README updates --- README.MD | 7 ++----- README_ZH.MD | 7 +------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/README.MD b/README.MD index 97ccc9d92..ad35aafcc 100644 --- a/README.MD +++ b/README.MD @@ -9,11 +9,8 @@ English | [简体中文](README_ZH.MD) -`GF(GoFrame)` is a modular, powerful, high-performance and enterprise-class application development framework -of Golang. Providing a series of core components and dozens of practical modules, such as: -cache, logging, containers, timer, resource, validator, database orm, etc. -Supporting web server integrated with router, cookie, session, middleware, logger, configure, -template, https, hooks, rewrites and many more features. +`GoFrame` is a modular, powerful, high-performance and enterprise-class application development framework +of Golang. > If you're a newbie to `Go`, you may consider `GoFrame` easy and great as `Laravel` in `PHP`, `SpringBoot` in `Java` or `Django` in `Python`. diff --git a/README_ZH.MD b/README_ZH.MD index 9fdcdf486..025b94cb5 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -8,12 +8,7 @@ [English](README.MD) | 简体中文 -`GF(Go Frame)`是一款模块化、高性能、企业级的Go基础开发框架。 -实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块, -如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、 -配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。 -并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等, -支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。 +`GoFrame`是一款模块化、高性能、企业级的Go基础开发框架。 > 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。 From 4d3273379022a9518c1dc20ebada612cddeed764 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 9 Mar 2021 22:54:38 +0800 Subject: [PATCH 179/492] improve ghttp.Client for #1179 --- net/ghttp/internal/client/client_request.go | 52 +++++++++++++-------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/net/ghttp/internal/client/client_request.go b/net/ghttp/internal/client/client_request.go index cc8da7504..3d664e361 100644 --- a/net/ghttp/internal/client/client_request.go +++ b/net/ghttp/internal/client/client_request.go @@ -136,53 +136,67 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h if len(c.prefix) > 0 { url = c.prefix + gstr.Trim(url) } - param := "" + var params string if len(data) > 0 { switch c.header["Content-Type"] { case "application/json": switch data[0].(type) { case string, []byte: - param = gconv.String(data[0]) + params = gconv.String(data[0]) default: if b, err := json.Marshal(data[0]); err != nil { return nil, err } else { - param = gconv.UnsafeBytesToStr(b) + params = string(b) } } case "application/xml": switch data[0].(type) { case string, []byte: - param = gconv.String(data[0]) + params = gconv.String(data[0]) default: if b, err := gparser.VarToXml(data[0]); err != nil { return nil, err } else { - param = gconv.UnsafeBytesToStr(b) + params = string(b) } } default: - param = httputil.BuildParams(data[0]) + params = httputil.BuildParams(data[0]) } } if method == "GET" { - // It appends the parameters to the url if http method is GET. - if param != "" { - if gstr.Contains(url, "?") { - url = url + "&" + param - } else { - url = url + "?" + param + var bodyBuffer *bytes.Buffer + if params != "" { + switch c.header["Content-Type"] { + case + "application/json", + "application/xml": + bodyBuffer = bytes.NewBuffer([]byte(params)) + default: + // It appends the parameters to the url + // if http method is GET and Content-Type is not specified. + if gstr.Contains(url, "?") { + url = url + "&" + params + } else { + url = url + "?" + params + } + bodyBuffer = bytes.NewBuffer(nil) } + } else { + bodyBuffer = bytes.NewBuffer(nil) } - if req, err = http.NewRequest(method, url, bytes.NewBuffer(nil)); err != nil { + if req, err = http.NewRequest(method, url, bodyBuffer); err != nil { return nil, err } } else { - if strings.Contains(param, "@file:") { + if strings.Contains(params, "@file:") { // File uploading request. - buffer := new(bytes.Buffer) - writer := multipart.NewWriter(buffer) - for _, item := range strings.Split(param, "&") { + var ( + buffer = bytes.NewBuffer(nil) + writer = multipart.NewWriter(buffer) + ) + for _, item := range strings.Split(params, "&") { array := strings.Split(item, "=") if len(array[1]) > 6 && strings.Compare(array[1][0:6], "@file:") == 0 { path := array[1][6:] @@ -225,7 +239,7 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h } } else { // Normal request. - paramBytes := []byte(param) + paramBytes := []byte(params) if req, err = http.NewRequest(method, url, bytes.NewReader(paramBytes)); err != nil { return nil, err } else { @@ -236,7 +250,7 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h if (paramBytes[0] == '[' || paramBytes[0] == '{') && json.Valid(paramBytes) { // Auto detecting and setting the post content format: JSON. req.Header.Set("Content-Type", "application/json") - } else if gregex.IsMatchString(`^[\w\[\]]+=.+`, param) { + } else if gregex.IsMatchString(`^[\w\[\]]+=.+`, params) { // If the parameters passed like "name=value", it then uses form type. req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } From 0d4c1c47d52250c28ee813f7bccdd4756ca7feb5 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 10 Mar 2021 21:19:11 +0800 Subject: [PATCH 180/492] improve package grand --- util/grand/grand_buffer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/grand/grand_buffer.go b/util/grand/grand_buffer.go index 8512e040a..07861a515 100644 --- a/util/grand/grand_buffer.go +++ b/util/grand/grand_buffer.go @@ -12,13 +12,13 @@ import ( const ( // Buffer size for uint32 random number. - gBUFFER_SIZE = 10000 + bufferChanSize = 10000 ) var ( // bufferChan is the buffer for random bytes, // every item storing 4 bytes. - bufferChan = make(chan []byte, gBUFFER_SIZE) + bufferChan = make(chan []byte, bufferChanSize) ) func init() { From 20f2a6c00333b93c63edc6c54af4390508dac8ef Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 10 Mar 2021 23:28:34 +0800 Subject: [PATCH 181/492] add recursive validation feature of struct attribute for package gvalid for #1165 --- internal/structs/structs_field.go | 20 +++++++++++ test/gtest/gtest_util.go | 2 +- util/gvalid/gvalid_validator_check_struct.go | 34 +++++++++++++------ util/gvalid/gvalid_z_unit_checkstruct_test.go | 28 ++++++++++++++- 4 files changed, 72 insertions(+), 12 deletions(-) diff --git a/internal/structs/structs_field.go b/internal/structs/structs_field.go index 8d3e0afe8..a0ad1a1cb 100644 --- a/internal/structs/structs_field.go +++ b/internal/structs/structs_field.go @@ -6,6 +6,8 @@ package structs +import "reflect" + // Tag returns the value associated with key in the tag string. If there is no // such key in the tag, Tag returns the empty string. func (f *Field) Tag(key string) string { @@ -34,6 +36,24 @@ func (f *Field) Type() Type { } } +// Kind returns the reflect.Kind for Value of Field `f`. +func (f *Field) Kind() reflect.Kind { + return f.Value.Kind() +} + +// OriginalKind retrieves and returns the original reflect.Kind for Value of Field `f`. +func (f *Field) OriginalKind() reflect.Kind { + var ( + kind = f.Value.Kind() + value = f.Value + ) + for kind == reflect.Ptr { + value = value.Elem() + kind = value.Kind() + } + return kind +} + // FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`. // // The parameter `pointer` should be type of struct/*struct. diff --git a/test/gtest/gtest_util.go b/test/gtest/gtest_util.go index 60b90d8de..a1644afa2 100644 --- a/test/gtest/gtest_util.go +++ b/test/gtest/gtest_util.go @@ -341,7 +341,7 @@ func compareMap(value, expect interface{}) error { return fmt.Errorf(`[ASSERT] EXPECT MAP LENGTH %d == %d`, rvValue.Len(), rvExpect.Len()) } } else { - return fmt.Errorf(`[ASSERT] EXPECT VALUE TO BE A MAP`) + return fmt.Errorf(`[ASSERT] EXPECT VALUE TO BE A MAP, BUT GIVEN "%s"`, rvValue.Kind()) } } return nil diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 580defc75..f56c54177 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -9,6 +9,7 @@ package gvalid import ( "github.com/gogf/gf/internal/structs" "github.com/gogf/gf/util/gconv" + "reflect" "strings" ) @@ -17,13 +18,31 @@ var ( aliasNameTagPriority = []string{"param", "params", "p"} // aliasNameTagPriority specifies the alias tag priority array. ) -// CheckStruct validates strcut and returns the error result. +// CheckStruct validates struct and returns the error result. // // The parameter should be type of struct/*struct. // The parameter can be type of []string/map[string]string. It supports sequence in error result // if is type of []string. // The optional parameter specifies the custom error messages for specified keys and rules. func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error { + var ( + errorMaps = make(ErrorMap) // Returned error. + ) + mapField, err := structs.FieldMap(object, aliasNameTagPriority) + if err != nil { + return newErrorStr("invalid_object", err.Error()) + } + // It checks the struct recursively the its attribute is also a struct. + for _, field := range mapField { + if field.OriginalKind() == reflect.Struct { + if err := v.CheckStruct(field.Value, rules, messages...); err != nil { + // It merges the errors into single error map. + for k, m := range err.errors { + errorMaps[k] = m + } + } + } + } // It here must use structs.TagFields not structs.FieldMap to ensure error sequence. tagField, err := structs.TagFields(object, structTagPriority) if err != nil { @@ -39,7 +58,6 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages customMessage = make(CustomMsg) fieldAliases = make(map[string]string) // Alias names for overwriting struct tag names. errorRules = make([]string, 0) // Sequence rules. - errorMaps = make(ErrorMap) // Returned error ) switch v := rules.(type) { // Sequence tag: []sequence tag @@ -85,10 +103,6 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages return nil } // Checks and extends the parameters map with struct alias tag. - mapField, err := structs.FieldMap(object, aliasNameTagPriority) - if err != nil { - return newErrorStr("invalid_object", err.Error()) - } for nameOrTag, field := range mapField { params[nameOrTag] = field.Value.Interface() params[field.Name()] = field.Value.Interface() @@ -167,10 +181,10 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages // It checks each rule and its value in loop. if e := v.doCheck(key, value, rule, customMessage[key], params); e != nil { _, item := e.FirstItem() - // =========================================================== - // Only in map and struct validations, if value is nil or empty - // string and has no required* rules, it clears the error message. - // =========================================================== + // =================================================================== + // Only in map and struct validations, if value is nil or empty string + // and has no required* rules, it clears the error message. + // =================================================================== if value == nil || gconv.String(value) == "" { required := false // rule => error diff --git a/util/gvalid/gvalid_z_unit_checkstruct_test.go b/util/gvalid/gvalid_z_unit_checkstruct_test.go index 4f630acae..97867763f 100755 --- a/util/gvalid/gvalid_z_unit_checkstruct_test.go +++ b/util/gvalid/gvalid_z_unit_checkstruct_test.go @@ -226,7 +226,7 @@ func Test_CheckStruct(t *testing.T) { }) } -func Test_CheckStruct_With_EmbedObject(t *testing.T) { +func Test_CheckStruct_With_EmbeddedObject(t *testing.T) { gtest.C(t, func(t *gtest.T) { type Pass struct { Pass1 string `valid:"password1@required|same:password2#请输入您的密码|您两次输入的密码不一致"` @@ -252,6 +252,32 @@ func Test_CheckStruct_With_EmbedObject(t *testing.T) { }) } +func Test_CheckStruct_With_StructAttribute(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Pass struct { + Pass1 string `valid:"password1@required|same:password2#请输入您的密码|您两次输入的密码不一致"` + Pass2 string `valid:"password2@required|same:password1#请再次输入您的密码|您两次输入的密码不一致"` + } + type User struct { + Id int + Name string `valid:"name@required#请输入您的姓名"` + Passwords Pass + } + user := &User{ + Name: "", + Passwords: Pass{ + Pass1: "1", + Pass2: "2", + }, + } + err := gvalid.CheckStruct(user, nil) + t.AssertNE(err, nil) + t.Assert(err.Maps()["name"], g.Map{"required": "请输入您的姓名"}) + t.Assert(err.Maps()["password1"], g.Map{"same": "您两次输入的密码不一致"}) + t.Assert(err.Maps()["password2"], g.Map{"same": "您两次输入的密码不一致"}) + }) +} + func Test_CheckStruct_Optional(t *testing.T) { gtest.C(t, func(t *gtest.T) { type Params struct { From 7702c5bfdef4838ebef30c5ecb72e5d9005c829f Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 11 Mar 2021 11:39:49 +0800 Subject: [PATCH 182/492] add internal logging for package gdb/gredis --- database/gdb/gdb.go | 6 +++++- database/gredis/gredis.go | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 98f0cb75c..10672ca3a 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -473,9 +473,13 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error v, _ := internalCache.GetOrSetFuncLock(node.String(), func() (interface{}, error) { sqlDb, err = c.db.Open(node) if err != nil { - intlog.Printf("DB open failed: %v, %+v", err, node) + intlog.Printf(`db open failed: %v, %+v`, err, node) return nil, err } + intlog.Printf( + `open new connection, master:%v, config:%+v, node:%+v`, + master, c.config, node, + ) if c.config.MaxIdleConnCount > 0 { sqlDb.SetMaxIdleConns(c.config.MaxIdleConnCount) } else { diff --git a/database/gredis/gredis.go b/database/gredis/gredis.go index bb20a6973..b28b73530 100644 --- a/database/gredis/gredis.go +++ b/database/gredis/gredis.go @@ -16,6 +16,7 @@ package gredis import ( "context" "fmt" + "github.com/gogf/gf/internal/intlog" "time" "github.com/gogf/gf/container/gmap" @@ -113,6 +114,7 @@ func New(config *Config) *Redis { if err != nil { return nil, err } + intlog.Printf(`open new connection, config:%+v`, config) // AUTH if len(config.Pass) > 0 { if _, err := c.Do("AUTH", config.Pass); err != nil { From d72d23c2eb896b3e2aa65411890948dd7a124d0a Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 11 Mar 2021 18:58:13 +0800 Subject: [PATCH 183/492] change browser mode from boolean markable variable to cookiejar for ghttp.Client --- net/ghttp/ghttp_unit_client_dump_test.go | 2 -- net/ghttp/internal/client/client.go | 7 +++++-- net/ghttp/internal/client/client_request.go | 12 ------------ 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/net/ghttp/ghttp_unit_client_dump_test.go b/net/ghttp/ghttp_unit_client_dump_test.go index e6bd789dc..f04f89eb0 100644 --- a/net/ghttp/ghttp_unit_client_dump_test.go +++ b/net/ghttp/ghttp_unit_client_dump_test.go @@ -52,7 +52,5 @@ func Test_Client_Request_13_Dump(t *testing.T) { t.Assert(gstr.Contains(dumpedText3, "test_for_request_body"), true) dumpedText4 := r2.RawResponse() t.Assert(gstr.Contains(dumpedText4, "test_for_request_body"), false) - }) - } diff --git a/net/ghttp/internal/client/client.go b/net/ghttp/internal/client/client.go index 7e6366e2d..b1e8620a8 100644 --- a/net/ghttp/internal/client/client.go +++ b/net/ghttp/internal/client/client.go @@ -18,6 +18,7 @@ import ( "golang.org/x/net/proxy" "net" "net/http" + "net/http/cookiejar" "net/url" "strings" "time" @@ -36,7 +37,6 @@ type Client struct { prefix string // Prefix for request. authUser string // HTTP basic authentication: user. authPass string // HTTP basic authentication: pass. - browserMode bool // Whether auto saving and sending cookie content. retryCount int // Retry count when request fails. retryInterval time.Duration // Retry interval when request fails. middlewareHandler []HandlerFunc // Interceptor handlers @@ -84,7 +84,10 @@ func (c *Client) Clone() *Client { // When browser mode is enabled, it automatically saves and sends cookie content // from and to server. func (c *Client) SetBrowserMode(enabled bool) *Client { - c.browserMode = enabled + if enabled { + jar, _ := cookiejar.New(nil) + c.Jar = jar + } return c } diff --git a/net/ghttp/internal/client/client_request.go b/net/ghttp/internal/client/client_request.go index 3d664e361..545e8e7b9 100644 --- a/net/ghttp/internal/client/client_request.go +++ b/net/ghttp/internal/client/client_request.go @@ -115,18 +115,6 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Respo } else { resp, err = c.callRequest(req) } - - // Auto saving cookie content. - if c.browserMode && resp != nil && resp.Response != nil { - now := time.Now() - for _, v := range resp.Response.Cookies() { - if !v.Expires.IsZero() && v.Expires.UnixNano() < now.UnixNano() { - delete(c.cookies, v.Name) - } else { - c.cookies[v.Name] = v.Value - } - } - } return resp, err } From 58362ad1436d3abdf66b3a3aaad3042514c4ec6c Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 11 Mar 2021 20:05:08 +0800 Subject: [PATCH 184/492] add grand.D for random time.Duration;add checking and removing session files for package gsession --- os/gsession/gsession_storage_file.go | 91 ++++++++++++++++++++-------- util/grand/grand.go | 15 +++++ util/grand/grand_z_unit_test.go | 18 ++++++ 3 files changed, 98 insertions(+), 26 deletions(-) diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index f1df7d8db..f67aee337 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -7,11 +7,11 @@ package gsession import ( - "fmt" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" + "github.com/gogf/gf/util/grand" "os" "time" @@ -36,10 +36,12 @@ type StorageFile struct { } var ( - DefaultStorageFilePath = gfile.TempDir("gsessions") - DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!") - DefaultStorageFileCryptoEnabled = false - DefaultStorageFileLoopInterval = 10 * time.Second + DefaultStorageFilePath = gfile.TempDir("gsessions") + DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!") + DefaultStorageFileCryptoEnabled = false + DefaultStorageFileLoopInterval = 10 * time.Second + DefaultStorageFileRemoveIntervalMin = time.Hour + DefaultStorageFileRemoveIntervalMax = time.Hour * 24 ) // NewStorageFile creates and returns a file storage object for session. @@ -48,10 +50,10 @@ func NewStorageFile(path ...string) *StorageFile { if len(path) > 0 && path[0] != "" { storagePath, _ = gfile.Search(path[0]) if storagePath == "" { - panic(fmt.Sprintf("'%s' does not exist", path[0])) + panic(gerror.Newf("'%s' does not exist", path[0])) } if !gfile.IsWritable(storagePath) { - panic(fmt.Sprintf("'%s' is not writable", path[0])) + panic(gerror.Newf("'%s' is not writable", path[0])) } } if storagePath != "" { @@ -65,24 +67,61 @@ func NewStorageFile(path ...string) *StorageFile { cryptoEnabled: DefaultStorageFileCryptoEnabled, updatingIdSet: gset.NewStrSet(true), } - // Batch updates the TTL for session ids timely. - gtimer.AddSingleton(DefaultStorageFileLoopInterval, func() { - //intlog.Print("StorageFile.timer start") - var ( - id string - err error - ) - for { - if id = s.updatingIdSet.Pop(); id == "" { - break - } - if err = s.doUpdateTTL(id); err != nil { - intlog.Error(err) + + gtimer.AddSingleton(DefaultStorageFileLoopInterval, s.updateSessionTimely) + gtimer.AddOnce( + grand.D(DefaultStorageFileRemoveIntervalMin, DefaultStorageFileRemoveIntervalMax), + s.checkAndRemoveSessionTimely, + ) + return s +} + +// updateSessionTimely batch updates the TTL for sessions timely. +func (s *StorageFile) updateSessionTimely() { + var ( + id string + err error + ) + // Batch updating sessions. + for { + if id = s.updatingIdSet.Pop(); id == "" { + break + } + if err = s.updateSessionTTl(id); err != nil { + intlog.Error(err) + } + } +} + +// checkAndRemoveSessionTimely checks the session storage directory path and removes the expired session files. +func (s *StorageFile) checkAndRemoveSessionTimely() { + defer gtimer.AddOnce( + grand.D(DefaultStorageFileRemoveIntervalMin, DefaultStorageFileRemoveIntervalMax), + s.checkAndRemoveSessionTimely, + ) + + var ( + timestampMilliFile int64 + timestampMilliNow = gtime.Now().TimestampMilli() + files, _ = gfile.ScanDirFile(s.path, "*") + timestampMilliBytes = make([]byte, 8) + ) + for _, path := range files { + file, err := gfile.OpenWithFlag(path, os.O_RDONLY) + if err != nil { + continue + } + if _, err := file.Read(timestampMilliBytes); err == nil { + timestampMilliFile = gbinary.DecodeToInt64(timestampMilliBytes) + if timestampMilliNow >= timestampMilliFile { + intlog.Printf( + "remove expired session file: %s, result:%v", + path, gfile.Remove(path), + ) } } - //intlog.Print("StorageFile.timer end") - }) - return s + file.Close() + } } // SetCryptoKey sets the crypto key for session storage. @@ -229,9 +268,9 @@ func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error { return nil } -// doUpdateTTL updates the TTL for session id. -func (s *StorageFile) doUpdateTTL(id string) error { - intlog.Printf("StorageFile.doUpdateTTL: %s", id) +// updateSessionTTL updates the TTL for specified session id. +func (s *StorageFile) updateSessionTTl(id string) error { + intlog.Printf("StorageFile.updateSession: %s", id) path := s.sessionFilePath(id) file, err := gfile.OpenWithFlag(path, os.O_WRONLY) if err != nil { diff --git a/util/grand/grand.go b/util/grand/grand.go index 892e6341d..fcd9e3f4d 100644 --- a/util/grand/grand.go +++ b/util/grand/grand.go @@ -9,6 +9,7 @@ package grand import ( "encoding/binary" + "time" "unsafe" ) @@ -97,6 +98,20 @@ func S(n int, symbols ...bool) string { return *(*string)(unsafe.Pointer(&b)) } +// D returns a random time.Duration between min and max: [min, max]. +func D(min, max time.Duration) time.Duration { + multiple := 1 + if min != 0 { + for min%10 == 0 { + multiple *= 10 + min /= 10 + max /= 10 + } + } + n := N(int(min), int(max)) + return time.Duration(n * multiple) +} + // Str randomly picks and returns count of chars from given string . // It also supports unicode string like Chinese/Russian/Japanese, etc. func Str(s string, n int) string { diff --git a/util/grand/grand_z_unit_test.go b/util/grand/grand_z_unit_test.go index 16953742a..133df3282 100644 --- a/util/grand/grand_z_unit_test.go +++ b/util/grand/grand_z_unit_test.go @@ -11,6 +11,7 @@ package grand_test import ( "github.com/gogf/gf/text/gstr" "testing" + "time" "github.com/gogf/gf/test/gtest" "github.com/gogf/gf/util/grand" @@ -73,6 +74,23 @@ func Test_N(t *testing.T) { }) } +func Test_D(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + for i := 0; i < 100; i++ { + t.Assert(grand.D(time.Second, time.Second), time.Second) + } + for i := 0; i < 100; i++ { + t.Assert(grand.D(0, 0), time.Duration(0)) + } + for i := 0; i < 100; i++ { + t.AssertIN( + grand.D(1*time.Second, 3*time.Second), + []time.Duration{1 * time.Second, 2 * time.Second, 3 * time.Second}, + ) + } + }) +} + func Test_Rand(t *testing.T) { gtest.C(t, func(t *gtest.T) { for i := 0; i < 100; i++ { From 6376b8aaa6901024be1e9d7e14ebc1b87fdd7241 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 11 Mar 2021 21:21:47 +0800 Subject: [PATCH 185/492] remove session storage file removing feature for package gsession --- .../net/ghttp/server/session/basic/session.go | 1 - os/gsession/gsession_storage_file.go | 46 ++----------------- 2 files changed, 4 insertions(+), 43 deletions(-) diff --git a/.example/net/ghttp/server/session/basic/session.go b/.example/net/ghttp/server/session/basic/session.go index d7ae0e1ec..c7a21a684 100644 --- a/.example/net/ghttp/server/session/basic/session.go +++ b/.example/net/ghttp/server/session/basic/session.go @@ -8,7 +8,6 @@ import ( func main() { s := g.Server() - s.SetSessionCookieMaxAge(0) s.Group("/", func(group *ghttp.RouterGroup) { group.GET("/set", func(r *ghttp.Request) { r.Session.Set("time", gtime.Timestamp()) diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index f67aee337..51936a499 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -11,7 +11,6 @@ import ( "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" - "github.com/gogf/gf/util/grand" "os" "time" @@ -36,12 +35,10 @@ type StorageFile struct { } var ( - DefaultStorageFilePath = gfile.TempDir("gsessions") - DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!") - DefaultStorageFileCryptoEnabled = false - DefaultStorageFileLoopInterval = 10 * time.Second - DefaultStorageFileRemoveIntervalMin = time.Hour - DefaultStorageFileRemoveIntervalMax = time.Hour * 24 + DefaultStorageFilePath = gfile.TempDir("gsessions") + DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!") + DefaultStorageFileCryptoEnabled = false + DefaultStorageFileLoopInterval = 10 * time.Second ) // NewStorageFile creates and returns a file storage object for session. @@ -69,10 +66,6 @@ func NewStorageFile(path ...string) *StorageFile { } gtimer.AddSingleton(DefaultStorageFileLoopInterval, s.updateSessionTimely) - gtimer.AddOnce( - grand.D(DefaultStorageFileRemoveIntervalMin, DefaultStorageFileRemoveIntervalMax), - s.checkAndRemoveSessionTimely, - ) return s } @@ -93,37 +86,6 @@ func (s *StorageFile) updateSessionTimely() { } } -// checkAndRemoveSessionTimely checks the session storage directory path and removes the expired session files. -func (s *StorageFile) checkAndRemoveSessionTimely() { - defer gtimer.AddOnce( - grand.D(DefaultStorageFileRemoveIntervalMin, DefaultStorageFileRemoveIntervalMax), - s.checkAndRemoveSessionTimely, - ) - - var ( - timestampMilliFile int64 - timestampMilliNow = gtime.Now().TimestampMilli() - files, _ = gfile.ScanDirFile(s.path, "*") - timestampMilliBytes = make([]byte, 8) - ) - for _, path := range files { - file, err := gfile.OpenWithFlag(path, os.O_RDONLY) - if err != nil { - continue - } - if _, err := file.Read(timestampMilliBytes); err == nil { - timestampMilliFile = gbinary.DecodeToInt64(timestampMilliBytes) - if timestampMilliNow >= timestampMilliFile { - intlog.Printf( - "remove expired session file: %s, result:%v", - path, gfile.Remove(path), - ) - } - } - file.Close() - } -} - // SetCryptoKey sets the crypto key for session storage. // The crypto key is used when crypto feature is enabled. func (s *StorageFile) SetCryptoKey(key []byte) { From 41f2138b393814bd98d1c154b05d619e6aa8ecea Mon Sep 17 00:00:00 2001 From: John Date: Thu, 11 Mar 2021 23:29:39 +0800 Subject: [PATCH 186/492] fix issue of overflow in grand.D --- util/grand/grand.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/grand/grand.go b/util/grand/grand.go index fcd9e3f4d..8da9dbcd1 100644 --- a/util/grand/grand.go +++ b/util/grand/grand.go @@ -100,7 +100,7 @@ func S(n int, symbols ...bool) string { // D returns a random time.Duration between min and max: [min, max]. func D(min, max time.Duration) time.Duration { - multiple := 1 + multiple := int64(1) if min != 0 { for min%10 == 0 { multiple *= 10 @@ -108,7 +108,7 @@ func D(min, max time.Duration) time.Duration { max /= 10 } } - n := N(int(min), int(max)) + n := int64(N(int(min), int(max))) return time.Duration(n * multiple) } From 17233084f1de8e4c8436ac29a75b699d3602ce8c Mon Sep 17 00:00:00 2001 From: yys <4752017@qq.com> Date: Sat, 13 Mar 2021 14:07:22 +0800 Subject: [PATCH 187/492] =?UTF-8?q?=E4=B8=BAgtime=E6=B7=BB=E5=8A=A0scanner?= =?UTF-8?q?=E5=92=8Cvaluer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/gtime/gtime_sql.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 os/gtime/gtime_sql.go diff --git a/os/gtime/gtime_sql.go b/os/gtime/gtime_sql.go new file mode 100644 index 000000000..142448d98 --- /dev/null +++ b/os/gtime/gtime_sql.go @@ -0,0 +1,17 @@ +package gtime + +import ( + "database/sql/driver" +) + +//add Scanner +func (t *Time) Scan(value interface{}) error { + newTime := New(value) + t.Time = newTime.Time + return nil +} + +//add valuer +func (t *Time) Value() (driver.Value, error) { + return t.Time, nil +} From 150f237f1339c899ec81497254b084b84202b63e Mon Sep 17 00:00:00 2001 From: jianchenma Date: Tue, 16 Mar 2021 14:39:01 +0800 Subject: [PATCH 188/492] fix issue in incorrect parameter sequence in package gi18n;improve package gcfg for detailed error printing --- frame/gins/gins_database.go | 14 ++- frame/gins/gins_redis.go | 9 +- i18n/gi18n/gi18n.go | 2 +- os/gcfg/gcfg.go | 151 +++++++++++++-------------- os/gcfg/gcfg_api.go | 98 ++++++++--------- os/gcfg/gcfg_config.go | 16 +-- os/gcfg/gcfg_instance.go | 2 +- os/gcfg/gcfg_z_unit_basic_test.go | 11 +- os/gcfg/gcfg_z_unit_instance_test.go | 3 +- 9 files changed, 155 insertions(+), 151 deletions(-) diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index e313e1ec5..2e92392ed 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -48,22 +48,26 @@ func Database(name ...string) gdb.DB { configMap = Config().GetMap(configNodeKey) } if len(configMap) == 0 && !gdb.IsConfigured() { - if !Config().Available() { + configFilePath, err := Config().GetFilePath() + if configFilePath == "" { exampleFileName := "config.example.toml" - if Config().Available(exampleFileName) { - panic(gerror.Newf( + if exampleConfigFilePath, _ := Config().GetFilePath(exampleFileName); exampleConfigFilePath != "" { + panic(gerror.Wrapf( + err, `configuration file "%s" not found, but found "%s", did you miss renaming the configuration example file?`, Config().GetFileName(), exampleFileName, )) } else { - panic(gerror.Newf( + panic(gerror.Wrapf( + err, `configuration file "%s" not found, did you miss the configuration file or the file name setting?`, Config().GetFileName(), )) } } - panic(gerror.Newf( + panic(gerror.Wrapf( + err, `database initialization failed: "%s" node not found, is configuration file or configuration node missing?`, configNodeNameDatabase, )) diff --git a/frame/gins/gins_redis.go b/frame/gins/gins_redis.go index 12ba495b4..ef0ad47f4 100644 --- a/frame/gins/gins_redis.go +++ b/frame/gins/gins_redis.go @@ -47,7 +47,14 @@ func Redis(name ...string) *gredis.Redis { panic(fmt.Sprintf(`configuration for redis not found for group "%s"`, group)) } } else { - panic(fmt.Sprintf(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath())) + filepath, err := config.GetFilePath() + if err != nil { + panic(err) + } + panic(fmt.Sprintf( + `incomplete configuration for redis: "redis" node not found in config file "%s"`, + filepath, + )) } return nil }) diff --git a/i18n/gi18n/gi18n.go b/i18n/gi18n/gi18n.go index 6fc153d73..c3665ad72 100644 --- a/i18n/gi18n/gi18n.go +++ b/i18n/gi18n/gi18n.go @@ -53,7 +53,7 @@ func TranslateFormat(format string, values ...interface{}) string { // configured language. If is given empty string, it uses the default configured // language for the translation. func TranslateFormatLang(language string, format string, values ...interface{}) string { - return defaultManager.TranslateFormatLang(format, language, values...) + return defaultManager.TranslateFormatLang(language, format, values...) } // Translate translates with configured language and returns the translated content. diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index ef672f429..1cd6a7d25 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -11,6 +11,7 @@ import ( "bytes" "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gcmd" "github.com/gogf/gf/text/gstr" @@ -45,7 +46,7 @@ var ( ) // New returns a new configuration management object. -// The parameter specifies the default configuration file name for reading. +// The parameter `file` specifies the default configuration file name for reading. func New(file ...string) *Config { name := DefaultConfigFile if len(file) > 0 { @@ -67,7 +68,7 @@ func New(file ...string) *Config { _ = c.SetPath(customPath) } else { if errorPrint() { - glog.Errorf("Configuration directory path does not exist: %s", customPath) + glog.Errorf("[gcfg] Configuration directory path does not exist: %s", customPath) } } } else { @@ -93,42 +94,8 @@ func New(file ...string) *Config { return c } -// filePath returns the absolute configuration file path for the given filename by . -func (c *Config) filePath(file ...string) (path string) { - name := c.defaultName - if len(file) > 0 { - name = file[0] - } - path = c.FilePath(name) - if path == "" { - var ( - buffer = bytes.NewBuffer(nil) - ) - if c.searchPaths.Len() > 0 { - buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name)) - c.searchPaths.RLockFunc(func(array []string) { - index := 1 - for _, v := range array { - v = gstr.TrimRight(v, `\/`) - buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) - index++ - buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) - index++ - } - }) - - } else { - buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path set/add", name)) - } - if errorPrint() { - glog.Error(buffer.String()) - } - } - return path -} - // SetPath sets the configuration directory path for file search. -// The parameter can be absolute or relative path, +// The parameter `path` can be absolute or relative path, // but absolute path is strongly recommended. func (c *Config) SetPath(path string) error { var ( @@ -246,14 +213,14 @@ func (c *Config) AddPath(path string) error { } else { buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path)) } - err := errors.New(buffer.String()) + err := gerror.New(buffer.String()) if errorPrint() { glog.Error(err) } return err } if !isDir { - err := fmt.Errorf(`[gcfg] AddPath failed: path "%s" should be directory type`, path) + err := gerror.Newf(`[gcfg] AddPath failed: path "%s" should be directory type`, path) if errorPrint() { glog.Error(err) } @@ -268,11 +235,38 @@ func (c *Config) AddPath(path string) error { return nil } -// GetFilePath returns the absolute path of the specified configuration file. -// If is not passed, it returns the configuration file path of the default name. -// If the specified configuration file does not exist, -// an empty string is returned. -func (c *Config) FilePath(file ...string) (path string) { +// SetFileName sets the default configuration file name. +func (c *Config) SetFileName(name string) *Config { + c.defaultName = name + return c +} + +// GetFileName returns the default configuration file name. +func (c *Config) GetFileName() string { + return c.defaultName +} + +// Available checks and returns whether configuration of given `file` is available. +func (c *Config) Available(file ...string) bool { + var name string + if len(file) > 0 && file[0] != "" { + name = file[0] + } else { + name = c.defaultName + } + if path, _ := c.GetFilePath(name); path != "" { + return true + } + if GetContent(name) != "" { + return true + } + return false +} + +// GetFilePath returns the absolute configuration file path for the given filename by `file`. +// If `file` is not passed, it returns the configuration file path of the default name. +// It returns an empty `path` string and an error if the given `file` does not exist. +func (c *Config) GetFilePath(file ...string) (path string, err error) { name := c.defaultName if len(file) > 0 { name = file[0] @@ -308,38 +302,32 @@ func (c *Config) FilePath(file ...string) (path string) { } } }) + // If it cannot find the path of `file`, it formats and returns a detailed error. + if path == "" { + var ( + buffer = bytes.NewBuffer(nil) + ) + if c.searchPaths.Len() > 0 { + buffer.WriteString(fmt.Sprintf(`[gcfg] cannot find config file "%s" in resource manager or the following paths:`, name)) + c.searchPaths.RLockFunc(func(array []string) { + index := 1 + for _, v := range array { + v = gstr.TrimRight(v, `\/`) + buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) + index++ + buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) + index++ + } + }) + } else { + buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path configured", name)) + } + err = gerror.New(buffer.String()) + } return } -// SetFileName sets the default configuration file name. -func (c *Config) SetFileName(name string) *Config { - c.defaultName = name - return c -} - -// GetFileName returns the default configuration file name. -func (c *Config) GetFileName() string { - return c.defaultName -} - -// Available checks and returns whether configuration of given is available. -func (c *Config) Available(file ...string) bool { - var name string - if len(file) > 0 && file[0] != "" { - name = file[0] - } else { - name = c.defaultName - } - if c.FilePath(name) != "" { - return true - } - if GetContent(name) != "" { - return true - } - return false -} - -// getJson returns a *gjson.Json object for the specified content. +// getJson returns a *gjson.Json object for the specified `file` content. // It would print error if file reading fails. It return nil if any error occurs. func (c *Config) getJson(file ...string) *gjson.Json { var name string @@ -350,14 +338,18 @@ func (c *Config) getJson(file ...string) *gjson.Json { } r := c.jsonMap.GetOrSetFuncLock(name, func() interface{} { var ( - content = "" - filePath = "" + err error + content string + filePath string ) // The configured content can be any kind of data type different from its file type. isFromConfigContent := true if content = GetContent(name); content == "" { isFromConfigContent = false - filePath = c.filePath(name) + filePath, err = c.GetFilePath(name) + if err != nil && errorPrint() { + glog.Error(err) + } if filePath == "" { return nil } @@ -369,8 +361,7 @@ func (c *Config) getJson(file ...string) *gjson.Json { } // Note that the underlying configuration json object operations are concurrent safe. var ( - j *gjson.Json - err error + j *gjson.Json ) dataType := gfile.ExtName(name) if gjson.IsValidDataType(dataType) && !isFromConfigContent { @@ -394,9 +385,9 @@ func (c *Config) getJson(file ...string) *gjson.Json { } if errorPrint() { if filePath != "" { - glog.Criticalf(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error()) + glog.Criticalf(`[gcfg] load config file "%s" failed: %s`, filePath, err.Error()) } else { - glog.Criticalf(`[gcfg] Load configuration failed: %s`, err.Error()) + glog.Criticalf(`[gcfg] load configuration failed: %s`, err.Error()) } } return nil diff --git a/os/gcfg/gcfg_api.go b/os/gcfg/gcfg_api.go index 75f80a4d0..504e447d2 100644 --- a/os/gcfg/gcfg_api.go +++ b/os/gcfg/gcfg_api.go @@ -16,7 +16,7 @@ import ( "github.com/gogf/gf/os/gtime" ) -// Set sets value with specified . +// Set sets value with specified `pattern`. // It supports hierarchical data access by char separator, which is '.' in default. // It is commonly used for updates certain configuration value in runtime. func (c *Config) Set(pattern string, value interface{}) error { @@ -26,14 +26,14 @@ func (c *Config) Set(pattern string, value interface{}) error { return nil } -// Get retrieves and returns value by specified . -// It returns all values of current Json object if is given empty or string ".". -// It returns nil if no value found by . +// Get retrieves and returns value by specified `pattern`. +// It returns all values of current Json object if `pattern` is given empty or string ".". +// It returns nil if no value found by `pattern`. // -// We can also access slice item by its index number in like: +// We can also access slice item by its index number in `pattern` like: // "list.10", "array.0.name", "array.0.1.id". // -// It returns a default value specified by if value for is not found. +// It returns a default value specified by `def` if value for `pattern` is not found. func (c *Config) Get(pattern string, def ...interface{}) interface{} { if j := c.getJson(); j != nil { return j.Get(pattern, def...) @@ -41,7 +41,7 @@ func (c *Config) Get(pattern string, def ...interface{}) interface{} { return nil } -// GetVar returns a gvar.Var with value by given . +// GetVar returns a gvar.Var with value by given `pattern`. func (c *Config) GetVar(pattern string, def ...interface{}) *gvar.Var { if j := c.getJson(); j != nil { return j.GetVar(pattern, def...) @@ -49,7 +49,7 @@ func (c *Config) GetVar(pattern string, def ...interface{}) *gvar.Var { return gvar.New(nil) } -// Contains checks whether the value by specified exist. +// Contains checks whether the value by specified `pattern` exist. func (c *Config) Contains(pattern string) bool { if j := c.getJson(); j != nil { return j.Contains(pattern) @@ -57,7 +57,7 @@ func (c *Config) Contains(pattern string) bool { return false } -// GetMap retrieves and returns the value by specified as map[string]interface{}. +// GetMap retrieves and returns the value by specified `pattern` as map[string]interface{}. func (c *Config) GetMap(pattern string, def ...interface{}) map[string]interface{} { if j := c.getJson(); j != nil { return j.GetMap(pattern, def...) @@ -65,7 +65,7 @@ func (c *Config) GetMap(pattern string, def ...interface{}) map[string]interface return nil } -// GetMapStrStr retrieves and returns the value by specified as map[string]string. +// GetMapStrStr retrieves and returns the value by specified `pattern` as map[string]string. func (c *Config) GetMapStrStr(pattern string, def ...interface{}) map[string]string { if j := c.getJson(); j != nil { return j.GetMapStrStr(pattern, def...) @@ -73,7 +73,7 @@ func (c *Config) GetMapStrStr(pattern string, def ...interface{}) map[string]str return nil } -// GetArray retrieves the value by specified , +// GetArray retrieves the value by specified `pattern`, // and converts it to a slice of []interface{}. func (c *Config) GetArray(pattern string, def ...interface{}) []interface{} { if j := c.getJson(); j != nil { @@ -82,7 +82,7 @@ func (c *Config) GetArray(pattern string, def ...interface{}) []interface{} { return nil } -// GetBytes retrieves the value by specified and converts it to []byte. +// GetBytes retrieves the value by specified `pattern` and converts it to []byte. func (c *Config) GetBytes(pattern string, def ...interface{}) []byte { if j := c.getJson(); j != nil { return j.GetBytes(pattern, def...) @@ -90,7 +90,7 @@ func (c *Config) GetBytes(pattern string, def ...interface{}) []byte { return nil } -// GetString retrieves the value by specified and converts it to string. +// GetString retrieves the value by specified `pattern` and converts it to string. func (c *Config) GetString(pattern string, def ...interface{}) string { if j := c.getJson(); j != nil { return j.GetString(pattern, def...) @@ -98,7 +98,7 @@ func (c *Config) GetString(pattern string, def ...interface{}) string { return "" } -// GetStrings retrieves the value by specified and converts it to []string. +// GetStrings retrieves the value by specified `pattern` and converts it to []string. func (c *Config) GetStrings(pattern string, def ...interface{}) []string { if j := c.getJson(); j != nil { return j.GetStrings(pattern, def...) @@ -115,7 +115,7 @@ func (c *Config) GetInterfaces(pattern string, def ...interface{}) []interface{} return nil } -// GetBool retrieves the value by specified , +// GetBool retrieves the value by specified `pattern`, // converts and returns it as bool. // It returns false when value is: "", 0, false, off, nil; // or returns true instead. @@ -126,7 +126,7 @@ func (c *Config) GetBool(pattern string, def ...interface{}) bool { return false } -// GetFloat32 retrieves the value by specified and converts it to float32. +// GetFloat32 retrieves the value by specified `pattern` and converts it to float32. func (c *Config) GetFloat32(pattern string, def ...interface{}) float32 { if j := c.getJson(); j != nil { return j.GetFloat32(pattern, def...) @@ -134,7 +134,7 @@ func (c *Config) GetFloat32(pattern string, def ...interface{}) float32 { return 0 } -// GetFloat64 retrieves the value by specified and converts it to float64. +// GetFloat64 retrieves the value by specified `pattern` and converts it to float64. func (c *Config) GetFloat64(pattern string, def ...interface{}) float64 { if j := c.getJson(); j != nil { return j.GetFloat64(pattern, def...) @@ -142,7 +142,7 @@ func (c *Config) GetFloat64(pattern string, def ...interface{}) float64 { return 0 } -// GetFloats retrieves the value by specified and converts it to []float64. +// GetFloats retrieves the value by specified `pattern` and converts it to []float64. func (c *Config) GetFloats(pattern string, def ...interface{}) []float64 { if j := c.getJson(); j != nil { return j.GetFloats(pattern, def...) @@ -150,7 +150,7 @@ func (c *Config) GetFloats(pattern string, def ...interface{}) []float64 { return nil } -// GetInt retrieves the value by specified and converts it to int. +// GetInt retrieves the value by specified `pattern` and converts it to int. func (c *Config) GetInt(pattern string, def ...interface{}) int { if j := c.getJson(); j != nil { return j.GetInt(pattern, def...) @@ -158,7 +158,7 @@ func (c *Config) GetInt(pattern string, def ...interface{}) int { return 0 } -// GetInt8 retrieves the value by specified and converts it to int8. +// GetInt8 retrieves the value by specified `pattern` and converts it to int8. func (c *Config) GetInt8(pattern string, def ...interface{}) int8 { if j := c.getJson(); j != nil { return j.GetInt8(pattern, def...) @@ -166,7 +166,7 @@ func (c *Config) GetInt8(pattern string, def ...interface{}) int8 { return 0 } -// GetInt16 retrieves the value by specified and converts it to int16. +// GetInt16 retrieves the value by specified `pattern` and converts it to int16. func (c *Config) GetInt16(pattern string, def ...interface{}) int16 { if j := c.getJson(); j != nil { return j.GetInt16(pattern, def...) @@ -174,7 +174,7 @@ func (c *Config) GetInt16(pattern string, def ...interface{}) int16 { return 0 } -// GetInt32 retrieves the value by specified and converts it to int32. +// GetInt32 retrieves the value by specified `pattern` and converts it to int32. func (c *Config) GetInt32(pattern string, def ...interface{}) int32 { if j := c.getJson(); j != nil { return j.GetInt32(pattern, def...) @@ -182,7 +182,7 @@ func (c *Config) GetInt32(pattern string, def ...interface{}) int32 { return 0 } -// GetInt64 retrieves the value by specified and converts it to int64. +// GetInt64 retrieves the value by specified `pattern` and converts it to int64. func (c *Config) GetInt64(pattern string, def ...interface{}) int64 { if j := c.getJson(); j != nil { return j.GetInt64(pattern, def...) @@ -190,7 +190,7 @@ func (c *Config) GetInt64(pattern string, def ...interface{}) int64 { return 0 } -// GetInts retrieves the value by specified and converts it to []int. +// GetInts retrieves the value by specified `pattern` and converts it to []int. func (c *Config) GetInts(pattern string, def ...interface{}) []int { if j := c.getJson(); j != nil { return j.GetInts(pattern, def...) @@ -198,7 +198,7 @@ func (c *Config) GetInts(pattern string, def ...interface{}) []int { return nil } -// GetUint retrieves the value by specified and converts it to uint. +// GetUint retrieves the value by specified `pattern` and converts it to uint. func (c *Config) GetUint(pattern string, def ...interface{}) uint { if j := c.getJson(); j != nil { return j.GetUint(pattern, def...) @@ -206,7 +206,7 @@ func (c *Config) GetUint(pattern string, def ...interface{}) uint { return 0 } -// GetUint8 retrieves the value by specified and converts it to uint8. +// GetUint8 retrieves the value by specified `pattern` and converts it to uint8. func (c *Config) GetUint8(pattern string, def ...interface{}) uint8 { if j := c.getJson(); j != nil { return j.GetUint8(pattern, def...) @@ -214,7 +214,7 @@ func (c *Config) GetUint8(pattern string, def ...interface{}) uint8 { return 0 } -// GetUint16 retrieves the value by specified and converts it to uint16. +// GetUint16 retrieves the value by specified `pattern` and converts it to uint16. func (c *Config) GetUint16(pattern string, def ...interface{}) uint16 { if j := c.getJson(); j != nil { return j.GetUint16(pattern, def...) @@ -222,7 +222,7 @@ func (c *Config) GetUint16(pattern string, def ...interface{}) uint16 { return 0 } -// GetUint32 retrieves the value by specified and converts it to uint32. +// GetUint32 retrieves the value by specified `pattern` and converts it to uint32. func (c *Config) GetUint32(pattern string, def ...interface{}) uint32 { if j := c.getJson(); j != nil { return j.GetUint32(pattern, def...) @@ -230,7 +230,7 @@ func (c *Config) GetUint32(pattern string, def ...interface{}) uint32 { return 0 } -// GetUint64 retrieves the value by specified and converts it to uint64. +// GetUint64 retrieves the value by specified `pattern` and converts it to uint64. func (c *Config) GetUint64(pattern string, def ...interface{}) uint64 { if j := c.getJson(); j != nil { return j.GetUint64(pattern, def...) @@ -238,7 +238,7 @@ func (c *Config) GetUint64(pattern string, def ...interface{}) uint64 { return 0 } -// GetTime retrieves the value by specified and converts it to time.Time. +// GetTime retrieves the value by specified `pattern` and converts it to time.Time. func (c *Config) GetTime(pattern string, format ...string) time.Time { if j := c.getJson(); j != nil { return j.GetTime(pattern, format...) @@ -246,7 +246,7 @@ func (c *Config) GetTime(pattern string, format ...string) time.Time { return time.Time{} } -// GetDuration retrieves the value by specified and converts it to time.Duration. +// GetDuration retrieves the value by specified `pattern` and converts it to time.Duration. func (c *Config) GetDuration(pattern string, def ...interface{}) time.Duration { if j := c.getJson(); j != nil { return j.GetDuration(pattern, def...) @@ -254,7 +254,7 @@ func (c *Config) GetDuration(pattern string, def ...interface{}) time.Duration { return 0 } -// GetGTime retrieves the value by specified and converts it to *gtime.Time. +// GetGTime retrieves the value by specified `pattern` and converts it to *gtime.Time. func (c *Config) GetGTime(pattern string, format ...string) *gtime.Time { if j := c.getJson(); j != nil { return j.GetGTime(pattern, format...) @@ -262,7 +262,7 @@ func (c *Config) GetGTime(pattern string, format ...string) *gtime.Time { return nil } -// GetJson gets the value by specified , +// GetJson gets the value by specified `pattern`, // and converts it to a un-concurrent-safe Json object. func (c *Config) GetJson(pattern string, def ...interface{}) *gjson.Json { if j := c.getJson(); j != nil { @@ -271,7 +271,7 @@ func (c *Config) GetJson(pattern string, def ...interface{}) *gjson.Json { return nil } -// GetJsons gets the value by specified , +// GetJsons gets the value by specified `pattern`, // and converts it to a slice of un-concurrent-safe Json object. func (c *Config) GetJsons(pattern string, def ...interface{}) []*gjson.Json { if j := c.getJson(); j != nil { @@ -280,7 +280,7 @@ func (c *Config) GetJsons(pattern string, def ...interface{}) []*gjson.Json { return nil } -// GetJsonMap gets the value by specified , +// GetJsonMap gets the value by specified `pattern`, // and converts it to a map of un-concurrent-safe Json object. func (c *Config) GetJsonMap(pattern string, def ...interface{}) map[string]*gjson.Json { if j := c.getJson(); j != nil { @@ -289,8 +289,8 @@ func (c *Config) GetJsonMap(pattern string, def ...interface{}) map[string]*gjso return nil } -// GetStruct retrieves the value by specified and converts it to specified object -// . The should be the pointer to an object. +// GetStruct retrieves the value by specified `pattern` and converts it to specified object +// `pointer`. The `pointer` should be the pointer to an object. func (c *Config) GetStruct(pattern string, pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { return j.GetStruct(pattern, pointer, mapping...) @@ -324,7 +324,7 @@ func (c *Config) GetStructsDeep(pattern string, pointer interface{}, mapping ... return errors.New("configuration not found") } -// GetMapToMap retrieves the value by specified and converts it to specified map variable. +// GetMapToMap retrieves the value by specified `pattern` and converts it to specified map variable. // See gconv.MapToMap. func (c *Config) GetMapToMap(pattern string, pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { @@ -333,7 +333,7 @@ func (c *Config) GetMapToMap(pattern string, pointer interface{}, mapping ...map return errors.New("configuration not found") } -// GetMapToMapDeep retrieves the value by specified and converts it to specified map +// GetMapToMapDeep retrieves the value by specified `pattern` and converts it to specified map // variable recursively. // See gconv.MapToMapDeep. func (c *Config) GetMapToMapDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { @@ -343,7 +343,7 @@ func (c *Config) GetMapToMapDeep(pattern string, pointer interface{}, mapping .. return errors.New("configuration not found") } -// GetMapToMaps retrieves the value by specified and converts it to specified map slice +// GetMapToMaps retrieves the value by specified `pattern` and converts it to specified map slice // variable. // See gconv.MapToMaps. func (c *Config) GetMapToMaps(pattern string, pointer interface{}, mapping ...map[string]string) error { @@ -353,7 +353,7 @@ func (c *Config) GetMapToMaps(pattern string, pointer interface{}, mapping ...ma return errors.New("configuration not found") } -// GetMapToMapsDeep retrieves the value by specified and converts it to specified map slice +// GetMapToMapsDeep retrieves the value by specified `pattern` and converts it to specified map slice // variable recursively. // See gconv.MapToMapsDeep. func (c *Config) GetMapToMapsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { @@ -382,7 +382,7 @@ func (c *Config) ToArray() []interface{} { } // ToStruct converts current Json object to specified object. -// The should be a pointer type of *struct. +// The `pointer` should be a pointer type of *struct. func (c *Config) ToStruct(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { return j.ToStruct(pointer, mapping...) @@ -391,7 +391,7 @@ func (c *Config) ToStruct(pointer interface{}, mapping ...map[string]string) err } // ToStructDeep converts current Json object to specified object recursively. -// The should be a pointer type of *struct. +// The `pointer` should be a pointer type of *struct. func (c *Config) ToStructDeep(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { return j.ToStructDeep(pointer, mapping...) @@ -400,7 +400,7 @@ func (c *Config) ToStructDeep(pointer interface{}, mapping ...map[string]string) } // ToStructs converts current Json object to specified object slice. -// The should be a pointer type of []struct/*struct. +// The `pointer` should be a pointer type of []struct/*struct. func (c *Config) ToStructs(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { return j.ToStructs(pointer, mapping...) @@ -409,7 +409,7 @@ func (c *Config) ToStructs(pointer interface{}, mapping ...map[string]string) er } // ToStructsDeep converts current Json object to specified object slice recursively. -// The should be a pointer type of []struct/*struct. +// The `pointer` should be a pointer type of []struct/*struct. func (c *Config) ToStructsDeep(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { return j.ToStructsDeep(pointer, mapping...) @@ -418,7 +418,7 @@ func (c *Config) ToStructsDeep(pointer interface{}, mapping ...map[string]string } // ToMapToMap converts current Json object to specified map variable. -// The parameter of should be type of *map. +// The parameter of `pointer` should be type of *map. func (c *Config) ToMapToMap(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { return j.ToMapToMap(pointer, mapping...) @@ -427,7 +427,7 @@ func (c *Config) ToMapToMap(pointer interface{}, mapping ...map[string]string) e } // ToMapToMapDeep converts current Json object to specified map variable recursively. -// The parameter of should be type of *map. +// The parameter of `pointer` should be type of *map. func (c *Config) ToMapToMapDeep(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { return j.ToMapToMapDeep(pointer, mapping...) @@ -436,7 +436,7 @@ func (c *Config) ToMapToMapDeep(pointer interface{}, mapping ...map[string]strin } // ToMapToMaps converts current Json object to specified map variable slice. -// The parameter of should be type of []map/*map. +// The parameter of `pointer` should be type of []map/*map. func (c *Config) ToMapToMaps(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { return j.ToMapToMaps(pointer, mapping...) @@ -445,7 +445,7 @@ func (c *Config) ToMapToMaps(pointer interface{}, mapping ...map[string]string) } // ToMapToMapsDeep converts current Json object to specified map variable slice recursively. -// The parameter of should be type of []map/*map. +// The parameter of `pointer` should be type of []map/*map. func (c *Config) ToMapToMapsDeep(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { return j.ToMapToMapsDeep(pointer, mapping...) diff --git a/os/gcfg/gcfg_config.go b/os/gcfg/gcfg_config.go index dbf8aef64..68adf275a 100644 --- a/os/gcfg/gcfg_config.go +++ b/os/gcfg/gcfg_config.go @@ -16,14 +16,14 @@ var ( configs = gmap.NewStrStrMap(true) ) -// SetContent sets customized configuration content for specified . -// The is unnecessary param, default is DefaultConfigFile. +// SetContent sets customized configuration content for specified `file`. +// The `file` is unnecessary param, default is DefaultConfigFile. func SetContent(content string, file ...string) { name := DefaultConfigFile if len(file) > 0 { name = file[0] } - // Clear file cache for instances which cached . + // Clear file cache for instances which cached `name`. instances.LockFunc(func(m map[string]interface{}) { if configs.Contains(name) { for _, v := range m { @@ -34,8 +34,8 @@ func SetContent(content string, file ...string) { }) } -// GetContent returns customized configuration content for specified . -// The is unnecessary param, default is DefaultConfigFile. +// GetContent returns customized configuration content for specified `file`. +// The `file` is unnecessary param, default is DefaultConfigFile. func GetContent(file ...string) string { name := DefaultConfigFile if len(file) > 0 { @@ -44,14 +44,14 @@ func GetContent(file ...string) string { return configs.Get(name) } -// RemoveContent removes the global configuration with specified . -// If is not passed, it removes configuration of the default group name. +// RemoveContent removes the global configuration with specified `file`. +// If `name` is not passed, it removes configuration of the default group name. func RemoveContent(file ...string) { name := DefaultConfigFile if len(file) > 0 { name = file[0] } - // Clear file cache for instances which cached . + // Clear file cache for instances which cached `name`. instances.LockFunc(func(m map[string]interface{}) { if configs.Contains(name) { for _, v := range m { diff --git a/os/gcfg/gcfg_instance.go b/os/gcfg/gcfg_instance.go index 9eeb07905..4ef396801 100644 --- a/os/gcfg/gcfg_instance.go +++ b/os/gcfg/gcfg_instance.go @@ -22,7 +22,7 @@ var ( ) // Instance returns an instance of Config with default settings. -// The parameter is the name for the instance. But very note that, if the file "name.toml" +// The parameter `name` is the name for the instance. But very note that, if the file "name.toml" // exists in the configuration directory, it then sets it as the default configuration file. The // toml file type is the default configuration file type. func Instance(name ...string) *Config { diff --git a/os/gcfg/gcfg_z_unit_basic_test.go b/os/gcfg/gcfg_z_unit_basic_test.go index 133f90adc..51fd92638 100644 --- a/os/gcfg/gcfg_z_unit_basic_test.go +++ b/os/gcfg/gcfg_z_unit_basic_test.go @@ -81,7 +81,8 @@ array = [1,2,3] "disk": "127.0.0.1:6379,0", "cache": "127.0.0.1:6379,1", }) - t.AssertEQ(c.FilePath(), gfile.Pwd()+gfile.Separator+path) + filepath, _ := c.GetFilePath() + t.AssertEQ(filepath, gfile.Pwd()+gfile.Separator+path) }) } @@ -223,8 +224,8 @@ func Test_SetFileName(t *testing.T) { "disk": "127.0.0.1:6379,0", "cache": "127.0.0.1:6379,1", }) - t.AssertEQ(c.FilePath(), gfile.Pwd()+gfile.Separator+path) - + filepath, _ := c.GetFilePath() + t.AssertEQ(filepath, gfile.Pwd()+gfile.Separator+path) }) } @@ -281,9 +282,9 @@ func TestCfg_AddPath(t *testing.T) { func TestCfg_FilePath(t *testing.T) { gtest.C(t, func(t *gtest.T) { c := gcfg.New("config.yml") - path := c.FilePath("tmp") + path, _ := c.GetFilePath("tmp") t.Assert(path, "") - path = c.FilePath("tmp") + path, _ = c.GetFilePath("tmp") t.Assert(path, "") }) } diff --git a/os/gcfg/gcfg_z_unit_instance_test.go b/os/gcfg/gcfg_z_unit_instance_test.go index 2cc5e3ab1..879a5461d 100644 --- a/os/gcfg/gcfg_z_unit_instance_test.go +++ b/os/gcfg/gcfg_z_unit_instance_test.go @@ -77,7 +77,8 @@ v4 = "1.234" "disk": "127.0.0.1:6379,0", "cache": "127.0.0.1:6379,1", }) - t.AssertEQ(c.FilePath(), gfile.Pwd()+gfile.Separator+path) + filepath, _ := c.GetFilePath() + t.AssertEQ(filepath, gfile.Pwd()+gfile.Separator+path) }) } From 6a24b595f0de17d2c217fc3af4ac82e46fa93b1f Mon Sep 17 00:00:00 2001 From: jianchenma Date: Thu, 18 Mar 2021 10:39:23 +0800 Subject: [PATCH 189/492] upgrade otel from 0.16.0 to 0.18.0 --- database/gdb/gdb_core_tracing.go | 24 ++++----- database/gredis/gredis_conn_tracing.go | 14 +++--- go.mod | 4 +- net/ghttp/ghttp_middleware_tracing.go | 50 +++++++++---------- net/ghttp/internal/client/client_tracing.go | 29 +++++------ .../internal/client/client_tracing_tracer.go | 45 ++++++++--------- net/gtrace/gtrace.go | 32 ++++++++---- net/gtrace/gtrace_baggage.go | 14 +++--- net/gtrace/gtrace_carrier.go | 42 +++++++++------- os/gfile/gfile.go | 8 ++- 10 files changed, 139 insertions(+), 123 deletions(-) diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index 678ad8558..3c746ec2c 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -14,8 +14,8 @@ import ( "github.com/gogf/gf/net/gtrace" "github.com/gogf/gf/os/gcmd" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" ) @@ -59,33 +59,33 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { if sql.Error != nil { span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, sql.Error)) } - labels := make([]label.KeyValue, 0) + labels := make([]attribute.KeyValue, 0) labels = append(labels, gtrace.CommonLabels()...) labels = append(labels, - label.String(tracingAttrDbType, c.db.GetConfig().Type), + attribute.String(tracingAttrDbType, c.db.GetConfig().Type), ) if c.db.GetConfig().Host != "" { - labels = append(labels, label.String(tracingAttrDbHost, c.db.GetConfig().Host)) + labels = append(labels, attribute.String(tracingAttrDbHost, c.db.GetConfig().Host)) } if c.db.GetConfig().Port != "" { - labels = append(labels, label.String(tracingAttrDbPort, c.db.GetConfig().Port)) + labels = append(labels, attribute.String(tracingAttrDbPort, c.db.GetConfig().Port)) } if c.db.GetConfig().Name != "" { - labels = append(labels, label.String(tracingAttrDbName, c.db.GetConfig().Name)) + labels = append(labels, attribute.String(tracingAttrDbName, c.db.GetConfig().Name)) } if c.db.GetConfig().User != "" { - labels = append(labels, label.String(tracingAttrDbUser, c.db.GetConfig().User)) + labels = append(labels, attribute.String(tracingAttrDbUser, c.db.GetConfig().User)) } if filteredLinkInfo := c.db.FilteredLinkInfo(); filteredLinkInfo != "" { - labels = append(labels, label.String(tracingAttrDbLink, c.db.FilteredLinkInfo())) + labels = append(labels, attribute.String(tracingAttrDbLink, c.db.FilteredLinkInfo())) } if group := c.db.GetGroup(); group != "" { - labels = append(labels, label.String(tracingAttrDbGroup, group)) + labels = append(labels, attribute.String(tracingAttrDbGroup, group)) } span.SetAttributes(labels...) span.AddEvent(tracingEventDbExecution, trace.WithAttributes( - label.String(tracingEventDbExecutionSql, sql.Format), - label.String(tracingEventDbExecutionCost, fmt.Sprintf(`%d ms`, sql.End-sql.Start)), - label.String(tracingEventDbExecutionType, sql.Type), + attribute.String(tracingEventDbExecutionSql, sql.Format), + attribute.String(tracingEventDbExecutionCost, fmt.Sprintf(`%d ms`, sql.End-sql.Start)), + attribute.String(tracingEventDbExecutionType, sql.Type), )) } diff --git a/database/gredis/gredis_conn_tracing.go b/database/gredis/gredis_conn_tracing.go index 2e4c794fa..9ee411323 100644 --- a/database/gredis/gredis_conn_tracing.go +++ b/database/gredis/gredis_conn_tracing.go @@ -14,8 +14,8 @@ import ( "github.com/gogf/gf/net/gtrace" "github.com/gogf/gf/os/gcmd" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" ) @@ -68,14 +68,14 @@ func (c *Conn) addTracingItem(item *tracingItem) { } span.SetAttributes(gtrace.CommonLabels()...) span.SetAttributes( - label.String(tracingAttrRedisHost, c.redis.config.Host), - label.Int(tracingAttrRedisPort, c.redis.config.Port), - label.Int(tracingAttrRedisDb, c.redis.config.Db), + attribute.String(tracingAttrRedisHost, c.redis.config.Host), + attribute.Int(tracingAttrRedisPort, c.redis.config.Port), + attribute.Int(tracingAttrRedisDb, c.redis.config.Db), ) jsonBytes, _ := json.Marshal(item.arguments) span.AddEvent(tracingEventRedisExecution, trace.WithAttributes( - label.String(tracingEventRedisExecutionCommand, item.commandName), - label.String(tracingEventRedisExecutionCost, fmt.Sprintf(`%d ms`, item.costMilli)), - label.String(tracingEventRedisExecutionArguments, string(jsonBytes)), + attribute.String(tracingEventRedisExecutionCommand, item.commandName), + attribute.String(tracingEventRedisExecutionCost, fmt.Sprintf(`%d ms`, item.costMilli)), + attribute.String(tracingEventRedisExecutionArguments, string(jsonBytes)), )) } diff --git a/go.mod b/go.mod index 6268b8843..0b8f1b44b 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,9 @@ require ( github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf github.com/mattn/go-runewidth v0.0.10 // indirect github.com/olekukonko/tablewriter v0.0.1 - go.opentelemetry.io/otel v0.16.0 + go.opentelemetry.io/otel v0.18.0 + go.opentelemetry.io/otel/oteltest v0.18.0 + go.opentelemetry.io/otel/trace v0.18.0 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 golang.org/x/text v0.3.4 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index 22b4cdc2b..052b4ee7e 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -13,16 +13,17 @@ import ( "github.com/gogf/gf/net/ghttp/internal/client" "github.com/gogf/gf/net/ghttp/internal/httputil" "github.com/gogf/gf/net/gtrace" + "github.com/gogf/gf/text/gstr" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "io/ioutil" "net/http" ) const ( - tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. tracingInstrumentName = "github.com/gogf/gf/net/ghttp.Server" tracingEventHttpRequest = "http.request" tracingEventHttpRequestHeaders = "http.request.headers" @@ -41,7 +42,7 @@ func MiddlewareClientTracing(c *Client, r *http.Request) (*ClientResponse, error // MiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry. func MiddlewareServerTracing(r *Request) { tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) - ctx := otel.GetTextMapPropagator().Extract(r.Context(), r.Header) + ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header)) ctx, span := tr.Start(ctx, r.URL.String(), trace.WithSpanKind(trace.SpanKindServer)) defer span.End() @@ -51,21 +52,17 @@ func MiddlewareServerTracing(r *Request) { r.SetCtx(ctx) // Request content logging. - var reqBodyContent string - if r.ContentLength <= tracingMaxContentLogSize { - reqBodyContentBytes, _ := ioutil.ReadAll(r.Body) - r.Body = utils.NewReadCloser(reqBodyContentBytes, false) - reqBodyContent = string(reqBodyContentBytes) - } else { - reqBodyContent = fmt.Sprintf( - "[Request Body Too Large For Tracing, Max: %d bytes]", - tracingMaxContentLogSize, - ) - } + reqBodyContentBytes, _ := ioutil.ReadAll(r.Body) + r.Body = utils.NewReadCloser(reqBodyContentBytes, false) + span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( - label.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)), - label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx)), - label.String(tracingEventHttpRequestBody, reqBodyContent), + attribute.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)), + attribute.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx)), + attribute.String(tracingEventHttpRequestBody, gstr.StrLimit( + string(reqBodyContentBytes), + gtrace.MaxContentLogSize(), + "...", + )), )) // Continue executing. @@ -77,17 +74,16 @@ func MiddlewareServerTracing(r *Request) { } // Response content logging. var resBodyContent string - if r.Response.BufferLength() <= tracingMaxContentLogSize { - resBodyContent = r.Response.BufferString() - } else { - resBodyContent = fmt.Sprintf( - "[Response Body Too Large For Tracing, Max: %d bytes]", - tracingMaxContentLogSize, - ) - } + resBodyContent = r.Response.BufferString() + resBodyContent = gstr.StrLimit( + r.Response.BufferString(), + gtrace.MaxContentLogSize(), + "...", + ) + span.AddEvent(tracingEventHttpResponse, trace.WithAttributes( - label.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(r.Response.Header())), - label.String(tracingEventHttpResponseBody, resBodyContent), + attribute.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(r.Response.Header())), + attribute.String(tracingEventHttpResponseBody, resBodyContent), )) return } diff --git a/net/ghttp/internal/client/client_tracing.go b/net/ghttp/internal/client/client_tracing.go index 244ea05b9..567ea6c14 100644 --- a/net/ghttp/internal/client/client_tracing.go +++ b/net/ghttp/internal/client/client_tracing.go @@ -12,9 +12,11 @@ import ( "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/net/ghttp/internal/httputil" "github.com/gogf/gf/net/gtrace" + "github.com/gogf/gf/text/gstr" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "io/ioutil" "net/http" @@ -22,7 +24,6 @@ import ( ) const ( - tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body. tracingInstrumentName = "github.com/gogf/gf/net/ghttp.Client" tracingAttrHttpAddressRemote = "http.address.remote" tracingAttrHttpAddressLocal = "http.address.local" @@ -48,7 +49,7 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro span.SetAttributes(gtrace.CommonLabels()...) // Inject tracing content into http header. - otel.GetTextMapPropagator().Inject(ctx, r.Header) + otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(r.Header)) // Continue client handler executing. response, err = c.Next( @@ -64,21 +65,17 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro if response == nil || response.Response == nil { return } - var resBodyContent string - if response.ContentLength <= tracingMaxContentLogSize { - reqBodyContentBytes, _ := ioutil.ReadAll(response.Body) - resBodyContent = string(reqBodyContentBytes) - response.Body = utils.NewReadCloser(reqBodyContentBytes, false) - } else { - resBodyContent = fmt.Sprintf( - "[Response Body Too Large For Tracing, Max: %d bytes]", - tracingMaxContentLogSize, - ) - } + + reqBodyContentBytes, _ := ioutil.ReadAll(response.Body) + response.Body = utils.NewReadCloser(reqBodyContentBytes, false) span.AddEvent(tracingEventHttpResponse, trace.WithAttributes( - label.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(response.Header)), - label.String(tracingEventHttpResponseBody, resBodyContent), + attribute.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(response.Header)), + attribute.String(tracingEventHttpResponseBody, gstr.StrLimit( + string(reqBodyContentBytes), + gtrace.MaxContentLogSize(), + "...", + )), )) return } diff --git a/net/ghttp/internal/client/client_tracing_tracer.go b/net/ghttp/internal/client/client_tracing_tracer.go index 694db4db2..ef4733f45 100644 --- a/net/ghttp/internal/client/client_tracing_tracer.go +++ b/net/ghttp/internal/client/client_tracing_tracer.go @@ -12,8 +12,9 @@ import ( "fmt" "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/net/gtrace" + "github.com/gogf/gf/text/gstr" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/label" "go.opentelemetry.io/otel/trace" "io/ioutil" "net/http" @@ -39,11 +40,11 @@ func newClientTrace(ctx context.Context, span trace.Span, request *http.Request) request: request, headers: make(map[string]interface{}), } - if ct.request.ContentLength <= tracingMaxContentLogSize { - reqBodyContent, _ := ioutil.ReadAll(ct.request.Body) - ct.requestBody = reqBodyContent - ct.request.Body = utils.NewReadCloser(reqBodyContent, false) - } + + reqBodyContent, _ := ioutil.ReadAll(ct.request.Body) + ct.requestBody = reqBodyContent + ct.request.Body = utils.NewReadCloser(reqBodyContent, false) + return &httptrace.ClientTrace{ GetConn: ct.getConn, GotConn: ct.gotConn, @@ -70,8 +71,8 @@ func (ct *clientTracer) getConn(host string) { func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) { ct.span.SetAttributes( - label.String(tracingAttrHttpAddressRemote, info.Conn.RemoteAddr().String()), - label.String(tracingAttrHttpAddressLocal, info.Conn.LocalAddr().String()), + attribute.String(tracingAttrHttpAddressRemote, info.Conn.RemoteAddr().String()), + attribute.String(tracingAttrHttpAddressLocal, info.Conn.LocalAddr().String()), ) } @@ -83,7 +84,7 @@ func (ct *clientTracer) putIdleConn(err error) { func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) { ct.span.SetAttributes( - label.String(tracingAttrHttpDnsStart, info.Host), + attribute.String(tracingAttrHttpDnsStart, info.Host), ) } @@ -99,13 +100,13 @@ func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) { ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) } ct.span.SetAttributes( - label.String(tracingAttrHttpDnsDone, buffer.String()), + attribute.String(tracingAttrHttpDnsDone, buffer.String()), ) } func (ct *clientTracer) connectStart(network, addr string) { ct.span.SetAttributes( - label.String(tracingAttrHttpConnectStart, network+"@"+addr), + attribute.String(tracingAttrHttpConnectStart, network+"@"+addr), ) } @@ -114,7 +115,7 @@ func (ct *clientTracer) connectDone(network, addr string, err error) { ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) } ct.span.SetAttributes( - label.String(tracingAttrHttpConnectDone, network+"@"+addr), + attribute.String(tracingAttrHttpConnectDone, network+"@"+addr), ) } @@ -144,19 +145,15 @@ func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { if info.Err != nil { ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) } - var bodyContent string - if ct.request.ContentLength <= tracingMaxContentLogSize { - bodyContent = string(ct.requestBody) - } else { - bodyContent = fmt.Sprintf( - "[Request Body Too Large For Logging, Max: %d bytes]", - tracingMaxContentLogSize, - ) - } + ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( - label.Any(tracingEventHttpRequestHeaders, ct.headers), - label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context)), - label.String(tracingEventHttpRequestBody, bodyContent), + attribute.Any(tracingEventHttpRequestHeaders, ct.headers), + attribute.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context)), + attribute.String(tracingEventHttpRequestBody, gstr.StrLimit( + string(ct.requestBody), + gtrace.MaxContentLogSize(), + "...", + )), )) } diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index a166ddcb0..98490cd7f 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -9,11 +9,13 @@ package gtrace import ( "context" + "fmt" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/net/gipv4" + "github.com/gogf/gf/os/gcmd" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/label" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" "os" @@ -23,12 +25,14 @@ import ( const ( tracingCommonKeyIpIntranet = `ip.intranet` tracingCommonKeyIpHostname = `hostname` + cmdEnvKey = "gf.gtrace" // Configuration key for command argument or environment. ) var ( - intranetIps, _ = gipv4.GetIntranetIpArray() - intranetIpStr = strings.Join(intranetIps, ",") - hostname, _ = os.Hostname() + intranetIps, _ = gipv4.GetIntranetIpArray() + intranetIpStr = strings.Join(intranetIps, ",") + hostname, _ = os.Hostname() + tracingMaxContentLogSize = 256 * 1024 // Max log size for request and response body, especially for HTTP/RPC request. // defaultTextMapPropagator is the default propagator for context propagation between peers. defaultTextMapPropagator = propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, @@ -37,15 +41,23 @@ var ( ) func init() { + if maxContentLogSize := gcmd.GetOptWithEnv(fmt.Sprintf("%s.maxlogsize", cmdEnvKey)).Int(); maxContentLogSize > 0 { + tracingMaxContentLogSize = maxContentLogSize + } CheckSetDefaultTextMapPropagator() } +// MaxContentLogSize returns the max log size for request and response body, especially for HTTP/RPC request. +func MaxContentLogSize() int { + return tracingMaxContentLogSize +} + // CommonLabels returns common used attribute labels: // ip.intranet, hostname. -func CommonLabels() []label.KeyValue { - return []label.KeyValue{ - label.String(tracingCommonKeyIpHostname, hostname), - label.String(tracingCommonKeyIpIntranet, intranetIpStr), +func CommonLabels() []attribute.KeyValue { + return []attribute.KeyValue{ + attribute.String(tracingCommonKeyIpHostname, hostname), + attribute.String(tracingCommonKeyIpIntranet, intranetIpStr), } } @@ -94,13 +106,13 @@ func GetSpanId(ctx context.Context) string { } // SetBaggageValue is a convenient function for adding one key-value pair to baggage. -// Note that it uses label.Any to set the key-value pair. +// Note that it uses attribute.Any to set the key-value pair. func SetBaggageValue(ctx context.Context, key string, value interface{}) context.Context { return NewBaggage(ctx).SetValue(key, value) } // SetBaggageMap is a convenient function for adding map key-value pairs to baggage. -// Note that it uses label.Any to set the key-value pair. +// Note that it uses attribute.Any to set the key-value pair. func SetBaggageMap(ctx context.Context, data map[string]interface{}) context.Context { return NewBaggage(ctx).SetMap(data) } diff --git a/net/gtrace/gtrace_baggage.go b/net/gtrace/gtrace_baggage.go index f06aaa9c7..28a175c20 100644 --- a/net/gtrace/gtrace_baggage.go +++ b/net/gtrace/gtrace_baggage.go @@ -10,8 +10,8 @@ import ( "context" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/baggage" - "go.opentelemetry.io/otel/label" ) // Baggage holds the data through all tracing spans. @@ -35,18 +35,18 @@ func (b *Baggage) Ctx() context.Context { } // SetValue is a convenient function for adding one key-value pair to baggage. -// Note that it uses label.Any to set the key-value pair. +// Note that it uses attribute.Any to set the key-value pair. func (b *Baggage) SetValue(key string, value interface{}) context.Context { - b.ctx = baggage.ContextWithValues(b.ctx, label.Any(key, value)) + b.ctx = baggage.ContextWithValues(b.ctx, attribute.Any(key, value)) return b.ctx } // SetMap is a convenient function for adding map key-value pairs to baggage. -// Note that it uses label.Any to set the key-value pair. +// Note that it uses attribute.Any to set the key-value pair. func (b *Baggage) SetMap(data map[string]interface{}) context.Context { - pairs := make([]label.KeyValue, 0) + pairs := make([]attribute.KeyValue, 0) for k, v := range data { - pairs = append(pairs, label.Any(k, v)) + pairs = append(pairs, attribute.Any(k, v)) } b.ctx = baggage.ContextWithValues(b.ctx, pairs...) return b.ctx @@ -70,6 +70,6 @@ func (b *Baggage) GetMap() *gmap.StrAnyMap { // GetVar retrieves value and returns a *gvar.Var for specified key from baggage. func (b *Baggage) GetVar(key string) *gvar.Var { - value := baggage.Value(b.ctx, label.Key(key)) + value := baggage.Value(b.ctx, attribute.Key(key)) return gvar.New(value.AsInterface()) } diff --git a/net/gtrace/gtrace_carrier.go b/net/gtrace/gtrace_carrier.go index 6cc55f857..c0cd41979 100644 --- a/net/gtrace/gtrace_carrier.go +++ b/net/gtrace/gtrace_carrier.go @@ -11,41 +11,49 @@ import ( "github.com/gogf/gf/util/gconv" ) -type Carrier struct { - data map[string]interface{} -} +// Carrier is the storage medium used by a TextMapPropagator. +type Carrier map[string]interface{} -func NewCarrier(data ...map[string]interface{}) *Carrier { - carrier := &Carrier{} +func NewCarrier(data ...map[string]interface{}) Carrier { if len(data) > 0 && data[0] != nil { - carrier.data = data[0] + return data[0] } else { - carrier.data = make(map[string]interface{}) + return make(map[string]interface{}) } - return carrier } -func (c *Carrier) Get(k string) string { - return gconv.String(c.data[k]) +// Get returns the value associated with the passed key. +func (c Carrier) Get(k string) string { + return gconv.String(c[k]) } -func (c *Carrier) Set(k, v string) { - c.data[k] = v +// Set stores the key-value pair. +func (c Carrier) Set(k, v string) { + c[k] = v } -func (c *Carrier) MustMarshal() []byte { - b, err := json.Marshal(c.data) +// Keys lists the keys stored in this carrier. +func (c Carrier) Keys() []string { + keys := make([]string, 0, len(c)) + for k := range c { + keys = append(keys, k) + } + return keys +} + +func (c Carrier) MustMarshal() []byte { + b, err := json.Marshal(c) if err != nil { panic(err) } return b } -func (c *Carrier) String() string { +func (c Carrier) String() string { return string(c.MustMarshal()) } -func (c *Carrier) UnmarshalJSON(b []byte) error { +func (c Carrier) UnmarshalJSON(b []byte) error { carrier := NewCarrier(nil) - return json.Unmarshal(b, carrier.data) + return json.Unmarshal(b, carrier) } diff --git a/os/gfile/gfile.go b/os/gfile/gfile.go index b21d20a43..84eede389 100644 --- a/os/gfile/gfile.go +++ b/os/gfile/gfile.go @@ -343,10 +343,14 @@ func Name(path string) string { // Dir returns all but the last element of path, typically the path's directory. // After dropping the final element, Dir calls Clean on the path and trailing // slashes are removed. -// If the path is empty, Dir returns ".". -// If the path consists entirely of separators, Dir returns a single separator. +// If the `path` is empty, Dir returns ".". +// If the `path` is ".", Dir treats the path as current working directory. +// If the `path` consists entirely of separators, Dir returns a single separator. // The returned path does not end in a separator unless it is the root directory. func Dir(path string) string { + if path == "." { + return filepath.Dir(RealPath(path)) + } return filepath.Dir(path) } From 9eab5daf19152e035d7aee05db0d445837dbbcba Mon Sep 17 00:00:00 2001 From: jianchenma Date: Thu, 18 Mar 2021 11:19:36 +0800 Subject: [PATCH 190/492] update required minimum go version from 1.11 to 1.14 --- .travis.yml | 4 +--- go.mod | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 96d41718a..d11a3c960 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,9 @@ arch: arm64-graviton2 language: go go: - - "1.11.x" - - "1.12.x" - - "1.13.x" - "1.14.x" - "1.15.x" + - "1.16.x" branches: only: diff --git a/go.mod b/go.mod index 0b8f1b44b..bb4169602 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gogf/gf -go 1.11 +go 1.14 require ( github.com/BurntSushi/toml v0.3.1 From 9ed8d8c1135d5b7f6e85f68c3416d0209b857b50 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Thu, 18 Mar 2021 11:22:44 +0800 Subject: [PATCH 191/492] rename max content log size name for gtrace from maxlogsize to maxcontentlogsize --- net/gtrace/gtrace.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index 98490cd7f..6dadbf83e 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -41,7 +41,7 @@ var ( ) func init() { - if maxContentLogSize := gcmd.GetOptWithEnv(fmt.Sprintf("%s.maxlogsize", cmdEnvKey)).Int(); maxContentLogSize > 0 { + if maxContentLogSize := gcmd.GetOptWithEnv(fmt.Sprintf("%s.maxcontentlogsize", cmdEnvKey)).Int(); maxContentLogSize > 0 { tracingMaxContentLogSize = maxContentLogSize } CheckSetDefaultTextMapPropagator() From e3ebc908bb0fd3883ce9a94f4f74af03e64d5fc5 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Thu, 18 Mar 2021 15:21:05 +0800 Subject: [PATCH 192/492] improve package gconv --- util/gconv/gconv.go | 18 ++++++++++++++++ util/gconv/gconv_interface.go | 30 ++++++++++++++++++++++++++ util/gconv/gconv_z_unit_struct_test.go | 25 +++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 75a16afc9..19d838a2f 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -296,6 +296,9 @@ func Bytes(any interface{}) []byte { case []byte: return value default: + if f, ok := value.(apiBytes); ok { + return f.Bytes() + } return gbinary.Encode(any) } } @@ -439,6 +442,9 @@ func Bool(any interface{}) bool { } return true default: + if f, ok := value.(apiBool); ok { + return f.Bool() + } rv := reflect.ValueOf(any) switch rv.Kind() { case reflect.Ptr: @@ -543,6 +549,9 @@ func Int64(any interface{}) int64 { case []byte: return gbinary.DecodeToInt64(value) default: + if f, ok := value.(apiInt64); ok { + return f.Int64() + } s := String(value) isMinus := false if len(s) > 0 { @@ -665,6 +674,9 @@ func Uint64(any interface{}) uint64 { case []byte: return gbinary.DecodeToUint64(value) default: + if f, ok := value.(apiUint64); ok { + return f.Uint64() + } s := String(value) // Hexadecimal if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { @@ -700,6 +712,9 @@ func Float32(any interface{}) float32 { case []byte: return gbinary.DecodeToFloat32(value) default: + if f, ok := value.(apiFloat32); ok { + return f.Float32() + } v, _ := strconv.ParseFloat(String(any), 64) return float32(v) } @@ -718,6 +733,9 @@ func Float64(any interface{}) float64 { case []byte: return gbinary.DecodeToFloat64(value) default: + if f, ok := value.(apiFloat64); ok { + return f.Float64() + } v, _ := strconv.ParseFloat(String(any), 64) return v } diff --git a/util/gconv/gconv_interface.go b/util/gconv/gconv_interface.go index 9ff344889..8ddbe7c3b 100644 --- a/util/gconv/gconv_interface.go +++ b/util/gconv/gconv_interface.go @@ -11,11 +11,41 @@ type apiString interface { String() string } +// apiBool is used for type assert api for Bool(). +type apiBool interface { + Bool() bool +} + +// apiInt64 is used for type assert api for Int64(). +type apiInt64 interface { + Int64() int64 +} + +// apiUint64 is used for type assert api for Uint64(). +type apiUint64 interface { + Uint64() uint64 +} + +// apiFloat32 is used for type assert api for Float32(). +type apiFloat32 interface { + Float32() float32 +} + +// apiFloat64 is used for type assert api for Float64(). +type apiFloat64 interface { + Float64() float64 +} + // apiError is used for type assert api for Error(). type apiError interface { Error() string } +// apiBytes is used for type assert api for Bytes(). +type apiBytes interface { + Bytes() []byte +} + // apiInterfaces is used for type assert api for Interfaces(). type apiInterfaces interface { Interfaces() []interface{} diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index 6c08a6d21..062c17d50 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -7,6 +7,7 @@ package gconv_test import ( + "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/os/gtime" @@ -1139,3 +1140,27 @@ func Test_Struct_JsonParam(t *testing.T) { t.Assert(a.Name, "john") }) } + +func Test_Struct_GVarAttribute(t *testing.T) { + type A struct { + Id int `json:"id"` + Name string `json:"name"` + Status bool `json:"status"` + } + gtest.C(t, func(t *gtest.T) { + var ( + a = A{} + data = g.Map{ + "id": 100, + "name": "john", + "status": gvar.New(false), + } + ) + err := gconv.Struct(data, &a) + t.Assert(err, nil) + t.Assert(a.Id, data["id"]) + t.Assert(a.Name, data["name"]) + t.Assert(a.Status, data["status"]) + }) + +} From 48d840a1b89e23cf2846ebe50e24d7712a6a00ba Mon Sep 17 00:00:00 2001 From: jianchenma Date: Thu, 18 Mar 2021 15:29:42 +0800 Subject: [PATCH 193/492] ad go.sum --- .gitignore | 1 - go.sum | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 go.sum diff --git a/.gitignore b/.gitignore index a21ea7259..79efb0edc 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,4 @@ bin/ cbuild **/.DS_Store .vscode/ -go.sum .example/other/ diff --git a/go.sum b/go.sum new file mode 100644 index 000000000..2f20ad630 --- /dev/null +++ b/go.sum @@ -0,0 +1,58 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4= +github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= +github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +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/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.opentelemetry.io/otel v0.18.0 h1:d5Of7+Zw4ANFOJB+TIn2K3QWsgS2Ht7OU9DqZHI6qu8= +go.opentelemetry.io/otel v0.18.0/go.mod h1:PT5zQj4lTsR1YeARt8YNKcFb88/c2IKoSABK9mX0r78= +go.opentelemetry.io/otel/metric v0.18.0 h1:yuZCmY9e1ZTaMlZXLrrbAPmYW6tW1A5ozOZeOYGaTaY= +go.opentelemetry.io/otel/metric v0.18.0/go.mod h1:kEH2QtzAyBy3xDVQfGZKIcok4ZZFvd5xyKPfPcuK6pE= +go.opentelemetry.io/otel/oteltest v0.18.0 h1:FbKDFm/LnQDOHuGjED+fy3s5YMVg0z019GJ9Er66hYo= +go.opentelemetry.io/otel/oteltest v0.18.0/go.mod h1:NyierCU3/G8DLTva7KRzGii2fdxdR89zXKH1bNWY7Bo= +go.opentelemetry.io/otel/trace v0.18.0 h1:ilCfc/fptVKaDMK1vWk0elxpolurJbEgey9J6g6s+wk= +go.opentelemetry.io/otel/trace v0.18.0/go.mod h1:FzdUu3BPwZSZebfQ1vl5/tAa8LyMLXSJN57AXIt/iDk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 36963c05a2379ae0636489ae2473208a2991c401 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Fri, 19 Mar 2021 11:35:12 +0800 Subject: [PATCH 194/492] fix issue #1190 --- os/gfile/gfile_source.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/os/gfile/gfile_source.go b/os/gfile/gfile_source.go index efe93840b..2f620dc48 100644 --- a/os/gfile/gfile_source.go +++ b/os/gfile/gfile_source.go @@ -8,6 +8,7 @@ package gfile import ( "github.com/gogf/gf/text/gstr" + "os" "runtime" "strings" @@ -44,6 +45,7 @@ func MainPkgPath() string { if path != "" { return path } + var lastFile string for i := 1; i < 10000; i++ { if pc, file, _, ok := runtime.Caller(i); ok { if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter { @@ -61,6 +63,7 @@ func MainPkgPath() string { if Ext(file) != ".go" { continue } + lastFile = file if gregex.IsMatchString(`package\s+main`, GetContents(file)) { mainPkgPath.Set(Dir(file)) return Dir(file) @@ -69,5 +72,20 @@ func MainPkgPath() string { break } } + // If it still cannot find the path of the package main, + // it recursively searches the directory and its parents directory of the last go file. + // It's usually necessary for uint testing cases of business project. + if lastFile != "" { + for path = Dir(lastFile); len(path) > 1 && Exists(path) && path[len(path)-1] != os.PathSeparator; { + files, _ := ScanDir(path, "*.go") + for _, v := range files { + if gregex.IsMatchString(`package\s+main`, GetContents(v)) { + mainPkgPath.Set(path) + return path + } + } + path = Dir(path) + } + } return "" } From cf7706b16d9966cb27e19999337f905a8e398b86 Mon Sep 17 00:00:00 2001 From: Frank Yang Date: Fri, 19 Mar 2021 11:45:17 +0800 Subject: [PATCH 195/492] =?UTF-8?q?ZTE=20=E7=9A=84=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E5=90=8D=E5=BA=94=E8=AF=A5=E6=98=AF=E2=80=9C=E4=B8=AD=E5=85=B4?= =?UTF-8?q?=E9=80=9A=E8=AE=AF=E2=80=9D=EF=BC=8C=E4=B8=8D=E6=98=AF=E2=80=9C?= =?UTF-8?q?=E4=B8=AD=E5=85=B4=E7=A7=91=E6=8A=80=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README_ZH.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_ZH.MD b/README_ZH.MD index 025b94cb5..3575a9cea 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -81,7 +81,7 @@ golang版本 >= 1.11 # 用户 - [腾讯科技](https://www.tencent.com/) -- [中兴科技](https://www.zte.com.cn/china/) +- [中兴通讯](https://www.zte.com.cn/china/) - [蚂蚁金服](https://www.antfin.com/) - [医联科技](https://www.medlinker.com/) - [库币科技](https://www.kucoin.io/) From 2f3df76f377e91f42cdb92b4a4b416532ce7a738 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Fri, 19 Mar 2021 15:37:33 +0800 Subject: [PATCH 196/492] fix issue #1190 --- os/gfile/gfile_source.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/os/gfile/gfile_source.go b/os/gfile/gfile_source.go index 2f620dc48..cc2cbea79 100644 --- a/os/gfile/gfile_source.go +++ b/os/gfile/gfile_source.go @@ -51,6 +51,10 @@ func MainPkgPath() string { if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter { continue } + if Ext(file) != ".go" { + continue + } + lastFile = file // Check if it is called in package initialization function, // in which it here cannot retrieve main package path, // it so just returns that can make next check. @@ -60,10 +64,6 @@ func MainPkgPath() string { continue } } - if Ext(file) != ".go" { - continue - } - lastFile = file if gregex.IsMatchString(`package\s+main`, GetContents(file)) { mainPkgPath.Set(Dir(file)) return Dir(file) From 4aaf09fdedaa54d074e8ac8797e620cc7256c387 Mon Sep 17 00:00:00 2001 From: jianchenma Date: Fri, 19 Mar 2021 16:35:55 +0800 Subject: [PATCH 197/492] improve gfile.MainPkgPath --- os/gfile/gfile_source.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/gfile/gfile_source.go b/os/gfile/gfile_source.go index cc2cbea79..b82e04de9 100644 --- a/os/gfile/gfile_source.go +++ b/os/gfile/gfile_source.go @@ -64,7 +64,7 @@ func MainPkgPath() string { continue } } - if gregex.IsMatchString(`package\s+main`, GetContents(file)) { + if gregex.IsMatchString(`package\s+main\s+{`, GetContents(file)) { mainPkgPath.Set(Dir(file)) return Dir(file) } @@ -79,7 +79,7 @@ func MainPkgPath() string { for path = Dir(lastFile); len(path) > 1 && Exists(path) && path[len(path)-1] != os.PathSeparator; { files, _ := ScanDir(path, "*.go") for _, v := range files { - if gregex.IsMatchString(`package\s+main`, GetContents(v)) { + if gregex.IsMatchString(`package\s+main\s+{`, GetContents(v)) { mainPkgPath.Set(path) return path } From 958b109a123df8d3fc12e2aa70c71a598dd5d5bd Mon Sep 17 00:00:00 2001 From: jianchenma Date: Fri, 19 Mar 2021 19:16:21 +0800 Subject: [PATCH 198/492] fix removing cookie issue --- net/ghttp/ghttp_server_cookie.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ghttp/ghttp_server_cookie.go b/net/ghttp/ghttp_server_cookie.go index 47fbfb918..6647e9d83 100644 --- a/net/ghttp/ghttp_server_cookie.go +++ b/net/ghttp/ghttp_server_cookie.go @@ -160,14 +160,14 @@ func (c *Cookie) Remove(key string) { "", c.request.Server.GetCookieDomain(), c.request.Server.GetCookiePath(), - -86400, + -24*time.Hour, ) } // RemoveCookie deletes specified key and its value from cookie using given domain and path. // It actually tells the http client that the cookie is expired, do not send it to server next time. func (c *Cookie) RemoveCookie(key, domain, path string) { - c.SetCookie(key, "", domain, path, -86400) + c.SetCookie(key, "", domain, path, -24*time.Hour) } // Flush outputs the cookie items to client. From c423ad483078c5a5d1f661291d5586770bf56972 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 23 Mar 2021 14:21:54 +0800 Subject: [PATCH 199/492] fix issue in gfile.MainPkgPath;improve fields filter for package gdb --- database/gdb/gdb_model_fields.go | 16 ++++++++-------- database/gdb/gdb_model_utility.go | 7 ++++++- os/gfile/gfile_source.go | 4 ++-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index 6ec438a67..bbcb5978f 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -36,18 +36,18 @@ func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model { // String slice. case length >= 2: model := m.getModel() - model.fields = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct)), ",") + model.fields = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), ",") return model // It need type asserting. case length == 1: model := m.getModel() switch r := fieldNamesOrMapStruct[0].(type) { case string: - model.fields = gstr.Join(m.mappingAndFilterToTableFields([]string{r}), ",") + model.fields = gstr.Join(m.mappingAndFilterToTableFields([]string{r}, false), ",") case []string: - model.fields = gstr.Join(m.mappingAndFilterToTableFields(r), ",") + model.fields = gstr.Join(m.mappingAndFilterToTableFields(r, true), ",") default: - model.fields = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r)), ",") + model.fields = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",") } return model } @@ -65,16 +65,16 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model { model := m.getModel() switch { case length >= 2: - model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct)), ",") + model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), ",") return model case length == 1: switch r := fieldNamesOrMapStruct[0].(type) { case string: - model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields([]string{r}), ",") + model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields([]string{r}, false), ",") case []string: - model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(r), ",") + model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(r, true), ",") default: - model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r)), ",") + model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",") } return model } diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 716db7a30..93ede1a04 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -29,7 +29,10 @@ func (m *Model) getModel() *Model { } // mappingAndFilterToTableFields mappings and changes given field name to really table field name. -func (m *Model) mappingAndFilterToTableFields(fields []string) []string { +// Eg: +// ID -> id +// NICK_Name -> nickname +func (m *Model) mappingAndFilterToTableFields(fields []string, filter bool) []string { fieldsMap, err := m.db.TableFields(m.tables) if err != nil || len(fieldsMap) == 0 { return fields @@ -52,6 +55,8 @@ func (m *Model) mappingAndFilterToTableFields(fields []string) []string { // Eg: id, name if foundKey, _ := gutil.MapPossibleItemByKey(fieldsKeyMap, field); foundKey != "" { outputFieldsArray = append(outputFieldsArray, foundKey) + } else if !filter { + outputFieldsArray = append(outputFieldsArray, field) } } } else { diff --git a/os/gfile/gfile_source.go b/os/gfile/gfile_source.go index b82e04de9..37611ef17 100644 --- a/os/gfile/gfile_source.go +++ b/os/gfile/gfile_source.go @@ -64,7 +64,7 @@ func MainPkgPath() string { continue } } - if gregex.IsMatchString(`package\s+main\s+{`, GetContents(file)) { + if gregex.IsMatchString(`package\s+main\s+`, GetContents(file)) { mainPkgPath.Set(Dir(file)) return Dir(file) } @@ -79,7 +79,7 @@ func MainPkgPath() string { for path = Dir(lastFile); len(path) > 1 && Exists(path) && path[len(path)-1] != os.PathSeparator; { files, _ := ScanDir(path, "*.go") for _, v := range files { - if gregex.IsMatchString(`package\s+main\s+{`, GetContents(v)) { + if gregex.IsMatchString(`package\s+main\s+`, GetContents(v)) { mainPkgPath.Set(path) return path } From 482e0933318a6b04b204e7421e82d69b06da9e1f Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 23 Mar 2021 17:53:20 +0800 Subject: [PATCH 200/492] add map/[]map converting support for gconv.Scan;improve gconv.MaptoMaps --- container/gvar/gvar_map.go | 7 - encoding/gjson/gjson_api.go | 7 - encoding/gjson/gjson_deprecated.go | 9 - os/gcfg/gcfg_api.go | 19 -- util/gconv/gconv.go | 42 ++-- util/gconv/gconv_map.go | 208 +----------------- util/gconv/gconv_maps.go | 119 ++++++++++ util/gconv/gconv_maptomap.go | 141 ++++++++++++ util/gconv/gconv_maptomaps.go | 146 ++++++++++++ util/gconv/gconv_scan.go | 45 +++- util/gconv/gconv_slice.go | 120 ---------- util/gconv/gconv_slice_any.go | 2 +- util/gconv/gconv_slice_float.go | 6 +- util/gconv/gconv_slice_int.go | 6 +- util/gconv/gconv_slice_str.go | 2 +- util/gconv/gconv_slice_uint.go | 6 +- util/gconv/gconv_struct.go | 20 +- util/gconv/gconv_structs.go | 14 +- util/gconv/gconv_time.go | 16 +- util/gconv/gconv_unsafe.go | 4 +- util/gconv/gconv_z_unit_maptomap_test.go | 204 +++-------------- util/gconv/gconv_z_unit_scan_test.go | 74 ++++++- util/gpage/gpage.go | 12 +- util/gpage/gpage_unit_test.go | 2 +- util/grand/grand.go | 22 +- util/guid/guid_string.go | 12 +- util/gutil/gutil.go | 6 +- util/gutil/gutil_list.go | 20 +- util/gutil/gutil_map.go | 12 +- util/gutil/gutil_slice.go | 6 +- util/gvalid/gvalid.go | 22 +- util/gvalid/gvalid_custom_rule.go | 8 +- util/gvalid/gvalid_validator_check.go | 8 +- util/gvalid/gvalid_validator_check_map.go | 6 +- util/gvalid/gvalid_validator_check_struct.go | 12 +- util/gvalid/gvalid_validator_rule_length.go | 2 +- util/gvalid/gvalid_validator_rule_luhn.go | 2 +- util/gvalid/gvalid_validator_rule_range.go | 2 +- util/gvalid/gvalid_validator_rule_required.go | 2 +- 39 files changed, 688 insertions(+), 685 deletions(-) create mode 100644 util/gconv/gconv_maps.go create mode 100644 util/gconv/gconv_maptomap.go create mode 100644 util/gconv/gconv_maptomaps.go diff --git a/container/gvar/gvar_map.go b/container/gvar/gvar_map.go index 40bb97e9a..090dd372a 100644 --- a/container/gvar/gvar_map.go +++ b/container/gvar/gvar_map.go @@ -71,13 +71,6 @@ func (v *Var) MapToMap(pointer interface{}, mapping ...map[string]string) (err e return gconv.MapToMap(v.Val(), pointer, mapping...) } -// MapToMapDeep converts any map type variable to another map type variable -// recursively. -// See gconv.MapToMapDeep. -func (v *Var) MapToMapDeep(pointer interface{}, mapping ...map[string]string) (err error) { - return gconv.MapToMapDeep(v.Val(), pointer, mapping...) -} - // MapToMaps converts any map type variable to another map type variable . // See gconv.MapToMaps. func (v *Var) MapToMaps(pointer interface{}, mapping ...map[string]string) (err error) { diff --git a/encoding/gjson/gjson_api.go b/encoding/gjson/gjson_api.go index 51787f102..d065910f2 100644 --- a/encoding/gjson/gjson_api.go +++ b/encoding/gjson/gjson_api.go @@ -354,13 +354,6 @@ func (j *Json) GetMapToMap(pattern string, pointer interface{}, mapping ...map[s return gconv.MapToMap(j.Get(pattern), pointer, mapping...) } -// GetMapToMapDeep retrieves the value by specified and converts it to specified map -// variable recursively. -// See gconv.MapToMapDeep. -func (j *Json) GetMapToMapDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { - return gconv.MapToMapDeep(j.Get(pattern), pointer, mapping...) -} - // GetMapToMaps retrieves the value by specified and converts it to specified map slice // variable. // See gconv.MapToMaps. diff --git a/encoding/gjson/gjson_deprecated.go b/encoding/gjson/gjson_deprecated.go index a373e6823..9283866ca 100644 --- a/encoding/gjson/gjson_deprecated.go +++ b/encoding/gjson/gjson_deprecated.go @@ -85,15 +85,6 @@ func (j *Json) ToMapToMap(pointer interface{}, mapping ...map[string]string) err return gconv.MapToMap(*(j.p), pointer, mapping...) } -// ToMapToMapDeep converts current Json object to specified map variable recursively. -// The parameter of should be type of *map. -// Deprecated, use MapToMap instead. -func (j *Json) ToMapToMapDeep(pointer interface{}, mapping ...map[string]string) error { - j.mu.RLock() - defer j.mu.RUnlock() - return gconv.MapToMapDeep(*(j.p), pointer, mapping...) -} - // ToMapToMaps converts current Json object to specified map variable slice. // The parameter of should be type of []map/*map. // Deprecated, use MapToMaps instead. diff --git a/os/gcfg/gcfg_api.go b/os/gcfg/gcfg_api.go index 504e447d2..1fafb391b 100644 --- a/os/gcfg/gcfg_api.go +++ b/os/gcfg/gcfg_api.go @@ -333,16 +333,6 @@ func (c *Config) GetMapToMap(pattern string, pointer interface{}, mapping ...map return errors.New("configuration not found") } -// GetMapToMapDeep retrieves the value by specified `pattern` and converts it to specified map -// variable recursively. -// See gconv.MapToMapDeep. -func (c *Config) GetMapToMapDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.GetMapToMapDeep(pattern, pointer, mapping...) - } - return errors.New("configuration not found") -} - // GetMapToMaps retrieves the value by specified `pattern` and converts it to specified map slice // variable. // See gconv.MapToMaps. @@ -426,15 +416,6 @@ func (c *Config) ToMapToMap(pointer interface{}, mapping ...map[string]string) e return errors.New("configuration not found") } -// ToMapToMapDeep converts current Json object to specified map variable recursively. -// The parameter of `pointer` should be type of *map. -func (c *Config) ToMapToMapDeep(pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.ToMapToMapDeep(pointer, mapping...) - } - return errors.New("configuration not found") -} - // ToMapToMaps converts current Json object to specified map variable slice. // The parameter of `pointer` should be type of []map/*map. func (c *Config) ToMapToMaps(pointer interface{}, mapping ...map[string]string) error { diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 19d838a2f..271c23cb7 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -45,8 +45,8 @@ var ( StructTagPriority = []string{"gconv", "param", "params", "c", "p", "json"} ) -// Convert converts the variable to the type , the type is specified by string. -// The optional parameter is used for additional necessary parameter for this conversion. +// Convert converts the variable `i` to the type `t`, the type `t` is specified by string. +// The optional parameter `params` is used for additional necessary parameter for this conversion. // It supports common types conversion as its conversion based on type name string. func Convert(any interface{}, t string, params ...interface{}) interface{} { switch t { @@ -277,7 +277,7 @@ func Convert(any interface{}, t string, params ...interface{}) interface{} { } } -// Byte converts to byte. +// Byte converts `i` to byte. func Byte(any interface{}) byte { if v, ok := any.(byte); ok { return v @@ -285,7 +285,7 @@ func Byte(any interface{}) byte { return Uint8(any) } -// Bytes converts to []byte. +// Bytes converts `i` to []byte. func Bytes(any interface{}) []byte { if any == nil { return nil @@ -303,7 +303,7 @@ func Bytes(any interface{}) []byte { } } -// Rune converts to rune. +// Rune converts `i` to rune. func Rune(any interface{}) rune { if v, ok := any.(rune); ok { return v @@ -311,7 +311,7 @@ func Rune(any interface{}) rune { return rune(Int32(any)) } -// Runes converts to []rune. +// Runes converts `i` to []rune. func Runes(any interface{}) []rune { if v, ok := any.([]rune); ok { return v @@ -319,7 +319,7 @@ func Runes(any interface{}) []rune { return []rune(String(any)) } -// String converts to string. +// String converts `i` to string. // It's most common used converting function. func String(any interface{}) string { if any == nil { @@ -422,8 +422,8 @@ func String(any interface{}) string { } } -// Bool converts to bool. -// It returns false if is: false, "", 0, "false", "off", "no", empty slice/map. +// Bool converts `i` to bool. +// It returns false if `i` is: false, "", 0, "false", "off", "no", empty slice/map. func Bool(any interface{}) bool { if any == nil { return false @@ -467,7 +467,7 @@ func Bool(any interface{}) bool { } } -// Int converts to int. +// Int converts `i` to int. func Int(any interface{}) int { if any == nil { return 0 @@ -478,7 +478,7 @@ func Int(any interface{}) int { return int(Int64(any)) } -// Int8 converts to int8. +// Int8 converts `i` to int8. func Int8(any interface{}) int8 { if any == nil { return 0 @@ -489,7 +489,7 @@ func Int8(any interface{}) int8 { return int8(Int64(any)) } -// Int16 converts to int16. +// Int16 converts `i` to int16. func Int16(any interface{}) int16 { if any == nil { return 0 @@ -500,7 +500,7 @@ func Int16(any interface{}) int16 { return int16(Int64(any)) } -// Int32 converts to int32. +// Int32 converts `i` to int32. func Int32(any interface{}) int32 { if any == nil { return 0 @@ -511,7 +511,7 @@ func Int32(any interface{}) int32 { return int32(Int64(any)) } -// Int64 converts to int64. +// Int64 converts `i` to int64. func Int64(any interface{}) int64 { if any == nil { return 0 @@ -592,7 +592,7 @@ func Int64(any interface{}) int64 { } } -// Uint converts to uint. +// Uint converts `i` to uint. func Uint(any interface{}) uint { if any == nil { return 0 @@ -603,7 +603,7 @@ func Uint(any interface{}) uint { return uint(Uint64(any)) } -// Uint8 converts to uint8. +// Uint8 converts `i` to uint8. func Uint8(any interface{}) uint8 { if any == nil { return 0 @@ -614,7 +614,7 @@ func Uint8(any interface{}) uint8 { return uint8(Uint64(any)) } -// Uint16 converts to uint16. +// Uint16 converts `i` to uint16. func Uint16(any interface{}) uint16 { if any == nil { return 0 @@ -625,7 +625,7 @@ func Uint16(any interface{}) uint16 { return uint16(Uint64(any)) } -// Uint32 converts to uint32. +// Uint32 converts `i` to uint32. func Uint32(any interface{}) uint32 { if any == nil { return 0 @@ -636,7 +636,7 @@ func Uint32(any interface{}) uint32 { return uint32(Uint64(any)) } -// Uint64 converts to uint64. +// Uint64 converts `i` to uint64. func Uint64(any interface{}) uint64 { if any == nil { return 0 @@ -699,7 +699,7 @@ func Uint64(any interface{}) uint64 { } } -// Float32 converts to float32. +// Float32 converts `i` to float32. func Float32(any interface{}) float32 { if any == nil { return 0 @@ -720,7 +720,7 @@ func Float32(any interface{}) float32 { } } -// Float64 converts to float64. +// Float64 converts `i` to float64. func Float64(any interface{}) float64 { if any == nil { return 0 diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index cb7631af0..5563ddda8 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -7,7 +7,6 @@ package gconv import ( - "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "reflect" "strings" @@ -16,17 +15,17 @@ import ( "github.com/gogf/gf/internal/utils" ) -// Map converts any variable to map[string]interface{}. If the parameter is not a +// Map converts any variable `value` to map[string]interface{}. If the parameter `value` is not a // map/struct/*struct type, then the conversion will fail and returns nil. // -// If is a struct/*struct object, the second parameter specifies the most priority +// If `value` is a struct/*struct object, the second parameter `tags` specifies the most priority // tags that will be detected, otherwise it detects the tags in order of: // gconv, json, field name. func Map(value interface{}, tags ...string) map[string]interface{} { return doMapConvert(value, false, tags...) } -// MapDeep does Map function recursively, which means if the attribute of +// MapDeep does Map function recursively, which means if the attribute of `value` // is also a struct/*struct, calls Map function on this attribute converting it to // a map[string]interface{} type variable. // Also see Map. @@ -35,7 +34,7 @@ func MapDeep(value interface{}, tags ...string) map[string]interface{} { } // doMapConvert implements the map converting. -// It automatically checks and converts json string to map if is string/[]byte. +// It automatically checks and converts json string to map if `value` is string/[]byte. // // TODO completely implement the recursive converting for all types, especially the map. func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]interface{} { @@ -154,7 +153,7 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string] reflectKind = reflectValue.Kind() } switch reflectKind { - // If is type of array, it converts the value of even number index as its key and + // If `value` is type of array, it converts the value of even number index as its key and // the value of odd number index as its corresponding value, for example: // []string{"k1","v1","k2","v2"} => map[string]interface{}{"k1":"v1", "k2":"v2"} // []string{"k1","v1","k2"} => map[string]interface{}{"k1":"v1", "k2":nil} @@ -354,7 +353,7 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b return value } -// MapStrStr converts to map[string]string. +// MapStrStr converts `value` to map[string]string. // Note that there might be data copy for this map type converting. func MapStrStr(value interface{}, tags ...string) map[string]string { if r, ok := value.(map[string]string); ok { @@ -371,7 +370,7 @@ func MapStrStr(value interface{}, tags ...string) map[string]string { return nil } -// MapStrStrDeep converts to map[string]string recursively. +// MapStrStrDeep converts `value` to map[string]string recursively. // Note that there might be data copy for this map type converting. func MapStrStrDeep(value interface{}, tags ...string) map[string]string { if r, ok := value.(map[string]string); ok { @@ -387,196 +386,3 @@ func MapStrStrDeep(value interface{}, tags ...string) map[string]string { } return nil } - -// MapToMap converts any map type variable to another map type variable -// using reflect. -// See doMapToMap. -func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error { - return doMapToMap(params, pointer, mapping...) -} - -// MapToMapDeep converts any map type variable to another map type variable -// using reflect recursively. -// Deprecated, use MapToMap instead. -func MapToMapDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error { - return doMapToMap(params, pointer, mapping...) -} - -// doMapToMap converts any map type variable to another map type variable . -// -// The parameter can be any type of map, like: -// map[string]string, map[string]struct, , map[string]*struct, etc. -// -// The parameter should be type of *map, like: -// map[int]string, map[string]struct, , map[string]*struct, etc. -// -// The optional parameter is used for struct attribute to map key mapping, which makes -// sense only if the items of original map is type struct. -func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - var ( - paramsRv = reflect.ValueOf(params) - paramsKind = paramsRv.Kind() - ) - if paramsKind == reflect.Ptr { - paramsRv = paramsRv.Elem() - paramsKind = paramsRv.Kind() - } - if paramsKind != reflect.Map { - return gerror.New("params should be type of map") - } - // Empty params map, no need continue. - if paramsRv.Len() == 0 { - return nil - } - var pointerRv reflect.Value - if v, ok := pointer.(reflect.Value); ok { - pointerRv = v - } else { - pointerRv = reflect.ValueOf(pointer) - } - pointerKind := pointerRv.Kind() - for pointerKind == reflect.Ptr { - pointerRv = pointerRv.Elem() - pointerKind = pointerRv.Kind() - } - if pointerKind != reflect.Map { - return gerror.New("pointer should be type of *map") - } - defer func() { - // Catch the panic, especially the reflect operation panics. - if exception := recover(); exception != nil { - if e, ok := exception.(errorStack); ok { - err = e - } else { - err = gerror.NewSkipf(1, "%v", exception) - } - } - }() - var ( - paramsKeys = paramsRv.MapKeys() - pointerKeyType = pointerRv.Type().Key() - pointerValueType = pointerRv.Type().Elem() - pointerValueKind = pointerValueType.Kind() - dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys)) - ) - // Retrieve the true element type of target map. - if pointerValueKind == reflect.Ptr { - pointerValueKind = pointerValueType.Elem().Kind() - } - for _, key := range paramsKeys { - e := reflect.New(pointerValueType).Elem() - switch pointerValueKind { - case reflect.Map, reflect.Struct: - if err = Struct(paramsRv.MapIndex(key).Interface(), e, mapping...); err != nil { - return err - } - default: - e.Set( - reflect.ValueOf( - Convert( - paramsRv.MapIndex(key).Interface(), - pointerValueType.String(), - ), - ), - ) - } - dataMap.SetMapIndex( - reflect.ValueOf( - Convert( - key.Interface(), - pointerKeyType.Name(), - ), - ), - e, - ) - } - pointerRv.Set(dataMap) - return nil -} - -// MapToMaps converts any map type variable to another map type variable . -// See doMapToMaps. -func MapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) error { - return doMapToMaps(params, pointer, mapping...) -} - -// MapToMapsDeep converts any map type variable to another map type variable -// recursively. -// Deprecated, use MapToMaps instead. -func MapToMapsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error { - return doMapToMaps(params, pointer, mapping...) -} - -// doMapToMaps converts any map type variable to another map type variable . -// -// The parameter can be any type of map, of which the item type is slice map, like: -// map[int][]map, map[string][]map. -// -// The parameter should be type of *map, of which the item type is slice map, like: -// map[string][]struct, map[string][]*struct. -// -// The optional parameter is used for struct attribute to map key mapping, which makes -// sense only if the items of original map is type struct. -// -// TODO it's supposed supporting target type like: map[int][]map, map[string][]map. -func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - var ( - paramsRv = reflect.ValueOf(params) - paramsKind = paramsRv.Kind() - ) - if paramsKind == reflect.Ptr { - paramsRv = paramsRv.Elem() - paramsKind = paramsRv.Kind() - } - if paramsKind != reflect.Map { - return gerror.New("params should be type of map") - } - // Empty params map, no need continue. - if paramsRv.Len() == 0 { - return nil - } - var ( - pointerRv = reflect.ValueOf(pointer) - pointerKind = pointerRv.Kind() - ) - for pointerKind == reflect.Ptr { - pointerRv = pointerRv.Elem() - pointerKind = pointerRv.Kind() - } - if pointerKind != reflect.Map { - return gerror.New("pointer should be type of *map/**map") - } - defer func() { - // Catch the panic, especially the reflect operation panics. - if exception := recover(); exception != nil { - if e, ok := exception.(errorStack); ok { - err = e - } else { - err = gerror.NewSkipf(1, "%v", exception) - } - } - }() - var ( - paramsKeys = paramsRv.MapKeys() - pointerKeyType = pointerRv.Type().Key() - pointerValueType = pointerRv.Type().Elem() - dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys)) - ) - for _, key := range paramsKeys { - e := reflect.New(pointerValueType).Elem() - if err = Structs(paramsRv.MapIndex(key).Interface(), e.Addr(), mapping...); err != nil { - return err - } - dataMap.SetMapIndex( - reflect.ValueOf( - Convert( - key.Interface(), - pointerKeyType.Name(), - ), - ), - e, - ) - } - pointerRv.Set(dataMap) - return nil -} diff --git a/util/gconv/gconv_maps.go b/util/gconv/gconv_maps.go new file mode 100644 index 000000000..8a05e9600 --- /dev/null +++ b/util/gconv/gconv_maps.go @@ -0,0 +1,119 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gconv + +import "github.com/gogf/gf/internal/json" + +// SliceMap is alias of Maps. +func SliceMap(any interface{}) []map[string]interface{} { + return Maps(any) +} + +// SliceMapDeep is alias of MapsDeep. +func SliceMapDeep(any interface{}) []map[string]interface{} { + return MapsDeep(any) +} + +// SliceStruct is alias of Structs. +func SliceStruct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { + return Structs(params, pointer, mapping...) +} + +// Maps converts `i` to []map[string]interface{}. +// Note that it automatically checks and converts json string to []map if `value` is string/[]byte. +func Maps(value interface{}, tags ...string) []map[string]interface{} { + if value == nil { + return nil + } + switch r := value.(type) { + case string: + list := make([]map[string]interface{}, 0) + if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { + if err := json.Unmarshal([]byte(r), &list); err != nil { + return nil + } + return list + } else { + return nil + } + + case []byte: + list := make([]map[string]interface{}, 0) + if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { + if err := json.Unmarshal(r, &list); err != nil { + return nil + } + return list + } else { + return nil + } + + case []map[string]interface{}: + return r + + default: + array := Interfaces(value) + if len(array) == 0 { + return nil + } + list := make([]map[string]interface{}, len(array)) + for k, v := range array { + list[k] = Map(v, tags...) + } + return list + } +} + +// MapsDeep converts `i` to []map[string]interface{} recursively. +// +// TODO completely implement the recursive converting for all types. +func MapsDeep(value interface{}, tags ...string) []map[string]interface{} { + if value == nil { + return nil + } + switch r := value.(type) { + case string: + list := make([]map[string]interface{}, 0) + if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { + if err := json.Unmarshal([]byte(r), &list); err != nil { + return nil + } + return list + } else { + return nil + } + + case []byte: + list := make([]map[string]interface{}, 0) + if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { + if err := json.Unmarshal(r, &list); err != nil { + return nil + } + return list + } else { + return nil + } + + case []map[string]interface{}: + list := make([]map[string]interface{}, len(r)) + for k, v := range r { + list[k] = MapDeep(v, tags...) + } + return list + + default: + array := Interfaces(value) + if len(array) == 0 { + return nil + } + list := make([]map[string]interface{}, len(array)) + for k, v := range array { + list[k] = MapDeep(v, tags...) + } + return list + } +} diff --git a/util/gconv/gconv_maptomap.go b/util/gconv/gconv_maptomap.go new file mode 100644 index 000000000..143d6d013 --- /dev/null +++ b/util/gconv/gconv_maptomap.go @@ -0,0 +1,141 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gconv + +import ( + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/json" + "reflect" +) + +// MapToMap converts any map type variable `params` to another map type variable `pointer` +// using reflect. +// See doMapToMap. +func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error { + return doMapToMap(params, pointer, mapping...) +} + +// doMapToMap converts any map type variable `params` to another map type variable `pointer`. +// +// The parameter `params` can be any type of map, like: +// map[string]string, map[string]struct, , map[string]*struct, etc. +// +// The parameter `pointer` should be type of *map, like: +// map[int]string, map[string]struct, , map[string]*struct, etc. +// +// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes +// sense only if the items of original map `params` is type struct. +func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { + // If given `params` is JSON, it then uses json.Unmarshal doing the converting. + switch r := params.(type) { + case []byte: + if json.Valid(r) { + if rv, ok := pointer.(reflect.Value); ok { + if rv.Kind() == reflect.Ptr { + return json.Unmarshal(r, rv.Interface()) + } + } else { + return json.Unmarshal(r, pointer) + } + } + case string: + if paramsBytes := []byte(r); json.Valid(paramsBytes) { + if rv, ok := pointer.(reflect.Value); ok { + if rv.Kind() == reflect.Ptr { + return json.Unmarshal(paramsBytes, rv.Interface()) + } + } else { + return json.Unmarshal(paramsBytes, pointer) + } + } + } + var ( + paramsRv reflect.Value + paramsKind reflect.Kind + ) + if v, ok := params.(reflect.Value); ok { + paramsRv = v + } else { + paramsRv = reflect.ValueOf(params) + } + paramsKind = paramsRv.Kind() + if paramsKind == reflect.Ptr { + paramsRv = paramsRv.Elem() + paramsKind = paramsRv.Kind() + } + if paramsKind != reflect.Map { + return doMapToMap(Map(params), pointer, mapping...) + } + // Empty params map, no need continue. + if paramsRv.Len() == 0 { + return nil + } + var pointerRv reflect.Value + if v, ok := pointer.(reflect.Value); ok { + pointerRv = v + } else { + pointerRv = reflect.ValueOf(pointer) + } + pointerKind := pointerRv.Kind() + for pointerKind == reflect.Ptr { + pointerRv = pointerRv.Elem() + pointerKind = pointerRv.Kind() + } + if pointerKind != reflect.Map { + return gerror.Newf("pointer should be type of *map, but got:%s", pointerKind) + } + defer func() { + // Catch the panic, especially the reflect operation panics. + if exception := recover(); exception != nil { + if e, ok := exception.(errorStack); ok { + err = e + } else { + err = gerror.NewSkipf(1, "%v", exception) + } + } + }() + var ( + paramsKeys = paramsRv.MapKeys() + pointerKeyType = pointerRv.Type().Key() + pointerValueType = pointerRv.Type().Elem() + pointerValueKind = pointerValueType.Kind() + dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys)) + ) + // Retrieve the true element type of target map. + if pointerValueKind == reflect.Ptr { + pointerValueKind = pointerValueType.Elem().Kind() + } + for _, key := range paramsKeys { + e := reflect.New(pointerValueType).Elem() + switch pointerValueKind { + case reflect.Map, reflect.Struct: + if err = Struct(paramsRv.MapIndex(key).Interface(), e, mapping...); err != nil { + return err + } + default: + e.Set( + reflect.ValueOf( + Convert( + paramsRv.MapIndex(key).Interface(), + pointerValueType.String(), + ), + ), + ) + } + dataMap.SetMapIndex( + reflect.ValueOf( + Convert( + key.Interface(), + pointerKeyType.Name(), + ), + ), + e, + ) + } + pointerRv.Set(dataMap) + return nil +} diff --git a/util/gconv/gconv_maptomaps.go b/util/gconv/gconv_maptomaps.go new file mode 100644 index 000000000..9e0d58306 --- /dev/null +++ b/util/gconv/gconv_maptomaps.go @@ -0,0 +1,146 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gconv + +import ( + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/json" + "reflect" +) + +// MapToMaps converts any slice type variable `params` to another map slice type variable `pointer`. +// See doMapToMaps. +func MapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) error { + return doMapToMaps(params, pointer, mapping...) +} + +// MapToMapsDeep converts any slice type variable `params` to another map slice type variable +// `pointer` recursively. +// Deprecated, use MapToMaps instead. +func MapToMapsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error { + return doMapToMaps(params, pointer, mapping...) +} + +// doMapToMaps converts any map type variable `params` to another map slice variable `pointer`. +// +// The parameter `params` can be type of []map, []*map, []struct, []*struct. +// +// The parameter `pointer` should be type of []map, []*map. +// +// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes +// sense only if the item of `params` is type struct. +func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { + // If given `params` is JSON, it then uses json.Unmarshal doing the converting. + switch r := params.(type) { + case []byte: + if json.Valid(r) { + if rv, ok := pointer.(reflect.Value); ok { + if rv.Kind() == reflect.Ptr { + return json.Unmarshal(r, rv.Interface()) + } + } else { + return json.Unmarshal(r, pointer) + } + } + case string: + if paramsBytes := []byte(r); json.Valid(paramsBytes) { + if rv, ok := pointer.(reflect.Value); ok { + if rv.Kind() == reflect.Ptr { + return json.Unmarshal(paramsBytes, rv.Interface()) + } + } else { + return json.Unmarshal(paramsBytes, pointer) + } + } + } + // Params and its element type check. + var ( + paramsRv reflect.Value + paramsKind reflect.Kind + ) + if v, ok := params.(reflect.Value); ok { + paramsRv = v + } else { + paramsRv = reflect.ValueOf(params) + } + paramsKind = paramsRv.Kind() + if paramsKind == reflect.Ptr { + paramsRv = paramsRv.Elem() + paramsKind = paramsRv.Kind() + } + if paramsKind != reflect.Array && paramsKind != reflect.Slice { + return gerror.New("params should be type of slice, eg: []map/[]*map/[]struct/[]*struct") + } + var ( + paramsElem = paramsRv.Type().Elem() + paramsElemKind = paramsElem.Kind() + ) + if paramsElemKind == reflect.Ptr { + paramsElem = paramsElem.Elem() + paramsElemKind = paramsElem.Kind() + } + if paramsElemKind != reflect.Map && paramsElemKind != reflect.Struct && paramsElemKind != reflect.Interface { + return gerror.Newf("params element should be type of map/*map/struct/*struct, but got: %s", paramsElemKind) + } + // Empty slice, no need continue. + if paramsRv.Len() == 0 { + return nil + } + // Pointer and its element type check. + var ( + pointerRv = reflect.ValueOf(pointer) + pointerKind = pointerRv.Kind() + ) + for pointerKind == reflect.Ptr { + pointerRv = pointerRv.Elem() + pointerKind = pointerRv.Kind() + } + if pointerKind != reflect.Array && pointerKind != reflect.Slice { + return gerror.New("pointer should be type of *[]map/*[]*map") + } + var ( + pointerElemType = pointerRv.Type().Elem() + pointerElemKind = pointerElemType.Kind() + ) + if pointerElemKind == reflect.Ptr { + pointerElemKind = pointerElemType.Elem().Kind() + } + if pointerElemKind != reflect.Map { + return gerror.New("pointer element should be type of map/*map") + } + defer func() { + // Catch the panic, especially the reflect operation panics. + if exception := recover(); exception != nil { + if e, ok := exception.(errorStack); ok { + err = e + } else { + err = gerror.NewSkipf(1, "%v", exception) + } + } + }() + var ( + pointerSlice = reflect.MakeSlice(pointerRv.Type(), paramsRv.Len(), paramsRv.Len()) + ) + for i := 0; i < paramsRv.Len(); i++ { + var item reflect.Value + if pointerElemType.Kind() == reflect.Ptr { + item = reflect.New(pointerElemType.Elem()) + if err = MapToMap(paramsRv.Index(i).Interface(), item, mapping...); err != nil { + return err + } + pointerSlice.Index(i).Set(item) + } else { + item = reflect.New(pointerElemType) + if err = MapToMap(paramsRv.Index(i).Interface(), item, mapping...); err != nil { + return err + } + pointerSlice.Index(i).Set(item.Elem()) + } + } + pointerRv.Set(pointerSlice) + return +} diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index a43893969..4b481057b 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -11,18 +11,39 @@ import ( "reflect" ) -// Scan automatically calls Struct or Structs function according to the type of parameter -// to implement the converting. -// It calls function Struct if is type of *struct/**struct to do the converting. -// It calls function Structs if is type of *[]struct/*[]*struct to do the converting. +// Scan automatically calls MapToMap, MapToMaps, Struct or Structs function according to +// the type of parameter `pointer` to implement the converting. +// It calls function MapToMap if `pointer` is type of *map to do the converting. +// It calls function MapToMaps if `pointer` is type of *[]map/*[]*map to do the converting. +// It calls function Struct if `pointer` is type of *struct/**struct to do the converting. +// It calls function Structs if `pointer` is type of *[]struct/*[]*struct to do the converting. func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - t := reflect.TypeOf(pointer) - k := t.Kind() - if k != reflect.Ptr { - return gerror.Newf("params should be type of pointer, but got: %v", k) + var ( + pointerType = reflect.TypeOf(pointer) + pointerKind = pointerType.Kind() + ) + if pointerKind != reflect.Ptr { + return gerror.Newf("params should be type of pointer, but got: %v", pointerKind) } - switch t.Elem().Kind() { + var ( + pointerElem = pointerType.Elem() + pointerElemKind = pointerElem.Kind() + ) + switch pointerElemKind { + case reflect.Map: + return MapToMap(params, pointer, mapping...) case reflect.Array, reflect.Slice: + var ( + sliceElem = pointerElem.Elem() + sliceElemKind = sliceElem.Kind() + ) + for sliceElemKind == reflect.Ptr { + sliceElem = sliceElem.Elem() + sliceElemKind = sliceElem.Kind() + } + if sliceElemKind == reflect.Map { + return MapToMaps(params, pointer, mapping...) + } return Structs(params, pointer, mapping...) default: return Struct(params, pointer, mapping...) @@ -30,9 +51,9 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) } // ScanDeep automatically calls StructDeep or StructsDeep function according to the type of -// parameter to implement the converting.. -// It calls function StructDeep if is type of *struct/**struct to do the converting. -// It calls function StructsDeep if is type of *[]struct/*[]*struct to do the converting. +// parameter `pointer` to implement the converting.. +// It calls function StructDeep if `pointer` is type of *struct/**struct to do the converting. +// It calls function StructsDeep if `pointer` is type of *[]struct/*[]*struct to do the converting. // Deprecated, use Scan instead. func ScanDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { t := reflect.TypeOf(pointer) diff --git a/util/gconv/gconv_slice.go b/util/gconv/gconv_slice.go index 265dd4d3a..a5c4126a5 100644 --- a/util/gconv/gconv_slice.go +++ b/util/gconv/gconv_slice.go @@ -5,123 +5,3 @@ // You can obtain one at https://github.com/gogf/gf. package gconv - -import ( - "github.com/gogf/gf/internal/json" -) - -// SliceMap is alias of Maps. -func SliceMap(any interface{}) []map[string]interface{} { - return Maps(any) -} - -// SliceMapDeep is alias of MapsDeep. -func SliceMapDeep(any interface{}) []map[string]interface{} { - return MapsDeep(any) -} - -// SliceStruct is alias of Structs. -func SliceStruct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - return Structs(params, pointer, mapping...) -} - -// SliceStructDeep is alias of StructsDeep. -// Deprecated, use SliceStruct instead. -func SliceStructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - return StructsDeep(params, pointer, mapping...) -} - -// Maps converts to []map[string]interface{}. -// Note that it automatically checks and converts json string to []map if is string/[]byte. -func Maps(value interface{}, tags ...string) []map[string]interface{} { - if value == nil { - return nil - } - switch r := value.(type) { - case string: - list := make([]map[string]interface{}, 0) - if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { - if err := json.Unmarshal([]byte(r), &list); err != nil { - return nil - } - return list - } else { - return nil - } - - case []byte: - list := make([]map[string]interface{}, 0) - if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { - if err := json.Unmarshal(r, &list); err != nil { - return nil - } - return list - } else { - return nil - } - - case []map[string]interface{}: - return r - - default: - array := Interfaces(value) - if len(array) == 0 { - return nil - } - list := make([]map[string]interface{}, len(array)) - for k, v := range array { - list[k] = Map(v, tags...) - } - return list - } -} - -// MapsDeep converts to []map[string]interface{} recursively. -// -// TODO completely implement the recursive converting for all types. -func MapsDeep(value interface{}, tags ...string) []map[string]interface{} { - if value == nil { - return nil - } - switch r := value.(type) { - case string: - list := make([]map[string]interface{}, 0) - if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { - if err := json.Unmarshal([]byte(r), &list); err != nil { - return nil - } - return list - } else { - return nil - } - - case []byte: - list := make([]map[string]interface{}, 0) - if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { - if err := json.Unmarshal(r, &list); err != nil { - return nil - } - return list - } else { - return nil - } - - case []map[string]interface{}: - list := make([]map[string]interface{}, len(r)) - for k, v := range r { - list[k] = MapDeep(v, tags...) - } - return list - - default: - array := Interfaces(value) - if len(array) == 0 { - return nil - } - list := make([]map[string]interface{}, len(array)) - for k, v := range array { - list[k] = MapDeep(v, tags...) - } - return list - } -} diff --git a/util/gconv/gconv_slice_any.go b/util/gconv/gconv_slice_any.go index 865d21452..e249c56ab 100644 --- a/util/gconv/gconv_slice_any.go +++ b/util/gconv/gconv_slice_any.go @@ -15,7 +15,7 @@ func SliceAny(any interface{}) []interface{} { return Interfaces(any) } -// Interfaces converts to []interface{}. +// Interfaces converts `i` to []interface{}. func Interfaces(any interface{}) []interface{} { if any == nil { return nil diff --git a/util/gconv/gconv_slice_float.go b/util/gconv/gconv_slice_float.go index 56c1f7f4b..d3d442c03 100644 --- a/util/gconv/gconv_slice_float.go +++ b/util/gconv/gconv_slice_float.go @@ -23,12 +23,12 @@ func SliceFloat64(any interface{}) []float64 { return Floats(any) } -// Floats converts to []float64. +// Floats converts `i` to []float64. func Floats(any interface{}) []float64 { return Float64s(any) } -// Float32s converts to []float32. +// Float32s converts `i` to []float32. func Float32s(any interface{}) []float32 { if any == nil { return nil @@ -148,7 +148,7 @@ func Float32s(any interface{}) []float32 { return array } -// Float64s converts to []float64. +// Float64s converts `i` to []float64. func Float64s(any interface{}) []float64 { if any == nil { return nil diff --git a/util/gconv/gconv_slice_int.go b/util/gconv/gconv_slice_int.go index 0fa2e680c..f19afadd4 100644 --- a/util/gconv/gconv_slice_int.go +++ b/util/gconv/gconv_slice_int.go @@ -23,7 +23,7 @@ func SliceInt64(any interface{}) []int64 { return Int64s(any) } -// Ints converts to []int. +// Ints converts `i` to []int. func Ints(any interface{}) []int { if any == nil { return nil @@ -153,7 +153,7 @@ func Ints(any interface{}) []int { return array } -// Int32s converts to []int32. +// Int32s converts `i` to []int32. func Int32s(any interface{}) []int32 { if any == nil { return nil @@ -283,7 +283,7 @@ func Int32s(any interface{}) []int32 { return array } -// Int64s converts to []int64. +// Int64s converts `i` to []int64. func Int64s(any interface{}) []int64 { if any == nil { return nil diff --git a/util/gconv/gconv_slice_str.go b/util/gconv/gconv_slice_str.go index 02c24f288..ab003cec2 100644 --- a/util/gconv/gconv_slice_str.go +++ b/util/gconv/gconv_slice_str.go @@ -13,7 +13,7 @@ func SliceStr(any interface{}) []string { return Strings(any) } -// Strings converts to []string. +// Strings converts `i` to []string. func Strings(any interface{}) []string { if any == nil { return nil diff --git a/util/gconv/gconv_slice_uint.go b/util/gconv/gconv_slice_uint.go index 8573ffd5b..f2277573b 100644 --- a/util/gconv/gconv_slice_uint.go +++ b/util/gconv/gconv_slice_uint.go @@ -23,7 +23,7 @@ func SliceUint64(any interface{}) []uint64 { return Uint64s(any) } -// Uints converts to []uint. +// Uints converts `i` to []uint. func Uints(any interface{}) []uint { if any == nil { return nil @@ -149,7 +149,7 @@ func Uints(any interface{}) []uint { return array } -// Uint32s converts to []uint32. +// Uint32s converts `i` to []uint32. func Uint32s(any interface{}) []uint32 { if any == nil { return nil @@ -274,7 +274,7 @@ func Uint32s(any interface{}) []uint32 { return array } -// Uint64s converts to []uint64. +// Uint64s converts `i` to []uint64. func Uint64s(any interface{}) []uint64 { if any == nil { return nil diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 7e6b955a8..0d4924bd7 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -19,15 +19,15 @@ import ( ) // Struct maps the params key-value pairs to the corresponding struct object's attributes. -// The third parameter is unnecessary, indicating the mapping rules between the +// The third parameter `mapping` is unnecessary, indicating the mapping rules between the // custom key name and the attribute name(case sensitive). // // Note: -// 1. The can be any type of map/struct, usually a map. -// 2. The should be type of *struct/**struct, which is a pointer to struct object +// 1. The `params` can be any type of map/struct, usually a map. +// 2. The `pointer` should be type of *struct/**struct, which is a pointer to struct object // or struct pointer. // 3. Only the public attributes of struct object can be mapped. -// 4. If is a map, the key of the map can be lowercase. +// 4. If `params` is a map, the key of the map `params` can be lowercase. // It will automatically convert the first letter of the key to uppercase // in mapping procedure to do the matching. // It ignores the map key, if it does not match. @@ -59,7 +59,7 @@ func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]s // doStruct is the core internal converting function for any data to struct. func doStruct(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) { if params == nil { - // If is nil, no conversion. + // If `params` is nil, no conversion. return nil } if pointer == nil { @@ -77,7 +77,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string } }() - // If given is JSON, it then uses json.Unmarshal doing the converting. + // If given `params` is JSON, it then uses json.Unmarshal doing the converting. switch r := params.(type) { case []byte: if json.Valid(r) { @@ -140,7 +140,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string } // It automatically creates struct object if necessary. - // For example, if is **User, then is *User, which is a pointer to User. + // For example, if `pointer` is **User, then `elem` is *User, which is a pointer to User. if pointerElemReflectValue.Kind() == reflect.Ptr { if !pointerElemReflectValue.IsValid() || pointerElemReflectValue.IsNil() { e := reflect.New(pointerElemReflectValue.Type().Elem()).Elem() @@ -249,7 +249,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string // string cases and chars like '-'/'_'/'.'/' '. // Matching the parameters to struct tag names. - // The is the attribute name of the struct. + // The `tagV` is the attribute name of the struct. for attrKey, cmpKey := range tagMap { if strings.EqualFold(checkName, cmpKey) { attrName = attrKey @@ -349,7 +349,7 @@ func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value i return nil, false } -// bindVarToReflectValue sets to reflect value object . +// bindVarToReflectValue sets `value` to reflect value object `structFieldValue`. func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, mapping map[string]string, priorityTag string) (err error) { if err, ok := bindVarToReflectValueWithInterfaceCheck(structFieldValue, value); ok { return err @@ -455,7 +455,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma ) } }() - // It here uses reflect converting to type of the attribute and assigns + // It here uses reflect converting `value` to type of the attribute and assigns // the result value to the attribute. It might fail and panic if the usual Go // conversion rules do not allow conversion. structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type())) diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index a7c294abc..89e220ac4 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -40,14 +40,14 @@ func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string] // doStructs converts any slice to given struct slice. // -// It automatically checks and converts json string to []map if is string/[]byte. +// It automatically checks and converts json string to []map if `params` is string/[]byte. // -// The parameter should be type of pointer to slice of struct. -// Note that if is a pointer to another pointer of type of slice of struct, +// The parameter `pointer` should be type of pointer to slice of struct. +// Note that if `pointer` is a pointer to another pointer of type of slice of struct, // it will create the struct/pointer internally. func doStructs(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) { if params == nil { - // If is nil, no conversion. + // If `params` is nil, no conversion. return nil } if pointer == nil { @@ -68,7 +68,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin } } }() - // If given is JSON, it then uses json.Unmarshal doing the converting. + // If given `params` is JSON, it then uses json.Unmarshal doing the converting. switch r := params.(type) { case []byte: if json.Valid(r) { @@ -99,9 +99,9 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin return gerror.Newf("pointer should be type of pointer, but got: %v", kind) } } - // Converting to map slice. + // Converting `params` to map slice. paramsMaps := Maps(params) - // If is an empty slice, no conversion. + // If `params` is an empty slice, no conversion. if len(paramsMaps) == 0 { return nil } diff --git a/util/gconv/gconv_time.go b/util/gconv/gconv_time.go index 866cd3513..006188985 100644 --- a/util/gconv/gconv_time.go +++ b/util/gconv/gconv_time.go @@ -13,7 +13,7 @@ import ( "github.com/gogf/gf/os/gtime" ) -// Time converts to time.Time. +// Time converts `i` to time.Time. func Time(any interface{}, format ...string) time.Time { // It's already this type. if len(format) == 0 { @@ -27,9 +27,9 @@ func Time(any interface{}, format ...string) time.Time { return time.Time{} } -// Duration converts to time.Duration. -// If is string, then it uses time.ParseDuration to convert it. -// If is numeric, then it converts as nanoseconds. +// Duration converts `i` to time.Duration. +// If `i` is string, then it uses time.ParseDuration to convert it. +// If `i` is numeric, then it converts `i` as nanoseconds. func Duration(any interface{}) time.Duration { // It's already this type. if v, ok := any.(time.Duration); ok { @@ -43,10 +43,10 @@ func Duration(any interface{}) time.Duration { return time.Duration(Int64(any)) } -// GTime converts to *gtime.Time. -// The parameter can be used to specify the format of . -// If no given, it converts using gtime.NewFromTimeStamp if is numeric, -// or using gtime.StrToTime if is string. +// GTime converts `i` to *gtime.Time. +// The parameter `format` can be used to specify the format of `i`. +// If no `format` given, it converts `i` using gtime.NewFromTimeStamp if `i` is numeric, +// or using gtime.StrToTime if `i` is string. func GTime(any interface{}, format ...string) *gtime.Time { if any == nil { return nil diff --git a/util/gconv/gconv_unsafe.go b/util/gconv/gconv_unsafe.go index 0c6152cdf..e4b24fdfc 100644 --- a/util/gconv/gconv_unsafe.go +++ b/util/gconv/gconv_unsafe.go @@ -9,14 +9,14 @@ package gconv import "unsafe" // UnsafeStrToBytes converts string to []byte without memory copy. -// Note that, if you completely sure you will never use variable in the feature, +// Note that, if you completely sure you will never use `s` variable in the feature, // you can use this unsafe function to implement type conversion in high performance. func UnsafeStrToBytes(s string) []byte { return *(*[]byte)(unsafe.Pointer(&s)) } // UnsafeBytesToStr converts []byte to string without memory copy. -// Note that, if you completely sure you will never use variable in the feature, +// Note that, if you completely sure you will never use `b` variable in the feature, // you can use this unsafe function to implement type conversion in high performance. func UnsafeBytesToStr(b []byte) string { return *(*string)(unsafe.Pointer(&b)) diff --git a/util/gconv/gconv_z_unit_maptomap_test.go b/util/gconv/gconv_z_unit_maptomap_test.go index bef736c7d..cd38f80a3 100644 --- a/util/gconv/gconv_z_unit_maptomap_test.go +++ b/util/gconv/gconv_z_unit_maptomap_test.go @@ -143,184 +143,46 @@ func Test_MapToMapDeep(t *testing.T) { }) } -func Test_MapToMaps1(t *testing.T) { +func Test_MapToMaps(t *testing.T) { + params := g.Slice{ + g.Map{"id": 1, "name": "john"}, + g.Map{"id": 2, "name": "smith"}, + } + gtest.C(t, func(t *gtest.T) { + var s []g.Map + err := gconv.MapToMaps(params, &s) + t.AssertNil(err) + t.Assert(len(s), 2) + t.Assert(s, params) + }) + gtest.C(t, func(t *gtest.T) { + var s []*g.Map + err := gconv.MapToMaps(params, &s) + t.AssertNil(err) + t.Assert(len(s), 2) + t.Assert(s, params) + }) +} + +func Test_MapToMaps_StructParams(t *testing.T) { type User struct { Id int - Name int - } - params := g.Map{ - "key1": g.Slice{ - g.Map{"id": 1, "name": "john"}, - g.Map{"id": 2, "name": "smith"}, - }, - "key2": g.Slice{ - g.Map{"id": 3, "name": "green"}, - g.Map{"id": 4, "name": "jim"}, - }, - } - gtest.C(t, func(t *gtest.T) { - m := make(map[string][]User) - err := gconv.MapToMaps(params, &m) - t.Assert(err, nil) - t.Assert(len(m), 2) - t.Assert(m["key1"][0].Id, 1) - t.Assert(m["key1"][1].Id, 2) - t.Assert(m["key2"][0].Id, 3) - t.Assert(m["key2"][1].Id, 4) - }) - gtest.C(t, func(t *gtest.T) { - m := (map[string][]User)(nil) - err := gconv.MapToMaps(params, &m) - t.Assert(err, nil) - t.Assert(len(m), 2) - t.Assert(m["key1"][0].Id, 1) - t.Assert(m["key1"][1].Id, 2) - t.Assert(m["key2"][0].Id, 3) - t.Assert(m["key2"][1].Id, 4) - }) - gtest.C(t, func(t *gtest.T) { - m := make(map[string][]*User) - err := gconv.MapToMaps(params, &m) - t.Assert(err, nil) - t.Assert(len(m), 2) - t.Assert(m["key1"][0].Id, 1) - t.Assert(m["key1"][1].Id, 2) - t.Assert(m["key2"][0].Id, 3) - t.Assert(m["key2"][1].Id, 4) - }) - gtest.C(t, func(t *gtest.T) { - m := (map[string][]*User)(nil) - err := gconv.MapToMaps(params, &m) - t.Assert(err, nil) - t.Assert(len(m), 2) - t.Assert(m["key1"][0].Id, 1) - t.Assert(m["key1"][1].Id, 2) - t.Assert(m["key2"][0].Id, 3) - t.Assert(m["key2"][1].Id, 4) - }) -} - -func Test_MapToMaps2(t *testing.T) { - type User struct { - Id int - Name int - } - params := g.MapIntAny{ - 100: g.Slice{ - g.Map{"id": 1, "name": "john"}, - g.Map{"id": 2, "name": "smith"}, - }, - 200: g.Slice{ - g.Map{"id": 3, "name": "green"}, - g.Map{"id": 4, "name": "jim"}, - }, - } - gtest.C(t, func(t *gtest.T) { - m := make(map[int][]User) - err := gconv.MapToMaps(params, &m) - t.Assert(err, nil) - t.Assert(len(m), 2) - t.Assert(m[100][0].Id, 1) - t.Assert(m[100][1].Id, 2) - t.Assert(m[200][0].Id, 3) - t.Assert(m[200][1].Id, 4) - }) - gtest.C(t, func(t *gtest.T) { - m := make(map[int][]*User) - err := gconv.MapToMaps(params, &m) - t.Assert(err, nil) - t.Assert(len(m), 2) - t.Assert(m[100][0].Id, 1) - t.Assert(m[100][1].Id, 2) - t.Assert(m[200][0].Id, 3) - t.Assert(m[200][1].Id, 4) - }) - gtest.C(t, func(t *gtest.T) { - m := make(map[string][]*User) - err := gconv.MapToMaps(params, &m) - t.Assert(err, nil) - t.Assert(len(m), 2) - t.Assert(m["100"][0].Id, 1) - t.Assert(m["100"][1].Id, 2) - t.Assert(m["200"][0].Id, 3) - t.Assert(m["200"][1].Id, 4) - }) -} - -func Test_MapToMaps3(t *testing.T) { - type Ids struct { - Id int - Uid int - } - type Base struct { - Ids - Time string - } - type User struct { - Base Name string } - params := g.MapIntAny{ - 100: g.Slice{ - g.Map{"id": 1, "name": "john"}, - g.Map{"id": 2, "name": "smith"}, - }, - 200: g.Slice{ - g.Map{"id": 3, "name": "green"}, - g.Map{"id": 4, "name": "jim"}, - }, + params := g.Slice{ + User{1, "name1"}, + User{2, "name2"}, } gtest.C(t, func(t *gtest.T) { - m := make(map[string][]*User) - err := gconv.MapToMaps(params, &m) - t.Assert(err, nil) - t.Assert(len(m), 2) - t.Assert(m["100"][0].Id, 1) - t.Assert(m["100"][1].Id, 2) - t.Assert(m["100"][0].Name, "john") - t.Assert(m["100"][1].Name, "smith") - t.Assert(m["200"][0].Id, 3) - t.Assert(m["200"][1].Id, 4) - t.Assert(m["200"][0].Name, "green") - t.Assert(m["200"][1].Name, "jim") + var s []g.Map + err := gconv.MapToMaps(params, &s) + t.AssertNil(err) + t.Assert(len(s), 2) }) -} - -func Test_MapToMapsWithTag(t *testing.T) { - type Ids struct { - Id int - Uid int - } - type Base struct { - Ids `json:"ids"` - Time string - } - type User struct { - Base `json:"base"` - Name string - } - params := g.MapIntAny{ - 100: g.Slice{ - g.Map{"id": 1, "name": "john"}, - g.Map{"id": 2, "name": "smith"}, - }, - 200: g.Slice{ - g.Map{"id": 3, "name": "green"}, - g.Map{"id": 4, "name": "jim"}, - }, - } gtest.C(t, func(t *gtest.T) { - m := make(map[string][]*User) - err := gconv.MapToMaps(params, &m) - t.Assert(err, nil) - t.Assert(len(m), 2) - t.Assert(m["100"][0].Id, 1) - t.Assert(m["100"][1].Id, 2) - t.Assert(m["100"][0].Name, "john") - t.Assert(m["100"][1].Name, "smith") - t.Assert(m["200"][0].Id, 3) - t.Assert(m["200"][1].Id, 4) - t.Assert(m["200"][0].Name, "green") - t.Assert(m["200"][1].Name, "jim") + var s []*g.Map + err := gconv.MapToMaps(params, &s) + t.AssertNil(err) + t.Assert(len(s), 2) }) } diff --git a/util/gconv/gconv_z_unit_scan_test.go b/util/gconv/gconv_z_unit_scan_test.go index 18027ae7d..d80448818 100644 --- a/util/gconv/gconv_z_unit_scan_test.go +++ b/util/gconv/gconv_z_unit_scan_test.go @@ -13,7 +13,7 @@ import ( "testing" ) -func Test_Scan(t *testing.T) { +func Test_Scan_StructStructs(t *testing.T) { type User struct { Uid int Name string @@ -76,7 +76,7 @@ func Test_Scan(t *testing.T) { }) } -func Test_ScanStr(t *testing.T) { +func Test_Scan_StructStr(t *testing.T) { type User struct { Uid int Name string @@ -123,3 +123,73 @@ func Test_ScanStr(t *testing.T) { }) }) } + +func Test_Scan_Map(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var m map[string]string + data := g.Map{ + "k1": "v1", + "k2": "v2", + } + err := gconv.Scan(data, &m) + t.AssertNil(err) + t.Assert(data, m) + }) + gtest.C(t, func(t *gtest.T) { + var m map[int]int + data := g.Map{ + "1": "11", + "2": "22", + } + err := gconv.Scan(data, &m) + t.AssertNil(err) + t.Assert(data, m) + }) + // json string parameter. + gtest.C(t, func(t *gtest.T) { + var m map[string]string + data := `{"k1":"v1","k2":"v2"}` + err := gconv.Scan(data, &m) + t.AssertNil(err) + t.Assert(m, g.Map{ + "k1": "v1", + "k2": "v2", + }) + }) +} + +func Test_Scan_Maps(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var maps []map[string]string + data := g.Slice{ + g.Map{ + "k1": "v1", + "k2": "v2", + }, + g.Map{ + "k3": "v3", + "k4": "v4", + }, + } + err := gconv.Scan(data, &maps) + t.AssertNil(err) + t.Assert(data, maps) + }) + // json string parameter. + gtest.C(t, func(t *gtest.T) { + var maps []map[string]string + data := `[{"k1":"v1","k2":"v2"},{"k3":"v3","k4":"v4"}]` + err := gconv.Scan(data, &maps) + t.AssertNil(err) + t.Assert(maps, g.Slice{ + g.Map{ + "k1": "v1", + "k2": "v2", + }, + g.Map{ + "k3": "v3", + "k4": "v4", + }, + }) + }) +} diff --git a/util/gpage/gpage.go b/util/gpage/gpage.go index de0ac1259..ef9b9e2d3 100644 --- a/util/gpage/gpage.go +++ b/util/gpage/gpage.go @@ -21,9 +21,9 @@ type Page struct { TotalPage int // Total page, which is automatically calculated. CurrentPage int // Current page number >= 1. UrlTemplate string // Custom url template for page url producing. - LinkStyle string // CSS style name for HTML link tag . - SpanStyle string // CSS style name for HTML span tag , which is used for first, current and last page tag. - SelectStyle string // CSS style name for HTML select tag 页`) - t.Assert(page.GetContent(3), `首页上一页12345下一页尾页当前页2/5 共5条`) + t.Assert(page.GetContent(3), `首页上一页12345下一页尾页`span`当前页2/5 `span`共5条`) t.Assert(page.GetContent(4), `首页上一页12345下一页尾页`) t.Assert(page.GetContent(5), ``) }) diff --git a/util/grand/grand.go b/util/grand/grand.go index 8da9dbcd1..63a71618d 100644 --- a/util/grand/grand.go +++ b/util/grand/grand.go @@ -23,8 +23,8 @@ var ( // Intn returns a int number which is between 0 and max: [0, max). // // Note that: -// 1. The can only be greater than 0, or else it returns directly; -// 2. The result is greater than or equal to 0, but less than ; +// 1. The `max` can only be greater than 0, or else it returns `max` directly; +// 2. The result is greater than or equal to 0, but less than `max`; // 3. The result number is 32bit and less than math.MaxUint32. func Intn(max int) int { if max <= 0 { @@ -37,7 +37,7 @@ func Intn(max int) int { return n } -// B retrieves and returns random bytes of given length . +// B retrieves and returns random bytes of given length `n`. func B(n int) []byte { if n <= 0 { return nil @@ -55,7 +55,7 @@ func B(n int) []byte { } // N returns a random int between min and max: [min, max]. -// The and also support negative numbers. +// The `min` and `max` also support negative numbers. func N(min, max int) int { if min >= max { return min @@ -77,8 +77,8 @@ func N(min, max int) int { return 0 } -// S returns a random string which contains digits and letters, and its length is . -// The optional parameter specifies whether the result could contain symbols, +// S returns a random string which contains digits and letters, and its length is `n`. +// The optional parameter `symbols` specifies whether the result could contain symbols, // which is false in default. func S(n int, symbols ...bool) string { if n <= 0 { @@ -112,7 +112,7 @@ func D(min, max time.Duration) time.Duration { return time.Duration(n * multiple) } -// Str randomly picks and returns count of chars from given string . +// Str randomly picks and returns `n` count of chars from given string `s`. // It also supports unicode string like Chinese/Russian/Japanese, etc. func Str(s string, n int) string { if n <= 0 { @@ -135,7 +135,7 @@ func Str(s string, n int) string { return string(b) } -// Digits returns a random string which contains only digits, and its length is . +// Digits returns a random string which contains only digits, and its length is `n`. func Digits(n int) string { if n <= 0 { return "" @@ -150,7 +150,7 @@ func Digits(n int) string { return *(*string)(unsafe.Pointer(&b)) } -// Letters returns a random string which contains only letters, and its length is . +// Letters returns a random string which contains only letters, and its length is `n`. func Letters(n int) string { if n <= 0 { return "" @@ -165,7 +165,7 @@ func Letters(n int) string { return *(*string)(unsafe.Pointer(&b)) } -// Symbols returns a random string which contains only symbols, and its length is . +// Symbols returns a random string which contains only symbols, and its length is `n`. func Symbols(n int) string { if n <= 0 { return "" @@ -192,7 +192,7 @@ func Perm(n int) []int { return m } -// Meet randomly calculate whether the given probability / is met. +// Meet randomly calculate whether the given probability `num`/`total` is met. func Meet(num, total int) bool { return Intn(total) < num } diff --git a/util/guid/guid_string.go b/util/guid/guid_string.go index 61916da2c..2927b9e62 100644 --- a/util/guid/guid_string.go +++ b/util/guid/guid_string.go @@ -50,11 +50,11 @@ func init() { // S creates and returns a global unique string in 32 bytes that meets most common // usages without strict UUID algorithm. It returns an unique string using default -// unique algorithm if no is given. +// unique algorithm if no `data` is given. // -// The specified can be no more than 2 parts. No matter how long each of the -// size is, each of them will be hashed into 7 bytes as part of the result. -// If given parts is less than 2, the leftover size of the result bytes will +// The specified `data` can be no more than 2 parts. No matter how long each of the +// `data` size is, each of them will be hashed into 7 bytes as part of the result. +// If given `data` parts is less than 2, the leftover size of the result bytes will // be token by random string. // // The returned string is composed with: @@ -63,7 +63,7 @@ func init() { // // Note that: // 1. The returned length is fixed to 32 bytes for performance purpose. -// 2. The custom parameter composed should have unique attribute in your +// 2. The custom parameter `data` composed should have unique attribute in your // business situation. func S(data ...[]byte) string { var ( @@ -103,7 +103,7 @@ func getSequence() []byte { return b } -// getRandomStr randomly picks and returns count of chars from randomStrBase. +// getRandomStr randomly picks and returns `n` count of chars from randomStrBase. func getRandomStr(n int) []byte { if n <= 0 { return []byte{} diff --git a/util/gutil/gutil.go b/util/gutil/gutil.go index 9736016cf..38716cdf9 100644 --- a/util/gutil/gutil.go +++ b/util/gutil/gutil.go @@ -32,7 +32,7 @@ func Try(try func()) (err error) { } // TryCatch implements try...catch... logistics using internal panic...recover. -// It automatically calls function if any exception occurs ans passes the exception as an error. +// It automatically calls function `catch` if any exception occurs ans passes the exception as an error. func TryCatch(try func(), catch ...func(exception error)) { defer func() { if exception := recover(); exception != nil && len(catch) > 0 { @@ -46,8 +46,8 @@ func TryCatch(try func(), catch ...func(exception error)) { try() } -// IsEmpty checks given empty or not. -// It returns false if is: integer(0), bool(false), slice/map(len=0), nil; +// IsEmpty checks given `value` empty or not. +// It returns false if `value` is: integer(0), bool(false), slice/map(len=0), nil; // or else returns true. func IsEmpty(value interface{}) bool { return empty.IsEmpty(value) diff --git a/util/gutil/gutil_list.go b/util/gutil/gutil_list.go index 0fa22e7e7..14fd8e2f6 100644 --- a/util/gutil/gutil_list.go +++ b/util/gutil/gutil_list.go @@ -10,16 +10,16 @@ import ( "reflect" ) -// ListItemValues retrieves and returns the elements of all item struct/map with key . -// Note that the parameter should be type of slice which contains elements of map or struct, +// ListItemValues retrieves and returns the elements of all item struct/map with key `key`. +// Note that the parameter `list` should be type of slice which contains elements of map or struct, // or else it returns an empty slice. // -// The parameter supports types like: +// The parameter `list` supports types like: // []map[string]interface{} // []map[string]sub-map // []struct // []struct:sub-struct -// Note that the sub-map/sub-struct makes sense only if the optional parameter is given. +// Note that the sub-map/sub-struct makes sense only if the optional parameter `subKey` is given. func ListItemValues(list interface{}, key interface{}, subKey ...interface{}) (values []interface{}) { var reflectValue reflect.Value if v, ok := list.(reflect.Value); ok { @@ -58,8 +58,8 @@ func ListItemValues(list interface{}, key interface{}, subKey ...interface{}) (v return } -// ItemValue retrieves and returns its value of which name/attribute specified by . -// The parameter can be type of map/*map/struct/*struct. +// ItemValue retrieves and returns its value of which name/attribute specified by `key`. +// The parameter `item` can be type of map/*map/struct/*struct. func ItemValue(item interface{}, key interface{}) (value interface{}, found bool) { var reflectValue reflect.Value if v, ok := item.(reflect.Value); ok { @@ -84,7 +84,7 @@ func ItemValue(item interface{}, key interface{}) (value interface{}, found bool } switch reflectKind { case reflect.Array, reflect.Slice: - // The must be type of string. + // The `key` must be type of string. values := ListItemValues(reflectValue, keyValue.String()) if values == nil { return nil, false @@ -99,7 +99,7 @@ func ItemValue(item interface{}, key interface{}) (value interface{}, found bool } case reflect.Struct: - // The must be type of string. + // The `mapKey` must be type of string. v := reflectValue.FieldByName(keyValue.String()) if v.IsValid() { found = true @@ -109,8 +109,8 @@ func ItemValue(item interface{}, key interface{}) (value interface{}, found bool return } -// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key . -// Note that the parameter should be type of slice which contains elements of map or struct, +// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key `key`. +// Note that the parameter `list` should be type of slice which contains elements of map or struct, // or else it returns an empty slice. func ListItemValuesUnique(list interface{}, key string, subKey ...interface{}) []interface{} { values := ListItemValues(list, key, subKey...) diff --git a/util/gutil/gutil_map.go b/util/gutil/gutil_map.go index 2761c9d43..8516b0035 100644 --- a/util/gutil/gutil_map.go +++ b/util/gutil/gutil_map.go @@ -11,7 +11,7 @@ import ( "reflect" ) -// MapCopy does a shallow copy from map to for most commonly used map type +// MapCopy does a shallow copy from map `data` to `copy` for most commonly used map type // map[string]interface{}. func MapCopy(data map[string]interface{}) (copy map[string]interface{}) { copy = make(map[string]interface{}, len(data)) @@ -21,7 +21,7 @@ func MapCopy(data map[string]interface{}) (copy map[string]interface{}) { return } -// MapContains checks whether map contains . +// MapContains checks whether map `data` contains `key`. func MapContains(data map[string]interface{}, key string) (ok bool) { if len(data) == 0 { return @@ -30,7 +30,7 @@ func MapContains(data map[string]interface{}, key string) (ok bool) { return } -// MapDelete deletes all from map . +// MapDelete deletes all `keys` from map `data`. func MapDelete(data map[string]interface{}, keys ...string) { if len(data) == 0 { return @@ -40,7 +40,7 @@ func MapDelete(data map[string]interface{}, keys ...string) { } } -// MapMerge merges all map from to map . +// MapMerge merges all map from `src` to map `dst`. func MapMerge(dst map[string]interface{}, src ...map[string]interface{}) { if dst == nil { return @@ -52,7 +52,7 @@ func MapMerge(dst map[string]interface{}, src ...map[string]interface{}) { } } -// MapMergeCopy creates and returns a new map which merges all map from . +// MapMergeCopy creates and returns a new map which merges all map from `src`. func MapMergeCopy(src ...map[string]interface{}) (copy map[string]interface{}) { copy = make(map[string]interface{}) for _, m := range src { @@ -82,7 +82,7 @@ func MapPossibleItemByKey(data map[string]interface{}, key string) (foundKey str return "", nil } -// MapContainsPossibleKey checks if the given is contained in given map . +// MapContainsPossibleKey checks if the given `key` is contained in given map `data`. // It checks the key ignoring cases and symbols. // // Note that this function might be of low performance. diff --git a/util/gutil/gutil_slice.go b/util/gutil/gutil_slice.go index d83d79c8c..c2a183495 100644 --- a/util/gutil/gutil_slice.go +++ b/util/gutil/gutil_slice.go @@ -11,7 +11,7 @@ import ( "reflect" ) -// SliceCopy does a shallow copy of slice for most commonly used slice type +// SliceCopy does a shallow copy of slice `data` for most commonly used slice type // []interface{}. func SliceCopy(data []interface{}) []interface{} { newData := make([]interface{}, len(data)) @@ -19,8 +19,8 @@ func SliceCopy(data []interface{}) []interface{} { return newData } -// SliceDelete deletes an element at and returns the new slice. -// It does nothing if the given is invalid. +// SliceDelete deletes an element at `index` and returns the new slice. +// It does nothing if the given `index` is invalid. func SliceDelete(data []interface{}, index int) (newSlice []interface{}) { if index < 0 || index >= len(data) { return data diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 136b37d87..42351d218 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -163,12 +163,12 @@ var ( // Check checks single value with specified rules. // It returns nil if successful validation. // -// The parameter can be any type of variable, which will be converted to string +// The parameter `value` can be any type of variable, which will be converted to string // for validation. -// The parameter can be one or more rules, multiple rules joined using char '|'. -// The parameter specifies the custom error messages, which can be type of: +// The parameter `rules` can be one or more rules, multiple rules joined using char '|'. +// The parameter `messages` specifies the custom error messages, which can be type of: // string/map/struct/*struct. -// The optional parameter specifies the extra validation parameters for some rules +// The optional parameter `params` specifies the extra validation parameters for some rules // like: required-*、same、different, etc. func Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error { return defaultValidator.Check(value, rules, messages, params...) @@ -176,19 +176,19 @@ func Check(value interface{}, rules string, messages interface{}, params ...inte // CheckMap validates map and returns the error result. It returns nil if with successful validation. // -// The parameter can be type of []string/map[string]string. It supports sequence in error result -// if is type of []string. -// The optional parameter specifies the custom error messages for specified keys and rules. +// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result +// if `rules` is type of []string. +// The optional parameter `messages` specifies the custom error messages for specified keys and rules. func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error { return defaultValidator.CheckMap(params, rules, messages...) } // CheckStruct validates strcut and returns the error result. // -// The parameter should be type of struct/*struct. -// The parameter can be type of []string/map[string]string. It supports sequence in error result -// if is type of []string. -// The optional parameter specifies the custom error messages for specified keys and rules. +// The parameter `object` should be type of struct/*struct. +// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result +// if `rules` is type of []string. +// The optional parameter `messages` specifies the custom error messages for specified keys and rules. func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error { return defaultValidator.CheckStruct(object, rules, messages...) } diff --git a/util/gvalid/gvalid_custom_rule.go b/util/gvalid/gvalid_custom_rule.go index df098a3e3..83fd4c2bb 100644 --- a/util/gvalid/gvalid_custom_rule.go +++ b/util/gvalid/gvalid_custom_rule.go @@ -7,10 +7,10 @@ package gvalid // RuleFunc is the custom function for data validation. -// The parameter specifies the validation rule string, like "required", "between:1,100", etc. -// The parameter specifies the value for this rule to validate. -// The parameter specifies the custom error message or configured i18n message for this rule. -// The parameter specifies all the parameters that needs. You can ignore parameter if +// The parameter `rule` specifies the validation rule string, like "required", "between:1,100", etc. +// The parameter `value` specifies the value for this rule to validate. +// The parameter `message` specifies the custom error message or configured i18n message for this rule. +// The parameter `params` specifies all the parameters that needs. You can ignore parameter `params` if // you do not really need it in your custom validation rule. type RuleFunc func(rule string, value interface{}, message string, params map[string]interface{}) error diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index 674f5d761..3b05e88bd 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -21,12 +21,12 @@ import ( // Check checks single value with specified rules. // It returns nil if successful validation. // -// The parameter can be any type of variable, which will be converted to string +// The parameter `value` can be any type of variable, which will be converted to string // for validation. -// The parameter can be one or more rules, multiple rules joined using char '|'. -// The parameter specifies the custom error messages, which can be type of: +// The parameter `rules` can be one or more rules, multiple rules joined using char '|'. +// The parameter `messages` specifies the custom error messages, which can be type of: // string/map/struct/*struct. -// The optional parameter specifies the extra validation parameters for some rules +// The optional parameter `params` specifies the extra validation parameters for some rules // like: required-*、same、different, etc. func (v *Validator) Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error { return v.doCheck("", value, rules, messages, params...) diff --git a/util/gvalid/gvalid_validator_check_map.go b/util/gvalid/gvalid_validator_check_map.go index 195253743..3229e2752 100644 --- a/util/gvalid/gvalid_validator_check_map.go +++ b/util/gvalid/gvalid_validator_check_map.go @@ -13,9 +13,9 @@ import ( // CheckMap validates map and returns the error result. It returns nil if with successful validation. // -// The parameter can be type of []string/map[string]string. It supports sequence in error result -// if is type of []string. -// The optional parameter specifies the custom error messages for specified keys and rules. +// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result +// if `rules` is type of []string. +// The optional parameter `messages` specifies the custom error messages for specified keys and rules. func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error { // If there's no validation rules, it does nothing and returns quickly. if params == nil || rules == nil { diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index f56c54177..3180806f4 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -20,10 +20,10 @@ var ( // CheckStruct validates struct and returns the error result. // -// The parameter should be type of struct/*struct. -// The parameter can be type of []string/map[string]string. It supports sequence in error result -// if is type of []string. -// The optional parameter specifies the custom error messages for specified keys and rules. +// The parameter `object` should be type of struct/*struct. +// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result +// if `rules` is type of []string. +// The optional parameter `messages` specifies the custom error messages for specified keys and rules. func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error { var ( errorMaps = make(ErrorMap) // Returned error. @@ -56,7 +56,7 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages params = make(map[string]interface{}) checkRules = make(map[string]string) customMessage = make(CustomMsg) - fieldAliases = make(map[string]string) // Alias names for overwriting struct tag names. + fieldAliases = make(map[string]string) // Alias names for `messages` overwriting struct tag names. errorRules = make([]string, 0) // Sequence rules. ) switch v := rules.(type) { @@ -159,7 +159,7 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages } // Custom error messages, - // which have the most priority than and struct tag. + // which have the most priority than `rules` and struct tag. if len(messages) > 0 && len(messages[0]) > 0 { for k, v := range messages[0] { if a, ok := fieldAliases[k]; ok { diff --git a/util/gvalid/gvalid_validator_rule_length.go b/util/gvalid/gvalid_validator_rule_length.go index df0f36189..33edfa97a 100644 --- a/util/gvalid/gvalid_validator_rule_length.go +++ b/util/gvalid/gvalid_validator_rule_length.go @@ -12,7 +12,7 @@ import ( "strings" ) -// checkLength checks using length rules. +// checkLength checks `value` using length rules. // The length is calculated using unicode string, which means one chinese character or letter // both has the length of 1. func (v *Validator) checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) string { diff --git a/util/gvalid/gvalid_validator_rule_luhn.go b/util/gvalid/gvalid_validator_rule_luhn.go index d6eff5fd1..9d743535d 100644 --- a/util/gvalid/gvalid_validator_rule_luhn.go +++ b/util/gvalid/gvalid_validator_rule_luhn.go @@ -6,7 +6,7 @@ package gvalid -// checkLuHn checks with LUHN algorithm. +// checkLuHn checks `value` with LUHN algorithm. // It's usually used for bank card number validation. func (v *Validator) checkLuHn(value string) bool { var ( diff --git a/util/gvalid/gvalid_validator_rule_range.go b/util/gvalid/gvalid_validator_rule_range.go index 7730cd610..71627eb53 100644 --- a/util/gvalid/gvalid_validator_rule_range.go +++ b/util/gvalid/gvalid_validator_rule_range.go @@ -11,7 +11,7 @@ import ( "strings" ) -// checkRange checks using range rules. +// checkRange checks `value` using range rules. func (v *Validator) checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) string { msg := "" switch ruleKey { diff --git a/util/gvalid/gvalid_validator_rule_required.go b/util/gvalid/gvalid_validator_rule_required.go index 162c97a57..c86e1c8fd 100644 --- a/util/gvalid/gvalid_validator_rule_required.go +++ b/util/gvalid/gvalid_validator_rule_required.go @@ -12,7 +12,7 @@ import ( "strings" ) -// checkRequired checks using required rules. +// checkRequired checks `value` using required rules. // It also supports require checks for `value` of type: slice, map. func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string, params map[string]string) bool { required := false From 6deb817fd030b2f7f37a3300a5f656a99b690855 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 23 Mar 2021 17:58:09 +0800 Subject: [PATCH 201/492] fix issue in unit testing case for package gpage --- util/gpage/gpage_unit_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/gpage/gpage_unit_test.go b/util/gpage/gpage_unit_test.go index 8061f80ae..d2a12b1f6 100644 --- a/util/gpage/gpage_unit_test.go +++ b/util/gpage/gpage_unit_test.go @@ -109,7 +109,7 @@ func Test_PredefinedContent(t *testing.T) { page.AjaxActionName = "LoadPage" t.Assert(page.GetContent(1), `上一页 2 下一页`) t.Assert(page.GetContent(2), `首页<<上一页[第2页]下一页>>尾页页`) - t.Assert(page.GetContent(3), `首页上一页12345下一页尾页`span`当前页2/5 `span`共5条`) + t.Assert(page.GetContent(3), `首页上一页12345下一页尾页当前页2/5 共5条`) t.Assert(page.GetContent(4), `首页上一页12345下一页尾页`) t.Assert(page.GetContent(5), ``) }) From 54395b39a6fbd7d49e3dd223bd8350ea187da701 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 24 Mar 2021 21:19:23 +0800 Subject: [PATCH 202/492] fix issue #1202 --- database/gdb/gdb_z_mysql_struct_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index 9766b4291..b1b7e7efe 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -363,6 +363,30 @@ func Test_Model_Scan_CustomType_Time(t *testing.T) { }) } +func Test_Model_Scan_CustomType_String(t *testing.T) { + type MyString string + + type MyStringSt struct { + Passport MyString + } + + table := createInitTable() + defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + st := new(MyStringSt) + err := db.Model(table).Fields("Passport").WherePri(1).Scan(st) + t.AssertNil(err) + t.Assert(st.Passport, "user_1") + }) + gtest.C(t, func(t *gtest.T) { + var sts []MyStringSt + err := db.Model(table).Fields("Passport").Order("id asc").Scan(&sts) + t.AssertNil(err) + t.Assert(len(sts), TableSize) + t.Assert(sts[0], "user_1") + }) +} + type User struct { Id int Passport string From bb6883213fc2aa695cbd00c2048f6b4364712cfb Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 24 Mar 2021 22:04:04 +0800 Subject: [PATCH 203/492] fix issue #1202 --- database/gdb/gdb_type_result.go | 2 +- database/gdb/gdb_z_mysql_struct_test.go | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/database/gdb/gdb_type_result.go b/database/gdb/gdb_type_result.go index 776cb0657..a5800ece9 100644 --- a/database/gdb/gdb_type_result.go +++ b/database/gdb/gdb_type_result.go @@ -189,5 +189,5 @@ func (r Result) RecordKeyUint(key string) map[uint]Record { // Structs converts `r` to struct slice. // Note that the parameter `pointer` should be type of *[]struct/*[]*struct. func (r Result) Structs(pointer interface{}) (err error) { - return gconv.StructsTag(r, pointer, OrmTagForStruct) + return gconv.StructsTag(r.List(), pointer, OrmTagForStruct) } diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index b1b7e7efe..67a1858cb 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -8,7 +8,6 @@ package gdb_test import ( "database/sql" - "github.com/gogf/gf/database/gdb" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" @@ -383,7 +382,7 @@ func Test_Model_Scan_CustomType_String(t *testing.T) { err := db.Model(table).Fields("Passport").Order("id asc").Scan(&sts) t.AssertNil(err) t.Assert(len(sts), TableSize) - t.Assert(sts[0], "user_1") + t.Assert(sts[0].Passport, "user_1") }) } @@ -398,11 +397,11 @@ type User struct { func (user *User) UnmarshalValue(value interface{}) error { switch result := value.(type) { case map[string]interface{}: - user.Id = result["id"].(gdb.Value).Int() - user.Passport = result["passport"].(gdb.Value).String() + user.Id = result["id"].(int) + user.Passport = result["passport"].(string) user.Password = "" - user.Nickname = result["nickname"].(gdb.Value).String() - user.CreateTime = result["create_time"].(gdb.Value).GTime() + user.Nickname = result["nickname"].(string) + user.CreateTime = gtime.New(result["create_time"]) return nil default: return gconv.Struct(value, user) From 7c51ff47075625fbf56a0a5c18ffb30de7e55557 Mon Sep 17 00:00:00 2001 From: yys <4752017@qq.com> Date: Thu, 25 Mar 2021 13:38:59 +0800 Subject: [PATCH 204/492] =?UTF-8?q?gtime=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/gtime/gtime_sql.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/os/gtime/gtime_sql.go b/os/gtime/gtime_sql.go index 142448d98..dc810ffe9 100644 --- a/os/gtime/gtime_sql.go +++ b/os/gtime/gtime_sql.go @@ -13,5 +13,8 @@ func (t *Time) Scan(value interface{}) error { //add valuer func (t *Time) Value() (driver.Value, error) { + if t == nil { + return nil, nil + } return t.Time, nil } From 93a01a1aafccbd88641a1a300322ca02041e0f11 Mon Sep 17 00:00:00 2001 From: Zh1Cheung <945503088@qq.com> Date: Sat, 27 Mar 2021 16:21:28 +0800 Subject: [PATCH 205/492] bug:The parameter of the New method is a pointer --- .example/database/gredis/gredis.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.example/database/gredis/gredis.go b/.example/database/gredis/gredis.go index 0ba2a4b2c..a276da6f9 100644 --- a/.example/database/gredis/gredis.go +++ b/.example/database/gredis/gredis.go @@ -9,10 +9,11 @@ import ( // 使用原生gredis.New操作redis,但是注意需要自己调用Close方法关闭redis链接池 func main() { - redis := gredis.New(gredis.Config{ + config := &gredis.Config{ Host: "127.0.0.1", Port: 6379, - }) + } + redis := gredis.New(config) defer redis.Close() redis.Do("SET", "k", "v") v, _ := redis.Do("GET", "k") From 473fdba14f440c9bbd06e2cb2beb81531f2a4ab9 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 29 Mar 2021 15:51:03 +0800 Subject: [PATCH 206/492] add replace in go.mod for otel --- go.mod | 13 ++++++++++--- go.sum | 13 ++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index bb4169602..c23bbc639 100644 --- a/go.mod +++ b/go.mod @@ -12,10 +12,17 @@ require ( github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf github.com/mattn/go-runewidth v0.0.10 // indirect github.com/olekukonko/tablewriter v0.0.1 - go.opentelemetry.io/otel v0.18.0 - go.opentelemetry.io/otel/oteltest v0.18.0 - go.opentelemetry.io/otel/trace v0.18.0 + go.opentelemetry.io/otel v0.19.0 + go.opentelemetry.io/otel/metric v0.19.0 // indirect + go.opentelemetry.io/otel/oteltest v0.19.0 + go.opentelemetry.io/otel/trace v0.19.0 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 golang.org/x/text v0.3.4 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c ) + +replace ( + go.opentelemetry.io/otel => go.opentelemetry.io/otel v0.18.0 + go.opentelemetry.io/otel/oteltest => go.opentelemetry.io/otel/oteltest v0.18.0 + go.opentelemetry.io/otel/trace => go.opentelemetry.io/otel/trace v0.18.0 +) diff --git a/go.sum b/go.sum index 2f20ad630..6de4225b3 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,9 @@ github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gG github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= @@ -29,12 +30,18 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/otel v0.18.0 h1:d5Of7+Zw4ANFOJB+TIn2K3QWsgS2Ht7OU9DqZHI6qu8= go.opentelemetry.io/otel v0.18.0/go.mod h1:PT5zQj4lTsR1YeARt8YNKcFb88/c2IKoSABK9mX0r78= -go.opentelemetry.io/otel/metric v0.18.0 h1:yuZCmY9e1ZTaMlZXLrrbAPmYW6tW1A5ozOZeOYGaTaY= +go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng= +go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= go.opentelemetry.io/otel/metric v0.18.0/go.mod h1:kEH2QtzAyBy3xDVQfGZKIcok4ZZFvd5xyKPfPcuK6pE= -go.opentelemetry.io/otel/oteltest v0.18.0 h1:FbKDFm/LnQDOHuGjED+fy3s5YMVg0z019GJ9Er66hYo= +go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg= +go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= go.opentelemetry.io/otel/oteltest v0.18.0/go.mod h1:NyierCU3/G8DLTva7KRzGii2fdxdR89zXKH1bNWY7Bo= +go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q= +go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= go.opentelemetry.io/otel/trace v0.18.0 h1:ilCfc/fptVKaDMK1vWk0elxpolurJbEgey9J6g6s+wk= go.opentelemetry.io/otel/trace v0.18.0/go.mod h1:FzdUu3BPwZSZebfQ1vl5/tAa8LyMLXSJN57AXIt/iDk= +go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc= +go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= From 6f1340ce361a576bba5154574c809435f9d5a91d Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 29 Mar 2021 16:00:56 +0800 Subject: [PATCH 207/492] upgrade otel from v0.18.0 to v0.19.0 --- go.mod | 7 ------- go.sum | 7 ------- net/gtrace/gtrace.go | 4 ++-- os/glog/glog_logger.go | 2 +- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index c23bbc639..b27eb6035 100644 --- a/go.mod +++ b/go.mod @@ -13,16 +13,9 @@ require ( github.com/mattn/go-runewidth v0.0.10 // indirect github.com/olekukonko/tablewriter v0.0.1 go.opentelemetry.io/otel v0.19.0 - go.opentelemetry.io/otel/metric v0.19.0 // indirect go.opentelemetry.io/otel/oteltest v0.19.0 go.opentelemetry.io/otel/trace v0.19.0 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 golang.org/x/text v0.3.4 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c ) - -replace ( - go.opentelemetry.io/otel => go.opentelemetry.io/otel v0.18.0 - go.opentelemetry.io/otel/oteltest => go.opentelemetry.io/otel/oteltest v0.18.0 - go.opentelemetry.io/otel/trace => go.opentelemetry.io/otel/trace v0.18.0 -) diff --git a/go.sum b/go.sum index 6de4225b3..f936b51f4 100644 --- a/go.sum +++ b/go.sum @@ -10,7 +10,6 @@ github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gG github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= @@ -28,18 +27,12 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v0.18.0 h1:d5Of7+Zw4ANFOJB+TIn2K3QWsgS2Ht7OU9DqZHI6qu8= -go.opentelemetry.io/otel v0.18.0/go.mod h1:PT5zQj4lTsR1YeARt8YNKcFb88/c2IKoSABK9mX0r78= go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng= go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= -go.opentelemetry.io/otel/metric v0.18.0/go.mod h1:kEH2QtzAyBy3xDVQfGZKIcok4ZZFvd5xyKPfPcuK6pE= go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg= go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= -go.opentelemetry.io/otel/oteltest v0.18.0/go.mod h1:NyierCU3/G8DLTva7KRzGii2fdxdR89zXKH1bNWY7Bo= go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q= go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= -go.opentelemetry.io/otel/trace v0.18.0 h1:ilCfc/fptVKaDMK1vWk0elxpolurJbEgey9J6g6s+wk= -go.opentelemetry.io/otel/trace v0.18.0/go.mod h1:FzdUu3BPwZSZebfQ1vl5/tAa8LyMLXSJN57AXIt/iDk= go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc= go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index 6dadbf83e..34d906cee 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -85,7 +85,7 @@ func GetTraceId(ctx context.Context) string { if ctx == nil { return "" } - traceId := trace.SpanContextFromContext(ctx).TraceID + traceId := trace.SpanContextFromContext(ctx).TraceID() if traceId.IsValid() { return traceId.String() } @@ -98,7 +98,7 @@ func GetSpanId(ctx context.Context) string { if ctx == nil { return "" } - spanId := trace.SpanContextFromContext(ctx).SpanID + spanId := trace.SpanContextFromContext(ctx).SpanID() if spanId.IsValid() { return spanId.String() } diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 47cda36b0..59d3d4f88 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -166,7 +166,7 @@ func (l *Logger) print(std io.Writer, lead string, values ...interface{}) { if l.ctx != nil { // Tracing values. spanCtx := trace.SpanContextFromContext(l.ctx) - if traceId := spanCtx.TraceID; traceId.IsValid() { + if traceId := spanCtx.TraceID(); traceId.IsValid() { buffer.WriteString(fmt.Sprintf("{TraceID:%s} ", traceId.String())) } // Context values. From ae5ecb5bfa925a2628162de0e2a5ce6360eb0f65 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 29 Mar 2021 16:12:14 +0800 Subject: [PATCH 208/492] update unit testing case for package gtrace --- net/gtrace/gtrace_unit_carrier_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/gtrace/gtrace_unit_carrier_test.go b/net/gtrace/gtrace_unit_carrier_test.go index 85720a3ba..f67e85ae0 100644 --- a/net/gtrace/gtrace_unit_carrier_test.go +++ b/net/gtrace/gtrace_unit_carrier_test.go @@ -46,11 +46,11 @@ func mustSpanIDFromHex(s string) (t trace.SpanID) { func TestNewCarrier(t *testing.T) { gtest.C(t, func(t *gtest.T) { - ctx := trace.ContextWithRemoteSpanContext(context.Background(), trace.SpanContext{ + ctx := trace.ContextWithRemoteSpanContext(context.Background(), trace.NewSpanContext(trace.SpanContextConfig{ TraceID: traceID, SpanID: spanID, TraceFlags: trace.FlagsSampled, - }) + })) ctx, _ = oteltest.DefaultTracer().Start(ctx, "inject") carrier1 := gtrace.NewCarrier() otel.GetTextMapPropagator().Inject(ctx, carrier1) @@ -58,7 +58,7 @@ func TestNewCarrier(t *testing.T) { ctx = otel.GetTextMapPropagator().Extract(ctx, carrier1) gotSc := trace.RemoteSpanContextFromContext(ctx) - t.Assert(gotSc.TraceID.String(), traceID.String()) - t.Assert(gotSc.SpanID.String(), "0000000000000002") + t.Assert(gotSc.TraceID().String(), traceID.String()) + t.Assert(gotSc.SpanID().String(), "0000000000000002") }) } From b2acd22f58857676911de5a401fec10c3e0942d2 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 29 Mar 2021 17:36:51 +0800 Subject: [PATCH 209/492] improve package gi18n --- i18n/gi18n/gi18n.go | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/i18n/gi18n/gi18n.go b/i18n/gi18n/gi18n.go index c3665ad72..b0a85400b 100644 --- a/i18n/gi18n/gi18n.go +++ b/i18n/gi18n/gi18n.go @@ -7,45 +7,40 @@ // Package gi18n implements internationalization and localization. package gi18n -var ( - // defaultManager is the default i18n instance for package functions. - defaultManager = Instance() -) - // SetPath sets the directory path storing i18n files. func SetPath(path string) error { - return defaultManager.SetPath(path) + return Instance().SetPath(path) } // SetLanguage sets the language for translator. func SetLanguage(language string) { - defaultManager.SetLanguage(language) + Instance().SetLanguage(language) } // SetDelimiters sets the delimiters for translator. func SetDelimiters(left, right string) { - defaultManager.SetDelimiters(left, right) + Instance().SetDelimiters(left, right) } // T is alias of Translate for convenience. func T(content string, language ...string) string { - return defaultManager.T(content, language...) + return Instance().T(content, language...) } // Tf is alias of TranslateFormat for convenience. func Tf(format string, values ...interface{}) string { - return defaultManager.TranslateFormat(format, values...) + return Instance().TranslateFormat(format, values...) } // Tfl is alias of TranslateFormatLang for convenience. func Tfl(language string, format string, values ...interface{}) string { - return defaultManager.TranslateFormatLang(language, format, values...) + return Instance().TranslateFormatLang(language, format, values...) } // TranslateFormat translates, formats and returns the with configured language // and given . func TranslateFormat(format string, values ...interface{}) string { - return defaultManager.TranslateFormat(format, values...) + return Instance().TranslateFormat(format, values...) } // TranslateFormatLang translates, formats and returns the with configured language @@ -53,17 +48,17 @@ func TranslateFormat(format string, values ...interface{}) string { // configured language. If is given empty string, it uses the default configured // language for the translation. func TranslateFormatLang(language string, format string, values ...interface{}) string { - return defaultManager.TranslateFormatLang(language, format, values...) + return Instance().TranslateFormatLang(language, format, values...) } // Translate translates with configured language and returns the translated content. // The parameter specifies custom translation language ignoring configured language. func Translate(content string, language ...string) string { - return defaultManager.Translate(content, language...) + return Instance().Translate(content, language...) } // GetValue retrieves and returns the configured content for given key and specified language. // It returns an empty string if not found. func GetContent(key string, language ...string) string { - return defaultManager.GetContent(key, language...) + return Instance().GetContent(key, language...) } From d4e4b9addf1b99d5cbc71647b42b063efeb6737e Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 29 Mar 2021 18:05:47 +0800 Subject: [PATCH 210/492] improve package gcfg for automatically checking and adding path of package main --- os/gcfg/gcfg.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index 1cd6a7d25..76f5f7985 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -15,6 +15,7 @@ import ( "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gcmd" "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/util/gmode" "github.com/gogf/gf/os/gres" @@ -290,6 +291,7 @@ func (c *Config) GetFilePath(file ...string) (path string, err error) { } }) } + c.autoCheckAndAddMainPkgPathToSearchPaths() // Searching the file system. c.searchPaths.RLockFunc(func(array []string) { for _, prefix := range array { @@ -327,6 +329,19 @@ func (c *Config) GetFilePath(file ...string) (path string, err error) { return } +// autoCheckAndAddMainPkgPathToSearchPaths automatically checks and adds directory path of package main +// to the searching path list if it's currently in development environment. +func (c *Config) autoCheckAndAddMainPkgPathToSearchPaths() { + if gmode.IsDevelop() { + mainPkgPath := gfile.MainPkgPath() + if mainPkgPath != "" { + if !c.searchPaths.Contains(mainPkgPath) { + c.searchPaths.Append(mainPkgPath) + } + } + } +} + // getJson returns a *gjson.Json object for the specified `file` content. // It would print error if file reading fails. It return nil if any error occurs. func (c *Config) getJson(file ...string) *gjson.Json { From 78027d2ec6ec86009d350abbac75f9187386c909 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 30 Mar 2021 13:43:08 +0800 Subject: [PATCH 211/492] deprecated comments update --- database/gdb/gdb_model_condition.go | 6 ++-- database/gdb/gdb_type_record_deprecated.go | 8 ++--- database/gdb/gdb_type_result_deprecated.go | 20 ++++++------- database/gredis/gredis.go | 2 +- net/ghttp/ghttp_request_param.go | 4 +-- net/ghttp/ghttp_request_param_post.go | 34 +++++++++++----------- os/gcmd/gcmd.go | 2 +- text/gstr/gstr.go | 2 +- 8 files changed, 39 insertions(+), 39 deletions(-) diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index e95c284b6..a78c43047 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -95,7 +95,7 @@ func (m *Model) Group(groupBy string) *Model { // GroupBy is alias of Model.Group. // See Model.Group. -// Deprecated. +// Deprecated, use Group instead. func (m *Model) GroupBy(groupBy string) *Model { return m.Group(groupBy) } @@ -109,7 +109,7 @@ func (m *Model) Order(orderBy ...string) *Model { // OrderBy is alias of Model.Order. // See Model.Order. -// Deprecated. +// Deprecated, use Order instead. func (m *Model) OrderBy(orderBy string) *Model { return m.Order(orderBy) } @@ -153,7 +153,7 @@ func (m *Model) Page(page, limit int) *Model { // ForPage is alias of Model.Page. // See Model.Page. -// Deprecated. +// Deprecated, use Page instead. func (m *Model) ForPage(page, limit int) *Model { return m.Page(page, limit) } diff --git a/database/gdb/gdb_type_record_deprecated.go b/database/gdb/gdb_type_record_deprecated.go index a21df29c1..d07a79807 100644 --- a/database/gdb/gdb_type_record_deprecated.go +++ b/database/gdb/gdb_type_record_deprecated.go @@ -6,22 +6,22 @@ package gdb -// Deprecated. +// Deprecated, use Json instead. func (r Record) ToJson() string { return r.Json() } -// Deprecated. +// Deprecated, use Xml instead. func (r Record) ToXml(rootTag ...string) string { return r.Xml(rootTag...) } -// Deprecated. +// Deprecated, use Map instead. func (r Record) ToMap() Map { return r.Map() } -// Deprecated. +// Deprecated, use Struct instead. func (r Record) ToStruct(pointer interface{}) error { return r.Struct(pointer) } diff --git a/database/gdb/gdb_type_result_deprecated.go b/database/gdb/gdb_type_result_deprecated.go index b09c75971..8f6faf3da 100644 --- a/database/gdb/gdb_type_result_deprecated.go +++ b/database/gdb/gdb_type_result_deprecated.go @@ -6,52 +6,52 @@ package gdb -// Deprecated. +// Deprecated, use Json instead. func (r Result) ToJson() string { return r.Json() } -// Deprecated. +// Deprecated, use Xml instead. func (r Result) ToXml(rootTag ...string) string { return r.Xml(rootTag...) } -// Deprecated. +// Deprecated, use List instead. func (r Result) ToList() List { return r.List() } -// Deprecated. +// Deprecated, use MapKeyStr instead. func (r Result) ToStringMap(key string) map[string]Map { return r.MapKeyStr(key) } -// Deprecated. +// Deprecated, use MapKetInt instead. func (r Result) ToIntMap(key string) map[int]Map { return r.MapKeyInt(key) } -// Deprecated. +// Deprecated, use MapKeyUint instead. func (r Result) ToUintMap(key string) map[uint]Map { return r.MapKeyUint(key) } -// Deprecated. +// Deprecated, use RecordKeyStr instead. func (r Result) ToStringRecord(key string) map[string]Record { return r.RecordKeyStr(key) } -// Deprecated. +// Deprecated, use RecordKetInt instead. func (r Result) ToIntRecord(key string) map[int]Record { return r.RecordKeyInt(key) } -// Deprecated. +// Deprecated, use RecordKetUint instead. func (r Result) ToUintRecord(key string) map[uint]Record { return r.RecordKeyUint(key) } -// Deprecated. +// Deprecated, use Structs instead. func (r Result) ToStructs(pointer interface{}) (err error) { return r.Structs(pointer) } diff --git a/database/gredis/gredis.go b/database/gredis/gredis.go index b28b73530..744ba4da6 100644 --- a/database/gredis/gredis.go +++ b/database/gredis/gredis.go @@ -190,7 +190,7 @@ func (r *Redis) Conn() *Conn { } // Alias of Conn, see Conn. -// Deprecated. +// Deprecated, use Conn instead. func (r *Redis) GetConn() *Conn { return r.Conn() } diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index 5f7a1d4f4..f77eeeec3 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -139,14 +139,14 @@ func (r *Request) GetVar(key string, def ...interface{}) *gvar.Var { // GetRaw is alias of GetBody. // See GetBody. -// Deprecated. +// Deprecated, use GetBody instead. func (r *Request) GetRaw() []byte { return r.GetBody() } // GetRawString is alias of GetBodyString. // See GetBodyString. -// Deprecated. +// Deprecated, use GetBodyString instead. func (r *Request) GetRawString() string { return r.GetBodyString() } diff --git a/net/ghttp/ghttp_request_param_post.go b/net/ghttp/ghttp_request_param_post.go index b3147b232..8ddfa17ab 100644 --- a/net/ghttp/ghttp_request_param_post.go +++ b/net/ghttp/ghttp_request_param_post.go @@ -18,7 +18,7 @@ import ( // Note that if there're multiple parameters with the same name, the parameters are retrieved // and overwrote in order of priority: form > body. // -// Deprecated. +// Deprecated, use GetForm instead. func (r *Request) GetPost(key string, def ...interface{}) interface{} { r.parseForm() if len(r.formMap) > 0 { @@ -38,82 +38,82 @@ func (r *Request) GetPost(key string, def ...interface{}) interface{} { return nil } -// Deprecated. +// Deprecated, use GetFormVar instead. func (r *Request) GetPostVar(key string, def ...interface{}) *gvar.Var { return gvar.New(r.GetPost(key, def...)) } -// Deprecated. +// Deprecated, use GetFormString instead. func (r *Request) GetPostString(key string, def ...interface{}) string { return r.GetPostVar(key, def...).String() } -// Deprecated. +// Deprecated, use GetFormBool instead. func (r *Request) GetPostBool(key string, def ...interface{}) bool { return r.GetPostVar(key, def...).Bool() } -// Deprecated. +// Deprecated, use GetFormInt instead. func (r *Request) GetPostInt(key string, def ...interface{}) int { return r.GetPostVar(key, def...).Int() } -// Deprecated. +// Deprecated, use GetFormInt32 instead. func (r *Request) GetPostInt32(key string, def ...interface{}) int32 { return r.GetPostVar(key, def...).Int32() } -// Deprecated. +// Deprecated, use GetFormInt64 instead. func (r *Request) GetPostInt64(key string, def ...interface{}) int64 { return r.GetPostVar(key, def...).Int64() } -// Deprecated. +// Deprecated, use GetFormInts instead. func (r *Request) GetPostInts(key string, def ...interface{}) []int { return r.GetPostVar(key, def...).Ints() } -// Deprecated. +// Deprecated, use GetFormUint instead. func (r *Request) GetPostUint(key string, def ...interface{}) uint { return r.GetPostVar(key, def...).Uint() } -// Deprecated. +// Deprecated, use GetFormUint32 instead. func (r *Request) GetPostUint32(key string, def ...interface{}) uint32 { return r.GetPostVar(key, def...).Uint32() } -// Deprecated. +// Deprecated, use GetFormUint64 instead. func (r *Request) GetPostUint64(key string, def ...interface{}) uint64 { return r.GetPostVar(key, def...).Uint64() } -// Deprecated. +// Deprecated, use GetFormFloat32 instead. func (r *Request) GetPostFloat32(key string, def ...interface{}) float32 { return r.GetPostVar(key, def...).Float32() } -// Deprecated. +// Deprecated, use GetFormFloat64 instead. func (r *Request) GetPostFloat64(key string, def ...interface{}) float64 { return r.GetPostVar(key, def...).Float64() } -// Deprecated. +// Deprecated, use GetFormFloats instead. func (r *Request) GetPostFloats(key string, def ...interface{}) []float64 { return r.GetPostVar(key, def...).Floats() } -// Deprecated. +// Deprecated, use GetFormArray instead. func (r *Request) GetPostArray(key string, def ...interface{}) []string { return r.GetPostVar(key, def...).Strings() } -// Deprecated. +// Deprecated, use GetFormStrings instead. func (r *Request) GetPostStrings(key string, def ...interface{}) []string { return r.GetPostVar(key, def...).Strings() } -// Deprecated. +// Deprecated, use GetFormInterfaces instead. func (r *Request) GetPostInterfaces(key string, def ...interface{}) []interface{} { return r.GetPostVar(key, def...).Interfaces() } diff --git a/os/gcmd/gcmd.go b/os/gcmd/gcmd.go index 777a881ac..d32ca03d6 100644 --- a/os/gcmd/gcmd.go +++ b/os/gcmd/gcmd.go @@ -67,7 +67,7 @@ func GetArgAll() []string { } // GetWithEnv is alias of GetOptWithEnv. -// Deprecated. +// Deprecated, use GetOptWithEnv instead. func GetWithEnv(key string, def ...interface{}) *gvar.Var { return GetOptWithEnv(key, def...) } diff --git a/text/gstr/gstr.go b/text/gstr/gstr.go index e1805c713..903d609df 100644 --- a/text/gstr/gstr.go +++ b/text/gstr/gstr.go @@ -540,7 +540,7 @@ func SplitAndTrim(str, delimiter string, characterMask ...string) []string { // SplitAndTrimSpace splits string by a string to an array, // and calls TrimSpace to every element of this array. -// Deprecated. +// Deprecated, use SplitAndTrim instead. func SplitAndTrimSpace(str, delimiter string) []string { array := make([]string, 0) for _, v := range strings.Split(str, delimiter) { From 9c70fe3be811aacc2e458f3cef642bdcc050b4a9 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 30 Mar 2021 15:25:26 +0800 Subject: [PATCH 212/492] add more unit testing case for package gvalid --- util/gvalid/gvalid_z_unit_checkstruct_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/util/gvalid/gvalid_z_unit_checkstruct_test.go b/util/gvalid/gvalid_z_unit_checkstruct_test.go index 97867763f..188ea4b5c 100755 --- a/util/gvalid/gvalid_z_unit_checkstruct_test.go +++ b/util/gvalid/gvalid_z_unit_checkstruct_test.go @@ -335,3 +335,20 @@ func Test_CheckStruct_NoTag(t *testing.T) { t.Assert(err, nil) }) } + +func Test_CheckStruct_InvalidRule(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Params struct { + Name string + Age uint + Phone string `v:"mobile"` + } + obj := &Params{ + Name: "john", + Age: 18, + Phone: "123", + } + err := gvalid.CheckStruct(obj, nil) + t.AssertNE(err, nil) + }) +} From c4fc9f9618ccb156dbf986704b9cfbeabc789882 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 30 Mar 2021 16:06:20 +0800 Subject: [PATCH 213/492] fix issue #1204: improve validation support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. --- util/gvalid/gvalid_validator_check.go | 14 ++++++++++++++ util/gvalid/gvalid_z_unit_basic_all_test.go | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index 3b05e88bd..a349f3486 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -16,8 +16,14 @@ import ( "github.com/gogf/gf/util/gconv" "strconv" "strings" + "time" ) +type apiTime interface { + Date() (year int, month time.Month, day int) + IsZero() bool +} + // Check checks single value with specified rules. // It returns nil if successful validation. // @@ -196,6 +202,10 @@ func (v *Validator) doCheckBuildInRules( // Date rules. case "date": + // support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. + if v, ok := value.(apiTime); ok { + return !v.IsZero(), nil + } // Standard date string, which must contain char '-' or '.'. if _, err := gtime.StrToTime(valueStr); err == nil { match = true @@ -209,6 +219,10 @@ func (v *Validator) doCheckBuildInRules( // Date rule with specified format. case "date-format": + // support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. + if v, ok := value.(apiTime); ok { + return !v.IsZero(), nil + } if _, err := gtime.StrToTimeFormat(valueStr, rulePattern); err == nil { match = true } else { diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index 81861aa14..25ffc0b65 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -8,7 +8,9 @@ package gvalid_test import ( "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/os/gtime" "testing" + "time" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/test/gtest" @@ -205,6 +207,14 @@ func Test_DateFormat(t *testing.T) { t.Assert(err5, nil) t.AssertNE(err6, nil) }) + gtest.C(t, func(t *gtest.T) { + t1 := gtime.Now() + t2 := time.Time{} + err1 := gvalid.Check(t1, "date-format:Y", nil) + err2 := gvalid.Check(t2, "date-format:Y", nil) + t.Assert(err1, nil) + t.AssertNE(err2, nil) + }) } func Test_Email(t *testing.T) { From dc51023c61c33abb7418523f34f3a3a65dc76700 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 30 Mar 2021 16:54:52 +0800 Subject: [PATCH 214/492] upgrade github.com/olekukonko/tablewriter from 0.0.1 to 0.0.5 --- go.mod | 2 +- go.sum | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b27eb6035..ca87687b7 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/gorilla/websocket v1.4.1 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf github.com/mattn/go-runewidth v0.0.10 // indirect - github.com/olekukonko/tablewriter v0.0.1 + github.com/olekukonko/tablewriter v0.0.5 go.opentelemetry.io/otel v0.19.0 go.opentelemetry.io/otel/oteltest v0.19.0 go.opentelemetry.io/otel/trace v0.19.0 diff --git a/go.sum b/go.sum index f936b51f4..331f2824d 100644 --- a/go.sum +++ b/go.sum @@ -16,10 +16,11 @@ github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvK github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= From d9754a532bcc103243d190d5b1b9d5f12cbde538 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 30 Mar 2021 17:20:13 +0800 Subject: [PATCH 215/492] version updates --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index b22017481..04fce82b3 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.15.4" +const VERSION = "v1.15.5" const AUTHORS = "john" From de0f9e728cd863863d8931cd071e7df8ee4cecf5 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 30 Mar 2021 17:39:56 +0800 Subject: [PATCH 216/492] README updates --- README.MD | 22 +++++++++++++++------- README_ZH.MD | 20 ++++++++++++++------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/README.MD b/README.MD index ad35aafcc..0d09a46be 100644 --- a/README.MD +++ b/README.MD @@ -1,11 +1,19 @@ -# GoFrame +

+ + +

-[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) -[![Build Status](https://travis-ci.com/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) -[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf) -[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master) -[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf) -[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf) +

+Go Doc +Build Status +Go Report +Code Coverage +Production Ready +License +

+ + +# GoFrame English | [简体中文](README_ZH.MD) diff --git a/README_ZH.MD b/README_ZH.MD index 3575a9cea..5d42ef3e4 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -1,10 +1,18 @@ +

+ + +

+ +

+Go Doc +Build Status +Go Report +Code Coverage +Production Ready +License +

+ # GoFrame -[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) -[![Build Status](https://travis-ci.com/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) -[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf) -[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master) -[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf) -[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf) [English](README.MD) | 简体中文 From cfef7262058da240985b7d04a28391d99a7bce93 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 30 Mar 2021 17:41:10 +0800 Subject: [PATCH 217/492] README updates --- README.MD | 2 +- README_ZH.MD | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.MD b/README.MD index 0d09a46be..06f2b16c7 100644 --- a/README.MD +++ b/README.MD @@ -1,6 +1,6 @@

- +

diff --git a/README_ZH.MD b/README_ZH.MD index 5d42ef3e4..2d8ee3051 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -1,6 +1,6 @@

- +

From 83a6abc70fac557432ec6a7a8b57aeb5b7a383f3 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 30 Mar 2021 17:42:00 +0800 Subject: [PATCH 218/492] README updates --- README.MD | 2 +- README_ZH.MD | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.MD b/README.MD index 06f2b16c7..280c35aa2 100644 --- a/README.MD +++ b/README.MD @@ -1,6 +1,6 @@

- +

diff --git a/README_ZH.MD b/README_ZH.MD index 2d8ee3051..3b5f93fe8 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -1,6 +1,6 @@

- +

From 345fb69521d0a9fb33f21a0c3cf8cce6bc0e9526 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 30 Mar 2021 17:43:44 +0800 Subject: [PATCH 219/492] README updates --- README.MD | 4 +--- README_ZH.MD | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/README.MD b/README.MD index 280c35aa2..3db99b6e5 100644 --- a/README.MD +++ b/README.MD @@ -1,9 +1,7 @@

-

- -

+ Go Doc Build Status Go Report diff --git a/README_ZH.MD b/README_ZH.MD index 3b5f93fe8..7a052a5ec 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -1,9 +1,7 @@

-

- -

+ Go Doc Build Status Go Report From 1b6765db507fb5c611b88791f38d86486e0d2e32 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 30 Mar 2021 17:44:57 +0800 Subject: [PATCH 220/492] README updates --- README.MD | 3 +++ README_ZH.MD | 3 +++ 2 files changed, 6 insertions(+) diff --git a/README.MD b/README.MD index 3db99b6e5..08c4a0ac0 100644 --- a/README.MD +++ b/README.MD @@ -2,6 +2,9 @@ +

+ +

Go Doc Build Status Go Report diff --git a/README_ZH.MD b/README_ZH.MD index 7a052a5ec..74d44bde9 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -2,6 +2,9 @@ +

+ +

Go Doc Build Status Go Report From 3911c2e0a2a0a93ab1a622c1da8cf961afcacb39 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 30 Mar 2021 18:00:44 +0800 Subject: [PATCH 221/492] README updates --- README.MD | 23 +++++++---------------- README_ZH.MD | 21 ++++++--------------- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/README.MD b/README.MD index 08c4a0ac0..ad35aafcc 100644 --- a/README.MD +++ b/README.MD @@ -1,21 +1,12 @@ -

- - - -

- -

-Go Doc -Build Status -Go Report -Code Coverage -Production Ready -License -

- - # GoFrame +[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) +[![Build Status](https://travis-ci.com/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) +[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf) +[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master) +[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf) +[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf) + English | [简体中文](README_ZH.MD) `GoFrame` is a modular, powerful, high-performance and enterprise-class application development framework diff --git a/README_ZH.MD b/README_ZH.MD index 74d44bde9..3575a9cea 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -1,19 +1,10 @@ -

- - - -

- -

-Go Doc -Build Status -Go Report -Code Coverage -Production Ready -License -

- # GoFrame +[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) +[![Build Status](https://travis-ci.com/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) +[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf) +[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master) +[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf) +[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf) [English](README.MD) | 简体中文 From ed71d2b2acb9d359a545939ad3078aed7c02a4d2 Mon Sep 17 00:00:00 2001 From: yys <4752017@qq.com> Date: Wed, 31 Mar 2021 14:07:28 +0800 Subject: [PATCH 222/492] add test --- os/gtime/gtime_sql.go | 13 ++++++++-- os/gtime/gtime_z_unit_sql_test.go | 41 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 os/gtime/gtime_z_unit_sql_test.go diff --git a/os/gtime/gtime_sql.go b/os/gtime/gtime_sql.go index dc810ffe9..5f7a7709b 100644 --- a/os/gtime/gtime_sql.go +++ b/os/gtime/gtime_sql.go @@ -4,17 +4,26 @@ import ( "database/sql/driver" ) -//add Scanner +//Scanner is an interface used by Scan. +//Scan value from database +//database/sql func (t *Time) Scan(value interface{}) error { + if t == nil { + return nil + } newTime := New(value) t.Time = newTime.Time return nil } -//add valuer +// Valuer is the interface providing the Value method. database/sql/driver +// Value insert into mysql need this function. func (t *Time) Value() (driver.Value, error) { if t == nil { return nil, nil } + if t.IsZero() { + return nil, nil + } return t.Time, nil } diff --git a/os/gtime/gtime_z_unit_sql_test.go b/os/gtime/gtime_z_unit_sql_test.go new file mode 100644 index 000000000..fa40e71fd --- /dev/null +++ b/os/gtime/gtime_z_unit_sql_test.go @@ -0,0 +1,41 @@ +package gtime_test + +import ( + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/test/gtest" + "testing" +) + +func TestTime_Scan(t1 *testing.T) { + gtest.C(t1, func(t *gtest.T) { + tt := gtime.Time{} + //test string + s := gtime.Now().String() + t.Assert(tt.Scan(s), nil) + t.Assert(tt.String(), s) + //test nano + n := gtime.TimestampNano() + t.Assert(tt.Scan(n), nil) + t.Assert(tt.TimestampNano(), n) + //test nil + none := (*gtime.Time)(nil) + t.Assert(none.Scan(nil), nil) + t.Assert(none, nil) + }) + +} + +func TestTime_Value(t1 *testing.T) { + gtest.C(t1, func(t *gtest.T) { + tt := gtime.Now() + s, err := tt.Value() + t.Assert(err, nil) + t.Assert(s, tt.Time) + //test nil + none := (*gtime.Time)(nil) + s, err = none.Value() + t.Assert(err, nil) + t.Assert(s, nil) + + }) +} From ec00e994771dbf91c356c736a29a8d09676ff65c Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 1 Apr 2021 09:41:14 +0800 Subject: [PATCH 223/492] comments update for package gtime --- os/gtime/gtime_sql.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/os/gtime/gtime_sql.go b/os/gtime/gtime_sql.go index 5f7a7709b..e10822c43 100644 --- a/os/gtime/gtime_sql.go +++ b/os/gtime/gtime_sql.go @@ -4,20 +4,19 @@ import ( "database/sql/driver" ) -//Scanner is an interface used by Scan. -//Scan value from database -//database/sql +// Scanner is an interface used by Scan in package database/sql for Scanning value +// from database to local golang variable. func (t *Time) Scan(value interface{}) error { if t == nil { return nil } newTime := New(value) - t.Time = newTime.Time + *t = *newTime return nil } -// Valuer is the interface providing the Value method. database/sql/driver -// Value insert into mysql need this function. +// Value is the interface providing the Value method for package database/sql/driver +// for retrieving value from golang variable to database. func (t *Time) Value() (driver.Value, error) { if t == nil { return nil, nil From 4d1e25cdab9991853d64a9f1435110fc3f6c9c35 Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 2 Apr 2021 16:16:54 +0800 Subject: [PATCH 224/492] change internal constant varibale names for package gqueue --- container/gqueue/gqueue.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/container/gqueue/gqueue.go b/container/gqueue/gqueue.go index fe9bde541..dc2f9d39d 100644 --- a/container/gqueue/gqueue.go +++ b/container/gqueue/gqueue.go @@ -35,10 +35,8 @@ type Queue struct { } const ( - // Size for queue buffer. - gDEFAULT_QUEUE_SIZE = 10000 - // Max batch size per-fetching from list. - gDEFAULT_MAX_BATCH_SIZE = 10 + defaultQueueSize = 10000 // Size for queue buffer. + defaultBatchSize = 10 // Max batch size per-fetching from list. ) // New returns an empty queue object. @@ -54,7 +52,7 @@ func New(limit ...int) *Queue { } else { q.list = glist.New(true) q.events = make(chan struct{}, math.MaxInt32) - q.C = make(chan interface{}, gDEFAULT_QUEUE_SIZE) + q.C = make(chan interface{}, defaultQueueSize) go q.asyncLoopFromListToChannel() } return q @@ -72,8 +70,8 @@ func (q *Queue) asyncLoopFromListToChannel() { <-q.events for !q.closed.Val() { if length := q.list.Len(); length > 0 { - if length > gDEFAULT_MAX_BATCH_SIZE { - length = gDEFAULT_MAX_BATCH_SIZE + if length > defaultBatchSize { + length = defaultBatchSize } for _, v := range q.list.PopFronts(length) { // When q.C is closed, it will panic here, especially q.C is being blocked for writing. @@ -101,7 +99,7 @@ func (q *Queue) Push(v interface{}) { q.C <- v } else { q.list.PushBack(v) - if len(q.events) < gDEFAULT_QUEUE_SIZE { + if len(q.events) < defaultQueueSize { q.events <- struct{}{} } } @@ -124,7 +122,7 @@ func (q *Queue) Close() { if q.limit > 0 { close(q.C) } - for i := 0; i < gDEFAULT_MAX_BATCH_SIZE; i++ { + for i := 0; i < defaultBatchSize; i++ { q.Pop() } } From ac4485dc8484a724208ef09b500ba54e05fb4872 Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 2 Apr 2021 18:00:50 +0800 Subject: [PATCH 225/492] comment update for package gsession --- os/gsession/gsession_manager.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/os/gsession/gsession_manager.go b/os/gsession/gsession_manager.go index dc83ae50d..c52095146 100644 --- a/os/gsession/gsession_manager.go +++ b/os/gsession/gsession_manager.go @@ -15,9 +15,13 @@ import ( // Manager for sessions. type Manager struct { - ttl time.Duration // TTL for sessions. - storage Storage // Storage interface for session storage. - sessionData *gcache.Cache // Session data cache for session TTL. + ttl time.Duration // TTL for sessions. + storage Storage // Storage interface for session storage. + + // sessionData is the memory data cache for session TTL, + // which is available only if the Storage does not stores any session data in synchronizing. + // Please refer to the implements of StorageFile, StorageMemory and StorageRedis. + sessionData *gcache.Cache } // New creates and returns a new session manager. From a3b94c24de33321e2f51e5ca7a4da8a13db9d379 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 4 Apr 2021 12:01:22 +0800 Subject: [PATCH 226/492] fix issue #854; add maxIdleTime configuration and comments update for package gdb; version updates --- database/gdb/gdb.go | 175 +++++++++++++--------- database/gdb/gdb_core.go | 17 +-- database/gdb/gdb_core_config.go | 48 ++++-- database/gdb/gdb_core_structure.go | 2 +- database/gdb/gdb_driver_mssql.go | 69 +++++---- database/gdb/gdb_driver_mysql.go | 68 +++++---- database/gdb/gdb_driver_oracle.go | 50 ++++--- database/gdb/gdb_driver_pgsql.go | 58 +++---- database/gdb/gdb_driver_sqlite.go | 51 ++++--- database/gdb/gdb_model_fields.go | 6 +- database/gdb/gdb_model_select.go | 2 +- database/gdb/gdb_model_time.go | 2 +- database/gdb/gdb_model_utility.go | 23 ++- database/gdb/gdb_z_init_test.go | 2 +- database/gdb/gdb_z_mysql_internal_test.go | 2 +- version.go | 2 +- 16 files changed, 343 insertions(+), 234 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 10672ca3a..6a3772397 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -35,6 +35,7 @@ type DB interface { // The DB interface is designed not only for // relational databases but also for NoSQL databases in the future. The name // "Table" is not proper for that purpose any more. + // Also see Core.Table. // Deprecated, use Model instead. Table(table ...string) *Model @@ -46,144 +47,151 @@ type DB interface { // Model("user, user_detail") // Model("user u, user_detail ud") // 2. Model name with alias: Model("user", "u") + // Also see Core.Model. Model(table ...string) *Model // Schema creates and returns a schema. + // Also see Core.Schema. Schema(schema string) *Schema // With creates and returns an ORM model based on meta data of given object. + // Also see Core.With. With(object interface{}) *Model // Open creates a raw connection object for database with given node configuration. // Note that it is not recommended using the this function manually. + // Also see DriverMysql.Open. Open(config *ConfigNode) (*sql.DB, error) // Ctx is a chaining function, which creates and returns a new DB that is a shallow copy // of current DB object and with given context in it. // Note that this returned DB object can be used only once, so do not assign it to // a global or package variable for long using. + // Also see Core.Ctx. Ctx(ctx context.Context) DB // =========================================================================== // Query APIs. // =========================================================================== - Query(sql string, args ...interface{}) (*sql.Rows, error) - Exec(sql string, args ...interface{}) (sql.Result, error) - Prepare(sql string, execOnMaster ...bool) (*Stmt, error) + Query(sql string, args ...interface{}) (*sql.Rows, error) // See Core.Query. + Exec(sql string, args ...interface{}) (sql.Result, error) // See Core.Exec. + Prepare(sql string, execOnMaster ...bool) (*Stmt, error) // See Core.Prepare. // =========================================================================== // Common APIs for CURD. // =========================================================================== - Insert(table string, data interface{}, batch ...int) (sql.Result, error) - InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) - Replace(table string, data interface{}, batch ...int) (sql.Result, error) - Save(table string, data interface{}, batch ...int) (sql.Result, error) + Insert(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Insert. + InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.InsertIgnore. + Replace(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Replace. + Save(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Save. - BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) - BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) - BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) + BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchInsert. + BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchReplace. + BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchSave. - Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) - Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) + Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Update. + Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete. // =========================================================================== // Internal APIs for CURD, which can be overwrote for custom CURD implements. // =========================================================================== - DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) - DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) - DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) - DoPrepare(link Link, sql string) (*Stmt, error) - DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) - DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) - DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) - DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) + DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery. + DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll. + DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec. + DoPrepare(link Link, sql string) (*Stmt, error) // See Core.DoPrepare. + DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoInsert. + DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoBatchInsert. + DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate. + DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete. // =========================================================================== // Query APIs for convenience purpose. // =========================================================================== - GetAll(sql string, args ...interface{}) (Result, error) - GetOne(sql string, args ...interface{}) (Record, error) - GetValue(sql string, args ...interface{}) (Value, error) - GetArray(sql string, args ...interface{}) ([]Value, error) - GetCount(sql string, args ...interface{}) (int, error) - GetStruct(objPointer interface{}, sql string, args ...interface{}) error - GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error - GetScan(objPointer interface{}, sql string, args ...interface{}) error + GetAll(sql string, args ...interface{}) (Result, error) // See Core.GetAll. + GetOne(sql string, args ...interface{}) (Record, error) // See Core.GetOne. + GetValue(sql string, args ...interface{}) (Value, error) // See Core.GetValue. + GetArray(sql string, args ...interface{}) ([]Value, error) // See Core.GetArray. + GetCount(sql string, args ...interface{}) (int, error) // See Core.GetCount. + GetStruct(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetStruct. + GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error // See Core.GetStructs. + GetScan(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan. // =========================================================================== // Master/Slave specification support. // =========================================================================== - Master() (*sql.DB, error) - Slave() (*sql.DB, error) + Master() (*sql.DB, error) // See Core.Master. + Slave() (*sql.DB, error) // See Core.Slave. // =========================================================================== // Ping-Pong. // =========================================================================== - PingMaster() error - PingSlave() error + PingMaster() error // See Core.PingMaster. + PingSlave() error // See Core.PingSlave. // =========================================================================== // Transaction. // =========================================================================== - Begin() (*TX, error) - Transaction(f func(tx *TX) error) (err error) + Begin() (*TX, error) // See Core.Begin. + Transaction(f func(tx *TX) error) (err error) // See Core.Transaction. // =========================================================================== // Configuration methods. // =========================================================================== - GetCache() *gcache.Cache - SetDebug(debug bool) - GetDebug() bool - SetSchema(schema string) - GetSchema() string - GetPrefix() string - GetGroup() string - SetDryRun(dryrun bool) - GetDryRun() bool - SetLogger(logger *glog.Logger) - GetLogger() *glog.Logger - GetConfig() *ConfigNode - SetMaxIdleConnCount(n int) - SetMaxOpenConnCount(n int) - SetMaxConnLifetime(d time.Duration) + GetCache() *gcache.Cache // See Core.GetCache. + SetDebug(debug bool) // See Core.SetDebug. + GetDebug() bool // See Core.GetDebug. + SetSchema(schema string) // See Core.SetSchema. + GetSchema() string // See Core.GetSchema. + GetPrefix() string // See Core.GetPrefix. + GetGroup() string // See Core.GetGroup. + SetDryRun(enabled bool) // See Core.SetDryRun. + GetDryRun() bool // See Core.GetDryRun. + SetLogger(logger *glog.Logger) // See Core.SetLogger. + GetLogger() *glog.Logger // See Core.GetLogger. + GetConfig() *ConfigNode // See Core.GetConfig. + SetMaxIdleConnCount(n int) // See Core.SetMaxIdleConnCount. + SetMaxOpenConnCount(n int) // See Core.SetMaxOpenConnCount. + SetMaxConnLifeTime(d time.Duration) // See Core.SetMaxConnLifeTime. + SetMaxConnIdleTime(d time.Duration) // See Core.SetMaxConnIdleTime // =========================================================================== // Utility methods. // =========================================================================== - GetCtx() context.Context - GetChars() (charLeft string, charRight string) - GetMaster(schema ...string) (*sql.DB, error) - GetSlave(schema ...string) (*sql.DB, error) - QuoteWord(s string) string - QuoteString(s string) string - QuotePrefixTableName(table string) string - Tables(schema ...string) (tables []string, err error) - TableFields(table string, schema ...string) (map[string]*TableField, error) - HasTable(name string) (bool, error) - FilteredLinkInfo() string + GetCtx() context.Context // See Core.GetCtx. + GetChars() (charLeft string, charRight string) // See Core.GetChars. + GetMaster(schema ...string) (*sql.DB, error) // See Core.GetMaster. + GetSlave(schema ...string) (*sql.DB, error) // See Core.GetSlave. + QuoteWord(s string) string // See Core.QuoteWord. + QuoteString(s string) string // See Core.QuoteString. + QuotePrefixTableName(table string) string // See Core.QuotePrefixTableName. + Tables(schema ...string) (tables []string, err error) // See Core.Tables. + TableFields(link Link, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields. + HasTable(name string) (bool, error) // See Core.HasTable. + FilteredLinkInfo() string // See Core.FilteredLinkInfo. // HandleSqlBeforeCommit is a hook function, which deals with the sql string before // it's committed to underlying driver. The parameter `link` specifies the current // database connection operation object. You can modify the sql string `sql` and its // arguments `args` as you wish before they're committed to driver. + // Also see Core.HandleSqlBeforeCommit. HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) // =========================================================================== // Internal methods, for internal usage purpose, you do not need consider it. // =========================================================================== - mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) - convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{} - convertRowsToResult(rows *sql.Rows) (Result, error) + mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) // See Core.mappingAndFilterData. + convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{} // See Core.convertFieldValueToLocalValue. + convertRowsToResult(rows *sql.Rows) (Result, error) // See Core.convertRowsToResult. } // Core is the base struct for database management. @@ -299,6 +307,9 @@ var ( // internalCache is the memory cache for internal usage. internalCache = gcache.New() + // tableFieldsMap caches the table information retrived from database. + tableFieldsMap = gmap.New(true) + // allDryRun sets dry-run feature for all database connections. // It is commonly used for command options for convenience. allDryRun = false @@ -471,15 +482,26 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error } // Cache the underlying connection pool object by node. v, _ := internalCache.GetOrSetFuncLock(node.String(), func() (interface{}, error) { - sqlDb, err = c.db.Open(node) - if err != nil { - intlog.Printf(`db open failed: %v, %+v`, err, node) - return nil, err - } intlog.Printf( - `open new connection, master:%v, config:%+v, node:%+v`, + `open new connection, master:%#v, config:%#v, node:%#v`, master, c.config, node, ) + defer func() { + if err != nil { + intlog.Printf(`open new connection failed: %v, %#v`, err, node) + } else { + intlog.Printf( + `open new connection success, master:%#v, config:%#v, node:%#v`, + master, c.config, node, + ) + } + }() + + sqlDb, err = c.db.Open(node) + if err != nil { + return nil, err + } + if c.config.MaxIdleConnCount > 0 { sqlDb.SetMaxIdleConns(c.config.MaxIdleConnCount) } else { @@ -490,17 +512,24 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error } else { sqlDb.SetMaxOpenConns(defaultMaxOpenConnCount) } - if c.config.MaxConnLifetime > 0 { + if c.config.MaxConnLifeTime > 0 { // Automatically checks whether MaxConnLifetime is configured using string like: "30s", "60s", etc. // Or else it is configured just using number, which means value in seconds. - if c.config.MaxConnLifetime > time.Second { - sqlDb.SetConnMaxLifetime(c.config.MaxConnLifetime) + if c.config.MaxConnLifeTime > time.Second { + sqlDb.SetConnMaxLifetime(c.config.MaxConnLifeTime) } else { - sqlDb.SetConnMaxLifetime(c.config.MaxConnLifetime * time.Second) + sqlDb.SetConnMaxLifetime(c.config.MaxConnLifeTime * time.Second) } } else { sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime) } + if c.config.MaxConnIdleTime > 0 { + if c.config.MaxConnIdleTime > time.Second { + sqlDb.SetConnMaxIdleTime(c.config.MaxConnIdleTime) + } else { + sqlDb.SetConnMaxIdleTime(c.config.MaxConnIdleTime * time.Second) + } + } return sqlDb, nil }, 0) if v != nil && sqlDb == nil { diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 7f2a6a1b5..8265c540a 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -113,7 +113,6 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro if c.GetConfig().QueryTimeout > 0 { ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) } - mTime1 := gtime.TimestampMilli() rows, err = link.QueryContext(ctx, sql, args...) mTime2 := gtime.TimestampMilli() @@ -277,7 +276,7 @@ func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) { } // GetArray queries and returns data values as slice from database. -// Note that if there're multiple columns in the result, it returns just one column values randomly. +// Note that if there are multiple columns in the result, it returns just one column values randomly. func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) { all, err := c.db.DoGetAll(nil, sql, args...) if err != nil { @@ -382,13 +381,13 @@ func (c *Core) Begin() (*TX, error) { if master, err := c.db.Master(); err != nil { return nil, err } else { - ctx := c.db.GetCtx() - if c.GetConfig().TranTimeout > 0 { - var cancelFunc context.CancelFunc - ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().TranTimeout) - defer cancelFunc() - } - if tx, err := master.BeginTx(ctx, nil); err == nil { + //ctx := c.db.GetCtx() + //if c.GetConfig().TranTimeout > 0 { + // var cancelFunc context.CancelFunc + // ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().TranTimeout) + // defer cancelFunc() + //} + if tx, err := master.Begin(); err == nil { return &TX{ db: c.db, tx: tx, diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index ab222cf44..a5df3c52a 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -42,7 +42,8 @@ type ConfigNode struct { LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored. MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool. MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool. - MaxConnLifetime time.Duration `json:"maxLifetime"` // (Optional) Max connection TTL configuration for underlying connection pool. + MaxConnIdleTime time.Duration `json:"maxIdleTime"` // (Optional) Max connection TTL configuration for underlying connection pool. + MaxConnLifeTime time.Duration `json:"maxLifeTime"` // (Optional) Max amount of time a connection may be idle before being closed. QueryTimeout time.Duration `json:"queryTimeout"` // (Optional) Max query time for per dql. ExecTimeout time.Duration `json:"execTimeout"` // (Optional) Max exec time for dml. TranTimeout time.Duration `json:"tranTimeout"` // (Optional) Max exec time time for a transaction. @@ -141,31 +142,60 @@ func (c *Core) GetLogger() *glog.Logger { return c.logger } -// SetMaxIdleConnCount sets the max idle connection count for underlying connection pool. +// SetMaxIdleConnCount sets the maximum number of connections in the idle +// connection pool. +// +// If MaxOpenConns is greater than 0 but less than the new MaxIdleConns, +// then the new MaxIdleConns will be reduced to match the MaxOpenConns limit. +// +// If n <= 0, no idle connections are retained. +// +// The default max idle connections is currently 2. This may change in +// a future release. func (c *Core) SetMaxIdleConnCount(n int) { c.config.MaxIdleConnCount = n } -// SetMaxOpenConnCount sets the max open connection count for underlying connection pool. +// SetMaxOpenConnCount sets the maximum number of open connections to the database. +// +// If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than +// MaxIdleConns, then MaxIdleConns will be reduced to match the new +// MaxOpenConns limit. +// +// If n <= 0, then there is no limit on the number of open connections. +// The default is 0 (unlimited). func (c *Core) SetMaxOpenConnCount(n int) { c.config.MaxOpenConnCount = n } -// SetMaxConnLifetime sets the connection TTL for underlying connection pool. -// If parameter `d` <= 0, it means the connection never expires. -func (c *Core) SetMaxConnLifetime(d time.Duration) { - c.config.MaxConnLifetime = d +// SetMaxConnIdleTime sets the maximum amount of time a connection may be idle. +// +// Expired connections may be closed lazily before reuse. +// +// If d <= 0, connections are not closed due to a connection's idle time. +func (c *Core) SetMaxConnIdleTime(d time.Duration) { + c.config.MaxConnIdleTime = d +} + +// SetMaxConnLifeTime sets the maximum amount of time a connection may be reused. +// +// Expired connections may be closed lazily before reuse. +// +// If d <= 0, connections are not closed due to a connection's age. +func (c *Core) SetMaxConnLifeTime(d time.Duration) { + c.config.MaxConnLifeTime = d } // String returns the node as string. func (node *ConfigNode) String() string { return fmt.Sprintf( - `%s@%s:%s,%s,%s,%s,%s,%v,%d-%d-%d#%s`, + `%s@%s:%s,%s,%s,%s,%s,%v,%d-%d-%d-%d#%s`, node.User, node.Host, node.Port, node.Name, node.Type, node.Role, node.Charset, node.Debug, node.MaxIdleConnCount, node.MaxOpenConnCount, - node.MaxConnLifetime, + node.MaxConnIdleTime, + node.MaxConnLifeTime, node.LinkInfo, ) } diff --git a/database/gdb/gdb_core_structure.go b/database/gdb/gdb_core_structure.go index de5b9e770..8e28f239b 100644 --- a/database/gdb/gdb_core_structure.go +++ b/database/gdb/gdb_core_structure.go @@ -149,7 +149,7 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s // mappingAndFilterData automatically mappings the map key to table field and removes // all key-value pairs that are not the field of given table. func (c *Core) mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) { - if fieldsMap, err := c.db.TableFields(table, schema); err == nil { + if fieldsMap, err := c.db.TableFields(nil, table, schema); err == nil { fieldsKeyMap := make(map[string]interface{}, len(fieldsMap)) for k, _ := range fieldsMap { fieldsKeyMap[k] = nil diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index 6ba855489..f288187c9 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -203,7 +203,9 @@ func (d *DriverMssql) Tables(schema ...string) (tables []string, err error) { } // TableFields retrieves and returns the fields information of specified table of current schema. -func (d *DriverMssql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) { +// +// Also see DriverMysql.TableFields. +func (d *DriverMssql) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -213,18 +215,21 @@ func (d *DriverMssql) TableFields(table string, schema ...string) (fields map[st if len(schema) > 0 && schema[0] != "" { checkSchema = schema[0] } - v, _ := internalCache.GetOrSetFunc( - fmt.Sprintf(`mssql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()), - func() (interface{}, error) { - var ( - result Result - link *sql.DB - ) + tableFieldsCacheKey := fmt.Sprintf( + `mssql_table_fields_%s_%s@group:%s`, + table, checkSchema, d.GetGroup(), + ) + v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { + var ( + result Result + ) + if link == nil { link, err = d.db.GetSlave(checkSchema) if err != nil { - return nil, err + return nil } - structureSql := fmt.Sprintf(` + } + structureSql := fmt.Sprintf(` SELECT a.name Field, CASE b.name @@ -252,29 +257,29 @@ LEFT JOIN sys.extended_properties g ON a.id=g.major_id AND a.colid=g.minor_id LEFT JOIN sys.extended_properties f ON d.id=f.major_id AND f.minor_id =0 WHERE d.name='%s' ORDER BY a.id,a.colorder`, - strings.ToUpper(table), - ) - structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - result, err = d.db.DoGetAll(link, structureSql) - if err != nil { - return nil, err + strings.ToUpper(table), + ) + structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) + result, err = d.db.DoGetAll(link, structureSql) + if err != nil { + return nil + } + fields = make(map[string]*TableField) + for i, m := range result { + fields[strings.ToLower(m["Field"].String())] = &TableField{ + Index: i, + Name: strings.ToLower(m["Field"].String()), + Type: strings.ToLower(m["Type"].String()), + Null: m["Null"].Bool(), + Key: m["Key"].String(), + Default: m["Default"].Val(), + Extra: m["Extra"].String(), + Comment: m["Comment"].String(), } - fields = make(map[string]*TableField) - for i, m := range result { - fields[strings.ToLower(m["Field"].String())] = &TableField{ - Index: i, - Name: strings.ToLower(m["Field"].String()), - Type: strings.ToLower(m["Type"].String()), - Null: m["Null"].Bool(), - Key: m["Key"].String(), - Default: m["Default"].Val(), - Extra: m["Extra"].String(), - Comment: m["Comment"].String(), - } - } - return fields, nil - }, 0) - if err == nil { + } + return fields + }) + if v != nil { fields = v.(map[string]*TableField) } return diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index 5ac100350..9f6f6bf45 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -102,13 +102,16 @@ func (d *DriverMysql) Tables(schema ...string) (tables []string, err error) { // TableFields retrieves and returns the fields information of specified table of current // schema. // +// The parameter `link` is optional, if given nil it automatically retrieves a raw sql connection +// as its link to proceed necessary sql query. +// // Note that it returns a map containing the field name and its corresponding fields. // As a map is unsorted, the TableField struct has a "Index" field marks its sequence in // the fields. // // It's using cache feature to enhance the performance, which is never expired util the // process restarts. -func (d *DriverMysql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) { +func (d *DriverMysql) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -118,40 +121,43 @@ func (d *DriverMysql) TableFields(table string, schema ...string) (fields map[st if len(schema) > 0 && schema[0] != "" { checkSchema = schema[0] } - v, _ := internalCache.GetOrSetFunc( - fmt.Sprintf(`mysql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()), - func() (interface{}, error) { - var ( - result Result - link *sql.DB - ) + tableFieldsCacheKey := fmt.Sprintf( + `mysql_table_fields_%s_%s@group:%s`, + table, checkSchema, d.GetGroup(), + ) + v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { + var ( + result Result + ) + if link == nil { link, err = d.db.GetSlave(checkSchema) if err != nil { - return nil, err + return nil } - result, err = d.db.DoGetAll( - link, - fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.db.QuoteWord(table)), - ) - if err != nil { - return nil, err + } + result, err = d.db.DoGetAll( + link, + fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.db.QuoteWord(table)), + ) + if err != nil { + return nil + } + fields = make(map[string]*TableField) + for i, m := range result { + fields[m["Field"].String()] = &TableField{ + Index: i, + Name: m["Field"].String(), + Type: m["Type"].String(), + Null: m["Null"].Bool(), + Key: m["Key"].String(), + Default: m["Default"].Val(), + Extra: m["Extra"].String(), + Comment: m["Comment"].String(), } - fields = make(map[string]*TableField) - for i, m := range result { - fields[m["Field"].String()] = &TableField{ - Index: i, - Name: m["Field"].String(), - Type: m["Type"].String(), - Null: m["Null"].Bool(), - Key: m["Key"].String(), - Default: m["Default"].Val(), - Extra: m["Extra"].String(), - Comment: m["Comment"].String(), - } - } - return fields, nil - }, 0) - if err == nil { + } + return fields + }) + if v != nil { fields = v.(map[string]*TableField) } return diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index e0bac602f..f8011b91a 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -179,7 +179,9 @@ func (d *DriverOracle) Tables(schema ...string) (tables []string, err error) { } // TableFields retrieves and returns the fields information of specified table of current schema. -func (d *DriverOracle) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) { +// +// Also see DriverMysql.TableFields. +func (d *DriverOracle) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -189,11 +191,14 @@ func (d *DriverOracle) TableFields(table string, schema ...string) (fields map[s if len(schema) > 0 && schema[0] != "" { checkSchema = schema[0] } - v, _ := internalCache.GetOrSetFunc( - fmt.Sprintf(`oracle_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()), - func() (interface{}, error) { - result := (Result)(nil) - structureSql := fmt.Sprintf(` + tableFieldsCacheKey := fmt.Sprintf( + `oracle_table_fields_%s_%s@group:%s`, + table, checkSchema, d.GetGroup(), + ) + v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { + var ( + result Result + structureSql = fmt.Sprintf(` SELECT COLUMN_NAME AS FIELD, CASE DATA_TYPE @@ -203,22 +208,29 @@ SELECT FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, strings.ToUpper(table), ) - structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - result, err = d.db.GetAll(structureSql) + ) + structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) + if link == nil { + link, err = d.db.GetSlave(checkSchema) if err != nil { - return nil, err + return nil } - fields = make(map[string]*TableField) - for i, m := range result { - fields[strings.ToLower(m["FIELD"].String())] = &TableField{ - Index: i, - Name: strings.ToLower(m["FIELD"].String()), - Type: strings.ToLower(m["TYPE"].String()), - } + } + result, err = d.db.DoGetAll(link, structureSql) + if err != nil { + return nil + } + fields = make(map[string]*TableField) + for i, m := range result { + fields[strings.ToLower(m["FIELD"].String())] = &TableField{ + Index: i, + Name: strings.ToLower(m["FIELD"].String()), + Type: strings.ToLower(m["TYPE"].String()), } - return fields, nil - }, 0) - if err == nil { + } + return fields + }) + if v != nil { fields = v.(map[string]*TableField) } return diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index a047733a2..95cf65087 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -111,7 +111,9 @@ func (d *DriverPgsql) Tables(schema ...string) (tables []string, err error) { } // TableFields retrieves and returns the fields information of specified table of current schema. -func (d *DriverPgsql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) { +// +// Also see DriverMysql.TableFields. +func (d *DriverPgsql) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -122,41 +124,43 @@ func (d *DriverPgsql) TableFields(table string, schema ...string) (fields map[st if len(schema) > 0 && schema[0] != "" { checkSchema = schema[0] } - v, _ := internalCache.GetOrSetFunc( - fmt.Sprintf(`pgsql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()), - func() (interface{}, error) { - var ( - result Result - link *sql.DB - ) - link, err = d.db.GetSlave(checkSchema) - if err != nil { - return nil, err - } - structureSql := fmt.Sprintf(` + tableFieldsCacheKey := fmt.Sprintf( + `pgsql_table_fields_%s_%s@group:%s`, + table, checkSchema, d.GetGroup(), + ) + v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { + var ( + result Result + structureSql = fmt.Sprintf(` SELECT a.attname AS field, t.typname AS type FROM pg_class c, pg_attribute a LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,pg_type t WHERE c.relname = '%s' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid ORDER BY a.attnum`, strings.ToLower(table), ) - structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - result, err = d.db.DoGetAll(link, structureSql) + ) + structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) + if link == nil { + link, err = d.db.GetSlave(checkSchema) if err != nil { - return nil, err + return nil } - - fields = make(map[string]*TableField) - for i, m := range result { - fields[m["field"].String()] = &TableField{ - Index: i, - Name: m["field"].String(), - Type: m["type"].String(), - } + } + result, err = d.db.DoGetAll(link, structureSql) + if err != nil { + return nil + } + fields = make(map[string]*TableField) + for i, m := range result { + fields[m["field"].String()] = &TableField{ + Index: i, + Name: m["field"].String(), + Type: m["type"].String(), } - return fields, nil - }, 0) - if err == nil { + } + return fields + }) + if v != nil { fields = v.(map[string]*TableField) } return diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index ed577217a..ba000fbbb 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -93,7 +93,9 @@ func (d *DriverSqlite) Tables(schema ...string) (tables []string, err error) { } // TableFields retrieves and returns the fields information of specified table of current schema. -func (d *DriverSqlite) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) { +// +// Also see DriverMysql.TableFields. +func (d *DriverSqlite) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -103,32 +105,35 @@ func (d *DriverSqlite) TableFields(table string, schema ...string) (fields map[s if len(schema) > 0 && schema[0] != "" { checkSchema = schema[0] } - v, _ := internalCache.GetOrSetFunc( - fmt.Sprintf(`sqlite_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()), - func() (interface{}, error) { - var ( - result Result - link *sql.DB - ) + tableFieldsCacheKey := fmt.Sprintf( + `sqlite_table_fields_%s_%s@group:%s`, + table, checkSchema, d.GetGroup(), + ) + v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { + var ( + result Result + ) + if link == nil { link, err = d.db.GetSlave(checkSchema) if err != nil { - return nil, err + return nil } - result, err = d.db.DoGetAll(link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table)) - if err != nil { - return nil, err + } + result, err = d.db.DoGetAll(link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table)) + if err != nil { + return nil + } + fields = make(map[string]*TableField) + for i, m := range result { + fields[strings.ToLower(m["name"].String())] = &TableField{ + Index: i, + Name: strings.ToLower(m["name"].String()), + Type: strings.ToLower(m["type"].String()), } - fields = make(map[string]*TableField) - for i, m := range result { - fields[strings.ToLower(m["name"].String())] = &TableField{ - Index: i, - Name: strings.ToLower(m["name"].String()), - Type: strings.ToLower(m["type"].String()), - } - } - return fields, nil - }, 0) - if err == nil { + } + return fields + }) + if v != nil { fields = v.(map[string]*TableField) } return diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index bbcb5978f..c10e00461 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -94,7 +94,7 @@ func (m *Model) GetFieldsStr(prefix ...string) string { if len(prefix) > 0 { prefixStr = prefix[0] } - tableFields, err := m.db.TableFields(m.tables) + tableFields, err := m.TableFields(m.tables) if err != nil { panic(err) } @@ -131,7 +131,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string { if len(prefix) > 0 { prefixStr = prefix[0] } - tableFields, err := m.db.TableFields(m.tables) + tableFields, err := m.TableFields(m.tables) if err != nil { panic(err) } @@ -159,7 +159,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string { // HasField determine whether the field exists in the table. func (m *Model) HasField(field string) (bool, error) { - tableFields, err := m.db.TableFields(m.tables) + tableFields, err := m.TableFields(m.tables) if err != nil { return false, err } diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 1cd5139fa..962592b33 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -84,7 +84,7 @@ func (m *Model) getFieldsFiltered() string { panic("function FieldsEx supports only single table operations") } // Filter table fields with fieldEx. - tableFields, err := m.db.TableFields(m.tables) + tableFields, err := m.TableFields(m.tables) if err != nil { panic(err) } diff --git a/database/gdb/gdb_model_time.go b/database/gdb/gdb_model_time.go index ce14d831e..e167fbb35 100644 --- a/database/gdb/gdb_model_time.go +++ b/database/gdb/gdb_model_time.go @@ -93,7 +93,7 @@ func (m *Model) getSoftFieldNameDeleted(table ...string) (field string) { // getSoftFieldName retrieves and returns the field name of the table for possible key. func (m *Model) getSoftFieldName(table string, keys []string) (field string) { - fieldsMap, _ := m.db.TableFields(table) + fieldsMap, _ := m.TableFields(table) if len(fieldsMap) > 0 { for _, key := range keys { field, _ = gutil.MapPossibleItemByKey( diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 93ede1a04..93890326b 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -28,12 +28,31 @@ func (m *Model) getModel() *Model { } } +// TableFields retrieves and returns the fields information of specified table of current +// schema. +// +// Also see DriverMysql.TableFields. +func (m *Model) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) { + var ( + link Link + ) + if m.tx != nil { + link = m.tx.tx + } else { + link, err = m.db.GetSlave(schema...) + if err != nil { + return + } + } + return m.db.TableFields(link, table, schema...) +} + // mappingAndFilterToTableFields mappings and changes given field name to really table field name. // Eg: // ID -> id // NICK_Name -> nickname func (m *Model) mappingAndFilterToTableFields(fields []string, filter bool) []string { - fieldsMap, err := m.db.TableFields(m.tables) + fieldsMap, err := m.TableFields(m.tables) if err != nil || len(fieldsMap) == 0 { return fields } @@ -188,7 +207,7 @@ func (m *Model) getLink(master bool) Link { // "user", "user u", "user as u, user_detail as ud". func (m *Model) getPrimaryKey() string { table := gstr.SplitAndTrim(m.tables, " ")[0] - tableFields, err := m.db.TableFields(table) + tableFields, err := m.TableFields(table) if err != nil { return "" } diff --git a/database/gdb/gdb_z_init_test.go b/database/gdb/gdb_z_init_test.go index fe9fcc47a..abe205257 100644 --- a/database/gdb/gdb_z_init_test.go +++ b/database/gdb/gdb_z_init_test.go @@ -52,7 +52,7 @@ func init() { Weight: 1, MaxIdleConnCount: 10, MaxOpenConnCount: 10, - MaxConnLifetime: 600, + MaxConnLifeTime: 600, } nodePrefix := configNode nodePrefix.Prefix = TableNamePrefix1 diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 736a1c1f0..ed80711a6 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -45,7 +45,7 @@ func init() { Weight: 1, MaxIdleConnCount: 10, MaxOpenConnCount: 10, - MaxConnLifetime: 600, + MaxConnLifeTime: 600, } AddConfigNode(DefaultGroupName, configNode) // Default db. diff --git a/version.go b/version.go index 04fce82b3..189fb2e19 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.15.5" +const VERSION = "v1.15.6" const AUTHORS = "john" From 49dd17c0479b2831b9fb459a5e4f18272c7cdf48 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 4 Apr 2021 12:13:51 +0800 Subject: [PATCH 227/492] remove maxIdleTime feature due to be compatible with old Golang version <= v1.14 --- database/gdb/gdb.go | 8 -------- database/gdb/gdb_core_config.go | 13 +------------ 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 6a3772397..b9a448b7d 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -160,7 +160,6 @@ type DB interface { SetMaxIdleConnCount(n int) // See Core.SetMaxIdleConnCount. SetMaxOpenConnCount(n int) // See Core.SetMaxOpenConnCount. SetMaxConnLifeTime(d time.Duration) // See Core.SetMaxConnLifeTime. - SetMaxConnIdleTime(d time.Duration) // See Core.SetMaxConnIdleTime // =========================================================================== // Utility methods. @@ -523,13 +522,6 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error } else { sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime) } - if c.config.MaxConnIdleTime > 0 { - if c.config.MaxConnIdleTime > time.Second { - sqlDb.SetConnMaxIdleTime(c.config.MaxConnIdleTime) - } else { - sqlDb.SetConnMaxIdleTime(c.config.MaxConnIdleTime * time.Second) - } - } return sqlDb, nil }, 0) if v != nil && sqlDb == nil { diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index a5df3c52a..1f70a0164 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -42,7 +42,6 @@ type ConfigNode struct { LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored. MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool. MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool. - MaxConnIdleTime time.Duration `json:"maxIdleTime"` // (Optional) Max connection TTL configuration for underlying connection pool. MaxConnLifeTime time.Duration `json:"maxLifeTime"` // (Optional) Max amount of time a connection may be idle before being closed. QueryTimeout time.Duration `json:"queryTimeout"` // (Optional) Max query time for per dql. ExecTimeout time.Duration `json:"execTimeout"` // (Optional) Max exec time for dml. @@ -168,15 +167,6 @@ func (c *Core) SetMaxOpenConnCount(n int) { c.config.MaxOpenConnCount = n } -// SetMaxConnIdleTime sets the maximum amount of time a connection may be idle. -// -// Expired connections may be closed lazily before reuse. -// -// If d <= 0, connections are not closed due to a connection's idle time. -func (c *Core) SetMaxConnIdleTime(d time.Duration) { - c.config.MaxConnIdleTime = d -} - // SetMaxConnLifeTime sets the maximum amount of time a connection may be reused. // // Expired connections may be closed lazily before reuse. @@ -189,12 +179,11 @@ func (c *Core) SetMaxConnLifeTime(d time.Duration) { // String returns the node as string. func (node *ConfigNode) String() string { return fmt.Sprintf( - `%s@%s:%s,%s,%s,%s,%s,%v,%d-%d-%d-%d#%s`, + `%s@%s:%s,%s,%s,%s,%s,%v,%d-%d-%d#%s`, node.User, node.Host, node.Port, node.Name, node.Type, node.Role, node.Charset, node.Debug, node.MaxIdleConnCount, node.MaxOpenConnCount, - node.MaxConnIdleTime, node.MaxConnLifeTime, node.LinkInfo, ) From 802187abc4095c065c1239a196d86c907e2dac86 Mon Sep 17 00:00:00 2001 From: ansionfor <77931774@qq.com> Date: Wed, 7 Apr 2021 16:06:37 +0800 Subject: [PATCH 228/492] add graceful reload timeout --- net/ghttp/ghttp_server.go | 2 +- net/ghttp/ghttp_server_config.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index af9edfd5c..e0c389801 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -193,7 +193,7 @@ func (s *Server) Start() error { // If this is a child process, it then notifies its parent exit. if gproc.IsChild() { - gtimer.SetTimeout(2*time.Second, func() { + gtimer.SetTimeout(time.Duration(s.config.GracefulTimeout) * time.Second, func() { if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil { //glog.Error("server error in process communication:", err) } diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 760b17b43..7a59f7726 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -218,6 +218,9 @@ type ServerConfig struct { // Graceful enables graceful reload feature for all servers of the process. Graceful bool `json:"graceful"` + + // GracefulTimeout set the maximum survival time (seconds) of the parent process. + GracefulTimeout uint8 `json:"gracefulTimeout"` } // Deprecated. Use NewConfig instead. @@ -265,6 +268,7 @@ func NewConfig() ServerConfig { FormParsingMemory: 1024 * 1024, // 1MB Rewrites: make(map[string]string), Graceful: false, + GracefulTimeout: 2, // seconds } } From 90f4bba8fd13e40fc504cd8aafa6572cc031b5e9 Mon Sep 17 00:00:00 2001 From: wujia Date: Wed, 7 Apr 2021 20:52:38 +0800 Subject: [PATCH 229/492] =?UTF-8?q?gofmt=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- net/ghttp/ghttp_server.go | 2 +- net/ghttp/ghttp_server_config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index e0c389801..68092e4e4 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -193,7 +193,7 @@ func (s *Server) Start() error { // If this is a child process, it then notifies its parent exit. if gproc.IsChild() { - gtimer.SetTimeout(time.Duration(s.config.GracefulTimeout) * time.Second, func() { + gtimer.SetTimeout(time.Duration(s.config.GracefulTimeout)*time.Second, func() { if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil { //glog.Error("server error in process communication:", err) } diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 7a59f7726..e7b19c926 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -268,7 +268,7 @@ func NewConfig() ServerConfig { FormParsingMemory: 1024 * 1024, // 1MB Rewrites: make(map[string]string), Graceful: false, - GracefulTimeout: 2, // seconds + GracefulTimeout: 2, // seconds } } From 5cca0640d9e0195019abd548b1165259d4d7f9de Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 16 Apr 2021 10:21:42 +0800 Subject: [PATCH 230/492] README update --- README.MD | 8 ++++++-- README_ZH.MD | 30 +++++++++++++++++++----------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/README.MD b/README.MD index ad35aafcc..9fcf2a1c5 100644 --- a/README.MD +++ b/README.MD @@ -55,8 +55,12 @@ The `Web` component performance of `GoFrame`, please refer to third-party projec # Discussion -- QQ Group:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7) -- WX Group:Add friend`389961817` in WeChat, commenting `GF` +- QQ Group:Scan QR or search group ID in QQ +

GoFrame实战1群(满)

GoFrame实战2群

116707870

74341849

+ +- WX Group:Scan QR or search `389961817` in WeChat, commenting `GF` +

添加后拉群

+ - Issues:https://github.com/gogf/gf/issues > It's recommended learning `GoFrame` through its awesome source codes and API reference. diff --git a/README_ZH.MD b/README_ZH.MD index 3575a9cea..3cdbde13e 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -13,14 +13,16 @@ > 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。 # 特点 -* 模块化、松耦合设计; -* 模块丰富、开箱即用; -* 简便易用、易于维护; -* 高代码质量、高单元测试覆盖率; -* 社区活跃,大牛谦逊低调脾气好; -* 详尽的开发文档及示例; -* 完善的本地中文化支持; -* 设计为团队及企业使用; +* 模块化、松耦合 +* 模块丰富、开箱即用 +* 简洁易用、快速接入 +* 文档详尽、易于维护 +* 自顶向下、体系化设计 +* 统一框架、统一组件、降低选择成本 +* 开发规范、设计模式、代码分层模型 +* 强大便捷的开发工具链 +* 完善的本地中文化支持 +* 设计为团队及企业使用 # 地址 - **主库**:https://github.com/gogf/gf @@ -68,9 +70,15 @@ golang版本 >= 1.11 接口文档:https://godoc.org/github.com/gogf/gf # 帮助 -- QQ交流群:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7) -- WX交流群:微信添加`389961817`备注`GF` -- 主库ISSUE:https://github.com/gogf/gf/issues +* QQ交流群:扫描或群号搜索添加 + +

GoFrame实战1群(满)

GoFrame实战2群

116707870

74341849

+ +* 微信交流群:扫描或微信添加`389961817`备注`GF`加群 + +

添加后拉群

+ +* 主库ISSUE:[https://github.com/gogf/gf/issues](https://github.com/gogf/gf/issues) > 建议通过阅读`GoFrame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。 From 88c49dc2cb6d91629271b521c3a43d937b502050 Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 16 Apr 2021 10:27:39 +0800 Subject: [PATCH 231/492] README update --- README.MD | 11 ----------- README_ZH.MD | 14 +------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/README.MD b/README.MD index 9fcf2a1c5..2521491ab 100644 --- a/README.MD +++ b/README.MD @@ -54,17 +54,6 @@ The `Web` component performance of `GoFrame`, please refer to third-party projec * GoDoc API: https://godoc.org/github.com/gogf/gf -# Discussion -- QQ Group:Scan QR or search group ID in QQ -

GoFrame实战1群(满)

GoFrame实战2群

116707870

74341849

- -- WX Group:Scan QR or search `389961817` in WeChat, commenting `GF` -

添加后拉群

- -- Issues:https://github.com/gogf/gf/issues - -> It's recommended learning `GoFrame` through its awesome source codes and API reference. - # License `GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever. diff --git a/README_ZH.MD b/README_ZH.MD index 3cdbde13e..1e40f73f3 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -65,22 +65,10 @@ golang版本 >= 1.11 # 文档 -开发文档:https://goframe.org +官方站点:https://goframe.org 接口文档:https://godoc.org/github.com/gogf/gf -# 帮助 -* QQ交流群:扫描或群号搜索添加 - -

GoFrame实战1群(满)

GoFrame实战2群

116707870

74341849

- -* 微信交流群:扫描或微信添加`389961817`备注`GF`加群 - -

添加后拉群

- -* 主库ISSUE:[https://github.com/gogf/gf/issues](https://github.com/gogf/gf/issues) - -> 建议通过阅读`GoFrame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。 # 协议 From 30b60754e3e4f240b472a23618c776bf2e879bff Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 16 Apr 2021 10:32:55 +0800 Subject: [PATCH 232/492] README update --- README.MD | 4 ++-- README_ZH.MD | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.MD b/README.MD index 2521491ab..4b7d82f95 100644 --- a/README.MD +++ b/README.MD @@ -50,8 +50,8 @@ The `Web` component performance of `GoFrame`, please refer to third-party projec # Documentation -* 中文官网: https://goframe.org -* GoDoc API: https://godoc.org/github.com/gogf/gf +* 中文官网: [https://goframe.org](https://goframe.org/display/gf) +* GoDoc API: [https://pkg.go.dev/github.com/gogf/gf](https://pkg.go.dev/github.com/gogf/gf) # License diff --git a/README_ZH.MD b/README_ZH.MD index 1e40f73f3..79b22cfde 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -65,9 +65,9 @@ golang版本 >= 1.11 # 文档 -官方站点:https://goframe.org +官方站点:[https://goframe.org](https://goframe.org/display/gf) -接口文档:https://godoc.org/github.com/gogf/gf +接口文档:[https://pkg.go.dev/github.com/gogf/gf](https://pkg.go.dev/github.com/gogf/gf) # 协议 From 524e3bedc73db801a2bc2fa81234cddf091b45bf Mon Sep 17 00:00:00 2001 From: jflyfox Date: Mon, 26 Apr 2021 20:37:36 +0800 Subject: [PATCH 233/492] improve empty value validation for required-with* patterns for package gvalid --- internal/empty/empty.go | 12 ++++ os/gtime/gtime_time.go | 9 +++ util/gvalid/gvalid_validator_check.go | 12 ++-- util/gvalid/gvalid_validator_rule_required.go | 19 +++--- util/gvalid/gvalid_z_unit_basic_all_test.go | 66 +++++++++++++++++++ 5 files changed, 102 insertions(+), 16 deletions(-) diff --git a/internal/empty/empty.go b/internal/empty/empty.go index 234378aca..1d42441ff 100644 --- a/internal/empty/empty.go +++ b/internal/empty/empty.go @@ -9,6 +9,7 @@ package empty import ( "reflect" + "time" ) // apiString is used for type assert api for String(). @@ -26,6 +27,11 @@ type apiMapStrAny interface { MapStrAny() map[string]interface{} } +type apiTime interface { + Date() (year int, month time.Month, day int) + IsZero() bool +} + // IsEmpty checks whether given `value` empty. // It returns true if `value` is in: 0, nil, false, "", len(slice/map/chan) == 0, // or else it returns false. @@ -80,6 +86,12 @@ func IsEmpty(value interface{}) bool { return len(value) == 0 default: // Common interfaces checks. + if f, ok := value.(apiTime); ok { + if f == nil { + return true + } + return f.IsZero() + } if f, ok := value.(apiString); ok { if f == nil { return true diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 997cf43ad..871013ed4 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -220,6 +220,15 @@ func (t *Time) String() string { return t.Format("Y-m-d H:i:s") } +// IsZero reports whether t represents the zero time instant, +// January 1, year 1, 00:00:00 UTC. +func (t *Time) IsZero() bool { + if t == nil { + return true + } + return t.Time.IsZero() +} + // Clone returns a new Time object which is a clone of current time object. func (t *Time) Clone() *Time { return New(t.Time) diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index a349f3486..831930df1 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -47,13 +47,11 @@ func (v *Validator) doCheck(key string, value interface{}, rules string, message // It converts value to string and then does the validation. var ( // Do not trim it as the space is also part of the value. - data = make(map[string]string) + data = make(map[string]interface{}) errorMsgArray = make(map[string]string) ) if len(params) > 0 { - for k, v := range gconv.Map(params[0]) { - data[k] = gconv.String(v) - } + data = gconv.Map(params[0]) } // Custom error messages handling. var ( @@ -150,7 +148,7 @@ func (v *Validator) doCheckBuildInRules( ruleKey string, rulePattern string, ruleItems []string, - dataMap map[string]string, + dataMap map[string]interface{}, customMsgMap map[string]string, ) (match bool, err error) { valueStr := gconv.String(value) @@ -235,7 +233,7 @@ func (v *Validator) doCheckBuildInRules( // Values of two fields should be equal as string. case "same": if v, ok := dataMap[rulePattern]; ok { - if strings.Compare(valueStr, v) == 0 { + if strings.Compare(valueStr, gconv.String(v)) == 0 { match = true } } @@ -250,7 +248,7 @@ func (v *Validator) doCheckBuildInRules( case "different": match = true if v, ok := dataMap[rulePattern]; ok { - if strings.Compare(valueStr, v) == 0 { + if strings.Compare(valueStr, gconv.String(v)) == 0 { match = false } } diff --git a/util/gvalid/gvalid_validator_rule_required.go b/util/gvalid/gvalid_validator_rule_required.go index c86e1c8fd..969d7bb56 100644 --- a/util/gvalid/gvalid_validator_rule_required.go +++ b/util/gvalid/gvalid_validator_rule_required.go @@ -7,6 +7,7 @@ package gvalid import ( + "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/util/gconv" "reflect" "strings" @@ -14,7 +15,7 @@ import ( // checkRequired checks `value` using required rules. // It also supports require checks for `value` of type: slice, map. -func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string, params map[string]string) bool { +func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string, dataMap map[string]interface{}) bool { required := false switch ruleKey { // Required. @@ -31,8 +32,8 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string for i := 0; i < len(array); { tk := array[i] tv := array[i+1] - if v, ok := params[tk]; ok { - if strings.Compare(tv, v) == 0 { + if v, ok := dataMap[tk]; ok { + if strings.Compare(tv, gconv.String(v)) == 0 { required = true break } @@ -51,8 +52,8 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string for i := 0; i < len(array); { tk := array[i] tv := array[i+1] - if v, ok := params[tk]; ok { - if strings.Compare(tv, v) == 0 { + if v, ok := dataMap[tk]; ok { + if strings.Compare(tv, gconv.String(v)) == 0 { required = false break } @@ -67,7 +68,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string required = false array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { - if params[array[i]] != "" { + if !empty.IsEmpty(dataMap[array[i]]) { required = true break } @@ -79,7 +80,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string required = true array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { - if params[array[i]] == "" { + if empty.IsEmpty(dataMap[array[i]]) { required = false break } @@ -91,7 +92,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string required = false array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { - if params[array[i]] == "" { + if empty.IsEmpty(dataMap[array[i]]) { required = true break } @@ -103,7 +104,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string required = true array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { - if params[array[i]] != "" { + if !empty.IsEmpty(dataMap[array[i]]) { required = false break } diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index 25ffc0b65..bfde55995 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -88,6 +88,72 @@ func Test_RequiredWith(t *testing.T) { t.AssertNE(err2, nil) t.AssertNE(err3, nil) }) + // time.Time + gtest.C(t, func(t *gtest.T) { + rule := "required-with:id,time" + val1 := "" + params1 := g.Map{ + "age": 18, + } + params2 := g.Map{ + "id": 100, + } + params3 := g.Map{ + "time": time.Time{}, + } + err1 := gvalid.Check(val1, rule, nil, params1) + err2 := gvalid.Check(val1, rule, nil, params2) + err3 := gvalid.Check(val1, rule, nil, params3) + t.Assert(err1, nil) + t.AssertNE(err2, nil) + t.Assert(err3, nil) + }) + gtest.C(t, func(t *gtest.T) { + rule := "required-with:id,time" + val1 := "" + params1 := g.Map{ + "age": 18, + } + params2 := g.Map{ + "id": 100, + } + params3 := g.Map{ + "time": time.Now(), + } + err1 := gvalid.Check(val1, rule, nil, params1) + err2 := gvalid.Check(val1, rule, nil, params2) + err3 := gvalid.Check(val1, rule, nil, params3) + t.Assert(err1, nil) + t.AssertNE(err2, nil) + t.AssertNE(err3, nil) + }) + // gtime.Time + gtest.C(t, func(t *gtest.T) { + type UserApiSearch struct { + Uid int64 `json:"uid"` + Nickname string `json:"nickname" v:"required-with:Uid"` + StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"` + EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"` + } + data := UserApiSearch{ + StartTime: nil, + EndTime: nil, + } + t.Assert(gvalid.CheckStruct(data, nil), nil) + }) + gtest.C(t, func(t *gtest.T) { + type UserApiSearch struct { + Uid int64 `json:"uid"` + Nickname string `json:"nickname" v:"required-with:Uid"` + StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"` + EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"` + } + data := UserApiSearch{ + StartTime: nil, + EndTime: gtime.Now(), + } + t.AssertNE(gvalid.CheckStruct(data, nil), nil) + }) } func Test_RequiredWithAll(t *testing.T) { From f3983cd6b7d7594802716a322c373a1547f0a74f Mon Sep 17 00:00:00 2001 From: max <398820605@qq.com> Date: Wed, 28 Apr 2021 14:51:40 +0800 Subject: [PATCH 234/492] =?UTF-8?q?feat:=20pg=E6=9F=A5=E8=AF=A2=E8=A1=A8?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=97=B6=E8=8E=B7=E5=8F=96=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/gdb/gdb_driver_pgsql.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index a047733a2..b587c9fca 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -134,7 +134,7 @@ func (d *DriverPgsql) TableFields(table string, schema ...string) (fields map[st return nil, err } structureSql := fmt.Sprintf(` -SELECT a.attname AS field, t.typname AS type FROM pg_class c, pg_attribute a +SELECT a.attname AS field, t.typname AS type,b.description as comment FROM pg_class c, pg_attribute a LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,pg_type t WHERE c.relname = '%s' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid ORDER BY a.attnum`, @@ -152,6 +152,7 @@ ORDER BY a.attnum`, Index: i, Name: m["field"].String(), Type: m["type"].String(), + Comment: m["comment"].String(), } } return fields, nil From 563509c4a6cc08f010450cc9a932b0eff494387f Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 1 May 2021 09:04:16 +0800 Subject: [PATCH 235/492] route map dumping updates --- net/ghttp/ghttp_server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 68092e4e4..509ae5f65 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -263,12 +263,12 @@ func (s *Server) GetRouterArray() []RouterItem { if len(item.handler.middleware) > 0 { for _, v := range item.handler.middleware { if item.Middleware != "" { - item.Middleware += "," + item.Middleware += "\n" } item.Middleware += gdebug.FuncName(v) } } - // If the domain does not exist in the dump map, it create the map. + // If the domain does not exist in the dump map, it creates the map. // The value of the map is a custom sorted array. if _, ok := m[item.Domain]; !ok { // Sort in ASC order. From 5856f74d83efe0292ba5ffa6cd7aaf371a538e04 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 2 May 2021 08:10:35 +0800 Subject: [PATCH 236/492] up --- net/ghttp/ghttp_server.go | 2 +- net/ghttp/ghttp_server_handler.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 509ae5f65..7b347ce6d 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -263,7 +263,7 @@ func (s *Server) GetRouterArray() []RouterItem { if len(item.handler.middleware) > 0 { for _, v := range item.handler.middleware { if item.Middleware != "" { - item.Middleware += "\n" + item.Middleware += "," } item.Middleware += gdebug.FuncName(v) } diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index 5b0331ad4..bc2bb05d9 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -80,9 +80,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Close the request and response body // to release the file descriptor in time. - request.Request.Body.Close() + _ = request.Request.Body.Close() if request.Request.Response != nil { - request.Request.Response.Body.Close() + _ = request.Request.Response.Body.Close() } }() @@ -168,7 +168,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Automatically set the session id to cookie - // if it creates a new session id in this request. + // if it creates a new session id in this request + // and SessionCookieOutput is enabled. if s.config.SessionCookieOutput && request.Session.IsDirty() && request.Session.Id() != request.GetSessionId() { From d7eb1cca070e5c21d3299addc949fb3109315c93 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 2 May 2021 09:35:54 +0800 Subject: [PATCH 237/492] add nested transaction feature for package gdb --- database/gdb/gdb_transaction.go | 93 ++++++++++++- database/gdb/gdb_z_mysql_transaction_test.go | 134 +++++++++++++++++++ 2 files changed, 221 insertions(+), 6 deletions(-) diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_transaction.go index 5f4a35f79..7f71a3662 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_transaction.go @@ -9,6 +9,7 @@ package gdb import ( "database/sql" "fmt" + "github.com/gogf/gf/util/gconv" "reflect" "github.com/gogf/gf/text/gregex" @@ -16,21 +17,101 @@ import ( // TX is the struct for transaction management. type TX struct { - db DB - tx *sql.Tx - master *sql.DB + db DB // db is the current gdb database manager. + tx *sql.Tx // tx is the raw and underlying transaction manager. + master *sql.DB // master is the raw and underlying database manager. + transactionCount int // transactionCount marks the times that Begins. } -// Commit commits the transaction. +const ( + transactionPointerPrefix = "transaction" +) + +// Commit commits current transaction. +// Note that it releases previous saved transaction point if it's in a nested transaction procedure, +// or else it commits the hole transaction. func (tx *TX) Commit() error { + if tx.transactionCount > 0 { + tx.transactionCount-- + _, err := tx.Exec("RELEASE SAVEPOINT " + tx.transactionKey()) + return err + } return tx.tx.Commit() } -// Rollback aborts the transaction. +// Rollback aborts current transaction. +// Note that it aborts current transaction if it's in a nested transaction procedure, +// or else it aborts the hole transaction. func (tx *TX) Rollback() error { + if tx.transactionCount > 0 { + tx.transactionCount-- + _, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.transactionKey()) + return err + } return tx.tx.Rollback() } +// Begin starts a nested transaction procedure. +func (tx *TX) Begin() error { + _, err := tx.Exec("SAVEPOINT " + tx.transactionKey()) + if err != nil { + return err + } + tx.transactionCount++ + return nil +} + +// SavePoint performs `SAVEPOINT xxx` SQL statement that saves transaction at current point. +// The parameter `point` specifies the point name that will be saved to server. +func (tx *TX) SavePoint(point string) error { + _, err := tx.Exec("SAVEPOINT " + tx.db.QuoteWord(point)) + return err +} + +// RollbackTo performs `ROLLBACK TO SAVEPOINT xxx` SQL statement that rollbacks to specified saved transaction. +// The parameter `point` specifies the point name that was saved previously. +func (tx *TX) RollbackTo(point string) error { + _, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.db.QuoteWord(point)) + return err +} + +// transactionKey forms and returns the transaction key at current save point. +func (tx *TX) transactionKey() string { + return tx.db.QuoteWord(transactionPointerPrefix + gconv.String(tx.transactionCount)) +} + +// Transaction wraps the transaction logic using function `f`. +// It rollbacks the transaction and returns the error from function `f` if +// it returns non-nil error. It commits the transaction and returns nil if +// function `f` returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function `f` +// as it is automatically handled by this function. +func (tx *TX) Transaction(f func(tx *TX) error) (err error) { + err = tx.Begin() + if err != nil { + return err + } + defer func() { + if err == nil { + if e := recover(); e != nil { + err = fmt.Errorf("%v", e) + } + } + if err != nil { + if e := tx.Rollback(); e != nil { + err = e + } + } else { + if e := tx.Commit(); e != nil { + err = e + } + } + }() + err = f(tx) + return +} + // Query does query operation on transaction. // See Core.Query. func (tx *TX) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) { @@ -221,7 +302,7 @@ func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Res return tx.Model(table).Data(list).Insert() } -// BatchInsert batch inserts data with ignore option. +// BatchInsertIgnore batch inserts data with ignore option. // The parameter `list` must be type of slice of map or struct. func (tx *TX) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index 96b3dd978..264d59b47 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -789,3 +789,137 @@ func Test_Transaction_Panic(t *testing.T) { } }) } + +func Test_Transaction_Nested_Begin_Rollback_Commit(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + tx, err := db.Begin() + t.AssertNil(err) + // tx begin. + err = tx.Begin() + t.AssertNil(err) + // tx rollback. + _, err = tx.Model(table).Data(g.Map{ + "id": 1, + "passport": "user_1", + "password": "pass_1", + "nickname": "name_1", + }).Insert() + err = tx.Rollback() + t.AssertNil(err) + // tx commit. + _, err = tx.Model(table).Data(g.Map{ + "id": 2, + "passport": "user_2", + "password": "pass_2", + "nickname": "name_2", + }).Insert() + err = tx.Commit() + t.AssertNil(err) + // check data. + all, err := db.Model(table).All() + t.AssertNil(err) + t.Assert(len(all), 1) + t.Assert(all[0]["id"], 2) + }) +} + +func Test_Transaction_Nested_TX_Transaction(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + var err error + err = db.Transaction(func(tx *gdb.TX) error { + // commit + err = tx.Transaction(func(tx *gdb.TX) error { + err = tx.Transaction(func(tx *gdb.TX) error { + err = tx.Transaction(func(tx *gdb.TX) error { + err = tx.Transaction(func(tx *gdb.TX) error { + err = tx.Transaction(func(tx *gdb.TX) error { + _, err = tx.Model(table).Data(g.Map{ + "id": 1, + "passport": "USER_1", + "password": "PASS_1", + "nickname": "NAME_1", + "create_time": gtime.Now().String(), + }).Insert() + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + // rollback + err = tx.Transaction(func(tx *gdb.TX) error { + _, err = tx.Model(table).Data(g.Map{ + "id": 2, + "passport": "USER_2", + "password": "PASS_2", + "nickname": "NAME_2", + "create_time": gtime.Now().String(), + }).Insert() + t.AssertNil(err) + panic("error") + return err + }) + t.AssertNE(err, nil) + return nil + }) + t.AssertNil(err) + + all, err := db.Model(table).All() + t.AssertNil(err) + t.Assert(len(all), 1) + t.Assert(all[0]["id"], 1) + }) +} + +func Test_Transaction_Nested_SavePoint_RollbackTo(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + tx, err := db.Begin() + t.AssertNil(err) + // tx save point. + _, err = tx.Model(table).Data(g.Map{ + "id": 1, + "passport": "user_1", + "password": "pass_1", + "nickname": "name_1", + }).Insert() + err = tx.SavePoint("MyPoint") + t.AssertNil(err) + _, err = tx.Model(table).Data(g.Map{ + "id": 2, + "passport": "user_2", + "password": "pass_2", + "nickname": "name_2", + }).Insert() + // tx rollback to. + err = tx.RollbackTo("MyPoint") + t.AssertNil(err) + // tx commit. + err = tx.Commit() + t.AssertNil(err) + + // check data. + all, err := db.Model(table).All() + t.AssertNil(err) + t.Assert(len(all), 1) + t.Assert(all[0]["id"], 1) + }) +} From bd84b97614ef413a5f7d9df01ba7ade30cd948fb Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 2 May 2021 12:17:06 +0800 Subject: [PATCH 238/492] add more Where*/Min/Max/Avg/Sum/CountColumn/Increment/Decrement/OrderAsc/OrderDesc/OrderRandom functions and associated unit testing cases for package gdb --- database/gdb/gdb_core.go | 11 +- database/gdb/gdb_model.go | 1 + database/gdb/gdb_model_condition.go | 175 +++++++++++++-- database/gdb/gdb_model_fields.go | 34 +-- database/gdb/gdb_model_insert.go | 12 + database/gdb/gdb_model_select.go | 65 +++++- database/gdb/gdb_model_update.go | 16 ++ database/gdb/gdb_model_utility.go | 20 +- database/gdb/gdb_z_mysql_model_test.go | 294 +++++++++++++++++++++++++ 9 files changed, 579 insertions(+), 49 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 8265c540a..099ada0c3 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -812,13 +812,19 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str switch value := v.(type) { case *Counter: if value.Value != 0 { - column := c.db.QuoteWord(value.Field) + column := k + if value.Field != "" { + column = c.db.QuoteWord(value.Field) + } fields = append(fields, fmt.Sprintf("%s=%s+?", column, column)) params = append(params, value.Value) } case Counter: if value.Value != 0 { - column := c.db.QuoteWord(value.Field) + column := k + if value.Field != "" { + column = c.db.QuoteWord(value.Field) + } fields = append(fields, fmt.Sprintf("%s=%s+?", column, column)) params = append(params, value.Value) } @@ -829,7 +835,6 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str fields = append(fields, c.db.QuoteWord(k)+"=?") params = append(params, v) } - } } updates = strings.Join(fields, ",") diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 4b109534f..ae734d69e 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -39,6 +39,7 @@ type Model struct { data interface{} // Data for operation, which can be type of map/[]map/struct/*struct/string, etc. batch int // Batch number for batch Insert/Replace/Save operations. filter bool // Filter data and where key-value pairs according to the fields of the table. + distinct string // Force the query to only return distinct results. lockInfo string // Lock for update or in shared lock. cacheEnabled bool // Enable sql result cache feature. cacheDuration time.Duration // Cache TTL duration. diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index a78c43047..3ece170d8 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -7,6 +7,7 @@ package gdb import ( + "fmt" "strings" ) @@ -58,7 +59,125 @@ func (m *Model) WherePri(where interface{}, args ...interface{}) *Model { return m.Where(newWhere[0], newWhere[1:]...) } +// WhereBetween builds `xxx BETWEEN x AND y` statement. +func (m *Model) WhereBetween(column string, min, max interface{}) *Model { + return m.Where(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max) +} + +// WhereLike builds `xxx LIKE x` statement. +func (m *Model) WhereLike(column string, like interface{}) *Model { + return m.Where(fmt.Sprintf(`%s LIKE ?`, m.db.QuoteWord(column)), like) +} + +// WhereIn builds `xxx IN (x)` statement. +func (m *Model) WhereIn(column string, in interface{}) *Model { + return m.Where(fmt.Sprintf(`%s IN (?)`, m.db.QuoteWord(column)), in) +} + +// WhereNull builds `xxx IS NULL` statement. +func (m *Model) WhereNull(columns ...string) *Model { + model := m + for _, column := range columns { + model = m.Where(fmt.Sprintf(`%s IS NULL`, m.db.QuoteWord(column))) + } + return model +} + +// WhereNotBetween builds `xxx NOT BETWEEN x AND y` statement. +func (m *Model) WhereNotBetween(column string, min, max interface{}) *Model { + return m.Where(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max) +} + +// WhereNotLike builds `xxx NOT LIKE x` statement. +func (m *Model) WhereNotLike(column string, like interface{}) *Model { + return m.Where(fmt.Sprintf(`%s NOT LIKE ?`, m.db.QuoteWord(column)), like) +} + +// WhereNotIn builds `xxx NOT IN (x)` statement. +func (m *Model) WhereNotIn(column string, in interface{}) *Model { + return m.Where(fmt.Sprintf(`%s NOT IN (?)`, m.db.QuoteWord(column)), in) +} + +// WhereNotNull builds `xxx IS NOT NULL` statement. +func (m *Model) WhereNotNull(columns ...string) *Model { + model := m + for _, column := range columns { + model = m.Where(fmt.Sprintf(`%s IS NOT NULL`, m.db.QuoteWord(column))) + } + return model +} + +// WhereOr adds "OR" condition to the where statement. +func (m *Model) WhereOr(where interface{}, args ...interface{}) *Model { + model := m.getModel() + if model.whereHolder == nil { + model.whereHolder = make([]*whereHolder, 0) + } + model.whereHolder = append(model.whereHolder, &whereHolder{ + operator: whereHolderOr, + where: where, + args: args, + }) + return model +} + +// WhereOrBetween builds `xxx BETWEEN x AND y` statement in `OR` conditions. +func (m *Model) WhereOrBetween(column string, min, max interface{}) *Model { + return m.WhereOr(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max) +} + +// WhereOrLike builds `xxx LIKE x` statement in `OR` conditions. +func (m *Model) WhereOrLike(column string, like interface{}) *Model { + return m.WhereOr(fmt.Sprintf(`%s LIKE ?`, m.db.QuoteWord(column)), like) +} + +// WhereOrIn builds `xxx IN (x)` statement in `OR` conditions. +func (m *Model) WhereOrIn(column string, in interface{}) *Model { + return m.WhereOr(fmt.Sprintf(`%s IN (?)`, m.db.QuoteWord(column)), in) +} + +// WhereOrNull builds `xxx IS NULL` statement in `OR` conditions. +func (m *Model) WhereOrNull(columns ...string) *Model { + model := m + for _, column := range columns { + model = m.WhereOr(fmt.Sprintf(`%s IS NULL`, m.db.QuoteWord(column))) + } + return model +} + +// WhereOrNotBetween builds `xxx NOT BETWEEN x AND y` statement in `OR` conditions. +func (m *Model) WhereOrNotBetween(column string, min, max interface{}) *Model { + return m.WhereOr(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max) +} + +// WhereOrNotLike builds `xxx NOT LIKE x` statement in `OR` conditions. +func (m *Model) WhereOrNotLike(column string, like interface{}) *Model { + return m.WhereOr(fmt.Sprintf(`%s NOT LIKE ?`, m.db.QuoteWord(column)), like) +} + +// WhereOrNotIn builds `xxx NOT IN (x)` statement. +func (m *Model) WhereOrNotIn(column string, in interface{}) *Model { + return m.WhereOr(fmt.Sprintf(`%s NOT IN (?)`, m.db.QuoteWord(column)), in) +} + +// WhereOrNotNull builds `xxx IS NOT NULL` statement in `OR` conditions. +func (m *Model) WhereOrNotNull(columns ...string) *Model { + model := m + for _, column := range columns { + model = m.WhereOr(fmt.Sprintf(`%s IS NOT NULL`, m.db.QuoteWord(column))) + } + return model +} + +// Group sets the "GROUP BY" statement for the model. +func (m *Model) Group(groupBy string) *Model { + model := m.getModel() + model.groupBy = m.db.QuoteString(groupBy) + return model +} + // And adds "AND" condition to the where statement. +// Deprecated, use Where instead. func (m *Model) And(where interface{}, args ...interface{}) *Model { model := m.getModel() if model.whereHolder == nil { @@ -73,24 +192,9 @@ func (m *Model) And(where interface{}, args ...interface{}) *Model { } // Or adds "OR" condition to the where statement. +// Deprecated, use WhereOr instead. func (m *Model) Or(where interface{}, args ...interface{}) *Model { - model := m.getModel() - if model.whereHolder == nil { - model.whereHolder = make([]*whereHolder, 0) - } - model.whereHolder = append(model.whereHolder, &whereHolder{ - operator: whereHolderOr, - where: where, - args: args, - }) - return model -} - -// Group sets the "GROUP BY" statement for the model. -func (m *Model) Group(groupBy string) *Model { - model := m.getModel() - model.groupBy = m.db.QuoteString(groupBy) - return model + return m.WhereOr(where, args...) } // GroupBy is alias of Model.Group. @@ -102,11 +206,41 @@ func (m *Model) GroupBy(groupBy string) *Model { // Order sets the "ORDER BY" statement for the model. func (m *Model) Order(orderBy ...string) *Model { + if len(orderBy) == 0 { + return m + } model := m.getModel() model.orderBy = m.db.QuoteString(strings.Join(orderBy, " ")) return model } +// OrderAsc sets the "ORDER BY xxx ASC" statement for the model. +func (m *Model) OrderAsc(column string) *Model { + if len(column) == 0 { + return m + } + model := m.getModel() + model.orderBy = m.db.QuoteWord(column) + " ASC" + return model +} + +// OrderDesc sets the "ORDER BY xxx DESC" statement for the model. +func (m *Model) OrderDesc(column string) *Model { + if len(column) == 0 { + return m + } + model := m.getModel() + model.orderBy = m.db.QuoteWord(column) + " DESC" + return model +} + +// OrderRandom sets the "ORDER BY RANDOM()" statement for the model. +func (m *Model) OrderRandom(orderBy ...string) *Model { + model := m.getModel() + model.orderBy = "RAND()" + return model +} + // OrderBy is alias of Model.Order. // See Model.Order. // Deprecated, use Order instead. @@ -138,6 +272,13 @@ func (m *Model) Offset(offset int) *Model { return model } +// Distinct forces the query to only return distinct results. +func (m *Model) Distinct() *Model { + model := m.getModel() + model.distinct = "DISTINCT " + return model +} + // Page sets the paging number for the model. // The parameter `page` is started from 1 for paging. // Note that, it differs that the Limit function starts from 0 for "LIMIT" statement. diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index c10e00461..4d278358d 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -14,17 +14,6 @@ import ( "github.com/gogf/gf/util/gutil" ) -// Filter marks filtering the fields which does not exist in the fields of the operated table. -// Note that this function supports only single table operations. -func (m *Model) Filter() *Model { - if gstr.Contains(m.tables, " ") { - panic("function Filter supports only single table operations") - } - model := m.getModel() - model.filter = true - return model -} - // Fields sets the operation fields of the model, multiple fields joined using char ','. // The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct. func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model { @@ -81,13 +70,25 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model { return m } +// Filter marks filtering the fields which does not exist in the fields of the operated table. +// Note that this function supports only single table operations. +func (m *Model) Filter() *Model { + if gstr.Contains(m.tables, " ") { + panic("function Filter supports only single table operations") + } + model := m.getModel() + model.filter = true + return model +} + +// FieldsStr retrieves and returns all fields from the table, joined with char ','. +// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsStr("u."). // Deprecated, use GetFieldsStr instead. -// This function name confuses the user that it was a chaining function. func (m *Model) FieldsStr(prefix ...string) string { return m.GetFieldsStr(prefix...) } -// FieldsStr retrieves and returns all fields from the table, joined with char ','. +// GetFieldsStr retrieves and returns all fields from the table, joined with char ','. // The optional parameter `prefix` specifies the prefix for each field, eg: FieldsStr("u."). func (m *Model) GetFieldsStr(prefix ...string) string { prefixStr := "" @@ -116,13 +117,16 @@ func (m *Model) GetFieldsStr(prefix ...string) string { return newFields } +// FieldsExStr retrieves and returns fields which are not in parameter `fields` from the table, +// joined with char ','. +// The parameter `fields` specifies the fields that are excluded. +// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsExStr("id", "u."). // Deprecated, use GetFieldsExStr instead. -// This function name confuses the user that it was a chaining function. func (m *Model) FieldsExStr(fields string, prefix ...string) string { return m.GetFieldsExStr(fields, prefix...) } -// FieldsExStr retrieves and returns fields which are not in parameter `fields` from the table, +// GetFieldsExStr retrieves and returns fields which are not in parameter `fields` from the table, // joined with char ','. // The parameter `fields` specifies the fields that are excluded. // The optional parameter `prefix` specifies the prefix for each field, eg: FieldsExStr("id", "u."). diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index b0959d304..d67978b72 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -109,6 +109,18 @@ func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) { return m.doInsertWithOption(insertOptionDefault) } +// InsertAndGetId performs action Insert and returns the last insert id that automatically generated. +func (m *Model) InsertAndGetId(data ...interface{}) (lastInsertId int64, err error) { + if len(data) > 0 { + return m.Data(data...).InsertAndGetId() + } + result, err := m.doInsertWithOption(insertOptionDefault) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + // InsertIgnore does "INSERT IGNORE INTO ..." statement for the model. // The optional parameter `data` is the same as the parameter of Model.Data function, // see Model.Data. diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 962592b33..31fd0007d 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -19,7 +19,7 @@ import ( // Select is alias of Model.All. // See Model.All. -// Deprecated. +// Deprecated, use All instead. func (m *Model) Select(where ...interface{}) (Result, error) { return m.All(where...) } @@ -50,7 +50,8 @@ func (m *Model) doGetAll(limit1 bool, where ...interface{}) (Result, error) { // DISTINCT t.user_id uid return m.doGetAllBySql( fmt.Sprintf( - "SELECT %s FROM %s%s", + "SELECT %s%s FROM %s%s", + m.distinct, m.getFieldsFiltered(), m.tables, conditionWhere+conditionExtra, @@ -182,7 +183,7 @@ func (m *Model) Value(fieldsAndWhere ...interface{}) (Value, error) { } // Array queries and returns data values as slice from database. -// Note that if there're multiple columns in the result, it returns just one column values randomly. +// Note that if there are multiple columns in the result, it returns just one column values randomly. // // If the optional parameter `fieldsAndWhere` is given, the fieldsAndWhere[0] is the selected fields // and fieldsAndWhere[1:] is treated as where condition fields. @@ -334,7 +335,7 @@ func (m *Model) Count(where ...interface{}) (int, error) { if m.fields != "" && m.fields != "*" { // DO NOT quote the m.fields here, in case of fields like: // DISTINCT t.user_id uid - countFields = fmt.Sprintf(`COUNT(%s)`, m.fields) + countFields = fmt.Sprintf(`COUNT(%s%s)`, m.distinct, m.fields) } conditionWhere, conditionExtra, conditionArgs := m.formatCondition(false, true) s := fmt.Sprintf("SELECT %s FROM %s%s", countFields, m.tables, conditionWhere+conditionExtra) @@ -353,6 +354,62 @@ func (m *Model) Count(where ...interface{}) (int, error) { return 0, nil } +// CountColumn does "SELECT COUNT(x) FROM ..." statement for the model. +func (m *Model) CountColumn(column string) (int, error) { + if len(column) == 0 { + return 0, nil + } + return m.Fields(column).Count() +} + +// Min does "SELECT MIN(x) FROM ..." statement for the model. +func (m *Model) Min(column string) (float64, error) { + if len(column) == 0 { + return 0, nil + } + value, err := m.Fields(fmt.Sprintf(`MIN(%s)`, m.db.QuoteWord(column))).Value() + if err != nil { + return 0, err + } + return value.Float64(), err +} + +// Max does "SELECT MAX(x) FROM ..." statement for the model. +func (m *Model) Max(column string) (float64, error) { + if len(column) == 0 { + return 0, nil + } + value, err := m.Fields(fmt.Sprintf(`MAX(%s)`, m.db.QuoteWord(column))).Value() + if err != nil { + return 0, err + } + return value.Float64(), err +} + +// Avg does "SELECT AVG(x) FROM ..." statement for the model. +func (m *Model) Avg(column string) (float64, error) { + if len(column) == 0 { + return 0, nil + } + value, err := m.Fields(fmt.Sprintf(`AVG(%s)`, m.db.QuoteWord(column))).Value() + if err != nil { + return 0, err + } + return value.Float64(), err +} + +// Sum does "SELECT SUM(x) FROM ..." statement for the model. +func (m *Model) Sum(column string) (float64, error) { + if len(column) == 0 { + return 0, nil + } + value, err := m.Fields(fmt.Sprintf(`SUM(%s)`, m.db.QuoteWord(column))).Value() + if err != nil { + return 0, err + } + return value.Float64(), err +} + // FindOne retrieves and returns a single Record by Model.WherePri and Model.One. // Also see Model.WherePri and Model.One. func (m *Model) FindOne(where ...interface{}) (Record, error) { diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index b1f15ff62..ad65e723f 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -89,3 +89,19 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro m.mergeArguments(conditionArgs)..., ) } + +// Increment increments a column's value by a given amount. +func (m *Model) Increment(column string, amount float64) (sql.Result, error) { + return m.getModel().Data(column, &Counter{ + Field: column, + Value: amount, + }).Update() +} + +// Decrement decrements a column's value by a given amount. +func (m *Model) Decrement(column string, amount float64) (sql.Result, error) { + return m.getModel().Data(column, &Counter{ + Field: column, + Value: -amount, + }).Update() +} diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 93890326b..944946bb6 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -18,16 +18,6 @@ import ( "time" ) -// getModel creates and returns a cloned model of current model if `safe` is true, or else it returns -// the current model. -func (m *Model) getModel() *Model { - if !m.safe { - return m - } else { - return m.Clone() - } -} - // TableFields retrieves and returns the fields information of specified table of current // schema. // @@ -47,6 +37,16 @@ func (m *Model) TableFields(table string, schema ...string) (fields map[string]* return m.db.TableFields(link, table, schema...) } +// getModel creates and returns a cloned model of current model if `safe` is true, or else it returns +// the current model. +func (m *Model) getModel() *Model { + if !m.safe { + return m + } else { + return m.Clone() + } +} + // mappingAndFilterToTableFields mappings and changes given field name to really table field name. // Eg: // ID -> id diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 0eb1b454a..e74123571 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -2778,6 +2778,11 @@ func Test_Model_Distinct(t *testing.T) { t.AssertNil(err) t.Assert(len(all), 2) }) + gtest.C(t, func(t *gtest.T) { + count, err := db.Model(table).Where("id > 1").Distinct().Count() + t.AssertNil(err) + t.Assert(count, 9) + }) } func Test_Model_Min_Max(t *testing.T) { @@ -3321,3 +3326,292 @@ func Test_Model_Fields_AutoFilterInJoinStatement(t *testing.T) { t.Assert(one["number"].String(), "n") }) } + +func Test_Model_WhereIn(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereIn("id", g.Slice{1, 2, 3, 4}).WhereIn("id", g.Slice{3, 4, 5}).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 2) + t.Assert(result[0]["id"], 3) + t.Assert(result[1]["id"], 4) + }) +} + +func Test_Model_WhereNotIn(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereNotIn("id", g.Slice{1, 2, 3, 4}).WhereNotIn("id", g.Slice{3, 4, 5}).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 5) + t.Assert(result[0]["id"], 6) + t.Assert(result[1]["id"], 7) + }) +} + +func Test_Model_WhereOrIn(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereOrIn("id", g.Slice{1, 2, 3, 4}).WhereOrIn("id", g.Slice{3, 4, 5}).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 5) + t.Assert(result[0]["id"], 1) + t.Assert(result[4]["id"], 5) + }) +} + +func Test_Model_WhereOrNotIn(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereOrNotIn("id", g.Slice{1, 2, 3, 4}).WhereOrNotIn("id", g.Slice{3, 4, 5}).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 8) + t.Assert(result[0]["id"], 1) + t.Assert(result[4]["id"], 7) + }) +} + +func Test_Model_WhereBetween(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereBetween("id", 1, 4).WhereBetween("id", 3, 5).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 2) + t.Assert(result[0]["id"], 3) + t.Assert(result[1]["id"], 4) + }) +} + +func Test_Model_WhereNotBetween(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereNotBetween("id", 2, 8).WhereNotBetween("id", 3, 100).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 1) + t.Assert(result[0]["id"], 1) + }) +} + +func Test_Model_WhereOrBetween(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereOrBetween("id", 1, 4).WhereOrBetween("id", 3, 5).OrderDesc("id").All() + t.AssertNil(err) + t.Assert(len(result), 5) + t.Assert(result[0]["id"], 5) + t.Assert(result[4]["id"], 1) + }) +} + +func Test_Model_WhereOrNotBetween(t *testing.T) { + table := createInitTable() + defer dropTable(table) + //db.SetDebug(true) + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereOrNotBetween("id", 1, 4).WhereOrNotBetween("id", 3, 5).OrderDesc("id").All() + t.AssertNil(err) + t.Assert(len(result), 8) + t.Assert(result[0]["id"], 10) + t.Assert(result[4]["id"], 6) + }) +} + +func Test_Model_WhereLike(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereLike("nickname", "name%").OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), TableSize) + t.Assert(result[0]["id"], 1) + t.Assert(result[TableSize-1]["id"], TableSize) + }) +} + +func Test_Model_WhereNotLike(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereNotLike("nickname", "name%").OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 0) + }) +} + +func Test_Model_WhereOrLike(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereOrLike("nickname", "namexxx%").WhereOrLike("nickname", "name%").OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), TableSize) + t.Assert(result[0]["id"], 1) + t.Assert(result[TableSize-1]["id"], TableSize) + }) +} + +func Test_Model_WhereOrNotLike(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereOrNotLike("nickname", "namexxx%").WhereOrNotLike("nickname", "name%").OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), TableSize) + t.Assert(result[0]["id"], 1) + t.Assert(result[TableSize-1]["id"], TableSize) + }) +} + +func Test_Model_WhereNull(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereNull("nickname").WhereNull("passport").OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 0) + }) +} + +func Test_Model_WhereNotNull(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereNotNull("nickname").WhereNotNull("passport").OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), TableSize) + t.Assert(result[0]["id"], 1) + t.Assert(result[TableSize-1]["id"], TableSize) + }) +} + +func Test_Model_WhereOrNull(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereOrNull("nickname").WhereOrNull("passport").OrderAsc("id").OrderRandom().All() + t.AssertNil(err) + t.Assert(len(result), 0) + }) +} + +func Test_Model_WhereOrNotNull(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereOrNotNull("nickname").WhereOrNotNull("passport").OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), TableSize) + t.Assert(result[0]["id"], 1) + t.Assert(result[TableSize-1]["id"], TableSize) + }) +} + +func Test_Model_Min_Max_Avg_Sum(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).Min("id") + t.AssertNil(err) + t.Assert(result, 1) + }) + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).Max("id") + t.AssertNil(err) + t.Assert(result, TableSize) + }) + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).Avg("id") + t.AssertNil(err) + t.Assert(result, 5.5) + }) + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).Sum("id") + t.AssertNil(err) + t.Assert(result, 55) + }) +} + +func Test_Model_CountColumn(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).CountColumn("id") + t.AssertNil(err) + t.Assert(result, TableSize) + }) + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).CountColumn("id") + t.AssertNil(err) + t.Assert(result, 3) + }) +} + +func Test_Model_InsertAndGetId(t *testing.T) { + table := createTable() + defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + id, err := db.Model(table).Data(g.Map{ + "id": 1, + "passport": "user_1", + "password": "pass_1", + "nickname": "name_1", + }).InsertAndGetId() + t.AssertNil(err) + t.Assert(id, 1) + }) + gtest.C(t, func(t *gtest.T) { + id, err := db.Model(table).Data(g.Map{ + "passport": "user_2", + "password": "pass_2", + "nickname": "name_2", + }).InsertAndGetId() + t.AssertNil(err) + t.Assert(id, 2) + }) +} + +func Test_Model_Increment_Decrement(t *testing.T) { + table := createInitTable() + defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).Where("id", 1).Increment("id", 100) + t.AssertNil(err) + rows, _ := result.RowsAffected() + t.Assert(rows, 1) + }) + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).Where("id", 101).Decrement("id", 10) + t.AssertNil(err) + rows, _ := result.RowsAffected() + t.Assert(rows, 1) + }) + gtest.C(t, func(t *gtest.T) { + count, err := db.Model(table).Where("id", 91).Count() + t.AssertNil(err) + t.Assert(count, 1) + }) +} From cdc97e9b2b7916c5b3f2efe1f5489b012f182474 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 2 May 2021 15:58:28 +0800 Subject: [PATCH 239/492] improve logging for transaction feature for package gdb --- .../database/gdb/mysql/gdb_transaction.go | 27 +++++++++++ .../gdb/mysql/gdb_transaction_closure.go | 34 ++++++++++++++ .../gdb/mysql/gdb_transaction_savepoint.go | 40 +++++++++++++++++ database/gdb/gdb.go | 6 ++- database/gdb/gdb_transaction.go | 45 ++++++++++++++++++- 5 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 .example/database/gdb/mysql/gdb_transaction.go create mode 100644 .example/database/gdb/mysql/gdb_transaction_closure.go create mode 100644 .example/database/gdb/mysql/gdb_transaction_savepoint.go diff --git a/.example/database/gdb/mysql/gdb_transaction.go b/.example/database/gdb/mysql/gdb_transaction.go new file mode 100644 index 000000000..fe5642ca2 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_transaction.go @@ -0,0 +1,27 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" +) + +func main() { + var ( + db = g.DB() + table = "user" + ) + tx, err := db.Begin() + if err != nil { + panic(err) + } + if err = tx.Begin(); err != nil { + panic(err) + } + _, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert() + if err = tx.Rollback(); err != nil { + panic(err) + } + _, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert() + if err = tx.Commit(); err != nil { + panic(err) + } +} diff --git a/.example/database/gdb/mysql/gdb_transaction_closure.go b/.example/database/gdb/mysql/gdb_transaction_closure.go new file mode 100644 index 000000000..aa05d48d4 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_transaction_closure.go @@ -0,0 +1,34 @@ +package main + +import ( + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" +) + +func main() { + var ( + err error + db = g.DB() + table = "user" + ) + if err = db.Transaction(func(tx *gdb.TX) error { + // Nested transaction 1. + if err = tx.Transaction(func(tx *gdb.TX) error { + _, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert() + return err + }); err != nil { + return err + } + // Nested transaction 2, panic. + if err = tx.Transaction(func(tx *gdb.TX) error { + _, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert() + // Create a panic that can make this transaction rollback automatically. + panic("error") + }); err != nil { + return err + } + return nil + }); err != nil { + panic(err) + } +} diff --git a/.example/database/gdb/mysql/gdb_transaction_savepoint.go b/.example/database/gdb/mysql/gdb_transaction_savepoint.go new file mode 100644 index 000000000..e677f15ab --- /dev/null +++ b/.example/database/gdb/mysql/gdb_transaction_savepoint.go @@ -0,0 +1,40 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" +) + +func main() { + var ( + err error + db = g.DB() + table = "user" + ) + tx, err := db.Begin() + if err != nil { + panic(err) + } + defer func() { + if err := recover(); err != nil { + _ = tx.Rollback() + } + }() + if _, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert(); err != nil { + panic(err) + } + if err = tx.SavePoint("MyPoint"); err != nil { + panic(err) + } + if _, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert(); err != nil { + panic(err) + } + if _, err = tx.Model(table).Data(g.Map{"id": 3, "name": "green"}).Insert(); err != nil { + panic(err) + } + if err = tx.RollbackTo("MyPoint"); err != nil { + panic(err) + } + if err = tx.Commit(); err != nil { + panic(err) + } +} diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index b9a448b7d..be69262a6 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -32,11 +32,11 @@ type DB interface { // Model creation. // =========================================================================== + // Table function is deprecated, use Model instead. // The DB interface is designed not only for // relational databases but also for NoSQL databases in the future. The name // "Table" is not proper for that purpose any more. - // Also see Core.Table. - // Deprecated, use Model instead. + // Also see Core.Table. Table(table ...string) *Model // Model creates and returns a new ORM model from given schema. @@ -191,6 +191,8 @@ type DB interface { mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) // See Core.mappingAndFilterData. convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{} // See Core.convertFieldValueToLocalValue. convertRowsToResult(rows *sql.Rows) (Result, error) // See Core.convertRowsToResult. + addSqlToTracing(ctx context.Context, sql *Sql) // See Core.addSqlToTracing. + writeSqlToLogger(v *Sql) // See Core.writeSqlToLogger. } // Core is the base struct for database management. diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_transaction.go index 7f71a3662..63031b815 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_transaction.go @@ -9,6 +9,7 @@ package gdb import ( "database/sql" "fmt" + "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" "reflect" @@ -36,7 +37,27 @@ func (tx *TX) Commit() error { _, err := tx.Exec("RELEASE SAVEPOINT " + tx.transactionKey()) return err } - return tx.tx.Commit() + var ( + sqlStr = "COMMIT" + mTime1 = gtime.TimestampMilli() + err = tx.tx.Commit() + mTime2 = gtime.TimestampMilli() + sqlObj = &Sql{ + Sql: sqlStr, + Type: "TX.Commit", + Args: nil, + Format: sqlStr, + Error: err, + Start: mTime1, + End: mTime2, + Group: tx.db.GetGroup(), + } + ) + tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj) + if tx.db.GetDebug() { + tx.db.writeSqlToLogger(sqlObj) + } + return err } // Rollback aborts current transaction. @@ -48,7 +69,27 @@ func (tx *TX) Rollback() error { _, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.transactionKey()) return err } - return tx.tx.Rollback() + var ( + sqlStr = "ROLLBACK" + mTime1 = gtime.TimestampMilli() + err = tx.tx.Rollback() + mTime2 = gtime.TimestampMilli() + sqlObj = &Sql{ + Sql: sqlStr, + Type: "TX.Rollback", + Args: nil, + Format: sqlStr, + Error: err, + Start: mTime1, + End: mTime2, + Group: tx.db.GetGroup(), + } + ) + tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj) + if tx.db.GetDebug() { + tx.db.writeSqlToLogger(sqlObj) + } + return err } // Begin starts a nested transaction procedure. From 4b15ab5e99d0de8a62b9f4f73071f89cef6e0c40 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 2 May 2021 16:42:34 +0800 Subject: [PATCH 240/492] improve OrderRandom function for package gdb --- database/gdb/gdb_model_condition.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 3ece170d8..b71288647 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -235,7 +235,7 @@ func (m *Model) OrderDesc(column string) *Model { } // OrderRandom sets the "ORDER BY RANDOM()" statement for the model. -func (m *Model) OrderRandom(orderBy ...string) *Model { +func (m *Model) OrderRandom() *Model { model := m.getModel() model.orderBy = "RAND()" return model From df1ef5db78c0d8ab10a47817397b12f51560cf84 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 2 May 2021 17:57:00 +0800 Subject: [PATCH 241/492] add example for package gdb --- .example/database/gdb/mysql/gdb_distinct.go | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .example/database/gdb/mysql/gdb_distinct.go diff --git a/.example/database/gdb/mysql/gdb_distinct.go b/.example/database/gdb/mysql/gdb_distinct.go new file mode 100644 index 000000000..189422f6b --- /dev/null +++ b/.example/database/gdb/mysql/gdb_distinct.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" +) + +func main() { + g.DB().Model("user").Distinct().CountColumn("uid,name") +} From a8c3d07d9ff44495d63ebaa7268f63ceeb93db37 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 2 May 2021 22:35:47 +0800 Subject: [PATCH 242/492] improve with feature for package gdb --- .../database/gdb/mysql/gdb_with_insert.go | 66 ++++++++++++++++ .example/database/gdb/mysql/gdb_with_slect.go | 37 +++++++++ database/gdb/gdb.go | 6 +- database/gdb/gdb_func.go | 2 +- database/gdb/gdb_model.go | 54 ++++++++----- database/gdb/gdb_model_fields.go | 1 + database/gdb/gdb_model_with.go | 14 ++-- .../gdb/gdb_z_mysql_association_with_test.go | 75 +++++++++++++------ database/gdb/gdb_z_mysql_method_test.go | 49 ++++++------ database/gdb/gdb_z_mysql_model_test.go | 49 ++++++------ frame/g/g_object.go | 13 +--- 11 files changed, 258 insertions(+), 108 deletions(-) create mode 100644 .example/database/gdb/mysql/gdb_with_insert.go create mode 100644 .example/database/gdb/mysql/gdb_with_slect.go diff --git a/.example/database/gdb/mysql/gdb_with_insert.go b/.example/database/gdb/mysql/gdb_with_insert.go new file mode 100644 index 000000000..669ab3ce6 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_with_insert.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/util/gmeta" +) + +func main() { + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScore struct { + gmeta.Meta `orm:"table:user_score"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:user"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id"` + UserScores []*UserScore `orm:"with:uid=id"` + } + + db := g.DB() + db.Transaction(func(tx *gdb.TX) error { + for i := 1; i <= 5; i++ { + // User. + user := User{ + Name: fmt.Sprintf(`name_%d`, i), + } + lastInsertId, err := db.Model(user).Data(user).OmitEmpty().InsertAndGetId() + if err != nil { + return err + } + // Detail. + userDetail := UserDetail{ + Uid: int(lastInsertId), + Address: fmt.Sprintf(`address_%d`, lastInsertId), + } + _, err = db.Model(userDetail).Data(userDetail).OmitEmpty().Insert() + if err != nil { + return err + } + // Scores. + for j := 1; j <= 5; j++ { + userScore := UserScore{ + Uid: int(lastInsertId), + Score: j, + } + _, err = db.Model(userScore).Data(userScore).OmitEmpty().Insert() + if err != nil { + return err + } + } + } + return nil + }) +} diff --git a/.example/database/gdb/mysql/gdb_with_slect.go b/.example/database/gdb/mysql/gdb_with_slect.go new file mode 100644 index 000000000..e15cf40f9 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_with_slect.go @@ -0,0 +1,37 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/util/gmeta" +) + +func main() { + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScore struct { + gmeta.Meta `orm:"table:user_score"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:user"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id"` + UserScores []*UserScore `orm:"with:uid=id"` + } + + db := g.DB() + var user *User + err := db.Model(user).WithAll().Where("id", 3).Scan(&user) + if err != nil { + panic(err) + } + g.Dump(user) +} diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index be69262a6..24e6228db 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -37,7 +37,7 @@ type DB interface { // relational databases but also for NoSQL databases in the future. The name // "Table" is not proper for that purpose any more. // Also see Core.Table. - Table(table ...string) *Model + Table(tableNameOrStruct ...interface{}) *Model // Model creates and returns a new ORM model from given schema. // The parameter `table` can be more than one table names, and also alias name, like: @@ -48,7 +48,7 @@ type DB interface { // Model("user u, user_detail ud") // 2. Model name with alias: Model("user", "u") // Also see Core.Model. - Model(table ...string) *Model + Model(tableNameOrStruct ...interface{}) *Model // Schema creates and returns a schema. // Also see Core.Schema. @@ -56,7 +56,7 @@ type DB interface { // With creates and returns an ORM model based on meta data of given object. // Also see Core.With. - With(object interface{}) *Model + With(objects ...interface{}) *Model // Open creates a raw connection object for database with given node configuration. // Note that it is not recommended using the this function manually. diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 162f08e8c..ec2b7d7dc 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -235,7 +235,7 @@ func DataToMapDeep(value interface{}) map[string]interface{} { name = "" fieldTag = rtField.Tag for _, tag := range structTagPriority { - if s := fieldTag.Get(tag); s != "" { + if s := fieldTag.Get(tag); s != "" && gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, s) { name = s break } diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index ae734d69e..f345ad857 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -70,53 +70,71 @@ const ( // Table is alias of Core.Model. // See Core.Model. // Deprecated, use Model instead. -func (c *Core) Table(table ...string) *Model { - return c.db.Model(table...) +func (c *Core) Table(tableNameOrStruct ...interface{}) *Model { + return c.db.Model(tableNameOrStruct...) } // Model creates and returns a new ORM model from given schema. -// The parameter `table` can be more than one table names, and also alias name, like: +// The parameter `tableNameOrStruct` can be more than one table names, and also alias name, like: // 1. Model names: // Model("user") // Model("user u") // Model("user, user_detail") // Model("user u, user_detail ud") // 2. Model name with alias: Model("user", "u") -func (c *Core) Model(table ...string) *Model { - tables := "" - if len(table) > 1 { - tables = fmt.Sprintf( - `%s AS %s`, c.db.QuotePrefixTableName(table[0]), c.db.QuoteWord(table[1]), +func (c *Core) Model(tableNameOrStruct ...interface{}) *Model { + // With feature checks. + if len(tableNameOrStruct) > 0 { + if _, ok := tableNameOrStruct[0].(string); !ok { + return c.With(tableNameOrStruct...) + } + } + // Normal model creation. + var ( + tableStr = "" + tableNames = make([]string, len(tableNameOrStruct)) + ) + for k, v := range tableNameOrStruct { + if s, ok := v.(string); ok { + tableNames[k] = s + continue + } + } + + if len(tableNames) > 1 { + tableStr = fmt.Sprintf( + `%s AS %s`, c.db.QuotePrefixTableName(tableNames[0]), c.db.QuoteWord(tableNames[1]), ) - } else if len(table) == 1 { - tables = c.db.QuotePrefixTableName(table[0]) + } else if len(tableNames) == 1 { + tableStr = c.db.QuotePrefixTableName(tableNames[0]) } return &Model{ db: c.db, - tablesInit: tables, - tables: tables, + tablesInit: tableStr, + tables: tableStr, fields: "*", start: -1, offset: -1, option: OptionAllowEmpty, + filter: true, } } // With creates and returns an ORM model based on meta data of given object. -func (c *Core) With(object interface{}) *Model { - return c.db.Model().With(object) +func (c *Core) With(objects ...interface{}) *Model { + return c.db.Model().With(objects...) } // Table is alias of tx.Model. // Deprecated, use Model instead. -func (tx *TX) Table(table ...string) *Model { - return tx.Model(table...) +func (tx *TX) Table(tableNameOrStruct ...interface{}) *Model { + return tx.Model(tableNameOrStruct...) } // Model acts like Core.Model except it operates on transaction. // See Core.Model. -func (tx *TX) Model(table ...string) *Model { - model := tx.db.Model(table...) +func (tx *TX) Model(tableNameOrStruct ...interface{}) *Model { + model := tx.db.Model(tableNameOrStruct...) model.db = tx.db model.tx = tx return model diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index 4d278358d..be082b070 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -72,6 +72,7 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model { // Filter marks filtering the fields which does not exist in the fields of the operated table. // Note that this function supports only single table operations. +// Deprecated, filter feature is automatically enabled from GoFrame v1.16.0, it is so no longer used. func (m *Model) Filter() *Model { if gstr.Contains(m.tables, " ") { panic("function Filter supports only single table operations") diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 8ead1401c..2810d9a25 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -32,13 +32,17 @@ import ( // db.With(User{}.UserDetail).With(User{}.UserDetail).Scan(xxx) // Or: // db.With(UserDetail{}).With(UserDetail{}).Scan(xxx) -func (m *Model) With(object interface{}) *Model { +// Or: +// db.With(UserDetail{}, UserDetail{}).Scan(xxx) +func (m *Model) With(objects ...interface{}) *Model { model := m.getModel() - if m.tables == "" { - m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(object)) - return model + for _, object := range objects { + if m.tables == "" { + m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(object)) + return model + } + model.withArray = append(model.withArray, object) } - model.withArray = append(model.withArray, object) return model } diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index f4633f50b..d8a34cae9 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -18,7 +18,7 @@ func Test_Table_Relation_With_Scan(t *testing.T) { var ( tableUser = "user" tableUserDetail = "user_detail" - tableUserScores = "user_scores" + tableUserScores = "user_score" ) if _, err := db.Exec(fmt.Sprintf(` CREATE TABLE IF NOT EXISTS %s ( @@ -60,8 +60,8 @@ PRIMARY KEY (id) Address string `json:"address"` } - type UserScores struct { - gmeta.Meta `orm:"table:user_scores"` + type UserScore struct { + gmeta.Meta `orm:"table:user_score"` Id int `json:"id"` Uid int `json:"uid"` Score int `json:"score"` @@ -69,34 +69,61 @@ PRIMARY KEY (id) type User struct { gmeta.Meta `orm:"table:user"` - Id int `json:"id"` - Name string `json:"name"` - UserDetail *UserDetail `orm:"with:uid=id"` - UserScores []*UserScores `orm:"with:uid=id"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id"` + UserScores []*UserScore `orm:"with:uid=id"` } // Initialize the data. - var err error + gtest.C(t, func(t *gtest.T) { + for i := 1; i <= 5; i++ { + // User. + user := User{ + Name: fmt.Sprintf(`name_%d`, i), + } + lastInsertId, err := db.Model(user).Data(user).OmitEmpty().InsertAndGetId() + t.AssertNil(err) + // Detail. + userDetail := UserDetail{ + Uid: int(lastInsertId), + Address: fmt.Sprintf(`address_%d`, lastInsertId), + } + _, err = db.Model(userDetail).Data(userDetail).OmitEmpty().Insert() + t.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + userScore := UserScore{ + Uid: int(lastInsertId), + Score: j, + } + _, err = db.Model(userScore).Data(userScore).OmitEmpty().Insert() + t.AssertNil(err) + } + } + }) for i := 1; i <= 5; i++ { // User. - _, err = db.Insert(tableUser, g.Map{ - "id": i, - "name": fmt.Sprintf(`name_%d`, i), - }) - gtest.Assert(err, nil) + user := User{ + Name: fmt.Sprintf(`name_%d`, i), + } + lastInsertId, err := db.Model(user).Data(user).OmitEmpty().InsertAndGetId() + gtest.AssertNil(err) // Detail. - _, err = db.Insert(tableUserDetail, g.Map{ - "uid": i, - "address": fmt.Sprintf(`address_%d`, i), - }) - gtest.Assert(err, nil) + userDetail := UserDetail{ + Uid: int(lastInsertId), + Address: fmt.Sprintf(`address_%d`, lastInsertId), + } + _, err = db.Model(userDetail).Data(userDetail).Insert() + gtest.AssertNil(err) // Scores. for j := 1; j <= 5; j++ { - _, err = db.Insert(tableUserScores, g.Map{ - "uid": i, - "score": j, - }) - gtest.Assert(err, nil) + userScore := UserScore{ + Uid: int(lastInsertId), + Score: j, + } + _, err = db.Model(userScore).Data(userScore).Insert() + gtest.AssertNil(err) } } gtest.C(t, func(t *gtest.T) { @@ -139,7 +166,7 @@ PRIMARY KEY (id) var user *User err := db.With(User{}). With(UserDetail{}). - With(UserScores{}). + With(UserScore{}). Where("id", 4). Scan(&user) t.AssertNil(err) diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index 9db3b9f6c..d83f2f364 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -274,30 +274,31 @@ func Test_DB_Upadte_KeyFieldNameMapping(t *testing.T) { }) } -func Test_DB_Insert_KeyFieldNameMapping_Error(t *testing.T) { - table := createTable() - defer dropTable(table) - - gtest.C(t, func(t *gtest.T) { - type User struct { - Id int - Passport string - Password string - Nickname string - CreateTime string - NoneExistField string - } - data := User{ - Id: 1, - Passport: "user_1", - Password: "pass_1", - Nickname: "name_1", - CreateTime: "2020-10-10 12:00:01", - } - _, err := db.Insert(table, data) - t.AssertNE(err, nil) - }) -} +// This is no longer used as the filter feature is automatically enabled from GoFrame v1.16.0. +//func Test_DB_Insert_KeyFieldNameMapping_Error(t *testing.T) { +// table := createTable() +// defer dropTable(table) +// +// gtest.C(t, func(t *gtest.T) { +// type User struct { +// Id int +// Passport string +// Password string +// Nickname string +// CreateTime string +// NoneExistField string +// } +// data := User{ +// Id: 1, +// Passport: "user_1", +// Password: "pass_1", +// Nickname: "name_1", +// CreateTime: "2020-10-10 12:00:01", +// } +// _, err := db.Insert(table, data) +// t.AssertNE(err, nil) +// }) +//} func Test_DB_InsertIgnore(t *testing.T) { table := createInitTable() diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index e74123571..f7a5fc857 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -240,30 +240,31 @@ func Test_Model_Update_KeyFieldNameMapping(t *testing.T) { }) } -func Test_Model_Insert_KeyFieldNameMapping_Error(t *testing.T) { - table := createTable() - defer dropTable(table) - - gtest.C(t, func(t *gtest.T) { - type User struct { - Id int - Passport string - Password string - Nickname string - CreateTime string - NoneExistFiled string - } - data := User{ - Id: 1, - Passport: "user_1", - Password: "pass_1", - Nickname: "name_1", - CreateTime: "2020-10-10 12:00:01", - } - _, err := db.Model(table).Data(data).Insert() - t.AssertNE(err, nil) - }) -} +// This is no longer used as the filter feature is automatically enabled from GoFrame v1.16.0. +//func Test_Model_Insert_KeyFieldNameMapping_Error(t *testing.T) { +// table := createTable() +// defer dropTable(table) +// +// gtest.C(t, func(t *gtest.T) { +// type User struct { +// Id int +// Passport string +// Password string +// Nickname string +// CreateTime string +// NoneExistFiled string +// } +// data := User{ +// Id: 1, +// Passport: "user_1", +// Password: "pass_1", +// Nickname: "name_1", +// CreateTime: "2020-10-10 12:00:01", +// } +// _, err := db.Model(table).Data(data).Insert() +// t.AssertNE(err, nil) +// }) +//} func Test_Model_Insert_Time(t *testing.T) { table := createTable() diff --git a/frame/g/g_object.go b/frame/g/g_object.go index 6e0dceb64..d4bdfdef0 100644 --- a/frame/g/g_object.go +++ b/frame/g/g_object.go @@ -97,18 +97,13 @@ func DB(name ...string) gdb.DB { // relational databases but also for NoSQL databases in the future. The name // "Table" is not proper for that purpose any more. // Deprecated, use Model instead. -func Table(tables ...string) *gdb.Model { - return DB().Model(tables...) +func Table(tableNameOrStruct ...interface{}) *gdb.Model { + return DB().Model(tableNameOrStruct...) } // Model creates and returns a model based on configuration of default database group. -func Model(tables ...string) *gdb.Model { - return DB().Model(tables...) -} - -// With creates and returns an ORM model based on meta data of given object. -func With(object interface{}) *gdb.Model { - return DB().With(object) +func Model(tableNameOrStruct ...interface{}) *gdb.Model { + return DB().Model(tableNameOrStruct...) } // Redis returns an instance of redis client with specified configuration group name. From 742653ce75121b02060b6b92e9f550c57ff1396a Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 2 May 2021 23:28:24 +0800 Subject: [PATCH 243/492] improve Model function for struct parameter that can retrieve table name tag from --- database/gdb/gdb_model.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index f345ad857..ba834a457 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -83,21 +83,16 @@ func (c *Core) Table(tableNameOrStruct ...interface{}) *Model { // Model("user u, user_detail ud") // 2. Model name with alias: Model("user", "u") func (c *Core) Model(tableNameOrStruct ...interface{}) *Model { - // With feature checks. - if len(tableNameOrStruct) > 0 { - if _, ok := tableNameOrStruct[0].(string); !ok { - return c.With(tableNameOrStruct...) - } - } - // Normal model creation. var ( tableStr = "" + tableName = "" tableNames = make([]string, len(tableNameOrStruct)) ) for k, v := range tableNameOrStruct { if s, ok := v.(string); ok { tableNames[k] = s - continue + } else if tableName = getTableNameFromOrmTag(v); tableName != "" { + tableNames[k] = tableName } } From 6a80091fefde3fdce8bb19fe8689a502a1bb5729 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 3 May 2021 00:00:29 +0800 Subject: [PATCH 244/492] version updates --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 189fb2e19..78fbc6cf1 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.15.6" +const VERSION = "v1.15.7" const AUTHORS = "john" From 034a3f180888759c9f584df230183ab27b3b9064 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 11 May 2021 19:20:39 +0800 Subject: [PATCH 245/492] improve struct validation --- internal/empty/empty.go | 122 ++++++++++++++++++ util/gvalid/gvalid.go | 10 ++ util/gvalid/gvalid_validator.go | 12 +- util/gvalid/gvalid_validator_check.go | 14 +- util/gvalid/gvalid_validator_check_struct.go | 49 +++++-- ...lid_z_unit_checkstructwithparammap_test.go | 39 ++++++ 6 files changed, 227 insertions(+), 19 deletions(-) create mode 100755 util/gvalid/gvalid_z_unit_checkstructwithparammap_test.go diff --git a/internal/empty/empty.go b/internal/empty/empty.go index 1d42441ff..023f759a9 100644 --- a/internal/empty/empty.go +++ b/internal/empty/empty.go @@ -85,7 +85,9 @@ func IsEmpty(value interface{}) bool { case map[string]interface{}: return len(value) == 0 default: + // ========================= // Common interfaces checks. + // ========================= if f, ok := value.(apiTime); ok { if f == nil { return true @@ -163,6 +165,126 @@ func IsEmpty(value interface{}) bool { return false } +// IsEmptyLength checks whether given `value` is empty length. +// It returns true if `value` is in: nil, "", len(slice/map/chan) == 0, +// or else it returns false. +//func IsEmptyLength(value interface{}) bool { +// if value == nil { +// return true +// } +// // It firstly checks the variable as common types using assertion to enhance the performance, +// // and then using reflection. +// switch value := value.(type) { +// case +// int, +// int8, +// int16, +// int32, +// int64, +// uint, +// uint8, +// uint16, +// uint32, +// uint64, +// float32, +// float64, +// bool: +// return false +// case string: +// return value == "" +// case []byte: +// return len(value) == 0 +// case []rune: +// return len(value) == 0 +// case []int: +// return len(value) == 0 +// case []string: +// return len(value) == 0 +// case []float32: +// return len(value) == 0 +// case []float64: +// return len(value) == 0 +// case map[string]interface{}: +// return len(value) == 0 +// default: +// // ========================= +// // Common interfaces checks. +// // ========================= +// if f, ok := value.(apiTime); ok { +// if f == nil { +// return true +// } +// return f.IsZero() +// } +// if f, ok := value.(apiString); ok { +// if f == nil { +// return true +// } +// return f.String() == "" +// } +// if f, ok := value.(apiInterfaces); ok { +// if f == nil { +// return true +// } +// return len(f.Interfaces()) == 0 +// } +// if f, ok := value.(apiMapStrAny); ok { +// if f == nil { +// return true +// } +// return len(f.MapStrAny()) == 0 +// } +// // Finally using reflect. +// var rv reflect.Value +// if v, ok := value.(reflect.Value); ok { +// rv = v +// } else { +// rv = reflect.ValueOf(value) +// } +// +// switch rv.Kind() { +// case +// reflect.Int, +// reflect.Int8, +// reflect.Int16, +// reflect.Int32, +// reflect.Int64, +// reflect.Uint, +// reflect.Uint8, +// reflect.Uint16, +// reflect.Uint32, +// reflect.Uint64, +// reflect.Uintptr, +// reflect.Float32, +// reflect.Float64, +// reflect.Bool: +// return false +// case reflect.String: +// return rv.Len() == 0 +// case reflect.Struct: +// for i := 0; i < rv.NumField(); i++ { +// if !IsEmpty(rv) { +// return false +// } +// } +// return true +// case reflect.Chan, +// reflect.Map, +// reflect.Slice, +// reflect.Array: +// return rv.Len() == 0 +// case reflect.Func, +// reflect.Ptr, +// reflect.Interface, +// reflect.UnsafePointer: +// if rv.IsNil() { +// return true +// } +// } +// } +// return false +//} + // IsNil checks whether given `value` is nil. // Parameter `traceSource` is used for tracing to the source variable if given `value` is type // of a pinter that also points to a pointer. It returns nil if the source is nil when `traceSource` diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 42351d218..0b9e18d7e 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -193,6 +193,16 @@ func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) * return defaultValidator.CheckStruct(object, rules, messages...) } +// CheckStructWithParamMap validates struct with given parameter map and returns the error result. +// +// The parameter `object` should be type of struct/*struct. +// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result +// if `rules` is type of []string. +// The optional parameter `messages` specifies the custom error messages for specified keys and rules. +func CheckStructWithParamMap(object interface{}, paramMap interface{}, rules interface{}, messages ...CustomMsg) *Error { + return defaultValidator.CheckStructWithParamMap(object, paramMap, rules, messages...) +} + // parseSequenceTag parses one sequence tag to field, rule and error message. // The sequence tag is like: [alias@]rule[...#msg...] func parseSequenceTag(tag string) (field, rule, msg string) { diff --git a/util/gvalid/gvalid_validator.go b/util/gvalid/gvalid_validator.go index 49c8505aa..5709092f1 100644 --- a/util/gvalid/gvalid_validator.go +++ b/util/gvalid/gvalid_validator.go @@ -6,9 +6,12 @@ package gvalid +import "context" + // Validator is the validation manager. type Validator struct { - i18nLang string // I18n language. + i18nLang string // I18n language. + ctx context.Context // Context containing custom context variables. } // New creates and returns a new Validator. @@ -29,3 +32,10 @@ func (v *Validator) I18n(language string) *Validator { newValidator.i18nLang = language return newValidator } + +// Ctx is a chaining operation function which sets the context for next validation. +func (v *Validator) Ctx(ctx context.Context) *Validator { + newValidator := v.Clone() + newValidator.ctx = ctx + return newValidator +} diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index 831930df1..d3400fd48 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -34,12 +34,12 @@ type apiTime interface { // string/map/struct/*struct. // The optional parameter `params` specifies the extra validation parameters for some rules // like: required-*、same、different, etc. -func (v *Validator) Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error { - return v.doCheck("", value, rules, messages, params...) +func (v *Validator) Check(value interface{}, rules string, messages interface{}, paramMap ...interface{}) *Error { + return v.doCheck("", value, rules, messages, paramMap...) } // doCheck does the really rules validation for single key-value. -func (v *Validator) doCheck(key string, value interface{}, rules string, messages interface{}, params ...interface{}) *Error { +func (v *Validator) doCheck(key string, value interface{}, rules string, messages interface{}, paramMap ...interface{}) *Error { // If there's no validation rules, it does nothing and returns quickly. if rules == "" { return nil @@ -50,8 +50,8 @@ func (v *Validator) doCheck(key string, value interface{}, rules string, message data = make(map[string]interface{}) errorMsgArray = make(map[string]string) ) - if len(params) > 0 { - data = gconv.Map(params[0]) + if len(paramMap) > 0 { + data = gconv.Map(paramMap[0]) } // Custom error messages handling. var ( @@ -107,8 +107,8 @@ func (v *Validator) doCheck(key string, value interface{}, rules string, message dataMap map[string]interface{} message = v.getErrorMessageByRule(ruleKey, customMsgMap) ) - if len(params) > 0 { - dataMap = gconv.Map(params[0]) + if len(paramMap) > 0 { + dataMap = gconv.Map(paramMap[0]) } if err := f(ruleItems[index], value, message, dataMap); err != nil { match = false diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 3180806f4..6709f08ea 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -9,6 +9,7 @@ package gvalid import ( "github.com/gogf/gf/internal/structs" "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/gutil" "reflect" "strings" ) @@ -25,15 +26,25 @@ var ( // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error { + return v.CheckStructWithParamMap(object, nil, rules, messages...) +} + +// CheckStructWithParamMap validates struct with given parameter map and returns the error result. +// +// The parameter `object` should be type of struct/*struct. +// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result +// if `rules` is type of []string. +// The optional parameter `messages` specifies the custom error messages for specified keys and rules. +func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interface{}, rules interface{}, messages ...CustomMsg) *Error { var ( - errorMaps = make(ErrorMap) // Returned error. + errorMaps = make(ErrorMap) // Returning error. ) - mapField, err := structs.FieldMap(object, aliasNameTagPriority) + fieldMap, err := structs.FieldMap(object, aliasNameTagPriority) if err != nil { return newErrorStr("invalid_object", err.Error()) } // It checks the struct recursively the its attribute is also a struct. - for _, field := range mapField { + for _, field := range fieldMap { if field.OriginalKind() == reflect.Struct { if err := v.CheckStruct(field.Value, rules, messages...); err != nil { // It merges the errors into single error map. @@ -52,13 +63,19 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages if len(tagField) == 0 && rules == nil { return nil } + var ( - params = make(map[string]interface{}) + inputParamMap map[string]interface{} checkRules = make(map[string]string) customMessage = make(CustomMsg) fieldAliases = make(map[string]string) // Alias names for `messages` overwriting struct tag names. errorRules = make([]string, 0) // Sequence rules. ) + if paramMap == nil { + inputParamMap = make(map[string]interface{}) + } else { + inputParamMap = gconv.Map(paramMap) + } switch v := rules.(type) { // Sequence tag: []sequence tag // Sequence has order for error results. @@ -103,9 +120,19 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages return nil } // Checks and extends the parameters map with struct alias tag. - for nameOrTag, field := range mapField { - params[nameOrTag] = field.Value.Interface() - params[field.Name()] = field.Value.Interface() + for nameOrTag, field := range fieldMap { + if _, ok := inputParamMap[nameOrTag]; !ok { + if foundKey, foundValue := gutil.MapPossibleItemByKey(inputParamMap, nameOrTag); foundKey != "" { + inputParamMap[nameOrTag] = foundValue + inputParamMap[field.Name()] = foundValue + } else { + // If the custom tag alias name is quite different from the attribute name, + // it just uses the value of the attribute. + // Note that the attribute may have default value according to its type. + inputParamMap[nameOrTag] = field.Value.Interface() + inputParamMap[field.Name()] = field.Value.Interface() + } + } } for _, field := range tagField { fieldName := field.Name() @@ -118,8 +145,8 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages fieldAliases[fieldName] = name } // It here extends the params map using alias names. - if _, ok := params[name]; !ok { - params[name] = field.Value.Interface() + if _, ok := inputParamMap[name]; !ok { + inputParamMap[name] = field.Value.Interface() } if _, ok := checkRules[name]; !ok { if _, ok := checkRules[fieldName]; ok { @@ -175,11 +202,11 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages var value interface{} for key, rule := range checkRules { value = nil - if v, ok := params[key]; ok { + if v, ok := inputParamMap[key]; ok { value = v } // It checks each rule and its value in loop. - if e := v.doCheck(key, value, rule, customMessage[key], params); e != nil { + if e := v.doCheck(key, value, rule, customMessage[key], inputParamMap); e != nil { _, item := e.FirstItem() // =================================================================== // Only in map and struct validations, if value is nil or empty string diff --git a/util/gvalid/gvalid_z_unit_checkstructwithparammap_test.go b/util/gvalid/gvalid_z_unit_checkstructwithparammap_test.go new file mode 100755 index 000000000..a660fd65a --- /dev/null +++ b/util/gvalid/gvalid_z_unit_checkstructwithparammap_test.go @@ -0,0 +1,39 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gvalid_test + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/test/gtest" + "github.com/gogf/gf/util/gvalid" + "testing" +) + +func TestValidator_CheckStructWithParamMap(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type UserApiSearch struct { + Uid int64 `json:"uid" v:"required"` + Nickname string `json:"nickname" v:"required-with:Uid"` + } + data := UserApiSearch{} + t.Assert(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) + }) + gtest.C(t, func(t *gtest.T) { + type UserApiSearch struct { + Uid int64 `json:"uid"` + Nickname string `json:"nickname" v:"required-with:Uid"` + StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"` + EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"` + } + data := UserApiSearch{ + StartTime: nil, + EndTime: nil, + } + t.Assert(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) + }) +} From 2e38416e12a29a527e1d7318bd4b88f64ba6344e Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 11 May 2021 20:00:50 +0800 Subject: [PATCH 246/492] improve struct embedded association case of with feature for package gdb --- database/gdb/gdb_model_with.go | 10 +- .../gdb/gdb_z_mysql_association_with_test.go | 121 +++++++++++++++++- internal/structs/structs_field.go | 9 +- internal/structs/structs_z_unit_test.go | 4 +- util/gvalid/gvalid_validator_check_struct.go | 2 +- 5 files changed, 134 insertions(+), 12 deletions(-) diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 2810d9a25..892474228 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -55,7 +55,7 @@ func (m *Model) WithAll() *Model { // getWithTagObjectArrayFrom retrieves and returns object array that have "with" tag in the struct. func (m *Model) getWithTagObjectArrayFrom(pointer interface{}) ([]interface{}, error) { - fieldMap, err := structs.FieldMap(pointer, nil) + fieldMap, err := structs.FieldMap(pointer, nil, false) if err != nil { return nil, err } @@ -86,6 +86,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { err error withArray = m.withArray ) + // If with all feature is enabled, it then retrieves all the attributes which have with tag defined. if m.withAll { withArray, err = m.getWithTagObjectArrayFrom(pointer) if err != nil { @@ -95,10 +96,11 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { if len(withArray) == 0 { return nil } - fieldMap, err := structs.FieldMap(pointer, nil) + fieldMap, err := structs.FieldMap(pointer, nil, false) if err != nil { return err } + // Check the with array and automatically call the ScanList to complete association querying. for withIndex, withItem := range withArray { withItemReflectValueType, err := structs.StructType(withItem) if err != nil { @@ -110,6 +112,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { fieldType = fieldValue.Type() fieldTypeStr = gstr.TrimAll(fieldType.String(), "*[]") ) + // It does select operation if the field type is in the specified with type array. if gstr.Compare(fieldTypeStr, withItemReflectValueTypeStr) == 0 { var ( withTag string @@ -174,6 +177,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { } // doWithScanStructs handles model association operations feature for struct slice. +// Also see doWithScanStruct. func (m *Model) doWithScanStructs(pointer interface{}) error { var ( err error @@ -188,7 +192,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { if len(withArray) == 0 { return nil } - fieldMap, err := structs.FieldMap(pointer, nil) + fieldMap, err := structs.FieldMap(pointer, nil, false) if err != nil { return err } diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index d8a34cae9..7cf1bfd92 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -212,7 +212,7 @@ PRIMARY KEY (id) }) } -func Test_Table_Relation_With_ScanList(t *testing.T) { +func Test_Table_Relation_With(t *testing.T) { var ( tableUser = "user" tableUserDetail = "user_detail" @@ -411,7 +411,7 @@ PRIMARY KEY (id) }) } -func Test_Table_Relation_WithAll_Scan(t *testing.T) { +func Test_Table_Relation_WithAll(t *testing.T) { var ( tableUser = "user" tableUserDetail = "user_detail" @@ -526,7 +526,7 @@ PRIMARY KEY (id) }) } -func Test_Table_Relation_WithAll_ScanList(t *testing.T) { +func Test_Table_Relation_WithAll_List(t *testing.T) { var ( tableUser = "user" tableUserDetail = "user_detail" @@ -666,3 +666,118 @@ PRIMARY KEY (id) t.Assert(users[1].UserScores[4].Score, 5) }) } + +func Test_Table_Relation_WithAll_Embedded(t *testing.T) { + var ( + tableUser = "user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +uid int(10) unsigned NOT NULL AUTO_INCREMENT, +address varchar(45) NOT NULL, +PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +uid int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:user"` + *UserDetail `orm:"with:uid=id"` + Id int `json:"id"` + Name string `json:"name"` + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} diff --git a/internal/structs/structs_field.go b/internal/structs/structs_field.go index a0ad1a1cb..dda5a996e 100644 --- a/internal/structs/structs_field.go +++ b/internal/structs/structs_field.go @@ -61,8 +61,11 @@ func (f *Field) OriginalKind() reflect.Kind { // The parameter `priority` specifies the priority tag array for retrieving from high to low. // If it's given `nil`, it returns map[name]*Field, of which the `name` is attribute name. // +// The parameter `recursive` specifies the whether retrieving the fields recursively if the attribute +// is an embedded struct. +// // Note that it only retrieves the exported attributes with first letter up-case from struct. -func FieldMap(pointer interface{}, priority []string) (map[string]*Field, error) { +func FieldMap(pointer interface{}, priority []string, recursive bool) (map[string]*Field, error) { fields, err := getFieldValues(pointer) if err != nil { return nil, err @@ -88,8 +91,8 @@ func FieldMap(pointer interface{}, priority []string) (map[string]*Field, error) if tagValue != "" { mapField[tagValue] = tempField } else { - if field.IsEmbedded() { - m, err := FieldMap(field.Value, priority) + if recursive && field.IsEmbedded() { + m, err := FieldMap(field.Value, priority, recursive) if err != nil { return nil, err } diff --git a/internal/structs/structs_z_unit_test.go b/internal/structs/structs_z_unit_test.go index c4f31d1a2..4268f473a 100644 --- a/internal/structs/structs_z_unit_test.go +++ b/internal/structs/structs_z_unit_test.go @@ -110,7 +110,7 @@ func Test_FieldMap(t *testing.T) { Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"` } var user *User - m, _ := structs.FieldMap(user, []string{"params"}) + m, _ := structs.FieldMap(user, []string{"params"}, true) t.Assert(len(m), 3) _, ok := m["Id"] t.Assert(ok, true) @@ -130,7 +130,7 @@ func Test_FieldMap(t *testing.T) { Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"` } var user *User - m, _ := structs.FieldMap(user, nil) + m, _ := structs.FieldMap(user, nil, true) t.Assert(len(m), 3) _, ok := m["Id"] t.Assert(ok, true) diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 3180806f4..a3005b74c 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -28,7 +28,7 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages var ( errorMaps = make(ErrorMap) // Returned error. ) - mapField, err := structs.FieldMap(object, aliasNameTagPriority) + mapField, err := structs.FieldMap(object, aliasNameTagPriority, true) if err != nil { return newErrorStr("invalid_object", err.Error()) } From 1eab1cb3677b739f98075beded7016c674c48202 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 11 May 2021 20:14:06 +0800 Subject: [PATCH 247/492] add more unit testing cases,remove function Filter usage in unit testing cases for package gdb --- database/gdb/gdb_z_mysql_filter_test.go | 218 ++++++++++++++++++++++++ database/gdb/gdb_z_mysql_model_test.go | 185 ++------------------ database/gdb/gdb_z_mysql_raw_test.go | 4 +- database/gdb/gdb_z_mysql_struct_test.go | 8 +- 4 files changed, 234 insertions(+), 181 deletions(-) create mode 100644 database/gdb/gdb_z_mysql_filter_test.go diff --git a/database/gdb/gdb_z_mysql_filter_test.go b/database/gdb/gdb_z_mysql_filter_test.go new file mode 100644 index 000000000..cd9c30107 --- /dev/null +++ b/database/gdb/gdb_z_mysql_filter_test.go @@ -0,0 +1,218 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb_test + +import ( + "fmt" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/test/gtest" + "testing" +) + +// Using filter dose not affect the outside value inside function. +func Test_Model_Insert_Filter(t *testing.T) { + // map + gtest.C(t, func(t *gtest.T) { + table := createTable() + defer dropTable(table) + data := g.Map{ + "id": 1, + "uid": 1, + "passport": "t1", + "password": "25d55ad283aa400af464c76d713c07ad", + "nickname": "name_1", + "create_time": gtime.Now().String(), + } + result, err := db.Model(table).Data(data).Insert() + t.AssertNil(err) + n, _ := result.LastInsertId() + t.Assert(n, 1) + + t.Assert(data["uid"], 1) + }) + // slice + gtest.C(t, func(t *gtest.T) { + table := createTable() + defer dropTable(table) + data := g.List{ + g.Map{ + "id": 1, + "uid": 1, + "passport": "t1", + "password": "25d55ad283aa400af464c76d713c07ad", + "nickname": "name_1", + "create_time": gtime.Now().String(), + }, + g.Map{ + "id": 2, + "uid": 2, + "passport": "t1", + "password": "25d55ad283aa400af464c76d713c07ad", + "nickname": "name_1", + "create_time": gtime.Now().String(), + }, + } + + result, err := db.Model(table).Data(data).Insert() + t.AssertNil(err) + n, _ := result.LastInsertId() + t.Assert(n, 2) + + t.Assert(data[0]["uid"], 1) + t.Assert(data[1]["uid"], 2) + }) +} + +func Test_Model_Embedded_Filter(t *testing.T) { + table := createTable() + defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + type Base struct { + Id int + Uid int + CreateTime string + NoneExist string + } + type User struct { + Base + Passport string + Password string + Nickname string + } + result, err := db.Model(table).Data(User{ + Passport: "john-test", + Password: "123456", + Nickname: "John", + Base: Base{ + Id: 100, + Uid: 100, + CreateTime: gtime.Now().String(), + }, + }).Insert() + t.AssertNil(err) + n, _ := result.RowsAffected() + t.Assert(n, 1) + + var user *User + err = db.Model(table).Fields(user).Where("id=100").Scan(&user) + t.AssertNil(err) + t.Assert(user.Passport, "john-test") + t.Assert(user.Id, 100) + }) +} + +// This is no longer used as the filter feature is automatically enabled from GoFrame v1.16.0. +//func Test_Model_Insert_KeyFieldNameMapping_Error(t *testing.T) { +// table := createTable() +// defer dropTable(table) +// +// gtest.C(t, func(t *gtest.T) { +// type User struct { +// Id int +// Passport string +// Password string +// Nickname string +// CreateTime string +// NoneExistFiled string +// } +// data := User{ +// Id: 1, +// Passport: "user_1", +// Password: "pass_1", +// Nickname: "name_1", +// CreateTime: "2020-10-10 12:00:01", +// } +// _, err := db.Model(table).Data(data).Insert() +// t.AssertNE(err, nil) +// }) +//} + +func Test_Model_Fields_AutoFilterInJoinStatement(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var err error + table1 := "user" + table2 := "score" + table3 := "info" + if _, err := db.Exec(fmt.Sprintf(` + CREATE TABLE IF NOT EXISTS %s ( + id int(11) NOT NULL AUTO_INCREMENT, + name varchar(500) NOT NULL DEFAULT '', + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; + `, table1, + )); err != nil { + t.AssertNil(err) + } + defer dropTable(table1) + _, err = db.Model(table1).Insert(g.Map{ + "id": 1, + "name": "john", + }) + t.AssertNil(err) + + if _, err := db.Exec(fmt.Sprintf(` + CREATE TABLE IF NOT EXISTS %s ( + id int(11) NOT NULL AUTO_INCREMENT, + user_id int(11) NOT NULL DEFAULT 0, + number varchar(500) NOT NULL DEFAULT '', + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; + `, table2, + )); err != nil { + t.AssertNil(err) + } + defer dropTable(table2) + _, err = db.Model(table2).Insert(g.Map{ + "id": 1, + "user_id": 1, + "number": "n", + }) + t.AssertNil(err) + + if _, err := db.Exec(fmt.Sprintf(` + CREATE TABLE IF NOT EXISTS %s ( + id int(11) NOT NULL AUTO_INCREMENT, + user_id int(11) NOT NULL DEFAULT 0, + description varchar(500) NOT NULL DEFAULT '', + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; + `, table3, + )); err != nil { + t.AssertNil(err) + } + defer dropTable(table3) + _, err = db.Model(table3).Insert(g.Map{ + "id": 1, + "user_id": 1, + "description": "brief", + }) + t.AssertNil(err) + + one, err := db.Model("user"). + Where("user.id", 1). + Fields("score.number,user.name"). + LeftJoin("score", "user.id=score.user_id"). + LeftJoin("info", "info.id=info.user_id"). + Order("user.id asc"). + One() + t.AssertNil(err) + t.Assert(len(one), 2) + t.Assert(one["name"].String(), "john") + t.Assert(one["number"].String(), "n") + + one, err = db.Model("user"). + LeftJoin("score", "user.id=score.user_id"). + LeftJoin("info", "info.id=info.user_id"). + Fields("score.number,user.name"). + One() + t.AssertNil(err) + t.Assert(len(one), 2) + t.Assert(one["name"].String(), "john") + t.Assert(one["number"].String(), "n") + }) +} diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index f7a5fc857..55ca3aa30 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -30,7 +30,7 @@ func Test_Model_Insert(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { user := db.Model(table) - result, err := user.Filter().Data(g.Map{ + result, err := user.Data(g.Map{ "id": 1, "uid": 1, "passport": "t1", @@ -42,7 +42,7 @@ func Test_Model_Insert(t *testing.T) { n, _ := result.LastInsertId() t.Assert(n, 1) - result, err = db.Model(table).Filter().Data(g.Map{ + result, err = db.Model(table).Data(g.Map{ "id": "2", "uid": "2", "passport": "t2", @@ -63,7 +63,7 @@ func Test_Model_Insert(t *testing.T) { CreateTime *gtime.Time `json:"create_time"` } // Model inserting. - result, err = db.Model(table).Filter().Data(User{ + result, err = db.Model(table).Data(User{ Id: 3, Uid: 3, Passport: "t3", @@ -77,7 +77,7 @@ func Test_Model_Insert(t *testing.T) { t.AssertNil(err) t.Assert(value.String(), "t3") - result, err = db.Model(table).Filter().Data(&User{ + result, err = db.Model(table).Data(&User{ Id: 4, Uid: 4, Passport: "t4", @@ -99,60 +99,6 @@ func Test_Model_Insert(t *testing.T) { }) } -// Using filter dose not affect the outside value inside function. -func Test_Model_Insert_Filter(t *testing.T) { - // map - gtest.C(t, func(t *gtest.T) { - table := createTable() - defer dropTable(table) - data := g.Map{ - "id": 1, - "uid": 1, - "passport": "t1", - "password": "25d55ad283aa400af464c76d713c07ad", - "nickname": "name_1", - "create_time": gtime.Now().String(), - } - result, err := db.Model(table).Filter().Data(data).Insert() - t.AssertNil(err) - n, _ := result.LastInsertId() - t.Assert(n, 1) - - t.Assert(data["uid"], 1) - }) - // slice - gtest.C(t, func(t *gtest.T) { - table := createTable() - defer dropTable(table) - data := g.List{ - g.Map{ - "id": 1, - "uid": 1, - "passport": "t1", - "password": "25d55ad283aa400af464c76d713c07ad", - "nickname": "name_1", - "create_time": gtime.Now().String(), - }, - g.Map{ - "id": 2, - "uid": 2, - "passport": "t1", - "password": "25d55ad283aa400af464c76d713c07ad", - "nickname": "name_1", - "create_time": gtime.Now().String(), - }, - } - - result, err := db.Model(table).Filter().Data(data).Insert() - t.AssertNil(err) - n, _ := result.LastInsertId() - t.Assert(n, 2) - - t.Assert(data[0]["uid"], 1) - t.Assert(data[1]["uid"], 2) - }) -} - // Fix issue: https://github.com/gogf/gf/issues/819 func Test_Model_Insert_WithStructAndSliceAttribute(t *testing.T) { table := createTable() @@ -240,32 +186,6 @@ func Test_Model_Update_KeyFieldNameMapping(t *testing.T) { }) } -// This is no longer used as the filter feature is automatically enabled from GoFrame v1.16.0. -//func Test_Model_Insert_KeyFieldNameMapping_Error(t *testing.T) { -// table := createTable() -// defer dropTable(table) -// -// gtest.C(t, func(t *gtest.T) { -// type User struct { -// Id int -// Passport string -// Password string -// Nickname string -// CreateTime string -// NoneExistFiled string -// } -// data := User{ -// Id: 1, -// Passport: "user_1", -// Password: "pass_1", -// Nickname: "name_1", -// CreateTime: "2020-10-10 12:00:01", -// } -// _, err := db.Model(table).Data(data).Insert() -// t.AssertNE(err, nil) -// }) -//} - func Test_Model_Insert_Time(t *testing.T) { table := createTable() defer dropTable(table) @@ -305,7 +225,7 @@ func Test_Model_BatchInsertWithArrayStruct(t *testing.T) { }) } - result, err := user.Filter().Data(array).Insert() + result, err := user.Data(array).Insert() t.AssertNil(err) n, _ := result.LastInsertId() t.Assert(n, TableSize) @@ -316,7 +236,7 @@ func Test_Model_InsertIgnore(t *testing.T) { table := createInitTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - _, err := db.Model(table).Filter().Data(g.Map{ + _, err := db.Model(table).Data(g.Map{ "id": 1, "uid": 1, "passport": "t1", @@ -327,7 +247,7 @@ func Test_Model_InsertIgnore(t *testing.T) { t.AssertNE(err, nil) }) gtest.C(t, func(t *gtest.T) { - _, err := db.Model(table).Filter().Data(g.Map{ + _, err := db.Model(table).Data(g.Map{ "id": 1, "uid": 1, "passport": "t1", @@ -344,7 +264,7 @@ func Test_Model_Batch(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - result, err := db.Model(table).Filter().Data(g.List{ + result, err := db.Model(table).Data(g.List{ { "id": 2, "uid": 2, @@ -2441,7 +2361,7 @@ func Test_Model_Schema1(t *testing.T) { // Model. gtest.C(t, func(t *gtest.T) { i := 1000 - _, err := db.Model(table).Schema(TestSchema1).Filter().Insert(g.Map{ + _, err := db.Model(table).Schema(TestSchema1).Insert(g.Map{ "id": i, "passport": fmt.Sprintf(`user_%d`, i), "password": fmt.Sprintf(`pass_%d`, i), @@ -2503,7 +2423,7 @@ func Test_Model_Schema2(t *testing.T) { // Schema. gtest.C(t, func(t *gtest.T) { i := 1000 - _, err := db.Schema(TestSchema1).Table(table).Filter().Insert(g.Map{ + _, err := db.Schema(TestSchema1).Table(table).Insert(g.Map{ "id": i, "passport": fmt.Sprintf(`user_%d`, i), "password": fmt.Sprintf(`pass_%d`, i), @@ -3243,91 +3163,6 @@ func Test_Model_Fields_Map_Struct(t *testing.T) { }) } -func Test_Model_Fields_AutoFilterInJoinStatement(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var err error - table1 := "user" - table2 := "score" - table3 := "info" - if _, err := db.Exec(fmt.Sprintf(` - CREATE TABLE IF NOT EXISTS %s ( - id int(11) NOT NULL AUTO_INCREMENT, - name varchar(500) NOT NULL DEFAULT '', - PRIMARY KEY (id) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; - `, table1, - )); err != nil { - t.AssertNil(err) - } - defer dropTable(table1) - _, err = db.Model(table1).Insert(g.Map{ - "id": 1, - "name": "john", - }) - t.AssertNil(err) - - if _, err := db.Exec(fmt.Sprintf(` - CREATE TABLE IF NOT EXISTS %s ( - id int(11) NOT NULL AUTO_INCREMENT, - user_id int(11) NOT NULL DEFAULT 0, - number varchar(500) NOT NULL DEFAULT '', - PRIMARY KEY (id) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; - `, table2, - )); err != nil { - t.AssertNil(err) - } - defer dropTable(table2) - _, err = db.Model(table2).Insert(g.Map{ - "id": 1, - "user_id": 1, - "number": "n", - }) - t.AssertNil(err) - - if _, err := db.Exec(fmt.Sprintf(` - CREATE TABLE IF NOT EXISTS %s ( - id int(11) NOT NULL AUTO_INCREMENT, - user_id int(11) NOT NULL DEFAULT 0, - description varchar(500) NOT NULL DEFAULT '', - PRIMARY KEY (id) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; - `, table3, - )); err != nil { - t.AssertNil(err) - } - defer dropTable(table3) - _, err = db.Model(table3).Insert(g.Map{ - "id": 1, - "user_id": 1, - "description": "brief", - }) - t.AssertNil(err) - - one, err := db.Model("user"). - Where("user.id", 1). - Fields("score.number,user.name"). - LeftJoin("score", "user.id=score.user_id"). - LeftJoin("info", "info.id=info.user_id"). - Order("user.id asc"). - One() - t.AssertNil(err) - t.Assert(len(one), 2) - t.Assert(one["name"].String(), "john") - t.Assert(one["number"].String(), "n") - - one, err = db.Model("user"). - LeftJoin("score", "user.id=score.user_id"). - LeftJoin("info", "info.id=info.user_id"). - Fields("score.number,user.name"). - One() - t.AssertNil(err) - t.Assert(len(one), 2) - t.Assert(one["name"].String(), "john") - t.Assert(one["number"].String(), "n") - }) -} - func Test_Model_WhereIn(t *testing.T) { table := createInitTable() defer dropTable(table) diff --git a/database/gdb/gdb_z_mysql_raw_test.go b/database/gdb/gdb_z_mysql_raw_test.go index e4677e12f..3b3f3dc05 100644 --- a/database/gdb/gdb_z_mysql_raw_test.go +++ b/database/gdb/gdb_z_mysql_raw_test.go @@ -20,7 +20,7 @@ func Test_Insert_Raw(t *testing.T) { gtest.C(t, func(t *gtest.T) { user := db.Model(table) - result, err := user.Filter().Data(g.Map{ + result, err := user.Data(g.Map{ "id": gdb.Raw("id+2"), "passport": "port_1", "password": "pass_1", @@ -39,7 +39,7 @@ func Test_BatchInsert_Raw(t *testing.T) { gtest.C(t, func(t *gtest.T) { user := db.Model(table) - result, err := user.Filter().Data( + result, err := user.Data( g.List{ g.Map{ "id": gdb.Raw("id+2"), diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index 67a1858cb..090a991a7 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -15,7 +15,7 @@ import ( "testing" ) -func Test_Model_Inherit_Insert(t *testing.T) { +func Test_Model_Embedded_Insert(t *testing.T) { table := createTable() defer dropTable(table) @@ -31,7 +31,7 @@ func Test_Model_Inherit_Insert(t *testing.T) { Password string `json:"password"` Nickname string `json:"nickname"` } - result, err := db.Model(table).Filter().Data(User{ + result, err := db.Model(table).Data(User{ Passport: "john-test", Password: "123456", Nickname: "John", @@ -50,7 +50,7 @@ func Test_Model_Inherit_Insert(t *testing.T) { }) } -func Test_Model_Inherit_MapToStruct(t *testing.T) { +func Test_Model_Embedded_MapToStruct(t *testing.T) { table := createTable() defer dropTable(table) @@ -77,7 +77,7 @@ func Test_Model_Inherit_MapToStruct(t *testing.T) { "nickname": "T1", "create_time": gtime.Now().String(), } - result, err := db.Model(table).Filter().Data(data).Insert() + result, err := db.Model(table).Data(data).Insert() t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) From a4240bdfb78ecbcb7095e0e171fa3b8c65aef75f Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 11 May 2021 20:57:30 +0800 Subject: [PATCH 248/492] improve struct validation for package gvalid --- util/gvalid/gvalid_validator_check_struct.go | 80 +++++++++++++------ util/gvalid/gvalid_z_unit_checkstruct_test.go | 49 ++++++++++++ ...lid_z_unit_checkstructwithparammap_test.go | 39 --------- 3 files changed, 103 insertions(+), 65 deletions(-) delete mode 100755 util/gvalid/gvalid_z_unit_checkstructwithparammap_test.go diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 6709f08ea..3c690ef0a 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -9,11 +9,19 @@ package gvalid import ( "github.com/gogf/gf/internal/structs" "github.com/gogf/gf/util/gconv" - "github.com/gogf/gf/util/gutil" "reflect" "strings" ) +// doCheckStructWithParamMapInput is used for struct validation for internal function. +type doCheckStructWithParamMapInput struct { + Object interface{} // Can be type of struct/*struct. + ParamMap interface{} // Validation parameter map. Note that it acts different according attribute `ParamMapStrict`. + ParamMapStrict bool // Strictly using `ParamMap` as its validation source. If false, it ignores `ParamMap` and retrieves values from `Object`. + CustomRules interface{} // Custom validation rules. + CustomErrorMessageMap CustomMsg // Custom error message map for validation rules. +} + var ( structTagPriority = []string{"gvalid", "valid", "v"} // structTagPriority specifies the validation tag priority array. aliasNameTagPriority = []string{"param", "params", "p"} // aliasNameTagPriority specifies the alias tag priority array. @@ -25,8 +33,18 @@ var ( // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error { - return v.CheckStructWithParamMap(object, nil, rules, messages...) +func (v *Validator) CheckStruct(object interface{}, customRules interface{}, customErrorMessageMap ...CustomMsg) *Error { + var message CustomMsg + if len(customErrorMessageMap) > 0 { + message = customErrorMessageMap[0] + } + return v.doCheckStructWithParamMap(&doCheckStructWithParamMapInput{ + Object: object, + ParamMap: nil, + ParamMapStrict: false, + CustomRules: customRules, + CustomErrorMessageMap: message, + }) } // CheckStructWithParamMap validates struct with given parameter map and returns the error result. @@ -35,18 +53,32 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interface{}, rules interface{}, messages ...CustomMsg) *Error { +func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interface{}, customRules interface{}, customErrorMessageMap ...CustomMsg) *Error { + var message CustomMsg + if len(customErrorMessageMap) > 0 { + message = customErrorMessageMap[0] + } + return v.doCheckStructWithParamMap(&doCheckStructWithParamMapInput{ + Object: object, + ParamMap: paramMap, + ParamMapStrict: true, + CustomRules: customRules, + CustomErrorMessageMap: message, + }) +} + +func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapInput) *Error { var ( errorMaps = make(ErrorMap) // Returning error. ) - fieldMap, err := structs.FieldMap(object, aliasNameTagPriority) + fieldMap, err := structs.FieldMap(input.Object, aliasNameTagPriority) if err != nil { return newErrorStr("invalid_object", err.Error()) } // It checks the struct recursively the its attribute is also a struct. for _, field := range fieldMap { if field.OriginalKind() == reflect.Struct { - if err := v.CheckStruct(field.Value, rules, messages...); err != nil { + if err := v.CheckStruct(field.Value, input.CustomRules, input.CustomErrorMessageMap); err != nil { // It merges the errors into single error map. for k, m := range err.errors { errorMaps[k] = m @@ -55,12 +87,12 @@ func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interfa } } // It here must use structs.TagFields not structs.FieldMap to ensure error sequence. - tagField, err := structs.TagFields(object, structTagPriority) + tagField, err := structs.TagFields(input.Object, structTagPriority) if err != nil { return newErrorStr("invalid_object", err.Error()) } // If there's no struct tag and validation rules, it does nothing and returns quickly. - if len(tagField) == 0 && rules == nil { + if len(tagField) == 0 && input.CustomRules == nil { return nil } @@ -71,12 +103,7 @@ func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interfa fieldAliases = make(map[string]string) // Alias names for `messages` overwriting struct tag names. errorRules = make([]string, 0) // Sequence rules. ) - if paramMap == nil { - inputParamMap = make(map[string]interface{}) - } else { - inputParamMap = gconv.Map(paramMap) - } - switch v := rules.(type) { + switch v := input.CustomRules.(type) { // Sequence tag: []sequence tag // Sequence has order for error results. case []string: @@ -119,21 +146,22 @@ func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interfa if len(tagField) == 0 && len(checkRules) == 0 { return nil } + // Input parameter map handling. + if input.ParamMap == nil || !input.ParamMapStrict { + inputParamMap = make(map[string]interface{}) + } else { + inputParamMap = gconv.Map(input.ParamMap) + } // Checks and extends the parameters map with struct alias tag. - for nameOrTag, field := range fieldMap { - if _, ok := inputParamMap[nameOrTag]; !ok { - if foundKey, foundValue := gutil.MapPossibleItemByKey(inputParamMap, nameOrTag); foundKey != "" { - inputParamMap[nameOrTag] = foundValue - inputParamMap[field.Name()] = foundValue - } else { - // If the custom tag alias name is quite different from the attribute name, - // it just uses the value of the attribute. - // Note that the attribute may have default value according to its type. - inputParamMap[nameOrTag] = field.Value.Interface() + if !input.ParamMapStrict { + for nameOrTag, field := range fieldMap { + inputParamMap[nameOrTag] = field.Value.Interface() + if nameOrTag != field.Name() { inputParamMap[field.Name()] = field.Value.Interface() } } } + for _, field := range tagField { fieldName := field.Name() // sequence tag == struct tag @@ -187,8 +215,8 @@ func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interfa // Custom error messages, // which have the most priority than `rules` and struct tag. - if len(messages) > 0 && len(messages[0]) > 0 { - for k, v := range messages[0] { + if len(input.CustomErrorMessageMap) > 0 { + for k, v := range input.CustomErrorMessageMap { if a, ok := fieldAliases[k]; ok { // Overwrite the key of field name. customMessage[a] = v diff --git a/util/gvalid/gvalid_z_unit_checkstruct_test.go b/util/gvalid/gvalid_z_unit_checkstruct_test.go index 188ea4b5c..0f2446dcd 100755 --- a/util/gvalid/gvalid_z_unit_checkstruct_test.go +++ b/util/gvalid/gvalid_z_unit_checkstruct_test.go @@ -8,6 +8,7 @@ package gvalid_test import ( "github.com/gogf/gf/container/gvar" + "github.com/gogf/gf/os/gtime" "testing" "github.com/gogf/gf/frame/g" @@ -352,3 +353,51 @@ func Test_CheckStruct_InvalidRule(t *testing.T) { t.AssertNE(err, nil) }) } + +func TestValidator_CheckStructWithParamMap(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type UserApiSearch struct { + Uid int64 `json:"uid" v:"required"` + Nickname string `json:"nickname" v:"required-with:Uid"` + } + data := UserApiSearch{} + t.Assert(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) + }) + gtest.C(t, func(t *gtest.T) { + type UserApiSearch struct { + Uid int64 `json:"uid" v:"required"` + Nickname string `json:"nickname" v:"required-with:Uid"` + } + data := UserApiSearch{ + Uid: 1, + } + t.AssertNE(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) + }) + + gtest.C(t, func(t *gtest.T) { + type UserApiSearch struct { + Uid int64 `json:"uid"` + Nickname string `json:"nickname" v:"required-with:Uid"` + StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"` + EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"` + } + data := UserApiSearch{ + StartTime: nil, + EndTime: nil, + } + t.Assert(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) + }) + gtest.C(t, func(t *gtest.T) { + type UserApiSearch struct { + Uid int64 `json:"uid"` + Nickname string `json:"nickname" v:"required-with:Uid"` + StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"` + EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"` + } + data := UserApiSearch{ + StartTime: gtime.Now(), + EndTime: nil, + } + t.AssertNE(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) + }) +} diff --git a/util/gvalid/gvalid_z_unit_checkstructwithparammap_test.go b/util/gvalid/gvalid_z_unit_checkstructwithparammap_test.go deleted file mode 100755 index a660fd65a..000000000 --- a/util/gvalid/gvalid_z_unit_checkstructwithparammap_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gvalid_test - -import ( - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/gtime" - "github.com/gogf/gf/test/gtest" - "github.com/gogf/gf/util/gvalid" - "testing" -) - -func TestValidator_CheckStructWithParamMap(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - type UserApiSearch struct { - Uid int64 `json:"uid" v:"required"` - Nickname string `json:"nickname" v:"required-with:Uid"` - } - data := UserApiSearch{} - t.Assert(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) - }) - gtest.C(t, func(t *gtest.T) { - type UserApiSearch struct { - Uid int64 `json:"uid"` - Nickname string `json:"nickname" v:"required-with:Uid"` - StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"` - EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"` - } - data := UserApiSearch{ - StartTime: nil, - EndTime: nil, - } - t.Assert(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) - }) -} From b06580d343662f883e648e0c827e7d65991e0562 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 12 May 2021 00:01:52 +0800 Subject: [PATCH 249/492] improve struct validation for package gvalid --- net/ghttp/ghttp_request_param.go | 21 ++++--- net/ghttp/ghttp_request_param_form.go | 11 +++- net/ghttp/ghttp_request_param_query.go | 11 +++- net/ghttp/ghttp_request_param_request.go | 11 +++- net/ghttp/ghttp_unit_request_json_test.go | 2 +- net/ghttp/ghttp_unit_request_struct_test.go | 44 +++++++++++-- net/ghttp/ghttp_unit_request_xml_test.go | 2 +- util/gvalid/gvalid_validator_check.go | 12 ++-- util/gvalid/gvalid_validator_check_struct.go | 46 +++++++------- util/gvalid/gvalid_validator_rule_required.go | 62 +++++++++++++------ util/gvalid/gvalid_z_unit_checkstruct_test.go | 19 ++++-- 11 files changed, 168 insertions(+), 73 deletions(-) diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index f77eeeec3..991967df1 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -82,24 +82,27 @@ func (r *Request) doParse(pointer interface{}, requestType int) error { // 1. {"id":1, "name":"john"} // 2. ?id=1&name=john case reflect.Ptr, reflect.Struct: + var ( + err error + data map[string]interface{} + ) // Converting. switch requestType { case parseTypeQuery: - if err := r.GetQueryStruct(pointer); err != nil { + if data, err = r.doGetQueryStruct(pointer); err != nil { return err } case parseTypeForm: - if err := r.GetFormStruct(pointer); err != nil { + if data, err = r.doGetFormStruct(pointer); err != nil { return err } default: - if err := r.GetStruct(pointer); err != nil { + if data, err = r.doGetRequestStruct(pointer); err != nil { return err } } - // Validation. - if err := gvalid.CheckStruct(pointer, nil); err != nil { + if err := gvalid.CheckStructWithParamMap(pointer, data, nil); err != nil { return err } @@ -107,7 +110,7 @@ func (r *Request) doParse(pointer interface{}, requestType int) error { // [{"id":1, "name":"john"}, {"id":, "name":"smith"}] case reflect.Array, reflect.Slice: // If struct slice conversion, it might post JSON/XML content, - // so it uses gjson for the conversion. + // so it uses `gjson` for the conversion. j, err := gjson.LoadContent(r.GetBody()) if err != nil { return err @@ -116,7 +119,11 @@ func (r *Request) doParse(pointer interface{}, requestType int) error { return err } for i := 0; i < reflectVal2.Len(); i++ { - if err := gvalid.CheckStruct(reflectVal2.Index(i), nil); err != nil { + if err := gvalid.CheckStructWithParamMap( + reflectVal2.Index(i), + j.GetMap(gconv.String(i)), + nil, + ); err != nil { return err } } diff --git a/net/ghttp/ghttp_request_param_form.go b/net/ghttp/ghttp_request_param_form.go index 630e9ea74..2e09b8b00 100644 --- a/net/ghttp/ghttp_request_param_form.go +++ b/net/ghttp/ghttp_request_param_form.go @@ -188,13 +188,18 @@ func (r *Request) GetFormMapStrVar(kvMap ...map[string]interface{}) map[string]* // given struct object. Note that the parameter is a pointer to the struct object. // The optional parameter is used to specify the key to attribute mapping. func (r *Request) GetFormStruct(pointer interface{}, mapping ...map[string]string) error { + _, err := r.doGetFormStruct(pointer, mapping...) + return err +} + +func (r *Request) doGetFormStruct(pointer interface{}, mapping ...map[string]string) (data map[string]interface{}, err error) { r.parseForm() - data := r.formMap + data = r.formMap if data == nil { data = map[string]interface{}{} } if err := r.mergeDefaultStructValue(data, pointer); err != nil { - return nil + return data, nil } - return gconv.Struct(data, pointer, mapping...) + return data, gconv.Struct(data, pointer, mapping...) } diff --git a/net/ghttp/ghttp_request_param_query.go b/net/ghttp/ghttp_request_param_query.go index a7257ecbf..b108b4b3c 100644 --- a/net/ghttp/ghttp_request_param_query.go +++ b/net/ghttp/ghttp_request_param_query.go @@ -196,13 +196,18 @@ func (r *Request) GetQueryMapStrVar(kvMap ...map[string]interface{}) map[string] // to the struct object. The optional parameter is used to specify the key to // attribute mapping. func (r *Request) GetQueryStruct(pointer interface{}, mapping ...map[string]string) error { + _, err := r.doGetQueryStruct(pointer, mapping...) + return err +} + +func (r *Request) doGetQueryStruct(pointer interface{}, mapping ...map[string]string) (data map[string]interface{}, err error) { r.parseQuery() - data := r.GetQueryMap() + data = r.GetQueryMap() if data == nil { data = map[string]interface{}{} } if err := r.mergeDefaultStructValue(data, pointer); err != nil { - return nil + return data, nil } - return gconv.Struct(data, pointer, mapping...) + return data, gconv.Struct(data, pointer, mapping...) } diff --git a/net/ghttp/ghttp_request_param_request.go b/net/ghttp/ghttp_request_param_request.go index 95a2be8e0..2e62a1992 100644 --- a/net/ghttp/ghttp_request_param_request.go +++ b/net/ghttp/ghttp_request_param_request.go @@ -270,14 +270,19 @@ func (r *Request) GetRequestMapStrVar(kvMap ...map[string]interface{}) map[strin // the parameter is a pointer to the struct object. // The optional parameter is used to specify the key to attribute mapping. func (r *Request) GetRequestStruct(pointer interface{}, mapping ...map[string]string) error { - data := r.GetRequestMap() + _, err := r.doGetRequestStruct(pointer, mapping...) + return err +} + +func (r *Request) doGetRequestStruct(pointer interface{}, mapping ...map[string]string) (data map[string]interface{}, err error) { + data = r.GetRequestMap() if data == nil { data = map[string]interface{}{} } if err := r.mergeDefaultStructValue(data, pointer); err != nil { - return nil + return data, nil } - return gconv.Struct(data, pointer, mapping...) + return data, gconv.Struct(data, pointer, mapping...) } // mergeDefaultStructValue merges the request parameters with default values from struct tag definition. diff --git a/net/ghttp/ghttp_unit_request_json_test.go b/net/ghttp/ghttp_unit_request_json_test.go index 656ad6073..09e68c72b 100644 --- a/net/ghttp/ghttp_unit_request_json_test.go +++ b/net/ghttp/ghttp_unit_request_json_test.go @@ -23,7 +23,7 @@ func Test_Params_Json_Request(t *testing.T) { Name string Time *time.Time Pass1 string `p:"password1"` - Pass2 string `p:"password2" v:"required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致"` + Pass2 string `p:"password2" v:"password2@required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致"` } p, _ := ports.PopRand() s := g.Server(p) diff --git a/net/ghttp/ghttp_unit_request_struct_test.go b/net/ghttp/ghttp_unit_request_struct_test.go index 4bdd503a8..687c936c4 100644 --- a/net/ghttp/ghttp_unit_request_struct_test.go +++ b/net/ghttp/ghttp_unit_request_struct_test.go @@ -395,7 +395,7 @@ func Test_Params_Struct(t *testing.T) { Name string Time *time.Time Pass1 string `p:"password1"` - Pass2 string `p:"password2" v:"passwd1 @required|length:2,20|password3#||密码强度不足"` + Pass2 string `p:"password2" v:"password2 @required|length:2,20|password3#||密码强度不足"` } p, _ := ports.PopRand() s := g.Server(p) @@ -452,8 +452,8 @@ func Test_Params_Struct(t *testing.T) { t.Assert(client.PostContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`) t.Assert(client.PostContent("/struct2", `id=1&name=john&password1=123&password2=456`), `1john123456`) t.Assert(client.PostContent("/struct2", ``), ``) - t.Assert(client.PostContent("/struct-valid", `id=1&name=john&password1=123&password2=0`), `The passwd1 value length must be between 2 and 20; 密码强度不足`) - t.Assert(client.PostContent("/parse", `id=1&name=john&password1=123&password2=0`), `The passwd1 value length must be between 2 and 20; 密码强度不足`) + t.Assert(client.PostContent("/struct-valid", `id=1&name=john&password1=123&password2=0`), `The password2 value length must be between 2 and 20; 密码强度不足`) + t.Assert(client.PostContent("/parse", `id=1&name=john&password1=123&password2=0`), `The password2 value length must be between 2 and 20; 密码强度不足`) t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`) }) } @@ -464,7 +464,7 @@ func Test_Params_Structs(t *testing.T) { Name string Time *time.Time Pass1 string `p:"password1"` - Pass2 string `p:"password2" v:"passwd1 @required|length:2,20|password3#||密码强度不足"` + Pass2 string `p:"password2" v:"password2 @required|length:2,20|password3#||密码强度不足"` } p, _ := ports.PopRand() s := g.Server(p) @@ -491,3 +491,39 @@ func Test_Params_Structs(t *testing.T) { ) }) } + +func Test_Params_Struct_Validation(t *testing.T) { + type User struct { + Id int `v:"required"` + Name string `v:"name@required-with:id"` + } + p, _ := ports.PopRand() + s := g.Server(p) + s.Group("/", func(group *ghttp.RouterGroup) { + group.ALL("/", func(r *ghttp.Request) { + var ( + err error + user *User + ) + err = r.Parse(&user) + if err != nil { + r.Response.WriteExit(err) + } + r.Response.WriteExit(user.Id, user.Name) + }) + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + c := g.Client() + c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + t.Assert(c.GetContent("/", ``), `The Id field is required`) + t.Assert(c.GetContent("/", `id=1&name=john`), `1john`) + t.Assert(c.PostContent("/", `id=1&name=john&password1=123&password2=456`), `1john`) + t.Assert(c.PostContent("/", `id=1`), `The name field is required`) + }) +} diff --git a/net/ghttp/ghttp_unit_request_xml_test.go b/net/ghttp/ghttp_unit_request_xml_test.go index 37252b9cc..6dd253d11 100644 --- a/net/ghttp/ghttp_unit_request_xml_test.go +++ b/net/ghttp/ghttp_unit_request_xml_test.go @@ -22,7 +22,7 @@ func Test_Params_Xml_Request(t *testing.T) { Name string Time *time.Time Pass1 string `p:"password1"` - Pass2 string `p:"password2" v:"required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致"` + Pass2 string `p:"password2" v:"password2@required|length:2,20|password3|same:password1#||密码强度不足|两次密码不一致"` } p, _ := ports.PopRand() s := g.Server(p) diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index d3400fd48..c37e4c66f 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -14,6 +14,7 @@ import ( "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/gutil" "strconv" "strings" "time" @@ -232,8 +233,9 @@ func (v *Validator) doCheckBuildInRules( // Values of two fields should be equal as string. case "same": - if v, ok := dataMap[rulePattern]; ok { - if strings.Compare(valueStr, gconv.String(v)) == 0 { + _, foundValue := gutil.MapPossibleItemByKey(dataMap, rulePattern) + if foundValue != nil { + if strings.Compare(valueStr, gconv.String(foundValue)) == 0 { match = true } } @@ -247,8 +249,9 @@ func (v *Validator) doCheckBuildInRules( // Values of two fields should not be equal as string. case "different": match = true - if v, ok := dataMap[rulePattern]; ok { - if strings.Compare(valueStr, gconv.String(v)) == 0 { + _, foundValue := gutil.MapPossibleItemByKey(dataMap, rulePattern) + if foundValue != nil { + if strings.Compare(valueStr, gconv.String(foundValue)) == 0 { match = false } } @@ -302,6 +305,7 @@ func (v *Validator) doCheckBuildInRules( // 16x, 19x case "phone": match = gregex.IsMatchString(`^13[\d]{9}$|^14[5,7]{1}\d{8}$|^15[^4]{1}\d{8}$|^16[\d]{9}$|^17[0,2,3,5,6,7,8]{1}\d{8}$|^18[\d]{9}$|^19[\d]{9}$`, valueStr) + // Loose mobile phone number verification(宽松的手机号验证) // As long as the 11 digit numbers beginning with // 13, 14, 15, 16, 17, 18, 19 can pass the verification (只要满足 13、14、15、16、17、18、19开头的11位数字都可以通过验证) diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 3c690ef0a..fb7df4302 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -9,17 +9,18 @@ package gvalid import ( "github.com/gogf/gf/internal/structs" "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/gutil" "reflect" "strings" ) // doCheckStructWithParamMapInput is used for struct validation for internal function. type doCheckStructWithParamMapInput struct { - Object interface{} // Can be type of struct/*struct. - ParamMap interface{} // Validation parameter map. Note that it acts different according attribute `ParamMapStrict`. - ParamMapStrict bool // Strictly using `ParamMap` as its validation source. If false, it ignores `ParamMap` and retrieves values from `Object`. - CustomRules interface{} // Custom validation rules. - CustomErrorMessageMap CustomMsg // Custom error message map for validation rules. + Object interface{} // Can be type of struct/*struct. + ParamMap interface{} // Validation parameter map. Note that it acts different according attribute `UseParamMapInsteadOfObjectValue`. + UseParamMapInsteadOfObjectValue bool // Using `ParamMap` as its validation source instead of values from `Object`. + CustomRules interface{} // Custom validation rules. + CustomErrorMessageMap CustomMsg // Custom error message map for validation rules. } var ( @@ -39,11 +40,11 @@ func (v *Validator) CheckStruct(object interface{}, customRules interface{}, cus message = customErrorMessageMap[0] } return v.doCheckStructWithParamMap(&doCheckStructWithParamMapInput{ - Object: object, - ParamMap: nil, - ParamMapStrict: false, - CustomRules: customRules, - CustomErrorMessageMap: message, + Object: object, + ParamMap: nil, + UseParamMapInsteadOfObjectValue: false, + CustomRules: customRules, + CustomErrorMessageMap: message, }) } @@ -59,11 +60,11 @@ func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interfa message = customErrorMessageMap[0] } return v.doCheckStructWithParamMap(&doCheckStructWithParamMapInput{ - Object: object, - ParamMap: paramMap, - ParamMapStrict: true, - CustomRules: customRules, - CustomErrorMessageMap: message, + Object: object, + ParamMap: paramMap, + UseParamMapInsteadOfObjectValue: true, + CustomRules: customRules, + CustomErrorMessageMap: message, }) } @@ -147,13 +148,13 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn return nil } // Input parameter map handling. - if input.ParamMap == nil || !input.ParamMapStrict { + if input.ParamMap == nil || !input.UseParamMapInsteadOfObjectValue { inputParamMap = make(map[string]interface{}) } else { inputParamMap = gconv.Map(input.ParamMap) } // Checks and extends the parameters map with struct alias tag. - if !input.ParamMapStrict { + if !input.UseParamMapInsteadOfObjectValue { for nameOrTag, field := range fieldMap { inputParamMap[nameOrTag] = field.Value.Interface() if nameOrTag != field.Name() { @@ -174,7 +175,9 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn } // It here extends the params map using alias names. if _, ok := inputParamMap[name]; !ok { - inputParamMap[name] = field.Value.Interface() + if !input.UseParamMapInsteadOfObjectValue { + inputParamMap[name] = field.Value.Interface() + } } if _, ok := checkRules[name]; !ok { if _, ok := checkRules[fieldName]; ok { @@ -187,7 +190,7 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn } errorRules = append(errorRules, name+"@"+rule) } else { - // The passed rules can overwrite the rules in struct tag. + // The input rules can overwrite the rules in struct tag. continue } if len(msg) > 0 { @@ -229,10 +232,7 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn // The following logic is the same as some of CheckMap. var value interface{} for key, rule := range checkRules { - value = nil - if v, ok := inputParamMap[key]; ok { - value = v - } + _, value = gutil.MapPossibleItemByKey(inputParamMap, key) // It checks each rule and its value in loop. if e := v.doCheck(key, value, rule, customMessage[key], inputParamMap); e != nil { _, item := e.FirstItem() diff --git a/util/gvalid/gvalid_validator_rule_required.go b/util/gvalid/gvalid_validator_rule_required.go index 969d7bb56..9e30b704d 100644 --- a/util/gvalid/gvalid_validator_rule_required.go +++ b/util/gvalid/gvalid_validator_rule_required.go @@ -9,6 +9,7 @@ package gvalid import ( "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/gutil" "reflect" "strings" ) @@ -26,17 +27,19 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string // Example: required-if: id,1,age,18 case "required-if": required = false - array := strings.Split(rulePattern, ",") + var ( + array = strings.Split(rulePattern, ",") + foundValue interface{} + ) // It supports multiple field and value pairs. if len(array)%2 == 0 { for i := 0; i < len(array); { tk := array[i] tv := array[i+1] - if v, ok := dataMap[tk]; ok { - if strings.Compare(tv, gconv.String(v)) == 0 { - required = true - break - } + _, foundValue = gutil.MapPossibleItemByKey(dataMap, tk) + if strings.Compare(tv, gconv.String(foundValue)) == 0 { + required = true + break } i += 2 } @@ -46,18 +49,21 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string // Example: required-unless: id,1,age,18 case "required-unless": required = true - array := strings.Split(rulePattern, ",") + var ( + array = strings.Split(rulePattern, ",") + foundValue interface{} + ) // It supports multiple field and value pairs. if len(array)%2 == 0 { for i := 0; i < len(array); { tk := array[i] tv := array[i+1] - if v, ok := dataMap[tk]; ok { - if strings.Compare(tv, gconv.String(v)) == 0 { - required = false - break - } + _, foundValue = gutil.MapPossibleItemByKey(dataMap, tk) + if strings.Compare(tv, gconv.String(foundValue)) == 0 { + required = false + break } + i += 2 } } @@ -66,9 +72,13 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string // Example: required-with:id,name case "required-with": required = false - array := strings.Split(rulePattern, ",") + var ( + array = strings.Split(rulePattern, ",") + foundValue interface{} + ) for i := 0; i < len(array); i++ { - if !empty.IsEmpty(dataMap[array[i]]) { + _, foundValue = gutil.MapPossibleItemByKey(dataMap, array[i]) + if !empty.IsEmpty(foundValue) { required = true break } @@ -78,9 +88,13 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string // Example: required-with:id,name case "required-with-all": required = true - array := strings.Split(rulePattern, ",") + var ( + array = strings.Split(rulePattern, ",") + foundValue interface{} + ) for i := 0; i < len(array); i++ { - if empty.IsEmpty(dataMap[array[i]]) { + _, foundValue = gutil.MapPossibleItemByKey(dataMap, array[i]) + if empty.IsEmpty(foundValue) { required = false break } @@ -90,9 +104,13 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string // Example: required-with:id,name case "required-without": required = false - array := strings.Split(rulePattern, ",") + var ( + array = strings.Split(rulePattern, ",") + foundValue interface{} + ) for i := 0; i < len(array); i++ { - if empty.IsEmpty(dataMap[array[i]]) { + _, foundValue = gutil.MapPossibleItemByKey(dataMap, array[i]) + if empty.IsEmpty(foundValue) { required = true break } @@ -102,9 +120,13 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string // Example: required-with:id,name case "required-without-all": required = true - array := strings.Split(rulePattern, ",") + var ( + array = strings.Split(rulePattern, ",") + foundValue interface{} + ) for i := 0; i < len(array); i++ { - if !empty.IsEmpty(dataMap[array[i]]) { + _, foundValue = gutil.MapPossibleItemByKey(dataMap, array[i]) + if !empty.IsEmpty(foundValue) { required = false break } diff --git a/util/gvalid/gvalid_z_unit_checkstruct_test.go b/util/gvalid/gvalid_z_unit_checkstruct_test.go index 0f2446dcd..786145ffb 100755 --- a/util/gvalid/gvalid_z_unit_checkstruct_test.go +++ b/util/gvalid/gvalid_z_unit_checkstruct_test.go @@ -357,11 +357,22 @@ func Test_CheckStruct_InvalidRule(t *testing.T) { func TestValidator_CheckStructWithParamMap(t *testing.T) { gtest.C(t, func(t *gtest.T) { type UserApiSearch struct { - Uid int64 `json:"uid" v:"required"` - Nickname string `json:"nickname" v:"required-with:Uid"` + Uid int64 `v:"required"` + Nickname string `v:"required-with:uid"` + } + data := UserApiSearch{ + Uid: 1, + Nickname: "john", + } + t.Assert(gvalid.CheckStructWithParamMap(data, g.Map{"uid": 1, "nickname": "john"}, nil), nil) + }) + gtest.C(t, func(t *gtest.T) { + type UserApiSearch struct { + Uid int64 `v:"required"` + Nickname string `v:"required-with:uid"` } data := UserApiSearch{} - t.Assert(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) + t.AssertNE(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) }) gtest.C(t, func(t *gtest.T) { type UserApiSearch struct { @@ -398,6 +409,6 @@ func TestValidator_CheckStructWithParamMap(t *testing.T) { StartTime: gtime.Now(), EndTime: nil, } - t.AssertNE(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) + t.AssertNE(gvalid.CheckStructWithParamMap(data, g.Map{"start_time": gtime.Now()}, nil), nil) }) } From d21b9d58e1a2a626a0437dcf7708e93bf0f27a2e Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 12 May 2021 21:34:15 +0800 Subject: [PATCH 250/492] improve with feature by supporting attributes struct which also have 'with' tag --- database/gdb/gdb_model_with.go | 336 +++++++-------- ... gdb_z_mysql_association_scanlist_test.go} | 0 .../gdb/gdb_z_mysql_association_with_test.go | 408 ++++++++++++++++++ 3 files changed, 574 insertions(+), 170 deletions(-) rename database/gdb/{gdb_z_mysql_association_test.go => gdb_z_mysql_association_scanlist_test.go} (100%) diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 892474228..bd6d37883 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -53,18 +53,41 @@ func (m *Model) WithAll() *Model { return model } -// getWithTagObjectArrayFrom retrieves and returns object array that have "with" tag in the struct. -func (m *Model) getWithTagObjectArrayFrom(pointer interface{}) ([]interface{}, error) { +// doWithScanStruct handles model association operations feature for single struct. +func (m *Model) doWithScanStruct(pointer interface{}) error { + var ( + err error + allowedTypeStrArray = make([]string, 0) + ) fieldMap, err := structs.FieldMap(pointer, nil, false) if err != nil { - return nil, err + return err } - withTagObjectArray := make([]interface{}, 0) - for _, fieldValue := range fieldMap { + // It checks the with array and automatically calls the ScanList to complete association querying. + if !m.withAll { + for _, field := range fieldMap { + for _, withItem := range m.withArray { + withItemReflectValueType, err := structs.StructType(withItem) + if err != nil { + return err + } + var ( + fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") + withItemReflectValueTypeStr = gstr.TrimAll(withItemReflectValueType.String(), "*[]") + ) + // It does select operation if the field type is in the specified with type array. + if gstr.Compare(fieldTypeStr, withItemReflectValueTypeStr) == 0 { + allowedTypeStrArray = append(allowedTypeStrArray, fieldTypeStr) + } + } + } + } + for _, field := range fieldMap { var ( - withTag string - ormTag = fieldValue.Tag(OrmTagForStruct) - match, _ = gregex.MatchString( + withTag string + ormTag = field.Tag(OrmTagForStruct) + fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") + match, _ = gregex.MatchString( fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith), ormTag, ) @@ -75,103 +98,63 @@ func (m *Model) getWithTagObjectArrayFrom(pointer interface{}) ([]interface{}, e if withTag == "" { continue } - withTagObjectArray = append(withTagObjectArray, fieldValue.Value.Interface()) - } - return withTagObjectArray, nil -} - -// doWithScanStruct handles model association operations feature for single struct. -func (m *Model) doWithScanStruct(pointer interface{}) error { - var ( - err error - withArray = m.withArray - ) - // If with all feature is enabled, it then retrieves all the attributes which have with tag defined. - if m.withAll { - withArray, err = m.getWithTagObjectArrayFrom(pointer) - if err != nil { - return err + if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) { + continue } - } - if len(withArray) == 0 { - return nil - } - fieldMap, err := structs.FieldMap(pointer, nil, false) - if err != nil { - return err - } - // Check the with array and automatically call the ScanList to complete association querying. - for withIndex, withItem := range withArray { - withItemReflectValueType, err := structs.StructType(withItem) - if err != nil { - return err + array := gstr.SplitAndTrim(withTag, "=") + if len(array) == 1 { + // It supports using only one column name + // if both tables associates using the same column name. + array = append(array, withTag) } - withItemReflectValueTypeStr := gstr.TrimAll(withItemReflectValueType.String(), "*[]") - for _, fieldValue := range fieldMap { - var ( - fieldType = fieldValue.Type() - fieldTypeStr = gstr.TrimAll(fieldType.String(), "*[]") - ) - // It does select operation if the field type is in the specified with type array. - if gstr.Compare(fieldTypeStr, withItemReflectValueTypeStr) == 0 { - var ( - withTag string - ormTag = fieldValue.Tag(OrmTagForStruct) - match, _ = gregex.MatchString( - fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith), - ormTag, - ) - ) - if len(match) > 1 { - withTag = match[1] - } - if withTag == "" { - continue - } - array := gstr.SplitAndTrim(withTag, "=") - if len(array) != 2 { - return gerror.Newf(`invalid with tag "%s"`, withTag) - } - var ( - relatedFieldName = array[0] - relatedAttrName = array[1] - relatedFieldValue interface{} - ) - // Find the value of related attribute from `pointer`. - for attributeName, attributeValue := range fieldMap { - if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) { - relatedFieldValue = attributeValue.Value.Interface() - break - } - } - if relatedFieldValue == nil { - return gerror.Newf( - `cannot find the related value for attribute name "%s" of with tag "%s"`, - relatedAttrName, withTag, - ) - } - bindToReflectValue := fieldValue.Value - switch bindToReflectValue.Kind() { - case reflect.Array, reflect.Slice: - if bindToReflectValue.CanAddr() { - bindToReflectValue = bindToReflectValue.Addr() - } - } - model := m.db.With(fieldValue.Value) - for i, v := range withArray { - if i == withIndex { - continue - } - model = model.With(v) - } - err = model.Fields(withItemReflectValueType.FieldKeys()). - Where(relatedFieldName, relatedFieldValue). - Scan(bindToReflectValue) - if err != nil { - return err - } + var ( + model *Model + fieldKeys []string + relatedFieldName = array[0] + relatedAttrName = array[1] + relatedFieldValue interface{} + ) + // Find the value of related attribute from `pointer`. + for attributeName, attributeValue := range fieldMap { + if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) { + relatedFieldValue = attributeValue.Value.Interface() + break } } + if relatedFieldValue == nil { + return gerror.Newf( + `cannot find the related value for attribute name "%s" of with tag "%s"`, + relatedAttrName, withTag, + ) + } + bindToReflectValue := field.Value + switch bindToReflectValue.Kind() { + case reflect.Array, reflect.Slice: + if bindToReflectValue.CanAddr() { + bindToReflectValue = bindToReflectValue.Addr() + } + } + + // It automatically retrieves struct field names from current attribute struct/slice. + if structType, err := structs.StructType(field.Value); err != nil { + return err + } else { + fieldKeys = structType.FieldKeys() + } + + // Recursively with feature checks. + model = m.db.With(field.Value) + if m.withAll { + model = model.WithAll() + } else { + model = model.With(m.withArray...) + } + + err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).Scan(bindToReflectValue) + if err != nil { + return err + } + } return nil } @@ -180,85 +163,98 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { // Also see doWithScanStruct. func (m *Model) doWithScanStructs(pointer interface{}) error { var ( - err error - withArray = m.withArray + err error + allowedTypeStrArray = make([]string, 0) ) - if m.withAll { - withArray, err = m.getWithTagObjectArrayFrom(pointer) - if err != nil { - return err - } - } - if len(withArray) == 0 { - return nil - } fieldMap, err := structs.FieldMap(pointer, nil, false) if err != nil { return err } - for withIndex, withItem := range withArray { - withItemReflectValueType, err := structs.StructType(withItem) - if err != nil { - return err - } - withItemReflectValueTypeStr := gstr.TrimAll(withItemReflectValueType.String(), "*[]") - for fieldName, fieldValue := range fieldMap { - var ( - fieldType = fieldValue.Type() - fieldTypeStr = gstr.TrimAll(fieldType.String(), "*[]") - ) - if gstr.Compare(fieldTypeStr, withItemReflectValueTypeStr) == 0 { - var ( - withTag string - ormTag = fieldValue.Tag(OrmTagForStruct) - match, _ = gregex.MatchString( - fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith), - ormTag, - ) - ) - if len(match) > 1 { - withTag = match[1] - } - if withTag == "" { - continue - } - array := gstr.SplitAndTrim(withTag, "=") - if len(array) != 2 { - return gerror.Newf(`invalid with tag "%s"`, withTag) - } - var ( - relatedFieldName = array[0] - relatedAttrName = array[1] - relatedFieldValue interface{} - ) - // Find the value slice of related attribute from `pointer`. - for attributeName, _ := range fieldMap { - if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) { - relatedFieldValue = ListItemValuesUnique(pointer, attributeName) - break - } - } - if relatedFieldValue == nil { - return gerror.Newf( - `cannot find the related value for attribute name "%s" of with tag "%s"`, - relatedAttrName, withTag, - ) - } - model := m.db.With(fieldValue.Value) - for i, v := range withArray { - if i == withIndex { - continue - } - model = model.With(v) - } - err = model.Fields(withItemReflectValueType.FieldKeys()). - Where(relatedFieldName, relatedFieldValue). - ScanList(pointer, fieldName, withTag) + // It checks the with array and automatically calls the ScanList to complete association querying. + if !m.withAll { + for _, field := range fieldMap { + for _, withItem := range m.withArray { + withItemReflectValueType, err := structs.StructType(withItem) if err != nil { return err } + var ( + fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") + withItemReflectValueTypeStr = gstr.TrimAll(withItemReflectValueType.String(), "*[]") + ) + // It does select operation if the field type is in the specified with type array. + if gstr.Compare(fieldTypeStr, withItemReflectValueTypeStr) == 0 { + allowedTypeStrArray = append(allowedTypeStrArray, fieldTypeStr) + } } } } + + for fieldName, field := range fieldMap { + var ( + withTag string + ormTag = field.Tag(OrmTagForStruct) + fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") + match, _ = gregex.MatchString( + fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith), + ormTag, + ) + ) + if len(match) > 1 { + withTag = match[1] + } + if withTag == "" { + continue + } + if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) { + continue + } + array := gstr.SplitAndTrim(withTag, "=") + if len(array) == 1 { + // It supports using only one column name + // if both tables associates using the same column name. + array = append(array, withTag) + } + var ( + model *Model + fieldKeys []string + relatedFieldName = array[0] + relatedAttrName = array[1] + relatedFieldValue interface{} + ) + // Find the value slice of related attribute from `pointer`. + for attributeName, _ := range fieldMap { + if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) { + relatedFieldValue = ListItemValuesUnique(pointer, attributeName) + break + } + } + if relatedFieldValue == nil { + return gerror.Newf( + `cannot find the related value for attribute name "%s" of with tag "%s"`, + relatedAttrName, withTag, + ) + } + + // It automatically retrieves struct field names from current attribute struct/slice. + if structType, err := structs.StructType(field.Value); err != nil { + return err + } else { + fieldKeys = structType.FieldKeys() + } + + // Recursively with feature checks. + model = m.db.With(field.Value) + if m.withAll { + model = model.WithAll() + } else { + model = model.With(m.withArray...) + } + + err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).ScanList(pointer, fieldName, withTag) + if err != nil { + return err + } + } return nil } diff --git a/database/gdb/gdb_z_mysql_association_test.go b/database/gdb/gdb_z_mysql_association_scanlist_test.go similarity index 100% rename from database/gdb/gdb_z_mysql_association_test.go rename to database/gdb/gdb_z_mysql_association_scanlist_test.go diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index 7cf1bfd92..43a0f3ab7 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -781,3 +781,411 @@ PRIMARY KEY (id) t.Assert(user.UserScores[4].Score, 5) }) } + +func Test_Table_Relation_WithAll_AttributeStructAlsoHasWithTag(t *testing.T) { + var ( + tableUser = "user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +uid int(10) unsigned NOT NULL AUTO_INCREMENT, +address varchar(45) NOT NULL, +PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +uid int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserScores struct { + gmeta.Meta `orm:"table:user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + UserScores []*UserScores `orm:"with:uid"` + } + + type User struct { + gmeta.Meta `orm:"table:user"` + *UserDetail `orm:"with:uid=id"` + Id int `json:"id"` + Name string `json:"name"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserDetail.UserScores), 5) + t.Assert(user.UserDetail.UserScores[0].Uid, 3) + t.Assert(user.UserDetail.UserScores[0].Score, 1) + t.Assert(user.UserDetail.UserScores[4].Uid, 3) + t.Assert(user.UserDetail.UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserDetail.UserScores), 5) + t.Assert(user.UserDetail.UserScores[0].Uid, 4) + t.Assert(user.UserDetail.UserScores[0].Score, 1) + t.Assert(user.UserDetail.UserScores[4].Uid, 4) + t.Assert(user.UserDetail.UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_WithAll_AttributeStructAlsoHasWithTag_MoreDeep(t *testing.T) { + var ( + tableUser = "user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +uid int(10) unsigned NOT NULL AUTO_INCREMENT, +address varchar(45) NOT NULL, +PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +uid int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserScores struct { + gmeta.Meta `orm:"table:user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type UserDetail1 struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + UserScores []*UserScores `orm:"with:uid"` + } + + type UserDetail2 struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + UserDetail1 *UserDetail1 `orm:"with:uid"` + UserScores []*UserScores `orm:"with:uid"` + } + + type UserDetail3 struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + UserDetail2 *UserDetail2 `orm:"with:uid"` + UserScores []*UserScores `orm:"with:uid"` + } + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + UserDetail3 *UserDetail3 `orm:"with:uid"` + UserScores []*UserScores `orm:"with:uid"` + } + + type User struct { + gmeta.Meta `orm:"table:user"` + *UserDetail `orm:"with:uid=id"` + Id int `json:"id"` + Name string `json:"name"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.UserDetail3.Uid, 3) + t.Assert(user.UserDetail.UserDetail3.UserDetail2.Uid, 3) + t.Assert(user.UserDetail.UserDetail3.UserDetail2.UserDetail1.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserDetail.UserScores), 5) + t.Assert(user.UserDetail.UserScores[0].Uid, 3) + t.Assert(user.UserDetail.UserScores[0].Score, 1) + t.Assert(user.UserDetail.UserScores[4].Uid, 3) + t.Assert(user.UserDetail.UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.UserDetail3.Uid, 4) + t.Assert(user.UserDetail.UserDetail3.UserDetail2.Uid, 4) + t.Assert(user.UserDetail.UserDetail3.UserDetail2.UserDetail1.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserDetail.UserScores), 5) + t.Assert(user.UserDetail.UserScores[0].Uid, 4) + t.Assert(user.UserDetail.UserScores[0].Score, 1) + t.Assert(user.UserDetail.UserScores[4].Uid, 4) + t.Assert(user.UserDetail.UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_With_AttributeStructAlsoHasWithTag_MoreDeep(t *testing.T) { + var ( + tableUser = "user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +uid int(10) unsigned NOT NULL AUTO_INCREMENT, +address varchar(45) NOT NULL, +PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +uid int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserScores struct { + gmeta.Meta `orm:"table:user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type UserDetail1 struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + UserScores []*UserScores `orm:"with:uid"` + } + + type UserDetail2 struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + UserDetail1 *UserDetail1 `orm:"with:uid"` + UserScores []*UserScores `orm:"with:uid"` + } + + type UserDetail3 struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + UserDetail2 *UserDetail2 `orm:"with:uid"` + UserScores []*UserScores `orm:"with:uid"` + } + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + UserDetail3 *UserDetail3 `orm:"with:uid"` + UserScores []*UserScores `orm:"with:uid"` + } + + type User struct { + gmeta.Meta `orm:"table:user"` + *UserDetail `orm:"with:uid=id"` + Id int `json:"id"` + Name string `json:"name"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).With(UserDetail{}, UserDetail2{}, UserDetail3{}, UserScores{}).Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.UserDetail3.Uid, 3) + t.Assert(user.UserDetail.UserDetail3.UserDetail2.Uid, 3) + t.Assert(user.UserDetail.UserDetail3.UserDetail2.UserDetail1, nil) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserDetail.UserScores), 5) + t.Assert(user.UserDetail.UserScores[0].Uid, 3) + t.Assert(user.UserDetail.UserScores[0].Score, 1) + t.Assert(user.UserDetail.UserScores[4].Uid, 3) + t.Assert(user.UserDetail.UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).With(UserDetail{}, UserDetail2{}, UserDetail3{}, UserScores{}).Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.UserDetail3.Uid, 4) + t.Assert(user.UserDetail.UserDetail3.UserDetail2.Uid, 4) + t.Assert(user.UserDetail.UserDetail3.UserDetail2.UserDetail1, nil) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserDetail.UserScores), 5) + t.Assert(user.UserDetail.UserScores[0].Uid, 4) + t.Assert(user.UserDetail.UserScores[0].Score, 1) + t.Assert(user.UserDetail.UserScores[4].Uid, 4) + t.Assert(user.UserDetail.UserScores[4].Score, 5) + }) +} From a326f4a9890951b0cbd5dbecd983329f58c348f5 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 13 May 2021 00:16:45 +0800 Subject: [PATCH 251/492] improve package gi18n/gview/gvalid for more flexable i18n feature controll,add custom error configuration and i18n translation for custom error message --- i18n/gi18n/gi18n.go | 37 +++----- i18n/gi18n/gi18n_ctx.go | 29 +++++++ i18n/gi18n/gi18n_instance.go | 2 +- i18n/gi18n/gi18n_manager.go | 55 +++++------- i18n/gi18n/gi18n_unit_test.go | 61 ++++++-------- net/ghttp/ghttp_response_view.go | 6 +- net/ghttp/ghttp_server_admin.go | 2 +- net/ghttp/ghttp_server_pprof.go | 2 +- os/gview/gview.go | 5 +- os/gview/gview_buildin.go | 3 +- os/gview/gview_i18n.go | 31 +++++-- os/gview/gview_parse.go | 17 ++-- os/gview/gview_unit_basic_test.go | 89 ++++++++++---------- os/gview/gview_unit_config_test.go | 9 +- os/gview/gview_unit_encode_test.go | 5 +- os/gview/gview_unit_i18n_test.go | 13 +-- util/gvalid/gvalid_error.go | 5 +- util/gvalid/gvalid_validator.go | 36 ++++---- util/gvalid/gvalid_validator_message.go | 15 ++-- util/gvalid/gvalid_z_unit_i18n_test.go | 50 +++++++++++ util/gvalid/testdata/i18n/cn/validation.toml | 48 +++++++++++ util/gvalid/testdata/i18n/en/validation.toml | 45 ++++++++++ 22 files changed, 369 insertions(+), 196 deletions(-) create mode 100644 i18n/gi18n/gi18n_ctx.go create mode 100644 util/gvalid/gvalid_z_unit_i18n_test.go create mode 100644 util/gvalid/testdata/i18n/cn/validation.toml create mode 100644 util/gvalid/testdata/i18n/en/validation.toml diff --git a/i18n/gi18n/gi18n.go b/i18n/gi18n/gi18n.go index b0a85400b..bb2cc07e4 100644 --- a/i18n/gi18n/gi18n.go +++ b/i18n/gi18n/gi18n.go @@ -7,6 +7,8 @@ // Package gi18n implements internationalization and localization. package gi18n +import "context" + // SetPath sets the directory path storing i18n files. func SetPath(path string) error { return Instance().SetPath(path) @@ -23,42 +25,29 @@ func SetDelimiters(left, right string) { } // T is alias of Translate for convenience. -func T(content string, language ...string) string { - return Instance().T(content, language...) +func T(ctx context.Context, content string) string { + return Instance().T(ctx, content) } // Tf is alias of TranslateFormat for convenience. -func Tf(format string, values ...interface{}) string { - return Instance().TranslateFormat(format, values...) -} - -// Tfl is alias of TranslateFormatLang for convenience. -func Tfl(language string, format string, values ...interface{}) string { - return Instance().TranslateFormatLang(language, format, values...) +func Tf(ctx context.Context, format string, values ...interface{}) string { + return Instance().TranslateFormat(ctx, format, values...) } // TranslateFormat translates, formats and returns the with configured language // and given . -func TranslateFormat(format string, values ...interface{}) string { - return Instance().TranslateFormat(format, values...) -} - -// TranslateFormatLang translates, formats and returns the with configured language -// and given . The parameter specifies custom translation language ignoring -// configured language. If is given empty string, it uses the default configured -// language for the translation. -func TranslateFormatLang(language string, format string, values ...interface{}) string { - return Instance().TranslateFormatLang(language, format, values...) +func TranslateFormat(ctx context.Context, format string, values ...interface{}) string { + return Instance().TranslateFormat(ctx, format, values...) } // Translate translates with configured language and returns the translated content. // The parameter specifies custom translation language ignoring configured language. -func Translate(content string, language ...string) string { - return Instance().Translate(content, language...) +func Translate(ctx context.Context, content string) string { + return Instance().Translate(ctx, content) } -// GetValue retrieves and returns the configured content for given key and specified language. +// GetContent retrieves and returns the configured content for given key and specified language. // It returns an empty string if not found. -func GetContent(key string, language ...string) string { - return Instance().GetContent(key, language...) +func GetContent(ctx context.Context, key string) string { + return Instance().GetContent(ctx, key) } diff --git a/i18n/gi18n/gi18n_ctx.go b/i18n/gi18n/gi18n_ctx.go new file mode 100644 index 000000000..4a9bc06ce --- /dev/null +++ b/i18n/gi18n/gi18n_ctx.go @@ -0,0 +1,29 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package gi18n implements internationalization and localization. +package gi18n + +import "context" + +const ( + ctxLanguage = "I18nLanguage" +) + +// WithLanguage append language setting to the context and returns a new context. +func WithLanguage(ctx context.Context, language string) context.Context { + return context.WithValue(ctx, ctxLanguage, language) +} + +// LanguageFromCtx retrieves and returns language name from context. +// It returns an empty string if it is not set previously. +func LanguageFromCtx(ctx context.Context) string { + v := ctx.Value(ctxLanguage) + if v != nil { + return v.(string) + } + return "" +} diff --git a/i18n/gi18n/gi18n_instance.go b/i18n/gi18n/gi18n_instance.go index d9abdbde2..43af97cc1 100644 --- a/i18n/gi18n/gi18n_instance.go +++ b/i18n/gi18n/gi18n_instance.go @@ -9,7 +9,7 @@ package gi18n import "github.com/gogf/gf/container/gmap" const ( - // Default group name for instance usage. + // DefaultName is the default group name for instance usage. DefaultName = "default" ) diff --git a/i18n/gi18n/gi18n_manager.go b/i18n/gi18n/gi18n_manager.go index 6b737df94..9dc6a8639 100644 --- a/i18n/gi18n/gi18n_manager.go +++ b/i18n/gi18n/gi18n_manager.go @@ -7,6 +7,7 @@ package gi18n import ( + "context" "errors" "fmt" "github.com/gogf/gf/internal/intlog" @@ -25,7 +26,7 @@ import ( "github.com/gogf/gf/os/gres" ) -// Manager, it is concurrent safe, supporting hot reload. +// Manager for i18n contents, it is concurrent safe, supporting hot reload. type Manager struct { mu sync.RWMutex data map[string]map[string]string // Translating map. @@ -36,13 +37,13 @@ type Manager struct { // Options is used for i18n object configuration. type Options struct { Path string // I18n files storage path. - Language string // Local language. + Language string // Default local language. Delimiters []string // Delimiters for variable parsing. } var ( - // defaultDelimiters defines the default key variable delimiters. - defaultDelimiters = []string{"{#", "}"} + defaultLanguage = "en" // defaultDelimiters defines the default language if user does not specified in options. + defaultDelimiters = []string{"{#", "}"} // defaultDelimiters defines the default key variable delimiters. ) // New creates and returns a new i18n manager. @@ -55,6 +56,9 @@ func New(options ...Options) *Manager { } else { opts = DefaultOptions() } + if len(opts.Language) == 0 { + opts.Language = defaultLanguage + } if len(opts.Delimiters) == 0 { opts.Delimiters = defaultDelimiters } @@ -118,45 +122,30 @@ func (m *Manager) SetDelimiters(left, right string) { } // T is alias of Translate for convenience. -func (m *Manager) T(content string, language ...string) string { - return m.Translate(content, language...) +func (m *Manager) T(ctx context.Context, content string) string { + return m.Translate(ctx, content) } // Tf is alias of TranslateFormat for convenience. -func (m *Manager) Tf(format string, values ...interface{}) string { - return m.TranslateFormat(format, values...) -} - -// Tfl is alias of TranslateFormatLang for convenience. -func (m *Manager) Tfl(language string, format string, values ...interface{}) string { - return m.TranslateFormatLang(language, format, values...) +func (m *Manager) Tf(ctx context.Context, format string, values ...interface{}) string { + return m.TranslateFormat(ctx, format, values...) } // TranslateFormat translates, formats and returns the with configured language // and given . -func (m *Manager) TranslateFormat(format string, values ...interface{}) string { - return fmt.Sprintf(m.Translate(format), values...) -} - -// TranslateFormatLang translates, formats and returns the with configured language -// and given . The parameter specifies custom translation language ignoring -// configured language. If is given empty string, it uses the default configured -// language for the translation. -func (m *Manager) TranslateFormatLang(language string, format string, values ...interface{}) string { - return fmt.Sprintf(m.Translate(format, language), values...) +func (m *Manager) TranslateFormat(ctx context.Context, format string, values ...interface{}) string { + return fmt.Sprintf(m.Translate(ctx, format), values...) } // Translate translates with configured language. // The parameter specifies custom translation language ignoring configured language. -func (m *Manager) Translate(content string, language ...string) string { +func (m *Manager) Translate(ctx context.Context, content string) string { m.init() m.mu.RLock() defer m.mu.RUnlock() transLang := m.options.Language - if len(language) > 0 && language[0] != "" { - transLang = language[0] - } else { - transLang = m.options.Language + if lang := LanguageFromCtx(ctx); lang != "" { + transLang = lang } data := m.data[transLang] if data == nil { @@ -179,17 +168,15 @@ func (m *Manager) Translate(content string, language ...string) string { return result } -// GetValue retrieves and returns the configured content for given key and specified language. +// GetContent retrieves and returns the configured content for given key and specified language. // It returns an empty string if not found. -func (m *Manager) GetContent(key string, language ...string) string { +func (m *Manager) GetContent(ctx context.Context, key string) string { m.init() m.mu.RLock() defer m.mu.RUnlock() transLang := m.options.Language - if len(language) > 0 && language[0] != "" { - transLang = language[0] - } else { - transLang = m.options.Language + if lang := LanguageFromCtx(ctx); lang != "" { + transLang = lang } if data, ok := m.data[transLang]; ok { return data[key] diff --git a/i18n/gi18n/gi18n_unit_test.go b/i18n/gi18n/gi18n_unit_test.go index b0012c1c7..0579bce5c 100644 --- a/i18n/gi18n/gi18n_unit_test.go +++ b/i18n/gi18n/gi18n_unit_test.go @@ -7,6 +7,7 @@ package gi18n_test import ( + "context" "testing" "github.com/gogf/gf/os/gres" @@ -32,16 +33,16 @@ func Test_Basic(t *testing.T) { Path: gdebug.TestDataPath("i18n"), }) i18n.SetLanguage("none") - t.Assert(i18n.T("{#hello}{#world}"), "{#hello}{#world}") + t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}") i18n.SetLanguage("ja") - t.Assert(i18n.T("{#hello}{#world}"), "こんにちは世界") + t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "こんにちは世界") i18n.SetLanguage("zh-CN") - t.Assert(i18n.T("{#hello}{#world}"), "你好世界") + t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "你好世界") i18n.SetDelimiters("{$", "}") - t.Assert(i18n.T("{#hello}{#world}"), "{#hello}{#world}") - t.Assert(i18n.T("{$hello}{$world}"), "你好世界") + t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}") + t.Assert(i18n.T(context.Background(), "{$hello}{$world}"), "你好世界") }) gtest.C(t, func(t *gtest.T) { @@ -49,13 +50,13 @@ func Test_Basic(t *testing.T) { Path: gdebug.TestDataPath("i18n-file"), }) i18n.SetLanguage("none") - t.Assert(i18n.T("{#hello}{#world}"), "{#hello}{#world}") + t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}") i18n.SetLanguage("ja") - t.Assert(i18n.T("{#hello}{#world}"), "こんにちは世界") + t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "こんにちは世界") i18n.SetLanguage("zh-CN") - t.Assert(i18n.T("{#hello}{#world}"), "你好世界") + t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "你好世界") }) gtest.C(t, func(t *gtest.T) { @@ -63,13 +64,13 @@ func Test_Basic(t *testing.T) { Path: gdebug.CallerDirectory() + gfile.Separator + "testdata" + gfile.Separator + "i18n-dir", }) i18n.SetLanguage("none") - t.Assert(i18n.T("{#hello}{#world}"), "{#hello}{#world}") + t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}") i18n.SetLanguage("ja") - t.Assert(i18n.T("{#hello}{#world}"), "こんにちは世界") + t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "こんにちは世界") i18n.SetLanguage("zh-CN") - t.Assert(i18n.T("{#hello}{#world}"), "你好世界") + t.Assert(i18n.T(context.Background(), "{#hello}{#world}"), "你好世界") }) } @@ -80,18 +81,10 @@ func Test_TranslateFormat(t *testing.T) { Path: gdebug.TestDataPath("i18n"), }) i18n.SetLanguage("none") - t.Assert(i18n.Tf("{#hello}{#world} %d", 2020), "{#hello}{#world} 2020") + t.Assert(i18n.Tf(context.Background(), "{#hello}{#world} %d", 2020), "{#hello}{#world} 2020") i18n.SetLanguage("ja") - t.Assert(i18n.Tf("{#hello}{#world} %d", 2020), "こんにちは世界 2020") - }) - // Tfl - gtest.C(t, func(t *gtest.T) { - i18n := gi18n.New(gi18n.Options{ - Path: gdebug.TestDataPath("i18n"), - }) - t.Assert(i18n.Tfl("ja", "{#hello}{#world} %d", 2020), "こんにちは世界 2020") - t.Assert(i18n.Tfl("zh-CN", "{#hello}{#world} %d", 2020), "你好世界 2020") + t.Assert(i18n.Tf(context.Background(), "{#hello}{#world} %d", 2020), "こんにちは世界 2020") }) } @@ -101,13 +94,13 @@ func Test_DefaultManager(t *testing.T) { t.Assert(err, nil) gi18n.SetLanguage("none") - t.Assert(gi18n.T("{#hello}{#world}"), "{#hello}{#world}") + t.Assert(gi18n.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}") gi18n.SetLanguage("ja") - t.Assert(gi18n.T("{#hello}{#world}"), "こんにちは世界") + t.Assert(gi18n.T(context.Background(), "{#hello}{#world}"), "こんにちは世界") gi18n.SetLanguage("zh-CN") - t.Assert(gi18n.T("{#hello}{#world}"), "你好世界") + t.Assert(gi18n.T(context.Background(), "{#hello}{#world}"), "你好世界") }) gtest.C(t, func(t *gtest.T) { @@ -115,13 +108,13 @@ func Test_DefaultManager(t *testing.T) { t.Assert(err, nil) gi18n.SetLanguage("none") - t.Assert(gi18n.Translate("{#hello}{#world}"), "{#hello}{#world}") + t.Assert(gi18n.Translate(context.Background(), "{#hello}{#world}"), "{#hello}{#world}") gi18n.SetLanguage("ja") - t.Assert(gi18n.Translate("{#hello}{#world}"), "こんにちは世界") + t.Assert(gi18n.Translate(context.Background(), "{#hello}{#world}"), "こんにちは世界") gi18n.SetLanguage("zh-CN") - t.Assert(gi18n.Translate("{#hello}{#world}"), "你好世界") + t.Assert(gi18n.Translate(context.Background(), "{#hello}{#world}"), "你好世界") }) } @@ -132,21 +125,21 @@ func Test_Instance(t *testing.T) { err := m.SetPath("i18n-dir") t.Assert(err, nil) m.SetLanguage("zh-CN") - t.Assert(m.T("{#hello}{#world}"), "你好世界") + t.Assert(m.T(context.Background(), "{#hello}{#world}"), "你好世界") }) gtest.C(t, func(t *gtest.T) { m := gi18n.Instance() - t.Assert(m.T("{#hello}{#world}"), "你好世界") + t.Assert(m.T(context.Background(), "{#hello}{#world}"), "你好世界") }) gtest.C(t, func(t *gtest.T) { - t.Assert(g.I18n().T("{#hello}{#world}"), "你好世界") + t.Assert(g.I18n().T(context.Background(), "{#hello}{#world}"), "你好世界") }) // Default language is: en gtest.C(t, func(t *gtest.T) { m := gi18n.Instance(gconv.String(gtime.TimestampNano())) - t.Assert(m.T("{#hello}{#world}"), "HelloWorld") + t.Assert(m.T(context.Background(), "{#hello}{#world}"), "HelloWorld") }) } @@ -157,12 +150,12 @@ func Test_Resource(t *testing.T) { t.Assert(err, nil) m.SetLanguage("none") - t.Assert(m.T("{#hello}{#world}"), "{#hello}{#world}") + t.Assert(m.T(context.Background(), "{#hello}{#world}"), "{#hello}{#world}") m.SetLanguage("ja") - t.Assert(m.T("{#hello}{#world}"), "こんにちは世界") + t.Assert(m.T(context.Background(), "{#hello}{#world}"), "こんにちは世界") m.SetLanguage("zh-CN") - t.Assert(m.T("{#hello}{#world}"), "你好世界") + t.Assert(m.T(context.Background(), "{#hello}{#world}"), "你好世界") }) } diff --git a/net/ghttp/ghttp_response_view.go b/net/ghttp/ghttp_response_view.go index e3361197c..194d04c0d 100644 --- a/net/ghttp/ghttp_response_view.go +++ b/net/ghttp/ghttp_response_view.go @@ -59,18 +59,18 @@ func (r *Response) WriteTplContent(content string, params ...gview.Params) error // ParseTpl parses given template file with given template variables // and returns the parsed template content. func (r *Response) ParseTpl(tpl string, params ...gview.Params) (string, error) { - return r.Request.GetView().Parse(tpl, r.buildInVars(params...)) + return r.Request.GetView().Parse(r.Request.Context(), tpl, r.buildInVars(params...)) } // ParseDefault parses the default template file with params. func (r *Response) ParseTplDefault(params ...gview.Params) (string, error) { - return r.Request.GetView().ParseDefault(r.buildInVars(params...)) + return r.Request.GetView().ParseDefault(r.Request.Context(), r.buildInVars(params...)) } // ParseTplContent parses given template file with given template parameters // and returns the parsed template content. func (r *Response) ParseTplContent(content string, params ...gview.Params) (string, error) { - return r.Request.GetView().ParseContent(content, r.buildInVars(params...)) + return r.Request.GetView().ParseContent(r.Request.Context(), content, r.buildInVars(params...)) } // buildInVars merges build-in variables into and returns the new template variables. diff --git a/net/ghttp/ghttp_server_admin.go b/net/ghttp/ghttp_server_admin.go index ee47fc2b5..dc4b107ea 100644 --- a/net/ghttp/ghttp_server_admin.go +++ b/net/ghttp/ghttp_server_admin.go @@ -26,7 +26,7 @@ func (p *utilAdmin) Index(r *Request) { "path": gfile.SelfPath(), "uri": strings.TrimRight(r.URL.Path, "/"), } - buffer, _ := gview.ParseContent(` + buffer, _ := gview.ParseContent(r.Context(), ` GoFrame Web Server Admin diff --git a/net/ghttp/ghttp_server_pprof.go b/net/ghttp/ghttp_server_pprof.go index dc63a6a74..cef4f2d35 100644 --- a/net/ghttp/ghttp_server_pprof.go +++ b/net/ghttp/ghttp_server_pprof.go @@ -62,7 +62,7 @@ func (p *utilPProf) Index(r *Request) { "profiles": profiles, } if len(action) == 0 { - buffer, _ := gview.ParseContent(` + buffer, _ := gview.ParseContent(r.Context(), ` GoFrame PProf diff --git a/os/gview/gview.go b/os/gview/gview.go index 54813b9ab..dc0013add 100644 --- a/os/gview/gview.go +++ b/os/gview/gview.go @@ -11,6 +11,7 @@ package gview import ( + "context" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/internal/intlog" @@ -50,9 +51,9 @@ func checkAndInitDefaultView() { // ParseContent parses the template content directly using the default view object // and returns the parsed content. -func ParseContent(content string, params ...Params) (string, error) { +func ParseContent(ctx context.Context, content string, params ...Params) (string, error) { checkAndInitDefaultView() - return defaultViewObj.ParseContent(content, params...) + return defaultViewObj.ParseContent(ctx, content, params...) } // New returns a new view object. diff --git a/os/gview/gview_buildin.go b/os/gview/gview_buildin.go index 064903897..c838750ee 100644 --- a/os/gview/gview_buildin.go +++ b/os/gview/gview_buildin.go @@ -7,6 +7,7 @@ package gview import ( + "context" "fmt" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/util/gutil" @@ -115,7 +116,7 @@ func (view *View) buildInFuncInclude(file interface{}, data ...map[string]interf return "" } // It will search the file internally. - content, err := view.Parse(path, m) + content, err := view.Parse(context.TODO(), path, m) if err != nil { return htmltpl.HTML(err.Error()) } diff --git a/os/gview/gview_i18n.go b/os/gview/gview_i18n.go index df8ca6584..554f2f567 100644 --- a/os/gview/gview_i18n.go +++ b/os/gview/gview_i18n.go @@ -6,18 +6,33 @@ package gview -import "github.com/gogf/gf/util/gconv" +import ( + "context" + "github.com/gogf/gf/i18n/gi18n" + "github.com/gogf/gf/util/gconv" +) + +const ( + i18nLanguageVariableName = "I18nLanguage" +) // i18nTranslate translate the content with i18n feature. -func (view *View) i18nTranslate(content string, params Params) string { +func (view *View) i18nTranslate(ctx context.Context, content string, variables Params) string { if view.config.I18nManager != nil { - if v, ok := params["I18nLanguage"]; ok { - language := gconv.String(v) - if language != "" { - return view.config.I18nManager.T(content, language) - } + // Compatible with old version. + if language, ok := variables[i18nLanguageVariableName]; ok { + ctx = gi18n.WithLanguage(ctx, gconv.String(language)) } - return view.config.I18nManager.T(content) + return view.config.I18nManager.T(ctx, content) } return content } + +// setI18nLanguageFromCtx retrieves language name from context and sets it to template variables map. +func (view *View) setI18nLanguageFromCtx(ctx context.Context, variables map[string]interface{}) { + if language, ok := variables[i18nLanguageVariableName]; !ok { + if language = gi18n.LanguageFromCtx(ctx); language != "" { + variables[i18nLanguageVariableName] = language + } + } +} diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index e02d9dd75..f69d7e08b 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -8,6 +8,7 @@ package gview import ( "bytes" + "context" "errors" "fmt" "github.com/gogf/gf/encoding/ghash" @@ -54,7 +55,7 @@ var ( // Parse parses given template file with given template variables // and returns the parsed template content. -func (view *View) Parse(file string, params ...Params) (result string, err error) { +func (view *View) Parse(ctx context.Context, file string, params ...Params) (result string, err error) { var tpl interface{} // It caches the file, folder and its content to enhance performance. r := view.fileCacheMap.GetOrSetFuncLock(file, func() interface{} { @@ -125,6 +126,8 @@ func (view *View) Parse(file string, params ...Params) (result string, err error if len(view.data) > 0 { gutil.MapMerge(variables, view.data) } + view.setI18nLanguageFromCtx(ctx, variables) + buffer := bytes.NewBuffer(nil) if view.config.AutoEncode { newTpl, err := tpl.(*htmltpl.Template).Clone() @@ -142,18 +145,18 @@ func (view *View) Parse(file string, params ...Params) (result string, err error // TODO any graceful plan to replace ""? result = gstr.Replace(buffer.String(), "", "") - result = view.i18nTranslate(result, variables) + result = view.i18nTranslate(ctx, result, variables) return result, nil } // ParseDefault parses the default template file with params. -func (view *View) ParseDefault(params ...Params) (result string, err error) { - return view.Parse(view.config.DefaultFile, params...) +func (view *View) ParseDefault(ctx context.Context, params ...Params) (result string, err error) { + return view.Parse(ctx, view.config.DefaultFile, params...) } // ParseContent parses given template content with template variables // and returns the parsed content in []byte. -func (view *View) ParseContent(content string, params ...Params) (string, error) { +func (view *View) ParseContent(ctx context.Context, content string, params ...Params) (string, error) { // It's not necessary continuing parsing if template content is empty. if content == "" { return "", nil @@ -191,6 +194,8 @@ func (view *View) ParseContent(content string, params ...Params) (string, error) if len(view.data) > 0 { gutil.MapMerge(variables, view.data) } + view.setI18nLanguageFromCtx(ctx, variables) + buffer := bytes.NewBuffer(nil) if view.config.AutoEncode { newTpl, err := tpl.(*htmltpl.Template).Clone() @@ -207,7 +212,7 @@ func (view *View) ParseContent(content string, params ...Params) (string, error) } // TODO any graceful plan to replace ""? result := gstr.Replace(buffer.String(), "", "") - result = view.i18nTranslate(result, variables) + result = view.i18nTranslate(ctx, result, variables) return result, nil } diff --git a/os/gview/gview_unit_basic_test.go b/os/gview/gview_unit_basic_test.go index 53caf1b8c..4cad9247c 100644 --- a/os/gview/gview_unit_basic_test.go +++ b/os/gview/gview_unit_basic_test.go @@ -7,6 +7,7 @@ package gview_test import ( + "context" "github.com/gogf/gf/encoding/ghtml" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" @@ -39,18 +40,18 @@ func Test_Basic(t *testing.T) { view.Assigns(g.Map{"version": "1.7.0"}) view.BindFunc("GetName", func() string { return "gf" }) view.BindFuncMap(gview.FuncMap{"GetVersion": func() string { return "1.7.0" }}) - result, err := view.ParseContent(str, g.Map{"other": "that's all"}) + result, err := view.ParseContent(context.TODO(), str, g.Map{"other": "that's all"}) t.Assert(err != nil, false) t.Assert(result, "hello gf,version:1.7.0;hello gf,version:1.7.0;that's all") //测试api方法 str = `hello {{.name}}` - result, err = gview.ParseContent(str, g.Map{"name": "gf"}) + result, err = gview.ParseContent(context.TODO(), str, g.Map{"name": "gf"}) t.Assert(err != nil, false) t.Assert(result, "hello gf") //测试instance方法 - result, err = gview.Instance().ParseContent(str, g.Map{"name": "gf"}) + result, err = gview.Instance().ParseContent(context.TODO(), str, g.Map{"name": "gf"}) t.Assert(err != nil, false) t.Assert(result, "hello gf") }) @@ -59,132 +60,132 @@ func Test_Basic(t *testing.T) { func Test_Func(t *testing.T) { gtest.C(t, func(t *gtest.T) { str := `{{eq 1 1}};{{eq 1 2}};{{eq "A" "B"}}` - result, err := gview.ParseContent(str, nil) + result, err := gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `true;false;false`) str = `{{ne 1 2}};{{ne 1 1}};{{ne "A" "B"}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `true;false;true`) str = `{{lt 1 2}};{{lt 1 1}};{{lt 1 0}};{{lt "A" "B"}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `true;false;false;true`) str = `{{le 1 2}};{{le 1 1}};{{le 1 0}};{{le "A" "B"}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `true;true;false;true`) str = `{{gt 1 2}};{{gt 1 1}};{{gt 1 0}};{{gt "A" "B"}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `false;false;true;false`) str = `{{ge 1 2}};{{ge 1 1}};{{ge 1 0}};{{ge "A" "B"}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `false;true;true;false`) str = `{{"
测试
"|text}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `测试`) str = `{{"
测试
"|html}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `<div>测试</div>`) str = `{{"
测试
"|htmlencode}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `<div>测试</div>`) str = `{{"<div>测试</div>"|htmldecode}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `
测试
`) str = `{{"https://goframe.org"|url}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `https%3A%2F%2Fgoframe.org`) str = `{{"https://goframe.org"|urlencode}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `https%3A%2F%2Fgoframe.org`) str = `{{"https%3A%2F%2Fgoframe.org"|urldecode}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `https://goframe.org`) str = `{{"https%3NA%2F%2Fgoframe.org"|urldecode}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(gstr.Contains(result, "invalid URL escape"), true) str = `{{1540822968 | date "Y-m-d"}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `2018-10-29`) str = `{{date "Y-m-d"}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) str = `{{"我是中国人" | substr 2 -1}};{{"我是中国人" | substr 2 2}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `中国人;中国`) str = `{{"我是中国人" | strlimit 2 "..."}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `我是...`) str = `{{"I'm中国人" | replace "I'm" "我是"}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `我是中国人`) str = `{{compare "A" "B"}};{{compare "1" "2"}};{{compare 2 1}};{{compare 1 1}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `-1;-1;1;0`) str = `{{"热爱GF热爱生活" | hidestr 20 "*"}};{{"热爱GF热爱生活" | hidestr 50 "*"}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `热爱GF*爱生活;热爱****生活`) str = `{{"热爱GF热爱生活" | highlight "GF" "red"}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `热爱GF热爱生活`) str = `{{"gf" | toupper}};{{"GF" | tolower}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `GF;gf`) str = `{{concat "I" "Love" "GoFrame"}}` - result, err = gview.ParseContent(str, nil) + result, err = gview.ParseContent(context.TODO(), str, nil) t.Assert(err, nil) t.Assert(result, `ILoveGoFrame`) }) // eq: multiple values. gtest.C(t, func(t *gtest.T) { str := `{{eq 1 2 1 3 4 5}}` - result, err := gview.ParseContent(str, nil) + result, err := gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `true`) }) gtest.C(t, func(t *gtest.T) { str := `{{eq 6 2 1 3 4 5}}` - result, err := gview.ParseContent(str, nil) + result, err := gview.ParseContent(context.TODO(), str, nil) t.Assert(err != nil, false) t.Assert(result, `false`) }) @@ -193,7 +194,7 @@ func Test_Func(t *testing.T) { func Test_FuncNl2Br(t *testing.T) { gtest.C(t, func(t *gtest.T) { str := `{{"Go\nFrame" | nl2br}}` - result, err := gview.ParseContent(str, nil) + result, err := gview.ParseContent(context.TODO(), str, nil) t.Assert(err, nil) t.Assert(result, `Go
Frame`) }) @@ -203,7 +204,7 @@ func Test_FuncNl2Br(t *testing.T) { s += "Go\nFrame\n中文" } str := `{{.content | nl2br}}` - result, err := gview.ParseContent(str, g.Map{ + result, err := gview.ParseContent(context.TODO(), str, g.Map{ "content": s, }) t.Assert(err, nil) @@ -231,10 +232,10 @@ func Test_FuncInclude(t *testing.T) { ioutil.WriteFile(templatePath+gfile.Separator+"footer.html", []byte(footer), 0644) ioutil.WriteFile(templatePath+gfile.Separator+"layout.html", []byte(layout), 0644) view := gview.New(templatePath) - result, err := view.Parse("notfound.html") + result, err := view.Parse(context.TODO(), "notfound.html") t.Assert(err != nil, true) t.Assert(result, ``) - result, err = view.Parse("layout.html") + result, err = view.Parse(context.TODO(), "layout.html") t.Assert(err != nil, false) t.Assert(result, `

HEADER

hello gf

@@ -243,7 +244,7 @@ func Test_FuncInclude(t *testing.T) { gfile.Mkdir(templatePath + gfile.Separator + "template") gfile.Create(notfoundPath) ioutil.WriteFile(notfoundPath, []byte("notfound"), 0644) - result, err = view.Parse("notfound.html") + result, err = view.Parse(context.TODO(), "notfound.html") t.Assert(err != nil, true) t.Assert(result, ``) }) @@ -283,7 +284,7 @@ func Test_ParseContent(t *testing.T) { gtest.C(t, func(t *gtest.T) { str := `{{.name}}` view := gview.New() - result, err := view.ParseContent(str, g.Map{"name": func() {}}) + result, err := view.ParseContent(context.TODO(), str, g.Map{"name": func() {}}) t.Assert(err != nil, true) t.Assert(result, ``) }) @@ -306,7 +307,7 @@ func Test_HotReload(t *testing.T) { view := gview.New(dirPath) time.Sleep(100 * time.Millisecond) - result, err := view.Parse("test.html", g.Map{ + result, err := view.Parse(context.TODO(), "test.html", g.Map{ "var": "1", }) t.Assert(err, nil) @@ -317,7 +318,7 @@ func Test_HotReload(t *testing.T) { t.Assert(err, nil) time.Sleep(100 * time.Millisecond) - result, err = view.Parse("test.html", g.Map{ + result, err = view.Parse(context.TODO(), "test.html", g.Map{ "var": "2", }) t.Assert(err, nil) @@ -329,7 +330,7 @@ func Test_XSS(t *testing.T) { gtest.C(t, func(t *gtest.T) { v := gview.New() s := "
" - r, err := v.ParseContent("{{.v}}", g.Map{ + r, err := v.ParseContent(context.TODO(), "{{.v}}", g.Map{ "v": s, }) t.Assert(err, nil) @@ -339,7 +340,7 @@ func Test_XSS(t *testing.T) { v := gview.New() v.SetAutoEncode(true) s := "
" - r, err := v.ParseContent("{{.v}}", g.Map{ + r, err := v.ParseContent(context.TODO(), "{{.v}}", g.Map{ "v": s, }) t.Assert(err, nil) @@ -350,7 +351,7 @@ func Test_XSS(t *testing.T) { v := gview.New() v.SetAutoEncode(true) s := "
" - r, err := v.ParseContent("{{if eq 1 1}}{{.v}}{{end}}", g.Map{ + r, err := v.ParseContent(context.TODO(), "{{if eq 1 1}}{{.v}}{{end}}", g.Map{ "v": s, }) t.Assert(err, nil) @@ -371,7 +372,7 @@ func Test_BuildInFuncMap(t *testing.T) { gtest.C(t, func(t *gtest.T) { v := gview.New() v.Assign("v", new(TypeForBuildInFuncMap)) - r, err := v.ParseContent("{{range $k, $v := map .v.Test}} {{$k}}:{{$v}} {{end}}") + r, err := v.ParseContent(context.TODO(), "{{range $k, $v := map .v.Test}} {{$k}}:{{$v}} {{end}}") t.Assert(err, nil) t.Assert(gstr.Contains(r, "Name:john"), true) t.Assert(gstr.Contains(r, "Score:99.9"), true) @@ -394,7 +395,7 @@ func Test_BuildInFuncMaps(t *testing.T) { gtest.C(t, func(t *gtest.T) { v := gview.New() v.Assign("v", new(TypeForBuildInFuncMaps)) - r, err := v.ParseContent("{{range $k, $v := maps .v.Test}} {{$k}}:{{$v.Name}} {{$v.Score}} {{end}}") + r, err := v.ParseContent(context.TODO(), "{{range $k, $v := maps .v.Test}} {{$k}}:{{$v.Name}} {{$v.Score}} {{end}}") t.Assert(err, nil) t.Assert(r, ` 0:john 99.9 1:smith 100 `) }) @@ -407,7 +408,7 @@ func Test_BuildInFuncDump(t *testing.T) { "name": "john", "score": 100, }) - r, err := v.ParseContent("{{dump .}}") + r, err := v.ParseContent(context.TODO(), "{{dump .}}") t.Assert(err, nil) t.Assert(gstr.Contains(r, `"name": "john"`), true) t.Assert(gstr.Contains(r, `"score": 100`), true) @@ -420,7 +421,7 @@ func Test_BuildInFuncJson(t *testing.T) { v.Assign("v", g.Map{ "name": "john", }) - r, err := v.ParseContent("{{json .v}}") + r, err := v.ParseContent(context.TODO(), "{{json .v}}") t.Assert(err, nil) t.Assert(r, `{"name":"john"}`) }) diff --git a/os/gview/gview_unit_config_test.go b/os/gview/gview_unit_config_test.go index 314b4be48..899245157 100644 --- a/os/gview/gview_unit_config_test.go +++ b/os/gview/gview_unit_config_test.go @@ -7,6 +7,7 @@ package gview_test import ( + "context" "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gview" @@ -30,11 +31,11 @@ func Test_Config(t *testing.T) { str := `hello ${.name},version:${.version}` view.Assigns(g.Map{"version": "1.7.0"}) - result, err := view.ParseContent(str, nil) + result, err := view.ParseContent(context.TODO(), str, nil) t.Assert(err, nil) t.Assert(result, "hello gf,version:1.7.0") - result, err = view.ParseDefault() + result, err = view.ParseDefault(context.TODO()) t.Assert(err, nil) t.Assert(result, "name:gf") }) @@ -55,11 +56,11 @@ func Test_ConfigWithMap(t *testing.T) { str := `hello ${.name},version:${.version}` view.Assigns(g.Map{"version": "1.7.0"}) - result, err := view.ParseContent(str, nil) + result, err := view.ParseContent(context.TODO(), str, nil) t.Assert(err, nil) t.Assert(result, "hello gf,version:1.7.0") - result, err = view.ParseDefault() + result, err = view.ParseDefault(context.TODO()) t.Assert(err, nil) t.Assert(result, "name:gf") }) diff --git a/os/gview/gview_unit_encode_test.go b/os/gview/gview_unit_encode_test.go index 1aad3616d..892be705b 100644 --- a/os/gview/gview_unit_encode_test.go +++ b/os/gview/gview_unit_encode_test.go @@ -7,6 +7,7 @@ package gview_test import ( + "context" "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gfile" @@ -20,7 +21,7 @@ func Test_Encode_Parse(t *testing.T) { v := gview.New() v.SetPath(gdebug.TestDataPath("tpl")) v.SetAutoEncode(true) - result, err := v.Parse("encode.tpl", g.Map{ + result, err := v.Parse(context.TODO(), "encode.tpl", g.Map{ "title": "my title", }) t.Assert(err, nil) @@ -33,7 +34,7 @@ func Test_Encode_ParseContent(t *testing.T) { v := gview.New() tplContent := gfile.GetContents(gdebug.TestDataPath("tpl", "encode.tpl")) v.SetAutoEncode(true) - result, err := v.ParseContent(tplContent, g.Map{ + result, err := v.ParseContent(context.TODO(), tplContent, g.Map{ "title": "my title", }) t.Assert(err, nil) diff --git a/os/gview/gview_unit_i18n_test.go b/os/gview/gview_unit_i18n_test.go index b04ba2bbd..23550e66c 100644 --- a/os/gview/gview_unit_i18n_test.go +++ b/os/gview/gview_unit_i18n_test.go @@ -7,6 +7,7 @@ package gview_test import ( + "context" "testing" "github.com/gogf/gf/debug/gdebug" @@ -26,21 +27,21 @@ func Test_I18n(t *testing.T) { g.I18n().SetPath(gdebug.TestDataPath("i18n")) g.I18n().SetLanguage("zh-CN") - result1, err := g.View().ParseContent(content, g.Map{ + result1, err := g.View().ParseContent(context.TODO(), content, g.Map{ "name": "john", }) t.Assert(err, nil) t.Assert(result1, expect1) g.I18n().SetLanguage("ja") - result2, err := g.View().ParseContent(content, g.Map{ + result2, err := g.View().ParseContent(context.TODO(), content, g.Map{ "name": "john", }) t.Assert(err, nil) t.Assert(result2, expect2) g.I18n().SetLanguage("none") - result3, err := g.View().ParseContent(content, g.Map{ + result3, err := g.View().ParseContent(context.TODO(), content, g.Map{ "name": "john", }) t.Assert(err, nil) @@ -54,21 +55,21 @@ func Test_I18n(t *testing.T) { g.I18n().SetPath(gdebug.CallerDirectory() + gfile.Separator + "testdata" + gfile.Separator + "i18n") - result1, err := g.View().ParseContent(content, g.Map{ + result1, err := g.View().ParseContent(context.TODO(), content, g.Map{ "name": "john", "I18nLanguage": "zh-CN", }) t.Assert(err, nil) t.Assert(result1, expect1) - result2, err := g.View().ParseContent(content, g.Map{ + result2, err := g.View().ParseContent(context.TODO(), content, g.Map{ "name": "john", "I18nLanguage": "ja", }) t.Assert(err, nil) t.Assert(result2, expect2) - result3, err := g.View().ParseContent(content, g.Map{ + result3, err := g.View().ParseContent(context.TODO(), content, g.Map{ "name": "john", "I18nLanguage": "none", }) diff --git a/util/gvalid/gvalid_error.go b/util/gvalid/gvalid_error.go index 49ebeb510..7a2a30141 100644 --- a/util/gvalid/gvalid_error.go +++ b/util/gvalid/gvalid_error.go @@ -9,6 +9,7 @@ package gvalid import ( "errors" "github.com/gogf/gf/text/gregex" + "github.com/gogf/gf/text/gstr" "strings" ) @@ -29,7 +30,9 @@ func newError(rules []string, errors map[string]map[string]string) *Error { for field, m := range errors { for k, v := range m { v = strings.Replace(v, ":attribute", field, -1) - m[k], _ = gregex.ReplaceString(`\s{2,}`, ` `, v) + v, _ = gregex.ReplaceString(`\s{2,}`, ` `, v) + v = gstr.Trim(v) + m[k] = v } errors[field] = m } diff --git a/util/gvalid/gvalid_validator.go b/util/gvalid/gvalid_validator.go index 5709092f1..5f516a2c6 100644 --- a/util/gvalid/gvalid_validator.go +++ b/util/gvalid/gvalid_validator.go @@ -6,36 +6,34 @@ package gvalid -import "context" +import ( + "context" + "github.com/gogf/gf/i18n/gi18n" +) // Validator is the validation manager. type Validator struct { - i18nLang string // I18n language. - ctx context.Context // Context containing custom context variables. + ctx context.Context // Context containing custom context variables. + i18nManager *gi18n.Manager // I18n manager for error message translation. + } // New creates and returns a new Validator. func New() *Validator { - return &Validator{} + return &Validator{ + ctx: context.TODO(), + i18nManager: gi18n.Instance(), + } } -// Clone creates and returns a new Validator which is a shallow copy of current one. -func (v *Validator) Clone() *Validator { - newValidator := New() - *newValidator = *v - return newValidator -} - -// I18n is a chaining operation function which sets the I18n language for next validation. -func (v *Validator) I18n(language string) *Validator { - newValidator := v.Clone() - newValidator.i18nLang = language - return newValidator +// I18n sets the i18n manager for the validator. +func (v *Validator) I18n(i18nManager *gi18n.Manager) *Validator { + v.i18nManager = i18nManager + return v } // Ctx is a chaining operation function which sets the context for next validation. func (v *Validator) Ctx(ctx context.Context) *Validator { - newValidator := v.Clone() - newValidator.ctx = ctx - return newValidator + v.ctx = ctx + return v } diff --git a/util/gvalid/gvalid_validator_message.go b/util/gvalid/gvalid_validator_message.go index 56bf17251..e1737dce9 100644 --- a/util/gvalid/gvalid_validator_message.go +++ b/util/gvalid/gvalid_validator_message.go @@ -6,9 +6,8 @@ package gvalid -import ( - "fmt" - "github.com/gogf/gf/i18n/gi18n" +const ( + ruleMessagePrefixForI18n = "gf.gvalid.rule." ) // defaultMessages is the default error messages. @@ -66,15 +65,21 @@ var defaultMessages = map[string]string{ func (v *Validator) getErrorMessageByRule(ruleKey string, customMsgMap map[string]string) string { content := customMsgMap[ruleKey] if content != "" { + // I18n translation. + i18nContent := v.i18nManager.GetContent(v.ctx, content) + if i18nContent != "" { + return i18nContent + } return content } - content = gi18n.GetContent(fmt.Sprintf(`gf.gvalid.rule.%s`, ruleKey), v.i18nLang) + // Retrieve default message according to certain rule. + content = v.i18nManager.GetContent(v.ctx, ruleMessagePrefixForI18n+ruleKey) if content == "" { content = defaultMessages[ruleKey] } // If there's no configured rule message, it uses default one. if content == "" { - content = gi18n.GetContent(`gf.gvalid.rule.__default__`, v.i18nLang) + content = v.i18nManager.GetContent(v.ctx, `gf.gvalid.rule.__default__`) if content == "" { content = defaultMessages["__default__"] } diff --git a/util/gvalid/gvalid_z_unit_i18n_test.go b/util/gvalid/gvalid_z_unit_i18n_test.go new file mode 100644 index 000000000..bb27a3f4e --- /dev/null +++ b/util/gvalid/gvalid_z_unit_i18n_test.go @@ -0,0 +1,50 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gvalid_test + +import ( + "context" + "github.com/gogf/gf/debug/gdebug" + "github.com/gogf/gf/i18n/gi18n" + "github.com/gogf/gf/util/gvalid" + "testing" + + "github.com/gogf/gf/test/gtest" +) + +func TestValidator_I18n(t *testing.T) { + var ( + err *gvalid.Error + i18nManager = gi18n.New(gi18n.Options{Path: gdebug.TestDataPath("i18n")}) + ctxCn = gi18n.WithLanguage(context.TODO(), "cn") + validator = gvalid.New().I18n(i18nManager) + ) + gtest.C(t, func(t *gtest.T) { + err = validator.Check("", "required", nil) + t.Assert(err.String(), "The field is required") + + err = validator.Ctx(ctxCn).Check("", "required", nil) + t.Assert(err.String(), "字段不能为空") + }) + gtest.C(t, func(t *gtest.T) { + err = validator.Ctx(ctxCn).Check("", "required", "CustomMessage") + t.Assert(err.String(), "自定义错误") + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + Page int `v:"required|min:1 # page is required"` + Size int `v:"required|between:1,100 # size is required"` + ProjectId int `v:"between:1,10000 # project id must between :min, :max"` + } + obj := &Params{ + Page: 1, + Size: 10, + } + err := validator.Ctx(ctxCn).CheckStruct(obj, nil) + t.Assert(err.String(), "项目ID必须大于等于1并且要小于等于10000") + }) +} diff --git a/util/gvalid/testdata/i18n/cn/validation.toml b/util/gvalid/testdata/i18n/cn/validation.toml new file mode 100644 index 000000000..0cc409eb5 --- /dev/null +++ b/util/gvalid/testdata/i18n/cn/validation.toml @@ -0,0 +1,48 @@ +"gf.gvalid.rule.required" = ":attribute 字段不能为空" +"gf.gvalid.rule.required-if" = ":attribute 字段不能为空" +"gf.gvalid.rule.required-unless" = ":attribute 字段不能为空" +"gf.gvalid.rule.required-with" = ":attribute 字段不能为空" +"gf.gvalid.rule.required-with-all" = ":attribute 字段不能为空" +"gf.gvalid.rule.required-without" = ":attribute 字段不能为空" +"gf.gvalid.rule.required-without-all" = ":attribute 字段不能为空" +"gf.gvalid.rule.date" = ":attribute 日期格式不正确" +"gf.gvalid.rule.date-format" = ":attribute 日期格式不满足:format" +"gf.gvalid.rule.email" = ":attribute 邮箱地址格式不正确" +"gf.gvalid.rule.phone" = ":attribute 手机号码格式不正确" +"gf.gvalid.rule.phone-loose" = ":attribute 手机号码格式不正确" +"gf.gvalid.rule.telephone" = ":attribute 电话号码格式不正确" +"gf.gvalid.rule.passport" = ":attribute 账号格式不合法,必需以字母开头,只能包含字母、数字和下划线,长度在6~18之间" +"gf.gvalid.rule.password" = ":attribute 密码格式不合法,密码格式为任意6-18位的可见字符" +"gf.gvalid.rule.password2" = ":attribute 密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母和数字" +"gf.gvalid.rule.password3" = ":attribute 密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母、数字和特殊字符" +"gf.gvalid.rule.postcode" = ":attribute 邮政编码不正确" +"gf.gvalid.rule.resident-id" = ":attribute 身份证号码格式不正确" +"gf.gvalid.rule.bank-card" = ":attribute 银行卡号格式不正确" +"gf.gvalid.rule.qq" = ":attribute QQ号码格式不正确" +"gf.gvalid.rule.ip" = ":attribute IP地址格式不正确" +"gf.gvalid.rule.ipv4" = ":attribute IPv4地址格式不正确" +"gf.gvalid.rule.ipv6" = ":attribute IPv6地址格式不正确" +"gf.gvalid.rule.mac" = ":attribute MAC地址格式不正确" +"gf.gvalid.rule.url" = ":attribute URL地址格式不正确" +"gf.gvalid.rule.domain" = ":attribute 域名格式不正确" +"gf.gvalid.rule.length" = ":attribute 字段长度为:min到:max个字符" +"gf.gvalid.rule.min-length" = ":attribute 字段最小长度为:min" +"gf.gvalid.rule.max-length" = ":attribute 字段最大长度为:max" +"gf.gvalid.rule.between" = ":attribute 字段大小为:min到:max" +"gf.gvalid.rule.min" = ":attribute 字段最小值为:min" +"gf.gvalid.rule.max" = ":attribute 字段最大值为:max" +"gf.gvalid.rule.json" = ":attribute 字段应当为JSON格式" +"gf.gvalid.rule.xml" = ":attribute 字段应当为XML格式" +"gf.gvalid.rule.array" = ":attribute 字段应当为数组" +"gf.gvalid.rule.integer" = ":attribute 字段应当为整数" +"gf.gvalid.rule.float" = ":attribute 字段应当为浮点数" +"gf.gvalid.rule.boolean" = ":attribute 字段应当为布尔值" +"gf.gvalid.rule.same" = ":attribute 字段值必须和:field相同" +"gf.gvalid.rule.different" = ":attribute 字段值不能与:field相同" +"gf.gvalid.rule.in" = ":attribute 字段值不合法" +"gf.gvalid.rule.not-in" = ":attribute 字段值不合法" +"gf.gvalid.rule.regex" = ":attribute 字段值不合法" +"gf.gvalid.rule.__default__" = ":attribute 字段值不合法" + +"CustomMessage" = "自定义错误" +"project id must between :min, :max" = "项目ID必须大于等于:min并且要小于等于:max" \ No newline at end of file diff --git a/util/gvalid/testdata/i18n/en/validation.toml b/util/gvalid/testdata/i18n/en/validation.toml new file mode 100644 index 000000000..7dc56cab0 --- /dev/null +++ b/util/gvalid/testdata/i18n/en/validation.toml @@ -0,0 +1,45 @@ +"gf.gvalid.rule.required" = "The :attribute field is required" +"gf.gvalid.rule.required-if" = "The :attribute field is required" +"gf.gvalid.rule.required-unless" = "The :attribute field is required" +"gf.gvalid.rule.required-with" = "The :attribute field is required" +"gf.gvalid.rule.required-with-all" = "The :attribute field is required" +"gf.gvalid.rule.required-without" = "The :attribute field is required" +"gf.gvalid.rule.required-without-all" = "The :attribute field is required" +"gf.gvalid.rule.date" = "The :attribute value is not a valid date" +"gf.gvalid.rule.date-format" = "The :attribute value does not match the format :format" +"gf.gvalid.rule.email" = "The :attribute value must be a valid email address" +"gf.gvalid.rule.phone" = "The :attribute value must be a valid phone number" +"gf.gvalid.rule.phone-loose" = "The :attribute value must be a valid phone number" +"gf.gvalid.rule.telephone" = "The :attribute value must be a valid telephone number" +"gf.gvalid.rule.passport" = "The :attribute value is not a valid passport format" +"gf.gvalid.rule.password" = "The :attribute value is not a valid passport format" +"gf.gvalid.rule.password2" = "The :attribute value is not a valid passport format" +"gf.gvalid.rule.password3" = "The :attribute value is not a valid passport format" +"gf.gvalid.rule.postcode" = "The :attribute value is not a valid passport format" +"gf.gvalid.rule.resident-id" = "The :attribute value is not a valid resident id number" +"gf.gvalid.rule.bank-card" = "The :attribute value must be a valid bank card number" +"gf.gvalid.rule.qq" = "The :attribute value must be a valid QQ number" +"gf.gvalid.rule.ip" = "The :attribute value must be a valid IP address" +"gf.gvalid.rule.ipv4" = "The :attribute value must be a valid IPv4 address" +"gf.gvalid.rule.ipv6" = "The :attribute value must be a valid IPv6 address" +"gf.gvalid.rule.mac" = "The :attribute value must be a valid MAC address" +"gf.gvalid.rule.url" = "The :attribute value must be a valid URL address" +"gf.gvalid.rule.domain" = "The :attribute value must be a valid domain format" +"gf.gvalid.rule.length" = "The :attribute value length must be between :min and :max" +"gf.gvalid.rule.min-length" = "The :attribute value length must be equal or greater than :min" +"gf.gvalid.rule.max-length" = "The :attribute value length must be equal or lesser than :max" +"gf.gvalid.rule.between" = "The :attribute value must be between :min and :max" +"gf.gvalid.rule.min" = "The :attribute value must be equal or greater than :min" +"gf.gvalid.rule.max" = "The :attribute value must be equal or lesser than :max" +"gf.gvalid.rule.json" = "The :attribute value must be a valid JSON string" +"gf.gvalid.rule.xml" = "The :attribute value must be a valid XML string" +"gf.gvalid.rule.array" = "The :attribute value must be an array" +"gf.gvalid.rule.integer" = "The :attribute value must be an integer" +"gf.gvalid.rule.float" = "The :attribute value must be a float" +"gf.gvalid.rule.boolean" = "The :attribute value field must be true or false" +"gf.gvalid.rule.same" = "The :attribute value must be the same as field :field" +"gf.gvalid.rule.different" = "The :attribute value must be different from field :field" +"gf.gvalid.rule.in" = "The :attribute value is not in acceptable range" +"gf.gvalid.rule.not-in" = "The :attribute value is not in acceptable range" +"gf.gvalid.rule.regex" = "The :attribute value is invalid" +"gf.gvalid.rule.__default__" = "The :attribute value is invalid" \ No newline at end of file From 23110b5d195fb7e83ce1697ef3a05d60e7117b26 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 13 May 2021 08:53:54 +0800 Subject: [PATCH 252/492] fix issue in unit testing case of package gins --- frame/gins/gins_z_unit_view_test.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/frame/gins/gins_z_unit_view_test.go b/frame/gins/gins_z_unit_view_test.go index bc8f2d409..084c09705 100644 --- a/frame/gins/gins_z_unit_view_test.go +++ b/frame/gins/gins_z_unit_view_test.go @@ -7,6 +7,7 @@ package gins import ( + "context" "fmt" "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/os/gcfg" @@ -20,7 +21,7 @@ import ( func Test_View(t *testing.T) { gtest.C(t, func(t *gtest.T) { t.AssertNE(View(), nil) - b, e := View().ParseContent(`{{"我是中国人" | substr 2 -1}}`, nil) + b, e := View().ParseContent(context.TODO(), `{{"我是中国人" | substr 2 -1}}`, nil) t.Assert(e, nil) t.Assert(b, "中国人") }) @@ -30,7 +31,7 @@ func Test_View(t *testing.T) { t.Assert(err, nil) defer gfile.Remove(tpl) - b, e := View().Parse("t.tpl", nil) + b, e := View().Parse(context.TODO(), "t.tpl", nil) t.Assert(e, nil) t.Assert(b, "中国人") }) @@ -43,7 +44,7 @@ func Test_View(t *testing.T) { err = View().AddPath(path) t.Assert(err, nil) - b, e := View().Parse("t.tpl", nil) + b, e := View().Parse(context.TODO(), "t.tpl", nil) t.Assert(e, nil) t.Assert(b, "中国人") }) @@ -64,11 +65,11 @@ func Test_View_Config(t *testing.T) { str := `hello ${.name},version:${.version}` view.Assigns(map[string]interface{}{"version": "1.9.0"}) - result, err := view.ParseContent(str, nil) + result, err := view.ParseContent(context.TODO(), str, nil) t.Assert(err, nil) t.Assert(result, "hello test1,version:1.9.0") - result, err = view.ParseDefault() + result, err = view.ParseDefault(context.TODO()) t.Assert(err, nil) t.Assert(result, "test1:test1") }) @@ -86,11 +87,11 @@ func Test_View_Config(t *testing.T) { str := `hello #{.name},version:#{.version}` view.Assigns(map[string]interface{}{"version": "1.9.0"}) - result, err := view.ParseContent(str, nil) + result, err := view.ParseContent(context.TODO(), str, nil) t.Assert(err, nil) t.Assert(result, "hello test2,version:1.9.0") - result, err = view.ParseDefault() + result, err = view.ParseDefault(context.TODO()) t.Assert(err, nil) t.Assert(result, "test2:test2") }) @@ -108,11 +109,11 @@ func Test_View_Config(t *testing.T) { str := `hello {.name},version:{.version}` view.Assigns(map[string]interface{}{"version": "1.9.0"}) - result, err := view.ParseContent(str, nil) + result, err := view.ParseContent(context.TODO(), str, nil) t.Assert(err, nil) t.Assert(result, "hello test,version:1.9.0") - result, err = view.ParseDefault() + result, err = view.ParseDefault(context.TODO()) t.Assert(err, nil) t.Assert(result, "test:test") }) @@ -130,11 +131,11 @@ func Test_View_Config(t *testing.T) { str := `hello {.name},version:{.version}` view.Assigns(map[string]interface{}{"version": "1.9.0"}) - result, err := view.ParseContent(str, nil) + result, err := view.ParseContent(context.TODO(), str, nil) t.Assert(err, nil) t.Assert(result, "hello test,version:1.9.0") - result, err = view.ParseDefault() + result, err = view.ParseDefault(context.TODO()) t.Assert(err, nil) t.Assert(result, "test:test") }) From 09de115670d17e4007a91d236aa58651ac9b1245 Mon Sep 17 00:00:00 2001 From: tom <1326675091@qq.com> Date: Thu, 13 May 2021 17:51:06 +0800 Subject: [PATCH 253/492] add websocket client --- net/ghttp/ghttp_client_websocket.go | 29 +++++++++ net/ghttp/ghttp_unit_websocket_client_test.go | 64 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 net/ghttp/ghttp_client_websocket.go create mode 100644 net/ghttp/ghttp_unit_websocket_client_test.go diff --git a/net/ghttp/ghttp_client_websocket.go b/net/ghttp/ghttp_client_websocket.go new file mode 100644 index 000000000..1f1f228f7 --- /dev/null +++ b/net/ghttp/ghttp_client_websocket.go @@ -0,0 +1,29 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp + +import ( + "github.com/gorilla/websocket" + "net/http" + "time" +) + +// WebSocketClient wraps the underlying websocket client connection +// and provides convenient functions. +type WebSocketClient struct { + *websocket.Dialer +} + +// NewWebSocketClient New creates and returns a new WebSocketClient object. +func NewWebSocketClient() *WebSocketClient { + return &WebSocketClient{ + &websocket.Dialer{ + Proxy: http.ProxyFromEnvironment, + HandshakeTimeout: 45 * time.Second, + }, + } +} diff --git a/net/ghttp/ghttp_unit_websocket_client_test.go b/net/ghttp/ghttp_unit_websocket_client_test.go new file mode 100644 index 000000000..823130ee1 --- /dev/null +++ b/net/ghttp/ghttp_unit_websocket_client_test.go @@ -0,0 +1,64 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp_test + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gorilla/websocket" + + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/test/gtest" +) + +func Test_WebSocketClient(t *testing.T) { + p, _ := ports.PopRand() + s := g.Server(p) + s.BindHandler("/ws", func(r *ghttp.Request) { + ws, err := r.WebSocket() + if err != nil { + r.Exit() + } + for { + msgType, msg, err := ws.ReadMessage() + if err != nil { + return + } + if err = ws.WriteMessage(msgType, msg); err != nil { + return + } + } + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + client := ghttp.NewWebSocketClient() + client.Proxy = http.ProxyFromEnvironment + client.HandshakeTimeout = time.Minute + + conn, _, err := client.Dial(fmt.Sprintf("ws://127.0.0.1:%d/ws", p), nil) + t.Assert(err, nil) + defer conn.Close() + + msg := []byte("hello") + err = conn.WriteMessage(websocket.TextMessage, msg) + t.Assert(err, nil) + + mt, data, err := conn.ReadMessage() + t.Assert(err, nil) + t.Assert(mt, websocket.TextMessage) + t.Assert(data, msg) + }) +} From cc1224e0329ac512ccdf23aa0225496575e53de0 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 13 May 2021 20:56:52 +0800 Subject: [PATCH 254/492] add context for package gvalid.Check* functions --- .../server/request/request_validation.go | 4 +- .example/util/gvalid/gvalid.go | 15 +- .example/util/gvalid/gvalid_custom_message.go | 3 +- .example/util/gvalid/gvalid_error.go | 3 +- .example/util/gvalid/gvalid_result.go | 3 +- .example/util/gvalid/gvalid_sequence.go | 3 +- .example/util/gvalid/gvalid_struct1.go | 5 +- .example/util/gvalid/gvalid_struct2.go | 3 +- .example/util/gvalid/gvalid_struct3.go | 3 +- i18n/gi18n/gi18n_ctx.go | 6 + net/ghttp/ghttp_request_param.go | 3 +- net/ghttp/ghttp_unit_request_struct_test.go | 2 +- util/gvalid/gvalid.go | 17 +- util/gvalid/gvalid_validator.go | 4 +- util/gvalid/gvalid_z_example_test.go | 25 +- util/gvalid/gvalid_z_unit_basic_all_test.go | 415 +++++++++--------- util/gvalid/gvalid_z_unit_checkmap_test.go | 21 +- util/gvalid/gvalid_z_unit_checkstruct_test.go | 47 +- util/gvalid/gvalid_z_unit_custom_rule_test.go | 27 +- util/gvalid/gvalid_z_unit_customerror_test.go | 9 +- 20 files changed, 320 insertions(+), 298 deletions(-) diff --git a/.example/net/ghttp/server/request/request_validation.go b/.example/net/ghttp/server/request/request_validation.go index fa23190b2..2e180ad77 100644 --- a/.example/net/ghttp/server/request/request_validation.go +++ b/.example/net/ghttp/server/request/request_validation.go @@ -18,13 +18,13 @@ func main() { s.Group("/", func(rgroup *ghttp.RouterGroup) { rgroup.ALL("/user", func(r *ghttp.Request) { user := new(User) - if err := r.GetToStruct(user); err != nil { + if err := r.GetStruct(user); err != nil { r.Response.WriteJsonExit(g.Map{ "message": err, "errcode": 1, }) } - if err := gvalid.CheckStruct(user, nil); err != nil { + if err := gvalid.CheckStruct(r.Context(), user, nil); err != nil { r.Response.WriteJsonExit(g.Map{ "message": err.Maps(), "errcode": 1, diff --git a/.example/util/gvalid/gvalid.go b/.example/util/gvalid/gvalid.go index 9975bd443..a21b63f77 100644 --- a/.example/util/gvalid/gvalid.go +++ b/.example/util/gvalid/gvalid.go @@ -1,31 +1,32 @@ package main import ( + "context" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gvalid" ) func main() { //rule := "length:6,16" - //if m := gvalid.Check("123456", rule, nil); m != nil { + //if m := gvalid.Check(context.TODO(), "123456", rule, nil); m != nil { // fmt.Println(m) //} - //if m := gvalid.Check("12345", rule, nil); m != nil { + //if m := gvalid.Check(context.TODO(), "12345", rule, nil); m != nil { // fmt.Println(m) // // map[length:字段长度为6到16个字符] //} //rule := "integer|between:6,16" //msgs := "请输入一个整数|参数大小不对啊老铁" - //fmt.Println(gvalid.Check("5.66", rule, msgs)) + //fmt.Println(gvalid.Check(context.TODO(), "5.66", rule, msgs)) //// map[integer:请输入一个整数 between:参数大小不对啊老铁] //// 参数长度至少为6个数字或者6个字母,但是总长度不能超过16个字符 //rule := `regex:\d{6,}|\D{6,}|max-length:16` - //if m := gvalid.Check("123456", rule, nil); m != nil { + //if m := gvalid.Check(context.TODO(), "123456", rule, nil); m != nil { // fmt.Println(m) //} - //if m := gvalid.Check("abcde6", rule, nil); m != nil { + //if m := gvalid.Check(context.TODO(), "abcde6", rule, nil); m != nil { // fmt.Println(m) // // map[regex:字段值不合法] //} @@ -40,7 +41,7 @@ func main() { // "password" : "required|length:6,16|same:password2", // "password2" : "required|length:6,16", //} - //fmt.Println(gvalid.CheckMap(params, rules)) + //fmt.Println(gvalid.CheckMap(context.TODO(), params, rules)) //// map[passport:map[length:字段长度为6到16个字符] password:map[same:字段值不合法]] params := map[string]interface{}{ @@ -60,7 +61,7 @@ func main() { "same": "两次密码输入不相等", }, } - if e := gvalid.CheckMap(params, rules, msgs); e != nil { + if e := gvalid.CheckMap(context.TODO(), params, rules, msgs); e != nil { g.Dump(e.Maps()) } // map[passport:map[length:账号长度应当在6到16之间] password:map[same:两次密码输入不相等]] diff --git a/.example/util/gvalid/gvalid_custom_message.go b/.example/util/gvalid/gvalid_custom_message.go index 816749f10..3c8798693 100644 --- a/.example/util/gvalid/gvalid_custom_message.go +++ b/.example/util/gvalid/gvalid_custom_message.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gvalid" @@ -8,6 +9,6 @@ import ( func main() { g.I18n().SetLanguage("cn") - err := gvalid.Check("", "required", nil) + err := gvalid.Check(context.TODO(), "", "required", nil) fmt.Println(err.String()) } diff --git a/.example/util/gvalid/gvalid_error.go b/.example/util/gvalid/gvalid_error.go index fe5c0a2f2..1f5d350d4 100644 --- a/.example/util/gvalid/gvalid_error.go +++ b/.example/util/gvalid/gvalid_error.go @@ -1,6 +1,7 @@ package main import ( + "context" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gvalid" ) @@ -17,7 +18,7 @@ func main() { ConfiemPassword: "", } - e := gvalid.CheckStruct(user, nil) + e := gvalid.CheckStruct(context.TODO(), user, nil) g.Dump(e.Map()) g.Dump(e.Maps()) g.Dump(e.String()) diff --git a/.example/util/gvalid/gvalid_result.go b/.example/util/gvalid/gvalid_result.go index 0190f0df4..ff9e01e6d 100644 --- a/.example/util/gvalid/gvalid_result.go +++ b/.example/util/gvalid/gvalid_result.go @@ -1,6 +1,7 @@ package main import ( + "context" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gvalid" ) @@ -18,7 +19,7 @@ func main() { Pass2: "123", } - e := gvalid.CheckStruct(user, nil) + e := gvalid.CheckStruct(context.TODO(), user, nil) g.Dump(e.String()) g.Dump(e.FirstString()) } diff --git a/.example/util/gvalid/gvalid_sequence.go b/.example/util/gvalid/gvalid_sequence.go index 8274cd1c9..0296a4ab5 100644 --- a/.example/util/gvalid/gvalid_sequence.go +++ b/.example/util/gvalid/gvalid_sequence.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "github.com/gogf/gf/util/gvalid" @@ -17,7 +18,7 @@ func main() { "password@required|length:6,16|same:password2#密码不能为空}|两次密码输入不相等", "password2@required|length:6,16#", } - if e := gvalid.CheckMap(params, rules); e != nil { + if e := gvalid.CheckMap(context.TODO(), params, rules); e != nil { fmt.Println(e.Map()) fmt.Println(e.FirstItem()) fmt.Println(e.FirstString()) diff --git a/.example/util/gvalid/gvalid_struct1.go b/.example/util/gvalid/gvalid_struct1.go index d992860ee..6995ec2be 100644 --- a/.example/util/gvalid/gvalid_struct1.go +++ b/.example/util/gvalid/gvalid_struct1.go @@ -1,6 +1,7 @@ package main import ( + "context" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gvalid" ) @@ -20,7 +21,7 @@ func main() { } // 使用结构体定义的校验规则和错误提示进行校验 - g.Dump(gvalid.CheckStruct(user, nil).Map()) + g.Dump(gvalid.CheckStruct(context.TODO(), user, nil).Map()) // 自定义校验规则和错误提示,对定义的特定校验规则和错误提示进行覆盖 rules := map[string]string{ @@ -31,5 +32,5 @@ func main() { "password3": "名称不能为空", }, } - g.Dump(gvalid.CheckStruct(user, rules, msgs).Map()) + g.Dump(gvalid.CheckStruct(context.TODO(), user, rules, msgs).Map()) } diff --git a/.example/util/gvalid/gvalid_struct2.go b/.example/util/gvalid/gvalid_struct2.go index a4c9442fb..8e6c11601 100644 --- a/.example/util/gvalid/gvalid_struct2.go +++ b/.example/util/gvalid/gvalid_struct2.go @@ -1,6 +1,7 @@ package main import ( + "context" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gvalid" ) @@ -13,5 +14,5 @@ func main() { user := &User{} - g.Dump(gvalid.CheckStruct(user, nil)) + g.Dump(gvalid.CheckStruct(context.TODO(), user, nil)) } diff --git a/.example/util/gvalid/gvalid_struct3.go b/.example/util/gvalid/gvalid_struct3.go index de9980ca3..fdd0ea965 100644 --- a/.example/util/gvalid/gvalid_struct3.go +++ b/.example/util/gvalid/gvalid_struct3.go @@ -1,6 +1,7 @@ package main import ( + "context" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gvalid" ) @@ -15,5 +16,5 @@ func main() { Pass: "1", } - g.Dump(gvalid.CheckStruct(user, nil).Maps()) + g.Dump(gvalid.CheckStruct(context.TODO(), user, nil).Maps()) } diff --git a/i18n/gi18n/gi18n_ctx.go b/i18n/gi18n/gi18n_ctx.go index 4a9bc06ce..a2c7293a7 100644 --- a/i18n/gi18n/gi18n_ctx.go +++ b/i18n/gi18n/gi18n_ctx.go @@ -15,12 +15,18 @@ const ( // WithLanguage append language setting to the context and returns a new context. func WithLanguage(ctx context.Context, language string) context.Context { + if ctx == nil { + ctx = context.TODO() + } return context.WithValue(ctx, ctxLanguage, language) } // LanguageFromCtx retrieves and returns language name from context. // It returns an empty string if it is not set previously. func LanguageFromCtx(ctx context.Context) string { + if ctx == nil { + return "" + } v := ctx.Value(ctxLanguage) if v != nil { return v.(string) diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index 991967df1..c9bfdcbec 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -102,7 +102,7 @@ func (r *Request) doParse(pointer interface{}, requestType int) error { } } // Validation. - if err := gvalid.CheckStructWithParamMap(pointer, data, nil); err != nil { + if err := gvalid.CheckStructWithParamMap(r.Context(), pointer, data, nil); err != nil { return err } @@ -120,6 +120,7 @@ func (r *Request) doParse(pointer interface{}, requestType int) error { } for i := 0; i < reflectVal2.Len(); i++ { if err := gvalid.CheckStructWithParamMap( + r.Context(), reflectVal2.Index(i), j.GetMap(gconv.String(i)), nil, diff --git a/net/ghttp/ghttp_unit_request_struct_test.go b/net/ghttp/ghttp_unit_request_struct_test.go index 687c936c4..5f4601744 100644 --- a/net/ghttp/ghttp_unit_request_struct_test.go +++ b/net/ghttp/ghttp_unit_request_struct_test.go @@ -425,7 +425,7 @@ func Test_Params_Struct(t *testing.T) { if err := r.GetStruct(user); err != nil { r.Response.WriteExit(err) } - if err := gvalid.CheckStruct(user, nil); err != nil { + if err := gvalid.CheckStruct(r.Context(), user, nil); err != nil { r.Response.WriteExit(err) } } diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 0b9e18d7e..4a660a954 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -8,6 +8,7 @@ package gvalid import ( + "context" "regexp" "strings" @@ -170,8 +171,8 @@ var ( // string/map/struct/*struct. // The optional parameter `params` specifies the extra validation parameters for some rules // like: required-*、same、different, etc. -func Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error { - return defaultValidator.Check(value, rules, messages, params...) +func Check(ctx context.Context, value interface{}, rules string, messages interface{}, params ...interface{}) *Error { + return defaultValidator.Ctx(ctx).Check(value, rules, messages, params...) } // CheckMap validates map and returns the error result. It returns nil if with successful validation. @@ -179,8 +180,8 @@ func Check(value interface{}, rules string, messages interface{}, params ...inte // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error { - return defaultValidator.CheckMap(params, rules, messages...) +func CheckMap(ctx context.Context, params interface{}, rules interface{}, messages ...CustomMsg) *Error { + return defaultValidator.Ctx(ctx).CheckMap(params, rules, messages...) } // CheckStruct validates strcut and returns the error result. @@ -189,8 +190,8 @@ func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Err // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error { - return defaultValidator.CheckStruct(object, rules, messages...) +func CheckStruct(ctx context.Context, object interface{}, rules interface{}, messages ...CustomMsg) *Error { + return defaultValidator.Ctx(ctx).CheckStruct(object, rules, messages...) } // CheckStructWithParamMap validates struct with given parameter map and returns the error result. @@ -199,8 +200,8 @@ func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) * // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func CheckStructWithParamMap(object interface{}, paramMap interface{}, rules interface{}, messages ...CustomMsg) *Error { - return defaultValidator.CheckStructWithParamMap(object, paramMap, rules, messages...) +func CheckStructWithParamMap(ctx context.Context, object interface{}, paramMap interface{}, rules interface{}, messages ...CustomMsg) *Error { + return defaultValidator.Ctx(ctx).CheckStructWithParamMap(object, paramMap, rules, messages...) } // parseSequenceTag parses one sequence tag to field, rule and error message. diff --git a/util/gvalid/gvalid_validator.go b/util/gvalid/gvalid_validator.go index 5f516a2c6..de6ace5f1 100644 --- a/util/gvalid/gvalid_validator.go +++ b/util/gvalid/gvalid_validator.go @@ -21,8 +21,8 @@ type Validator struct { // New creates and returns a new Validator. func New() *Validator { return &Validator{ - ctx: context.TODO(), - i18nManager: gi18n.Instance(), + ctx: context.TODO(), // Initialize an empty context. + i18nManager: gi18n.Instance(), // Use default i18n manager. } } diff --git a/util/gvalid/gvalid_z_example_test.go b/util/gvalid/gvalid_z_example_test.go index 2fa6cedfa..fac360194 100644 --- a/util/gvalid/gvalid_z_example_test.go +++ b/util/gvalid/gvalid_z_example_test.go @@ -7,6 +7,7 @@ package gvalid_test import ( + "context" "errors" "fmt" "github.com/gogf/gf/container/gvar" @@ -28,7 +29,7 @@ func ExampleCheckMap() { "password@required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等", "password2@required|length:6,16#", } - if e := gvalid.CheckMap(params, rules); e != nil { + if e := gvalid.CheckMap(context.TODO(), params, rules); e != nil { fmt.Println(e.Map()) fmt.Println(e.FirstItem()) fmt.Println(e.FirstString()) @@ -50,7 +51,7 @@ func ExampleCheckMap2() { "password@required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等", "password2@required|length:6,16#", } - if e := gvalid.CheckMap(params, rules); e != nil { + if e := gvalid.CheckMap(context.TODO(), params, rules); e != nil { fmt.Println(e.Map()) fmt.Println(e.FirstItem()) fmt.Println(e.FirstString()) @@ -72,7 +73,7 @@ func ExampleCheckStruct() { Page: 1, Size: 10, } - err := gvalid.CheckStruct(obj, nil) + err := gvalid.CheckStruct(context.TODO(), obj, nil) fmt.Println(err == nil) // Output: // true @@ -89,7 +90,7 @@ func ExampleCheckStruct2() { Page: 1, Size: 10, } - err := gvalid.CheckStruct(obj, nil) + err := gvalid.CheckStruct(context.TODO(), obj, nil) fmt.Println(err == nil) // Output: // true @@ -106,7 +107,7 @@ func ExampleCheckStruct3() { Page: 1, Size: 10, } - err := gvalid.CheckStruct(obj, nil) + err := gvalid.CheckStruct(context.TODO(), obj, nil) fmt.Println(err) // Output: // project id must between 1, 10000 @@ -138,7 +139,7 @@ func ExampleRegisterRule() { Name: "john", Pass: "123456", } - err := gvalid.CheckStruct(user, nil) + err := gvalid.CheckStruct(context.TODO(), user, nil) fmt.Println(err.Error()) // May Output: // 用户名称已被占用 @@ -172,14 +173,14 @@ func ExampleRegisterRule_OverwriteRequired() { } return nil }) - fmt.Println(gvalid.Check("", "required", "It's required")) - fmt.Println(gvalid.Check(0, "required", "It's required")) - fmt.Println(gvalid.Check(false, "required", "It's required")) + fmt.Println(gvalid.Check(context.TODO(), "", "required", "It's required")) + fmt.Println(gvalid.Check(context.TODO(), 0, "required", "It's required")) + fmt.Println(gvalid.Check(context.TODO(), false, "required", "It's required")) gvalid.DeleteRule(rule) fmt.Println("rule deleted") - fmt.Println(gvalid.Check("", "required", "It's required")) - fmt.Println(gvalid.Check(0, "required", "It's required")) - fmt.Println(gvalid.Check(false, "required", "It's required")) + fmt.Println(gvalid.Check(context.TODO(), "", "required", "It's required")) + fmt.Println(gvalid.Check(context.TODO(), 0, "required", "It's required")) + fmt.Println(gvalid.Check(context.TODO(), false, "required", "It's required")) // Output: // It's required // It's required diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index bfde55995..55e7687ab 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -7,6 +7,7 @@ package gvalid_test import ( + "context" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" "testing" @@ -23,9 +24,9 @@ func Test_Check(t *testing.T) { val1 := 0 val2 := 7 val3 := 20 - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) t.Assert(err1, "invalid_rules: abc:6,16") t.Assert(err2, "invalid_rules: abc:6,16") t.Assert(err3, "invalid_rules: abc:6,16") @@ -33,16 +34,16 @@ func Test_Check(t *testing.T) { } func Test_Required(t *testing.T) { - if m := gvalid.Check("1", "required", nil); m != nil { + if m := gvalid.Check(context.TODO(), "1", "required", nil); m != nil { t.Error(m) } - if m := gvalid.Check("", "required", nil); m == nil { + if m := gvalid.Check(context.TODO(), "", "required", nil); m == nil { t.Error(m) } - if m := gvalid.Check("", "required-if: id,1,age,18", nil, map[string]interface{}{"id": 1, "age": 19}); m == nil { + if m := gvalid.Check(context.TODO(), "", "required-if: id,1,age,18", nil, map[string]interface{}{"id": 1, "age": 19}); m == nil { t.Error("Required校验失败") } - if m := gvalid.Check("", "required-if: id,1,age,18", nil, map[string]interface{}{"id": 2, "age": 19}); m != nil { + if m := gvalid.Check(context.TODO(), "", "required-if: id,1,age,18", nil, map[string]interface{}{"id": 2, "age": 19}); m != nil { t.Error("Required校验失败") } } @@ -50,20 +51,20 @@ func Test_Required(t *testing.T) { func Test_RequiredIf(t *testing.T) { gtest.C(t, func(t *gtest.T) { rule := "required-if:id,1,age,18" - t.AssertNE(gvalid.Check("", rule, nil, g.Map{"id": 1}), nil) - t.Assert(gvalid.Check("", rule, nil, g.Map{"id": 0}), nil) - t.AssertNE(gvalid.Check("", rule, nil, g.Map{"age": 18}), nil) - t.Assert(gvalid.Check("", rule, nil, g.Map{"age": 20}), nil) + t.AssertNE(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"id": 1}), nil) + t.Assert(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"id": 0}), nil) + t.AssertNE(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"age": 18}), nil) + t.Assert(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"age": 20}), nil) }) } func Test_RequiredUnless(t *testing.T) { gtest.C(t, func(t *gtest.T) { rule := "required-unless:id,1,age,18" - t.Assert(gvalid.Check("", rule, nil, g.Map{"id": 1}), nil) - t.AssertNE(gvalid.Check("", rule, nil, g.Map{"id": 0}), nil) - t.Assert(gvalid.Check("", rule, nil, g.Map{"age": 18}), nil) - t.AssertNE(gvalid.Check("", rule, nil, g.Map{"age": 20}), nil) + t.Assert(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"id": 1}), nil) + t.AssertNE(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"id": 0}), nil) + t.Assert(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"age": 18}), nil) + t.AssertNE(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"age": 20}), nil) }) } @@ -81,9 +82,9 @@ func Test_RequiredWith(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(val1, rule, nil, params1) - err2 := gvalid.Check(val1, rule, nil, params2) - err3 := gvalid.Check(val1, rule, nil, params3) + err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) t.Assert(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -101,9 +102,9 @@ func Test_RequiredWith(t *testing.T) { params3 := g.Map{ "time": time.Time{}, } - err1 := gvalid.Check(val1, rule, nil, params1) - err2 := gvalid.Check(val1, rule, nil, params2) - err3 := gvalid.Check(val1, rule, nil, params3) + err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) t.Assert(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -120,9 +121,9 @@ func Test_RequiredWith(t *testing.T) { params3 := g.Map{ "time": time.Now(), } - err1 := gvalid.Check(val1, rule, nil, params1) - err2 := gvalid.Check(val1, rule, nil, params2) - err3 := gvalid.Check(val1, rule, nil, params3) + err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) t.Assert(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -139,7 +140,7 @@ func Test_RequiredWith(t *testing.T) { StartTime: nil, EndTime: nil, } - t.Assert(gvalid.CheckStruct(data, nil), nil) + t.Assert(gvalid.CheckStruct(context.TODO(), data, nil), nil) }) gtest.C(t, func(t *gtest.T) { type UserApiSearch struct { @@ -152,7 +153,7 @@ func Test_RequiredWith(t *testing.T) { StartTime: nil, EndTime: gtime.Now(), } - t.AssertNE(gvalid.CheckStruct(data, nil), nil) + t.AssertNE(gvalid.CheckStruct(context.TODO(), data, nil), nil) }) } @@ -170,9 +171,9 @@ func Test_RequiredWithAll(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(val1, rule, nil, params1) - err2 := gvalid.Check(val1, rule, nil, params2) - err3 := gvalid.Check(val1, rule, nil, params3) + err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) t.Assert(err1, nil) t.Assert(err2, nil) t.AssertNE(err3, nil) @@ -193,9 +194,9 @@ func Test_RequiredWithOut(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(val1, rule, nil, params1) - err2 := gvalid.Check(val1, rule, nil, params2) - err3 := gvalid.Check(val1, rule, nil, params3) + err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -216,9 +217,9 @@ func Test_RequiredWithOutAll(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(val1, rule, nil, params1) - err2 := gvalid.Check(val1, rule, nil, params2) - err3 := gvalid.Check(val1, rule, nil, params3) + err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -235,13 +236,13 @@ func Test_Date(t *testing.T) { val5 := "2010.11.01" val6 := "2010/11/01" val7 := "2010=11=01" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) - err6 := gvalid.Check(val6, rule, nil) - err7 := gvalid.Check(val7, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err6 := gvalid.Check(context.TODO(), val6, rule, nil) + err7 := gvalid.Check(context.TODO(), val7, rule, nil) t.Assert(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -260,12 +261,12 @@ func Test_DateFormat(t *testing.T) { val4 := "201011-01" val5 := "2010~11~01" val6 := "2010-11~01" - err1 := gvalid.Check(val1, "date-format:Y", nil) - err2 := gvalid.Check(val2, "date-format:Ym", nil) - err3 := gvalid.Check(val3, "date-format:Y.m", nil) - err4 := gvalid.Check(val4, "date-format:Ym-d", nil) - err5 := gvalid.Check(val5, "date-format:Y~m~d", nil) - err6 := gvalid.Check(val6, "date-format:Y~m~d", nil) + err1 := gvalid.Check(context.TODO(), val1, "date-format:Y", nil) + err2 := gvalid.Check(context.TODO(), val2, "date-format:Ym", nil) + err3 := gvalid.Check(context.TODO(), val3, "date-format:Y.m", nil) + err4 := gvalid.Check(context.TODO(), val4, "date-format:Ym-d", nil) + err5 := gvalid.Check(context.TODO(), val5, "date-format:Y~m~d", nil) + err6 := gvalid.Check(context.TODO(), val6, "date-format:Y~m~d", nil) t.Assert(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -276,8 +277,8 @@ func Test_DateFormat(t *testing.T) { gtest.C(t, func(t *gtest.T) { t1 := gtime.Now() t2 := time.Time{} - err1 := gvalid.Check(t1, "date-format:Y", nil) - err2 := gvalid.Check(t2, "date-format:Y", nil) + err1 := gvalid.Check(context.TODO(), t1, "date-format:Y", nil) + err2 := gvalid.Check(context.TODO(), t2, "date-format:Y", nil) t.Assert(err1, nil) t.AssertNE(err2, nil) }) @@ -290,10 +291,10 @@ func Test_Email(t *testing.T) { value2 := "m@www@johngcn" value3 := "m-m_m@mail.johng.cn" value4 := "m.m-m@johng.cn" - err1 := gvalid.Check(value1, rule, nil) - err2 := gvalid.Check(value2, rule, nil) - err3 := gvalid.Check(value3, rule, nil) - err4 := gvalid.Check(value4, rule, nil) + err1 := gvalid.Check(context.TODO(), value1, rule, nil) + err2 := gvalid.Check(context.TODO(), value2, rule, nil) + err3 := gvalid.Check(context.TODO(), value3, rule, nil) + err4 := gvalid.Check(context.TODO(), value4, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -303,10 +304,10 @@ func Test_Email(t *testing.T) { func Test_Phone(t *testing.T) { gtest.C(t, func(t *gtest.T) { - err1 := gvalid.Check("1361990897", "phone", nil) - err2 := gvalid.Check("13619908979", "phone", nil) - err3 := gvalid.Check("16719908979", "phone", nil) - err4 := gvalid.Check("19719908989", "phone", nil) + err1 := gvalid.Check(context.TODO(), "1361990897", "phone", nil) + err2 := gvalid.Check(context.TODO(), "13619908979", "phone", nil) + err3 := gvalid.Check(context.TODO(), "16719908979", "phone", nil) + err4 := gvalid.Check(context.TODO(), "19719908989", "phone", nil) t.AssertNE(err1.String(), nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -316,12 +317,12 @@ func Test_Phone(t *testing.T) { func Test_PhoneLoose(t *testing.T) { gtest.C(t, func(t *gtest.T) { - err1 := gvalid.Check("13333333333", "phone-loose", nil) - err2 := gvalid.Check("15555555555", "phone-loose", nil) - err3 := gvalid.Check("16666666666", "phone-loose", nil) - err4 := gvalid.Check("23333333333", "phone-loose", nil) - err5 := gvalid.Check("1333333333", "phone-loose", nil) - err6 := gvalid.Check("10333333333", "phone-loose", nil) + err1 := gvalid.Check(context.TODO(), "13333333333", "phone-loose", nil) + err2 := gvalid.Check(context.TODO(), "15555555555", "phone-loose", nil) + err3 := gvalid.Check(context.TODO(), "16666666666", "phone-loose", nil) + err4 := gvalid.Check(context.TODO(), "23333333333", "phone-loose", nil) + err5 := gvalid.Check(context.TODO(), "1333333333", "phone-loose", nil) + err6 := gvalid.Check(context.TODO(), "10333333333", "phone-loose", nil) t.Assert(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -338,11 +339,11 @@ func Test_Telephone(t *testing.T) { val3 := "86292651" val4 := "028-8692651" val5 := "0830-8692651" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -359,11 +360,11 @@ func Test_Passport(t *testing.T) { val3 := "aaaaa" val4 := "aaaaaa" val5 := "a123_456" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -380,11 +381,11 @@ func Test_Password(t *testing.T) { val3 := "a12345-6" val4 := ">,/;'[09-" val5 := "a123_456" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -403,13 +404,13 @@ func Test_Password2(t *testing.T) { val5 := "a123_456" val6 := "Nant1986" val7 := "Nant1986!" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) - err6 := gvalid.Check(val6, rule, nil) - err7 := gvalid.Check(val7, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err6 := gvalid.Check(context.TODO(), val6, rule, nil) + err7 := gvalid.Check(context.TODO(), val7, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -430,13 +431,13 @@ func Test_Password3(t *testing.T) { val5 := "a123_456" val6 := "Nant1986" val7 := "Nant1986!" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) - err6 := gvalid.Check(val6, rule, nil) - err7 := gvalid.Check(val7, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err6 := gvalid.Check(context.TODO(), val6, rule, nil) + err7 := gvalid.Check(context.TODO(), val7, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -452,8 +453,8 @@ func Test_Postcode(t *testing.T) { rule := "postcode" val1 := "12345" val2 := "610036" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) }) @@ -467,11 +468,11 @@ func Test_ResidentId(t *testing.T) { val3 := "311128500121201" val4 := "510521198607185367" val5 := "51052119860718536x" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -485,8 +486,8 @@ func Test_BankCard(t *testing.T) { rule := "bank-card" val1 := "6230514630000424470" val2 := "6230514630000424473" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) }) @@ -500,11 +501,11 @@ func Test_QQ(t *testing.T) { val3 := "10000" val4 := "38996181" val5 := "389961817" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -514,31 +515,31 @@ func Test_QQ(t *testing.T) { } func Test_Ip(t *testing.T) { - if m := gvalid.Check("10.0.0.1", "ip", nil); m != nil { + if m := gvalid.Check(context.TODO(), "10.0.0.1", "ip", nil); m != nil { t.Error(m) } - if m := gvalid.Check("10.0.0.1", "ipv4", nil); m != nil { + if m := gvalid.Check(context.TODO(), "10.0.0.1", "ipv4", nil); m != nil { t.Error(m) } - if m := gvalid.Check("0.0.0.0", "ipv4", nil); m != nil { + if m := gvalid.Check(context.TODO(), "0.0.0.0", "ipv4", nil); m != nil { t.Error(m) } - if m := gvalid.Check("1920.0.0.0", "ipv4", nil); m == nil { + if m := gvalid.Check(context.TODO(), "1920.0.0.0", "ipv4", nil); m == nil { t.Error("ipv4校验失败") } - if m := gvalid.Check("1920.0.0.0", "ip", nil); m == nil { + if m := gvalid.Check(context.TODO(), "1920.0.0.0", "ip", nil); m == nil { t.Error("ipv4校验失败") } - if m := gvalid.Check("fe80::5484:7aff:fefe:9799", "ipv6", nil); m != nil { + if m := gvalid.Check(context.TODO(), "fe80::5484:7aff:fefe:9799", "ipv6", nil); m != nil { t.Error(m) } - if m := gvalid.Check("fe80::5484:7aff:fefe:9799123", "ipv6", nil); m == nil { + if m := gvalid.Check(context.TODO(), "fe80::5484:7aff:fefe:9799123", "ipv6", nil); m == nil { t.Error(m) } - if m := gvalid.Check("fe80::5484:7aff:fefe:9799", "ip", nil); m != nil { + if m := gvalid.Check(context.TODO(), "fe80::5484:7aff:fefe:9799", "ip", nil); m != nil { t.Error(m) } - if m := gvalid.Check("fe80::5484:7aff:fefe:9799123", "ip", nil); m == nil { + if m := gvalid.Check(context.TODO(), "fe80::5484:7aff:fefe:9799123", "ip", nil); m == nil { t.Error(m) } } @@ -551,11 +552,11 @@ func Test_IPv4(t *testing.T) { val3 := "1.1.1.1" val4 := "255.255.255.0" val5 := "127.0.0.1" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -572,11 +573,11 @@ func Test_IPv6(t *testing.T) { val3 := "1030::C9B4:FF12:48AA:1A2B" val4 := "2000:0:0:0:0:0:0:1" val5 := "0000:0000:0000:0000:0000:ffff:c0a8:5909" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -591,9 +592,9 @@ func Test_MAC(t *testing.T) { val1 := "192.168.1.1" val2 := "44-45-53-54-00-00" val3 := "01:00:5e:00:00:00" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -607,10 +608,10 @@ func Test_URL(t *testing.T) { val2 := "https://www.baidu.com" val3 := "http://127.0.0.1" val4 := "file:///tmp/test.txt" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -637,7 +638,7 @@ func Test_Domain(t *testing.T) { } var err error for k, v := range m { - err = gvalid.Check(k, "domain", nil) + err = gvalid.Check(context.TODO(), k, "domain", nil) if v { //fmt.Println(k) t.Assert(err, nil) @@ -651,10 +652,10 @@ func Test_Domain(t *testing.T) { func Test_Length(t *testing.T) { rule := "length:6,16" - if m := gvalid.Check("123456", rule, nil); m != nil { + if m := gvalid.Check(context.TODO(), "123456", rule, nil); m != nil { t.Error(m) } - if m := gvalid.Check("12345", rule, nil); m == nil { + if m := gvalid.Check(context.TODO(), "12345", rule, nil); m == nil { t.Error("长度校验失败") } } @@ -664,18 +665,18 @@ func Test_MinLength(t *testing.T) { msgs := map[string]string{ "min-length": "地址长度至少为:min位", } - if m := gvalid.Check("123456", rule, nil); m != nil { + if m := gvalid.Check(context.TODO(), "123456", rule, nil); m != nil { t.Error(m) } - if m := gvalid.Check("12345", rule, nil); m == nil { + if m := gvalid.Check(context.TODO(), "12345", rule, nil); m == nil { t.Error("长度校验失败") } - if m := gvalid.Check("12345", rule, msgs); m == nil { + if m := gvalid.Check(context.TODO(), "12345", rule, msgs); m == nil { t.Error("长度校验失败") } rule2 := "min-length:abc" - if m := gvalid.Check("123456", rule2, nil); m == nil { + if m := gvalid.Check(context.TODO(), "123456", rule2, nil); m == nil { t.Error("长度校验失败") } } @@ -685,31 +686,31 @@ func Test_MaxLength(t *testing.T) { msgs := map[string]string{ "max-length": "地址长度至大为:max位", } - if m := gvalid.Check("12345", rule, nil); m != nil { + if m := gvalid.Check(context.TODO(), "12345", rule, nil); m != nil { t.Error(m) } - if m := gvalid.Check("1234567", rule, nil); m == nil { + if m := gvalid.Check(context.TODO(), "1234567", rule, nil); m == nil { t.Error("长度校验失败") } - if m := gvalid.Check("1234567", rule, msgs); m == nil { + if m := gvalid.Check(context.TODO(), "1234567", rule, msgs); m == nil { t.Error("长度校验失败") } rule2 := "max-length:abc" - if m := gvalid.Check("123456", rule2, nil); m == nil { + if m := gvalid.Check(context.TODO(), "123456", rule2, nil); m == nil { t.Error("长度校验失败") } } func Test_Between(t *testing.T) { rule := "between:6.01, 10.01" - if m := gvalid.Check(10, rule, nil); m != nil { + if m := gvalid.Check(context.TODO(), 10, rule, nil); m != nil { t.Error(m) } - if m := gvalid.Check(10.02, rule, nil); m == nil { + if m := gvalid.Check(context.TODO(), 10.02, rule, nil); m == nil { t.Error("大小范围校验失败") } - if m := gvalid.Check("a", rule, nil); m == nil { + if m := gvalid.Check(context.TODO(), "a", rule, nil); m == nil { t.Error("大小范围校验失败") } } @@ -722,11 +723,11 @@ func Test_Min(t *testing.T) { val3 := "100" val4 := "1000" val5 := "a" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -734,7 +735,7 @@ func Test_Min(t *testing.T) { t.AssertNE(err5, nil) rule2 := "min:a" - err6 := gvalid.Check(val1, rule2, nil) + err6 := gvalid.Check(context.TODO(), val1, rule2, nil) t.AssertNE(err6, nil) }) } @@ -747,11 +748,11 @@ func Test_Max(t *testing.T) { val3 := "100" val4 := "1000" val5 := "a" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) t.Assert(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -759,7 +760,7 @@ func Test_Max(t *testing.T) { t.AssertNE(err5, nil) rule2 := "max:a" - err6 := gvalid.Check(val1, rule2, nil) + err6 := gvalid.Check(context.TODO(), val1, rule2, nil) t.AssertNE(err6, nil) }) } @@ -773,12 +774,12 @@ func Test_Json(t *testing.T) { val4 := "[]" val5 := "[1,2,3,4]" val6 := `{"list":[1,2,3,4]}` - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) - err6 := gvalid.Check(val6, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err6 := gvalid.Check(context.TODO(), val6, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -797,12 +798,12 @@ func Test_Integer(t *testing.T) { val4 := "1" val5 := "100" val6 := `999999999` - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) - err6 := gvalid.Check(val6, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err6 := gvalid.Check(context.TODO(), val6, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -821,12 +822,12 @@ func Test_Float(t *testing.T) { val4 := "1.0" val5 := "1.1" val6 := `0.1` - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) - err6 := gvalid.Check(val6, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err6 := gvalid.Check(context.TODO(), val6, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -845,12 +846,12 @@ func Test_Boolean(t *testing.T) { val4 := "1" val5 := "true" val6 := `off` - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) - err5 := gvalid.Check(val5, rule, nil) - err6 := gvalid.Check(val6, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err6 := gvalid.Check(context.TODO(), val6, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -874,9 +875,9 @@ func Test_Same(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(val1, rule, nil, params1) - err2 := gvalid.Check(val1, rule, nil, params2) - err3 := gvalid.Check(val1, rule, nil, params3) + err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -897,9 +898,9 @@ func Test_Different(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(val1, rule, nil, params1) - err2 := gvalid.Check(val1, rule, nil, params2) - err3 := gvalid.Check(val1, rule, nil, params3) + err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) t.Assert(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -913,10 +914,10 @@ func Test_In(t *testing.T) { val2 := "1" val3 := "100" val4 := "200" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -931,10 +932,10 @@ func Test_NotIn(t *testing.T) { val2 := "1" val3 := "100" val4 := "200" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) t.Assert(err1, nil) t.Assert(err2, nil) t.AssertNE(err3, nil) @@ -946,10 +947,10 @@ func Test_NotIn(t *testing.T) { val2 := "1" val3 := "100" val4 := "200" - err1 := gvalid.Check(val1, rule, nil) - err2 := gvalid.Check(val2, rule, nil) - err3 := gvalid.Check(val3, rule, nil) - err4 := gvalid.Check(val4, rule, nil) + err1 := gvalid.Check(context.TODO(), val1, rule, nil) + err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err4 := gvalid.Check(context.TODO(), val4, rule, nil) t.Assert(err1, nil) t.Assert(err2, nil) t.AssertNE(err3, nil) @@ -959,10 +960,10 @@ func Test_NotIn(t *testing.T) { func Test_Regex1(t *testing.T) { rule := `regex:\d{6}|\D{6}|length:6,16` - if m := gvalid.Check("123456", rule, nil); m != nil { + if m := gvalid.Check(context.TODO(), "123456", rule, nil); m != nil { t.Error(m) } - if m := gvalid.Check("abcde6", rule, nil); m == nil { + if m := gvalid.Check(context.TODO(), "abcde6", rule, nil); m == nil { t.Error("校验失败") } } @@ -973,9 +974,9 @@ func Test_Regex2(t *testing.T) { str1 := "" str2 := "data" str3 := "data:image/jpeg;base64,/9jrbattq22r" - err1 := gvalid.Check(str1, rule, nil) - err2 := gvalid.Check(str2, rule, nil) - err3 := gvalid.Check(str3, rule, nil) + err1 := gvalid.Check(context.TODO(), str1, rule, nil) + err2 := gvalid.Check(context.TODO(), str2, rule, nil) + err3 := gvalid.Check(context.TODO(), str3, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -992,7 +993,7 @@ func Test_InternalError_String(t *testing.T) { Name string `v:"hh"` } aa := a{Name: "2"} - err := gvalid.CheckStruct(&aa, nil) + err := gvalid.CheckStruct(context.TODO(), &aa, nil) t.Assert(err.String(), "invalid_rules: hh") t.Assert(err.Strings(), g.Slice{"invalid_rules: hh"}) diff --git a/util/gvalid/gvalid_z_unit_checkmap_test.go b/util/gvalid/gvalid_z_unit_checkmap_test.go index 4660ebf21..c74ab2925 100755 --- a/util/gvalid/gvalid_z_unit_checkmap_test.go +++ b/util/gvalid/gvalid_z_unit_checkmap_test.go @@ -7,6 +7,7 @@ package gvalid_test import ( + "context" "github.com/gogf/gf/errors/gerror" "testing" @@ -24,7 +25,7 @@ func Test_CheckMap1(t *testing.T) { "id": "required|between:1,100", "name": "required|length:6,16", } - if m := gvalid.CheckMap(data, rules); m == nil { + if m := gvalid.CheckMap(context.TODO(), data, rules); m == nil { t.Error("CheckMap校验失败") } else { t.Assert(len(m.Maps()), 2) @@ -37,7 +38,7 @@ func Test_CheckMap1(t *testing.T) { func Test_CheckMap2(t *testing.T) { var params interface{} gtest.C(t, func(t *gtest.T) { - if err := gvalid.CheckMap(params, nil, nil); err == nil { + if err := gvalid.CheckMap(context.TODO(), params, nil, nil); err == nil { t.Assert(err, nil) } }) @@ -57,7 +58,7 @@ func Test_CheckMap2(t *testing.T) { "length": "名称长度为:min到:max个字符", }, } - if m := gvalid.CheckMap(kvmap, rules, msgs); m == nil { + if m := gvalid.CheckMap(context.TODO(), kvmap, rules, msgs); m == nil { t.Error("CheckMap校验失败") } @@ -76,7 +77,7 @@ func Test_CheckMap2(t *testing.T) { "length": "名称长度为:min到:max个字符", }, } - if m := gvalid.CheckMap(kvmap, rules, msgs); m != nil { + if m := gvalid.CheckMap(context.TODO(), kvmap, rules, msgs); m != nil { t.Error(m) } @@ -95,7 +96,7 @@ func Test_CheckMap2(t *testing.T) { "length": "名称长度为:min到:max个字符", }, } - if m := gvalid.CheckMap(kvmap, rules, msgs); m != nil { + if m := gvalid.CheckMap(context.TODO(), kvmap, rules, msgs); m != nil { t.Error(m) } @@ -114,7 +115,7 @@ func Test_CheckMap2(t *testing.T) { "length": "名称长度为:min到:max个字符", }, } - if m := gvalid.CheckMap(kvmap, rules2, msgs); m != nil { + if m := gvalid.CheckMap(context.TODO(), kvmap, rules2, msgs); m != nil { t.Error(m) } @@ -133,7 +134,7 @@ func Test_CheckMap2(t *testing.T) { "length": "名称长度为:min到:max个字符", }, } - if m := gvalid.CheckMap(kvmap, rules2, msgs); m != nil { + if m := gvalid.CheckMap(context.TODO(), kvmap, rules2, msgs); m != nil { t.Error(m) } @@ -152,7 +153,7 @@ func Test_CheckMap2(t *testing.T) { "length": "名称长度为:min到:max个字符", }, } - if m := gvalid.CheckMap(kvmap, rules2, msgs); m != nil { + if m := gvalid.CheckMap(context.TODO(), kvmap, rules2, msgs); m != nil { t.Error(m) } } @@ -166,7 +167,7 @@ func Test_CheckMapWithNilAndNotRequiredField(t *testing.T) { "id": "required", "name": "length:4,16", } - if m := gvalid.CheckMap(data, rules); m != nil { + if m := gvalid.CheckMap(context.TODO(), data, rules); m != nil { t.Error(m) } } @@ -183,7 +184,7 @@ func Test_Sequence(t *testing.T) { "password@required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等", "password2@required|length:6,16#", } - err := gvalid.CheckMap(params, rules) + err := gvalid.CheckMap(context.TODO(), params, rules) t.AssertNE(err, nil) t.Assert(len(err.Map()), 2) t.Assert(err.Map()["required"], "账号不能为空") diff --git a/util/gvalid/gvalid_z_unit_checkstruct_test.go b/util/gvalid/gvalid_z_unit_checkstruct_test.go index 786145ffb..da76790fb 100755 --- a/util/gvalid/gvalid_z_unit_checkstruct_test.go +++ b/util/gvalid/gvalid_z_unit_checkstruct_test.go @@ -7,6 +7,7 @@ package gvalid_test import ( + "context" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/os/gtime" "testing" @@ -35,7 +36,7 @@ func Test_CheckStruct(t *testing.T) { "Age": "年龄为18到30周岁", } obj := &Object{"john", 16} - err := gvalid.CheckStruct(obj, rules, msgs) + err := gvalid.CheckStruct(context.TODO(), obj, rules, msgs) t.Assert(err, nil) }) @@ -56,7 +57,7 @@ func Test_CheckStruct(t *testing.T) { "Age": "年龄为18到30周岁", } obj := &Object{"john", 16} - err := gvalid.CheckStruct(obj, rules, msgs) + err := gvalid.CheckStruct(context.TODO(), obj, rules, msgs) t.AssertNE(err, nil) t.Assert(len(err.Maps()), 2) t.Assert(err.Maps()["Name"]["required"], "") @@ -81,7 +82,7 @@ func Test_CheckStruct(t *testing.T) { "Age": "年龄为18到30周岁", } obj := &Object{"john", 16} - err := gvalid.CheckStruct(obj, rules, msgs) + err := gvalid.CheckStruct(context.TODO(), obj, rules, msgs) t.AssertNE(err, nil) t.Assert(len(err.Maps()), 2) t.Assert(err.Maps()["Name"]["required"], "") @@ -106,7 +107,7 @@ func Test_CheckStruct(t *testing.T) { "Age": "年龄为18到30周岁", } obj := &Object{"john", 16} - err := gvalid.CheckStruct(obj, rules, msgs) + err := gvalid.CheckStruct(context.TODO(), obj, rules, msgs) t.AssertNE(err, nil) t.Assert(len(err.Maps()), 2) t.Assert(err.Maps()["Name"]["required"], "") @@ -120,7 +121,7 @@ func Test_CheckStruct(t *testing.T) { Password string `json:"password" gvalid:"password@required#登录密码不能为空"` } var login LoginRequest - err := gvalid.CheckStruct(login, nil) + err := gvalid.CheckStruct(context.TODO(), login, nil) t.AssertNE(err, nil) t.Assert(len(err.Maps()), 2) t.Assert(err.Maps()["username"]["required"], "用户名不能为空") @@ -133,7 +134,7 @@ func Test_CheckStruct(t *testing.T) { Password string `json:"password" gvalid:"@required#登录密码不能为空"` } var login LoginRequest - err := gvalid.CheckStruct(login, nil) + err := gvalid.CheckStruct(context.TODO(), login, nil) t.Assert(err, nil) }) @@ -143,7 +144,7 @@ func Test_CheckStruct(t *testing.T) { Password string `json:"password" gvalid:"password@required#登录密码不能为空"` } var login LoginRequest - err := gvalid.CheckStruct(login, nil) + err := gvalid.CheckStruct(context.TODO(), login, nil) t.AssertNE(err, nil) t.Assert(err.Maps()["password"]["required"], "登录密码不能为空") }) @@ -161,7 +162,7 @@ func Test_CheckStruct(t *testing.T) { Username: "john", Password: "123456", } - err := gvalid.CheckStruct(user, nil) + err := gvalid.CheckStruct(context.TODO(), user, nil) t.AssertNE(err, nil) t.Assert(len(err.Maps()), 1) t.Assert(err.Maps()["uid"]["min"], "ID不能为空") @@ -184,7 +185,7 @@ func Test_CheckStruct(t *testing.T) { "username@required#用户名不能为空", } - err := gvalid.CheckStruct(user, rules) + err := gvalid.CheckStruct(context.TODO(), user, rules) t.AssertNE(err, nil) t.Assert(len(err.Maps()), 1) t.Assert(err.Maps()["uid"]["min"], "ID不能为空") @@ -202,7 +203,7 @@ func Test_CheckStruct(t *testing.T) { Username: "john", Password: "123456", } - err := gvalid.CheckStruct(user, nil) + err := gvalid.CheckStruct(context.TODO(), user, nil) t.AssertNE(err, nil) t.Assert(len(err.Maps()), 1) }) @@ -220,7 +221,7 @@ func Test_CheckStruct(t *testing.T) { Username: "john", Password: "123456", } - err := gvalid.CheckStruct(user, nil) + err := gvalid.CheckStruct(context.TODO(), user, nil) t.AssertNE(err, nil) t.Assert(len(err.Maps()), 1) t.Assert(err.Maps()["uid"]["min"], "ID不能为空") @@ -245,7 +246,7 @@ func Test_CheckStruct_With_EmbeddedObject(t *testing.T) { Pass2: "2", }, } - err := gvalid.CheckStruct(user, nil) + err := gvalid.CheckStruct(context.TODO(), user, nil) t.AssertNE(err, nil) t.Assert(err.Maps()["name"], g.Map{"required": "请输入您的姓名"}) t.Assert(err.Maps()["password1"], g.Map{"same": "您两次输入的密码不一致"}) @@ -271,7 +272,7 @@ func Test_CheckStruct_With_StructAttribute(t *testing.T) { Pass2: "2", }, } - err := gvalid.CheckStruct(user, nil) + err := gvalid.CheckStruct(context.TODO(), user, nil) t.AssertNE(err, nil) t.Assert(err.Maps()["name"], g.Map{"required": "请输入您的姓名"}) t.Assert(err.Maps()["password1"], g.Map{"same": "您两次输入的密码不一致"}) @@ -290,7 +291,7 @@ func Test_CheckStruct_Optional(t *testing.T) { Page: 1, Size: 10, } - err := gvalid.CheckStruct(obj, nil) + err := gvalid.CheckStruct(context.TODO(), obj, nil) t.Assert(err, nil) }) gtest.C(t, func(t *gtest.T) { @@ -303,7 +304,7 @@ func Test_CheckStruct_Optional(t *testing.T) { Page: 1, Size: 10, } - err := gvalid.CheckStruct(obj, nil) + err := gvalid.CheckStruct(context.TODO(), obj, nil) t.Assert(err, nil) }) gtest.C(t, func(t *gtest.T) { @@ -316,7 +317,7 @@ func Test_CheckStruct_Optional(t *testing.T) { Page: 1, Size: 10, } - err := gvalid.CheckStruct(obj, nil) + err := gvalid.CheckStruct(context.TODO(), obj, nil) t.Assert(err.String(), "project id must between 1, 10000") }) } @@ -332,7 +333,7 @@ func Test_CheckStruct_NoTag(t *testing.T) { Page: 1, Size: 10, } - err := gvalid.CheckStruct(obj, nil) + err := gvalid.CheckStruct(context.TODO(), obj, nil) t.Assert(err, nil) }) } @@ -349,7 +350,7 @@ func Test_CheckStruct_InvalidRule(t *testing.T) { Age: 18, Phone: "123", } - err := gvalid.CheckStruct(obj, nil) + err := gvalid.CheckStruct(context.TODO(), obj, nil) t.AssertNE(err, nil) }) } @@ -364,7 +365,7 @@ func TestValidator_CheckStructWithParamMap(t *testing.T) { Uid: 1, Nickname: "john", } - t.Assert(gvalid.CheckStructWithParamMap(data, g.Map{"uid": 1, "nickname": "john"}, nil), nil) + t.Assert(gvalid.CheckStructWithParamMap(context.TODO(), data, g.Map{"uid": 1, "nickname": "john"}, nil), nil) }) gtest.C(t, func(t *gtest.T) { type UserApiSearch struct { @@ -372,7 +373,7 @@ func TestValidator_CheckStructWithParamMap(t *testing.T) { Nickname string `v:"required-with:uid"` } data := UserApiSearch{} - t.AssertNE(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) + t.AssertNE(gvalid.CheckStructWithParamMap(context.TODO(), data, g.Map{}, nil), nil) }) gtest.C(t, func(t *gtest.T) { type UserApiSearch struct { @@ -382,7 +383,7 @@ func TestValidator_CheckStructWithParamMap(t *testing.T) { data := UserApiSearch{ Uid: 1, } - t.AssertNE(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) + t.AssertNE(gvalid.CheckStructWithParamMap(context.TODO(), data, g.Map{}, nil), nil) }) gtest.C(t, func(t *gtest.T) { @@ -396,7 +397,7 @@ func TestValidator_CheckStructWithParamMap(t *testing.T) { StartTime: nil, EndTime: nil, } - t.Assert(gvalid.CheckStructWithParamMap(data, g.Map{}, nil), nil) + t.Assert(gvalid.CheckStructWithParamMap(context.TODO(), data, g.Map{}, nil), nil) }) gtest.C(t, func(t *gtest.T) { type UserApiSearch struct { @@ -409,6 +410,6 @@ func TestValidator_CheckStructWithParamMap(t *testing.T) { StartTime: gtime.Now(), EndTime: nil, } - t.AssertNE(gvalid.CheckStructWithParamMap(data, g.Map{"start_time": gtime.Now()}, nil), nil) + t.AssertNE(gvalid.CheckStructWithParamMap(context.TODO(), data, g.Map{"start_time": gtime.Now()}, nil), nil) }) } diff --git a/util/gvalid/gvalid_z_unit_custom_rule_test.go b/util/gvalid/gvalid_z_unit_custom_rule_test.go index 2fdc36a92..326c42ebd 100644 --- a/util/gvalid/gvalid_z_unit_custom_rule_test.go +++ b/util/gvalid/gvalid_z_unit_custom_rule_test.go @@ -7,6 +7,7 @@ package gvalid_test import ( + "context" "errors" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gconv" @@ -30,9 +31,9 @@ func Test_CustomRule1(t *testing.T) { }) gtest.Assert(err, nil) gtest.C(t, func(t *gtest.T) { - err := gvalid.Check("123456", rule, "custom message") + err := gvalid.Check(context.TODO(), "123456", rule, "custom message") t.Assert(err.String(), "custom message") - err = gvalid.Check("123456", rule, "custom message", g.Map{"data": "123456"}) + err = gvalid.Check(context.TODO(), "123456", rule, "custom message", g.Map{"data": "123456"}) t.Assert(err, nil) }) // Error with struct validation. @@ -45,7 +46,7 @@ func Test_CustomRule1(t *testing.T) { Value: "123", Data: "123456", } - err := gvalid.CheckStruct(st, nil) + err := gvalid.CheckStruct(context.TODO(), st, nil) t.Assert(err.String(), "自定义错误") }) // No error with struct validation. @@ -58,7 +59,7 @@ func Test_CustomRule1(t *testing.T) { Value: "123456", Data: "123456", } - err := gvalid.CheckStruct(st, nil) + err := gvalid.CheckStruct(context.TODO(), st, nil) t.Assert(err, nil) }) } @@ -76,8 +77,8 @@ func Test_CustomRule2(t *testing.T) { // Check. gtest.C(t, func(t *gtest.T) { errStr := "data map should not be empty" - t.Assert(gvalid.Check(g.Map{}, rule, errStr).String(), errStr) - t.Assert(gvalid.Check(g.Map{"k": "v"}, rule, errStr).String(), nil) + t.Assert(gvalid.Check(context.TODO(), g.Map{}, rule, errStr).String(), errStr) + t.Assert(gvalid.Check(context.TODO(), g.Map{"k": "v"}, rule, errStr).String(), nil) }) // Error with struct validation. gtest.C(t, func(t *gtest.T) { @@ -89,7 +90,7 @@ func Test_CustomRule2(t *testing.T) { Value: map[string]string{}, Data: "123456", } - err := gvalid.CheckStruct(st, nil) + err := gvalid.CheckStruct(context.TODO(), st, nil) t.Assert(err.String(), "自定义错误") }) // No error with struct validation. @@ -102,7 +103,7 @@ func Test_CustomRule2(t *testing.T) { Value: map[string]string{"k": "v"}, Data: "123456", } - err := gvalid.CheckStruct(st, nil) + err := gvalid.CheckStruct(context.TODO(), st, nil) t.Assert(err, nil) }) } @@ -120,9 +121,9 @@ func Test_CustomRule_AllowEmpty(t *testing.T) { // Check. gtest.C(t, func(t *gtest.T) { errStr := "error" - t.Assert(gvalid.Check("", rule, errStr).String(), "") - t.Assert(gvalid.Check("gf", rule, errStr).String(), "") - t.Assert(gvalid.Check("gf2", rule, errStr).String(), errStr) + t.Assert(gvalid.Check(context.TODO(), "", rule, errStr).String(), "") + t.Assert(gvalid.Check(context.TODO(), "gf", rule, errStr).String(), "") + t.Assert(gvalid.Check(context.TODO(), "gf2", rule, errStr).String(), errStr) }) // Error with struct validation. gtest.C(t, func(t *gtest.T) { @@ -134,7 +135,7 @@ func Test_CustomRule_AllowEmpty(t *testing.T) { Value: "", Data: "123456", } - err := gvalid.CheckStruct(st, nil) + err := gvalid.CheckStruct(context.TODO(), st, nil) t.Assert(err.String(), "") }) // No error with struct validation. @@ -147,7 +148,7 @@ func Test_CustomRule_AllowEmpty(t *testing.T) { Value: "john", Data: "123456", } - err := gvalid.CheckStruct(st, nil) + err := gvalid.CheckStruct(context.TODO(), st, nil) t.Assert(err.String(), "自定义错误") }) } diff --git a/util/gvalid/gvalid_z_unit_customerror_test.go b/util/gvalid/gvalid_z_unit_customerror_test.go index 9f73afa4b..ecccf7e4d 100755 --- a/util/gvalid/gvalid_z_unit_customerror_test.go +++ b/util/gvalid/gvalid_z_unit_customerror_test.go @@ -7,6 +7,7 @@ package gvalid_test import ( + "context" "strings" "testing" @@ -19,7 +20,7 @@ func Test_Map(t *testing.T) { var ( rule = "ipv4" val = "0.0.0" - err = gvalid.Check(val, rule, nil) + err = gvalid.Check(context.TODO(), val, rule, nil) msg = map[string]string{ "ipv4": "The value must be a valid IPv4 address", } @@ -33,7 +34,7 @@ func Test_FirstString(t *testing.T) { var ( rule = "ipv4" val = "0.0.0" - err = gvalid.Check(val, rule, nil) + err = gvalid.Check(context.TODO(), val, rule, nil) n = err.FirstString() ) t.Assert(n, "The value must be a valid IPv4 address") @@ -46,7 +47,7 @@ func Test_CustomError1(t *testing.T) { "integer": "请输入一个整数", "length": "参数长度不对啊老铁", } - e := gvalid.Check("6.66", rule, msgs) + e := gvalid.Check(context.TODO(), "6.66", rule, msgs) if e == nil || len(e.Map()) != 2 { t.Error("规则校验失败") } else { @@ -66,7 +67,7 @@ func Test_CustomError1(t *testing.T) { func Test_CustomError2(t *testing.T) { rule := "integer|length:6,16" msgs := "请输入一个整数|参数长度不对啊老铁" - e := gvalid.Check("6.66", rule, msgs) + e := gvalid.Check(context.TODO(), "6.66", rule, msgs) if e == nil || len(e.Map()) != 2 { t.Error("规则校验失败") } else { From d76e4c8aed7be69bd4e08fd2afd8e1778edc1fdf Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 15 May 2021 18:13:51 +0800 Subject: [PATCH 255/492] refract package gtimer for more stable --- container/gqueue/gqueue.go | 2 +- ...e_bench_test.go => gqueue_z_bench_test.go} | 0 ...eue_unit_test.go => gqueue_z_unit_test.go} | 0 net/ghttp/ghttp_server.go | 4 +- os/gcron/gcron.go | 12 +- os/gcron/gcron_cron.go | 28 +-- os/gcron/gcron_entry.go | 56 ++--- os/gcron/gcron_unit_2_test.go | 2 +- os/gtimer/gtimer.go | 73 +++--- os/gtimer/gtimer_entry.go | 211 ----------------- os/gtimer/gtimer_job.go | 134 +++++++++++ os/gtimer/gtimer_loop.go | 93 -------- os/gtimer/gtimer_queue.go | 91 ++++++++ os/gtimer/gtimer_queue_heap.go | 41 ++++ os/gtimer/gtimer_timer.go | 220 ++++-------------- os/gtimer/gtimer_timer_loop.go | 68 ++++++ os/gtimer/gtimer_z_bench_test.go | 2 +- os/gtimer/gtimer_z_unit_api_test.go | 8 +- os/gtimer/gtimer_z_unit_entry_test.go | 37 +-- .../gtimer_z_unit_timer_internal_test.go | 50 ++-- os/gtimer/gtimer_z_unit_timer_test.go | 41 ++-- 21 files changed, 549 insertions(+), 624 deletions(-) rename container/gqueue/{gqueue_bench_test.go => gqueue_z_bench_test.go} (100%) rename container/gqueue/{gqueue_unit_test.go => gqueue_z_unit_test.go} (100%) delete mode 100644 os/gtimer/gtimer_entry.go create mode 100644 os/gtimer/gtimer_job.go delete mode 100644 os/gtimer/gtimer_loop.go create mode 100644 os/gtimer/gtimer_queue.go create mode 100644 os/gtimer/gtimer_queue_heap.go create mode 100644 os/gtimer/gtimer_timer_loop.go diff --git a/container/gqueue/gqueue.go b/container/gqueue/gqueue.go index dc2f9d39d..b8a2dfbaa 100644 --- a/container/gqueue/gqueue.go +++ b/container/gqueue/gqueue.go @@ -129,7 +129,7 @@ func (q *Queue) Close() { // Len returns the length of the queue. // Note that the result might not be accurate as there's a -// asynchronize channel reading the list constantly. +// asynchronous channel reading the list constantly. func (q *Queue) Len() (length int) { if q.list != nil { length += q.list.Len() diff --git a/container/gqueue/gqueue_bench_test.go b/container/gqueue/gqueue_z_bench_test.go similarity index 100% rename from container/gqueue/gqueue_bench_test.go rename to container/gqueue/gqueue_z_bench_test.go diff --git a/container/gqueue/gqueue_unit_test.go b/container/gqueue/gqueue_z_unit_test.go similarity index 100% rename from container/gqueue/gqueue_unit_test.go rename to container/gqueue/gqueue_z_unit_test.go diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 7b347ce6d..5896ace8c 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -78,7 +78,7 @@ func serverProcessInit() { // It's an ugly calling for better initializing the main package path // in source development environment. It is useful only be used in main goroutine. - // It fails retrieving the main package path in asynchronized goroutines. + // It fails retrieving the main package path in asynchronous goroutines. gfile.MainPkgPath() } @@ -416,7 +416,7 @@ func (s *Server) startServer(fdMap listenerFdMap) { s.servers = append(s.servers, s.newGracefulServer(itemFunc)) } } - // Start listening asynchronizedly. + // Start listening asynchronously. serverRunning.Add(1) for _, v := range s.servers { go func(server *gracefulServer) { diff --git a/os/gcron/gcron.go b/os/gcron/gcron.go index 7f45c4b2c..b3a17f426 100644 --- a/os/gcron/gcron.go +++ b/os/gcron/gcron.go @@ -50,7 +50,7 @@ func GetLogLevel() int { // Add adds a timed task to default cron object. // A unique can be bound with the timed task. // It returns and error if the is already used. -func Add(pattern string, job func(), name ...string) (*Entry, error) { +func Add(pattern string, job func(), name ...string) (*Job, error) { return defaultCron.Add(pattern, job, name...) } @@ -58,21 +58,21 @@ func Add(pattern string, job func(), name ...string) (*Entry, error) { // A singleton timed task is that can only be running one single instance at the same time. // A unique can be bound with the timed task. // It returns and error if the is already used. -func AddSingleton(pattern string, job func(), name ...string) (*Entry, error) { +func AddSingleton(pattern string, job func(), name ...string) (*Job, error) { return defaultCron.AddSingleton(pattern, job, name...) } // AddOnce adds a timed task which can be run only once, to default cron object. // A unique can be bound with the timed task. // It returns and error if the is already used. -func AddOnce(pattern string, job func(), name ...string) (*Entry, error) { +func AddOnce(pattern string, job func(), name ...string) (*Job, error) { return defaultCron.AddOnce(pattern, job, name...) } // AddTimes adds a timed task which can be run specified times, to default cron object. // A unique can be bound with the timed task. // It returns and error if the is already used. -func AddTimes(pattern string, times int, job func(), name ...string) (*Entry, error) { +func AddTimes(pattern string, times int, job func(), name ...string) (*Job, error) { return defaultCron.AddTimes(pattern, times, job, name...) } @@ -100,7 +100,7 @@ func DelayAddTimes(delay time.Duration, pattern string, times int, job func(), n // Search returns a scheduled task with the specified . // It returns nil if no found. -func Search(name string) *Entry { +func Search(name string) *Job { return defaultCron.Search(name) } @@ -115,7 +115,7 @@ func Size() int { } // Entries return all timed tasks as slice. -func Entries() []*Entry { +func Entries() []*Job { return defaultCron.Entries() } diff --git a/os/gcron/gcron_cron.go b/os/gcron/gcron_cron.go index 28e2b8522..de9493fcc 100644 --- a/os/gcron/gcron_cron.go +++ b/os/gcron/gcron_cron.go @@ -60,20 +60,20 @@ func (c *Cron) GetLogLevel() int { // Add adds a timed task. // A unique can be bound with the timed task. // It returns and error if the is already used. -func (c *Cron) Add(pattern string, job func(), name ...string) (*Entry, error) { +func (c *Cron) Add(pattern string, job func(), name ...string) (*Job, error) { if len(name) > 0 { if c.Search(name[0]) != nil { return nil, errors.New(fmt.Sprintf(`cron job "%s" already exists`, name[0])) } } - return c.addEntry(pattern, job, false, name...) + return c.addJob(pattern, job, false, name...) } // AddSingleton adds a singleton timed task. // A singleton timed task is that can only be running one single instance at the same time. // A unique can be bound with the timed task. // It returns and error if the is already used. -func (c *Cron) AddSingleton(pattern string, job func(), name ...string) (*Entry, error) { +func (c *Cron) AddSingleton(pattern string, job func(), name ...string) (*Job, error) { if entry, err := c.Add(pattern, job, name...); err != nil { return nil, err } else { @@ -85,7 +85,7 @@ func (c *Cron) AddSingleton(pattern string, job func(), name ...string) (*Entry, // AddOnce adds a timed task which can be run only once. // A unique can be bound with the timed task. // It returns and error if the is already used. -func (c *Cron) AddOnce(pattern string, job func(), name ...string) (*Entry, error) { +func (c *Cron) AddOnce(pattern string, job func(), name ...string) (*Job, error) { if entry, err := c.Add(pattern, job, name...); err != nil { return nil, err } else { @@ -97,7 +97,7 @@ func (c *Cron) AddOnce(pattern string, job func(), name ...string) (*Entry, erro // AddTimes adds a timed task which can be run specified times. // A unique can be bound with the timed task. // It returns and error if the is already used. -func (c *Cron) AddTimes(pattern string, times int, job func(), name ...string) (*Entry, error) { +func (c *Cron) AddTimes(pattern string, times int, job func(), name ...string) (*Job, error) { if entry, err := c.Add(pattern, job, name...); err != nil { return nil, err } else { @@ -146,9 +146,9 @@ func (c *Cron) DelayAddTimes(delay time.Duration, pattern string, times int, job // Search returns a scheduled task with the specified . // It returns nil if no found. -func (c *Cron) Search(name string) *Entry { +func (c *Cron) Search(name string) *Job { if v := c.entries.Get(name); v != nil { - return v.(*Entry) + return v.(*Job) } return nil } @@ -182,7 +182,7 @@ func (c *Cron) Stop(name ...string) { // Remove deletes scheduled task which named . func (c *Cron) Remove(name string) { if v := c.entries.Get(name); v != nil { - v.(*Entry).Close() + v.(*Job).Close() } } @@ -197,10 +197,10 @@ func (c *Cron) Size() int { } // Entries return all timed tasks as slice(order by registered time asc). -func (c *Cron) Entries() []*Entry { +func (c *Cron) Entries() []*Job { array := garray.NewSortedArraySize(c.entries.Size(), func(v1, v2 interface{}) int { - entry1 := v1.(*Entry) - entry2 := v2.(*Entry) + entry1 := v1.(*Job) + entry2 := v2.(*Job) if entry1.Time.Nanosecond() > entry2.Time.Nanosecond() { return 1 } @@ -208,13 +208,13 @@ func (c *Cron) Entries() []*Entry { }, true) c.entries.RLockFunc(func(m map[string]interface{}) { for _, v := range m { - array.Add(v.(*Entry)) + array.Add(v.(*Job)) } }) - entries := make([]*Entry, array.Len()) + entries := make([]*Job, array.Len()) array.RLockFunc(func(array []interface{}) { for k, v := range array { - entries[k] = v.(*Entry) + entries[k] = v.(*Job) } }) return entries diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index 94ff02f43..e0c33e474 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -18,28 +18,28 @@ import ( ) // Timed task entry. -type Entry struct { +type Job struct { cron *Cron // Cron object belonged to. - entry *gtimer.Entry // Associated gtimer.Entry. + job *gtimer.Job // Associated gtimer.Job. schedule *cronSchedule // Timed schedule object. jobName string // Callback function name(address info). times *gtype.Int // Running times limit. - Name string // Entry name. + Name string // Job name. Job func() `json:"-"` // Callback function. Time time.Time // Registered time. } -// addEntry creates and returns a new Entry object. +// addJob creates and returns a new Job object. // Param is the callback function for timed task execution. // Param specifies whether timed task executing in singleton mode. // Param names this entry for manual control. -func (c *Cron) addEntry(pattern string, job func(), singleton bool, name ...string) (*Entry, error) { +func (c *Cron) addJob(pattern string, job func(), singleton bool, name ...string) (*Job, error) { schedule, err := newSchedule(pattern) if err != nil { return nil, err } // No limit for , for gtimer checking scheduling every second. - entry := &Entry{ + entry := &Job{ cron: c, schedule: schedule, jobName: runtime.FuncForPC(reflect.ValueOf(job).Pointer()).Name(), @@ -57,57 +57,57 @@ func (c *Cron) addEntry(pattern string, job func(), singleton bool, name ...stri // It should start running after the entry is added to the entries map, // to avoid the task from running during adding where the entries // does not have the entry information, which might cause panic. - entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, -1, gtimer.StatusStopped) + entry.job = gtimer.AddJob(time.Second, entry.check, singleton, -1, gtimer.StatusStopped) c.entries.Set(entry.Name, entry) - entry.entry.Start() + entry.job.Start() return entry, nil } // IsSingleton return whether this entry is a singleton timed task. -func (entry *Entry) IsSingleton() bool { - return entry.entry.IsSingleton() +func (entry *Job) IsSingleton() bool { + return entry.job.IsSingleton() } // SetSingleton sets the entry running in singleton mode. -func (entry *Entry) SetSingleton(enabled bool) { - entry.entry.SetSingleton(true) +func (entry *Job) SetSingleton(enabled bool) { + entry.job.SetSingleton(true) } // SetTimes sets the times which the entry can run. -func (entry *Entry) SetTimes(times int) { +func (entry *Job) SetTimes(times int) { entry.times.Set(times) } // Status returns the status of entry. -func (entry *Entry) Status() int { - return entry.entry.Status() +func (entry *Job) Status() int { + return entry.job.Status() } // SetStatus sets the status of the entry. -func (entry *Entry) SetStatus(status int) int { - return entry.entry.SetStatus(status) +func (entry *Job) SetStatus(status int) int { + return entry.job.SetStatus(status) } // Start starts running the entry. -func (entry *Entry) Start() { - entry.entry.Start() +func (entry *Job) Start() { + entry.job.Start() } // Stop stops running the entry. -func (entry *Entry) Stop() { - entry.entry.Stop() +func (entry *Job) Stop() { + entry.job.Stop() } // Close stops and removes the entry from cron. -func (entry *Entry) Close() { +func (entry *Job) Close() { entry.cron.entries.Remove(entry.Name) - entry.entry.Close() + entry.job.Close() } // Timed task check execution. -// The running times limits feature is implemented by gcron.Entry and cannot be implemented by gtimer.Entry. -// gcron.Entry relies on gtimer to implement a scheduled task check for gcron.Entry per second. -func (entry *Entry) check() { +// The running times limits feature is implemented by gcron.Job and cannot be implemented by gtimer.Job. +// gcron.Job relies on gtimer to implement a scheduled task check for gcron.Job per second. +func (entry *Job) check() { if entry.schedule.meet(time.Now()) { path := entry.cron.GetLogPath() level := entry.cron.GetLogLevel() @@ -125,7 +125,7 @@ func (entry *Entry) check() { // Running times check. times := entry.times.Add(-1) if times <= 0 { - if entry.entry.SetStatus(StatusClosed) == StatusClosed || times < 0 { + if entry.job.SetStatus(StatusClosed) == StatusClosed || times < 0 { return } } @@ -139,7 +139,7 @@ func (entry *Entry) check() { } else { glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s end", entry.Name, entry.schedule.pattern, entry.jobName) } - if entry.entry.Status() == StatusClosed { + if entry.job.Status() == StatusClosed { entry.Close() } }() diff --git a/os/gcron/gcron_unit_2_test.go b/os/gcron/gcron_unit_2_test.go index 5956dc8bd..6fca7c849 100644 --- a/os/gcron/gcron_unit_2_test.go +++ b/os/gcron/gcron_unit_2_test.go @@ -16,7 +16,7 @@ import ( "github.com/gogf/gf/test/gtest" ) -func TestCron_Entry_Operations(t *testing.T) { +func TestCron_Job_Operations(t *testing.T) { gtest.C(t, func(t *gtest.T) { var ( cron = gcron.New() diff --git a/os/gtimer/gtimer.go b/os/gtimer/gtimer.go index 80840682c..21474a498 100644 --- a/os/gtimer/gtimer.go +++ b/os/gtimer/gtimer.go @@ -4,8 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gtimer implements Hierarchical Timing Wheel for interval/delayed jobs -// running and management. +// Package gtimer implements timer for interval/delayed jobs running and management. // // This package is designed for management for millions of timing jobs. The differences // between gtimer and gcron are as follows: @@ -21,33 +20,53 @@ package gtimer import ( "fmt" + "github.com/gogf/gf/container/gtype" "math" + "sync" "time" "github.com/gogf/gf/os/gcmd" ) +// Timer is the timer manager, which uses ticks to calculate the timing interval. +type Timer struct { + mu sync.RWMutex + queue *priorityQueue // queue is a priority queue based on heap structure. + status *gtype.Int // status is the current timer status. + ticks *gtype.Int64 // ticks is the proceeded interval number by the timer. + options TimerOptions // timer options is used for timer configuration. +} + +// TimerOptions is the configuration object for Timer. +type TimerOptions struct { + Interval time.Duration // Interval is the interval escaped of the timer. +} + const ( - StatusReady = 0 // Job is ready for running. - StatusRunning = 1 // Job is already running. - StatusStopped = 2 // Job is stopped. - StatusReset = 3 // Job is reset. - StatusClosed = -1 // Job is closed and waiting to be deleted. - panicExit = "exit" // Internal usage for custom job exit function with panic. - defaultTimes = math.MaxInt32 // Default limit running times, a big number. - defaultSlotNumber = 10 // Default slot number. - defaultWheelInterval = 60 // Default wheel interval, for better manually reading. - defaultWheelLevel = 5 // Default wheel level. + StatusReady = 0 // Job or Timer is ready for running. + StatusRunning = 1 // Job or Timer is already running. + StatusStopped = 2 // Job or Timer is stopped. + StatusClosed = -1 // Job or Timer is closed and waiting to be deleted. + panicExit = "exit" // panicExit is used for custom job exit with panic. + defaultTimes = math.MaxInt32 // defaultTimes is the default limit running times, a big number. + defaultTimerInterval = 100 // defaultTimerInterval is the default timer interval in milliseconds. cmdEnvKey = "gf.gtimer" // Configuration key for command argument or environment. ) var ( - defaultSlots = gcmd.GetOptWithEnv(fmt.Sprintf("%s.slots", cmdEnvKey), defaultSlotNumber).Int() - defaultLevel = gcmd.GetOptWithEnv(fmt.Sprintf("%s.level", cmdEnvKey), defaultWheelLevel).Int() - defaultInterval = gcmd.GetOptWithEnv(fmt.Sprintf("%s.interval", cmdEnvKey), defaultWheelInterval).Duration() * time.Millisecond - defaultTimer = New(defaultSlots, defaultInterval, defaultLevel) + defaultTimer = New() + defaultInterval = gcmd.GetOptWithEnv( + fmt.Sprintf("%s.interval", cmdEnvKey), defaultTimerInterval, + ).Duration() * time.Millisecond ) +// DefaultOptions creates and returns a default options object for Timer creation. +func DefaultOptions() TimerOptions { + return TimerOptions{ + Interval: defaultInterval, + } +} + // SetTimeout runs the job once after duration of . // It is like the one in javascript. func SetTimeout(delay time.Duration, job JobFunc) { @@ -61,11 +80,11 @@ func SetInterval(interval time.Duration, job JobFunc) { } // Add adds a timing job to the default timer, which runs in interval of . -func Add(interval time.Duration, job JobFunc) *Entry { +func Add(interval time.Duration, job JobFunc) *Job { return defaultTimer.Add(interval, job) } -// AddEntry adds a timing job to the default timer with detailed parameters. +// AddJob adds a timing job to the default timer with detailed parameters. // // The parameter specifies the running interval of the job. // @@ -76,22 +95,22 @@ func Add(interval time.Duration, job JobFunc) *Entry { // exits if its run times exceeds the . // // The parameter specifies the job status when it's firstly added to the timer. -func AddEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry { - return defaultTimer.AddEntry(interval, job, singleton, times, status) +func AddJob(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Job { + return defaultTimer.AddJob(interval, job, singleton, times, status) } // AddSingleton is a convenience function for add singleton mode job. -func AddSingleton(interval time.Duration, job JobFunc) *Entry { +func AddSingleton(interval time.Duration, job JobFunc) *Job { return defaultTimer.AddSingleton(interval, job) } // AddOnce is a convenience function for adding a job which only runs once and then exits. -func AddOnce(interval time.Duration, job JobFunc) *Entry { +func AddOnce(interval time.Duration, job JobFunc) *Job { return defaultTimer.AddOnce(interval, job) } // AddTimes is a convenience function for adding a job which is limited running times. -func AddTimes(interval time.Duration, times int, job JobFunc) *Entry { +func AddTimes(interval time.Duration, times int, job JobFunc) *Job { return defaultTimer.AddTimes(interval, times, job) } @@ -101,10 +120,10 @@ func DelayAdd(delay time.Duration, interval time.Duration, job JobFunc) { defaultTimer.DelayAdd(delay, interval, job) } -// DelayAddEntry adds a timing job after delay of duration. -// Also see AddEntry. -func DelayAddEntry(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) { - defaultTimer.DelayAddEntry(delay, interval, job, singleton, times, status) +// DelayAddJob adds a timing job after delay of duration. +// Also see AddJob. +func DelayAddJob(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) { + defaultTimer.DelayAddJob(delay, interval, job, singleton, times, status) } // DelayAddSingleton adds a timing job after delay of duration. diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go deleted file mode 100644 index f413646c4..000000000 --- a/os/gtimer/gtimer_entry.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gtimer - -import ( - "time" - - "github.com/gogf/gf/container/gtype" -) - -// Entry is the timing job entry to wheel. -type Entry struct { - name string - wheel *wheel // Belonged wheel. - job JobFunc // The job function. - singleton *gtype.Bool // Singleton mode. - status *gtype.Int // Job status. - times *gtype.Int // Limit running times. - intervalTicks int64 // The interval ticks of the job. - createTicks int64 // Timer ticks when the job installed. - createMs int64 // The timestamp in milliseconds when job installed. - intervalMs int64 // The interval milliseconds of the job. - installIntervalMs int64 // Interval when first installation in milliseconds. -} - -// JobFunc is the job function. -type JobFunc = func() - -// addEntry adds a timing job to the wheel. -func (w *wheel) addEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry { - if times <= 0 { - times = defaultTimes - } - var ( - intervalMs = interval.Nanoseconds() / 1e6 - intervalTicks = intervalMs / w.intervalMs - ) - if intervalTicks == 0 { - // If the given interval is lesser than the one of the wheel, - // then sets it to one tick, which means it will be run in one interval. - intervalTicks = 1 - } - var ( - nowMs = time.Now().UnixNano() / 1e6 - nowTicks = w.ticks.Val() - entry = &Entry{ - wheel: w, - job: job, - times: gtype.NewInt(times), - status: gtype.NewInt(status), - createTicks: nowTicks, - intervalTicks: intervalTicks, - singleton: gtype.NewBool(singleton), - createMs: nowMs, - intervalMs: intervalMs, - installIntervalMs: intervalMs, - } - ) - // Install the job to the list of the slot. - w.slots[(nowTicks+intervalTicks)%w.number].PushBack(entry) - return entry -} - -// addEntryByParent adds a timing job with parent entry. -// The parameter `rollOn` specifies if just rolling on the entry, which was not met the runnable requirement -// and not executed previously. This is true often when the job internal is too long. -func (w *wheel) addEntryByParent(rollOn bool, nowMs, interval int64, parent *Entry) *Entry { - intervalTicks := interval / w.intervalMs - if intervalTicks == 0 { - intervalTicks = 1 - } - nowTicks := w.ticks.Val() - entry := &Entry{ - name: parent.name, - wheel: w, - job: parent.job, - times: parent.times, - status: parent.status, - intervalTicks: intervalTicks, - singleton: parent.singleton, - createTicks: nowTicks, - createMs: nowMs, - intervalMs: interval, - installIntervalMs: parent.installIntervalMs, - } - if rollOn { - entry.createMs = parent.createMs - if parent.wheel.level == w.level { - entry.createTicks = parent.createTicks - } - } - w.slots[(nowTicks+intervalTicks)%w.number].PushBack(entry) - return entry -} - -// Status returns the status of the job. -func (entry *Entry) Status() int { - return entry.status.Val() -} - -// SetStatus custom sets the status for the job. -func (entry *Entry) SetStatus(status int) int { - return entry.status.Set(status) -} - -// Start starts the job. -func (entry *Entry) Start() { - entry.status.Set(StatusReady) -} - -// Stop stops the job. -func (entry *Entry) Stop() { - entry.status.Set(StatusStopped) -} - -//Reset reset the job. -func (entry *Entry) Reset() { - entry.status.Set(StatusReset) -} - -// Close closes the job, and then it will be removed from the timer. -func (entry *Entry) Close() { - entry.status.Set(StatusClosed) -} - -// IsSingleton checks and returns whether the job in singleton mode. -func (entry *Entry) IsSingleton() bool { - return entry.singleton.Val() -} - -// SetSingleton sets the job singleton mode. -func (entry *Entry) SetSingleton(enabled bool) { - entry.singleton.Set(enabled) -} - -// SetTimes sets the limit running times for the job. -func (entry *Entry) SetTimes(times int) { - entry.times.Set(times) -} - -// Run runs the job. -func (entry *Entry) Run() { - entry.job() -} - -// check checks if the job should be run in given ticks and timestamp milliseconds. -func (entry *Entry) check(nowTicks int64, nowMs int64) (runnable, addable bool) { - switch entry.status.Val() { - case StatusStopped: - return false, true - case StatusClosed: - return false, false - case StatusReset: - return false, true - } - // Firstly checks using the ticks, this may be low precision as one tick is a little bit long. - //if entry.name == "1" { - // intlog.Print("check:", nowTicks-entry.createTicks, nowTicks, entry.createTicks, entry.intervalTicks) - //} - if diff := nowTicks - entry.createTicks; diff > 0 && diff%entry.intervalTicks == 0 { - // If not the lowest level wheel. - if entry.wheel.level > 0 { - diffMs := nowMs - entry.createMs - switch { - // Add it to the next slot, which means it will run on next interval. - case diffMs < entry.wheel.timer.intervalMs: - entry.wheel.slots[(nowTicks+entry.intervalTicks)%entry.wheel.number].PushBack(entry) - return false, false - - // Normal rolls on the job. - case diffMs >= entry.wheel.timer.intervalMs: - // Calculate the leftover milliseconds, - // if it is greater than the minimum interval, then re-install it. - if leftMs := entry.intervalMs - diffMs; leftMs > entry.wheel.timer.intervalMs { - // Re-calculate and re-installs the job proper slot. - entry.wheel.timer.doAddEntryByParent(false, nowMs, leftMs, entry) - return false, false - } - } - } - // Singleton mode check. - if entry.IsSingleton() { - // Note that it is atomic operation to ensure concurrent safety. - if entry.status.Set(StatusRunning) == StatusRunning { - return false, true - } - } - // Limit running times. - times := entry.times.Add(-1) - if times <= 0 { - // Note that it is atomic operation to ensure concurrent safety. - if entry.status.Set(StatusClosed) == StatusClosed || times < 0 { - return false, false - } - } - // This means it does not limit the running times. - // I know it's ugly, but it is surely high performance for running times limit. - if times < 2000000000 && times > 1000000000 { - entry.times.Set(defaultTimes) - } - //if entry.name == "1" { - // intlog.Print("runnable:", nowTicks-entry.createTicks, nowTicks, entry.createTicks, entry.createTicks, entry.interval) - //} - return true, true - } - return false, true -} diff --git a/os/gtimer/gtimer_job.go b/os/gtimer/gtimer_job.go new file mode 100644 index 000000000..39fc938ae --- /dev/null +++ b/os/gtimer/gtimer_job.go @@ -0,0 +1,134 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtimer + +import ( + "github.com/gogf/gf/container/gtype" + "math" +) + +// Job is the timing job. +type Job struct { + job JobFunc // The job function. + timer *Timer // Belonged timer. + ticks int64 // The job runs every ticks. + times *gtype.Int // Limit running times. + status *gtype.Int // Job status. + singleton *gtype.Bool // Singleton mode. + nextTicks *gtype.Int64 // Next run ticks of the job. +} + +// JobFunc is the job function. +type JobFunc = func() + +// Status returns the status of the job. +func (j *Job) Status() int { + return j.status.Val() +} + +// Run runs the timer job asynchronously. +func (j *Job) Run() { + leftRunningTimes := j.times.Add(-1) + if leftRunningTimes < 0 { + j.status.Set(StatusClosed) + return + } + // This means it does not limit the running times. + // I know it's ugly, but it is surely high performance for running times limit. + if leftRunningTimes < 2000000000 && leftRunningTimes > 1000000000 { + j.times.Set(math.MaxInt32) + } + go func() { + defer func() { + if err := recover(); err != nil { + if err != panicExit { + panic(err) + } else { + j.Close() + return + } + } + if j.Status() == StatusRunning { + j.SetStatus(StatusReady) + } + }() + j.job() + }() +} + +// doCheckAndRunByTicks checks the if job can run in given timer ticks, +// it runs asynchronously if the given `currentTimerTicks` meets or else +// it increments its ticks and waits for next running check. +func (j *Job) doCheckAndRunByTicks(currentTimerTicks int64) { + // Ticks check. + if currentTimerTicks < j.nextTicks.Val() { + return + } + j.nextTicks.Set(currentTimerTicks + j.ticks) + // Perform job checking. + switch j.status.Val() { + case StatusRunning: + if j.IsSingleton() { + return + } + case StatusReady: + if !j.status.Cas(StatusReady, StatusRunning) { + return + } + case StatusStopped: + return + case StatusClosed: + return + } + // Perform job running. + j.Run() +} + +// SetStatus custom sets the status for the job. +func (j *Job) SetStatus(status int) int { + return j.status.Set(status) +} + +// Start starts the job. +func (j *Job) Start() { + j.status.Set(StatusReady) +} + +// Stop stops the job. +func (j *Job) Stop() { + j.status.Set(StatusStopped) +} + +// Close closes the job, and then it will be removed from the timer. +func (j *Job) Close() { + j.status.Set(StatusClosed) +} + +// Reset reset the job, which resets its ticks for next running. +func (j *Job) Reset() { + j.nextTicks.Set(j.timer.ticks.Val() + j.ticks) +} + +// IsSingleton checks and returns whether the job in singleton mode. +func (j *Job) IsSingleton() bool { + return j.singleton.Val() +} + +// SetSingleton sets the job singleton mode. +func (j *Job) SetSingleton(enabled bool) { + j.singleton.Set(enabled) +} + +// Job returns the job function of this job. +func (j *Job) Job() JobFunc { + return j.job +} + +// SetTimes sets the limit running times for the job. +func (j *Job) SetTimes(times int) { + j.times.Set(times) +} diff --git a/os/gtimer/gtimer_loop.go b/os/gtimer/gtimer_loop.go deleted file mode 100644 index 725f6de46..000000000 --- a/os/gtimer/gtimer_loop.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gtimer - -import ( - "time" - - "github.com/gogf/gf/container/glist" -) - -// start starts the ticker using a standalone goroutine. -func (w *wheel) start() { - go func() { - var ( - tickDuration = time.Duration(w.intervalMs) * time.Millisecond - ticker = time.NewTicker(tickDuration) - ) - for { - select { - case <-ticker.C: - switch w.timer.status.Val() { - case StatusRunning: - w.proceed() - - case StatusStopped: - // Do nothing. - - case StatusClosed: - ticker.Stop() - return - } - - } - } - }() -} - -// proceed checks and rolls on the job. -// If a timing job is time for running, it runs in an asynchronous goroutine, -// or else it removes from current slot and re-installs the job to another wheel and slot -// according to its leftover interval in milliseconds. -func (w *wheel) proceed() { - var ( - nowTicks = w.ticks.Add(1) - list = w.slots[int(nowTicks%w.number)] - length = list.Len() - nowMs = w.timer.nowFunc().UnixNano() / 1e6 - ) - if length > 0 { - go func(l *glist.List, nowTicks int64) { - var entry *Entry - for i := length; i > 0; i-- { - if v := l.PopFront(); v == nil { - break - } else { - entry = v.(*Entry) - } - // Checks whether the time for running. - runnable, addable := entry.check(nowTicks, nowMs) - if runnable { - // Just run it in another goroutine. - go func(entry *Entry) { - defer func() { - if err := recover(); err != nil { - if err != panicExit { - panic(err) - } else { - entry.Close() - } - } - if entry.Status() == StatusRunning { - entry.SetStatus(StatusReady) - } - }() - entry.job() - }(entry) - } - // Add job again, which make the job continuous running. - if addable { - // If StatusReset, reset to runnable state. - if entry.Status() == StatusReset { - entry.SetStatus(StatusReady) - } - entry.wheel.timer.doAddEntryByParent(!runnable, nowMs, entry.installIntervalMs, entry) - } - } - }(list, nowTicks) - } -} diff --git a/os/gtimer/gtimer_queue.go b/os/gtimer/gtimer_queue.go new file mode 100644 index 000000000..b805d7d22 --- /dev/null +++ b/os/gtimer/gtimer_queue.go @@ -0,0 +1,91 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtimer + +import ( + "container/heap" + "github.com/gogf/gf/container/gtype" + "math" + "sync" +) + +// priorityQueue is an abstract data type similar to a regular queue or stack data structure in which +// each element additionally has a "priority" associated with it. In a priority queue, an element with +// high priority is served before an element with low priority. +// priorityQueue is based on heap structure. +type priorityQueue struct { + mu sync.RWMutex + heap *priorityQueueHeap // the underlying queue items manager using heap. + latestPriority *gtype.Int64 // latestPriority stores the most priority value of the heap, which is used to check if necessary to call the Pop of heap by Timer. +} + +// priorityQueueHeap is a heap manager, of which the underlying `array` is a array implementing a heap structure. +type priorityQueueHeap struct { + array []priorityQueueItem +} + +// priorityQueueItem stores the queue item which has a `priority` attribute to sort itself in heap. +type priorityQueueItem struct { + value interface{} + priority int64 +} + +// newPriorityQueue creates and returns a priority queue. +func newPriorityQueue() *priorityQueue { + queue := &priorityQueue{ + heap: &priorityQueueHeap{ + array: make([]priorityQueueItem, 0), + }, + latestPriority: gtype.NewInt64(math.MaxInt64), + } + heap.Init(queue.heap) + return queue +} + +// Len retrieves and returns the length of the queue. +func (q *priorityQueue) Len() int { + q.mu.RLock() + defer q.mu.RUnlock() + return q.heap.Len() +} + +// LatestPriority retrieves and returns the minimum and the most priority value of the queue. +func (q *priorityQueue) LatestPriority() int64 { + return q.latestPriority.Val() +} + +// Push pushes a value to the queue. +// The `priority` specifies the priority of the value. +// The lesser the `priority` value the higher priority of the `value`. +func (q *priorityQueue) Push(value interface{}, priority int64) { + q.mu.Lock() + defer q.mu.Unlock() + // Update the minimum priority using atomic operation. + for { + latestPriority := q.latestPriority.Val() + if priority >= latestPriority { + break + } + if q.latestPriority.Cas(latestPriority, priority) { + break + } + } + heap.Push(q.heap, priorityQueueItem{ + value: value, + priority: priority, + }) +} + +// Pop retrieves, removes and returns the most high priority value from the queue. +func (q *priorityQueue) Pop() interface{} { + q.mu.Lock() + defer q.mu.Unlock() + if item := heap.Pop(q.heap); item != nil { + return item.(priorityQueueItem).value + } + return nil +} diff --git a/os/gtimer/gtimer_queue_heap.go b/os/gtimer/gtimer_queue_heap.go new file mode 100644 index 000000000..30879f770 --- /dev/null +++ b/os/gtimer/gtimer_queue_heap.go @@ -0,0 +1,41 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtimer + +// Len is used to implement the interface of sort.Interface. +func (h *priorityQueueHeap) Len() int { + return len(h.array) +} + +// Less is used to implement the interface of sort.Interface. +func (h *priorityQueueHeap) Less(i, j int) bool { + return h.array[i].priority < h.array[j].priority +} + +// Swap is used to implement the interface of sort.Interface. +func (h *priorityQueueHeap) Swap(i, j int) { + if len(h.array) == 0 { + return + } + h.array[i], h.array[j] = h.array[j], h.array[i] +} + +// Push pushes an item to the heap. +func (h *priorityQueueHeap) Push(x interface{}) { + h.array = append(h.array, x.(priorityQueueItem)) +} + +// Pop retrieves, removes and returns the most high priority item from the heap. +func (h *priorityQueueHeap) Pop() interface{} { + length := len(h.array) + if length == 0 { + return nil + } + item := h.array[length-1] + h.array = h.array[0 : length-1] + return item +} diff --git a/os/gtimer/gtimer_timer.go b/os/gtimer/gtimer_timer.go index 0280f0680..00e700d4d 100644 --- a/os/gtimer/gtimer_timer.go +++ b/os/gtimer/gtimer_timer.go @@ -7,107 +7,31 @@ package gtimer import ( - "fmt" - "time" - - "github.com/gogf/gf/container/glist" "github.com/gogf/gf/container/gtype" + "time" ) -// Timer is a Hierarchical Timing Wheel manager for timing jobs. -type Timer struct { - status *gtype.Int // Timer status. - wheels []*wheel // The underlying wheels. - length int // Max level of the wheels. - number int // Slot Number of each wheel. - intervalMs int64 // Interval of the slot in milliseconds. - nowFunc func() time.Time // nowFunc returns the current time, which can be custom. -} - -// Wheel is a slot wrapper for timing job install and uninstall. -type wheel struct { - timer *Timer // Belonged timer. - level int // The level in the timer. - slots []*glist.List // Slot array. - number int64 // Slot Number=len(slots). - ticks *gtype.Int64 // Ticked count of the wheel, one tick is one of its interval passed. - totalMs int64 // Total duration in milliseconds=number*interval. - createMs int64 // Created timestamp in milliseconds. - intervalMs int64 // Interval in milliseconds, which is the duration of one slot. -} - -// New creates and returns a Hierarchical Timing Wheel designed timer. -// The parameter specifies the interval of the timer. -// The optional parameter specifies the wheels count of the timer, -// which is defaultWheelLevel in default. -func New(slot int, interval time.Duration, level ...int) *Timer { - t := doNewWithoutAutoStart(slot, interval, level...) - t.wheels[0].start() - return t -} - -func doNewWithoutAutoStart(slot int, interval time.Duration, level ...int) *Timer { - if slot <= 0 { - panic(fmt.Sprintf("invalid slot number: %d", slot)) - } - length := defaultWheelLevel - if len(level) > 0 { - length = level[0] - } +func New(options ...TimerOptions) *Timer { t := &Timer{ - status: gtype.NewInt(StatusRunning), - wheels: make([]*wheel, length), - length: length, - number: slot, - intervalMs: interval.Nanoseconds() / 1e6, - nowFunc: func() time.Time { - return time.Now() - }, + queue: newPriorityQueue(), + status: gtype.NewInt(StatusRunning), + ticks: gtype.NewInt64(), } - for i := 0; i < length; i++ { - if i > 0 { - n := time.Duration(t.wheels[i-1].totalMs) * time.Millisecond - if n <= 0 { - panic(fmt.Sprintf(`inteval is too large with level: %dms x %d`, interval, length)) - } - w := t.newWheel(i, slot, n) - t.wheels[i] = w - t.wheels[i-1].addEntry(n, w.proceed, false, defaultTimes, StatusReady) - if i == length-1 { - t.wheels[i].addEntry(n, w.proceed, false, defaultTimes, StatusReady) - } - } else { - w := t.newWheel(i, slot, interval) - t.wheels[i] = w - } + if len(options) > 0 { + t.options = options[0] + } else { + t.options = DefaultOptions() } + go t.loop() return t } -// newWheel creates and returns a single wheel. -func (t *Timer) newWheel(level int, slot int, interval time.Duration) *wheel { - w := &wheel{ - timer: t, - level: level, - slots: make([]*glist.List, slot), - number: int64(slot), - ticks: gtype.NewInt64(), - totalMs: int64(slot) * interval.Nanoseconds() / 1e6, - createMs: time.Now().UnixNano() / 1e6, - intervalMs: interval.Nanoseconds() / 1e6, - } - for i := int64(0); i < w.number; i++ { - w.slots[i] = glist.New(true) - } - return w -} - // Add adds a timing job to the timer, which runs in interval of . -func (t *Timer) Add(interval time.Duration, job JobFunc) *Entry { - return t.doAddEntry(interval, job, false, defaultTimes, StatusReady) +func (t *Timer) Add(interval time.Duration, job JobFunc) *Job { + return t.createJob(interval, job, false, defaultTimes, StatusReady) } -// AddEntry adds a timing job to the timer with detailed parameters. +// AddJob adds a timing job to the timer with detailed parameters. // // The parameter specifies the running interval of the job. // @@ -118,23 +42,23 @@ func (t *Timer) Add(interval time.Duration, job JobFunc) *Entry { // exits if its run times exceeds the . // // The parameter specifies the job status when it's firstly added to the timer. -func (t *Timer) AddEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry { - return t.doAddEntry(interval, job, singleton, times, status) +func (t *Timer) AddJob(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Job { + return t.createJob(interval, job, singleton, times, status) } // AddSingleton is a convenience function for add singleton mode job. -func (t *Timer) AddSingleton(interval time.Duration, job JobFunc) *Entry { - return t.doAddEntry(interval, job, true, defaultTimes, StatusReady) +func (t *Timer) AddSingleton(interval time.Duration, job JobFunc) *Job { + return t.createJob(interval, job, true, defaultTimes, StatusReady) } // AddOnce is a convenience function for adding a job which only runs once and then exits. -func (t *Timer) AddOnce(interval time.Duration, job JobFunc) *Entry { - return t.doAddEntry(interval, job, true, 1, StatusReady) +func (t *Timer) AddOnce(interval time.Duration, job JobFunc) *Job { + return t.createJob(interval, job, true, 1, StatusReady) } // AddTimes is a convenience function for adding a job which is limited running times. -func (t *Timer) AddTimes(interval time.Duration, times int, job JobFunc) *Entry { - return t.doAddEntry(interval, job, true, times, StatusReady) +func (t *Timer) AddTimes(interval time.Duration, times int, job JobFunc) *Job { + return t.createJob(interval, job, true, times, StatusReady) } // DelayAdd adds a timing job after delay of duration. @@ -145,11 +69,11 @@ func (t *Timer) DelayAdd(delay time.Duration, interval time.Duration, job JobFun }) } -// DelayAddEntry adds a timing job after delay of duration. -// Also see AddEntry. -func (t *Timer) DelayAddEntry(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) { +// DelayAddJob adds a timing job after delay of duration. +// Also see AddJob. +func (t *Timer) DelayAddJob(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) { t.AddOnce(delay, func() { - t.AddEntry(interval, job, singleton, times, status) + t.AddJob(interval, job, singleton, times, status) }) } @@ -192,77 +116,29 @@ func (t *Timer) Close() { t.status.Set(StatusClosed) } -// doAddEntry adds a timing job to timer for internal usage. -func (t *Timer) doAddEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry { - return t.wheels[t.getLevelByIntervalMs(interval.Nanoseconds()/1e6)].addEntry(interval, job, singleton, times, status) -} - -// doAddEntryByParent adds a timing job to timer with parent entry for internal usage. -func (t *Timer) doAddEntryByParent(rollOn bool, nowMs, interval int64, parent *Entry) *Entry { - return t.wheels[t.getLevelByIntervalMs(interval)].addEntryByParent(rollOn, nowMs, interval, parent) -} - -// getLevelByIntervalMs calculates and returns the level of timer wheel with given milliseconds. -func (t *Timer) getLevelByIntervalMs(intervalMs int64) int { - pos, cmp := t.binSearchIndex(intervalMs) - switch cmp { - // If equals to the last comparison value, do not add it directly to this wheel, - // but loop and continue comparison from the index to the first level, - // and add it to the proper level wheel. - case 0: - fallthrough - // If lesser than the last comparison value, - // loop and continue comparison from the index to the first level, - // and add it to the proper level wheel. - case -1: - i := pos - for ; i > 0; i-- { - if intervalMs > t.wheels[i].intervalMs && intervalMs <= t.wheels[i].totalMs { - return i - } - } - return i - - // If greater than the last comparison value, - // loop and continue comparison from the index to the last level, - // and add it to the proper level wheel. - case 1: - i := pos - for ; i < t.length-1; i++ { - if intervalMs > t.wheels[i].intervalMs && intervalMs <= t.wheels[i].totalMs { - return i - } - } - return i +// createJob creates and adds a timing job to the timer. +func (t *Timer) createJob(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Job { + if times <= 0 { + times = defaultTimes } - return 0 -} - -// binSearchIndex uses binary search algorithm for finding the possible level of the wheel -// for the interval value. -func (t *Timer) binSearchIndex(n int64) (index int, result int) { - min := 0 - max := t.length - 1 - mid := 0 - cmp := -2 - for min <= max { - mid = min + int((max-min)/2) - switch { - case t.wheels[mid].intervalMs == n: - cmp = 0 - case t.wheels[mid].intervalMs > n: - cmp = -1 - case t.wheels[mid].intervalMs < n: - cmp = 1 - } - switch cmp { - case -1: - max = mid - 1 - case 1: - min = mid + 1 - case 0: - return mid, cmp - } + var ( + intervalTicksOfJob = int64(interval / t.options.Interval) + ) + if intervalTicksOfJob == 0 { + // If the given interval is lesser than the one of the wheel, + // then sets it to one tick, which means it will be run in one interval. + intervalTicksOfJob = 1 } - return mid, cmp + nextTicks := t.ticks.Val() + intervalTicksOfJob + j := &Job{ + job: job, + timer: t, + ticks: intervalTicksOfJob, + times: gtype.NewInt(times), + status: gtype.NewInt(status), + singleton: gtype.NewBool(singleton), + nextTicks: gtype.NewInt64(nextTicks), + } + t.queue.Push(j, nextTicks) + return j } diff --git a/os/gtimer/gtimer_timer_loop.go b/os/gtimer/gtimer_timer_loop.go new file mode 100644 index 000000000..d2e9909d7 --- /dev/null +++ b/os/gtimer/gtimer_timer_loop.go @@ -0,0 +1,68 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtimer + +import "time" + +// loop starts the ticker using a standalone goroutine. +func (t *Timer) loop() { + go func() { + var ( + currentTimerTicks int64 + timerIntervalTicker = time.NewTicker(t.options.Interval) + ) + defer timerIntervalTicker.Stop() + for { + select { + case <-timerIntervalTicker.C: + // Check the timer status. + switch t.status.Val() { + case StatusRunning: + // Timer proceeding. + currentTimerTicks = t.ticks.Add(1) + if currentTimerTicks >= t.queue.LatestPriority() { + t.proceed(currentTimerTicks) + } + + case StatusStopped: + // Do nothing. + + case StatusClosed: + // Timer exits. + return + } + } + } + }() +} + +// proceed proceeds the timer job checking and running logic. +func (t *Timer) proceed(currentTimerTicks int64) { + var ( + value interface{} + ) + for { + value = t.queue.Pop() + if value == nil { + break + } + job := value.(*Job) + // It checks if it meets the ticks requirement. + if jobNextTicks := job.nextTicks.Val(); currentTimerTicks < jobNextTicks { + // It push the job back if current ticks does not meet its running ticks requirement. + t.queue.Push(job, job.nextTicks.Val()) + break + } + // It checks the job running requirements and then does asynchronous running. + job.doCheckAndRunByTicks(currentTimerTicks) + // Status check: push back or ignore it. + if job.Status() != StatusClosed { + // It pushes the job back to queue for next running. + t.queue.Push(job, job.nextTicks.Val()) + } + } +} diff --git a/os/gtimer/gtimer_z_bench_test.go b/os/gtimer/gtimer_z_bench_test.go index 1f5eea33c..66ab4e0d6 100644 --- a/os/gtimer/gtimer_z_bench_test.go +++ b/os/gtimer/gtimer_z_bench_test.go @@ -14,7 +14,7 @@ import ( ) var ( - timer = gtimer.New(5, 30*time.Millisecond) + timer = gtimer.New() ) func Benchmark_Add(b *testing.B) { diff --git a/os/gtimer/gtimer_z_unit_api_test.go b/os/gtimer/gtimer_z_unit_api_test.go index debc25d9c..60fdb0837 100644 --- a/os/gtimer/gtimer_z_unit_api_test.go +++ b/os/gtimer/gtimer_z_unit_api_test.go @@ -39,10 +39,10 @@ func TestSetInterval(t *testing.T) { }) } -func TestAddEntry(t *testing.T) { +func TestAddJob(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := garray.New(true) - gtimer.AddEntry(200*time.Millisecond, func() { + gtimer.AddJob(200*time.Millisecond, func() { array.Append(1) }, false, 2, gtimer.StatusReady) time.Sleep(1100 * time.Millisecond) @@ -86,10 +86,10 @@ func TestDelayAdd(t *testing.T) { }) } -func TestDelayAddEntry(t *testing.T) { +func TestDelayAddJob(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := garray.New(true) - gtimer.DelayAddEntry(200*time.Millisecond, 200*time.Millisecond, func() { + gtimer.DelayAddJob(200*time.Millisecond, 200*time.Millisecond, func() { array.Append(1) }, false, 2, gtimer.StatusReady) time.Sleep(300 * time.Millisecond) diff --git a/os/gtimer/gtimer_z_unit_entry_test.go b/os/gtimer/gtimer_z_unit_entry_test.go index ba0d2d421..a542111b7 100644 --- a/os/gtimer/gtimer_z_unit_entry_test.go +++ b/os/gtimer/gtimer_z_unit_entry_test.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Entry Operations +// Job Operations package gtimer_test @@ -17,40 +17,40 @@ import ( "github.com/gogf/gf/test/gtest" ) -func TestEntry_Start_Stop_Close(t *testing.T) { +func TestJob_Start_Stop_Close(t *testing.T) { gtest.C(t, func(t *gtest.T) { timer := New() array := garray.New(true) - entry := timer.Add(200*time.Millisecond, func() { + job := timer.Add(200*time.Millisecond, func() { array.Append(1) }) time.Sleep(250 * time.Millisecond) t.Assert(array.Len(), 1) - entry.Stop() + job.Stop() time.Sleep(250 * time.Millisecond) t.Assert(array.Len(), 1) - entry.Start() + job.Start() time.Sleep(250 * time.Millisecond) t.Assert(array.Len(), 2) - entry.Close() + job.Close() time.Sleep(250 * time.Millisecond) t.Assert(array.Len(), 2) - t.Assert(entry.Status(), gtimer.StatusClosed) + t.Assert(job.Status(), gtimer.StatusClosed) }) } -func TestEntry_Singleton(t *testing.T) { +func TestJob_Singleton(t *testing.T) { gtest.C(t, func(t *gtest.T) { timer := New() array := garray.New(true) - entry := timer.Add(200*time.Millisecond, func() { + job := timer.Add(200*time.Millisecond, func() { array.Append(1) time.Sleep(10 * time.Second) }) - t.Assert(entry.IsSingleton(), false) - entry.SetSingleton(true) - t.Assert(entry.IsSingleton(), true) + t.Assert(job.IsSingleton(), false) + job.SetSingleton(true) + t.Assert(job.IsSingleton(), true) time.Sleep(250 * time.Millisecond) t.Assert(array.Len(), 1) @@ -59,27 +59,28 @@ func TestEntry_Singleton(t *testing.T) { }) } -func TestEntry_SetTimes(t *testing.T) { +func TestJob_SetTimes(t *testing.T) { gtest.C(t, func(t *gtest.T) { timer := New() array := garray.New(true) - entry := timer.Add(200*time.Millisecond, func() { + job := timer.Add(200*time.Millisecond, func() { array.Append(1) }) - entry.SetTimes(2) + job.SetTimes(2) + //job.IsSingleton() time.Sleep(1200 * time.Millisecond) t.Assert(array.Len(), 2) }) } -func TestEntry_Run(t *testing.T) { +func TestJob_Run(t *testing.T) { gtest.C(t, func(t *gtest.T) { timer := New() array := garray.New(true) - entry := timer.Add(1000*time.Millisecond, func() { + job := timer.Add(1000*time.Millisecond, func() { array.Append(1) }) - entry.Run() + job.Job()() t.Assert(array.Len(), 1) }) } diff --git a/os/gtimer/gtimer_z_unit_timer_internal_test.go b/os/gtimer/gtimer_z_unit_timer_internal_test.go index 5d144fb9a..9882d21c3 100644 --- a/os/gtimer/gtimer_z_unit_timer_internal_test.go +++ b/os/gtimer/gtimer_z_unit_timer_internal_test.go @@ -8,7 +8,6 @@ package gtimer import ( "github.com/gogf/gf/container/garray" - "github.com/gogf/gf/container/gtype" "github.com/gogf/gf/test/gtest" "testing" "time" @@ -16,35 +15,34 @@ import ( func TestTimer_Proceed(t *testing.T) { gtest.C(t, func(t *gtest.T) { - index := gtype.NewInt() array := garray.New(true) - timer := doNewWithoutAutoStart(10, 60*time.Millisecond, 6) - timer.nowFunc = func() time.Time { - return time.Now().Add(time.Duration(index.Add(1)) * time.Millisecond * 60) - } - timer.AddOnce(2*time.Second, func() { + timer := New(TimerOptions{ + Interval: time.Hour, + }) + timer.Add(10000*time.Hour, func() { array.Append(1) }) - timer.AddOnce(1*time.Minute, func() { - array.Append(2) + timer.proceed(10001) + time.Sleep(10 * time.Millisecond) + t.Assert(array.Len(), 1) + timer.proceed(20001) + time.Sleep(10 * time.Millisecond) + t.Assert(array.Len(), 2) + }) + gtest.C(t, func(t *gtest.T) { + array := garray.New(true) + timer := New(TimerOptions{ + Interval: time.Millisecond * 100, }) - timer.AddOnce(5*time.Minute, func() { - array.Append(3) + timer.Add(10000*time.Hour, func() { + array.Append(1) }) - timer.AddOnce(1*time.Hour, func() { - array.Append(4) - }) - timer.AddOnce(100*time.Minute, func() { - array.Append(5) - }) - timer.AddOnce(2*time.Hour, func() { - array.Append(6) - }) - for i := 0; i < 500000; i++ { - timer.wheels[0].proceed() - time.Sleep(10 * time.Microsecond) - } - time.Sleep(time.Second) - t.Assert(array.Slice(), []int{1, 2, 3, 4, 5, 6}) + ticks := int64((10000 * time.Hour) / (time.Millisecond * 100)) + timer.proceed(ticks + 1) + time.Sleep(10 * time.Millisecond) + t.Assert(array.Len(), 1) + timer.proceed(2*ticks + 1) + time.Sleep(10 * time.Millisecond) + t.Assert(array.Len(), 2) }) } diff --git a/os/gtimer/gtimer_z_unit_timer_test.go b/os/gtimer/gtimer_z_unit_timer_test.go index d6da5a8b4..f126d79cb 100644 --- a/os/gtimer/gtimer_z_unit_timer_test.go +++ b/os/gtimer/gtimer_z_unit_timer_test.go @@ -9,7 +9,6 @@ package gtimer_test import ( - "github.com/gogf/gf/os/glog" "testing" "time" @@ -19,7 +18,7 @@ import ( ) func New() *gtimer.Timer { - return gtimer.New(10, 10*time.Millisecond) + return gtimer.New() } func TestTimer_Add_Close(t *testing.T) { @@ -28,15 +27,15 @@ func TestTimer_Add_Close(t *testing.T) { array := garray.New(true) //fmt.Println("start", time.Now()) timer.Add(200*time.Millisecond, func() { - //fmt.Println("entry1", time.Now()) + //fmt.Println("job1", time.Now()) array.Append(1) }) timer.Add(200*time.Millisecond, func() { - //fmt.Println("entry2", time.Now()) + //fmt.Println("job2", time.Now()) array.Append(1) }) timer.Add(400*time.Millisecond, func() { - //fmt.Println("entry3", time.Now()) + //fmt.Println("job3", time.Now()) array.Append(1) }) time.Sleep(250 * time.Millisecond) @@ -74,21 +73,21 @@ func TestTimer_Start_Stop_Close(t *testing.T) { }) } -func TestTimer_Reset(t *testing.T) { +func TestJob_Reset(t *testing.T) { gtest.C(t, func(t *gtest.T) { timer := New() array := garray.New(true) - glog.Printf("start time:%d", time.Now().Unix()) - singleton := timer.AddSingleton(2*time.Second, func() { - timestamp := time.Now().Unix() - glog.Println(timestamp) - array.Append(timestamp) + job := timer.AddSingleton(500*time.Millisecond, func() { + array.Append(1) }) - time.Sleep(5 * time.Second) - glog.Printf("reset time:%d", time.Now().Unix()) - singleton.Reset() - time.Sleep(10 * time.Second) - t.Assert(array.Len(), 6) + time.Sleep(300 * time.Millisecond) + job.Reset() + time.Sleep(300 * time.Millisecond) + job.Reset() + time.Sleep(300 * time.Millisecond) + job.Reset() + time.Sleep(600 * time.Millisecond) + t.Assert(array.Len(), 1) }) } @@ -156,11 +155,11 @@ func TestTimer_DelayAdd(t *testing.T) { }) } -func TestTimer_DelayAddEntry(t *testing.T) { +func TestTimer_DelayAddJob(t *testing.T) { gtest.C(t, func(t *gtest.T) { timer := New() array := garray.New(true) - timer.DelayAddEntry(200*time.Millisecond, 200*time.Millisecond, func() { + timer.DelayAddJob(200*time.Millisecond, 200*time.Millisecond, func() { array.Append(1) }, false, 100, gtimer.StatusReady) time.Sleep(250 * time.Millisecond) @@ -227,7 +226,9 @@ func TestTimer_DelayAddTimes(t *testing.T) { func TestTimer_AddLessThanInterval(t *testing.T) { gtest.C(t, func(t *gtest.T) { - timer := gtimer.New(10, 100*time.Millisecond) + timer := gtimer.New(gtimer.TimerOptions{ + Interval: 100 * time.Millisecond, + }) array := garray.New(true) timer.Add(20*time.Millisecond, func() { array.Append(1) @@ -243,7 +244,7 @@ func TestTimer_AddLessThanInterval(t *testing.T) { }) } -func TestTimer_AddLeveledEntry1(t *testing.T) { +func TestTimer_AddLeveledJob1(t *testing.T) { gtest.C(t, func(t *gtest.T) { timer := New() array := garray.New(true) From 1b1355a595b84c8b9f3dac6a71604957d0b1dc33 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 15 May 2021 18:27:46 +0800 Subject: [PATCH 256/492] improve package gtimer --- os/gtimer/gtimer_queue.go | 29 +++++++++++++++++++++-------- os/gtimer/gtimer_z_bench_test.go | 12 ++++++++---- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/os/gtimer/gtimer_queue.go b/os/gtimer/gtimer_queue.go index b805d7d22..9fb6af73d 100644 --- a/os/gtimer/gtimer_queue.go +++ b/os/gtimer/gtimer_queue.go @@ -63,7 +63,11 @@ func (q *priorityQueue) LatestPriority() int64 { // The lesser the `priority` value the higher priority of the `value`. func (q *priorityQueue) Push(value interface{}, priority int64) { q.mu.Lock() - defer q.mu.Unlock() + heap.Push(q.heap, priorityQueueItem{ + value: value, + priority: priority, + }) + q.mu.Unlock() // Update the minimum priority using atomic operation. for { latestPriority := q.latestPriority.Val() @@ -74,18 +78,27 @@ func (q *priorityQueue) Push(value interface{}, priority int64) { break } } - heap.Push(q.heap, priorityQueueItem{ - value: value, - priority: priority, - }) } // Pop retrieves, removes and returns the most high priority value from the queue. func (q *priorityQueue) Pop() interface{} { q.mu.Lock() - defer q.mu.Unlock() - if item := heap.Pop(q.heap); item != nil { - return item.(priorityQueueItem).value + if v := heap.Pop(q.heap); v != nil { + item := v.(priorityQueueItem) + q.mu.Unlock() + // Update the minimum priority using atomic operation. + for { + latestPriority := q.latestPriority.Val() + if item.priority >= latestPriority { + break + } + if q.latestPriority.Cas(latestPriority, item.priority) { + break + } + } + return item.value + } else { + q.mu.Unlock() } return nil } diff --git a/os/gtimer/gtimer_z_bench_test.go b/os/gtimer/gtimer_z_bench_test.go index 66ab4e0d6..139bf1c4a 100644 --- a/os/gtimer/gtimer_z_bench_test.go +++ b/os/gtimer/gtimer_z_bench_test.go @@ -4,17 +4,15 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package gtimer_test +package gtimer import ( "testing" "time" - - "github.com/gogf/gf/os/gtimer" ) var ( - timer = gtimer.New() + timer = New() ) func Benchmark_Add(b *testing.B) { @@ -25,6 +23,12 @@ func Benchmark_Add(b *testing.B) { } } +func Benchmark_PriorityQueue_Pop(b *testing.B) { + for i := 0; i < b.N; i++ { + timer.queue.Pop() + } +} + func Benchmark_StartStop(b *testing.B) { for i := 0; i < b.N; i++ { timer.Start() From facb2949c3a4f3117a94f943dc563060f8fa7871 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 15 May 2021 18:31:46 +0800 Subject: [PATCH 257/492] improve unit testing cases for package gtimer --- ...z_unit_entry_test.go => gtimer_z_unit_job_test.go} | 0 os/gtimer/gtimer_z_unit_timer_test.go | 11 +++++------ 2 files changed, 5 insertions(+), 6 deletions(-) rename os/gtimer/{gtimer_z_unit_entry_test.go => gtimer_z_unit_job_test.go} (100%) diff --git a/os/gtimer/gtimer_z_unit_entry_test.go b/os/gtimer/gtimer_z_unit_job_test.go similarity index 100% rename from os/gtimer/gtimer_z_unit_entry_test.go rename to os/gtimer/gtimer_z_unit_job_test.go diff --git a/os/gtimer/gtimer_z_unit_timer_test.go b/os/gtimer/gtimer_z_unit_timer_test.go index f126d79cb..7be8eeef5 100644 --- a/os/gtimer/gtimer_z_unit_timer_test.go +++ b/os/gtimer/gtimer_z_unit_timer_test.go @@ -54,21 +54,20 @@ func TestTimer_Start_Stop_Close(t *testing.T) { gtest.C(t, func(t *gtest.T) { timer := New() array := garray.New(true) - timer.Add(200*time.Millisecond, func() { - //glog.Println("add...") + timer.Add(1000*time.Millisecond, func() { array.Append(1) }) t.Assert(array.Len(), 0) - time.Sleep(300 * time.Millisecond) + time.Sleep(1200 * time.Millisecond) t.Assert(array.Len(), 1) timer.Stop() - time.Sleep(1000 * time.Millisecond) + time.Sleep(1200 * time.Millisecond) t.Assert(array.Len(), 1) timer.Start() - time.Sleep(200 * time.Millisecond) + time.Sleep(1200 * time.Millisecond) t.Assert(array.Len(), 2) timer.Close() - time.Sleep(1000 * time.Millisecond) + time.Sleep(1200 * time.Millisecond) t.Assert(array.Len(), 2) }) } From 7003c284d03433d295c7a39c926bcb3faa80b53f Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 15 May 2021 22:38:07 +0800 Subject: [PATCH 258/492] replace json.Unmarshal with json.UnmarshalUseNumber for packages --- .example/container/garray/json_unmarshal.go | 2 +- .example/container/glist/json_unmarshal.go | 2 +- .../container/gmap/gmap_json_unmarshal.go | 2 +- .example/container/gset/json_unmarshal.go | 2 +- .example/container/gtype/json_unmarshal.go | 2 +- .example/container/gvar/json_unmarshal.go | 2 +- .example/encoding/gjson/issue#IZXU2.go | 2 +- .../server/session/redis/redis_bigint.go | 36 ++++++++++ .../gtcp/pkg_operations/common/funcs/funcs.go | 2 +- .../monitor/gtcp_monitor_server.go | 2 +- container/garray/garray_normal_any.go | 4 +- container/garray/garray_normal_int.go | 4 +- container/garray/garray_normal_str.go | 4 +- container/garray/garray_sorted_any.go | 4 +- container/garray/garray_sorted_int.go | 4 +- container/garray/garray_sorted_str.go | 4 +- .../garray_z_unit_normal_any_array_test.go | 12 ++-- .../garray_z_unit_normal_int_array_test.go | 12 ++-- .../garray_z_unit_normal_str_array_test.go | 12 ++-- .../garray_z_unit_sorted_any_array_test.go | 12 ++-- .../garray_z_unit_sorted_int_array_test.go | 12 ++-- .../garray_z_unit_sorted_str_array_test.go | 12 ++-- container/glist/glist.go | 4 +- container/glist/glist_z_unit_test.go | 4 +- container/gmap/gmap_hash_any_any_map.go | 2 +- container/gmap/gmap_hash_int_any_map.go | 4 +- container/gmap/gmap_hash_int_int_map.go | 4 +- container/gmap/gmap_hash_int_str_map.go | 4 +- container/gmap/gmap_hash_str_any_map.go | 2 +- container/gmap/gmap_hash_str_int_map.go | 4 +- container/gmap/gmap_hash_str_str_map.go | 2 +- container/gmap/gmap_list_map.go | 2 +- container/gmap/gmap_z_unit_any_any_test.go | 4 +- container/gmap/gmap_z_unit_int_any_test.go | 2 +- container/gmap/gmap_z_unit_int_int_test.go | 2 +- container/gmap/gmap_z_unit_int_str_test.go | 2 +- container/gmap/gmap_z_unit_list_map_test.go | 4 +- container/gmap/gmap_z_unit_str_any_test.go | 4 +- container/gmap/gmap_z_unit_str_int_test.go | 4 +- container/gmap/gmap_z_unit_str_str_test.go | 4 +- container/gmap/gmap_z_unit_tree_map_test.go | 4 +- container/gset/gset_any_set.go | 4 +- container/gset/gset_int_set.go | 4 +- container/gset/gset_str_set.go | 4 +- container/gset/gset_z_unit_any_test.go | 4 +- container/gset/gset_z_unit_int_test.go | 4 +- container/gset/gset_z_unit_str_test.go | 4 +- container/gtree/gtree_redblacktree.go | 2 +- container/gtype/interface.go | 2 +- container/gtype/z_unit_bool_test.go | 12 ++-- container/gtype/z_unit_byte_test.go | 2 +- container/gtype/z_unit_bytes_test.go | 2 +- container/gtype/z_unit_float32_test.go | 2 +- container/gtype/z_unit_float64_test.go | 2 +- container/gtype/z_unit_int32_test.go | 2 +- container/gtype/z_unit_int64_test.go | 2 +- container/gtype/z_unit_int_test.go | 2 +- container/gtype/z_unit_interface_test.go | 2 +- container/gtype/z_unit_string_test.go | 2 +- container/gtype/z_unit_uint32_test.go | 2 +- container/gtype/z_unit_uint64_test.go | 2 +- container/gtype/z_unit_uint_test.go | 2 +- container/gvar/gvar.go | 2 +- container/gvar/gvar_z_unit_json_test.go | 4 +- database/gdb/gdb_model_select.go | 2 +- encoding/gjson/gjson.go | 6 +- encoding/gjson/gjson_api_new_load.go | 65 ++++++++++--------- encoding/gjson/gjson_z_unit_basic_test.go | 2 +- .../gjson/gjson_z_unit_implements_test.go | 2 +- encoding/gjson/gjson_z_unit_new_test.go | 4 +- internal/json/json.go | 8 +++ net/ghttp/ghttp_request_param.go | 2 +- net/ghttp/ghttp_unit_request_json_test.go | 8 +-- net/gtrace/gtrace_carrier.go | 2 +- os/gbuild/gbuild.go | 2 +- os/gproc/gproc_comm_receive.go | 2 +- os/gproc/gproc_comm_send.go | 2 +- os/gsession/gsession_storage_file.go | 2 +- os/gsession/gsession_storage_redis.go | 2 +- os/gtime/gtime_z_unit_json_test.go | 2 +- util/gconv/gconv_map.go | 4 +- util/gconv/gconv_maps.go | 8 +-- util/gconv/gconv_maptomap.go | 8 +-- util/gconv/gconv_maptomaps.go | 8 +-- util/gconv/gconv_struct.go | 8 +-- util/gconv/gconv_structs.go | 8 +-- util/gconv/gconv_z_unit_struct_test.go | 2 +- 87 files changed, 243 insertions(+), 198 deletions(-) create mode 100644 .example/net/ghttp/server/session/redis/redis_bigint.go diff --git a/.example/container/garray/json_unmarshal.go b/.example/container/garray/json_unmarshal.go index 07e81adb0..99fb7adec 100644 --- a/.example/container/garray/json_unmarshal.go +++ b/.example/container/garray/json_unmarshal.go @@ -14,6 +14,6 @@ func main() { Scores *garray.IntArray } s := Student{} - json.Unmarshal(b, &s) + json.UnmarshalUseNumber(b, &s) fmt.Println(s) } diff --git a/.example/container/glist/json_unmarshal.go b/.example/container/glist/json_unmarshal.go index 313dfe570..32e654bcb 100644 --- a/.example/container/glist/json_unmarshal.go +++ b/.example/container/glist/json_unmarshal.go @@ -14,6 +14,6 @@ func main() { Scores *glist.List } s := Student{} - json.Unmarshal(b, &s) + json.UnmarshalUseNumber(b, &s) fmt.Println(s) } diff --git a/.example/container/gmap/gmap_json_unmarshal.go b/.example/container/gmap/gmap_json_unmarshal.go index af3c134a8..01bdc4f36 100644 --- a/.example/container/gmap/gmap_json_unmarshal.go +++ b/.example/container/gmap/gmap_json_unmarshal.go @@ -9,6 +9,6 @@ import ( func main() { m := gmap.Map{} s := []byte(`{"name":"john","score":100}`) - json.Unmarshal(s, &m) + json.UnmarshalUseNumber(s, &m) fmt.Println(m.Map()) } diff --git a/.example/container/gset/json_unmarshal.go b/.example/container/gset/json_unmarshal.go index 4e90ce823..f85afac05 100644 --- a/.example/container/gset/json_unmarshal.go +++ b/.example/container/gset/json_unmarshal.go @@ -14,6 +14,6 @@ func main() { Scores *gset.IntSet } s := Student{} - json.Unmarshal(b, &s) + json.UnmarshalUseNumber(b, &s) fmt.Println(s) } diff --git a/.example/container/gtype/json_unmarshal.go b/.example/container/gtype/json_unmarshal.go index 6b72cab08..7a17a1971 100644 --- a/.example/container/gtype/json_unmarshal.go +++ b/.example/container/gtype/json_unmarshal.go @@ -14,6 +14,6 @@ func main() { Scores *gtype.Interface } s := Student{} - json.Unmarshal(b, &s) + json.UnmarshalUseNumber(b, &s) fmt.Println(s) } diff --git a/.example/container/gvar/json_unmarshal.go b/.example/container/gvar/json_unmarshal.go index 31aa9843a..a4a9deba3 100644 --- a/.example/container/gvar/json_unmarshal.go +++ b/.example/container/gvar/json_unmarshal.go @@ -14,6 +14,6 @@ func main() { Scores *g.Var } s := Student{} - json.Unmarshal(b, &s) + json.UnmarshalUseNumber(b, &s) fmt.Println(s) } diff --git a/.example/encoding/gjson/issue#IZXU2.go b/.example/encoding/gjson/issue#IZXU2.go index e96f428d3..f56152fa6 100644 --- a/.example/encoding/gjson/issue#IZXU2.go +++ b/.example/encoding/gjson/issue#IZXU2.go @@ -140,7 +140,7 @@ var data = `{ func main() { struct1 := new(XinYanModel) - err := json.Unmarshal([]byte(data), struct1) + err := json.UnmarshalUseNumber([]byte(data), struct1) fmt.Println(err) fmt.Println(struct1) diff --git a/.example/net/ghttp/server/session/redis/redis_bigint.go b/.example/net/ghttp/server/session/redis/redis_bigint.go new file mode 100644 index 000000000..b8a5b3460 --- /dev/null +++ b/.example/net/ghttp/server/session/redis/redis_bigint.go @@ -0,0 +1,36 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/os/gsession" +) + +func main() { + type User struct { + Id int64 + Name string + } + s := g.Server() + s.SetSessionStorage(gsession.NewStorageRedis(g.Redis())) + s.Group("/", func(group *ghttp.RouterGroup) { + group.GET("/set", func(r *ghttp.Request) { + user := &User{ + Id: 1265476890672672808, + Name: "john", + } + if err := r.Session.Set("user", user); err != nil { + panic(err) + } + r.Response.Write("ok") + }) + group.GET("/get", func(r *ghttp.Request) { + r.Response.WriteJson(r.Session.Get("user")) + }) + group.GET("/clear", func(r *ghttp.Request) { + r.Session.Clear() + }) + }) + s.SetPort(8199) + s.Run() +} diff --git a/.example/net/gtcp/pkg_operations/common/funcs/funcs.go b/.example/net/gtcp/pkg_operations/common/funcs/funcs.go index 368874c1e..4d8c33d10 100644 --- a/.example/net/gtcp/pkg_operations/common/funcs/funcs.go +++ b/.example/net/gtcp/pkg_operations/common/funcs/funcs.go @@ -30,7 +30,7 @@ func RecvPkg(conn *gtcp.Conn) (msg *types.Msg, err error) { return nil, err } else { msg = &types.Msg{} - err = json.Unmarshal(data, msg) + err = json.UnmarshalUseNumber(data, msg) if err != nil { return nil, fmt.Errorf("invalid package structure: %s", err.Error()) } diff --git a/.example/net/gtcp/pkg_operations/monitor/gtcp_monitor_server.go b/.example/net/gtcp/pkg_operations/monitor/gtcp_monitor_server.go index b9758f893..b0820320c 100644 --- a/.example/net/gtcp/pkg_operations/monitor/gtcp_monitor_server.go +++ b/.example/net/gtcp/pkg_operations/monitor/gtcp_monitor_server.go @@ -21,7 +21,7 @@ func main() { break } info := &types.NodeInfo{} - if err := json.Unmarshal(data, info); err != nil { + if err := json.UnmarshalUseNumber(data, info); err != nil { glog.Errorf("invalid package structure: %s", err.Error()) } else { glog.Println(info) diff --git a/container/garray/garray_normal_any.go b/container/garray/garray_normal_any.go index 3e0324a10..616a59062 100644 --- a/container/garray/garray_normal_any.go +++ b/container/garray/garray_normal_any.go @@ -736,7 +736,7 @@ func (a *Array) UnmarshalJSON(b []byte) error { } a.mu.Lock() defer a.mu.Unlock() - if err := json.Unmarshal(b, &a.array); err != nil { + if err := json.UnmarshalUseNumber(b, &a.array); err != nil { return err } return nil @@ -748,7 +748,7 @@ func (a *Array) UnmarshalValue(value interface{}) error { defer a.mu.Unlock() switch value.(type) { case string, []byte: - return json.Unmarshal(gconv.Bytes(value), &a.array) + return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array) default: a.array = gconv.SliceAny(value) } diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index 10b64529d..10dbe16fe 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -735,7 +735,7 @@ func (a *IntArray) UnmarshalJSON(b []byte) error { } a.mu.Lock() defer a.mu.Unlock() - if err := json.Unmarshal(b, &a.array); err != nil { + if err := json.UnmarshalUseNumber(b, &a.array); err != nil { return err } return nil @@ -747,7 +747,7 @@ func (a *IntArray) UnmarshalValue(value interface{}) error { defer a.mu.Unlock() switch value.(type) { case string, []byte: - return json.Unmarshal(gconv.Bytes(value), &a.array) + return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array) default: a.array = gconv.SliceInt(value) } diff --git a/container/garray/garray_normal_str.go b/container/garray/garray_normal_str.go index 477156a8a..523a75123 100644 --- a/container/garray/garray_normal_str.go +++ b/container/garray/garray_normal_str.go @@ -750,7 +750,7 @@ func (a *StrArray) UnmarshalJSON(b []byte) error { } a.mu.Lock() defer a.mu.Unlock() - if err := json.Unmarshal(b, &a.array); err != nil { + if err := json.UnmarshalUseNumber(b, &a.array); err != nil { return err } return nil @@ -762,7 +762,7 @@ func (a *StrArray) UnmarshalValue(value interface{}) error { defer a.mu.Unlock() switch value.(type) { case string, []byte: - return json.Unmarshal(gconv.Bytes(value), &a.array) + return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array) default: a.array = gconv.SliceStr(value) } diff --git a/container/garray/garray_sorted_any.go b/container/garray/garray_sorted_any.go index 497ea9e67..91d7fa073 100644 --- a/container/garray/garray_sorted_any.go +++ b/container/garray/garray_sorted_any.go @@ -685,7 +685,7 @@ func (a *SortedArray) UnmarshalJSON(b []byte) error { } a.mu.Lock() defer a.mu.Unlock() - if err := json.Unmarshal(b, &a.array); err != nil { + if err := json.UnmarshalUseNumber(b, &a.array); err != nil { return err } if a.comparator != nil && a.array != nil { @@ -706,7 +706,7 @@ func (a *SortedArray) UnmarshalValue(value interface{}) (err error) { defer a.mu.Unlock() switch value.(type) { case string, []byte: - err = json.Unmarshal(gconv.Bytes(value), &a.array) + err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array) default: a.array = gconv.SliceAny(value) } diff --git a/container/garray/garray_sorted_int.go b/container/garray/garray_sorted_int.go index ab926a63a..6b96b2c3a 100644 --- a/container/garray/garray_sorted_int.go +++ b/container/garray/garray_sorted_int.go @@ -658,7 +658,7 @@ func (a *SortedIntArray) UnmarshalJSON(b []byte) error { } a.mu.Lock() defer a.mu.Unlock() - if err := json.Unmarshal(b, &a.array); err != nil { + if err := json.UnmarshalUseNumber(b, &a.array); err != nil { return err } if a.array != nil { @@ -676,7 +676,7 @@ func (a *SortedIntArray) UnmarshalValue(value interface{}) (err error) { defer a.mu.Unlock() switch value.(type) { case string, []byte: - err = json.Unmarshal(gconv.Bytes(value), &a.array) + err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array) default: a.array = gconv.SliceInt(value) } diff --git a/container/garray/garray_sorted_str.go b/container/garray/garray_sorted_str.go index 5450f5252..7b8e23b1b 100644 --- a/container/garray/garray_sorted_str.go +++ b/container/garray/garray_sorted_str.go @@ -671,7 +671,7 @@ func (a *SortedStrArray) UnmarshalJSON(b []byte) error { } a.mu.Lock() defer a.mu.Unlock() - if err := json.Unmarshal(b, &a.array); err != nil { + if err := json.UnmarshalUseNumber(b, &a.array); err != nil { return err } if a.array != nil { @@ -689,7 +689,7 @@ func (a *SortedStrArray) UnmarshalValue(value interface{}) (err error) { defer a.mu.Unlock() switch value.(type) { case string, []byte: - err = json.Unmarshal(gconv.Bytes(value), &a.array) + err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array) default: a.array = gconv.SliceStr(value) } diff --git a/container/garray/garray_z_unit_normal_any_array_test.go b/container/garray/garray_z_unit_normal_any_array_test.go index b74fc7656..004e9bfd3 100644 --- a/container/garray/garray_z_unit_normal_any_array_test.go +++ b/container/garray/garray_z_unit_normal_any_array_test.go @@ -570,12 +570,12 @@ func TestArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.New() - err2 = json.Unmarshal(b2, &a2) + err2 = json.UnmarshalUseNumber(b2, &a2) t.Assert(err2, nil) t.Assert(a2.Slice(), s1) var a3 garray.Array - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) }) @@ -589,12 +589,12 @@ func TestArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.New() - err2 = json.Unmarshal(b2, &a2) + err2 = json.UnmarshalUseNumber(b2, &a2) t.Assert(err2, nil) t.Assert(a2.Slice(), s1) var a3 garray.Array - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) }) @@ -612,7 +612,7 @@ func TestArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.Assert(user.Scores, data["Scores"]) @@ -631,7 +631,7 @@ func TestArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.Assert(user.Scores, data["Scores"]) diff --git a/container/garray/garray_z_unit_normal_int_array_test.go b/container/garray/garray_z_unit_normal_int_array_test.go index 3d6b0b27b..ef1e763ef 100644 --- a/container/garray/garray_z_unit_normal_int_array_test.go +++ b/container/garray/garray_z_unit_normal_int_array_test.go @@ -615,11 +615,11 @@ func TestIntArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.NewIntArray() - err1 = json.Unmarshal(b2, &a2) + err1 = json.UnmarshalUseNumber(b2, &a2) t.Assert(a2.Slice(), s1) var a3 garray.IntArray - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) }) @@ -633,11 +633,11 @@ func TestIntArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.NewIntArray() - err1 = json.Unmarshal(b2, &a2) + err1 = json.UnmarshalUseNumber(b2, &a2) t.Assert(a2.Slice(), s1) var a3 garray.IntArray - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) }) @@ -655,7 +655,7 @@ func TestIntArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.Assert(user.Scores, data["Scores"]) @@ -674,7 +674,7 @@ func TestIntArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.Assert(user.Scores, data["Scores"]) diff --git a/container/garray/garray_z_unit_normal_str_array_test.go b/container/garray/garray_z_unit_normal_str_array_test.go index f4a02744c..be165863b 100644 --- a/container/garray/garray_z_unit_normal_str_array_test.go +++ b/container/garray/garray_z_unit_normal_str_array_test.go @@ -614,11 +614,11 @@ func TestStrArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.NewStrArray() - err1 = json.Unmarshal(b2, &a2) + err1 = json.UnmarshalUseNumber(b2, &a2) t.Assert(a2.Slice(), s1) var a3 garray.StrArray - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) }) @@ -632,11 +632,11 @@ func TestStrArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.NewStrArray() - err1 = json.Unmarshal(b2, &a2) + err1 = json.UnmarshalUseNumber(b2, &a2) t.Assert(a2.Slice(), s1) var a3 garray.StrArray - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) }) @@ -654,7 +654,7 @@ func TestStrArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.Assert(user.Scores, data["Scores"]) @@ -673,7 +673,7 @@ func TestStrArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.Assert(user.Scores, data["Scores"]) diff --git a/container/garray/garray_z_unit_sorted_any_array_test.go b/container/garray/garray_z_unit_sorted_any_array_test.go index f3ce498ea..baecec1b5 100644 --- a/container/garray/garray_z_unit_sorted_any_array_test.go +++ b/container/garray/garray_z_unit_sorted_any_array_test.go @@ -656,11 +656,11 @@ func TestSortedArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.NewSortedArray(gutil.ComparatorString) - err1 = json.Unmarshal(b2, &a2) + err1 = json.UnmarshalUseNumber(b2, &a2) t.Assert(a2.Slice(), s2) var a3 garray.SortedArray - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) t.Assert(a3.Interfaces(), s1) @@ -676,11 +676,11 @@ func TestSortedArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.NewSortedArray(gutil.ComparatorString) - err1 = json.Unmarshal(b2, &a2) + err1 = json.UnmarshalUseNumber(b2, &a2) t.Assert(a2.Slice(), s2) var a3 garray.SortedArray - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) t.Assert(a3.Interfaces(), s1) @@ -699,7 +699,7 @@ func TestSortedArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.AssertNE(user.Scores, nil) @@ -735,7 +735,7 @@ func TestSortedArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.AssertNE(user.Scores, nil) diff --git a/container/garray/garray_z_unit_sorted_int_array_test.go b/container/garray/garray_z_unit_sorted_int_array_test.go index c4e2e04b7..56b873f47 100644 --- a/container/garray/garray_z_unit_sorted_int_array_test.go +++ b/container/garray/garray_z_unit_sorted_int_array_test.go @@ -557,11 +557,11 @@ func TestSortedIntArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.NewSortedIntArray() - err1 = json.Unmarshal(b2, &a2) + err1 = json.UnmarshalUseNumber(b2, &a2) t.Assert(a2.Slice(), s2) var a3 garray.SortedIntArray - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) }) @@ -576,11 +576,11 @@ func TestSortedIntArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.NewSortedIntArray() - err1 = json.Unmarshal(b2, &a2) + err1 = json.UnmarshalUseNumber(b2, &a2) t.Assert(a2.Slice(), s2) var a3 garray.SortedIntArray - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) }) @@ -598,7 +598,7 @@ func TestSortedIntArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.Assert(user.Scores, []int{98, 99, 100}) @@ -617,7 +617,7 @@ func TestSortedIntArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.Assert(user.Scores, []int{98, 99, 100}) diff --git a/container/garray/garray_z_unit_sorted_str_array_test.go b/container/garray/garray_z_unit_sorted_str_array_test.go index 5be967e54..bab5c539e 100644 --- a/container/garray/garray_z_unit_sorted_str_array_test.go +++ b/container/garray/garray_z_unit_sorted_str_array_test.go @@ -577,12 +577,12 @@ func TestSortedStrArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.NewSortedStrArray() - err1 = json.Unmarshal(b2, &a2) + err1 = json.UnmarshalUseNumber(b2, &a2) t.Assert(a2.Slice(), s2) t.Assert(a2.Interfaces(), s2) var a3 garray.SortedStrArray - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) t.Assert(a3.Interfaces(), s1) @@ -598,12 +598,12 @@ func TestSortedStrArray_Json(t *testing.T) { t.Assert(err1, err2) a2 := garray.NewSortedStrArray() - err1 = json.Unmarshal(b2, &a2) + err1 = json.UnmarshalUseNumber(b2, &a2) t.Assert(a2.Slice(), s2) t.Assert(a2.Interfaces(), s2) var a3 garray.SortedStrArray - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Slice(), s1) t.Assert(a3.Interfaces(), s1) @@ -622,7 +622,7 @@ func TestSortedStrArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.Assert(user.Scores, []string{"A", "A", "A+"}) @@ -641,7 +641,7 @@ func TestSortedStrArray_Json(t *testing.T) { t.Assert(err, nil) user := new(User) - err = json.Unmarshal(b, user) + err = json.UnmarshalUseNumber(b, user) t.Assert(err, nil) t.Assert(user.Name, data["Name"]) t.Assert(user.Scores, []string{"A", "A", "A+"}) diff --git a/container/glist/glist.go b/container/glist/glist.go index 1a3fd22bb..94319e50e 100644 --- a/container/glist/glist.go +++ b/container/glist/glist.go @@ -519,7 +519,7 @@ func (l *List) UnmarshalJSON(b []byte) error { l.list = list.New() } var array []interface{} - if err := json.Unmarshal(b, &array); err != nil { + if err := json.UnmarshalUseNumber(b, &array); err != nil { return err } l.PushBacks(array) @@ -536,7 +536,7 @@ func (l *List) UnmarshalValue(value interface{}) (err error) { var array []interface{} switch value.(type) { case string, []byte: - err = json.Unmarshal(gconv.Bytes(value), &array) + err = json.UnmarshalUseNumber(gconv.Bytes(value), &array) default: array = gconv.SliceAny(value) } diff --git a/container/glist/glist_z_unit_test.go b/container/glist/glist_z_unit_test.go index d1dd57c3e..5e1da47c8 100644 --- a/container/glist/glist_z_unit_test.go +++ b/container/glist/glist_z_unit_test.go @@ -711,7 +711,7 @@ func TestList_Json(t *testing.T) { b, err := json.Marshal(a) t.Assert(err, nil) - err = json.Unmarshal(b, l) + err = json.UnmarshalUseNumber(b, l) t.Assert(err, nil) t.Assert(l.FrontAll(), a) }) @@ -721,7 +721,7 @@ func TestList_Json(t *testing.T) { b, err := json.Marshal(a) t.Assert(err, nil) - err = json.Unmarshal(b, &l) + err = json.UnmarshalUseNumber(b, &l) t.Assert(err, nil) t.Assert(l.FrontAll(), a) }) diff --git a/container/gmap/gmap_hash_any_any_map.go b/container/gmap/gmap_hash_any_any_map.go index acc10da60..a42cf26ea 100644 --- a/container/gmap/gmap_hash_any_any_map.go +++ b/container/gmap/gmap_hash_any_any_map.go @@ -476,7 +476,7 @@ func (m *AnyAnyMap) UnmarshalJSON(b []byte) error { m.data = make(map[interface{}]interface{}) } var data map[string]interface{} - if err := json.Unmarshal(b, &data); err != nil { + if err := json.UnmarshalUseNumber(b, &data); err != nil { return err } for k, v := range data { diff --git a/container/gmap/gmap_hash_int_any_map.go b/container/gmap/gmap_hash_int_any_map.go index d04c49a09..f99fd77d4 100644 --- a/container/gmap/gmap_hash_int_any_map.go +++ b/container/gmap/gmap_hash_int_any_map.go @@ -475,7 +475,7 @@ func (m *IntAnyMap) UnmarshalJSON(b []byte) error { if m.data == nil { m.data = make(map[int]interface{}) } - if err := json.Unmarshal(b, &m.data); err != nil { + if err := json.UnmarshalUseNumber(b, &m.data); err != nil { return err } return nil @@ -490,7 +490,7 @@ func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) { } switch value.(type) { case string, []byte: - return json.Unmarshal(gconv.Bytes(value), &m.data) + return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data) default: for k, v := range gconv.Map(value) { m.data[gconv.Int(k)] = v diff --git a/container/gmap/gmap_hash_int_int_map.go b/container/gmap/gmap_hash_int_int_map.go index 4372246e3..3f8b8d411 100644 --- a/container/gmap/gmap_hash_int_int_map.go +++ b/container/gmap/gmap_hash_int_int_map.go @@ -446,7 +446,7 @@ func (m *IntIntMap) UnmarshalJSON(b []byte) error { if m.data == nil { m.data = make(map[int]int) } - if err := json.Unmarshal(b, &m.data); err != nil { + if err := json.UnmarshalUseNumber(b, &m.data); err != nil { return err } return nil @@ -461,7 +461,7 @@ func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) { } switch value.(type) { case string, []byte: - return json.Unmarshal(gconv.Bytes(value), &m.data) + return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data) default: for k, v := range gconv.Map(value) { m.data[gconv.Int(k)] = gconv.Int(v) diff --git a/container/gmap/gmap_hash_int_str_map.go b/container/gmap/gmap_hash_int_str_map.go index e92f8023a..c5f5ac323 100644 --- a/container/gmap/gmap_hash_int_str_map.go +++ b/container/gmap/gmap_hash_int_str_map.go @@ -446,7 +446,7 @@ func (m *IntStrMap) UnmarshalJSON(b []byte) error { if m.data == nil { m.data = make(map[int]string) } - if err := json.Unmarshal(b, &m.data); err != nil { + if err := json.UnmarshalUseNumber(b, &m.data); err != nil { return err } return nil @@ -461,7 +461,7 @@ func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) { } switch value.(type) { case string, []byte: - return json.Unmarshal(gconv.Bytes(value), &m.data) + return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data) default: for k, v := range gconv.Map(value) { m.data[gconv.Int(k)] = gconv.String(v) diff --git a/container/gmap/gmap_hash_str_any_map.go b/container/gmap/gmap_hash_str_any_map.go index 1ee97612e..04d70f664 100644 --- a/container/gmap/gmap_hash_str_any_map.go +++ b/container/gmap/gmap_hash_str_any_map.go @@ -471,7 +471,7 @@ func (m *StrAnyMap) UnmarshalJSON(b []byte) error { if m.data == nil { m.data = make(map[string]interface{}) } - if err := json.Unmarshal(b, &m.data); err != nil { + if err := json.UnmarshalUseNumber(b, &m.data); err != nil { return err } return nil diff --git a/container/gmap/gmap_hash_str_int_map.go b/container/gmap/gmap_hash_str_int_map.go index 7a51f6ad9..7bc03cd34 100644 --- a/container/gmap/gmap_hash_str_int_map.go +++ b/container/gmap/gmap_hash_str_int_map.go @@ -449,7 +449,7 @@ func (m *StrIntMap) UnmarshalJSON(b []byte) error { if m.data == nil { m.data = make(map[string]int) } - if err := json.Unmarshal(b, &m.data); err != nil { + if err := json.UnmarshalUseNumber(b, &m.data); err != nil { return err } return nil @@ -464,7 +464,7 @@ func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) { } switch value.(type) { case string, []byte: - return json.Unmarshal(gconv.Bytes(value), &m.data) + return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data) default: for k, v := range gconv.Map(value) { m.data[k] = gconv.Int(v) diff --git a/container/gmap/gmap_hash_str_str_map.go b/container/gmap/gmap_hash_str_str_map.go index 04f6c0698..ae68640c8 100644 --- a/container/gmap/gmap_hash_str_str_map.go +++ b/container/gmap/gmap_hash_str_str_map.go @@ -449,7 +449,7 @@ func (m *StrStrMap) UnmarshalJSON(b []byte) error { if m.data == nil { m.data = make(map[string]string) } - if err := json.Unmarshal(b, &m.data); err != nil { + if err := json.UnmarshalUseNumber(b, &m.data); err != nil { return err } return nil diff --git a/container/gmap/gmap_list_map.go b/container/gmap/gmap_list_map.go index 224cb9f63..790998da7 100644 --- a/container/gmap/gmap_list_map.go +++ b/container/gmap/gmap_list_map.go @@ -530,7 +530,7 @@ func (m *ListMap) UnmarshalJSON(b []byte) error { m.list = glist.New() } var data map[string]interface{} - if err := json.Unmarshal(b, &data); err != nil { + if err := json.UnmarshalUseNumber(b, &data); err != nil { return err } for key, value := range data { diff --git a/container/gmap/gmap_z_unit_any_any_test.go b/container/gmap/gmap_z_unit_any_any_test.go index 4a06b4e04..3a5c78592 100644 --- a/container/gmap/gmap_z_unit_any_any_test.go +++ b/container/gmap/gmap_z_unit_any_any_test.go @@ -255,7 +255,7 @@ func Test_AnyAnyMap_Json(t *testing.T) { t.Assert(err, nil) m := gmap.New() - err = json.Unmarshal(b, m) + err = json.UnmarshalUseNumber(b, m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) @@ -269,7 +269,7 @@ func Test_AnyAnyMap_Json(t *testing.T) { t.Assert(err, nil) var m gmap.Map - err = json.Unmarshal(b, &m) + err = json.UnmarshalUseNumber(b, &m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) diff --git a/container/gmap/gmap_z_unit_int_any_test.go b/container/gmap/gmap_z_unit_int_any_test.go index c6ac46486..2c6bc25ea 100644 --- a/container/gmap/gmap_z_unit_int_any_test.go +++ b/container/gmap/gmap_z_unit_int_any_test.go @@ -245,7 +245,7 @@ func Test_IntAnyMap_Json(t *testing.T) { t.Assert(err, nil) m := gmap.NewIntAnyMap() - err = json.Unmarshal(b, m) + err = json.UnmarshalUseNumber(b, m) t.Assert(err, nil) t.Assert(m.Get(1), data[1]) t.Assert(m.Get(2), data[2]) diff --git a/container/gmap/gmap_z_unit_int_int_test.go b/container/gmap/gmap_z_unit_int_int_test.go index 37b940152..762ba223f 100644 --- a/container/gmap/gmap_z_unit_int_int_test.go +++ b/container/gmap/gmap_z_unit_int_int_test.go @@ -251,7 +251,7 @@ func Test_IntIntMap_Json(t *testing.T) { t.Assert(err, nil) m := gmap.NewIntIntMap() - err = json.Unmarshal(b, m) + err = json.UnmarshalUseNumber(b, m) t.Assert(err, nil) t.Assert(m.Get(1), data[1]) t.Assert(m.Get(2), data[2]) diff --git a/container/gmap/gmap_z_unit_int_str_test.go b/container/gmap/gmap_z_unit_int_str_test.go index 2b4292ef1..6a91b00f6 100644 --- a/container/gmap/gmap_z_unit_int_str_test.go +++ b/container/gmap/gmap_z_unit_int_str_test.go @@ -249,7 +249,7 @@ func Test_IntStrMap_Json(t *testing.T) { t.Assert(err, nil) m := gmap.NewIntStrMap() - err = json.Unmarshal(b, m) + err = json.UnmarshalUseNumber(b, m) t.Assert(err, nil) t.Assert(m.Get(1), data[1]) t.Assert(m.Get(2), data[2]) diff --git a/container/gmap/gmap_z_unit_list_map_test.go b/container/gmap/gmap_z_unit_list_map_test.go index 045d673a7..4a226cfca 100644 --- a/container/gmap/gmap_z_unit_list_map_test.go +++ b/container/gmap/gmap_z_unit_list_map_test.go @@ -204,7 +204,7 @@ func Test_ListMap_Json(t *testing.T) { t.Assert(err, nil) m := gmap.NewListMap() - err = json.Unmarshal(b, m) + err = json.UnmarshalUseNumber(b, m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) @@ -219,7 +219,7 @@ func Test_ListMap_Json(t *testing.T) { t.Assert(err, nil) var m gmap.ListMap - err = json.Unmarshal(b, &m) + err = json.UnmarshalUseNumber(b, &m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) diff --git a/container/gmap/gmap_z_unit_str_any_test.go b/container/gmap/gmap_z_unit_str_any_test.go index 22180c901..1cee92de2 100644 --- a/container/gmap/gmap_z_unit_str_any_test.go +++ b/container/gmap/gmap_z_unit_str_any_test.go @@ -243,7 +243,7 @@ func Test_StrAnyMap_Json(t *testing.T) { t.Assert(err, nil) m := gmap.NewStrAnyMap() - err = json.Unmarshal(b, m) + err = json.UnmarshalUseNumber(b, m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) @@ -257,7 +257,7 @@ func Test_StrAnyMap_Json(t *testing.T) { t.Assert(err, nil) var m gmap.StrAnyMap - err = json.Unmarshal(b, &m) + err = json.UnmarshalUseNumber(b, &m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) diff --git a/container/gmap/gmap_z_unit_str_int_test.go b/container/gmap/gmap_z_unit_str_int_test.go index 56ba1141a..eb80c7760 100644 --- a/container/gmap/gmap_z_unit_str_int_test.go +++ b/container/gmap/gmap_z_unit_str_int_test.go @@ -247,7 +247,7 @@ func Test_StrIntMap_Json(t *testing.T) { t.Assert(err, nil) m := gmap.NewStrIntMap() - err = json.Unmarshal(b, m) + err = json.UnmarshalUseNumber(b, m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) @@ -261,7 +261,7 @@ func Test_StrIntMap_Json(t *testing.T) { t.Assert(err, nil) var m gmap.StrIntMap - err = json.Unmarshal(b, &m) + err = json.UnmarshalUseNumber(b, &m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) diff --git a/container/gmap/gmap_z_unit_str_str_test.go b/container/gmap/gmap_z_unit_str_str_test.go index 0dfffd3a5..e64a36af1 100644 --- a/container/gmap/gmap_z_unit_str_str_test.go +++ b/container/gmap/gmap_z_unit_str_str_test.go @@ -244,7 +244,7 @@ func Test_StrStrMap_Json(t *testing.T) { t.Assert(err, nil) m := gmap.NewStrStrMap() - err = json.Unmarshal(b, m) + err = json.UnmarshalUseNumber(b, m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) @@ -258,7 +258,7 @@ func Test_StrStrMap_Json(t *testing.T) { t.Assert(err, nil) var m gmap.StrStrMap - err = json.Unmarshal(b, &m) + err = json.UnmarshalUseNumber(b, &m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) diff --git a/container/gmap/gmap_z_unit_tree_map_test.go b/container/gmap/gmap_z_unit_tree_map_test.go index e008d9986..470cf40f0 100644 --- a/container/gmap/gmap_z_unit_tree_map_test.go +++ b/container/gmap/gmap_z_unit_tree_map_test.go @@ -188,7 +188,7 @@ func Test_TreeMap_Json(t *testing.T) { t.Assert(err, nil) m := gmap.NewTreeMap(gutil.ComparatorString) - err = json.Unmarshal(b, m) + err = json.UnmarshalUseNumber(b, m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) @@ -202,7 +202,7 @@ func Test_TreeMap_Json(t *testing.T) { t.Assert(err, nil) var m gmap.TreeMap - err = json.Unmarshal(b, &m) + err = json.UnmarshalUseNumber(b, &m) t.Assert(err, nil) t.Assert(m.Get("k1"), data["k1"]) t.Assert(m.Get("k2"), data["k2"]) diff --git a/container/gset/gset_any_set.go b/container/gset/gset_any_set.go index f6ceccfa6..9db79a47a 100644 --- a/container/gset/gset_any_set.go +++ b/container/gset/gset_any_set.go @@ -477,7 +477,7 @@ func (set *Set) UnmarshalJSON(b []byte) error { set.data = make(map[interface{}]struct{}) } var array []interface{} - if err := json.Unmarshal(b, &array); err != nil { + if err := json.UnmarshalUseNumber(b, &array); err != nil { return err } for _, v := range array { @@ -496,7 +496,7 @@ func (set *Set) UnmarshalValue(value interface{}) (err error) { var array []interface{} switch value.(type) { case string, []byte: - err = json.Unmarshal(gconv.Bytes(value), &array) + err = json.UnmarshalUseNumber(gconv.Bytes(value), &array) default: array = gconv.SliceAny(value) } diff --git a/container/gset/gset_int_set.go b/container/gset/gset_int_set.go index 18d6ca9e2..39a98d9f3 100644 --- a/container/gset/gset_int_set.go +++ b/container/gset/gset_int_set.go @@ -437,7 +437,7 @@ func (set *IntSet) UnmarshalJSON(b []byte) error { set.data = make(map[int]struct{}) } var array []int - if err := json.Unmarshal(b, &array); err != nil { + if err := json.UnmarshalUseNumber(b, &array); err != nil { return err } for _, v := range array { @@ -456,7 +456,7 @@ func (set *IntSet) UnmarshalValue(value interface{}) (err error) { var array []int switch value.(type) { case string, []byte: - err = json.Unmarshal(gconv.Bytes(value), &array) + err = json.UnmarshalUseNumber(gconv.Bytes(value), &array) default: array = gconv.SliceInt(value) } diff --git a/container/gset/gset_str_set.go b/container/gset/gset_str_set.go index d800af22e..68b4ec6bc 100644 --- a/container/gset/gset_str_set.go +++ b/container/gset/gset_str_set.go @@ -465,7 +465,7 @@ func (set *StrSet) UnmarshalJSON(b []byte) error { set.data = make(map[string]struct{}) } var array []string - if err := json.Unmarshal(b, &array); err != nil { + if err := json.UnmarshalUseNumber(b, &array); err != nil { return err } for _, v := range array { @@ -484,7 +484,7 @@ func (set *StrSet) UnmarshalValue(value interface{}) (err error) { var array []string switch value.(type) { case string, []byte: - err = json.Unmarshal(gconv.Bytes(value), &array) + err = json.UnmarshalUseNumber(gconv.Bytes(value), &array) default: array = gconv.SliceStr(value) } diff --git a/container/gset/gset_z_unit_any_test.go b/container/gset/gset_z_unit_any_test.go index d9d6e6479..bef113955 100644 --- a/container/gset/gset_z_unit_any_test.go +++ b/container/gset/gset_z_unit_any_test.go @@ -327,7 +327,7 @@ func TestSet_Json(t *testing.T) { t.Assert(err1, err2) a2 := gset.New() - err2 = json.Unmarshal(b2, &a2) + err2 = json.UnmarshalUseNumber(b2, &a2) t.Assert(err2, nil) t.Assert(a2.Contains("a"), true) t.Assert(a2.Contains("b"), true) @@ -336,7 +336,7 @@ func TestSet_Json(t *testing.T) { t.Assert(a2.Contains("e"), false) var a3 gset.Set - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Contains("a"), true) t.Assert(a3.Contains("b"), true) diff --git a/container/gset/gset_z_unit_int_test.go b/container/gset/gset_z_unit_int_test.go index e45fa21d4..618200691 100644 --- a/container/gset/gset_z_unit_int_test.go +++ b/container/gset/gset_z_unit_int_test.go @@ -358,7 +358,7 @@ func TestIntSet_Json(t *testing.T) { t.Assert(err1, err2) a2 := gset.NewIntSet() - err2 = json.Unmarshal(b2, &a2) + err2 = json.UnmarshalUseNumber(b2, &a2) t.Assert(err2, nil) t.Assert(a2.Contains(1), true) t.Assert(a2.Contains(2), true) @@ -367,7 +367,7 @@ func TestIntSet_Json(t *testing.T) { t.Assert(a2.Contains(5), false) var a3 gset.IntSet - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a2.Contains(1), true) t.Assert(a2.Contains(2), true) diff --git a/container/gset/gset_z_unit_str_test.go b/container/gset/gset_z_unit_str_test.go index 33586cd8e..ab57cf6ca 100644 --- a/container/gset/gset_z_unit_str_test.go +++ b/container/gset/gset_z_unit_str_test.go @@ -404,7 +404,7 @@ func TestStrSet_Json(t *testing.T) { t.Assert(err1, err2) a2 := gset.NewStrSet() - err2 = json.Unmarshal(b2, &a2) + err2 = json.UnmarshalUseNumber(b2, &a2) t.Assert(err2, nil) t.Assert(a2.Contains("a"), true) t.Assert(a2.Contains("b"), true) @@ -413,7 +413,7 @@ func TestStrSet_Json(t *testing.T) { t.Assert(a2.Contains("e"), false) var a3 gset.StrSet - err := json.Unmarshal(b2, &a3) + err := json.UnmarshalUseNumber(b2, &a3) t.Assert(err, nil) t.Assert(a3.Contains("a"), true) t.Assert(a3.Contains("b"), true) diff --git a/container/gtree/gtree_redblacktree.go b/container/gtree/gtree_redblacktree.go index 33a8d45dd..7ccf0fd1c 100644 --- a/container/gtree/gtree_redblacktree.go +++ b/container/gtree/gtree_redblacktree.go @@ -937,7 +937,7 @@ func (tree *RedBlackTree) UnmarshalJSON(b []byte) error { tree.comparator = gutil.ComparatorString } var data map[string]interface{} - if err := json.Unmarshal(b, &data); err != nil { + if err := json.UnmarshalUseNumber(b, &data); err != nil { return err } for k, v := range data { diff --git a/container/gtype/interface.go b/container/gtype/interface.go index acb26f46c..8cd554dde 100644 --- a/container/gtype/interface.go +++ b/container/gtype/interface.go @@ -58,7 +58,7 @@ func (v *Interface) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal. func (v *Interface) UnmarshalJSON(b []byte) error { var i interface{} - err := json.Unmarshal(b, &i) + err := json.UnmarshalUseNumber(b, &i) if err != nil { return err } diff --git a/container/gtype/z_unit_bool_test.go b/container/gtype/z_unit_bool_test.go index 297f5de85..b30738a84 100644 --- a/container/gtype/z_unit_bool_test.go +++ b/container/gtype/z_unit_bool_test.go @@ -55,16 +55,16 @@ func Test_Bool_JSON(t *testing.T) { gtest.C(t, func(t *gtest.T) { var err error i := gtype.NewBool() - err = json.Unmarshal([]byte("true"), &i) + err = json.UnmarshalUseNumber([]byte("true"), &i) t.Assert(err, nil) t.Assert(i.Val(), true) - err = json.Unmarshal([]byte("false"), &i) + err = json.UnmarshalUseNumber([]byte("false"), &i) t.Assert(err, nil) t.Assert(i.Val(), false) - err = json.Unmarshal([]byte("1"), &i) + err = json.UnmarshalUseNumber([]byte("1"), &i) t.Assert(err, nil) t.Assert(i.Val(), true) - err = json.Unmarshal([]byte("0"), &i) + err = json.UnmarshalUseNumber([]byte("0"), &i) t.Assert(err, nil) t.Assert(i.Val(), false) }) @@ -78,7 +78,7 @@ func Test_Bool_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewBool() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), i.Val()) }) @@ -91,7 +91,7 @@ func Test_Bool_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewBool() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), i.Val()) }) diff --git a/container/gtype/z_unit_byte_test.go b/container/gtype/z_unit_byte_test.go index 0a6c77df4..86ba6cfb9 100644 --- a/container/gtype/z_unit_byte_test.go +++ b/container/gtype/z_unit_byte_test.go @@ -53,7 +53,7 @@ func Test_Byte_JSON(t *testing.T) { gtest.C(t, func(t *gtest.T) { var err error i := gtype.NewByte() - err = json.Unmarshal([]byte("49"), &i) + err = json.UnmarshalUseNumber([]byte("49"), &i) t.Assert(err, nil) t.Assert(i.Val(), "49") }) diff --git a/container/gtype/z_unit_bytes_test.go b/container/gtype/z_unit_bytes_test.go index 234307ae8..49145d290 100644 --- a/container/gtype/z_unit_bytes_test.go +++ b/container/gtype/z_unit_bytes_test.go @@ -39,7 +39,7 @@ func Test_Bytes_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewBytes() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), b) }) diff --git a/container/gtype/z_unit_float32_test.go b/container/gtype/z_unit_float32_test.go index 14914ed27..7aff7f79c 100644 --- a/container/gtype/z_unit_float32_test.go +++ b/container/gtype/z_unit_float32_test.go @@ -40,7 +40,7 @@ func Test_Float32_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewFloat32() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), v) }) diff --git a/container/gtype/z_unit_float64_test.go b/container/gtype/z_unit_float64_test.go index 717e0500a..1ba6cfa1e 100644 --- a/container/gtype/z_unit_float64_test.go +++ b/container/gtype/z_unit_float64_test.go @@ -38,7 +38,7 @@ func Test_Float64_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewFloat64() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), v) }) diff --git a/container/gtype/z_unit_int32_test.go b/container/gtype/z_unit_int32_test.go index c2b7d5690..cf2250ae5 100644 --- a/container/gtype/z_unit_int32_test.go +++ b/container/gtype/z_unit_int32_test.go @@ -51,7 +51,7 @@ func Test_Int32_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewInt32() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), v) }) diff --git a/container/gtype/z_unit_int64_test.go b/container/gtype/z_unit_int64_test.go index 85a0bbea8..8b5c6a299 100644 --- a/container/gtype/z_unit_int64_test.go +++ b/container/gtype/z_unit_int64_test.go @@ -50,7 +50,7 @@ func Test_Int64_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewInt64() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), i) }) diff --git a/container/gtype/z_unit_int_test.go b/container/gtype/z_unit_int_test.go index 49e44b2f2..939ef81e9 100644 --- a/container/gtype/z_unit_int_test.go +++ b/container/gtype/z_unit_int_test.go @@ -50,7 +50,7 @@ func Test_Int_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewInt() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), v) }) diff --git a/container/gtype/z_unit_interface_test.go b/container/gtype/z_unit_interface_test.go index 990951b53..130102f63 100644 --- a/container/gtype/z_unit_interface_test.go +++ b/container/gtype/z_unit_interface_test.go @@ -40,7 +40,7 @@ func Test_Interface_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.New() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), s) }) diff --git a/container/gtype/z_unit_string_test.go b/container/gtype/z_unit_string_test.go index 16132474d..3b42cca41 100644 --- a/container/gtype/z_unit_string_test.go +++ b/container/gtype/z_unit_string_test.go @@ -38,7 +38,7 @@ func Test_String_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewString() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), s) }) diff --git a/container/gtype/z_unit_uint32_test.go b/container/gtype/z_unit_uint32_test.go index 1e9d0a508..abe188dad 100644 --- a/container/gtype/z_unit_uint32_test.go +++ b/container/gtype/z_unit_uint32_test.go @@ -50,7 +50,7 @@ func Test_Uint32_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewUint32() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), i) }) diff --git a/container/gtype/z_unit_uint64_test.go b/container/gtype/z_unit_uint64_test.go index 1c97b53c5..ab05217ff 100644 --- a/container/gtype/z_unit_uint64_test.go +++ b/container/gtype/z_unit_uint64_test.go @@ -55,7 +55,7 @@ func Test_Uint64_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewUint64() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), i) }) diff --git a/container/gtype/z_unit_uint_test.go b/container/gtype/z_unit_uint_test.go index d9237940a..4f314856e 100644 --- a/container/gtype/z_unit_uint_test.go +++ b/container/gtype/z_unit_uint_test.go @@ -49,7 +49,7 @@ func Test_Uint_JSON(t *testing.T) { t.Assert(b1, b2) i2 := gtype.NewUint() - err := json.Unmarshal(b2, &i2) + err := json.UnmarshalUseNumber(b2, &i2) t.Assert(err, nil) t.Assert(i2.Val(), i) }) diff --git a/container/gvar/gvar.go b/container/gvar/gvar.go index 1babba781..36480d71a 100644 --- a/container/gvar/gvar.go +++ b/container/gvar/gvar.go @@ -189,7 +189,7 @@ func (v *Var) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal. func (v *Var) UnmarshalJSON(b []byte) error { var i interface{} - err := json.Unmarshal(b, &i) + err := json.UnmarshalUseNumber(b, &i) if err != nil { return err } diff --git a/container/gvar/gvar_z_unit_json_test.go b/container/gvar/gvar_z_unit_json_test.go index 53dc856f3..dde33a732 100644 --- a/container/gvar/gvar_z_unit_json_test.go +++ b/container/gvar/gvar_z_unit_json_test.go @@ -41,7 +41,7 @@ func TestVar_Json(t *testing.T) { b, err := json.Marshal(s) t.Assert(err, nil) - err = json.Unmarshal(b, v) + err = json.UnmarshalUseNumber(b, v) t.Assert(err, nil) t.Assert(v.String(), s) }) @@ -52,7 +52,7 @@ func TestVar_Json(t *testing.T) { b, err := json.Marshal(s) t.Assert(err, nil) - err = json.Unmarshal(b, &v) + err = json.UnmarshalUseNumber(b, &v) t.Assert(err, nil) t.Assert(v.String(), s) }) diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 31fd0007d..bafd477bc 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -488,7 +488,7 @@ func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, e } else { // Other cache, it needs conversion. var result Result - if err = json.Unmarshal(v.Bytes(), &result); err != nil { + if err = json.UnmarshalUseNumber(v.Bytes(), &result); err != nil { return nil, err } else { return result, nil diff --git a/encoding/gjson/gjson.go b/encoding/gjson/gjson.go index df30d9e75..573acce39 100644 --- a/encoding/gjson/gjson.go +++ b/encoding/gjson/gjson.go @@ -22,7 +22,7 @@ const ( defaultSplitChar = '.' ) -// The customized JSON struct. +// Json is the customized JSON struct. type Json struct { mu *rwmutex.RWMutex p *interface{} // Pointer for hierarchical data access, it's the root of data in default. @@ -30,8 +30,8 @@ type Json struct { vc bool // Violence Check(false in default), which is used to access data when the hierarchical data key contains separator char. } -// Option for Json object creating. -type Option struct { +// Options for Json object creating. +type Options struct { Safe bool // Mark this object is for in concurrent-safe usage. Tags string // Custom priority tags for decoding. StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64. diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index 8759f2e71..bd6dcb504 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -42,22 +42,22 @@ func New(data interface{}, safe ...bool) *Json { // The parameter specifies whether using this Json object in concurrent-safe context, which // is false in default. func NewWithTag(data interface{}, tags string, safe ...bool) *Json { - option := Option{ + option := Options{ Tags: tags, } if len(safe) > 0 && safe[0] { option.Safe = true } - return NewWithOption(data, option) + return NewWithOptions(data, option) } -// NewWithOption creates a Json object with any variable type of , but should be a map +// NewWithOptions creates a Json object with any variable type of , but should be a map // or slice for data access reason, or it will make no sense. -func NewWithOption(data interface{}, option Option) *Json { +func NewWithOptions(data interface{}, options Options) *Json { var j *Json switch data.(type) { case string, []byte: - if r, err := loadContentWithOption(data, option); err == nil { + if r, err := loadContentWithOptions(data, options); err == nil { j = r } else { j = &Json{ @@ -86,7 +86,7 @@ func NewWithOption(data interface{}, option Option) *Json { } case reflect.Map, reflect.Struct: i := interface{}(nil) - i = gconv.MapDeep(data, option.Tags) + i = gconv.MapDeep(data, options.Tags) j = &Json{ p: &i, c: byte(defaultSplitChar), @@ -100,7 +100,7 @@ func NewWithOption(data interface{}, option Option) *Json { } } } - j.mu = rwmutex.New(option.Safe) + j.mu = rwmutex.New(options.Safe) return j } @@ -111,56 +111,56 @@ func Load(path string, safe ...bool) (*Json, error) { } else { path = p } - option := Option{} + option := Options{} if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOption(gfile.Ext(path), gfile.GetBytesWithCache(path), option) + return doLoadContentWithOptions(gfile.Ext(path), gfile.GetBytesWithCache(path), option) } // LoadJson creates a Json object from given JSON format content. func LoadJson(data interface{}, safe ...bool) (*Json, error) { - option := Option{} + option := Options{} if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOption("json", gconv.Bytes(data), option) + return doLoadContentWithOptions("json", gconv.Bytes(data), option) } // LoadXml creates a Json object from given XML format content. func LoadXml(data interface{}, safe ...bool) (*Json, error) { - option := Option{} + option := Options{} if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOption("xml", gconv.Bytes(data), option) + return doLoadContentWithOptions("xml", gconv.Bytes(data), option) } // LoadIni creates a Json object from given INI format content. func LoadIni(data interface{}, safe ...bool) (*Json, error) { - option := Option{} + option := Options{} if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOption("ini", gconv.Bytes(data), option) + return doLoadContentWithOptions("ini", gconv.Bytes(data), option) } // LoadYaml creates a Json object from given YAML format content. func LoadYaml(data interface{}, safe ...bool) (*Json, error) { - option := Option{} + option := Options{} if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOption("yaml", gconv.Bytes(data), option) + return doLoadContentWithOptions("yaml", gconv.Bytes(data), option) } // LoadToml creates a Json object from given TOML format content. func LoadToml(data interface{}, safe ...bool) (*Json, error) { - option := Option{} + option := Options{} if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOption("toml", gconv.Bytes(data), option) + return doLoadContentWithOptions("toml", gconv.Bytes(data), option) } // LoadContent creates a Json object from given content, it checks the data type of @@ -186,11 +186,11 @@ func LoadContentType(dataType string, data interface{}, safe ...bool) (*Json, er if content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF { content = content[3:] } - option := Option{} + option := Options{} if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOption(dataType, content, option) + return doLoadContentWithOptions(dataType, content, option) } // IsValidDataType checks and returns whether given a valid data type for loading. @@ -208,36 +208,36 @@ func IsValidDataType(dataType string) bool { return false } -func loadContentWithOption(data interface{}, option Option) (*Json, error) { +func loadContentWithOptions(data interface{}, options Options) (*Json, error) { content := gconv.Bytes(data) if len(content) == 0 { - return NewWithOption(nil, option), nil + return NewWithOptions(nil, options), nil } - return loadContentTypeWithOption(checkDataType(content), content, option) + return loadContentTypeWithOptions(checkDataType(content), content, options) } -func loadContentTypeWithOption(dataType string, data interface{}, option Option) (*Json, error) { +func loadContentTypeWithOptions(dataType string, data interface{}, options Options) (*Json, error) { content := gconv.Bytes(data) if len(content) == 0 { - return NewWithOption(nil, option), nil + return NewWithOptions(nil, options), nil } //ignore UTF8-BOM if content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF { content = content[3:] } - return doLoadContentWithOption(dataType, content, option) + return doLoadContentWithOptions(dataType, content, options) } // doLoadContent creates a Json object from given content. // It supports data content type as follows: // JSON, XML, INI, YAML and TOML. -func doLoadContentWithOption(dataType string, data []byte, option Option) (*Json, error) { +func doLoadContentWithOptions(dataType string, data []byte, options Options) (*Json, error) { var ( err error result interface{} ) if len(data) == 0 { - return NewWithOption(nil, option), nil + return NewWithOptions(nil, options), nil } if dataType == "" { dataType = checkDataType(data) @@ -270,7 +270,7 @@ func doLoadContentWithOption(dataType string, data []byte, option Option) (*Json return nil, err } decoder := json.NewDecoder(bytes.NewReader(data)) - if option.StrNumber { + if options.StrNumber { decoder.UseNumber() } if err := decoder.Decode(&result); err != nil { @@ -280,7 +280,7 @@ func doLoadContentWithOption(dataType string, data []byte, option Option) (*Json case string, []byte: return nil, fmt.Errorf(`json decoding failed for content: %s`, string(data)) } - return NewWithOption(result, option), nil + return NewWithOptions(result, options), nil } // checkDataType automatically checks and returns the data type for . @@ -291,7 +291,8 @@ func checkDataType(content []byte) string { return "json" } else if gregex.IsMatch(`^<.+>[\S\s]+<.+>\s*$`, content) { return "xml" - } else if !gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*"""[\s\S]+"""`, content) && !gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*'''[\s\S]+'''`, content) && + } else if !gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*"""[\s\S]+"""`, content) && + !gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*'''[\s\S]+'''`, content) && ((gregex.IsMatch(`^[\n\r]*[\w\-\s\t]+\s*:\s*".+"`, content) || gregex.IsMatch(`^[\n\r]*[\w\-\s\t]+\s*:\s*\w+`, content)) || (gregex.IsMatch(`[\n\r]+[\w\-\s\t]+\s*:\s*".+"`, content) || gregex.IsMatch(`[\n\r]+[\w\-\s\t]+\s*:\s*\w+`, content))) { return "yml" diff --git a/encoding/gjson/gjson_z_unit_basic_test.go b/encoding/gjson/gjson_z_unit_basic_test.go index 733adea8e..c89b615ad 100644 --- a/encoding/gjson/gjson_z_unit_basic_test.go +++ b/encoding/gjson/gjson_z_unit_basic_test.go @@ -474,7 +474,7 @@ func TestJson_Var(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { data := []byte("[9223372036854775807, 9223372036854775806]") - array := gjson.NewWithOption(data, gjson.Option{StrNumber: true}).Var().Array() + array := gjson.NewWithOptions(data, gjson.Options{StrNumber: true}).Var().Array() t.Assert(array, []uint64{9223372036854775807, 9223372036854775806}) }) } diff --git a/encoding/gjson/gjson_z_unit_implements_test.go b/encoding/gjson/gjson_z_unit_implements_test.go index 8264e1625..8350f3a29 100644 --- a/encoding/gjson/gjson_z_unit_implements_test.go +++ b/encoding/gjson/gjson_z_unit_implements_test.go @@ -20,7 +20,7 @@ func TestJson_UnmarshalJSON(t *testing.T) { data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`) gtest.C(t, func(t *gtest.T) { j := gjson.New(nil) - err := json.Unmarshal(data, j) + err := json.UnmarshalUseNumber(data, j) t.Assert(err, nil) t.Assert(j.Get("n"), "123456789") t.Assert(j.Get("m"), g.Map{"k": "v"}) diff --git a/encoding/gjson/gjson_z_unit_new_test.go b/encoding/gjson/gjson_z_unit_new_test.go index b4f911379..359cafd64 100644 --- a/encoding/gjson/gjson_z_unit_new_test.go +++ b/encoding/gjson/gjson_z_unit_new_test.go @@ -98,7 +98,7 @@ func Test_New_HierarchicalStruct(t *testing.T) { }) } -func Test_NewWithOption(t *testing.T) { +func Test_NewWithOptions(t *testing.T) { gtest.C(t, func(t *gtest.T) { data := []byte("[9223372036854775807, 9223372036854775806]") array := gjson.New(data).Array() @@ -106,7 +106,7 @@ func Test_NewWithOption(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { data := []byte("[9223372036854775807, 9223372036854775806]") - array := gjson.NewWithOption(data, gjson.Option{StrNumber: true}).Array() + array := gjson.NewWithOptions(data, gjson.Options{StrNumber: true}).Array() t.Assert(array, []uint64{9223372036854775807, 9223372036854775806}) }) } diff --git a/internal/json/json.go b/internal/json/json.go index 1ed12ba79..ae6c38f13 100644 --- a/internal/json/json.go +++ b/internal/json/json.go @@ -8,6 +8,7 @@ package json import ( + "bytes" "encoding/json" "io" ) @@ -33,6 +34,13 @@ func Unmarshal(data []byte, v interface{}) error { return json.Unmarshal(data, v) } +// UnmarshalUseNumber decodes the json data bytes to target interface using number option. +func UnmarshalUseNumber(data []byte, v interface{}) error { + decoder := NewDecoder(bytes.NewReader(data)) + decoder.UseNumber() + return decoder.Decode(v) +} + // NewEncoder same as json.NewEncoder func NewEncoder(writer io.Writer) *json.Encoder { return json.NewEncoder(writer) diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index c9bfdcbec..d79788d65 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -320,7 +320,7 @@ func (r *Request) parseBody() { body = bytes.TrimSpace(body) // JSON format checks. if body[0] == '{' && body[len(body)-1] == '}' { - _ = json.Unmarshal(body, &r.bodyMap) + _ = json.UnmarshalUseNumber(body, &r.bodyMap) } // XML format checks. if len(body) > 5 && bytes.EqualFold(body[:5], xmlHeaderBytes) { diff --git a/net/ghttp/ghttp_unit_request_json_test.go b/net/ghttp/ghttp_unit_request_json_test.go index 09e68c72b..7feb32e52 100644 --- a/net/ghttp/ghttp_unit_request_json_test.go +++ b/net/ghttp/ghttp_unit_request_json_test.go @@ -144,7 +144,7 @@ func Test_Params_Json_Response(t *testing.T) { client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) map1 := make(map[string]interface{}) - err1 := json.Unmarshal([]byte(client.GetContent("/json1")), &map1) + err1 := json.UnmarshalUseNumber([]byte(client.GetContent("/json1")), &map1) t.Assert(err1, nil) t.Assert(len(map1), 4) t.Assert(map1["Name"], "john") @@ -153,7 +153,7 @@ func Test_Params_Json_Response(t *testing.T) { t.Assert(map1["password2"], "456") map2 := make(map[string]interface{}) - err2 := json.Unmarshal([]byte(client.GetContent("/json2")), &map2) + err2 := json.UnmarshalUseNumber([]byte(client.GetContent("/json2")), &map2) t.Assert(err2, nil) t.Assert(len(map2), 4) t.Assert(map2["Name"], "john") @@ -162,14 +162,14 @@ func Test_Params_Json_Response(t *testing.T) { t.Assert(map2["password2"], "456") map3 := make(map[string]interface{}) - err3 := json.Unmarshal([]byte(client.GetContent("/json3")), &map3) + err3 := json.UnmarshalUseNumber([]byte(client.GetContent("/json3")), &map3) t.Assert(err3, nil) t.Assert(len(map3), 2) t.Assert(map3["success"], "true") t.Assert(map3["message"], g.Map{"body": "测试", "code": 3, "error": "error"}) map4 := make(map[string]interface{}) - err4 := json.Unmarshal([]byte(client.GetContent("/json4")), &map4) + err4 := json.UnmarshalUseNumber([]byte(client.GetContent("/json4")), &map4) t.Assert(err4, nil) t.Assert(len(map4), 2) t.Assert(map4["success"], "true") diff --git a/net/gtrace/gtrace_carrier.go b/net/gtrace/gtrace_carrier.go index c0cd41979..997f2ea91 100644 --- a/net/gtrace/gtrace_carrier.go +++ b/net/gtrace/gtrace_carrier.go @@ -55,5 +55,5 @@ func (c Carrier) String() string { func (c Carrier) UnmarshalJSON(b []byte) error { carrier := NewCarrier(nil) - return json.Unmarshal(b, carrier) + return json.UnmarshalUseNumber(b, carrier) } diff --git a/os/gbuild/gbuild.go b/os/gbuild/gbuild.go index 7f5cd6e99..5447c0cb7 100644 --- a/os/gbuild/gbuild.go +++ b/os/gbuild/gbuild.go @@ -24,7 +24,7 @@ var ( func init() { if builtInVarStr != "" { - err := json.Unmarshal(gbase64.MustDecodeString(builtInVarStr), &builtInVarMap) + err := json.UnmarshalUseNumber(gbase64.MustDecodeString(builtInVarStr), &builtInVarMap) if err != nil { intlog.Error(err) } diff --git a/os/gproc/gproc_comm_receive.go b/os/gproc/gproc_comm_receive.go index b0bd9f39a..18a815553 100644 --- a/os/gproc/gproc_comm_receive.go +++ b/os/gproc/gproc_comm_receive.go @@ -89,7 +89,7 @@ func receiveTcpHandler(conn *gtcp.Conn) { if len(buffer) > 0 { // Package decoding. msg := new(MsgRequest) - if err := json.Unmarshal(buffer, msg); err != nil { + if err := json.UnmarshalUseNumber(buffer, msg); err != nil { //glog.Error(err) continue } diff --git a/os/gproc/gproc_comm_send.go b/os/gproc/gproc_comm_send.go index 25af7c453..b53c5132e 100644 --- a/os/gproc/gproc_comm_send.go +++ b/os/gproc/gproc_comm_send.go @@ -43,7 +43,7 @@ func Send(pid int, data []byte, group ...string) error { }) if len(result) > 0 { response := new(MsgResponse) - err = json.Unmarshal(result, response) + err = json.UnmarshalUseNumber(result, response) if err == nil { if response.Code != 1 { err = errors.New(response.Message) diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index 51936a499..fb0bc7ca6 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -175,7 +175,7 @@ func (s *StorageFile) GetSession(id string, ttl time.Duration, data *gmap.StrAny } } var m map[string]interface{} - if err = json.Unmarshal(content, &m); err != nil { + if err = json.UnmarshalUseNumber(content, &m); err != nil { return nil, err } if m == nil { diff --git a/os/gsession/gsession_storage_redis.go b/os/gsession/gsession_storage_redis.go index bd5714e66..8d26e2db6 100644 --- a/os/gsession/gsession_storage_redis.go +++ b/os/gsession/gsession_storage_redis.go @@ -126,7 +126,7 @@ func (s *StorageRedis) GetSession(id string, ttl time.Duration, data *gmap.StrAn return nil, nil } var m map[string]interface{} - if err = json.Unmarshal(content, &m); err != nil { + if err = json.UnmarshalUseNumber(content, &m); err != nil { return nil, err } if m == nil { diff --git a/os/gtime/gtime_z_unit_json_test.go b/os/gtime/gtime_z_unit_json_test.go index b79fd3230..781da86d6 100644 --- a/os/gtime/gtime_z_unit_json_test.go +++ b/os/gtime/gtime_z_unit_json_test.go @@ -50,7 +50,7 @@ func Test_Json_Pointer(t *testing.T) { gtest.C(t, func(t *gtest.T) { var t1 gtime.Time s := []byte(`"2006-01-02 15:04:05"`) - err := json.Unmarshal(s, &t1) + err := json.UnmarshalUseNumber(s, &t1) t.Assert(err, nil) t.Assert(t1.String(), "2006-01-02 15:04:05") }) diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index 5563ddda8..abab585cb 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -56,7 +56,7 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string] case string: // If it is a JSON string, automatically unmarshal it! if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' { - if err := json.Unmarshal([]byte(r), &dataMap); err != nil { + if err := json.UnmarshalUseNumber([]byte(r), &dataMap); err != nil { return nil } } else { @@ -65,7 +65,7 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string] case []byte: // If it is a JSON string, automatically unmarshal it! if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' { - if err := json.Unmarshal(r, &dataMap); err != nil { + if err := json.UnmarshalUseNumber(r, &dataMap); err != nil { return nil } } else { diff --git a/util/gconv/gconv_maps.go b/util/gconv/gconv_maps.go index 8a05e9600..931b181cf 100644 --- a/util/gconv/gconv_maps.go +++ b/util/gconv/gconv_maps.go @@ -33,7 +33,7 @@ func Maps(value interface{}, tags ...string) []map[string]interface{} { case string: list := make([]map[string]interface{}, 0) if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { - if err := json.Unmarshal([]byte(r), &list); err != nil { + if err := json.UnmarshalUseNumber([]byte(r), &list); err != nil { return nil } return list @@ -44,7 +44,7 @@ func Maps(value interface{}, tags ...string) []map[string]interface{} { case []byte: list := make([]map[string]interface{}, 0) if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { - if err := json.Unmarshal(r, &list); err != nil { + if err := json.UnmarshalUseNumber(r, &list); err != nil { return nil } return list @@ -79,7 +79,7 @@ func MapsDeep(value interface{}, tags ...string) []map[string]interface{} { case string: list := make([]map[string]interface{}, 0) if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { - if err := json.Unmarshal([]byte(r), &list); err != nil { + if err := json.UnmarshalUseNumber([]byte(r), &list); err != nil { return nil } return list @@ -90,7 +90,7 @@ func MapsDeep(value interface{}, tags ...string) []map[string]interface{} { case []byte: list := make([]map[string]interface{}, 0) if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' { - if err := json.Unmarshal(r, &list); err != nil { + if err := json.UnmarshalUseNumber(r, &list); err != nil { return nil } return list diff --git a/util/gconv/gconv_maptomap.go b/util/gconv/gconv_maptomap.go index 143d6d013..ded5ea744 100644 --- a/util/gconv/gconv_maptomap.go +++ b/util/gconv/gconv_maptomap.go @@ -36,20 +36,20 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s if json.Valid(r) { if rv, ok := pointer.(reflect.Value); ok { if rv.Kind() == reflect.Ptr { - return json.Unmarshal(r, rv.Interface()) + return json.UnmarshalUseNumber(r, rv.Interface()) } } else { - return json.Unmarshal(r, pointer) + return json.UnmarshalUseNumber(r, pointer) } } case string: if paramsBytes := []byte(r); json.Valid(paramsBytes) { if rv, ok := pointer.(reflect.Value); ok { if rv.Kind() == reflect.Ptr { - return json.Unmarshal(paramsBytes, rv.Interface()) + return json.UnmarshalUseNumber(paramsBytes, rv.Interface()) } } else { - return json.Unmarshal(paramsBytes, pointer) + return json.UnmarshalUseNumber(paramsBytes, pointer) } } } diff --git a/util/gconv/gconv_maptomaps.go b/util/gconv/gconv_maptomaps.go index 9e0d58306..f4bebc7dc 100644 --- a/util/gconv/gconv_maptomaps.go +++ b/util/gconv/gconv_maptomaps.go @@ -40,20 +40,20 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] if json.Valid(r) { if rv, ok := pointer.(reflect.Value); ok { if rv.Kind() == reflect.Ptr { - return json.Unmarshal(r, rv.Interface()) + return json.UnmarshalUseNumber(r, rv.Interface()) } } else { - return json.Unmarshal(r, pointer) + return json.UnmarshalUseNumber(r, pointer) } } case string: if paramsBytes := []byte(r); json.Valid(paramsBytes) { if rv, ok := pointer.(reflect.Value); ok { if rv.Kind() == reflect.Ptr { - return json.Unmarshal(paramsBytes, rv.Interface()) + return json.UnmarshalUseNumber(paramsBytes, rv.Interface()) } } else { - return json.Unmarshal(paramsBytes, pointer) + return json.UnmarshalUseNumber(paramsBytes, pointer) } } } diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 0d4924bd7..06416f863 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -83,20 +83,20 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string if json.Valid(r) { if rv, ok := pointer.(reflect.Value); ok { if rv.Kind() == reflect.Ptr { - return json.Unmarshal(r, rv.Interface()) + return json.UnmarshalUseNumber(r, rv.Interface()) } } else { - return json.Unmarshal(r, pointer) + return json.UnmarshalUseNumber(r, pointer) } } case string: if paramsBytes := []byte(r); json.Valid(paramsBytes) { if rv, ok := pointer.(reflect.Value); ok { if rv.Kind() == reflect.Ptr { - return json.Unmarshal(paramsBytes, rv.Interface()) + return json.UnmarshalUseNumber(paramsBytes, rv.Interface()) } } else { - return json.Unmarshal(paramsBytes, pointer) + return json.UnmarshalUseNumber(paramsBytes, pointer) } } } diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index 89e220ac4..8d3626d36 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -74,20 +74,20 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin if json.Valid(r) { if rv, ok := pointer.(reflect.Value); ok { if rv.Kind() == reflect.Ptr { - return json.Unmarshal(r, rv.Interface()) + return json.UnmarshalUseNumber(r, rv.Interface()) } } else { - return json.Unmarshal(r, pointer) + return json.UnmarshalUseNumber(r, pointer) } } case string: if paramsBytes := []byte(r); json.Valid(paramsBytes) { if rv, ok := pointer.(reflect.Value); ok { if rv.Kind() == reflect.Ptr { - return json.Unmarshal(paramsBytes, rv.Interface()) + return json.UnmarshalUseNumber(paramsBytes, rv.Interface()) } } else { - return json.Unmarshal(paramsBytes, pointer) + return json.UnmarshalUseNumber(paramsBytes, pointer) } } } diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index 062c17d50..faa79d921 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -817,7 +817,7 @@ func Test_Struct_Complex(t *testing.T) { "errorMsg": null }` m := make(g.Map) - err := json.Unmarshal([]byte(data), &m) + err := json.UnmarshalUseNumber([]byte(data), &m) t.Assert(err, nil) model := new(XinYanModel) From f389688caaec8f27c4aae32f712de4e662498f01 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 15 May 2021 23:02:21 +0800 Subject: [PATCH 259/492] add unit testing cases for package internal/json --- internal/json/json_test.go | 93 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 internal/json/json_test.go diff --git a/internal/json/json_test.go b/internal/json/json_test.go new file mode 100644 index 000000000..7ff8b7341 --- /dev/null +++ b/internal/json/json_test.go @@ -0,0 +1,93 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package json_test + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/internal/json" + "github.com/gogf/gf/test/gtest" + "testing" +) + +type User struct { + Id int64 `json:"id"` + Name string `json:"name"` +} + +var ( + user = &User{ + Id: 1265476890672672808, + Name: "john", + } + userBytes, _ = json.Marshal(user) +) + +func TestMarshal(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + b, err := json.Marshal(user) + t.AssertNil(err) + t.Assert(b, `{"id":1265476890672672808,"name":"john"}`) + }) +} + +func TestMarshalIndent(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + b, err := json.MarshalIndent(user, "#", "@") + t.AssertNil(err) + t.Assert(b, `{ +#@"id": 1265476890672672808, +#@"name": "john" +#}`) + }) +} + +func TestUnmarshal(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var m map[string]interface{} + b, _ := json.Marshal(g.Map{ + "user": user, + }) + err := json.Unmarshal(b, &m) + t.AssertNil(err) + // precision lost for big int. + t.Assert(m["user"], g.Map{ + "id": 1265476890672672800, + "name": user.Name, + }) + }) +} + +func TestUnmarshalUseNumber(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var m map[string]interface{} + b, _ := json.Marshal(g.Map{ + "user": user, + }) + err := json.UnmarshalUseNumber(b, &m) + t.AssertNil(err) + t.Assert(m["user"], g.Map{ + "id": user.Id, + "name": user.Name, + }) + }) +} + +func TestValid(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + m := g.Map{ + `{}`: true, + `{"id":1,"name":"john"}`: true, + `1`: true, + `"john"`: true, + `"`: false, + ``: false, + } + for k, v := range m { + t.Assert(json.Valid([]byte(k)), v) + } + }) +} From 8aed1eca1332395454cf1bddced7c313ab5dc58f Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 15 May 2021 23:02:44 +0800 Subject: [PATCH 260/492] version updates --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 78fbc6cf1..d2895b5ad 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.15.7" +const VERSION = "v1.16.0" const AUTHORS = "john" From d12409b11830cd038827b20b00f7c5dcf4c57519 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 15 May 2021 23:13:31 +0800 Subject: [PATCH 261/492] revert json.UnmarshalUseNumber to json.Unmarshal for code in the example folder --- .example/container/garray/json_unmarshal.go | 2 +- .example/container/glist/json_unmarshal.go | 2 +- .example/container/gmap/gmap_json_unmarshal.go | 2 +- .example/container/gset/json_unmarshal.go | 2 +- .example/container/gtype/json_unmarshal.go | 2 +- .example/container/gvar/json_unmarshal.go | 2 +- .example/encoding/gjson/issue#IZXU2.go | 2 +- .example/net/gtcp/pkg_operations/common/funcs/funcs.go | 2 +- .example/net/gtcp/pkg_operations/monitor/gtcp_monitor_server.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.example/container/garray/json_unmarshal.go b/.example/container/garray/json_unmarshal.go index 99fb7adec..07e81adb0 100644 --- a/.example/container/garray/json_unmarshal.go +++ b/.example/container/garray/json_unmarshal.go @@ -14,6 +14,6 @@ func main() { Scores *garray.IntArray } s := Student{} - json.UnmarshalUseNumber(b, &s) + json.Unmarshal(b, &s) fmt.Println(s) } diff --git a/.example/container/glist/json_unmarshal.go b/.example/container/glist/json_unmarshal.go index 32e654bcb..313dfe570 100644 --- a/.example/container/glist/json_unmarshal.go +++ b/.example/container/glist/json_unmarshal.go @@ -14,6 +14,6 @@ func main() { Scores *glist.List } s := Student{} - json.UnmarshalUseNumber(b, &s) + json.Unmarshal(b, &s) fmt.Println(s) } diff --git a/.example/container/gmap/gmap_json_unmarshal.go b/.example/container/gmap/gmap_json_unmarshal.go index 01bdc4f36..af3c134a8 100644 --- a/.example/container/gmap/gmap_json_unmarshal.go +++ b/.example/container/gmap/gmap_json_unmarshal.go @@ -9,6 +9,6 @@ import ( func main() { m := gmap.Map{} s := []byte(`{"name":"john","score":100}`) - json.UnmarshalUseNumber(s, &m) + json.Unmarshal(s, &m) fmt.Println(m.Map()) } diff --git a/.example/container/gset/json_unmarshal.go b/.example/container/gset/json_unmarshal.go index f85afac05..4e90ce823 100644 --- a/.example/container/gset/json_unmarshal.go +++ b/.example/container/gset/json_unmarshal.go @@ -14,6 +14,6 @@ func main() { Scores *gset.IntSet } s := Student{} - json.UnmarshalUseNumber(b, &s) + json.Unmarshal(b, &s) fmt.Println(s) } diff --git a/.example/container/gtype/json_unmarshal.go b/.example/container/gtype/json_unmarshal.go index 7a17a1971..6b72cab08 100644 --- a/.example/container/gtype/json_unmarshal.go +++ b/.example/container/gtype/json_unmarshal.go @@ -14,6 +14,6 @@ func main() { Scores *gtype.Interface } s := Student{} - json.UnmarshalUseNumber(b, &s) + json.Unmarshal(b, &s) fmt.Println(s) } diff --git a/.example/container/gvar/json_unmarshal.go b/.example/container/gvar/json_unmarshal.go index a4a9deba3..31aa9843a 100644 --- a/.example/container/gvar/json_unmarshal.go +++ b/.example/container/gvar/json_unmarshal.go @@ -14,6 +14,6 @@ func main() { Scores *g.Var } s := Student{} - json.UnmarshalUseNumber(b, &s) + json.Unmarshal(b, &s) fmt.Println(s) } diff --git a/.example/encoding/gjson/issue#IZXU2.go b/.example/encoding/gjson/issue#IZXU2.go index f56152fa6..e96f428d3 100644 --- a/.example/encoding/gjson/issue#IZXU2.go +++ b/.example/encoding/gjson/issue#IZXU2.go @@ -140,7 +140,7 @@ var data = `{ func main() { struct1 := new(XinYanModel) - err := json.UnmarshalUseNumber([]byte(data), struct1) + err := json.Unmarshal([]byte(data), struct1) fmt.Println(err) fmt.Println(struct1) diff --git a/.example/net/gtcp/pkg_operations/common/funcs/funcs.go b/.example/net/gtcp/pkg_operations/common/funcs/funcs.go index 4d8c33d10..368874c1e 100644 --- a/.example/net/gtcp/pkg_operations/common/funcs/funcs.go +++ b/.example/net/gtcp/pkg_operations/common/funcs/funcs.go @@ -30,7 +30,7 @@ func RecvPkg(conn *gtcp.Conn) (msg *types.Msg, err error) { return nil, err } else { msg = &types.Msg{} - err = json.UnmarshalUseNumber(data, msg) + err = json.Unmarshal(data, msg) if err != nil { return nil, fmt.Errorf("invalid package structure: %s", err.Error()) } diff --git a/.example/net/gtcp/pkg_operations/monitor/gtcp_monitor_server.go b/.example/net/gtcp/pkg_operations/monitor/gtcp_monitor_server.go index b0820320c..b9758f893 100644 --- a/.example/net/gtcp/pkg_operations/monitor/gtcp_monitor_server.go +++ b/.example/net/gtcp/pkg_operations/monitor/gtcp_monitor_server.go @@ -21,7 +21,7 @@ func main() { break } info := &types.NodeInfo{} - if err := json.UnmarshalUseNumber(data, info); err != nil { + if err := json.Unmarshal(data, info); err != nil { glog.Errorf("invalid package structure: %s", err.Error()) } else { glog.Println(info) From b2a15c259e24797c2965ae860747ca7dc90cd2a4 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 15 May 2021 23:21:38 +0800 Subject: [PATCH 262/492] improve unit testing case for package internal/json --- internal/json/json_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/json/json_test.go b/internal/json/json_test.go index 7ff8b7341..4df795b27 100644 --- a/internal/json/json_test.go +++ b/internal/json/json_test.go @@ -23,7 +23,6 @@ var ( Id: 1265476890672672808, Name: "john", } - userBytes, _ = json.Marshal(user) ) func TestMarshal(t *testing.T) { From 33567ef33890eb97e52b32d8a86c66b5f1a881df Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 15 May 2021 23:42:39 +0800 Subject: [PATCH 263/492] remove unit testing file for package internal/json --- internal/json/json_test.go | 92 -------------------------------------- 1 file changed, 92 deletions(-) delete mode 100644 internal/json/json_test.go diff --git a/internal/json/json_test.go b/internal/json/json_test.go deleted file mode 100644 index 4df795b27..000000000 --- a/internal/json/json_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package json_test - -import ( - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/internal/json" - "github.com/gogf/gf/test/gtest" - "testing" -) - -type User struct { - Id int64 `json:"id"` - Name string `json:"name"` -} - -var ( - user = &User{ - Id: 1265476890672672808, - Name: "john", - } -) - -func TestMarshal(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - b, err := json.Marshal(user) - t.AssertNil(err) - t.Assert(b, `{"id":1265476890672672808,"name":"john"}`) - }) -} - -func TestMarshalIndent(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - b, err := json.MarshalIndent(user, "#", "@") - t.AssertNil(err) - t.Assert(b, `{ -#@"id": 1265476890672672808, -#@"name": "john" -#}`) - }) -} - -func TestUnmarshal(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var m map[string]interface{} - b, _ := json.Marshal(g.Map{ - "user": user, - }) - err := json.Unmarshal(b, &m) - t.AssertNil(err) - // precision lost for big int. - t.Assert(m["user"], g.Map{ - "id": 1265476890672672800, - "name": user.Name, - }) - }) -} - -func TestUnmarshalUseNumber(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var m map[string]interface{} - b, _ := json.Marshal(g.Map{ - "user": user, - }) - err := json.UnmarshalUseNumber(b, &m) - t.AssertNil(err) - t.Assert(m["user"], g.Map{ - "id": user.Id, - "name": user.Name, - }) - }) -} - -func TestValid(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - m := g.Map{ - `{}`: true, - `{"id":1,"name":"john"}`: true, - `1`: true, - `"john"`: true, - `"`: false, - ``: false, - } - for k, v := range m { - t.Assert(json.Valid([]byte(k)), v) - } - }) -} From a757fbd37d062f3f9f91e8bc625323aa10c638ea Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 17 May 2021 00:07:06 +0800 Subject: [PATCH 264/492] improve package gcfg --- os/gcfg/gcfg.go | 433 +++---------------- os/gcfg/gcfg_config.go | 441 +++++++++++++++++--- os/gcfg/{gcfg_api.go => gcfg_config_api.go} | 0 os/gcfg/gcfg_error.go | 22 - os/gcfg/gcfg_instance.go | 48 --- 5 files changed, 452 insertions(+), 492 deletions(-) rename os/gcfg/{gcfg_api.go => gcfg_config_api.go} (100%) delete mode 100644 os/gcfg/gcfg_error.go delete mode 100644 os/gcfg/gcfg_instance.go diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index 76f5f7985..d9eac9f2b 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -8,32 +8,13 @@ package gcfg import ( - "bytes" - "errors" - "fmt" - "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/internal/intlog" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/util/gmode" - - "github.com/gogf/gf/os/gres" - "github.com/gogf/gf/container/garray" "github.com/gogf/gf/container/gmap" - "github.com/gogf/gf/encoding/gjson" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/gfsnotify" - "github.com/gogf/gf/os/glog" - "github.com/gogf/gf/os/gspath" + "github.com/gogf/gf/internal/intlog" + "github.com/gogf/gf/os/gcmd" ) -const ( - DefaultConfigFile = "config.toml" // The default configuration file name. - cmdEnvKey = "gf.gcfg" // Configuration key for command argument or environment. -) - -// Configuration struct. +// Config is the configuration manager. type Config struct { defaultName string // Default configuration file name. searchPaths *garray.StrArray // Searching path array. @@ -41,374 +22,82 @@ type Config struct { violenceCheck bool // Whether do violence check in value index searching. It affects the performance when set true(false in default). } -var ( - supportedFileTypes = []string{"toml", "yaml", "yml", "json", "ini", "xml"} - resourceTryFiles = []string{"", "/", "config/", "config", "/config", "/config/"} +const ( + DefaultName = "config" // DefaultName is the default group name for instance usage. + DefaultConfigFile = "config.toml" // DefaultConfigFile is the default configuration file name. + cmdEnvKey = "gf.gcfg" // cmdEnvKey is the configuration key for command argument or environment. + errorPrintKey = "gf.gcfg.errorprint" // errorPrintKey is used to specify the key controlling error printing to stdout. ) -// New returns a new configuration management object. -// The parameter `file` specifies the default configuration file name for reading. -func New(file ...string) *Config { +var ( + supportedFileTypes = []string{"toml", "yaml", "yml", "json", "ini", "xml"} // All supported file types suffixes. + resourceTryFiles = []string{"", "/", "config/", "config", "/config", "/config/"} // Prefix array for trying searching in resource manager. + instances = gmap.NewStrAnyMap(true) // Instances map containing configuration instances. + customConfigContentMap = gmap.NewStrStrMap(true) // Customized configuration content. +) + +// SetContent sets customized configuration content for specified `file`. +// The `file` is unnecessary param, default is DefaultConfigFile. +func SetContent(content string, file ...string) { name := DefaultConfigFile if len(file) > 0 { name = file[0] - } else { - // Custom default configuration file name from command line or environment. - if customFile := gcmd.GetOptWithEnv(fmt.Sprintf("%s.file", cmdEnvKey)).String(); customFile != "" { - name = customFile - } } - c := &Config{ - defaultName: name, - searchPaths: garray.NewStrArray(true), - jsonMap: gmap.NewStrAnyMap(true), - } - // Customized dir path from env/cmd. - if customPath := gcmd.GetOptWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).String(); customPath != "" { - if gfile.Exists(customPath) { - _ = c.SetPath(customPath) - } else { - if errorPrint() { - glog.Errorf("[gcfg] Configuration directory path does not exist: %s", customPath) + // Clear file cache for instances which cached `name`. + instances.LockFunc(func(m map[string]interface{}) { + if customConfigContentMap.Contains(name) { + for _, v := range m { + v.(*Config).jsonMap.Remove(name) } } - } else { - // Dir path of working dir. - if err := c.AddPath(gfile.Pwd()); err != nil { - intlog.Error(err) - } - - // Dir path of main package. - if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) { - if err := c.AddPath(mainPath); err != nil { - intlog.Error(err) - } - } - - // Dir path of binary. - if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { - if err := c.AddPath(selfPath); err != nil { - intlog.Error(err) - } - } - } - return c + customConfigContentMap.Set(name, content) + }) } -// SetPath sets the configuration directory path for file search. -// The parameter `path` can be absolute or relative path, -// but absolute path is strongly recommended. -func (c *Config) SetPath(path string) error { - var ( - isDir = false - realPath = "" - ) - if file := gres.Get(path); file != nil { - realPath = path - isDir = file.FileInfo().IsDir() - } else { - // Absolute path. - realPath = gfile.RealPath(path) - if realPath == "" { - // Relative path. - c.searchPaths.RLockFunc(func(array []string) { - for _, v := range array { - if path, _ := gspath.Search(v, path); path != "" { - realPath = path - break - } - } - }) - } - if realPath != "" { - isDir = gfile.IsDir(realPath) - } - } - // Path not exist. - if realPath == "" { - buffer := bytes.NewBuffer(nil) - if c.searchPaths.Len() > 0 { - buffer.WriteString(fmt.Sprintf("[gcfg] SetPath failed: cannot find directory \"%s\" in following paths:", path)) - c.searchPaths.RLockFunc(func(array []string) { - for k, v := range array { - buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) - } - }) - } else { - buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path)) - } - err := errors.New(buffer.String()) - if errorPrint() { - glog.Error(err) - } - return err - } - // Should be a directory. - if !isDir { - err := fmt.Errorf(`[gcfg] SetPath failed: path "%s" should be directory type`, path) - if errorPrint() { - glog.Error(err) - } - return err - } - // Repeated path check. - if c.searchPaths.Search(realPath) != -1 { - return nil - } - c.jsonMap.Clear() - c.searchPaths.Clear() - c.searchPaths.Append(realPath) - intlog.Print("SetPath:", realPath) - return nil -} - -// SetViolenceCheck sets whether to perform hierarchical conflict checking. -// This feature needs to be enabled when there is a level symbol in the key name. -// It is off in default. -// -// Note that, turning on this feature is quite expensive, and it is not recommended -// to allow separators in the key names. It is best to avoid this on the application side. -func (c *Config) SetViolenceCheck(check bool) { - c.violenceCheck = check - c.Clear() -} - -// AddPath adds a absolute or relative path to the search paths. -func (c *Config) AddPath(path string) error { - var ( - isDir = false - realPath = "" - ) - // It firstly checks the resource manager, - // and then checks the filesystem for the path. - if file := gres.Get(path); file != nil { - realPath = path - isDir = file.FileInfo().IsDir() - } else { - // Absolute path. - realPath = gfile.RealPath(path) - if realPath == "" { - // Relative path. - c.searchPaths.RLockFunc(func(array []string) { - for _, v := range array { - if path, _ := gspath.Search(v, path); path != "" { - realPath = path - break - } - } - }) - } - if realPath != "" { - isDir = gfile.IsDir(realPath) - } - } - if realPath == "" { - buffer := bytes.NewBuffer(nil) - if c.searchPaths.Len() > 0 { - buffer.WriteString(fmt.Sprintf("[gcfg] AddPath failed: cannot find directory \"%s\" in following paths:", path)) - c.searchPaths.RLockFunc(func(array []string) { - for k, v := range array { - buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) - } - }) - } else { - buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path)) - } - err := gerror.New(buffer.String()) - if errorPrint() { - glog.Error(err) - } - return err - } - if !isDir { - err := gerror.Newf(`[gcfg] AddPath failed: path "%s" should be directory type`, path) - if errorPrint() { - glog.Error(err) - } - return err - } - // Repeated path check. - if c.searchPaths.Search(realPath) != -1 { - return nil - } - c.searchPaths.Append(realPath) - intlog.Print("AddPath:", realPath) - return nil -} - -// SetFileName sets the default configuration file name. -func (c *Config) SetFileName(name string) *Config { - c.defaultName = name - return c -} - -// GetFileName returns the default configuration file name. -func (c *Config) GetFileName() string { - return c.defaultName -} - -// Available checks and returns whether configuration of given `file` is available. -func (c *Config) Available(file ...string) bool { - var name string - if len(file) > 0 && file[0] != "" { - name = file[0] - } else { - name = c.defaultName - } - if path, _ := c.GetFilePath(name); path != "" { - return true - } - if GetContent(name) != "" { - return true - } - return false -} - -// GetFilePath returns the absolute configuration file path for the given filename by `file`. -// If `file` is not passed, it returns the configuration file path of the default name. -// It returns an empty `path` string and an error if the given `file` does not exist. -func (c *Config) GetFilePath(file ...string) (path string, err error) { - name := c.defaultName +// GetContent returns customized configuration content for specified `file`. +// The `file` is unnecessary param, default is DefaultConfigFile. +func GetContent(file ...string) string { + name := DefaultConfigFile if len(file) > 0 { name = file[0] } - // Searching resource manager. - if !gres.IsEmpty() { - for _, v := range resourceTryFiles { - if file := gres.Get(v + name); file != nil { - path = file.Name() - return - } - } - c.searchPaths.RLockFunc(func(array []string) { - for _, prefix := range array { - for _, v := range resourceTryFiles { - if file := gres.Get(prefix + v + name); file != nil { - path = file.Name() - return - } - } - } - }) - } - c.autoCheckAndAddMainPkgPathToSearchPaths() - // Searching the file system. - c.searchPaths.RLockFunc(func(array []string) { - for _, prefix := range array { - prefix = gstr.TrimRight(prefix, `\/`) - if path, _ = gspath.Search(prefix, name); path != "" { - return - } - if path, _ = gspath.Search(prefix+gfile.Separator+"config", name); path != "" { - return - } - } - }) - // If it cannot find the path of `file`, it formats and returns a detailed error. - if path == "" { - var ( - buffer = bytes.NewBuffer(nil) - ) - if c.searchPaths.Len() > 0 { - buffer.WriteString(fmt.Sprintf(`[gcfg] cannot find config file "%s" in resource manager or the following paths:`, name)) - c.searchPaths.RLockFunc(func(array []string) { - index := 1 - for _, v := range array { - v = gstr.TrimRight(v, `\/`) - buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) - index++ - buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) - index++ - } - }) - } else { - buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path configured", name)) - } - err = gerror.New(buffer.String()) - } - return + return customConfigContentMap.Get(name) } -// autoCheckAndAddMainPkgPathToSearchPaths automatically checks and adds directory path of package main -// to the searching path list if it's currently in development environment. -func (c *Config) autoCheckAndAddMainPkgPathToSearchPaths() { - if gmode.IsDevelop() { - mainPkgPath := gfile.MainPkgPath() - if mainPkgPath != "" { - if !c.searchPaths.Contains(mainPkgPath) { - c.searchPaths.Append(mainPkgPath) - } - } - } -} - -// getJson returns a *gjson.Json object for the specified `file` content. -// It would print error if file reading fails. It return nil if any error occurs. -func (c *Config) getJson(file ...string) *gjson.Json { - var name string - if len(file) > 0 && file[0] != "" { +// RemoveContent removes the global configuration with specified `file`. +// If `name` is not passed, it removes configuration of the default group name. +func RemoveContent(file ...string) { + name := DefaultConfigFile + if len(file) > 0 { name = file[0] - } else { - name = c.defaultName } - r := c.jsonMap.GetOrSetFuncLock(name, func() interface{} { - var ( - err error - content string - filePath string - ) - // The configured content can be any kind of data type different from its file type. - isFromConfigContent := true - if content = GetContent(name); content == "" { - isFromConfigContent = false - filePath, err = c.GetFilePath(name) - if err != nil && errorPrint() { - glog.Error(err) - } - if filePath == "" { - return nil - } - if file := gres.Get(filePath); file != nil { - content = string(file.Content()) - } else { - content = gfile.GetContents(filePath) + // Clear file cache for instances which cached `name`. + instances.LockFunc(func(m map[string]interface{}) { + if customConfigContentMap.Contains(name) { + for _, v := range m { + v.(*Config).jsonMap.Remove(name) } + customConfigContentMap.Remove(name) } - // Note that the underlying configuration json object operations are concurrent safe. - var ( - j *gjson.Json - ) - dataType := gfile.ExtName(name) - if gjson.IsValidDataType(dataType) && !isFromConfigContent { - j, err = gjson.LoadContentType(dataType, content, true) - } else { - j, err = gjson.LoadContent(content, true) - } - if err == nil { - j.SetViolenceCheck(c.violenceCheck) - // Add monitor for this configuration file, - // any changes of this file will refresh its cache in Config object. - if filePath != "" && !gres.Contains(filePath) { - _, err = gfsnotify.Add(filePath, func(event *gfsnotify.Event) { - c.jsonMap.Remove(name) - }) - if err != nil && errorPrint() { - glog.Error(err) - } - } - return j - } - if errorPrint() { - if filePath != "" { - glog.Criticalf(`[gcfg] load config file "%s" failed: %s`, filePath, err.Error()) - } else { - glog.Criticalf(`[gcfg] load configuration failed: %s`, err.Error()) - } - } - return nil }) - if r != nil { - return r.(*gjson.Json) - } - return nil + + intlog.Printf(`RemoveContent: %s`, name) +} + +// ClearContent removes all global configuration contents. +func ClearContent() { + customConfigContentMap.Clear() + // Clear cache for all instances. + instances.LockFunc(func(m map[string]interface{}) { + for _, v := range m { + v.(*Config).jsonMap.Clear() + } + }) + + intlog.Print(`RemoveConfig`) +} + +// errorPrint checks whether printing error to stdout. +func errorPrint() bool { + return gcmd.GetOptWithEnv(errorPrintKey, true).Bool() } diff --git a/os/gcfg/gcfg_config.go b/os/gcfg/gcfg_config.go index 68adf275a..6034f1f88 100644 --- a/os/gcfg/gcfg_config.go +++ b/os/gcfg/gcfg_config.go @@ -7,72 +7,413 @@ package gcfg import ( + "bytes" + "errors" + "fmt" + "github.com/gogf/gf/container/garray" "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/encoding/gjson" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gfsnotify" + "github.com/gogf/gf/os/glog" + "github.com/gogf/gf/os/gres" + "github.com/gogf/gf/os/gspath" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/util/gmode" ) -var ( - // Customized configuration content. - configs = gmap.NewStrStrMap(true) -) - -// SetContent sets customized configuration content for specified `file`. -// The `file` is unnecessary param, default is DefaultConfigFile. -func SetContent(content string, file ...string) { +// New returns a new configuration management object. +// The parameter `file` specifies the default configuration file name for reading. +func New(file ...string) *Config { name := DefaultConfigFile if len(file) > 0 { name = file[0] + } else { + // Custom default configuration file name from command line or environment. + if customFile := gcmd.GetOptWithEnv(fmt.Sprintf("%s.file", cmdEnvKey)).String(); customFile != "" { + name = customFile + } } - // Clear file cache for instances which cached `name`. - instances.LockFunc(func(m map[string]interface{}) { - if configs.Contains(name) { - for _, v := range m { - v.(*Config).jsonMap.Remove(name) + c := &Config{ + defaultName: name, + searchPaths: garray.NewStrArray(true), + jsonMap: gmap.NewStrAnyMap(true), + } + // Customized dir path from env/cmd. + if customPath := gcmd.GetOptWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).String(); customPath != "" { + if gfile.Exists(customPath) { + _ = c.SetPath(customPath) + } else { + if errorPrint() { + glog.Errorf("[gcfg] Configuration directory path does not exist: %s", customPath) } } - configs.Set(name, content) - }) -} + } else { + // Dir path of working dir. + if err := c.AddPath(gfile.Pwd()); err != nil { + intlog.Error(err) + } -// GetContent returns customized configuration content for specified `file`. -// The `file` is unnecessary param, default is DefaultConfigFile. -func GetContent(file ...string) string { - name := DefaultConfigFile - if len(file) > 0 { - name = file[0] - } - return configs.Get(name) -} - -// RemoveContent removes the global configuration with specified `file`. -// If `name` is not passed, it removes configuration of the default group name. -func RemoveContent(file ...string) { - name := DefaultConfigFile - if len(file) > 0 { - name = file[0] - } - // Clear file cache for instances which cached `name`. - instances.LockFunc(func(m map[string]interface{}) { - if configs.Contains(name) { - for _, v := range m { - v.(*Config).jsonMap.Remove(name) + // Dir path of main package. + if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) { + if err := c.AddPath(mainPath); err != nil { + intlog.Error(err) } - configs.Remove(name) } - }) - intlog.Printf(`RemoveContent: %s`, name) + // Dir path of binary. + if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { + if err := c.AddPath(selfPath); err != nil { + intlog.Error(err) + } + } + } + return c } -// ClearContent removes all global configuration contents. -func ClearContent() { - configs.Clear() - // Clear cache for all instances. - instances.LockFunc(func(m map[string]interface{}) { - for _, v := range m { - v.(*Config).jsonMap.Clear() +// Instance returns an instance of Config with default settings. +// The parameter `name` is the name for the instance. But very note that, if the file "name.toml" +// exists in the configuration directory, it then sets it as the default configuration file. The +// toml file type is the default configuration file type. +func Instance(name ...string) *Config { + key := DefaultName + if len(name) > 0 && name[0] != "" { + key = name[0] + } + return instances.GetOrSetFuncLock(key, func() interface{} { + c := New() + // If it's not using default configuration or its configuration file is not available, + // it searches the possible configuration file according to the name and all supported + // file types. + if key != DefaultName || !c.Available() { + for _, fileType := range supportedFileTypes { + if file := fmt.Sprintf(`%s.%s`, key, fileType); c.Available(file) { + c.SetFileName(file) + break + } + } + } + return c + }).(*Config) +} + +// SetPath sets the configuration directory path for file search. +// The parameter `path` can be absolute or relative path, +// but absolute path is strongly recommended. +func (c *Config) SetPath(path string) error { + var ( + isDir = false + realPath = "" + ) + if file := gres.Get(path); file != nil { + realPath = path + isDir = file.FileInfo().IsDir() + } else { + // Absolute path. + realPath = gfile.RealPath(path) + if realPath == "" { + // Relative path. + c.searchPaths.RLockFunc(func(array []string) { + for _, v := range array { + if path, _ := gspath.Search(v, path); path != "" { + realPath = path + break + } + } + }) + } + if realPath != "" { + isDir = gfile.IsDir(realPath) + } + } + // Path not exist. + if realPath == "" { + buffer := bytes.NewBuffer(nil) + if c.searchPaths.Len() > 0 { + buffer.WriteString(fmt.Sprintf("[gcfg] SetPath failed: cannot find directory \"%s\" in following paths:", path)) + c.searchPaths.RLockFunc(func(array []string) { + for k, v := range array { + buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) + } + }) + } else { + buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path)) + } + err := errors.New(buffer.String()) + if errorPrint() { + glog.Error(err) + } + return err + } + // Should be a directory. + if !isDir { + err := fmt.Errorf(`[gcfg] SetPath failed: path "%s" should be directory type`, path) + if errorPrint() { + glog.Error(err) + } + return err + } + // Repeated path check. + if c.searchPaths.Search(realPath) != -1 { + return nil + } + c.jsonMap.Clear() + c.searchPaths.Clear() + c.searchPaths.Append(realPath) + intlog.Print("SetPath:", realPath) + return nil +} + +// SetViolenceCheck sets whether to perform hierarchical conflict checking. +// This feature needs to be enabled when there is a level symbol in the key name. +// It is off in default. +// +// Note that, turning on this feature is quite expensive, and it is not recommended +// to allow separators in the key names. It is best to avoid this on the application side. +func (c *Config) SetViolenceCheck(check bool) { + c.violenceCheck = check + c.Clear() +} + +// AddPath adds a absolute or relative path to the search paths. +func (c *Config) AddPath(path string) error { + var ( + isDir = false + realPath = "" + ) + // It firstly checks the resource manager, + // and then checks the filesystem for the path. + if file := gres.Get(path); file != nil { + realPath = path + isDir = file.FileInfo().IsDir() + } else { + // Absolute path. + realPath = gfile.RealPath(path) + if realPath == "" { + // Relative path. + c.searchPaths.RLockFunc(func(array []string) { + for _, v := range array { + if path, _ := gspath.Search(v, path); path != "" { + realPath = path + break + } + } + }) + } + if realPath != "" { + isDir = gfile.IsDir(realPath) + } + } + if realPath == "" { + buffer := bytes.NewBuffer(nil) + if c.searchPaths.Len() > 0 { + buffer.WriteString(fmt.Sprintf("[gcfg] AddPath failed: cannot find directory \"%s\" in following paths:", path)) + c.searchPaths.RLockFunc(func(array []string) { + for k, v := range array { + buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) + } + }) + } else { + buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path)) + } + err := gerror.New(buffer.String()) + if errorPrint() { + glog.Error(err) + } + return err + } + if !isDir { + err := gerror.Newf(`[gcfg] AddPath failed: path "%s" should be directory type`, path) + if errorPrint() { + glog.Error(err) + } + return err + } + // Repeated path check. + if c.searchPaths.Search(realPath) != -1 { + return nil + } + c.searchPaths.Append(realPath) + intlog.Print("AddPath:", realPath) + return nil +} + +// SetFileName sets the default configuration file name. +func (c *Config) SetFileName(name string) *Config { + c.defaultName = name + return c +} + +// GetFileName returns the default configuration file name. +func (c *Config) GetFileName() string { + return c.defaultName +} + +// Available checks and returns whether configuration of given `file` is available. +func (c *Config) Available(file ...string) bool { + var name string + if len(file) > 0 && file[0] != "" { + name = file[0] + } else { + name = c.defaultName + } + if path, _ := c.GetFilePath(name); path != "" { + return true + } + if GetContent(name) != "" { + return true + } + return false +} + +// GetFilePath returns the absolute configuration file path for the given filename by `file`. +// If `file` is not passed, it returns the configuration file path of the default name. +// It returns an empty `path` string and an error if the given `file` does not exist. +func (c *Config) GetFilePath(file ...string) (path string, err error) { + name := c.defaultName + if len(file) > 0 { + name = file[0] + } + // Searching resource manager. + if !gres.IsEmpty() { + for _, v := range resourceTryFiles { + if file := gres.Get(v + name); file != nil { + path = file.Name() + return + } + } + c.searchPaths.RLockFunc(func(array []string) { + for _, prefix := range array { + for _, v := range resourceTryFiles { + if file := gres.Get(prefix + v + name); file != nil { + path = file.Name() + return + } + } + } + }) + } + c.autoCheckAndAddMainPkgPathToSearchPaths() + // Searching the file system. + c.searchPaths.RLockFunc(func(array []string) { + for _, prefix := range array { + prefix = gstr.TrimRight(prefix, `\/`) + if path, _ = gspath.Search(prefix, name); path != "" { + return + } + if path, _ = gspath.Search(prefix+gfile.Separator+"config", name); path != "" { + return + } } }) - - intlog.Print(`RemoveConfig`) + // If it cannot find the path of `file`, it formats and returns a detailed error. + if path == "" { + var ( + buffer = bytes.NewBuffer(nil) + ) + if c.searchPaths.Len() > 0 { + buffer.WriteString(fmt.Sprintf(`[gcfg] cannot find config file "%s" in resource manager or the following paths:`, name)) + c.searchPaths.RLockFunc(func(array []string) { + index := 1 + for _, v := range array { + v = gstr.TrimRight(v, `\/`) + buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v)) + index++ + buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config")) + index++ + } + }) + } else { + buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path configured", name)) + } + err = gerror.New(buffer.String()) + } + return +} + +// autoCheckAndAddMainPkgPathToSearchPaths automatically checks and adds directory path of package main +// to the searching path list if it's currently in development environment. +func (c *Config) autoCheckAndAddMainPkgPathToSearchPaths() { + if gmode.IsDevelop() { + mainPkgPath := gfile.MainPkgPath() + if mainPkgPath != "" { + if !c.searchPaths.Contains(mainPkgPath) { + c.searchPaths.Append(mainPkgPath) + } + } + } +} + +// getJson returns a *gjson.Json object for the specified `file` content. +// It would print error if file reading fails. It return nil if any error occurs. +func (c *Config) getJson(file ...string) *gjson.Json { + var name string + if len(file) > 0 && file[0] != "" { + name = file[0] + } else { + name = c.defaultName + } + r := c.jsonMap.GetOrSetFuncLock(name, func() interface{} { + var ( + err error + content string + filePath string + ) + // The configured content can be any kind of data type different from its file type. + isFromConfigContent := true + if content = GetContent(name); content == "" { + isFromConfigContent = false + filePath, err = c.GetFilePath(name) + if err != nil && errorPrint() { + glog.Error(err) + } + if filePath == "" { + return nil + } + if file := gres.Get(filePath); file != nil { + content = string(file.Content()) + } else { + content = gfile.GetContents(filePath) + } + } + // Note that the underlying configuration json object operations are concurrent safe. + var ( + j *gjson.Json + ) + dataType := gfile.ExtName(name) + if gjson.IsValidDataType(dataType) && !isFromConfigContent { + j, err = gjson.LoadContentType(dataType, content, true) + } else { + j, err = gjson.LoadContent(content, true) + } + if err == nil { + j.SetViolenceCheck(c.violenceCheck) + // Add monitor for this configuration file, + // any changes of this file will refresh its cache in Config object. + if filePath != "" && !gres.Contains(filePath) { + _, err = gfsnotify.Add(filePath, func(event *gfsnotify.Event) { + c.jsonMap.Remove(name) + }) + if err != nil && errorPrint() { + glog.Error(err) + } + } + return j + } + if errorPrint() { + if filePath != "" { + glog.Criticalf(`[gcfg] load config file "%s" failed: %s`, filePath, err.Error()) + } else { + glog.Criticalf(`[gcfg] load configuration failed: %s`, err.Error()) + } + } + return nil + }) + if r != nil { + return r.(*gjson.Json) + } + return nil } diff --git a/os/gcfg/gcfg_api.go b/os/gcfg/gcfg_config_api.go similarity index 100% rename from os/gcfg/gcfg_api.go rename to os/gcfg/gcfg_config_api.go diff --git a/os/gcfg/gcfg_error.go b/os/gcfg/gcfg_error.go deleted file mode 100644 index fd7df93b3..000000000 --- a/os/gcfg/gcfg_error.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gcfg - -import ( - "github.com/gogf/gf/os/gcmd" -) - -const ( - // errorPrintKey is used to specify the key controlling error printing to stdout. - // This error is designed not to be returned by functions. - errorPrintKey = "gf.gcfg.errorprint" -) - -// errorPrint checks whether printing error to stdout. -func errorPrint() bool { - return gcmd.GetOptWithEnv(errorPrintKey, true).Bool() -} diff --git a/os/gcfg/gcfg_instance.go b/os/gcfg/gcfg_instance.go deleted file mode 100644 index 4ef396801..000000000 --- a/os/gcfg/gcfg_instance.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gcfg - -import ( - "fmt" - "github.com/gogf/gf/container/gmap" -) - -const ( - // Default group name for instance usage. - DefaultName = "config" -) - -var ( - // Instances map containing configuration instances. - instances = gmap.NewStrAnyMap(true) -) - -// Instance returns an instance of Config with default settings. -// The parameter `name` is the name for the instance. But very note that, if the file "name.toml" -// exists in the configuration directory, it then sets it as the default configuration file. The -// toml file type is the default configuration file type. -func Instance(name ...string) *Config { - key := DefaultName - if len(name) > 0 && name[0] != "" { - key = name[0] - } - return instances.GetOrSetFuncLock(key, func() interface{} { - c := New() - // If it's not using default configuration or its configuration file is not available, - // it searches the possible configuration file according to the name and all supported - // file types. - if key != DefaultName || !c.Available() { - for _, fileType := range supportedFileTypes { - if file := fmt.Sprintf(`%s.%s`, key, fileType); c.Available(file) { - c.SetFileName(file) - break - } - } - } - return c - }).(*Config) -} From 522f6cb455d1241b63610ef4a5b142b996cfeae6 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 17 May 2021 13:43:36 +0800 Subject: [PATCH 265/492] fix issue of failing in folder watching with no recursive option for package gfsnotify --- os/gfsnotify/gfsnotify.go | 4 +- os/gfsnotify/gfsnotify_watcher_loop.go | 86 +++++++++++++------------- os/gfsnotify/gfsnotify_z_unit_test.go | 25 ++++++++ 3 files changed, 70 insertions(+), 45 deletions(-) diff --git a/os/gfsnotify/gfsnotify.go b/os/gfsnotify/gfsnotify.go index 07817a8e5..619454189 100644 --- a/os/gfsnotify/gfsnotify.go +++ b/os/gfsnotify/gfsnotify.go @@ -91,8 +91,8 @@ func New() (*Watcher, error) { intlog.Printf("New watcher failed: %v", err) return nil, err } - w.startWatchLoop() - w.startEventLoop() + w.watchLoop() + w.eventLoop() return w, nil } diff --git a/os/gfsnotify/gfsnotify_watcher_loop.go b/os/gfsnotify/gfsnotify_watcher_loop.go index a05c663de..531225051 100644 --- a/os/gfsnotify/gfsnotify_watcher_loop.go +++ b/os/gfsnotify/gfsnotify_watcher_loop.go @@ -11,8 +11,8 @@ import ( "github.com/gogf/gf/internal/intlog" ) -// startWatchLoop starts the loop for event listening fro underlying inotify monitor. -func (w *Watcher) startWatchLoop() { +// watchLoop starts the loop for event listening fro underlying inotify monitor. +func (w *Watcher) watchLoop() { go func() { for { select { @@ -40,47 +40,8 @@ func (w *Watcher) startWatchLoop() { }() } -// getCallbacks searches and returns all callbacks with given . -// It also searches its parent for callbacks if they're recursive. -func (w *Watcher) getCallbacks(path string) (callbacks []*Callback) { - // Firstly add the callbacks of itself. - if v := w.callbacks.Get(path); v != nil { - for _, v := range v.(*glist.List).FrontAll() { - callback := v.(*Callback) - callbacks = append(callbacks, callback) - } - } - // Secondly searches its parent for callbacks. - dirPath := fileDir(path) - if v := w.callbacks.Get(dirPath); v != nil { - for _, v := range v.(*glist.List).FrontAll() { - callback := v.(*Callback) - if callback.recursive { - callbacks = append(callbacks, callback) - } - } - } - // Lastly searches the parent recursively for callbacks. - for { - parentDirPath := fileDir(dirPath) - if parentDirPath == dirPath { - break - } - if v := w.callbacks.Get(parentDirPath); v != nil { - for _, v := range v.(*glist.List).FrontAll() { - callback := v.(*Callback) - if callback.recursive { - callbacks = append(callbacks, callback) - } - } - } - dirPath = parentDirPath - } - return -} - -// startEventLoop is the core event handler. -func (w *Watcher) startEventLoop() { +// eventLoop is the core event handler. +func (w *Watcher) eventLoop() { go func() { for { if v := w.events.Pop(); v != nil { @@ -171,3 +132,42 @@ func (w *Watcher) startEventLoop() { } }() } + +// getCallbacks searches and returns all callbacks with given . +// It also searches its parents for callbacks if they're recursive. +func (w *Watcher) getCallbacks(path string) (callbacks []*Callback) { + // Firstly add the callbacks of itself. + if v := w.callbacks.Get(path); v != nil { + for _, v := range v.(*glist.List).FrontAll() { + callback := v.(*Callback) + callbacks = append(callbacks, callback) + } + } + // Secondly searches its direct parent for callbacks. + // It is special handling here, which is the different between `recursive` and `not recursive` logic + // for direct parent folder of `path` that events are from. + dirPath := fileDir(path) + if v := w.callbacks.Get(dirPath); v != nil { + for _, v := range v.(*glist.List).FrontAll() { + callback := v.(*Callback) + callbacks = append(callbacks, callback) + } + } + // Lastly searches all the parents of directory of `path` recursively for callbacks. + for { + parentDirPath := fileDir(dirPath) + if parentDirPath == dirPath { + break + } + if v := w.callbacks.Get(parentDirPath); v != nil { + for _, v := range v.(*glist.List).FrontAll() { + callback := v.(*Callback) + if callback.recursive { + callbacks = append(callbacks, callback) + } + } + } + dirPath = parentDirPath + } + return +} diff --git a/os/gfsnotify/gfsnotify_z_unit_test.go b/os/gfsnotify/gfsnotify_z_unit_test.go index a1f195dc1..2fecbb7f2 100644 --- a/os/gfsnotify/gfsnotify_z_unit_test.go +++ b/os/gfsnotify/gfsnotify_z_unit_test.go @@ -7,6 +7,7 @@ package gfsnotify_test import ( + "github.com/gogf/gf/container/garray" "testing" "time" @@ -191,3 +192,27 @@ func TestWatcher_Callback2(t *testing.T) { t.Assert(v2.Val(), 2) }) } + +func TestWatcher_WatchFolderWithoutRecursively(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + err error + array = garray.New(true) + dirPath = gfile.TempDir(gtime.TimestampNanoStr()) + ) + err = gfile.Mkdir(dirPath) + t.AssertNil(err) + + _, err = gfsnotify.Add(dirPath, func(event *gfsnotify.Event) { + array.Append(1) + }, false) + t.AssertNil(err) + time.Sleep(time.Millisecond * 100) + t.Assert(array.Len(), 0) + + err = gfile.PutContents(gfile.Join(dirPath, "1"), "1") + t.AssertNil(err) + time.Sleep(time.Millisecond * 100) + t.Assert(array.Len(), 1) + }) +} From 302e234bfe8f3aff2894a548212c0a684a126897 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 17 May 2021 19:17:04 +0800 Subject: [PATCH 266/492] comment update for package gfsnotify --- os/gfsnotify/gfsnotify_watcher.go | 1 + 1 file changed, 1 insertion(+) diff --git a/os/gfsnotify/gfsnotify_watcher.go b/os/gfsnotify/gfsnotify_watcher.go index 421b40a59..37067a988 100644 --- a/os/gfsnotify/gfsnotify_watcher.go +++ b/os/gfsnotify/gfsnotify_watcher.go @@ -61,6 +61,7 @@ func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), re } // addWithCallbackFunc adds the path to underlying monitor, creates and returns a callback object. +// Very note that if it calls multiple times with the same `path`, the latest one will overwrite the previous one. func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { // Check and convert the given path to absolute path. if t := fileRealPath(path); t == "" { From 2274a10cfd1109e85695f01aa75b78d382075ee4 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 17 May 2021 19:59:34 +0800 Subject: [PATCH 267/492] fx issue #1250 --- internal/structs/structs_field.go | 10 ++++ os/gtime/gtime_time.go | 5 ++ util/gvalid/gvalid.go | 22 ++++++- util/gvalid/gvalid_validator_check_struct.go | 34 +++++------ util/gvalid/gvalid_z_unit_checkstruct_test.go | 59 +++++++++++++++++-- 5 files changed, 105 insertions(+), 25 deletions(-) diff --git a/internal/structs/structs_field.go b/internal/structs/structs_field.go index dda5a996e..81ae1d960 100644 --- a/internal/structs/structs_field.go +++ b/internal/structs/structs_field.go @@ -14,6 +14,16 @@ func (f *Field) Tag(key string) string { return f.Field.Tag.Get(key) } +// TagLookup returns the value associated with key in the tag string. +// If the key is present in the tag the value (which may be empty) +// is returned. Otherwise the returned value will be the empty string. +// The ok return value reports whether the value was explicitly set in +// the tag string. If the tag does not have the conventional format, +// the value returned by Lookup is unspecified. +func (f *Field) TagLookup(key string) (value string, ok bool) { + return f.Field.Tag.Lookup(key) +} + // IsEmbedded returns true if the given field is an anonymous field (embedded) func (f *Field) IsEmbedded() bool { return f.Field.Anonymous diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 871013ed4..d3a02594a 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -464,3 +464,8 @@ func (t *Time) UnmarshalText(data []byte) error { } return gerror.Newf(`invalid time value: %s`, data) } + +// NoValidation marks this struct object will not be validated by package gvalid. +func (t *Time) NoValidation() { + +} diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 4a660a954..2a2e1316a 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -64,17 +64,35 @@ import ( // like: map[field] => string|map[rule]string type CustomMsg = map[string]interface{} +// doCheckStructWithParamMapInput is used for struct validation for internal function. +type doCheckStructWithParamMapInput struct { + Object interface{} // Can be type of struct/*struct. + ParamMap interface{} // Validation parameter map. Note that it acts different according attribute `UseParamMapInsteadOfObjectValue`. + UseParamMapInsteadOfObjectValue bool // Using `ParamMap` as its validation source instead of values from `Object`. + CustomRules interface{} // Custom validation rules. + CustomErrorMessageMap CustomMsg // Custom error message map for validation rules. +} + +// apiNoValidation is an interface that marks current struct not validated by package `gvalid`. +type apiNoValidation interface { + NoValidation() +} + const ( // regular expression pattern for single validation rule. singleRulePattern = `^([\w-]+):{0,1}(.*)` invalidRulesErrKey = "invalid_rules" invalidParamsErrKey = "invalid_params" invalidObjectErrKey = "invalid_object" + + // no validation tag name for struct attribute. + noValidationTagName = "nv" ) var ( - // defaultValidator is the default validator for package functions. - defaultValidator = New() + defaultValidator = New() // defaultValidator is the default validator for package functions. + structTagPriority = []string{"gvalid", "valid", "v"} // structTagPriority specifies the validation tag priority array. + aliasNameTagPriority = []string{"param", "params", "p"} // aliasNameTagPriority specifies the alias tag priority array. // all internal error keys. internalErrKeyMap = map[string]string{ diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 6aab61a49..5ed23c45e 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -10,24 +10,9 @@ import ( "github.com/gogf/gf/internal/structs" "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gutil" - "reflect" "strings" ) -// doCheckStructWithParamMapInput is used for struct validation for internal function. -type doCheckStructWithParamMapInput struct { - Object interface{} // Can be type of struct/*struct. - ParamMap interface{} // Validation parameter map. Note that it acts different according attribute `UseParamMapInsteadOfObjectValue`. - UseParamMapInsteadOfObjectValue bool // Using `ParamMap` as its validation source instead of values from `Object`. - CustomRules interface{} // Custom validation rules. - CustomErrorMessageMap CustomMsg // Custom error message map for validation rules. -} - -var ( - structTagPriority = []string{"gvalid", "valid", "v"} // structTagPriority specifies the validation tag priority array. - aliasNameTagPriority = []string{"param", "params", "p"} // aliasNameTagPriority specifies the alias tag priority array. -) - // CheckStruct validates struct and returns the error result. // // The parameter `object` should be type of struct/*struct. @@ -70,16 +55,27 @@ func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interfa func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapInput) *Error { var ( - errorMaps = make(ErrorMap) // Returning error. + // Returning error. + errorMaps = make(ErrorMap) ) fieldMap, err := structs.FieldMap(input.Object, aliasNameTagPriority, true) if err != nil { return newErrorStr("invalid_object", err.Error()) } - // It checks the struct recursively the its attribute is also a struct. + // It checks the struct recursively the its attribute is an embedded struct. for _, field := range fieldMap { - if field.OriginalKind() == reflect.Struct { - if err := v.CheckStruct(field.Value, input.CustomRules, input.CustomErrorMessageMap); err != nil { + if field.IsEmbedded() { + // No validation interface implements check. + if _, ok := field.Value.Interface().(apiNoValidation); ok { + continue + } + if _, ok := field.TagLookup(noValidationTagName); ok { + continue + } + recursiveInput := doCheckStructWithParamMapInput{} + recursiveInput = *input + recursiveInput.Object = field.Value + if err := v.doCheckStructWithParamMap(&recursiveInput); err != nil { // It merges the errors into single error map. for k, m := range err.errors { errorMaps[k] = m diff --git a/util/gvalid/gvalid_z_unit_checkstruct_test.go b/util/gvalid/gvalid_z_unit_checkstruct_test.go index da76790fb..ac65bd8b4 100755 --- a/util/gvalid/gvalid_z_unit_checkstruct_test.go +++ b/util/gvalid/gvalid_z_unit_checkstruct_test.go @@ -228,6 +228,57 @@ func Test_CheckStruct(t *testing.T) { }) } +func Test_CheckStruct_EmbeddedObject_Attribute(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Base struct { + Time *gtime.Time + } + type Object struct { + Base + Name string + Type int + } + rules := map[string]string{ + "Name": "required", + "Type": "required", + } + ruleMsg := map[string]interface{}{ + "Name": "名称必填", + "Type": "类型必填", + } + obj := &Object{} + obj.Type = 1 + obj.Name = "john" + obj.Time = gtime.Now() + err := gvalid.CheckStruct(context.TODO(), obj, rules, ruleMsg) + t.Assert(err, nil) + }) + gtest.C(t, func(t *gtest.T) { + type Base struct { + Name string + Type int + } + type Object struct { + Base Base + Name string + Type int + } + rules := map[string]string{ + "Name": "required", + "Type": "required", + } + ruleMsg := map[string]interface{}{ + "Name": "名称必填", + "Type": "类型必填", + } + obj := &Object{} + obj.Type = 1 + obj.Name = "john" + err := gvalid.CheckStruct(context.TODO(), obj, rules, ruleMsg) + t.Assert(err, nil) + }) +} + func Test_CheckStruct_With_EmbeddedObject(t *testing.T) { gtest.C(t, func(t *gtest.T) { type Pass struct { @@ -261,13 +312,13 @@ func Test_CheckStruct_With_StructAttribute(t *testing.T) { Pass2 string `valid:"password2@required|same:password1#请再次输入您的密码|您两次输入的密码不一致"` } type User struct { - Id int - Name string `valid:"name@required#请输入您的姓名"` - Passwords Pass + Pass + Id int + Name string `valid:"name@required#请输入您的姓名"` } user := &User{ Name: "", - Passwords: Pass{ + Pass: Pass{ Pass1: "1", Pass2: "2", }, From e5734425ba1c74fee5f14d37a6d8ac06999a0eab Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 17 May 2021 20:18:21 +0800 Subject: [PATCH 268/492] debug --- os/gfsnotify/gfsnotify_z_unit_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/os/gfsnotify/gfsnotify_z_unit_test.go b/os/gfsnotify/gfsnotify_z_unit_test.go index 2fecbb7f2..42df8cb32 100644 --- a/os/gfsnotify/gfsnotify_z_unit_test.go +++ b/os/gfsnotify/gfsnotify_z_unit_test.go @@ -7,6 +7,7 @@ package gfsnotify_test import ( + "fmt" "github.com/gogf/gf/container/garray" "testing" "time" @@ -204,6 +205,7 @@ func TestWatcher_WatchFolderWithoutRecursively(t *testing.T) { t.AssertNil(err) _, err = gfsnotify.Add(dirPath, func(event *gfsnotify.Event) { + fmt.Println(event.String()) array.Append(1) }, false) t.AssertNil(err) From b84ca9cc13cf601ff5580ccf6f19e421d896d4f1 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 17 May 2021 20:25:00 +0800 Subject: [PATCH 269/492] debug --- os/gfsnotify/gfsnotify_z_unit_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/os/gfsnotify/gfsnotify_z_unit_test.go b/os/gfsnotify/gfsnotify_z_unit_test.go index 42df8cb32..ca9730b05 100644 --- a/os/gfsnotify/gfsnotify_z_unit_test.go +++ b/os/gfsnotify/gfsnotify_z_unit_test.go @@ -212,8 +212,9 @@ func TestWatcher_WatchFolderWithoutRecursively(t *testing.T) { time.Sleep(time.Millisecond * 100) t.Assert(array.Len(), 0) - err = gfile.PutContents(gfile.Join(dirPath, "1"), "1") + f, err := gfile.Create(gfile.Join(dirPath, "1")) t.AssertNil(err) + t.AssertNil(f.Close()) time.Sleep(time.Millisecond * 100) t.Assert(array.Len(), 1) }) From 0dfd9688249c9f2853e2447a1fb982b869225b16 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 17 May 2021 21:26:39 +0800 Subject: [PATCH 270/492] comment update for package gconv/gproc --- os/gfsnotify/gfsnotify_z_unit_test.go | 3 +- os/gproc/gproc_manager.go | 34 ++++++++++++----------- os/gproc/gproc_signal.go | 2 +- util/gconv/gconv.go | 40 +++++++++++++-------------- util/gconv/gconv_maps.go | 4 +-- util/gconv/gconv_slice_any.go | 2 +- util/gconv/gconv_slice_float.go | 6 ++-- util/gconv/gconv_slice_int.go | 6 ++-- util/gconv/gconv_slice_str.go | 2 +- util/gconv/gconv_slice_uint.go | 6 ++-- util/gconv/gconv_time.go | 16 +++++------ 11 files changed, 61 insertions(+), 60 deletions(-) diff --git a/os/gfsnotify/gfsnotify_z_unit_test.go b/os/gfsnotify/gfsnotify_z_unit_test.go index ca9730b05..e1e5b1f95 100644 --- a/os/gfsnotify/gfsnotify_z_unit_test.go +++ b/os/gfsnotify/gfsnotify_z_unit_test.go @@ -7,7 +7,6 @@ package gfsnotify_test import ( - "fmt" "github.com/gogf/gf/container/garray" "testing" "time" @@ -205,7 +204,7 @@ func TestWatcher_WatchFolderWithoutRecursively(t *testing.T) { t.AssertNil(err) _, err = gfsnotify.Add(dirPath, func(event *gfsnotify.Event) { - fmt.Println(event.String()) + //fmt.Println(event.String()) array.Append(1) }, false) t.AssertNil(err) diff --git a/os/gproc/gproc_manager.go b/os/gproc/gproc_manager.go index 8f3cea114..cc448eb65 100644 --- a/os/gproc/gproc_manager.go +++ b/os/gproc/gproc_manager.go @@ -12,26 +12,27 @@ import ( "github.com/gogf/gf/container/gmap" ) -// 进程管理器 +// Manager is a process manager maintaining multiple processes. type Manager struct { - processes *gmap.IntAnyMap // 所管理的子进程map + processes *gmap.IntAnyMap // Process id to Process object mapping. } -// 创建一个进程管理器 +// NewManager creates and returns a new process manager. func NewManager() *Manager { return &Manager{ processes: gmap.NewIntAnyMap(true), } } -// 创建一个进程(不执行) +// NewProcess creates and returns a Process object. func (m *Manager) NewProcess(path string, args []string, environment []string) *Process { p := NewProcess(path, args, environment) p.Manager = m return p } -// 获取当前进程管理器中的一个进程 +// GetProcess retrieves and returns a Process object. +// It returns nil if it does not find the process with given `pid`. func (m *Manager) GetProcess(pid int) *Process { if v := m.processes.Get(pid); v != nil { return v.(*Process) @@ -39,7 +40,8 @@ func (m *Manager) GetProcess(pid int) *Process { return nil } -// 添加一个已存在进程到进程管理器中 +// AddProcess adds a process to current manager. +// It does nothing if the process with given `pid` does not exist. func (m *Manager) AddProcess(pid int) { if m.processes.Get(pid) == nil { if process, err := os.FindProcess(pid); err == nil { @@ -50,12 +52,12 @@ func (m *Manager) AddProcess(pid int) { } } -// 移除进程管理器中的指定进程 +// RemoveProcess removes a process from current manager. func (m *Manager) RemoveProcess(pid int) { m.processes.Remove(pid) } -// 获取所有的进程对象,构成列表返回 +// Processes retrieves and returns all processes in current manager. func (m *Manager) Processes() []*Process { processes := make([]*Process, 0) m.processes.RLockFunc(func(m map[int]interface{}) { @@ -66,12 +68,12 @@ func (m *Manager) Processes() []*Process { return processes } -// 获取所有的进程pid,构成列表返回 +// Pids retrieves and returns all process id array in current manager. func (m *Manager) Pids() []int { return m.processes.Keys() } -// 等待所有子进程结束 +// WaitAll waits until all process exit. func (m *Manager) WaitAll() { processes := m.Processes() if len(processes) > 0 { @@ -81,7 +83,7 @@ func (m *Manager) WaitAll() { } } -// 关闭所有的进程 +// KillAll kills all processes in current manager. func (m *Manager) KillAll() error { for _, p := range m.Processes() { if err := p.Kill(); err != nil { @@ -91,7 +93,7 @@ func (m *Manager) KillAll() error { return nil } -// 向所有进程发送信号量 +// SignalAll sends a signal `sig` to all processes in current manager. func (m *Manager) SignalAll(sig os.Signal) error { for _, p := range m.Processes() { if err := p.Signal(sig); err != nil { @@ -101,24 +103,24 @@ func (m *Manager) SignalAll(sig os.Signal) error { return nil } -// 向所有进程发送消息 +// Send sends data bytes to all processes in current manager. func (m *Manager) Send(data []byte) { for _, p := range m.Processes() { p.Send(data) } } -// 向指定进程发送消息 +// SendTo sneds data bytes to specified processe in current manager. func (m *Manager) SendTo(pid int, data []byte) error { return Send(pid, data) } -// 清空管理器 +// Clear removes all processes in current manager. func (m *Manager) Clear() { m.processes.Clear() } -// 当前进程总数 +// Size returns the size of processes in current manager. func (m *Manager) Size() int { return m.processes.Size() } diff --git a/os/gproc/gproc_signal.go b/os/gproc/gproc_signal.go index 40ad5464b..6a2b9b700 100644 --- a/os/gproc/gproc_signal.go +++ b/os/gproc/gproc_signal.go @@ -53,7 +53,7 @@ func AddSigHandlerShutdown(handler SigHandler) { } } -// ListenSignal blocks and does signal listening and handling. +// Listen blocks and does signal listening and handling. func Listen() { signals := make([]os.Signal, 0) for sig, _ := range signalHandlerMap { diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 271c23cb7..af74e7369 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -45,7 +45,7 @@ var ( StructTagPriority = []string{"gconv", "param", "params", "c", "p", "json"} ) -// Convert converts the variable `i` to the type `t`, the type `t` is specified by string. +// Convert converts the variable `any` to the type `t`, the type `t` is specified by string. // The optional parameter `params` is used for additional necessary parameter for this conversion. // It supports common types conversion as its conversion based on type name string. func Convert(any interface{}, t string, params ...interface{}) interface{} { @@ -277,7 +277,7 @@ func Convert(any interface{}, t string, params ...interface{}) interface{} { } } -// Byte converts `i` to byte. +// Byte converts `any` to byte. func Byte(any interface{}) byte { if v, ok := any.(byte); ok { return v @@ -285,7 +285,7 @@ func Byte(any interface{}) byte { return Uint8(any) } -// Bytes converts `i` to []byte. +// Bytes converts `any` to []byte. func Bytes(any interface{}) []byte { if any == nil { return nil @@ -303,7 +303,7 @@ func Bytes(any interface{}) []byte { } } -// Rune converts `i` to rune. +// Rune converts `any` to rune. func Rune(any interface{}) rune { if v, ok := any.(rune); ok { return v @@ -311,7 +311,7 @@ func Rune(any interface{}) rune { return rune(Int32(any)) } -// Runes converts `i` to []rune. +// Runes converts `any` to []rune. func Runes(any interface{}) []rune { if v, ok := any.([]rune); ok { return v @@ -319,7 +319,7 @@ func Runes(any interface{}) []rune { return []rune(String(any)) } -// String converts `i` to string. +// String converts `any` to string. // It's most common used converting function. func String(any interface{}) string { if any == nil { @@ -422,8 +422,8 @@ func String(any interface{}) string { } } -// Bool converts `i` to bool. -// It returns false if `i` is: false, "", 0, "false", "off", "no", empty slice/map. +// Bool converts `any` to bool. +// It returns false if `any` is: false, "", 0, "false", "off", "no", empty slice/map. func Bool(any interface{}) bool { if any == nil { return false @@ -467,7 +467,7 @@ func Bool(any interface{}) bool { } } -// Int converts `i` to int. +// Int converts `any` to int. func Int(any interface{}) int { if any == nil { return 0 @@ -478,7 +478,7 @@ func Int(any interface{}) int { return int(Int64(any)) } -// Int8 converts `i` to int8. +// Int8 converts `any` to int8. func Int8(any interface{}) int8 { if any == nil { return 0 @@ -489,7 +489,7 @@ func Int8(any interface{}) int8 { return int8(Int64(any)) } -// Int16 converts `i` to int16. +// Int16 converts `any` to int16. func Int16(any interface{}) int16 { if any == nil { return 0 @@ -500,7 +500,7 @@ func Int16(any interface{}) int16 { return int16(Int64(any)) } -// Int32 converts `i` to int32. +// Int32 converts `any` to int32. func Int32(any interface{}) int32 { if any == nil { return 0 @@ -511,7 +511,7 @@ func Int32(any interface{}) int32 { return int32(Int64(any)) } -// Int64 converts `i` to int64. +// Int64 converts `any` to int64. func Int64(any interface{}) int64 { if any == nil { return 0 @@ -592,7 +592,7 @@ func Int64(any interface{}) int64 { } } -// Uint converts `i` to uint. +// Uint converts `any` to uint. func Uint(any interface{}) uint { if any == nil { return 0 @@ -603,7 +603,7 @@ func Uint(any interface{}) uint { return uint(Uint64(any)) } -// Uint8 converts `i` to uint8. +// Uint8 converts `any` to uint8. func Uint8(any interface{}) uint8 { if any == nil { return 0 @@ -614,7 +614,7 @@ func Uint8(any interface{}) uint8 { return uint8(Uint64(any)) } -// Uint16 converts `i` to uint16. +// Uint16 converts `any` to uint16. func Uint16(any interface{}) uint16 { if any == nil { return 0 @@ -625,7 +625,7 @@ func Uint16(any interface{}) uint16 { return uint16(Uint64(any)) } -// Uint32 converts `i` to uint32. +// Uint32 converts `any` to uint32. func Uint32(any interface{}) uint32 { if any == nil { return 0 @@ -636,7 +636,7 @@ func Uint32(any interface{}) uint32 { return uint32(Uint64(any)) } -// Uint64 converts `i` to uint64. +// Uint64 converts `any` to uint64. func Uint64(any interface{}) uint64 { if any == nil { return 0 @@ -699,7 +699,7 @@ func Uint64(any interface{}) uint64 { } } -// Float32 converts `i` to float32. +// Float32 converts `any` to float32. func Float32(any interface{}) float32 { if any == nil { return 0 @@ -720,7 +720,7 @@ func Float32(any interface{}) float32 { } } -// Float64 converts `i` to float64. +// Float64 converts `any` to float64. func Float64(any interface{}) float64 { if any == nil { return 0 diff --git a/util/gconv/gconv_maps.go b/util/gconv/gconv_maps.go index 931b181cf..cc0dd33fb 100644 --- a/util/gconv/gconv_maps.go +++ b/util/gconv/gconv_maps.go @@ -23,7 +23,7 @@ func SliceStruct(params interface{}, pointer interface{}, mapping ...map[string] return Structs(params, pointer, mapping...) } -// Maps converts `i` to []map[string]interface{}. +// Maps converts `value` to []map[string]interface{}. // Note that it automatically checks and converts json string to []map if `value` is string/[]byte. func Maps(value interface{}, tags ...string) []map[string]interface{} { if value == nil { @@ -68,7 +68,7 @@ func Maps(value interface{}, tags ...string) []map[string]interface{} { } } -// MapsDeep converts `i` to []map[string]interface{} recursively. +// MapsDeep converts `value` to []map[string]interface{} recursively. // // TODO completely implement the recursive converting for all types. func MapsDeep(value interface{}, tags ...string) []map[string]interface{} { diff --git a/util/gconv/gconv_slice_any.go b/util/gconv/gconv_slice_any.go index e249c56ab..90b5b372b 100644 --- a/util/gconv/gconv_slice_any.go +++ b/util/gconv/gconv_slice_any.go @@ -15,7 +15,7 @@ func SliceAny(any interface{}) []interface{} { return Interfaces(any) } -// Interfaces converts `i` to []interface{}. +// Interfaces converts `any` to []interface{}. func Interfaces(any interface{}) []interface{} { if any == nil { return nil diff --git a/util/gconv/gconv_slice_float.go b/util/gconv/gconv_slice_float.go index d3d442c03..86dd7ebe9 100644 --- a/util/gconv/gconv_slice_float.go +++ b/util/gconv/gconv_slice_float.go @@ -23,12 +23,12 @@ func SliceFloat64(any interface{}) []float64 { return Floats(any) } -// Floats converts `i` to []float64. +// Floats converts `any` to []float64. func Floats(any interface{}) []float64 { return Float64s(any) } -// Float32s converts `i` to []float32. +// Float32s converts `any` to []float32. func Float32s(any interface{}) []float32 { if any == nil { return nil @@ -148,7 +148,7 @@ func Float32s(any interface{}) []float32 { return array } -// Float64s converts `i` to []float64. +// Float64s converts `any` to []float64. func Float64s(any interface{}) []float64 { if any == nil { return nil diff --git a/util/gconv/gconv_slice_int.go b/util/gconv/gconv_slice_int.go index f19afadd4..1195d2053 100644 --- a/util/gconv/gconv_slice_int.go +++ b/util/gconv/gconv_slice_int.go @@ -23,7 +23,7 @@ func SliceInt64(any interface{}) []int64 { return Int64s(any) } -// Ints converts `i` to []int. +// Ints converts `any` to []int. func Ints(any interface{}) []int { if any == nil { return nil @@ -153,7 +153,7 @@ func Ints(any interface{}) []int { return array } -// Int32s converts `i` to []int32. +// Int32s converts `any` to []int32. func Int32s(any interface{}) []int32 { if any == nil { return nil @@ -283,7 +283,7 @@ func Int32s(any interface{}) []int32 { return array } -// Int64s converts `i` to []int64. +// Int64s converts `any` to []int64. func Int64s(any interface{}) []int64 { if any == nil { return nil diff --git a/util/gconv/gconv_slice_str.go b/util/gconv/gconv_slice_str.go index ab003cec2..0d235d529 100644 --- a/util/gconv/gconv_slice_str.go +++ b/util/gconv/gconv_slice_str.go @@ -13,7 +13,7 @@ func SliceStr(any interface{}) []string { return Strings(any) } -// Strings converts `i` to []string. +// Strings converts `any` to []string. func Strings(any interface{}) []string { if any == nil { return nil diff --git a/util/gconv/gconv_slice_uint.go b/util/gconv/gconv_slice_uint.go index f2277573b..2cb3321d9 100644 --- a/util/gconv/gconv_slice_uint.go +++ b/util/gconv/gconv_slice_uint.go @@ -23,7 +23,7 @@ func SliceUint64(any interface{}) []uint64 { return Uint64s(any) } -// Uints converts `i` to []uint. +// Uints converts `any` to []uint. func Uints(any interface{}) []uint { if any == nil { return nil @@ -149,7 +149,7 @@ func Uints(any interface{}) []uint { return array } -// Uint32s converts `i` to []uint32. +// Uint32s converts `any` to []uint32. func Uint32s(any interface{}) []uint32 { if any == nil { return nil @@ -274,7 +274,7 @@ func Uint32s(any interface{}) []uint32 { return array } -// Uint64s converts `i` to []uint64. +// Uint64s converts `any` to []uint64. func Uint64s(any interface{}) []uint64 { if any == nil { return nil diff --git a/util/gconv/gconv_time.go b/util/gconv/gconv_time.go index 006188985..98aac418d 100644 --- a/util/gconv/gconv_time.go +++ b/util/gconv/gconv_time.go @@ -13,7 +13,7 @@ import ( "github.com/gogf/gf/os/gtime" ) -// Time converts `i` to time.Time. +// Time converts `any` to time.Time. func Time(any interface{}, format ...string) time.Time { // It's already this type. if len(format) == 0 { @@ -27,9 +27,9 @@ func Time(any interface{}, format ...string) time.Time { return time.Time{} } -// Duration converts `i` to time.Duration. -// If `i` is string, then it uses time.ParseDuration to convert it. -// If `i` is numeric, then it converts `i` as nanoseconds. +// Duration converts `any` to time.Duration. +// If `any` is string, then it uses time.ParseDuration to convert it. +// If `any` is numeric, then it converts `any` as nanoseconds. func Duration(any interface{}) time.Duration { // It's already this type. if v, ok := any.(time.Duration); ok { @@ -43,10 +43,10 @@ func Duration(any interface{}) time.Duration { return time.Duration(Int64(any)) } -// GTime converts `i` to *gtime.Time. -// The parameter `format` can be used to specify the format of `i`. -// If no `format` given, it converts `i` using gtime.NewFromTimeStamp if `i` is numeric, -// or using gtime.StrToTime if `i` is string. +// GTime converts `any` to *gtime.Time. +// The parameter `format` can be used to specify the format of `any`. +// If no `format` given, it converts `any` using gtime.NewFromTimeStamp if `any` is numeric, +// or using gtime.StrToTime if `any` is string. func GTime(any interface{}, format ...string) *gtime.Time { if any == nil { return nil From c8c28770fb2ce6f805e1ff1beb9826bbdc5cee41 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 18 May 2021 20:51:31 +0800 Subject: [PATCH 271/492] change Error from struct to interface for package gvalid;error string update for package gdb --- .example/i18n/gi18n/gi18n-dir.go | 5 +- .example/i18n/gi18n/gi18n-file.go | 5 +- .example/i18n/gi18n/gi18n.go | 12 +++- .example/i18n/gi18n/http_view_i18n.go | 12 ++-- .../net/ghttp/client/middleware/client.go | 64 +++++++++++++++++++ .../net/ghttp/client/middleware/server.go | 17 +++++ .../ghttp/server/request/json-xml/test2.go | 2 +- .../server/request/validation/validation2.go | 2 +- database/gdb/gdb.go | 12 +++- frame/gins/gins_database.go | 4 +- i18n/gi18n/gi18n.go | 1 - i18n/gi18n/gi18n_manager.go | 1 - util/gvalid/gvalid.go | 8 +-- util/gvalid/gvalid_error.go | 49 ++++++++------ util/gvalid/gvalid_validator_check.go | 6 +- util/gvalid/gvalid_validator_check_map.go | 4 +- util/gvalid/gvalid_validator_check_struct.go | 22 ++++--- util/gvalid/gvalid_z_example_test.go | 2 + util/gvalid/gvalid_z_unit_custom_rule_test.go | 8 +-- util/gvalid/gvalid_z_unit_i18n_test.go | 2 +- 20 files changed, 175 insertions(+), 63 deletions(-) create mode 100644 .example/net/ghttp/client/middleware/client.go create mode 100644 .example/net/ghttp/client/middleware/server.go diff --git a/.example/i18n/gi18n/gi18n-dir.go b/.example/i18n/gi18n/gi18n-dir.go index e9359c5ea..552298176 100644 --- a/.example/i18n/gi18n/gi18n-dir.go +++ b/.example/i18n/gi18n/gi18n-dir.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "github.com/gogf/gf/i18n/gi18n" @@ -13,6 +14,6 @@ func main() { if err != nil { panic(err) } - fmt.Println(t.Translate(`hello`)) - fmt.Println(t.Translate(`{#hello}{#world}!`)) + fmt.Println(t.Translate(context.TODO(), `hello`)) + fmt.Println(t.Translate(context.TODO(), `{#hello}{#world}!`)) } diff --git a/.example/i18n/gi18n/gi18n-file.go b/.example/i18n/gi18n/gi18n-file.go index 1a6d337c3..f1a4423ee 100644 --- a/.example/i18n/gi18n/gi18n-file.go +++ b/.example/i18n/gi18n/gi18n-file.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "github.com/gogf/gf/i18n/gi18n" @@ -13,6 +14,6 @@ func main() { if err != nil { panic(err) } - fmt.Println(t.Translate(`hello`)) - fmt.Println(t.Translate(`{#hello}{#world}!`)) + fmt.Println(t.Translate(context.TODO(), `hello`)) + fmt.Println(t.Translate(context.TODO(), `{#hello}{#world}!`)) } diff --git a/.example/i18n/gi18n/gi18n.go b/.example/i18n/gi18n/gi18n.go index 630d5a954..17d830c0d 100644 --- a/.example/i18n/gi18n/gi18n.go +++ b/.example/i18n/gi18n/gi18n.go @@ -1,8 +1,10 @@ package main import ( + "context" "fmt" "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/i18n/gi18n" ) func main() { @@ -10,6 +12,12 @@ func main() { orderId = 865271654 orderAmount = 99.8 ) - fmt.Println(g.I18n().Tfl(`en`, `{#OrderPaid}`, orderId, orderAmount)) - fmt.Println(g.I18n().Tfl(`zh-CN`, `{#OrderPaid}`, orderId, orderAmount)) + fmt.Println(g.I18n().Tf( + gi18n.WithLanguage(context.TODO(), `en`), + `{#OrderPaid}`, orderId, orderAmount, + )) + fmt.Println(g.I18n().Tf( + gi18n.WithLanguage(context.TODO(), `zh-CN`), + `{#OrderPaid}`, orderId, orderAmount, + )) } diff --git a/.example/i18n/gi18n/http_view_i18n.go b/.example/i18n/gi18n/http_view_i18n.go index ca5956c7b..1f1cc2302 100644 --- a/.example/i18n/gi18n/http_view_i18n.go +++ b/.example/i18n/gi18n/http_view_i18n.go @@ -2,15 +2,19 @@ package main import ( "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/i18n/gi18n" "github.com/gogf/gf/net/ghttp" ) func main() { - g.I18n().SetPath("/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/i18n/gi18n/i18n") s := g.Server() - s.BindHandler("/", func(r *ghttp.Request) { - r.Response.WriteTplContent(`{#hello}{#world}!`, g.Map{ - "I18nLanguage": r.Get("lang", "zh-CN"), + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(func(r *ghttp.Request) { + r.SetCtx(gi18n.WithLanguage(r.Context(), "zh-CN")) + r.Middleware.Next() + }) + group.ALL("/", func(r *ghttp.Request) { + r.Response.WriteTplContent(`{#hello}{#world}!`) }) }) s.SetPort(8199) diff --git a/.example/net/ghttp/client/middleware/client.go b/.example/net/ghttp/client/middleware/client.go new file mode 100644 index 000000000..7ba07b4bf --- /dev/null +++ b/.example/net/ghttp/client/middleware/client.go @@ -0,0 +1,64 @@ +package main + +import ( + "bytes" + "fmt" + "github.com/gogf/gf/container/garray" + "github.com/gogf/gf/crypto/gmd5" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/internal/json" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/guid" + "github.com/gogf/gf/util/gutil" + "io/ioutil" + "net/http" +) + +const ( + appId = "123" + appSecret = "456" +) + +// 注入统一的接口签名参数 +func injectSignature(jsonContent []byte) []byte { + var m map[string]interface{} + _ = json.Unmarshal(jsonContent, &m) + if len(m) > 0 { + m["appid"] = appId + m["nonce"] = guid.S() + m["timestamp"] = gtime.Timestamp() + var ( + keyArray = garray.NewSortedStrArrayFrom(gutil.Keys(m)) + sigContent string + ) + keyArray.Iterator(func(k int, v string) bool { + sigContent += v + sigContent += gconv.String(m[v]) + return true + }) + m["signature"] = gmd5.MustEncryptString(gmd5.MustEncryptString(sigContent) + appSecret) + jsonContent, _ = json.Marshal(m) + } + return jsonContent +} + +func main() { + c := g.Client() + c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) { + bodyBytes, _ := ioutil.ReadAll(r.Body) + if len(bodyBytes) > 0 { + // 注入签名相关参数,修改Request原有的提交参数 + bodyBytes = injectSignature(bodyBytes) + r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) + r.ContentLength = int64(len(bodyBytes)) + } + return c.Next(r) + }) + content := c.ContentJson().PostContent("http://127.0.0.1:8199/", g.Map{ + "name": "goframe", + "site": "https://goframe.org", + }) + fmt.Println(content) +} diff --git a/.example/net/ghttp/client/middleware/server.go b/.example/net/ghttp/client/middleware/server.go new file mode 100644 index 000000000..8770dd634 --- /dev/null +++ b/.example/net/ghttp/client/middleware/server.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" +) + +func main() { + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.ALL("/", func(r *ghttp.Request) { + r.Response.Write(r.GetMap()) + }) + }) + s.SetPort(8199) + s.Run() +} diff --git a/.example/net/ghttp/server/request/json-xml/test2.go b/.example/net/ghttp/server/request/json-xml/test2.go index 5e3aed050..ced00e20d 100644 --- a/.example/net/ghttp/server/request/json-xml/test2.go +++ b/.example/net/ghttp/server/request/json-xml/test2.go @@ -25,7 +25,7 @@ func main() { //fmt.Println(r.GetBody()) if err := r.Parse(&req); err != nil { // Validation error. - if v, ok := err.(*gvalid.Error); ok { + if v, ok := err.(gvalid.Error); ok { r.Response.WriteJsonExit(RegisterRes{ Code: 1, Error: v.FirstString(), diff --git a/.example/net/ghttp/server/request/validation/validation2.go b/.example/net/ghttp/server/request/validation/validation2.go index ccee1e3f9..2800feab1 100644 --- a/.example/net/ghttp/server/request/validation/validation2.go +++ b/.example/net/ghttp/server/request/validation/validation2.go @@ -24,7 +24,7 @@ func main() { var req *RegisterReq if err := r.Parse(&req); err != nil { // Validation error. - if v, ok := err.(*gvalid.Error); ok { + if v, ok := err.(gvalid.Error); ok { r.Response.WriteJsonExit(RegisterRes{ Code: 1, Error: v.FirstString(), diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 24e6228db..8421900d5 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -339,7 +339,7 @@ func New(group ...string) (db DB, err error) { defer configs.RUnlock() if len(configs.config) < 1 { - return nil, gerror.New("empty database configuration") + return nil, gerror.New("database configuration is empty, please set the database configuration before using") } if _, ok := configs.config[groupName]; ok { if node, err := getConfigNodeByGroup(groupName, true); err == nil { @@ -358,13 +358,19 @@ func New(group ...string) (db DB, err error) { } return c.db, nil } else { - return nil, gerror.New(fmt.Sprintf(`unsupported database type "%s"`, node.Type)) + return nil, gerror.Newf( + `cannot find database driver for specified database type "%s", did you misspell type name "%s" or forget importing the database driver?`, + node.Type, node.Type, + ) } } else { return nil, err } } else { - return nil, gerror.New(fmt.Sprintf(`database configuration node "%s" is not found`, groupName)) + return nil, gerror.Newf( + `database configuration node "%s" is not found, did you misspell group name "%s" or miss the database configuration?`, + groupName, groupName, + ) } } diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index 2e92392ed..26e97b5fe 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -54,14 +54,14 @@ func Database(name ...string) gdb.DB { if exampleConfigFilePath, _ := Config().GetFilePath(exampleFileName); exampleConfigFilePath != "" { panic(gerror.Wrapf( err, - `configuration file "%s" not found, but found "%s", did you miss renaming the configuration example file?`, + `configuration file "%s" not found, but found "%s", did you miss renaming the example configuration file?`, Config().GetFileName(), exampleFileName, )) } else { panic(gerror.Wrapf( err, - `configuration file "%s" not found, did you miss the configuration file or the file name setting?`, + `configuration file "%s" not found, did you miss the configuration file or the misspell the configuration file name?`, Config().GetFileName(), )) } diff --git a/i18n/gi18n/gi18n.go b/i18n/gi18n/gi18n.go index bb2cc07e4..460239807 100644 --- a/i18n/gi18n/gi18n.go +++ b/i18n/gi18n/gi18n.go @@ -41,7 +41,6 @@ func TranslateFormat(ctx context.Context, format string, values ...interface{}) } // Translate translates with configured language and returns the translated content. -// The parameter specifies custom translation language ignoring configured language. func Translate(ctx context.Context, content string) string { return Instance().Translate(ctx, content) } diff --git a/i18n/gi18n/gi18n_manager.go b/i18n/gi18n/gi18n_manager.go index 9dc6a8639..0949c1f44 100644 --- a/i18n/gi18n/gi18n_manager.go +++ b/i18n/gi18n/gi18n_manager.go @@ -138,7 +138,6 @@ func (m *Manager) TranslateFormat(ctx context.Context, format string, values ... } // Translate translates with configured language. -// The parameter specifies custom translation language ignoring configured language. func (m *Manager) Translate(ctx context.Context, content string) string { m.init() m.mu.RLock() diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 2a2e1316a..b9d234dae 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -189,7 +189,7 @@ var ( // string/map/struct/*struct. // The optional parameter `params` specifies the extra validation parameters for some rules // like: required-*、same、different, etc. -func Check(ctx context.Context, value interface{}, rules string, messages interface{}, params ...interface{}) *Error { +func Check(ctx context.Context, value interface{}, rules string, messages interface{}, params ...interface{}) Error { return defaultValidator.Ctx(ctx).Check(value, rules, messages, params...) } @@ -198,7 +198,7 @@ func Check(ctx context.Context, value interface{}, rules string, messages interf // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func CheckMap(ctx context.Context, params interface{}, rules interface{}, messages ...CustomMsg) *Error { +func CheckMap(ctx context.Context, params interface{}, rules interface{}, messages ...CustomMsg) Error { return defaultValidator.Ctx(ctx).CheckMap(params, rules, messages...) } @@ -208,7 +208,7 @@ func CheckMap(ctx context.Context, params interface{}, rules interface{}, messag // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func CheckStruct(ctx context.Context, object interface{}, rules interface{}, messages ...CustomMsg) *Error { +func CheckStruct(ctx context.Context, object interface{}, rules interface{}, messages ...CustomMsg) Error { return defaultValidator.Ctx(ctx).CheckStruct(object, rules, messages...) } @@ -218,7 +218,7 @@ func CheckStruct(ctx context.Context, object interface{}, rules interface{}, mes // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func CheckStructWithParamMap(ctx context.Context, object interface{}, paramMap interface{}, rules interface{}, messages ...CustomMsg) *Error { +func CheckStructWithParamMap(ctx context.Context, object interface{}, paramMap interface{}, rules interface{}, messages ...CustomMsg) Error { return defaultValidator.Ctx(ctx).CheckStructWithParamMap(object, paramMap, rules, messages...) } diff --git a/util/gvalid/gvalid_error.go b/util/gvalid/gvalid_error.go index 7a2a30141..c55da3f56 100644 --- a/util/gvalid/gvalid_error.go +++ b/util/gvalid/gvalid_error.go @@ -14,19 +14,28 @@ import ( ) // Error is the validation error for validation result. -type Error struct { - rules []string // Rules by sequence, which is used for keeping error sequence. - errors ErrorMap // Error map. - firstKey string // The first error rule key(nil in default). - firstItem map[string]string // The first error rule value(nil in default). +type Error interface { + Current() error + Error() string + FirstItem() (key string, messages map[string]string) + FirstRule() (rule string, err string) + FirstString() (err string) + Map() map[string]string + Maps() map[string]map[string]string + String() string + Strings() (errs []string) } -// ErrorMap is the validation error map: -// map[field]map[rule]message -type ErrorMap map[string]map[string]string +// validationError is the validation error for validation result. +type validationError struct { + rules []string // Rules by sequence, which is used for keeping error sequence. + errors map[string]map[string]string // Error map:map[field]map[rule]message + firstKey string // The first error rule key(nil in default). + firstItem map[string]string // The first error rule value(nil in default). +} // newError creates and returns a validation error. -func newError(rules []string, errors map[string]map[string]string) *Error { +func newError(rules []string, errors map[string]map[string]string) *validationError { for field, m := range errors { for k, v := range m { v = strings.Replace(v, ":attribute", field, -1) @@ -36,14 +45,14 @@ func newError(rules []string, errors map[string]map[string]string) *Error { } errors[field] = m } - return &Error{ + return &validationError{ rules: rules, errors: errors, } } // newErrorStr creates and returns a validation error by string. -func newErrorStr(key, err string) *Error { +func newErrorStr(key, err string) *validationError { return newError(nil, map[string]map[string]string{ "__gvalid__": { key: err, @@ -52,7 +61,7 @@ func newErrorStr(key, err string) *Error { } // Map returns the first error message as map. -func (e *Error) Map() map[string]string { +func (e *validationError) Map() map[string]string { if e == nil { return map[string]string{} } @@ -61,7 +70,7 @@ func (e *Error) Map() map[string]string { } // Maps returns all error messages as map. -func (e *Error) Maps() ErrorMap { +func (e *validationError) Maps() map[string]map[string]string { if e == nil { return nil } @@ -69,7 +78,7 @@ func (e *Error) Maps() ErrorMap { } // FirstItem returns the field name and error messages for the first validation rule error. -func (e *Error) FirstItem() (key string, messages map[string]string) { +func (e *validationError) FirstItem() (key string, messages map[string]string) { if e == nil { return "", map[string]string{} } @@ -97,7 +106,7 @@ func (e *Error) FirstItem() (key string, messages map[string]string) { } // FirstRule returns the first error rule and message string. -func (e *Error) FirstRule() (rule string, err string) { +func (e *validationError) FirstRule() (rule string, err string) { if e == nil { return "", "" } @@ -127,7 +136,7 @@ func (e *Error) FirstRule() (rule string, err string) { // FirstString returns the first error message as string. // Note that the returned message might be different if it has no sequence. -func (e *Error) FirstString() (err string) { +func (e *validationError) FirstString() (err string) { if e == nil { return "" } @@ -136,7 +145,7 @@ func (e *Error) FirstString() (err string) { } // Current is alis of FirstString, which implements interface gerror.ApiCurrent. -func (e *Error) Current() error { +func (e *validationError) Current() error { if e == nil { return nil } @@ -145,7 +154,7 @@ func (e *Error) Current() error { } // String returns all error messages as string, multiple error messages joined using char ';'. -func (e *Error) String() string { +func (e *validationError) String() string { if e == nil { return "" } @@ -153,7 +162,7 @@ func (e *Error) String() string { } // Error implements interface of error.Error. -func (e *Error) Error() string { +func (e *validationError) Error() string { if e == nil { return "" } @@ -161,7 +170,7 @@ func (e *Error) Error() string { } // Strings returns all error messages as string array. -func (e *Error) Strings() (errs []string) { +func (e *validationError) Strings() (errs []string) { if e == nil { return []string{} } diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index c37e4c66f..86e367cce 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -35,12 +35,12 @@ type apiTime interface { // string/map/struct/*struct. // The optional parameter `params` specifies the extra validation parameters for some rules // like: required-*、same、different, etc. -func (v *Validator) Check(value interface{}, rules string, messages interface{}, paramMap ...interface{}) *Error { +func (v *Validator) Check(value interface{}, rules string, messages interface{}, paramMap ...interface{}) Error { return v.doCheck("", value, rules, messages, paramMap...) } // doCheck does the really rules validation for single key-value. -func (v *Validator) doCheck(key string, value interface{}, rules string, messages interface{}, paramMap ...interface{}) *Error { +func (v *Validator) doCheck(key string, value interface{}, rules string, messages interface{}, paramMap ...interface{}) Error { // If there's no validation rules, it does nothing and returns quickly. if rules == "" { return nil @@ -136,7 +136,7 @@ func (v *Validator) doCheck(key string, value interface{}, rules string, message index++ } if len(errorMsgArray) > 0 { - return newError([]string{rules}, ErrorMap{ + return newError([]string{rules}, map[string]map[string]string{ key: errorMsgArray, }) } diff --git a/util/gvalid/gvalid_validator_check_map.go b/util/gvalid/gvalid_validator_check_map.go index 3229e2752..2f24759a3 100644 --- a/util/gvalid/gvalid_validator_check_map.go +++ b/util/gvalid/gvalid_validator_check_map.go @@ -16,7 +16,7 @@ import ( // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error { +func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) Error { // If there's no validation rules, it does nothing and returns quickly. if params == nil || rules == nil { return nil @@ -25,7 +25,7 @@ func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ... checkRules = make(map[string]string) customMsgs = make(CustomMsg) errorRules = make([]string, 0) - errorMaps = make(ErrorMap) + errorMaps = make(map[string]map[string]string) ) switch v := rules.(type) { // Sequence tag: []sequence tag diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 5ed23c45e..abb4c7829 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -16,10 +16,10 @@ import ( // CheckStruct validates struct and returns the error result. // // The parameter `object` should be type of struct/*struct. -// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result +// The parameter `customRules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. -// The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func (v *Validator) CheckStruct(object interface{}, customRules interface{}, customErrorMessageMap ...CustomMsg) *Error { +// The optional parameter `customErrorMessageMap` specifies the custom error messages for specified keys and rules. +func (v *Validator) CheckStruct(object interface{}, customRules interface{}, customErrorMessageMap ...CustomMsg) Error { var message CustomMsg if len(customErrorMessageMap) > 0 { message = customErrorMessageMap[0] @@ -36,10 +36,11 @@ func (v *Validator) CheckStruct(object interface{}, customRules interface{}, cus // CheckStructWithParamMap validates struct with given parameter map and returns the error result. // // The parameter `object` should be type of struct/*struct. -// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result +// The parameter `paramMap` should be type of map, which specifies the parameter map used in validation. +// The parameter `customRules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. -// The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interface{}, customRules interface{}, customErrorMessageMap ...CustomMsg) *Error { +// The optional parameter `customErrorMessageMap` specifies the custom error messages for specified keys and rules. +func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interface{}, customRules interface{}, customErrorMessageMap ...CustomMsg) Error { var message CustomMsg if len(customErrorMessageMap) > 0 { message = customErrorMessageMap[0] @@ -53,10 +54,10 @@ func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interfa }) } -func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapInput) *Error { +func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapInput) Error { var ( // Returning error. - errorMaps = make(ErrorMap) + errorMaps = make(map[string]map[string]string) ) fieldMap, err := structs.FieldMap(input.Object, aliasNameTagPriority, true) if err != nil { @@ -77,7 +78,7 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn recursiveInput.Object = field.Value if err := v.doCheckStructWithParamMap(&recursiveInput); err != nil { // It merges the errors into single error map. - for k, m := range err.errors { + for k, m := range err.(*validationError).errors { errorMaps[k] = m } } @@ -158,7 +159,8 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn } } } - + // Merge the custom validation rules with rules in struct tag. + // The custom rules has the most high priority that can overwrite the struct tag rules. for _, field := range tagField { fieldName := field.Name() // sequence tag == struct tag diff --git a/util/gvalid/gvalid_z_example_test.go b/util/gvalid/gvalid_z_example_test.go index fac360194..3f7648bfe 100644 --- a/util/gvalid/gvalid_z_example_test.go +++ b/util/gvalid/gvalid_z_example_test.go @@ -187,4 +187,6 @@ func ExampleRegisterRule_OverwriteRequired() { // It's required // rule deleted // It's required + // + // } diff --git a/util/gvalid/gvalid_z_unit_custom_rule_test.go b/util/gvalid/gvalid_z_unit_custom_rule_test.go index 326c42ebd..e4a7f45a6 100644 --- a/util/gvalid/gvalid_z_unit_custom_rule_test.go +++ b/util/gvalid/gvalid_z_unit_custom_rule_test.go @@ -78,7 +78,7 @@ func Test_CustomRule2(t *testing.T) { gtest.C(t, func(t *gtest.T) { errStr := "data map should not be empty" t.Assert(gvalid.Check(context.TODO(), g.Map{}, rule, errStr).String(), errStr) - t.Assert(gvalid.Check(context.TODO(), g.Map{"k": "v"}, rule, errStr).String(), nil) + t.Assert(gvalid.Check(context.TODO(), g.Map{"k": "v"}, rule, errStr), nil) }) // Error with struct validation. gtest.C(t, func(t *gtest.T) { @@ -121,8 +121,8 @@ func Test_CustomRule_AllowEmpty(t *testing.T) { // Check. gtest.C(t, func(t *gtest.T) { errStr := "error" - t.Assert(gvalid.Check(context.TODO(), "", rule, errStr).String(), "") - t.Assert(gvalid.Check(context.TODO(), "gf", rule, errStr).String(), "") + t.Assert(gvalid.Check(context.TODO(), "", rule, errStr), nil) + t.Assert(gvalid.Check(context.TODO(), "gf", rule, errStr), nil) t.Assert(gvalid.Check(context.TODO(), "gf2", rule, errStr).String(), errStr) }) // Error with struct validation. @@ -136,7 +136,7 @@ func Test_CustomRule_AllowEmpty(t *testing.T) { Data: "123456", } err := gvalid.CheckStruct(context.TODO(), st, nil) - t.Assert(err.String(), "") + t.Assert(err, nil) }) // No error with struct validation. gtest.C(t, func(t *gtest.T) { diff --git a/util/gvalid/gvalid_z_unit_i18n_test.go b/util/gvalid/gvalid_z_unit_i18n_test.go index bb27a3f4e..16bc2d809 100644 --- a/util/gvalid/gvalid_z_unit_i18n_test.go +++ b/util/gvalid/gvalid_z_unit_i18n_test.go @@ -18,7 +18,7 @@ import ( func TestValidator_I18n(t *testing.T) { var ( - err *gvalid.Error + err gvalid.Error i18nManager = gi18n.New(gi18n.Options{Path: gdebug.TestDataPath("i18n")}) ctxCn = gi18n.WithLanguage(context.TODO(), "cn") validator = gvalid.New().I18n(i18nManager) From 0bd1ea07a7750b486a314d5f68bb0f6500e05a0a Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 18 May 2021 20:52:39 +0800 Subject: [PATCH 272/492] example update --- .../server/request/validation/{ => validation1}/validation1.go | 0 .../server/request/validation/{ => validation2}/validation2.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .example/net/ghttp/server/request/validation/{ => validation1}/validation1.go (100%) rename .example/net/ghttp/server/request/validation/{ => validation2}/validation2.go (100%) diff --git a/.example/net/ghttp/server/request/validation/validation1.go b/.example/net/ghttp/server/request/validation/validation1/validation1.go similarity index 100% rename from .example/net/ghttp/server/request/validation/validation1.go rename to .example/net/ghttp/server/request/validation/validation1/validation1.go diff --git a/.example/net/ghttp/server/request/validation/validation2.go b/.example/net/ghttp/server/request/validation/validation2/validation2.go similarity index 100% rename from .example/net/ghttp/server/request/validation/validation2.go rename to .example/net/ghttp/server/request/validation/validation2/validation2.go From ea0340db8e440a33c9502cd395253ae7fd9be74e Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 19 May 2021 09:25:49 +0800 Subject: [PATCH 273/492] improve validation manager feature for package gvalid --- frame/g/g_object.go | 8 +- frame/g/g_setting.go | 2 +- net/ghttp/ghttp_request_param.go | 4 +- util/gvalid/gvalid.go | 43 +- util/gvalid/gvalid_validator.go | 64 ++- util/gvalid/gvalid_validator_check.go | 22 +- util/gvalid/gvalid_validator_check_map.go | 23 +- util/gvalid/gvalid_validator_check_struct.go | 68 +-- util/gvalid/gvalid_z_example_test.go | 12 +- util/gvalid/gvalid_z_unit_basic_all_test.go | 408 +++++++++--------- util/gvalid/gvalid_z_unit_checkstruct_test.go | 12 +- util/gvalid/gvalid_z_unit_custom_rule_test.go | 14 +- util/gvalid/gvalid_z_unit_customerror_test.go | 8 +- util/gvalid/gvalid_z_unit_i18n_test.go | 8 +- 14 files changed, 352 insertions(+), 344 deletions(-) diff --git a/frame/g/g_object.go b/frame/g/g_object.go index d4bdfdef0..d3dfb888c 100644 --- a/frame/g/g_object.go +++ b/frame/g/g_object.go @@ -18,9 +18,10 @@ import ( "github.com/gogf/gf/os/glog" "github.com/gogf/gf/os/gres" "github.com/gogf/gf/os/gview" + "github.com/gogf/gf/util/gvalid" ) -// Client is a convenience function, that creates and returns a new HTTP client. +// Client is a convenience function, which creates and returns a new HTTP client. func Client() *ghttp.Client { return ghttp.NewClient() } @@ -110,3 +111,8 @@ func Model(tableNameOrStruct ...interface{}) *gdb.Model { func Redis(name ...string) *gredis.Redis { return gins.Redis(name...) } + +// Validator is a convenience function, which creates and returns a new validation manager object. +func Validator() *gvalid.Validator { + return gvalid.New() +} diff --git a/frame/g/g_setting.go b/frame/g/g_setting.go index a8adff947..ddb9e966a 100644 --- a/frame/g/g_setting.go +++ b/frame/g/g_setting.go @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/net/ghttp" ) -// SetEnabled enables/disables the GoFrame internal logging manually. +// SetDebug enables/disables the GoFrame internal logging manually. // Note that this function is not concurrent safe, be aware of the DATA RACE, // which means you should call this function in your boot but not the runtime. func SetDebug(enabled bool) { diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index d79788d65..1da2af60a 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -102,7 +102,7 @@ func (r *Request) doParse(pointer interface{}, requestType int) error { } } // Validation. - if err := gvalid.CheckStructWithParamMap(r.Context(), pointer, data, nil); err != nil { + if err := gvalid.CheckStructWithData(r.Context(), pointer, data, nil); err != nil { return err } @@ -119,7 +119,7 @@ func (r *Request) doParse(pointer interface{}, requestType int) error { return err } for i := 0; i < reflectVal2.Len(); i++ { - if err := gvalid.CheckStructWithParamMap( + if err := gvalid.CheckStructWithData( r.Context(), reflectVal2.Index(i), j.GetMap(gconv.String(i)), diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index b9d234dae..532b6fcfd 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -64,15 +64,6 @@ import ( // like: map[field] => string|map[rule]string type CustomMsg = map[string]interface{} -// doCheckStructWithParamMapInput is used for struct validation for internal function. -type doCheckStructWithParamMapInput struct { - Object interface{} // Can be type of struct/*struct. - ParamMap interface{} // Validation parameter map. Note that it acts different according attribute `UseParamMapInsteadOfObjectValue`. - UseParamMapInsteadOfObjectValue bool // Using `ParamMap` as its validation source instead of values from `Object`. - CustomRules interface{} // Custom validation rules. - CustomErrorMessageMap CustomMsg // Custom error message map for validation rules. -} - // apiNoValidation is an interface that marks current struct not validated by package `gvalid`. type apiNoValidation interface { NoValidation() @@ -179,7 +170,7 @@ var ( } ) -// Check checks single value with specified rules. +// CheckValue checks single value with specified rules. // It returns nil if successful validation. // // The parameter `value` can be any type of variable, which will be converted to string @@ -189,8 +180,12 @@ var ( // string/map/struct/*struct. // The optional parameter `params` specifies the extra validation parameters for some rules // like: required-*、same、different, etc. -func Check(ctx context.Context, value interface{}, rules string, messages interface{}, params ...interface{}) Error { - return defaultValidator.Ctx(ctx).Check(value, rules, messages, params...) +func CheckValue(ctx context.Context, value interface{}, rules string, messages interface{}, params ...interface{}) Error { + var data interface{} + if len(params) > 0 { + data = params[0] + } + return defaultValidator.Ctx(ctx).Rules(rules).Data(data).Messages(messages).CheckValue(value) } // CheckMap validates map and returns the error result. It returns nil if with successful validation. @@ -199,27 +194,39 @@ func Check(ctx context.Context, value interface{}, rules string, messages interf // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. func CheckMap(ctx context.Context, params interface{}, rules interface{}, messages ...CustomMsg) Error { - return defaultValidator.Ctx(ctx).CheckMap(params, rules, messages...) + var customErrorMessages CustomMsg + if len(messages) > 0 { + customErrorMessages = messages[0] + } + return defaultValidator.Ctx(ctx).Rules(rules).Messages(customErrorMessages).CheckMap(params) } -// CheckStruct validates strcut and returns the error result. +// CheckStruct validates struct and returns the error result. // // The parameter `object` should be type of struct/*struct. // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. func CheckStruct(ctx context.Context, object interface{}, rules interface{}, messages ...CustomMsg) Error { - return defaultValidator.Ctx(ctx).CheckStruct(object, rules, messages...) + var customErrorMessages CustomMsg + if len(messages) > 0 { + customErrorMessages = messages[0] + } + return defaultValidator.Ctx(ctx).Rules(rules).Messages(customErrorMessages).CheckStruct(object) } -// CheckStructWithParamMap validates struct with given parameter map and returns the error result. +// CheckStructWithData validates struct with given parameter map and returns the error result. // // The parameter `object` should be type of struct/*struct. // The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result // if `rules` is type of []string. // The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func CheckStructWithParamMap(ctx context.Context, object interface{}, paramMap interface{}, rules interface{}, messages ...CustomMsg) Error { - return defaultValidator.Ctx(ctx).CheckStructWithParamMap(object, paramMap, rules, messages...) +func CheckStructWithData(ctx context.Context, object interface{}, data interface{}, rules interface{}, messages ...CustomMsg) Error { + var customErrorMessages CustomMsg + if len(messages) > 0 { + customErrorMessages = messages[0] + } + return defaultValidator.Ctx(ctx).Data(data).Rules(rules).Messages(customErrorMessages).CheckStruct(object) } // parseSequenceTag parses one sequence tag to field, rule and error message. diff --git a/util/gvalid/gvalid_validator.go b/util/gvalid/gvalid_validator.go index de6ace5f1..a1592e362 100644 --- a/util/gvalid/gvalid_validator.go +++ b/util/gvalid/gvalid_validator.go @@ -11,11 +11,16 @@ import ( "github.com/gogf/gf/i18n/gi18n" ) -// Validator is the validation manager. +// Validator is the validation manager for chaining operations. type Validator struct { - ctx context.Context // Context containing custom context variables. - i18nManager *gi18n.Manager // I18n manager for error message translation. - + ctx context.Context // Context containing custom context variables. + i18nManager *gi18n.Manager // I18n manager for error message translation. + key string // Single validation key. + value interface{} // Single validation value. + data interface{} // Validation data, which is usually a map. + rules interface{} // Custom validation data. + messages interface{} // Custom validation error messages, which can be string or type of CustomMsg. + useDataInsteadOfObjectAttributes bool // Using `data` as its validation source instead of attribute values from `Object`. } // New creates and returns a new Validator. @@ -26,14 +31,49 @@ func New() *Validator { } } -// I18n sets the i18n manager for the validator. -func (v *Validator) I18n(i18nManager *gi18n.Manager) *Validator { - v.i18nManager = i18nManager - return v +// Clone creates and returns a new Validator which is a shallow copy of current one. +func (v *Validator) Clone() *Validator { + newValidator := New() + *newValidator = *v + return newValidator } -// Ctx is a chaining operation function which sets the context for next validation. -func (v *Validator) Ctx(ctx context.Context) *Validator { - v.ctx = ctx - return v +// I18n sets the i18n manager for the validator. +func (v *Validator) I18n(i18nManager *gi18n.Manager) *Validator { + newValidator := v.Clone() + newValidator.i18nManager = i18nManager + return newValidator +} + +// Ctx is a chaining operation function, which sets the context for next validation. +func (v *Validator) Ctx(ctx context.Context) *Validator { + newValidator := v.Clone() + newValidator.ctx = ctx + return newValidator +} + +// Data is a chaining operation function, which sets validation data for current operation. +// The parameter `data` usually be type of map, which specifies the parameter map used in validation. +// Calling this function also sets `useDataInsteadOfObjectAttributes` true no mather the `data` is nil or not. +func (v *Validator) Data(data interface{}) *Validator { + newValidator := v.Clone() + newValidator.data = data + newValidator.useDataInsteadOfObjectAttributes = true + return newValidator +} + +// Rules is a chaining operation function, which sets custom validation rules for current operation. +func (v *Validator) Rules(rules interface{}) *Validator { + newValidator := v.Clone() + newValidator.rules = rules + return newValidator +} + +// Messages is a chaining operation function, which sets custom error messages for current operation. +// The parameter `messages` can be type of string/[]string/map[string]string. It supports sequence in error result +// if `rules` is type of []string. +func (v *Validator) Messages(messages interface{}) *Validator { + newValidator := v.Clone() + newValidator.messages = messages + return newValidator } diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index 86e367cce..ec2e66a83 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -25,22 +25,14 @@ type apiTime interface { IsZero() bool } -// Check checks single value with specified rules. +// CheckValue checks single value with specified rules. // It returns nil if successful validation. -// -// The parameter `value` can be any type of variable, which will be converted to string -// for validation. -// The parameter `rules` can be one or more rules, multiple rules joined using char '|'. -// The parameter `messages` specifies the custom error messages, which can be type of: -// string/map/struct/*struct. -// The optional parameter `params` specifies the extra validation parameters for some rules -// like: required-*、same、different, etc. -func (v *Validator) Check(value interface{}, rules string, messages interface{}, paramMap ...interface{}) Error { - return v.doCheck("", value, rules, messages, paramMap...) +func (v *Validator) CheckValue(value interface{}) Error { + return v.doCheckValue("", value, gconv.String(v.rules), v.messages, v.data) } -// doCheck does the really rules validation for single key-value. -func (v *Validator) doCheck(key string, value interface{}, rules string, messages interface{}, paramMap ...interface{}) Error { +// doCheckSingleValue does the really rules validation for single key-value. +func (v *Validator) doCheckValue(key string, value interface{}, rules string, messages interface{}, paramMap ...interface{}) Error { // If there's no validation rules, it does nothing and returns quickly. if rules == "" { return nil @@ -51,7 +43,7 @@ func (v *Validator) doCheck(key string, value interface{}, rules string, message data = make(map[string]interface{}) errorMsgArray = make(map[string]string) ) - if len(paramMap) > 0 { + if len(paramMap) > 0 && paramMap[0] != nil { data = gconv.Map(paramMap[0]) } // Custom error messages handling. @@ -108,7 +100,7 @@ func (v *Validator) doCheck(key string, value interface{}, rules string, message dataMap map[string]interface{} message = v.getErrorMessageByRule(ruleKey, customMsgMap) ) - if len(paramMap) > 0 { + if len(paramMap) > 0 && paramMap[0] != nil { dataMap = gconv.Map(paramMap[0]) } if err := f(ruleItems[index], value, message, dataMap); err != nil { diff --git a/util/gvalid/gvalid_validator_check_map.go b/util/gvalid/gvalid_validator_check_map.go index 2f24759a3..e829ddcec 100644 --- a/util/gvalid/gvalid_validator_check_map.go +++ b/util/gvalid/gvalid_validator_check_map.go @@ -12,13 +12,14 @@ import ( ) // CheckMap validates map and returns the error result. It returns nil if with successful validation. -// -// The parameter `rules` can be type of []string/map[string]string. It supports sequence in error result -// if `rules` is type of []string. -// The optional parameter `messages` specifies the custom error messages for specified keys and rules. -func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) Error { +// The parameter `params` should be type of map. +func (v *Validator) CheckMap(params interface{}) Error { + return v.doCheckMap(params) +} + +func (v *Validator) doCheckMap(params interface{}) Error { // If there's no validation rules, it does nothing and returns quickly. - if params == nil || rules == nil { + if params == nil || v.rules == nil { return nil } var ( @@ -27,7 +28,7 @@ func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ... errorRules = make([]string, 0) errorMaps = make(map[string]map[string]string) ) - switch v := rules.(type) { + switch v := v.rules.(type) { // Sequence tag: []sequence tag // Sequence has order for error results. case []string: @@ -76,13 +77,13 @@ func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ... "invalid params type: convert to map failed", ) } - if len(messages) > 0 && len(messages[0]) > 0 { + if msg, ok := v.messages.(CustomMsg); ok && len(msg) > 0 { if len(customMsgs) > 0 { - for k, v := range messages[0] { + for k, v := range msg { customMsgs[k] = v } } else { - customMsgs = messages[0] + customMsgs = msg } } var value interface{} @@ -95,7 +96,7 @@ func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ... value = v } // It checks each rule and its value in loop. - if e := v.doCheck(key, value, rule, customMsgs[key], data); e != nil { + if e := v.doCheckValue(key, value, rule, customMsgs[key], data); e != nil { _, item := e.FirstItem() // =========================================================== // Only in map and struct validations, if value is nil or empty diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index abb4c7829..62c578caf 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -14,52 +14,17 @@ import ( ) // CheckStruct validates struct and returns the error result. -// // The parameter `object` should be type of struct/*struct. -// The parameter `customRules` can be type of []string/map[string]string. It supports sequence in error result -// if `rules` is type of []string. -// The optional parameter `customErrorMessageMap` specifies the custom error messages for specified keys and rules. -func (v *Validator) CheckStruct(object interface{}, customRules interface{}, customErrorMessageMap ...CustomMsg) Error { - var message CustomMsg - if len(customErrorMessageMap) > 0 { - message = customErrorMessageMap[0] - } - return v.doCheckStructWithParamMap(&doCheckStructWithParamMapInput{ - Object: object, - ParamMap: nil, - UseParamMapInsteadOfObjectValue: false, - CustomRules: customRules, - CustomErrorMessageMap: message, - }) +func (v *Validator) CheckStruct(object interface{}) Error { + return v.doCheckStruct(object) } -// CheckStructWithParamMap validates struct with given parameter map and returns the error result. -// -// The parameter `object` should be type of struct/*struct. -// The parameter `paramMap` should be type of map, which specifies the parameter map used in validation. -// The parameter `customRules` can be type of []string/map[string]string. It supports sequence in error result -// if `rules` is type of []string. -// The optional parameter `customErrorMessageMap` specifies the custom error messages for specified keys and rules. -func (v *Validator) CheckStructWithParamMap(object interface{}, paramMap interface{}, customRules interface{}, customErrorMessageMap ...CustomMsg) Error { - var message CustomMsg - if len(customErrorMessageMap) > 0 { - message = customErrorMessageMap[0] - } - return v.doCheckStructWithParamMap(&doCheckStructWithParamMapInput{ - Object: object, - ParamMap: paramMap, - UseParamMapInsteadOfObjectValue: true, - CustomRules: customRules, - CustomErrorMessageMap: message, - }) -} - -func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapInput) Error { +func (v *Validator) doCheckStruct(object interface{}) Error { var ( // Returning error. errorMaps = make(map[string]map[string]string) ) - fieldMap, err := structs.FieldMap(input.Object, aliasNameTagPriority, true) + fieldMap, err := structs.FieldMap(object, aliasNameTagPriority, true) if err != nil { return newErrorStr("invalid_object", err.Error()) } @@ -73,10 +38,7 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn if _, ok := field.TagLookup(noValidationTagName); ok { continue } - recursiveInput := doCheckStructWithParamMapInput{} - recursiveInput = *input - recursiveInput.Object = field.Value - if err := v.doCheckStructWithParamMap(&recursiveInput); err != nil { + if err := v.doCheckStruct(field.Value); err != nil { // It merges the errors into single error map. for k, m := range err.(*validationError).errors { errorMaps[k] = m @@ -85,12 +47,12 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn } } // It here must use structs.TagFields not structs.FieldMap to ensure error sequence. - tagField, err := structs.TagFields(input.Object, structTagPriority) + tagField, err := structs.TagFields(object, structTagPriority) if err != nil { return newErrorStr("invalid_object", err.Error()) } // If there's no struct tag and validation rules, it does nothing and returns quickly. - if len(tagField) == 0 && input.CustomRules == nil { + if len(tagField) == 0 && v.messages == nil { return nil } @@ -101,7 +63,7 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn fieldAliases = make(map[string]string) // Alias names for `messages` overwriting struct tag names. errorRules = make([]string, 0) // Sequence rules. ) - switch v := input.CustomRules.(type) { + switch v := v.rules.(type) { // Sequence tag: []sequence tag // Sequence has order for error results. case []string: @@ -145,13 +107,13 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn return nil } // Input parameter map handling. - if input.ParamMap == nil || !input.UseParamMapInsteadOfObjectValue { + if v.data == nil || !v.useDataInsteadOfObjectAttributes { inputParamMap = make(map[string]interface{}) } else { - inputParamMap = gconv.Map(input.ParamMap) + inputParamMap = gconv.Map(v.data) } // Checks and extends the parameters map with struct alias tag. - if !input.UseParamMapInsteadOfObjectValue { + if !v.useDataInsteadOfObjectAttributes { for nameOrTag, field := range fieldMap { inputParamMap[nameOrTag] = field.Value.Interface() if nameOrTag != field.Name() { @@ -173,7 +135,7 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn } // It here extends the params map using alias names. if _, ok := inputParamMap[name]; !ok { - if !input.UseParamMapInsteadOfObjectValue { + if !v.useDataInsteadOfObjectAttributes { inputParamMap[name] = field.Value.Interface() } } @@ -216,8 +178,8 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn // Custom error messages, // which have the most priority than `rules` and struct tag. - if len(input.CustomErrorMessageMap) > 0 { - for k, v := range input.CustomErrorMessageMap { + if msg, ok := v.messages.(CustomMsg); ok && len(msg) > 0 { + for k, v := range msg { if a, ok := fieldAliases[k]; ok { // Overwrite the key of field name. customMessage[a] = v @@ -232,7 +194,7 @@ func (v *Validator) doCheckStructWithParamMap(input *doCheckStructWithParamMapIn for key, rule := range checkRules { _, value = gutil.MapPossibleItemByKey(inputParamMap, key) // It checks each rule and its value in loop. - if e := v.doCheck(key, value, rule, customMessage[key], inputParamMap); e != nil { + if e := v.doCheckValue(key, value, rule, customMessage[key], inputParamMap); e != nil { _, item := e.FirstItem() // =================================================================== // Only in map and struct validations, if value is nil or empty string diff --git a/util/gvalid/gvalid_z_example_test.go b/util/gvalid/gvalid_z_example_test.go index 3f7648bfe..d52175aca 100644 --- a/util/gvalid/gvalid_z_example_test.go +++ b/util/gvalid/gvalid_z_example_test.go @@ -173,14 +173,14 @@ func ExampleRegisterRule_OverwriteRequired() { } return nil }) - fmt.Println(gvalid.Check(context.TODO(), "", "required", "It's required")) - fmt.Println(gvalid.Check(context.TODO(), 0, "required", "It's required")) - fmt.Println(gvalid.Check(context.TODO(), false, "required", "It's required")) + fmt.Println(gvalid.CheckValue(context.TODO(), "", "required", "It's required")) + fmt.Println(gvalid.CheckValue(context.TODO(), 0, "required", "It's required")) + fmt.Println(gvalid.CheckValue(context.TODO(), false, "required", "It's required")) gvalid.DeleteRule(rule) fmt.Println("rule deleted") - fmt.Println(gvalid.Check(context.TODO(), "", "required", "It's required")) - fmt.Println(gvalid.Check(context.TODO(), 0, "required", "It's required")) - fmt.Println(gvalid.Check(context.TODO(), false, "required", "It's required")) + fmt.Println(gvalid.CheckValue(context.TODO(), "", "required", "It's required")) + fmt.Println(gvalid.CheckValue(context.TODO(), 0, "required", "It's required")) + fmt.Println(gvalid.CheckValue(context.TODO(), false, "required", "It's required")) // Output: // It's required // It's required diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index 55e7687ab..6bdf61a6f 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -24,9 +24,9 @@ func Test_Check(t *testing.T) { val1 := 0 val2 := 7 val3 := 20 - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) t.Assert(err1, "invalid_rules: abc:6,16") t.Assert(err2, "invalid_rules: abc:6,16") t.Assert(err3, "invalid_rules: abc:6,16") @@ -34,16 +34,16 @@ func Test_Check(t *testing.T) { } func Test_Required(t *testing.T) { - if m := gvalid.Check(context.TODO(), "1", "required", nil); m != nil { + if m := gvalid.CheckValue(context.TODO(), "1", "required", nil); m != nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "", "required", nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "", "required", nil); m == nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "", "required-if: id,1,age,18", nil, map[string]interface{}{"id": 1, "age": 19}); m == nil { + if m := gvalid.CheckValue(context.TODO(), "", "required-if: id,1,age,18", nil, map[string]interface{}{"id": 1, "age": 19}); m == nil { t.Error("Required校验失败") } - if m := gvalid.Check(context.TODO(), "", "required-if: id,1,age,18", nil, map[string]interface{}{"id": 2, "age": 19}); m != nil { + if m := gvalid.CheckValue(context.TODO(), "", "required-if: id,1,age,18", nil, map[string]interface{}{"id": 2, "age": 19}); m != nil { t.Error("Required校验失败") } } @@ -51,20 +51,20 @@ func Test_Required(t *testing.T) { func Test_RequiredIf(t *testing.T) { gtest.C(t, func(t *gtest.T) { rule := "required-if:id,1,age,18" - t.AssertNE(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"id": 1}), nil) - t.Assert(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"id": 0}), nil) - t.AssertNE(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"age": 18}), nil) - t.Assert(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"age": 20}), nil) + t.AssertNE(gvalid.CheckValue(context.TODO(), "", rule, nil, g.Map{"id": 1}), nil) + t.Assert(gvalid.CheckValue(context.TODO(), "", rule, nil, g.Map{"id": 0}), nil) + t.AssertNE(gvalid.CheckValue(context.TODO(), "", rule, nil, g.Map{"age": 18}), nil) + t.Assert(gvalid.CheckValue(context.TODO(), "", rule, nil, g.Map{"age": 20}), nil) }) } func Test_RequiredUnless(t *testing.T) { gtest.C(t, func(t *gtest.T) { rule := "required-unless:id,1,age,18" - t.Assert(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"id": 1}), nil) - t.AssertNE(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"id": 0}), nil) - t.Assert(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"age": 18}), nil) - t.AssertNE(gvalid.Check(context.TODO(), "", rule, nil, g.Map{"age": 20}), nil) + t.Assert(gvalid.CheckValue(context.TODO(), "", rule, nil, g.Map{"id": 1}), nil) + t.AssertNE(gvalid.CheckValue(context.TODO(), "", rule, nil, g.Map{"id": 0}), nil) + t.Assert(gvalid.CheckValue(context.TODO(), "", rule, nil, g.Map{"age": 18}), nil) + t.AssertNE(gvalid.CheckValue(context.TODO(), "", rule, nil, g.Map{"age": 20}), nil) }) } @@ -82,9 +82,9 @@ func Test_RequiredWith(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) - err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) - err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params3) t.Assert(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -102,9 +102,9 @@ func Test_RequiredWith(t *testing.T) { params3 := g.Map{ "time": time.Time{}, } - err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) - err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) - err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params3) t.Assert(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -121,9 +121,9 @@ func Test_RequiredWith(t *testing.T) { params3 := g.Map{ "time": time.Now(), } - err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) - err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) - err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params3) t.Assert(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -171,9 +171,9 @@ func Test_RequiredWithAll(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) - err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) - err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params3) t.Assert(err1, nil) t.Assert(err2, nil) t.AssertNE(err3, nil) @@ -194,9 +194,9 @@ func Test_RequiredWithOut(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) - err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) - err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params3) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -217,9 +217,9 @@ func Test_RequiredWithOutAll(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) - err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) - err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params3) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -236,13 +236,13 @@ func Test_Date(t *testing.T) { val5 := "2010.11.01" val6 := "2010/11/01" val7 := "2010=11=01" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) - err6 := gvalid.Check(context.TODO(), val6, rule, nil) - err7 := gvalid.Check(context.TODO(), val7, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) + err6 := gvalid.CheckValue(context.TODO(), val6, rule, nil) + err7 := gvalid.CheckValue(context.TODO(), val7, rule, nil) t.Assert(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -261,12 +261,12 @@ func Test_DateFormat(t *testing.T) { val4 := "201011-01" val5 := "2010~11~01" val6 := "2010-11~01" - err1 := gvalid.Check(context.TODO(), val1, "date-format:Y", nil) - err2 := gvalid.Check(context.TODO(), val2, "date-format:Ym", nil) - err3 := gvalid.Check(context.TODO(), val3, "date-format:Y.m", nil) - err4 := gvalid.Check(context.TODO(), val4, "date-format:Ym-d", nil) - err5 := gvalid.Check(context.TODO(), val5, "date-format:Y~m~d", nil) - err6 := gvalid.Check(context.TODO(), val6, "date-format:Y~m~d", nil) + err1 := gvalid.CheckValue(context.TODO(), val1, "date-format:Y", nil) + err2 := gvalid.CheckValue(context.TODO(), val2, "date-format:Ym", nil) + err3 := gvalid.CheckValue(context.TODO(), val3, "date-format:Y.m", nil) + err4 := gvalid.CheckValue(context.TODO(), val4, "date-format:Ym-d", nil) + err5 := gvalid.CheckValue(context.TODO(), val5, "date-format:Y~m~d", nil) + err6 := gvalid.CheckValue(context.TODO(), val6, "date-format:Y~m~d", nil) t.Assert(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -277,8 +277,8 @@ func Test_DateFormat(t *testing.T) { gtest.C(t, func(t *gtest.T) { t1 := gtime.Now() t2 := time.Time{} - err1 := gvalid.Check(context.TODO(), t1, "date-format:Y", nil) - err2 := gvalid.Check(context.TODO(), t2, "date-format:Y", nil) + err1 := gvalid.CheckValue(context.TODO(), t1, "date-format:Y", nil) + err2 := gvalid.CheckValue(context.TODO(), t2, "date-format:Y", nil) t.Assert(err1, nil) t.AssertNE(err2, nil) }) @@ -291,10 +291,10 @@ func Test_Email(t *testing.T) { value2 := "m@www@johngcn" value3 := "m-m_m@mail.johng.cn" value4 := "m.m-m@johng.cn" - err1 := gvalid.Check(context.TODO(), value1, rule, nil) - err2 := gvalid.Check(context.TODO(), value2, rule, nil) - err3 := gvalid.Check(context.TODO(), value3, rule, nil) - err4 := gvalid.Check(context.TODO(), value4, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), value1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), value2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), value3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), value4, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -304,10 +304,10 @@ func Test_Email(t *testing.T) { func Test_Phone(t *testing.T) { gtest.C(t, func(t *gtest.T) { - err1 := gvalid.Check(context.TODO(), "1361990897", "phone", nil) - err2 := gvalid.Check(context.TODO(), "13619908979", "phone", nil) - err3 := gvalid.Check(context.TODO(), "16719908979", "phone", nil) - err4 := gvalid.Check(context.TODO(), "19719908989", "phone", nil) + err1 := gvalid.CheckValue(context.TODO(), "1361990897", "phone", nil) + err2 := gvalid.CheckValue(context.TODO(), "13619908979", "phone", nil) + err3 := gvalid.CheckValue(context.TODO(), "16719908979", "phone", nil) + err4 := gvalid.CheckValue(context.TODO(), "19719908989", "phone", nil) t.AssertNE(err1.String(), nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -317,12 +317,12 @@ func Test_Phone(t *testing.T) { func Test_PhoneLoose(t *testing.T) { gtest.C(t, func(t *gtest.T) { - err1 := gvalid.Check(context.TODO(), "13333333333", "phone-loose", nil) - err2 := gvalid.Check(context.TODO(), "15555555555", "phone-loose", nil) - err3 := gvalid.Check(context.TODO(), "16666666666", "phone-loose", nil) - err4 := gvalid.Check(context.TODO(), "23333333333", "phone-loose", nil) - err5 := gvalid.Check(context.TODO(), "1333333333", "phone-loose", nil) - err6 := gvalid.Check(context.TODO(), "10333333333", "phone-loose", nil) + err1 := gvalid.CheckValue(context.TODO(), "13333333333", "phone-loose", nil) + err2 := gvalid.CheckValue(context.TODO(), "15555555555", "phone-loose", nil) + err3 := gvalid.CheckValue(context.TODO(), "16666666666", "phone-loose", nil) + err4 := gvalid.CheckValue(context.TODO(), "23333333333", "phone-loose", nil) + err5 := gvalid.CheckValue(context.TODO(), "1333333333", "phone-loose", nil) + err6 := gvalid.CheckValue(context.TODO(), "10333333333", "phone-loose", nil) t.Assert(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -339,11 +339,11 @@ func Test_Telephone(t *testing.T) { val3 := "86292651" val4 := "028-8692651" val5 := "0830-8692651" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -360,11 +360,11 @@ func Test_Passport(t *testing.T) { val3 := "aaaaa" val4 := "aaaaaa" val5 := "a123_456" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -381,11 +381,11 @@ func Test_Password(t *testing.T) { val3 := "a12345-6" val4 := ">,/;'[09-" val5 := "a123_456" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -404,13 +404,13 @@ func Test_Password2(t *testing.T) { val5 := "a123_456" val6 := "Nant1986" val7 := "Nant1986!" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) - err6 := gvalid.Check(context.TODO(), val6, rule, nil) - err7 := gvalid.Check(context.TODO(), val7, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) + err6 := gvalid.CheckValue(context.TODO(), val6, rule, nil) + err7 := gvalid.CheckValue(context.TODO(), val7, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -431,13 +431,13 @@ func Test_Password3(t *testing.T) { val5 := "a123_456" val6 := "Nant1986" val7 := "Nant1986!" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) - err6 := gvalid.Check(context.TODO(), val6, rule, nil) - err7 := gvalid.Check(context.TODO(), val7, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) + err6 := gvalid.CheckValue(context.TODO(), val6, rule, nil) + err7 := gvalid.CheckValue(context.TODO(), val7, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -453,8 +453,8 @@ func Test_Postcode(t *testing.T) { rule := "postcode" val1 := "12345" val2 := "610036" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) }) @@ -468,11 +468,11 @@ func Test_ResidentId(t *testing.T) { val3 := "311128500121201" val4 := "510521198607185367" val5 := "51052119860718536x" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -486,8 +486,8 @@ func Test_BankCard(t *testing.T) { rule := "bank-card" val1 := "6230514630000424470" val2 := "6230514630000424473" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) }) @@ -501,11 +501,11 @@ func Test_QQ(t *testing.T) { val3 := "10000" val4 := "38996181" val5 := "389961817" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -515,31 +515,31 @@ func Test_QQ(t *testing.T) { } func Test_Ip(t *testing.T) { - if m := gvalid.Check(context.TODO(), "10.0.0.1", "ip", nil); m != nil { + if m := gvalid.CheckValue(context.TODO(), "10.0.0.1", "ip", nil); m != nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "10.0.0.1", "ipv4", nil); m != nil { + if m := gvalid.CheckValue(context.TODO(), "10.0.0.1", "ipv4", nil); m != nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "0.0.0.0", "ipv4", nil); m != nil { + if m := gvalid.CheckValue(context.TODO(), "0.0.0.0", "ipv4", nil); m != nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "1920.0.0.0", "ipv4", nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "1920.0.0.0", "ipv4", nil); m == nil { t.Error("ipv4校验失败") } - if m := gvalid.Check(context.TODO(), "1920.0.0.0", "ip", nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "1920.0.0.0", "ip", nil); m == nil { t.Error("ipv4校验失败") } - if m := gvalid.Check(context.TODO(), "fe80::5484:7aff:fefe:9799", "ipv6", nil); m != nil { + if m := gvalid.CheckValue(context.TODO(), "fe80::5484:7aff:fefe:9799", "ipv6", nil); m != nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "fe80::5484:7aff:fefe:9799123", "ipv6", nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "fe80::5484:7aff:fefe:9799123", "ipv6", nil); m == nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "fe80::5484:7aff:fefe:9799", "ip", nil); m != nil { + if m := gvalid.CheckValue(context.TODO(), "fe80::5484:7aff:fefe:9799", "ip", nil); m != nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "fe80::5484:7aff:fefe:9799123", "ip", nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "fe80::5484:7aff:fefe:9799123", "ip", nil); m == nil { t.Error(m) } } @@ -552,11 +552,11 @@ func Test_IPv4(t *testing.T) { val3 := "1.1.1.1" val4 := "255.255.255.0" val5 := "127.0.0.1" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -573,11 +573,11 @@ func Test_IPv6(t *testing.T) { val3 := "1030::C9B4:FF12:48AA:1A2B" val4 := "2000:0:0:0:0:0:0:1" val5 := "0000:0000:0000:0000:0000:ffff:c0a8:5909" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -592,9 +592,9 @@ func Test_MAC(t *testing.T) { val1 := "192.168.1.1" val2 := "44-45-53-54-00-00" val3 := "01:00:5e:00:00:00" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -608,10 +608,10 @@ func Test_URL(t *testing.T) { val2 := "https://www.baidu.com" val3 := "http://127.0.0.1" val4 := "file:///tmp/test.txt" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -638,7 +638,7 @@ func Test_Domain(t *testing.T) { } var err error for k, v := range m { - err = gvalid.Check(context.TODO(), k, "domain", nil) + err = gvalid.CheckValue(context.TODO(), k, "domain", nil) if v { //fmt.Println(k) t.Assert(err, nil) @@ -652,10 +652,10 @@ func Test_Domain(t *testing.T) { func Test_Length(t *testing.T) { rule := "length:6,16" - if m := gvalid.Check(context.TODO(), "123456", rule, nil); m != nil { + if m := gvalid.CheckValue(context.TODO(), "123456", rule, nil); m != nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "12345", rule, nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "12345", rule, nil); m == nil { t.Error("长度校验失败") } } @@ -665,18 +665,18 @@ func Test_MinLength(t *testing.T) { msgs := map[string]string{ "min-length": "地址长度至少为:min位", } - if m := gvalid.Check(context.TODO(), "123456", rule, nil); m != nil { + if m := gvalid.CheckValue(context.TODO(), "123456", rule, nil); m != nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "12345", rule, nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "12345", rule, nil); m == nil { t.Error("长度校验失败") } - if m := gvalid.Check(context.TODO(), "12345", rule, msgs); m == nil { + if m := gvalid.CheckValue(context.TODO(), "12345", rule, msgs); m == nil { t.Error("长度校验失败") } rule2 := "min-length:abc" - if m := gvalid.Check(context.TODO(), "123456", rule2, nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "123456", rule2, nil); m == nil { t.Error("长度校验失败") } } @@ -686,31 +686,31 @@ func Test_MaxLength(t *testing.T) { msgs := map[string]string{ "max-length": "地址长度至大为:max位", } - if m := gvalid.Check(context.TODO(), "12345", rule, nil); m != nil { + if m := gvalid.CheckValue(context.TODO(), "12345", rule, nil); m != nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "1234567", rule, nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "1234567", rule, nil); m == nil { t.Error("长度校验失败") } - if m := gvalid.Check(context.TODO(), "1234567", rule, msgs); m == nil { + if m := gvalid.CheckValue(context.TODO(), "1234567", rule, msgs); m == nil { t.Error("长度校验失败") } rule2 := "max-length:abc" - if m := gvalid.Check(context.TODO(), "123456", rule2, nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "123456", rule2, nil); m == nil { t.Error("长度校验失败") } } func Test_Between(t *testing.T) { rule := "between:6.01, 10.01" - if m := gvalid.Check(context.TODO(), 10, rule, nil); m != nil { + if m := gvalid.CheckValue(context.TODO(), 10, rule, nil); m != nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), 10.02, rule, nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), 10.02, rule, nil); m == nil { t.Error("大小范围校验失败") } - if m := gvalid.Check(context.TODO(), "a", rule, nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "a", rule, nil); m == nil { t.Error("大小范围校验失败") } } @@ -723,11 +723,11 @@ func Test_Min(t *testing.T) { val3 := "100" val4 := "1000" val5 := "a" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -735,7 +735,7 @@ func Test_Min(t *testing.T) { t.AssertNE(err5, nil) rule2 := "min:a" - err6 := gvalid.Check(context.TODO(), val1, rule2, nil) + err6 := gvalid.CheckValue(context.TODO(), val1, rule2, nil) t.AssertNE(err6, nil) }) } @@ -748,11 +748,11 @@ func Test_Max(t *testing.T) { val3 := "100" val4 := "1000" val5 := "a" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) t.Assert(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -760,7 +760,7 @@ func Test_Max(t *testing.T) { t.AssertNE(err5, nil) rule2 := "max:a" - err6 := gvalid.Check(context.TODO(), val1, rule2, nil) + err6 := gvalid.CheckValue(context.TODO(), val1, rule2, nil) t.AssertNE(err6, nil) }) } @@ -774,12 +774,12 @@ func Test_Json(t *testing.T) { val4 := "[]" val5 := "[1,2,3,4]" val6 := `{"list":[1,2,3,4]}` - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) - err6 := gvalid.Check(context.TODO(), val6, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) + err6 := gvalid.CheckValue(context.TODO(), val6, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -798,12 +798,12 @@ func Test_Integer(t *testing.T) { val4 := "1" val5 := "100" val6 := `999999999` - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) - err6 := gvalid.Check(context.TODO(), val6, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) + err6 := gvalid.CheckValue(context.TODO(), val6, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -822,12 +822,12 @@ func Test_Float(t *testing.T) { val4 := "1.0" val5 := "1.1" val6 := `0.1` - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) - err6 := gvalid.Check(context.TODO(), val6, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) + err6 := gvalid.CheckValue(context.TODO(), val6, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -846,12 +846,12 @@ func Test_Boolean(t *testing.T) { val4 := "1" val5 := "true" val6 := `off` - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) - err5 := gvalid.Check(context.TODO(), val5, rule, nil) - err6 := gvalid.Check(context.TODO(), val6, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) + err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) + err6 := gvalid.CheckValue(context.TODO(), val6, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -875,9 +875,9 @@ func Test_Same(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) - err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) - err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params3) t.AssertNE(err1, nil) t.Assert(err2, nil) t.Assert(err3, nil) @@ -898,9 +898,9 @@ func Test_Different(t *testing.T) { "id": 100, "name": "john", } - err1 := gvalid.Check(context.TODO(), val1, rule, nil, params1) - err2 := gvalid.Check(context.TODO(), val1, rule, nil, params2) - err3 := gvalid.Check(context.TODO(), val1, rule, nil, params3) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params1) + err2 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params2) + err3 := gvalid.CheckValue(context.TODO(), val1, rule, nil, params3) t.Assert(err1, nil) t.AssertNE(err2, nil) t.AssertNE(err3, nil) @@ -914,10 +914,10 @@ func Test_In(t *testing.T) { val2 := "1" val3 := "100" val4 := "200" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) @@ -932,10 +932,10 @@ func Test_NotIn(t *testing.T) { val2 := "1" val3 := "100" val4 := "200" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) t.Assert(err1, nil) t.Assert(err2, nil) t.AssertNE(err3, nil) @@ -947,10 +947,10 @@ func Test_NotIn(t *testing.T) { val2 := "1" val3 := "100" val4 := "200" - err1 := gvalid.Check(context.TODO(), val1, rule, nil) - err2 := gvalid.Check(context.TODO(), val2, rule, nil) - err3 := gvalid.Check(context.TODO(), val3, rule, nil) - err4 := gvalid.Check(context.TODO(), val4, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) + err4 := gvalid.CheckValue(context.TODO(), val4, rule, nil) t.Assert(err1, nil) t.Assert(err2, nil) t.AssertNE(err3, nil) @@ -960,10 +960,10 @@ func Test_NotIn(t *testing.T) { func Test_Regex1(t *testing.T) { rule := `regex:\d{6}|\D{6}|length:6,16` - if m := gvalid.Check(context.TODO(), "123456", rule, nil); m != nil { + if m := gvalid.CheckValue(context.TODO(), "123456", rule, nil); m != nil { t.Error(m) } - if m := gvalid.Check(context.TODO(), "abcde6", rule, nil); m == nil { + if m := gvalid.CheckValue(context.TODO(), "abcde6", rule, nil); m == nil { t.Error("校验失败") } } @@ -974,9 +974,9 @@ func Test_Regex2(t *testing.T) { str1 := "" str2 := "data" str3 := "data:image/jpeg;base64,/9jrbattq22r" - err1 := gvalid.Check(context.TODO(), str1, rule, nil) - err2 := gvalid.Check(context.TODO(), str2, rule, nil) - err3 := gvalid.Check(context.TODO(), str3, rule, nil) + err1 := gvalid.CheckValue(context.TODO(), str1, rule, nil) + err2 := gvalid.CheckValue(context.TODO(), str2, rule, nil) + err3 := gvalid.CheckValue(context.TODO(), str3, rule, nil) t.AssertNE(err1, nil) t.AssertNE(err2, nil) t.Assert(err3, nil) diff --git a/util/gvalid/gvalid_z_unit_checkstruct_test.go b/util/gvalid/gvalid_z_unit_checkstruct_test.go index ac65bd8b4..14a80fb3a 100755 --- a/util/gvalid/gvalid_z_unit_checkstruct_test.go +++ b/util/gvalid/gvalid_z_unit_checkstruct_test.go @@ -406,7 +406,7 @@ func Test_CheckStruct_InvalidRule(t *testing.T) { }) } -func TestValidator_CheckStructWithParamMap(t *testing.T) { +func TestValidator_CheckStructWithData(t *testing.T) { gtest.C(t, func(t *gtest.T) { type UserApiSearch struct { Uid int64 `v:"required"` @@ -416,7 +416,7 @@ func TestValidator_CheckStructWithParamMap(t *testing.T) { Uid: 1, Nickname: "john", } - t.Assert(gvalid.CheckStructWithParamMap(context.TODO(), data, g.Map{"uid": 1, "nickname": "john"}, nil), nil) + t.Assert(gvalid.CheckStructWithData(context.TODO(), data, g.Map{"uid": 1, "nickname": "john"}, nil), nil) }) gtest.C(t, func(t *gtest.T) { type UserApiSearch struct { @@ -424,7 +424,7 @@ func TestValidator_CheckStructWithParamMap(t *testing.T) { Nickname string `v:"required-with:uid"` } data := UserApiSearch{} - t.AssertNE(gvalid.CheckStructWithParamMap(context.TODO(), data, g.Map{}, nil), nil) + t.AssertNE(gvalid.CheckStructWithData(context.TODO(), data, g.Map{}, nil), nil) }) gtest.C(t, func(t *gtest.T) { type UserApiSearch struct { @@ -434,7 +434,7 @@ func TestValidator_CheckStructWithParamMap(t *testing.T) { data := UserApiSearch{ Uid: 1, } - t.AssertNE(gvalid.CheckStructWithParamMap(context.TODO(), data, g.Map{}, nil), nil) + t.AssertNE(gvalid.CheckStructWithData(context.TODO(), data, g.Map{}, nil), nil) }) gtest.C(t, func(t *gtest.T) { @@ -448,7 +448,7 @@ func TestValidator_CheckStructWithParamMap(t *testing.T) { StartTime: nil, EndTime: nil, } - t.Assert(gvalid.CheckStructWithParamMap(context.TODO(), data, g.Map{}, nil), nil) + t.Assert(gvalid.CheckStructWithData(context.TODO(), data, g.Map{}, nil), nil) }) gtest.C(t, func(t *gtest.T) { type UserApiSearch struct { @@ -461,6 +461,6 @@ func TestValidator_CheckStructWithParamMap(t *testing.T) { StartTime: gtime.Now(), EndTime: nil, } - t.AssertNE(gvalid.CheckStructWithParamMap(context.TODO(), data, g.Map{"start_time": gtime.Now()}, nil), nil) + t.AssertNE(gvalid.CheckStructWithData(context.TODO(), data, g.Map{"start_time": gtime.Now()}, nil), nil) }) } diff --git a/util/gvalid/gvalid_z_unit_custom_rule_test.go b/util/gvalid/gvalid_z_unit_custom_rule_test.go index e4a7f45a6..9fd7caf62 100644 --- a/util/gvalid/gvalid_z_unit_custom_rule_test.go +++ b/util/gvalid/gvalid_z_unit_custom_rule_test.go @@ -31,9 +31,9 @@ func Test_CustomRule1(t *testing.T) { }) gtest.Assert(err, nil) gtest.C(t, func(t *gtest.T) { - err := gvalid.Check(context.TODO(), "123456", rule, "custom message") + err := gvalid.CheckValue(context.TODO(), "123456", rule, "custom message") t.Assert(err.String(), "custom message") - err = gvalid.Check(context.TODO(), "123456", rule, "custom message", g.Map{"data": "123456"}) + err = gvalid.CheckValue(context.TODO(), "123456", rule, "custom message", g.Map{"data": "123456"}) t.Assert(err, nil) }) // Error with struct validation. @@ -77,8 +77,8 @@ func Test_CustomRule2(t *testing.T) { // Check. gtest.C(t, func(t *gtest.T) { errStr := "data map should not be empty" - t.Assert(gvalid.Check(context.TODO(), g.Map{}, rule, errStr).String(), errStr) - t.Assert(gvalid.Check(context.TODO(), g.Map{"k": "v"}, rule, errStr), nil) + t.Assert(gvalid.CheckValue(context.TODO(), g.Map{}, rule, errStr).String(), errStr) + t.Assert(gvalid.CheckValue(context.TODO(), g.Map{"k": "v"}, rule, errStr), nil) }) // Error with struct validation. gtest.C(t, func(t *gtest.T) { @@ -121,9 +121,9 @@ func Test_CustomRule_AllowEmpty(t *testing.T) { // Check. gtest.C(t, func(t *gtest.T) { errStr := "error" - t.Assert(gvalid.Check(context.TODO(), "", rule, errStr), nil) - t.Assert(gvalid.Check(context.TODO(), "gf", rule, errStr), nil) - t.Assert(gvalid.Check(context.TODO(), "gf2", rule, errStr).String(), errStr) + t.Assert(gvalid.CheckValue(context.TODO(), "", rule, errStr), nil) + t.Assert(gvalid.CheckValue(context.TODO(), "gf", rule, errStr), nil) + t.Assert(gvalid.CheckValue(context.TODO(), "gf2", rule, errStr).String(), errStr) }) // Error with struct validation. gtest.C(t, func(t *gtest.T) { diff --git a/util/gvalid/gvalid_z_unit_customerror_test.go b/util/gvalid/gvalid_z_unit_customerror_test.go index ecccf7e4d..75bc8f21a 100755 --- a/util/gvalid/gvalid_z_unit_customerror_test.go +++ b/util/gvalid/gvalid_z_unit_customerror_test.go @@ -20,7 +20,7 @@ func Test_Map(t *testing.T) { var ( rule = "ipv4" val = "0.0.0" - err = gvalid.Check(context.TODO(), val, rule, nil) + err = gvalid.CheckValue(context.TODO(), val, rule, nil) msg = map[string]string{ "ipv4": "The value must be a valid IPv4 address", } @@ -34,7 +34,7 @@ func Test_FirstString(t *testing.T) { var ( rule = "ipv4" val = "0.0.0" - err = gvalid.Check(context.TODO(), val, rule, nil) + err = gvalid.CheckValue(context.TODO(), val, rule, nil) n = err.FirstString() ) t.Assert(n, "The value must be a valid IPv4 address") @@ -47,7 +47,7 @@ func Test_CustomError1(t *testing.T) { "integer": "请输入一个整数", "length": "参数长度不对啊老铁", } - e := gvalid.Check(context.TODO(), "6.66", rule, msgs) + e := gvalid.CheckValue(context.TODO(), "6.66", rule, msgs) if e == nil || len(e.Map()) != 2 { t.Error("规则校验失败") } else { @@ -67,7 +67,7 @@ func Test_CustomError1(t *testing.T) { func Test_CustomError2(t *testing.T) { rule := "integer|length:6,16" msgs := "请输入一个整数|参数长度不对啊老铁" - e := gvalid.Check(context.TODO(), "6.66", rule, msgs) + e := gvalid.CheckValue(context.TODO(), "6.66", rule, msgs) if e == nil || len(e.Map()) != 2 { t.Error("规则校验失败") } else { diff --git a/util/gvalid/gvalid_z_unit_i18n_test.go b/util/gvalid/gvalid_z_unit_i18n_test.go index 16bc2d809..5294c7e3c 100644 --- a/util/gvalid/gvalid_z_unit_i18n_test.go +++ b/util/gvalid/gvalid_z_unit_i18n_test.go @@ -24,14 +24,14 @@ func TestValidator_I18n(t *testing.T) { validator = gvalid.New().I18n(i18nManager) ) gtest.C(t, func(t *gtest.T) { - err = validator.Check("", "required", nil) + err = validator.Rules("required").CheckValue("") t.Assert(err.String(), "The field is required") - err = validator.Ctx(ctxCn).Check("", "required", nil) + err = validator.Ctx(ctxCn).Rules("required").CheckValue("") t.Assert(err.String(), "字段不能为空") }) gtest.C(t, func(t *gtest.T) { - err = validator.Ctx(ctxCn).Check("", "required", "CustomMessage") + err = validator.Ctx(ctxCn).Rules("required").Messages("CustomMessage").CheckValue("") t.Assert(err.String(), "自定义错误") }) gtest.C(t, func(t *gtest.T) { @@ -44,7 +44,7 @@ func TestValidator_I18n(t *testing.T) { Page: 1, Size: 10, } - err := validator.Ctx(ctxCn).CheckStruct(obj, nil) + err := validator.Ctx(ctxCn).CheckStruct(obj) t.Assert(err.String(), "项目ID必须大于等于1并且要小于等于10000") }) } From 420e0b9ca4cca71cb29f2da419aef750be96387a Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 19 May 2021 13:29:40 +0800 Subject: [PATCH 274/492] improve package gvalid --- .../util/gvalid/gvalid_checkstructwithdata.go | 28 +++++++ util/gvalid/gvalid.go | 70 +++++++++++++++--- util/gvalid/gvalid_error.go | 73 +++++++++++++------ util/gvalid/gvalid_validator_check.go | 4 +- util/gvalid/gvalid_validator_check_map.go | 2 +- util/gvalid/gvalid_validator_check_struct.go | 4 +- util/gvalid/gvalid_validator_message.go | 57 +-------------- util/gvalid/gvalid_z_unit_basic_all_test.go | 14 ++-- 8 files changed, 152 insertions(+), 100 deletions(-) create mode 100644 .example/util/gvalid/gvalid_checkstructwithdata.go diff --git a/.example/util/gvalid/gvalid_checkstructwithdata.go b/.example/util/gvalid/gvalid_checkstructwithdata.go new file mode 100644 index 000000000..7e1636f88 --- /dev/null +++ b/.example/util/gvalid/gvalid_checkstructwithdata.go @@ -0,0 +1,28 @@ +package main + +import ( + "context" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/gvalid" +) + +func main() { + type User struct { + Name string `v:"required#请输入用户姓名"` + Type int `v:"required#请选择用户类型"` + } + data := g.Map{ + "name": "john", + } + user := User{} + if err := gconv.Scan(data, &user); err != nil { + panic(err) + } + err := gvalid.CheckStructWithData(context.TODO(), user, data, nil) + // 也可以使用 + // err := g.Validator().Data(data).CheckStruct(user) + if err != nil { + g.Dump(err.Items()) + } +} diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 532b6fcfd..275b17771 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -70,14 +70,14 @@ type apiNoValidation interface { } const ( - // regular expression pattern for single validation rule. - singleRulePattern = `^([\w-]+):{0,1}(.*)` - invalidRulesErrKey = "invalid_rules" - invalidParamsErrKey = "invalid_params" - invalidObjectErrKey = "invalid_object" - - // no validation tag name for struct attribute. - noValidationTagName = "nv" + singleRulePattern = `^([\w-]+):{0,1}(.*)` // regular expression pattern for single validation rule. + internalRulesErrRuleName = "InvalidRules" // rule name for internal invalid rules validation error. + internalParamsErrRuleName = "InvalidParams" // rule name for internal invalid params validation error. + internalObjectErrRuleName = "InvalidObject" // rule name for internal invalid object validation error. + internalErrorMapKey = "__InternalError__" // error map key for internal errors. + internalDefaultRuleName = "__default__" // default rule name for i18n error message format if no i18n message found for specified error rule. + ruleMessagePrefixForI18n = "gf.gvalid.rule." // prefix string for each rule configuration in i18n content. + noValidationTagName = "nv" // no validation tag name for struct attribute. ) var ( @@ -87,9 +87,9 @@ var ( // all internal error keys. internalErrKeyMap = map[string]string{ - invalidRulesErrKey: invalidRulesErrKey, - invalidParamsErrKey: invalidParamsErrKey, - invalidObjectErrKey: invalidObjectErrKey, + internalRulesErrRuleName: internalRulesErrRuleName, + internalParamsErrRuleName: internalParamsErrRuleName, + internalObjectErrRuleName: internalObjectErrRuleName, } // regular expression object for single rule // which is compiled just once and of repeatable usage. @@ -168,6 +168,54 @@ var ( "off": {}, "no": {}, } + // defaultMessages is the default error messages. + // Note that these messages are synchronized from ./i18n/en/validation.toml . + defaultMessages = map[string]string{ + "required": "The :attribute field is required", + "required-if": "The :attribute field is required", + "required-unless": "The :attribute field is required", + "required-with": "The :attribute field is required", + "required-with-all": "The :attribute field is required", + "required-without": "The :attribute field is required", + "required-without-all": "The :attribute field is required", + "date": "The :attribute value is not a valid date", + "date-format": "The :attribute value does not match the format :format", + "email": "The :attribute value must be a valid email address", + "phone": "The :attribute value must be a valid phone number", + "telephone": "The :attribute value must be a valid telephone number", + "passport": "The :attribute value is not a valid passport format", + "password": "The :attribute value is not a valid passport format", + "password2": "The :attribute value is not a valid passport format", + "password3": "The :attribute value is not a valid passport format", + "postcode": "The :attribute value is not a valid passport format", + "resident-id": "The :attribute value is not a valid resident id number", + "bank-card": "The :attribute value must be a valid bank card number", + "qq": "The :attribute value must be a valid QQ number", + "ip": "The :attribute value must be a valid IP address", + "ipv4": "The :attribute value must be a valid IPv4 address", + "ipv6": "The :attribute value must be a valid IPv6 address", + "mac": "The :attribute value must be a valid MAC address", + "url": "The :attribute value must be a valid URL address", + "domain": "The :attribute value must be a valid domain format", + "length": "The :attribute value length must be between :min and :max", + "min-length": "The :attribute value length must be equal or greater than :min", + "max-length": "The :attribute value length must be equal or lesser than :max", + "between": "The :attribute value must be between :min and :max", + "min": "The :attribute value must be equal or greater than :min", + "max": "The :attribute value must be equal or lesser than :max", + "json": "The :attribute value must be a valid JSON string", + "xml": "The :attribute value must be a valid XML string", + "array": "The :attribute value must be an array", + "integer": "The :attribute value must be an integer", + "float": "The :attribute value must be a float", + "boolean": "The :attribute value field must be true or false", + "same": "The :attribute value must be the same as field :field", + "different": "The :attribute value must be different from field :field", + "in": "The :attribute value is not in acceptable range", + "not-in": "The :attribute value is not in acceptable range", + "regex": "The :attribute value is invalid", + internalDefaultRuleName: "The :attribute value is invalid", + } ) // CheckValue checks single value with specified rules. diff --git a/util/gvalid/gvalid_error.go b/util/gvalid/gvalid_error.go index c55da3f56..64e2ae688 100644 --- a/util/gvalid/gvalid_error.go +++ b/util/gvalid/gvalid_error.go @@ -20,6 +20,7 @@ type Error interface { FirstItem() (key string, messages map[string]string) FirstRule() (rule string, err string) FirstString() (err string) + Items() (items []map[string]map[string]string) Map() map[string]string Maps() map[string]map[string]string String() string @@ -30,7 +31,7 @@ type Error interface { type validationError struct { rules []string // Rules by sequence, which is used for keeping error sequence. errors map[string]map[string]string // Error map:map[field]map[rule]message - firstKey string // The first error rule key(nil in default). + firstKey string // The first error rule key(empty in default). firstItem map[string]string // The first error rule value(nil in default). } @@ -54,7 +55,7 @@ func newError(rules []string, errors map[string]map[string]string) *validationEr // newErrorStr creates and returns a validation error by string. func newErrorStr(key, err string) *validationError { return newError(nil, map[string]map[string]string{ - "__gvalid__": { + internalErrorMapKey: { key: err, }, }) @@ -77,6 +78,34 @@ func (e *validationError) Maps() map[string]map[string]string { return e.errors } +// Items retrieves and returns error items array in sequence if possible, +// or else it returns error items with no sequence . +func (e *validationError) Items() (items []map[string]map[string]string) { + if e == nil { + return []map[string]map[string]string{} + } + items = make([]map[string]map[string]string, 0) + // By sequence. + if len(e.rules) > 0 { + for _, v := range e.rules { + name, _, _ := parseSequenceTag(v) + if errorItemMap, ok := e.errors[name]; ok { + items = append(items, map[string]map[string]string{ + name: errorItemMap, + }) + } + } + return items + } + // No sequence. + for name, errorRuleMap := range e.errors { + items = append(items, map[string]map[string]string{ + name: errorRuleMap, + }) + } + return +} + // FirstItem returns the field name and error messages for the first validation rule error. func (e *validationError) FirstItem() (key string, messages map[string]string) { if e == nil { @@ -89,10 +118,10 @@ func (e *validationError) FirstItem() (key string, messages map[string]string) { if len(e.rules) > 0 { for _, v := range e.rules { name, _, _ := parseSequenceTag(v) - if m, ok := e.errors[name]; ok { + if errorItemMap, ok := e.errors[name]; ok { e.firstKey = name - e.firstItem = m - return name, m + e.firstItem = errorItemMap + return name, errorItemMap } } } @@ -113,21 +142,21 @@ func (e *validationError) FirstRule() (rule string, err string) { // By sequence. if len(e.rules) > 0 { for _, v := range e.rules { - name, rule, _ := parseSequenceTag(v) - if m, ok := e.errors[name]; ok { - for _, rule := range strings.Split(rule, "|") { - array := strings.Split(rule, ":") - rule = strings.TrimSpace(array[0]) - if err, ok := m[rule]; ok { - return rule, err + name, ruleStr, _ := parseSequenceTag(v) + if errorItemMap, ok := e.errors[name]; ok { + for _, ruleItem := range strings.Split(ruleStr, "|") { + array := strings.Split(ruleItem, ":") + ruleItem = strings.TrimSpace(array[0]) + if err, ok = errorItemMap[ruleItem]; ok { + return ruleStr, err } } } } } // No sequence. - for _, m := range e.errors { - for k, v := range m { + for _, errorItemMap := range e.errors { + for k, v := range errorItemMap { return k, v } } @@ -178,18 +207,18 @@ func (e *validationError) Strings() (errs []string) { // By sequence. if len(e.rules) > 0 { for _, v := range e.rules { - name, rule, _ := parseSequenceTag(v) - if m, ok := e.errors[name]; ok { + name, ruleStr, _ := parseSequenceTag(v) + if errorItemMap, ok := e.errors[name]; ok { // validation error checks. - for _, rule := range strings.Split(rule, "|") { - rule = strings.TrimSpace(strings.Split(rule, ":")[0]) - if err, ok := m[rule]; ok { + for _, ruleItem := range strings.Split(ruleStr, "|") { + ruleItem = strings.TrimSpace(strings.Split(ruleItem, ":")[0]) + if err, ok := errorItemMap[ruleItem]; ok { errs = append(errs, err) } } // internal error checks. for k, _ := range internalErrKeyMap { - if err, ok := m[k]; ok { + if err, ok := errorItemMap[k]; ok { errs = append(errs, err) } } @@ -198,8 +227,8 @@ func (e *validationError) Strings() (errs []string) { return errs } // No sequence. - for _, m := range e.errors { - for _, err := range m { + for _, errorItemMap := range e.errors { + for _, err := range errorItemMap { errs = append(errs, err) } } diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index ec2e66a83..fd1d78f20 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -71,8 +71,8 @@ func (v *Validator) doCheckValue(key string, value interface{}, rules string, me ruleItems = append(ruleItems[:i], ruleItems[i+1:]...) } else { return newErrorStr( - invalidRulesErrKey, - invalidRulesErrKey+": "+rules, + internalRulesErrRuleName, + internalRulesErrRuleName+": "+rules, ) } } else { diff --git a/util/gvalid/gvalid_validator_check_map.go b/util/gvalid/gvalid_validator_check_map.go index e829ddcec..cefb987ca 100644 --- a/util/gvalid/gvalid_validator_check_map.go +++ b/util/gvalid/gvalid_validator_check_map.go @@ -73,7 +73,7 @@ func (v *Validator) doCheckMap(params interface{}) Error { data := gconv.Map(params) if data == nil { return newErrorStr( - "invalid_params", + internalParamsErrRuleName, "invalid params type: convert to map failed", ) } diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 62c578caf..55e0d7c33 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -26,7 +26,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error { ) fieldMap, err := structs.FieldMap(object, aliasNameTagPriority, true) if err != nil { - return newErrorStr("invalid_object", err.Error()) + return newErrorStr(internalObjectErrRuleName, err.Error()) } // It checks the struct recursively the its attribute is an embedded struct. for _, field := range fieldMap { @@ -49,7 +49,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error { // It here must use structs.TagFields not structs.FieldMap to ensure error sequence. tagField, err := structs.TagFields(object, structTagPriority) if err != nil { - return newErrorStr("invalid_object", err.Error()) + return newErrorStr(internalObjectErrRuleName, err.Error()) } // If there's no struct tag and validation rules, it does nothing and returns quickly. if len(tagField) == 0 && v.messages == nil { diff --git a/util/gvalid/gvalid_validator_message.go b/util/gvalid/gvalid_validator_message.go index e1737dce9..7701e5f96 100644 --- a/util/gvalid/gvalid_validator_message.go +++ b/util/gvalid/gvalid_validator_message.go @@ -6,59 +6,6 @@ package gvalid -const ( - ruleMessagePrefixForI18n = "gf.gvalid.rule." -) - -// defaultMessages is the default error messages. -// Note that these messages are synchronized from ./i18n/en/validation.toml . -var defaultMessages = map[string]string{ - "required": "The :attribute field is required", - "required-if": "The :attribute field is required", - "required-unless": "The :attribute field is required", - "required-with": "The :attribute field is required", - "required-with-all": "The :attribute field is required", - "required-without": "The :attribute field is required", - "required-without-all": "The :attribute field is required", - "date": "The :attribute value is not a valid date", - "date-format": "The :attribute value does not match the format :format", - "email": "The :attribute value must be a valid email address", - "phone": "The :attribute value must be a valid phone number", - "telephone": "The :attribute value must be a valid telephone number", - "passport": "The :attribute value is not a valid passport format", - "password": "The :attribute value is not a valid passport format", - "password2": "The :attribute value is not a valid passport format", - "password3": "The :attribute value is not a valid passport format", - "postcode": "The :attribute value is not a valid passport format", - "resident-id": "The :attribute value is not a valid resident id number", - "bank-card": "The :attribute value must be a valid bank card number", - "qq": "The :attribute value must be a valid QQ number", - "ip": "The :attribute value must be a valid IP address", - "ipv4": "The :attribute value must be a valid IPv4 address", - "ipv6": "The :attribute value must be a valid IPv6 address", - "mac": "The :attribute value must be a valid MAC address", - "url": "The :attribute value must be a valid URL address", - "domain": "The :attribute value must be a valid domain format", - "length": "The :attribute value length must be between :min and :max", - "min-length": "The :attribute value length must be equal or greater than :min", - "max-length": "The :attribute value length must be equal or lesser than :max", - "between": "The :attribute value must be between :min and :max", - "min": "The :attribute value must be equal or greater than :min", - "max": "The :attribute value must be equal or lesser than :max", - "json": "The :attribute value must be a valid JSON string", - "xml": "The :attribute value must be a valid XML string", - "array": "The :attribute value must be an array", - "integer": "The :attribute value must be an integer", - "float": "The :attribute value must be a float", - "boolean": "The :attribute value field must be true or false", - "same": "The :attribute value must be the same as field :field", - "different": "The :attribute value must be different from field :field", - "in": "The :attribute value is not in acceptable range", - "not-in": "The :attribute value is not in acceptable range", - "regex": "The :attribute value is invalid", - "__default__": "The :attribute value is invalid", -} - // getErrorMessageByRule retrieves and returns the error message for specified rule. // It firstly retrieves the message from custom message map, and then checks i18n manager, // it returns the default error message if it's not found in custom message map or i18n manager. @@ -79,9 +26,9 @@ func (v *Validator) getErrorMessageByRule(ruleKey string, customMsgMap map[strin } // If there's no configured rule message, it uses default one. if content == "" { - content = v.i18nManager.GetContent(v.ctx, `gf.gvalid.rule.__default__`) + content = v.i18nManager.GetContent(v.ctx, ruleMessagePrefixForI18n+internalDefaultRuleName) if content == "" { - content = defaultMessages["__default__"] + content = defaultMessages[internalDefaultRuleName] } } return content diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index 6bdf61a6f..fa687880c 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -27,9 +27,9 @@ func Test_Check(t *testing.T) { err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) - t.Assert(err1, "invalid_rules: abc:6,16") - t.Assert(err2, "invalid_rules: abc:6,16") - t.Assert(err3, "invalid_rules: abc:6,16") + t.Assert(err1, "InvalidRules: abc:6,16") + t.Assert(err2, "InvalidRules: abc:6,16") + t.Assert(err3, "InvalidRules: abc:6,16") }) } @@ -995,9 +995,9 @@ func Test_InternalError_String(t *testing.T) { aa := a{Name: "2"} err := gvalid.CheckStruct(context.TODO(), &aa, nil) - t.Assert(err.String(), "invalid_rules: hh") - t.Assert(err.Strings(), g.Slice{"invalid_rules: hh"}) - t.Assert(err.FirstString(), "invalid_rules: hh") - t.Assert(gerror.Current(err), "invalid_rules: hh") + t.Assert(err.String(), "InvalidRules: hh") + t.Assert(err.Strings(), g.Slice{"InvalidRules: hh"}) + t.Assert(err.FirstString(), "InvalidRules: hh") + t.Assert(gerror.Current(err), "InvalidRules: hh") }) } From fac9ab5c010f28243e7121548e2ec1cf01016c66 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 19 May 2021 19:00:34 +0800 Subject: [PATCH 275/492] improve package gvalid --- os/gres/testdata/data/data.go | 2 +- os/gres/testdata/testdata.go | 2 +- util/gvalid/gvalid_error.go | 2 +- util/gvalid/gvalid_z_unit_checkmap_test.go | 5 +++++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/os/gres/testdata/data/data.go b/os/gres/testdata/data/data.go index 099d514e3..ff96670de 100644 --- a/os/gres/testdata/data/data.go +++ b/os/gres/testdata/data/data.go @@ -3,7 +3,7 @@ package data import "github.com/gogf/gf/os/gres" func init() { - if err := gres.Add("H4sIAAAAAAAC/7RaeTiU6/9+sq8hSyohYpAxtkiyFLKMfYtSGgxxGGHsLShlSUqh0nZQpGObiCMplX3LlrQoCpF9ZAv9rkjed4wl5/ftuuIfz33fn/t9tvfz3oZoSioOQAfowCDW0RBA/q0D9MDWFWfv6ICa/SWBd3VxNjOlBms6dtofEqvQRaK0xMsMM8wM62pKa/QaUShUE0q7ClmqLa6NNG8Lk9DRKxM/325saGioVSZebQ7uycpKN8m8lHkpkyUtI5fTIGVIgUYghB0SbS7KmVJQAfDjhyGalu76swt25gAAewDA4vKY5+W5+Eo44hz/X5UZzyszn1c2FqimSKoMgNM2ksZQZTS/lc1IGmumOPxzMPRPFi9rw+/BSFtPD7yry//MfKP5Es3mSzRVl3u7vPkcC1T+D56BybzAffMCJWnbTy//DJhJBS77KAC47HYTViQdoAd2ju5SKA9Pm5nhze9SDq38SXJBhv/8j8RjPfBSEngf/LxLEnp1SG2khJSpVnXNNu2qbWt+l1nw4NLO9QAAtiU5GOY4ZrDnccljhlCqTLPPmLOUdVS/MFfvmPR/c0wa5pg0eceMF1TnH8lLuWLHpGcck4Y7thBTT4VQsmLHpFex3JkAPXCU2oFDYXHwFd5WrlVVVrYvw8xwCEhdzi0nhrsbq04eZVWjZ50TZ+uNVOUDAPCvjMEJsxiD37sd4271n0si7vPJSxx7l3so9P6uJyII6q1Eu5KsThP/y2DNHKeRA7+XFABg28o43T0X48yT/GHzPHjzermDr/0ytX8Riud3PjsfzqwTAH7XuCPvLyQAQGRJPpY5Pr8jSDV9OOV8lQTvwvo7/tGzXj6lULyrdOtNAhXdHFVXS3mEEABgy4qpTPf9P1KRn1Y/qVaxEBl/DUXaObqjsLhVrkUIAuoI1tnZdakpOj9H3L862XIAAFj/iMDb1d3ZjoRArAZVrm2UYWbIQAsluMHmiSAlWMYDJ8x/9cAJ88sDX8wCD3KMF66h+XWaby9myT9ztP8J2awfMLIZOzKNDf0Itm+I95Qgy+TmbmtnrpnZuWJH3D1X4QgnHOGXI9CDf5EFPu+G87EtIptn6v0Tolk3YDeMubmRl0G6aWC/9j5av+z8WAulmNk4VmHIhgUgvzxx8nDF/daa0vHzmmM46w3BeH5/cFrze1IXbGHMW36WkOGbtYYc36xFhPkJY5U9bxJDL3qalG95k0z3rcIk7gUgv0zygUxuQmZ6aZWRCBopVl1Z88BMukHG0FirCl1tUFaJzjETQ2rXZxsScj4RymSzSh98+llfRtnM2vOfNdNleMsuiU3HMxhUf5fI6scVaQAAMPhTdbOW/kd1ojOr1X/WerkJVt7YCGULav45cRPCutH6JOIWngF0EHGruF6wzQ23d3TG/u/uGHAaJ8xK98jZY79IZ3dX2XnHmqQRkTUUc8QcxRUWPy8aoksSs8KI3T1XshUtetd4ZoyaEgcAIJZ99ZmnnL1wLFx8C5Y6XjWofVxDQfRw4eb6mwa9Rm0p1L9vmcqGqIcSAADxJYnZSYlN9/2/L6AQXd8TRvph7y/NiMysPTuQ6zxc6De/S5WpltdjAACOS85aeqjUVb5HuLu64lG2Hh6r2G/WQYajPPC+zliJOaCfRtUZJ8/sj1oVNdtqkdX/6GQbS8qJJWfQMMYkml/+Itxhd7fTUnRrUkd0IpelRyPF7zkp1mLTvR0AIL2keIY5dkcXjAN2FfLZYQAoZ1cHV4mjOIff+p1jfXc0/LWpJEk6OlRgZ8zLK+qW9pWnudUwkiMupcay9mWl4vbC/s0l7obszQ84yqK2HzAVOK3AQRD6HmVb7hV15/qou36/b41nC9GqiVjSP31cZXS0IP37dGdo5kNZLirtIAACbtPTuDACwPpo9OMJAPg9lPMoAOjBvNn4lVLn0Z2fZ7fK5k8vqMDa0J4WpYBhXTdw2FV0w9dS9yxJzTZHDpHzqh6IPVMi+Kd9u8e3TIngGS4GU343yk/WTby19jMLdVS5et8ZDOsA37pX2zeHJARvfx1QzB/U+LeToVmIBB0TTQI31bo6LpdhSgdWrZ2sLFzh508XHjt+/O8Ch79li824rdBoxrthAVtYxZgotIO7DLCtk6bJlZdOCo+78speDHW40CUuHcHQMlX8b33aAzz/Bb5rKjWRO0WHn5xLruQ72OrIcYXv/NpTamaYHzRsAyelhr8ypavoCH7EqKMEgrWwUpijIpPWClxX1Se8c96rnHzTNR2pNm70RYuShq1EIN5NPF5iD2fanfiUtPiUwGDBCw5sWrl5MgwjLLVPWmVpYvYS6xrfZlNuurhB3Xrjp43szoeTRuQnz37hy6eOUBUe/3Hr8U7jg5S+5sSzUx/UTz6n99kaWnz7o0WeHUOfGNvepEMahdPrJ+Pcc/wTOU/pJhKOPlKrD044/GP99/4AzI8rA7e+Un7PVMnj5+hJP9hAjXw03E8JVAuFjyj9QztwKYGRWVHEbVzsm1j23eMqOWZWu4/t/yJi+OG8gVTPiKD/o0OCJv4hBVMbvk8jzOWV28I9NTKDNZ0fspzbJX/3xZndApFBCKX+igphujGLK6fufx0fCDyk9OBdFnWnS1rMuYB8oZaeSqehnfK9Z+p9eomjtvvQ1Q5hsbpf/D7jzp7ujLG+Ynn9K2gxHhG0E41lVEhnO3Mi1uJkQdCWhtLYJkuV+5aSZpyb3vnbDIx62lx4IX/l3WU6pbwvxA1itIyXs2U34rTzsm7y8agp1ga4BPP60NUOeLkKYxnTvr44jIsro2hqxvO3I+P3ve42Dm5KfH41hKnilMDVnB4xwVf5m8XT2IrcTl0MOy+tg2D2SeJswOtphiOLNOiuh5qaP37tx7O/T7Y3qI3VS1nz/V4Vcd1U0+mq2vO56jj2QINzGMGU9tp6Hvv7RyptbQPMfY2FakVzqI3bnqgrx3zQlH4+ZR88Uv2Gm00Ubz3McsSwje5xReWjhM8Xf6A36rgHnjQIMx66S8N9cDueS8LrfndnZDCPnmD952gcUXtANOv7t9pXHlG3H8ZMbdDQ+nYuhGXPgy43lZqJAU5uo620rP+WI5CONWrPH1M7dB9N0vU5r03jrPAyx8BboG7c+FF6X5fpTh6Lb2UHMQNWrp1OVqkPRp4PixSuC89jpn30XHiNWq+Kwmmr6afPY68iULa9hJH1B1+bsg+uieMpD7coutqK/WgqPoxvvZePfjkY2C0ldeRzerDaVgdE+GvvjQirl4gj6TI4/j24orhqpwHVQtozOoLMjROb37GMC/xlvxcR3GprIZ81/N6MX8W8M+kWVoH76DUF9bsjsQ1yu7d8s2VMfKjae6bxr9I8xU25CsObKQKfZR5CpzRXqCWsk3TH8BcGnW1i7r/gtumOuZZvtq1io1h6sJtKo2WZ+dnNsTYfjG7u3Jo/2RHxcnrKsLmHdxLVpWyQct/az+FQaE1oz18tFt2EVq1r+DPh3bIjavWGx7W+lSVzvcNPXrzpj2RnP1z2YJ04H59WRZQ/9d6kwfdTTnL+n6yCLZ+YTR8N8y5+i6jw846/YemAOiHuyy7IFJZYk/+Q3sZLYaA4Ze3Hs61Xr01rvi2ZTIj6Xq17ryTksnyUxpn7DOeQD/I+hAw84CshOLrveNB6WLTqQhrH61MEgqDSE//rj/7NfvNtMPjDAXxyAbGtPSCMKvWMhZefeQNvq8ydNTXxkRhV3rGso7ly/s6V397qntCxqgofXK9ZHbk9hDqPmp5vhwBRzF03tle2O2TK+YaDVgtHZE6oJgNv51B2jtbxqiMvHjdx12MH9Fj8iUMK8t47o+W8dGLd1/sGqmVnEffu3btncO8g0eAf1YxJKgPFSsVGuwyNApNYOpYo1xoR8cGWNh2eAj2f3E4n+b+G3na82Vv3dOT7X9h/mZn7/51EFlOarYks5Dp4Tb6JicMkT5ivibdYb/hqo2PKmdJch5MT2c0fr8kpRaMk+W9Lhdhad8bk7dfkTG15iX469gN3jb0uvLT4iF1yeljh9WcjtRvV2N+WMPOZ4+3kPBk8CkwRZsV5N1Is44fCWyKZI+J16Xa5ynGytHS5H+hKe1ohpVi5Gydmb/50jJFtIzUq3gBFredq6reWZvh1vb73EcGLVpqflQQ/qbp4Bx4LEsisai4qajuq+SAp1V6xtK/WKvmF7uV2Z+24qU/x1xKVLD4e7o9x2GJOeN2s0GfFOjAVQsmq6ZySkx8pcElajiW+jw9Jl8PrM5RLEGDf/i3L7t2djvKPzTdDQ5nenOilxKYhLW2UxxwfHn47FfciB7MhtX2/5wcuW8Gwa592pBfF/sP4NZm/aUvRjZ7eWN0nqJyguAgDKrSeuP6rgtemfFua2iVPSx1TDzRhjjDpZHqJodpxsv+szOeiUOXxBB3vKqG91uLja5UenyiVPhA7dG2oYG9GJ1Er8Vq1rNKhvm8yhl5N9uPoi3Vu2RRf8zpaJnLHc/E5DGcJcXt6cxIPlLwPuvPk5I++1sEJHf9nSX+HbWLWRwzeFb3ZlZBbvyPpBnHwXPox76nLvXHtJeUWbZk1dXERdu0V+8zzk2gv9GTJqfXgEbue3zxGzfb99qsKXOSEscCT5J7r6a937Us68rhrs772eAPjs3vq0wFJpxt5DxWcS5Wydabl9LNKPS9Vd+J0oL5lvSndQP8lAd9dYQO6uiwX1eI43nRlxVQr8h1/sxkx2euv0a0RJ8BvwYFXRTcc9OGXl2zd/8L27bo3idzJgx29ee22DaX8OyzM918q9XvTQ5BoMI7mkcphkfbm76jVsvUvfn5PngFJQFSNVkeljX6x387dTfxg5xBagxhn2adfQZ+5fkr1jE/EOl2rOAHlraNf0ZGVpoHB8bHl0m6sIevZA/hLtl0vUL9d+vmG4unvd/FWj5kaDhyg7BFpVDRxP1a9kVfq/bP+7W8ZXLEbKeIKhLmtfdrX4IuFm5rVDQYk3+10KRvDKn8Z1tpKTIgZLbx0Kq91tJKvsuPhk+Zj7UQd3aIMO2EEa9F7r3f+xzJv47OH9yhb04iVDFbzj+Ad23I41/ffFH4kkUaMMBIcVmadbHEfkZpips7PKcS2pmVVlidFDezAtqa1vKq+8s+VU8Y6Vp7xhOEHkoe2Zrndr0B+Q3t+JPbLxERUmOFy8y039RwfczUvG6a9p4coq0Gl+nTcEfH1oGpQSZa+/aK92dpgwsq+2rAtJ10pcxLPLdCcXpOpcPe9LuJS7kvHeBfLKaeQbTdc2ngPpr/CHNRO29qgiTg7UaDB/GJfVyXl83RUecaBEcuIBsUBDaHS/F3u+syjrYEjbwuR/677J+SY0+FH6g7XvGmnYnsPmOAIeRPrWrWPmpz2DrjI87f/bn1cJkeqcDi3L16mdquIcolH/9vgdUVBfkaT/E9aNzUU8764je1zaXrZbPO53NUj6FqCtfvZpFO+iu+VpzKEZAOpdwjkjdT2buuuu65xMZUG2d9sMnbswgsLJGJ98OF3pfUn2Ao27vjwge8GftDGzRPvLHX35ai8Y5aiQSIuKvJD4UR5UIPJDzWB0Om04zS8V0w4fTmln6U9H1Ut6mtpoylqTmWPLt4jNp3GzExhzsKJq/ym9eW1HLcXl6vJCb1Ht7xik33YEv7JyR5pLB5qtEddo+GLduNplfEQw+XRXT8h+QorKbVLqckifUwrO0Cf8dCtzQeY8KonaToaGDd87Erd+0V3Oj/5jF5UZjTGZpyDpXNiyD3m0GOCzf3Qg/20eabhxl5vY827TlXWfRbOPI8f0s/UwmXrEMKDFJU7zzxrqDk15jC6P7xDHc3ryRdNX3P6htnhfslIbCxRXLuaJzwgU4eloCtMNruyOSKcgVVJga2IpkGjHY0l3mpIKrze945HaNCgzuRSYjYrLtTk+iHpbXVCkZ05Q7d3CVp94tmJeiOcd2IIoR87Ush5P0PTgcYqkOL4fVP9o8Ty3EDZqdefej6dfUt1pZhtWGAf5mywDbqAg3oXQ49s37B05AaPHBHWxocjTo/S6S+9d0ejT3TfHIlpLJY+3lCb3QPi3XrUVRjLt9tR+b3GyuhZazyWQXsjy7epnHvZHHKFMVLVg8UtTNk1deRQQJv0Yc24JhU0rfONpM17VJJ4C7Imd1b6pNn272OM7LeJQ6Brzu4dyOlXjtZlQtbemdj3bJr9d9ev/3ZABhMAl5mXa67Pvvng7LA+EkfwkNdb8epal2J+JqqPDtLBRWCQ4oxonupGngyGLc/Mo52tL04bxPQnGmgW7Y8IT3h2ICzcw7R9MrBwu092EoHLnmhIw1fYnzJ292tad+HN8FPyBcwf+BCn+UIL/P069IY2ae+n2ew/UPew0sK/76unPR+9w5PcdAGG+MPBu6tfyPMkyJee8rzR/WTvWN8U41xFmN4x03MAgIfL9vB/VrSK3s3P10A81uWoMwaPJWfIkLgRylykTMustBKtnWoo8uvbqplWRaV2I+Xv5sLjGBEgCADYvORLKyuUzBnj6+r563Pin7268pGBQdm64vAYRxzWHa4/JU1fW6+iEm1mqFNds027shKNNE5JbSeUSWZ5jIwyug0PezATamsksz6lpOlXoe+lzjdMhGjf9ssDAKSWFMNDToy9qyt+CSUVNSjteRnEURa3xTSYFitqLf8ljKyGI1iM3eIayrX0dGc1zPZoJLOIRz3xi+qY8L9Qvx0AgPpzHbO/4ToIRvhB77WhTEfNSkXKqjI/EQKN7oWKelkxMDAwyAldFVK459U3jtMI/fceg3Jo9PgdIbkJVqOMsO7uqIZ3QddfRJlSC10oaBC482a/xKbjIRvktMdFow0TjKUrOKI81oWHu0RHEYk662SIxuGXJZl3lza+2iPbnWQXg7l2MeaWdVAjSKZKwW06HoJ2Hi48QfW7yPLqi1uiAAATfzqJpVcxicl4JU123pDMkfk1t995uIqT5HPMCnnIzQ0CfA7M84grNe9aJQ+5Z5+SVl2JrhbV056bhHUVImVVRsYv76e27yntahe27wjm+Hybftud9s5Y/r/mP5NTjXfS/pyD+ks+Hk5yOlwwjqv5PMm/GNbMDyky/nkOD8tkwe2r1Oud+VC59j9QSS9CRfKkbFR820mpyHf756iWODHWUHBQzmuFxqXWAfrff5cY+PPnEgkuUiBouokZBnRrHogk5zOPQT4HNbcwf+xWWAMWpqLgAqDZow0wAV6/By+ViiLFg0aFOGB4PBRg6fzSUnUxw+p6TApFpjzyHe45jN7daygBuaQRvBpoIogLVo0qZPgiSSNSMGgUiAEGljgHBo8ULWUIFcyQsV8Aq/BBhgqQyw/BpUNzPnAf3CHDF8kPkYJBAz5wH17NgcGDQiv3QYwakKaC4NzQb2tMMG4sNSCfCiKFgMZx4BAlcxCksZ8FKiAJGziEIg0gn+IhhYDGWVhgEPfnIMgEc1aOwkULFs3crPx5BP5CWdG8ZITNy/xfQxfGaeAlQJMv8Kk5DUcgF6chBYOmXOBg1nRguejMymt7CgWDxWTgcqApFrgcCnqwXEyGFAyaV4GDuZKAkYnBrLy2OigYLPAClwPNpHDC5HAwgGUCL6RY0PAJHOs4CdbCTMtSla2FVfYRikUaXiE5NyDBEvipKsQIVhBeIcWDBkfgeNEL8ciEU1ZeJDUTWDx8AhcFjXpww0TpLAAhEz4hhYOGM+BwXxbCLUyLLLUp0cE2pWBmQC7fsfhBwQZT82JuONl8BykONFMBx9m2FiwR4CDFgQYlWOGXBxgOSR6DFAYae4Bfz9azgKUzFqRQ0FgCOwzqAikUSWpiqUdFD3tUiqyAbKhhpZcbf1ZALtQALwQaMYBf5vMgw8mEGpbSwQDTsYMNkM8nkOyJkI4d3FJfGMDCfAIpErRTBj/YPXjBov2+lR/sGnyAtMcGFwBtgcFLCeADS/bYlnKVFeZqLxRpYQMNLgjav+KDCZLlBytuoC14fYJ0pHhgqIXkUMl1NBbsg5DmEhxSYwtYWWOLFBLayoFDVpGDJNenWPlTIQiApTpCcGXQrg1c2TgZmBX5B23QwCExgmBlzR9SSGivBQ5ZRg7yT/3jhPlntBUs27KBy4P2VPhh8m4shkWuZUMKC+2fwGHphMDK2zMrP5g9ILCQjYWa5ucfKANl4M8GAOHn+wr4vwAAAP//lfLuQkM3AAA="); err != nil { + if err := gres.Add("H4sIAAAAAAAC/7RaeTiU6/9+sq8hSyohYpAxtkiyFLKMfYtSGgxxGGHsLShlSUqh0nZQpGObiCMplX3LlrQoCpF9RIR+V4T3HWPJ+X27rvjHc9/3536f7f28tyGakooD0AE6MIh1NASQf+sAPbB1xdk7OqBmfkngXV2czUypwZqOnfaHxCp0kSgt8TLDDDPDuprSGr1GFArVhNKuQpZqi2sjzdvCJHT0ysTPtxsbGhpqlYlXm4N7srLSTTIvZV7KZEnLyOU0SBlSoBEIYYdEm4typhRUAPz8aYimpbv+7IKdOQDAHgCwuDzmeXkuvhKOOMf/V2XG88rM55V9D1RTJFUGwDmZB8ZQZTRzyqYl0WckHv41GPoni5e1YW4w0tbTA+/q8j8z32i+RLP5Ek3V5d4ubz7HApX/g2dgMi9w37xASdr208s/A2ZSgcs+CgAuu92EFUkH6IGdo7sUysPTZnp487uUQyt/klyQ4b/+I/FYD7yUBN4HP++ShF4dUhspIWWqVV2zTbtq25q5MgseXNq5HgDAtiQHwyzHNPY8LnnMEEqVKfZpc5ayjuo35uodk/5vjknDHJMm75jxgur8I3kpV+yY9LRj0nDHFmLqqRBKVuyY9CqWOxOgB45SO3AoLA6+wtvKtarKyvZlmBkOAanLueXEcHdj1YmjrGr0rLPibL2RqnwAAP6VMThhFmPwe7djzK3+c0nEfT55iWPvcg+F3t/1RARBvZVoV5LVaeJ/GayZ5TRy4PeSAgBsWxmnu+dinHmSP22eB29eL3fwtV+m9m9C8fzOZ+fDmXUCwFyNO/L+QgIARJbkY5nl8zuCVNOHU85XSfAurL/jHz3j5VMKxbtKt94kUNHNUnW1lEcIAQC2rJjKdN//IxX5afWLahULkfH3UKSdozsKi1vlWoQgoI5gnZ1dl5qi83PE/auTLQcAgPWPCLxd3Z3tSAjEalDl2kYZZoYMtFCCG2yeCFKCZTxwwvxXD5wwvz3wxSzwIMd44RqaX6f59mKW/NNH+5+QzfgBI5u2I9PY0I9g+4Z4TwmyTG7utnbmmp6dK3bE3XMVjnDCEX47Aj34F1ng8244H9sisnm63j8hmnEDdsOYnRt5GaSbBvZr76P1y86PtVCK6Y1jFYZsWADy2xMnD1fcnNaUjl/XHMMZbwjG8/uD05q5SV2whTFv+VlChm/GGnJ8MxYR5ieMVfa8SQy96ClSvuVNMt23CpO4F4D8NskHMrkJmemlVUYiaKRYdWXNAzPpBhlDY60qdLVBWSU6x0wMqV2fbUjI+UQok80qffDpV30ZZdNrz3/GTJfhLbskNh3PYFCdK5HVjyvSAABg8KfqZiz9j+pEp1er/4z1cuOsvLERyhbU/LPixoV1o/VJxC08A+gg4lZxvWCbHW7v6Iz9390x4DROmJXukTPHfpHO7q6y8441SSMiayhmiTmKKyx+XTRElyRmhRG7e65kK1r0rvHMGDUpDgBALPvqM085c+FYuPgWLHW8alD7mIaC6OHCzfU3DXqN2lKo526ZyoaohxIAAPElidlJiU33/b8voBBd3xNG+mHvL02LzKw9O5DrPFzoN79LlamW12MAAI5Lzlp6qNRVvke4u7riUbYeHqvYb9ZBhqM88L7OWIlZoF9G1RknT++PWhU122qR1f/oZBtLyoklZ9AwxiSaX/4i3GF3t9NSdGtSR3Qil6VHI8XcnBRrseneDgCQXlI8wyy7owvGAbsK+ewwAJSzq4OrxFGcw5x+51jfHQ1/bSpJko4OFdgZ8/KKuqV95WluNYzkiEupsax9Wam4vbB/c4m7IXvzA46yqO0HTAVOK3AQhH5E2ZZ7Rd25Puqu3+9b49lCtGoilvRPHVcZHS1I/zHVGZr5UJaLSjsIgIDb9DQujACwPhr9eAIAfg/lPAoAejBvNn6l1Hl059fZrbL50wsqsDa0p0UpYFjXDRx2Fd3wtdQ9S1KzzZFD5LyqB2LPpAj+ad/usS2TIniGi8GUP4zyk3UTb639zEIdVa7edwbDOsC37tX2zSEJwdtfBxTzBzX+7WRoFiJBx0STwE21ro7LZZjSgVVrJysLV/j504XHjh//u8Dhb9liM24rNJrxbljAFlYxJgrt4C4DbOuEaXLlpZPCY668shdDHS50iUtHMLRMFv9bn/YAz3+B75pKTeRO0eEn55Ir+Q62OnJc4Tu/9pSaGeYnDdvASanhr0zpKjqCHzHqKIFgLawU5qjIhLUC11X1ce+c9yon33RNRaqNGX3RoqRhKxGIdxOPl9jDmXYnPiUtPiUwWPCCA5tWbp4MwwhL7ZNWWZqYvcS6xrfZlJsublC33vhpI7vz4aQR+YmzX/jyqSNUhcd+3nq80/ggpa858ezkB/WTz+l9toYW3/5okWfH0CfGtjfpkEbh1PqJOPcc/0TOU7qJhKOP1OqDEw7/XP+jPwDz88rAra+UPzJV8vg5etIPNlAjHw33UwLVQuEjSv/QDlxKYGRWFHEbE/smln33uEqOmdXuY/u/iBh+OG8g1TMi6P/okKCJf0jB5IYfUwhzeeW2cE+NzGBN54cs53bJ331xZrdAZBBCqb+iQpjuu8WVU/e/jg0EHlJ68C6LutMlLeZcQL5QS0+l09BO+d4z9T69xFHbfehqh7BY3S9+n3FnT3fGWF+xvP4VtBiPCNqJxjIqpLOdORFrcbIgaEtDaWyTpcp9S0kzzk3v/G0GRj1tLryQv/LuMp1S3hfiBjFaxsvZshtx2nlZN/l41BRrA1yCeX3oage8XIWxjGlfXxzGxZVRNDXj+duR8ftedxsHNyU+vxrCVHFK4GpOj5jgq/zN4mlsRW6nLoadl9ZBMPskcTbg9TTDkUUadNdDTc0fv/bj2d8n2xvUxuqlrPl+r4q4bqrpVFXt+Vx1HHugwTmMYEp7bT2P/f0jlba2Aea+xkK1ojnUxm1P1JVjPmhKP5+0Dx6pfsPNJoq3HmY5YthG97ii8lHC54s/0Rt13ANPGoQZD92l4T64Hc8l4XW/uzMymEdPsP5zNI6oPSCa9eNb7SuPqNsPYyY3aGh9OxfCsudBl5tKzfgAJ7fRVlrWf8sRSMcateePqR26jybp+pzXpnFWeJlj4C1QN2b8KL2vy3Qnj8W3soOYASvXTier1Acjz4dFCteF5zHTPnouvEatV0XhtNXU0+exVxEo217CyPqDr03ZB9fE8ZSHWxRdbcV+NBUfxrfey0e/HAzslpI68jk9WG2rAyL8tfdGhNVLxJF0GRz/HlxRXLXTgGoh7RkdQebG8c3vWMYE/rLfiwhutbWQzxp+b8avYt6ZdAurwH30moL63ZHYBrndW77ZMiY+VO090/hXaZ7iplyF4c0Ugc8yD6FTmivUEtZJumP4C4PONjH3X3DbdMdcyzfbVrFRLD3YTaXRssz87OZYmw9GN3duzZ/oiHg5NWnY3MM7gepSNki5b+3ncCi0JrTnrxaLbkKr1jX8mfBu2RG1esPjWt/Kkrne4Scu3vRHsrMfLnuwTpyPT6siyp96b9Lg+0knOf9PVsGWT8ymjoZ5F79FVPh5x9+wdECdEPdlF2QKS6zJf0hv46UwUJyy9uPZ1qvXpjTflkwkRP2o1r1XEnJZPkrjzH2Gc8gHeR9CBh7wlRAc3Xc8aD0sWnUhjeP1KQJBUOmJ//VH/2a/+TYY/OEAPrmA2NYeEEaVesbCy8+8gbdV5s6amvhIjCrv96yjuXL+zpXf3uqe0LGqCh9cr1kduT2EOo+anm+HAFHMXTe2V7Y7ZNL5hoNWC0dkTqgmA2/nUHaO1vGqIy8eN3HXYwf0WPyJQwry3juj5bx0Yt3X+waqZWcR9+7du2dw7yDR4B/VjAkqA8VKxUa7DI0Ck1g6lijXGhHxwZY2HZ4CPZ/cTif5v4bedrzZW/d05Mdf2H+Zmfv/nUAWU5qtiSzkOnhNvomJwyRPmK+Jt1hv+GqjY8qZ0lyHk+PZzR+vySlFoyT5b0uF2Fp3xuTt1+RMbXmJfvr9J+4ae114afERu+T0sMLrz0ZqN6qxvy1h5jPH28l5MngUmCLMivNupFjGD4W3RDJHxOvS7XKV42Rp6XI/0JX2tEJKsXI3Tsze/Ol3RraN1Kh4AxS1nqup31qa4df1+t5HBC9aaX5WEvyk6uIdeCxIILOquaio7ajmg6RUe8XSvlqr5Be6l9udteMmP8VfS1Sy+Hi4P8ZhiznhdbNCnxXrwGQIJaumc0pOfqTAJWk5lvg+PiRdDq/PUC5BgH37tyy7d3c6yj823wwNZXpzopcSm4a0tFH+7vjw8NvJuBc5mA2p7fs9P3DZCoZd+7QjvSj2H8avyfxNW4pu9PTG6j5B5QTFRRhQofXE9V8VvDbl29LULnla6ph6oAlzhEkn00sM1Y6T/WdlPheFKo8l6HhXCe21Fh9bq/T4RKn0gdiha0MFezM6iVqJ16pllQ71fZMx9GqyH0NfrHPLpvia19EynjuWi89hOEuI29Obk3ig5H3QnScnf/a1Do7r+D9L+jtsE7M+YvCu6M2uhNz6HUk3iIPn0o95T17ujWsvKbdoy6ypi4uwa6/YZ56fRHuhJ0tOrQeP2PX85jFqth+3X1XgIseNBZ4k91xPf71rX9KRx12b9bXHGhif3VOfCkg63ch7qOBcqpStMy2nn1Xqeam6E6cD9S3rTekG+i8J+O4KG9DVZbmoFsfxpisrplqR7/ibzYiJXn+Nbo04AX4LDrwquuGgD7+8ZOv+F7Zv171J5E4e7OjNa7dtKOXfYWG+/1Kp35segkSDcTSPVA6LtDd/R62WrX/x83vyDEgComq0Oipt9Iv9du5u4gc7h9AaxBjLPv0K+sz1k6pnfCLW6VrFCShvHf2Kjqw0DQyOjy2XdmMNWc8ewF+y7XqB+u3SzzcUT/+4i7d6zNRw4ABlj0ijoon7seqNvFLvn/Vvf8vgit1IEVcgzG3t074GXyzc1KxuMCD5bqdL2Xes8pdhra3EhJjRwkun8lpHK/kqOx4+aT7WTtTRLcqwE0awFr33eud/LPM2Pnt4j7I1jVjJYDX/CN6xLYdzff9N4UcSacQII8FhZdaJFvcRqUlm6vycQmxrWlZleVLUwA5sa1rLq+or/1w5Zaxj5RlPGH4geWhrltv9CuQ3tOdHYr9MTESFGS4333JTz/HvruZlw7T39BBlNahUn447Ir4eVA0qydK3X7Q3WxuMW9lXG7blpCtlTuC5BZrTazIV7r7XRVzKfekY72I56RSy7YZLG+/B9FeYg9ppWxs0EWfHCzSYX+zrqqR8no4qzzgwYhnRoDigIVSav8tdn3m0NXDkbSHy33X/hBxzOvxI3eGaN+1kbO8BExwhb3xdq/ZRk9PeARd5/vbfrY/L5EgVDuf2xcvUbhVRLvHofxu8rijIz2iC/0nrpoZi3he3sX0uTS+bbT6Xu3oEXUuwdj+bdMpX8b3yZIaQbCD1DoG8kdrebd111zUuptIg+5tNvh+78MICiVgffPhdaf0JtoKNOz584LuBH7Rx88Q7S919OSrvmKVokIiLivxQOF4e1GDyU00gdCrtOA3vFRNOX07pZ2nPR1WL+lraaIqaU9mji/eITaUxM1OYs3DiKr9pfXktx+3F5WpyQu/RLa/YZB+2hH9yskcai4ca7VHXaPii3XhaZTzEcHl0109IvsJKSu1SarJI/66VHaDPeOjW5gNMeNWTNB0NjBs+dqXu/aI7lZ98Ri8qMxpjM8bB0jk+5B5z6DHB5n7owX7aPNNwY6+3seZdpyrrPgtnnscP6Wdq4bJ1COFBisqdZ5411Jz67jC6P7xDHc3ryRdNX3P6htnhfslIbCxRXLuaJzwgU4eloCtMNruyOSKcgVVJga2IpkGjHY0l3mpIKrze945HaNCgzuRSYjYrLtTk+iHpbXVCkZ05Q7d3CVp94tmJeiOcd2IIoR87Ush5P0PTgcYqkOL4fVP9o8Ty3EDZydefej6dfUt1pZhtWGAf5mywDbqAg3oXQ49s37B05AaPHBHWxocjTo/S6S+9d0ejT3TfHIlpLJY+3lCb3QPi3XrUVRjLt9tR+b3GyuhZazyWQXsjy7epnHvZHHKFMVLVg8UtTNk1deRQQJv0Yc24JhU0rfONpM17VJJ4C7Imdlb6pNn272OM7LeJQ6Brzu4dyOlXjtZlQtbeGd/3bIp9ruvXfzsggwmAy8zLNddn3nxwdlgfiSN4yOuteHWtSzE/E9VHB+ngIjBIcUY0T3UjTwbDlmfm0c7WF6cMYvoTDTSL9keEJzw7EBbuYdo+EVi43Sc7icBlTzSk4SvsT/l+92tad+HN8FPyBcwf+BCn+UIL/P069IY2ae+n2ew/UPew0sK/76unPR+9w5PcdAGG+MPBu6tfyPMkyJee8rzR/WTv975JxtmKML3fTc8BAB4u28P/VdEqeje/XgPxWJejzhg8lpwhQ+JGKHORMi2z0kq0dqqhyO9vq2ZaFZXajZRzzYXHMSJAEACwecmXVlYomTPG19Xz9+fEP3t15SMDg7J1xeExjjisO1x/Spq+tl5FJdrMUKe6Zpt2ZSUaaZyS2k4ok8zyGBlldBse9mAm1NZIZn1KSdOvQt9LnW+YCNG+7ZcHAEgtKYaHnBh7V1f8EkoqalDa8zKIoyxui2kwLVbUWv5LGFkNR7AYu8U1lGvp6c5omOnRSGYRj3riF9Ux7n+hfjsAAPXnOmZ+w3UQjPCD3mtDmY6alYqUVWV+IgQa3QsV9bJiYGBgkBO6KqRwz6tvDKcR+u89BuXQ6LE7QnLjrEYZYd3dUQ3vgq6/iDKlFrpQ0CBw581+iU3HQzbIaY+JRhsmGEtXcER5rAsPd4mOIhJ11skQjcMvSzLvLm18tUe2O8kuBnPtYswt66BGkEyVgtt0PATtPFx4gmquyPLqi1uiAADjfzqJpVcxicl4JU123pDMkfk1t995uIqT5HPMCnnIzQ0CfA7M84grNe9aJQ+5Z5+SVl2JrhbV056dhHUVImVVRsYv76e27yntahe27wjm+Hybftud9s5Y/r/mP5NTjXXS/pqD+ks+Hk5yOlwwjqv5PMm/GNb0Dyky/nkOD8tkwe2r1Oud/lC59j9QSS9CRfKkbFR820mpyHf7Z6mWODHWUHBQzmuFxqXWAfq5v0sM/PVziQQXKRA03cQMA7o1D0SS85nHIJ+Dml2YP3crrAELU1FwAdDs0QaYAK+5wUulokjxoFEhDhgeDwVYOr+0VF3MsLoek0KRKY98h3sWo3f3GkpALmkErwaaCOKCVaMKGb5I0ogUDBoFYoCBJc6CwSNFSxlCBTPk+2+AVfggQwXI5Yfg0qE5H7gP7pDhi+SHSMGgAR+4D69mweBBoZX7IEYNSFNBcG7otzUmGDeWGpBPBZFCQOM4cIiSWQjS2M8CFZCEDRxCkQaQT/GQQkDjLCwwiPuzEGSCOStH4aIFi2ZuVv48An+jrGheMsLmZf7voQvjNPASoMkX+NScgiOQi9OQgkFTLnAwazqwXHRm5bU9hYLBYjJwOdAUC1wOBT1YLiZDCgbNq8DBXEnAyMRgVl5bHRQMFniBy4FmUjhhcjgYwDKBF1IsaPgEjnWcBGthpmWpytbCKvsIxSINr5CcG5BgCfxUFWIEKwivkOJBgyNwvOiFeGTCKSsvkpoJLB4+gYuCRj24YaJ0FoCQCZ+QwkHDGXC4LwvhFqZFltqU6GCbUjAzIJfvWPygYIOpeTE7nGy+gxQHmqmA42xbC5YIcJDiQIMSrPDLAwyHJI9BCgONPcCvZ+tZwNIZC1IoaCyBHQZ1gRSKJDWx1KOihz0qRVZANtSw0suNPysgF2qAFwKNGMAv83mQ4WRCDUvpYIDp2MEGyOcTSPZESMcObqkvDGBhPoEUCdopgx/sHrxg0X7fyg92DT5A2mODC4C2wOClBPCBJXtsS7nKCnO1F4q0sIEGFwTtX/HBBMnygxU30Ba8PkE6Ujww1EJyqOQ6Ggv2QUhzCQ6psQWsrLFFCglt5cAhq8hBkutTrPypEATAUh0huDJo1waubIwMzIr8gzZo4JAYQbCy5g8pJLTXAocsIwf5p/5xwvwz2gqWbdnA5UF7KvwweTcWwyLXsiGFhfZP4LB0QmDl7ZmVH8weEFjIxkJN8+sPlIEy8GcDgPDrfQX8XwAAAP//frYA1UM3AAA="); err != nil { panic("add binary content to resource manager failed: " + err.Error()) } } diff --git a/os/gres/testdata/testdata.go b/os/gres/testdata/testdata.go index b228bde1c..37c21f2a6 100644 --- a/os/gres/testdata/testdata.go +++ b/os/gres/testdata/testdata.go @@ -3,7 +3,7 @@ package testdata import "github.com/gogf/gf/os/gres" func init() { - if err := gres.Add("H4sIAAAAAAAC/7SaCTTU6xvH3+xZQpYUIWKQMQyRZCnKMpaxRimNNWKEka0FpSxJKVTaLop0rRFJuir7viUpRSGyj2yh/ykX8xszmnT/nVNTJ+fzfN/v+7y/5ZkvGkVNwwkYAABX3G6hAcEvZrAa2Ds623kgbFyx9o4Opia0YFXL2+SDaBQ9A+EPkkdwECHgNp4eOFeXX5IYwLCdI4QkQJr077+kcK4uzj+pXdvtD0pU6MIRWpJl6HRTdH1NaY1eEwKBaEZoV8FLtSW14WYdoVI6emWSFzqN0Gi0VplktRm4LyeHbJatla2VzULKyuc0yhhSoWAwUYcE60vyplQ0AHz//kOriYZ8qxkAwH5ZrRvIaHXxkXLEOv6nMo0XZe5dlClN33nm1zJ5iGT+37xEL4o0WRR54/lF21+LJO6g/4eJRov6zBb1TQSoKxHrW9rhjAv6bB3dZVZwRNZAAAgPT+sVnA6+JZAfv+E4Ow+cjBTOG7dol5RePVwbLiVjolVds0W7asuqhfUWPLy8fR0AgH3ZSmzQSj8rLNJJk4OpVWc5AAAslDuJ/FMnkf+Fk0iIk0jSThotWa9fBD/1bzqJ/OkkEurkUrKeambJ7zjpKLMNS4GTxNLWQABwW0f3BWGUbwf7EgjCDvtHO0LAQRy2c3Z2hV6qOsq1qsrK9qabokeAzBWwat409y9ONpw/Df/9Ml6u7s62RGUkahDl2obppmhGesIyN9k9YcRlKHLFCfPfuOKE+dcVH8wSV3KM0L5vt026NXwqCX8goCB1/G3uwdVs89Lz7SUsBAEA61dQcs4hSMmfBmUYoX0zbd7g7yvr+IP5Qrd2WjlzAwBYf9cjd88VeMRLivOvR4T3kIW+yZP+bv0iaOM6+QOvfTO0F/1xPr5JbCMAgHsF5eb8gdyy5vsnL737+YUwFgJ/7L70P1n3yx7iXFrI9zBcXf+PHqugqH9dcvJwxS7oTu76cQ9Fz7mV+WN7vQob7vpF5TqtWjgEBZuY8n7dSWSrzplFquqcaZmLTWWZvWgbYz9qlrgqpbaZ7F2BbfxkUP/a5k1wGDIz0kqrDMVQcInqypqHpshGWbSRVhWq2qCsEpVjKgHXbshGZ+Z8zCyTyyp9+PHHWtPLfp5Yvzl7XUY37ZDiPZHOqLawXDZf7ggDAIDByjTOmfyHGsV/nnG/uc2Qn2LjjwlXMacVnJc4JaobpU8kcemOsEIl/vjrCjZj3VIKwg673L0htxwf5m6kNn2UTX3xkNt4wdUEAACCv13MCUPpJTfkwY5nYrAinZ09ZRccaxLHxFZRzZfnLK4wlwEAiC9bnptEeXdPSq5mc6VpJfOJrznPjRAzkgAAGIXvNIuFf55ZEqd1yRUCpxbYOblbUfxQ4caGWwb9hh3JtAuPNCpoxCMpAIDksuXXky5vsvc/P2vBuj4nDfVD313+KTWj7txQrvNooe/iJa5MrbwBAwBwXFbwWojg/18/EtVxwpCrQ6YXaTfjbUuyuo39CJ5kDB0Ej/1oxS2/U9ndk1xlCvvQZlveETgAQGzZqlzQqnMtCCm8uOL5+9NPd/+hUrqnfPtNPA3DfMGetvJwEQDApt8saLL3Pyy43NO7u6sr7o/eg34AEDYeHiu4qPIsgSA8cD7OdlLzuB8rrzdK+nl31qqo2VIHr/5bJ9tIWl4iKZ2OKTrB7Mpn0S7be90W4psTu6ISuC08mqgWLnYSbda9WwEAyGUXwgbV4OiCcVjJ/WE9CQzC2dXBVeoo1mFhLc4xPtsaj/CWJCKjQoS2R9de1bCwrzzDo46RHnMpNZKzLyuVtBf1aylxR3O0POQsi9y630TojCJnpsi3SJvyY5F3b4y76w/61Hi24S2b8SWDsydUx8cL0r7NdodkPJLjptEOBMD/zmo6FyYA2J6MfzgJgKCHSh4VAH2YNxu+UOs8ufvj6VJ148eXNGBNSF+bsv+orhs45Cq+/kupe5a0Zocjp9gFNQ/Yrhkx3D8DOyc3zYjhGC8FUX8zzE/STbi95hMrbWS5xsBZDNuQwNpXWzcGxwdtfe1fLBjY9JcT2jRYioGZLp6HZm09t8sotQOb1nY2Vu6wC2cKj5848VeBw19yxaY8ligU071Q/01sEsxU2kE9Bnbt0yZJlZdPiU668stdCnG42COJDGdsmyl+3JD6ECd4UeC6ak3EdvHRZ+eTKgUOtDtyXhW4sOa0uinmOx370CmZ0S/Maao6wh8wGgihIC07GcxRsWkrRe5rGlNeOe9UT73pmY1QnzT8rEVNx14iFOcmGSe1iyv1blxyalxyQJDwRQd2rdw8WcYx1rpn7XJ00Xvw9U2t2dS8l9ZrWG34uIHD+VDimML0uc8C+bThaqKT328/3W50gNrHDH9u5r3GqRervTeHFN/5YJ5nyzggwb4n8eDuwtl107HuOX4JXKd1EzKPPlFvCIo/9H3dt0F/zPerQ7e/UH/LUM0T5OxLO9BIC38yOkgN1ApFDyv/TT90OZ6JRUnMbVLiq0T2vROqOaaWO4/v+yyGfn/BQKZvTNjvyUFhY7/ggpn132ZhZgoqHWGeuzOCNJ0fsZ7foXDv5dmdQhGBMOXBigpRhgnzq6cffJkcCjio/PBtFm23S2r0ef98kba+SqeR7Qr9Zxu8+/HjNntR1Q6hMbqffT9hz53pjra6anHjC2gzGhO2FY9hUkxjP3syxvxUQeCmxtKYZgvVBxbSply8b/2sh8Y9rS++VLj69gqDct5n/HoJeqYr2XIbsNp5WbcE+NSV6vxdgvi9GeqGjrmK2jGlfnl5CBtbRtXcghPshMftfd1rFNSc8OJaMHPFaaFrOX0Swq/yN0qmshe5nb4UegGpA2PxTuRqxOlphsGLdjPcCDExe/ral2/fgFx/YAfbMRXNd3tUJXVTTGar6i7kamA5AgzOY4STO+sa+OwfHK60sfE38zESqRPPoTXqeKahEv1eE/lixj5orPoND7s4zmqU9TC6g+FpReWT+E+XvqM26LgHnDIINRq5R8dzYCuOW+rYg97uiCA+PeGGT1FYvPaQeNa3r3WvPCLvPIqeWb9b6+v5YNZdD3vcVGumhrh4DDfTsz0uh8Eda9RfPKV16D2aqOt9QZvOWbE2x8BLqH7S6EnaQI/Jdj7zr2UHMEOWrt1OlikPx16MihWuDctjoX/yQnSVer+q4hnL2X9exFyDIWz6M8fWHXhtwjG8KpavPMy86Fq73QcTyVFc+/18VO1wQK+MzOFPaUHqmx1gYa+9NsAsa2GH02SxgruwRbHVTkNqhfRndYRZmqY2vmWdFDpivwcW1G5jrpA1+s5UUNWsO/G2nSLP0euKGvfGYhrld276asOU8Eit/2zTkdI8Jd5cxdGNVAHPMw6iklsq1OPXSrtjBAsDzzWzDF50471rpuWTbaPUJJEW5KbaZFFmdm5jjPV7w1vbN+dPd4XXzs6gW/r4pxE9KgbJD6x8HQ6G1IT0HWkz781s17qOOxvWKzem3oA+ofW1LIn7LW760i0/OAfHobKHayUFBLQqIv1o9yQOv5txkvf7aBlk8cx09mioV3ErrMLXK+6mhQPipKQPhzBzaEJN/qPV1scUh4qT13w4137t+qxma8l0fOS3at37JcFXFCJ3n33AeB7+MO998NBDgZJMR/dtD9sPiVddTOV8fTozU1j5md+NJ4+z33wdDnq/H5dUgO/o9A+lSTlrfszXrJG/Xfbuqpq4CIwa/0TW0Vx5P+fKr626J3Usq8KG12lWR2wNps2jXS2wTQgv4a4b0y/XGzzjfNNBq40zIidEk5G/eyQ7R+tE1eGXT5t5GuyG9Fj98COKCl7bo+SP6cS4r/MJUM/Owu/Zs2fX8J5hvMHfaunTNAZKlUpNtum7C4xjGFgjXWvEJIfbOnT4CvS8c7udFI6MtHa92VP/z9i3I3aPWVgGH0/Di6lNV0UUch+4rtDMzGmcJyrQzF+sN3qtyTH5bGmuw6mp7JYP1+WVoxDSgndkgm2suqPz9mlypbTVov6Z+I69zlEfVlp82DYpLbTwxvOxug3qHK0lLAJmOFt5T0aPAhOYaXHezWSLuJGwtgiW8Dhdhh2u8lysbT3u+3tS/6mQUarciZWwN/tngol9Ay0izgBBq+dq4ruGbvR1g77XYeFLlpqflIU/qrl4BRwPFMqoaikq6jiq+TAxxV6pdKDOMuml7pVOZ+3YmY9x1xOUzT8cGox22GSW+bpFccCSbWgmmJpN0zk5Jz9C6DJSnjVuQADOkMPvPZKbKcSx9WuW7du7XeUfWm6FhDC/OdlPbZcKt7BWmXB8dKh1JvZlDmZ9Suc+z/fcNsKh1z9uSyuK+ZvpS5Jg86aim339MbrPEDmBseEGNCg9Sf1XBa9NBDY1d0qfkTmuEWDMEm7czVyLodl2avCc7KeiEJXJeB2vKpE9VpKTa5SfnixF7o8ZuT5SsCe9G6+VcL1aTvngwFdZ9LFm+0nUpXq3bKoveV1tU7mTubgcxnOZsbv6cxL2l7wLvPvs1PeB9uEpHb/niX+F8rLow4bvid/qic9t2JZ4Ez98Pu2418yV/tjOknLzjoya+thw286KvWb5ifQX+7Lk1ftwsB0vbh2nZf9251UFNmLKSOhZUt+NtNc79iYeftqzUV97spHp+X2NWf/EM038BwvOp8jYONNz+VqmXJCpP3kmQN+iwYRhaPCykM+O0CFdXdZL6rGcb3qyoquVBE682Qib7vfb3bs7VkjQnBOnhmo84C2oIN2+76VN69o3CTxJw139eZ02jaWC28zN9l0u9X3TlynVaBTFJ5PDivQS7KrTsvErfnFfgRGeCasar45MHf9sv5WnF//e1iGkBjbJule/YnXGuhm1s97ha3UtY4VUNo9/QUVUmgQExcWUI93Ygtdx+AuWbLlRoHGn9NNNpTPf7uEsnzI37t9P3SfWpGTsfrx6A7/Mu+eDW1sZXe02UMUWiPJYeXeuwhWLNrdoGAxJv93uUjZhp/J5VGszPj56vPDy6bz28UqByq5Hz1qOd+J1dIvSbUVhbEXvjr31O55xB5c9ukvFik6iZLhacAzn2JHDtW7wlugTqVR8uKHwqArbdJv7mMwMC21+TqFde2pWZXli5NA2u/bUtlfVV/++etpIx9IzLnP0ofTBzVluDyrgX1GeH/CDstHhFabY3HwL3r4TE65mZaP09/VgZTWIFO+uu2I+HjSNqknIOy87W6wMpiztq9EdOWnKGdM4HqGWtJoMxXvvdGGXc2sd41wsZpyCt9x06eA/kPYKc0A7dXOjJuzcVMFulpd7eyqpX6QhytP3j1mENyoN7RYpzd/hrs8y3h4w1loIf7z27+DjToeeaDhc96Kfienfb4zNzJta26591PiMl/8lvr/8dupjMzhTRMN4fHCydZvFVEo8BluD1hYF+hpOCz5r520s5n95x27Apbm2xfpTuatH4PV4K/dziad9lN6pzKSLyAXQbhPKG6vr39Jbf2P3pRQ6+GCL8cTxiy/N4bB1QYfeljacZC/YsO39e4GbuGFrN0+cs8y92nEFxywlgwRsZMT7wqnywEbj7+pCIbOpJ+j4rxpz+XAhn6e+GFcrGmjroCtqSeGIKt4lMZvKwkJlxsqFrfyq9fm1PM8xblfjk3pPbh+LSfJmj/87J3usqXikyR5xnU4gyo2vXdZDApvHcOOk9Cs7aZkdys3maRNa2f76TAdvb9zPjFM7RdfVyLT+Q0/Kns+6s/lJZ/UiM6Iw1pOcrN1TI+7RB59mWj8IOTBIn2cSZnSsNcas53Rl/SfRjAu4Ef0MLWy2TmZYoJJK99nnjTWnJxzG94V1aaD4PQWiVtecuWl6aFA6wi4GL6ldzRfmn6HDWtATKpdd2RIexsimrMheRNe4uxNlh7/dmFh4Y+Atn8iwQb3x5YRsNmyI8Y2DyC31IhHdOSN3dghbfuTbjngjmndyBKYfM1bI9SBd04HOMoDqxAMT/aP48twAuZnXH/s+nmuluVrMPiq0F3MuyBpVwEm7g7FPbmAUGbHeI0eMrenRmNOTtNWX37mjUCd7b41FNxUjTzTWZfeBOLc+DVWm8q22NL6v7WT1rHY/lUV5wcu3qJ6vbQm+yhSh5sHqFqrimjJ20L8DeUgztlkVRe98M3HjLtVE/oKs6e2V3qk2g3uZIgatY2GomnN7hnIGVaJ0meF1d6f2Pp/lWJhLD97xT2cG4AoLZW+wc+8/WFs7b6nDOII3WMnqOpdiQWaaDw7IoCIwTHVWPE9tA18646bnZlHOVpdmDaIHEww0i/aFh8U/3x8a5mHSOR1QuNU7OzGT2x6PphMoHEyeuPcltbfwVthphQKW9wKwMwIhBX6+XXojvNr76Db6DdU/qjT3G/jiaS+w2uFZbpoQY9yhoJ3VLxX44hVKT3ve7H22Z2Jghml+XZj+CZPzAIBHFL7n4uxcjjpjcH/2cjgPIWXQiKQhwkysTMu0tBKlnYIW+/frZVOtikrtJuqFWdbTaDEgDADYuKxu7qUlnTE+rp44mRXoFyELQ9i4YnEYR6ydO3Qtyan62noVlShTtE51zRbtykoU3Cg5pTOzTDrLY2ycyW101IMls65GOutjcqp+Fep+yuKsToS+dVABACCzrCQh8pLsXV1xy+ipqEFoL4rBj7O6kVNiUqyk9esR1TJKDtthbMkrKdfS051TMjcklM7CH/XEkVUz5XexYSsAALFSNXOfUDWZhrhhrzUhzEdNS8XKqjI+ZgYY3g8RP2bJyMjIKC9yTUTx/rGBSezukMf3GVVCoibvishPsRmmh/b2Rja+DbzxMtKEVuRiQaPQ3Tf7pHhPBK+X154Uj0LHGyErOCM91oaFuURF4vE6a2XxRmFXpFl2lja92iXXm2gbjbl+Kfq2VWATSKJJxvKeCEY5jxaepFlYann1pU2RAICplbU4cgUtTtY3JMl+IuqdxdO5z3m0iovoy8ffqkaqZzKhvbFYTVK5ZccfVSPVE8mp1ZWoanE97fkWra8QK6syNKp9kNK5q7SnU9S+K4jz053VW+52dscIHlkMDtBMdtP/6FD9ZbeNl7waF4zjSr68F12e+PMPGRKOeo6OymZBDa3U6//5Nf6aPy6IJFOQaAetVX06iQsCcMZa2oiwIO18wZ+8iRaqQ0vdWUXFSU0+Yzc/7Ozf+eOTTOKOPIIDglAnQixN3M2T5rwjTLQJLJAASAgIJ00imRIjphIG0DZAqIqrACXZOGIgYViMBwL8QASkSB9htosDgguiAsvmzZbbCUbITohQA9LJMPKANRCAPQRAlGeCrocwu8UHWc+DJRAyyTBiJGFoiw2C5KIB5CNglPvjSohBrsCfdAhgWX8IE1lQf74ugZDJexEjCaNYUH+saAH5YBfl/jxewBCluBYBpHJbi4ApCIAoxUVeBTsEokoHlk1xQU0hjFpBfT5HikMqxUWMJIxVQZEDJJEkEluUr9aUHiybzoJKI4xNQaXdJMUhlc4iRhIGpKBINgZAWfqK8tX6LEVCclZQaYQhKF6ItEJSHBI5K2IiYdoJSpReDSiKUi23Vk7IWq8tJRJnpoguqQRJJuiN8RMZFKnMFDGVMKkEpe5jBBRnoihfdi1JKiTzBBVImC3ihwjkZQKUZp6IoYRpICj0GTno0pDScotmhSzamhksEyuCSiNMWayDSEtaSlkS4yCmEYZ4oDQuFvDL3BAxjTCZww2hXSFBI4oBEcMIczbQRzKWNYCSaA8xkDAHsx4CPE8aSBTWWW4n1kJ4yqxguSwNMYgwvgIF3YWCiMMySxQRJFKgIAE2sFz2hRhEGPzggoCioCAScRbKWXTs4BdJFcofPrwWWEQhFEofznIhAKIQCnRFhGEQ6LP9zBIIiRDKcprYIJoC1oLl8iREtzuC2Sq0vUtJYJbmSYh5hDNN6L6VCoBfzGcpt/2OICA/E4UKIhxWQhfYuQTyu6K4IaLMN4FfDzyh4ggnjSLQM0wWRm7gueSVl2B2KARhbxMCvzO5XHKbIxgDQsHPyYNJDZWIwYRDNygYLQx+Z6ZI+Z4pbQa/nuBBVRLO16Aqg8nCKPKVcJQGBY+RB1PiK+FUDAq2FQG/M5dbzldeiK8j5MFEIzaoVMLplyhEqoYo+N0RGzGccNIFhWdTAEeS94LUsGzei+87RWFgyeiMlu7H/6oCVbCFAwDrH48+4H8BAAD//4eDbUOjOgAA"); err != nil { + if err := gres.Add("H4sIAAAAAAAC/7SaCTTU6xvH3+xZQpYUIWKQMQyRZCnKMpaxRimNNWKEka0FpSxJKVTaLop07USSrsq+b0lKUYjsIyL0P+VifmNGk+6/c2rq5Hye7/t9n/e3PPNFo6hpOAEDAOCK2y00IPjFDFYDe0dnOw+EjSvW3tHB1IQWrGp9k3wQjaJnIPxB8ggOIgTcxtMD5+rySxIDGLFzhJAESJP+/ZcUztXF+Se1e7v9QYlKXThCS7IcnW6Kbqgtq9VrRiAQLQjtaniZtqQ23KwzVEpHr1zyQpcRGo3WKpesMQP35eSQLbJ1snWy2UhZ+dwmGUMqFAwm6pBgfUnelIoGgO/ff2g10ZBvMwMA2C+rdQMZrS4+Uo5Yx/9UpvGizL2LMqXpu878WiYPkcz/m5foRZEmiyJvPLto+2uRxB30/zDRaFGf2aK+rwHqSsT6lnY444I+W0d3mRUckTUQAMLD03oFp4NvCeTHbzjOzgMnI4Xzxi3aJaXXANeGS8mYaNXUbtGu3rJqYb2FWZe3rwMAsC9biQ1a6WeFRTppcjC16iwHAICFcieRf+ok8r9wEglxEknaSaMl6/WL4Kf+TSeRP51EQp1cStZTzSz9HScdZbZhKXCSWNoaCABu6+i+IIzy7WBfAkHYYf9oRwg4iMN2zs6u0EtVZ4VWdXn53nRT9CiQuQJWzZvm/tnJhvOn4b9fxsvV3dmWqIxELaJC2zDdFM1IT1jmJrsnjLgMRa44Yf4bV5ww/7rig1niSq4R2vfNtkm3xo+l4Q8EFKSOv8k7uJptXnqBvYSFIABg/QpKzjkEKfnToAwjtG+mzWv8fWUdfzBf6NZOK2duAADr73rk7rkCj3hJcf71iPAestA3+dLfrZ8HbVwnf+CVb4b2oj/OxzeJbQQAcK+g3Jw/kFvWfP/kp/c8uxDGQuCP3eeBx+t+2UOcSwv5Hoar6//RYxUU9a9LTh6u2AXdyd0/7qHoObcyf2yvV1HjXb+oPKdVC4egcBNT/q87iWzVObNIVZ0zLXOxqSxzFm1jHEDNElel1DaTvSuwjZ8M6l/bvAkOQ2ZGWlm1oRgKLlFTVZtlimySRRtpVaNqDMqrULmmEnDtxhx0Zu6HzHK57LKsDz/Wml7+88T6zdnrMrZphxTviXRGtYXlsvlyRxgAAAxWpnHO5D/UKP7zjPvNbYb8FBt/TLiKOa3gvMQpUd0ofSKJS3eEFSrxx19XsBnrllIQdtjl7g15FfgwdyO16aNs6ouH3MYLriYAABD87WJOGEovuSEPdjwVgxXr7Owtv+BYmzgutopqvjxnSaW5DABAfNny3CTKu3tScjWbK00rWUB8zXlmhJiRBADAKHynWSz888ySOK1LrhA4tcCuyd2K4oeKNjbeMhgw7EymXXikUUEjHkoBACSXLb+edHmTvf/5WQvW9TlpqB/69vJPqRn154bznMeKfBcvceVqFY0YAIDjsoLXQgT///qRqI4ThlwdMr1IuxlvW5rdY+xH8CRj6CB47Ecrbvmdyu6e5CpT2Ic22/KPwAEAYstW5YJWnWtBSOHFFc/fn366+w+V0j3l26/jaRjmC/a2V4SLAAA2/WZBk73/YcHlnt7dXV1xf/Qe9AOAsPHwWMFFlWcJBOGB83G2k5rH/Vh5g1HSz7uzVmXtlnp4zd86OUbS8hJJ6XRM0QlmVz6Jdtve67EQ35zYHZXAbeHRTLVwsZNot+7bCgBALrsQNqgGRxeMw0ruD+tJYBDOrg6uUkexDgtrcY7x2dZ0hLc0ERkVIrQ9uu6qhoV91RkedYz0uEuZkZx9eZmkvahfa6k7mqM1i7M8cut+E6EzipyZIt8ibSqORd69MeGuP+RT69mOt2zBlw7NnlCdmChM+zbbE5LxUI6bRjsQAP87q+lcmABgezzx/iQAgh4q+VQA9GNeb/hMrfP47o+nS9WNH17QgDUh/e3K/mO6buCQq/j6z2Xu2dKanY6cYhfUPGC7ZsRw/wzunNw0I4ZjvBRE/c2wIEk34faaj6y0kRUag2cxbMMCa19u3RgcH7T1lX+JYGDzX05o02ApBma6eB6atQ3cLmPUDmxa29lYucMunCk6fuLEX4UOf8mVmPJYolBM90L9N7FJMFNpB/Ua2HVMmyRVXT4lOunKL3cpxOFiryQynLF9puRRY2oWTvCiwHXV2ojt4mNPzydVCRzocOS8KnBhzWl1U8x3OvbhUzJjn5nTVHWE32M0EEJBWnYymKNi01aK3Nc0prxy36qeet07G6E+afhJi5qOvVQozk0yTmoXV+rduOTUuOSAIOGLDuxaefmyjOOs9U875Oii9+AbmttyqHkvrdew2vBhA4fzocRxhelznwQKaMPVRCe/336y3egAtY8Z/tzMO41Tz1d7bw4pufPePN+WcVCCfU/iwd1Fs+umY91z/RK4TusmZB59rN4YFH/o+7pvQ/6Y71eHb3+m/pahmi/I2Z92oIkW/nhsiBqoFYkeVv6bfvhyPBOLkpjbpMQXiZx7J1RzTS13Ht/3SQz97oKBTP+4sN/jg8LGfsGFM+u/zcLMFFQ6wzx3ZwRpOj9kPb9D4d6LszuFIgJhykOVlaIMX82vnn7weXI44KBy1pts2h6X1Ojz/gUi7f1VTqPbFQbONnoP4Cds9qJqHEJjdD/5fsSeO9MTbXXV4sZn0G40LmwrHsOkmMZ+9mSM+anCwE1NZTEtFqoPLKRNuXjf+FkPT3haX3yhcPXNFQbl/E/49RL0TFdy5DZgtfOzbwnwqSvV+7sE8Xsz1A8fcxW1Y0r9/OIQNracqqUVJ9gFj9v7qs8oqCXh+bVg5srTQtdy+yWEXxZslExlL3Y7fSn0AlIHxuKdyNWE09MMgxfvZrgRYmL25JUv375BuYHATrZjKppv96hK6qaYzFbXX8jTwHIEGJzHCCd31Tfy2T84XGVj42/mYyRSL55La9T5VEMl+p0m8vmMfdB4zWsednGc1RjrYXQnw5PKqsfxHy99R23QcQ84ZRBqNHqPjufAVhy31LEHfT0RQXx6wo0fo7B47WHx7G9f6l96RN55GD2zfrfWl/PBrLuyet1Ua6eGuXgMN9OzPaqAwR1r1Z8/oXXoO5qo631Bm85ZsS7XwEuoYdLocdpgr8l2PvMv5Qcww5auPU6WKVnjz8fEitaG5bPQP34uukp9QFXxjOXsP89jrsEQNgOZ4+sOvDLhGFkVy1cRZl58rcPuvYnkGK7jfgGqbiSgT0bm8Me0IPXNDrCwV14bYJZ1sMNpsljBXdji2BqnYbUi+rM6wizNUxvfsE4KHbHfAwvqsDFXyB57ayqoataTeNtOkefodUWNe+MxTfI7N32xYUp4qDZwtvlIWb4Sb57i2EaqgGcZB1HJrZXq8Wul3TGCRYHnWliGLrrx3jXT8smxUWqWSAtyU222KDc7tzHG+p3hre2bC6a7w+tmZ9Ct/fzTiF4Vg+QHVr4OB0NqQ/qPtJv3ZXZoXcedDeuTG1dvRJ/Q+lKexP0GN33plh+cg+NQedZaSQEBrcpIP9o9iSNvZ5zk/T5YBlk8NZ09GupV0gar9PWKu2nhgDgp6cMhzByaUFvwcLX1McXhkuQ17891XLs+q9lWOh0f+a1G935p8BWFyN1nHzCeh2flvwsezhIozXR035bVcUi8+mIq56vTmZnCyk/9bjx+lPP6y0jQu/24pEJ8Z5d/KE3KWfNjvmZN/B2yd1fVxkVg1Pi/Zh/Nk/dzrvrSpntSx7I6bGSdZk3E1mDafNrVAtuE8BLuujEDcn3BM843HbTaOSNyQzQZ+XtGc3K1TlQffvGkhafRbliP1Q8/qqjgtT1K/phOjPs6nwD1nGz8nj17do3sGcEb/K2WPk1joFSl1GybvrvQOIaBNdK1VkxypL1Th69Qzzuvx0nhyGhb9+s9Df+Mfzti94iFZejRNLyE2nRVRBH3gesKLcycxvmiAi38JXpj15odk8+W5TmcmsppfX9dXjkKIS14RybYxqonOn+fJldKex3qn6/fsdc5GsLKSg7bJqWFFt14Nl6/QZ2jrZRFwAxnK+/J6FFoAjMtyb+ZbBE3GtYewRIep8uww1Wei7W9131/b+o/lTJKVTuxEvZm/3xlYt9Ai4gzQNDquZr4rqEbe9Wo73VY+JKl5kdl4Q9qLl4BxwOFMqpbi4s7j2pmJabYK5UN1lsmvdC90uWsHTvzIe56grL5+0ND0Q6bzDJftSoOWrINzwRTs2k6J+cWRAhdRsqzxg0KwBly+b1H8zKFOLZ+ybZ9c7e74n3rrZAQ5tcnB6jtUuEW1ipfHR8eapuJfZGLWZ/Stc/zHbeNcOj1D9vSimP+ZvqcJNiyqfhm/0CM7lNEbmBsuAENSk9S/2XhKxOBTS1d0mdkjmsEGLOEG/cw12Fotp0aOif7sThEZTJex6taZI+V5OQa5Scny5D7Y0avjxbuSe/BayVcr5FTPjj4RRZ9rMV+EnWpwS2H6nN+d/tU3mQeLpfxXGbsroHchP2lbwPvPj31fbBjZErH71niX6G8LPqwkXvit3rj8xq3Jd7Ej5xPO+41c2Ugtqu0wrwzo7YhNty2q3KvWUEi/cX+bHn1fhxsx/Nbx2nZv915WYmNmDISeprUfyPt1Y69iYef9G7U155sYnp2X2PWP/FMM//BwvMpMjbO9Fy+likXZBpOngnQt2g0YRgeuizksyN0WFeX9ZJ6LOfr3uzoGiWBE683wqYH/Hb37Y4VEjTnxKmhmg54CypId+x7YdO29nUCT9JI90B+l01TmeA2c7N9l8t8X/dnSjUZRfHJ5LIivQS767Vs/Eqe31dghGfCqidqIlMnPtlv5enDv7N1CKmFTbLu1a9cnbFuRu2sd/haXctYIZXNE59REVUmAUFxMRVIN7bgdRz+gqVbbhRq3Cn7eFPpzLd7OMsnzE3791P3izUrGbsfr9nAL/P22dDWNkZXuw1UsYWiPFbeXatwJaItrRoGw9JvtruUf7VT+TSmtRkfHz1RdPl0fsdElUBV98Onrce78Dq6xem2ojC24rfH3vgdz7iDyxnbpWJFJ1E6UiM4jnPszOVaN3RL9LFUKj7cUHhMhW263X1cZoaFtiC3yK4jNbuqIjFyeJtdR2r7y5qrf189baRj6RmXOZYlfXBzttuDSvgXlOd7/JBsdHilKTavwIK3/8RXV7PyMfr7erDyWkSKd/ddMR8PmibVJOSdF12tVgZTlvY16M7cNOWMaRyPUGtabYbivbe6sMt5dY5xLhYzTsFbbrp08h9Ie4k5oJ26uUkTdm6qcDfLi729VdTP0xAV6fvHLcKblIZ3i5QV7HDXZ5noCBhvK4I/Wvt38HGnQ481HK570c/EDOw3xmbmT63t0D5qfMbL/xLfX3479bEZnCmiYTw+ONn6zWIqpR5DbUFriwN9DacFn3bwNpXwv7hjN+jSUtdq/bHC1SPweryV+7nE0z5Kb1Vm0kXkAmi3CeWP1w9s6Wu4sftSCh18qNX46/GLL8zhsHVBh96UNZ5kL9yw7d07gZu4EWs3T5yzzL26CQXHbCWDBGxkxLuiqYrAJuPv6kIhs6kn6PivGnP5cCGfpT6fUCsebO+kK25N4Ygq2SUxm8rCQmXGyoWt+qL16ZU8zzFuV+OTeo9vH4tJ8maP/zs3Z7y5ZLTZHnGdTiDKja9D1kMCm89w46T0SztpmR3KLeZpX7Vy/PWZDt7euJ8Zp3aKrruJaf373pQ9n3RnC5LO6kVmRGGsJzlZe6ZG3aMPPsm0fhByYIg+3yTM6FhbjFnv6aqGj6IZF3Cj+hla2BydzLBAJZWes8+aak9/dZjYF9atgeL3FIhaXXvmpumhIekIuxi8pHYNX5h/hg5rYW+oXE5Va3gYI5uyInsxXdPuLpQd/nZTYtGNwTd8IiMGDcaXE3LYsCHGNw4itzSIRPTkjt7ZIWz5gW874rVo/slRmH7MeBHXg3RNBzrLAKoTD0z0j+Ir8gLkZl596P9wro3magn7mNBezLkga1QhJ+0Oxn65wTFkxHqPXDG25ofjTo/TVl9+645Cney7NR7dXII80VSf0w/i3Po1VJkqttrS+L6yk9Wz2v1EFuUFr9iier6uNfgqU4SaB6tbqIpryvhB/07kIc3YFlUUvfPNxI27VBP5C7Ont1d5p9oM7WWKGLKOhaFqz+0Zzh1SidJlhtffndr7bJZjYS49dMc/nRmAKyyUvcHOvf9gbe28pQ7jCN5gJWvqXUoEmWneOyCDisEI1VnxfLUNfOmMm56ZRTlbXZo1iB5KMNAs3hceFv9sf2iYh0nXdEDRVu+cxExuezyaTqBoKPnrvc+pfUW3wk4rFLK8E4CdEQgp9PPt1hvl1d5Ht9FvuOFhlbnf4GdPe4HVDk/z0oQY4w4F7ax5ocAXr1B22vNm39M9XwdnmObXhRn4anIeAPCQwvdcnJ3LUWcM7s9eDuchpAwalTREmImVa5mWVaG0U9Bi/369bKpVWaXdTL0wy3oSLQaEAQAbl9XNvbSkM8bH1RMnswL9ImRhCBtXLA7jiLVzh64lOVVfW6+yCmWK1qmp3aJdVYWCGyWndGWWS2d7jE8wuY2NebBk1tdKZ39ITtWvRt1PWZzVidC3DSkAAGSWlSREXpK9qytuGT2VtQjtRTH4CVY3ckpMSpS0fj2iWkbJYTuMLXklFVp6unNK5oaE0tn4o544smqm/C42bgUAIFaqZu4TqibTEDfitSaE+ahpmVh5dcaHzADD+yHixywZGRkZ5UWuiSjePzY4id0d8ug+o0pI1ORdEfkpNsP00L6+yKY3gTdeRJrQilwsbBK6+3qfFO+J4PXy2pPiUeh4I2QlZ6TH2rAwl6hIPF5nrSzeKOyKNMvOsuaXu+T6Em2jMdcvRd+2CmwGSTTJWN4TwSjnsaKTNAtLrai5tCkSADC1shZHrqDFyfqGJNlPRL2zeDr3OY9VcxF9+fhb1Uj1TCa0NxarSSq37vijaqR6Ijm1pgpVI66nPd+iDZVi5dWGRnUPUrp2lfV2idp3B3F+vLN6y92unhjBI4vBAZrJHvofHaq/7LbxklfjgnFcyZf3ossTf/4hQ8JRz7Ex2WyooVV6Az+/xl/zxwWRZAoS7aC1qk8XcUEAzstmGREWpJ0v+JO3Oj3h0FJ3VlFxUpPP2M0POwd2/vgkk7gjj+CAINSJEEsTd/OkOe8IE20CCyQAEgLCSZNIpsSIqYQBtA0QquIqQEk2jhhIGBbjgQDfEwEp0keY7eKA4IKowLJ5s+V2ghGyEyLUgHQyjDxgDQRgDwEQ5Zmg6yHMbvFB1vNgCYRMMowYSRjaYoMguWgA+QgY5f64EmKQK/AnHQJY1h/CRBbUny9LIGTyXsRIwigW1B8rWkA+2EW5P48WMEQprkUAqdzWImAKAiBKcZFXwQ6BqNKBZVNcUFMIo1ZQn8+R4pBKcREjCWNVUOQgSSSJxBblqzWlB8ums6DSCGNTUGk3SXFIpbOIkYQBKSiSjQFQlr6ifLU+S5GQnBVUGmEIihcirYgUh0TOiphImHaCEqVXA4qiVMutlROy1mtLicSZKaJLKkGSCXpj/EgGRSozRUwlTCpBqfsYAcWZKMqXXUeSCsk8QQUSZov4IQJ5mQClmSdiKGEaCAp9Sg66NKS03KJZIYu2ZgbLxIqg0ghTFusg0pKWUpbEOIhphCEeKI2LBfwyN0RMI0zmcENoV0jQiGJAxDDCnA30kYxlDaAk2kMMJMzBrIcAz5MGEoV1ltuJtRCeMitYLktDDCKMr0BBd6Eg4rDMEkUEiRQoSIANLJd9IQYRBj+4IKAoKIhEnIVyFh07+EVShfKHD68FFlEIhdKHszwIgCiEAl0RYRgE+mw/swRCIoSynCY2iKaAtWC5PAnR7Y5gtgpt7zISmKV5EmIe4UwTum9lAuAX81nKbb8jCMjPRKGCCIeV0AV2LYH8rihuiCjzTeDXA0+oOMJJowj0DJOFkRt4LnnlJZgdCkHY24TA70wul9zmCMaAUPAz8mBSQyViMOHQDQpGC4PfmSlSvmdKm8GvJ3hQlYTzNajKYLIwinwlHKVBwePkwZT4SjgVg4JtRcDvzOWW85UX4usoeTDRiA0qlXD6JQqRqiEKfnfERgwnnHRB4TkUwJHkvSA1LJv34vtOURhYMjqjpfvxv6pAFWzhAMD6x6MP+F8AAAD//+csdAyjOgAA"); err != nil { panic("add binary content to resource manager failed: " + err.Error()) } } diff --git a/util/gvalid/gvalid_error.go b/util/gvalid/gvalid_error.go index 64e2ae688..08c3b498e 100644 --- a/util/gvalid/gvalid_error.go +++ b/util/gvalid/gvalid_error.go @@ -148,7 +148,7 @@ func (e *validationError) FirstRule() (rule string, err string) { array := strings.Split(ruleItem, ":") ruleItem = strings.TrimSpace(array[0]) if err, ok = errorItemMap[ruleItem]; ok { - return ruleStr, err + return ruleItem, err } } } diff --git a/util/gvalid/gvalid_z_unit_checkmap_test.go b/util/gvalid/gvalid_z_unit_checkmap_test.go index c74ab2925..dbb693e24 100755 --- a/util/gvalid/gvalid_z_unit_checkmap_test.go +++ b/util/gvalid/gvalid_z_unit_checkmap_test.go @@ -191,6 +191,11 @@ func Test_Sequence(t *testing.T) { t.Assert(err.Map()["length"], "账号长度应当在6到16之间") t.Assert(len(err.Maps()), 2) + t.Assert(len(err.Items()), 2) + t.Assert(err.Items()[0]["passport"]["length"], "账号长度应当在6到16之间") + t.Assert(err.Items()[0]["passport"]["required"], "账号不能为空") + t.Assert(err.Items()[1]["password"]["same"], "两次密码输入不相等") + t.Assert(err.String(), "账号不能为空; 账号长度应当在6到16之间; 两次密码输入不相等") t.Assert(err.Strings(), []string{"账号不能为空", "账号长度应当在6到16之间", "两次密码输入不相等"}) From ab2ef13d993b135978386eacb6dbd3cae13d0822 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 19 May 2021 20:33:19 +0800 Subject: [PATCH 276/492] improve transaction for package gdb --- database/gdb/gdb.go | 1 + database/gdb/gdb_core.go | 40 +++++++++++++++++--- database/gdb/gdb_z_mysql_transaction_test.go | 8 ++-- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 8421900d5..138eb65b8 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -84,6 +84,7 @@ type DB interface { Insert(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Insert. InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.InsertIgnore. + InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) // See Core.InsertAndGetId. Replace(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Replace. Save(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Save. diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 099ada0c3..1b6d36ee8 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -11,11 +11,12 @@ import ( "context" "database/sql" "fmt" - "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/text/gstr" "reflect" "strings" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/container/gvar" @@ -387,15 +388,34 @@ func (c *Core) Begin() (*TX, error) { // ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().TranTimeout) // defer cancelFunc() //} - if tx, err := master.Begin(); err == nil { + var ( + sqlStr = "BEGIN" + mTime1 = gtime.TimestampMilli() + rawTx, err = master.Begin() + mTime2 = gtime.TimestampMilli() + sqlObj = &Sql{ + Sql: sqlStr, + Type: "DB.Begin", + Args: nil, + Format: sqlStr, + Error: err, + Start: mTime1, + End: mTime2, + Group: c.db.GetGroup(), + } + ) + c.db.addSqlToTracing(c.db.GetCtx(), sqlObj) + if c.db.GetDebug() { + c.db.writeSqlToLogger(sqlObj) + } + if err == nil { return &TX{ db: c.db, - tx: tx, + tx: rawTx, master: master, }, nil - } else { - return nil, err } + return nil, err } } @@ -464,6 +484,14 @@ func (c *Core) InsertIgnore(table string, data interface{}, batch ...int) (sql.R return c.Model(table).Data(data).InsertIgnore() } +// InsertAndGetId performs action Insert and returns the last insert id that automatically generated. +func (c *Core) InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) { + if len(batch) > 0 { + return c.Model(table).Data(data).Batch(batch[0]).InsertAndGetId() + } + return c.Model(table).Data(data).InsertAndGetId() +} + // Replace does "REPLACE INTO ..." statement for the table. // If there's already one unique record of the data in the table, it deletes the record // and inserts a new one. diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index 264d59b47..ab1b96af3 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -8,9 +8,10 @@ package gdb_test import ( "fmt" + "testing" + "github.com/gogf/gf/database/gdb" "github.com/gogf/gf/errors/gerror" - "testing" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gtime" @@ -119,7 +120,7 @@ func Test_TX_Insert(t *testing.T) { if err != nil { gtest.Error(err) } - user := tx.Table(table) + user := tx.Model(table) if _, err := user.Data(g.Map{ "id": 1, "passport": "t1", @@ -129,7 +130,6 @@ func Test_TX_Insert(t *testing.T) { }).Insert(); err != nil { gtest.Error(err) } - if _, err := tx.Insert(table, g.Map{ "id": 2, "passport": "t1", @@ -140,7 +140,7 @@ func Test_TX_Insert(t *testing.T) { gtest.Error(err) } - if n, err := tx.Table(table).Count(); err != nil { + if n, err := tx.Model(table).Count(); err != nil { gtest.Error(err) } else { t.Assert(n, 2) From c94dee8191646a796688e34d6d460992779789b6 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 19 May 2021 21:11:51 +0800 Subject: [PATCH 277/492] improve nested transaction feature for package gdb --- database/gdb/gdb.go | 7 +- database/gdb/gdb_core.go | 13 ++- database/gdb/gdb_transaction.go | 35 ++++++- database/gdb/gdb_z_example_test.go | 4 +- .../gdb_z_mysql_association_scanlist_test.go | 6 +- database/gdb/gdb_z_mysql_model_test.go | 16 ++-- database/gdb/gdb_z_mysql_transaction_test.go | 95 ++++++++++++++++--- 7 files changed, 145 insertions(+), 31 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 138eb65b8..8ccb29a03 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -11,9 +11,10 @@ import ( "context" "database/sql" "fmt" + "time" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gcmd" - "time" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/intlog" @@ -139,8 +140,8 @@ type DB interface { // Transaction. // =========================================================================== - Begin() (*TX, error) // See Core.Begin. - Transaction(f func(tx *TX) error) (err error) // See Core.Transaction. + Begin() (*TX, error) // See Core.Begin. + Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) error // See Core.Transaction. // =========================================================================== // Configuration methods. diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 1b6d36ee8..9dbb37ad3 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -54,7 +54,7 @@ func (c *Core) GetCtx() context.Context { if c.ctx != nil { return c.ctx } - return context.Background() + return context.TODO() } // GetCtxTimeout returns the context and cancel function for specified timeout type. @@ -426,12 +426,19 @@ func (c *Core) Begin() (*TX, error) { // // Note that, you should not Commit or Rollback the transaction in function `f` // as it is automatically handled by this function. -func (c *Core) Transaction(f func(tx *TX) error) (err error) { +func (c *Core) Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) (err error) { var tx *TX + // Check transaction object from context. + tx = TXFromCtx(ctx) + if tx != nil { + return tx.Transaction(ctx, f) + } tx, err = c.db.Begin() if err != nil { return err } + // Inject transaction object into context. + ctx = WithTX(ctx, tx) defer func() { if err == nil { if e := recover(); e != nil { @@ -448,7 +455,7 @@ func (c *Core) Transaction(f func(tx *TX) error) (err error) { } } }() - err = f(tx) + err = f(ctx, tx) return } diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_transaction.go index 63031b815..1973a4e57 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_transaction.go @@ -7,11 +7,13 @@ package gdb import ( + "context" "database/sql" "fmt" + "reflect" + "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" - "reflect" "github.com/gogf/gf/text/gregex" ) @@ -26,8 +28,27 @@ type TX struct { const ( transactionPointerPrefix = "transaction" + contextTransactionKey = "TransactionObject" ) +// WithTX injects given transaction object into context and returns a new context. +func WithTX(ctx context.Context, tx *TX) context.Context { + return context.WithValue(ctx, contextTransactionKey, tx) +} + +// TXFromCtx retrieves and returns transaction object from context. +// It is usually used in nested transaction feature, and it returns nil if it is not set previously. +func TXFromCtx(ctx context.Context) *TX { + if ctx == nil { + return nil + } + v := ctx.Value(contextTransactionKey) + if v != nil { + return v.(*TX) + } + return nil +} + // Commit commits current transaction. // Note that it releases previous saved transaction point if it's in a nested transaction procedure, // or else it commits the hole transaction. @@ -128,7 +149,7 @@ func (tx *TX) transactionKey() string { // // Note that, you should not Commit or Rollback the transaction in function `f` // as it is automatically handled by this function. -func (tx *TX) Transaction(f func(tx *TX) error) (err error) { +func (tx *TX) Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) (err error) { err = tx.Begin() if err != nil { return err @@ -149,7 +170,7 @@ func (tx *TX) Transaction(f func(tx *TX) error) (err error) { } } }() - err = f(tx) + err = f(ctx, tx) return } @@ -297,6 +318,14 @@ func (tx *TX) InsertIgnore(table string, data interface{}, batch ...int) (sql.Re return tx.Model(table).Data(data).InsertIgnore() } +// InsertAndGetId performs action Insert and returns the last insert id that automatically generated. +func (tx *TX) InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) { + if len(batch) > 0 { + return tx.Model(table).Data(data).Batch(batch[0]).InsertAndGetId() + } + return tx.Model(table).Data(data).InsertAndGetId() +} + // Replace does "REPLACE INTO ..." statement for the table. // If there's already one unique record of the data in the table, it deletes the record // and inserts a new one. diff --git a/database/gdb/gdb_z_example_test.go b/database/gdb/gdb_z_example_test.go index 4ede0e233..8b692a788 100644 --- a/database/gdb/gdb_z_example_test.go +++ b/database/gdb/gdb_z_example_test.go @@ -7,12 +7,14 @@ package gdb_test import ( + "context" + "github.com/gogf/gf/database/gdb" "github.com/gogf/gf/frame/g" ) func Example_transaction() { - db.Transaction(func(tx *gdb.TX) error { + db.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { // user result, err := tx.Insert("user", g.Map{ "passport": "john", diff --git a/database/gdb/gdb_z_mysql_association_scanlist_test.go b/database/gdb/gdb_z_mysql_association_scanlist_test.go index 4aa3b11b8..54bf0ad9e 100644 --- a/database/gdb/gdb_z_mysql_association_scanlist_test.go +++ b/database/gdb/gdb_z_mysql_association_scanlist_test.go @@ -7,11 +7,13 @@ package gdb_test import ( + "context" "fmt" + "testing" + "github.com/gogf/gf/database/gdb" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gconv" - "testing" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" @@ -84,7 +86,7 @@ CREATE TABLE %s ( // Initialize the data. var err error gtest.C(t, func(t *gtest.T) { - err = db.Transaction(func(tx *gdb.TX) error { + err = db.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { r, err := tx.Model(tableUser).Save(EntityUser{ Name: "john", }) diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 55ca3aa30..78db30e4f 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -7,16 +7,18 @@ package gdb_test import ( + "context" "database/sql" "fmt" + "testing" + "time" + "github.com/gogf/gf/container/garray" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/encoding/gparser" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/util/gutil" - "testing" - "time" "github.com/gogf/gf/database/gdb" @@ -2618,8 +2620,8 @@ func Test_Model_Cache(t *testing.T) { t.AssertNil(err) t.Assert(n, 1) - err = db.Transaction(func(tx *gdb.TX) error { - one, err := tx.Table(table).Cache(time.Second, "test3").FindOne(3) + err = db.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { + one, err := tx.Model(table).Cache(time.Second, "test3").FindOne(3) t.AssertNil(err) t.Assert(one["passport"], "user_300") return nil @@ -2642,13 +2644,13 @@ func Test_Model_Cache(t *testing.T) { t.AssertNil(err) t.Assert(n, 1) - err = db.Transaction(func(tx *gdb.TX) error { + err = db.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error { // Cache feature disabled. - one, err := tx.Table(table).Cache(time.Second, "test4").FindOne(4) + one, err := tx.Model(table).Cache(time.Second, "test4").FindOne(4) t.AssertNil(err) t.Assert(one["passport"], "user_400") // Update the cache. - r, err := tx.Table(table).Data("passport", "user_4000"). + r, err := tx.Model(table).Data("passport", "user_4000"). Cache(-1, "test4").WherePri(4).Update() t.AssertNil(err) n, err := r.RowsAffected() diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index ab1b96af3..1fa33ea23 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -7,6 +7,7 @@ package gdb_test import ( + "context" "fmt" "testing" @@ -718,7 +719,8 @@ func Test_Transaction(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - err := db.Transaction(func(tx *gdb.TX) error { + ctx := context.TODO() + err := db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { if _, err := tx.Replace(table, g.Map{ "id": 1, "passport": "USER_1", @@ -740,7 +742,8 @@ func Test_Transaction(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { - err := db.Transaction(func(tx *gdb.TX) error { + ctx := context.TODO() + err := db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { if _, err := tx.Replace(table, g.Map{ "id": 1, "passport": "USER_1", @@ -767,7 +770,8 @@ func Test_Transaction_Panic(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - err := db.Transaction(func(tx *gdb.TX) error { + ctx := context.TODO() + err := db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { if _, err := tx.Replace(table, g.Map{ "id": 1, "passport": "USER_1", @@ -826,19 +830,22 @@ func Test_Transaction_Nested_Begin_Rollback_Commit(t *testing.T) { }) } -func Test_Transaction_Nested_TX_Transaction(t *testing.T) { +func Test_Transaction_Nested_TX_Transaction_UseTX(t *testing.T) { table := createTable() defer dropTable(table) gtest.C(t, func(t *gtest.T) { - var err error - err = db.Transaction(func(tx *gdb.TX) error { + var ( + err error + ctx = context.TODO() + ) + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { // commit - err = tx.Transaction(func(tx *gdb.TX) error { - err = tx.Transaction(func(tx *gdb.TX) error { - err = tx.Transaction(func(tx *gdb.TX) error { - err = tx.Transaction(func(tx *gdb.TX) error { - err = tx.Transaction(func(tx *gdb.TX) error { + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { _, err = tx.Model(table).Data(g.Map{ "id": 1, "passport": "USER_1", @@ -863,7 +870,71 @@ func Test_Transaction_Nested_TX_Transaction(t *testing.T) { }) t.AssertNil(err) // rollback - err = tx.Transaction(func(tx *gdb.TX) error { + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + _, err = tx.Model(table).Data(g.Map{ + "id": 2, + "passport": "USER_2", + "password": "PASS_2", + "nickname": "NAME_2", + "create_time": gtime.Now().String(), + }).Insert() + t.AssertNil(err) + panic("error") + return err + }) + t.AssertNE(err, nil) + return nil + }) + t.AssertNil(err) + + all, err := db.Model(table).All() + t.AssertNil(err) + t.Assert(len(all), 1) + t.Assert(all[0]["id"], 1) + }) +} + +func Test_Transaction_Nested_TX_Transaction_UseDB(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + var ( + err error + ctx = context.TODO() + ) + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + // commit + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + _, err = db.Model(table).Data(g.Map{ + "id": 1, + "passport": "USER_1", + "password": "PASS_1", + "nickname": "NAME_1", + "create_time": gtime.Now().String(), + }).Insert() + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + // rollback + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { _, err = tx.Model(table).Data(g.Map{ "id": 2, "passport": "USER_2", From 3e2662582c2e5470148e8f294b925be6476fb019 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 19 May 2021 21:17:21 +0800 Subject: [PATCH 278/492] improve nested transaction feature for package gdb --- database/gdb/gdb_transaction.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_transaction.go index 1973a4e57..187e9e9ea 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_transaction.go @@ -150,6 +150,11 @@ func (tx *TX) transactionKey() string { // Note that, you should not Commit or Rollback the transaction in function `f` // as it is automatically handled by this function. func (tx *TX) Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) (err error) { + // Check transaction object from context. + if TXFromCtx(ctx) == nil { + // Inject transaction object into context. + ctx = WithTX(ctx, tx) + } err = tx.Begin() if err != nil { return err From 7d3233c7ada58d63426df96125b2ec6ce1762a56 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 19 May 2021 22:33:50 +0800 Subject: [PATCH 279/492] add context for custom validation rule function;add function WhereNot for gdb.Model --- database/gdb/gdb_model_condition.go | 5 ++ util/gvalid/gvalid_custom_rule.go | 4 +- util/gvalid/gvalid_validator_check.go | 9 +- util/gvalid/gvalid_z_example_test.go | 84 ++++++++++++++++++- util/gvalid/gvalid_z_unit_custom_rule_test.go | 9 +- 5 files changed, 98 insertions(+), 13 deletions(-) diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index b71288647..775858ced 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -93,6 +93,11 @@ func (m *Model) WhereNotLike(column string, like interface{}) *Model { return m.Where(fmt.Sprintf(`%s NOT LIKE ?`, m.db.QuoteWord(column)), like) } +// WhereNot builds `xxx != x` statement. +func (m *Model) WhereNot(column string, value interface{}) *Model { + return m.Where(fmt.Sprintf(`%s != ?`, m.db.QuoteWord(column)), value) +} + // WhereNotIn builds `xxx NOT IN (x)` statement. func (m *Model) WhereNotIn(column string, in interface{}) *Model { return m.Where(fmt.Sprintf(`%s NOT IN (?)`, m.db.QuoteWord(column)), in) diff --git a/util/gvalid/gvalid_custom_rule.go b/util/gvalid/gvalid_custom_rule.go index 83fd4c2bb..85e431f58 100644 --- a/util/gvalid/gvalid_custom_rule.go +++ b/util/gvalid/gvalid_custom_rule.go @@ -6,13 +6,15 @@ package gvalid +import "context" + // RuleFunc is the custom function for data validation. // The parameter `rule` specifies the validation rule string, like "required", "between:1,100", etc. // The parameter `value` specifies the value for this rule to validate. // The parameter `message` specifies the custom error message or configured i18n message for this rule. // The parameter `params` specifies all the parameters that needs. You can ignore parameter `params` if // you do not really need it in your custom validation rule. -type RuleFunc func(rule string, value interface{}, message string, params map[string]interface{}) error +type RuleFunc func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error var ( // customRuleFuncMap stores the custom rule functions. diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index fd1d78f20..9c01f21c2 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -8,6 +8,10 @@ package gvalid import ( "errors" + "strconv" + "strings" + "time" + "github.com/gogf/gf/internal/json" "github.com/gogf/gf/net/gipv4" "github.com/gogf/gf/net/gipv6" @@ -15,9 +19,6 @@ import ( "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gutil" - "strconv" - "strings" - "time" ) type apiTime interface { @@ -103,7 +104,7 @@ func (v *Validator) doCheckValue(key string, value interface{}, rules string, me if len(paramMap) > 0 && paramMap[0] != nil { dataMap = gconv.Map(paramMap[0]) } - if err := f(ruleItems[index], value, message, dataMap); err != nil { + if err := f(v.ctx, ruleItems[index], value, message, dataMap); err != nil { match = false errorMsgArray[ruleKey] = err.Error() } else { diff --git a/util/gvalid/gvalid_z_example_test.go b/util/gvalid/gvalid_z_example_test.go index d52175aca..423957391 100644 --- a/util/gvalid/gvalid_z_example_test.go +++ b/util/gvalid/gvalid_z_example_test.go @@ -10,12 +10,13 @@ import ( "context" "errors" "fmt" + "math" + "reflect" + "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gvalid" - "math" - "reflect" ) func ExampleCheckMap() { @@ -115,7 +116,7 @@ func ExampleCheckStruct3() { func ExampleRegisterRule() { rule := "unique-name" - gvalid.RegisterRule(rule, func(rule string, value interface{}, message string, params map[string]interface{}) error { + gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error { var ( id = gconv.Int(params["Id"]) name = gconv.String(value) @@ -147,7 +148,7 @@ func ExampleRegisterRule() { func ExampleRegisterRule_OverwriteRequired() { rule := "required" - gvalid.RegisterRule(rule, func(rule string, value interface{}, message string, params map[string]interface{}) error { + gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error { reflectValue := reflect.ValueOf(value) if reflectValue.Kind() == reflect.Ptr { reflectValue = reflectValue.Elem() @@ -190,3 +191,78 @@ func ExampleRegisterRule_OverwriteRequired() { // // } + +func ExampleValidator_Rules() { + data := g.Map{ + "password": "123", + } + err := g.Validator().Data(data).Rules("required-with:password").Messages("请输入确认密码").CheckValue("") + fmt.Println(err.String()) + + // Output: + // 请输入确认密码 +} + +func ExampleValidator_CheckValue() { + err := g.Validator().Rules("min:18").Messages("未成年人不允许注册哟").CheckValue(16) + fmt.Println(err.String()) + + // Output: + // 未成年人不允许注册哟 +} + +func ExampleValidator_CheckMap() { + params := map[string]interface{}{ + "passport": "", + "password": "123456", + "password2": "1234567", + } + rules := map[string]string{ + "passport": "required|length:6,16", + "password": "required|length:6,16|same:password2", + "password2": "required|length:6,16", + } + messages := map[string]interface{}{ + "passport": "账号不能为空|账号长度应当在:min到:max之间", + "password": map[string]string{ + "required": "密码不能为空", + "same": "两次密码输入不相等", + }, + } + err := g.Validator().Messages(messages).Rules(rules).CheckMap(params) + if err != nil { + g.Dump(err.Maps()) + } + + // May Output: + //{ + // "passport": { + // "length": "账号长度应当在6到16之间", + // "required": "账号不能为空" + //}, + // "password": { + // "same": "两次密码输入不相等" + //} + //} +} + +func ExampleValidator_CheckStruct() { + type User struct { + Name string `v:"required#请输入用户姓名"` + Type int `v:"required#请选择用户类型"` + } + data := g.Map{ + "name": "john", + } + user := User{} + if err := gconv.Scan(data, &user); err != nil { + panic(err) + } + err := g.Validator().Data(data).CheckStruct(user) + if err != nil { + fmt.Println(err.Items()) + } + + // Output: + // [map[Type:map[required:请选择用户类型]]] +} diff --git a/util/gvalid/gvalid_z_unit_custom_rule_test.go b/util/gvalid/gvalid_z_unit_custom_rule_test.go index 9fd7caf62..c9493c861 100644 --- a/util/gvalid/gvalid_z_unit_custom_rule_test.go +++ b/util/gvalid/gvalid_z_unit_custom_rule_test.go @@ -9,9 +9,10 @@ package gvalid_test import ( "context" "errors" + "testing" + "github.com/gogf/gf/frame/g" "github.com/gogf/gf/util/gconv" - "testing" "github.com/gogf/gf/test/gtest" "github.com/gogf/gf/util/gvalid" @@ -19,7 +20,7 @@ import ( func Test_CustomRule1(t *testing.T) { rule := "custom" - err := gvalid.RegisterRule(rule, func(rule string, value interface{}, message string, params map[string]interface{}) error { + err := gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error { pass := gconv.String(value) if len(pass) != 6 { return errors.New(message) @@ -66,7 +67,7 @@ func Test_CustomRule1(t *testing.T) { func Test_CustomRule2(t *testing.T) { rule := "required-map" - err := gvalid.RegisterRule(rule, func(rule string, value interface{}, message string, params map[string]interface{}) error { + err := gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error { m := gconv.Map(value) if len(m) == 0 { return errors.New(message) @@ -110,7 +111,7 @@ func Test_CustomRule2(t *testing.T) { func Test_CustomRule_AllowEmpty(t *testing.T) { rule := "allow-empty-str" - err := gvalid.RegisterRule(rule, func(rule string, value interface{}, message string, params map[string]interface{}) error { + err := gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error { s := gconv.String(value) if len(s) == 0 || s == "gf" { return nil From 017c6e4e1f5ac755a53b6bad5893ec9b5e60c0ff Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 19 May 2021 23:17:13 +0800 Subject: [PATCH 280/492] add example for i18n feature of package gvalid --- .example/util/gvalid/gvalid_i18n.go | 36 ++++++++++++++++++++++++++++ .example/util/gvalid/i18n/cn.toml | 14 ----------- .example/util/gvalid/i18n/en.toml | 15 ++++++++++++ .example/util/gvalid/i18n/zh-CN.toml | 15 ++++++++++++ 4 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 .example/util/gvalid/gvalid_i18n.go delete mode 100644 .example/util/gvalid/i18n/cn.toml create mode 100644 .example/util/gvalid/i18n/en.toml create mode 100644 .example/util/gvalid/i18n/zh-CN.toml diff --git a/.example/util/gvalid/gvalid_i18n.go b/.example/util/gvalid/gvalid_i18n.go new file mode 100644 index 000000000..dccde019c --- /dev/null +++ b/.example/util/gvalid/gvalid_i18n.go @@ -0,0 +1,36 @@ +package main + +import ( + "context" + + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/i18n/gi18n" + "github.com/gogf/gf/util/gconv" +) + +func main() { + type User struct { + Name string `v:"required#ReuiredUserName"` + Type int `v:"required#ReuiredUserType"` + } + var ( + data = g.Map{ + "name": "john", + } + user = User{} + ctxEn = gi18n.WithLanguage(context.TODO(), "en") + ctxCh = gi18n.WithLanguage(context.TODO(), "zh-CN") + ) + + if err := gconv.Scan(data, &user); err != nil { + panic(err) + } + // 英文 + if err := g.Validator().Ctx(ctxEn).Data(data).CheckStruct(user); err != nil { + g.Dump(err.String()) + } + // 中文 + if err := g.Validator().Ctx(ctxCh).Data(data).CheckStruct(user); err != nil { + g.Dump(err.String()) + } +} diff --git a/.example/util/gvalid/i18n/cn.toml b/.example/util/gvalid/i18n/cn.toml deleted file mode 100644 index 498f60e24..000000000 --- a/.example/util/gvalid/i18n/cn.toml +++ /dev/null @@ -1,14 +0,0 @@ - - -"gf.gvalid.required" = "字段不能为空" - - - - - - - - - - - diff --git a/.example/util/gvalid/i18n/en.toml b/.example/util/gvalid/i18n/en.toml new file mode 100644 index 000000000..bcca1267d --- /dev/null +++ b/.example/util/gvalid/i18n/en.toml @@ -0,0 +1,15 @@ + + +"gf.gvalid.required" = "字段不能为空" + + +"ReuiredUserName" = "Please input user name" +"ReuiredUserType" = "Please select user type" + + + + + + + + diff --git a/.example/util/gvalid/i18n/zh-CN.toml b/.example/util/gvalid/i18n/zh-CN.toml new file mode 100644 index 000000000..750787e56 --- /dev/null +++ b/.example/util/gvalid/i18n/zh-CN.toml @@ -0,0 +1,15 @@ + + +"gf.gvalid.required" = "字段不能为空" + +"ReuiredUserName" = "请输入用户名称" +"ReuiredUserType" = "请选择用户类型" + + + + + + + + + From 4e41d8aff8d25f83d98053a7acc65eeac3275af3 Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 21 May 2021 13:25:53 +0800 Subject: [PATCH 281/492] improve context and nested transaction feature for package gdb --- database/gdb/gdb.go | 44 +--- database/gdb/gdb_core.go | 234 ++++++------------ database/gdb/gdb_core_config.go | 11 +- database/gdb/gdb_core_structure.go | 5 +- database/gdb/gdb_driver_mssql.go | 18 +- database/gdb/gdb_driver_mysql.go | 19 +- database/gdb/gdb_driver_oracle.go | 49 ++-- database/gdb/gdb_driver_pgsql.go | 18 +- database/gdb/gdb_driver_sqlite.go | 18 +- database/gdb/gdb_func.go | 17 +- database/gdb/gdb_model.go | 19 +- database/gdb/gdb_model_cache.go | 2 +- database/gdb/gdb_model_condition.go | 42 ++-- database/gdb/gdb_model_delete.go | 8 +- database/gdb/gdb_model_fields.go | 4 +- database/gdb/gdb_model_insert.go | 9 +- database/gdb/gdb_model_join.go | 5 +- database/gdb/gdb_model_select.go | 19 +- database/gdb/gdb_model_time.go | 8 +- database/gdb/gdb_model_update.go | 6 +- database/gdb/gdb_model_utility.go | 13 +- database/gdb/gdb_model_with.go | 5 +- database/gdb/gdb_statement.go | 4 +- database/gdb/gdb_transaction.go | 242 ++++++++++++++----- database/gdb/gdb_z_driver_test.go | 8 +- database/gdb/gdb_z_mysql_model_test.go | 4 +- database/gdb/gdb_z_mysql_transaction_test.go | 10 +- os/glog/glog_logger_config.go | 25 +- 28 files changed, 472 insertions(+), 394 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 8ccb29a03..8d634359e 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -96,19 +96,6 @@ type DB interface { Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Update. Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete. - // =========================================================================== - // Internal APIs for CURD, which can be overwrote for custom CURD implements. - // =========================================================================== - - DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery. - DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll. - DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec. - DoPrepare(link Link, sql string) (*Stmt, error) // See Core.DoPrepare. - DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoInsert. - DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoBatchInsert. - DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate. - DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete. - // =========================================================================== // Query APIs for convenience purpose. // =========================================================================== @@ -167,40 +154,25 @@ type DB interface { // Utility methods. // =========================================================================== - GetCtx() context.Context // See Core.GetCtx. - GetChars() (charLeft string, charRight string) // See Core.GetChars. - GetMaster(schema ...string) (*sql.DB, error) // See Core.GetMaster. - GetSlave(schema ...string) (*sql.DB, error) // See Core.GetSlave. - QuoteWord(s string) string // See Core.QuoteWord. - QuoteString(s string) string // See Core.QuoteString. - QuotePrefixTableName(table string) string // See Core.QuotePrefixTableName. - Tables(schema ...string) (tables []string, err error) // See Core.Tables. - TableFields(link Link, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields. - HasTable(name string) (bool, error) // See Core.HasTable. - FilteredLinkInfo() string // See Core.FilteredLinkInfo. + GetCtx() context.Context // See Core.GetCtx. + GetCore() *Core // See Core.GetCore + GetChars() (charLeft string, charRight string) // See Core.GetChars. + Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables. + TableFields(ctx context.Context, link Link, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields. + FilteredLinkInfo() string // See Core.FilteredLinkInfo. // HandleSqlBeforeCommit is a hook function, which deals with the sql string before // it's committed to underlying driver. The parameter `link` specifies the current // database connection operation object. You can modify the sql string `sql` and its // arguments `args` as you wish before they're committed to driver. // Also see Core.HandleSqlBeforeCommit. - HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) - - // =========================================================================== - // Internal methods, for internal usage purpose, you do not need consider it. - // =========================================================================== - - mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) // See Core.mappingAndFilterData. - convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{} // See Core.convertFieldValueToLocalValue. - convertRowsToResult(rows *sql.Rows) (Result, error) // See Core.convertRowsToResult. - addSqlToTracing(ctx context.Context, sql *Sql) // See Core.addSqlToTracing. - writeSqlToLogger(v *Sql) // See Core.writeSqlToLogger. + HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) } // Core is the base struct for database management. type Core struct { db DB // DB interface object. - ctx context.Context // Context for chaining operation only. + ctx context.Context // Context for chaining operation only. Do not set a default value in Core initialization. group string // Configuration group name. debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime. cache *gcache.Cache // Cache manager, SQL result cache only. diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 9dbb37ad3..14a14917c 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -15,9 +15,8 @@ import ( "strings" "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/internal/utils" + "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/os/gtime" @@ -25,6 +24,10 @@ import ( "github.com/gogf/gf/util/gconv" ) +func (c *Core) GetCore() *Core { + return c +} + // Ctx is a chaining function, which creates and returns a new DB that is a shallow copy // of current DB object and with given context in it. // Note that this returned DB object can be used only once, so do not assign it to @@ -33,6 +36,11 @@ func (c *Core) Ctx(ctx context.Context) DB { if ctx == nil { return c.db } + // It is already set context in previous chaining operation. + if c.ctx != nil { + return c.db + } + // It makes a shallow copy of current db and changes its context for next chaining operation. var ( err error newCore = &Core{} @@ -41,9 +49,10 @@ func (c *Core) Ctx(ctx context.Context) DB { *newCore = *c newCore.ctx = ctx newCore.db, err = driverMap[configNode.Type].New(newCore, configNode) - // Seldom error, just log it. if err != nil { - c.db.GetLogger().Ctx(ctx).Error(err) + // It is really a serious error here. + // Do not let it continue. + panic(err) } return newCore.db } @@ -60,7 +69,7 @@ func (c *Core) GetCtx() context.Context { // GetCtxTimeout returns the context and cancel function for specified timeout type. func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Context, context.CancelFunc) { if ctx == nil { - ctx = c.db.GetCtx() + ctx = c.GetCtx() } else { ctx = context.WithValue(ctx, "WrappedByGetCtxTimeout", nil) } @@ -102,15 +111,14 @@ func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error if err != nil { return nil, err } - return c.db.DoQuery(link, sql, args...) + return c.DoQuery(c.GetCtx(), link, sql, args...) } // DoQuery commits the sql string and its arguments to underlying driver // through given link object and returns the execution result. -func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { +func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { sql, args = formatSql(sql, args) - sql, args = c.db.HandleSqlBeforeCommit(link, sql, args) - ctx := c.db.GetCtx() + sql, args = c.db.HandleSqlBeforeCommit(ctx, link, sql, args) if c.GetConfig().QueryTimeout > 0 { ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) } @@ -127,9 +135,10 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro End: mTime2, Group: c.db.GetGroup(), } + // Tracing and logging. c.addSqlToTracing(ctx, sqlObj) if c.db.GetDebug() { - c.writeSqlToLogger(sqlObj) + c.writeSqlToLogger(ctx, sqlObj) } if err == nil { return rows, nil @@ -146,15 +155,14 @@ func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err err if err != nil { return nil, err } - return c.db.DoExec(link, sql, args...) + return c.DoExec(c.GetCtx(), link, sql, args...) } // DoExec commits the sql string and its arguments to underlying driver // through given link object and returns the execution result. -func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) { +func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) { sql, args = formatSql(sql, args) - sql, args = c.db.HandleSqlBeforeCommit(link, sql, args) - ctx := c.db.GetCtx() + sql, args = c.db.HandleSqlBeforeCommit(ctx, link, sql, args) if c.GetConfig().ExecTimeout > 0 { var cancelFunc context.CancelFunc ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout) @@ -178,9 +186,10 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re End: mTime2, Group: c.db.GetGroup(), } + // Tracing and logging. c.addSqlToTracing(ctx, sqlObj) if c.db.GetDebug() { - c.writeSqlToLogger(sqlObj) + c.writeSqlToLogger(ctx, sqlObj) } return result, formatError(err, sql, args...) } @@ -207,12 +216,11 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) { return nil, err } } - return c.db.DoPrepare(link, sql) + return c.DoPrepare(c.GetCtx(), link, sql) } -// doPrepare calls prepare function on given link object and returns the statement object. -func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) { - ctx := c.db.GetCtx() +// DoPrepare calls prepare function on given link object and returns the statement object. +func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) { if c.GetConfig().PrepareTimeout > 0 { // DO NOT USE cancel function in prepare statement. ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout) @@ -232,9 +240,10 @@ func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) { Group: c.db.GetGroup(), } ) + // Tracing and logging. c.addSqlToTracing(ctx, sqlObj) if c.db.GetDebug() { - c.writeSqlToLogger(sqlObj) + c.writeSqlToLogger(ctx, sqlObj) } return &Stmt{ Stmt: stmt, @@ -245,23 +254,23 @@ func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) { // GetAll queries and returns data records from database. func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) { - return c.db.DoGetAll(nil, sql, args...) + return c.DoGetAll(c.GetCtx(), nil, sql, args...) } // DoGetAll queries and returns data records from database. -func (c *Core) DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) { +func (c *Core) DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) { if link == nil { link, err = c.db.Slave() if err != nil { return nil, err } } - rows, err := c.db.DoQuery(link, sql, args...) + rows, err := c.DoQuery(ctx, link, sql, args...) if err != nil || rows == nil { return nil, err } defer rows.Close() - return c.db.convertRowsToResult(rows) + return c.convertRowsToResult(rows) } // GetOne queries and returns one record from database. @@ -279,7 +288,7 @@ func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) { // GetArray queries and returns data values as slice from database. // Note that if there are multiple columns in the result, it returns just one column values randomly. func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) { - all, err := c.db.DoGetAll(nil, sql, args...) + all, err := c.DoGetAll(c.GetCtx(), nil, sql, args...) if err != nil { return nil, err } @@ -374,91 +383,6 @@ func (c *Core) PingSlave() error { } } -// Begin starts and returns the transaction object. -// You should call Commit or Rollback functions of the transaction object -// if you no longer use the transaction. Commit or Rollback functions will also -// close the transaction automatically. -func (c *Core) Begin() (*TX, error) { - if master, err := c.db.Master(); err != nil { - return nil, err - } else { - //ctx := c.db.GetCtx() - //if c.GetConfig().TranTimeout > 0 { - // var cancelFunc context.CancelFunc - // ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().TranTimeout) - // defer cancelFunc() - //} - var ( - sqlStr = "BEGIN" - mTime1 = gtime.TimestampMilli() - rawTx, err = master.Begin() - mTime2 = gtime.TimestampMilli() - sqlObj = &Sql{ - Sql: sqlStr, - Type: "DB.Begin", - Args: nil, - Format: sqlStr, - Error: err, - Start: mTime1, - End: mTime2, - Group: c.db.GetGroup(), - } - ) - c.db.addSqlToTracing(c.db.GetCtx(), sqlObj) - if c.db.GetDebug() { - c.db.writeSqlToLogger(sqlObj) - } - if err == nil { - return &TX{ - db: c.db, - tx: rawTx, - master: master, - }, nil - } - return nil, err - } -} - -// Transaction wraps the transaction logic using function `f`. -// It rollbacks the transaction and returns the error from function `f` if -// it returns non-nil error. It commits the transaction and returns nil if -// function `f` returns nil. -// -// Note that, you should not Commit or Rollback the transaction in function `f` -// as it is automatically handled by this function. -func (c *Core) Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) (err error) { - var tx *TX - // Check transaction object from context. - tx = TXFromCtx(ctx) - if tx != nil { - return tx.Transaction(ctx, f) - } - tx, err = c.db.Begin() - if err != nil { - return err - } - // Inject transaction object into context. - ctx = WithTX(ctx, tx) - defer func() { - if err == nil { - if e := recover(); e != nil { - err = fmt.Errorf("%v", e) - } - } - if err != nil { - if e := tx.Rollback(); e != nil { - err = e - } - } else { - if e := tx.Commit(); e != nil { - err = e - } - } - }() - err = f(ctx, tx) - return -} - // Insert does "INSERT INTO ..." statement for the table. // If there's already one unique record of the data in the table, it returns error. // @@ -536,7 +460,7 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e return c.Model(table).Data(data).Save() } -// doInsert inserts or updates data for given table. +// DoInsert inserts or updates data for given table. // This function is usually used for custom interface definition, you do not need call it manually. // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // Eg: @@ -548,8 +472,8 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e // 1: replace: if there's unique/primary key in the data, it deletes it from table and inserts a new one; // 2: save: if there's unique/primary key in the data, it updates it or else inserts a new one; // 3: ignore: if there's unique/primary key in the data, it ignores the inserting; -func (c *Core) DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) { - table = c.db.QuotePrefixTableName(table) +func (c *Core) DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) { + table = c.QuotePrefixTableName(table) var ( fields []string values []string @@ -564,10 +488,10 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b } switch reflectKind { case reflect.Slice, reflect.Array: - return c.db.DoBatchInsert(link, table, data, option, batch...) + return c.DoBatchInsert(ctx, link, table, data, option, batch...) case reflect.Struct: if _, ok := data.(apiInterfaces); ok { - return c.db.DoBatchInsert(link, table, data, option, batch...) + return c.DoBatchInsert(ctx, link, table, data, option, batch...) } else { dataMap = ConvertDataForTableRecord(data) } @@ -616,15 +540,11 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b return nil, err } } - return c.db.DoExec( - link, - fmt.Sprintf( - "%s INTO %s(%s) VALUES(%s) %s", - operation, table, strings.Join(fields, ","), - strings.Join(values, ","), updateStr, - ), - params..., - ) + return c.DoExec(ctx, link, fmt.Sprintf( + "%s INTO %s(%s) VALUES(%s) %s", + operation, table, strings.Join(fields, ","), + strings.Join(values, ","), updateStr, + ), params...) } // BatchInsert batch inserts data. @@ -665,8 +585,8 @@ func (c *Core) BatchSave(table string, list interface{}, batch ...int) (sql.Resu // DoBatchInsert batch inserts/replaces/saves data. // This function is usually used for custom interface definition, you do not need call it manually. -func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) { - table = c.db.QuotePrefixTableName(table) +func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) { + table = c.QuotePrefixTableName(table) var ( keys []string // Field names. values []string // Value holder string array, like: (?,?,?) @@ -777,16 +697,12 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i } valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")") if len(valueHolder) == batchNum || (i == listMapLen-1 && len(valueHolder) > 0) { - r, err := c.db.DoExec( - link, - fmt.Sprintf( - "%s INTO %s(%s) VALUES%s %s", - operation, table, keysStr, - gstr.Join(valueHolder, ","), - updateStr, - ), - params..., - ) + r, err := c.DoExec(ctx, link, fmt.Sprintf( + "%s INTO %s(%s) VALUES%s %s", + operation, table, keysStr, + gstr.Join(valueHolder, ","), + updateStr, + ), params...) if err != nil { return r, err } @@ -821,10 +737,10 @@ func (c *Core) Update(table string, data interface{}, condition interface{}, arg return c.Model(table).Data(data).Where(condition, args...).Update() } -// doUpdate does "UPDATE ... " statement for the table. +// DoUpdate does "UPDATE ... " statement for the table. // This function is usually used for custom interface definition, you do not need call it manually. -func (c *Core) DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) { - table = c.db.QuotePrefixTableName(table) +func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) { + table = c.QuotePrefixTableName(table) var ( rv = reflect.ValueOf(data) kind = rv.Kind() @@ -849,7 +765,7 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str if value.Value != 0 { column := k if value.Field != "" { - column = c.db.QuoteWord(value.Field) + column = c.QuoteWord(value.Field) } fields = append(fields, fmt.Sprintf("%s=%s+?", column, column)) params = append(params, value.Value) @@ -858,16 +774,16 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str if value.Value != 0 { column := k if value.Field != "" { - column = c.db.QuoteWord(value.Field) + column = c.QuoteWord(value.Field) } fields = append(fields, fmt.Sprintf("%s=%s+?", column, column)) params = append(params, value.Value) } default: if s, ok := v.(Raw); ok { - fields = append(fields, c.db.QuoteWord(k)+"="+gconv.String(s)) + fields = append(fields, c.QuoteWord(k)+"="+gconv.String(s)) } else { - fields = append(fields, c.db.QuoteWord(k)+"=?") + fields = append(fields, c.QuoteWord(k)+"=?") params = append(params, v) } } @@ -888,11 +804,7 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str return nil, err } } - return c.db.DoExec( - link, - fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition), - args..., - ) + return c.DoExec(ctx, link, fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition), args...) } // Delete does "DELETE FROM ... " statement for the table. @@ -912,14 +824,14 @@ func (c *Core) Delete(table string, condition interface{}, args ...interface{}) // DoDelete does "DELETE FROM ... " statement for the table. // This function is usually used for custom interface definition, you do not need call it manually. -func (c *Core) DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) { +func (c *Core) DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) { if link == nil { if link, err = c.db.Master(); err != nil { return nil, err } } - table = c.db.QuotePrefixTableName(table) - return c.db.DoExec(link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...) + table = c.QuotePrefixTableName(table) + return c.DoExec(ctx, link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...) } // convertRowsToResult converts underlying data record type sql.Rows to Result type. @@ -955,7 +867,7 @@ func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) { if value == nil { row[columnNames[i]] = gvar.New(nil) } else { - row[columnNames[i]] = gvar.New(c.db.convertFieldValueToLocalValue(value, columnTypes[i])) + row[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i])) } } records = append(records, row) @@ -977,19 +889,23 @@ func (c *Core) MarshalJSON() ([]byte, error) { // writeSqlToLogger outputs the sql object to logger. // It is enabled only if configuration "debug" is true. -func (c *Core) writeSqlToLogger(v *Sql) { - s := fmt.Sprintf("[%3d ms] [%s] %s", v.End-v.Start, v.Group, v.Format) - if v.Error != nil { - s += "\nError: " + v.Error.Error() - c.logger.Ctx(c.db.GetCtx()).Error(s) +func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) { + var transactionIdStr string + if v := ctx.Value(transactionIdForLoggerCtx); v != nil { + transactionIdStr = fmt.Sprintf(`[%d] `, v.(uint64)) + } + s := fmt.Sprintf("[%3d ms] [%s] %s%s", sql.End-sql.Start, sql.Group, transactionIdStr, sql.Format) + if sql.Error != nil { + s += "\nError: " + sql.Error.Error() + c.logger.Ctx(ctx).Error(s) } else { - c.logger.Ctx(c.db.GetCtx()).Debug(s) + c.logger.Ctx(ctx).Debug(s) } } // HasTable determine whether the table name exists in the database. func (c *Core) HasTable(name string) (bool, error) { - tableList, err := c.db.Tables() + tableList, err := c.db.Tables(c.GetCtx()) if err != nil { return false, err } diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index 1f70a0164..8ce85dc8e 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -8,15 +8,12 @@ package gdb import ( "fmt" - "github.com/gogf/gf/os/gcache" "sync" "time" - "github.com/gogf/gf/os/glog" -) + "github.com/gogf/gf/os/gcache" -const ( - DefaultGroupName = "default" // Default group name. + "github.com/gogf/gf/os/glog" ) // Config is the configuration management object. @@ -53,6 +50,10 @@ type ConfigNode struct { TimeMaintainDisabled bool `json:"timeMaintainDisabled"` // (Optional) Disable the automatic time maintaining feature. } +const ( + DefaultGroupName = "default" // Default group name. +) + // configs is internal used configuration object. var configs struct { sync.RWMutex diff --git a/database/gdb/gdb_core_structure.go b/database/gdb/gdb_core_structure.go index 8e28f239b..02b9d11ac 100644 --- a/database/gdb/gdb_core_structure.go +++ b/database/gdb/gdb_core_structure.go @@ -7,10 +7,11 @@ package gdb import ( - "github.com/gogf/gf/util/gutil" "strings" "time" + "github.com/gogf/gf/util/gutil" + "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/os/gtime" @@ -149,7 +150,7 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s // mappingAndFilterData automatically mappings the map key to table field and removes // all key-value pairs that are not the field of given table. func (c *Core) mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) { - if fieldsMap, err := c.db.TableFields(nil, table, schema); err == nil { + if fieldsMap, err := c.db.TableFields(c.GetCtx(), nil, table, schema); err == nil { fieldsKeyMap := make(map[string]interface{}, len(fieldsMap)) for k, _ := range fieldsMap { fieldsKeyMap[k] = nil diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index f288187c9..fde84e762 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -12,12 +12,14 @@ package gdb import ( + "context" "database/sql" "fmt" - "github.com/gogf/gf/errors/gerror" "strconv" "strings" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/text/gstr" @@ -77,7 +79,7 @@ func (d *DriverMssql) GetChars() (charLeft string, charRight string) { } // HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverMssql) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) { +func (d *DriverMssql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { var index int // Convert place holder char '?' to string "@px". str, _ := gregex.ReplaceStringFunc("\\?", sql, func(s string) string { @@ -183,14 +185,14 @@ func (d *DriverMssql) parseSql(sql string) string { // Tables retrieves and returns the tables of current schema. // It's mainly used in cli tool chain for automatically generating the models. -func (d *DriverMssql) Tables(schema ...string) (tables []string, err error) { +func (d *DriverMssql) Tables(ctx context.Context, schema ...string) (tables []string, err error) { var result Result - link, err := d.db.GetSlave(schema...) + link, err := d.GetSlave(schema...) if err != nil { return nil, err } - result, err = d.db.DoGetAll(link, `SELECT NAME FROM SYSOBJECTS WHERE XTYPE='U' AND STATUS >= 0 ORDER BY NAME`) + result, err = d.DoGetAll(ctx, link, `SELECT NAME FROM SYSOBJECTS WHERE XTYPE='U' AND STATUS >= 0 ORDER BY NAME`) if err != nil { return } @@ -205,7 +207,7 @@ func (d *DriverMssql) Tables(schema ...string) (tables []string, err error) { // TableFields retrieves and returns the fields information of specified table of current schema. // // Also see DriverMysql.TableFields. -func (d *DriverMssql) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) { +func (d *DriverMssql) TableFields(ctx context.Context, link Link, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -224,7 +226,7 @@ func (d *DriverMssql) TableFields(link Link, table string, schema ...string) (fi result Result ) if link == nil { - link, err = d.db.GetSlave(checkSchema) + link, err = d.GetSlave(checkSchema) if err != nil { return nil } @@ -260,7 +262,7 @@ ORDER BY a.id,a.colorder`, strings.ToUpper(table), ) structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - result, err = d.db.DoGetAll(link, structureSql) + result, err = d.DoGetAll(ctx, link, structureSql) if err != nil { return nil } diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index 9f6f6bf45..ee87ecd94 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -7,8 +7,10 @@ package gdb import ( + "context" "database/sql" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/text/gregex" @@ -75,19 +77,19 @@ func (d *DriverMysql) GetChars() (charLeft string, charRight string) { } // HandleSqlBeforeCommit handles the sql before posts it to database. -func (d *DriverMysql) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) { +func (d *DriverMysql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { return sql, args } // Tables retrieves and returns the tables of current schema. // It's mainly used in cli tool chain for automatically generating the models. -func (d *DriverMysql) Tables(schema ...string) (tables []string, err error) { +func (d *DriverMysql) Tables(ctx context.Context, schema ...string) (tables []string, err error) { var result Result - link, err := d.db.GetSlave(schema...) + link, err := d.GetSlave(schema...) if err != nil { return nil, err } - result, err = d.db.DoGetAll(link, `SHOW TABLES`) + result, err = d.DoGetAll(ctx, link, `SHOW TABLES`) if err != nil { return } @@ -111,7 +113,7 @@ func (d *DriverMysql) Tables(schema ...string) (tables []string, err error) { // // It's using cache feature to enhance the performance, which is never expired util the // process restarts. -func (d *DriverMysql) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) { +func (d *DriverMysql) TableFields(ctx context.Context, link Link, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -130,14 +132,13 @@ func (d *DriverMysql) TableFields(link Link, table string, schema ...string) (fi result Result ) if link == nil { - link, err = d.db.GetSlave(checkSchema) + link, err = d.GetSlave(checkSchema) if err != nil { return nil } } - result, err = d.db.DoGetAll( - link, - fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.db.QuoteWord(table)), + result, err = d.DoGetAll(ctx, link, + fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.QuoteWord(table)), ) if err != nil { return nil diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index f8011b91a..b067de9b4 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -12,17 +12,19 @@ package gdb import ( + "context" "database/sql" "fmt" + "reflect" + "strconv" + "strings" + "time" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" - "reflect" - "strconv" - "strings" - "time" ) // DriverOracle is the driver for oracle database. @@ -83,7 +85,7 @@ func (d *DriverOracle) GetChars() (charLeft string, charRight string) { } // HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverOracle) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}) { +func (d *DriverOracle) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}) { var index int // Convert place holder char '?' to string ":vx". newSql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string { @@ -164,9 +166,9 @@ func (d *DriverOracle) parseSql(sql string) string { // Tables retrieves and returns the tables of current schema. // It's mainly used in cli tool chain for automatically generating the models. // Note that it ignores the parameter `schema` in oracle database, as it is not necessary. -func (d *DriverOracle) Tables(schema ...string) (tables []string, err error) { +func (d *DriverOracle) Tables(ctx context.Context, schema ...string) (tables []string, err error) { var result Result - result, err = d.db.DoGetAll(nil, "SELECT TABLE_NAME FROM USER_TABLES ORDER BY TABLE_NAME") + result, err = d.DoGetAll(ctx, nil, "SELECT TABLE_NAME FROM USER_TABLES ORDER BY TABLE_NAME") if err != nil { return } @@ -181,7 +183,7 @@ func (d *DriverOracle) Tables(schema ...string) (tables []string, err error) { // TableFields retrieves and returns the fields information of specified table of current schema. // // Also see DriverMysql.TableFields. -func (d *DriverOracle) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) { +func (d *DriverOracle) TableFields(ctx context.Context, link Link, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -211,12 +213,12 @@ FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, ) structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) if link == nil { - link, err = d.db.GetSlave(checkSchema) + link, err = d.GetSlave(checkSchema) if err != nil { return nil } } - result, err = d.db.DoGetAll(link, structureSql) + result, err = d.DoGetAll(ctx, link, structureSql) if err != nil { return nil } @@ -264,7 +266,7 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[ return } -func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) { +func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) { var ( fields []string values []string @@ -279,7 +281,7 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio } switch kind { case reflect.Slice, reflect.Array: - return d.db.DoBatchInsert(link, table, data, option, batch...) + return d.DoBatchInsert(ctx, link, table, data, option, batch...) case reflect.Map: fallthrough case reflect.Struct: @@ -353,20 +355,17 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio table, tableAlias1, strings.Join(subSqlStr, ","), tableAlias2, strings.Join(onStr, "AND"), strings.Join(updateStr, ","), strings.Join(fields, ","), strings.Join(values, ","), ) - return d.db.DoExec(link, tmp, params...) + return d.DoExec(ctx, link, tmp, params...) case insertOptionIgnore: - return d.db.DoExec(link, - fmt.Sprintf( - "INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(%s(%s)) */ INTO %s(%s) VALUES(%s)", - table, strings.Join(indexes, ","), table, strings.Join(fields, ","), strings.Join(values, ","), - ), - params...) + return d.DoExec(ctx, link, fmt.Sprintf( + "INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(%s(%s)) */ INTO %s(%s) VALUES(%s)", + table, strings.Join(indexes, ","), table, strings.Join(fields, ","), strings.Join(values, ","), + ), params...) } } - return d.db.DoExec( - link, + return d.DoExec(ctx, link, fmt.Sprintf( "INSERT INTO %s(%s) VALUES(%s)", table, strings.Join(fields, ","), strings.Join(values, ","), @@ -374,7 +373,7 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio params...) } -func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) { +func (d *DriverOracle) DoBatchInsert(ctx context.Context, link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) { var ( keys []string values []string @@ -435,7 +434,7 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{}, ) if option != insertOptionDefault { for _, v := range listMap { - r, err := d.db.DoInsert(link, table, v, option, 1) + r, err := d.DoInsert(ctx, link, table, v, option, 1) if err != nil { return r, err } @@ -463,7 +462,7 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{}, values = append(values, valueHolderStr) intoStr = append(intoStr, fmt.Sprintf(" INTO %s(%s) VALUES(%s) ", table, keyStr, valueHolderStr)) if len(intoStr) == batchNum { - r, err := d.db.DoExec(link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...) + r, err := d.DoExec(ctx, link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...) if err != nil { return r, err } @@ -479,7 +478,7 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{}, } // The leftover data. if len(intoStr) > 0 { - r, err := d.db.DoExec(link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...) + r, err := d.DoExec(ctx, link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...) if err != nil { return r, err } diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 95cf65087..2d0bd76f6 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -12,12 +12,14 @@ package gdb import ( + "context" "database/sql" "fmt" + "strings" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/text/gstr" - "strings" "github.com/gogf/gf/text/gregex" ) @@ -75,7 +77,7 @@ func (d *DriverPgsql) GetChars() (charLeft string, charRight string) { } // HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverPgsql) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) { +func (d *DriverPgsql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { var index int // Convert place holder char '?' to string "$x". sql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string { @@ -88,9 +90,9 @@ func (d *DriverPgsql) HandleSqlBeforeCommit(link Link, sql string, args []interf // Tables retrieves and returns the tables of current schema. // It's mainly used in cli tool chain for automatically generating the models. -func (d *DriverPgsql) Tables(schema ...string) (tables []string, err error) { +func (d *DriverPgsql) Tables(ctx context.Context, schema ...string) (tables []string, err error) { var result Result - link, err := d.db.GetSlave(schema...) + link, err := d.GetSlave(schema...) if err != nil { return nil, err } @@ -98,7 +100,7 @@ func (d *DriverPgsql) Tables(schema ...string) (tables []string, err error) { if len(schema) > 0 && schema[0] != "" { query = fmt.Sprintf("SELECT TABLENAME FROM PG_TABLES WHERE SCHEMANAME = '%s' ORDER BY TABLENAME", schema[0]) } - result, err = d.db.DoGetAll(link, query) + result, err = d.DoGetAll(ctx, link, query) if err != nil { return } @@ -113,7 +115,7 @@ func (d *DriverPgsql) Tables(schema ...string) (tables []string, err error) { // TableFields retrieves and returns the fields information of specified table of current schema. // // Also see DriverMysql.TableFields. -func (d *DriverPgsql) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) { +func (d *DriverPgsql) TableFields(ctx context.Context, link Link, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -141,12 +143,12 @@ ORDER BY a.attnum`, ) structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) if link == nil { - link, err = d.db.GetSlave(checkSchema) + link, err = d.GetSlave(checkSchema) if err != nil { return nil } } - result, err = d.db.DoGetAll(link, structureSql) + result, err = d.DoGetAll(ctx, link, structureSql) if err != nil { return nil } diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index ba000fbbb..c78a3eb27 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -11,13 +11,15 @@ package gdb import ( + "context" "database/sql" "fmt" + "strings" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/text/gstr" - "strings" ) // DriverSqlite is the driver for sqlite database. @@ -67,20 +69,20 @@ func (d *DriverSqlite) GetChars() (charLeft string, charRight string) { // HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. // TODO 需要增加对Save方法的支持,可使用正则来实现替换, // TODO 将ON DUPLICATE KEY UPDATE触发器修改为两条SQL语句(INSERT OR IGNORE & UPDATE) -func (d *DriverSqlite) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) { +func (d *DriverSqlite) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { return sql, args } // Tables retrieves and returns the tables of current schema. // It's mainly used in cli tool chain for automatically generating the models. -func (d *DriverSqlite) Tables(schema ...string) (tables []string, err error) { +func (d *DriverSqlite) Tables(ctx context.Context, schema ...string) (tables []string, err error) { var result Result - link, err := d.db.GetSlave(schema...) + link, err := d.GetSlave(schema...) if err != nil { return nil, err } - result, err = d.db.DoGetAll(link, `SELECT NAME FROM SQLITE_MASTER WHERE TYPE='table' ORDER BY NAME`) + result, err = d.DoGetAll(ctx, link, `SELECT NAME FROM SQLITE_MASTER WHERE TYPE='table' ORDER BY NAME`) if err != nil { return } @@ -95,7 +97,7 @@ func (d *DriverSqlite) Tables(schema ...string) (tables []string, err error) { // TableFields retrieves and returns the fields information of specified table of current schema. // // Also see DriverMysql.TableFields. -func (d *DriverSqlite) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) { +func (d *DriverSqlite) TableFields(ctx context.Context, link Link, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -114,12 +116,12 @@ func (d *DriverSqlite) TableFields(link Link, table string, schema ...string) (f result Result ) if link == nil { - link, err = d.db.GetSlave(checkSchema) + link, err = d.GetSlave(checkSchema) if err != nil { return nil } } - result, err = d.db.DoGetAll(link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table)) + result, err = d.DoGetAll(ctx, link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table)) if err != nil { return nil } diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index ec2b7d7dc..13db19d09 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -9,6 +9,11 @@ package gdb import ( "bytes" "fmt" + "reflect" + "regexp" + "strings" + "time" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/internal/json" @@ -16,10 +21,6 @@ import ( "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gmeta" "github.com/gogf/gf/util/gutil" - "reflect" - "regexp" - "strings" - "time" "github.com/gogf/gf/internal/structs" @@ -504,7 +505,7 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) ( // Eg: Where/And/Or("uid>=", 1) newWhere += "?" } else if gregex.IsMatchString(regularFieldNameRegPattern, newWhere) { - newWhere = db.QuoteString(newWhere) + newWhere = db.GetCore().QuoteString(newWhere) if len(newArgs) > 0 { if utils.IsArray(newArgs[0]) { // Eg: @@ -543,9 +544,9 @@ func formatWhereInterfaces(db DB, where []interface{}, buffer *bytes.Buffer, new for i := 0; i < len(where); i += 2 { str = gconv.String(where[i]) if buffer.Len() > 0 { - buffer.WriteString(" AND " + db.QuoteWord(str) + "=?") + buffer.WriteString(" AND " + db.GetCore().QuoteWord(str) + "=?") } else { - buffer.WriteString(db.QuoteWord(str) + "=?") + buffer.WriteString(db.GetCore().QuoteWord(str) + "=?") } if s, ok := where[i+1].(Raw); ok { buffer.WriteString(gconv.String(s)) @@ -558,7 +559,7 @@ func formatWhereInterfaces(db DB, where []interface{}, buffer *bytes.Buffer, new // formatWhereKeyValue handles each key-value pair of the parameter map. func formatWhereKeyValue(db DB, buffer *bytes.Buffer, newArgs []interface{}, key string, value interface{}) []interface{} { - quotedKey := db.QuoteWord(key) + quotedKey := db.GetCore().QuoteWord(key) if buffer.Len() > 0 { buffer.WriteString(" AND ") } diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index ba834a457..45084a106 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -9,9 +9,10 @@ package gdb import ( "context" "fmt" - "github.com/gogf/gf/text/gregex" "time" + "github.com/gogf/gf/text/gregex" + "github.com/gogf/gf/text/gstr" ) @@ -98,10 +99,10 @@ func (c *Core) Model(tableNameOrStruct ...interface{}) *Model { if len(tableNames) > 1 { tableStr = fmt.Sprintf( - `%s AS %s`, c.db.QuotePrefixTableName(tableNames[0]), c.db.QuoteWord(tableNames[1]), + `%s AS %s`, c.QuotePrefixTableName(tableNames[0]), c.QuoteWord(tableNames[1]), ) } else if len(tableNames) == 1 { - tableStr = c.db.QuotePrefixTableName(tableNames[0]) + tableStr = c.QuotePrefixTableName(tableNames[0]) } return &Model{ db: c.db, @@ -148,9 +149,21 @@ func (m *Model) Ctx(ctx context.Context) *Model { } model := m.getModel() model.db = model.db.Ctx(ctx) + if m.tx != nil { + model.tx = model.tx.Ctx(ctx) + } return model } +// GetCtx returns the context for current Model. +// It returns `context.Background()` is there's no context previously set. +func (m *Model) GetCtx() context.Context { + if m.tx != nil && m.tx.ctx != nil { + return m.tx.ctx + } + return m.db.GetCtx() +} + // As sets an alias name for current table. func (m *Model) As(as string) *Model { if m.tables != "" { diff --git a/database/gdb/gdb_model_cache.go b/database/gdb/gdb_model_cache.go index 0529bf5f6..b9ba72873 100644 --- a/database/gdb/gdb_model_cache.go +++ b/database/gdb/gdb_model_cache.go @@ -38,6 +38,6 @@ func (m *Model) Cache(duration time.Duration, name ...string) *Model { // cache feature is enabled. func (m *Model) checkAndRemoveCache() { if m.cacheEnabled && m.cacheDuration < 0 && len(m.cacheName) > 0 { - m.db.GetCache().Ctx(m.db.GetCtx()).Remove(m.cacheName) + m.db.GetCache().Ctx(m.GetCtx()).Remove(m.cacheName) } } diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 775858ced..641fee5d7 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -61,53 +61,53 @@ func (m *Model) WherePri(where interface{}, args ...interface{}) *Model { // WhereBetween builds `xxx BETWEEN x AND y` statement. func (m *Model) WhereBetween(column string, min, max interface{}) *Model { - return m.Where(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max) + return m.Where(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column)), min, max) } // WhereLike builds `xxx LIKE x` statement. func (m *Model) WhereLike(column string, like interface{}) *Model { - return m.Where(fmt.Sprintf(`%s LIKE ?`, m.db.QuoteWord(column)), like) + return m.Where(fmt.Sprintf(`%s LIKE ?`, m.db.GetCore().QuoteWord(column)), like) } // WhereIn builds `xxx IN (x)` statement. func (m *Model) WhereIn(column string, in interface{}) *Model { - return m.Where(fmt.Sprintf(`%s IN (?)`, m.db.QuoteWord(column)), in) + return m.Where(fmt.Sprintf(`%s IN (?)`, m.db.GetCore().QuoteWord(column)), in) } // WhereNull builds `xxx IS NULL` statement. func (m *Model) WhereNull(columns ...string) *Model { model := m for _, column := range columns { - model = m.Where(fmt.Sprintf(`%s IS NULL`, m.db.QuoteWord(column))) + model = m.Where(fmt.Sprintf(`%s IS NULL`, m.db.GetCore().QuoteWord(column))) } return model } // WhereNotBetween builds `xxx NOT BETWEEN x AND y` statement. func (m *Model) WhereNotBetween(column string, min, max interface{}) *Model { - return m.Where(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max) + return m.Where(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column)), min, max) } // WhereNotLike builds `xxx NOT LIKE x` statement. func (m *Model) WhereNotLike(column string, like interface{}) *Model { - return m.Where(fmt.Sprintf(`%s NOT LIKE ?`, m.db.QuoteWord(column)), like) + return m.Where(fmt.Sprintf(`%s NOT LIKE ?`, m.db.GetCore().QuoteWord(column)), like) } // WhereNot builds `xxx != x` statement. func (m *Model) WhereNot(column string, value interface{}) *Model { - return m.Where(fmt.Sprintf(`%s != ?`, m.db.QuoteWord(column)), value) + return m.Where(fmt.Sprintf(`%s != ?`, m.db.GetCore().QuoteWord(column)), value) } // WhereNotIn builds `xxx NOT IN (x)` statement. func (m *Model) WhereNotIn(column string, in interface{}) *Model { - return m.Where(fmt.Sprintf(`%s NOT IN (?)`, m.db.QuoteWord(column)), in) + return m.Where(fmt.Sprintf(`%s NOT IN (?)`, m.db.GetCore().QuoteWord(column)), in) } // WhereNotNull builds `xxx IS NOT NULL` statement. func (m *Model) WhereNotNull(columns ...string) *Model { model := m for _, column := range columns { - model = m.Where(fmt.Sprintf(`%s IS NOT NULL`, m.db.QuoteWord(column))) + model = m.Where(fmt.Sprintf(`%s IS NOT NULL`, m.db.GetCore().QuoteWord(column))) } return model } @@ -128,48 +128,48 @@ func (m *Model) WhereOr(where interface{}, args ...interface{}) *Model { // WhereOrBetween builds `xxx BETWEEN x AND y` statement in `OR` conditions. func (m *Model) WhereOrBetween(column string, min, max interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max) + return m.WhereOr(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column)), min, max) } // WhereOrLike builds `xxx LIKE x` statement in `OR` conditions. func (m *Model) WhereOrLike(column string, like interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s LIKE ?`, m.db.QuoteWord(column)), like) + return m.WhereOr(fmt.Sprintf(`%s LIKE ?`, m.db.GetCore().QuoteWord(column)), like) } // WhereOrIn builds `xxx IN (x)` statement in `OR` conditions. func (m *Model) WhereOrIn(column string, in interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s IN (?)`, m.db.QuoteWord(column)), in) + return m.WhereOr(fmt.Sprintf(`%s IN (?)`, m.db.GetCore().QuoteWord(column)), in) } // WhereOrNull builds `xxx IS NULL` statement in `OR` conditions. func (m *Model) WhereOrNull(columns ...string) *Model { model := m for _, column := range columns { - model = m.WhereOr(fmt.Sprintf(`%s IS NULL`, m.db.QuoteWord(column))) + model = m.WhereOr(fmt.Sprintf(`%s IS NULL`, m.db.GetCore().QuoteWord(column))) } return model } // WhereOrNotBetween builds `xxx NOT BETWEEN x AND y` statement in `OR` conditions. func (m *Model) WhereOrNotBetween(column string, min, max interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max) + return m.WhereOr(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column)), min, max) } // WhereOrNotLike builds `xxx NOT LIKE x` statement in `OR` conditions. func (m *Model) WhereOrNotLike(column string, like interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s NOT LIKE ?`, m.db.QuoteWord(column)), like) + return m.WhereOr(fmt.Sprintf(`%s NOT LIKE ?`, m.db.GetCore().QuoteWord(column)), like) } // WhereOrNotIn builds `xxx NOT IN (x)` statement. func (m *Model) WhereOrNotIn(column string, in interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s NOT IN (?)`, m.db.QuoteWord(column)), in) + return m.WhereOr(fmt.Sprintf(`%s NOT IN (?)`, m.db.GetCore().QuoteWord(column)), in) } // WhereOrNotNull builds `xxx IS NOT NULL` statement in `OR` conditions. func (m *Model) WhereOrNotNull(columns ...string) *Model { model := m for _, column := range columns { - model = m.WhereOr(fmt.Sprintf(`%s IS NOT NULL`, m.db.QuoteWord(column))) + model = m.WhereOr(fmt.Sprintf(`%s IS NOT NULL`, m.db.GetCore().QuoteWord(column))) } return model } @@ -177,7 +177,7 @@ func (m *Model) WhereOrNotNull(columns ...string) *Model { // Group sets the "GROUP BY" statement for the model. func (m *Model) Group(groupBy string) *Model { model := m.getModel() - model.groupBy = m.db.QuoteString(groupBy) + model.groupBy = m.db.GetCore().QuoteString(groupBy) return model } @@ -215,7 +215,7 @@ func (m *Model) Order(orderBy ...string) *Model { return m } model := m.getModel() - model.orderBy = m.db.QuoteString(strings.Join(orderBy, " ")) + model.orderBy = m.db.GetCore().QuoteString(strings.Join(orderBy, " ")) return model } @@ -225,7 +225,7 @@ func (m *Model) OrderAsc(column string) *Model { return m } model := m.getModel() - model.orderBy = m.db.QuoteWord(column) + " ASC" + model.orderBy = m.db.GetCore().QuoteWord(column) + " ASC" return model } @@ -235,7 +235,7 @@ func (m *Model) OrderDesc(column string) *Model { return m } model := m.getModel() - model.orderBy = m.db.QuoteWord(column) + " DESC" + model.orderBy = m.db.GetCore().QuoteWord(column) + " DESC" return model } diff --git a/database/gdb/gdb_model_delete.go b/database/gdb/gdb_model_delete.go index 4c56cdcd4..2ce680444 100644 --- a/database/gdb/gdb_model_delete.go +++ b/database/gdb/gdb_model_delete.go @@ -9,6 +9,7 @@ package gdb import ( "database/sql" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/text/gstr" @@ -32,10 +33,11 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) { ) // Soft deleting. if !m.unscoped && fieldNameDelete != "" { - return m.db.DoUpdate( + return m.db.GetCore().DoUpdate( + m.GetCtx(), m.getLink(true), m.tables, - fmt.Sprintf(`%s=?`, m.db.QuoteString(fieldNameDelete)), + fmt.Sprintf(`%s=?`, m.db.GetCore().QuoteString(fieldNameDelete)), conditionWhere+conditionExtra, append([]interface{}{gtime.Now().String()}, conditionArgs...), ) @@ -44,5 +46,5 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) { if !gstr.ContainsI(conditionStr, " WHERE ") { return nil, gerror.New("there should be WHERE condition statement for DELETE operation") } - return m.db.DoDelete(m.getLink(true), m.tables, conditionStr, conditionArgs...) + return m.db.GetCore().DoDelete(m.GetCtx(), m.getLink(true), m.tables, conditionStr, conditionArgs...) } diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index be082b070..46d7af223 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -114,7 +114,7 @@ func (m *Model) GetFieldsStr(prefix ...string) string { } newFields += prefixStr + k } - newFields = m.db.QuoteString(newFields) + newFields = m.db.GetCore().QuoteString(newFields) return newFields } @@ -158,7 +158,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string { } newFields += prefixStr + k } - newFields = m.db.QuoteString(newFields) + newFields = m.db.GetCore().QuoteString(newFields) return newFields } diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index d67978b72..3062354fa 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -8,12 +8,13 @@ package gdb import ( "database/sql" + "reflect" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gutil" - "reflect" ) // Batch sets the batch operation number for the model. @@ -194,7 +195,8 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { list[k] = v } } - return m.db.DoBatchInsert( + return m.db.GetCore().DoBatchInsert( + m.GetCtx(), m.getLink(true), m.tables, newData, @@ -219,7 +221,8 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { data[fieldNameUpdate] = nowString } } - return m.db.DoInsert( + return m.db.GetCore().DoInsert( + m.GetCtx(), m.getLink(true), m.tables, newData, diff --git a/database/gdb/gdb_model_join.go b/database/gdb/gdb_model_join.go index 847c11abd..10e4c9255 100644 --- a/database/gdb/gdb_model_join.go +++ b/database/gdb/gdb_model_join.go @@ -8,6 +8,7 @@ package gdb import ( "fmt" + "github.com/gogf/gf/text/gstr" ) @@ -72,13 +73,13 @@ func (m *Model) doJoin(operator string, table ...string) *Model { joinStr = "(" + joinStr + ")" } } else { - joinStr = m.db.QuotePrefixTableName(table[0]) + joinStr = m.db.GetCore().QuotePrefixTableName(table[0]) } } if len(table) > 2 { model.tables += fmt.Sprintf( " %s JOIN %s AS %s ON (%s)", - operator, joinStr, m.db.QuoteWord(table[1]), table[2], + operator, joinStr, m.db.GetCore().QuoteWord(table[1]), table[2], ) } else if len(table) == 2 { model.tables += fmt.Sprintf( diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index bafd477bc..3536174dc 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -8,13 +8,14 @@ package gdb import ( "fmt" + "reflect" + "github.com/gogf/gf/container/gset" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" - "reflect" ) // Select is alias of Model.All. @@ -66,7 +67,7 @@ func (m *Model) getFieldsFiltered() string { if m.fieldsEx == "" { // No filtering. if !gstr.Contains(m.fields, ".") && !gstr.Contains(m.fields, " ") { - return m.db.QuoteString(m.fields) + return m.db.GetCore().QuoteString(m.fields) } return m.fields } @@ -105,7 +106,7 @@ func (m *Model) getFieldsFiltered() string { if len(newFields) > 0 { newFields += "," } - newFields += m.db.QuoteWord(k) + newFields += m.db.GetCore().QuoteWord(k) } return newFields } @@ -367,7 +368,7 @@ func (m *Model) Min(column string) (float64, error) { if len(column) == 0 { return 0, nil } - value, err := m.Fields(fmt.Sprintf(`MIN(%s)`, m.db.QuoteWord(column))).Value() + value, err := m.Fields(fmt.Sprintf(`MIN(%s)`, m.db.GetCore().QuoteWord(column))).Value() if err != nil { return 0, err } @@ -379,7 +380,7 @@ func (m *Model) Max(column string) (float64, error) { if len(column) == 0 { return 0, nil } - value, err := m.Fields(fmt.Sprintf(`MAX(%s)`, m.db.QuoteWord(column))).Value() + value, err := m.Fields(fmt.Sprintf(`MAX(%s)`, m.db.GetCore().QuoteWord(column))).Value() if err != nil { return 0, err } @@ -391,7 +392,7 @@ func (m *Model) Avg(column string) (float64, error) { if len(column) == 0 { return 0, nil } - value, err := m.Fields(fmt.Sprintf(`AVG(%s)`, m.db.QuoteWord(column))).Value() + value, err := m.Fields(fmt.Sprintf(`AVG(%s)`, m.db.GetCore().QuoteWord(column))).Value() if err != nil { return 0, err } @@ -403,7 +404,7 @@ func (m *Model) Sum(column string) (float64, error) { if len(column) == 0 { return 0, nil } - value, err := m.Fields(fmt.Sprintf(`SUM(%s)`, m.db.QuoteWord(column))).Value() + value, err := m.Fields(fmt.Sprintf(`SUM(%s)`, m.db.GetCore().QuoteWord(column))).Value() if err != nil { return 0, err } @@ -474,7 +475,7 @@ func (m *Model) FindScan(pointer interface{}, where ...interface{}) error { // doGetAllBySql does the select statement on the database. func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, err error) { cacheKey := "" - cacheObj := m.db.GetCache().Ctx(m.db.GetCtx()) + cacheObj := m.db.GetCache().Ctx(m.GetCtx()) // Retrieve from cache. if m.cacheEnabled && m.tx == nil { cacheKey = m.cacheName @@ -496,7 +497,7 @@ func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, e } } } - result, err = m.db.DoGetAll(m.getLink(false), sql, m.mergeArguments(args)...) + result, err = m.db.GetCore().DoGetAll(m.GetCtx(), m.getLink(false), sql, m.mergeArguments(args)...) // Cache the result. if cacheKey != "" && err == nil { if m.cacheDuration < 0 { diff --git a/database/gdb/gdb_model_time.go b/database/gdb/gdb_model_time.go index e167fbb35..e4047e4a1 100644 --- a/database/gdb/gdb_model_time.go +++ b/database/gdb/gdb_model_time.go @@ -140,7 +140,7 @@ func (m *Model) getConditionForSoftDeleting() string { } // Only one table. if fieldName := m.getSoftFieldNameDeleted(); fieldName != "" { - return fmt.Sprintf(`%s IS NULL`, m.db.QuoteWord(fieldName)) + return fmt.Sprintf(`%s IS NULL`, m.db.GetCore().QuoteWord(fieldName)) } return "" } @@ -163,12 +163,12 @@ func (m *Model) getConditionOfTableStringForSoftDeleting(s string) string { return "" } if len(array1) >= 3 { - return fmt.Sprintf(`%s.%s IS NULL`, m.db.QuoteWord(array1[2]), m.db.QuoteWord(field)) + return fmt.Sprintf(`%s.%s IS NULL`, m.db.GetCore().QuoteWord(array1[2]), m.db.GetCore().QuoteWord(field)) } if len(array1) >= 2 { - return fmt.Sprintf(`%s.%s IS NULL`, m.db.QuoteWord(array1[1]), m.db.QuoteWord(field)) + return fmt.Sprintf(`%s.%s IS NULL`, m.db.GetCore().QuoteWord(array1[1]), m.db.GetCore().QuoteWord(field)) } - return fmt.Sprintf(`%s.%s IS NULL`, m.db.QuoteWord(table), m.db.QuoteWord(field)) + return fmt.Sprintf(`%s.%s IS NULL`, m.db.GetCore().QuoteWord(table), m.db.GetCore().QuoteWord(field)) } // getPrimaryTableName parses and returns the primary table name. diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index ad65e723f..8252789e3 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -9,12 +9,13 @@ package gdb import ( "database/sql" "fmt" + "reflect" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gutil" - "reflect" ) // Update does "UPDATE ... " statement for the model. @@ -81,7 +82,8 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro if !gstr.ContainsI(conditionStr, " WHERE ") { return nil, gerror.New("there should be WHERE condition statement for UPDATE operation") } - return m.db.DoUpdate( + return m.db.GetCore().DoUpdate( + m.GetCtx(), m.getLink(true), m.tables, newData, diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 944946bb6..4dcd80a66 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -8,6 +8,8 @@ package gdb import ( "fmt" + "time" + "github.com/gogf/gf/container/gset" "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/os/gtime" @@ -15,7 +17,6 @@ import ( "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gutil" - "time" ) // TableFields retrieves and returns the fields information of specified table of current @@ -29,12 +30,12 @@ func (m *Model) TableFields(table string, schema ...string) (fields map[string]* if m.tx != nil { link = m.tx.tx } else { - link, err = m.db.GetSlave(schema...) + link, err = m.db.GetCore().GetSlave(schema...) if err != nil { return } } - return m.db.TableFields(link, table, schema...) + return m.db.TableFields(m.GetCtx(), link, table, schema...) } // getModel creates and returns a cloned model of current model if `safe` is true, or else it returns @@ -111,7 +112,7 @@ func (m *Model) filterDataForInsertOrUpdate(data interface{}) (interface{}, erro // Note that, it does not filter list item, which is also type of map, for "omit empty" feature. func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEmpty bool) (Map, error) { var err error - data, err = m.db.mappingAndFilterData(m.schema, m.tables, data, m.filter) + data, err = m.db.GetCore().mappingAndFilterData(m.schema, m.tables, data, m.filter) if err != nil { return nil, err } @@ -187,13 +188,13 @@ func (m *Model) getLink(master bool) Link { } switch linkType { case linkTypeMaster: - link, err := m.db.GetMaster(m.schema) + link, err := m.db.GetCore().GetMaster(m.schema) if err != nil { panic(err) } return link case linkTypeSlave: - link, err := m.db.GetSlave(m.schema) + link, err := m.db.GetCore().GetSlave(m.schema) if err != nil { panic(err) } diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index bd6d37883..1fbd891d3 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -8,12 +8,13 @@ package gdb import ( "fmt" + "reflect" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/structs" "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" - "reflect" ) // With creates and returns an ORM model based on meta data of given object. @@ -38,7 +39,7 @@ func (m *Model) With(objects ...interface{}) *Model { model := m.getModel() for _, object := range objects { if m.tables == "" { - m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(object)) + m.tables = m.db.GetCore().QuotePrefixTableName(getTableNameFromOrmTag(object)) return model } model.withArray = append(model.withArray, object) diff --git a/database/gdb/gdb_statement.go b/database/gdb/gdb_statement.go index e856ba0b0..cc74e858c 100644 --- a/database/gdb/gdb_statement.go +++ b/database/gdb/gdb_statement.go @@ -9,6 +9,7 @@ package gdb import ( "context" "database/sql" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" ) @@ -72,9 +73,10 @@ func (s *Stmt) doStmtCommit(stmtType string, ctx context.Context, args ...interf Group: s.core.db.GetGroup(), } ) + // Tracing and logging. s.core.addSqlToTracing(ctx, sqlObj) if s.core.db.GetDebug() { - s.core.writeSqlToLogger(sqlObj) + s.core.writeSqlToLogger(ctx, sqlObj) } return result, err } diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_transaction.go index 187e9e9ea..abc46a7da 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_transaction.go @@ -12,50 +12,184 @@ import ( "fmt" "reflect" + "github.com/gogf/gf/container/gtype" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/guid" "github.com/gogf/gf/text/gregex" ) // TX is the struct for transaction management. type TX struct { - db DB // db is the current gdb database manager. - tx *sql.Tx // tx is the raw and underlying transaction manager. - master *sql.DB // master is the raw and underlying database manager. - transactionCount int // transactionCount marks the times that Begins. + db DB // db is the current gdb database manager. + tx *sql.Tx // tx is the raw and underlying transaction manager. + ctx context.Context // ctx is the context for this transaction only. + master *sql.DB // master is the raw and underlying database manager. + transactionId string // transactionId is an unique id generated by this object for this transaction. + transactionCount int // transactionCount marks the times that Begins. } const ( - transactionPointerPrefix = "transaction" - contextTransactionKey = "TransactionObject" + transactionPointerPrefix = "transaction" + contextTransactionKeyPrefix = "TransactionObjectForGroup_" + transactionIdForLoggerCtx = "TransactionId" ) +var ( + transactionIdGenerator = gtype.NewUint64() +) + +// Begin starts and returns the transaction object. +// You should call Commit or Rollback functions of the transaction object +// if you no longer use the transaction. Commit or Rollback functions will also +// close the transaction automatically. +func (c *Core) Begin() (tx *TX, err error) { + return c.doBeginCtx(c.GetCtx()) +} + +func (c *Core) doBeginCtx(ctx context.Context) (*TX, error) { + if master, err := c.db.Master(); err != nil { + return nil, err + } else { + var ( + tx *TX + sqlStr = "BEGIN" + mTime1 = gtime.TimestampMilli() + rawTx, err = master.Begin() + mTime2 = gtime.TimestampMilli() + sqlObj = &Sql{ + Sql: sqlStr, + Type: "DB.Begin", + Args: nil, + Format: sqlStr, + Error: err, + Start: mTime1, + End: mTime2, + Group: c.db.GetGroup(), + } + ) + if err == nil { + tx = &TX{ + db: c.db, + tx: rawTx, + ctx: context.WithValue(ctx, transactionIdForLoggerCtx, transactionIdGenerator.Add(1)), + master: master, + transactionId: guid.S(), + } + ctx = tx.ctx + } + // Tracing and logging. + c.addSqlToTracing(ctx, sqlObj) + if c.db.GetDebug() { + c.writeSqlToLogger(ctx, sqlObj) + } + return tx, err + } +} + +// Transaction wraps the transaction logic using function `f`. +// It rollbacks the transaction and returns the error from function `f` if +// it returns non-nil error. It commits the transaction and returns nil if +// function `f` returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function `f` +// as it is automatically handled by this function. +func (c *Core) Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) (err error) { + var ( + tx *TX + ) + if ctx == nil { + ctx = c.GetCtx() + } + // Check transaction object from context. + tx = TXFromCtx(ctx, c.db.GetGroup()) + if tx != nil { + return tx.Transaction(ctx, f) + } + tx, err = c.doBeginCtx(ctx) + if err != nil { + return err + } + // Inject transaction object into context. + tx.ctx = WithTX(tx.ctx, tx) + defer func() { + if err == nil { + if e := recover(); e != nil { + err = fmt.Errorf("%v", e) + } + } + if err != nil { + if e := tx.Rollback(); e != nil { + err = e + } + } else { + if e := tx.Commit(); e != nil { + err = e + } + } + }() + err = f(tx.ctx, tx) + return +} + // WithTX injects given transaction object into context and returns a new context. func WithTX(ctx context.Context, tx *TX) context.Context { - return context.WithValue(ctx, contextTransactionKey, tx) + if tx == nil { + return ctx + } + // Check repeat injection from given. + group := tx.db.GetGroup() + if tx := TXFromCtx(ctx, group); tx != nil && tx.db.GetGroup() == group { + return ctx + } + dbCtx := tx.db.GetCtx() + if tx := TXFromCtx(dbCtx, group); tx != nil && tx.db.GetGroup() == group { + return dbCtx + } + // Inject transaction object and id into context. + ctx = context.WithValue(ctx, transactionKeyForContext(group), tx) + return ctx } // TXFromCtx retrieves and returns transaction object from context. // It is usually used in nested transaction feature, and it returns nil if it is not set previously. -func TXFromCtx(ctx context.Context) *TX { +func TXFromCtx(ctx context.Context, group string) *TX { if ctx == nil { return nil } - v := ctx.Value(contextTransactionKey) + v := ctx.Value(transactionKeyForContext(group)) if v != nil { - return v.(*TX) + tx := v.(*TX) + tx.ctx = ctx + return tx } return nil } +// transactionKeyForContext forms and returns a string for storing transaction object of certain database group into context. +func transactionKeyForContext(group string) string { + return contextTransactionKeyPrefix + group +} + +// transactionKeyForNestedPoint forms and returns the transaction key at current save point. +func (tx *TX) transactionKeyForNestedPoint() string { + return tx.db.GetCore().QuoteWord(transactionPointerPrefix + gconv.String(tx.transactionCount)) +} + +// Ctx sets the context for current transaction. +func (tx *TX) Ctx(ctx context.Context) *TX { + tx.ctx = ctx + return tx +} + // Commit commits current transaction. // Note that it releases previous saved transaction point if it's in a nested transaction procedure, // or else it commits the hole transaction. func (tx *TX) Commit() error { if tx.transactionCount > 0 { tx.transactionCount-- - _, err := tx.Exec("RELEASE SAVEPOINT " + tx.transactionKey()) + _, err := tx.Exec("RELEASE SAVEPOINT " + tx.transactionKeyForNestedPoint()) return err } var ( @@ -74,9 +208,9 @@ func (tx *TX) Commit() error { Group: tx.db.GetGroup(), } ) - tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj) + tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj) if tx.db.GetDebug() { - tx.db.writeSqlToLogger(sqlObj) + tx.db.GetCore().writeSqlToLogger(tx.ctx, sqlObj) } return err } @@ -87,7 +221,7 @@ func (tx *TX) Commit() error { func (tx *TX) Rollback() error { if tx.transactionCount > 0 { tx.transactionCount-- - _, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.transactionKey()) + _, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.transactionKeyForNestedPoint()) return err } var ( @@ -106,16 +240,16 @@ func (tx *TX) Rollback() error { Group: tx.db.GetGroup(), } ) - tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj) + tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj) if tx.db.GetDebug() { - tx.db.writeSqlToLogger(sqlObj) + tx.db.GetCore().writeSqlToLogger(tx.ctx, sqlObj) } return err } // Begin starts a nested transaction procedure. func (tx *TX) Begin() error { - _, err := tx.Exec("SAVEPOINT " + tx.transactionKey()) + _, err := tx.Exec("SAVEPOINT " + tx.transactionKeyForNestedPoint()) if err != nil { return err } @@ -126,22 +260,17 @@ func (tx *TX) Begin() error { // SavePoint performs `SAVEPOINT xxx` SQL statement that saves transaction at current point. // The parameter `point` specifies the point name that will be saved to server. func (tx *TX) SavePoint(point string) error { - _, err := tx.Exec("SAVEPOINT " + tx.db.QuoteWord(point)) + _, err := tx.Exec("SAVEPOINT " + tx.db.GetCore().QuoteWord(point)) return err } // RollbackTo performs `ROLLBACK TO SAVEPOINT xxx` SQL statement that rollbacks to specified saved transaction. // The parameter `point` specifies the point name that was saved previously. func (tx *TX) RollbackTo(point string) error { - _, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.db.QuoteWord(point)) + _, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.db.GetCore().QuoteWord(point)) return err } -// transactionKey forms and returns the transaction key at current save point. -func (tx *TX) transactionKey() string { - return tx.db.QuoteWord(transactionPointerPrefix + gconv.String(tx.transactionCount)) -} - // Transaction wraps the transaction logic using function `f`. // It rollbacks the transaction and returns the error from function `f` if // it returns non-nil error. It commits the transaction and returns nil if @@ -150,10 +279,13 @@ func (tx *TX) transactionKey() string { // Note that, you should not Commit or Rollback the transaction in function `f` // as it is automatically handled by this function. func (tx *TX) Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) (err error) { + if ctx != nil { + tx.ctx = ctx + } // Check transaction object from context. - if TXFromCtx(ctx) == nil { + if TXFromCtx(tx.ctx, tx.db.GetGroup()) == nil { // Inject transaction object into context. - ctx = WithTX(ctx, tx) + tx.ctx = WithTX(tx.ctx, tx) } err = tx.Begin() if err != nil { @@ -175,20 +307,20 @@ func (tx *TX) Transaction(ctx context.Context, f func(ctx context.Context, tx *T } } }() - err = f(ctx, tx) + err = f(tx.ctx, tx) return } // Query does query operation on transaction. // See Core.Query. func (tx *TX) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) { - return tx.db.DoQuery(tx.tx, sql, args...) + return tx.db.GetCore().DoQuery(tx.ctx, tx.tx, sql, args...) } // Exec does none query operation on transaction. // See Core.Exec. func (tx *TX) Exec(sql string, args ...interface{}) (sql.Result, error) { - return tx.db.DoExec(tx.tx, sql, args...) + return tx.db.GetCore().DoExec(tx.ctx, tx.tx, sql, args...) } // Prepare creates a prepared statement for later queries or executions. @@ -197,7 +329,7 @@ func (tx *TX) Exec(sql string, args ...interface{}) (sql.Result, error) { // The caller must call the statement's Close method // when the statement is no longer needed. func (tx *TX) Prepare(sql string) (*Stmt, error) { - return tx.db.DoPrepare(tx.tx, sql) + return tx.db.GetCore().DoPrepare(tx.ctx, tx.tx, sql) } // GetAll queries and returns data records from database. @@ -207,7 +339,7 @@ func (tx *TX) GetAll(sql string, args ...interface{}) (Result, error) { return nil, err } defer rows.Close() - return tx.db.convertRowsToResult(rows) + return tx.db.GetCore().convertRowsToResult(rows) } // GetOne queries and returns one record from database. @@ -248,8 +380,8 @@ func (tx *TX) GetStructs(objPointerSlice interface{}, sql string, args ...interf // If parameter `pointer` is type of struct pointer, it calls GetStruct internally for // the conversion. If parameter `pointer` is type of slice, it calls GetStructs internally // for conversion. -func (tx *TX) GetScan(objPointer interface{}, sql string, args ...interface{}) error { - t := reflect.TypeOf(objPointer) +func (tx *TX) GetScan(pointer interface{}, sql string, args ...interface{}) error { + t := reflect.TypeOf(pointer) k := t.Kind() if k != reflect.Ptr { return fmt.Errorf("params should be type of pointer, but got: %v", k) @@ -257,9 +389,9 @@ func (tx *TX) GetScan(objPointer interface{}, sql string, args ...interface{}) e k = t.Elem().Kind() switch k { case reflect.Array, reflect.Slice: - return tx.db.GetStructs(objPointer, sql, args...) + return tx.GetStructs(pointer, sql, args...) case reflect.Struct: - return tx.db.GetStruct(objPointer, sql, args...) + return tx.GetStruct(pointer, sql, args...) default: return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k) } @@ -302,9 +434,9 @@ func (tx *TX) GetCount(sql string, args ...interface{}) (int, error) { // The parameter `batch` specifies the batch operation count when given data is slice. func (tx *TX) Insert(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { - return tx.Model(table).Data(data).Batch(batch[0]).Insert() + return tx.Model(table).Ctx(tx.ctx).Data(data).Batch(batch[0]).Insert() } - return tx.Model(table).Data(data).Insert() + return tx.Model(table).Ctx(tx.ctx).Data(data).Insert() } // InsertIgnore does "INSERT IGNORE INTO ..." statement for the table. @@ -318,17 +450,17 @@ func (tx *TX) Insert(table string, data interface{}, batch ...int) (sql.Result, // The parameter `batch` specifies the batch operation count when given data is slice. func (tx *TX) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { - return tx.Model(table).Data(data).Batch(batch[0]).InsertIgnore() + return tx.Model(table).Ctx(tx.ctx).Data(data).Batch(batch[0]).InsertIgnore() } - return tx.Model(table).Data(data).InsertIgnore() + return tx.Model(table).Ctx(tx.ctx).Data(data).InsertIgnore() } // InsertAndGetId performs action Insert and returns the last insert id that automatically generated. func (tx *TX) InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) { if len(batch) > 0 { - return tx.Model(table).Data(data).Batch(batch[0]).InsertAndGetId() + return tx.Model(table).Ctx(tx.ctx).Data(data).Batch(batch[0]).InsertAndGetId() } - return tx.Model(table).Data(data).InsertAndGetId() + return tx.Model(table).Ctx(tx.ctx).Data(data).InsertAndGetId() } // Replace does "REPLACE INTO ..." statement for the table. @@ -345,9 +477,9 @@ func (tx *TX) InsertAndGetId(table string, data interface{}, batch ...int) (int6 // `batch` specifies the batch operation count. func (tx *TX) Replace(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { - return tx.Model(table).Data(data).Batch(batch[0]).Replace() + return tx.Model(table).Ctx(tx.ctx).Data(data).Batch(batch[0]).Replace() } - return tx.Model(table).Data(data).Replace() + return tx.Model(table).Ctx(tx.ctx).Data(data).Replace() } // Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the table. @@ -363,45 +495,45 @@ func (tx *TX) Replace(table string, data interface{}, batch ...int) (sql.Result, // `batch` specifies the batch operation count. func (tx *TX) Save(table string, data interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { - return tx.Model(table).Data(data).Batch(batch[0]).Save() + return tx.Model(table).Ctx(tx.ctx).Data(data).Batch(batch[0]).Save() } - return tx.Model(table).Data(data).Save() + return tx.Model(table).Ctx(tx.ctx).Data(data).Save() } // BatchInsert batch inserts data. // The parameter `list` must be type of slice of map or struct. func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { - return tx.Model(table).Data(list).Batch(batch[0]).Insert() + return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).Insert() } - return tx.Model(table).Data(list).Insert() + return tx.Model(table).Ctx(tx.ctx).Data(list).Insert() } // BatchInsertIgnore batch inserts data with ignore option. // The parameter `list` must be type of slice of map or struct. func (tx *TX) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { - return tx.Model(table).Data(list).Batch(batch[0]).InsertIgnore() + return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).InsertIgnore() } - return tx.Model(table).Data(list).InsertIgnore() + return tx.Model(table).Ctx(tx.ctx).Data(list).InsertIgnore() } // BatchReplace batch replaces data. // The parameter `list` must be type of slice of map or struct. func (tx *TX) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { - return tx.Model(table).Data(list).Batch(batch[0]).Replace() + return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).Replace() } - return tx.Model(table).Data(list).Replace() + return tx.Model(table).Ctx(tx.ctx).Data(list).Replace() } // BatchSave batch replaces data. // The parameter `list` must be type of slice of map or struct. func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) { if len(batch) > 0 { - return tx.Model(table).Data(list).Batch(batch[0]).Save() + return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).Save() } - return tx.Model(table).Data(list).Save() + return tx.Model(table).Ctx(tx.ctx).Data(list).Save() } // Update does "UPDATE ... " statement for the table. @@ -419,7 +551,7 @@ func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Resul // "age IN(?,?)", 18, 50 // User{ Id : 1, UserName : "john"} func (tx *TX) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) { - return tx.Model(table).Data(data).Where(condition, args...).Update() + return tx.Model(table).Ctx(tx.ctx).Data(data).Where(condition, args...).Update() } // Delete does "DELETE FROM ... " statement for the table. @@ -434,5 +566,5 @@ func (tx *TX) Update(table string, data interface{}, condition interface{}, args // "age IN(?,?)", 18, 50 // User{ Id : 1, UserName : "john"} func (tx *TX) Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) { - return tx.Model(table).Where(condition, args...).Delete() + return tx.Model(table).Ctx(tx.ctx).Where(condition, args...).Delete() } diff --git a/database/gdb/gdb_z_driver_test.go b/database/gdb/gdb_z_driver_test.go index 80686d203..e166856d9 100644 --- a/database/gdb/gdb_z_driver_test.go +++ b/database/gdb/gdb_z_driver_test.go @@ -7,11 +7,13 @@ package gdb_test import ( + "context" + "testing" + "github.com/gogf/gf/container/gtype" "github.com/gogf/gf/database/gdb" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/test/gtest" - "testing" ) // MyDriver is a custom database driver, which is used for testing only. @@ -41,9 +43,9 @@ func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) { // HandleSqlBeforeCommit handles the sql before posts it to database. // It here overwrites the same method of gdb.DriverMysql and makes some custom changes. -func (d *MyDriver) HandleSqlBeforeCommit(link gdb.Link, sql string, args []interface{}) (string, []interface{}) { +func (d *MyDriver) HandleSqlBeforeCommit(ctx context.Context, link gdb.Link, sql string, args []interface{}) (string, []interface{}) { latestSqlString.Set(sql) - return d.DriverMysql.HandleSqlBeforeCommit(link, sql, args) + return d.DriverMysql.HandleSqlBeforeCommit(ctx, link, sql, args) } func init() { diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 78db30e4f..b3087a4ea 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -2910,13 +2910,13 @@ func Test_Model_HasTable(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - result, err := db.HasTable(table) + result, err := db.GetCore().HasTable(table) t.Assert(result, true) t.AssertNil(err) }) gtest.C(t, func(t *gtest.T) { - result, err := db.HasTable("table12321") + result, err := db.GetCore().HasTable("table12321") t.Assert(result, false) t.AssertNil(err) }) diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index 1fa33ea23..b22b2a765 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -833,7 +833,7 @@ func Test_Transaction_Nested_Begin_Rollback_Commit(t *testing.T) { func Test_Transaction_Nested_TX_Transaction_UseTX(t *testing.T) { table := createTable() defer dropTable(table) - + db.SetDebug(true) gtest.C(t, func(t *gtest.T) { var ( err error @@ -897,7 +897,7 @@ func Test_Transaction_Nested_TX_Transaction_UseTX(t *testing.T) { func Test_Transaction_Nested_TX_Transaction_UseDB(t *testing.T) { table := createTable() defer dropTable(table) - + db.SetDebug(true) gtest.C(t, func(t *gtest.T) { var ( err error @@ -910,7 +910,7 @@ func Test_Transaction_Nested_TX_Transaction_UseDB(t *testing.T) { err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - _, err = db.Model(table).Data(g.Map{ + _, err = db.Model(table).Ctx(ctx).Data(g.Map{ "id": 1, "passport": "USER_1", "password": "PASS_1", @@ -933,9 +933,10 @@ func Test_Transaction_Nested_TX_Transaction_UseDB(t *testing.T) { return err }) t.AssertNil(err) + // rollback err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - _, err = tx.Model(table).Data(g.Map{ + _, err = tx.Model(table).Ctx(ctx).Data(g.Map{ "id": 2, "passport": "USER_2", "password": "PASS_2", @@ -943,6 +944,7 @@ func Test_Transaction_Nested_TX_Transaction_UseDB(t *testing.T) { "create_time": gtime.Now().String(), }).Insert() t.AssertNil(err) + // panic makes this transaction rollback. panic("error") return err }) diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index dd619d20e..f28e6862a 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -9,14 +9,15 @@ package glog import ( "errors" "fmt" + "io" + "strings" + "time" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gutil" - "io" - "strings" - "time" ) // Config is the configuration object for logger. @@ -165,6 +166,24 @@ func (l *Logger) SetCtxKeys(keys ...interface{}) { l.config.CtxKeys = keys } +// AppendCtxKeys appends extra keys to logger. +// It ignores the key if it is already appended to the logger previously. +func (l *Logger) AppendCtxKeys(keys ...interface{}) { + var isExist bool + for _, key := range keys { + isExist = false + for _, ctxKey := range l.config.CtxKeys { + if ctxKey == key { + isExist = true + break + } + } + if !isExist { + l.config.CtxKeys = append(l.config.CtxKeys, key) + } + } +} + // GetCtxKeys retrieves and returns the context keys for logging. func (l *Logger) GetCtxKeys() []interface{} { return l.config.CtxKeys From 406b6bf410147f7331f44f6754079a4ef1184286 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Fri, 21 May 2021 15:30:21 +0800 Subject: [PATCH 282/492] infract internal link --- database/gdb/gdb.go | 42 ++-- database/gdb/gdb_core.go | 181 +++-------------- database/gdb/gdb_core_link.go | 31 +++ ...transaction.go => gdb_core_transaction.go} | 57 +++--- database/gdb/gdb_core_underlying.go | 186 ++++++++++++++++++ database/gdb/gdb_core_utility.go | 24 ++- database/gdb/gdb_driver_mssql.go | 10 +- database/gdb/gdb_driver_mysql.go | 10 +- database/gdb/gdb_driver_oracle.go | 12 +- database/gdb/gdb_driver_pgsql.go | 10 +- database/gdb/gdb_driver_sqlite.go | 10 +- database/gdb/gdb_model_utility.go | 10 +- database/gdb/gdb_statement.go | 18 +- database/gdb/gdb_z_mysql_transaction_test.go | 120 ++++++++++- 14 files changed, 465 insertions(+), 256 deletions(-) create mode 100644 database/gdb/gdb_core_link.go rename database/gdb/{gdb_transaction.go => gdb_core_transaction.go} (95%) create mode 100644 database/gdb/gdb_core_underlying.go diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 8d634359e..5b18d5639 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -113,8 +113,8 @@ type DB interface { // Master/Slave specification support. // =========================================================================== - Master() (*sql.DB, error) // See Core.Master. - Slave() (*sql.DB, error) // See Core.Slave. + Master(schema ...string) (*sql.DB, error) // See Core.Master. + Slave(schema ...string) (*sql.DB, error) // See Core.Slave. // =========================================================================== // Ping-Pong. @@ -187,16 +187,28 @@ type Driver interface { New(core *Core, node *ConfigNode) (DB, error) } +// Link is a common database function wrapper interface. +type Link interface { + Query(sql string, args ...interface{}) (*sql.Rows, error) + Exec(sql string, args ...interface{}) (sql.Result, error) + Prepare(sql string) (*sql.Stmt, error) + QueryContext(ctx context.Context, sql string, args ...interface{}) (*sql.Rows, error) + ExecContext(ctx context.Context, sql string, args ...interface{}) (sql.Result, error) + PrepareContext(ctx context.Context, sql string) (*sql.Stmt, error) + IsTransaction() bool +} + // Sql is the sql recording struct. type Sql struct { - Sql string // SQL string(may contain reserved char '?'). - Type string // SQL operation type. - Args []interface{} // Arguments for this sql. - Format string // Formatted sql which contains arguments in the sql. - Error error // Execution result. - Start int64 // Start execution timestamp in milliseconds. - End int64 // End execution timestamp in milliseconds. - Group string // Group is the group name of the configuration that the sql is executed from. + Sql string // SQL string(may contain reserved char '?'). + Type string // SQL operation type. + Args []interface{} // Arguments for this sql. + Format string // Formatted sql which contains arguments in the sql. + Error error // Execution result. + Start int64 // Start execution timestamp in milliseconds. + End int64 // End execution timestamp in milliseconds. + Group string // Group is the group name of the configuration that the sql is executed from. + IsTransaction bool // IsTransaction marks whether this sql is executed in transaction. } // TableField is the struct for table field. @@ -211,16 +223,6 @@ type TableField struct { Comment string // Comment. } -// Link is a common database function wrapper interface. -type Link interface { - Query(sql string, args ...interface{}) (*sql.Rows, error) - Exec(sql string, args ...interface{}) (sql.Result, error) - Prepare(sql string) (*sql.Stmt, error) - QueryContext(ctx context.Context, sql string, args ...interface{}) (*sql.Rows, error) - ExecContext(ctx context.Context, sql string, args ...interface{}) (sql.Result, error) - PrepareContext(ctx context.Context, sql string) (*sql.Stmt, error) -} - // Counter is the type for update count. type Counter struct { Field string diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 14a14917c..9c0786935 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -19,7 +19,6 @@ import ( "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/container/gvar" - "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/util/gconv" ) @@ -94,162 +93,26 @@ func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Cont // Master creates and returns a connection from master node if master-slave configured. // It returns the default connection if master-slave not configured. -func (c *Core) Master() (*sql.DB, error) { - return c.getSqlDb(true, c.schema.Val()) +func (c *Core) Master(schema ...string) (*sql.DB, error) { + useSchema := "" + if len(schema) > 0 && schema[0] != "" { + useSchema = schema[0] + } else { + useSchema = c.schema.Val() + } + return c.getSqlDb(true, useSchema) } // Slave creates and returns a connection from slave node if master-slave configured. // It returns the default connection if master-slave not configured. -func (c *Core) Slave() (*sql.DB, error) { - return c.getSqlDb(false, c.schema.Val()) -} - -// Query commits one query SQL to underlying driver and returns the execution result. -// It is most commonly used for data querying. -func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) { - link, err := c.db.Slave() - if err != nil { - return nil, err - } - return c.DoQuery(c.GetCtx(), link, sql, args...) -} - -// DoQuery commits the sql string and its arguments to underlying driver -// through given link object and returns the execution result. -func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { - sql, args = formatSql(sql, args) - sql, args = c.db.HandleSqlBeforeCommit(ctx, link, sql, args) - if c.GetConfig().QueryTimeout > 0 { - ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) - } - mTime1 := gtime.TimestampMilli() - rows, err = link.QueryContext(ctx, sql, args...) - mTime2 := gtime.TimestampMilli() - sqlObj := &Sql{ - Sql: sql, - Type: "DB.QueryContext", - Args: args, - Format: FormatSqlWithArgs(sql, args), - Error: err, - Start: mTime1, - End: mTime2, - Group: c.db.GetGroup(), - } - // Tracing and logging. - c.addSqlToTracing(ctx, sqlObj) - if c.db.GetDebug() { - c.writeSqlToLogger(ctx, sqlObj) - } - if err == nil { - return rows, nil +func (c *Core) Slave(schema ...string) (*sql.DB, error) { + useSchema := "" + if len(schema) > 0 && schema[0] != "" { + useSchema = schema[0] } else { - err = formatError(err, sql, args...) + useSchema = c.schema.Val() } - return nil, err -} - -// Exec commits one query SQL to underlying driver and returns the execution result. -// It is most commonly used for data inserting and updating. -func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error) { - link, err := c.db.Master() - if err != nil { - return nil, err - } - return c.DoExec(c.GetCtx(), link, sql, args...) -} - -// DoExec commits the sql string and its arguments to underlying driver -// through given link object and returns the execution result. -func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) { - sql, args = formatSql(sql, args) - sql, args = c.db.HandleSqlBeforeCommit(ctx, link, sql, args) - if c.GetConfig().ExecTimeout > 0 { - var cancelFunc context.CancelFunc - ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout) - defer cancelFunc() - } - - mTime1 := gtime.TimestampMilli() - if !c.db.GetDryRun() { - result, err = link.ExecContext(ctx, sql, args...) - } else { - result = new(SqlResult) - } - mTime2 := gtime.TimestampMilli() - sqlObj := &Sql{ - Sql: sql, - Type: "DB.ExecContext", - Args: args, - Format: FormatSqlWithArgs(sql, args), - Error: err, - Start: mTime1, - End: mTime2, - Group: c.db.GetGroup(), - } - // Tracing and logging. - c.addSqlToTracing(ctx, sqlObj) - if c.db.GetDebug() { - c.writeSqlToLogger(ctx, sqlObj) - } - return result, formatError(err, sql, args...) -} - -// Prepare creates a prepared statement for later queries or executions. -// Multiple queries or executions may be run concurrently from the -// returned statement. -// The caller must call the statement's Close method -// when the statement is no longer needed. -// -// The parameter `execOnMaster` specifies whether executing the sql on master node, -// or else it executes the sql on slave node if master-slave configured. -func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) { - var ( - err error - link Link - ) - if len(execOnMaster) > 0 && execOnMaster[0] { - if link, err = c.db.Master(); err != nil { - return nil, err - } - } else { - if link, err = c.db.Slave(); err != nil { - return nil, err - } - } - return c.DoPrepare(c.GetCtx(), link, sql) -} - -// DoPrepare calls prepare function on given link object and returns the statement object. -func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) { - if c.GetConfig().PrepareTimeout > 0 { - // DO NOT USE cancel function in prepare statement. - ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout) - } - var ( - mTime1 = gtime.TimestampMilli() - stmt, err = link.PrepareContext(ctx, sql) - mTime2 = gtime.TimestampMilli() - sqlObj = &Sql{ - Sql: sql, - Type: "DB.PrepareContext", - Args: nil, - Format: FormatSqlWithArgs(sql, nil), - Error: err, - Start: mTime1, - End: mTime2, - Group: c.db.GetGroup(), - } - ) - // Tracing and logging. - c.addSqlToTracing(ctx, sqlObj) - if c.db.GetDebug() { - c.writeSqlToLogger(ctx, sqlObj) - } - return &Stmt{ - Stmt: stmt, - core: c, - sql: sql, - }, err + return c.getSqlDb(false, useSchema) } // GetAll queries and returns data records from database. @@ -260,7 +123,7 @@ func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) { // DoGetAll queries and returns data records from database. func (c *Core) DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) { if link == nil { - link, err = c.db.Slave() + link, err = c.SlaveLink() if err != nil { return nil, err } @@ -536,7 +399,7 @@ func (c *Core) DoInsert(ctx context.Context, link Link, table string, data inter updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", updateStr) } if link == nil { - if link, err = c.db.Master(); err != nil { + if link, err = c.MasterLink(); err != nil { return nil, err } } @@ -641,7 +504,7 @@ func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list return result, gerror.New("data list cannot be empty") } if link == nil { - if link, err = c.db.Master(); err != nil { + if link, err = c.MasterLink(); err != nil { return } } @@ -800,7 +663,7 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter } // If no link passed, it then uses the master link. if link == nil { - if link, err = c.db.Master(); err != nil { + if link, err = c.MasterLink(); err != nil { return nil, err } } @@ -826,7 +689,7 @@ func (c *Core) Delete(table string, condition interface{}, args ...interface{}) // This function is usually used for custom interface definition, you do not need call it manually. func (c *Core) DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) { if link == nil { - if link, err = c.db.Master(); err != nil { + if link, err = c.MasterLink(); err != nil { return nil, err } } @@ -891,8 +754,10 @@ func (c *Core) MarshalJSON() ([]byte, error) { // It is enabled only if configuration "debug" is true. func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) { var transactionIdStr string - if v := ctx.Value(transactionIdForLoggerCtx); v != nil { - transactionIdStr = fmt.Sprintf(`[%d] `, v.(uint64)) + if sql.IsTransaction { + if v := ctx.Value(transactionIdForLoggerCtx); v != nil { + transactionIdStr = fmt.Sprintf(`[%d] `, v.(uint64)) + } } s := fmt.Sprintf("[%3d ms] [%s] %s%s", sql.End-sql.Start, sql.Group, transactionIdStr, sql.Format) if sql.Error != nil { diff --git a/database/gdb/gdb_core_link.go b/database/gdb/gdb_core_link.go new file mode 100644 index 000000000..06ea3190b --- /dev/null +++ b/database/gdb/gdb_core_link.go @@ -0,0 +1,31 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +import ( + "database/sql" +) + +// dbLink is used to implement interface Link for DB. +type dbLink struct { + *sql.DB +} + +// txLink is used to implement interface Link for TX. +type txLink struct { + *sql.Tx +} + +// IsTransaction returns if current Link is a transaction. +func (*dbLink) IsTransaction() bool { + return false +} + +// IsTransaction returns if current Link is a transaction. +func (*txLink) IsTransaction() bool { + return true +} diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_core_transaction.go similarity index 95% rename from database/gdb/gdb_transaction.go rename to database/gdb/gdb_core_transaction.go index abc46a7da..4636d9166 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_core_transaction.go @@ -59,14 +59,15 @@ func (c *Core) doBeginCtx(ctx context.Context) (*TX, error) { rawTx, err = master.Begin() mTime2 = gtime.TimestampMilli() sqlObj = &Sql{ - Sql: sqlStr, - Type: "DB.Begin", - Args: nil, - Format: sqlStr, - Error: err, - Start: mTime1, - End: mTime2, - Group: c.db.GetGroup(), + Sql: sqlStr, + Type: "DB.Begin", + Args: nil, + Format: sqlStr, + Error: err, + Start: mTime1, + End: mTime2, + Group: c.db.GetGroup(), + IsTransaction: true, } ) if err == nil { @@ -198,14 +199,15 @@ func (tx *TX) Commit() error { err = tx.tx.Commit() mTime2 = gtime.TimestampMilli() sqlObj = &Sql{ - Sql: sqlStr, - Type: "TX.Commit", - Args: nil, - Format: sqlStr, - Error: err, - Start: mTime1, - End: mTime2, - Group: tx.db.GetGroup(), + Sql: sqlStr, + Type: "TX.Commit", + Args: nil, + Format: sqlStr, + Error: err, + Start: mTime1, + End: mTime2, + Group: tx.db.GetGroup(), + IsTransaction: true, } ) tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj) @@ -230,14 +232,15 @@ func (tx *TX) Rollback() error { err = tx.tx.Rollback() mTime2 = gtime.TimestampMilli() sqlObj = &Sql{ - Sql: sqlStr, - Type: "TX.Rollback", - Args: nil, - Format: sqlStr, - Error: err, - Start: mTime1, - End: mTime2, - Group: tx.db.GetGroup(), + Sql: sqlStr, + Type: "TX.Rollback", + Args: nil, + Format: sqlStr, + Error: err, + Start: mTime1, + End: mTime2, + Group: tx.db.GetGroup(), + IsTransaction: true, } ) tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj) @@ -314,13 +317,13 @@ func (tx *TX) Transaction(ctx context.Context, f func(ctx context.Context, tx *T // Query does query operation on transaction. // See Core.Query. func (tx *TX) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) { - return tx.db.GetCore().DoQuery(tx.ctx, tx.tx, sql, args...) + return tx.db.GetCore().DoQuery(tx.ctx, &txLink{tx.tx}, sql, args...) } // Exec does none query operation on transaction. // See Core.Exec. func (tx *TX) Exec(sql string, args ...interface{}) (sql.Result, error) { - return tx.db.GetCore().DoExec(tx.ctx, tx.tx, sql, args...) + return tx.db.GetCore().DoExec(tx.ctx, &txLink{tx.tx}, sql, args...) } // Prepare creates a prepared statement for later queries or executions. @@ -329,7 +332,7 @@ func (tx *TX) Exec(sql string, args ...interface{}) (sql.Result, error) { // The caller must call the statement's Close method // when the statement is no longer needed. func (tx *TX) Prepare(sql string) (*Stmt, error) { - return tx.db.GetCore().DoPrepare(tx.ctx, tx.tx, sql) + return tx.db.GetCore().DoPrepare(tx.ctx, &txLink{tx.tx}, sql) } // GetAll queries and returns data records from database. diff --git a/database/gdb/gdb_core_underlying.go b/database/gdb/gdb_core_underlying.go new file mode 100644 index 000000000..316127df0 --- /dev/null +++ b/database/gdb/gdb_core_underlying.go @@ -0,0 +1,186 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. +// + +package gdb + +import ( + "context" + "database/sql" + + "github.com/gogf/gf/os/gtime" +) + +// Query commits one query SQL to underlying driver and returns the execution result. +// It is most commonly used for data querying. +func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) { + return c.DoQuery(c.GetCtx(), nil, sql, args...) +} + +// DoQuery commits the sql string and its arguments to underlying driver +// through given link object and returns the execution result. +func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { + // Transaction checks. + if link == nil { + if link, err = c.SlaveLink(); err != nil { + return nil, err + } + } else if !link.IsTransaction() { + if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { + link = &txLink{tx.tx} + } + } + // Link execution. + sql, args = formatSql(sql, args) + sql, args = c.db.HandleSqlBeforeCommit(ctx, link, sql, args) + if c.GetConfig().QueryTimeout > 0 { + ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) + } + mTime1 := gtime.TimestampMilli() + rows, err = link.QueryContext(ctx, sql, args...) + mTime2 := gtime.TimestampMilli() + sqlObj := &Sql{ + Sql: sql, + Type: "DB.QueryContext", + Args: args, + Format: FormatSqlWithArgs(sql, args), + Error: err, + Start: mTime1, + End: mTime2, + Group: c.db.GetGroup(), + IsTransaction: link.IsTransaction(), + } + // Tracing and logging. + c.addSqlToTracing(ctx, sqlObj) + if c.db.GetDebug() { + c.writeSqlToLogger(ctx, sqlObj) + } + if err == nil { + return rows, nil + } else { + err = formatError(err, sql, args...) + } + return nil, err +} + +// Exec commits one query SQL to underlying driver and returns the execution result. +// It is most commonly used for data inserting and updating. +func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error) { + return c.DoExec(c.GetCtx(), nil, sql, args...) +} + +// DoExec commits the sql string and its arguments to underlying driver +// through given link object and returns the execution result. +func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) { + // Transaction checks. + if link == nil { + if link, err = c.MasterLink(); err != nil { + return nil, err + } + } else if !link.IsTransaction() { + if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { + link = &txLink{tx.tx} + } + } + // Link execution. + sql, args = formatSql(sql, args) + sql, args = c.db.HandleSqlBeforeCommit(ctx, link, sql, args) + if c.GetConfig().ExecTimeout > 0 { + var cancelFunc context.CancelFunc + ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout) + defer cancelFunc() + } + + mTime1 := gtime.TimestampMilli() + if !c.db.GetDryRun() { + result, err = link.ExecContext(ctx, sql, args...) + } else { + result = new(SqlResult) + } + mTime2 := gtime.TimestampMilli() + sqlObj := &Sql{ + Sql: sql, + Type: "DB.ExecContext", + Args: args, + Format: FormatSqlWithArgs(sql, args), + Error: err, + Start: mTime1, + End: mTime2, + Group: c.db.GetGroup(), + IsTransaction: link.IsTransaction(), + } + // Tracing and logging. + c.addSqlToTracing(ctx, sqlObj) + if c.db.GetDebug() { + c.writeSqlToLogger(ctx, sqlObj) + } + return result, formatError(err, sql, args...) +} + +// Prepare creates a prepared statement for later queries or executions. +// Multiple queries or executions may be run concurrently from the +// returned statement. +// The caller must call the statement's Close method +// when the statement is no longer needed. +// +// The parameter `execOnMaster` specifies whether executing the sql on master node, +// or else it executes the sql on slave node if master-slave configured. +func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) { + var ( + err error + link Link + ) + if len(execOnMaster) > 0 && execOnMaster[0] { + if link, err = c.MasterLink(); err != nil { + return nil, err + } + } else { + if link, err = c.SlaveLink(); err != nil { + return nil, err + } + } + return c.DoPrepare(c.GetCtx(), link, sql) +} + +// DoPrepare calls prepare function on given link object and returns the statement object. +func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) { + if link != nil && !link.IsTransaction() { + if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { + link = &txLink{tx.tx} + } + } + if c.GetConfig().PrepareTimeout > 0 { + // DO NOT USE cancel function in prepare statement. + ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout) + } + var ( + mTime1 = gtime.TimestampMilli() + stmt, err = link.PrepareContext(ctx, sql) + mTime2 = gtime.TimestampMilli() + sqlObj = &Sql{ + Sql: sql, + Type: "DB.PrepareContext", + Args: nil, + Format: FormatSqlWithArgs(sql, nil), + Error: err, + Start: mTime1, + End: mTime2, + Group: c.db.GetGroup(), + IsTransaction: link.IsTransaction(), + } + ) + // Tracing and logging. + c.addSqlToTracing(ctx, sqlObj) + if c.db.GetDebug() { + c.writeSqlToLogger(ctx, sqlObj) + } + return &Stmt{ + Stmt: stmt, + core: c, + link: link, + sql: sql, + }, err +} diff --git a/database/gdb/gdb_core_utility.go b/database/gdb/gdb_core_utility.go index 3528d873e..0eaeb16c4 100644 --- a/database/gdb/gdb_core_utility.go +++ b/database/gdb/gdb_core_utility.go @@ -7,22 +7,26 @@ package gdb -import ( - "database/sql" -) - -// GetMaster acts like function Master but with additional `schema` parameter specifying +// MasterLink acts like function Master but with additional `schema` parameter specifying // the schema for the connection. It is defined for internal usage. // Also see Master. -func (c *Core) GetMaster(schema ...string) (*sql.DB, error) { - return c.getSqlDb(true, schema...) +func (c *Core) MasterLink(schema ...string) (Link, error) { + db, err := c.db.Master(schema...) + if err != nil { + return nil, err + } + return &dbLink{db}, nil } -// GetSlave acts like function Slave but with additional `schema` parameter specifying +// SlaveLink acts like function Slave but with additional `schema` parameter specifying // the schema for the connection. It is defined for internal usage. // Also see Slave. -func (c *Core) GetSlave(schema ...string) (*sql.DB, error) { - return c.getSqlDb(false, schema...) +func (c *Core) SlaveLink(schema ...string) (Link, error) { + db, err := c.db.Slave(schema...) + if err != nil { + return nil, err + } + return &dbLink{db}, nil } // QuoteWord checks given string `s` a word, if true quotes it with security chars of the database diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index fde84e762..d0bb93cfb 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -187,7 +187,7 @@ func (d *DriverMssql) parseSql(sql string) string { // It's mainly used in cli tool chain for automatically generating the models. func (d *DriverMssql) Tables(ctx context.Context, schema ...string) (tables []string, err error) { var result Result - link, err := d.GetSlave(schema...) + link, err := d.SlaveLink(schema...) if err != nil { return nil, err } @@ -213,20 +213,20 @@ func (d *DriverMssql) TableFields(ctx context.Context, link Link, table string, if gstr.Contains(table, " ") { return nil, gerror.New("function TableFields supports only single table operations") } - checkSchema := d.db.GetSchema() + useSchema := d.db.GetSchema() if len(schema) > 0 && schema[0] != "" { - checkSchema = schema[0] + useSchema = schema[0] } tableFieldsCacheKey := fmt.Sprintf( `mssql_table_fields_%s_%s@group:%s`, - table, checkSchema, d.GetGroup(), + table, useSchema, d.GetGroup(), ) v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { var ( result Result ) if link == nil { - link, err = d.GetSlave(checkSchema) + link, err = d.SlaveLink(useSchema) if err != nil { return nil } diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index ee87ecd94..724b353ec 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -85,7 +85,7 @@ func (d *DriverMysql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql // It's mainly used in cli tool chain for automatically generating the models. func (d *DriverMysql) Tables(ctx context.Context, schema ...string) (tables []string, err error) { var result Result - link, err := d.GetSlave(schema...) + link, err := d.SlaveLink(schema...) if err != nil { return nil, err } @@ -119,20 +119,20 @@ func (d *DriverMysql) TableFields(ctx context.Context, link Link, table string, if gstr.Contains(table, " ") { return nil, gerror.New("function TableFields supports only single table operations") } - checkSchema := d.schema.Val() + useSchema := d.schema.Val() if len(schema) > 0 && schema[0] != "" { - checkSchema = schema[0] + useSchema = schema[0] } tableFieldsCacheKey := fmt.Sprintf( `mysql_table_fields_%s_%s@group:%s`, - table, checkSchema, d.GetGroup(), + table, useSchema, d.GetGroup(), ) v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { var ( result Result ) if link == nil { - link, err = d.GetSlave(checkSchema) + link, err = d.SlaveLink(useSchema) if err != nil { return nil } diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index b067de9b4..be356f176 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -189,13 +189,13 @@ func (d *DriverOracle) TableFields(ctx context.Context, link Link, table string, if gstr.Contains(table, " ") { return nil, gerror.New("function TableFields supports only single table operations") } - checkSchema := d.db.GetSchema() + useSchema := d.db.GetSchema() if len(schema) > 0 && schema[0] != "" { - checkSchema = schema[0] + useSchema = schema[0] } tableFieldsCacheKey := fmt.Sprintf( `oracle_table_fields_%s_%s@group:%s`, - table, checkSchema, d.GetGroup(), + table, useSchema, d.GetGroup(), ) v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { var ( @@ -213,7 +213,7 @@ FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, ) structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) if link == nil { - link, err = d.GetSlave(checkSchema) + link, err = d.SlaveLink(useSchema) if err != nil { return nil } @@ -340,7 +340,7 @@ func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, da } if link == nil { - if link, err = d.db.Master(); err != nil { + if link, err = d.MasterLink(); err != nil { return nil, err } } @@ -416,7 +416,7 @@ func (d *DriverOracle) DoBatchInsert(ctx context.Context, link Link, table strin return result, gerror.New("empty data list") } if link == nil { - if link, err = d.db.Master(); err != nil { + if link, err = d.MasterLink(); err != nil { return } } diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 2d0bd76f6..8fb6ad243 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -92,7 +92,7 @@ func (d *DriverPgsql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql // It's mainly used in cli tool chain for automatically generating the models. func (d *DriverPgsql) Tables(ctx context.Context, schema ...string) (tables []string, err error) { var result Result - link, err := d.GetSlave(schema...) + link, err := d.SlaveLink(schema...) if err != nil { return nil, err } @@ -122,13 +122,13 @@ func (d *DriverPgsql) TableFields(ctx context.Context, link Link, table string, return nil, gerror.New("function TableFields supports only single table operations") } table, _ = gregex.ReplaceString("\"", "", table) - checkSchema := d.db.GetSchema() + useSchema := d.db.GetSchema() if len(schema) > 0 && schema[0] != "" { - checkSchema = schema[0] + useSchema = schema[0] } tableFieldsCacheKey := fmt.Sprintf( `pgsql_table_fields_%s_%s@group:%s`, - table, checkSchema, d.GetGroup(), + table, useSchema, d.GetGroup(), ) v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { var ( @@ -143,7 +143,7 @@ ORDER BY a.attnum`, ) structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) if link == nil { - link, err = d.GetSlave(checkSchema) + link, err = d.SlaveLink(useSchema) if err != nil { return nil } diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index c78a3eb27..69f4a42a5 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -77,7 +77,7 @@ func (d *DriverSqlite) HandleSqlBeforeCommit(ctx context.Context, link Link, sql // It's mainly used in cli tool chain for automatically generating the models. func (d *DriverSqlite) Tables(ctx context.Context, schema ...string) (tables []string, err error) { var result Result - link, err := d.GetSlave(schema...) + link, err := d.SlaveLink(schema...) if err != nil { return nil, err } @@ -103,20 +103,20 @@ func (d *DriverSqlite) TableFields(ctx context.Context, link Link, table string, if gstr.Contains(table, " ") { return nil, gerror.New("function TableFields supports only single table operations") } - checkSchema := d.db.GetSchema() + useSchema := d.db.GetSchema() if len(schema) > 0 && schema[0] != "" { - checkSchema = schema[0] + useSchema = schema[0] } tableFieldsCacheKey := fmt.Sprintf( `sqlite_table_fields_%s_%s@group:%s`, - table, checkSchema, d.GetGroup(), + table, useSchema, d.GetGroup(), ) v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { var ( result Result ) if link == nil { - link, err = d.GetSlave(checkSchema) + link, err = d.SlaveLink(useSchema) if err != nil { return nil } diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 4dcd80a66..9efee81cf 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -28,9 +28,9 @@ func (m *Model) TableFields(table string, schema ...string) (fields map[string]* link Link ) if m.tx != nil { - link = m.tx.tx + link = &txLink{m.tx.tx} } else { - link, err = m.db.GetCore().GetSlave(schema...) + link, err = m.db.GetCore().SlaveLink(schema...) if err != nil { return } @@ -176,7 +176,7 @@ func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEm // The parameter `master` specifies whether using the master node if master-slave configured. func (m *Model) getLink(master bool) Link { if m.tx != nil { - return m.tx.tx + return &txLink{m.tx.tx} } linkType := m.linkType if linkType == 0 { @@ -188,13 +188,13 @@ func (m *Model) getLink(master bool) Link { } switch linkType { case linkTypeMaster: - link, err := m.db.GetCore().GetMaster(m.schema) + link, err := m.db.GetCore().MasterLink(m.schema) if err != nil { panic(err) } return link case linkTypeSlave: - link, err := m.db.GetCore().GetSlave(m.schema) + link, err := m.db.GetCore().SlaveLink(m.schema) if err != nil { panic(err) } diff --git a/database/gdb/gdb_statement.go b/database/gdb/gdb_statement.go index cc74e858c..81f14affe 100644 --- a/database/gdb/gdb_statement.go +++ b/database/gdb/gdb_statement.go @@ -26,6 +26,7 @@ import ( type Stmt struct { *sql.Stmt core *Core + link Link sql string } @@ -63,14 +64,15 @@ func (s *Stmt) doStmtCommit(stmtType string, ctx context.Context, args ...interf var ( timestampMilli2 = gtime.TimestampMilli() sqlObj = &Sql{ - Sql: s.sql, - Type: stmtType, - Args: args, - Format: FormatSqlWithArgs(s.sql, args), - Error: err, - Start: timestampMilli1, - End: timestampMilli2, - Group: s.core.db.GetGroup(), + Sql: s.sql, + Type: stmtType, + Args: args, + Format: FormatSqlWithArgs(s.sql, args), + Error: err, + Start: timestampMilli1, + End: timestampMilli2, + Group: s.core.db.GetGroup(), + IsTransaction: s.link.IsTransaction(), } ) // Tracing and logging. diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index b22b2a765..906bc6aca 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -833,7 +833,10 @@ func Test_Transaction_Nested_Begin_Rollback_Commit(t *testing.T) { func Test_Transaction_Nested_TX_Transaction_UseTX(t *testing.T) { table := createTable() defer dropTable(table) + db.SetDebug(true) + defer db.SetDebug(false) + gtest.C(t, func(t *gtest.T) { var ( err error @@ -887,17 +890,75 @@ func Test_Transaction_Nested_TX_Transaction_UseTX(t *testing.T) { }) t.AssertNil(err) - all, err := db.Model(table).All() + all, err := db.Ctx(ctx).Model(table).All() t.AssertNil(err) t.Assert(len(all), 1) t.Assert(all[0]["id"], 1) + + // another record. + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + // commit + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + _, err = tx.Model(table).Data(g.Map{ + "id": 3, + "passport": "USER_1", + "password": "PASS_1", + "nickname": "NAME_1", + "create_time": gtime.Now().String(), + }).Insert() + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + // rollback + err = tx.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + _, err = tx.Model(table).Data(g.Map{ + "id": 4, + "passport": "USER_2", + "password": "PASS_2", + "nickname": "NAME_2", + "create_time": gtime.Now().String(), + }).Insert() + t.AssertNil(err) + panic("error") + return err + }) + t.AssertNE(err, nil) + return nil + }) + t.AssertNil(err) + + all, err = db.Ctx(ctx).Model(table).All() + t.AssertNil(err) + t.Assert(len(all), 2) + t.Assert(all[0]["id"], 1) + t.Assert(all[1]["id"], 3) }) } func Test_Transaction_Nested_TX_Transaction_UseDB(t *testing.T) { table := createTable() defer dropTable(table) + db.SetDebug(true) + defer db.SetDebug(false) + gtest.C(t, func(t *gtest.T) { var ( err error @@ -952,11 +1013,66 @@ func Test_Transaction_Nested_TX_Transaction_UseDB(t *testing.T) { return nil }) t.AssertNil(err) - all, err := db.Model(table).All() t.AssertNil(err) t.Assert(len(all), 1) t.Assert(all[0]["id"], 1) + + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + // commit + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + _, err = db.Model(table).Ctx(ctx).Data(g.Map{ + "id": 3, + "passport": "USER_1", + "password": "PASS_1", + "nickname": "NAME_1", + "create_time": gtime.Now().String(), + }).Insert() + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + return err + }) + t.AssertNil(err) + + // rollback + err = db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + _, err = tx.Model(table).Ctx(ctx).Data(g.Map{ + "id": 4, + "passport": "USER_2", + "password": "PASS_2", + "nickname": "NAME_2", + "create_time": gtime.Now().String(), + }).Insert() + t.AssertNil(err) + // panic makes this transaction rollback. + panic("error") + return err + }) + t.AssertNE(err, nil) + return nil + }) + t.AssertNil(err) + + all, err = db.Model(table).All() + t.AssertNil(err) + t.Assert(len(all), 2) + t.Assert(all[0]["id"], 1) + t.Assert(all[1]["id"], 3) }) } From 6d81aa4462d3483ac1a60212ecf72079ee81caa3 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Fri, 21 May 2021 15:38:56 +0800 Subject: [PATCH 283/492] infract internal link --- database/gdb/gdb_core.go | 7 +------ database/gdb/gdb_model_utility.go | 13 +------------ 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 9c0786935..6aa55414c 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -23,6 +23,7 @@ import ( "github.com/gogf/gf/util/gconv" ) +// GetCore returns the underlying *Core object. func (c *Core) GetCore() *Core { return c } @@ -122,12 +123,6 @@ func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) { // DoGetAll queries and returns data records from database. func (c *Core) DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) { - if link == nil { - link, err = c.SlaveLink() - if err != nil { - return nil, err - } - } rows, err := c.DoQuery(ctx, link, sql, args...) if err != nil || rows == nil { return nil, err diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 9efee81cf..362219965 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -24,18 +24,7 @@ import ( // // Also see DriverMysql.TableFields. func (m *Model) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) { - var ( - link Link - ) - if m.tx != nil { - link = &txLink{m.tx.tx} - } else { - link, err = m.db.GetCore().SlaveLink(schema...) - if err != nil { - return - } - } - return m.db.TableFields(m.GetCtx(), link, table, schema...) + return m.db.TableFields(m.GetCtx(), m.getLink(false), table, schema...) } // getModel creates and returns a cloned model of current model if `safe` is true, or else it returns From 64a9b06e64cd09ffc0e599d802ee0c7897ba4e33 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 23 May 2021 00:02:49 +0800 Subject: [PATCH 284/492] comment update --- database/gdb/gdb_core.go | 1 + database/gredis/gredis.go | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 6aa55414c..a275ef2bd 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -48,6 +48,7 @@ func (c *Core) Ctx(ctx context.Context) DB { ) *newCore = *c newCore.ctx = ctx + // It creates a new DB object, which is commonly a wrapper for object `Core`. newCore.db, err = driverMap[configNode.Type].New(newCore, configNode) if err != nil { // It is really a serious error here. diff --git a/database/gredis/gredis.go b/database/gredis/gredis.go index 744ba4da6..13211d81d 100644 --- a/database/gredis/gredis.go +++ b/database/gredis/gredis.go @@ -32,14 +32,14 @@ type Redis struct { ctx context.Context // Context. } -// Redis connection. +// Conn is redis connection. type Conn struct { redis.Conn ctx context.Context redis *Redis } -// Redis configuration. +// Config is redis configuration. type Config struct { Host string `json:"host"` Port int `json:"port"` @@ -54,7 +54,7 @@ type Config struct { TLSSkipVerify bool `json:"tlsSkipVerify"` // Disables server name verification when connecting over TLS. } -// Pool statistics. +// PoolStats is statistics of redis connection pool. type PoolStats struct { redis.PoolStats } @@ -189,7 +189,7 @@ func (r *Redis) Conn() *Conn { } } -// Alias of Conn, see Conn. +// GetConn is alias of Conn, see Conn. // Deprecated, use Conn instead. func (r *Redis) GetConn() *Conn { return r.Conn() From 211e62a8e7e55a5822d80411d98e9e85cd04605d Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 24 May 2021 13:30:04 +0800 Subject: [PATCH 285/492] add timestamptz type support for pgsql for package gdb --- database/gdb/gdb_core_structure.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database/gdb/gdb_core_structure.go b/database/gdb/gdb_core_structure.go index 02b9d11ac..be34cf062 100644 --- a/database/gdb/gdb_core_structure.go +++ b/database/gdb/gdb_core_structure.go @@ -100,7 +100,8 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s case "datetime", - "timestamp": + "timestamp", + "timestamptz": if t, ok := fieldValue.(time.Time); ok { return gtime.NewFromTime(t) } From 5903eb8ceb0474d2dcd0dbe91a848c52bcdb7f79 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 25 May 2021 09:56:23 +0800 Subject: [PATCH 286/492] fix issue #1254 --- database/gdb/gdb_z_mysql_struct_test.go | 23 ++++++++++++++++++++ util/gconv/gconv.go | 29 ++++++++++++++++++++++++- util/gconv/gconv_z_unit_slice_test.go | 2 ++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index 090a991a7..dbcf48d97 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -429,3 +429,26 @@ func Test_Model_Scan_UnmarshalValue(t *testing.T) { t.Assert(users[9].CreateTime.String(), CreateTime) }) } + +func Test_Model_Scan_Map(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(table).Order("id asc").Scan(&users) + t.AssertNil(err) + t.Assert(len(users), TableSize) + t.Assert(users[0].Id, 1) + t.Assert(users[0].Passport, "user_1") + t.Assert(users[0].Password, "") + t.Assert(users[0].Nickname, "name_1") + t.Assert(users[0].CreateTime.String(), CreateTime) + + t.Assert(users[9].Id, 10) + t.Assert(users[9].Passport, "user_10") + t.Assert(users[9].Password, "") + t.Assert(users[9].Nickname, "name_10") + t.Assert(users[9].CreateTime.String(), CreateTime) + }) +} diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index af74e7369..83d140625 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -13,6 +13,7 @@ import ( "fmt" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/os/gtime" + "math" "reflect" "strconv" "strings" @@ -299,6 +300,32 @@ func Bytes(any interface{}) []byte { if f, ok := value.(apiBytes); ok { return f.Bytes() } + var ( + reflectValue = reflect.ValueOf(any) + reflectKind = reflectValue.Kind() + ) + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { + case reflect.Array, reflect.Slice: + var ( + ok = true + bytes = make([]byte, reflectValue.Len()) + ) + for i, _ := range bytes { + int32Value := Int32(reflectValue.Index(i).Interface()) + if int32Value < 0 || int32Value > math.MaxUint8 { + ok = false + break + } + bytes[i] = byte(int32Value) + } + if ok { + return bytes + } + } return gbinary.Encode(any) } } @@ -308,7 +335,7 @@ func Rune(any interface{}) rune { if v, ok := any.(rune); ok { return v } - return rune(Int32(any)) + return Int32(any) } // Runes converts `any` to []rune. diff --git a/util/gconv/gconv_z_unit_slice_test.go b/util/gconv/gconv_z_unit_slice_test.go index 062b6fc17..c67f4bf3f 100644 --- a/util/gconv/gconv_z_unit_slice_test.go +++ b/util/gconv/gconv_z_unit_slice_test.go @@ -19,6 +19,8 @@ func Test_Slice(t *testing.T) { gtest.C(t, func(t *gtest.T) { value := 123.456 t.AssertEQ(gconv.Bytes("123"), []byte("123")) + t.AssertEQ(gconv.Bytes([]interface{}{1}), []byte{1}) + t.AssertEQ(gconv.Bytes([]interface{}{300}), []byte("[300]")) t.AssertEQ(gconv.Strings(value), []string{"123.456"}) t.AssertEQ(gconv.Ints(value), []int{123}) t.AssertEQ(gconv.Floats(value), []float64{123.456}) From aa0494831941151ffd71844c000a38c52b0fe1d4 Mon Sep 17 00:00:00 2001 From: fangjw Date: Tue, 25 May 2021 16:10:52 +0800 Subject: [PATCH 287/492] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B5=AE=E7=82=B9?= =?UTF-8?q?=E5=9E=8B=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原因: 返回值,强制转成 int 类型,会导致浮点型比较不准确,例如:0.33,转成 int 类型时,会变成 0 --- util/gutil/gutil_comparator.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/util/gutil/gutil_comparator.go b/util/gutil/gutil_comparator.go index 3c902d601..9c9280790 100644 --- a/util/gutil/gutil_comparator.go +++ b/util/gutil/gutil_comparator.go @@ -77,12 +77,28 @@ func ComparatorUint64(a, b interface{}) int { // ComparatorFloat32 provides a basic comparison on float32. func ComparatorFloat32(a, b interface{}) int { - return int(gconv.Float32(a) - gconv.Float32(b)) + aFloat := gconv.Float64(a) + bFloat := gconv.Float64(b) + if aFloat == bFloat{ + return 0 + } + if aFloat > bFloat{ + return 1 + } + return -1 } // ComparatorFloat64 provides a basic comparison on float64. func ComparatorFloat64(a, b interface{}) int { - return int(gconv.Float64(a) - gconv.Float64(b)) + aFloat := gconv.Float64(a) + bFloat := gconv.Float64(b) + if aFloat == bFloat{ + return 0 + } + if aFloat > bFloat{ + return 1 + } + return -1 } // ComparatorByte provides a basic comparison on byte. From 7b327910065a1d6ab7bfa33392db2c011cb3b3ef Mon Sep 17 00:00:00 2001 From: fangjw Date: Tue, 25 May 2021 16:15:02 +0800 Subject: [PATCH 288/492] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B5=AE=E7=82=B9?= =?UTF-8?q?=E5=9E=8B=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原因: 返回值,强制转成 int 类型,会导致浮点型比较不准确,例如:0.33,转成 int 类型时,会变成 0 --- util/gutil/gutil_comparator.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/gutil/gutil_comparator.go b/util/gutil/gutil_comparator.go index 9c9280790..39c364868 100644 --- a/util/gutil/gutil_comparator.go +++ b/util/gutil/gutil_comparator.go @@ -79,10 +79,10 @@ func ComparatorUint64(a, b interface{}) int { func ComparatorFloat32(a, b interface{}) int { aFloat := gconv.Float64(a) bFloat := gconv.Float64(b) - if aFloat == bFloat{ + if aFloat == bFloat { return 0 } - if aFloat > bFloat{ + if aFloat > bFloat { return 1 } return -1 @@ -92,10 +92,10 @@ func ComparatorFloat32(a, b interface{}) int { func ComparatorFloat64(a, b interface{}) int { aFloat := gconv.Float64(a) bFloat := gconv.Float64(b) - if aFloat == bFloat{ + if aFloat == bFloat { return 0 } - if aFloat > bFloat{ + if aFloat > bFloat { return 1 } return -1 From fab6c4048d18d1685a425709cac721d57a134353 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 25 May 2021 22:16:55 +0800 Subject: [PATCH 289/492] comment update --- debug/gdebug/gdebug_caller.go | 4 ++-- util/gconv/gconv.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/debug/gdebug/gdebug_caller.go b/debug/gdebug/gdebug_caller.go index a70ffda6a..485a61228 100644 --- a/debug/gdebug/gdebug_caller.go +++ b/debug/gdebug/gdebug_caller.go @@ -42,13 +42,13 @@ func init() { } } -// CallerPath returns the function name and the absolute file path along with its line +// Caller returns the function name and the absolute file path along with its line // number of the caller. func Caller(skip ...int) (function string, path string, line int) { return CallerWithFilter("", skip...) } -// CallerPathWithFilter returns the function name and the absolute file path along with +// CallerWithFilter returns the function name and the absolute file path along with // its line number of the caller. // // The parameter is used to filter the path of the caller. diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 83d140625..be6b027ac 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -40,7 +40,7 @@ var ( "false": {}, } - // Priority tags for Map*/Struct* functions. + // StructTagPriority defines the default priority tags for Map*/Struct* functions. // Note, the "gconv", "param", "params" tags are used by old version of package. // It is strongly recommended using short tag "c" or "p" instead in the future. StructTagPriority = []string{"gconv", "param", "params", "c", "p", "json"} From fc88001a8cdfa423c03d2a21eebad42cf98039a9 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 26 May 2021 09:55:33 +0800 Subject: [PATCH 290/492] revert name from Job to Entry for package gtimer/gcron --- os/gcron/gcron.go | 12 +-- os/gcron/gcron_cron.go | 28 +++---- os/gcron/gcron_entry.go | 60 +++++++-------- os/gcron/gcron_unit_2_test.go | 2 +- os/gtimer/gtimer.go | 22 +++--- os/gtimer/{gtimer_job.go => gtimer_entry.go} | 74 +++++++++---------- os/gtimer/gtimer_timer.go | 40 +++++----- os/gtimer/gtimer_timer_loop.go | 12 +-- os/gtimer/gtimer_z_unit_api_test.go | 8 +- ...ob_test.go => gtimer_z_unit_entry_test.go} | 0 os/gtimer/gtimer_z_unit_timer_test.go | 2 +- 11 files changed, 130 insertions(+), 130 deletions(-) rename os/gtimer/{gtimer_job.go => gtimer_entry.go} (62%) rename os/gtimer/{gtimer_z_unit_job_test.go => gtimer_z_unit_entry_test.go} (100%) diff --git a/os/gcron/gcron.go b/os/gcron/gcron.go index b3a17f426..7f45c4b2c 100644 --- a/os/gcron/gcron.go +++ b/os/gcron/gcron.go @@ -50,7 +50,7 @@ func GetLogLevel() int { // Add adds a timed task to default cron object. // A unique can be bound with the timed task. // It returns and error if the is already used. -func Add(pattern string, job func(), name ...string) (*Job, error) { +func Add(pattern string, job func(), name ...string) (*Entry, error) { return defaultCron.Add(pattern, job, name...) } @@ -58,21 +58,21 @@ func Add(pattern string, job func(), name ...string) (*Job, error) { // A singleton timed task is that can only be running one single instance at the same time. // A unique can be bound with the timed task. // It returns and error if the is already used. -func AddSingleton(pattern string, job func(), name ...string) (*Job, error) { +func AddSingleton(pattern string, job func(), name ...string) (*Entry, error) { return defaultCron.AddSingleton(pattern, job, name...) } // AddOnce adds a timed task which can be run only once, to default cron object. // A unique can be bound with the timed task. // It returns and error if the is already used. -func AddOnce(pattern string, job func(), name ...string) (*Job, error) { +func AddOnce(pattern string, job func(), name ...string) (*Entry, error) { return defaultCron.AddOnce(pattern, job, name...) } // AddTimes adds a timed task which can be run specified times, to default cron object. // A unique can be bound with the timed task. // It returns and error if the is already used. -func AddTimes(pattern string, times int, job func(), name ...string) (*Job, error) { +func AddTimes(pattern string, times int, job func(), name ...string) (*Entry, error) { return defaultCron.AddTimes(pattern, times, job, name...) } @@ -100,7 +100,7 @@ func DelayAddTimes(delay time.Duration, pattern string, times int, job func(), n // Search returns a scheduled task with the specified . // It returns nil if no found. -func Search(name string) *Job { +func Search(name string) *Entry { return defaultCron.Search(name) } @@ -115,7 +115,7 @@ func Size() int { } // Entries return all timed tasks as slice. -func Entries() []*Job { +func Entries() []*Entry { return defaultCron.Entries() } diff --git a/os/gcron/gcron_cron.go b/os/gcron/gcron_cron.go index de9493fcc..28e2b8522 100644 --- a/os/gcron/gcron_cron.go +++ b/os/gcron/gcron_cron.go @@ -60,20 +60,20 @@ func (c *Cron) GetLogLevel() int { // Add adds a timed task. // A unique can be bound with the timed task. // It returns and error if the is already used. -func (c *Cron) Add(pattern string, job func(), name ...string) (*Job, error) { +func (c *Cron) Add(pattern string, job func(), name ...string) (*Entry, error) { if len(name) > 0 { if c.Search(name[0]) != nil { return nil, errors.New(fmt.Sprintf(`cron job "%s" already exists`, name[0])) } } - return c.addJob(pattern, job, false, name...) + return c.addEntry(pattern, job, false, name...) } // AddSingleton adds a singleton timed task. // A singleton timed task is that can only be running one single instance at the same time. // A unique can be bound with the timed task. // It returns and error if the is already used. -func (c *Cron) AddSingleton(pattern string, job func(), name ...string) (*Job, error) { +func (c *Cron) AddSingleton(pattern string, job func(), name ...string) (*Entry, error) { if entry, err := c.Add(pattern, job, name...); err != nil { return nil, err } else { @@ -85,7 +85,7 @@ func (c *Cron) AddSingleton(pattern string, job func(), name ...string) (*Job, e // AddOnce adds a timed task which can be run only once. // A unique can be bound with the timed task. // It returns and error if the is already used. -func (c *Cron) AddOnce(pattern string, job func(), name ...string) (*Job, error) { +func (c *Cron) AddOnce(pattern string, job func(), name ...string) (*Entry, error) { if entry, err := c.Add(pattern, job, name...); err != nil { return nil, err } else { @@ -97,7 +97,7 @@ func (c *Cron) AddOnce(pattern string, job func(), name ...string) (*Job, error) // AddTimes adds a timed task which can be run specified times. // A unique can be bound with the timed task. // It returns and error if the is already used. -func (c *Cron) AddTimes(pattern string, times int, job func(), name ...string) (*Job, error) { +func (c *Cron) AddTimes(pattern string, times int, job func(), name ...string) (*Entry, error) { if entry, err := c.Add(pattern, job, name...); err != nil { return nil, err } else { @@ -146,9 +146,9 @@ func (c *Cron) DelayAddTimes(delay time.Duration, pattern string, times int, job // Search returns a scheduled task with the specified . // It returns nil if no found. -func (c *Cron) Search(name string) *Job { +func (c *Cron) Search(name string) *Entry { if v := c.entries.Get(name); v != nil { - return v.(*Job) + return v.(*Entry) } return nil } @@ -182,7 +182,7 @@ func (c *Cron) Stop(name ...string) { // Remove deletes scheduled task which named . func (c *Cron) Remove(name string) { if v := c.entries.Get(name); v != nil { - v.(*Job).Close() + v.(*Entry).Close() } } @@ -197,10 +197,10 @@ func (c *Cron) Size() int { } // Entries return all timed tasks as slice(order by registered time asc). -func (c *Cron) Entries() []*Job { +func (c *Cron) Entries() []*Entry { array := garray.NewSortedArraySize(c.entries.Size(), func(v1, v2 interface{}) int { - entry1 := v1.(*Job) - entry2 := v2.(*Job) + entry1 := v1.(*Entry) + entry2 := v2.(*Entry) if entry1.Time.Nanosecond() > entry2.Time.Nanosecond() { return 1 } @@ -208,13 +208,13 @@ func (c *Cron) Entries() []*Job { }, true) c.entries.RLockFunc(func(m map[string]interface{}) { for _, v := range m { - array.Add(v.(*Job)) + array.Add(v.(*Entry)) } }) - entries := make([]*Job, array.Len()) + entries := make([]*Entry, array.Len()) array.RLockFunc(func(array []interface{}) { for k, v := range array { - entries[k] = v.(*Job) + entries[k] = v.(*Entry) } }) return entries diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index e0c33e474..68fd6d14c 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -17,29 +17,29 @@ import ( "github.com/gogf/gf/util/gconv" ) -// Timed task entry. -type Job struct { +// Entry is timing task entry. +type Entry struct { cron *Cron // Cron object belonged to. - job *gtimer.Job // Associated gtimer.Job. + entry *gtimer.Entry // Associated gtimer.Entry. schedule *cronSchedule // Timed schedule object. jobName string // Callback function name(address info). times *gtype.Int // Running times limit. - Name string // Job name. + Name string // Entry name. Job func() `json:"-"` // Callback function. Time time.Time // Registered time. } -// addJob creates and returns a new Job object. +// addEntry creates and returns a new Entry object. // Param is the callback function for timed task execution. // Param specifies whether timed task executing in singleton mode. // Param names this entry for manual control. -func (c *Cron) addJob(pattern string, job func(), singleton bool, name ...string) (*Job, error) { +func (c *Cron) addEntry(pattern string, job func(), singleton bool, name ...string) (*Entry, error) { schedule, err := newSchedule(pattern) if err != nil { return nil, err } // No limit for , for gtimer checking scheduling every second. - entry := &Job{ + entry := &Entry{ cron: c, schedule: schedule, jobName: runtime.FuncForPC(reflect.ValueOf(job).Pointer()).Name(), @@ -57,57 +57,57 @@ func (c *Cron) addJob(pattern string, job func(), singleton bool, name ...string // It should start running after the entry is added to the entries map, // to avoid the task from running during adding where the entries // does not have the entry information, which might cause panic. - entry.job = gtimer.AddJob(time.Second, entry.check, singleton, -1, gtimer.StatusStopped) + entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, -1, gtimer.StatusStopped) c.entries.Set(entry.Name, entry) - entry.job.Start() + entry.entry.Start() return entry, nil } // IsSingleton return whether this entry is a singleton timed task. -func (entry *Job) IsSingleton() bool { - return entry.job.IsSingleton() +func (entry *Entry) IsSingleton() bool { + return entry.entry.IsSingleton() } // SetSingleton sets the entry running in singleton mode. -func (entry *Job) SetSingleton(enabled bool) { - entry.job.SetSingleton(true) +func (entry *Entry) SetSingleton(enabled bool) { + entry.entry.SetSingleton(true) } // SetTimes sets the times which the entry can run. -func (entry *Job) SetTimes(times int) { +func (entry *Entry) SetTimes(times int) { entry.times.Set(times) } // Status returns the status of entry. -func (entry *Job) Status() int { - return entry.job.Status() +func (entry *Entry) Status() int { + return entry.entry.Status() } // SetStatus sets the status of the entry. -func (entry *Job) SetStatus(status int) int { - return entry.job.SetStatus(status) +func (entry *Entry) SetStatus(status int) int { + return entry.entry.SetStatus(status) } // Start starts running the entry. -func (entry *Job) Start() { - entry.job.Start() +func (entry *Entry) Start() { + entry.entry.Start() } // Stop stops running the entry. -func (entry *Job) Stop() { - entry.job.Stop() +func (entry *Entry) Stop() { + entry.entry.Stop() } // Close stops and removes the entry from cron. -func (entry *Job) Close() { +func (entry *Entry) Close() { entry.cron.entries.Remove(entry.Name) - entry.job.Close() + entry.entry.Close() } -// Timed task check execution. -// The running times limits feature is implemented by gcron.Job and cannot be implemented by gtimer.Job. -// gcron.Job relies on gtimer to implement a scheduled task check for gcron.Job per second. -func (entry *Job) check() { +// Timing task check execution. +// The running times limits feature is implemented by gcron.Entry and cannot be implemented by gtimer.Entry. +// gcron.Entry relies on gtimer to implement a scheduled task check for gcron.Entry per second. +func (entry *Entry) check() { if entry.schedule.meet(time.Now()) { path := entry.cron.GetLogPath() level := entry.cron.GetLogLevel() @@ -125,7 +125,7 @@ func (entry *Job) check() { // Running times check. times := entry.times.Add(-1) if times <= 0 { - if entry.job.SetStatus(StatusClosed) == StatusClosed || times < 0 { + if entry.entry.SetStatus(StatusClosed) == StatusClosed || times < 0 { return } } @@ -139,7 +139,7 @@ func (entry *Job) check() { } else { glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s end", entry.Name, entry.schedule.pattern, entry.jobName) } - if entry.job.Status() == StatusClosed { + if entry.entry.Status() == StatusClosed { entry.Close() } }() diff --git a/os/gcron/gcron_unit_2_test.go b/os/gcron/gcron_unit_2_test.go index 6fca7c849..5956dc8bd 100644 --- a/os/gcron/gcron_unit_2_test.go +++ b/os/gcron/gcron_unit_2_test.go @@ -16,7 +16,7 @@ import ( "github.com/gogf/gf/test/gtest" ) -func TestCron_Job_Operations(t *testing.T) { +func TestCron_Entry_Operations(t *testing.T) { gtest.C(t, func(t *gtest.T) { var ( cron = gcron.New() diff --git a/os/gtimer/gtimer.go b/os/gtimer/gtimer.go index 21474a498..3ffd36326 100644 --- a/os/gtimer/gtimer.go +++ b/os/gtimer/gtimer.go @@ -80,11 +80,11 @@ func SetInterval(interval time.Duration, job JobFunc) { } // Add adds a timing job to the default timer, which runs in interval of . -func Add(interval time.Duration, job JobFunc) *Job { +func Add(interval time.Duration, job JobFunc) *Entry { return defaultTimer.Add(interval, job) } -// AddJob adds a timing job to the default timer with detailed parameters. +// AddEntry adds a timing job to the default timer with detailed parameters. // // The parameter specifies the running interval of the job. // @@ -95,22 +95,22 @@ func Add(interval time.Duration, job JobFunc) *Job { // exits if its run times exceeds the . // // The parameter specifies the job status when it's firstly added to the timer. -func AddJob(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Job { - return defaultTimer.AddJob(interval, job, singleton, times, status) +func AddEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry { + return defaultTimer.AddEntry(interval, job, singleton, times, status) } // AddSingleton is a convenience function for add singleton mode job. -func AddSingleton(interval time.Duration, job JobFunc) *Job { +func AddSingleton(interval time.Duration, job JobFunc) *Entry { return defaultTimer.AddSingleton(interval, job) } // AddOnce is a convenience function for adding a job which only runs once and then exits. -func AddOnce(interval time.Duration, job JobFunc) *Job { +func AddOnce(interval time.Duration, job JobFunc) *Entry { return defaultTimer.AddOnce(interval, job) } // AddTimes is a convenience function for adding a job which is limited running times. -func AddTimes(interval time.Duration, times int, job JobFunc) *Job { +func AddTimes(interval time.Duration, times int, job JobFunc) *Entry { return defaultTimer.AddTimes(interval, times, job) } @@ -120,10 +120,10 @@ func DelayAdd(delay time.Duration, interval time.Duration, job JobFunc) { defaultTimer.DelayAdd(delay, interval, job) } -// DelayAddJob adds a timing job after delay of duration. -// Also see AddJob. -func DelayAddJob(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) { - defaultTimer.DelayAddJob(delay, interval, job, singleton, times, status) +// DelayAddEntry adds a timing job after delay of duration. +// Also see AddEntry. +func DelayAddEntry(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) { + defaultTimer.DelayAddEntry(delay, interval, job, singleton, times, status) } // DelayAddSingleton adds a timing job after delay of duration. diff --git a/os/gtimer/gtimer_job.go b/os/gtimer/gtimer_entry.go similarity index 62% rename from os/gtimer/gtimer_job.go rename to os/gtimer/gtimer_entry.go index 39fc938ae..1cfca8d62 100644 --- a/os/gtimer/gtimer_job.go +++ b/os/gtimer/gtimer_entry.go @@ -11,8 +11,8 @@ import ( "math" ) -// Job is the timing job. -type Job struct { +// Entry is the timing job. +type Entry struct { job JobFunc // The job function. timer *Timer // Belonged timer. ticks int64 // The job runs every ticks. @@ -26,21 +26,21 @@ type Job struct { type JobFunc = func() // Status returns the status of the job. -func (j *Job) Status() int { - return j.status.Val() +func (entry *Entry) Status() int { + return entry.status.Val() } // Run runs the timer job asynchronously. -func (j *Job) Run() { - leftRunningTimes := j.times.Add(-1) +func (entry *Entry) Run() { + leftRunningTimes := entry.times.Add(-1) if leftRunningTimes < 0 { - j.status.Set(StatusClosed) + entry.status.Set(StatusClosed) return } // This means it does not limit the running times. // I know it's ugly, but it is surely high performance for running times limit. if leftRunningTimes < 2000000000 && leftRunningTimes > 1000000000 { - j.times.Set(math.MaxInt32) + entry.times.Set(math.MaxInt32) } go func() { defer func() { @@ -48,35 +48,35 @@ func (j *Job) Run() { if err != panicExit { panic(err) } else { - j.Close() + entry.Close() return } } - if j.Status() == StatusRunning { - j.SetStatus(StatusReady) + if entry.Status() == StatusRunning { + entry.SetStatus(StatusReady) } }() - j.job() + entry.job() }() } // doCheckAndRunByTicks checks the if job can run in given timer ticks, // it runs asynchronously if the given `currentTimerTicks` meets or else // it increments its ticks and waits for next running check. -func (j *Job) doCheckAndRunByTicks(currentTimerTicks int64) { +func (entry *Entry) doCheckAndRunByTicks(currentTimerTicks int64) { // Ticks check. - if currentTimerTicks < j.nextTicks.Val() { + if currentTimerTicks < entry.nextTicks.Val() { return } - j.nextTicks.Set(currentTimerTicks + j.ticks) + entry.nextTicks.Set(currentTimerTicks + entry.ticks) // Perform job checking. - switch j.status.Val() { + switch entry.status.Val() { case StatusRunning: - if j.IsSingleton() { + if entry.IsSingleton() { return } case StatusReady: - if !j.status.Cas(StatusReady, StatusRunning) { + if !entry.status.Cas(StatusReady, StatusRunning) { return } case StatusStopped: @@ -85,50 +85,50 @@ func (j *Job) doCheckAndRunByTicks(currentTimerTicks int64) { return } // Perform job running. - j.Run() + entry.Run() } // SetStatus custom sets the status for the job. -func (j *Job) SetStatus(status int) int { - return j.status.Set(status) +func (entry *Entry) SetStatus(status int) int { + return entry.status.Set(status) } // Start starts the job. -func (j *Job) Start() { - j.status.Set(StatusReady) +func (entry *Entry) Start() { + entry.status.Set(StatusReady) } // Stop stops the job. -func (j *Job) Stop() { - j.status.Set(StatusStopped) +func (entry *Entry) Stop() { + entry.status.Set(StatusStopped) } // Close closes the job, and then it will be removed from the timer. -func (j *Job) Close() { - j.status.Set(StatusClosed) +func (entry *Entry) Close() { + entry.status.Set(StatusClosed) } // Reset reset the job, which resets its ticks for next running. -func (j *Job) Reset() { - j.nextTicks.Set(j.timer.ticks.Val() + j.ticks) +func (entry *Entry) Reset() { + entry.nextTicks.Set(entry.timer.ticks.Val() + entry.ticks) } // IsSingleton checks and returns whether the job in singleton mode. -func (j *Job) IsSingleton() bool { - return j.singleton.Val() +func (entry *Entry) IsSingleton() bool { + return entry.singleton.Val() } // SetSingleton sets the job singleton mode. -func (j *Job) SetSingleton(enabled bool) { - j.singleton.Set(enabled) +func (entry *Entry) SetSingleton(enabled bool) { + entry.singleton.Set(enabled) } // Job returns the job function of this job. -func (j *Job) Job() JobFunc { - return j.job +func (entry *Entry) Job() JobFunc { + return entry.job } // SetTimes sets the limit running times for the job. -func (j *Job) SetTimes(times int) { - j.times.Set(times) +func (entry *Entry) SetTimes(times int) { + entry.times.Set(times) } diff --git a/os/gtimer/gtimer_timer.go b/os/gtimer/gtimer_timer.go index 00e700d4d..21bf19112 100644 --- a/os/gtimer/gtimer_timer.go +++ b/os/gtimer/gtimer_timer.go @@ -27,11 +27,11 @@ func New(options ...TimerOptions) *Timer { } // Add adds a timing job to the timer, which runs in interval of . -func (t *Timer) Add(interval time.Duration, job JobFunc) *Job { - return t.createJob(interval, job, false, defaultTimes, StatusReady) +func (t *Timer) Add(interval time.Duration, job JobFunc) *Entry { + return t.createEntry(interval, job, false, defaultTimes, StatusReady) } -// AddJob adds a timing job to the timer with detailed parameters. +// AddEntry adds a timing job to the timer with detailed parameters. // // The parameter specifies the running interval of the job. // @@ -42,23 +42,23 @@ func (t *Timer) Add(interval time.Duration, job JobFunc) *Job { // exits if its run times exceeds the . // // The parameter specifies the job status when it's firstly added to the timer. -func (t *Timer) AddJob(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Job { - return t.createJob(interval, job, singleton, times, status) +func (t *Timer) AddEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry { + return t.createEntry(interval, job, singleton, times, status) } // AddSingleton is a convenience function for add singleton mode job. -func (t *Timer) AddSingleton(interval time.Duration, job JobFunc) *Job { - return t.createJob(interval, job, true, defaultTimes, StatusReady) +func (t *Timer) AddSingleton(interval time.Duration, job JobFunc) *Entry { + return t.createEntry(interval, job, true, defaultTimes, StatusReady) } // AddOnce is a convenience function for adding a job which only runs once and then exits. -func (t *Timer) AddOnce(interval time.Duration, job JobFunc) *Job { - return t.createJob(interval, job, true, 1, StatusReady) +func (t *Timer) AddOnce(interval time.Duration, job JobFunc) *Entry { + return t.createEntry(interval, job, true, 1, StatusReady) } // AddTimes is a convenience function for adding a job which is limited running times. -func (t *Timer) AddTimes(interval time.Duration, times int, job JobFunc) *Job { - return t.createJob(interval, job, true, times, StatusReady) +func (t *Timer) AddTimes(interval time.Duration, times int, job JobFunc) *Entry { + return t.createEntry(interval, job, true, times, StatusReady) } // DelayAdd adds a timing job after delay of duration. @@ -69,11 +69,11 @@ func (t *Timer) DelayAdd(delay time.Duration, interval time.Duration, job JobFun }) } -// DelayAddJob adds a timing job after delay of duration. -// Also see AddJob. -func (t *Timer) DelayAddJob(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) { +// DelayAddEntry adds a timing job after delay of duration. +// Also see AddEntry. +func (t *Timer) DelayAddEntry(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) { t.AddOnce(delay, func() { - t.AddJob(interval, job, singleton, times, status) + t.AddEntry(interval, job, singleton, times, status) }) } @@ -116,8 +116,8 @@ func (t *Timer) Close() { t.status.Set(StatusClosed) } -// createJob creates and adds a timing job to the timer. -func (t *Timer) createJob(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Job { +// createEntry creates and adds a timing job to the timer. +func (t *Timer) createEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry { if times <= 0 { times = defaultTimes } @@ -130,7 +130,7 @@ func (t *Timer) createJob(interval time.Duration, job JobFunc, singleton bool, t intervalTicksOfJob = 1 } nextTicks := t.ticks.Val() + intervalTicksOfJob - j := &Job{ + entry := &Entry{ job: job, timer: t, ticks: intervalTicksOfJob, @@ -139,6 +139,6 @@ func (t *Timer) createJob(interval time.Duration, job JobFunc, singleton bool, t singleton: gtype.NewBool(singleton), nextTicks: gtype.NewInt64(nextTicks), } - t.queue.Push(j, nextTicks) - return j + t.queue.Push(entry, nextTicks) + return entry } diff --git a/os/gtimer/gtimer_timer_loop.go b/os/gtimer/gtimer_timer_loop.go index d2e9909d7..61e2ee672 100644 --- a/os/gtimer/gtimer_timer_loop.go +++ b/os/gtimer/gtimer_timer_loop.go @@ -50,19 +50,19 @@ func (t *Timer) proceed(currentTimerTicks int64) { if value == nil { break } - job := value.(*Job) + entry := value.(*Entry) // It checks if it meets the ticks requirement. - if jobNextTicks := job.nextTicks.Val(); currentTimerTicks < jobNextTicks { + if jobNextTicks := entry.nextTicks.Val(); currentTimerTicks < jobNextTicks { // It push the job back if current ticks does not meet its running ticks requirement. - t.queue.Push(job, job.nextTicks.Val()) + t.queue.Push(entry, entry.nextTicks.Val()) break } // It checks the job running requirements and then does asynchronous running. - job.doCheckAndRunByTicks(currentTimerTicks) + entry.doCheckAndRunByTicks(currentTimerTicks) // Status check: push back or ignore it. - if job.Status() != StatusClosed { + if entry.Status() != StatusClosed { // It pushes the job back to queue for next running. - t.queue.Push(job, job.nextTicks.Val()) + t.queue.Push(entry, entry.nextTicks.Val()) } } } diff --git a/os/gtimer/gtimer_z_unit_api_test.go b/os/gtimer/gtimer_z_unit_api_test.go index 60fdb0837..debc25d9c 100644 --- a/os/gtimer/gtimer_z_unit_api_test.go +++ b/os/gtimer/gtimer_z_unit_api_test.go @@ -39,10 +39,10 @@ func TestSetInterval(t *testing.T) { }) } -func TestAddJob(t *testing.T) { +func TestAddEntry(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := garray.New(true) - gtimer.AddJob(200*time.Millisecond, func() { + gtimer.AddEntry(200*time.Millisecond, func() { array.Append(1) }, false, 2, gtimer.StatusReady) time.Sleep(1100 * time.Millisecond) @@ -86,10 +86,10 @@ func TestDelayAdd(t *testing.T) { }) } -func TestDelayAddJob(t *testing.T) { +func TestDelayAddEntry(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := garray.New(true) - gtimer.DelayAddJob(200*time.Millisecond, 200*time.Millisecond, func() { + gtimer.DelayAddEntry(200*time.Millisecond, 200*time.Millisecond, func() { array.Append(1) }, false, 2, gtimer.StatusReady) time.Sleep(300 * time.Millisecond) diff --git a/os/gtimer/gtimer_z_unit_job_test.go b/os/gtimer/gtimer_z_unit_entry_test.go similarity index 100% rename from os/gtimer/gtimer_z_unit_job_test.go rename to os/gtimer/gtimer_z_unit_entry_test.go diff --git a/os/gtimer/gtimer_z_unit_timer_test.go b/os/gtimer/gtimer_z_unit_timer_test.go index 7be8eeef5..3faf7e908 100644 --- a/os/gtimer/gtimer_z_unit_timer_test.go +++ b/os/gtimer/gtimer_z_unit_timer_test.go @@ -158,7 +158,7 @@ func TestTimer_DelayAddJob(t *testing.T) { gtest.C(t, func(t *gtest.T) { timer := New() array := garray.New(true) - timer.DelayAddJob(200*time.Millisecond, 200*time.Millisecond, func() { + timer.DelayAddEntry(200*time.Millisecond, 200*time.Millisecond, func() { array.Append(1) }, false, 100, gtimer.StatusReady) time.Sleep(250 * time.Millisecond) From 8acb921ee3cc17ed79a7801f815de49f3d4feda1 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 27 May 2021 13:10:10 +0800 Subject: [PATCH 291/492] improve package gtimer for times limitation --- os/gtimer/gtimer_entry.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go index 1cfca8d62..beebf21a6 100644 --- a/os/gtimer/gtimer_entry.go +++ b/os/gtimer/gtimer_entry.go @@ -33,13 +33,13 @@ func (entry *Entry) Status() int { // Run runs the timer job asynchronously. func (entry *Entry) Run() { leftRunningTimes := entry.times.Add(-1) + // Running times exceeding checks. if leftRunningTimes < 0 { entry.status.Set(StatusClosed) return } // This means it does not limit the running times. - // I know it's ugly, but it is surely high performance for running times limit. - if leftRunningTimes < 2000000000 && leftRunningTimes > 1000000000 { + if leftRunningTimes == math.MaxInt32-1 { entry.times.Set(math.MaxInt32) } go func() { From 5100e0e8b74e8e867fe21f7e9947c156d8009684 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 27 May 2021 22:18:16 +0800 Subject: [PATCH 292/492] add sub-query feature for orm --- database/gdb/gdb.go | 4 +- database/gdb/gdb_func.go | 35 ++++++- database/gdb/gdb_model.go | 77 ++++++++------ database/gdb/gdb_model_condition.go | 108 ++++++++++++++++++++ database/gdb/gdb_model_select.go | 71 ++++++++----- database/gdb/gdb_model_utility.go | 116 ++-------------------- database/gdb/gdb_z_mysql_subquery_test.go | 66 ++++++++++++ 7 files changed, 305 insertions(+), 172 deletions(-) create mode 100644 database/gdb/gdb_z_mysql_subquery_test.go diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 5b18d5639..726f9ae43 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -274,12 +274,12 @@ var ( // regularFieldNameRegPattern is the regular expression pattern for a string // which is a regular field name of table. - regularFieldNameRegPattern = `^[\w\.\-\_]+$` + regularFieldNameRegPattern = `^[\w\.\-]+$` // regularFieldNameWithoutDotRegPattern is similar to regularFieldNameRegPattern but not allows '.'. // Note that, although some databases allow char '.' in the field name, but it here does not allow '.' // in the field name as it conflicts with "db.table.field" pattern in SOME situations. - regularFieldNameWithoutDotRegPattern = `^[\w\-\_]+$` + regularFieldNameWithoutDotRegPattern = `^[\w\-]+$` // internalCache is the memory cache for internal usage. internalCache = gcache.New() diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 13db19d09..fc2c4d1ca 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -444,14 +444,14 @@ func formatSql(sql string, args []interface{}) (newSql string, newArgs []interfa return handleArguments(sql, args) } -// formatWhere formats where statement and its arguments. +// formatWhere formats where statement and its arguments for `Where` and `Having` statements. func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (newWhere string, newArgs []interface{}) { var ( buffer = bytes.NewBuffer(nil) rv = reflect.ValueOf(where) kind = rv.Kind() ) - if kind == reflect.Ptr { + for kind == reflect.Ptr { rv = rv.Elem() kind = rv.Kind() } @@ -491,7 +491,36 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) ( } default: - buffer.WriteString(gconv.String(where)) + // Usually a string. + var ( + i = 0 + whereStr = gconv.String(where) + ) + for { + if i >= len(args) { + break + } + // Sub query, which is always used along with a string condition. + if model, ok := args[i].(*Model); ok { + var ( + index = -1 + ) + whereStr, _ = gregex.ReplaceStringFunc(`(\?)`, whereStr, func(s string) string { + index++ + if i+len(newArgs) == index { + sqlWithHolder, holderArgs := model.getFormattedSqlAndArgs(queryTypeNormal, false) + newArgs = append(newArgs, holderArgs...) + // Automatically adding the brackets. + return "(" + sqlWithHolder + ")" + } + return s + }) + args = gutil.SliceDelete(args, i) + continue + } + i++ + } + buffer.WriteString(whereStr) } if buffer.Len() == 0 { diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 45084a106..627051adb 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -9,6 +9,7 @@ package gdb import ( "context" "fmt" + "github.com/gogf/gf/util/gconv" "time" "github.com/gogf/gf/text/gregex" @@ -28,7 +29,7 @@ type Model struct { fieldsEx string // Excluded operation fields, multiple fields joined using char ','. withArray []interface{} // Arguments for With feature. withAll bool // Enable model association operations on all objects that have "with" tag in the struct. - extraArgs []interface{} // Extra custom arguments for sql. + extraArgs []interface{} // Extra custom arguments for sql, which are prepended to the arguments before sql committed to underlying driver. whereHolder []*whereHolder // Condition strings for where operation. groupBy string // Used for "group by" statement. orderBy string // Used for "order by" statement. @@ -57,52 +58,63 @@ type whereHolder struct { } const ( - OPTION_OMITEMPTY = 1 // Deprecated, use OptionOmitEmpty instead. - OPTION_ALLOWEMPTY = 2 // Deprecated, use OptionAllowEmpty instead. - OptionOmitEmpty = 1 - OptionAllowEmpty = 2 - linkTypeMaster = 1 - linkTypeSlave = 2 - whereHolderWhere = 1 - whereHolderAnd = 2 - whereHolderOr = 3 + OptionOmitEmpty = 1 + OptionAllowEmpty = 2 + linkTypeMaster = 1 + linkTypeSlave = 2 + whereHolderWhere = 1 + whereHolderAnd = 2 + whereHolderOr = 3 ) // Table is alias of Core.Model. // See Core.Model. // Deprecated, use Model instead. -func (c *Core) Table(tableNameOrStruct ...interface{}) *Model { - return c.db.Model(tableNameOrStruct...) +func (c *Core) Table(tableNameQueryOrStruct ...interface{}) *Model { + return c.db.Model(tableNameQueryOrStruct...) } // Model creates and returns a new ORM model from given schema. -// The parameter `tableNameOrStruct` can be more than one table names, and also alias name, like: +// The parameter `tableNameQueryOrStruct` can be more than one table names, and also alias name, like: // 1. Model names: // Model("user") // Model("user u") // Model("user, user_detail") // Model("user u, user_detail ud") // 2. Model name with alias: Model("user", "u") -func (c *Core) Model(tableNameOrStruct ...interface{}) *Model { +func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { var ( - tableStr = "" - tableName = "" - tableNames = make([]string, len(tableNameOrStruct)) + tableStr string + tableName string + extraArgs []interface{} + tableNames = make([]string, len(tableNameQueryOrStruct)) ) - for k, v := range tableNameOrStruct { - if s, ok := v.(string); ok { - tableNames[k] = s - } else if tableName = getTableNameFromOrmTag(v); tableName != "" { - tableNames[k] = tableName + // Model creation with sub-query. + if len(tableNameQueryOrStruct) > 1 { + conditionStr := gconv.String(tableNameQueryOrStruct[0]) + if gstr.Contains(conditionStr, "?") { + tableStr, extraArgs = formatWhere( + c.db, conditionStr, tableNameQueryOrStruct[1:], false, + ) } } + // Normal model creation. + if tableStr == "" { + for k, v := range tableNameQueryOrStruct { + if s, ok := v.(string); ok { + tableNames[k] = s + } else if tableName = getTableNameFromOrmTag(v); tableName != "" { + tableNames[k] = tableName + } + } - if len(tableNames) > 1 { - tableStr = fmt.Sprintf( - `%s AS %s`, c.QuotePrefixTableName(tableNames[0]), c.QuoteWord(tableNames[1]), - ) - } else if len(tableNames) == 1 { - tableStr = c.QuotePrefixTableName(tableNames[0]) + if len(tableNames) > 1 { + tableStr = fmt.Sprintf( + `%s AS %s`, c.QuotePrefixTableName(tableNames[0]), c.QuoteWord(tableNames[1]), + ) + } else if len(tableNames) == 1 { + tableStr = c.QuotePrefixTableName(tableNames[0]) + } } return &Model{ db: c.db, @@ -113,6 +125,7 @@ func (c *Core) Model(tableNameOrStruct ...interface{}) *Model { offset: -1, option: OptionAllowEmpty, filter: true, + extraArgs: extraArgs, } } @@ -123,14 +136,14 @@ func (c *Core) With(objects ...interface{}) *Model { // Table is alias of tx.Model. // Deprecated, use Model instead. -func (tx *TX) Table(tableNameOrStruct ...interface{}) *Model { - return tx.Model(tableNameOrStruct...) +func (tx *TX) Table(tableNameQueryOrStruct ...interface{}) *Model { + return tx.Model(tableNameQueryOrStruct...) } // Model acts like Core.Model except it operates on transaction. // See Core.Model. -func (tx *TX) Model(tableNameOrStruct ...interface{}) *Model { - model := tx.db.Model(tableNameOrStruct...) +func (tx *TX) Model(tableNameQueryOrStruct ...interface{}) *Model { + model := tx.db.Model(tableNameQueryOrStruct...) model.db = tx.db model.tx = tx return model diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 641fee5d7..5135bd3a5 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -8,6 +8,7 @@ package gdb import ( "fmt" + "github.com/gogf/gf/util/gconv" "strings" ) @@ -303,3 +304,110 @@ func (m *Model) Page(page, limit int) *Model { func (m *Model) ForPage(page, limit int) *Model { return m.Page(page, limit) } + +// formatCondition formats where arguments of the model and returns a new condition sql and its arguments. +// Note that this function does not change any attribute value of the `m`. +// +// The parameter `limit1` specifies whether limits querying only one record if m.limit is not set. +func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) { + if len(m.whereHolder) > 0 { + for _, v := range m.whereHolder { + switch v.operator { + case whereHolderWhere: + if conditionWhere == "" { + newWhere, newArgs := formatWhere( + m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, + ) + if len(newWhere) > 0 { + conditionWhere = newWhere + conditionArgs = newArgs + } + continue + } + fallthrough + + case whereHolderAnd: + newWhere, newArgs := formatWhere( + m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, + ) + if len(newWhere) > 0 { + if len(conditionWhere) == 0 { + conditionWhere = newWhere + } else if conditionWhere[0] == '(' { + conditionWhere = fmt.Sprintf(`%s AND (%s)`, conditionWhere, newWhere) + } else { + conditionWhere = fmt.Sprintf(`(%s) AND (%s)`, conditionWhere, newWhere) + } + conditionArgs = append(conditionArgs, newArgs...) + } + + case whereHolderOr: + newWhere, newArgs := formatWhere( + m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, + ) + if len(newWhere) > 0 { + if len(conditionWhere) == 0 { + conditionWhere = newWhere + } else if conditionWhere[0] == '(' { + conditionWhere = fmt.Sprintf(`%s OR (%s)`, conditionWhere, newWhere) + } else { + conditionWhere = fmt.Sprintf(`(%s) OR (%s)`, conditionWhere, newWhere) + } + conditionArgs = append(conditionArgs, newArgs...) + } + } + } + } + // Soft deletion. + softDeletingCondition := m.getConditionForSoftDeleting() + if !m.unscoped && softDeletingCondition != "" { + if conditionWhere == "" { + conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition) + } else { + conditionWhere = fmt.Sprintf(` WHERE (%s) AND %s`, conditionWhere, softDeletingCondition) + } + } else { + if conditionWhere != "" { + conditionWhere = " WHERE " + conditionWhere + } + } + // GROUP BY. + if m.groupBy != "" { + conditionExtra += " GROUP BY " + m.groupBy + } + // HAVING. + if len(m.having) > 0 { + havingStr, havingArgs := formatWhere( + m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0, + ) + if len(havingStr) > 0 { + conditionExtra += " HAVING " + havingStr + conditionArgs = append(conditionArgs, havingArgs...) + } + } + // ORDER BY. + if m.orderBy != "" { + conditionExtra += " ORDER BY " + m.orderBy + } + // LIMIT. + if !isCountStatement { + if m.limit != 0 { + if m.start >= 0 { + conditionExtra += fmt.Sprintf(" LIMIT %d,%d", m.start, m.limit) + } else { + conditionExtra += fmt.Sprintf(" LIMIT %d", m.limit) + } + } else if limit1 { + conditionExtra += " LIMIT 1" + } + + if m.offset >= 0 { + conditionExtra += fmt.Sprintf(" OFFSET %d", m.offset) + } + } + + if m.lockInfo != "" { + conditionExtra += " " + m.lockInfo + } + return +} diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 3536174dc..041cb65e9 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -18,6 +18,11 @@ import ( "github.com/gogf/gf/util/gconv" ) +const ( + queryTypeNormal = "NormalQuery" + queryTypeCount = "CountQuery" +) + // Select is alias of Model.All. // See Model.All. // Deprecated, use All instead. @@ -46,19 +51,8 @@ func (m *Model) doGetAll(limit1 bool, where ...interface{}) (Result, error) { if len(where) > 0 { return m.Where(where[0], where[1:]...).All() } - conditionWhere, conditionExtra, conditionArgs := m.formatCondition(limit1, false) - // DO NOT quote the m.fields where, in case of fields like: - // DISTINCT t.user_id uid - return m.doGetAllBySql( - fmt.Sprintf( - "SELECT %s%s FROM %s%s", - m.distinct, - m.getFieldsFiltered(), - m.tables, - conditionWhere+conditionExtra, - ), - conditionArgs..., - ) + sqlWithHolder, holderArgs := m.getFormattedSqlAndArgs(queryTypeNormal, limit1) + return m.doGetAllBySql(sqlWithHolder, holderArgs...) } // getFieldsFiltered checks the fields and fieldsEx attributes, filters and returns the fields that will @@ -332,18 +326,10 @@ func (m *Model) Count(where ...interface{}) (int, error) { if len(where) > 0 { return m.Where(where[0], where[1:]...).Count() } - countFields := "COUNT(1)" - if m.fields != "" && m.fields != "*" { - // DO NOT quote the m.fields here, in case of fields like: - // DISTINCT t.user_id uid - countFields = fmt.Sprintf(`COUNT(%s%s)`, m.distinct, m.fields) - } - conditionWhere, conditionExtra, conditionArgs := m.formatCondition(false, true) - s := fmt.Sprintf("SELECT %s FROM %s%s", countFields, m.tables, conditionWhere+conditionExtra) - if len(m.groupBy) > 0 { - s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s) - } - list, err := m.doGetAllBySql(s, conditionArgs...) + var ( + sqlWithHolder, holderArgs = m.getFormattedSqlAndArgs(queryTypeCount, false) + list, err = m.doGetAllBySql(sqlWithHolder, holderArgs...) + ) if err != nil { return 0, err } @@ -497,7 +483,9 @@ func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, e } } } - result, err = m.db.GetCore().DoGetAll(m.GetCtx(), m.getLink(false), sql, m.mergeArguments(args)...) + result, err = m.db.GetCore().DoGetAll( + m.GetCtx(), m.getLink(false), sql, m.mergeArguments(args)..., + ) // Cache the result. if cacheKey != "" && err == nil { if m.cacheDuration < 0 { @@ -512,3 +500,34 @@ func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, e } return result, err } + +func (m *Model) getFormattedSqlAndArgs(queryType string, limit1 bool) (sqlWithHolder string, holderArgs []interface{}) { + switch queryType { + case queryTypeCount: + countFields := "COUNT(1)" + if m.fields != "" && m.fields != "*" { + // DO NOT quote the m.fields here, in case of fields like: + // DISTINCT t.user_id uid + countFields = fmt.Sprintf(`COUNT(%s%s)`, m.distinct, m.fields) + } + conditionWhere, conditionExtra, conditionArgs := m.formatCondition(false, true) + sqlWithHolder = fmt.Sprintf("SELECT %s FROM %s%s", countFields, m.tables, conditionWhere+conditionExtra) + if len(m.groupBy) > 0 { + sqlWithHolder = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", sqlWithHolder) + } + return sqlWithHolder, conditionArgs + + default: + conditionWhere, conditionExtra, conditionArgs := m.formatCondition(limit1, false) + // DO NOT quote the m.fields where, in case of fields like: + // DISTINCT t.user_id uid + sqlWithHolder = fmt.Sprintf( + "SELECT %s%s FROM %s%s", + m.distinct, + m.getFieldsFiltered(), + m.tables, + conditionWhere+conditionExtra, + ) + return sqlWithHolder, conditionArgs + } +} diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 362219965..bf29b0a9c 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -7,7 +7,6 @@ package gdb import ( - "fmt" "time" "github.com/gogf/gf/container/gset" @@ -15,7 +14,6 @@ import ( "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gutil" ) @@ -24,6 +22,13 @@ import ( // // Also see DriverMysql.TableFields. func (m *Model) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) { + charL, charR := m.db.GetChars() + if charL != "" || charR != "" { + table = gstr.Trim(table, charL+charR) + } + if !gregex.IsMatchString(regularFieldNameRegPattern, table) { + return nil, nil + } return m.db.TableFields(m.GetCtx(), m.getLink(false), table, schema...) } @@ -209,113 +214,6 @@ func (m *Model) getPrimaryKey() string { return "" } -// formatCondition formats where arguments of the model and returns a new condition sql and its arguments. -// Note that this function does not change any attribute value of the `m`. -// -// The parameter `limit1` specifies whether limits querying only one record if m.limit is not set. -func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) { - if len(m.whereHolder) > 0 { - for _, v := range m.whereHolder { - switch v.operator { - case whereHolderWhere: - if conditionWhere == "" { - newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, - ) - if len(newWhere) > 0 { - conditionWhere = newWhere - conditionArgs = newArgs - } - continue - } - fallthrough - - case whereHolderAnd: - newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, - ) - if len(newWhere) > 0 { - if len(conditionWhere) == 0 { - conditionWhere = newWhere - } else if conditionWhere[0] == '(' { - conditionWhere = fmt.Sprintf(`%s AND (%s)`, conditionWhere, newWhere) - } else { - conditionWhere = fmt.Sprintf(`(%s) AND (%s)`, conditionWhere, newWhere) - } - conditionArgs = append(conditionArgs, newArgs...) - } - - case whereHolderOr: - newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, - ) - if len(newWhere) > 0 { - if len(conditionWhere) == 0 { - conditionWhere = newWhere - } else if conditionWhere[0] == '(' { - conditionWhere = fmt.Sprintf(`%s OR (%s)`, conditionWhere, newWhere) - } else { - conditionWhere = fmt.Sprintf(`(%s) OR (%s)`, conditionWhere, newWhere) - } - conditionArgs = append(conditionArgs, newArgs...) - } - } - } - } - // Soft deletion. - softDeletingCondition := m.getConditionForSoftDeleting() - if !m.unscoped && softDeletingCondition != "" { - if conditionWhere == "" { - conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition) - } else { - conditionWhere = fmt.Sprintf(` WHERE (%s) AND %s`, conditionWhere, softDeletingCondition) - } - } else { - if conditionWhere != "" { - conditionWhere = " WHERE " + conditionWhere - } - } - // GROUP BY. - if m.groupBy != "" { - conditionExtra += " GROUP BY " + m.groupBy - } - // HAVING. - if len(m.having) > 0 { - havingStr, havingArgs := formatWhere( - m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0, - ) - if len(havingStr) > 0 { - conditionExtra += " HAVING " + havingStr - conditionArgs = append(conditionArgs, havingArgs...) - } - } - // ORDER BY. - if m.orderBy != "" { - conditionExtra += " ORDER BY " + m.orderBy - } - // LIMIT. - if !isCountStatement { - if m.limit != 0 { - if m.start >= 0 { - conditionExtra += fmt.Sprintf(" LIMIT %d,%d", m.start, m.limit) - } else { - conditionExtra += fmt.Sprintf(" LIMIT %d", m.limit) - } - } else if limit1 { - conditionExtra += " LIMIT 1" - } - - if m.offset >= 0 { - conditionExtra += fmt.Sprintf(" OFFSET %d", m.offset) - } - } - - if m.lockInfo != "" { - conditionExtra += " " + m.lockInfo - } - return -} - // mergeArguments creates and returns new arguments by merging and given `args`. func (m *Model) mergeArguments(args []interface{}) []interface{} { if len(m.extraArgs) > 0 { diff --git a/database/gdb/gdb_z_mysql_subquery_test.go b/database/gdb/gdb_z_mysql_subquery_test.go new file mode 100644 index 000000000..2f292085b --- /dev/null +++ b/database/gdb/gdb_z_mysql_subquery_test.go @@ -0,0 +1,66 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb_test + +import ( + "github.com/gogf/gf/frame/g" + "testing" + + "github.com/gogf/gf/test/gtest" +) + +func Test_Model_SubQuery_Where(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table).Where( + "id in ?", + db.Model(table).Fields("id").Where("id", g.Slice{1, 3, 5}), + ).OrderAsc("id").All() + t.AssertNil(err) + + t.Assert(len(r), 3) + t.Assert(r[0]["id"], 1) + t.Assert(r[1]["id"], 3) + t.Assert(r[2]["id"], 5) + }) +} + +func Test_Model_SubQuery_Having(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table).Where( + "id in ?", + db.Model(table).Fields("id").Where("id", g.Slice{1, 3, 5}), + ).Having( + "id > ?", + db.Model(table).Fields("MAX(id)").Where("id", g.Slice{1, 3}), + ).OrderAsc("id").All() + t.AssertNil(err) + + t.Assert(len(r), 1) + t.Assert(r[0]["id"], 5) + }) +} + +func Test_Model_SubQuery_Model(t *testing.T) { + table := createInitTable() + defer dropTable(table) + db.SetDebug(true) + gtest.C(t, func(t *gtest.T) { + subQuery1 := db.Model(table).Where("id", g.Slice{1, 3, 5}) + subQuery2 := db.Model(table).Where("id", g.Slice{5, 7, 9}) + r, err := db.Model("? AS a, ? AS b", subQuery1, subQuery2).Fields("a.id").Where("a.id=b.id").OrderAsc("id").All() + t.AssertNil(err) + + t.Assert(len(r), 1) + t.Assert(r[0]["id"], 5) + }) +} From b2d3c7d1fd7b6c88db1dfdadd4fb331f6528edbc Mon Sep 17 00:00:00 2001 From: wanna Date: Fri, 28 May 2021 11:34:51 +0800 Subject: [PATCH 293/492] "gvalid length" compatible with only one parameter --- util/gvalid/gvalid_validator_rule_length.go | 2 ++ util/gvalid/gvalid_z_unit_basic_all_test.go | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/util/gvalid/gvalid_validator_rule_length.go b/util/gvalid/gvalid_validator_rule_length.go index 33edfa97a..d692cc0bd 100644 --- a/util/gvalid/gvalid_validator_rule_length.go +++ b/util/gvalid/gvalid_validator_rule_length.go @@ -31,6 +31,8 @@ func (v *Validator) checkLength(value, ruleKey, ruleVal string, customMsgMap map if len(array) > 0 { if v, err := strconv.Atoi(strings.TrimSpace(array[0])); err == nil { min = v + // compatible with only one parameter + max = v } } if len(array) > 1 { diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index fa687880c..7bfd14a30 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -658,6 +658,17 @@ func Test_Length(t *testing.T) { if m := gvalid.CheckValue(context.TODO(), "12345", rule, nil); m == nil { t.Error("长度校验失败") } + // only one parameter + rule2 := "length:5" + if m := gvalid.CheckValue(context.TODO(), "1234", rule2, nil); m == nil { + t.Error(m) + } + if m := gvalid.CheckValue(context.TODO(), "12345", rule2, nil); m != nil { + t.Error(m) + } + if m := gvalid.CheckValue(context.TODO(), "123456", rule2, nil); m == nil { + t.Error("长度校验失败") + } } func Test_MinLength(t *testing.T) { From d7b8a2684a0efd718a5e28aec6ca9928090b0a09 Mon Sep 17 00:00:00 2001 From: wanna Date: Sat, 29 May 2021 11:28:26 +0800 Subject: [PATCH 294/492] Revert ""gvalid length" compatible with only one parameter" This reverts commit b2d3c7d1fd7b6c88db1dfdadd4fb331f6528edbc. --- util/gvalid/gvalid_validator_rule_length.go | 2 -- util/gvalid/gvalid_z_unit_basic_all_test.go | 11 ----------- 2 files changed, 13 deletions(-) diff --git a/util/gvalid/gvalid_validator_rule_length.go b/util/gvalid/gvalid_validator_rule_length.go index d692cc0bd..33edfa97a 100644 --- a/util/gvalid/gvalid_validator_rule_length.go +++ b/util/gvalid/gvalid_validator_rule_length.go @@ -31,8 +31,6 @@ func (v *Validator) checkLength(value, ruleKey, ruleVal string, customMsgMap map if len(array) > 0 { if v, err := strconv.Atoi(strings.TrimSpace(array[0])); err == nil { min = v - // compatible with only one parameter - max = v } } if len(array) > 1 { diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index 7bfd14a30..fa687880c 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -658,17 +658,6 @@ func Test_Length(t *testing.T) { if m := gvalid.CheckValue(context.TODO(), "12345", rule, nil); m == nil { t.Error("长度校验失败") } - // only one parameter - rule2 := "length:5" - if m := gvalid.CheckValue(context.TODO(), "1234", rule2, nil); m == nil { - t.Error(m) - } - if m := gvalid.CheckValue(context.TODO(), "12345", rule2, nil); m != nil { - t.Error(m) - } - if m := gvalid.CheckValue(context.TODO(), "123456", rule2, nil); m == nil { - t.Error("长度校验失败") - } } func Test_MinLength(t *testing.T) { From fa1814ff5405d7ab772662b5b299c240faf0341f Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 29 May 2021 11:30:34 +0800 Subject: [PATCH 295/492] add custom rule fucntions feature for package gvalid --- .example/i18n/gi18n/http_view_i18n.go | 2 +- .example/i18n/gi18n/i18n/zh-CN.toml | 4 +- util/gvalid/gvalid_custom_rule.go | 6 +- util/gvalid/gvalid_validator.go | 47 ++++-- util/gvalid/gvalid_validator_check_map.go | 4 +- util/gvalid/gvalid_validator_check_struct.go | 18 ++- ...eck.go => gvalid_validator_check_value.go} | 53 ++++--- util/gvalid/gvalid_z_example_test.go | 33 ++-- util/gvalid/gvalid_z_unit_custom_rule_test.go | 143 ++++++++++++++++-- 9 files changed, 236 insertions(+), 74 deletions(-) rename util/gvalid/{gvalid_validator_check.go => gvalid_validator_check_value.go} (87%) diff --git a/.example/i18n/gi18n/http_view_i18n.go b/.example/i18n/gi18n/http_view_i18n.go index 1f1cc2302..cbf0b6e36 100644 --- a/.example/i18n/gi18n/http_view_i18n.go +++ b/.example/i18n/gi18n/http_view_i18n.go @@ -10,7 +10,7 @@ func main() { s := g.Server() s.Group("/", func(group *ghttp.RouterGroup) { group.Middleware(func(r *ghttp.Request) { - r.SetCtx(gi18n.WithLanguage(r.Context(), "zh-CN")) + r.SetCtx(gi18n.WithLanguage(r.Context(), r.GetString("lang", "zh-CN"))) r.Middleware.Next() }) group.ALL("/", func(r *ghttp.Request) { diff --git a/.example/i18n/gi18n/i18n/zh-CN.toml b/.example/i18n/gi18n/i18n/zh-CN.toml index 80acf06de..20406d93a 100644 --- a/.example/i18n/gi18n/i18n/zh-CN.toml +++ b/.example/i18n/gi18n/i18n/zh-CN.toml @@ -1 +1,3 @@ -OrderPaid = "您已成功完成订单号 #%d 支付,支付金额¥%.2f。" \ No newline at end of file +OrderPaid = "您已成功完成订单号 #%d 支付,支付金额¥%.2f。" +hello = "你好" +world = "世界" \ No newline at end of file diff --git a/util/gvalid/gvalid_custom_rule.go b/util/gvalid/gvalid_custom_rule.go index 85e431f58..d1e3589bb 100644 --- a/util/gvalid/gvalid_custom_rule.go +++ b/util/gvalid/gvalid_custom_rule.go @@ -12,9 +12,9 @@ import "context" // The parameter `rule` specifies the validation rule string, like "required", "between:1,100", etc. // The parameter `value` specifies the value for this rule to validate. // The parameter `message` specifies the custom error message or configured i18n message for this rule. -// The parameter `params` specifies all the parameters that needs. You can ignore parameter `params` if -// you do not really need it in your custom validation rule. -type RuleFunc func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error +// The parameter `data` specifies the `data` which is passed to the Validator. It might be type of map/struct or a nil value. +// You can ignore the parameter `data` if you do not really need it in your custom validation rule. +type RuleFunc func(ctx context.Context, rule string, value interface{}, message string, data interface{}) error var ( // customRuleFuncMap stores the custom rule functions. diff --git a/util/gvalid/gvalid_validator.go b/util/gvalid/gvalid_validator.go index a1592e362..e46c70295 100644 --- a/util/gvalid/gvalid_validator.go +++ b/util/gvalid/gvalid_validator.go @@ -13,21 +13,23 @@ import ( // Validator is the validation manager for chaining operations. type Validator struct { - ctx context.Context // Context containing custom context variables. - i18nManager *gi18n.Manager // I18n manager for error message translation. - key string // Single validation key. - value interface{} // Single validation value. - data interface{} // Validation data, which is usually a map. - rules interface{} // Custom validation data. - messages interface{} // Custom validation error messages, which can be string or type of CustomMsg. - useDataInsteadOfObjectAttributes bool // Using `data` as its validation source instead of attribute values from `Object`. + ctx context.Context // Context containing custom context variables. + i18nManager *gi18n.Manager // I18n manager for error message translation. + key string // Single validation key. + value interface{} // Single validation value. + data interface{} // Validation data, which is usually a map. + rules interface{} // Custom validation data. + messages interface{} // Custom validation error messages, which can be string or type of CustomMsg. + ruleFuncMap map[string]RuleFunc // ruleFuncMap stores custom rule functions for current Validator. + useDataInsteadOfObjectAttributes bool // Using `data` as its validation source instead of attribute values from `Object`. } // New creates and returns a new Validator. func New() *Validator { return &Validator{ - ctx: context.TODO(), // Initialize an empty context. - i18nManager: gi18n.Instance(), // Use default i18n manager. + ctx: context.TODO(), // Initialize an empty context. + i18nManager: gi18n.Instance(), // Use default i18n manager. + ruleFuncMap: make(map[string]RuleFunc), // Custom rule function storing map. } } @@ -77,3 +79,28 @@ func (v *Validator) Messages(messages interface{}) *Validator { newValidator.messages = messages return newValidator } + +// RuleFunc registers one custom rule function to current Validator. +func (v *Validator) RuleFunc(rule string, f RuleFunc) *Validator { + newValidator := v.Clone() + newValidator.ruleFuncMap[rule] = f + return newValidator +} + +// RuleFuncMap registers multiple custom rule functions to current Validator. +func (v *Validator) RuleFuncMap(m map[string]RuleFunc) *Validator { + newValidator := v.Clone() + for k, v := range m { + newValidator.ruleFuncMap[k] = v + } + return newValidator +} + +// getRuleFunc retrieves and returns the custom rule function for specified rule. +func (v *Validator) getRuleFunc(rule string) RuleFunc { + ruleFunc := v.ruleFuncMap[rule] + if ruleFunc == nil { + ruleFunc = customRuleFuncMap[rule] + } + return ruleFunc +} diff --git a/util/gvalid/gvalid_validator_check_map.go b/util/gvalid/gvalid_validator_check_map.go index cefb987ca..e7e88d67e 100644 --- a/util/gvalid/gvalid_validator_check_map.go +++ b/util/gvalid/gvalid_validator_check_map.go @@ -96,7 +96,7 @@ func (v *Validator) doCheckMap(params interface{}) Error { value = v } // It checks each rule and its value in loop. - if e := v.doCheckValue(key, value, rule, customMsgs[key], data); e != nil { + if e := v.doCheckValue(key, value, rule, customMsgs[key], params, data); e != nil { _, item := e.FirstItem() // =========================================================== // Only in map and struct validations, if value is nil or empty @@ -112,7 +112,7 @@ func (v *Validator) doCheckMap(params interface{}) Error { break } // Custom rules are also required in default. - if _, ok := customRuleFuncMap[k]; ok { + if f := v.getRuleFunc(k); f != nil { required = true break } diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 55e0d7c33..2c921568b 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -57,12 +57,16 @@ func (v *Validator) doCheckStruct(object interface{}) Error { } var ( - inputParamMap map[string]interface{} - checkRules = make(map[string]string) - customMessage = make(CustomMsg) - fieldAliases = make(map[string]string) // Alias names for `messages` overwriting struct tag names. - errorRules = make([]string, 0) // Sequence rules. + inputParamMap map[string]interface{} + checkRules = make(map[string]string) + customMessage = make(CustomMsg) + checkValueData = v.data + fieldAliases = make(map[string]string) // Alias names for `messages` overwriting struct tag names. + errorRules = make([]string, 0) // Sequence rules. ) + if checkValueData == nil { + checkValueData = object + } switch v := v.rules.(type) { // Sequence tag: []sequence tag // Sequence has order for error results. @@ -194,7 +198,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error { for key, rule := range checkRules { _, value = gutil.MapPossibleItemByKey(inputParamMap, key) // It checks each rule and its value in loop. - if e := v.doCheckValue(key, value, rule, customMessage[key], inputParamMap); e != nil { + if e := v.doCheckValue(key, value, rule, customMessage[key], checkValueData, inputParamMap); e != nil { _, item := e.FirstItem() // =================================================================== // Only in map and struct validations, if value is nil or empty string @@ -210,7 +214,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error { break } // Custom rules are also required in default. - if _, ok := customRuleFuncMap[k]; ok { + if f := v.getRuleFunc(k); f != nil { required = true break } diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check_value.go similarity index 87% rename from util/gvalid/gvalid_validator_check.go rename to util/gvalid/gvalid_validator_check_value.go index 9c01f21c2..1a6493c3e 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -29,11 +29,24 @@ type apiTime interface { // CheckValue checks single value with specified rules. // It returns nil if successful validation. func (v *Validator) CheckValue(value interface{}) Error { - return v.doCheckValue("", value, gconv.String(v.rules), v.messages, v.data) + return v.doCheckValue("", value, gconv.String(v.rules), v.messages, v.data, gconv.Map(v.data)) } // doCheckSingleValue does the really rules validation for single key-value. -func (v *Validator) doCheckValue(key string, value interface{}, rules string, messages interface{}, paramMap ...interface{}) Error { +// +// The parameter `rules` specifies the validation rules string, like "required", "required|between:1,100", etc. +// The parameter `value` specifies the value for this rules to be validated. +// The parameter `messages` specifies the custom error messages for this rule, which is usually type of map/slice. +// The parameter `dataRaw` specifies the `raw data` which is passed to the Validator. It might be type of map/struct or a nil value. +// The parameter `dataMap` specifies the map that is converted from `dataRaw`. It is usually used internally +func (v *Validator) doCheckValue( + key string, + value interface{}, + rules string, + messages interface{}, + dataRaw interface{}, + dataMap map[string]interface{}, +) Error { // If there's no validation rules, it does nothing and returns quickly. if rules == "" { return nil @@ -41,12 +54,8 @@ func (v *Validator) doCheckValue(key string, value interface{}, rules string, me // It converts value to string and then does the validation. var ( // Do not trim it as the space is also part of the value. - data = make(map[string]interface{}) errorMsgArray = make(map[string]string) ) - if len(paramMap) > 0 && paramMap[0] != nil { - data = gconv.Map(paramMap[0]) - } // Custom error messages handling. var ( msgArray = make([]string, 0) @@ -66,7 +75,7 @@ func (v *Validator) doCheckValue(key string, value interface{}, rules string, me for i := 0; ; { array := strings.Split(ruleItems[i], ":") _, ok := allSupportedRules[array[0]] - if !ok && customRuleFuncMap[array[0]] == nil { + if !ok && v.getRuleFunc(array[0]) == nil { if i > 0 && ruleItems[i-1][:5] == "regex" { ruleItems[i-1] += "|" + ruleItems[i] ruleItems = append(ruleItems[:i], ruleItems[i+1:]...) @@ -85,26 +94,26 @@ func (v *Validator) doCheckValue(key string, value interface{}, rules string, me } for index := 0; index < len(ruleItems); { var ( - err error - match = false - results = ruleRegex.FindStringSubmatch(ruleItems[index]) - ruleKey = strings.TrimSpace(results[1]) - rulePattern = strings.TrimSpace(results[2]) + err error + match = false + results = ruleRegex.FindStringSubmatch(ruleItems[index]) + ruleKey = strings.TrimSpace(results[1]) + rulePattern = strings.TrimSpace(results[2]) + customRuleFunc RuleFunc ) if len(msgArray) > index { customMsgMap[ruleKey] = strings.TrimSpace(msgArray[index]) } - if f, ok := customRuleFuncMap[ruleKey]; ok { + // Custom rule handling. + // 1. It firstly checks and uses the custom registered rules functions in the current Validator. + // 2. It secondly checks and uses the globally registered rules functions. + // 3. It finally checks and uses the build-in rules functions. + customRuleFunc = v.getRuleFunc(ruleKey) + if customRuleFunc != nil { // It checks custom validation rules with most priority. - var ( - dataMap map[string]interface{} - message = v.getErrorMessageByRule(ruleKey, customMsgMap) - ) - if len(paramMap) > 0 && paramMap[0] != nil { - dataMap = gconv.Map(paramMap[0]) - } - if err := f(v.ctx, ruleItems[index], value, message, dataMap); err != nil { + message := v.getErrorMessageByRule(ruleKey, customMsgMap) + if err := customRuleFunc(v.ctx, ruleItems[index], value, message, dataRaw); err != nil { match = false errorMsgArray[ruleKey] = err.Error() } else { @@ -112,7 +121,7 @@ func (v *Validator) doCheckValue(key string, value interface{}, rules string, me } } else { // It checks build-in validation rules if there's no custom rule. - match, err = v.doCheckBuildInRules(index, value, ruleKey, rulePattern, ruleItems, data, customMsgMap) + match, err = v.doCheckBuildInRules(index, value, ruleKey, rulePattern, ruleItems, dataMap, customMsgMap) if !match && err != nil { errorMsgArray[ruleKey] = err.Error() } diff --git a/util/gvalid/gvalid_z_example_test.go b/util/gvalid/gvalid_z_example_test.go index 423957391..d11fb4e9f 100644 --- a/util/gvalid/gvalid_z_example_test.go +++ b/util/gvalid/gvalid_z_example_test.go @@ -115,21 +115,6 @@ func ExampleCheckStruct3() { } func ExampleRegisterRule() { - rule := "unique-name" - gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error { - var ( - id = gconv.Int(params["Id"]) - name = gconv.String(value) - ) - n, err := g.Table("user").Where("id != ? and name = ?", id, name).Count() - if err != nil { - return err - } - if n > 0 { - return errors.New(message) - } - return nil - }) type User struct { Id int Name string `v:"required|unique-name # 请输入用户名称|用户名称已被占用"` @@ -140,6 +125,22 @@ func ExampleRegisterRule() { Name: "john", Pass: "123456", } + + rule := "unique-name" + gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, data interface{}) error { + var ( + id = data.(*User).Id + name = gconv.String(value) + ) + n, err := g.Model("user").Where("id != ? and name = ?", id, name).Count() + if err != nil { + return err + } + if n > 0 { + return errors.New(message) + } + return nil + }) err := gvalid.CheckStruct(context.TODO(), user, nil) fmt.Println(err.Error()) // May Output: @@ -148,7 +149,7 @@ func ExampleRegisterRule() { func ExampleRegisterRule_OverwriteRequired() { rule := "required" - gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error { + gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, data interface{}) error { reflectValue := reflect.ValueOf(value) if reflectValue.Kind() == reflect.Ptr { reflectValue = reflectValue.Elem() diff --git a/util/gvalid/gvalid_z_unit_custom_rule_test.go b/util/gvalid/gvalid_z_unit_custom_rule_test.go index c9493c861..0a4e694b4 100644 --- a/util/gvalid/gvalid_z_unit_custom_rule_test.go +++ b/util/gvalid/gvalid_z_unit_custom_rule_test.go @@ -20,16 +20,20 @@ import ( func Test_CustomRule1(t *testing.T) { rule := "custom" - err := gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error { - pass := gconv.String(value) - if len(pass) != 6 { - return errors.New(message) - } - if params["data"] != pass { - return errors.New(message) - } - return nil - }) + err := gvalid.RegisterRule( + rule, + func(ctx context.Context, rule string, value interface{}, message string, data interface{}) error { + pass := gconv.String(value) + if len(pass) != 6 { + return errors.New(message) + } + m := gconv.Map(data) + if m["data"] != pass { + return errors.New(message) + } + return nil + }, + ) gtest.Assert(err, nil) gtest.C(t, func(t *gtest.T) { err := gvalid.CheckValue(context.TODO(), "123456", rule, "custom message") @@ -67,7 +71,7 @@ func Test_CustomRule1(t *testing.T) { func Test_CustomRule2(t *testing.T) { rule := "required-map" - err := gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error { + err := gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, data interface{}) error { m := gconv.Map(value) if len(m) == 0 { return errors.New(message) @@ -111,7 +115,7 @@ func Test_CustomRule2(t *testing.T) { func Test_CustomRule_AllowEmpty(t *testing.T) { rule := "allow-empty-str" - err := gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, params map[string]interface{}) error { + err := gvalid.RegisterRule(rule, func(ctx context.Context, rule string, value interface{}, message string, data interface{}) error { s := gconv.String(value) if len(s) == 0 || s == "gf" { return nil @@ -153,3 +157,118 @@ func Test_CustomRule_AllowEmpty(t *testing.T) { t.Assert(err.String(), "自定义错误") }) } + +func TestValidator_RuleFunc(t *testing.T) { + ruleName := "custom_1" + ruleFunc := func(ctx context.Context, rule string, value interface{}, message string, data interface{}) error { + pass := gconv.String(value) + if len(pass) != 6 { + return errors.New(message) + } + if m := gconv.Map(data); m["data"] != pass { + return errors.New(message) + } + return nil + } + gtest.C(t, func(t *gtest.T) { + err := g.Validator().Rules(ruleName).Messages("custom message").RuleFunc(ruleName, ruleFunc).CheckValue("123456") + t.Assert(err.String(), "custom message") + err = g.Validator(). + Rules(ruleName). + Messages("custom message"). + Data(g.Map{"data": "123456"}). + RuleFunc(ruleName, ruleFunc). + CheckValue("123456") + t.AssertNil(err) + }) + // Error with struct validation. + gtest.C(t, func(t *gtest.T) { + type T struct { + Value string `v:"uid@custom_1#自定义错误"` + Data string `p:"data"` + } + st := &T{ + Value: "123", + Data: "123456", + } + err := g.Validator().RuleFunc(ruleName, ruleFunc).CheckStruct(st) + t.Assert(err.String(), "自定义错误") + }) + // No error with struct validation. + gtest.C(t, func(t *gtest.T) { + type T struct { + Value string `v:"uid@custom_1#自定义错误"` + Data string `p:"data"` + } + st := &T{ + Value: "123456", + Data: "123456", + } + err := g.Validator().RuleFunc(ruleName, ruleFunc).CheckStruct(st) + t.AssertNil(err) + }) +} + +func TestValidator_RuleFuncMap(t *testing.T) { + ruleName := "custom_1" + ruleFunc := func(ctx context.Context, rule string, value interface{}, message string, data interface{}) error { + pass := gconv.String(value) + if len(pass) != 6 { + return errors.New(message) + } + if m := gconv.Map(data); m["data"] != pass { + return errors.New(message) + } + return nil + } + gtest.C(t, func(t *gtest.T) { + err := g.Validator(). + Rules(ruleName). + Messages("custom message"). + RuleFuncMap(map[string]gvalid.RuleFunc{ + ruleName: ruleFunc, + }).CheckValue("123456") + t.Assert(err.String(), "custom message") + err = g.Validator(). + Rules(ruleName). + Messages("custom message"). + Data(g.Map{"data": "123456"}). + RuleFuncMap(map[string]gvalid.RuleFunc{ + ruleName: ruleFunc, + }). + CheckValue("123456") + t.AssertNil(err) + }) + // Error with struct validation. + gtest.C(t, func(t *gtest.T) { + type T struct { + Value string `v:"uid@custom_1#自定义错误"` + Data string `p:"data"` + } + st := &T{ + Value: "123", + Data: "123456", + } + err := g.Validator(). + RuleFuncMap(map[string]gvalid.RuleFunc{ + ruleName: ruleFunc, + }).CheckStruct(st) + t.Assert(err.String(), "自定义错误") + }) + // No error with struct validation. + gtest.C(t, func(t *gtest.T) { + type T struct { + Value string `v:"uid@custom_1#自定义错误"` + Data string `p:"data"` + } + st := &T{ + Value: "123456", + Data: "123456", + } + err := g.Validator(). + RuleFuncMap(map[string]gvalid.RuleFunc{ + ruleName: ruleFunc, + }).CheckStruct(st) + t.AssertNil(err) + }) +} From 7bfd48e2ab7cde5f79ee3fcc55fd83fbc6506d82 Mon Sep 17 00:00:00 2001 From: wanna Date: Sat, 29 May 2021 12:15:37 +0800 Subject: [PATCH 296/492] valid field length --- .example/util/gvalid/gvalid.go | 3 +++ .example/util/gvalid/gvalid_i18n.go | 8 +++++--- .example/util/gvalid/i18n/en.toml | 1 + .example/util/gvalid/i18n/zh-CN.toml | 1 + util/gvalid/gvalid.go | 3 +++ util/gvalid/gvalid_validator_check.go | 3 ++- util/gvalid/gvalid_validator_rule_length.go | 7 +++++++ util/gvalid/gvalid_z_unit_basic_all_test.go | 10 ++++++++++ util/gvalid/i18n/cn/validation.toml | 1 + util/gvalid/i18n/en/validation.toml | 1 + util/gvalid/testdata/i18n/cn/validation.toml | 1 + util/gvalid/testdata/i18n/en/validation.toml | 1 + 12 files changed, 36 insertions(+), 4 deletions(-) diff --git a/.example/util/gvalid/gvalid.go b/.example/util/gvalid/gvalid.go index a21b63f77..4d6212876 100644 --- a/.example/util/gvalid/gvalid.go +++ b/.example/util/gvalid/gvalid.go @@ -48,11 +48,13 @@ func main() { "passport": "john", "password": "123456", "password2": "1234567", + "name": "gf", } rules := map[string]string{ "passport": "required|length:6,16", "password": "required|length:6,16|same:password2", "password2": "required|length:6,16", + "name": "size:5", } msgs := map[string]interface{}{ "passport": "账号不能为空|账号长度应当在:min到:max之间", @@ -60,6 +62,7 @@ func main() { "required": "密码不能为空", "same": "两次密码输入不相等", }, + "name": "名字长度必须为:size", } if e := gvalid.CheckMap(context.TODO(), params, rules, msgs); e != nil { g.Dump(e.Maps()) diff --git a/.example/util/gvalid/gvalid_i18n.go b/.example/util/gvalid/gvalid_i18n.go index dccde019c..cb2a5e905 100644 --- a/.example/util/gvalid/gvalid_i18n.go +++ b/.example/util/gvalid/gvalid_i18n.go @@ -10,12 +10,14 @@ import ( func main() { type User struct { - Name string `v:"required#ReuiredUserName"` - Type int `v:"required#ReuiredUserType"` + Name string `v:"required#ReuiredUserName"` + Type int `v:"required#ReuiredUserType"` + Project string `v:"size:10#MustSize"` } var ( data = g.Map{ - "name": "john", + "name": "john", + "project": "gf", } user = User{} ctxEn = gi18n.WithLanguage(context.TODO(), "en") diff --git a/.example/util/gvalid/i18n/en.toml b/.example/util/gvalid/i18n/en.toml index bcca1267d..7d1f481c8 100644 --- a/.example/util/gvalid/i18n/en.toml +++ b/.example/util/gvalid/i18n/en.toml @@ -5,6 +5,7 @@ "ReuiredUserName" = "Please input user name" "ReuiredUserType" = "Please select user type" +"MustSize" = "Size of :attribute must be :size" diff --git a/.example/util/gvalid/i18n/zh-CN.toml b/.example/util/gvalid/i18n/zh-CN.toml index 750787e56..3db393e20 100644 --- a/.example/util/gvalid/i18n/zh-CN.toml +++ b/.example/util/gvalid/i18n/zh-CN.toml @@ -4,6 +4,7 @@ "ReuiredUserName" = "请输入用户名称" "ReuiredUserType" = "请选择用户类型" +"MustSize" = ":attribute长度必须为:size" diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 275b17771..7d8155136 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -47,6 +47,7 @@ import ( // length format: length:min,max brief: Length between :min and :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. // min-length format: min-length:min brief: Length is equal or greater than :min. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. // max-length format: max-length:max brief: Length is equal or lesser than :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. +// siz format: size brief: Length must be :size. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. // between format: between:min,max brief: Range between :min and :max. It supports both integer and float. // min format: min:min brief: Equal or greater than :min. It supports both integer and float. // max format: max:max brief: Equal or lesser than :max. It supports both integer and float. @@ -143,6 +144,7 @@ var ( "length": {}, "min-length": {}, "max-length": {}, + "size": {}, "between": {}, "min": {}, "max": {}, @@ -200,6 +202,7 @@ var ( "length": "The :attribute value length must be between :min and :max", "min-length": "The :attribute value length must be equal or greater than :min", "max-length": "The :attribute value length must be equal or lesser than :max", + "size": "The :attribute value length must be :size", "between": "The :attribute value must be between :min and :max", "min": "The :attribute value must be equal or greater than :min", "max": "The :attribute value must be equal or lesser than :max", diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index 9c01f21c2..089f95044 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -163,7 +163,8 @@ func (v *Validator) doCheckBuildInRules( case "length", "min-length", - "max-length": + "max-length", + "size": if msg := v.checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { return match, errors.New(msg) } else { diff --git a/util/gvalid/gvalid_validator_rule_length.go b/util/gvalid/gvalid_validator_rule_length.go index 33edfa97a..7bd5b188e 100644 --- a/util/gvalid/gvalid_validator_rule_length.go +++ b/util/gvalid/gvalid_validator_rule_length.go @@ -58,6 +58,13 @@ func (v *Validator) checkLength(value, ruleKey, ruleVal string, customMsgMap map msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1) } + + case "size": + size, err := strconv.Atoi(ruleVal) + if valueLen != size || err != nil { + msg = v.getErrorMessageByRule(ruleKey, customMsgMap) + msg = strings.Replace(msg, ":size", strconv.Itoa(size), -1) + } } return msg } diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index fa687880c..75f22609b 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -702,6 +702,16 @@ func Test_MaxLength(t *testing.T) { } } +func Test_Size(t *testing.T) { + rule := "size:5" + if m := gvalid.CheckValue(context.TODO(), "12345", rule, nil); m != nil { + t.Error(m) + } + if m := gvalid.CheckValue(context.TODO(), "123456", rule, nil); m == nil { + t.Error("长度校验失败") + } +} + func Test_Between(t *testing.T) { rule := "between:6.01, 10.01" if m := gvalid.CheckValue(context.TODO(), 10, rule, nil); m != nil { diff --git a/util/gvalid/i18n/cn/validation.toml b/util/gvalid/i18n/cn/validation.toml index fab6dfba6..fb6c7c787 100644 --- a/util/gvalid/i18n/cn/validation.toml +++ b/util/gvalid/i18n/cn/validation.toml @@ -28,6 +28,7 @@ "gf.gvalid.rule.length" = ":attribute 字段长度为:min到:max个字符" "gf.gvalid.rule.min-length" = ":attribute 字段最小长度为:min" "gf.gvalid.rule.max-length" = ":attribute 字段最大长度为:max" +"gf.gvalid.rule.size" = ":attribute 字段长度必须为:size" "gf.gvalid.rule.between" = ":attribute 字段大小为:min到:max" "gf.gvalid.rule.min" = ":attribute 字段最小值为:min" "gf.gvalid.rule.max" = ":attribute 字段最大值为:max" diff --git a/util/gvalid/i18n/en/validation.toml b/util/gvalid/i18n/en/validation.toml index 7dc56cab0..4de5880d2 100644 --- a/util/gvalid/i18n/en/validation.toml +++ b/util/gvalid/i18n/en/validation.toml @@ -28,6 +28,7 @@ "gf.gvalid.rule.length" = "The :attribute value length must be between :min and :max" "gf.gvalid.rule.min-length" = "The :attribute value length must be equal or greater than :min" "gf.gvalid.rule.max-length" = "The :attribute value length must be equal or lesser than :max" +"gf.gvalid.rule.size" = "The :attribute value length must be :size" "gf.gvalid.rule.between" = "The :attribute value must be between :min and :max" "gf.gvalid.rule.min" = "The :attribute value must be equal or greater than :min" "gf.gvalid.rule.max" = "The :attribute value must be equal or lesser than :max" diff --git a/util/gvalid/testdata/i18n/cn/validation.toml b/util/gvalid/testdata/i18n/cn/validation.toml index 0cc409eb5..531aaa2ca 100644 --- a/util/gvalid/testdata/i18n/cn/validation.toml +++ b/util/gvalid/testdata/i18n/cn/validation.toml @@ -28,6 +28,7 @@ "gf.gvalid.rule.length" = ":attribute 字段长度为:min到:max个字符" "gf.gvalid.rule.min-length" = ":attribute 字段最小长度为:min" "gf.gvalid.rule.max-length" = ":attribute 字段最大长度为:max" +"gf.gvalid.rule.size" = ":attribute 字段长度为必须为:size" "gf.gvalid.rule.between" = ":attribute 字段大小为:min到:max" "gf.gvalid.rule.min" = ":attribute 字段最小值为:min" "gf.gvalid.rule.max" = ":attribute 字段最大值为:max" diff --git a/util/gvalid/testdata/i18n/en/validation.toml b/util/gvalid/testdata/i18n/en/validation.toml index 7dc56cab0..4de5880d2 100644 --- a/util/gvalid/testdata/i18n/en/validation.toml +++ b/util/gvalid/testdata/i18n/en/validation.toml @@ -28,6 +28,7 @@ "gf.gvalid.rule.length" = "The :attribute value length must be between :min and :max" "gf.gvalid.rule.min-length" = "The :attribute value length must be equal or greater than :min" "gf.gvalid.rule.max-length" = "The :attribute value length must be equal or lesser than :max" +"gf.gvalid.rule.size" = "The :attribute value length must be :size" "gf.gvalid.rule.between" = "The :attribute value must be between :min and :max" "gf.gvalid.rule.min" = "The :attribute value must be equal or greater than :min" "gf.gvalid.rule.max" = "The :attribute value must be equal or lesser than :max" From c1db01425ea2f5a288ea61ecfa7fa455e9a7ae65 Mon Sep 17 00:00:00 2001 From: wanna Date: Sat, 29 May 2021 12:25:50 +0800 Subject: [PATCH 297/492] fix remarks --- util/gvalid/gvalid.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 7d8155136..cac7abe74 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -47,7 +47,7 @@ import ( // length format: length:min,max brief: Length between :min and :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. // min-length format: min-length:min brief: Length is equal or greater than :min. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. // max-length format: max-length:max brief: Length is equal or lesser than :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. -// siz format: size brief: Length must be :size. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. +// size format: size brief: Length must be :size. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. // between format: between:min,max brief: Range between :min and :max. It supports both integer and float. // min format: min:min brief: Equal or greater than :min. It supports both integer and float. // max format: max:max brief: Equal or lesser than :max. It supports both integer and float. From 1441ce7f5f4253f7c41e93ff5c00ef6fe4900754 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 29 May 2021 15:47:07 +0800 Subject: [PATCH 298/492] add examples for package gproc --- .example/os/gproc/signal/signal_handler.go | 54 +++++++++++++++++++ .../os/gproc/signal/signal_handler_gproc.go | 27 ++++++++++ frame/g/g_object.go | 7 --- os/gproc/gproc_signal.go | 8 +-- 4 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 .example/os/gproc/signal/signal_handler.go create mode 100644 .example/os/gproc/signal/signal_handler_gproc.go diff --git a/.example/os/gproc/signal/signal_handler.go b/.example/os/gproc/signal/signal_handler.go new file mode 100644 index 000000000..a1d495347 --- /dev/null +++ b/.example/os/gproc/signal/signal_handler.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "os" + "os/signal" + "syscall" + "time" +) + +func signalHandlerForMQ() { + var ( + sig os.Signal + receivedChan = make(chan os.Signal) + ) + signal.Notify( + receivedChan, + syscall.SIGINT, + syscall.SIGQUIT, + syscall.SIGKILL, + syscall.SIGTERM, + syscall.SIGABRT, + ) + for { + sig = <-receivedChan + fmt.Println("MQ is shutting down due to signal:", sig.String()) + time.Sleep(time.Second) + fmt.Println("MQ is shut down smoothly") + return + } +} + +func main() { + fmt.Println("Process start, pid:", os.Getpid()) + go signalHandlerForMQ() + + var ( + sig os.Signal + receivedChan = make(chan os.Signal) + ) + signal.Notify( + receivedChan, + syscall.SIGINT, + syscall.SIGQUIT, + syscall.SIGKILL, + syscall.SIGTERM, + syscall.SIGABRT, + ) + for { + sig = <-receivedChan + fmt.Println("MainProcess is shutting down due to signal:", sig.String()) + return + } +} diff --git a/.example/os/gproc/signal/signal_handler_gproc.go b/.example/os/gproc/signal/signal_handler_gproc.go new file mode 100644 index 000000000..e8e473484 --- /dev/null +++ b/.example/os/gproc/signal/signal_handler_gproc.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/os/gproc" + "os" + "time" +) + +func signalHandlerForMQ(sig os.Signal) { + fmt.Println("MQ is shutting down due to signal:", sig.String()) + time.Sleep(time.Second) + fmt.Println("MQ is shut down smoothly") +} + +func signalHandlerForMain(sig os.Signal) { + fmt.Println("MainProcess is shutting down due to signal:", sig.String()) +} + +func main() { + fmt.Println("Process start, pid:", os.Getpid()) + gproc.AddSigHandlerShutdown( + signalHandlerForMQ, + signalHandlerForMain, + ) + gproc.Listen() +} diff --git a/frame/g/g_object.go b/frame/g/g_object.go index d3dfb888c..571d47247 100644 --- a/frame/g/g_object.go +++ b/frame/g/g_object.go @@ -81,13 +81,6 @@ func Log(name ...string) *glog.Logger { return gins.Log(name...) } -// Database is alias of DB. -// See DB. -// Deprecated, use DB instead. -func Database(name ...string) gdb.DB { - return gins.Database(name...) -} - // DB returns an instance of database ORM object with specified configuration group name. func DB(name ...string) gdb.DB { return gins.Database(name...) diff --git a/os/gproc/gproc_signal.go b/os/gproc/gproc_signal.go index 6a2b9b700..08d059e00 100644 --- a/os/gproc/gproc_signal.go +++ b/os/gproc/gproc_signal.go @@ -47,9 +47,11 @@ func AddSigHandler(handler SigHandler, signals ...os.Signal) { // syscall.SIGKILL, // syscall.SIGTERM, // syscall.SIGABRT. -func AddSigHandlerShutdown(handler SigHandler) { - for sig, _ := range shutdownSignalMap { - signalHandlerMap[sig] = append(signalHandlerMap[sig], handler) +func AddSigHandlerShutdown(handler ...SigHandler) { + for _, h := range handler { + for sig, _ := range shutdownSignalMap { + signalHandlerMap[sig] = append(signalHandlerMap[sig], h) + } } } From d9bc8b05e1883a1c5ac73ced7af62c3f4fd79570 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 29 May 2021 16:45:08 +0800 Subject: [PATCH 299/492] add example for package gvalid --- .example/util/gvalid/config.toml | 14 ++++++++ .example/util/gvalid/gvalid_struct_meta.go | 40 ++++++++++++++++++++++ .example/util/gvalid/i18n/en.toml | 9 ----- 3 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 .example/util/gvalid/config.toml create mode 100644 .example/util/gvalid/gvalid_struct_meta.go diff --git a/.example/util/gvalid/config.toml b/.example/util/gvalid/config.toml new file mode 100644 index 000000000..26beac3a7 --- /dev/null +++ b/.example/util/gvalid/config.toml @@ -0,0 +1,14 @@ + + + +# MySQL. +[database] + [database.default] + link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + debug = true + + + + + + diff --git a/.example/util/gvalid/gvalid_struct_meta.go b/.example/util/gvalid/gvalid_struct_meta.go new file mode 100644 index 000000000..a08e8d8ca --- /dev/null +++ b/.example/util/gvalid/gvalid_struct_meta.go @@ -0,0 +1,40 @@ +package main + +import ( + "context" + "fmt" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/util/gmeta" +) + +type UserCreateReq struct { + gmeta.Meta `v:"UserCreateReq"` + Name string + Pass string +} + +func UserCreateReqChecker(ctx context.Context, rule string, value interface{}, message string, data interface{}) error { + user := &UserCreateReq{} + if v, ok := data.(*UserCreateReq); ok { + user = v + } + // SELECT COUNT(*) FROM `user` WHERE `name` = xxx + count, err := g.Model("user").Ctx(ctx).Where("name", user.Name).Count() + if err != nil { + return err + } + if count > 0 { + return gerror.Newf(`The name "%s" is already token`, user.Name) + } + return nil +} + +func main() { + user := &UserCreateReq{ + Name: "john", + Pass: "123456", + } + err := g.Validator().RuleFunc("UserCreateReq", UserCreateReqChecker).CheckStruct(user) + fmt.Println(err) +} diff --git a/.example/util/gvalid/i18n/en.toml b/.example/util/gvalid/i18n/en.toml index bcca1267d..8aee475de 100644 --- a/.example/util/gvalid/i18n/en.toml +++ b/.example/util/gvalid/i18n/en.toml @@ -1,15 +1,6 @@ - - "gf.gvalid.required" = "字段不能为空" "ReuiredUserName" = "Please input user name" "ReuiredUserType" = "Please select user type" - - - - - - - From 578e7d634bbc92ad17d8ae74557473b50ddaf3ff Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 29 May 2021 16:47:39 +0800 Subject: [PATCH 300/492] comment updates for package gvalid --- util/gvalid/gvalid.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index cac7abe74..5b390a125 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -47,7 +47,7 @@ import ( // length format: length:min,max brief: Length between :min and :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. // min-length format: min-length:min brief: Length is equal or greater than :min. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. // max-length format: max-length:max brief: Length is equal or lesser than :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. -// size format: size brief: Length must be :size. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. +// size format: size:size brief: Length must be :size. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. // between format: between:min,max brief: Range between :min and :max. It supports both integer and float. // min format: min:min brief: Equal or greater than :min. It supports both integer and float. // max format: max:max brief: Equal or lesser than :max. It supports both integer and float. From fe142c93fdfc25c3cd076ab03c0a625e7d2510dd Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 29 May 2021 17:07:49 +0800 Subject: [PATCH 301/492] add function Transaction for gdb.Model --- database/gdb/gdb_model_transaction.go | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 database/gdb/gdb_model_transaction.go diff --git a/database/gdb/gdb_model_transaction.go b/database/gdb/gdb_model_transaction.go new file mode 100644 index 000000000..6aaf933bf --- /dev/null +++ b/database/gdb/gdb_model_transaction.go @@ -0,0 +1,28 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +import ( + "context" +) + +// Transaction wraps the transaction logic using function `f`. +// It rollbacks the transaction and returns the error from function `f` if +// it returns non-nil error. It commits the transaction and returns nil if +// function `f` returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function `f` +// as it is automatically handled by this function. +func (m *Model) Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) (err error) { + if ctx == nil { + ctx = m.GetCtx() + } + if m.tx != nil { + return m.tx.Transaction(ctx, f) + } + return m.db.Transaction(ctx, f) +} From 392c81ad691997319bdb71f75dcf6e2584a59eca Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 29 May 2021 17:26:08 +0800 Subject: [PATCH 302/492] remove Link parameter from function TableFields for package gdb --- database/gdb/gdb.go | 12 ++++++------ database/gdb/gdb_core_structure.go | 2 +- database/gdb/gdb_driver_mssql.go | 12 +++++------- database/gdb/gdb_driver_mysql.go | 16 ++++++---------- database/gdb/gdb_driver_oracle.go | 12 +++++------- database/gdb/gdb_driver_pgsql.go | 12 +++++------- database/gdb/gdb_driver_sqlite.go | 12 +++++------- database/gdb/gdb_model_utility.go | 2 +- database/gdb/gdb_z_mysql_subquery_test.go | 2 +- 9 files changed, 35 insertions(+), 47 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 726f9ae43..d543bdb11 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -154,12 +154,12 @@ type DB interface { // Utility methods. // =========================================================================== - GetCtx() context.Context // See Core.GetCtx. - GetCore() *Core // See Core.GetCore - GetChars() (charLeft string, charRight string) // See Core.GetChars. - Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables. - TableFields(ctx context.Context, link Link, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields. - FilteredLinkInfo() string // See Core.FilteredLinkInfo. + GetCtx() context.Context // See Core.GetCtx. + GetCore() *Core // See Core.GetCore + GetChars() (charLeft string, charRight string) // See Core.GetChars. + Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables. + TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields. + FilteredLinkInfo() string // See Core.FilteredLinkInfo. // HandleSqlBeforeCommit is a hook function, which deals with the sql string before // it's committed to underlying driver. The parameter `link` specifies the current diff --git a/database/gdb/gdb_core_structure.go b/database/gdb/gdb_core_structure.go index be34cf062..e7aa76b67 100644 --- a/database/gdb/gdb_core_structure.go +++ b/database/gdb/gdb_core_structure.go @@ -151,7 +151,7 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s // mappingAndFilterData automatically mappings the map key to table field and removes // all key-value pairs that are not the field of given table. func (c *Core) mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) { - if fieldsMap, err := c.db.TableFields(c.GetCtx(), nil, table, schema); err == nil { + if fieldsMap, err := c.db.TableFields(c.GetCtx(), table, schema); err == nil { fieldsKeyMap := make(map[string]interface{}, len(fieldsMap)) for k, _ := range fieldsMap { fieldsKeyMap[k] = nil diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index d0bb93cfb..33d3f8732 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -207,7 +207,7 @@ func (d *DriverMssql) Tables(ctx context.Context, schema ...string) (tables []st // TableFields retrieves and returns the fields information of specified table of current schema. // // Also see DriverMysql.TableFields. -func (d *DriverMssql) TableFields(ctx context.Context, link Link, table string, schema ...string) (fields map[string]*TableField, err error) { +func (d *DriverMssql) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -223,13 +223,11 @@ func (d *DriverMssql) TableFields(ctx context.Context, link Link, table string, ) v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { var ( - result Result - ) - if link == nil { + result Result link, err = d.SlaveLink(useSchema) - if err != nil { - return nil - } + ) + if err != nil { + return nil } structureSql := fmt.Sprintf(` SELECT diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index 724b353ec..d39078c75 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -113,7 +113,7 @@ func (d *DriverMysql) Tables(ctx context.Context, schema ...string) (tables []st // // It's using cache feature to enhance the performance, which is never expired util the // process restarts. -func (d *DriverMysql) TableFields(ctx context.Context, link Link, table string, schema ...string) (fields map[string]*TableField, err error) { +func (d *DriverMysql) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -129,20 +129,16 @@ func (d *DriverMysql) TableFields(ctx context.Context, link Link, table string, ) v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { var ( - result Result - ) - if link == nil { + result Result link, err = d.SlaveLink(useSchema) - if err != nil { - return nil - } - } - result, err = d.DoGetAll(ctx, link, - fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.QuoteWord(table)), ) if err != nil { return nil } + result, err = d.DoGetAll(ctx, link, fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.QuoteWord(table))) + if err != nil { + return nil + } fields = make(map[string]*TableField) for i, m := range result { fields[m["Field"].String()] = &TableField{ diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index be356f176..9d8543936 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -183,7 +183,7 @@ func (d *DriverOracle) Tables(ctx context.Context, schema ...string) (tables []s // TableFields retrieves and returns the fields information of specified table of current schema. // // Also see DriverMysql.TableFields. -func (d *DriverOracle) TableFields(ctx context.Context, link Link, table string, schema ...string) (fields map[string]*TableField, err error) { +func (d *DriverOracle) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -200,6 +200,7 @@ func (d *DriverOracle) TableFields(ctx context.Context, link Link, table string, v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { var ( result Result + link, err = d.SlaveLink(useSchema) structureSql = fmt.Sprintf(` SELECT COLUMN_NAME AS FIELD, @@ -211,13 +212,10 @@ FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, strings.ToUpper(table), ) ) - structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - if link == nil { - link, err = d.SlaveLink(useSchema) - if err != nil { - return nil - } + if err != nil { + return nil } + structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) result, err = d.DoGetAll(ctx, link, structureSql) if err != nil { return nil diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 8fb6ad243..b0695f8c0 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -115,7 +115,7 @@ func (d *DriverPgsql) Tables(ctx context.Context, schema ...string) (tables []st // TableFields retrieves and returns the fields information of specified table of current schema. // // Also see DriverMysql.TableFields. -func (d *DriverPgsql) TableFields(ctx context.Context, link Link, table string, schema ...string) (fields map[string]*TableField, err error) { +func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -133,6 +133,7 @@ func (d *DriverPgsql) TableFields(ctx context.Context, link Link, table string, v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { var ( result Result + link, err = d.SlaveLink(useSchema) structureSql = fmt.Sprintf(` SELECT a.attname AS field, t.typname AS type FROM pg_class c, pg_attribute a LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,pg_type t @@ -141,13 +142,10 @@ ORDER BY a.attnum`, strings.ToLower(table), ) ) - structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - if link == nil { - link, err = d.SlaveLink(useSchema) - if err != nil { - return nil - } + if err != nil { + return nil } + structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) result, err = d.DoGetAll(ctx, link, structureSql) if err != nil { return nil diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index 69f4a42a5..99ae55fcf 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -97,7 +97,7 @@ func (d *DriverSqlite) Tables(ctx context.Context, schema ...string) (tables []s // TableFields retrieves and returns the fields information of specified table of current schema. // // Also see DriverMysql.TableFields. -func (d *DriverSqlite) TableFields(ctx context.Context, link Link, table string, schema ...string) (fields map[string]*TableField, err error) { +func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*TableField, err error) { charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { @@ -113,13 +113,11 @@ func (d *DriverSqlite) TableFields(ctx context.Context, link Link, table string, ) v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { var ( - result Result - ) - if link == nil { + result Result link, err = d.SlaveLink(useSchema) - if err != nil { - return nil - } + ) + if err != nil { + return nil } result, err = d.DoGetAll(ctx, link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table)) if err != nil { diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index bf29b0a9c..7b1c36b60 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -29,7 +29,7 @@ func (m *Model) TableFields(table string, schema ...string) (fields map[string]* if !gregex.IsMatchString(regularFieldNameRegPattern, table) { return nil, nil } - return m.db.TableFields(m.GetCtx(), m.getLink(false), table, schema...) + return m.db.TableFields(m.GetCtx(), table, schema...) } // getModel creates and returns a cloned model of current model if `safe` is true, or else it returns diff --git a/database/gdb/gdb_z_mysql_subquery_test.go b/database/gdb/gdb_z_mysql_subquery_test.go index 2f292085b..6cb76abf2 100644 --- a/database/gdb/gdb_z_mysql_subquery_test.go +++ b/database/gdb/gdb_z_mysql_subquery_test.go @@ -53,7 +53,7 @@ func Test_Model_SubQuery_Having(t *testing.T) { func Test_Model_SubQuery_Model(t *testing.T) { table := createInitTable() defer dropTable(table) - db.SetDebug(true) + gtest.C(t, func(t *gtest.T) { subQuery1 := db.Model(table).Where("id", g.Slice{1, 3, 5}) subQuery2 := db.Model(table).Where("id", g.Slice{5, 7, 9}) From 3e6b9864d52ffade73169b5d76761c8b179b545e Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 1 Jun 2021 19:47:02 +0800 Subject: [PATCH 303/492] fix issue #1256 --- util/gconv/gconv_interface.go | 7 +++++++ util/gconv/gconv_time.go | 9 +++++++++ util/gconv/gconv_z_unit_all_test.go | 7 ------- util/gconv/gconv_z_unit_time_test.go | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/util/gconv/gconv_interface.go b/util/gconv/gconv_interface.go index 8ddbe7c3b..cce763cc0 100644 --- a/util/gconv/gconv_interface.go +++ b/util/gconv/gconv_interface.go @@ -6,6 +6,8 @@ package gconv +import "github.com/gogf/gf/os/gtime" + // apiString is used for type assert api for String(). type apiString interface { String() string @@ -92,3 +94,8 @@ type apiUnmarshalText interface { type apiSet interface { Set(value interface{}) (old interface{}) } + +// apiGTime is the interface for gtime.Time converting. +type apiGTime interface { + GTime(format ...string) *gtime.Time +} diff --git a/util/gconv/gconv_time.go b/util/gconv/gconv_time.go index 98aac418d..20d9b77b7 100644 --- a/util/gconv/gconv_time.go +++ b/util/gconv/gconv_time.go @@ -51,11 +51,20 @@ func GTime(any interface{}, format ...string) *gtime.Time { if any == nil { return nil } + if v, ok := any.(apiGTime); ok { + return v.GTime(format...) + } // It's already this type. if len(format) == 0 { if v, ok := any.(*gtime.Time); ok { return v } + if t, ok := any.(time.Time); ok { + return gtime.New(t) + } + if t, ok := any.(*time.Time); ok { + return gtime.New(t) + } } s := String(any) if len(s) == 0 { diff --git a/util/gconv/gconv_z_unit_all_test.go b/util/gconv/gconv_z_unit_all_test.go index 2441e8e6b..a358c2362 100644 --- a/util/gconv/gconv_z_unit_all_test.go +++ b/util/gconv/gconv_z_unit_all_test.go @@ -647,13 +647,6 @@ func Test_Convert_All(t *testing.T) { }) } -func Test_Time_All(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - t.AssertEQ(gconv.Duration(""), time.Duration(int64(0))) - t.AssertEQ(gconv.GTime(""), gtime.New()) - }) -} - func Test_Slice_All(t *testing.T) { gtest.C(t, func(t *gtest.T) { value := 123.456 diff --git a/util/gconv/gconv_z_unit_time_test.go b/util/gconv/gconv_z_unit_time_test.go index 99d3e0115..f44acc035 100644 --- a/util/gconv/gconv_z_unit_time_test.go +++ b/util/gconv/gconv_z_unit_time_test.go @@ -7,6 +7,7 @@ package gconv_test import ( + "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/frame/g" "testing" "time" @@ -17,6 +18,11 @@ import ( ) func Test_Time(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.AssertEQ(gconv.Duration(""), time.Duration(int64(0))) + t.AssertEQ(gconv.GTime(""), gtime.New()) + }) + gtest.C(t, func(t *gtest.T) { s := "2011-10-10 01:02:03.456" t.AssertEQ(gconv.GTime(s), gtime.NewFromStr(s)) @@ -42,6 +48,15 @@ func Test_Time(t *testing.T) { t.AssertEQ(gconv.GTime(s), gtime.NewFromStr(s)) t.AssertEQ(gconv.Time(s), gtime.NewFromStr(s).Time) }) + gtest.C(t, func(t *gtest.T) { + t1 := gtime.NewFromStr("2021-05-21 05:04:51.206547+00") + t2 := gconv.GTime(gvar.New(t1)) + t3 := gvar.New(t1).GTime() + t.AssertEQ(t1, t2) + t.AssertEQ(t1.Local(), t2.Local()) + t.AssertEQ(t1, t3) + t.AssertEQ(t1.Local(), t3.Local()) + }) } func Test_Time_Slice_Attribute(t *testing.T) { From db943468637c1f93469da20809dfb9731ffb83b1 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 1 Jun 2021 19:59:57 +0800 Subject: [PATCH 304/492] gitee/github template update --- .gitee/ISSUE_TEMPLATE.MD | 5 ++++- .github/ISSUE_TEMPLATE.MD | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.gitee/ISSUE_TEMPLATE.MD b/.gitee/ISSUE_TEMPLATE.MD index 1197badda..4d24425c3 100644 --- a/.gitee/ISSUE_TEMPLATE.MD +++ b/.gitee/ISSUE_TEMPLATE.MD @@ -1,4 +1,7 @@ - + + + + ### 1. 您当前使用的`Go`版本,及系统版本、系统架构? diff --git a/.github/ISSUE_TEMPLATE.MD b/.github/ISSUE_TEMPLATE.MD index 09e907f4e..364044d16 100644 --- a/.github/ISSUE_TEMPLATE.MD +++ b/.github/ISSUE_TEMPLATE.MD @@ -1,5 +1,10 @@ + + + + + ### 1. What version of `Go` and system type/arch are you using? -### 3. Can this issue be reproduced with the latest release? +### 3. Can this issue be re-produced with the latest release? From 1c09846d3efa4eb5f7dce0e36a6f4e363857ebc2 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 1 Jun 2021 20:09:52 +0800 Subject: [PATCH 305/492] fix issue #1272 --- util/gvalid/gvalid_validator_check_value.go | 11 +---------- util/gvalid/gvalid_z_unit_basic_all_test.go | 7 +++++-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/util/gvalid/gvalid_validator_check_value.go b/util/gvalid/gvalid_validator_check_value.go index e470d96dc..a72cee3b3 100644 --- a/util/gvalid/gvalid_validator_check_value.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -208,16 +208,7 @@ func (v *Validator) doCheckBuildInRules( if v, ok := value.(apiTime); ok { return !v.IsZero(), nil } - // Standard date string, which must contain char '-' or '.'. - if _, err := gtime.StrToTime(valueStr); err == nil { - match = true - break - } - // Date that not contains char '-' or '.'. - if _, err := gtime.StrToTime(valueStr, "Ymd"); err == nil { - match = true - break - } + match = gregex.IsMatchString(`\d{4}[\.\-\_/]{0,1}\d{2}[\.\-\_/]{0,1}\d{2}`, valueStr) // Date rule with specified format. case "date-format": diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index 75f22609b..30f44e470 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -236,6 +236,7 @@ func Test_Date(t *testing.T) { val5 := "2010.11.01" val6 := "2010/11/01" val7 := "2010=11=01" + val8 := "123" err1 := gvalid.CheckValue(context.TODO(), val1, rule, nil) err2 := gvalid.CheckValue(context.TODO(), val2, rule, nil) err3 := gvalid.CheckValue(context.TODO(), val3, rule, nil) @@ -243,13 +244,15 @@ func Test_Date(t *testing.T) { err5 := gvalid.CheckValue(context.TODO(), val5, rule, nil) err6 := gvalid.CheckValue(context.TODO(), val6, rule, nil) err7 := gvalid.CheckValue(context.TODO(), val7, rule, nil) - t.Assert(err1, nil) - t.Assert(err2, nil) + err8 := gvalid.CheckValue(context.TODO(), val8, rule, nil) + t.AssertNE(err1, nil) + t.AssertNE(err2, nil) t.Assert(err3, nil) t.Assert(err4, nil) t.Assert(err5, nil) t.Assert(err6, nil) t.AssertNE(err7, nil) + t.AssertNE(err8, nil) }) } From e3f5c9175cc5086788b1a98c73a4dc21ae99871b Mon Sep 17 00:00:00 2001 From: fangjw Date: Wed, 2 Jun 2021 09:17:50 +0800 Subject: [PATCH 306/492] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B5=AE=E7=82=B9?= =?UTF-8?q?=E6=95=B0=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- util/gutil/gutil_z_unit_comparator_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/util/gutil/gutil_z_unit_comparator_test.go b/util/gutil/gutil_z_unit_comparator_test.go index 4538ba22b..67a1a74a6 100755 --- a/util/gutil/gutil_z_unit_comparator_test.go +++ b/util/gutil/gutil_z_unit_comparator_test.go @@ -160,3 +160,20 @@ func Test_ComparatorTime(t *testing.T) { t.Assert(l, -1) }) } + +func Test_ComparatorFloat32OfFixed(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gutil.ComparatorFloat32(0.1, 0.1), 0) + t.Assert(gutil.ComparatorFloat32(1.1, 2.1), -1) + t.Assert(gutil.ComparatorFloat32(2.1, 1.1), 1) + }) +} + +func Test_ComparatorFloat64OfFixed(t *testing.T) { + + gtest.C(t, func(t *gtest.T) { + t.Assert(gutil.ComparatorFloat32(0.1, 0.1), 0) + t.Assert(gutil.ComparatorFloat32(1.1, 2.1), -1) + t.Assert(gutil.ComparatorFloat32(2.1, 1.1), 1) + }) +} \ No newline at end of file From ee3d375532eba1baa20777e181861572ac27ba79 Mon Sep 17 00:00:00 2001 From: fangjw Date: Wed, 2 Jun 2021 09:18:52 +0800 Subject: [PATCH 307/492] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B5=AE=E7=82=B9?= =?UTF-8?q?=E6=95=B0=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- util/gutil/gutil_z_unit_comparator_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/gutil/gutil_z_unit_comparator_test.go b/util/gutil/gutil_z_unit_comparator_test.go index 67a1a74a6..db6ad9092 100755 --- a/util/gutil/gutil_z_unit_comparator_test.go +++ b/util/gutil/gutil_z_unit_comparator_test.go @@ -172,8 +172,8 @@ func Test_ComparatorFloat32OfFixed(t *testing.T) { func Test_ComparatorFloat64OfFixed(t *testing.T) { gtest.C(t, func(t *gtest.T) { - t.Assert(gutil.ComparatorFloat32(0.1, 0.1), 0) - t.Assert(gutil.ComparatorFloat32(1.1, 2.1), -1) - t.Assert(gutil.ComparatorFloat32(2.1, 1.1), 1) + t.Assert(gutil.ComparatorFloat64(0.1, 0.1), 0) + t.Assert(gutil.ComparatorFloat64(1.1, 2.1), -1) + t.Assert(gutil.ComparatorFloat64(2.1, 1.1), 1) }) -} \ No newline at end of file +} From c3c5414ce2703dfcbe2e224a1f5396b7b57e2495 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 2 Jun 2021 09:42:27 +0800 Subject: [PATCH 308/492] add custom handler feature --- container/garray/garray_normal_any.go | 91 +++++++------- container/garray/garray_normal_int.go | 103 ++++++++-------- container/garray/garray_normal_str.go | 99 ++++++++------- container/garray/garray_sorted_any.go | 83 +++++++------ container/garray/garray_sorted_int.go | 95 ++++++++------- container/garray/garray_sorted_str.go | 91 +++++++------- container/garray/garray_z_example_any_test.go | 10 +- os/glog/glog.go | 2 +- os/glog/glog_api.go | 1 + os/glog/glog_config.go | 5 + os/glog/glog_logger.go | 114 ++++++++++-------- os/glog/glog_logger_api.go | 36 +++--- os/glog/glog_logger_config.go | 6 + os/glog/glog_logger_handler.go | 78 ++++++++++++ os/glog/glog_logger_level.go | 1 + os/glog/glog_z_unit_handler_test.go | 80 ++++++++++++ 16 files changed, 561 insertions(+), 334 deletions(-) create mode 100644 os/glog/glog_logger_handler.go create mode 100644 os/glog/glog_z_unit_handler_test.go diff --git a/container/garray/garray_normal_any.go b/container/garray/garray_normal_any.go index 616a59062..985c02fc7 100644 --- a/container/garray/garray_normal_any.go +++ b/container/garray/garray_normal_any.go @@ -30,7 +30,7 @@ type Array struct { } // New creates and returns an empty array. -// The parameter is used to specify whether using array in concurrent-safety, +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func New(safe ...bool) *Array { return NewArraySize(0, 0, safe...) @@ -42,7 +42,7 @@ func NewArray(safe ...bool) *Array { } // NewArraySize create and returns an array with given size and cap. -// The parameter is used to specify whether using array in concurrent-safety, +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewArraySize(size int, cap int, safe ...bool) *Array { return &Array{ @@ -51,8 +51,8 @@ func NewArraySize(size int, cap int, safe ...bool) *Array { } } -// NewArrayRange creates and returns a array by a range from to -// with step value . +// NewArrayRange creates and returns a array by a range from `start` to `end` +// with step value `step`. func NewArrayRange(start, end, step int, safe ...bool) *Array { if step == 0 { panic(fmt.Sprintf(`invalid step value: %d`, step)) @@ -66,18 +66,20 @@ func NewArrayRange(start, end, step int, safe ...bool) *Array { return NewArrayFrom(slice, safe...) } +// NewFrom is alias of NewArrayFrom. // See NewArrayFrom. func NewFrom(array []interface{}, safe ...bool) *Array { return NewArrayFrom(array, safe...) } +// NewFromCopy is alias of NewArrayFromCopy. // See NewArrayFromCopy. func NewFromCopy(array []interface{}, safe ...bool) *Array { return NewArrayFromCopy(array, safe...) } -// NewArrayFrom creates and returns an array with given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewArrayFrom creates and returns an array with given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewArrayFrom(array []interface{}, safe ...bool) *Array { return &Array{ @@ -86,8 +88,8 @@ func NewArrayFrom(array []interface{}, safe ...bool) *Array { } } -// NewArrayFromCopy creates and returns an array from a copy of given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewArrayFromCopy creates and returns an array from a copy of given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewArrayFromCopy(array []interface{}, safe ...bool) *Array { newArray := make([]interface{}, len(array)) @@ -98,8 +100,15 @@ func NewArrayFromCopy(array []interface{}, safe ...bool) *Array { } } +// At returns the value by the specified index. +// If the given `index` is out of range of the array, it returns `nil`. +func (a *Array) At(index int) (value interface{}) { + value, _ = a.Get(index) + return +} + // Get returns the value by the specified index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *Array) Get(index int) (value interface{}, found bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -120,7 +129,7 @@ func (a *Array) Set(index int, value interface{}) error { return nil } -// SetArray sets the underlying slice array with the given . +// SetArray sets the underlying slice array with the given `array`. func (a *Array) SetArray(array []interface{}) *Array { a.mu.Lock() defer a.mu.Unlock() @@ -128,7 +137,7 @@ func (a *Array) SetArray(array []interface{}) *Array { return a } -// Replace replaces the array items by given from the beginning of array. +// Replace replaces the array items by given `array` from the beginning of array. func (a *Array) Replace(array []interface{}) *Array { a.mu.Lock() defer a.mu.Unlock() @@ -152,7 +161,7 @@ func (a *Array) Sum() (sum int) { return } -// SortFunc sorts the array by custom function . +// SortFunc sorts the array by custom function `less`. func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array { a.mu.Lock() defer a.mu.Unlock() @@ -162,7 +171,7 @@ func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array { return a } -// InsertBefore inserts the to the front of . +// InsertBefore inserts the `value` to the front of `index`. func (a *Array) InsertBefore(index int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() @@ -175,7 +184,7 @@ func (a *Array) InsertBefore(index int, value interface{}) error { return nil } -// InsertAfter inserts the to the back of . +// InsertAfter inserts the `value` to the back of `index`. func (a *Array) InsertAfter(index int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() @@ -189,7 +198,7 @@ func (a *Array) InsertAfter(index int, value interface{}) error { } // Remove removes an item by index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *Array) Remove(index int) (value interface{}, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -247,14 +256,14 @@ func (a *Array) PushRight(value ...interface{}) *Array { } // PopRand randomly pops and return an item out of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *Array) PopRand() (value interface{}, found bool) { a.mu.Lock() defer a.mu.Unlock() return a.doRemoveWithoutLock(grand.Intn(len(a.array))) } -// PopRands randomly pops and returns items out of array. +// PopRands randomly pops and returns `size` items out of array. func (a *Array) PopRands(size int) []interface{} { a.mu.Lock() defer a.mu.Unlock() @@ -272,7 +281,7 @@ func (a *Array) PopRands(size int) []interface{} { } // PopLeft pops and returns an item from the beginning of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *Array) PopLeft() (value interface{}, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -285,7 +294,7 @@ func (a *Array) PopLeft() (value interface{}, found bool) { } // PopRight pops and returns an item from the end of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *Array) PopRight() (value interface{}, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -298,7 +307,7 @@ func (a *Array) PopRight() (value interface{}, found bool) { return value, true } -// PopLefts pops and returns items from the beginning of array. +// PopLefts pops and returns `size` items from the beginning of array. func (a *Array) PopLefts(size int) []interface{} { a.mu.Lock() defer a.mu.Unlock() @@ -315,7 +324,7 @@ func (a *Array) PopLefts(size int) []interface{} { return value } -// PopRights pops and returns items from the end of array. +// PopRights pops and returns `size` items from the end of array. func (a *Array) PopRights(size int) []interface{} { a.mu.Lock() defer a.mu.Unlock() @@ -337,8 +346,8 @@ func (a *Array) PopRights(size int) []interface{} { // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. // -// If is negative, then the offset will start from the end of array. -// If is omitted, then the sequence will have everything from start up +// If `end` is negative, then the offset will start from the end of array. +// If `end` is omitted, then the sequence will have everything from start up // until the end of the array. func (a *Array) Range(start int, end ...int) []interface{} { a.mu.RLock() @@ -364,7 +373,7 @@ func (a *Array) Range(start int, end ...int) []interface{} { } // SubSlice returns a slice of elements from the array as specified -// by the and parameters. +// by the `offset` and `size` parameters. // If in concurrent safe usage, it returns a copy of the slice; else a pointer. // // If offset is non-negative, the sequence will start at that offset in the array. @@ -471,7 +480,7 @@ func (a *Array) Contains(value interface{}) bool { return a.Search(value) != -1 } -// Search searches array by , returns the index of , +// Search searches array by `value`, returns the index of `value`, // or returns -1 if not exists. func (a *Array) Search(value interface{}) int { a.mu.RLock() @@ -506,7 +515,7 @@ func (a *Array) Unique() *Array { return a } -// LockFunc locks writing by callback function . +// LockFunc locks writing by callback function `f`. func (a *Array) LockFunc(f func(array []interface{})) *Array { a.mu.Lock() defer a.mu.Unlock() @@ -514,7 +523,7 @@ func (a *Array) LockFunc(f func(array []interface{})) *Array { return a } -// RLockFunc locks reading by callback function . +// RLockFunc locks reading by callback function `f`. func (a *Array) RLockFunc(f func(array []interface{})) *Array { a.mu.RLock() defer a.mu.RUnlock() @@ -522,16 +531,16 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array { return a } -// Merge merges into current array. -// The parameter can be any garray or slice type. +// Merge merges `array` into current array. +// The parameter `array` can be any garray or slice type. // The difference between Merge and Append is Append supports only specified slice type, // but Merge supports more parameter types. func (a *Array) Merge(array interface{}) *Array { return a.Append(gconv.Interfaces(array)...) } -// Fill fills an array with num entries of the value , -// keys starting at the parameter. +// Fill fills an array with num entries of the value `value`, +// keys starting at the `startIndex` parameter. func (a *Array) Fill(startIndex int, num int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() @@ -549,7 +558,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) error { } // Chunk splits an array into multiple arrays, -// the size of each array is determined by . +// the size of each array is determined by `size`. // The last chunk may contain less than size elements. func (a *Array) Chunk(size int) [][]interface{} { if size < 1 { @@ -571,9 +580,9 @@ func (a *Array) Chunk(size int) [][]interface{} { return n } -// Pad pads array to the specified length with . +// Pad pads array to the specified length with `value`. // If size is positive then the array is padded on the right, or negative on the left. -// If the absolute value of is less than or equal to the length of the array +// If the absolute value of `size` is less than or equal to the length of the array // then no padding takes place. func (a *Array) Pad(size int, val interface{}) *Array { a.mu.Lock() @@ -608,7 +617,7 @@ func (a *Array) Rand() (value interface{}, found bool) { return a.array[grand.Intn(len(a.array))], true } -// Rands randomly returns items from array(no deleting). +// Rands randomly returns `size` items from array(no deleting). func (a *Array) Rands(size int) []interface{} { a.mu.RLock() defer a.mu.RUnlock() @@ -642,7 +651,7 @@ func (a *Array) Reverse() *Array { return a } -// Join joins array elements with a string . +// Join joins array elements with a string `glue`. func (a *Array) Join(glue string) string { a.mu.RLock() defer a.mu.RUnlock() @@ -675,8 +684,8 @@ func (a *Array) Iterator(f func(k int, v interface{}) bool) { a.IteratorAsc(f) } -// IteratorAsc iterates the array readonly in ascending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorAsc iterates the array readonly in ascending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -687,8 +696,8 @@ func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) { } } -// IteratorDesc iterates the array readonly in descending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorDesc iterates the array readonly in descending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -784,7 +793,7 @@ func (a *Array) FilterEmpty() *Array { return a } -// Walk applies a user supplied function to every item of array. +// Walk applies a user supplied function `f` to every item of array. func (a *Array) Walk(f func(value interface{}) interface{}) *Array { a.mu.Lock() defer a.mu.Unlock() diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index 10dbe16fe..05a10f0fe 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -28,14 +28,14 @@ type IntArray struct { } // NewIntArray creates and returns an empty array. -// The parameter is used to specify whether using array in concurrent-safety, +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewIntArray(safe ...bool) *IntArray { return NewIntArraySize(0, 0, safe...) } // NewIntArraySize create and returns an array with given size and cap. -// The parameter is used to specify whether using array in concurrent-safety, +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewIntArraySize(size int, cap int, safe ...bool) *IntArray { return &IntArray{ @@ -44,8 +44,8 @@ func NewIntArraySize(size int, cap int, safe ...bool) *IntArray { } } -// NewIntArrayRange creates and returns a array by a range from to -// with step value . +// NewIntArrayRange creates and returns a array by a range from `start` to `end` +// with step value `step`. func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray { if step == 0 { panic(fmt.Sprintf(`invalid step value: %d`, step)) @@ -59,8 +59,8 @@ func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray { return NewIntArrayFrom(slice, safe...) } -// NewIntArrayFrom creates and returns an array with given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewIntArrayFrom creates and returns an array with given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewIntArrayFrom(array []int, safe ...bool) *IntArray { return &IntArray{ @@ -69,8 +69,8 @@ func NewIntArrayFrom(array []int, safe ...bool) *IntArray { } } -// NewIntArrayFromCopy creates and returns an array from a copy of given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewIntArrayFromCopy creates and returns an array from a copy of given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray { newArray := make([]int, len(array)) @@ -81,8 +81,15 @@ func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray { } } +// At returns the value by the specified index. +// If the given `index` is out of range of the array, it returns `0`. +func (a *IntArray) At(index int) (value int) { + value, _ = a.Get(index) + return +} + // Get returns the value by the specified index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *IntArray) Get(index int) (value int, found bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -103,7 +110,7 @@ func (a *IntArray) Set(index int, value int) error { return nil } -// SetArray sets the underlying slice array with the given . +// SetArray sets the underlying slice array with the given `array`. func (a *IntArray) SetArray(array []int) *IntArray { a.mu.Lock() defer a.mu.Unlock() @@ -111,7 +118,7 @@ func (a *IntArray) SetArray(array []int) *IntArray { return a } -// Replace replaces the array items by given from the beginning of array. +// Replace replaces the array items by given `array` from the beginning of array. func (a *IntArray) Replace(array []int) *IntArray { a.mu.Lock() defer a.mu.Unlock() @@ -136,7 +143,7 @@ func (a *IntArray) Sum() (sum int) { } // Sort sorts the array in increasing order. -// The parameter controls whether sort in increasing order(default) or decreasing order. +// The parameter `reverse` controls whether sort in increasing order(default) or decreasing order. func (a *IntArray) Sort(reverse ...bool) *IntArray { a.mu.Lock() defer a.mu.Unlock() @@ -153,7 +160,7 @@ func (a *IntArray) Sort(reverse ...bool) *IntArray { return a } -// SortFunc sorts the array by custom function . +// SortFunc sorts the array by custom function `less`. func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray { a.mu.Lock() defer a.mu.Unlock() @@ -163,7 +170,7 @@ func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray { return a } -// InsertBefore inserts the to the front of . +// InsertBefore inserts the `value` to the front of `index`. func (a *IntArray) InsertBefore(index int, value int) error { a.mu.Lock() defer a.mu.Unlock() @@ -176,7 +183,7 @@ func (a *IntArray) InsertBefore(index int, value int) error { return nil } -// InsertAfter inserts the to the back of . +// InsertAfter inserts the `value` to the back of `index`. func (a *IntArray) InsertAfter(index int, value int) error { a.mu.Lock() defer a.mu.Unlock() @@ -190,7 +197,7 @@ func (a *IntArray) InsertAfter(index int, value int) error { } // Remove removes an item by index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *IntArray) Remove(index int) (value int, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -248,7 +255,7 @@ func (a *IntArray) PushRight(value ...int) *IntArray { } // PopLeft pops and returns an item from the beginning of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *IntArray) PopLeft() (value int, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -261,7 +268,7 @@ func (a *IntArray) PopLeft() (value int, found bool) { } // PopRight pops and returns an item from the end of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *IntArray) PopRight() (value int, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -275,16 +282,16 @@ func (a *IntArray) PopRight() (value int, found bool) { } // PopRand randomly pops and return an item out of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *IntArray) PopRand() (value int, found bool) { a.mu.Lock() defer a.mu.Unlock() return a.doRemoveWithoutLock(grand.Intn(len(a.array))) } -// PopRands randomly pops and returns items out of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopRands randomly pops and returns `size` items out of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *IntArray) PopRands(size int) []int { a.mu.Lock() defer a.mu.Unlock() @@ -301,9 +308,9 @@ func (a *IntArray) PopRands(size int) []int { return array } -// PopLefts pops and returns items from the beginning of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopLefts pops and returns `size` items from the beginning of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *IntArray) PopLefts(size int) []int { a.mu.Lock() defer a.mu.Unlock() @@ -320,9 +327,9 @@ func (a *IntArray) PopLefts(size int) []int { return value } -// PopRights pops and returns items from the end of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopRights pops and returns `size` items from the end of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *IntArray) PopRights(size int) []int { a.mu.Lock() defer a.mu.Unlock() @@ -344,8 +351,8 @@ func (a *IntArray) PopRights(size int) []int { // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. // -// If is negative, then the offset will start from the end of array. -// If is omitted, then the sequence will have everything from start up +// If `end` is negative, then the offset will start from the end of array. +// If `end` is omitted, then the sequence will have everything from start up // until the end of the array. func (a *IntArray) Range(start int, end ...int) []int { a.mu.RLock() @@ -371,7 +378,7 @@ func (a *IntArray) Range(start int, end ...int) []int { } // SubSlice returns a slice of elements from the array as specified -// by the and parameters. +// by the `offset` and `size` parameters. // If in concurrent safe usage, it returns a copy of the slice; else a pointer. // // If offset is non-negative, the sequence will start at that offset in the array. @@ -487,7 +494,7 @@ func (a *IntArray) Contains(value int) bool { return a.Search(value) != -1 } -// Search searches array by , returns the index of , +// Search searches array by `value`, returns the index of `value`, // or returns -1 if not exists. func (a *IntArray) Search(value int) int { a.mu.RLock() @@ -522,7 +529,7 @@ func (a *IntArray) Unique() *IntArray { return a } -// LockFunc locks writing by callback function . +// LockFunc locks writing by callback function `f`. func (a *IntArray) LockFunc(f func(array []int)) *IntArray { a.mu.Lock() defer a.mu.Unlock() @@ -530,7 +537,7 @@ func (a *IntArray) LockFunc(f func(array []int)) *IntArray { return a } -// RLockFunc locks reading by callback function . +// RLockFunc locks reading by callback function `f`. func (a *IntArray) RLockFunc(f func(array []int)) *IntArray { a.mu.RLock() defer a.mu.RUnlock() @@ -538,16 +545,16 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray { return a } -// Merge merges into current array. -// The parameter can be any garray or slice type. +// Merge merges `array` into current array. +// The parameter `array` can be any garray or slice type. // The difference between Merge and Append is Append supports only specified slice type, // but Merge supports more parameter types. func (a *IntArray) Merge(array interface{}) *IntArray { return a.Append(gconv.Ints(array)...) } -// Fill fills an array with num entries of the value , -// keys starting at the parameter. +// Fill fills an array with num entries of the value `value`, +// keys starting at the `startIndex` parameter. func (a *IntArray) Fill(startIndex int, num int, value int) error { a.mu.Lock() defer a.mu.Unlock() @@ -565,7 +572,7 @@ func (a *IntArray) Fill(startIndex int, num int, value int) error { } // Chunk splits an array into multiple arrays, -// the size of each array is determined by . +// the size of each array is determined by `size`. // The last chunk may contain less than size elements. func (a *IntArray) Chunk(size int) [][]int { if size < 1 { @@ -587,9 +594,9 @@ func (a *IntArray) Chunk(size int) [][]int { return n } -// Pad pads array to the specified length with . +// Pad pads array to the specified length with `value`. // If size is positive then the array is padded on the right, or negative on the left. -// If the absolute value of is less than or equal to the length of the array +// If the absolute value of `size` is less than or equal to the length of the array // then no padding takes place. func (a *IntArray) Pad(size int, value int) *IntArray { a.mu.Lock() @@ -624,7 +631,7 @@ func (a *IntArray) Rand() (value int, found bool) { return a.array[grand.Intn(len(a.array))], true } -// Rands randomly returns items from array(no deleting). +// Rands randomly returns `size` items from array(no deleting). func (a *IntArray) Rands(size int) []int { a.mu.RLock() defer a.mu.RUnlock() @@ -658,7 +665,7 @@ func (a *IntArray) Reverse() *IntArray { return a } -// Join joins array elements with a string . +// Join joins array elements with a string `glue`. func (a *IntArray) Join(glue string) string { a.mu.RLock() defer a.mu.RUnlock() @@ -691,8 +698,8 @@ func (a *IntArray) Iterator(f func(k int, v int) bool) { a.IteratorAsc(f) } -// IteratorAsc iterates the array readonly in ascending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorAsc iterates the array readonly in ascending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *IntArray) IteratorAsc(f func(k int, v int) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -703,8 +710,8 @@ func (a *IntArray) IteratorAsc(f func(k int, v int) bool) { } } -// IteratorDesc iterates the array readonly in descending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorDesc iterates the array readonly in descending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *IntArray) IteratorDesc(f func(k int, v int) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -768,7 +775,7 @@ func (a *IntArray) FilterEmpty() *IntArray { return a } -// Walk applies a user supplied function to every item of array. +// Walk applies a user supplied function `f` to every item of array. func (a *IntArray) Walk(f func(value int) int) *IntArray { a.mu.Lock() defer a.mu.Unlock() diff --git a/container/garray/garray_normal_str.go b/container/garray/garray_normal_str.go index 523a75123..f1754e80d 100644 --- a/container/garray/garray_normal_str.go +++ b/container/garray/garray_normal_str.go @@ -30,14 +30,14 @@ type StrArray struct { } // NewStrArray creates and returns an empty array. -// The parameter is used to specify whether using array in concurrent-safety, +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewStrArray(safe ...bool) *StrArray { return NewStrArraySize(0, 0, safe...) } // NewStrArraySize create and returns an array with given size and cap. -// The parameter is used to specify whether using array in concurrent-safety, +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewStrArraySize(size int, cap int, safe ...bool) *StrArray { return &StrArray{ @@ -46,8 +46,8 @@ func NewStrArraySize(size int, cap int, safe ...bool) *StrArray { } } -// NewStrArrayFrom creates and returns an array with given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewStrArrayFrom creates and returns an array with given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewStrArrayFrom(array []string, safe ...bool) *StrArray { return &StrArray{ @@ -56,8 +56,8 @@ func NewStrArrayFrom(array []string, safe ...bool) *StrArray { } } -// NewStrArrayFromCopy creates and returns an array from a copy of given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewStrArrayFromCopy creates and returns an array from a copy of given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray { newArray := make([]string, len(array)) @@ -68,8 +68,15 @@ func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray { } } +// At returns the value by the specified index. +// If the given `index` is out of range of the array, it returns an empty string. +func (a *StrArray) At(index int) (value string) { + value, _ = a.Get(index) + return +} + // Get returns the value by the specified index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *StrArray) Get(index int) (value string, found bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -90,7 +97,7 @@ func (a *StrArray) Set(index int, value string) error { return nil } -// SetArray sets the underlying slice array with the given . +// SetArray sets the underlying slice array with the given `array`. func (a *StrArray) SetArray(array []string) *StrArray { a.mu.Lock() defer a.mu.Unlock() @@ -98,7 +105,7 @@ func (a *StrArray) SetArray(array []string) *StrArray { return a } -// Replace replaces the array items by given from the beginning of array. +// Replace replaces the array items by given `array` from the beginning of array. func (a *StrArray) Replace(array []string) *StrArray { a.mu.Lock() defer a.mu.Unlock() @@ -123,7 +130,7 @@ func (a *StrArray) Sum() (sum int) { } // Sort sorts the array in increasing order. -// The parameter controls whether sort +// The parameter `reverse` controls whether sort // in increasing order(default) or decreasing order func (a *StrArray) Sort(reverse ...bool) *StrArray { a.mu.Lock() @@ -141,7 +148,7 @@ func (a *StrArray) Sort(reverse ...bool) *StrArray { return a } -// SortFunc sorts the array by custom function . +// SortFunc sorts the array by custom function `less`. func (a *StrArray) SortFunc(less func(v1, v2 string) bool) *StrArray { a.mu.Lock() defer a.mu.Unlock() @@ -151,7 +158,7 @@ func (a *StrArray) SortFunc(less func(v1, v2 string) bool) *StrArray { return a } -// InsertBefore inserts the to the front of . +// InsertBefore inserts the `value` to the front of `index`. func (a *StrArray) InsertBefore(index int, value string) error { a.mu.Lock() defer a.mu.Unlock() @@ -164,7 +171,7 @@ func (a *StrArray) InsertBefore(index int, value string) error { return nil } -// InsertAfter inserts the to the back of . +// InsertAfter inserts the `value` to the back of `index`. func (a *StrArray) InsertAfter(index int, value string) error { a.mu.Lock() defer a.mu.Unlock() @@ -178,7 +185,7 @@ func (a *StrArray) InsertAfter(index int, value string) error { } // Remove removes an item by index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *StrArray) Remove(index int) (value string, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -236,7 +243,7 @@ func (a *StrArray) PushRight(value ...string) *StrArray { } // PopLeft pops and returns an item from the beginning of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *StrArray) PopLeft() (value string, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -249,7 +256,7 @@ func (a *StrArray) PopLeft() (value string, found bool) { } // PopRight pops and returns an item from the end of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *StrArray) PopRight() (value string, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -263,16 +270,16 @@ func (a *StrArray) PopRight() (value string, found bool) { } // PopRand randomly pops and return an item out of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *StrArray) PopRand() (value string, found bool) { a.mu.Lock() defer a.mu.Unlock() return a.doRemoveWithoutLock(grand.Intn(len(a.array))) } -// PopRands randomly pops and returns items out of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopRands randomly pops and returns `size` items out of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *StrArray) PopRands(size int) []string { a.mu.Lock() defer a.mu.Unlock() @@ -289,9 +296,9 @@ func (a *StrArray) PopRands(size int) []string { return array } -// PopLefts pops and returns items from the beginning of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopLefts pops and returns `size` items from the beginning of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *StrArray) PopLefts(size int) []string { a.mu.Lock() defer a.mu.Unlock() @@ -308,9 +315,9 @@ func (a *StrArray) PopLefts(size int) []string { return value } -// PopRights pops and returns items from the end of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopRights pops and returns `size` items from the end of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *StrArray) PopRights(size int) []string { a.mu.Lock() defer a.mu.Unlock() @@ -332,8 +339,8 @@ func (a *StrArray) PopRights(size int) []string { // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. // -// If is negative, then the offset will start from the end of array. -// If is omitted, then the sequence will have everything from start up +// If `end` is negative, then the offset will start from the end of array. +// If `end` is omitted, then the sequence will have everything from start up // until the end of the array. func (a *StrArray) Range(start int, end ...int) []string { a.mu.RLock() @@ -359,7 +366,7 @@ func (a *StrArray) Range(start int, end ...int) []string { } // SubSlice returns a slice of elements from the array as specified -// by the and parameters. +// by the `offset` and `size` parameters. // If in concurrent safe usage, it returns a copy of the slice; else a pointer. // // If offset is non-negative, the sequence will start at that offset in the array. @@ -491,7 +498,7 @@ func (a *StrArray) ContainsI(value string) bool { return false } -// Search searches array by , returns the index of , +// Search searches array by `value`, returns the index of `value`, // or returns -1 if not exists. func (a *StrArray) Search(value string) int { a.mu.RLock() @@ -526,7 +533,7 @@ func (a *StrArray) Unique() *StrArray { return a } -// LockFunc locks writing by callback function . +// LockFunc locks writing by callback function `f`. func (a *StrArray) LockFunc(f func(array []string)) *StrArray { a.mu.Lock() defer a.mu.Unlock() @@ -534,7 +541,7 @@ func (a *StrArray) LockFunc(f func(array []string)) *StrArray { return a } -// RLockFunc locks reading by callback function . +// RLockFunc locks reading by callback function `f`. func (a *StrArray) RLockFunc(f func(array []string)) *StrArray { a.mu.RLock() defer a.mu.RUnlock() @@ -542,16 +549,16 @@ func (a *StrArray) RLockFunc(f func(array []string)) *StrArray { return a } -// Merge merges into current array. -// The parameter can be any garray or slice type. +// Merge merges `array` into current array. +// The parameter `array` can be any garray or slice type. // The difference between Merge and Append is Append supports only specified slice type, // but Merge supports more parameter types. func (a *StrArray) Merge(array interface{}) *StrArray { return a.Append(gconv.Strings(array)...) } -// Fill fills an array with num entries of the value , -// keys starting at the parameter. +// Fill fills an array with num entries of the value `value`, +// keys starting at the `startIndex` parameter. func (a *StrArray) Fill(startIndex int, num int, value string) error { a.mu.Lock() defer a.mu.Unlock() @@ -569,7 +576,7 @@ func (a *StrArray) Fill(startIndex int, num int, value string) error { } // Chunk splits an array into multiple arrays, -// the size of each array is determined by . +// the size of each array is determined by `size`. // The last chunk may contain less than size elements. func (a *StrArray) Chunk(size int) [][]string { if size < 1 { @@ -591,9 +598,9 @@ func (a *StrArray) Chunk(size int) [][]string { return n } -// Pad pads array to the specified length with . +// Pad pads array to the specified length with `value`. // If size is positive then the array is padded on the right, or negative on the left. -// If the absolute value of is less than or equal to the length of the array +// If the absolute value of `size` is less than or equal to the length of the array // then no padding takes place. func (a *StrArray) Pad(size int, value string) *StrArray { a.mu.Lock() @@ -628,7 +635,7 @@ func (a *StrArray) Rand() (value string, found bool) { return a.array[grand.Intn(len(a.array))], true } -// Rands randomly returns items from array(no deleting). +// Rands randomly returns `size` items from array(no deleting). func (a *StrArray) Rands(size int) []string { a.mu.RLock() defer a.mu.RUnlock() @@ -662,7 +669,7 @@ func (a *StrArray) Reverse() *StrArray { return a } -// Join joins array elements with a string . +// Join joins array elements with a string `glue`. func (a *StrArray) Join(glue string) string { a.mu.RLock() defer a.mu.RUnlock() @@ -695,8 +702,8 @@ func (a *StrArray) Iterator(f func(k int, v string) bool) { a.IteratorAsc(f) } -// IteratorAsc iterates the array readonly in ascending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorAsc iterates the array readonly in ascending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *StrArray) IteratorAsc(f func(k int, v string) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -707,8 +714,8 @@ func (a *StrArray) IteratorAsc(f func(k int, v string) bool) { } } -// IteratorDesc iterates the array readonly in descending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorDesc iterates the array readonly in descending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *StrArray) IteratorDesc(f func(k int, v string) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -783,7 +790,7 @@ func (a *StrArray) FilterEmpty() *StrArray { return a } -// Walk applies a user supplied function to every item of array. +// Walk applies a user supplied function `f` to every item of array. func (a *StrArray) Walk(f func(value string) string) *StrArray { a.mu.Lock() defer a.mu.Unlock() diff --git a/container/garray/garray_sorted_any.go b/container/garray/garray_sorted_any.go index 91d7fa073..cd14d6252 100644 --- a/container/garray/garray_sorted_any.go +++ b/container/garray/garray_sorted_any.go @@ -34,8 +34,8 @@ type SortedArray struct { } // NewSortedArray creates and returns an empty sorted array. -// The parameter is used to specify whether using array in concurrent-safety, which is false in default. -// The parameter used to compare values to sort in array, +// The parameter `safe` is used to specify whether using array in concurrent-safety, which is false in default. +// The parameter `comparator` used to compare values to sort in array, // if it returns value < 0, means v1 < v2; the v1 will be inserted before v2; // if it returns value = 0, means v1 = v2; the v1 will be replaced by v2; // if it returns value > 0, means v1 > v2; the v1 will be inserted after v2; @@ -44,7 +44,7 @@ func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *Sorted } // NewSortedArraySize create and returns an sorted array with given size and cap. -// The parameter is used to specify whether using array in concurrent-safety, +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray { return &SortedArray{ @@ -54,8 +54,8 @@ func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ... } } -// NewSortedArrayRange creates and returns a array by a range from to -// with step value . +// NewSortedArrayRange creates and returns a array by a range from `start` to `end` +// with step value `step`. func NewSortedArrayRange(start, end, step int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray { if step == 0 { panic(fmt.Sprintf(`invalid step value: %d`, step)) @@ -69,8 +69,8 @@ func NewSortedArrayRange(start, end, step int, comparator func(a, b interface{}) return NewSortedArrayFrom(slice, comparator, safe...) } -// NewSortedArrayFrom creates and returns an sorted array with given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewSortedArrayFrom creates and returns an sorted array with given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray { a := NewSortedArraySize(0, comparator, safe...) @@ -81,8 +81,8 @@ func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) i return a } -// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewSortedArrayFromCopy(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray { newArray := make([]interface{}, len(array)) @@ -90,7 +90,14 @@ func NewSortedArrayFromCopy(array []interface{}, comparator func(a, b interface{ return NewSortedArrayFrom(newArray, comparator, safe...) } -// SetArray sets the underlying slice array with the given . +// At returns the value by the specified index. +// If the given `index` is out of range of the array, it returns `nil`. +func (a *SortedArray) At(index int) (value interface{}) { + value, _ = a.Get(index) + return +} + +// SetArray sets the underlying slice array with the given `array`. func (a *SortedArray) SetArray(array []interface{}) *SortedArray { a.mu.Lock() defer a.mu.Unlock() @@ -113,7 +120,7 @@ func (a *SortedArray) SetComparator(comparator func(a, b interface{}) int) { } // Sort sorts the array in increasing order. -// The parameter controls whether sort +// The parameter `reverse` controls whether sort // in increasing order(default) or decreasing order func (a *SortedArray) Sort() *SortedArray { a.mu.Lock() @@ -157,7 +164,7 @@ func (a *SortedArray) Append(values ...interface{}) *SortedArray { } // Get returns the value by the specified index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *SortedArray) Get(index int) (value interface{}, found bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -168,7 +175,7 @@ func (a *SortedArray) Get(index int) (value interface{}, found bool) { } // Remove removes an item by index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *SortedArray) Remove(index int) (value interface{}, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -209,7 +216,7 @@ func (a *SortedArray) RemoveValue(value interface{}) bool { } // PopLeft pops and returns an item from the beginning of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *SortedArray) PopLeft() (value interface{}, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -222,7 +229,7 @@ func (a *SortedArray) PopLeft() (value interface{}, found bool) { } // PopRight pops and returns an item from the end of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *SortedArray) PopRight() (value interface{}, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -236,14 +243,14 @@ func (a *SortedArray) PopRight() (value interface{}, found bool) { } // PopRand randomly pops and return an item out of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *SortedArray) PopRand() (value interface{}, found bool) { a.mu.Lock() defer a.mu.Unlock() return a.doRemoveWithoutLock(grand.Intn(len(a.array))) } -// PopRands randomly pops and returns items out of array. +// PopRands randomly pops and returns `size` items out of array. func (a *SortedArray) PopRands(size int) []interface{} { a.mu.Lock() defer a.mu.Unlock() @@ -260,7 +267,7 @@ func (a *SortedArray) PopRands(size int) []interface{} { return array } -// PopLefts pops and returns items from the beginning of array. +// PopLefts pops and returns `size` items from the beginning of array. func (a *SortedArray) PopLefts(size int) []interface{} { a.mu.Lock() defer a.mu.Unlock() @@ -277,7 +284,7 @@ func (a *SortedArray) PopLefts(size int) []interface{} { return value } -// PopRights pops and returns items from the end of array. +// PopRights pops and returns `size` items from the end of array. func (a *SortedArray) PopRights(size int) []interface{} { a.mu.Lock() defer a.mu.Unlock() @@ -299,8 +306,8 @@ func (a *SortedArray) PopRights(size int) []interface{} { // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. // -// If is negative, then the offset will start from the end of array. -// If is omitted, then the sequence will have everything from start up +// If `end` is negative, then the offset will start from the end of array. +// If `end` is omitted, then the sequence will have everything from start up // until the end of the array. func (a *SortedArray) Range(start int, end ...int) []interface{} { a.mu.RLock() @@ -326,7 +333,7 @@ func (a *SortedArray) Range(start int, end ...int) []interface{} { } // SubSlice returns a slice of elements from the array as specified -// by the and parameters. +// by the `offset` and `size` parameters. // If in concurrent safe usage, it returns a copy of the slice; else a pointer. // // If offset is non-negative, the sequence will start at that offset in the array. @@ -419,7 +426,7 @@ func (a *SortedArray) Contains(value interface{}) bool { return a.Search(value) != -1 } -// Search searches array by , returns the index of , +// Search searches array by `value`, returns the index of `value`, // or returns -1 if not exists. func (a *SortedArray) Search(value interface{}) (index int) { if i, r := a.binSearch(value, true); r == 0 { @@ -430,9 +437,9 @@ func (a *SortedArray) Search(value interface{}) (index int) { // Binary search. // It returns the last compared index and the result. -// If equals to 0, it means the value at is equals to . -// If lesser than 0, it means the value at is lesser than . -// If greater than 0, it means the value at is greater than . +// If `result` equals to 0, it means the value at `index` is equals to `value`. +// If `result` lesser than 0, it means the value at `index` is lesser than `value`. +// If `result` greater than 0, it means the value at `index` is greater than `value`. func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result int) { if lock { a.mu.RLock() @@ -512,7 +519,7 @@ func (a *SortedArray) Clear() *SortedArray { return a } -// LockFunc locks writing by callback function . +// LockFunc locks writing by callback function `f`. func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray { a.mu.Lock() defer a.mu.Unlock() @@ -526,7 +533,7 @@ func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray { return a } -// RLockFunc locks reading by callback function . +// RLockFunc locks reading by callback function `f`. func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray { a.mu.RLock() defer a.mu.RUnlock() @@ -534,8 +541,8 @@ func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray { return a } -// Merge merges into current array. -// The parameter can be any garray or slice type. +// Merge merges `array` into current array. +// The parameter `array` can be any garray or slice type. // The difference between Merge and Append is Append supports only specified slice type, // but Merge supports more parameter types. func (a *SortedArray) Merge(array interface{}) *SortedArray { @@ -543,7 +550,7 @@ func (a *SortedArray) Merge(array interface{}) *SortedArray { } // Chunk splits an array into multiple arrays, -// the size of each array is determined by . +// the size of each array is determined by `size`. // The last chunk may contain less than size elements. func (a *SortedArray) Chunk(size int) [][]interface{} { if size < 1 { @@ -575,7 +582,7 @@ func (a *SortedArray) Rand() (value interface{}, found bool) { return a.array[grand.Intn(len(a.array))], true } -// Rands randomly returns items from array(no deleting). +// Rands randomly returns `size` items from array(no deleting). func (a *SortedArray) Rands(size int) []interface{} { a.mu.RLock() defer a.mu.RUnlock() @@ -589,7 +596,7 @@ func (a *SortedArray) Rands(size int) []interface{} { return array } -// Join joins array elements with a string . +// Join joins array elements with a string `glue`. func (a *SortedArray) Join(glue string) string { a.mu.RLock() defer a.mu.RUnlock() @@ -622,8 +629,8 @@ func (a *SortedArray) Iterator(f func(k int, v interface{}) bool) { a.IteratorAsc(f) } -// IteratorAsc iterates the array readonly in ascending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorAsc iterates the array readonly in ascending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -634,8 +641,8 @@ func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) { } } -// IteratorDesc iterates the array readonly in descending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorDesc iterates the array readonly in descending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *SortedArray) IteratorDesc(f func(k int, v interface{}) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -761,7 +768,7 @@ func (a *SortedArray) FilterEmpty() *SortedArray { return a } -// Walk applies a user supplied function to every item of array. +// Walk applies a user supplied function `f` to every item of array. func (a *SortedArray) Walk(f func(value interface{}) interface{}) *SortedArray { a.mu.Lock() defer a.mu.Unlock() diff --git a/container/garray/garray_sorted_int.go b/container/garray/garray_sorted_int.go index 6b96b2c3a..f250d989c 100644 --- a/container/garray/garray_sorted_int.go +++ b/container/garray/garray_sorted_int.go @@ -31,14 +31,14 @@ type SortedIntArray struct { } // NewSortedIntArray creates and returns an empty sorted array. -// The parameter is used to specify whether using array in concurrent-safety, +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewSortedIntArray(safe ...bool) *SortedIntArray { return NewSortedIntArraySize(0, safe...) } // NewSortedIntArrayComparator creates and returns an empty sorted array with specified comparator. -// The parameter is used to specify whether using array in concurrent-safety which is false in default. +// The parameter `safe` is used to specify whether using array in concurrent-safety which is false in default. func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *SortedIntArray { array := NewSortedIntArray(safe...) array.comparator = comparator @@ -46,7 +46,7 @@ func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *S } // NewSortedIntArraySize create and returns an sorted array with given size and cap. -// The parameter is used to specify whether using array in concurrent-safety, +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray { return &SortedIntArray{ @@ -56,8 +56,8 @@ func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray { } } -// NewSortedIntArrayRange creates and returns a array by a range from to -// with step value . +// NewSortedIntArrayRange creates and returns a array by a range from `start` to `end` +// with step value `step`. func NewSortedIntArrayRange(start, end, step int, safe ...bool) *SortedIntArray { if step == 0 { panic(fmt.Sprintf(`invalid step value: %d`, step)) @@ -71,8 +71,8 @@ func NewSortedIntArrayRange(start, end, step int, safe ...bool) *SortedIntArray return NewSortedIntArrayFrom(slice, safe...) } -// NewIntArrayFrom creates and returns an sorted array with given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewSortedIntArrayFrom creates and returns an sorted array with given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewSortedIntArrayFrom(array []int, safe ...bool) *SortedIntArray { a := NewSortedIntArraySize(0, safe...) @@ -81,8 +81,8 @@ func NewSortedIntArrayFrom(array []int, safe ...bool) *SortedIntArray { return a } -// NewSortedIntArrayFromCopy creates and returns an sorted array from a copy of given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewSortedIntArrayFromCopy creates and returns an sorted array from a copy of given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewSortedIntArrayFromCopy(array []int, safe ...bool) *SortedIntArray { newArray := make([]int, len(array)) @@ -90,7 +90,14 @@ func NewSortedIntArrayFromCopy(array []int, safe ...bool) *SortedIntArray { return NewSortedIntArrayFrom(newArray, safe...) } -// SetArray sets the underlying slice array with the given . +// At returns the value by the specified index. +// If the given `index` is out of range of the array, it returns `0`. +func (a *SortedIntArray) At(index int) (value int) { + value, _ = a.Get(index) + return +} + +// SetArray sets the underlying slice array with the given `array`. func (a *SortedIntArray) SetArray(array []int) *SortedIntArray { a.mu.Lock() defer a.mu.Unlock() @@ -100,7 +107,7 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray { } // Sort sorts the array in increasing order. -// The parameter controls whether sort +// The parameter `reverse` controls whether sort // in increasing order(default) or decreasing order. func (a *SortedIntArray) Sort() *SortedIntArray { a.mu.Lock() @@ -142,7 +149,7 @@ func (a *SortedIntArray) Append(values ...int) *SortedIntArray { } // Get returns the value by the specified index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *SortedIntArray) Get(index int) (value int, found bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -153,7 +160,7 @@ func (a *SortedIntArray) Get(index int) (value int, found bool) { } // Remove removes an item by index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *SortedIntArray) Remove(index int) (value int, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -194,7 +201,7 @@ func (a *SortedIntArray) RemoveValue(value int) bool { } // PopLeft pops and returns an item from the beginning of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *SortedIntArray) PopLeft() (value int, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -207,7 +214,7 @@ func (a *SortedIntArray) PopLeft() (value int, found bool) { } // PopRight pops and returns an item from the end of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *SortedIntArray) PopRight() (value int, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -221,16 +228,16 @@ func (a *SortedIntArray) PopRight() (value int, found bool) { } // PopRand randomly pops and return an item out of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *SortedIntArray) PopRand() (value int, found bool) { a.mu.Lock() defer a.mu.Unlock() return a.doRemoveWithoutLock(grand.Intn(len(a.array))) } -// PopRands randomly pops and returns items out of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopRands randomly pops and returns `size` items out of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *SortedIntArray) PopRands(size int) []int { a.mu.Lock() defer a.mu.Unlock() @@ -247,9 +254,9 @@ func (a *SortedIntArray) PopRands(size int) []int { return array } -// PopLefts pops and returns items from the beginning of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopLefts pops and returns `size` items from the beginning of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *SortedIntArray) PopLefts(size int) []int { a.mu.Lock() defer a.mu.Unlock() @@ -266,9 +273,9 @@ func (a *SortedIntArray) PopLefts(size int) []int { return value } -// PopRights pops and returns items from the end of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopRights pops and returns `size` items from the end of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *SortedIntArray) PopRights(size int) []int { a.mu.Lock() defer a.mu.Unlock() @@ -290,8 +297,8 @@ func (a *SortedIntArray) PopRights(size int) []int { // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. // -// If is negative, then the offset will start from the end of array. -// If is omitted, then the sequence will have everything from start up +// If `end` is negative, then the offset will start from the end of array. +// If `end` is omitted, then the sequence will have everything from start up // until the end of the array. func (a *SortedIntArray) Range(start int, end ...int) []int { a.mu.RLock() @@ -317,7 +324,7 @@ func (a *SortedIntArray) Range(start int, end ...int) []int { } // SubSlice returns a slice of elements from the array as specified -// by the and parameters. +// by the `offset` and `size` parameters. // If in concurrent safe usage, it returns a copy of the slice; else a pointer. // // If offset is non-negative, the sequence will start at that offset in the array. @@ -416,7 +423,7 @@ func (a *SortedIntArray) Contains(value int) bool { return a.Search(value) != -1 } -// Search searches array by , returns the index of , +// Search searches array by `value`, returns the index of `value`, // or returns -1 if not exists. func (a *SortedIntArray) Search(value int) (index int) { if i, r := a.binSearch(value, true); r == 0 { @@ -427,9 +434,9 @@ func (a *SortedIntArray) Search(value int) (index int) { // Binary search. // It returns the last compared index and the result. -// If equals to 0, it means the value at is equals to . -// If lesser than 0, it means the value at is lesser than . -// If greater than 0, it means the value at is greater than . +// If `result` equals to 0, it means the value at `index` is equals to `value`. +// If `result` lesser than 0, it means the value at `index` is lesser than `value`. +// If `result` greater than 0, it means the value at `index` is greater than `value`. func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) { if lock { a.mu.RLock() @@ -509,7 +516,7 @@ func (a *SortedIntArray) Clear() *SortedIntArray { return a } -// LockFunc locks writing by callback function . +// LockFunc locks writing by callback function `f`. func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray { a.mu.Lock() defer a.mu.Unlock() @@ -517,7 +524,7 @@ func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray { return a } -// RLockFunc locks reading by callback function . +// RLockFunc locks reading by callback function `f`. func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray { a.mu.RLock() defer a.mu.RUnlock() @@ -525,8 +532,8 @@ func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray { return a } -// Merge merges into current array. -// The parameter can be any garray or slice type. +// Merge merges `array` into current array. +// The parameter `array` can be any garray or slice type. // The difference between Merge and Append is Append supports only specified slice type, // but Merge supports more parameter types. func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray { @@ -534,7 +541,7 @@ func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray { } // Chunk splits an array into multiple arrays, -// the size of each array is determined by . +// the size of each array is determined by `size`. // The last chunk may contain less than size elements. func (a *SortedIntArray) Chunk(size int) [][]int { if size < 1 { @@ -566,7 +573,7 @@ func (a *SortedIntArray) Rand() (value int, found bool) { return a.array[grand.Intn(len(a.array))], true } -// Rands randomly returns items from array(no deleting). +// Rands randomly returns `size` items from array(no deleting). func (a *SortedIntArray) Rands(size int) []int { a.mu.RLock() defer a.mu.RUnlock() @@ -580,7 +587,7 @@ func (a *SortedIntArray) Rands(size int) []int { return array } -// Join joins array elements with a string . +// Join joins array elements with a string `glue`. func (a *SortedIntArray) Join(glue string) string { a.mu.RLock() defer a.mu.RUnlock() @@ -613,8 +620,8 @@ func (a *SortedIntArray) Iterator(f func(k int, v int) bool) { a.IteratorAsc(f) } -// IteratorAsc iterates the array readonly in ascending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorAsc iterates the array readonly in ascending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -625,8 +632,8 @@ func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) { } } -// IteratorDesc iterates the array readonly in descending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorDesc iterates the array readonly in descending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *SortedIntArray) IteratorDesc(f func(k int, v int) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -707,7 +714,7 @@ func (a *SortedIntArray) FilterEmpty() *SortedIntArray { return a } -// Walk applies a user supplied function to every item of array. +// Walk applies a user supplied function `f` to every item of array. func (a *SortedIntArray) Walk(f func(value int) int) *SortedIntArray { a.mu.Lock() defer a.mu.Unlock() diff --git a/container/garray/garray_sorted_str.go b/container/garray/garray_sorted_str.go index 7b8e23b1b..896030e65 100644 --- a/container/garray/garray_sorted_str.go +++ b/container/garray/garray_sorted_str.go @@ -32,14 +32,14 @@ type SortedStrArray struct { } // NewSortedStrArray creates and returns an empty sorted array. -// The parameter is used to specify whether using array in concurrent-safety, +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewSortedStrArray(safe ...bool) *SortedStrArray { return NewSortedStrArraySize(0, safe...) } // NewSortedStrArrayComparator creates and returns an empty sorted array with specified comparator. -// The parameter is used to specify whether using array in concurrent-safety which is false in default. +// The parameter `safe` is used to specify whether using array in concurrent-safety which is false in default. func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool) *SortedStrArray { array := NewSortedStrArray(safe...) array.comparator = comparator @@ -47,7 +47,7 @@ func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool) } // NewSortedStrArraySize create and returns an sorted array with given size and cap. -// The parameter is used to specify whether using array in concurrent-safety, +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray { return &SortedStrArray{ @@ -57,8 +57,8 @@ func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray { } } -// NewSortedStrArrayFrom creates and returns an sorted array with given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewSortedStrArrayFrom creates and returns an sorted array with given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewSortedStrArrayFrom(array []string, safe ...bool) *SortedStrArray { a := NewSortedStrArraySize(0, safe...) @@ -67,8 +67,8 @@ func NewSortedStrArrayFrom(array []string, safe ...bool) *SortedStrArray { return a } -// NewSortedStrArrayFromCopy creates and returns an sorted array from a copy of given slice . -// The parameter is used to specify whether using array in concurrent-safety, +// NewSortedStrArrayFromCopy creates and returns an sorted array from a copy of given slice `array`. +// The parameter `safe` is used to specify whether using array in concurrent-safety, // which is false in default. func NewSortedStrArrayFromCopy(array []string, safe ...bool) *SortedStrArray { newArray := make([]string, len(array)) @@ -76,7 +76,7 @@ func NewSortedStrArrayFromCopy(array []string, safe ...bool) *SortedStrArray { return NewSortedStrArrayFrom(newArray, safe...) } -// SetArray sets the underlying slice array with the given . +// SetArray sets the underlying slice array with the given `array`. func (a *SortedStrArray) SetArray(array []string) *SortedStrArray { a.mu.Lock() defer a.mu.Unlock() @@ -85,8 +85,15 @@ func (a *SortedStrArray) SetArray(array []string) *SortedStrArray { return a } +// At returns the value by the specified index. +// If the given `index` is out of range of the array, it returns an empty string. +func (a *SortedStrArray) At(index int) (value string) { + value, _ = a.Get(index) + return +} + // Sort sorts the array in increasing order. -// The parameter controls whether sort +// The parameter `reverse` controls whether sort // in increasing order(default) or decreasing order. func (a *SortedStrArray) Sort() *SortedStrArray { a.mu.Lock() @@ -128,7 +135,7 @@ func (a *SortedStrArray) Append(values ...string) *SortedStrArray { } // Get returns the value by the specified index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *SortedStrArray) Get(index int) (value string, found bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -139,7 +146,7 @@ func (a *SortedStrArray) Get(index int) (value string, found bool) { } // Remove removes an item by index. -// If the given is out of range of the array, the is false. +// If the given `index` is out of range of the array, the `found` is false. func (a *SortedStrArray) Remove(index int) (value string, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -180,7 +187,7 @@ func (a *SortedStrArray) RemoveValue(value string) bool { } // PopLeft pops and returns an item from the beginning of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *SortedStrArray) PopLeft() (value string, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -193,7 +200,7 @@ func (a *SortedStrArray) PopLeft() (value string, found bool) { } // PopRight pops and returns an item from the end of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *SortedStrArray) PopRight() (value string, found bool) { a.mu.Lock() defer a.mu.Unlock() @@ -207,16 +214,16 @@ func (a *SortedStrArray) PopRight() (value string, found bool) { } // PopRand randomly pops and return an item out of array. -// Note that if the array is empty, the is false. +// Note that if the array is empty, the `found` is false. func (a *SortedStrArray) PopRand() (value string, found bool) { a.mu.Lock() defer a.mu.Unlock() return a.doRemoveWithoutLock(grand.Intn(len(a.array))) } -// PopRands randomly pops and returns items out of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopRands randomly pops and returns `size` items out of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *SortedStrArray) PopRands(size int) []string { a.mu.Lock() defer a.mu.Unlock() @@ -233,9 +240,9 @@ func (a *SortedStrArray) PopRands(size int) []string { return array } -// PopLefts pops and returns items from the beginning of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopLefts pops and returns `size` items from the beginning of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *SortedStrArray) PopLefts(size int) []string { a.mu.Lock() defer a.mu.Unlock() @@ -252,9 +259,9 @@ func (a *SortedStrArray) PopLefts(size int) []string { return value } -// PopRights pops and returns items from the end of array. -// If the given is greater than size of the array, it returns all elements of the array. -// Note that if given <= 0 or the array is empty, it returns nil. +// PopRights pops and returns `size` items from the end of array. +// If the given `size` is greater than size of the array, it returns all elements of the array. +// Note that if given `size` <= 0 or the array is empty, it returns nil. func (a *SortedStrArray) PopRights(size int) []string { a.mu.Lock() defer a.mu.Unlock() @@ -276,8 +283,8 @@ func (a *SortedStrArray) PopRights(size int) []string { // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. // -// If is negative, then the offset will start from the end of array. -// If is omitted, then the sequence will have everything from start up +// If `end` is negative, then the offset will start from the end of array. +// If `end` is omitted, then the sequence will have everything from start up // until the end of the array. func (a *SortedStrArray) Range(start int, end ...int) []string { a.mu.RLock() @@ -303,7 +310,7 @@ func (a *SortedStrArray) Range(start int, end ...int) []string { } // SubSlice returns a slice of elements from the array as specified -// by the and parameters. +// by the `offset` and `size` parameters. // If in concurrent safe usage, it returns a copy of the slice; else a pointer. // // If offset is non-negative, the sequence will start at that offset in the array. @@ -418,7 +425,7 @@ func (a *SortedStrArray) ContainsI(value string) bool { return false } -// Search searches array by , returns the index of , +// Search searches array by `value`, returns the index of `value`, // or returns -1 if not exists. func (a *SortedStrArray) Search(value string) (index int) { if i, r := a.binSearch(value, true); r == 0 { @@ -429,9 +436,9 @@ func (a *SortedStrArray) Search(value string) (index int) { // Binary search. // It returns the last compared index and the result. -// If equals to 0, it means the value at is equals to . -// If lesser than 0, it means the value at is lesser than . -// If greater than 0, it means the value at is greater than . +// If `result` equals to 0, it means the value at `index` is equals to `value`. +// If `result` lesser than 0, it means the value at `index` is lesser than `value`. +// If `result` greater than 0, it means the value at `index` is greater than `value`. func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result int) { if lock { a.mu.RLock() @@ -511,7 +518,7 @@ func (a *SortedStrArray) Clear() *SortedStrArray { return a } -// LockFunc locks writing by callback function . +// LockFunc locks writing by callback function `f`. func (a *SortedStrArray) LockFunc(f func(array []string)) *SortedStrArray { a.mu.Lock() defer a.mu.Unlock() @@ -519,7 +526,7 @@ func (a *SortedStrArray) LockFunc(f func(array []string)) *SortedStrArray { return a } -// RLockFunc locks reading by callback function . +// RLockFunc locks reading by callback function `f`. func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray { a.mu.RLock() defer a.mu.RUnlock() @@ -527,8 +534,8 @@ func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray { return a } -// Merge merges into current array. -// The parameter can be any garray or slice type. +// Merge merges `array` into current array. +// The parameter `array` can be any garray or slice type. // The difference between Merge and Append is Append supports only specified slice type, // but Merge supports more parameter types. func (a *SortedStrArray) Merge(array interface{}) *SortedStrArray { @@ -536,7 +543,7 @@ func (a *SortedStrArray) Merge(array interface{}) *SortedStrArray { } // Chunk splits an array into multiple arrays, -// the size of each array is determined by . +// the size of each array is determined by `size`. // The last chunk may contain less than size elements. func (a *SortedStrArray) Chunk(size int) [][]string { if size < 1 { @@ -568,7 +575,7 @@ func (a *SortedStrArray) Rand() (value string, found bool) { return a.array[grand.Intn(len(a.array))], true } -// Rands randomly returns items from array(no deleting). +// Rands randomly returns `size` items from array(no deleting). func (a *SortedStrArray) Rands(size int) []string { a.mu.RLock() defer a.mu.RUnlock() @@ -582,7 +589,7 @@ func (a *SortedStrArray) Rands(size int) []string { return array } -// Join joins array elements with a string . +// Join joins array elements with a string `glue`. func (a *SortedStrArray) Join(glue string) string { a.mu.RLock() defer a.mu.RUnlock() @@ -615,8 +622,8 @@ func (a *SortedStrArray) Iterator(f func(k int, v string) bool) { a.IteratorAsc(f) } -// IteratorAsc iterates the array readonly in ascending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorAsc iterates the array readonly in ascending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -627,8 +634,8 @@ func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) { } } -// IteratorDesc iterates the array readonly in descending order with given callback function . -// If returns true, then it continues iterating; or false to stop. +// IteratorDesc iterates the array readonly in descending order with given callback function `f`. +// If `f` returns true, then it continues iterating; or false to stop. func (a *SortedStrArray) IteratorDesc(f func(k int, v string) bool) { a.mu.RLock() defer a.mu.RUnlock() @@ -720,7 +727,7 @@ func (a *SortedStrArray) FilterEmpty() *SortedStrArray { return a } -// Walk applies a user supplied function to every item of array. +// Walk applies a user supplied function `f` to every item of array. func (a *SortedStrArray) Walk(f func(value string) string) *SortedStrArray { a.mu.Lock() defer a.mu.Unlock() diff --git a/container/garray/garray_z_example_any_test.go b/container/garray/garray_z_example_any_test.go index f79fe1adc..fccf99716 100644 --- a/container/garray/garray_z_example_any_test.go +++ b/container/garray/garray_z_example_any_test.go @@ -75,14 +75,14 @@ func ExampleNew() { func ExampleArray_Iterator() { array := garray.NewArrayFrom(g.Slice{"a", "b", "c"}) // Iterator is alias of IteratorAsc, which iterates the array readonly in ascending order - // with given callback function . - // If returns true, then it continues iterating; or false to stop. + // with given callback function `f`. + // If `f` returns true, then it continues iterating; or false to stop. array.Iterator(func(k int, v interface{}) bool { fmt.Println(k, v) return true }) - // IteratorDesc iterates the array readonly in descending order with given callback function . - // If returns true, then it continues iterating; or false to stop. + // IteratorDesc iterates the array readonly in descending order with given callback function `f`. + // If `f` returns true, then it continues iterating; or false to stop. array.IteratorDesc(func(k int, v interface{}) bool { fmt.Println(k, v) return true @@ -150,7 +150,7 @@ func ExampleArray_Chunk() { array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9}) // Chunk splits an array into multiple arrays, - // the size of each array is determined by . + // the size of each array is determined by `size`. // The last chunk may contain less than size elements. fmt.Println(array.Chunk(2)) diff --git a/os/glog/glog.go b/os/glog/glog.go index 089f853f7..a465a5b43 100644 --- a/os/glog/glog.go +++ b/os/glog/glog.go @@ -30,7 +30,7 @@ func init() { SetDebug(defaultDebug) } -// Default returns the default logger. +// DefaultLogger returns the default logger. func DefaultLogger() *Logger { return logger } diff --git a/os/glog/glog_api.go b/os/glog/glog_api.go index 830d6f64f..243a7b993 100644 --- a/os/glog/glog_api.go +++ b/os/glog/glog_api.go @@ -18,6 +18,7 @@ func Printf(format string, v ...interface{}) { logger.Printf(format, v...) } +// Println is alias of Print. // See Print. func Println(v ...interface{}) { logger.Println(v...) diff --git a/os/glog/glog_config.go b/os/glog/glog_config.go index c38454495..6e4402dee 100644 --- a/os/glog/glog_config.go +++ b/os/glog/glog_config.go @@ -148,3 +148,8 @@ func SetLevelPrefixes(prefixes map[int]string) { func GetLevelPrefix(level int) string { return logger.GetLevelPrefix(level) } + +// SetHandlers sets the logging handlers for default logger. +func SetHandlers(handlers ...Handler) { + logger.SetHandlers(handlers...) +} diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 59d3d4f88..7f4dcbc91 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -62,6 +62,7 @@ func New() *Logger { init: gtype.NewBool(), config: DefaultConfig(), } + logger.config.Handlers = []Handler{defaultHandler} return logger } @@ -94,7 +95,7 @@ func (l *Logger) getFilePath(now time.Time) string { } // print prints to defined writer, logging file or passed . -func (l *Logger) print(std io.Writer, lead string, values ...interface{}) { +func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { // Lazy initialize for rotation feature. // It uses atomic reading operation to enhance the performance checking. // It here uses CAP for performance and concurrent safety. @@ -111,69 +112,70 @@ func (l *Logger) print(std io.Writer, lead string, values ...interface{}) { } var ( - now = time.Now() - buffer = bytes.NewBuffer(nil) + now = time.Now() + input = &HandlerInput{ + logger: l, + index: -1, + Ctx: ctx, + Time: now, + Level: level, + } ) if l.config.HeaderPrint { // Time. timeFormat := "" if l.config.Flags&F_TIME_DATE > 0 { - timeFormat += "2006-01-02 " + timeFormat += "2006-01-02" } if l.config.Flags&F_TIME_TIME > 0 { - timeFormat += "15:04:05 " + if timeFormat != "" { + timeFormat += " " + } + timeFormat += "15:04:05" } if l.config.Flags&F_TIME_MILLI > 0 { - timeFormat += "15:04:05.000 " + if timeFormat != "" { + timeFormat += " " + } + timeFormat += "15:04:05.000" } if len(timeFormat) > 0 { - buffer.WriteString(now.Format(timeFormat)) - } - // Lead string. - if len(lead) > 0 { - buffer.WriteString(lead) - if len(values) > 0 { - buffer.WriteByte(' ') - } + input.TimeFormat = now.Format(timeFormat) } + + // Level string. + input.LevelFormat = l.getLevelPrefixWithBrackets(level) + // Caller path and Fn name. if l.config.Flags&(F_FILE_LONG|F_FILE_SHORT|F_CALLER_FN) > 0 { - callerPath := "" callerFnName, path, line := gdebug.CallerWithFilter(pathFilterKey, l.config.StSkip) if l.config.Flags&F_CALLER_FN > 0 { - buffer.WriteString(fmt.Sprintf(`[%s] `, callerFnName)) + input.CallerFunc = fmt.Sprintf(`[%s]`, callerFnName) } if l.config.Flags&F_FILE_LONG > 0 { - callerPath = fmt.Sprintf(`%s:%d: `, path, line) + input.CallerPath = fmt.Sprintf(`%s:%d:`, path, line) } if l.config.Flags&F_FILE_SHORT > 0 { - callerPath = fmt.Sprintf(`%s:%d: `, gfile.Basename(path), line) + input.CallerPath = fmt.Sprintf(`%s:%d:`, gfile.Basename(path), line) } - buffer.WriteString(callerPath) - } // Prefix. if len(l.config.Prefix) > 0 { - buffer.WriteString(l.config.Prefix + " ") + input.Prefix = l.config.Prefix } } // Convert value to string. - var ( - tempStr = "" - valueStr = "" - ) - - if l.ctx != nil { + if ctx != nil { // Tracing values. - spanCtx := trace.SpanContextFromContext(l.ctx) + spanCtx := trace.SpanContextFromContext(ctx) if traceId := spanCtx.TraceID(); traceId.IsValid() { - buffer.WriteString(fmt.Sprintf("{TraceID:%s} ", traceId.String())) + input.CtxStr = "{TraceID:" + traceId.String() + "}" } // Context values. if len(l.config.CtxKeys) > 0 { ctxStr := "" for _, key := range l.config.CtxKeys { - if v := l.ctx.Value(key); v != nil { + if v := ctx.Value(key); v != nil { if ctxStr != "" { ctxStr += ", " } @@ -181,51 +183,52 @@ func (l *Logger) print(std io.Writer, lead string, values ...interface{}) { } } if ctxStr != "" { - buffer.WriteString(fmt.Sprintf("{%s} ", ctxStr)) + input.CtxStr += "{" + ctxStr + "}" } } } - + var tempStr string for _, v := range values { tempStr = gconv.String(v) - if len(valueStr) > 0 { - if valueStr[len(valueStr)-1] == '\n' { + if len(input.Content) > 0 { + if input.Content[len(input.Content)-1] == '\n' { // Remove one blank line(\n\n). if tempStr[0] == '\n' { - valueStr += tempStr[1:] + input.Content += tempStr[1:] } else { - valueStr += tempStr + input.Content += tempStr } } else { - valueStr += " " + tempStr + input.Content += " " + tempStr } } else { - valueStr = tempStr + input.Content = tempStr } } - buffer.WriteString(valueStr + "\n") if l.config.Flags&F_ASYNC > 0 { + input.IsAsync = true err := asyncPool.Add(func() { - l.printToWriter(now, std, buffer) + input.Next() }) if err != nil { intlog.Error(err) } } else { - l.printToWriter(now, std, buffer) + input.Next() } } // printToWriter writes buffer to writer. -func (l *Logger) printToWriter(now time.Time, std io.Writer, buffer *bytes.Buffer) { +func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { + buffer := input.Buffer() if l.config.Writer == nil { // Output content to disk file. if l.config.Path != "" { - l.printToFile(now, buffer) + l.printToFile(input.Time, buffer) } // Allow output to stdout? if l.config.StdoutPrint { - if _, err := std.Write(buffer.Bytes()); err != nil { + if _, err := os.Stdout.Write(buffer.Bytes()); err != nil { intlog.Error(err) } } @@ -238,9 +241,9 @@ func (l *Logger) printToWriter(now time.Time, std io.Writer, buffer *bytes.Buffe } // printToFile outputs logging content to disk file. -func (l *Logger) printToFile(now time.Time, buffer *bytes.Buffer) { +func (l *Logger) printToFile(t time.Time, buffer *bytes.Buffer) { var ( - logFilePath = l.getFilePath(now) + logFilePath = l.getFilePath(t) memoryLockKey = "glog.printToFile:" + logFilePath ) gmlock.Lock(memoryLockKey) @@ -249,7 +252,7 @@ func (l *Logger) printToFile(now time.Time, buffer *bytes.Buffer) { // Rotation file size checks. if l.config.RotateSize > 0 { if gfile.Size(logFilePath) > l.config.RotateSize { - l.rotateFileBySize(now) + l.rotateFileBySize(t) } } // Logging content outputting to disk file. @@ -280,20 +283,29 @@ func (l *Logger) getFilePointer(path string) *gfpool.File { return file } +// getCtx returns the context which is set through chaining operations. +// It returns an empty context if no context set previously. +func (l *Logger) getCtx() context.Context { + if l.ctx != nil { + return l.ctx + } + return context.TODO() +} + // printStd prints content without stack. -func (l *Logger) printStd(lead string, value ...interface{}) { - l.print(os.Stdout, lead, value...) +func (l *Logger) printStd(level int, value ...interface{}) { + l.print(l.getCtx(), level, value...) } // printStd prints content with stack check. -func (l *Logger) printErr(lead string, value ...interface{}) { +func (l *Logger) printErr(level int, value ...interface{}) { if l.config.StStatus == 1 { if s := l.GetStack(); s != "" { value = append(value, "\nStack:\n"+s) } } // In matter of sequence, do not use stderr here, but use the same stdout. - l.print(os.Stdout, lead, value...) + l.print(l.getCtx(), level, value...) } // format formats using fmt.Sprintf. diff --git a/os/glog/glog_logger_api.go b/os/glog/glog_logger_api.go index f7769630d..321da0752 100644 --- a/os/glog/glog_logger_api.go +++ b/os/glog/glog_logger_api.go @@ -14,13 +14,13 @@ import ( // Print prints with newline using fmt.Sprintln. // The parameter can be multiple variables. func (l *Logger) Print(v ...interface{}) { - l.printStd("", v...) + l.printStd(LEVEL_NONE, v...) } // Printf prints with format using fmt.Sprintf. // The parameter can be multiple variables. func (l *Logger) Printf(format string, v ...interface{}) { - l.printStd("", l.format(format, v...)) + l.printStd(LEVEL_NONE, l.format(format, v...)) } // Println is alias of Print. @@ -31,53 +31,53 @@ func (l *Logger) Println(v ...interface{}) { // Fatal prints the logging content with [FATA] header and newline, then exit the current process. func (l *Logger) Fatal(v ...interface{}) { - l.printErr(l.getLevelPrefixWithBrackets(LEVEL_FATA), v...) + l.printErr(LEVEL_FATA, v...) os.Exit(1) } // Fatalf prints the logging content with [FATA] header, custom format and newline, then exit the current process. func (l *Logger) Fatalf(format string, v ...interface{}) { - l.printErr(l.getLevelPrefixWithBrackets(LEVEL_FATA), l.format(format, v...)) + l.printErr(LEVEL_FATA, l.format(format, v...)) os.Exit(1) } // Panic prints the logging content with [PANI] header and newline, then panics. func (l *Logger) Panic(v ...interface{}) { - l.printErr(l.getLevelPrefixWithBrackets(LEVEL_PANI), v...) + l.printErr(LEVEL_PANI, v...) panic(fmt.Sprint(v...)) } // Panicf prints the logging content with [PANI] header, custom format and newline, then panics. func (l *Logger) Panicf(format string, v ...interface{}) { - l.printErr(l.getLevelPrefixWithBrackets(LEVEL_PANI), l.format(format, v...)) + l.printErr(LEVEL_PANI, l.format(format, v...)) panic(l.format(format, v...)) } // Info prints the logging content with [INFO] header and newline. func (l *Logger) Info(v ...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.printStd(l.getLevelPrefixWithBrackets(LEVEL_INFO), v...) + l.printStd(LEVEL_INFO, v...) } } // Infof prints the logging content with [INFO] header, custom format and newline. func (l *Logger) Infof(format string, v ...interface{}) { if l.checkLevel(LEVEL_INFO) { - l.printStd(l.getLevelPrefixWithBrackets(LEVEL_INFO), l.format(format, v...)) + l.printStd(LEVEL_INFO, l.format(format, v...)) } } // Debug prints the logging content with [DEBU] header and newline. func (l *Logger) Debug(v ...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.printStd(l.getLevelPrefixWithBrackets(LEVEL_DEBU), v...) + l.printStd(LEVEL_DEBU, v...) } } // Debugf prints the logging content with [DEBU] header, custom format and newline. func (l *Logger) Debugf(format string, v ...interface{}) { if l.checkLevel(LEVEL_DEBU) { - l.printStd(l.getLevelPrefixWithBrackets(LEVEL_DEBU), l.format(format, v...)) + l.printStd(LEVEL_DEBU, l.format(format, v...)) } } @@ -85,7 +85,7 @@ func (l *Logger) Debugf(format string, v ...interface{}) { // It also prints caller stack info if stack feature is enabled. func (l *Logger) Notice(v ...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.printStd(l.getLevelPrefixWithBrackets(LEVEL_NOTI), v...) + l.printStd(LEVEL_NOTI, v...) } } @@ -93,7 +93,7 @@ func (l *Logger) Notice(v ...interface{}) { // It also prints caller stack info if stack feature is enabled. func (l *Logger) Noticef(format string, v ...interface{}) { if l.checkLevel(LEVEL_NOTI) { - l.printStd(l.getLevelPrefixWithBrackets(LEVEL_NOTI), l.format(format, v...)) + l.printStd(LEVEL_NOTI, l.format(format, v...)) } } @@ -101,7 +101,7 @@ func (l *Logger) Noticef(format string, v ...interface{}) { // It also prints caller stack info if stack feature is enabled. func (l *Logger) Warning(v ...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.printStd(l.getLevelPrefixWithBrackets(LEVEL_WARN), v...) + l.printStd(LEVEL_WARN, v...) } } @@ -109,7 +109,7 @@ func (l *Logger) Warning(v ...interface{}) { // It also prints caller stack info if stack feature is enabled. func (l *Logger) Warningf(format string, v ...interface{}) { if l.checkLevel(LEVEL_WARN) { - l.printStd(l.getLevelPrefixWithBrackets(LEVEL_WARN), l.format(format, v...)) + l.printStd(LEVEL_WARN, l.format(format, v...)) } } @@ -117,7 +117,7 @@ func (l *Logger) Warningf(format string, v ...interface{}) { // It also prints caller stack info if stack feature is enabled. func (l *Logger) Error(v ...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.printErr(l.getLevelPrefixWithBrackets(LEVEL_ERRO), v...) + l.printErr(LEVEL_ERRO, v...) } } @@ -125,7 +125,7 @@ func (l *Logger) Error(v ...interface{}) { // It also prints caller stack info if stack feature is enabled. func (l *Logger) Errorf(format string, v ...interface{}) { if l.checkLevel(LEVEL_ERRO) { - l.printErr(l.getLevelPrefixWithBrackets(LEVEL_ERRO), l.format(format, v...)) + l.printErr(LEVEL_ERRO, l.format(format, v...)) } } @@ -133,7 +133,7 @@ func (l *Logger) Errorf(format string, v ...interface{}) { // It also prints caller stack info if stack feature is enabled. func (l *Logger) Critical(v ...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.printErr(l.getLevelPrefixWithBrackets(LEVEL_CRIT), v...) + l.printErr(LEVEL_CRIT, v...) } } @@ -141,7 +141,7 @@ func (l *Logger) Critical(v ...interface{}) { // It also prints caller stack info if stack feature is enabled. func (l *Logger) Criticalf(format string, v ...interface{}) { if l.checkLevel(LEVEL_CRIT) { - l.printErr(l.getLevelPrefixWithBrackets(LEVEL_CRIT), l.format(format, v...)) + l.printErr(LEVEL_CRIT, l.format(format, v...)) } } diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index f28e6862a..6b00a6afd 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -22,6 +22,7 @@ import ( // Config is the configuration object for logger. type Config struct { + Handlers []Handler `json:"-"` // Logger handlers which implement feature similar as middleware. Writer io.Writer `json:"-"` // Customized io.Writer. Flags int `json:"flags"` // Extra flags for logging output features. Path string `json:"path"` // Logging directory path. @@ -246,3 +247,8 @@ func (l *Logger) SetHeaderPrint(enabled bool) { func (l *Logger) SetPrefix(prefix string) { l.config.Prefix = prefix } + +// SetHandlers sets the logging handlers for current logger. +func (l *Logger) SetHandlers(handlers ...Handler) { + l.config.Handlers = append(handlers, defaultHandler) +} diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go new file mode 100644 index 000000000..1991cd547 --- /dev/null +++ b/os/glog/glog_logger_handler.go @@ -0,0 +1,78 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package glog + +import ( + "bytes" + "context" + "time" +) + +type Handler func(ctx context.Context, input *HandlerInput) + +type HandlerInput struct { + logger *Logger + index int + Ctx context.Context + Time time.Time + TimeFormat string + Level int + LevelFormat string + CallerFunc string + CallerPath string + CtxStr string + Prefix string + Content string + IsAsync bool +} + +// defaultHandler is the default handler for logger. +func defaultHandler(ctx context.Context, input *HandlerInput) { + input.logger.printToWriter(ctx, input) +} + +func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, s string) { + if buffer.Len() > 0 { + buffer.WriteByte(' ') + } + buffer.WriteString(s) +} + +func (i *HandlerInput) Buffer() *bytes.Buffer { + buffer := bytes.NewBuffer(nil) + buffer.WriteString(i.TimeFormat) + if i.LevelFormat != "" { + i.addStringToBuffer(buffer, i.LevelFormat) + } + if i.CallerFunc != "" { + i.addStringToBuffer(buffer, i.CallerFunc) + } + if i.CallerPath != "" { + i.addStringToBuffer(buffer, i.CallerPath) + } + if i.Prefix != "" { + i.addStringToBuffer(buffer, i.Prefix) + } + if i.CtxStr != "" { + i.addStringToBuffer(buffer, i.CtxStr) + } + if i.Content != "" { + i.addStringToBuffer(buffer, i.Content) + } + return buffer +} + +func (i *HandlerInput) String() string { + return i.Buffer().String() +} + +func (i *HandlerInput) Next() { + if len(i.logger.config.Handlers)-1 > i.index { + i.index++ + i.logger.config.Handlers[i.index](i.Ctx, i) + } +} diff --git a/os/glog/glog_logger_level.go b/os/glog/glog_logger_level.go index f9b5c28ed..a756bc6f8 100644 --- a/os/glog/glog_logger_level.go +++ b/os/glog/glog_logger_level.go @@ -18,6 +18,7 @@ const ( LEVEL_ALL = LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT LEVEL_DEV = LEVEL_ALL LEVEL_PROD = LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT + LEVEL_NONE = 0 LEVEL_DEBU = 1 << iota // 8 LEVEL_INFO // 16 LEVEL_NOTI // 32 diff --git a/os/glog/glog_z_unit_handler_test.go b/os/glog/glog_z_unit_handler_test.go new file mode 100644 index 000000000..70acc0f5c --- /dev/null +++ b/os/glog/glog_z_unit_handler_test.go @@ -0,0 +1,80 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package glog_test + +import ( + "bytes" + "context" + "github.com/gogf/gf/container/garray" + "github.com/gogf/gf/os/glog" + "github.com/gogf/gf/test/gtest" + "github.com/gogf/gf/text/gstr" + "testing" +) + +var arrayForHandlerTest1 = garray.NewStrArray() + +func customHandler1(ctx context.Context, input *glog.HandlerInput) { + arrayForHandlerTest1.Append(input.String()) + input.Next() +} + +func TestLogger_SetHandlers1(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + w := bytes.NewBuffer(nil) + l := glog.NewWithWriter(w) + l.SetHandlers(customHandler1) + l.SetCtxKeys("Trace-Id", "Span-Id", "Test") + ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890") + ctx = context.WithValue(ctx, "Span-Id", "abcdefg") + + l.Ctx(ctx).Print(1, 2, 3) + t.Assert(gstr.Count(w.String(), "Trace-Id"), 1) + t.Assert(gstr.Count(w.String(), "1234567890"), 1) + t.Assert(gstr.Count(w.String(), "Span-Id"), 1) + t.Assert(gstr.Count(w.String(), "abcdefg"), 1) + t.Assert(gstr.Count(w.String(), "1 2 3"), 1) + + t.Assert(arrayForHandlerTest1.Len(), 1) + t.Assert(gstr.Count(arrayForHandlerTest1.At(0), "Trace-Id"), 1) + t.Assert(gstr.Count(arrayForHandlerTest1.At(0), "1234567890"), 1) + t.Assert(gstr.Count(arrayForHandlerTest1.At(0), "Span-Id"), 1) + t.Assert(gstr.Count(arrayForHandlerTest1.At(0), "abcdefg"), 1) + t.Assert(gstr.Count(arrayForHandlerTest1.At(0), "1 2 3"), 1) + }) +} + +var arrayForHandlerTest2 = garray.NewStrArray() + +func customHandler2(ctx context.Context, input *glog.HandlerInput) { + arrayForHandlerTest2.Append(input.String()) +} + +func TestLogger_SetHandlers2(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + w := bytes.NewBuffer(nil) + l := glog.NewWithWriter(w) + l.SetHandlers(customHandler2) + l.SetCtxKeys("Trace-Id", "Span-Id", "Test") + ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890") + ctx = context.WithValue(ctx, "Span-Id", "abcdefg") + + l.Ctx(ctx).Print(1, 2, 3) + t.Assert(gstr.Count(w.String(), "Trace-Id"), 0) + t.Assert(gstr.Count(w.String(), "1234567890"), 0) + t.Assert(gstr.Count(w.String(), "Span-Id"), 0) + t.Assert(gstr.Count(w.String(), "abcdefg"), 0) + t.Assert(gstr.Count(w.String(), "1 2 3"), 0) + + t.Assert(arrayForHandlerTest2.Len(), 1) + t.Assert(gstr.Count(arrayForHandlerTest2.At(0), "Trace-Id"), 1) + t.Assert(gstr.Count(arrayForHandlerTest2.At(0), "1234567890"), 1) + t.Assert(gstr.Count(arrayForHandlerTest2.At(0), "Span-Id"), 1) + t.Assert(gstr.Count(arrayForHandlerTest2.At(0), "abcdefg"), 1) + t.Assert(gstr.Count(arrayForHandlerTest2.At(0), "1 2 3"), 1) + }) +} From 702a2962581fb16f079f83c140b377c2d8427bcf Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 2 Jun 2021 09:53:08 +0800 Subject: [PATCH 309/492] improve handler feature for package glog --- os/glog/glog_logger_handler.go | 1 + 1 file changed, 1 insertion(+) diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index 1991cd547..69a703992 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -63,6 +63,7 @@ func (i *HandlerInput) Buffer() *bytes.Buffer { if i.Content != "" { i.addStringToBuffer(buffer, i.Content) } + i.addStringToBuffer(buffer, "\n") return buffer } From 742c7913eae7a2c27939020ff2d43fac8c914bc0 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 2 Jun 2021 21:12:27 +0800 Subject: [PATCH 310/492] fix issue in function Parse for package ghttp --- net/ghttp/ghttp_unit_request_test.go | 35 ++++++++++++++++++ util/gvalid/gvalid_validator_check_struct.go | 39 ++++++++++++++------ 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/net/ghttp/ghttp_unit_request_test.go b/net/ghttp/ghttp_unit_request_test.go index f7b28618a..26880df6b 100644 --- a/net/ghttp/ghttp_unit_request_test.go +++ b/net/ghttp/ghttp_unit_request_test.go @@ -586,3 +586,38 @@ func Test_Params_Parse_DefaultValueTag(t *testing.T) { t.Assert(client.PostContent("/parse", `{"name":"smith", "score":100}`), `{"Name":"smith","Score":100}`) }) } + +func Test_Params_Parse_Validation(t *testing.T) { + type RegisterReq struct { + Name string `p:"username" v:"required|length:6,30#请输入账号|账号长度为:min到:max位"` + Pass string `p:"password1" v:"required|length:6,30#请输入密码|密码长度不够"` + Pass2 string `p:"password2" v:"required|length:6,30|same:password1#请确认密码|密码长度不够|两次密码不一致"` + } + + p, _ := ports.PopRand() + s := g.Server(p) + s.BindHandler("/parse", func(r *ghttp.Request) { + var req *RegisterReq + if err := r.Parse(&req); err != nil { + r.Response.Write(err) + } else { + r.Response.Write("ok") + } + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + prefix := fmt.Sprintf("http://127.0.0.1:%d", p) + client := g.Client() + client.SetPrefix(prefix) + + t.Assert(client.GetContent("/parse"), `请输入账号; 账号长度为6到30位; 请输入密码; 密码长度不够; 请确认密码; 密码长度不够; 两次密码不一致`) + t.Assert(client.GetContent("/parse?name=john11&password1=123456&password2=123"), `密码长度不够; 两次密码不一致`) + t.Assert(client.GetContent("/parse?name=john&password1=123456&password2=123456"), `账号长度为6到30位`) + t.Assert(client.GetContent("/parse?name=john11&password1=123456&password2=123456"), `ok`) + }) +} diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 2c921568b..58426abfb 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -21,8 +21,8 @@ func (v *Validator) CheckStruct(object interface{}) Error { func (v *Validator) doCheckStruct(object interface{}) Error { var ( - // Returning error. - errorMaps = make(map[string]map[string]string) + errorMaps = make(map[string]map[string]string) // Returning error. + fieldToAliasNameMap = make(map[string]string) // Field name to alias name map. ) fieldMap, err := structs.FieldMap(object, aliasNameTagPriority, true) if err != nil { @@ -44,6 +44,10 @@ func (v *Validator) doCheckStruct(object interface{}) Error { errorMaps[k] = m } } + } else { + if field.TagValue != "" { + fieldToAliasNameMap[field.Name()] = field.TagValue + } } } // It here must use structs.TagFields not structs.FieldMap to ensure error sequence. @@ -61,8 +65,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error { checkRules = make(map[string]string) customMessage = make(CustomMsg) checkValueData = v.data - fieldAliases = make(map[string]string) // Alias names for `messages` overwriting struct tag names. - errorRules = make([]string, 0) // Sequence rules. + errorRules = make([]string, 0) // Sequence rules. ) if checkValueData == nil { checkValueData = object @@ -128,19 +131,33 @@ func (v *Validator) doCheckStruct(object interface{}) Error { // Merge the custom validation rules with rules in struct tag. // The custom rules has the most high priority that can overwrite the struct tag rules. for _, field := range tagField { - fieldName := field.Name() - // sequence tag == struct tag - // The name here is alias of field name. - name, rule, msg := parseSequenceTag(field.TagValue) + var ( + fieldName = field.Name() // Attribute name. + name, rule, msg = parseSequenceTag(field.TagValue) // The `name` is different from `attribute alias`, which is used for validation only. + ) if len(name) == 0 { - name = fieldName + if v, ok := fieldToAliasNameMap[fieldName]; ok { + // It uses alias name of the attribute if its alias name tag exists. + name = v + } else { + // It or else uses the attribute name directly. + name = fieldName + } } else { - fieldAliases[fieldName] = name + // It uses the alias name from validation rule. + fieldToAliasNameMap[fieldName] = name } // It here extends the params map using alias names. + // Note that the variable `name` might be alias name or attribute name. if _, ok := inputParamMap[name]; !ok { if !v.useDataInsteadOfObjectAttributes { inputParamMap[name] = field.Value.Interface() + } else { + if name != fieldName { + if foundKey, foundValue := gutil.MapPossibleItemByKey(inputParamMap, fieldName); foundKey != "" { + inputParamMap[name] = foundValue + } + } } } if _, ok := checkRules[name]; !ok { @@ -184,7 +201,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error { // which have the most priority than `rules` and struct tag. if msg, ok := v.messages.(CustomMsg); ok && len(msg) > 0 { for k, v := range msg { - if a, ok := fieldAliases[k]; ok { + if a, ok := fieldToAliasNameMap[k]; ok { // Overwrite the key of field name. customMessage[a] = v } else { From 8e1f6abac5e6b8229dff22db100c16ff74897d7d Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 3 Jun 2021 15:38:33 +0800 Subject: [PATCH 311/492] change github.com/go-sql-driver/mysql to github.com/gogf/mysql --- database/gdb/gdb_driver_mysql.go | 2 +- database/gdb/gdb_z_mysql_internal_test.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index d39078c75..aab15faec 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -16,7 +16,7 @@ import ( "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" - _ "github.com/go-sql-driver/mysql" + _ "github.com/gogf/mysql" ) // DriverMysql is the driver for mysql database. diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index ed80711a6..016aef2cb 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -8,11 +8,11 @@ package gdb import ( "fmt" - "github.com/go-sql-driver/mysql" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/os/gcmd" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" + "github.com/gogf/mysql" "testing" ) diff --git a/go.mod b/go.mod index ca87687b7..5022b6160 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 github.com/fsnotify/fsnotify v1.4.9 - github.com/go-sql-driver/mysql v1.5.0 + github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/websocket v1.4.1 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf diff --git a/go.sum b/go.sum index 331f2824d..582d62e0e 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw= +github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= From a54559d016ac080bd3b326aea4f3d4bd12cb8ce6 Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 4 Jun 2021 09:27:41 +0800 Subject: [PATCH 312/492] add WhereLT/WhereLTE/WhereGT/WhereGTE/WhereOrLT/WhereOrLTE/WhereOrGT/WhereOrGTE functions for gdb.Model --- database/gdb/gdb_model_condition.go | 127 ++++++++++++++++++------- database/gdb/gdb_z_mysql_model_test.go | 98 +++++++++++++++++++ 2 files changed, 191 insertions(+), 34 deletions(-) diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 5135bd3a5..7a7d41e5e 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -8,6 +8,7 @@ package gdb import ( "fmt" + "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" "strings" ) @@ -60,55 +61,84 @@ func (m *Model) WherePri(where interface{}, args ...interface{}) *Model { return m.Where(newWhere[0], newWhere[1:]...) } -// WhereBetween builds `xxx BETWEEN x AND y` statement. +// Wheref builds condition string using fmt.Sprintf and arguments. +func (m *Model) Wheref(format string, args ...interface{}) *Model { + var ( + placeHolderCount = gstr.Count(format, "?") + conditionStr = fmt.Sprintf(format, args[:len(args)-placeHolderCount]...) + ) + return m.Where(conditionStr, args[len(args)-placeHolderCount:]...) +} + +// WhereLT builds `column < value` statement. +func (m *Model) WhereLT(column string, value interface{}) *Model { + return m.Wheref(`%s < ?`, column, value) +} + +// WhereLTE builds `column <= value` statement. +func (m *Model) WhereLTE(column string, value interface{}) *Model { + return m.Wheref(`%s <= ?`, column, value) +} + +// WhereGT builds `column > value` statement. +func (m *Model) WhereGT(column string, value interface{}) *Model { + return m.Wheref(`%s > ?`, column, value) +} + +// WhereGTE builds `column >= value` statement. +func (m *Model) WhereGTE(column string, value interface{}) *Model { + return m.Wheref(`%s >= ?`, column, value) +} + +// WhereBetween builds `column BETWEEN min AND max` statement. func (m *Model) WhereBetween(column string, min, max interface{}) *Model { - return m.Where(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column)), min, max) + return m.Wheref(`%s BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column), min, max) } -// WhereLike builds `xxx LIKE x` statement. +// WhereLike builds `column LIKE like` statement. func (m *Model) WhereLike(column string, like interface{}) *Model { - return m.Where(fmt.Sprintf(`%s LIKE ?`, m.db.GetCore().QuoteWord(column)), like) + return m.Wheref(`%s LIKE ?`, m.db.GetCore().QuoteWord(column), like) } -// WhereIn builds `xxx IN (x)` statement. +// WhereIn builds `column IN (in)` statement. func (m *Model) WhereIn(column string, in interface{}) *Model { - return m.Where(fmt.Sprintf(`%s IN (?)`, m.db.GetCore().QuoteWord(column)), in) + return m.Wheref(`%s IN (?)`, m.db.GetCore().QuoteWord(column), in) } -// WhereNull builds `xxx IS NULL` statement. +// WhereNull builds `columns[0] IS NULL AND columns[1] IS NULL ...` statement. func (m *Model) WhereNull(columns ...string) *Model { model := m for _, column := range columns { - model = m.Where(fmt.Sprintf(`%s IS NULL`, m.db.GetCore().QuoteWord(column))) + model = m.Wheref(`%s IS NULL`, m.db.GetCore().QuoteWord(column)) } return model } -// WhereNotBetween builds `xxx NOT BETWEEN x AND y` statement. +// WhereNotBetween builds `column NOT BETWEEN min AND max` statement. func (m *Model) WhereNotBetween(column string, min, max interface{}) *Model { - return m.Where(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column)), min, max) + return m.Wheref(`%s NOT BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column), min, max) } -// WhereNotLike builds `xxx NOT LIKE x` statement. +// WhereNotLike builds `column NOT LIKE like` statement. func (m *Model) WhereNotLike(column string, like interface{}) *Model { - return m.Where(fmt.Sprintf(`%s NOT LIKE ?`, m.db.GetCore().QuoteWord(column)), like) + return m.Wheref(`%s NOT LIKE ?`, m.db.GetCore().QuoteWord(column), like) } -// WhereNot builds `xxx != x` statement. +// WhereNot builds `column != value` statement. func (m *Model) WhereNot(column string, value interface{}) *Model { - return m.Where(fmt.Sprintf(`%s != ?`, m.db.GetCore().QuoteWord(column)), value) + return m.Wheref(`%s != ?`, m.db.GetCore().QuoteWord(column), value) } -// WhereNotIn builds `xxx NOT IN (x)` statement. +// WhereNotIn builds `column NOT IN (in)` statement. func (m *Model) WhereNotIn(column string, in interface{}) *Model { - return m.Where(fmt.Sprintf(`%s NOT IN (?)`, m.db.GetCore().QuoteWord(column)), in) + return m.Wheref(`%s NOT IN (?)`, m.db.GetCore().QuoteWord(column), in) } -// WhereNotNull builds `xxx IS NOT NULL` statement. +// WhereNotNull builds `columns[0] IS NOT NULL AND columns[1] IS NOT NULL ...` statement. func (m *Model) WhereNotNull(columns ...string) *Model { model := m for _, column := range columns { - model = m.Where(fmt.Sprintf(`%s IS NOT NULL`, m.db.GetCore().QuoteWord(column))) + model = m.Wheref(`%s IS NOT NULL`, m.db.GetCore().QuoteWord(column)) } return model } @@ -127,50 +157,79 @@ func (m *Model) WhereOr(where interface{}, args ...interface{}) *Model { return model } -// WhereOrBetween builds `xxx BETWEEN x AND y` statement in `OR` conditions. +// WhereOrf builds `OR` condition string using fmt.Sprintf and arguments. +func (m *Model) WhereOrf(format string, args ...interface{}) *Model { + var ( + placeHolderCount = gstr.Count(format, "?") + conditionStr = fmt.Sprintf(format, args[:len(args)-placeHolderCount]...) + ) + return m.WhereOr(conditionStr, args[len(args)-placeHolderCount:]...) +} + +// WhereOrLT builds `column < value` statement in `OR` conditions.. +func (m *Model) WhereOrLT(column string, value interface{}) *Model { + return m.WhereOrf(`%s < ?`, column, value) +} + +// WhereOrLTE builds `column <= value` statement in `OR` conditions.. +func (m *Model) WhereOrLTE(column string, value interface{}) *Model { + return m.WhereOrf(`%s <= ?`, column, value) +} + +// WhereOrGT builds `column > value` statement in `OR` conditions.. +func (m *Model) WhereOrGT(column string, value interface{}) *Model { + return m.WhereOrf(`%s > ?`, column, value) +} + +// WhereOrGTE builds `column >= value` statement in `OR` conditions.. +func (m *Model) WhereOrGTE(column string, value interface{}) *Model { + return m.WhereOrf(`%s >= ?`, column, value) +} + +// WhereOrBetween builds `column BETWEEN min AND max` statement in `OR` conditions. func (m *Model) WhereOrBetween(column string, min, max interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column)), min, max) + return m.WhereOrf(`%s BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column), min, max) } -// WhereOrLike builds `xxx LIKE x` statement in `OR` conditions. +// WhereOrLike builds `column LIKE like` statement in `OR` conditions. func (m *Model) WhereOrLike(column string, like interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s LIKE ?`, m.db.GetCore().QuoteWord(column)), like) + return m.WhereOrf(`%s LIKE ?`, m.db.GetCore().QuoteWord(column), like) } -// WhereOrIn builds `xxx IN (x)` statement in `OR` conditions. +// WhereOrIn builds `column IN (in)` statement in `OR` conditions. func (m *Model) WhereOrIn(column string, in interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s IN (?)`, m.db.GetCore().QuoteWord(column)), in) + return m.WhereOrf(`%s IN (?)`, m.db.GetCore().QuoteWord(column), in) } -// WhereOrNull builds `xxx IS NULL` statement in `OR` conditions. +// WhereOrNull builds `columns[0] IS NULL OR columns[1] IS NULL ...` statement in `OR` conditions. func (m *Model) WhereOrNull(columns ...string) *Model { model := m for _, column := range columns { - model = m.WhereOr(fmt.Sprintf(`%s IS NULL`, m.db.GetCore().QuoteWord(column))) + model = m.WhereOrf(`%s IS NULL`, m.db.GetCore().QuoteWord(column)) } return model } -// WhereOrNotBetween builds `xxx NOT BETWEEN x AND y` statement in `OR` conditions. +// WhereOrNotBetween builds `column NOT BETWEEN min AND max` statement in `OR` conditions. func (m *Model) WhereOrNotBetween(column string, min, max interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column)), min, max) + return m.WhereOrf(`%s NOT BETWEEN ? AND ?`, m.db.GetCore().QuoteWord(column), min, max) } -// WhereOrNotLike builds `xxx NOT LIKE x` statement in `OR` conditions. +// WhereOrNotLike builds `column NOT LIKE like` statement in `OR` conditions. func (m *Model) WhereOrNotLike(column string, like interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s NOT LIKE ?`, m.db.GetCore().QuoteWord(column)), like) + return m.WhereOrf(`%s NOT LIKE ?`, m.db.GetCore().QuoteWord(column), like) } -// WhereOrNotIn builds `xxx NOT IN (x)` statement. +// WhereOrNotIn builds `column NOT IN (in)` statement. func (m *Model) WhereOrNotIn(column string, in interface{}) *Model { - return m.WhereOr(fmt.Sprintf(`%s NOT IN (?)`, m.db.GetCore().QuoteWord(column)), in) + return m.WhereOrf(`%s NOT IN (?)`, m.db.GetCore().QuoteWord(column), in) } -// WhereOrNotNull builds `xxx IS NOT NULL` statement in `OR` conditions. +// WhereOrNotNull builds `columns[0] IS NOT NULL OR columns[1] IS NOT NULL ...` statement in `OR` conditions. func (m *Model) WhereOrNotNull(columns ...string) *Model { model := m for _, column := range columns { - model = m.WhereOr(fmt.Sprintf(`%s IS NOT NULL`, m.db.GetCore().QuoteWord(column))) + model = m.WhereOrf(`%s IS NOT NULL`, m.db.GetCore().QuoteWord(column)) } return model } diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index b3087a4ea..7370e5bfc 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -3366,6 +3366,104 @@ func Test_Model_WhereOrNotNull(t *testing.T) { }) } +func Test_Model_WhereLT(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereLT("id", 3).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 2) + t.Assert(result[0]["id"], 1) + }) +} + +func Test_Model_WhereLTE(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereLTE("id", 3).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 3) + t.Assert(result[0]["id"], 1) + }) +} + +func Test_Model_WhereGT(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereGT("id", 8).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 2) + t.Assert(result[0]["id"], 9) + }) +} + +func Test_Model_WhereGTE(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereGTE("id", 8).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 3) + t.Assert(result[0]["id"], 8) + }) +} + +func Test_Model_WhereOrLT(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereLT("id", 3).WhereOrLT("id", 4).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 3) + t.Assert(result[0]["id"], 1) + t.Assert(result[2]["id"], 3) + }) +} + +func Test_Model_WhereOrLTE(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereLTE("id", 3).WhereOrLTE("id", 4).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 4) + t.Assert(result[0]["id"], 1) + t.Assert(result[3]["id"], 4) + }) +} + +func Test_Model_WhereOrGT(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereGT("id", 8).WhereOrGT("id", 7).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 3) + t.Assert(result[0]["id"], 8) + }) +} + +func Test_Model_WhereOrGTE(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Model(table).WhereGTE("id", 8).WhereOrGTE("id", 7).OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(result), 4) + t.Assert(result[0]["id"], 7) + }) +} + func Test_Model_Min_Max_Avg_Sum(t *testing.T) { table := createInitTable() defer dropTable(table) From 8aa7f0835090b208fe74f8ec763da7a260e7201b Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 4 Jun 2021 09:54:19 +0800 Subject: [PATCH 313/492] improve DB interface for package gdb --- .example/database/gdb/driver/driver/driver.go | 15 +++++---------- database/gdb/gdb.go | 13 +++++++++++++ database/gdb/gdb_core.go | 18 +++++++++--------- database/gdb/gdb_core_transaction.go | 6 +++--- database/gdb/gdb_core_underlying.go | 6 +++--- database/gdb/gdb_model_delete.go | 4 ++-- database/gdb/gdb_model_insert.go | 4 ++-- database/gdb/gdb_model_select.go | 2 +- database/gdb/gdb_model_update.go | 2 +- 9 files changed, 39 insertions(+), 31 deletions(-) diff --git a/.example/database/gdb/driver/driver/driver.go b/.example/database/gdb/driver/driver/driver.go index 5fd86304d..71af8399d 100644 --- a/.example/database/gdb/driver/driver/driver.go +++ b/.example/database/gdb/driver/driver/driver.go @@ -1,12 +1,7 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - package driver import ( + "context" "database/sql" "github.com/gogf/gf/database/gdb" "github.com/gogf/gf/os/gtime" @@ -47,9 +42,9 @@ func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) { // DoQuery commits the sql string and its arguments to underlying driver // through given link object and returns the execution result. -func (d *MyDriver) DoQuery(link gdb.Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { +func (d *MyDriver) DoQuery(ctx context.Context, link gdb.Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { tsMilli := gtime.TimestampMilli() - rows, err = d.DriverMysql.DoQuery(link, sql, args...) + rows, err = d.DriverMysql.DoQuery(ctx, link, sql, args...) link.Exec( "INSERT INTO `monitor`(`sql`,`cost`,`time`,`error`) VALUES(?,?,?,?)", gdb.FormatSqlWithArgs(sql, args), @@ -62,9 +57,9 @@ func (d *MyDriver) DoQuery(link gdb.Link, sql string, args ...interface{}) (rows // DoExec commits the query string and its arguments to underlying driver // through given link object and returns the execution result. -func (d *MyDriver) DoExec(link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) { +func (d *MyDriver) DoExec(ctx context.Context, link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) { tsMilli := gtime.TimestampMilli() - result, err = d.DriverMysql.DoExec(link, sql, args...) + result, err = d.DriverMysql.DoExec(ctx, link, sql, args...) link.Exec( "INSERT INTO `monitor`(`sql`,`cost`,`time`,`error`) VALUES(?,?,?,?)", gdb.FormatSqlWithArgs(sql, args), diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index d543bdb11..ab415021a 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -96,6 +96,19 @@ type DB interface { Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Update. Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete. + // =========================================================================== + // Internal APIs for CURD, which can be overwrote for custom CURD implements. + // =========================================================================== + + DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery. + DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec. + DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare. + DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll. + DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoInsert. + DoBatchInsert(ctx context.Context, link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoBatchInsert. + DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate. + DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete. + // =========================================================================== // Query APIs for convenience purpose. // =========================================================================== diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index a275ef2bd..84d50bbd9 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -119,12 +119,12 @@ func (c *Core) Slave(schema ...string) (*sql.DB, error) { // GetAll queries and returns data records from database. func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) { - return c.DoGetAll(c.GetCtx(), nil, sql, args...) + return c.db.DoGetAll(c.GetCtx(), nil, sql, args...) } // DoGetAll queries and returns data records from database. func (c *Core) DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) { - rows, err := c.DoQuery(ctx, link, sql, args...) + rows, err := c.db.DoQuery(ctx, link, sql, args...) if err != nil || rows == nil { return nil, err } @@ -147,7 +147,7 @@ func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) { // GetArray queries and returns data values as slice from database. // Note that if there are multiple columns in the result, it returns just one column values randomly. func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) { - all, err := c.DoGetAll(c.GetCtx(), nil, sql, args...) + all, err := c.db.DoGetAll(c.GetCtx(), nil, sql, args...) if err != nil { return nil, err } @@ -347,10 +347,10 @@ func (c *Core) DoInsert(ctx context.Context, link Link, table string, data inter } switch reflectKind { case reflect.Slice, reflect.Array: - return c.DoBatchInsert(ctx, link, table, data, option, batch...) + return c.db.DoBatchInsert(ctx, link, table, data, option, batch...) case reflect.Struct: if _, ok := data.(apiInterfaces); ok { - return c.DoBatchInsert(ctx, link, table, data, option, batch...) + return c.db.DoBatchInsert(ctx, link, table, data, option, batch...) } else { dataMap = ConvertDataForTableRecord(data) } @@ -399,7 +399,7 @@ func (c *Core) DoInsert(ctx context.Context, link Link, table string, data inter return nil, err } } - return c.DoExec(ctx, link, fmt.Sprintf( + return c.db.DoExec(ctx, link, fmt.Sprintf( "%s INTO %s(%s) VALUES(%s) %s", operation, table, strings.Join(fields, ","), strings.Join(values, ","), updateStr, @@ -556,7 +556,7 @@ func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list } valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")") if len(valueHolder) == batchNum || (i == listMapLen-1 && len(valueHolder) > 0) { - r, err := c.DoExec(ctx, link, fmt.Sprintf( + r, err := c.db.DoExec(ctx, link, fmt.Sprintf( "%s INTO %s(%s) VALUES%s %s", operation, table, keysStr, gstr.Join(valueHolder, ","), @@ -663,7 +663,7 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter return nil, err } } - return c.DoExec(ctx, link, fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition), args...) + return c.db.DoExec(ctx, link, fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition), args...) } // Delete does "DELETE FROM ... " statement for the table. @@ -690,7 +690,7 @@ func (c *Core) DoDelete(ctx context.Context, link Link, table string, condition } } table = c.QuotePrefixTableName(table) - return c.DoExec(ctx, link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...) + return c.db.DoExec(ctx, link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...) } // convertRowsToResult converts underlying data record type sql.Rows to Result type. diff --git a/database/gdb/gdb_core_transaction.go b/database/gdb/gdb_core_transaction.go index 4636d9166..d9e575ce8 100644 --- a/database/gdb/gdb_core_transaction.go +++ b/database/gdb/gdb_core_transaction.go @@ -317,13 +317,13 @@ func (tx *TX) Transaction(ctx context.Context, f func(ctx context.Context, tx *T // Query does query operation on transaction. // See Core.Query. func (tx *TX) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) { - return tx.db.GetCore().DoQuery(tx.ctx, &txLink{tx.tx}, sql, args...) + return tx.db.DoQuery(tx.ctx, &txLink{tx.tx}, sql, args...) } // Exec does none query operation on transaction. // See Core.Exec. func (tx *TX) Exec(sql string, args ...interface{}) (sql.Result, error) { - return tx.db.GetCore().DoExec(tx.ctx, &txLink{tx.tx}, sql, args...) + return tx.db.DoExec(tx.ctx, &txLink{tx.tx}, sql, args...) } // Prepare creates a prepared statement for later queries or executions. @@ -332,7 +332,7 @@ func (tx *TX) Exec(sql string, args ...interface{}) (sql.Result, error) { // The caller must call the statement's Close method // when the statement is no longer needed. func (tx *TX) Prepare(sql string) (*Stmt, error) { - return tx.db.GetCore().DoPrepare(tx.ctx, &txLink{tx.tx}, sql) + return tx.db.DoPrepare(tx.ctx, &txLink{tx.tx}, sql) } // GetAll queries and returns data records from database. diff --git a/database/gdb/gdb_core_underlying.go b/database/gdb/gdb_core_underlying.go index 316127df0..baa0e5027 100644 --- a/database/gdb/gdb_core_underlying.go +++ b/database/gdb/gdb_core_underlying.go @@ -17,7 +17,7 @@ import ( // Query commits one query SQL to underlying driver and returns the execution result. // It is most commonly used for data querying. func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) { - return c.DoQuery(c.GetCtx(), nil, sql, args...) + return c.db.DoQuery(c.GetCtx(), nil, sql, args...) } // DoQuery commits the sql string and its arguments to underlying driver @@ -69,7 +69,7 @@ func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...inter // Exec commits one query SQL to underlying driver and returns the execution result. // It is most commonly used for data inserting and updating. func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error) { - return c.DoExec(c.GetCtx(), nil, sql, args...) + return c.db.DoExec(c.GetCtx(), nil, sql, args...) } // DoExec commits the sql string and its arguments to underlying driver @@ -142,7 +142,7 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) { return nil, err } } - return c.DoPrepare(c.GetCtx(), link, sql) + return c.db.DoPrepare(c.GetCtx(), link, sql) } // DoPrepare calls prepare function on given link object and returns the statement object. diff --git a/database/gdb/gdb_model_delete.go b/database/gdb/gdb_model_delete.go index 2ce680444..1e08d68da 100644 --- a/database/gdb/gdb_model_delete.go +++ b/database/gdb/gdb_model_delete.go @@ -33,7 +33,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) { ) // Soft deleting. if !m.unscoped && fieldNameDelete != "" { - return m.db.GetCore().DoUpdate( + return m.db.DoUpdate( m.GetCtx(), m.getLink(true), m.tables, @@ -46,5 +46,5 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) { if !gstr.ContainsI(conditionStr, " WHERE ") { return nil, gerror.New("there should be WHERE condition statement for DELETE operation") } - return m.db.GetCore().DoDelete(m.GetCtx(), m.getLink(true), m.tables, conditionStr, conditionArgs...) + return m.db.DoDelete(m.GetCtx(), m.getLink(true), m.tables, conditionStr, conditionArgs...) } diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 3062354fa..09001e7d3 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -195,7 +195,7 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { list[k] = v } } - return m.db.GetCore().DoBatchInsert( + return m.db.DoBatchInsert( m.GetCtx(), m.getLink(true), m.tables, @@ -221,7 +221,7 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { data[fieldNameUpdate] = nowString } } - return m.db.GetCore().DoInsert( + return m.db.DoInsert( m.GetCtx(), m.getLink(true), m.tables, diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 041cb65e9..ff366f218 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -483,7 +483,7 @@ func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, e } } } - result, err = m.db.GetCore().DoGetAll( + result, err = m.db.DoGetAll( m.GetCtx(), m.getLink(false), sql, m.mergeArguments(args)..., ) // Cache the result. diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index 8252789e3..b32d1f5f8 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -82,7 +82,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro if !gstr.ContainsI(conditionStr, " WHERE ") { return nil, gerror.New("there should be WHERE condition statement for UPDATE operation") } - return m.db.GetCore().DoUpdate( + return m.db.DoUpdate( m.GetCtx(), m.getLink(true), m.tables, From eb723e47c2047544d9ae073c2860f7be5a0ffd17 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 5 Jun 2021 08:58:54 +0800 Subject: [PATCH 314/492] fix issue in gconv.MapDeep --- util/gconv/gconv_map.go | 2 +- util/gconv/gconv_z_unit_map_test.go | 54 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index abab585cb..6685433b2 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -301,7 +301,7 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b // It means this attribute field has desired tag. dataMap[mapKey] = doMapConvertForMapOrStructValue(false, rvAttrInterface, true, tags...) } else { - dataMap[mapKey] = doMapConvertForMapOrStructValue(false, rvAttrInterface, false, tags...) + dataMap[mapKey] = doMapConvertForMapOrStructValue(false, rvAttrInterface, recursive, tags...) } // The struct attribute is type of slice. diff --git a/util/gconv/gconv_z_unit_map_test.go b/util/gconv/gconv_z_unit_map_test.go index 55bca4f8d..f1676048c 100644 --- a/util/gconv/gconv_z_unit_map_test.go +++ b/util/gconv/gconv_z_unit_map_test.go @@ -317,6 +317,60 @@ func Test_MapDeep2(t *testing.T) { }) } +func Test_MapDeep3(t *testing.T) { + type Base struct { + Id int `c:"id"` + Date string `c:"date"` + } + type User struct { + UserBase Base `c:"base"` + Passport string `c:"passport"` + Password string `c:"password"` + Nickname string `c:"nickname"` + } + + gtest.C(t, func(t *gtest.T) { + user := &User{ + UserBase: Base{ + Id: 1, + Date: "2019-10-01", + }, + Passport: "john", + Password: "123456", + Nickname: "JohnGuo", + } + m := gconv.MapDeep(user) + t.Assert(m, g.Map{ + "base": g.Map{ + "id": user.UserBase.Id, + "date": user.UserBase.Date, + }, + "passport": user.Passport, + "password": user.Password, + "nickname": user.Nickname, + }) + }) + + gtest.C(t, func(t *gtest.T) { + user := &User{ + UserBase: Base{ + Id: 1, + Date: "2019-10-01", + }, + Passport: "john", + Password: "123456", + Nickname: "JohnGuo", + } + m := gconv.Map(user) + t.Assert(m, g.Map{ + "base": user.UserBase, + "passport": user.Passport, + "password": user.Password, + "nickname": user.Nickname, + }) + }) +} + func Test_MapDeepWithAttributeTag(t *testing.T) { type Ids struct { Id int `c:"id"` From 3ac577205937f818f283b6331cd093d1c0d982cb Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 6 Jun 2021 23:06:39 +0800 Subject: [PATCH 315/492] add UNION/UNION ALL feature for package gdb --- database/gdb/gdb.go | 6 + database/gdb/gdb_core.go | 36 ++++++ database/gdb/gdb_model.go | 3 +- database/gdb/gdb_model_condition.go | 2 + database/gdb/gdb_model_select.go | 26 ++++- database/gdb/gdb_model_time.go | 3 + database/gdb/gdb_z_mysql_union_test.go | 146 +++++++++++++++++++++++++ 7 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 database/gdb/gdb_z_mysql_union_test.go diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index ab415021a..7949dcf71 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -121,6 +121,8 @@ type DB interface { GetStruct(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetStruct. GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error // See Core.GetStructs. GetScan(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan. + Union(unions ...*Model) *Model // See Core.Union. + UnionAll(unions ...*Model) *Model // See Core.UnionAll. // =========================================================================== // Master/Slave specification support. @@ -252,6 +254,10 @@ type ( ) const ( + queryTypeNormal = 0 + queryTypeCount = 1 + unionTypeNormal = 0 + unionTypeAll = 1 insertOptionDefault = 0 insertOptionReplace = 1 insertOptionSave = 2 diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 84d50bbd9..fbace582e 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -224,6 +224,42 @@ func (c *Core) GetCount(sql string, args ...interface{}) (int, error) { return value.Int(), nil } +// Union does "(SELECT xxx FROM xxx) UNION (SELECT xxx FROM xxx) ..." statement. +func (c *Core) Union(unions ...*Model) *Model { + return c.doUnion(unionTypeNormal, unions...) +} + +// UnionAll does "(SELECT xxx FROM xxx) UNION ALL (SELECT xxx FROM xxx) ..." statement. +func (c *Core) UnionAll(unions ...*Model) *Model { + return c.doUnion(unionTypeAll, unions...) +} + +func (c *Core) doUnion(unionType int, unions ...*Model) *Model { + var ( + unionTypeStr string + composedSqlStr string + composedArgs = make([]interface{}, 0) + ) + if unionType == unionTypeAll { + unionTypeStr = "UNION ALL" + } else { + unionTypeStr = "UNION" + } + for _, v := range unions { + sqlWithHolder, holderArgs := v.getFormattedSqlAndArgs(queryTypeNormal, false) + if composedSqlStr == "" { + composedSqlStr += fmt.Sprintf(`(%s)`, sqlWithHolder) + } else { + composedSqlStr += fmt.Sprintf(` %s (%s)`, unionTypeStr, sqlWithHolder) + } + composedArgs = append(composedArgs, holderArgs...) + } + model := c.db.Model() + model.rawSql = composedSqlStr + model.extraArgs = composedArgs + return model +} + // PingMaster pings the master node to check authentication or keeps the connection alive. func (c *Core) PingMaster() error { if master, err := c.db.Master(); err != nil { diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 627051adb..68532f646 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -17,10 +17,11 @@ import ( "github.com/gogf/gf/text/gstr" ) -// Model is the DAO for ORM. +// Model is core struct implementing the DAO for ORM. type Model struct { db DB // Underlying DB interface. tx *TX // Underlying TX interface. + rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model. schema string // Custom database schema. linkType int // Mark for operation on master or slave. tablesInit string // Table names when model initialization. diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 7a7d41e5e..ecfba71d6 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -62,6 +62,8 @@ func (m *Model) WherePri(where interface{}, args ...interface{}) *Model { } // Wheref builds condition string using fmt.Sprintf and arguments. +// Note that if the number of `args` is more than the place holder in `format`, +// the extra `args` will be used as the where condition arguments of the Model. func (m *Model) Wheref(format string, args ...interface{}) *Model { var ( placeHolderCount = gstr.Count(format, "?") diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index ff366f218..2a9c02926 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -18,11 +18,6 @@ import ( "github.com/gogf/gf/util/gconv" ) -const ( - queryTypeNormal = "NormalQuery" - queryTypeCount = "CountQuery" -) - // Select is alias of Model.All. // See Model.All. // Deprecated, use All instead. @@ -458,6 +453,16 @@ func (m *Model) FindScan(pointer interface{}, where ...interface{}) error { return m.Scan(pointer) } +// Union does "(SELECT xxx FROM xxx) UNION (SELECT xxx FROM xxx) ..." statement for the model. +func (m *Model) Union(unions ...*Model) *Model { + return m.db.Union(unions...) +} + +// UnionAll does "(SELECT xxx FROM xxx) UNION ALL (SELECT xxx FROM xxx) ..." statement for the model. +func (m *Model) UnionAll(unions ...*Model) *Model { + return m.db.UnionAll(unions...) +} + // doGetAllBySql does the select statement on the database. func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, err error) { cacheKey := "" @@ -501,7 +506,7 @@ func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, e return result, err } -func (m *Model) getFormattedSqlAndArgs(queryType string, limit1 bool) (sqlWithHolder string, holderArgs []interface{}) { +func (m *Model) getFormattedSqlAndArgs(queryType int, limit1 bool) (sqlWithHolder string, holderArgs []interface{}) { switch queryType { case queryTypeCount: countFields := "COUNT(1)" @@ -519,6 +524,15 @@ func (m *Model) getFormattedSqlAndArgs(queryType string, limit1 bool) (sqlWithHo default: conditionWhere, conditionExtra, conditionArgs := m.formatCondition(limit1, false) + // Raw SQL Model, especially for UNION/UNION ALL featured SQL. + if m.rawSql != "" { + sqlWithHolder = fmt.Sprintf( + "%s%s", + m.rawSql, + conditionWhere+conditionExtra, + ) + return sqlWithHolder, conditionArgs + } // DO NOT quote the m.fields where, in case of fields like: // DISTINCT t.user_id uid sqlWithHolder = fmt.Sprintf( diff --git a/database/gdb/gdb_model_time.go b/database/gdb/gdb_model_time.go index e4047e4a1..76c1f0667 100644 --- a/database/gdb/gdb_model_time.go +++ b/database/gdb/gdb_model_time.go @@ -173,6 +173,9 @@ func (m *Model) getConditionOfTableStringForSoftDeleting(s string) string { // getPrimaryTableName parses and returns the primary table name. func (m *Model) getPrimaryTableName() string { + if m.tables == "" { + return "" + } array1 := gstr.SplitAndTrim(m.tables, ",") array2 := gstr.SplitAndTrim(array1[0], " ") array3 := gstr.SplitAndTrim(array2[0], ".") diff --git a/database/gdb/gdb_z_mysql_union_test.go b/database/gdb/gdb_z_mysql_union_test.go new file mode 100644 index 000000000..34df871b4 --- /dev/null +++ b/database/gdb/gdb_z_mysql_union_test.go @@ -0,0 +1,146 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb_test + +import ( + "github.com/gogf/gf/frame/g" + "testing" + + "github.com/gogf/gf/test/gtest" +) + +func Test_Union(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Union( + db.Model(table).Where("id", 1), + db.Model(table).Where("id", 2), + db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"), + ).OrderDesc("id").All() + + t.AssertNil(err) + + t.Assert(len(r), 3) + t.Assert(r[0]["id"], 3) + t.Assert(r[1]["id"], 2) + t.Assert(r[2]["id"], 1) + }) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Union( + db.Model(table).Where("id", 1), + db.Model(table).Where("id", 2), + db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"), + ).OrderDesc("id").One() + + t.AssertNil(err) + + t.Assert(r["id"], 3) + }) +} + +func Test_UnionAll(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + r, err := db.UnionAll( + db.Model(table).Where("id", 1), + db.Model(table).Where("id", 2), + db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"), + ).OrderDesc("id").All() + + t.AssertNil(err) + + t.Assert(len(r), 5) + t.Assert(r[0]["id"], 3) + t.Assert(r[1]["id"], 2) + t.Assert(r[2]["id"], 2) + t.Assert(r[3]["id"], 1) + t.Assert(r[4]["id"], 1) + }) + + gtest.C(t, func(t *gtest.T) { + r, err := db.UnionAll( + db.Model(table).Where("id", 1), + db.Model(table).Where("id", 2), + db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"), + ).OrderDesc("id").One() + + t.AssertNil(err) + + t.Assert(r["id"], 3) + }) +} + +func Test_Model_Union(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table).Union( + db.Model(table).Where("id", 1), + db.Model(table).Where("id", 2), + db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"), + ).OrderDesc("id").All() + + t.AssertNil(err) + + t.Assert(len(r), 3) + t.Assert(r[0]["id"], 3) + t.Assert(r[1]["id"], 2) + t.Assert(r[2]["id"], 1) + }) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table).Union( + db.Model(table).Where("id", 1), + db.Model(table).Where("id", 2), + db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"), + ).OrderDesc("id").One() + + t.AssertNil(err) + + t.Assert(r["id"], 3) + }) +} + +func Test_Model_UnionAll(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table).UnionAll( + db.Model(table).Where("id", 1), + db.Model(table).Where("id", 2), + db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"), + ).OrderDesc("id").All() + + t.AssertNil(err) + + t.Assert(len(r), 5) + t.Assert(r[0]["id"], 3) + t.Assert(r[1]["id"], 2) + t.Assert(r[2]["id"], 2) + t.Assert(r[3]["id"], 1) + t.Assert(r[4]["id"], 1) + }) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table).UnionAll( + db.Model(table).Where("id", 1), + db.Model(table).Where("id", 2), + db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"), + ).OrderDesc("id").One() + + t.AssertNil(err) + + t.Assert(r["id"], 3) + }) +} From 4f82be5bc0fb282bc57873db491b44b9a09adc80 Mon Sep 17 00:00:00 2001 From: fangjw Date: Mon, 7 Jun 2021 10:17:23 +0800 Subject: [PATCH 316/492] Fixed incorrect type conversion --- util/gutil/gutil_comparator.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/gutil/gutil_comparator.go b/util/gutil/gutil_comparator.go index 39c364868..0dbf4b98d 100644 --- a/util/gutil/gutil_comparator.go +++ b/util/gutil/gutil_comparator.go @@ -77,8 +77,8 @@ func ComparatorUint64(a, b interface{}) int { // ComparatorFloat32 provides a basic comparison on float32. func ComparatorFloat32(a, b interface{}) int { - aFloat := gconv.Float64(a) - bFloat := gconv.Float64(b) + aFloat := gconv.Float32(a) + bFloat := gconv.Float32(b) if aFloat == bFloat { return 0 } From 6eb7261dfdfa2754c864c3fbdc5be938751d9d59 Mon Sep 17 00:00:00 2001 From: qinyuguang Date: Fri, 4 Jun 2021 18:26:41 +0800 Subject: [PATCH 317/492] add timezone configuration for package gdb, effective for mysql and pgsql --- database/gdb/gdb_core_config.go | 1 + database/gdb/gdb_driver_mysql.go | 4 ++++ database/gdb/gdb_driver_pgsql.go | 7 +++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index 8ce85dc8e..3ca62b382 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -36,6 +36,7 @@ type ConfigNode struct { DryRun bool `json:"dryRun"` // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements. Weight int `json:"weight"` // (Optional) Weight for load balance calculating, it's useless if there's just one node. Charset string `json:"charset"` // (Optional, "utf8mb4" in default) Custom charset when operating on database. + Timezone string `json:"timezone"` // (Optional) Sets the time zone for displaying and interpreting time stamps. LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored. MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool. MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool. diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index aab15faec..b84de36ae 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -10,6 +10,7 @@ import ( "context" "database/sql" "fmt" + "net/url" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" @@ -47,6 +48,9 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) { "%s:%s@tcp(%s:%s)/%s?charset=%s", config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset, ) + if config.Timezone != "" { + source = fmt.Sprintf("%s&loc=%s", source, url.QueryEscape(config.Timezone)) + } } intlog.Printf("Open: %s", source) if db, err := sql.Open("mysql", source); err == nil { diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index b0695f8c0..3a58113cb 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -47,6 +47,9 @@ func (d *DriverPgsql) Open(config *ConfigNode) (*sql.DB, error) { "user=%s password=%s host=%s port=%s dbname=%s sslmode=disable", config.User, config.Pass, config.Host, config.Port, config.Name, ) + if config.Timezone != "" { + source = fmt.Sprintf("%s timezone=%s", source, config.Timezone) + } } intlog.Printf("Open: %s", source) if db, err := sql.Open("postgres", source); err == nil { @@ -135,9 +138,9 @@ func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...s result Result link, err = d.SlaveLink(useSchema) structureSql = fmt.Sprintf(` -SELECT a.attname AS field, t.typname AS type FROM pg_class c, pg_attribute a +SELECT a.attname AS field, t.typname AS type FROM pg_class c, pg_attribute a LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,pg_type t -WHERE c.relname = '%s' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid +WHERE c.relname = '%s' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid ORDER BY a.attnum`, strings.ToLower(table), ) From e68e7a322423e501874d370d21884c6b460ce99e Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 8 Jun 2021 20:32:34 +0800 Subject: [PATCH 318/492] remove Batch*/DoBatchInsert functions for package gdb --- database/gdb/gdb.go | 18 +-- database/gdb/gdb_core.go | 139 +++---------------- database/gdb/gdb_core_transaction.go | 36 ----- database/gdb/gdb_driver_oracle.go | 116 +--------------- database/gdb/gdb_func.go | 6 +- database/gdb/gdb_model_insert.go | 83 +++++------ database/gdb/gdb_z_init_test.go | 2 +- database/gdb/gdb_z_mysql_method_test.go | 10 +- database/gdb/gdb_z_mysql_transaction_test.go | 10 +- 9 files changed, 73 insertions(+), 347 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 7949dcf71..70c3de495 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -83,16 +83,11 @@ type DB interface { // Common APIs for CURD. // =========================================================================== - Insert(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Insert. - InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.InsertIgnore. - InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) // See Core.InsertAndGetId. - Replace(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Replace. - Save(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Save. - - BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchInsert. - BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchReplace. - BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchSave. - + Insert(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Insert. + InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.InsertIgnore. + InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) // See Core.InsertAndGetId. + Replace(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Replace. + Save(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Save. Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Update. Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete. @@ -104,8 +99,7 @@ type DB interface { DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec. DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare. DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll. - DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoInsert. - DoBatchInsert(ctx context.Context, link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoBatchInsert. + DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch int) (result sql.Result, err error) // See Core.DoInsert. DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate. DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete. diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index fbace582e..c7a66b232 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -367,120 +367,7 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e // 1: replace: if there's unique/primary key in the data, it deletes it from table and inserts a new one; // 2: save: if there's unique/primary key in the data, it updates it or else inserts a new one; // 3: ignore: if there's unique/primary key in the data, it ignores the inserting; -func (c *Core) DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) { - table = c.QuotePrefixTableName(table) - var ( - fields []string - values []string - params []interface{} - dataMap Map - reflectValue = reflect.ValueOf(data) - reflectKind = reflectValue.Kind() - ) - if reflectKind == reflect.Ptr { - reflectValue = reflectValue.Elem() - reflectKind = reflectValue.Kind() - } - switch reflectKind { - case reflect.Slice, reflect.Array: - return c.db.DoBatchInsert(ctx, link, table, data, option, batch...) - case reflect.Struct: - if _, ok := data.(apiInterfaces); ok { - return c.db.DoBatchInsert(ctx, link, table, data, option, batch...) - } else { - dataMap = ConvertDataForTableRecord(data) - } - case reflect.Map: - dataMap = ConvertDataForTableRecord(data) - default: - return result, gerror.New(fmt.Sprint("unsupported data type:", reflectKind)) - } - if len(dataMap) == 0 { - return nil, gerror.New("data cannot be empty") - } - var ( - charL, charR = c.db.GetChars() - operation = GetInsertOperationByOption(option) - updateStr = "" - ) - for k, v := range dataMap { - fields = append(fields, charL+k+charR) - if s, ok := v.(Raw); ok { - values = append(values, gconv.String(s)) - } else { - values = append(values, "?") - params = append(params, v) - } - } - if option == insertOptionSave { - for k, _ := range dataMap { - // If it's SAVE operation, - // do not automatically update the creating time. - if c.isSoftCreatedFiledName(k) { - continue - } - if len(updateStr) > 0 { - updateStr += "," - } - updateStr += fmt.Sprintf( - "%s%s%s=VALUES(%s%s%s)", - charL, k, charR, - charL, k, charR, - ) - } - updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", updateStr) - } - if link == nil { - if link, err = c.MasterLink(); err != nil { - return nil, err - } - } - return c.db.DoExec(ctx, link, fmt.Sprintf( - "%s INTO %s(%s) VALUES(%s) %s", - operation, table, strings.Join(fields, ","), - strings.Join(values, ","), updateStr, - ), params...) -} - -// BatchInsert batch inserts data. -// The parameter `list` must be type of slice of map or struct. -func (c *Core) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) { - if len(batch) > 0 { - return c.Model(table).Data(list).Batch(batch[0]).Insert() - } - return c.Model(table).Data(list).Insert() -} - -// BatchInsertIgnore batch inserts data with ignore option. -// The parameter `list` must be type of slice of map or struct. -func (c *Core) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) { - if len(batch) > 0 { - return c.Model(table).Data(list).Batch(batch[0]).InsertIgnore() - } - return c.Model(table).Data(list).InsertIgnore() -} - -// BatchReplace batch replaces data. -// The parameter `list` must be type of slice of map or struct. -func (c *Core) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) { - if len(batch) > 0 { - return c.Model(table).Data(list).Batch(batch[0]).Replace() - } - return c.Model(table).Data(list).Replace() -} - -// BatchSave batch replaces data. -// The parameter `list` must be type of slice of map or struct. -func (c *Core) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) { - if len(batch) > 0 { - return c.Model(table).Data(list).Batch(batch[0]).Save() - } - return c.Model(table).Data(list).Save() -} - -// DoBatchInsert batch inserts/replaces/saves data. -// This function is usually used for custom interface definition, you do not need call it manually. -func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) { +func (c *Core) DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch int) (result sql.Result, err error) { table = c.QuotePrefixTableName(table) var ( keys []string // Field names. @@ -488,18 +375,25 @@ func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list params []interface{} // Values that will be committed to underlying database driver. listMap List // The data list that passed from caller. ) - switch value := list.(type) { + switch value := data.(type) { case Result: listMap = value.List() + case Record: listMap = List{value.Map()} + case List: listMap = value + for i, v := range listMap { + listMap[i] = ConvertDataForTableRecord(v) + } + case Map: - listMap = List{value} + listMap = List{ConvertDataForTableRecord(value)} + default: var ( - rv = reflect.ValueOf(list) + rv = reflect.ValueOf(data) kind = rv.Kind() ) if kind == reflect.Ptr { @@ -513,8 +407,10 @@ func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list for i := 0; i < rv.Len(); i++ { listMap[i] = ConvertDataForTableRecord(rv.Index(i).Interface()) } + case reflect.Map: listMap = List{ConvertDataForTableRecord(value)} + case reflect.Struct: if v, ok := value.(apiInterfaces); ok { var ( @@ -528,6 +424,7 @@ func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list } else { listMap = List{ConvertDataForTableRecord(value)} } + default: return result, gerror.New(fmt.Sprint("unsupported list type:", kind)) } @@ -570,9 +467,8 @@ func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list } updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", updateStr) } - batchNum := defaultBatchNumber - if len(batch) > 0 && batch[0] > 0 { - batchNum = batch[0] + if batch <= 0 { + batch = defaultBatchNumber } var ( listMapLen = len(listMap) @@ -591,7 +487,8 @@ func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list } } valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")") - if len(valueHolder) == batchNum || (i == listMapLen-1 && len(valueHolder) > 0) { + // Batch package checks: It meets the batch number or it is the last element. + if len(valueHolder) == batch || (i == listMapLen-1 && len(valueHolder) > 0) { r, err := c.db.DoExec(ctx, link, fmt.Sprintf( "%s INTO %s(%s) VALUES%s %s", operation, table, keysStr, diff --git a/database/gdb/gdb_core_transaction.go b/database/gdb/gdb_core_transaction.go index d9e575ce8..1f09850e5 100644 --- a/database/gdb/gdb_core_transaction.go +++ b/database/gdb/gdb_core_transaction.go @@ -503,42 +503,6 @@ func (tx *TX) Save(table string, data interface{}, batch ...int) (sql.Result, er return tx.Model(table).Ctx(tx.ctx).Data(data).Save() } -// BatchInsert batch inserts data. -// The parameter `list` must be type of slice of map or struct. -func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) { - if len(batch) > 0 { - return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).Insert() - } - return tx.Model(table).Ctx(tx.ctx).Data(list).Insert() -} - -// BatchInsertIgnore batch inserts data with ignore option. -// The parameter `list` must be type of slice of map or struct. -func (tx *TX) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) { - if len(batch) > 0 { - return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).InsertIgnore() - } - return tx.Model(table).Ctx(tx.ctx).Data(list).InsertIgnore() -} - -// BatchReplace batch replaces data. -// The parameter `list` must be type of slice of map or struct. -func (tx *TX) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) { - if len(batch) > 0 { - return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).Replace() - } - return tx.Model(table).Ctx(tx.ctx).Data(list).Replace() -} - -// BatchSave batch replaces data. -// The parameter `list` must be type of slice of map or struct. -func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) { - if len(batch) > 0 { - return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).Save() - } - return tx.Model(table).Ctx(tx.ctx).Data(list).Save() -} - // Update does "UPDATE ... " statement for the table. // // The parameter `data` can be type of string/map/gmap/struct/*struct, etc. diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index 9d8543936..bfffb4afc 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -264,114 +264,7 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[ return } -func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) { - var ( - fields []string - values []string - params []interface{} - dataMap Map - rv = reflect.ValueOf(data) - kind = rv.Kind() - ) - if kind == reflect.Ptr { - rv = rv.Elem() - kind = rv.Kind() - } - switch kind { - case reflect.Slice, reflect.Array: - return d.DoBatchInsert(ctx, link, table, data, option, batch...) - case reflect.Map: - fallthrough - case reflect.Struct: - dataMap = ConvertDataForTableRecord(data) - default: - return result, gerror.New(fmt.Sprint("unsupported data type:", kind)) - } - var ( - indexes = make([]string, 0) - indexMap = make(map[string]string) - indexExists = false - ) - if option != insertOptionDefault { - index, err := d.getTableUniqueIndex(table) - if err != nil { - return nil, err - } - - if len(index) > 0 { - for _, v := range index { - for k, _ := range v { - indexes = append(indexes, k) - } - indexMap = v - indexExists = true - break - } - } - } - var ( - subSqlStr = make([]string, 0) - onStr = make([]string, 0) - updateStr = make([]string, 0) - ) - charL, charR := d.db.GetChars() - for k, v := range dataMap { - k = strings.ToUpper(k) - - // 操作类型为REPLACE/SAVE时且存在唯一索引才使用merge,否则使用insert - if (option == insertOptionReplace || option == insertOptionSave) && indexExists { - fields = append(fields, tableAlias1+"."+charL+k+charR) - values = append(values, tableAlias2+"."+charL+k+charR) - params = append(params, v) - subSqlStr = append(subSqlStr, fmt.Sprintf("%s?%s %s", charL, charR, k)) - //m erge中的on子句中由唯一索引组成, update子句中不含唯一索引 - if _, ok := indexMap[k]; ok { - onStr = append(onStr, fmt.Sprintf("%s.%s = %s.%s ", tableAlias1, k, tableAlias2, k)) - } else { - updateStr = append(updateStr, fmt.Sprintf("%s.%s = %s.%s ", tableAlias1, k, tableAlias2, k)) - } - } else { - fields = append(fields, charL+k+charR) - values = append(values, "?") - params = append(params, v) - } - } - - if link == nil { - if link, err = d.MasterLink(); err != nil { - return nil, err - } - } - - if indexExists && option != insertOptionDefault { - switch option { - case - insertOptionReplace, - insertOptionSave: - tmp := fmt.Sprintf( - "MERGE INTO %s %s USING(SELECT %s FROM DUAL) %s ON(%s) WHEN MATCHED THEN UPDATE SET %s WHEN NOT MATCHED THEN INSERT (%s) VALUES(%s)", - table, tableAlias1, strings.Join(subSqlStr, ","), tableAlias2, - strings.Join(onStr, "AND"), strings.Join(updateStr, ","), strings.Join(fields, ","), strings.Join(values, ","), - ) - return d.DoExec(ctx, link, tmp, params...) - - case insertOptionIgnore: - return d.DoExec(ctx, link, fmt.Sprintf( - "INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(%s(%s)) */ INTO %s(%s) VALUES(%s)", - table, strings.Join(indexes, ","), table, strings.Join(fields, ","), strings.Join(values, ","), - ), params...) - } - } - - return d.DoExec(ctx, link, - fmt.Sprintf( - "INSERT INTO %s(%s) VALUES(%s)", - table, strings.Join(fields, ","), strings.Join(values, ","), - ), - params...) -} - -func (d *DriverOracle) DoBatchInsert(ctx context.Context, link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) { +func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, list interface{}, option int, batch int) (result sql.Result, err error) { var ( keys []string values []string @@ -447,9 +340,8 @@ func (d *DriverOracle) DoBatchInsert(ctx context.Context, link Link, table strin return batchResult, nil } - batchNum := defaultBatchNumber - if len(batch) > 0 { - batchNum = batch[0] + if batch <= 0 { + batch = defaultBatchNumber } // Format "INSERT...INTO..." statement. intoStr := make([]string, 0) @@ -459,7 +351,7 @@ func (d *DriverOracle) DoBatchInsert(ctx context.Context, link Link, table strin } values = append(values, valueHolderStr) intoStr = append(intoStr, fmt.Sprintf(" INTO %s(%s) VALUES(%s) ", table, keyStr, valueHolderStr)) - if len(intoStr) == batchNum { + if len(intoStr) == batch { r, err := d.DoExec(ctx, link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...) if err != nil { return r, err diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index fc2c4d1ca..c0b9155a2 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -142,8 +142,8 @@ func GetInsertOperationByOption(option int) string { // ConvertDataForTableRecord is a very important function, which does converting for any data that // will be inserted into table as a record. // -// The parameter `obj` should be type of *map/map/*struct/struct. -// It supports inherit struct definition for struct. +// The parameter `value` should be type of *map/map/*struct/struct. +// It supports embedded struct definition for struct. func ConvertDataForTableRecord(value interface{}) map[string]interface{} { var ( rvValue reflect.Value @@ -186,7 +186,7 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} { // DataToMapDeep converts `value` to map type recursively. // The parameter `value` should be type of *map/map/*struct/struct. -// It supports inherit struct definition for struct. +// It supports embedded struct definition for struct. func DataToMapDeep(value interface{}) map[string]interface{} { if v, ok := value.(apiMapStrAny); ok { return v.MapStrAny() diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 09001e7d3..b3fab329b 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -166,68 +166,47 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { return nil, gerror.New("inserting into table with empty data") } var ( + list List nowString = gtime.Now().String() fieldNameCreate = m.getSoftFieldNameCreated() fieldNameUpdate = m.getSoftFieldNameUpdated() fieldNameDelete = m.getSoftFieldNameDeleted() ) - // Batch operation. - if list, ok := m.data.(List); ok { - batch := defaultBatchNumber - if m.batch > 0 { - batch = m.batch - } - newData, err := m.filterDataForInsertOrUpdate(list) - if err != nil { - return nil, err - } - list = newData.(List) - // Automatic handling for creating/updating time. - if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") { - for k, v := range list { - gutil.MapDelete(v, fieldNameCreate, fieldNameUpdate, fieldNameDelete) - if fieldNameCreate != "" { - v[fieldNameCreate] = nowString - } - if fieldNameUpdate != "" { - v[fieldNameUpdate] = nowString - } - list[k] = v - } - } - return m.db.DoBatchInsert( - m.GetCtx(), - m.getLink(true), - m.tables, - newData, - option, - batch, - ) + newData, err := m.filterDataForInsertOrUpdate(m.data) + if err != nil { + return nil, err } - // Single operation. - if data, ok := m.data.(Map); ok { - newData, err := m.filterDataForInsertOrUpdate(data) - if err != nil { - return nil, err - } - data = newData.(Map) - // Automatic handling for creating/updating time. - if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") { - gutil.MapDelete(data, fieldNameCreate, fieldNameUpdate, fieldNameDelete) + // It converts any data to List type for inserting. + switch newData.(type) { + case Map: + list = List{newData.(Map)} + + case List: + list = newData.(List) + + default: + return nil, gerror.New("inserting into table with invalid data type") + } + // Automatic handling for creating/updating time. + if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") { + for k, v := range list { + gutil.MapDelete(v, fieldNameCreate, fieldNameUpdate, fieldNameDelete) if fieldNameCreate != "" { - data[fieldNameCreate] = nowString + v[fieldNameCreate] = nowString } if fieldNameUpdate != "" { - data[fieldNameUpdate] = nowString + v[fieldNameUpdate] = nowString } + list[k] = v } - return m.db.DoInsert( - m.GetCtx(), - m.getLink(true), - m.tables, - newData, - option, - ) } - return nil, gerror.New("inserting into table with invalid data type") + return m.db.DoInsert(m.GetCtx(), m.getLink(true), m.tables, list, option, m.getBatch()) +} + +func (m *Model) getBatch() int { + batch := defaultBatchNumber + if m.batch > 0 { + batch = m.batch + } + return batch } diff --git a/database/gdb/gdb_z_init_test.go b/database/gdb/gdb_z_init_test.go index abe205257..d541322a7 100644 --- a/database/gdb/gdb_z_init_test.go +++ b/database/gdb/gdb_z_init_test.go @@ -195,7 +195,7 @@ func createInitTableWithDb(db gdb.DB, table ...string) (name string) { }) } - result, err := db.BatchInsert(name, array.Slice()) + result, err := db.Insert(name, array.Slice()) gtest.AssertNil(err) n, e := result.RowsAffected() diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index d83f2f364..86e24a930 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -329,7 +329,7 @@ func Test_DB_BatchInsert(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - r, err := db.BatchInsert(table, g.List{ + r, err := db.Insert(table, g.List{ { "id": 2, "passport": "t2", @@ -357,7 +357,7 @@ func Test_DB_BatchInsert(t *testing.T) { table := createTable() defer dropTable(table) // []interface{} - r, err := db.BatchInsert(table, g.Slice{ + r, err := db.Insert(table, g.Slice{ g.Map{ "id": 2, "passport": "t2", @@ -382,7 +382,7 @@ func Test_DB_BatchInsert(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - result, err := db.BatchInsert(table, g.Map{ + result, err := db.Insert(table, g.Map{ "id": 1, "passport": "t1", "password": "p1", @@ -416,7 +416,7 @@ func Test_DB_BatchInsert_Struct(t *testing.T) { NickName: "T1", CreateTime: gtime.Now(), } - result, err := db.BatchInsert(table, user) + result, err := db.Insert(table, user) t.AssertNil(err) n, _ := result.RowsAffected() t.Assert(n, 1) @@ -1283,7 +1283,7 @@ func Test_DB_Prefix(t *testing.T) { }) } - result, err := db.BatchInsert(name, array.Slice()) + result, err := db.Insert(name, array.Slice()) t.AssertNil(err) n, e := result.RowsAffected() diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index 906bc6aca..cb61b9867 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -163,7 +163,7 @@ func Test_TX_BatchInsert(t *testing.T) { if err != nil { gtest.Error(err) } - if _, err := tx.BatchInsert(table, g.List{ + if _, err := tx.Insert(table, g.List{ { "id": 2, "passport": "t", @@ -201,7 +201,7 @@ func Test_TX_BatchReplace(t *testing.T) { if err != nil { gtest.Error(err) } - if _, err := tx.BatchReplace(table, g.List{ + if _, err := tx.Replace(table, g.List{ { "id": 2, "passport": "USER_2", @@ -244,7 +244,7 @@ func Test_TX_BatchSave(t *testing.T) { if err != nil { gtest.Error(err) } - if _, err := tx.BatchSave(table, g.List{ + if _, err := tx.Save(table, g.List{ { "id": 4, "passport": "USER_4", @@ -956,8 +956,8 @@ func Test_Transaction_Nested_TX_Transaction_UseDB(t *testing.T) { table := createTable() defer dropTable(table) - db.SetDebug(true) - defer db.SetDebug(false) + //db.SetDebug(true) + //defer db.SetDebug(false) gtest.C(t, func(t *gtest.T) { var ( From 97879834bcae44bdb6230d1673679d7d2cd7052b Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 8 Jun 2021 21:28:41 +0800 Subject: [PATCH 319/492] remove deprecated functions for package gdb --- database/gdb/gdb_type_record_deprecated.go | 27 ---------- database/gdb/gdb_type_result_deprecated.go | 57 ---------------------- 2 files changed, 84 deletions(-) delete mode 100644 database/gdb/gdb_type_record_deprecated.go delete mode 100644 database/gdb/gdb_type_result_deprecated.go diff --git a/database/gdb/gdb_type_record_deprecated.go b/database/gdb/gdb_type_record_deprecated.go deleted file mode 100644 index d07a79807..000000000 --- a/database/gdb/gdb_type_record_deprecated.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gdb - -// Deprecated, use Json instead. -func (r Record) ToJson() string { - return r.Json() -} - -// Deprecated, use Xml instead. -func (r Record) ToXml(rootTag ...string) string { - return r.Xml(rootTag...) -} - -// Deprecated, use Map instead. -func (r Record) ToMap() Map { - return r.Map() -} - -// Deprecated, use Struct instead. -func (r Record) ToStruct(pointer interface{}) error { - return r.Struct(pointer) -} diff --git a/database/gdb/gdb_type_result_deprecated.go b/database/gdb/gdb_type_result_deprecated.go deleted file mode 100644 index 8f6faf3da..000000000 --- a/database/gdb/gdb_type_result_deprecated.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gdb - -// Deprecated, use Json instead. -func (r Result) ToJson() string { - return r.Json() -} - -// Deprecated, use Xml instead. -func (r Result) ToXml(rootTag ...string) string { - return r.Xml(rootTag...) -} - -// Deprecated, use List instead. -func (r Result) ToList() List { - return r.List() -} - -// Deprecated, use MapKeyStr instead. -func (r Result) ToStringMap(key string) map[string]Map { - return r.MapKeyStr(key) -} - -// Deprecated, use MapKetInt instead. -func (r Result) ToIntMap(key string) map[int]Map { - return r.MapKeyInt(key) -} - -// Deprecated, use MapKeyUint instead. -func (r Result) ToUintMap(key string) map[uint]Map { - return r.MapKeyUint(key) -} - -// Deprecated, use RecordKeyStr instead. -func (r Result) ToStringRecord(key string) map[string]Record { - return r.RecordKeyStr(key) -} - -// Deprecated, use RecordKetInt instead. -func (r Result) ToIntRecord(key string) map[int]Record { - return r.RecordKeyInt(key) -} - -// Deprecated, use RecordKetUint instead. -func (r Result) ToUintRecord(key string) map[uint]Record { - return r.RecordKeyUint(key) -} - -// Deprecated, use Structs instead. -func (r Result) ToStructs(pointer interface{}) (err error) { - return r.Structs(pointer) -} From 7c4a0453b7a41d2c64d39a4a0e4ee98ae27fe72f Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 8 Jun 2021 21:35:54 +0800 Subject: [PATCH 320/492] improve handler feature for package glog --- os/glog/glog_logger.go | 1 - os/glog/glog_logger_config.go | 2 +- os/glog/glog_logger_handler.go | 3 +++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 7f4dcbc91..44a704c6e 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -62,7 +62,6 @@ func New() *Logger { init: gtype.NewBool(), config: DefaultConfig(), } - logger.config.Handlers = []Handler{defaultHandler} return logger } diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 6b00a6afd..a08275c1d 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -250,5 +250,5 @@ func (l *Logger) SetPrefix(prefix string) { // SetHandlers sets the logging handlers for current logger. func (l *Logger) SetHandlers(handlers ...Handler) { - l.config.Handlers = append(handlers, defaultHandler) + l.config.Handlers = handlers } diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index 69a703992..acca9ea35 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -75,5 +75,8 @@ func (i *HandlerInput) Next() { if len(i.logger.config.Handlers)-1 > i.index { i.index++ i.logger.config.Handlers[i.index](i.Ctx, i) + } else { + // The last handler is the default handler. + defaultHandler(i.Ctx, i) } } From fe7209e76d854bbc303d41aa2735c3b83246e019 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 8 Jun 2021 21:55:55 +0800 Subject: [PATCH 321/492] rename HandleSqlBeforeCommit to DoCommit for package gdb --- database/gdb/gdb.go | 14 ++++---------- database/gdb/gdb_core_underlying.go | 4 ++-- database/gdb/gdb_core_utility.go | 8 +++++--- database/gdb/gdb_driver_mssql.go | 4 ++-- database/gdb/gdb_driver_mysql.go | 4 ++-- database/gdb/gdb_driver_oracle.go | 4 ++-- database/gdb/gdb_driver_pgsql.go | 4 ++-- database/gdb/gdb_driver_sqlite.go | 4 ++-- database/gdb/gdb_z_driver_test.go | 12 ++++++------ 9 files changed, 27 insertions(+), 31 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 70c3de495..aef802395 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -95,13 +95,14 @@ type DB interface { // Internal APIs for CURD, which can be overwrote for custom CURD implements. // =========================================================================== - DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery. - DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec. - DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare. DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll. DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch int) (result sql.Result, err error) // See Core.DoInsert. DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate. DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete. + DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery. + DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec. + DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}) // See Core.DoCommit. + DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare. // =========================================================================== // Query APIs for convenience purpose. @@ -169,13 +170,6 @@ type DB interface { Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables. TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields. FilteredLinkInfo() string // See Core.FilteredLinkInfo. - - // HandleSqlBeforeCommit is a hook function, which deals with the sql string before - // it's committed to underlying driver. The parameter `link` specifies the current - // database connection operation object. You can modify the sql string `sql` and its - // arguments `args` as you wish before they're committed to driver. - // Also see Core.HandleSqlBeforeCommit. - HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) } // Core is the base struct for database management. diff --git a/database/gdb/gdb_core_underlying.go b/database/gdb/gdb_core_underlying.go index baa0e5027..848270ac0 100644 --- a/database/gdb/gdb_core_underlying.go +++ b/database/gdb/gdb_core_underlying.go @@ -35,7 +35,7 @@ func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...inter } // Link execution. sql, args = formatSql(sql, args) - sql, args = c.db.HandleSqlBeforeCommit(ctx, link, sql, args) + sql, args = c.db.DoCommit(ctx, link, sql, args) if c.GetConfig().QueryTimeout > 0 { ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) } @@ -87,7 +87,7 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf } // Link execution. sql, args = formatSql(sql, args) - sql, args = c.db.HandleSqlBeforeCommit(ctx, link, sql, args) + sql, args = c.db.DoCommit(ctx, link, sql, args) if c.GetConfig().ExecTimeout > 0 { var cancelFunc context.CancelFunc ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout) diff --git a/database/gdb/gdb_core_utility.go b/database/gdb/gdb_core_utility.go index 0eaeb16c4..2fe4b1c19 100644 --- a/database/gdb/gdb_core_utility.go +++ b/database/gdb/gdb_core_utility.go @@ -63,9 +63,11 @@ func (c *Core) GetChars() (charLeft string, charRight string) { return "", "" } -// HandleSqlBeforeCommit handles the sql before posts it to database. -// It does nothing in default. -func (c *Core) HandleSqlBeforeCommit(sql string) string { +// DoCommit is a hook function, which deals with the sql string before it's committed to underlying driver. +// The parameter `link` specifies the current database connection operation object. You can modify the sql +// string `sql` and its arguments `args` as you wish before they're committed to driver. +// Also see Core.DoCommit. +func (c *Core) DoCommit(sql string) string { return sql } diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index 33d3f8732..79c1afc1b 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -78,8 +78,8 @@ func (d *DriverMssql) GetChars() (charLeft string, charRight string) { return "\"", "\"" } -// HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverMssql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { +// DoCommit deals with the sql string before commits it to underlying sql driver. +func (d *DriverMssql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { var index int // Convert place holder char '?' to string "@px". str, _ := gregex.ReplaceStringFunc("\\?", sql, func(s string) string { diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index b84de36ae..207a3d590 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -80,8 +80,8 @@ func (d *DriverMysql) GetChars() (charLeft string, charRight string) { return "`", "`" } -// HandleSqlBeforeCommit handles the sql before posts it to database. -func (d *DriverMysql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { +// DoCommit handles the sql before posts it to database. +func (d *DriverMysql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { return sql, args } diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index bfffb4afc..313b69e6c 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -84,8 +84,8 @@ func (d *DriverOracle) GetChars() (charLeft string, charRight string) { return "\"", "\"" } -// HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverOracle) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}) { +// DoCommit deals with the sql string before commits it to underlying sql driver. +func (d *DriverOracle) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}) { var index int // Convert place holder char '?' to string ":vx". newSql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string { diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 3a58113cb..ddcff4066 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -79,8 +79,8 @@ func (d *DriverPgsql) GetChars() (charLeft string, charRight string) { return "\"", "\"" } -// HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverPgsql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { +// DoCommit deals with the sql string before commits it to underlying sql driver. +func (d *DriverPgsql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { var index int // Convert place holder char '?' to string "$x". sql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string { diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index 99ae55fcf..90c8f36c9 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -66,10 +66,10 @@ func (d *DriverSqlite) GetChars() (charLeft string, charRight string) { return "`", "`" } -// HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. +// DoCommit deals with the sql string before commits it to underlying sql driver. // TODO 需要增加对Save方法的支持,可使用正则来实现替换, // TODO 将ON DUPLICATE KEY UPDATE触发器修改为两条SQL语句(INSERT OR IGNORE & UPDATE) -func (d *DriverSqlite) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { +func (d *DriverSqlite) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { return sql, args } diff --git a/database/gdb/gdb_z_driver_test.go b/database/gdb/gdb_z_driver_test.go index e166856d9..3d6053d5d 100644 --- a/database/gdb/gdb_z_driver_test.go +++ b/database/gdb/gdb_z_driver_test.go @@ -18,9 +18,9 @@ import ( // MyDriver is a custom database driver, which is used for testing only. // For simplifying the unit testing case purpose, MyDriver struct inherits the mysql driver -// gdb.DriverMysql and overwrites its function HandleSqlBeforeCommit. -// So if there's any sql execution, it goes through MyDriver.HandleSqlBeforeCommit firstly and -// then gdb.DriverMysql.HandleSqlBeforeCommit. +// gdb.DriverMysql and overwrites its function DoCommit. +// So if there's any sql execution, it goes through MyDriver.DoCommit firstly and +// then gdb.DriverMysql.DoCommit. // You can call it sql "HOOK" or "HiJack" as your will. type MyDriver struct { *gdb.DriverMysql @@ -41,11 +41,11 @@ func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) { }, nil } -// HandleSqlBeforeCommit handles the sql before posts it to database. +// DoCommit handles the sql before posts it to database. // It here overwrites the same method of gdb.DriverMysql and makes some custom changes. -func (d *MyDriver) HandleSqlBeforeCommit(ctx context.Context, link gdb.Link, sql string, args []interface{}) (string, []interface{}) { +func (d *MyDriver) DoCommit(ctx context.Context, link gdb.Link, sql string, args []interface{}) (string, []interface{}) { latestSqlString.Set(sql) - return d.DriverMysql.HandleSqlBeforeCommit(ctx, link, sql, args) + return d.DriverMysql.DoCommit(ctx, link, sql, args) } func init() { From f2bc29e5c12d1717040316478a2eb5ec76eb61b8 Mon Sep 17 00:00:00 2001 From: qinyuguang Date: Wed, 9 Jun 2021 18:49:49 +0800 Subject: [PATCH 322/492] add gutil.SliceToMapWithColumnAsKey --- util/gutil/gutil_slice.go | 26 ++++++++++++++++++++++++++ util/gutil/gutil_z_unit_slice_test.go | 20 ++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/util/gutil/gutil_slice.go b/util/gutil/gutil_slice.go index c2a183495..7da40a8db 100644 --- a/util/gutil/gutil_slice.go +++ b/util/gutil/gutil_slice.go @@ -65,3 +65,29 @@ func SliceToMap(slice interface{}) map[string]interface{} { } return nil } + +// SliceToMapWithColumnAsKey converts slice type variable `slice` to `map[interface{}]interface{}` +// The value of specified column use as the key for returned map. +// Eg: +// SliceToMapWithColumnAsKey([{"K1": "v1", "K2": 1}, {"K1": "v2", "K2": 2}], "K1") => {"v1": {"K1": "v1", "K2": 1}, "v2": {"K1": "v2", "K2": 2}} +// SliceToMapWithColumnAsKey([{"K1": "v1", "K2": 1}, {"K1": "v2", "K2": 2}], "K2") => {1: {"K1": "v1", "K2": 1}, 2: {"K1": "v2", "K2": 2}} +func SliceToMapWithColumnAsKey(slice interface{}, key interface{}) map[interface{}]interface{} { + var ( + reflectValue = reflect.ValueOf(slice) + reflectKind = reflectValue.Kind() + ) + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + data := make(map[interface{}]interface{}) + switch reflectKind { + case reflect.Slice, reflect.Array: + for i := 0; i < reflectValue.Len(); i++ { + if k, ok := ItemValue(reflectValue.Index(i), key); ok { + data[k] = reflectValue.Index(i).Interface() + } + } + } + return data +} diff --git a/util/gutil/gutil_z_unit_slice_test.go b/util/gutil/gutil_z_unit_slice_test.go index 0f3f61938..218b1b65b 100755 --- a/util/gutil/gutil_z_unit_slice_test.go +++ b/util/gutil/gutil_z_unit_slice_test.go @@ -35,3 +35,23 @@ func Test_SliceToMap(t *testing.T) { t.Assert(m, nil) }) } + +func Test_SliceToMapWithColumnAsKey(t *testing.T) { + m1 := g.Map{"K1": "v1", "K2": 1} + m2 := g.Map{"K1": "v2", "K2": 2} + s := g.Slice{m1, m2} + gtest.C(t, func(t *gtest.T) { + m := gutil.SliceToMapWithColumnAsKey(s, "K1") + t.Assert(m, g.MapAnyAny{ + "v1": m1, + "v2": m2, + }) + }) + gtest.C(t, func(t *gtest.T) { + m := gutil.SliceToMapWithColumnAsKey(s, "K2") + t.Assert(m, g.MapAnyAny{ + 1: m1, + 2: m2, + }) + }) +} From cca438d77fd695c439640ae418f6401b6eeca216 Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 10 Jun 2021 20:17:53 +0800 Subject: [PATCH 323/492] fix issue #1209 --- util/gconv/gconv_struct.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 06416f863..09920e1f6 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -84,6 +84,8 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string if rv, ok := pointer.(reflect.Value); ok { if rv.Kind() == reflect.Ptr { return json.UnmarshalUseNumber(r, rv.Interface()) + } else if rv.CanAddr() { + return json.UnmarshalUseNumber(r, rv.Addr().Interface()) } } else { return json.UnmarshalUseNumber(r, pointer) @@ -94,6 +96,8 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string if rv, ok := pointer.(reflect.Value); ok { if rv.Kind() == reflect.Ptr { return json.UnmarshalUseNumber(paramsBytes, rv.Interface()) + } else if rv.CanAddr() { + return json.UnmarshalUseNumber(paramsBytes, rv.Addr().Interface()) } } else { return json.UnmarshalUseNumber(paramsBytes, pointer) From 2679f92aa858076af5da2af5ac89b5a0d2dc25ea Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 10 Jun 2021 20:45:22 +0800 Subject: [PATCH 324/492] comment update for package gerror --- errors/gerror/gerror.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index ce5bc0fc5..99b8b45fd 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -237,7 +237,7 @@ func WrapCodeSkipf(code, skip int, err error, format string, args ...interface{} } } -// Cause returns the error code of current error. +// Code returns the error code of current error. // It returns -1 if it has no error code or it does not implements interface Code. func Code(err error) int { if err != nil { From 2af4fd86cc636cc96a24caaed443a42b3d729c65 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 15 Jun 2021 19:57:55 +0800 Subject: [PATCH 325/492] rename configuration node name from LinkInfo to Link --- .example/database/gdb/mysql/gdb_insert.go | 6 +++--- database/gdb/gdb.go | 2 +- database/gdb/gdb_core_config.go | 4 ++-- database/gdb/gdb_core_tracing.go | 4 ++-- database/gdb/gdb_driver_mssql.go | 12 ++++++------ database/gdb/gdb_driver_mysql.go | 10 +++++----- database/gdb/gdb_driver_oracle.go | 10 +++++----- database/gdb/gdb_driver_pgsql.go | 10 +++++----- database/gdb/gdb_driver_sqlite.go | 10 +++++----- frame/gins/gins_database.go | 16 ++++++++++------ 10 files changed, 44 insertions(+), 40 deletions(-) diff --git a/.example/database/gdb/mysql/gdb_insert.go b/.example/database/gdb/mysql/gdb_insert.go index ee345b370..a6afa8491 100644 --- a/.example/database/gdb/mysql/gdb_insert.go +++ b/.example/database/gdb/mysql/gdb_insert.go @@ -10,9 +10,9 @@ func main() { //db := g.DB() gdb.AddDefaultConfigNode(gdb.ConfigNode{ - LinkInfo: "root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local", - Type: "mysql", - Charset: "utf8", + Link: "root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local", + Type: "mysql", + Charset: "utf8", }) db, _ := gdb.New() diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index aef802395..2548fe6d2 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -169,7 +169,7 @@ type DB interface { GetChars() (charLeft string, charRight string) // See Core.GetChars. Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables. TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields. - FilteredLinkInfo() string // See Core.FilteredLinkInfo. + FilteredLink() string } // Core is the base struct for database management. diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index 3ca62b382..a839ce8ba 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -30,6 +30,7 @@ type ConfigNode struct { Pass string `json:"pass"` // Authentication password. Name string `json:"name"` // Default used database name. Type string `json:"type"` // Database type: mysql, sqlite, mssql, pgsql, oracle. + Link string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored. Role string `json:"role"` // (Optional, "master" in default) Node role, used for master-slave mode: master, slave. Debug bool `json:"debug"` // (Optional) Debug mode enables debug information logging and output. Prefix string `json:"prefix"` // (Optional) Table prefix. @@ -37,7 +38,6 @@ type ConfigNode struct { Weight int `json:"weight"` // (Optional) Weight for load balance calculating, it's useless if there's just one node. Charset string `json:"charset"` // (Optional, "utf8mb4" in default) Custom charset when operating on database. Timezone string `json:"timezone"` // (Optional) Sets the time zone for displaying and interpreting time stamps. - LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored. MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool. MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool. MaxConnLifeTime time.Duration `json:"maxLifeTime"` // (Optional) Max amount of time a connection may be idle before being closed. @@ -187,7 +187,7 @@ func (node *ConfigNode) String() string { node.MaxIdleConnCount, node.MaxOpenConnCount, node.MaxConnLifeTime, - node.LinkInfo, + node.Link, ) } diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index 3c746ec2c..2f4b87e77 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -76,8 +76,8 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { if c.db.GetConfig().User != "" { labels = append(labels, attribute.String(tracingAttrDbUser, c.db.GetConfig().User)) } - if filteredLinkInfo := c.db.FilteredLinkInfo(); filteredLinkInfo != "" { - labels = append(labels, attribute.String(tracingAttrDbLink, c.db.FilteredLinkInfo())) + if filteredLink := c.db.FilteredLink(); filteredLink != "" { + labels = append(labels, attribute.String(tracingAttrDbLink, c.db.FilteredLink())) } if group := c.db.GetGroup(); group != "" { labels = append(labels, attribute.String(tracingAttrDbGroup, group)) diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index 79c1afc1b..4ee218504 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -42,8 +42,8 @@ func (d *DriverMssql) New(core *Core, node *ConfigNode) (DB, error) { // Open creates and returns a underlying sql.DB object for mssql. func (d *DriverMssql) Open(config *ConfigNode) (*sql.DB, error) { source := "" - if config.LinkInfo != "" { - source = config.LinkInfo + if config.Link != "" { + source = config.Link } else { source = fmt.Sprintf( "user id=%s;password=%s;server=%s;port=%s;database=%s;encrypt=disable", @@ -58,17 +58,17 @@ func (d *DriverMssql) Open(config *ConfigNode) (*sql.DB, error) { } } -// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for +// FilteredLink retrieves and returns filtered `linkInfo` that can be using for // logging or tracing purpose. -func (d *DriverMssql) FilteredLinkInfo() string { - linkInfo := d.GetConfig().LinkInfo +func (d *DriverMssql) FilteredLink() string { + linkInfo := d.GetConfig().Link if linkInfo == "" { return "" } s, _ := gregex.ReplaceString( `(.+);\s*password=(.+);\s*server=(.+)`, `$1;password=xxx;server=$3`, - d.GetConfig().LinkInfo, + d.GetConfig().Link, ) return s } diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index 207a3d590..adb1db26a 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -37,8 +37,8 @@ func (d *DriverMysql) New(core *Core, node *ConfigNode) (DB, error) { // Note that it converts time.Time argument to local timezone in default. func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) { var source string - if config.LinkInfo != "" { - source = config.LinkInfo + if config.Link != "" { + source = config.Link // Custom changing the schema in runtime. if config.Name != "" { source, _ = gregex.ReplaceString(`/([\w\.\-]+)+`, "/"+config.Name, source) @@ -60,10 +60,10 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) { } } -// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for +// FilteredLink retrieves and returns filtered `linkInfo` that can be using for // logging or tracing purpose. -func (d *DriverMysql) FilteredLinkInfo() string { - linkInfo := d.GetConfig().LinkInfo +func (d *DriverMysql) FilteredLink() string { + linkInfo := d.GetConfig().Link if linkInfo == "" { return "" } diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index 313b69e6c..ab48340a7 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -48,8 +48,8 @@ func (d *DriverOracle) New(core *Core, node *ConfigNode) (DB, error) { // Open creates and returns a underlying sql.DB object for oracle. func (d *DriverOracle) Open(config *ConfigNode) (*sql.DB, error) { var source string - if config.LinkInfo != "" { - source = config.LinkInfo + if config.Link != "" { + source = config.Link } else { source = fmt.Sprintf( "%s/%s@%s:%s/%s", @@ -64,10 +64,10 @@ func (d *DriverOracle) Open(config *ConfigNode) (*sql.DB, error) { } } -// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for +// FilteredLink retrieves and returns filtered `linkInfo` that can be using for // logging or tracing purpose. -func (d *DriverOracle) FilteredLinkInfo() string { - linkInfo := d.GetConfig().LinkInfo +func (d *DriverOracle) FilteredLink() string { + linkInfo := d.GetConfig().Link if linkInfo == "" { return "" } diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index ddcff4066..a31546ae9 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -40,8 +40,8 @@ func (d *DriverPgsql) New(core *Core, node *ConfigNode) (DB, error) { // Open creates and returns a underlying sql.DB object for pgsql. func (d *DriverPgsql) Open(config *ConfigNode) (*sql.DB, error) { var source string - if config.LinkInfo != "" { - source = config.LinkInfo + if config.Link != "" { + source = config.Link } else { source = fmt.Sprintf( "user=%s password=%s host=%s port=%s dbname=%s sslmode=disable", @@ -59,10 +59,10 @@ func (d *DriverPgsql) Open(config *ConfigNode) (*sql.DB, error) { } } -// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for +// FilteredLink retrieves and returns filtered `linkInfo` that can be using for // logging or tracing purpose. -func (d *DriverPgsql) FilteredLinkInfo() string { - linkInfo := d.GetConfig().LinkInfo +func (d *DriverPgsql) FilteredLink() string { + linkInfo := d.GetConfig().Link if linkInfo == "" { return "" } diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index 90c8f36c9..5dc37c2ae 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -38,8 +38,8 @@ func (d *DriverSqlite) New(core *Core, node *ConfigNode) (DB, error) { // Open creates and returns a underlying sql.DB object for sqlite. func (d *DriverSqlite) Open(config *ConfigNode) (*sql.DB, error) { var source string - if config.LinkInfo != "" { - source = config.LinkInfo + if config.Link != "" { + source = config.Link } else { source = config.Name } @@ -55,10 +55,10 @@ func (d *DriverSqlite) Open(config *ConfigNode) (*sql.DB, error) { } } -// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for +// FilteredLink retrieves and returns filtered `linkInfo` that can be using for // logging or tracing purpose. -func (d *DriverSqlite) FilteredLinkInfo() string { - return d.GetConfig().LinkInfo +func (d *DriverSqlite) FilteredLink() string { + return d.GetConfig().Link } // GetChars returns the security char for this type of database. diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index 26e97b5fe..506a66cf0 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -104,7 +104,7 @@ func Database(name ...string) gdb.DB { // which is the default group configuration. if node := parseDBConfigNode(configMap); node != nil { cg := gdb.ConfigGroup{} - if node.LinkInfo != "" || node.Host != "" { + if node.Link != "" || node.Host != "" { cg = append(cg, *node) } @@ -156,15 +156,19 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode { if err != nil { panic(err) } - if _, v := gutil.MapPossibleItemByKey(nodeMap, "link"); v != nil { - node.LinkInfo = gconv.String(v) + // To be compatible with old version. + if _, v := gutil.MapPossibleItemByKey(nodeMap, "LinkInfo"); v != nil { + node.Link = gconv.String(v) + } + if _, v := gutil.MapPossibleItemByKey(nodeMap, "Link"); v != nil { + node.Link = gconv.String(v) } // Parse link syntax. - if node.LinkInfo != "" && node.Type == "" { - match, _ := gregex.MatchString(`([a-z]+):(.+)`, node.LinkInfo) + if node.Link != "" && node.Type == "" { + match, _ := gregex.MatchString(`([a-z]+):(.+)`, node.Link) if len(match) == 3 { node.Type = gstr.Trim(match[1]) - node.LinkInfo = gstr.Trim(match[2]) + node.Link = gstr.Trim(match[2]) } } return node From d450de8e0df1b222f20533f78fcf68f098b59b2d Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 16 Jun 2021 21:44:31 +0800 Subject: [PATCH 326/492] add OnDuplicate/OnDuplicateEx feature for package gdb --- container/gset/gset_str_set.go | 2 +- database/gdb/gdb.go | 10 +- database/gdb/gdb_core.go | 164 ++++++++------------ database/gdb/gdb_driver_oracle.go | 100 ++----------- database/gdb/gdb_func.go | 3 + database/gdb/gdb_model.go | 2 + database/gdb/gdb_model_insert.go | 197 ++++++++++++++++++++++++- database/gdb/gdb_z_mysql_model_test.go | 145 ++++++++++++++++++ 8 files changed, 430 insertions(+), 193 deletions(-) diff --git a/container/gset/gset_str_set.go b/container/gset/gset_str_set.go index 68b4ec6bc..24323c3e7 100644 --- a/container/gset/gset_str_set.go +++ b/container/gset/gset_str_set.go @@ -21,7 +21,7 @@ type StrSet struct { data map[string]struct{} } -// New create and returns a new set, which contains un-repeated items. +// NewStrSet create and returns a new set, which contains un-repeated items. // The parameter is used to specify whether using set in concurrent-safety, // which is false in default. func NewStrSet(safe ...bool) *StrSet { diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 2548fe6d2..e114aaf04 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -96,7 +96,7 @@ type DB interface { // =========================================================================== DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll. - DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch int) (result sql.Result, err error) // See Core.DoInsert. + DoInsert(ctx context.Context, link Link, table string, data List, option DoInsertOption) (result sql.Result, err error) // See Core.DoInsert. DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate. DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete. DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery. @@ -214,6 +214,14 @@ type Sql struct { IsTransaction bool // IsTransaction marks whether this sql is executed in transaction. } +// DoInsertOption is the input struct for function DoInsert. +type DoInsertOption struct { + OnDuplicateStr string + OnDuplicateMap map[string]interface{} + InsertOption int // Insert operation. + BatchCount int // Batch count for batch inserting. +} + // TableField is the struct for table field. type TableField struct { Index int // For ordering purpose as map is unordered. diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index c7a66b232..fb253b4a4 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -367,78 +367,15 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e // 1: replace: if there's unique/primary key in the data, it deletes it from table and inserts a new one; // 2: save: if there's unique/primary key in the data, it updates it or else inserts a new one; // 3: ignore: if there's unique/primary key in the data, it ignores the inserting; -func (c *Core) DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch int) (result sql.Result, err error) { - table = c.QuotePrefixTableName(table) +func (c *Core) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { var ( - keys []string // Field names. - values []string // Value holder string array, like: (?,?,?) - params []interface{} // Values that will be committed to underlying database driver. - listMap List // The data list that passed from caller. + keys []string // Field names. + values []string // Value holder string array, like: (?,?,?) + params []interface{} // Values that will be committed to underlying database driver. + onDuplicateStr string // onDuplicateStr is used in "ON DUPLICATE KEY UPDATE" statement. ) - switch value := data.(type) { - case Result: - listMap = value.List() - - case Record: - listMap = List{value.Map()} - - case List: - listMap = value - for i, v := range listMap { - listMap[i] = ConvertDataForTableRecord(v) - } - - case Map: - listMap = List{ConvertDataForTableRecord(value)} - - default: - var ( - rv = reflect.ValueOf(data) - kind = rv.Kind() - ) - if kind == reflect.Ptr { - rv = rv.Elem() - kind = rv.Kind() - } - switch kind { - // If it's slice type, it then converts it to List type. - case reflect.Slice, reflect.Array: - listMap = make(List, rv.Len()) - for i := 0; i < rv.Len(); i++ { - listMap[i] = ConvertDataForTableRecord(rv.Index(i).Interface()) - } - - case reflect.Map: - listMap = List{ConvertDataForTableRecord(value)} - - case reflect.Struct: - if v, ok := value.(apiInterfaces); ok { - var ( - array = v.Interfaces() - list = make(List, len(array)) - ) - for i := 0; i < len(array); i++ { - list[i] = ConvertDataForTableRecord(array[i]) - } - listMap = list - } else { - listMap = List{ConvertDataForTableRecord(value)} - } - - default: - return result, gerror.New(fmt.Sprint("unsupported list type:", kind)) - } - } - if len(listMap) < 1 { - return result, gerror.New("data list cannot be empty") - } - if link == nil { - if link, err = c.MasterLink(); err != nil { - return - } - } // Handle the field names and place holders. - for k, _ := range listMap[0] { + for k, _ := range list[0] { keys = append(keys, k) } // Prepare the batch result pointer. @@ -446,54 +383,35 @@ func (c *Core) DoInsert(ctx context.Context, link Link, table string, data inter charL, charR = c.db.GetChars() batchResult = new(SqlResult) keysStr = charL + strings.Join(keys, charR+","+charL) + charR - operation = GetInsertOperationByOption(option) - updateStr = "" + operation = GetInsertOperationByOption(option.InsertOption) ) - if option == insertOptionSave { - for _, k := range keys { - // If it's SAVE operation, - // do not automatically update the creating time. - if c.isSoftCreatedFiledName(k) { - continue - } - if len(updateStr) > 0 { - updateStr += "," - } - updateStr += fmt.Sprintf( - "%s%s%s=VALUES(%s%s%s)", - charL, k, charR, - charL, k, charR, - ) - } - updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", updateStr) - } - if batch <= 0 { - batch = defaultBatchNumber + if option.InsertOption == insertOptionSave { + onDuplicateStr = c.formatOnDuplicate(keys, option) } var ( - listMapLen = len(listMap) + listLength = len(list) valueHolder = make([]string, 0) ) - for i := 0; i < listMapLen; i++ { + for i := 0; i < listLength; i++ { values = values[:0] // Note that the map type is unordered, // so it should use slice+key to retrieve the value. for _, k := range keys { - if s, ok := listMap[i][k].(Raw); ok { + if s, ok := list[i][k].(Raw); ok { values = append(values, gconv.String(s)) } else { values = append(values, "?") - params = append(params, listMap[i][k]) + params = append(params, list[i][k]) } } valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")") // Batch package checks: It meets the batch number or it is the last element. - if len(valueHolder) == batch || (i == listMapLen-1 && len(valueHolder) > 0) { + if len(valueHolder) == option.BatchCount || (i == listLength-1 && len(valueHolder) > 0) { r, err := c.db.DoExec(ctx, link, fmt.Sprintf( "%s INTO %s(%s) VALUES%s %s", - operation, table, keysStr, + operation, c.QuotePrefixTableName(table), keysStr, gstr.Join(valueHolder, ","), - updateStr, + onDuplicateStr, ), params...) if err != nil { return r, err @@ -511,6 +429,52 @@ func (c *Core) DoInsert(ctx context.Context, link Link, table string, data inter return batchResult, nil } +func (c *Core) formatOnDuplicate(columns []string, option DoInsertOption) string { + var ( + onDuplicateStr string + ) + if option.OnDuplicateStr != "" { + onDuplicateStr = option.OnDuplicateStr + } else if len(option.OnDuplicateMap) > 0 { + for k, v := range option.OnDuplicateMap { + if len(onDuplicateStr) > 0 { + onDuplicateStr += "," + } + switch v.(type) { + case Raw, *Raw: + onDuplicateStr += fmt.Sprintf( + "%s=%s", + c.QuoteWord(k), + v, + ) + default: + onDuplicateStr += fmt.Sprintf( + "%s=VALUES(%s)", + c.QuoteWord(k), + c.QuoteWord(gconv.String(v)), + ) + } + } + } else { + for _, column := range columns { + // If it's SAVE operation, + // do not automatically update the creating time. + if c.isSoftCreatedFilledName(column) { + continue + } + if len(onDuplicateStr) > 0 { + onDuplicateStr += "," + } + onDuplicateStr += fmt.Sprintf( + "%s=VALUES(%s)", + c.QuoteWord(column), + c.QuoteWord(column), + ) + } + } + return fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", onDuplicateStr) +} + // Update does "UPDATE ... " statement for the table. // // The parameter `data` can be type of string/map/gmap/struct/*struct, etc. @@ -711,8 +675,8 @@ func (c *Core) HasTable(name string) (bool, error) { return false, nil } -// isSoftCreatedFiledName checks and returns whether given filed name is an automatic-filled created time. -func (c *Core) isSoftCreatedFiledName(fieldName string) bool { +// isSoftCreatedFilledName checks and returns whether given filed name is an automatic-filled created time. +func (c *Core) isSoftCreatedFilledName(fieldName string) bool { if fieldName == "" { return false } diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index ab48340a7..e6bb6fd13 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -264,95 +264,40 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[ return } -func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, list interface{}, option int, batch int) (result sql.Result, err error) { +func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { var ( keys []string values []string params []interface{} ) - listMap := (List)(nil) - switch v := list.(type) { - case Result: - listMap = v.List() - case Record: - listMap = List{v.Map()} - case List: - listMap = v - case Map: - listMap = List{v} - default: - var ( - rv = reflect.ValueOf(list) - kind = rv.Kind() - ) - if kind == reflect.Ptr { - rv = rv.Elem() - kind = rv.Kind() - } - switch kind { - case reflect.Slice, reflect.Array: - listMap = make(List, rv.Len()) - for i := 0; i < rv.Len(); i++ { - listMap[i] = ConvertDataForTableRecord(rv.Index(i).Interface()) - } - case reflect.Map: - fallthrough - case reflect.Struct: - listMap = List{ConvertDataForTableRecord(list)} - default: - return result, gerror.New(fmt.Sprint("unsupported list type:", kind)) - } - } - if len(listMap) < 1 { - return result, gerror.New("empty data list") - } - if link == nil { - if link, err = d.MasterLink(); err != nil { - return - } - } // Retrieve the table fields and length. - holders := []string(nil) - for k, _ := range listMap[0] { + var ( + listLength = len(list) + valueHolder = make([]string, 0) + ) + for k, _ := range list[0] { keys = append(keys, k) - holders = append(holders, "?") + valueHolder = append(valueHolder, "?") } var ( batchResult = new(SqlResult) charL, charR = d.db.GetChars() keyStr = charL + strings.Join(keys, charL+","+charR) + charR - valueHolderStr = strings.Join(holders, ",") + valueHolderStr = strings.Join(valueHolder, ",") ) - if option != insertOptionDefault { - for _, v := range listMap { - r, err := d.DoInsert(ctx, link, table, v, option, 1) - if err != nil { - return r, err - } - - if n, err := r.RowsAffected(); err != nil { - return r, err - } else { - batchResult.result = r - batchResult.affected += n - } - } - return batchResult, nil - } - - if batch <= 0 { - batch = defaultBatchNumber - } // Format "INSERT...INTO..." statement. intoStr := make([]string, 0) - for i := 0; i < len(listMap); i++ { + for i := 0; i < len(list); i++ { for _, k := range keys { - params = append(params, listMap[i][k]) + params = append(params, list[i][k]) } values = append(values, valueHolderStr) - intoStr = append(intoStr, fmt.Sprintf(" INTO %s(%s) VALUES(%s) ", table, keyStr, valueHolderStr)) - if len(intoStr) == batch { - r, err := d.DoExec(ctx, link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...) + intoStr = append(intoStr, fmt.Sprintf("INTO %s(%s) VALUES(%s)", table, keyStr, valueHolderStr)) + if len(intoStr) == option.BatchCount || (i == listLength-1 && len(valueHolder) > 0) { + r, err := d.DoExec(ctx, link, fmt.Sprintf( + "INSERT ALL %s SELECT * FROM DUAL", + strings.Join(intoStr, " "), + ), params...) if err != nil { return r, err } @@ -366,18 +311,5 @@ func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, li intoStr = intoStr[:0] } } - // The leftover data. - if len(intoStr) > 0 { - r, err := d.DoExec(ctx, link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...) - if err != nil { - return r, err - } - if n, err := r.RowsAffected(); err != nil { - return r, err - } else { - batchResult.result = r - batchResult.affected += n - } - } return batchResult, nil } diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index c0b9155a2..034ae7d09 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -164,12 +164,15 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} { // Convert the value to JSON. data[k], _ = json.Marshal(v) } + case reflect.Struct: switch v.(type) { case time.Time, *time.Time, gtime.Time, *gtime.Time: continue + case Counter, *Counter: continue + default: // Use string conversion in default. if s, ok := v.(apiString); ok { diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 68532f646..f4a2f1131 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -49,6 +49,8 @@ type Model struct { cacheName string // Cache name for custom operation. unscoped bool // Disables soft deleting features when select/delete operations. safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model. + onDuplicate interface{} // onDuplicate is used for ON "DUPLICATE KEY UPDATE" statement. + onDuplicateEx interface{} // onDuplicateEx is used for excluding some columns ON "DUPLICATE KEY UPDATE" statement. } // whereHolder is the holder for where condition preparing. diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index b3fab329b..b49d9ae0c 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -8,6 +8,8 @@ package gdb import ( "database/sql" + "fmt" + "github.com/gogf/gf/container/gset" "reflect" "github.com/gogf/gf/errors/gerror" @@ -51,16 +53,20 @@ func (m *Model) Data(data ...interface{}) *Model { switch params := data[0].(type) { case Result: model.data = params.List() + case Record: model.data = params.Map() + case List: list := make(List, len(params)) for k, v := range params { list[k] = gutil.MapCopy(v) } model.data = list + case Map: model.data = gutil.MapCopy(params) + default: var ( rv = reflect.ValueOf(params) @@ -100,6 +106,24 @@ func (m *Model) Data(data ...interface{}) *Model { return model } +// OnDuplicate sets the operations when columns conflicts occurs. +// In MySQL, this is used for "ON DUPLICATE KEY UPDATE" statement. +// The parameter `onDuplicate` can be type of string/Raw/*Raw/map/slice. +func (m *Model) OnDuplicate(onDuplicate interface{}) *Model { + model := m.getModel() + model.onDuplicate = onDuplicate + return model +} + +// OnDuplicateEx sets the excluding columns for operations when columns conflicts occurs. +// In MySQL, this is used for "ON DUPLICATE KEY UPDATE" statement. +// The parameter `onDuplicateEx` can be type of string/Raw/*Raw/map/slice. +func (m *Model) OnDuplicateEx(onDuplicateEx interface{}) *Model { + model := m.getModel() + model.onDuplicateEx = onDuplicateEx + return model +} + // Insert does "INSERT INTO ..." statement for the model. // The optional parameter `data` is the same as the parameter of Model.Data function, // see Model.Data. @@ -156,7 +180,7 @@ func (m *Model) Save(data ...interface{}) (result sql.Result, err error) { } // doInsertWithOption inserts data with option parameter. -func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { +func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err error) { defer func() { if err == nil { m.checkAndRemoveCache() @@ -176,17 +200,66 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { if err != nil { return nil, err } + // It converts any data to List type for inserting. - switch newData.(type) { - case Map: - list = List{newData.(Map)} + switch value := newData.(type) { + case Result: + list = value.List() + + case Record: + list = List{value.Map()} case List: - list = newData.(List) + list = value + for i, v := range list { + list[i] = ConvertDataForTableRecord(v) + } + + case Map: + list = List{ConvertDataForTableRecord(value)} default: - return nil, gerror.New("inserting into table with invalid data type") + var ( + rv = reflect.ValueOf(newData) + kind = rv.Kind() + ) + if kind == reflect.Ptr { + rv = rv.Elem() + kind = rv.Kind() + } + switch kind { + // If it's slice type, it then converts it to List type. + case reflect.Slice, reflect.Array: + list = make(List, rv.Len()) + for i := 0; i < rv.Len(); i++ { + list[i] = ConvertDataForTableRecord(rv.Index(i).Interface()) + } + + case reflect.Map: + list = List{ConvertDataForTableRecord(value)} + + case reflect.Struct: + if v, ok := value.(apiInterfaces); ok { + var ( + array = v.Interfaces() + ) + list = make(List, len(array)) + for i := 0; i < len(array); i++ { + list[i] = ConvertDataForTableRecord(array[i]) + } + } else { + list = List{ConvertDataForTableRecord(value)} + } + + default: + return result, gerror.New(fmt.Sprint("unsupported list type:", kind)) + } } + + if len(list) < 1 { + return result, gerror.New("data list cannot be empty") + } + // Automatic handling for creating/updating time. if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") { for k, v := range list { @@ -200,7 +273,117 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { list[k] = v } } - return m.db.DoInsert(m.GetCtx(), m.getLink(true), m.tables, list, option, m.getBatch()) + // Format DoInsertOption, especially for "ON DUPLICATE KEY UPDATE" statement. + columnNames := make([]string, 0, len(list[0])) + for k, _ := range list[0] { + columnNames = append(columnNames, k) + } + doInsertOption, err := m.formatDoInsertOption(insertOption, columnNames) + if err != nil { + return result, err + } + + return m.db.DoInsert(m.GetCtx(), m.getLink(true), m.tables, list, doInsertOption) +} + +func (m *Model) formatDoInsertOption(insertOption int, columnNames []string) (option DoInsertOption, err error) { + option = DoInsertOption{ + InsertOption: insertOption, + BatchCount: m.getBatch(), + } + if insertOption == insertOptionSave { + onDuplicateExKeys, err := m.formatOnDuplicateExKeys(m.onDuplicateEx) + if err != nil { + return option, err + } + var ( + onDuplicateExKeySet = gset.NewStrSetFrom(onDuplicateExKeys) + ) + if m.onDuplicate != nil { + switch m.onDuplicate.(type) { + case Raw, *Raw: + option.OnDuplicateStr = gconv.String(m.onDuplicate) + + default: + var ( + reflectValue = reflect.ValueOf(m.onDuplicate) + reflectKind = reflectValue.Kind() + ) + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { + case reflect.String: + option.OnDuplicateMap = make(map[string]interface{}) + for _, v := range gstr.SplitAndTrim(reflectValue.String(), ",") { + if onDuplicateExKeySet.Contains(v) { + continue + } + option.OnDuplicateMap[v] = v + } + + case reflect.Map: + option.OnDuplicateMap = make(map[string]interface{}) + for k, v := range gconv.Map(m.onDuplicate) { + if onDuplicateExKeySet.Contains(k) { + continue + } + option.OnDuplicateMap[k] = v + } + + case reflect.Slice, reflect.Array: + option.OnDuplicateMap = make(map[string]interface{}) + for _, v := range gconv.Strings(m.onDuplicate) { + if onDuplicateExKeySet.Contains(v) { + continue + } + option.OnDuplicateMap[v] = v + } + + default: + return option, gerror.Newf(`unsupported OnDuplicate parameter type "%s"`, reflect.TypeOf(m.onDuplicate)) + } + } + } else if onDuplicateExKeySet.Size() > 0 { + option.OnDuplicateMap = make(map[string]interface{}) + for _, v := range columnNames { + if onDuplicateExKeySet.Contains(v) { + continue + } + option.OnDuplicateMap[v] = v + } + } + } + return +} + +func (m *Model) formatOnDuplicateExKeys(onDuplicateEx interface{}) ([]string, error) { + if onDuplicateEx == nil { + return nil, nil + } + + var ( + reflectValue = reflect.ValueOf(onDuplicateEx) + reflectKind = reflectValue.Kind() + ) + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { + case reflect.String: + return gstr.SplitAndTrim(reflectValue.String(), ","), nil + + case reflect.Map: + return gutil.Keys(onDuplicateEx), nil + + case reflect.Slice, reflect.Array: + return gconv.Strings(onDuplicateEx), nil + + default: + return nil, gerror.Newf(`unsupported OnDuplicateEx parameter type "%s"`, reflect.TypeOf(onDuplicateEx)) + } } func (m *Model) getBatch() int { diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 7370e5bfc..3355e7e92 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -3551,3 +3551,148 @@ func Test_Model_Increment_Decrement(t *testing.T) { t.Assert(count, 1) }) } + +func Test_Model_OnDuplicate(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + // string. + gtest.C(t, func(t *gtest.T) { + data := g.Map{ + "id": 1, + "passport": "pp1", + "password": "pw1", + "nickname": "n1", + "create_time": "2016-06-06", + } + _, err := db.Model(table).OnDuplicate("passport,password").Data(data).Save() + t.AssertNil(err) + one, err := db.Model(table).FindOne(1) + t.AssertNil(err) + t.Assert(one["passport"], data["passport"]) + t.Assert(one["password"], data["password"]) + t.Assert(one["nickname"], "name_1") + }) + + // slice. + gtest.C(t, func(t *gtest.T) { + data := g.Map{ + "id": 1, + "passport": "pp1", + "password": "pw1", + "nickname": "n1", + "create_time": "2016-06-06", + } + _, err := db.Model(table).OnDuplicate(g.Slice{"passport", "password"}).Data(data).Save() + t.AssertNil(err) + one, err := db.Model(table).FindOne(1) + t.AssertNil(err) + t.Assert(one["passport"], data["passport"]) + t.Assert(one["password"], data["password"]) + t.Assert(one["nickname"], "name_1") + }) + + // map. + gtest.C(t, func(t *gtest.T) { + data := g.Map{ + "id": 1, + "passport": "pp1", + "password": "pw1", + "nickname": "n1", + "create_time": "2016-06-06", + } + _, err := db.Model(table).OnDuplicate(g.Map{ + "passport": "nickname", + "password": "nickname", + }).Data(data).Save() + t.AssertNil(err) + one, err := db.Model(table).FindOne(1) + t.AssertNil(err) + t.Assert(one["passport"], data["nickname"]) + t.Assert(one["password"], data["nickname"]) + t.Assert(one["nickname"], "name_1") + }) + + // map+raw. + gtest.C(t, func(t *gtest.T) { + data := g.MapStrStr{ + "id": "1", + "passport": "pp1", + "password": "pw1", + "nickname": "n1", + "create_time": "2016-06-06", + } + _, err := db.Model(table).OnDuplicate(g.Map{ + "passport": gdb.Raw("CONCAT(VALUES(`passport`), '1')"), + "password": gdb.Raw("CONCAT(VALUES(`password`), '2')"), + }).Data(data).Save() + t.AssertNil(err) + one, err := db.Model(table).FindOne(1) + t.AssertNil(err) + t.Assert(one["passport"], data["passport"]+"1") + t.Assert(one["password"], data["password"]+"2") + t.Assert(one["nickname"], "name_1") + }) +} + +func Test_Model_OnDuplicateEx(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + // string. + gtest.C(t, func(t *gtest.T) { + data := g.Map{ + "id": 1, + "passport": "pp1", + "password": "pw1", + "nickname": "n1", + "create_time": "2016-06-06", + } + _, err := db.Model(table).OnDuplicateEx("nickname,create_time").Data(data).Save() + t.AssertNil(err) + one, err := db.Model(table).FindOne(1) + t.AssertNil(err) + t.Assert(one["passport"], data["passport"]) + t.Assert(one["password"], data["password"]) + t.Assert(one["nickname"], "name_1") + }) + + // slice. + gtest.C(t, func(t *gtest.T) { + data := g.Map{ + "id": 1, + "passport": "pp1", + "password": "pw1", + "nickname": "n1", + "create_time": "2016-06-06", + } + _, err := db.Model(table).OnDuplicateEx(g.Slice{"nickname", "create_time"}).Data(data).Save() + t.AssertNil(err) + one, err := db.Model(table).FindOne(1) + t.AssertNil(err) + t.Assert(one["passport"], data["passport"]) + t.Assert(one["password"], data["password"]) + t.Assert(one["nickname"], "name_1") + }) + + // map. + gtest.C(t, func(t *gtest.T) { + data := g.Map{ + "id": 1, + "passport": "pp1", + "password": "pw1", + "nickname": "n1", + "create_time": "2016-06-06", + } + _, err := db.Model(table).OnDuplicateEx(g.Map{ + "nickname": "nickname", + "create_time": "nickname", + }).Data(data).Save() + t.AssertNil(err) + one, err := db.Model(table).FindOne(1) + t.AssertNil(err) + t.Assert(one["passport"], data["passport"]) + t.Assert(one["password"], data["password"]) + t.Assert(one["nickname"], "name_1") + }) +} From 2606ad83acd028dfcc20a91bd176d284083f8bf1 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 16 Jun 2021 21:51:44 +0800 Subject: [PATCH 327/492] add OnDuplicate/OnDuplicateEx feature for package gdb --- database/gdb/gdb_model_insert.go | 34 +++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index b49d9ae0c..02a60d373 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -109,18 +109,42 @@ func (m *Model) Data(data ...interface{}) *Model { // OnDuplicate sets the operations when columns conflicts occurs. // In MySQL, this is used for "ON DUPLICATE KEY UPDATE" statement. // The parameter `onDuplicate` can be type of string/Raw/*Raw/map/slice. -func (m *Model) OnDuplicate(onDuplicate interface{}) *Model { +// Example: +// OnDuplicate("nickname, age") +// OnDuplicate("nickname", "age") +// OnDuplicate(g.Map{ +// "nickname": gdb.Raw("CONCAT('name_', VALUES(`nickname`))"), +// }) +// OnDuplicate(g.Map{ +// "nickname": "passport", +// }) +func (m *Model) OnDuplicate(onDuplicate ...interface{}) *Model { model := m.getModel() - model.onDuplicate = onDuplicate + if len(onDuplicate) > 1 { + model.onDuplicate = onDuplicate + } else { + model.onDuplicate = onDuplicate[0] + } return model } // OnDuplicateEx sets the excluding columns for operations when columns conflicts occurs. // In MySQL, this is used for "ON DUPLICATE KEY UPDATE" statement. -// The parameter `onDuplicateEx` can be type of string/Raw/*Raw/map/slice. -func (m *Model) OnDuplicateEx(onDuplicateEx interface{}) *Model { +// The parameter `onDuplicateEx` can be type of string/map/slice. +// Example: +// OnDuplicateEx("passport, password") +// OnDuplicateEx("passport", "password") +// OnDuplicateEx(g.Map{ +// "passport": "", +// "password": "", +// }) +func (m *Model) OnDuplicateEx(onDuplicateEx ...interface{}) *Model { model := m.getModel() - model.onDuplicateEx = onDuplicateEx + if len(onDuplicateEx) > 1 { + model.onDuplicateEx = onDuplicateEx + } else { + model.onDuplicateEx = onDuplicateEx[0] + } return model } From 5fba250a1499c9155bdfebb37c124cfedb408a5b Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 18 Jun 2021 15:20:27 +0800 Subject: [PATCH 328/492] add Raw Model for package gdb --- database/gdb/gdb.go | 4 +++ database/gdb/gdb_core.go | 5 +--- database/gdb/gdb_model.go | 25 ++++++++++++++----- database/gdb/gdb_model_condition.go | 9 ++++++- database/gdb/gdb_z_mysql_model_test.go | 19 ++++++++++++++ ...w_test.go => gdb_z_mysql_raw_type_test.go} | 0 6 files changed, 51 insertions(+), 11 deletions(-) rename database/gdb/{gdb_z_mysql_raw_test.go => gdb_z_mysql_raw_type_test.go} (100%) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index e114aaf04..1bb2f2745 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -38,6 +38,7 @@ type DB interface { // relational databases but also for NoSQL databases in the future. The name // "Table" is not proper for that purpose any more. // Also see Core.Table. + // Deprecated. Table(tableNameOrStruct ...interface{}) *Model // Model creates and returns a new ORM model from given schema. @@ -51,6 +52,9 @@ type DB interface { // Also see Core.Model. Model(tableNameOrStruct ...interface{}) *Model + // Raw creates and returns a model based on a raw sql not a table. + Raw(rawSql string, args ...interface{}) *Model + // Schema creates and returns a schema. // Also see Core.Schema. Schema(schema string) *Schema diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index fb253b4a4..d793eb8e3 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -254,10 +254,7 @@ func (c *Core) doUnion(unionType int, unions ...*Model) *Model { } composedArgs = append(composedArgs, holderArgs...) } - model := c.db.Model() - model.rawSql = composedSqlStr - model.extraArgs = composedArgs - return model + return c.db.Raw(composedSqlStr, composedArgs...) } // PingMaster pings the master node to check authentication or keeps the connection alive. diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index f4a2f1131..855b1ecae 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -21,7 +21,7 @@ import ( type Model struct { db DB // Underlying DB interface. tx *TX // Underlying TX interface. - rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model. + rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model. schema string // Custom database schema. linkType int // Mark for operation on master or slave. tablesInit string // Table names when model initialization. @@ -80,11 +80,14 @@ func (c *Core) Table(tableNameQueryOrStruct ...interface{}) *Model { // Model creates and returns a new ORM model from given schema. // The parameter `tableNameQueryOrStruct` can be more than one table names, and also alias name, like: // 1. Model names: -// Model("user") -// Model("user u") -// Model("user, user_detail") -// Model("user u, user_detail ud") -// 2. Model name with alias: Model("user", "u") +// db.Model("user") +// db.Model("user u") +// db.Model("user, user_detail") +// db.Model("user u, user_detail ud") +// 2. Model name with alias: +// db.Model("user", "u") +// 3. Model name with sub-query: +// db.Model("? AS a, ? AS b", subQuery1, subQuery2) func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { var ( tableStr string @@ -132,6 +135,16 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { } } +// Raw creates and returns a model based on a raw sql not a table. +// Example: +// db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result) +func (c *Core) Raw(rawSql string, args ...interface{}) *Model { + model := c.Model() + model.rawSql = rawSql + model.extraArgs = args + return model +} + // With creates and returns an ORM model based on meta data of given object. func (c *Core) With(objects ...interface{}) *Model { return c.db.Model().With(objects...) diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index ecfba71d6..669c2d8fd 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -421,7 +421,13 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh } // Soft deletion. softDeletingCondition := m.getConditionForSoftDeleting() - if !m.unscoped && softDeletingCondition != "" { + if m.rawSql != "" && conditionWhere != "" { + if gstr.ContainsI(m.rawSql, " WHERE ") { + conditionWhere = " AND " + conditionWhere + } else { + conditionWhere = " WHERE " + conditionWhere + } + } else if !m.unscoped && softDeletingCondition != "" { if conditionWhere == "" { conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition) } else { @@ -432,6 +438,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh conditionWhere = " WHERE " + conditionWhere } } + // GROUP BY. if m.groupBy != "" { conditionExtra += " GROUP BY " + m.groupBy diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 3355e7e92..3dc675272 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -3696,3 +3696,22 @@ func Test_Model_OnDuplicateEx(t *testing.T) { t.Assert(one["nickname"], "name_1") }) } + +func Test_Model_Raw(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + all, err := db. + Raw(fmt.Sprintf("select * from %s where id in (?)", table), g.Slice{1, 5, 7, 8, 9, 10}). + WhereLT("id", 8). + WhereIn("id", g.Slice{1, 2, 3, 4, 5, 6, 7}). + OrderDesc("id"). + Limit(2). + All() + t.AssertNil(err) + t.Assert(len(all), 2) + t.Assert(all[0]["id"], 7) + t.Assert(all[1]["id"], 5) + }) +} diff --git a/database/gdb/gdb_z_mysql_raw_test.go b/database/gdb/gdb_z_mysql_raw_type_test.go similarity index 100% rename from database/gdb/gdb_z_mysql_raw_test.go rename to database/gdb/gdb_z_mysql_raw_type_test.go From 5fefe97b8755bc871260a74a2fff30bb80d6e9c3 Mon Sep 17 00:00:00 2001 From: John Guo Date: Fri, 18 Jun 2021 15:27:49 +0800 Subject: [PATCH 329/492] add Raw Model for package gdb --- database/gdb/gdb_model.go | 30 +++++++++++++------- database/gdb/gdb_z_mysql_transaction_test.go | 4 +-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 855b1ecae..043a32ccb 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -90,10 +90,9 @@ func (c *Core) Table(tableNameQueryOrStruct ...interface{}) *Model { // db.Model("? AS a, ? AS b", subQuery1, subQuery2) func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { var ( - tableStr string - tableName string - extraArgs []interface{} - tableNames = make([]string, len(tableNameQueryOrStruct)) + tableStr string + tableName string + extraArgs []interface{} ) // Model creation with sub-query. if len(tableNameQueryOrStruct) > 1 { @@ -106,6 +105,7 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { } // Normal model creation. if tableStr == "" { + tableNames := make([]string, len(tableNameQueryOrStruct)) for k, v := range tableNameQueryOrStruct { if s, ok := v.(string); ok { tableNames[k] = s @@ -113,7 +113,6 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { tableNames[k] = tableName } } - if len(tableNames) > 1 { tableStr = fmt.Sprintf( `%s AS %s`, c.QuotePrefixTableName(tableNames[0]), c.QuoteWord(tableNames[1]), @@ -145,17 +144,26 @@ func (c *Core) Raw(rawSql string, args ...interface{}) *Model { return model } +// Raw creates and returns a model based on a raw sql not a table. +// Example: +// db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result) +// See Core.Raw. +func (m *Model) Raw(rawSql string, args ...interface{}) *Model { + model := m.db.Raw(rawSql, args...) + model.db = m.db + model.tx = m.tx + return model +} + +func (tx *TX) Raw(rawSql string, args ...interface{}) *Model { + return tx.Model().Raw(rawSql, args...) +} + // With creates and returns an ORM model based on meta data of given object. func (c *Core) With(objects ...interface{}) *Model { return c.db.Model().With(objects...) } -// Table is alias of tx.Model. -// Deprecated, use Model instead. -func (tx *TX) Table(tableNameQueryOrStruct ...interface{}) *Model { - return tx.Model(tableNameQueryOrStruct...) -} - // Model acts like Core.Model except it operates on transaction. // See Core.Model. func (tx *TX) Model(tableNameQueryOrStruct ...interface{}) *Model { diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index cb61b9867..61d68ce68 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -349,7 +349,7 @@ func Test_TX_Update(t *testing.T) { if err := tx.Commit(); err != nil { gtest.Error(err) } - _, err = tx.Table(table).Fields("create_time").Where("id", 3).Value() + _, err = tx.Model(table).Fields("create_time").Where("id", 3).Value() t.AssertNE(err, nil) if value, err := db.Model(table).Fields("create_time").Where("id", 3).Value(); err != nil { @@ -697,7 +697,7 @@ func Test_TX_Delete(t *testing.T) { if _, err := tx.Delete(table, 1); err != nil { gtest.Error(err) } - if n, err := tx.Table(table).Count(); err != nil { + if n, err := tx.Model(table).Count(); err != nil { gtest.Error(err) } else { t.Assert(n, 0) From ba18e2bf6b6ae588dcb0daf9a9a85d57fb94592e Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 21 Jun 2021 09:37:12 +0800 Subject: [PATCH 330/492] comment update --- database/gdb/gdb_model_join.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/database/gdb/gdb_model_join.go b/database/gdb/gdb_model_join.go index 10e4c9255..faf993d0e 100644 --- a/database/gdb/gdb_model_join.go +++ b/database/gdb/gdb_model_join.go @@ -56,9 +56,9 @@ func (m *Model) InnerJoin(table ...string) *Model { // doJoin does "LEFT/RIGHT/INNER JOIN ... ON ..." statement on the model. // The parameter `table` can be joined table and its joined condition, // and also with its alias name, like: -// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid") -// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") -// Table("user", "u").InnerJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid") +// Model("user").InnerJoin("user_detail", "user_detail.uid=user.uid") +// Model("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") +// Model("user", "u").InnerJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid") // Related issues: // https://github.com/gogf/gf/issues/1024 func (m *Model) doJoin(operator string, table ...string) *Model { From 7c8bbcb3af4ce3236ce28b55f3b96926b85773ab Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 21 Jun 2021 09:38:19 +0800 Subject: [PATCH 331/492] version update --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index d2895b5ad..f843a5225 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.16.0" +const VERSION = "v1.16.3" const AUTHORS = "john" From 266f592739b15e4c83ea05e7c7910b62b37ac6f6 Mon Sep 17 00:00:00 2001 From: John Guo Date: Mon, 21 Jun 2021 19:21:38 +0800 Subject: [PATCH 332/492] improve raw sql count statement for package gdb --- database/gdb/gdb_model_select.go | 5 +++++ database/gdb/gdb_z_mysql_model_test.go | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 2a9c02926..e71bd2321 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -515,6 +515,11 @@ func (m *Model) getFormattedSqlAndArgs(queryType int, limit1 bool) (sqlWithHolde // DISTINCT t.user_id uid countFields = fmt.Sprintf(`COUNT(%s%s)`, m.distinct, m.fields) } + // Raw SQL Model. + if m.rawSql != "" { + sqlWithHolder = fmt.Sprintf("SELECT %s FROM (%s) AS T", countFields, m.rawSql) + return sqlWithHolder, nil + } conditionWhere, conditionExtra, conditionArgs := m.formatCondition(false, true) sqlWithHolder = fmt.Sprintf("SELECT %s FROM %s%s", countFields, m.tables, conditionWhere+conditionExtra) if len(m.groupBy) > 0 { diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 3dc675272..90e89a00a 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -3714,4 +3714,16 @@ func Test_Model_Raw(t *testing.T) { t.Assert(all[0]["id"], 7) t.Assert(all[1]["id"], 5) }) + + gtest.C(t, func(t *gtest.T) { + count, err := db. + Raw(fmt.Sprintf("select * from %s where id in (?)", table), g.Slice{1, 5, 7, 8, 9, 10}). + WhereLT("id", 8). + WhereIn("id", g.Slice{1, 2, 3, 4, 5, 6, 7}). + OrderDesc("id"). + Limit(2). + Count() + t.AssertNil(err) + t.Assert(count, 6) + }) } From fbad5f60eb6590d5b4e096216cb51219e23cb16f Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 22 Jun 2021 15:09:08 +0800 Subject: [PATCH 333/492] improve caller path filtering for package gdebug --- debug/gdebug/gdebug_caller.go | 23 +++++++++++++---------- debug/gdebug/gdebug_stack.go | 9 ++++++--- internal/utils/utils_debug.go | 4 ++-- util/gmode/gmode.go | 1 + 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/debug/gdebug/gdebug_caller.go b/debug/gdebug/gdebug_caller.go index 485a61228..fa9bb2115 100644 --- a/debug/gdebug/gdebug_caller.go +++ b/debug/gdebug/gdebug_caller.go @@ -8,6 +8,7 @@ package gdebug import ( "fmt" + "github.com/gogf/gf/internal/utils" "os" "os/exec" "path/filepath" @@ -53,11 +54,13 @@ func Caller(skip ...int) (function string, path string, line int) { // // The parameter is used to filter the path of the caller. func CallerWithFilter(filter string, skip ...int) (function string, path string, line int) { - number := 0 + var ( + number = 0 + ok = true + ) if len(skip) > 0 { number = skip[0] } - ok := true pc, file, line, start := callerFromIndex([]string{filter}) if start != -1 { for i := start + number; i < maxCallerDepth; i++ { @@ -65,12 +68,6 @@ func CallerWithFilter(filter string, skip ...int) (function string, path string, pc, file, line, ok = runtime.Caller(i) } if ok { - if filter != "" && strings.Contains(file, filter) { - continue - } - if strings.Contains(file, stackFilterKey) { - continue - } function := "" if fn := runtime.FuncForPC(pc); fn == nil { function = "unknown" @@ -104,8 +101,14 @@ func callerFromIndex(filters []string) (pc uintptr, file string, line int, index if filtered { continue } - if strings.Contains(file, stackFilterKey) { - continue + if !utils.IsDebugEnabled() { + if strings.Contains(file, utils.StackFilterKeyForGoFrame) { + continue + } + } else { + if strings.Contains(file, stackFilterKey) { + continue + } } if index > 0 { index-- diff --git a/debug/gdebug/gdebug_stack.go b/debug/gdebug/gdebug_stack.go index 09ac951cd..a9c0a1e9c 100644 --- a/debug/gdebug/gdebug_stack.go +++ b/debug/gdebug/gdebug_stack.go @@ -80,14 +80,17 @@ func StackWithFilters(filters []string, skip ...int) string { if filtered { continue } - if strings.Contains(file, stackFilterKey) { - continue - } + if !utils.IsDebugEnabled() { if strings.Contains(file, utils.StackFilterKeyForGoFrame) { continue } + } else { + if strings.Contains(file, stackFilterKey) { + continue + } } + if fn := runtime.FuncForPC(pc); fn == nil { name = "unknown" } else { diff --git a/internal/utils/utils_debug.go b/internal/utils/utils_debug.go index c03d30dd4..201b78662 100644 --- a/internal/utils/utils_debug.go +++ b/internal/utils/utils_debug.go @@ -11,8 +11,8 @@ import ( ) const ( - debugKey = "gf.debug" // Debug key for checking if in debug mode. - StackFilterKeyForGoFrame = "/github.com/gogf/gf/" // Stack filtering key for all GoFrame module paths. + debugKey = "gf.debug" // Debug key for checking if in debug mode. + StackFilterKeyForGoFrame = "github.com/gogf/gf/" // Stack filtering key for all GoFrame module paths. ) var ( diff --git a/util/gmode/gmode.go b/util/gmode/gmode.go index 9b43dacaf..14df6f5a2 100644 --- a/util/gmode/gmode.go +++ b/util/gmode/gmode.go @@ -25,6 +25,7 @@ const ( ) var ( + // Note that `currentMode` is not concurrent safe. currentMode = NOT_SET ) From 7144aa69997010b80b3912f70454ffae68359b90 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 22 Jun 2021 15:34:26 +0800 Subject: [PATCH 334/492] improve caller path filtering for package gdebug --- internal/utils/utils_debug.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/utils/utils_debug.go b/internal/utils/utils_debug.go index 201b78662..f08a1f668 100644 --- a/internal/utils/utils_debug.go +++ b/internal/utils/utils_debug.go @@ -12,7 +12,7 @@ import ( const ( debugKey = "gf.debug" // Debug key for checking if in debug mode. - StackFilterKeyForGoFrame = "github.com/gogf/gf/" // Stack filtering key for all GoFrame module paths. + StackFilterKeyForGoFrame = "github.com/gogf/gf@" // Stack filtering key for all GoFrame module paths. ) var ( From adca9222abe5a0a4442b1f9c46eeba1cb4d7e3f4 Mon Sep 17 00:00:00 2001 From: jflyfox Date: Tue, 22 Jun 2021 17:42:31 +0800 Subject: [PATCH 335/492] improve transaction feature for package gdb --- database/gdb/gdb_core_transaction.go | 11 +++++++++++ database/gdb/gdb_z_mysql_transaction_test.go | 10 +++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/database/gdb/gdb_core_transaction.go b/database/gdb/gdb_core_transaction.go index 1f09850e5..0252f1096 100644 --- a/database/gdb/gdb_core_transaction.go +++ b/database/gdb/gdb_core_transaction.go @@ -28,6 +28,7 @@ type TX struct { master *sql.DB // master is the raw and underlying database manager. transactionId string // transactionId is an unique id generated by this object for this transaction. transactionCount int // transactionCount marks the times that Begins. + isClosed bool // isClosed marks this transaction has already been committed or rolled back. } const ( @@ -162,6 +163,9 @@ func TXFromCtx(ctx context.Context, group string) *TX { v := ctx.Value(transactionKeyForContext(group)) if v != nil { tx := v.(*TX) + if tx.IsClosed() { + return nil + } tx.ctx = ctx return tx } @@ -210,6 +214,7 @@ func (tx *TX) Commit() error { IsTransaction: true, } ) + tx.isClosed = true tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj) if tx.db.GetDebug() { tx.db.GetCore().writeSqlToLogger(tx.ctx, sqlObj) @@ -243,6 +248,7 @@ func (tx *TX) Rollback() error { IsTransaction: true, } ) + tx.isClosed = true tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj) if tx.db.GetDebug() { tx.db.GetCore().writeSqlToLogger(tx.ctx, sqlObj) @@ -250,6 +256,11 @@ func (tx *TX) Rollback() error { return err } +// IsClosed checks and returns this transaction has already been committed or rolled back. +func (tx *TX) IsClosed() bool { + return tx.isClosed +} + // Begin starts a nested transaction procedure. func (tx *TX) Begin() error { _, err := tx.Exec("SAVEPOINT " + tx.transactionKeyForNestedPoint()) diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index 61d68ce68..669055145 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -666,7 +666,6 @@ func Test_TX_GetScan(t *testing.T) { } func Test_TX_Delete(t *testing.T) { - gtest.C(t, func(t *gtest.T) { table := createInitTable() defer dropTable(table) @@ -685,6 +684,8 @@ func Test_TX_Delete(t *testing.T) { } else { t.Assert(n, 0) } + + t.Assert(tx.IsClosed(), true) }) gtest.C(t, func(t *gtest.T) { @@ -711,6 +712,8 @@ func Test_TX_Delete(t *testing.T) { t.Assert(n, TableSize) t.AssertNE(n, 0) } + + t.Assert(tx.IsClosed(), true) }) } @@ -721,7 +724,7 @@ func Test_Transaction(t *testing.T) { gtest.C(t, func(t *gtest.T) { ctx := context.TODO() err := db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - if _, err := tx.Replace(table, g.Map{ + if _, err := tx.Ctx(ctx).Replace(table, g.Map{ "id": 1, "passport": "USER_1", "password": "PASS_1", @@ -730,11 +733,12 @@ func Test_Transaction(t *testing.T) { }); err != nil { t.Error(err) } + t.Assert(tx.IsClosed(), false) return gerror.New("error") }) t.AssertNE(err, nil) - if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil { + if value, err := db.Model(table).Ctx(ctx).Fields("nickname").Where("id", 1).Value(); err != nil { gtest.Error(err) } else { t.Assert(value.String(), "name_1") From 69dd5db774bce8ae7e969dd8b3cfcd529b9252f9 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 22 Jun 2021 20:05:37 +0800 Subject: [PATCH 336/492] improve record converting for package gdb --- database/gdb/gdb_core.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index d793eb8e3..ff518bcdc 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -605,7 +605,7 @@ func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) { } var ( values = make([]interface{}, len(columnNames)) - records = make(Result, 0) + result = make(Result, 0) scanArgs = make([]interface{}, len(values)) ) for i := range values { @@ -613,22 +613,22 @@ func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) { } for { if err := rows.Scan(scanArgs...); err != nil { - return records, err + return result, err } - row := make(Record) + record := Record{} for i, value := range values { if value == nil { - row[columnNames[i]] = gvar.New(nil) + record[columnNames[i]] = gvar.New(nil) } else { - row[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i])) + record[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i])) } } - records = append(records, row) + result = append(result, record) if !rows.Next() { break } } - return records, nil + return result, nil } // MarshalJSON implements the interface MarshalJSON for json.Marshal. From d6ea2220f7e8176eca92cd735df19c82da17c410 Mon Sep 17 00:00:00 2001 From: John Guo Date: Tue, 22 Jun 2021 21:48:56 +0800 Subject: [PATCH 337/492] add auto fields filtering feature for function Scan of package gdb; mark funtcion Struct/Structs deprecated for gdb.Model --- database/gdb/gdb_model_select.go | 109 +++++++++++++++++------- database/gdb/gdb_type_result.go | 2 +- database/gdb/gdb_z_mysql_model_test.go | 82 +++++++++--------- database/gdb/gdb_z_mysql_struct_test.go | 40 +++++++-- database/gdb/gdb_z_mysql_types_test.go | 2 +- 5 files changed, 156 insertions(+), 79 deletions(-) diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index e71bd2321..9d1dab718 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -8,6 +8,7 @@ package gdb import ( "fmt" + "github.com/gogf/gf/errors/gerror" "reflect" "github.com/gogf/gf/container/gset" @@ -195,6 +196,15 @@ func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) { return all.Array(), nil } +// Struct retrieves one record from table and converts it into given struct. +// The parameter `pointer` should be type of *struct/**struct. If type **struct is given, +// it can create the struct internally during converting. +// +// Deprecated, use Scan instead. +func (m *Model) Struct(pointer interface{}, where ...interface{}) error { + return m.doStruct(pointer, where...) +} + // Struct retrieves one record from table and converts it into given struct. // The parameter `pointer` should be type of *struct/**struct. If type **struct is given, // it can create the struct internally during converting. @@ -202,24 +212,38 @@ func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) { // The optional parameter `where` is the same as the parameter of Model.Where function, // see Model.Where. // -// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions -// from table and `pointer` is not nil. +// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has +// default value and there's no record retrieved with the given conditions from table. // -// Eg: +// Example: // user := new(User) -// err := db.Model("user").Where("id", 1).Struct(user) +// err := db.Model("user").Where("id", 1).Scan(user) // // user := (*User)(nil) -// err := db.Model("user").Where("id", 1).Struct(&user) -func (m *Model) Struct(pointer interface{}, where ...interface{}) error { - one, err := m.One(where...) +// err := db.Model("user").Where("id", 1).Scan(&user) +func (m *Model) doStruct(pointer interface{}, where ...interface{}) error { + model := m + // Auto selecting fields by struct attributes. + if model.fieldsEx == "" && (model.fields == "" || model.fields == "*") { + model = m.Fields(pointer) + } + one, err := model.One(where...) if err != nil { return err } if err = one.Struct(pointer); err != nil { return err } - return m.doWithScanStruct(pointer) + return model.doWithScanStruct(pointer) +} + +// Structs retrieves records from table and converts them into given struct slice. +// The parameter `pointer` should be type of *[]struct/*[]*struct. It can create and fill the struct +// slice internally during converting. +// +// Deprecated, use Scan instead. +func (m *Model) Structs(pointer interface{}, where ...interface{}) error { + return m.doStructs(pointer, where...) } // Structs retrieves records from table and converts them into given struct slice. @@ -229,37 +253,45 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error { // The optional parameter `where` is the same as the parameter of Model.Where function, // see Model.Where. // -// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions -// from table and `pointer` is not empty. +// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has +// default value and there's no record retrieved with the given conditions from table. // -// Eg: +// Example: // users := ([]User)(nil) -// err := db.Model("user").Structs(&users) +// err := db.Model("user").Scan(&users) // // users := ([]*User)(nil) -// err := db.Model("user").Structs(&users) -func (m *Model) Structs(pointer interface{}, where ...interface{}) error { - all, err := m.All(where...) +// err := db.Model("user").Scan(&users) +func (m *Model) doStructs(pointer interface{}, where ...interface{}) error { + model := m + // Auto selecting fields by struct attributes. + if model.fieldsEx == "" && (model.fields == "" || model.fields == "*") { + model = m.Fields( + reflect.New( + reflect.ValueOf(pointer).Elem().Type().Elem(), + ).Interface(), + ) + } + all, err := model.All(where...) if err != nil { return err } if err = all.Structs(pointer); err != nil { return err } - return m.doWithScanStructs(pointer) + return model.doWithScanStructs(pointer) } // Scan automatically calls Struct or Structs function according to the type of parameter `pointer`. -// It calls function Struct if `pointer` is type of *struct/**struct. -// It calls function Structs if `pointer` is type of *[]struct/*[]*struct. +// It calls function doStruct if `pointer` is type of *struct/**struct. +// It calls function doStructs if `pointer` is type of *[]struct/*[]*struct. // -// The optional parameter `where` is the same as the parameter of Model.Where function, -// see Model.Where. +// The optional parameter `where` is the same as the parameter of Model.Where function, see Model.Where. // -// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions -// from table. +// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has +// default value and there's no record retrieved with the given conditions from table. // -// Eg: +// Example: // user := new(User) // err := db.Model("user").Where("id", 1).Scan(user) // @@ -272,16 +304,35 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error { // users := ([]*User)(nil) // err := db.Model("user").Scan(&users) func (m *Model) Scan(pointer interface{}, where ...interface{}) error { - var reflectType reflect.Type + var ( + reflectValue reflect.Value + reflectKind reflect.Kind + ) if v, ok := pointer.(reflect.Value); ok { - reflectType = v.Type() + reflectValue = v } else { - reflectType = reflect.TypeOf(pointer) + reflectValue = reflect.ValueOf(pointer) } - if gstr.Contains(reflectType.String(), "[]") { - return m.Structs(pointer, where...) + + reflectKind = reflectValue.Kind() + if reflectKind != reflect.Ptr { + return gerror.New(`the parameter "pointer" for function Scan should type of pointer`) + } + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + + switch reflectKind { + case reflect.Slice, reflect.Array: + return m.doStructs(pointer, where...) + + case reflect.Struct, reflect.Invalid: + return m.doStruct(pointer, where...) + + default: + return gerror.New(`element of parameter "pointer" for function Scan should type of struct/*struct/[]struct/[]*struct`) } - return m.Struct(pointer, where...) } // ScanList converts `r` to struct slice which contains other complex struct attributes. diff --git a/database/gdb/gdb_type_result.go b/database/gdb/gdb_type_result.go index a5800ece9..05e15026c 100644 --- a/database/gdb/gdb_type_result.go +++ b/database/gdb/gdb_type_result.go @@ -153,7 +153,7 @@ func (r Result) MapKeyUint(key string) map[uint]Map { return m } -// RecordKeyInt converts `r` to a map[int]Record of which key is specified by `key`. +// RecordKeyStr converts `r` to a map[string]Record of which key is specified by `key`. func (r Result) RecordKeyStr(key string) map[string]Record { m := make(map[string]Record) for _, item := range r { diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 90e89a00a..5f96a5c46 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -899,7 +899,7 @@ func Test_Model_Struct(t *testing.T) { CreateTime gtime.Time } user := new(User) - err := db.Model(table).Where("id=1").Struct(user) + err := db.Model(table).Where("id=1").Scan(user) t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") @@ -913,7 +913,7 @@ func Test_Model_Struct(t *testing.T) { CreateTime *gtime.Time } user := new(User) - err := db.Model(table).Where("id=1").Struct(user) + err := db.Model(table).Where("id=1").Scan(user) t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") @@ -928,7 +928,7 @@ func Test_Model_Struct(t *testing.T) { CreateTime *gtime.Time } user := (*User)(nil) - err := db.Model(table).Where("id=1").Struct(&user) + err := db.Model(table).Where("id=1").Scan(&user) t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") @@ -960,7 +960,7 @@ func Test_Model_Struct(t *testing.T) { CreateTime *gtime.Time } user := new(User) - err := db.Model(table).Where("id=-1").Struct(user) + err := db.Model(table).Where("id=-1").Scan(user) t.Assert(err, sql.ErrNoRows) }) gtest.C(t, func(t *gtest.T) { @@ -972,7 +972,7 @@ func Test_Model_Struct(t *testing.T) { CreateTime *gtime.Time } var user *User - err := db.Model(table).Where("id=-1").Struct(&user) + err := db.Model(table).Where("id=-1").Scan(&user) t.AssertNil(err) }) } @@ -992,7 +992,7 @@ func Test_Model_Struct_CustomType(t *testing.T) { CreateTime gtime.Time } user := new(User) - err := db.Model(table).Where("id=1").Struct(user) + err := db.Model(table).Where("id=1").Scan(user) t.AssertNil(err) t.Assert(user.NickName, "name_1") t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00") @@ -1012,7 +1012,7 @@ func Test_Model_Structs(t *testing.T) { CreateTime gtime.Time } var users []User - err := db.Model(table).Order("id asc").Structs(&users) + err := db.Model(table).Order("id asc").Scan(&users) if err != nil { gtest.Error(err) } @@ -1035,7 +1035,7 @@ func Test_Model_Structs(t *testing.T) { CreateTime *gtime.Time } var users []*User - err := db.Model(table).Order("id asc").Structs(&users) + err := db.Model(table).Order("id asc").Scan(&users) if err != nil { gtest.Error(err) } @@ -1081,38 +1081,40 @@ func Test_Model_Structs(t *testing.T) { CreateTime *gtime.Time } var users []*User - err := db.Model(table).Where("id<0").Structs(&users) + err := db.Model(table).Where("id<0").Scan(&users) t.AssertNil(err) }) } -func Test_Model_StructsWithJsonTag(t *testing.T) { - table := createInitTable() - defer dropTable(table) - - gtest.C(t, func(t *gtest.T) { - type User struct { - Uid int `json:"id"` - Passport string - Password string - Name string `json:"nick_name"` - Time gtime.Time `json:"create_time"` - } - var users []User - err := db.Model(table).Order("id asc").Structs(&users) - if err != nil { - gtest.Error(err) - } - t.Assert(len(users), TableSize) - t.Assert(users[0].Uid, 1) - t.Assert(users[1].Uid, 2) - t.Assert(users[2].Uid, 3) - t.Assert(users[0].Name, "name_1") - t.Assert(users[1].Name, "name_2") - t.Assert(users[2].Name, "name_3") - t.Assert(users[0].Time.String(), "2018-10-24 10:00:00") - }) -} +// JSON tag is only used for JSON Marshal/Unmarshal, DO NOT use it in multiple purposes! +//func Test_Model_StructsWithJsonTag(t *testing.T) { +// table := createInitTable() +// defer dropTable(table) +// +// db.SetDebug(true) +// gtest.C(t, func(t *gtest.T) { +// type User struct { +// Uid int `json:"id"` +// Passport string +// Password string +// Name string `json:"nick_name"` +// Time gtime.Time `json:"create_time"` +// } +// var users []User +// err := db.Model(table).Order("id asc").Scan(&users) +// if err != nil { +// gtest.Error(err) +// } +// t.Assert(len(users), TableSize) +// t.Assert(users[0].Uid, 1) +// t.Assert(users[1].Uid, 2) +// t.Assert(users[2].Uid, 3) +// t.Assert(users[0].Name, "name_1") +// t.Assert(users[1].Name, "name_2") +// t.Assert(users[2].Name, "name_3") +// t.Assert(users[0].Time.String(), "2018-10-24 10:00:00") +// }) +//} func Test_Model_Scan(t *testing.T) { table := createInitTable() @@ -3098,7 +3100,7 @@ func Test_TimeZoneInsert(t *testing.T) { gtest.C(t, func(t *gtest.T) { _, _ = db.Model(tableName).Unscoped().Insert(u) userEntity := &User{} - err := db.Model(tableName).Where("id", 1).Unscoped().Struct(&userEntity) + err := db.Model(tableName).Where("id", 1).Unscoped().Scan(&userEntity) t.AssertNil(err) t.Assert(userEntity.CreatedAt.String(), "2020-11-22 04:23:45") t.Assert(userEntity.UpdatedAt.String(), "2020-11-22 05:23:45") @@ -3129,7 +3131,7 @@ func Test_Model_Fields_Map_Struct(t *testing.T) { XXX_TYPE int } var a = A{} - err := db.Model(table).Fields(a).Where("id", 1).Struct(&a) + err := db.Model(table).Fields(a).Where("id", 1).Scan(&a) t.AssertNil(err) t.Assert(a.ID, 1) t.Assert(a.PASSPORT, "user_1") @@ -3143,7 +3145,7 @@ func Test_Model_Fields_Map_Struct(t *testing.T) { XXX_TYPE int } var a *A - err := db.Model(table).Fields(a).Where("id", 1).Struct(&a) + err := db.Model(table).Fields(a).Where("id", 1).Scan(&a) t.AssertNil(err) t.Assert(a.ID, 1) t.Assert(a.PASSPORT, "user_1") @@ -3157,7 +3159,7 @@ func Test_Model_Fields_Map_Struct(t *testing.T) { XXX_TYPE int } var a *A - err := db.Model(table).Fields(&a).Where("id", 1).Struct(&a) + err := db.Model(table).Fields(&a).Where("id", 1).Scan(&a) t.AssertNil(err) t.Assert(a.ID, 1) t.Assert(a.PASSPORT, "user_1") diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index dbcf48d97..eb895be5a 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -121,7 +121,7 @@ func Test_Struct_Pointer_Attribute(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { user := new(User) - err := db.Model(table).Struct(user, "id=1") + err := db.Model(table).Scan(user, "id=1") t.AssertNil(err) t.Assert(*user.Id, 1) t.Assert(*user.Passport, "user_1") @@ -130,7 +130,7 @@ func Test_Struct_Pointer_Attribute(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { var user *User - err := db.Model(table).Struct(&user, "id=1") + err := db.Model(table).Scan(&user, "id=1") t.AssertNil(err) t.Assert(*user.Id, 1) t.Assert(*user.Passport, "user_1") @@ -201,7 +201,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) { // Structs gtest.C(t, func(t *gtest.T) { users := make([]User, 0) - err := db.Model(table).Structs(&users, "id < 3") + err := db.Model(table).Scan(&users, "id < 3") t.AssertNil(err) t.Assert(len(users), 2) t.Assert(*users[0].Id, 1) @@ -211,7 +211,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { users := make([]*User, 0) - err := db.Model(table).Structs(&users, "id < 3") + err := db.Model(table).Scan(&users, "id < 3") t.AssertNil(err) t.Assert(len(users), 2) t.Assert(*users[0].Id, 1) @@ -221,7 +221,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { var users []User - err := db.Model(table).Structs(&users, "id < 3") + err := db.Model(table).Scan(&users, "id < 3") t.AssertNil(err) t.Assert(len(users), 2) t.Assert(*users[0].Id, 1) @@ -231,7 +231,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { var users []*User - err := db.Model(table).Structs(&users, "id < 3") + err := db.Model(table).Scan(&users, "id < 3") t.AssertNil(err) t.Assert(len(users), 2) t.Assert(*users[0].Id, 1) @@ -254,7 +254,7 @@ func Test_Struct_Empty(t *testing.T) { gtest.C(t, func(t *gtest.T) { user := new(User) - err := db.Model(table).Where("id=100").Struct(user) + err := db.Model(table).Where("id=100").Scan(user) t.Assert(err, sql.ErrNoRows) t.AssertNE(user, nil) }) @@ -269,7 +269,7 @@ func Test_Struct_Empty(t *testing.T) { gtest.C(t, func(t *gtest.T) { var user *User - err := db.Model(table).Where("id=100").Struct(&user) + err := db.Model(table).Where("id=100").Scan(&user) t.AssertNil(err) t.Assert(user, nil) }) @@ -452,3 +452,27 @@ func Test_Model_Scan_Map(t *testing.T) { t.Assert(users[9].CreateTime.String(), CreateTime) }) } + +func Test_Scan_AutoFilteringByStructAttributes(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + type User struct { + Id int + Passport string + } + //db.SetDebug(true) + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(table).OrderAsc("id").Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 1) + }) + gtest.C(t, func(t *gtest.T) { + var users []User + err := db.Model(table).OrderAsc("id").Scan(&users) + t.AssertNil(err) + t.Assert(len(users), TableSize) + t.Assert(users[0].Id, 1) + }) +} diff --git a/database/gdb/gdb_z_mysql_types_test.go b/database/gdb/gdb_z_mysql_types_test.go index e26d763b9..7c02df03e 100644 --- a/database/gdb/gdb_z_mysql_types_test.go +++ b/database/gdb/gdb_z_mysql_types_test.go @@ -87,7 +87,7 @@ func Test_Types(t *testing.T) { TinyInt bool } var obj *T - err = db.Model("types").Struct(&obj) + err = db.Model("types").Scan(&obj) t.AssertNil(err) t.Assert(obj.Id, 1) t.Assert(obj.Blob, data["blob"]) From 816791b9c1f9ec8f90ad0b37b597e35533dd79d3 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 23 Jun 2021 09:34:53 +0800 Subject: [PATCH 338/492] improve function Increment/Decrement for package gdb --- database/gdb/gdb_model_update.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index b32d1f5f8..f72e622ff 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -93,17 +93,19 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro } // Increment increments a column's value by a given amount. -func (m *Model) Increment(column string, amount float64) (sql.Result, error) { +// The parameter `amount` can be type of float or integer. +func (m *Model) Increment(column string, amount interface{}) (sql.Result, error) { return m.getModel().Data(column, &Counter{ Field: column, - Value: amount, + Value: gconv.Float64(amount), }).Update() } // Decrement decrements a column's value by a given amount. -func (m *Model) Decrement(column string, amount float64) (sql.Result, error) { +// The parameter `amount` can be type of float or integer. +func (m *Model) Decrement(column string, amount interface{}) (sql.Result, error) { return m.getModel().Data(column, &Counter{ Field: column, - Value: -amount, + Value: -gconv.Float64(amount), }).Update() } From 7667aca4c2f60f85fd74ea642d35aeeabeb954f7 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 23 Jun 2021 09:42:10 +0800 Subject: [PATCH 339/492] improve Order feature for package gdb --- database/gdb/gdb_model_condition.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 669c2d8fd..5a83e4e96 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -277,6 +277,9 @@ func (m *Model) Order(orderBy ...string) *Model { return m } model := m.getModel() + if model.orderBy != "" { + model.orderBy += "," + } model.orderBy = m.db.GetCore().QuoteString(strings.Join(orderBy, " ")) return model } @@ -287,6 +290,9 @@ func (m *Model) OrderAsc(column string) *Model { return m } model := m.getModel() + if model.orderBy != "" { + model.orderBy += "," + } model.orderBy = m.db.GetCore().QuoteWord(column) + " ASC" return model } @@ -297,6 +303,9 @@ func (m *Model) OrderDesc(column string) *Model { return m } model := m.getModel() + if model.orderBy != "" { + model.orderBy += "," + } model.orderBy = m.db.GetCore().QuoteWord(column) + " DESC" return model } From 65131c6f22ffbc0b1211c388a09786287393ae0c Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 23 Jun 2021 12:04:16 +0800 Subject: [PATCH 340/492] add automatic fields mapping and filtering for Model.Where statements --- database/gdb/gdb_func.go | 9 +++++++-- database/gdb/gdb_model.go | 2 +- database/gdb/gdb_model_condition.go | 8 ++++---- database/gdb/gdb_z_mysql_model_test.go | 6 +++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 034ae7d09..e2a0b4c17 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -448,7 +448,7 @@ func formatSql(sql string, args []interface{}) (newSql string, newArgs []interfa } // formatWhere formats where statement and its arguments for `Where` and `Having` statements. -func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (newWhere string, newArgs []interface{}) { +func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, schema, table string) (newWhere string, newArgs []interface{}) { var ( buffer = bytes.NewBuffer(nil) rv = reflect.ValueOf(where) @@ -486,7 +486,12 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) ( }) break } - for key, value := range DataToMapDeep(where) { + // Automatically mapping and filtering the struct attribute. + data := DataToMapDeep(where) + if table != "" { + data, _ = db.GetCore().mappingAndFilterData(schema, table, data, true) + } + for key, value := range data { if omitEmpty && empty.IsEmpty(value) { continue } diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 043a32ccb..245398260 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -99,7 +99,7 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { conditionStr := gconv.String(tableNameQueryOrStruct[0]) if gstr.Contains(conditionStr, "?") { tableStr, extraArgs = formatWhere( - c.db, conditionStr, tableNameQueryOrStruct[1:], false, + c.db, conditionStr, tableNameQueryOrStruct[1:], false, "", "", ) } } diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 5a83e4e96..cf991c6d3 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -386,7 +386,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh case whereHolderWhere: if conditionWhere == "" { newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, + m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables, ) if len(newWhere) > 0 { conditionWhere = newWhere @@ -398,7 +398,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh case whereHolderAnd: newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, + m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables, ) if len(newWhere) > 0 { if len(conditionWhere) == 0 { @@ -413,7 +413,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh case whereHolderOr: newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, + m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables, ) if len(newWhere) > 0 { if len(conditionWhere) == 0 { @@ -455,7 +455,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh // HAVING. if len(m.having) > 0 { havingStr, havingArgs := formatWhere( - m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0, + m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0, m.schema, m.tables, ) if len(havingStr) > 0 { conditionExtra += " HAVING " + havingStr diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 5f96a5c46..923ccb68a 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -1471,11 +1471,11 @@ func Test_Model_Where(t *testing.T) { t.Assert(len(result), 3) t.Assert(result[0]["id"].Int(), 1) }) - // struct + // struct, automatic mapping and filtering. gtest.C(t, func(t *gtest.T) { type User struct { - Id int `json:"id"` - Nickname string `gconv:"nickname"` + Id int + Nickname string } result, err := db.Model(table).Where(User{3, "name_3"}).One() t.AssertNil(err) From e6688b9e868df563225491673012a86b31bcf5bb Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 23 Jun 2021 21:39:12 +0800 Subject: [PATCH 341/492] add more unit testing cases for package ghttp --- net/ghttp/ghttp_unit_request_test.go | 86 ++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/net/ghttp/ghttp_unit_request_test.go b/net/ghttp/ghttp_unit_request_test.go index 26880df6b..96ba9a375 100644 --- a/net/ghttp/ghttp_unit_request_test.go +++ b/net/ghttp/ghttp_unit_request_test.go @@ -621,3 +621,89 @@ func Test_Params_Parse_Validation(t *testing.T) { t.Assert(client.GetContent("/parse?name=john11&password1=123456&password2=123456"), `ok`) }) } + +func Test_Params_Parse_EmbeddedWithAliasName1(t *testing.T) { + // 获取内容列表 + type ContentGetListInput struct { + Type string + CategoryId uint + Page int + Size int + Sort int + UserId uint + } + // 获取内容列表 + type ContentGetListReq struct { + ContentGetListInput + CategoryId uint `p:"cate"` + Page int `d:"1" v:"min:0#分页号码错误"` + Size int `d:"10" v:"max:50#分页数量最大50条"` + } + + p, _ := ports.PopRand() + s := g.Server(p) + s.BindHandler("/parse", func(r *ghttp.Request) { + var req *ContentGetListReq + if err := r.Parse(&req); err != nil { + r.Response.Write(err) + } else { + r.Response.Write(req.ContentGetListInput) + } + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + prefix := fmt.Sprintf("http://127.0.0.1:%d", p) + client := g.Client() + client.SetPrefix(prefix) + + t.Assert(client.GetContent("/parse?cate=1&page=2&size=10"), `{"Type":"","CategoryId":0,"Page":2,"Size":10,"Sort":0,"UserId":0}`) + }) +} + +func Test_Params_Parse_EmbeddedWithAliasName2(t *testing.T) { + // 获取内容列表 + type ContentGetListInput struct { + Type string + CategoryId uint `p:"cate"` + Page int + Size int + Sort int + UserId uint + } + // 获取内容列表 + type ContentGetListReq struct { + ContentGetListInput + CategoryId uint `p:"cate"` + Page int `d:"1" v:"min:0#分页号码错误"` + Size int `d:"10" v:"max:50#分页数量最大50条"` + } + + p, _ := ports.PopRand() + s := g.Server(p) + s.BindHandler("/parse", func(r *ghttp.Request) { + var req *ContentGetListReq + if err := r.Parse(&req); err != nil { + r.Response.Write(err) + } else { + r.Response.Write(req.ContentGetListInput) + } + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + prefix := fmt.Sprintf("http://127.0.0.1:%d", p) + client := g.Client() + client.SetPrefix(prefix) + + t.Assert(client.GetContent("/parse?cate=1&page=2&size=10"), `{"Type":"","CategoryId":1,"Page":2,"Size":10,"Sort":0,"UserId":0}`) + }) +} From 4cd7e4e5a0d91d45dd5b5c85188bc83b89e353f9 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 23 Jun 2021 21:57:13 +0800 Subject: [PATCH 342/492] version updates --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index f843a5225..bd9e356f1 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.16.3" +const VERSION = "v1.16.4" const AUTHORS = "john" From 237f172ae5b4bdb5eff8aff98103de27e266dafe Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 26 Jun 2021 11:18:44 +0800 Subject: [PATCH 343/492] add file configuration support for logger in ghttp.Server --- frame/gins/gins_server.go | 25 ++++++++++---- net/ghttp/ghttp.go | 12 +++---- net/ghttp/ghttp_server.go | 2 +- net/ghttp/ghttp_server_config.go | 7 ++-- net/ghttp/ghttp_server_config_logging.go | 13 ++++++++ net/ghttp/ghttp_server_handler.go | 19 ++++++++--- net/ghttp/ghttp_server_log.go | 10 ++---- os/glog/glog_logger.go | 6 ++-- os/glog/glog_logger_rotate.go | 42 ++++++++++++++++-------- 9 files changed, 90 insertions(+), 46 deletions(-) diff --git a/frame/gins/gins_server.go b/frame/gins/gins_server.go index 090835628..e73eadaa8 100644 --- a/frame/gins/gins_server.go +++ b/frame/gins/gins_server.go @@ -24,17 +24,30 @@ func Server(name ...interface{}) *ghttp.Server { s := ghttp.GetServer(name...) // To avoid file no found error while it's not necessary. if Config().Available() { - var m map[string]interface{} + var ( + serverConfigMap map[string]interface{} + serverLoggerConfigMap map[string]interface{} + ) nodeKey, _ := gutil.MapPossibleItemByKey(Config().GetMap("."), configNodeNameServer) if nodeKey == "" { nodeKey = configNodeNameServer } - m = Config().GetMap(fmt.Sprintf(`%s.%s`, nodeKey, s.GetName())) - if len(m) == 0 { - m = Config().GetMap(nodeKey) + // Server configuration. + serverConfigMap = Config().GetMap(fmt.Sprintf(`%s.%s`, nodeKey, s.GetName())) + if len(serverConfigMap) == 0 { + serverConfigMap = Config().GetMap(nodeKey) } - if len(m) > 0 { - if err := s.SetConfigWithMap(m); err != nil { + if len(serverConfigMap) > 0 { + if err := s.SetConfigWithMap(serverConfigMap); err != nil { + panic(err) + } + } + // Server logger configuration. + serverLoggerConfigMap = Config().GetMap( + fmt.Sprintf(`%s.%s.%s`, nodeKey, s.GetName(), configNodeNameLogger), + ) + if len(serverLoggerConfigMap) > 0 { + if err := s.Logger().SetConfigWithMap(serverLoggerConfigMap); err != nil { panic(err) } } diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index ec486b0ef..54e2a4bbd 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -19,11 +19,11 @@ import ( ) type ( - // Server wraps the http.Server and provides more feature. + // Server wraps the http.Server and provides more rich features. Server struct { name string // Unique name for instance management. config ServerConfig // Configuration. - plugins []Plugin // Plugin array. + plugins []Plugin // Plugin array to extends server functionality. servers []*gracefulServer // Underlying http.Server array. serverCount *gtype.Int // Underlying http.Server count. closeChan chan struct{} // Used for underlying server closing event notification. @@ -44,7 +44,7 @@ type ( Priority int // Just for reference. } - // Router item just for route dumps. + // RouterItem is just for route dumps. RouterItem struct { Server string // Server name. Address string // Listening address. @@ -98,7 +98,7 @@ type ( Stack() string } - // Request handler function. + // HandlerFunc is request handler function. HandlerFunc = func(r *Request) // Listening file descriptor mapping. @@ -107,10 +107,6 @@ type ( ) const ( - HOOK_BEFORE_SERVE = "HOOK_BEFORE_SERVE" // Deprecated, use HookBeforeServe instead. - HOOK_AFTER_SERVE = "HOOK_AFTER_SERVE" // Deprecated, use HookAfterServe instead. - HOOK_BEFORE_OUTPUT = "HOOK_BEFORE_OUTPUT" // Deprecated, use HookBeforeOutput instead. - HOOK_AFTER_OUTPUT = "HOOK_AFTER_OUTPUT" // Deprecated, use HookAfterOutput instead. HookBeforeServe = "HOOK_BEFORE_SERVE" HookAfterServe = "HOOK_AFTER_SERVE" HookBeforeOutput = "HOOK_BEFORE_OUTPUT" diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 5896ace8c..2aa6b18cc 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -195,7 +195,7 @@ func (s *Server) Start() error { if gproc.IsChild() { gtimer.SetTimeout(time.Duration(s.config.GracefulTimeout)*time.Second, func() { if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil { - //glog.Error("server error in process communication:", err) + intlog.Error("server error in process communication:", err) } }) } diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index e7b19c926..0d4888e45 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -223,13 +223,14 @@ type ServerConfig struct { GracefulTimeout uint8 `json:"gracefulTimeout"` } +// Config creates and returns a ServerConfig object with default configurations. // Deprecated. Use NewConfig instead. func Config() ServerConfig { return NewConfig() } // NewConfig creates and returns a ServerConfig object with default configurations. -// Note that, do not define this default configuration to local package variable, as there're +// Note that, do not define this default configuration to local package variable, as there are // some pointer attributes that may be shared in different servers. func NewConfig() ServerConfig { return ServerConfig{ @@ -331,7 +332,9 @@ func (s *Server) SetConfig(c ServerConfig) error { return err } } - s.config.Logger.SetLevelStr(s.config.LogLevel) + if err := s.config.Logger.SetLevelStr(s.config.LogLevel); err != nil { + intlog.Error(err) + } SetGraceful(c.Graceful) intlog.Printf("SetConfig: %+v", s.config) diff --git a/net/ghttp/ghttp_server_config_logging.go b/net/ghttp/ghttp_server_config_logging.go index 3ea28f0de..471ea876c 100644 --- a/net/ghttp/ghttp_server_config_logging.go +++ b/net/ghttp/ghttp_server_config_logging.go @@ -6,6 +6,8 @@ package ghttp +import "github.com/gogf/gf/os/glog" + // SetLogPath sets the log path for server. // It logs content to file only if the log path is set. func (s *Server) SetLogPath(path string) error { @@ -23,6 +25,17 @@ func (s *Server) SetLogPath(path string) error { return nil } +// SetLogger sets the logger for logging responsibility. +// Note that it cannot be set in runtime as there may be concurrent safety issue. +func (s *Server) SetLogger(logger *glog.Logger) { + s.config.Logger = logger +} + +// Logger is alias of GetLogger. +func (s *Server) Logger() *glog.Logger { + return s.config.Logger +} + // SetLogLevel sets logging level by level string. func (s *Server) SetLogLevel(level string) { s.config.LogLevel = level diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index bc2bb05d9..cf9fcfb0a 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -7,6 +7,7 @@ package ghttp import ( + "github.com/gogf/gf/internal/intlog" "net/http" "os" "sort" @@ -80,9 +81,15 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Close the request and response body // to release the file descriptor in time. - _ = request.Request.Body.Close() + err := request.Request.Body.Close() + if err != nil { + intlog.Error(err) + } if request.Request.Response != nil { - _ = request.Request.Response.Body.Close() + err = request.Request.Response.Body.Close() + if err != nil { + intlog.Error(err) + } } }() @@ -188,9 +195,11 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // searchStaticFile searches the file with given URI. // It returns a file struct specifying the file information. func (s *Server) searchStaticFile(uri string) *staticFile { - var file *gres.File - var path string - var dir bool + var ( + file *gres.File + path string + dir bool + ) // Firstly search the StaticPaths mapping. if len(s.config.StaticPaths) > 0 { for _, item := range s.config.StaticPaths { diff --git a/net/ghttp/ghttp_server_log.go b/net/ghttp/ghttp_server_log.go index c8f0f5f8f..24ce77bcc 100644 --- a/net/ghttp/ghttp_server_log.go +++ b/net/ghttp/ghttp_server_log.go @@ -9,14 +9,8 @@ package ghttp import ( "fmt" "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/os/glog" ) -// Logger returns the logger of the server. -func (s *Server) Logger() *glog.Logger { - return s.config.Logger -} - // handleAccessLog handles the access logging for server. func (s *Server) handleAccessLog(r *Request) { if !s.IsAccessLogEnabled() { @@ -26,7 +20,7 @@ func (s *Server) handleAccessLog(r *Request) { if r.TLS != nil { scheme = "https" } - s.Logger().File(s.config.AccessLogPattern). + s.Logger().Ctx(r.Context()).File(s.config.AccessLogPattern). Stdout(s.config.LogStdout). Printf( `%d "%s %s %s %s %s" %.3f, %s, "%s", "%s"`, @@ -63,7 +57,7 @@ func (s *Server) handleErrorLog(err error, r *Request) { } else { content += ", " + err.Error() } - s.config.Logger. + s.Logger().Ctx(r.Context()). File(s.config.ErrorLogPattern). Stdout(s.config.LogStdout). Print(content) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 44a704c6e..397a779bc 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -102,9 +102,9 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { if p.parent != nil { p = p.parent } - if !p.init.Val() && p.init.Cas(false, true) { - // It just initializes once for each logger. - if p.config.RotateSize > 0 || p.config.RotateExpire > 0 { + // It just initializes once for each logger. + if p.config.RotateSize > 0 || p.config.RotateExpire > 0 { + if !p.init.Val() && p.init.Cas(false, true) { gtimer.AddOnce(p.config.RotateCheckInterval, p.rotateChecksTimely) intlog.Printf("logger rotation initialized: every %s", p.config.RotateCheckInterval.String()) } diff --git a/os/glog/glog_logger_rotate.go b/os/glog/glog_logger_rotate.go index e508b8006..8471e55f2 100644 --- a/os/glog/glog_logger_rotate.go +++ b/os/glog/glog_logger_rotate.go @@ -19,6 +19,10 @@ import ( "time" ) +const ( + memoryLockPrefixForRotating = "glog.rotateChecksTimely:" +) + // rotateFileBySize rotates the current logging file according to the // configured rotation size. func (l *Logger) rotateFileBySize(now time.Time) { @@ -91,6 +95,7 @@ func (l *Logger) doRotateFile(filePath string) error { // rotateChecksTimely timely checks the backups expiration and the compression. func (l *Logger) rotateChecksTimely() { defer gtimer.AddOnce(l.config.RotateCheckInterval, l.rotateChecksTimely) + // Checks whether file rotation not enabled. if l.config.RotateSize <= 0 && l.config.RotateExpire == 0 { intlog.Printf( @@ -101,17 +106,20 @@ func (l *Logger) rotateChecksTimely() { } // It here uses memory lock to guarantee the concurrent safety. - memoryLockKey := "glog.rotateChecksTimely:" + l.config.Path + memoryLockKey := memoryLockPrefixForRotating + l.config.Path if !gmlock.TryLock(memoryLockKey) { return } defer gmlock.Unlock(memoryLockKey) var ( - now = time.Now() - pattern = "*.log, *.gz" - files, _ = gfile.ScanDirFile(l.config.Path, pattern, true) + now = time.Now() + pattern = "*.log, *.gz" + files, err = gfile.ScanDirFile(l.config.Path, pattern, true) ) + if err != nil { + intlog.Error(err) + } intlog.Printf("logging rotation start checks: %+v", files) // ============================================================= // Rotation of expired file checks. @@ -141,7 +149,10 @@ func (l *Logger) rotateChecksTimely() { } if expireRotated { // Update the files array. - files, _ = gfile.ScanDirFile(l.config.Path, pattern, true) + files, err = gfile.ScanDirFile(l.config.Path, pattern, true) + if err != nil { + intlog.Error(err) + } } } @@ -175,7 +186,10 @@ func (l *Logger) rotateChecksTimely() { return true }) // Update the files array. - files, _ = gfile.ScanDirFile(l.config.Path, pattern, true) + files, err = gfile.ScanDirFile(l.config.Path, pattern, true) + if err != nil { + intlog.Error(err) + } } } @@ -192,10 +206,12 @@ func (l *Logger) rotateChecksTimely() { if backupFilesMap[originalLoggingFilePath] == nil { backupFilesMap[originalLoggingFilePath] = garray.NewSortedArray(func(a, b interface{}) int { // Sorted by rotated/backup file mtime. - // The old rotated/backup file is put in the head of array. - file1 := a.(string) - file2 := b.(string) - result := gfile.MTimestampMilli(file1) - gfile.MTimestampMilli(file2) + // The older rotated/backup file is put in the head of array. + var ( + file1 = a.(string) + file2 = b.(string) + result = gfile.MTimestampMilli(file1) - gfile.MTimestampMilli(file2) + ) if result <= 0 { return -1 } @@ -214,11 +230,11 @@ func (l *Logger) rotateChecksTimely() { path, _ := array.PopLeft() intlog.Printf(`remove exceeded backup limit file: %s`, path) if err := gfile.Remove(path.(string)); err != nil { - intlog.Print(err) + intlog.Error(err) } } } - // Backup expiration checks. + // Backups expiration checking. if l.config.RotateBackupExpire > 0 { var ( mtime time.Time @@ -235,7 +251,7 @@ func (l *Logger) rotateChecksTimely() { now, mtime, subDuration, l.config.RotateBackupExpire, path, ) if err := gfile.Remove(path); err != nil { - intlog.Print(err) + intlog.Error(err) } return true } else { From 025cdd66c5788e5064b482b2d77f79054745ff69 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 26 Jun 2021 11:25:54 +0800 Subject: [PATCH 344/492] improve details for package glog --- os/glog/glog_logger_config.go | 5 ++--- os/glog/glog_logger_level.go | 4 +++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index a08275c1d..709a7976c 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -41,7 +41,7 @@ type Config struct { RotateBackupLimit int `json:"rotateBackupLimit"` // Max backup for rotated files, default is 0, means no backups. RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration. RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression. - RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default. + RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronous checks the backups and expiration at intervals. It's 1 hour in default. } // DefaultConfig returns the default configuration for logger. @@ -104,8 +104,7 @@ func (l *Logger) SetConfigWithMap(m map[string]interface{}) error { return errors.New(fmt.Sprintf(`invalid rotate size: %v`, rotateSizeValue)) } } - err := gconv.Struct(m, &l.config) - if err != nil { + if err := gconv.Struct(m, &l.config); err != nil { return err } return l.SetConfig(l.config) diff --git a/os/glog/glog_logger_level.go b/os/glog/glog_logger_level.go index a756bc6f8..fe1cfdb4e 100644 --- a/os/glog/glog_logger_level.go +++ b/os/glog/glog_logger_level.go @@ -62,8 +62,10 @@ var levelStringMap = map[string]int{ } // SetLevel sets the logging level. +// Note that levels ` LEVEL_CRIT | LEVEL_PANI | LEVEL_FATA ` cannot be removed for logging content, +// which are automatically added to levels. func (l *Logger) SetLevel(level int) { - l.config.Level = level + l.config.Level = level | LEVEL_CRIT | LEVEL_PANI | LEVEL_FATA } // GetLevel returns the logging level value. From c25f88293bc1fee9deefe24a6e9092f6af7a1dcb Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 26 Jun 2021 12:08:18 +0800 Subject: [PATCH 345/492] unify command or enviroment key names for packages --- database/gdb/gdb.go | 3 +- database/gdb/gdb_core_tracing.go | 13 +-- database/gredis/gredis_conn_tracing.go | 13 +-- encoding/gjson/gjson_api.go | 12 -- encoding/gjson/gjson_api_encoding.go | 4 +- encoding/gjson/gjson_deprecated.go | 104 ------------------ .../gjson/gjson_z_example_conversion_test.go | 2 +- encoding/gjson/gjson_z_unit_basic_test.go | 10 +- encoding/gjson/gjson_z_unit_json_test.go | 4 +- encoding/gparser/gparser_unit_basic_test.go | 6 +- internal/utils/utils_debug.go | 4 +- net/ghttp/ghttp_server_admin_process.go | 2 +- net/gtrace/gtrace.go | 17 ++- os/gcfg/gcfg.go | 11 +- os/gcfg/gcfg_config.go | 4 +- os/gcfg/gcfg_config_api.go | 82 +++----------- os/gfile/gfile_cache.go | 10 +- os/glog/glog.go | 8 +- os/gtimer/gtimer.go | 21 ++-- os/gview/gview.go | 6 +- os/gview/gview_error.go | 6 +- util/gmode/gmode.go | 14 +-- 22 files changed, 93 insertions(+), 263 deletions(-) delete mode 100644 encoding/gjson/gjson_deprecated.go diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 1bb2f2745..fead99d3b 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -269,6 +269,7 @@ const ( ctxTimeoutTypeExec = iota ctxTimeoutTypeQuery ctxTimeoutTypePrepare + commandEnvKeyForDryRun = "gf.gdb.dryrun" ) var ( @@ -313,7 +314,7 @@ var ( func init() { // allDryRun is initialized from environment or command options. - allDryRun = gcmd.GetOptWithEnv("gf.gdb.dryrun", false).Bool() + allDryRun = gcmd.GetOptWithEnv(commandEnvKeyForDryRun, false).Bool() } // Register registers custom database driver to gdb. diff --git a/database/gdb/gdb_core_tracing.go b/database/gdb/gdb_core_tracing.go index 2f4b87e77..0b0bc6d59 100644 --- a/database/gdb/gdb_core_tracing.go +++ b/database/gdb/gdb_core_tracing.go @@ -12,7 +12,6 @@ import ( "fmt" "github.com/gogf/gf" "github.com/gogf/gf/net/gtrace" - "github.com/gogf/gf/os/gcmd" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" @@ -34,19 +33,9 @@ const ( tracingEventDbExecutionType = "db.execution.type" ) -var ( - // tracingInternal enables tracing for internal type spans. - // It's true in default. - tracingInternal = true -) - -func init() { - tracingInternal = gcmd.GetOptWithEnv("gf.tracing.internal", true).Bool() -} - // addSqlToTracing adds sql information to tracer if it's enabled. func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) { - if !tracingInternal || !gtrace.IsActivated(ctx) { + if !gtrace.IsTracingInternal() || !gtrace.IsActivated(ctx) { return } tr := otel.GetTracerProvider().Tracer( diff --git a/database/gredis/gredis_conn_tracing.go b/database/gredis/gredis_conn_tracing.go index 9ee411323..4fc478423 100644 --- a/database/gredis/gredis_conn_tracing.go +++ b/database/gredis/gredis_conn_tracing.go @@ -12,7 +12,6 @@ import ( "github.com/gogf/gf" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/net/gtrace" - "github.com/gogf/gf/os/gcmd" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" @@ -38,19 +37,9 @@ const ( tracingEventRedisExecutionArguments = "redis.execution.arguments" ) -var ( - // tracingInternal enables tracing for internal type spans. - // It's true in default. - tracingInternal = true -) - -func init() { - tracingInternal = gcmd.GetOptWithEnv("gf.tracing.internal", true).Bool() -} - // addTracingItem checks and adds redis tracing information to OpenTelemetry. func (c *Conn) addTracingItem(item *tracingItem) { - if !tracingInternal || !gtrace.IsActivated(c.ctx) { + if !gtrace.IsTracingInternal() || !gtrace.IsActivated(c.ctx) { return } tr := otel.GetTracerProvider().Tracer( diff --git a/encoding/gjson/gjson_api.go b/encoding/gjson/gjson_api.go index d065910f2..a45f6a6de 100644 --- a/encoding/gjson/gjson_api.go +++ b/encoding/gjson/gjson_api.go @@ -319,23 +319,11 @@ func (j *Json) GetStruct(pattern string, pointer interface{}, mapping ...map[str return gconv.Struct(j.Get(pattern), pointer, mapping...) } -// GetStructDeep does GetStruct recursively. -// Deprecated, use GetStruct instead. -func (j *Json) GetStructDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { - return gconv.StructDeep(j.Get(pattern), pointer, mapping...) -} - // GetStructs converts any slice to given struct slice. func (j *Json) GetStructs(pattern string, pointer interface{}, mapping ...map[string]string) error { return gconv.Structs(j.Get(pattern), pointer, mapping...) } -// GetStructsDeep converts any slice to given struct slice recursively. -// Deprecated, use GetStructs instead. -func (j *Json) GetStructsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { - return gconv.StructsDeep(j.Get(pattern), pointer, mapping...) -} - // GetScan automatically calls Struct or Structs function according to the type of parameter // to implement the converting.. func (j *Json) GetScan(pattern string, pointer interface{}, mapping ...map[string]string) error { diff --git a/encoding/gjson/gjson_api_encoding.go b/encoding/gjson/gjson_api_encoding.go index d745c7119..a0eabdb90 100644 --- a/encoding/gjson/gjson_api_encoding.go +++ b/encoding/gjson/gjson_api_encoding.go @@ -70,7 +70,7 @@ func (j *Json) MustToJsonIndentString() string { // ======================================================================== func (j *Json) ToXml(rootTag ...string) ([]byte, error) { - return gxml.Encode(j.ToMap(), rootTag...) + return gxml.Encode(j.Map(), rootTag...) } func (j *Json) ToXmlString(rootTag ...string) (string, error) { @@ -79,7 +79,7 @@ func (j *Json) ToXmlString(rootTag ...string) (string, error) { } func (j *Json) ToXmlIndent(rootTag ...string) ([]byte, error) { - return gxml.EncodeWithIndent(j.ToMap(), rootTag...) + return gxml.EncodeWithIndent(j.Map(), rootTag...) } func (j *Json) ToXmlIndentString(rootTag ...string) (string, error) { diff --git a/encoding/gjson/gjson_deprecated.go b/encoding/gjson/gjson_deprecated.go deleted file mode 100644 index 9283866ca..000000000 --- a/encoding/gjson/gjson_deprecated.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gjson - -import "github.com/gogf/gf/util/gconv" - -// ToMap converts current Json object to map[string]interface{}. -// It returns nil if fails. -// Deprecated, use Map instead. -func (j *Json) ToMap() map[string]interface{} { - j.mu.RLock() - defer j.mu.RUnlock() - return gconv.Map(*(j.p)) -} - -// ToArray converts current Json object to []interface{}. -// It returns nil if fails. -// Deprecated, use Array instead. -func (j *Json) ToArray() []interface{} { - j.mu.RLock() - defer j.mu.RUnlock() - return gconv.Interfaces(*(j.p)) -} - -// ToStruct converts current Json object to specified object. -// The should be a pointer type of *struct. -// Deprecated, use Struct instead. -func (j *Json) ToStruct(pointer interface{}, mapping ...map[string]string) error { - j.mu.RLock() - defer j.mu.RUnlock() - return gconv.Struct(*(j.p), pointer, mapping...) -} - -// ToStructDeep converts current Json object to specified object recursively. -// The should be a pointer type of *struct. -// Deprecated, use Struct instead. -func (j *Json) ToStructDeep(pointer interface{}, mapping ...map[string]string) error { - j.mu.RLock() - defer j.mu.RUnlock() - return gconv.StructDeep(*(j.p), pointer, mapping...) -} - -// ToStructs converts current Json object to specified object slice. -// The should be a pointer type of []struct/*struct. -// Deprecated, use Structs instead. -func (j *Json) ToStructs(pointer interface{}, mapping ...map[string]string) error { - j.mu.RLock() - defer j.mu.RUnlock() - return gconv.Structs(*(j.p), pointer, mapping...) -} - -// ToStructsDeep converts current Json object to specified object slice recursively. -// The should be a pointer type of []struct/*struct. -// Deprecated, use Structs instead. -func (j *Json) ToStructsDeep(pointer interface{}, mapping ...map[string]string) error { - j.mu.RLock() - defer j.mu.RUnlock() - return gconv.StructsDeep(*(j.p), pointer, mapping...) -} - -// ToScan automatically calls Struct or Structs function according to the type of parameter -// to implement the converting.. -// Deprecated, use Scan instead. -func (j *Json) ToScan(pointer interface{}, mapping ...map[string]string) error { - return gconv.Scan(*(j.p), pointer, mapping...) -} - -// ToScanDeep automatically calls StructDeep or StructsDeep function according to the type of -// parameter to implement the converting.. -// Deprecated, use Scan instead. -func (j *Json) ToScanDeep(pointer interface{}, mapping ...map[string]string) error { - return gconv.ScanDeep(*(j.p), pointer, mapping...) -} - -// ToMapToMap converts current Json object to specified map variable. -// The parameter of should be type of *map. -// Deprecated, use MapToMap instead. -func (j *Json) ToMapToMap(pointer interface{}, mapping ...map[string]string) error { - j.mu.RLock() - defer j.mu.RUnlock() - return gconv.MapToMap(*(j.p), pointer, mapping...) -} - -// ToMapToMaps converts current Json object to specified map variable slice. -// The parameter of should be type of []map/*map. -// Deprecated, use MapToMaps instead. -func (j *Json) ToMapToMaps(pointer interface{}, mapping ...map[string]string) error { - j.mu.RLock() - defer j.mu.RUnlock() - return gconv.MapToMaps(*(j.p), pointer, mapping...) -} - -// ToMapToMapsDeep converts current Json object to specified map variable slice recursively. -// The parameter of should be type of []map/*map. -// Deprecated, use MapToMaps instead. -func (j *Json) ToMapToMapsDeep(pointer interface{}, mapping ...map[string]string) error { - j.mu.RLock() - defer j.mu.RUnlock() - return gconv.MapToMapsDeep(*(j.p), pointer, mapping...) -} diff --git a/encoding/gjson/gjson_z_example_conversion_test.go b/encoding/gjson/gjson_z_example_conversion_test.go index 1a103f6a6..c55d9c409 100644 --- a/encoding/gjson/gjson_z_example_conversion_test.go +++ b/encoding/gjson/gjson_z_example_conversion_test.go @@ -100,7 +100,7 @@ func Example_conversionToStruct() { Array []string } users := new(Users) - if err := j.ToStruct(users); err != nil { + if err := j.Struct(users); err != nil { panic(err) } fmt.Printf(`%+v`, users) diff --git a/encoding/gjson/gjson_z_unit_basic_test.go b/encoding/gjson/gjson_z_unit_basic_test.go index c89b615ad..35b9538f0 100644 --- a/encoding/gjson/gjson_z_unit_basic_test.go +++ b/encoding/gjson/gjson_z_unit_basic_test.go @@ -353,7 +353,7 @@ func Test_Convert2(t *testing.T) { t.Assert(j.GetGTime("time").Format("Y-m-d"), "2019-06-12") t.Assert(j.GetDuration("time").String(), "0s") - err := j.ToStruct(&name) + err := j.Struct(&name) t.Assert(err, nil) t.Assert(name.Name, "gf") //j.Dump() @@ -369,7 +369,7 @@ func Test_Convert2(t *testing.T) { t.Assert(err, nil) j = gjson.New(`[1,2,3]`) - t.Assert(len(j.ToArray()), 3) + t.Assert(len(j.Array()), 3) }) } @@ -400,7 +400,7 @@ func Test_Basic(t *testing.T) { err = j.Remove("1") t.Assert(err, nil) t.Assert(j.Get("0"), 1) - t.Assert(len(j.ToArray()), 2) + t.Assert(len(j.Array()), 2) j = gjson.New(`[1,2,3]`) // If index 0 is delete, its next item will be at index 0. @@ -408,13 +408,13 @@ func Test_Basic(t *testing.T) { t.Assert(j.Remove("0"), nil) t.Assert(j.Remove("0"), nil) t.Assert(j.Get("0"), nil) - t.Assert(len(j.ToArray()), 0) + t.Assert(len(j.Array()), 0) j = gjson.New(`[1,2,3]`) err = j.Remove("3") t.Assert(err, nil) t.Assert(j.Get("0"), 1) - t.Assert(len(j.ToArray()), 3) + t.Assert(len(j.Array()), 3) j = gjson.New(`[1,2,3]`) err = j.Remove("0.3") diff --git a/encoding/gjson/gjson_z_unit_json_test.go b/encoding/gjson/gjson_z_unit_json_test.go index 8c2b50feb..27f5bd27c 100644 --- a/encoding/gjson/gjson_z_unit_json_test.go +++ b/encoding/gjson/gjson_z_unit_json_test.go @@ -61,7 +61,7 @@ func Test_MapAttributeConvert(t *testing.T) { Title map[string]interface{} }{} - err = j.ToStruct(&tx) + err = j.Struct(&tx) gtest.Assert(err, nil) t.Assert(tx.Title, g.Map{ "l1": "标签1", "l2": "标签2", @@ -76,7 +76,7 @@ func Test_MapAttributeConvert(t *testing.T) { Title map[string]string }{} - err = j.ToStruct(&tx) + err = j.Struct(&tx) gtest.Assert(err, nil) t.Assert(tx.Title, g.Map{ "l1": "标签1", "l2": "标签2", diff --git a/encoding/gparser/gparser_unit_basic_test.go b/encoding/gparser/gparser_unit_basic_test.go index 59beb1d69..bcdae3700 100644 --- a/encoding/gparser/gparser_unit_basic_test.go +++ b/encoding/gparser/gparser_unit_basic_test.go @@ -230,14 +230,14 @@ func Test_Convert(t *testing.T) { err := p.GetStruct("person", &name) t.Assert(err, nil) t.Assert(name.Name, "gf") - t.Assert(p.ToMap()["name"], "gf") - err = p.ToStruct(&name) + t.Assert(p.Map()["name"], "gf") + err = p.Struct(&name) t.Assert(err, nil) t.Assert(name.Name, "gf") //p.Dump() p = gparser.New(`[0,1,2]`) - t.Assert(p.ToArray()[0], 0) + t.Assert(p.Array()[0], 0) }) } diff --git a/internal/utils/utils_debug.go b/internal/utils/utils_debug.go index f08a1f668..7817aecf9 100644 --- a/internal/utils/utils_debug.go +++ b/internal/utils/utils_debug.go @@ -11,7 +11,7 @@ import ( ) const ( - debugKey = "gf.debug" // Debug key for checking if in debug mode. + commandEnvKeyForDebugKey = "gf.debug" // Debug key for checking if in debug mode. StackFilterKeyForGoFrame = "github.com/gogf/gf@" // Stack filtering key for all GoFrame module paths. ) @@ -22,7 +22,7 @@ var ( func init() { // Debugging configured. - value := command.GetOptWithEnv(debugKey) + value := command.GetOptWithEnv(commandEnvKeyForDebugKey) if value == "" || value == "0" || value == "false" { isDebugEnabled = false } else { diff --git a/net/ghttp/ghttp_server_admin_process.go b/net/ghttp/ghttp_server_admin_process.go index 577a0168d..d8a36a831 100644 --- a/net/ghttp/ghttp_server_admin_process.go +++ b/net/ghttp/ghttp_server_admin_process.go @@ -173,7 +173,7 @@ func bufferToServerFdMap(buffer []byte) map[string]listenerFdMap { sfm := make(map[string]listenerFdMap) if len(buffer) > 0 { j, _ := gjson.LoadContent(buffer) - for k, _ := range j.ToMap() { + for k, _ := range j.Map() { m := make(map[string]string) for k, v := range j.GetMap(k) { m[k] = gconv.String(v) diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index 34d906cee..6d847474b 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -9,7 +9,6 @@ package gtrace import ( "context" - "fmt" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/net/gipv4" @@ -23,15 +22,17 @@ import ( ) const ( - tracingCommonKeyIpIntranet = `ip.intranet` - tracingCommonKeyIpHostname = `hostname` - cmdEnvKey = "gf.gtrace" // Configuration key for command argument or environment. + tracingCommonKeyIpIntranet = `ip.intranet` + tracingCommonKeyIpHostname = `hostname` + commandEnvKeyForMaxContentLogSize = "gf.gtrace.maxcontentlogsize" + commandEnvKeyForTracingInternal = "gf.gtrace.tracinginternal" ) var ( intranetIps, _ = gipv4.GetIntranetIpArray() intranetIpStr = strings.Join(intranetIps, ",") hostname, _ = os.Hostname() + tracingInternal = true // tracingInternal enables tracing for internal type spans. tracingMaxContentLogSize = 256 * 1024 // Max log size for request and response body, especially for HTTP/RPC request. // defaultTextMapPropagator is the default propagator for context propagation between peers. defaultTextMapPropagator = propagation.NewCompositeTextMapPropagator( @@ -41,12 +42,18 @@ var ( ) func init() { - if maxContentLogSize := gcmd.GetOptWithEnv(fmt.Sprintf("%s.maxcontentlogsize", cmdEnvKey)).Int(); maxContentLogSize > 0 { + tracingInternal = gcmd.GetOptWithEnv(commandEnvKeyForTracingInternal, true).Bool() + if maxContentLogSize := gcmd.GetOptWithEnv(commandEnvKeyForMaxContentLogSize).Int(); maxContentLogSize > 0 { tracingMaxContentLogSize = maxContentLogSize } CheckSetDefaultTextMapPropagator() } +// IsTracingInternal returns whether tracing spans of internal components. +func IsTracingInternal() bool { + return tracingInternal +} + // MaxContentLogSize returns the max log size for request and response body, especially for HTTP/RPC request. func MaxContentLogSize() int { return tracingMaxContentLogSize diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index d9eac9f2b..b872cbf78 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -23,10 +23,11 @@ type Config struct { } const ( - DefaultName = "config" // DefaultName is the default group name for instance usage. - DefaultConfigFile = "config.toml" // DefaultConfigFile is the default configuration file name. - cmdEnvKey = "gf.gcfg" // cmdEnvKey is the configuration key for command argument or environment. - errorPrintKey = "gf.gcfg.errorprint" // errorPrintKey is used to specify the key controlling error printing to stdout. + DefaultName = "config" // DefaultName is the default group name for instance usage. + DefaultConfigFile = "config.toml" // DefaultConfigFile is the default configuration file name. + commandEnvKeyForFile = "gf.gcfg.file" // commandEnvKeyForFile is the configuration key for command argument or environment configuring file name. + commandEnvKeyForPath = "gf.gcfg.path" // commandEnvKeyForPath is the configuration key for command argument or environment configuring directory path. + commandEnvKeyForErrorPrint = "gf.gcfg.errorprint" // commandEnvKeyForErrorPrint is used to specify the key controlling error printing to stdout. ) var ( @@ -99,5 +100,5 @@ func ClearContent() { // errorPrint checks whether printing error to stdout. func errorPrint() bool { - return gcmd.GetOptWithEnv(errorPrintKey, true).Bool() + return gcmd.GetOptWithEnv(commandEnvKeyForErrorPrint, true).Bool() } diff --git a/os/gcfg/gcfg_config.go b/os/gcfg/gcfg_config.go index 6034f1f88..a76a36e41 100644 --- a/os/gcfg/gcfg_config.go +++ b/os/gcfg/gcfg_config.go @@ -33,7 +33,7 @@ func New(file ...string) *Config { name = file[0] } else { // Custom default configuration file name from command line or environment. - if customFile := gcmd.GetOptWithEnv(fmt.Sprintf("%s.file", cmdEnvKey)).String(); customFile != "" { + if customFile := gcmd.GetOptWithEnv(commandEnvKeyForFile).String(); customFile != "" { name = customFile } } @@ -43,7 +43,7 @@ func New(file ...string) *Config { jsonMap: gmap.NewStrAnyMap(true), } // Customized dir path from env/cmd. - if customPath := gcmd.GetOptWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).String(); customPath != "" { + if customPath := gcmd.GetOptWithEnv(commandEnvKeyForPath).String(); customPath != "" { if gfile.Exists(customPath) { _ = c.SetPath(customPath) } else { diff --git a/os/gcfg/gcfg_config_api.go b/os/gcfg/gcfg_config_api.go index 1fafb391b..aee7804a3 100644 --- a/os/gcfg/gcfg_config_api.go +++ b/os/gcfg/gcfg_config_api.go @@ -298,15 +298,6 @@ func (c *Config) GetStruct(pattern string, pointer interface{}, mapping ...map[s return errors.New("configuration not found") } -// GetStructDeep does GetStruct recursively. -// Deprecated, use GetStruct instead. -func (c *Config) GetStructDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.GetStructDeep(pattern, pointer, mapping...) - } - return errors.New("configuration not found") -} - // GetStructs converts any slice to given struct slice. func (c *Config) GetStructs(pattern string, pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { @@ -315,15 +306,6 @@ func (c *Config) GetStructs(pattern string, pointer interface{}, mapping ...map[ return errors.New("configuration not found") } -// GetStructsDeep converts any slice to given struct slice recursively. -// Deprecated, use GetStructs instead. -func (c *Config) GetStructsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.GetStructsDeep(pattern, pointer, mapping...) - } - return errors.New("configuration not found") -} - // GetMapToMap retrieves the value by specified `pattern` and converts it to specified map variable. // See gconv.MapToMap. func (c *Config) GetMapToMap(pattern string, pointer interface{}, mapping ...map[string]string) error { @@ -353,83 +335,55 @@ func (c *Config) GetMapToMapsDeep(pattern string, pointer interface{}, mapping . return errors.New("configuration not found") } -// ToMap converts current Json object to map[string]interface{}. -// It returns nil if fails. -func (c *Config) ToMap() map[string]interface{} { +// Map converts current Json object to map[string]interface{}. It returns nil if fails. +func (c *Config) Map() map[string]interface{} { if j := c.getJson(); j != nil { - return j.ToMap() + return j.Map() } return nil } -// ToArray converts current Json object to []interface{}. +// Array converts current Json object to []interface{}. // It returns nil if fails. -func (c *Config) ToArray() []interface{} { +func (c *Config) Array() []interface{} { if j := c.getJson(); j != nil { - return j.ToArray() + return j.Array() } return nil } -// ToStruct converts current Json object to specified object. +// Struct converts current Json object to specified object. // The `pointer` should be a pointer type of *struct. -func (c *Config) ToStruct(pointer interface{}, mapping ...map[string]string) error { +func (c *Config) Struct(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { - return j.ToStruct(pointer, mapping...) + return j.Struct(pointer, mapping...) } return errors.New("configuration not found") } -// ToStructDeep converts current Json object to specified object recursively. -// The `pointer` should be a pointer type of *struct. -func (c *Config) ToStructDeep(pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.ToStructDeep(pointer, mapping...) - } - return errors.New("configuration not found") -} - -// ToStructs converts current Json object to specified object slice. +// Structs converts current Json object to specified object slice. // The `pointer` should be a pointer type of []struct/*struct. -func (c *Config) ToStructs(pointer interface{}, mapping ...map[string]string) error { +func (c *Config) Structs(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { - return j.ToStructs(pointer, mapping...) + return j.Structs(pointer, mapping...) } return errors.New("configuration not found") } -// ToStructsDeep converts current Json object to specified object slice recursively. -// The `pointer` should be a pointer type of []struct/*struct. -func (c *Config) ToStructsDeep(pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.ToStructsDeep(pointer, mapping...) - } - return errors.New("configuration not found") -} - -// ToMapToMap converts current Json object to specified map variable. +// MapToMap converts current Json object to specified map variable. // The parameter of `pointer` should be type of *map. -func (c *Config) ToMapToMap(pointer interface{}, mapping ...map[string]string) error { +func (c *Config) MapToMap(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { - return j.ToMapToMap(pointer, mapping...) + return j.MapToMap(pointer, mapping...) } return errors.New("configuration not found") } -// ToMapToMaps converts current Json object to specified map variable slice. +// MapToMaps converts current Json object to specified map variable slice. // The parameter of `pointer` should be type of []map/*map. -func (c *Config) ToMapToMaps(pointer interface{}, mapping ...map[string]string) error { +func (c *Config) MapToMaps(pointer interface{}, mapping ...map[string]string) error { if j := c.getJson(); j != nil { - return j.ToMapToMaps(pointer, mapping...) - } - return errors.New("configuration not found") -} - -// ToMapToMapsDeep converts current Json object to specified map variable slice recursively. -// The parameter of `pointer` should be type of []map/*map. -func (c *Config) ToMapToMapsDeep(pointer interface{}, mapping ...map[string]string) error { - if j := c.getJson(); j != nil { - return j.ToMapToMapsDeep(pointer, mapping...) + return j.MapToMaps(pointer, mapping...) } return errors.New("configuration not found") } diff --git a/os/gfile/gfile_cache.go b/os/gfile/gfile_cache.go index 758d85495..e1354f19f 100644 --- a/os/gfile/gfile_cache.go +++ b/os/gfile/gfile_cache.go @@ -14,19 +14,19 @@ import ( ) const ( - // Default expire time for file content caching in seconds. - gDEFAULT_CACHE_EXPIRE = time.Minute + defaultCacheExpire = time.Minute // defaultCacheExpire is the expire time for file content caching in seconds. + commandEnvKeyForCache = "gf.gfile.cache" // commandEnvKeyForCache is the configuration key for command argument or environment configuring cache expire duration. ) var ( // Default expire time for file content caching. - cacheExpire = gcmd.GetOptWithEnv("gf.gfile.cache", gDEFAULT_CACHE_EXPIRE).Duration() + cacheExpire = gcmd.GetOptWithEnv(commandEnvKeyForCache, defaultCacheExpire).Duration() // internalCache is the memory cache for internal usage. internalCache = gcache.New() ) -// GetContents returns string content of given file by from cache. +// GetContentsWithCache returns string content of given file by from cache. // If there's no content in the cache, it will read it from disk file specified by . // The parameter specifies the caching time for this file content in seconds. func GetContentsWithCache(path string, duration ...time.Duration) string { @@ -62,5 +62,5 @@ func GetBytesWithCache(path string, duration ...time.Duration) []byte { // cacheKey produces the cache key for gcache. func cacheKey(path string) string { - return "gf.gfile.cache:" + path + return commandEnvKeyForCache + path } diff --git a/os/glog/glog.go b/os/glog/glog.go index a465a5b43..afa420db8 100644 --- a/os/glog/glog.go +++ b/os/glog/glog.go @@ -12,12 +12,16 @@ import ( "github.com/gogf/gf/os/grpool" ) +const ( + commandEnvKeyForDebug = "gf.glog.debug" +) + var ( // Default logger object, for package method usage. logger = New() // Goroutine pool for async logging output. - // It uses only one asynchronize worker to ensure log sequence. + // It uses only one asynchronous worker to ensure log sequence. asyncPool = grpool.New(1) // defaultDebug enables debug level or not in default, @@ -26,7 +30,7 @@ var ( ) func init() { - defaultDebug = gcmd.GetOptWithEnv("gf.glog.debug", true).Bool() + defaultDebug = gcmd.GetOptWithEnv(commandEnvKeyForDebug, true).Bool() SetDebug(defaultDebug) } diff --git a/os/gtimer/gtimer.go b/os/gtimer/gtimer.go index 3ffd36326..5cc849f7b 100644 --- a/os/gtimer/gtimer.go +++ b/os/gtimer/gtimer.go @@ -19,7 +19,6 @@ package gtimer import ( - "fmt" "github.com/gogf/gf/container/gtype" "math" "sync" @@ -43,21 +42,19 @@ type TimerOptions struct { } const ( - StatusReady = 0 // Job or Timer is ready for running. - StatusRunning = 1 // Job or Timer is already running. - StatusStopped = 2 // Job or Timer is stopped. - StatusClosed = -1 // Job or Timer is closed and waiting to be deleted. - panicExit = "exit" // panicExit is used for custom job exit with panic. - defaultTimes = math.MaxInt32 // defaultTimes is the default limit running times, a big number. - defaultTimerInterval = 100 // defaultTimerInterval is the default timer interval in milliseconds. - cmdEnvKey = "gf.gtimer" // Configuration key for command argument or environment. + StatusReady = 0 // Job or Timer is ready for running. + StatusRunning = 1 // Job or Timer is already running. + StatusStopped = 2 // Job or Timer is stopped. + StatusClosed = -1 // Job or Timer is closed and waiting to be deleted. + panicExit = "exit" // panicExit is used for custom job exit with panic. + defaultTimes = math.MaxInt32 // defaultTimes is the default limit running times, a big number. + defaultTimerInterval = 100 // defaultTimerInterval is the default timer interval in milliseconds. + commandEnvKeyForInterval = "gf.gtimer.interval" // commandEnvKeyForInterval is the key for command argument or environment configuring default interval duration for timer. ) var ( defaultTimer = New() - defaultInterval = gcmd.GetOptWithEnv( - fmt.Sprintf("%s.interval", cmdEnvKey), defaultTimerInterval, - ).Duration() * time.Millisecond + defaultInterval = gcmd.GetOptWithEnv(commandEnvKeyForInterval, defaultTimerInterval).Duration() * time.Millisecond ) // DefaultOptions creates and returns a default options object for Timer creation. diff --git a/os/gview/gview.go b/os/gview/gview.go index dc0013add..26dbd393e 100644 --- a/os/gview/gview.go +++ b/os/gview/gview.go @@ -36,6 +36,10 @@ type ( FuncMap = map[string]interface{} // FuncMap is type for custom template functions. ) +const ( + commandEnvKeyForPath = "gf.gview.path" +) + var ( // Default view object. defaultViewObj *View @@ -72,7 +76,7 @@ func New(path ...string) *View { } } else { // Customized dir path from env/cmd. - if envPath := gcmd.GetOptWithEnv("gf.gview.path").String(); envPath != "" { + if envPath := gcmd.GetOptWithEnv(commandEnvKeyForPath).String(); envPath != "" { if gfile.Exists(envPath) { if err := view.SetPath(envPath); err != nil { intlog.Error(err) diff --git a/os/gview/gview_error.go b/os/gview/gview_error.go index 4c38fafe5..268321bf7 100644 --- a/os/gview/gview_error.go +++ b/os/gview/gview_error.go @@ -11,12 +11,12 @@ import ( ) const ( - // gERROR_PRINT_KEY is used to specify the key controlling error printing to stdout. + // commandEnvKeyForErrorPrint is used to specify the key controlling error printing to stdout. // This error is designed not to be returned by functions. - gERROR_PRINT_KEY = "gf.gview.errorprint" + commandEnvKeyForErrorPrint = "gf.gview.errorprint" ) // errorPrint checks whether printing error to stdout. func errorPrint() bool { - return gcmd.GetOptWithEnv(gERROR_PRINT_KEY, true).Bool() + return gcmd.GetOptWithEnv(commandEnvKeyForErrorPrint, true).Bool() } diff --git a/util/gmode/gmode.go b/util/gmode/gmode.go index 14df6f5a2..199e89a0d 100644 --- a/util/gmode/gmode.go +++ b/util/gmode/gmode.go @@ -16,12 +16,12 @@ import ( ) const ( - NOT_SET = "not-set" - DEVELOP = "develop" - TESTING = "testing" - STAGING = "staging" - PRODUCT = "product" - cmdEnvKey = "gf.gmode" + NOT_SET = "not-set" + DEVELOP = "develop" + TESTING = "testing" + STAGING = "staging" + PRODUCT = "product" + commandEnvKey = "gf.gmode" ) var ( @@ -58,7 +58,7 @@ func SetProduct() { func Mode() string { // If current mode is not set, do this auto check. if currentMode == NOT_SET { - if v := gcmd.GetOptWithEnv(cmdEnvKey).String(); v != "" { + if v := gcmd.GetOptWithEnv(commandEnvKey).String(); v != "" { // Mode configured from command argument of environment. currentMode = v } else { From 50ffaef33fd68e801cc9a680f5b5f3749a1902c6 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 26 Jun 2021 16:23:54 +0800 Subject: [PATCH 346/492] add context for intlog/gsession;improve struct/structs converting for package gconv --- database/gdb/gdb.go | 4 +- database/gdb/gdb_driver_mssql.go | 2 +- database/gdb/gdb_driver_mysql.go | 2 +- database/gdb/gdb_driver_oracle.go | 2 +- database/gdb/gdb_driver_pgsql.go | 2 +- database/gdb/gdb_driver_sqlite.go | 2 +- database/gdb/gdb_model_select.go | 4 +- database/gdb/gdb_type_record.go | 2 +- database/gdb/gdb_type_result.go | 2 +- database/gdb/gdb_z_mysql_struct_test.go | 21 +- database/gredis/gredis.go | 2 +- database/gredis/gredis_config.go | 5 +- encoding/gcompress/gcompress_zip.go | 3 +- frame/gins/gins_database.go | 13 +- i18n/gi18n/gi18n_manager.go | 24 +- internal/intlog/intlog.go | 61 +++-- net/ghttp/ghttp_request.go | 7 +- net/ghttp/ghttp_request_param_file.go | 8 +- net/ghttp/ghttp_server.go | 13 +- net/ghttp/ghttp_server_admin_process.go | 5 +- net/ghttp/ghttp_server_admin_unix.go | 5 +- net/ghttp/ghttp_server_config.go | 5 +- net/ghttp/ghttp_server_handler.go | 4 +- net/ghttp/internal/client/client_request.go | 6 +- os/gcfg/gcfg.go | 5 +- os/gcfg/gcfg_config.go | 11 +- os/gfsnotify/gfsnotify.go | 3 +- os/gfsnotify/gfsnotify_watcher.go | 13 +- os/gfsnotify/gfsnotify_watcher_loop.go | 19 +- os/glog/glog_logger.go | 40 +-- os/glog/glog_logger_chaining.go | 6 +- os/glog/glog_logger_config.go | 4 +- os/glog/glog_logger_rotate.go | 37 +-- os/gproc/gproc_process.go | 6 +- os/gproc/gproc_signal.go | 3 +- os/gres/gres_func_zip.go | 3 +- os/gres/gres_resource.go | 5 +- os/gsession/gsession_manager.go | 4 +- os/gsession/gsession_session.go | 43 ++-- os/gsession/gsession_storage.go | 23 +- os/gsession/gsession_storage_file.go | 36 +-- os/gsession/gsession_storage_memory.go | 28 +-- os/gsession/gsession_storage_redis.go | 47 ++-- .../gsession_storage_redis_hashtable.go | 47 ++-- .../gsession_unit_storage_file_test.go | 7 +- .../gsession_unit_storage_memory_test.go | 7 +- ...ssion_unit_storage_redis_hashtable_test.go | 13 +- .../gsession_unit_storage_redis_test.go | 13 +- os/gspath/gspath.go | 3 +- os/gview/gview.go | 10 +- os/gview/gview_config.go | 3 +- os/gview/gview_parse.go | 2 +- util/gconv/gconv.go | 228 ++++++++++-------- util/gconv/gconv_map.go | 2 +- util/gconv/gconv_scan.go | 1 + util/gconv/gconv_struct.go | 25 +- util/gconv/gconv_structs.go | 35 ++- util/gconv/gconv_z_unit_scan_test.go | 2 +- 58 files changed, 517 insertions(+), 421 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index fead99d3b..c0ecb2a83 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -486,14 +486,16 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error // Cache the underlying connection pool object by node. v, _ := internalCache.GetOrSetFuncLock(node.String(), func() (interface{}, error) { intlog.Printf( + c.db.GetCtx(), `open new connection, master:%#v, config:%#v, node:%#v`, master, c.config, node, ) defer func() { if err != nil { - intlog.Printf(`open new connection failed: %v, %#v`, err, node) + intlog.Printf(c.db.GetCtx(), `open new connection failed: %v, %#v`, err, node) } else { intlog.Printf( + c.db.GetCtx(), `open new connection success, master:%#v, config:%#v, node:%#v`, master, c.config, node, ) diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index 4ee218504..0e91b48d8 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -50,7 +50,7 @@ func (d *DriverMssql) Open(config *ConfigNode) (*sql.DB, error) { config.User, config.Pass, config.Host, config.Port, config.Name, ) } - intlog.Printf("Open: %s", source) + intlog.Printf(d.GetCtx(), "Open: %s", source) if db, err := sql.Open("sqlserver", source); err == nil { return db, nil } else { diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index adb1db26a..b03b56839 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -52,7 +52,7 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) { source = fmt.Sprintf("%s&loc=%s", source, url.QueryEscape(config.Timezone)) } } - intlog.Printf("Open: %s", source) + intlog.Printf(d.GetCtx(), "Open: %s", source) if db, err := sql.Open("mysql", source); err == nil { return db, nil } else { diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index e6bb6fd13..682b5eee9 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -56,7 +56,7 @@ func (d *DriverOracle) Open(config *ConfigNode) (*sql.DB, error) { config.User, config.Pass, config.Host, config.Port, config.Name, ) } - intlog.Printf("Open: %s", source) + intlog.Printf(d.GetCtx(), "Open: %s", source) if db, err := sql.Open("oci8", source); err == nil { return db, nil } else { diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index a31546ae9..28d924e84 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -51,7 +51,7 @@ func (d *DriverPgsql) Open(config *ConfigNode) (*sql.DB, error) { source = fmt.Sprintf("%s timezone=%s", source, config.Timezone) } } - intlog.Printf("Open: %s", source) + intlog.Printf(d.GetCtx(), "Open: %s", source) if db, err := sql.Open("postgres", source); err == nil { return db, nil } else { diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index 5dc37c2ae..b236a3889 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -47,7 +47,7 @@ func (d *DriverSqlite) Open(config *ConfigNode) (*sql.DB, error) { if absolutePath, _ := gfile.Search(source); absolutePath != "" { source = absolutePath } - intlog.Printf("Open: %s", source) + intlog.Printf(d.GetCtx(), "Open: %s", source) if db, err := sql.Open("sqlite3", source); err == nil { return db, nil } else { diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 9d1dab718..cd39c4332 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -546,11 +546,11 @@ func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, e if cacheKey != "" && err == nil { if m.cacheDuration < 0 { if _, err := cacheObj.Remove(cacheKey); err != nil { - intlog.Error(err) + intlog.Error(m.GetCtx(), err) } } else { if err := cacheObj.Set(cacheKey, result, m.cacheDuration); err != nil { - intlog.Error(err) + intlog.Error(m.GetCtx(), err) } } } diff --git a/database/gdb/gdb_type_record.go b/database/gdb/gdb_type_record.go index 3685dd296..a660ec4ef 100644 --- a/database/gdb/gdb_type_record.go +++ b/database/gdb/gdb_type_record.go @@ -52,7 +52,7 @@ func (r Record) Struct(pointer interface{}) error { } return nil } - return gconv.StructTag(r.Map(), pointer, OrmTagForStruct) + return gconv.StructTag(r, pointer, OrmTagForStruct) } // IsEmpty checks and returns whether `r` is empty. diff --git a/database/gdb/gdb_type_result.go b/database/gdb/gdb_type_result.go index 05e15026c..489e3bc47 100644 --- a/database/gdb/gdb_type_result.go +++ b/database/gdb/gdb_type_result.go @@ -189,5 +189,5 @@ func (r Result) RecordKeyUint(key string) map[uint]Record { // Structs converts `r` to struct slice. // Note that the parameter `pointer` should be type of *[]struct/*[]*struct. func (r Result) Structs(pointer interface{}) (err error) { - return gconv.StructsTag(r.List(), pointer, OrmTagForStruct) + return gconv.StructsTag(r, pointer, OrmTagForStruct) } diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index eb895be5a..d4d7a7373 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -8,10 +8,13 @@ package gdb_test import ( "database/sql" + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" "github.com/gogf/gf/util/gconv" + "reflect" "testing" ) @@ -395,17 +398,17 @@ type User struct { } func (user *User) UnmarshalValue(value interface{}) error { - switch result := value.(type) { - case map[string]interface{}: - user.Id = result["id"].(int) - user.Passport = result["passport"].(string) - user.Password = "" - user.Nickname = result["nickname"].(string) - user.CreateTime = gtime.New(result["create_time"]) + if record, ok := value.(gdb.Record); ok { + *user = User{ + Id: record["id"].Int(), + Passport: record["passport"].String(), + Password: "", + Nickname: record["nickname"].String(), + CreateTime: record["create_time"].GTime(), + } return nil - default: - return gconv.Struct(value, user) } + return gerror.Newf(`unsupported value type for UnmarshalValue: %v`, reflect.TypeOf(value)) } func Test_Model_Scan_UnmarshalValue(t *testing.T) { diff --git a/database/gredis/gredis.go b/database/gredis/gredis.go index 13211d81d..8de774e15 100644 --- a/database/gredis/gredis.go +++ b/database/gredis/gredis.go @@ -114,7 +114,7 @@ func New(config *Config) *Redis { if err != nil { return nil, err } - intlog.Printf(`open new connection, config:%+v`, config) + intlog.Printf(context.TODO(), `open new connection, config:%+v`, config) // AUTH if len(config.Pass) > 0 { if _, err := c.Do("AUTH", config.Pass); err != nil { diff --git a/database/gredis/gredis_config.go b/database/gredis/gredis_config.go index c2f2ba42e..121b9c4a1 100644 --- a/database/gredis/gredis_config.go +++ b/database/gredis/gredis_config.go @@ -7,6 +7,7 @@ package gredis import ( + "context" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" @@ -36,7 +37,7 @@ func SetConfig(config *Config, name ...string) { configs.Set(group, config) instances.Remove(group) - intlog.Printf(`SetConfig for group "%s": %+v`, group, config) + intlog.Printf(context.TODO(), `SetConfig for group "%s": %+v`, group, config) } // SetConfigByStr sets the global configuration for specified group with string. @@ -78,7 +79,7 @@ func RemoveConfig(name ...string) { configs.Remove(group) instances.Remove(group) - intlog.Printf(`RemoveConfig: %s`, group) + intlog.Printf(context.TODO(), `RemoveConfig: %s`, group) } // ConfigFromStr parses and returns config from given str. diff --git a/encoding/gcompress/gcompress_zip.go b/encoding/gcompress/gcompress_zip.go index 72991aff9..dad27820b 100644 --- a/encoding/gcompress/gcompress_zip.go +++ b/encoding/gcompress/gcompress_zip.go @@ -9,6 +9,7 @@ package gcompress import ( "archive/zip" "bytes" + "context" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/text/gstr" @@ -92,7 +93,7 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix headerPrefix = strings.Replace(headerPrefix, "//", "/", -1) for _, file := range files { if exclude == file { - intlog.Printf(`exclude file path: %s`, file) + intlog.Printf(context.TODO(), `exclude file path: %s`, file) continue } dir := gfile.Dir(file[len(path):]) diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index 506a66cf0..1206ee7d4 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -7,6 +7,7 @@ package gins import ( + "context" "fmt" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" @@ -92,11 +93,11 @@ func Database(name ...string) gdb.DB { } if len(cg) > 0 { if gdb.GetConfig(group) == nil { - intlog.Printf("add configuration for group: %s, %#v", g, cg) + intlog.Printf(context.TODO(), "add configuration for group: %s, %#v", g, cg) gdb.SetConfigGroup(g, cg) } else { - intlog.Printf("ignore configuration as it already exists for group: %s, %#v", g, cg) - intlog.Printf("%s, %#v", g, cg) + intlog.Printf(context.TODO(), "ignore configuration as it already exists for group: %s, %#v", g, cg) + intlog.Printf(context.TODO(), "%s, %#v", g, cg) } } } @@ -110,11 +111,11 @@ func Database(name ...string) gdb.DB { if len(cg) > 0 { if gdb.GetConfig(group) == nil { - intlog.Printf("add configuration for group: %s, %#v", gdb.DefaultGroupName, cg) + intlog.Printf(context.TODO(), "add configuration for group: %s, %#v", gdb.DefaultGroupName, cg) gdb.SetConfigGroup(gdb.DefaultGroupName, cg) } else { - intlog.Printf("ignore configuration as it already exists for group: %s, %#v", gdb.DefaultGroupName, cg) - intlog.Printf("%s, %#v", gdb.DefaultGroupName, cg) + intlog.Printf(context.TODO(), "ignore configuration as it already exists for group: %s, %#v", gdb.DefaultGroupName, cg) + intlog.Printf(context.TODO(), "%s, %#v", gdb.DefaultGroupName, cg) } } } diff --git a/i18n/gi18n/gi18n_manager.go b/i18n/gi18n/gi18n_manager.go index 0949c1f44..d289e2053 100644 --- a/i18n/gi18n/gi18n_manager.go +++ b/i18n/gi18n/gi18n_manager.go @@ -70,7 +70,7 @@ func New(options ...Options) *Manager { gregex.Quote(opts.Delimiters[1]), ), } - intlog.Printf(`New: %#v`, m) + intlog.Printf(context.TODO(), `New: %#v`, m) return m } @@ -105,20 +105,20 @@ func (m *Manager) SetPath(path string) error { } m.options.Path = realPath } - intlog.Printf(`SetPath: %s`, m.options.Path) + intlog.Printf(context.TODO(), `SetPath: %s`, m.options.Path) return nil } // SetLanguage sets the language for translator. func (m *Manager) SetLanguage(language string) { m.options.Language = language - intlog.Printf(`SetLanguage: %s`, m.options.Language) + intlog.Printf(context.TODO(), `SetLanguage: %s`, m.options.Language) } // SetDelimiters sets the delimiters for translator. func (m *Manager) SetDelimiters(left, right string) { m.pattern = fmt.Sprintf(`%s(\w+)%s`, gregex.Quote(left), gregex.Quote(right)) - intlog.Printf(`SetDelimiters: %v`, m.pattern) + intlog.Printf(context.TODO(), `SetDelimiters: %v`, m.pattern) } // T is alias of Translate for convenience. @@ -139,7 +139,7 @@ func (m *Manager) TranslateFormat(ctx context.Context, format string, values ... // Translate translates with configured language. func (m *Manager) Translate(ctx context.Context, content string) string { - m.init() + m.init(ctx) m.mu.RLock() defer m.mu.RUnlock() transLang := m.options.Language @@ -163,14 +163,14 @@ func (m *Manager) Translate(ctx context.Context, content string) string { } return match[0] }) - intlog.Printf(`Translate for language: %s`, transLang) + intlog.Printf(ctx, `Translate for language: %s`, transLang) return result } // GetContent retrieves and returns the configured content for given key and specified language. // It returns an empty string if not found. func (m *Manager) GetContent(ctx context.Context, key string) string { - m.init() + m.init(ctx) m.mu.RLock() defer m.mu.RUnlock() transLang := m.options.Language @@ -185,7 +185,7 @@ func (m *Manager) GetContent(ctx context.Context, key string) string { // init initializes the manager for lazy initialization design. // The i18n manager is only initialized once. -func (m *Manager) init() { +func (m *Manager) init(ctx context.Context) { m.mu.RLock() // If the data is not nil, means it's already initialized. if m.data != nil { @@ -223,17 +223,13 @@ func (m *Manager) init() { m.data[lang][k] = gconv.String(v) } } else { - intlog.Errorf("load i18n file '%s' failed: %v", name, err) + intlog.Errorf(ctx, "load i18n file '%s' failed: %v", name, err) } } } } else if m.options.Path != "" { files, _ := gfile.ScanDirFile(m.options.Path, "*.*", true) if len(files) == 0 { - //intlog.Printf( - // "no i18n files found in configured directory: %s", - // m.options.Path, - //) return } var ( @@ -258,7 +254,7 @@ func (m *Manager) init() { m.data[lang][k] = gconv.String(v) } } else { - intlog.Errorf("load i18n file '%s' failed: %v", file, err) + intlog.Errorf(ctx, "load i18n file '%s' failed: %v", file, err) } } // Monitor changes of i18n files for hot reload feature. diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index f2675ce91..d5ca85d6a 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -8,9 +8,12 @@ package intlog import ( + "bytes" + "context" "fmt" "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/internal/utils" + "go.opentelemetry.io/otel/trace" "path/filepath" "time" ) @@ -39,42 +42,56 @@ func SetEnabled(enabled bool) { // Print prints `v` with newline using fmt.Println. // The parameter `v` can be multiple variables. -func Print(v ...interface{}) { - if !isGFDebug { - return - } - fmt.Println(append([]interface{}{now(), "[INTE]", file()}, v...)...) +func Print(ctx context.Context, v ...interface{}) { + doPrint(ctx, fmt.Sprint(v...), false) } // Printf prints `v` with format `format` using fmt.Printf. // The parameter `v` can be multiple variables. -func Printf(format string, v ...interface{}) { - if !isGFDebug { - return - } - fmt.Printf(now()+" [INTE] "+file()+" "+format+"\n", v...) +func Printf(ctx context.Context, format string, v ...interface{}) { + doPrint(ctx, fmt.Sprintf(format, v...), false) } // Error prints `v` with newline using fmt.Println. // The parameter `v` can be multiple variables. -func Error(v ...interface{}) { - if !isGFDebug { - return - } - array := append([]interface{}{now(), "[INTE]", file()}, v...) - array = append(array, "\n"+gdebug.StackWithFilter(stackFilterKey)) - fmt.Println(array...) +func Error(ctx context.Context, v ...interface{}) { + doPrint(ctx, fmt.Sprint(v...), true) } // Errorf prints `v` with format `format` using fmt.Printf. -func Errorf(format string, v ...interface{}) { +func Errorf(ctx context.Context, format string, v ...interface{}) { + doPrint(ctx, fmt.Sprintf(format, v...), true) +} + +func doPrint(ctx context.Context, content string, stack bool) { if !isGFDebug { return } - fmt.Printf( - now()+" [INTE] "+file()+" "+format+"\n%s\n", - append(v, gdebug.StackWithFilter(stackFilterKey))..., - ) + buffer := bytes.NewBuffer(nil) + buffer.WriteString(now()) + buffer.WriteString(" [INTE] ") + buffer.WriteString(file()) + if s := traceIdStr(ctx); s != "" { + buffer.WriteString(" " + s) + } + buffer.WriteString(content) + buffer.WriteString("\n") + if stack { + buffer.WriteString(gdebug.StackWithFilter(stackFilterKey)) + } + fmt.Print(buffer.String()) +} + +// traceIdStr retrieves and returns the trace id string for logging output. +func traceIdStr(ctx context.Context) string { + if ctx == nil { + return "" + } + spanCtx := trace.SpanContextFromContext(ctx) + if traceId := spanCtx.TraceID(); traceId.IsValid() { + return "{" + traceId.String() + "}" + } + return "" } // now returns current time string. diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index a07e115b5..fa86d02c5 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -73,7 +73,10 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { EnterTime: gtime.TimestampMilli(), } request.Cookie = GetCookie(request) - request.Session = s.sessionManager.New(request.GetSessionId()) + request.Session = s.sessionManager.New( + r.Context(), + request.GetSessionId(), + ) request.Response.Request = request request.Middleware = &middleware{ request: request, @@ -84,7 +87,7 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { address = request.RemoteAddr header = fmt.Sprintf("%v", request.Header) ) - intlog.Print(address, header) + intlog.Print(r.Context(), address, header) return guid.S([]byte(address), []byte(header)) }) if err != nil { diff --git a/net/ghttp/ghttp_request_param_file.go b/net/ghttp/ghttp_request_param_file.go index c5b803221..47811dfde 100644 --- a/net/ghttp/ghttp_request_param_file.go +++ b/net/ghttp/ghttp_request_param_file.go @@ -7,7 +7,9 @@ package ghttp import ( + "context" "errors" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/os/gtime" @@ -21,6 +23,7 @@ import ( // UploadFile wraps the multipart uploading file with more and convenient features. type UploadFile struct { *multipart.FileHeader + ctx context.Context } // UploadFiles is array type for *UploadFile. @@ -40,7 +43,7 @@ func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename stri return } } else if !gfile.IsDir(dirPath) { - return "", errors.New(`parameter "dirPath" should be a directory path`) + return "", gerror.New(`parameter "dirPath" should be a directory path`) } file, err := f.Open() @@ -60,7 +63,7 @@ func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename stri return "", err } defer newFile.Close() - intlog.Printf(`save upload file: %s`, filePath) + intlog.Printf(f.ctx, `save upload file: %s`, filePath) if _, err := io.Copy(newFile, file); err != nil { return "", err } @@ -114,6 +117,7 @@ func (r *Request) GetUploadFiles(name string) UploadFiles { uploadFiles := make(UploadFiles, len(multipartFiles)) for k, v := range multipartFiles { uploadFiles[k] = &UploadFile{ + ctx: r.Context(), FileHeader: v, } } diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 2aa6b18cc..ab7738e38 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -8,6 +8,7 @@ package ghttp import ( "bytes" + "context" "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" @@ -70,10 +71,10 @@ func serverProcessInit() { // Process message handler. // It's enabled only graceful feature is enabled. if gracefulEnabled { - intlog.Printf("%d: graceful reload feature is enabled", gproc.Pid()) + intlog.Printf(context.TODO(), "%d: graceful reload feature is enabled", gproc.Pid()) go handleProcessMessage() } else { - intlog.Printf("%d: graceful reload feature is disabled", gproc.Pid()) + intlog.Printf(context.TODO(), "%d: graceful reload feature is disabled", gproc.Pid()) } // It's an ugly calling for better initializing the main package path @@ -195,7 +196,7 @@ func (s *Server) Start() error { if gproc.IsChild() { gtimer.SetTimeout(time.Duration(s.config.GracefulTimeout)*time.Second, func() { if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil { - intlog.Error("server error in process communication:", err) + intlog.Error(context.TODO(), "server error in process communication:", err) } }) } @@ -315,9 +316,9 @@ func (s *Server) Run() { // Remove plugins. if len(s.plugins) > 0 { for _, p := range s.plugins { - intlog.Printf(`remove plugin: %s`, p.Name()) + intlog.Printf(context.TODO(), `remove plugin: %s`, p.Name()) if err := p.Remove(); err != nil { - intlog.Errorf("%+v", err) + intlog.Errorf(context.TODO(), "%+v", err) } } } @@ -333,7 +334,7 @@ func Wait() { s := v.(*Server) if len(s.plugins) > 0 { for _, p := range s.plugins { - intlog.Printf(`remove plugin: %s`, p.Name()) + intlog.Printf(context.TODO(), `remove plugin: %s`, p.Name()) p.Remove() } } diff --git a/net/ghttp/ghttp_server_admin_process.go b/net/ghttp/ghttp_server_admin_process.go index d8a36a831..6cb27d05f 100644 --- a/net/ghttp/ghttp_server_admin_process.go +++ b/net/ghttp/ghttp_server_admin_process.go @@ -8,6 +8,7 @@ package ghttp import ( "bytes" + "context" "errors" "fmt" "github.com/gogf/gf/internal/intlog" @@ -266,10 +267,10 @@ func handleProcessMessage() { for { if msg := gproc.Receive(adminGProcCommGroup); msg != nil { if bytes.EqualFold(msg.Data, []byte("exit")) { - intlog.Printf("%d: process message: exit", gproc.Pid()) + intlog.Printf(context.TODO(), "%d: process message: exit", gproc.Pid()) shutdownWebServersGracefully() allDoneChan <- struct{}{} - intlog.Printf("%d: process message: exit done", gproc.Pid()) + intlog.Printf(context.TODO(), "%d: process message: exit done", gproc.Pid()) return } } diff --git a/net/ghttp/ghttp_server_admin_unix.go b/net/ghttp/ghttp_server_admin_unix.go index b4f0cf6b7..3e5591473 100644 --- a/net/ghttp/ghttp_server_admin_unix.go +++ b/net/ghttp/ghttp_server_admin_unix.go @@ -9,6 +9,7 @@ package ghttp import ( + "context" "github.com/gogf/gf/internal/intlog" "os" "os/signal" @@ -33,7 +34,7 @@ func handleProcessSignal() { ) for { sig = <-procSignalChan - intlog.Printf(`signal received: %s`, sig.String()) + intlog.Printf(context.TODO(), `signal received: %s`, sig.String()) switch sig { // Shutdown the servers. case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGABRT: @@ -49,7 +50,7 @@ func handleProcessSignal() { // Restart the servers. case syscall.SIGUSR1: if err := restartWebServers(sig.String()); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } return diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 0d4888e45..5021c26d8 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -7,6 +7,7 @@ package ghttp import ( + "context" "crypto/tls" "fmt" "github.com/gogf/gf/internal/intlog" @@ -333,11 +334,11 @@ func (s *Server) SetConfig(c ServerConfig) error { } } if err := s.config.Logger.SetLevelStr(s.config.LogLevel); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } SetGraceful(c.Graceful) - intlog.Printf("SetConfig: %+v", s.config) + intlog.Printf(context.TODO(), "SetConfig: %+v", s.config) return nil } diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index cf9fcfb0a..64bdf0ee1 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -83,12 +83,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { // to release the file descriptor in time. err := request.Request.Body.Close() if err != nil { - intlog.Error(err) + intlog.Error(request.Context(), err) } if request.Request.Response != nil { err = request.Request.Response.Body.Close() if err != nil { - intlog.Error(err) + intlog.Error(request.Context(), err) } } }() diff --git a/net/ghttp/internal/client/client_request.go b/net/ghttp/internal/client/client_request.go index 545e8e7b9..2da8b6043 100644 --- a/net/ghttp/internal/client/client_request.go +++ b/net/ghttp/internal/client/client_request.go @@ -195,12 +195,12 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h if f, err := os.Open(path); err == nil { if _, err = io.Copy(file, f); err != nil { if err := f.Close(); err != nil { - intlog.Errorf(`%+v`, err) + intlog.Errorf(c.ctx, `%+v`, err) } return nil, err } if err := f.Close(); err != nil { - intlog.Errorf(`%+v`, err) + intlog.Errorf(c.ctx, `%+v`, err) } } else { return nil, err @@ -303,7 +303,7 @@ func (c *Client) callRequest(req *http.Request) (resp *Response, err error) { // The response might not be nil when err != nil. if resp.Response != nil { if err := resp.Response.Body.Close(); err != nil { - intlog.Errorf(`%+v`, err) + intlog.Errorf(c.ctx, `%+v`, err) } } if c.retryCount > 0 { diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index b872cbf78..ec8bf5153 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -8,6 +8,7 @@ package gcfg import ( + "context" "github.com/gogf/gf/container/garray" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/internal/intlog" @@ -82,7 +83,7 @@ func RemoveContent(file ...string) { } }) - intlog.Printf(`RemoveContent: %s`, name) + intlog.Printf(context.TODO(), `RemoveContent: %s`, name) } // ClearContent removes all global configuration contents. @@ -95,7 +96,7 @@ func ClearContent() { } }) - intlog.Print(`RemoveConfig`) + intlog.Print(context.TODO(), `RemoveConfig`) } // errorPrint checks whether printing error to stdout. diff --git a/os/gcfg/gcfg_config.go b/os/gcfg/gcfg_config.go index a76a36e41..d21f91978 100644 --- a/os/gcfg/gcfg_config.go +++ b/os/gcfg/gcfg_config.go @@ -8,6 +8,7 @@ package gcfg import ( "bytes" + "context" "errors" "fmt" "github.com/gogf/gf/container/garray" @@ -54,20 +55,20 @@ func New(file ...string) *Config { } else { // Dir path of working dir. if err := c.AddPath(gfile.Pwd()); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } // Dir path of main package. if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) { if err := c.AddPath(mainPath); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } } // Dir path of binary. if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { if err := c.AddPath(selfPath); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } } } @@ -163,7 +164,7 @@ func (c *Config) SetPath(path string) error { c.jsonMap.Clear() c.searchPaths.Clear() c.searchPaths.Append(realPath) - intlog.Print("SetPath:", realPath) + intlog.Print(context.TODO(), "SetPath:", realPath) return nil } @@ -237,7 +238,7 @@ func (c *Config) AddPath(path string) error { return nil } c.searchPaths.Append(realPath) - intlog.Print("AddPath:", realPath) + intlog.Print(context.TODO(), "AddPath:", realPath) return nil } diff --git a/os/gfsnotify/gfsnotify.go b/os/gfsnotify/gfsnotify.go index 619454189..670370b33 100644 --- a/os/gfsnotify/gfsnotify.go +++ b/os/gfsnotify/gfsnotify.go @@ -8,6 +8,7 @@ package gfsnotify import ( + "context" "errors" "fmt" "github.com/gogf/gf/container/gset" @@ -88,7 +89,7 @@ func New() (*Watcher, error) { if watcher, err := fsnotify.NewWatcher(); err == nil { w.watcher = watcher } else { - intlog.Printf("New watcher failed: %v", err) + intlog.Printf(context.TODO(), "New watcher failed: %v", err) return nil, err } w.watchLoop() diff --git a/os/gfsnotify/gfsnotify_watcher.go b/os/gfsnotify/gfsnotify_watcher.go index 37067a988..ab0f92eec 100644 --- a/os/gfsnotify/gfsnotify_watcher.go +++ b/os/gfsnotify/gfsnotify_watcher.go @@ -7,6 +7,7 @@ package gfsnotify import ( + "context" "errors" "fmt" "github.com/gogf/gf/internal/intlog" @@ -45,9 +46,9 @@ func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), re for _, subPath := range fileAllDirs(path) { if fileIsDir(subPath) { if err := w.watcher.Add(subPath); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } else { - intlog.Printf("watcher adds monitor for: %s", subPath) + intlog.Printf(context.TODO(), "watcher adds monitor for: %s", subPath) } } } @@ -93,9 +94,9 @@ func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event }) // Add the path to underlying monitor. if err := w.watcher.Add(path); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } else { - intlog.Printf("watcher adds monitor for: %s", path) + intlog.Printf(context.TODO(), "watcher adds monitor for: %s", path) } // Add the callback to global callback map. callbackIdMap.Set(callback.Id, callback) @@ -108,7 +109,7 @@ func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event func (w *Watcher) Close() { w.events.Close() if err := w.watcher.Close(); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } close(w.closeChan) } @@ -131,7 +132,7 @@ func (w *Watcher) Remove(path string) error { for _, subPath := range subPaths { if w.checkPathCanBeRemoved(subPath) { if err := w.watcher.Remove(subPath); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } } } diff --git a/os/gfsnotify/gfsnotify_watcher_loop.go b/os/gfsnotify/gfsnotify_watcher_loop.go index 531225051..2618239ce 100644 --- a/os/gfsnotify/gfsnotify_watcher_loop.go +++ b/os/gfsnotify/gfsnotify_watcher_loop.go @@ -7,6 +7,7 @@ package gfsnotify import ( + "context" "github.com/gogf/gf/container/glist" "github.com/gogf/gf/internal/intlog" ) @@ -34,7 +35,7 @@ func (w *Watcher) watchLoop() { }, repeatEventFilterDuration) case err := <-w.watcher.Errors: - intlog.Error(err) + intlog.Error(context.TODO(), err) } } }() @@ -60,9 +61,9 @@ func (w *Watcher) eventLoop() { // It adds the path back to monitor. // We need no worry about the repeat adding. if err := w.watcher.Add(event.Path); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } else { - intlog.Printf("fake remove event, watcher re-adds monitor for: %s", event.Path) + intlog.Printf(context.TODO(), "fake remove event, watcher re-adds monitor for: %s", event.Path) } // Change the event to RENAME, which means it renames itself to its origin name. event.Op = RENAME @@ -76,9 +77,9 @@ func (w *Watcher) eventLoop() { // It might lost the monitoring for the path, so we add the path back to monitor. // We need no worry about the repeat adding. if err := w.watcher.Add(event.Path); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } else { - intlog.Printf("fake rename event, watcher re-adds monitor for: %s", event.Path) + intlog.Printf(context.TODO(), "fake rename event, watcher re-adds monitor for: %s", event.Path) } // Change the event to CHMOD. event.Op = CHMOD @@ -94,18 +95,18 @@ func (w *Watcher) eventLoop() { for _, subPath := range fileAllDirs(event.Path) { if fileIsDir(subPath) { if err := w.watcher.Add(subPath); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } else { - intlog.Printf("folder creation event, watcher adds monitor for: %s", subPath) + intlog.Printf(context.TODO(), "folder creation event, watcher adds monitor for: %s", subPath) } } } } else { // If it's a file, it directly adds it to monitor. if err := w.watcher.Add(event.Path); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } else { - intlog.Printf("file creation event, watcher adds monitor for: %s", event.Path) + intlog.Printf(context.TODO(), "file creation event, watcher adds monitor for: %s", event.Path) } } diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 397a779bc..0f5552554 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -38,11 +38,12 @@ type Logger struct { } const ( - defaultFileFormat = `{Y-m-d}.log` - defaultFileFlags = os.O_CREATE | os.O_WRONLY | os.O_APPEND - defaultFilePerm = os.FileMode(0666) - defaultFileExpire = time.Minute - pathFilterKey = "/os/glog/glog" + defaultFileFormat = `{Y-m-d}.log` + defaultFileFlags = os.O_CREATE | os.O_WRONLY | os.O_APPEND + defaultFilePerm = os.FileMode(0666) + defaultFileExpire = time.Minute + pathFilterKey = "/os/glog/glog" + memoryLockPrefixForPrintingToFile = "glog.printToFile:" ) const ( @@ -106,7 +107,7 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { if p.config.RotateSize > 0 || p.config.RotateExpire > 0 { if !p.init.Val() && p.init.Cas(false, true) { gtimer.AddOnce(p.config.RotateCheckInterval, p.rotateChecksTimely) - intlog.Printf("logger rotation initialized: every %s", p.config.RotateCheckInterval.String()) + intlog.Printf(ctx, "logger rotation initialized: every %s", p.config.RotateCheckInterval.String()) } } @@ -168,7 +169,7 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { // Tracing values. spanCtx := trace.SpanContextFromContext(ctx) if traceId := spanCtx.TraceID(); traceId.IsValid() { - input.CtxStr = "{TraceID:" + traceId.String() + "}" + input.CtxStr = "{" + traceId.String() + "}" } // Context values. if len(l.config.CtxKeys) > 0 { @@ -210,7 +211,7 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { input.Next() }) if err != nil { - intlog.Error(err) + intlog.Error(ctx, err) } } else { input.Next() @@ -223,27 +224,26 @@ func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { if l.config.Writer == nil { // Output content to disk file. if l.config.Path != "" { - l.printToFile(input.Time, buffer) + l.printToFile(ctx, input.Time, buffer) } // Allow output to stdout? if l.config.StdoutPrint { if _, err := os.Stdout.Write(buffer.Bytes()); err != nil { - intlog.Error(err) + intlog.Error(ctx, err) } } } else { if _, err := l.config.Writer.Write(buffer.Bytes()); err != nil { - // panic(err) - intlog.Error(err) + intlog.Error(ctx, err) } } } // printToFile outputs logging content to disk file. -func (l *Logger) printToFile(t time.Time, buffer *bytes.Buffer) { +func (l *Logger) printToFile(ctx context.Context, t time.Time, buffer *bytes.Buffer) { var ( logFilePath = l.getFilePath(t) - memoryLockKey = "glog.printToFile:" + logFilePath + memoryLockKey = memoryLockPrefixForPrintingToFile + logFilePath ) gmlock.Lock(memoryLockKey) defer gmlock.Unlock(memoryLockKey) @@ -255,20 +255,20 @@ func (l *Logger) printToFile(t time.Time, buffer *bytes.Buffer) { } } // Logging content outputting to disk file. - if file := l.getFilePointer(logFilePath); file == nil { - intlog.Errorf(`got nil file pointer for: %s`, logFilePath) + if file := l.getFilePointer(ctx, logFilePath); file == nil { + intlog.Errorf(ctx, `got nil file pointer for: %s`, logFilePath) } else { if _, err := file.Write(buffer.Bytes()); err != nil { - intlog.Error(err) + intlog.Error(ctx, err) } if err := file.Close(); err != nil { - intlog.Error(err) + intlog.Error(ctx, err) } } } // getFilePointer retrieves and returns a file pointer from file pool. -func (l *Logger) getFilePointer(path string) *gfpool.File { +func (l *Logger) getFilePointer(ctx context.Context, path string) *gfpool.File { file, err := gfpool.Open( path, defaultFileFlags, @@ -277,7 +277,7 @@ func (l *Logger) getFilePointer(path string) *gfpool.File { ) if err != nil { // panic(err) - intlog.Error(err) + intlog.Error(ctx, err) } return file } diff --git a/os/glog/glog_logger_chaining.go b/os/glog/glog_logger_chaining.go index 3a5080b62..c0213b982 100644 --- a/os/glog/glog_logger_chaining.go +++ b/os/glog/glog_logger_chaining.go @@ -60,7 +60,7 @@ func (l *Logger) Path(path string) *Logger { if path != "" { if err := logger.SetPath(path); err != nil { // panic(err) - intlog.Error(err) + intlog.Error(l.getCtx(), err) } } return logger @@ -79,7 +79,7 @@ func (l *Logger) Cat(category string) *Logger { if logger.config.Path != "" { if err := logger.SetPath(gfile.Join(logger.config.Path, category)); err != nil { // panic(err) - intlog.Error(err) + intlog.Error(l.getCtx(), err) } } return logger @@ -122,7 +122,7 @@ func (l *Logger) LevelStr(levelStr string) *Logger { } if err := logger.SetLevelStr(levelStr); err != nil { // panic(err) - intlog.Error(err) + intlog.Error(l.getCtx(), err) } return logger } diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 709a7976c..ac295c1c5 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -71,11 +71,11 @@ func (l *Logger) SetConfig(config Config) error { // Necessary validation. if config.Path != "" { if err := l.SetPath(config.Path); err != nil { - intlog.Error(err) + intlog.Error(l.ctx, err) return err } } - intlog.Printf("SetConfig: %+v", l.config) + intlog.Printf(l.ctx, "SetConfig: %+v", l.config) return nil } diff --git a/os/glog/glog_logger_rotate.go b/os/glog/glog_logger_rotate.go index 8471e55f2..697793854 100644 --- a/os/glog/glog_logger_rotate.go +++ b/os/glog/glog_logger_rotate.go @@ -31,7 +31,7 @@ func (l *Logger) rotateFileBySize(now time.Time) { } if err := l.doRotateFile(l.getFilePath(now)); err != nil { // panic(err) - intlog.Error(err) + intlog.Error(l.ctx, err) } } @@ -48,7 +48,11 @@ func (l *Logger) doRotateFile(filePath string) error { if err := gfile.Remove(filePath); err != nil { return err } - intlog.Printf(`%d size exceeds, no backups set, remove original logging file: %s`, l.config.RotateSize, filePath) + intlog.Printf( + l.ctx, + `%d size exceeds, no backups set, remove original logging file: %s`, + l.config.RotateSize, filePath, + ) return nil } // Else it creates new backup files. @@ -83,7 +87,7 @@ func (l *Logger) doRotateFile(filePath string) error { if !gfile.Exists(newFilePath) { break } else { - intlog.Printf(`rotation file exists, continue: %s`, newFilePath) + intlog.Printf(l.ctx, `rotation file exists, continue: %s`, newFilePath) } } if err := gfile.Rename(filePath, newFilePath); err != nil { @@ -99,6 +103,7 @@ func (l *Logger) rotateChecksTimely() { // Checks whether file rotation not enabled. if l.config.RotateSize <= 0 && l.config.RotateExpire == 0 { intlog.Printf( + l.ctx, "logging rotation ignore checks: RotateSize: %d, RotateExpire: %s", l.config.RotateSize, l.config.RotateExpire.String(), ) @@ -118,9 +123,9 @@ func (l *Logger) rotateChecksTimely() { files, err = gfile.ScanDirFile(l.config.Path, pattern, true) ) if err != nil { - intlog.Error(err) + intlog.Error(l.ctx, err) } - intlog.Printf("logging rotation start checks: %+v", files) + intlog.Printf(l.ctx, "logging rotation start checks: %+v", files) // ============================================================= // Rotation of expired file checks. // ============================================================= @@ -139,11 +144,12 @@ func (l *Logger) rotateChecksTimely() { if subDuration > l.config.RotateExpire { expireRotated = true intlog.Printf( + l.ctx, `%v - %v = %v > %v, rotation expire logging file: %s`, now, mtime, subDuration, l.config.RotateExpire, file, ) if err := l.doRotateFile(file); err != nil { - intlog.Error(err) + intlog.Error(l.ctx, err) } } } @@ -151,7 +157,7 @@ func (l *Logger) rotateChecksTimely() { // Update the files array. files, err = gfile.ScanDirFile(l.config.Path, pattern, true) if err != nil { - intlog.Error(err) + intlog.Error(l.ctx, err) } } } @@ -176,19 +182,19 @@ func (l *Logger) rotateChecksTimely() { needCompressFileArray.Iterator(func(_ int, path string) bool { err := gcompress.GzipFile(path, path+".gz") if err == nil { - intlog.Printf(`compressed done, remove original logging file: %s`, path) + intlog.Printf(l.ctx, `compressed done, remove original logging file: %s`, path) if err = gfile.Remove(path); err != nil { - intlog.Print(err) + intlog.Print(l.ctx, err) } } else { - intlog.Print(err) + intlog.Print(l.ctx, err) } return true }) // Update the files array. files, err = gfile.ScanDirFile(l.config.Path, pattern, true) if err != nil { - intlog.Error(err) + intlog.Error(l.ctx, err) } } } @@ -223,14 +229,14 @@ func (l *Logger) rotateChecksTimely() { backupFilesMap[originalLoggingFilePath].Add(file) } } - intlog.Printf(`calculated backup files map: %+v`, backupFilesMap) + intlog.Printf(l.ctx, `calculated backup files map: %+v`, backupFilesMap) for _, array := range backupFilesMap { diff := array.Len() - l.config.RotateBackupLimit for i := 0; i < diff; i++ { path, _ := array.PopLeft() - intlog.Printf(`remove exceeded backup limit file: %s`, path) + intlog.Printf(l.ctx, `remove exceeded backup limit file: %s`, path) if err := gfile.Remove(path.(string)); err != nil { - intlog.Error(err) + intlog.Error(l.ctx, err) } } } @@ -247,11 +253,12 @@ func (l *Logger) rotateChecksTimely() { subDuration = now.Sub(mtime) if subDuration > l.config.RotateBackupExpire { intlog.Printf( + l.ctx, `%v - %v = %v > %v, remove expired backup file: %s`, now, mtime, subDuration, l.config.RotateBackupExpire, path, ) if err := gfile.Remove(path); err != nil { - intlog.Error(err) + intlog.Error(l.ctx, err) } return true } else { diff --git a/os/gproc/gproc_process.go b/os/gproc/gproc_process.go index 74bf04344..eb386345e 100644 --- a/os/gproc/gproc_process.go +++ b/os/gproc/gproc_process.go @@ -7,6 +7,7 @@ package gproc import ( + "context" "errors" "fmt" "github.com/gogf/gf/internal/intlog" @@ -118,12 +119,11 @@ func (p *Process) Kill() error { } if runtime.GOOS != "windows" { if err = p.Process.Release(); err != nil { - intlog.Error(err) - //return err + intlog.Error(context.TODO(), err) } } _, err = p.Process.Wait() - intlog.Error(err) + intlog.Error(context.TODO(), err) //return err return nil } else { diff --git a/os/gproc/gproc_signal.go b/os/gproc/gproc_signal.go index 08d059e00..1f742289f 100644 --- a/os/gproc/gproc_signal.go +++ b/os/gproc/gproc_signal.go @@ -7,6 +7,7 @@ package gproc import ( + "context" "github.com/gogf/gf/internal/intlog" "os" "os/signal" @@ -67,7 +68,7 @@ func Listen() { for { wg := sync.WaitGroup{} sig = <-sigChan - intlog.Printf(`signal received: %s`, sig.String()) + intlog.Printf(context.TODO(), `signal received: %s`, sig.String()) if handlers, ok := signalHandlerMap[sig]; ok { for _, handler := range handlers { wg.Add(1) diff --git a/os/gres/gres_func_zip.go b/os/gres/gres_func_zip.go index bc6e0ac50..706bd02bc 100644 --- a/os/gres/gres_func_zip.go +++ b/os/gres/gres_func_zip.go @@ -8,6 +8,7 @@ package gres import ( "archive/zip" + "context" "github.com/gogf/gf/internal/fileinfo" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfile" @@ -70,7 +71,7 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix headerPrefix = strings.Replace(headerPrefix, "//", "/", -1) for _, file := range files { if exclude == file { - intlog.Printf(`exclude file path: %s`, file) + intlog.Printf(context.TODO(), `exclude file path: %s`, file) continue } err = zipFile(file, headerPrefix+gfile.Dir(file[len(path):]), zipWriter) diff --git a/os/gres/gres_resource.go b/os/gres/gres_resource.go index 2ed7b2e46..cb9213b00 100644 --- a/os/gres/gres_resource.go +++ b/os/gres/gres_resource.go @@ -7,6 +7,7 @@ package gres import ( + "context" "fmt" "github.com/gogf/gf/internal/intlog" "os" @@ -42,7 +43,7 @@ func New() *Resource { func (r *Resource) Add(content string, prefix ...string) error { files, err := UnpackContent(content) if err != nil { - intlog.Printf("Add resource files failed: %v", err) + intlog.Printf(context.TODO(), "Add resource files failed: %v", err) return err } namePrefix := "" @@ -53,7 +54,7 @@ func (r *Resource) Add(content string, prefix ...string) error { files[i].resource = r r.tree.Set(namePrefix+files[i].file.Name, files[i]) } - intlog.Printf("Add %d files to resource manager", r.tree.Size()) + intlog.Printf(context.TODO(), "Add %d files to resource manager", r.tree.Size()) return nil } diff --git a/os/gsession/gsession_manager.go b/os/gsession/gsession_manager.go index c52095146..7c69c6fe4 100644 --- a/os/gsession/gsession_manager.go +++ b/os/gsession/gsession_manager.go @@ -7,6 +7,7 @@ package gsession import ( + "context" "github.com/gogf/gf/container/gmap" "time" @@ -41,13 +42,14 @@ func New(ttl time.Duration, storage ...Storage) *Manager { // New creates or fetches the session for given session id. // The parameter is optional, it creates a new one if not it's passed // depending on Storage.New. -func (m *Manager) New(sessionId ...string) *Session { +func (m *Manager) New(ctx context.Context, sessionId ...string) *Session { var id string if len(sessionId) > 0 && sessionId[0] != "" { id = sessionId[0] } return &Session{ id: id, + ctx: ctx, manager: m, } } diff --git a/os/gsession/gsession_session.go b/os/gsession/gsession_session.go index 17e764852..85d4d1995 100644 --- a/os/gsession/gsession_session.go +++ b/os/gsession/gsession_session.go @@ -7,6 +7,7 @@ package gsession import ( + "context" "errors" "github.com/gogf/gf/internal/intlog" "time" @@ -17,10 +18,12 @@ import ( "github.com/gogf/gf/util/gconv" ) -// Session struct for storing single session data, -// which is bound to a single request. +// Session struct for storing single session data, which is bound to a single request. +// The Session struct is the interface with user, but the Storage is the underlying adapter designed interface +// for functionality implements. type Session struct { id string // Session id. + ctx context.Context // Context for current session, note that: one session one context. data *gmap.StrAnyMap // Session data. dirty bool // Used to mark session is modified. start bool // Used to mark session is started. @@ -42,12 +45,12 @@ func (s *Session) init() { // Retrieve memory session data from manager. if r, _ := s.manager.sessionData.Get(s.id); r != nil { s.data = r.(*gmap.StrAnyMap) - intlog.Print("session init data:", s.data) + intlog.Print(s.ctx, "session init data:", s.data) } // Retrieve stored session data from storage. if s.manager.storage != nil { - if s.data, err = s.manager.storage.GetSession(s.id, s.manager.ttl, s.data); err != nil { - intlog.Errorf("session restoring failed for id '%s': %v", s.id, err) + if s.data, err = s.manager.storage.GetSession(s.ctx, s.id, s.manager.ttl, s.data); err != nil { + intlog.Errorf(s.ctx, "session restoring failed for id '%s': %v", s.id, err) } } } @@ -57,7 +60,7 @@ func (s *Session) init() { } // Use default session id creating function of storage. if s.id == "" { - s.id = s.manager.storage.New(s.manager.ttl) + s.id = s.manager.storage.New(s.ctx, s.manager.ttl) } // Use default session id creating function. if s.id == "" { @@ -78,11 +81,11 @@ func (s *Session) Close() { size := s.data.Size() if s.manager.storage != nil { if s.dirty { - if err := s.manager.storage.SetSession(s.id, s.data, s.manager.ttl); err != nil { + if err := s.manager.storage.SetSession(s.ctx, s.id, s.data, s.manager.ttl); err != nil { panic(err) } } else if size > 0 { - if err := s.manager.storage.UpdateTTL(s.id, s.manager.ttl); err != nil { + if err := s.manager.storage.UpdateTTL(s.ctx, s.id, s.manager.ttl); err != nil { panic(err) } } @@ -96,7 +99,7 @@ func (s *Session) Close() { // Set sets key-value pair to this session. func (s *Session) Set(key string, value interface{}) error { s.init() - if err := s.manager.storage.Set(s.id, key, value, s.manager.ttl); err != nil { + if err := s.manager.storage.Set(s.ctx, s.id, key, value, s.manager.ttl); err != nil { if err == ErrorDisabled { s.data.Set(key, value) } else { @@ -116,7 +119,7 @@ func (s *Session) Sets(data map[string]interface{}) error { // SetMap batch sets the session using map. func (s *Session) SetMap(data map[string]interface{}) error { s.init() - if err := s.manager.storage.SetMap(s.id, data, s.manager.ttl); err != nil { + if err := s.manager.storage.SetMap(s.ctx, s.id, data, s.manager.ttl); err != nil { if err == ErrorDisabled { s.data.Sets(data) } else { @@ -134,7 +137,7 @@ func (s *Session) Remove(keys ...string) error { } s.init() for _, key := range keys { - if err := s.manager.storage.Remove(s.id, key); err != nil { + if err := s.manager.storage.Remove(s.ctx, s.id, key); err != nil { if err == ErrorDisabled { s.data.Remove(key) } else { @@ -157,7 +160,7 @@ func (s *Session) RemoveAll() error { return nil } s.init() - if err := s.manager.storage.RemoveAll(s.id); err != nil { + if err := s.manager.storage.RemoveAll(s.ctx, s.id); err != nil { if err == ErrorDisabled { s.data.Clear() } else { @@ -200,7 +203,7 @@ func (s *Session) SetIdFunc(f func(ttl time.Duration) string) error { func (s *Session) Map() map[string]interface{} { if s.id != "" { s.init() - if data := s.manager.storage.GetMap(s.id); data != nil { + if data := s.manager.storage.GetMap(s.ctx, s.id); data != nil { return data } return s.data.Map() @@ -212,7 +215,7 @@ func (s *Session) Map() map[string]interface{} { func (s *Session) Size() int { if s.id != "" { s.init() - if size := s.manager.storage.GetSize(s.id); size >= 0 { + if size := s.manager.storage.GetSize(s.ctx, s.id); size >= 0 { return size } return s.data.Size() @@ -239,7 +242,7 @@ func (s *Session) Get(key string, def ...interface{}) interface{} { return nil } s.init() - if v := s.manager.storage.Get(s.id, key); v != nil { + if v := s.manager.storage.Get(s.ctx, s.id, key); v != nil { return v } if v := s.data.Get(key); v != nil { @@ -363,16 +366,6 @@ func (s *Session) GetStruct(key string, pointer interface{}, mapping ...map[stri return gconv.Struct(s.Get(key), pointer, mapping...) } -// Deprecated, use GetStruct instead. -func (s *Session) GetStructDeep(key string, pointer interface{}, mapping ...map[string]string) error { - return gconv.StructDeep(s.Get(key), pointer, mapping...) -} - func (s *Session) GetStructs(key string, pointer interface{}, mapping ...map[string]string) error { return gconv.Structs(s.Get(key), pointer, mapping...) } - -// Deprecated, use GetStructs instead. -func (s *Session) GetStructsDeep(key string, pointer interface{}, mapping ...map[string]string) error { - return gconv.StructsDeep(s.Get(key), pointer, mapping...) -} diff --git a/os/gsession/gsession_storage.go b/os/gsession/gsession_storage.go index 02b72e322..87ef2a669 100644 --- a/os/gsession/gsession_storage.go +++ b/os/gsession/gsession_storage.go @@ -7,6 +7,7 @@ package gsession import ( + "context" "github.com/gogf/gf/container/gmap" "time" ) @@ -15,31 +16,31 @@ import ( type Storage interface { // New creates a custom session id. // This function can be used for custom session creation. - New(ttl time.Duration) (id string) + New(ctx context.Context, ttl time.Duration) (id string) // Get retrieves and returns session value with given key. // It returns nil if the key does not exist in the session. - Get(id string, key string) interface{} + Get(ctx context.Context, id string, key string) interface{} // GetMap retrieves all key-value pairs as map from storage. - GetMap(id string) map[string]interface{} + GetMap(ctx context.Context, id string) map[string]interface{} // GetSize retrieves and returns the size of key-value pairs from storage. - GetSize(id string) int + GetSize(ctx context.Context, id string) int // Set sets one key-value session pair to the storage. // The parameter specifies the TTL for the session id. - Set(id string, key string, value interface{}, ttl time.Duration) error + Set(ctx context.Context, id string, key string, value interface{}, ttl time.Duration) error // SetMap batch sets key-value session pairs as map to the storage. // The parameter specifies the TTL for the session id. - SetMap(id string, data map[string]interface{}, ttl time.Duration) error + SetMap(ctx context.Context, id string, data map[string]interface{}, ttl time.Duration) error // Remove deletes key with its value from storage. - Remove(id string, key string) error + Remove(ctx context.Context, id string, key string) error // RemoveAll deletes all key-value pairs from storage. - RemoveAll(id string) error + RemoveAll(ctx context.Context, id string) error // GetSession returns the session data as *gmap.StrAnyMap for given session id from storage. // @@ -48,14 +49,14 @@ type Storage interface { // and for some storage it might be nil if memory storage is disabled. // // This function is called ever when session starts. It returns nil if the TTL is exceeded. - GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) + GetSession(ctx context.Context, id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) // SetSession updates the data for specified session id. // This function is called ever after session, which is changed dirty, is closed. // This copy all session data map from memory to storage. - SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error + SetSession(ctx context.Context, id string, data *gmap.StrAnyMap, ttl time.Duration) error // UpdateTTL updates the TTL for specified session id. // This function is called ever after session, which is not dirty, is closed. - UpdateTTL(id string, ttl time.Duration) error + UpdateTTL(ctx context.Context, id string, ttl time.Duration) error } diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index fb0bc7ca6..7bbbaf2ef 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -7,6 +7,7 @@ package gsession import ( + "context" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" @@ -80,8 +81,8 @@ func (s *StorageFile) updateSessionTimely() { if id = s.updatingIdSet.Pop(); id == "" { break } - if err = s.updateSessionTTl(id); err != nil { - intlog.Error(err) + if err = s.updateSessionTTl(context.TODO(), id); err != nil { + intlog.Error(context.TODO(), err) } } } @@ -104,45 +105,45 @@ func (s *StorageFile) sessionFilePath(id string) string { // New creates a session id. // This function can be used for custom session creation. -func (s *StorageFile) New(ttl time.Duration) (id string) { +func (s *StorageFile) New(ctx context.Context, ttl time.Duration) (id string) { return "" } // Get retrieves session value with given key. // It returns nil if the key does not exist in the session. -func (s *StorageFile) Get(id string, key string) interface{} { +func (s *StorageFile) Get(ctx context.Context, id string, key string) interface{} { return nil } // GetMap retrieves all key-value pairs as map from storage. -func (s *StorageFile) GetMap(id string) map[string]interface{} { +func (s *StorageFile) GetMap(ctx context.Context, id string) map[string]interface{} { return nil } // GetSize retrieves the size of key-value pairs from storage. -func (s *StorageFile) GetSize(id string) int { +func (s *StorageFile) GetSize(ctx context.Context, id string) int { return -1 } // Set sets key-value session pair to the storage. // The parameter specifies the TTL for the session id (not for the key-value pair). -func (s *StorageFile) Set(id string, key string, value interface{}, ttl time.Duration) error { +func (s *StorageFile) Set(ctx context.Context, id string, key string, value interface{}, ttl time.Duration) error { return ErrorDisabled } // SetMap batch sets key-value session pairs with map to the storage. // The parameter specifies the TTL for the session id(not for the key-value pair). -func (s *StorageFile) SetMap(id string, data map[string]interface{}, ttl time.Duration) error { +func (s *StorageFile) SetMap(ctx context.Context, id string, data map[string]interface{}, ttl time.Duration) error { return ErrorDisabled } // Remove deletes key with its value from storage. -func (s *StorageFile) Remove(id string, key string) error { +func (s *StorageFile) Remove(ctx context.Context, id string, key string) error { return ErrorDisabled } // RemoveAll deletes all key-value pairs from storage. -func (s *StorageFile) RemoveAll(id string) error { +func (s *StorageFile) RemoveAll(ctx context.Context, id string) error { return ErrorDisabled } @@ -153,11 +154,10 @@ func (s *StorageFile) RemoveAll(id string) error { // and for some storage it might be nil if memory storage is disabled. // // This function is called ever when session starts. -func (s *StorageFile) GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) { +func (s *StorageFile) GetSession(ctx context.Context, id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) { if data != nil { return data, nil } - //intlog.Printf("StorageFile.GetSession: %s, %v", id, ttl) path := s.sessionFilePath(id) content := gfile.GetBytes(path) if len(content) > 8 { @@ -189,8 +189,8 @@ func (s *StorageFile) GetSession(id string, ttl time.Duration, data *gmap.StrAny // SetSession updates the data map for specified session id. // This function is called ever after session, which is changed dirty, is closed. // This copy all session data map from memory to storage. -func (s *StorageFile) SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error { - intlog.Printf("StorageFile.SetSession: %s, %v, %v", id, data, ttl) +func (s *StorageFile) SetSession(ctx context.Context, id string, data *gmap.StrAnyMap, ttl time.Duration) error { + intlog.Printf(ctx, "StorageFile.SetSession: %s, %v, %v", id, data, ttl) path := s.sessionFilePath(id) content, err := json.Marshal(data) if err != nil { @@ -222,8 +222,8 @@ func (s *StorageFile) SetSession(id string, data *gmap.StrAnyMap, ttl time.Durat // UpdateTTL updates the TTL for specified session id. // This function is called ever after session, which is not dirty, is closed. // It just adds the session id to the async handling queue. -func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error { - intlog.Printf("StorageFile.UpdateTTL: %s, %v", id, ttl) +func (s *StorageFile) UpdateTTL(ctx context.Context, id string, ttl time.Duration) error { + intlog.Printf(ctx, "StorageFile.UpdateTTL: %s, %v", id, ttl) if ttl >= DefaultStorageFileLoopInterval { s.updatingIdSet.Add(id) } @@ -231,8 +231,8 @@ func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error { } // updateSessionTTL updates the TTL for specified session id. -func (s *StorageFile) updateSessionTTl(id string) error { - intlog.Printf("StorageFile.updateSession: %s", id) +func (s *StorageFile) updateSessionTTl(ctx context.Context, id string) error { + intlog.Printf(ctx, "StorageFile.updateSession: %s", id) path := s.sessionFilePath(id) file, err := gfile.OpenWithFlag(path, os.O_WRONLY) if err != nil { diff --git a/os/gsession/gsession_storage_memory.go b/os/gsession/gsession_storage_memory.go index 7b41e7ff6..26f217b92 100644 --- a/os/gsession/gsession_storage_memory.go +++ b/os/gsession/gsession_storage_memory.go @@ -7,6 +7,7 @@ package gsession import ( + "context" "github.com/gogf/gf/container/gmap" "time" ) @@ -21,45 +22,45 @@ func NewStorageMemory() *StorageMemory { // New creates a session id. // This function can be used for custom session creation. -func (s *StorageMemory) New(ttl time.Duration) (id string) { +func (s *StorageMemory) New(ctx context.Context, ttl time.Duration) (id string) { return "" } // Get retrieves session value with given key. // It returns nil if the key does not exist in the session. -func (s *StorageMemory) Get(id string, key string) interface{} { +func (s *StorageMemory) Get(ctx context.Context, id string, key string) interface{} { return nil } // GetMap retrieves all key-value pairs as map from storage. -func (s *StorageMemory) GetMap(id string) map[string]interface{} { +func (s *StorageMemory) GetMap(ctx context.Context, id string) map[string]interface{} { return nil } // GetSize retrieves the size of key-value pairs from storage. -func (s *StorageMemory) GetSize(id string) int { +func (s *StorageMemory) GetSize(ctx context.Context, id string) int { return -1 } // Set sets key-value session pair to the storage. // The parameter specifies the TTL for the session id (not for the key-value pair). -func (s *StorageMemory) Set(id string, key string, value interface{}, ttl time.Duration) error { +func (s *StorageMemory) Set(ctx context.Context, id string, key string, value interface{}, ttl time.Duration) error { return ErrorDisabled } // SetMap batch sets key-value session pairs with map to the storage. // The parameter specifies the TTL for the session id(not for the key-value pair). -func (s *StorageMemory) SetMap(id string, data map[string]interface{}, ttl time.Duration) error { +func (s *StorageMemory) SetMap(ctx context.Context, id string, data map[string]interface{}, ttl time.Duration) error { return ErrorDisabled } // Remove deletes key with its value from storage. -func (s *StorageMemory) Remove(id string, key string) error { +func (s *StorageMemory) Remove(ctx context.Context, id string, key string) error { return ErrorDisabled } // RemoveAll deletes all key-value pairs from storage. -func (s *StorageMemory) RemoveAll(id string) error { +func (s *StorageMemory) RemoveAll(ctx context.Context, id string) error { return ErrorDisabled } @@ -70,25 +71,20 @@ func (s *StorageMemory) RemoveAll(id string) error { // and for some storage it might be nil if memory storage is disabled. // // This function is called ever when session starts. -func (s *StorageMemory) GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) { +func (s *StorageMemory) GetSession(ctx context.Context, id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) { return data, nil } // SetSession updates the data map for specified session id. // This function is called ever after session, which is changed dirty, is closed. // This copy all session data map from memory to storage. -func (s *StorageMemory) SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error { +func (s *StorageMemory) SetSession(ctx context.Context, id string, data *gmap.StrAnyMap, ttl time.Duration) error { return nil } // UpdateTTL updates the TTL for specified session id. // This function is called ever after session, which is not dirty, is closed. // It just adds the session id to the async handling queue. -func (s *StorageMemory) UpdateTTL(id string, ttl time.Duration) error { - return nil -} - -// doUpdateTTL updates the TTL for session id. -func (s *StorageMemory) doUpdateTTL(id string) error { +func (s *StorageMemory) UpdateTTL(ctx context.Context, id string, ttl time.Duration) error { return nil } diff --git a/os/gsession/gsession_storage_redis.go b/os/gsession/gsession_storage_redis.go index 8d26e2db6..a8b8016af 100644 --- a/os/gsession/gsession_storage_redis.go +++ b/os/gsession/gsession_storage_redis.go @@ -7,6 +7,7 @@ package gsession import ( + "context" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/database/gredis" "github.com/gogf/gf/internal/intlog" @@ -44,7 +45,7 @@ func NewStorageRedis(redis *gredis.Redis, prefix ...string) *StorageRedis { } // Batch updates the TTL for session ids timely. gtimer.AddSingleton(DefaultStorageRedisLoopInterval, func() { - intlog.Print("StorageRedis.timer start") + intlog.Print(context.TODO(), "StorageRedis.timer start") var ( id string err error @@ -54,57 +55,57 @@ func NewStorageRedis(redis *gredis.Redis, prefix ...string) *StorageRedis { if id, ttlSeconds = s.updatingIdMap.Pop(); id == "" { break } else { - if err = s.doUpdateTTL(id, ttlSeconds); err != nil { - intlog.Error(err) + if err = s.doUpdateTTL(context.TODO(), id, ttlSeconds); err != nil { + intlog.Error(context.TODO(), err) } } } - intlog.Print("StorageRedis.timer end") + intlog.Print(context.TODO(), "StorageRedis.timer end") }) return s } // New creates a session id. // This function can be used for custom session creation. -func (s *StorageRedis) New(ttl time.Duration) (id string) { +func (s *StorageRedis) New(ctx context.Context, ttl time.Duration) (id string) { return "" } // Get retrieves session value with given key. // It returns nil if the key does not exist in the session. -func (s *StorageRedis) Get(id string, key string) interface{} { +func (s *StorageRedis) Get(ctx context.Context, id string, key string) interface{} { return nil } // GetMap retrieves all key-value pairs as map from storage. -func (s *StorageRedis) GetMap(id string) map[string]interface{} { +func (s *StorageRedis) GetMap(ctx context.Context, id string) map[string]interface{} { return nil } // GetSize retrieves the size of key-value pairs from storage. -func (s *StorageRedis) GetSize(id string) int { +func (s *StorageRedis) GetSize(ctx context.Context, id string) int { return -1 } // Set sets key-value session pair to the storage. // The parameter specifies the TTL for the session id (not for the key-value pair). -func (s *StorageRedis) Set(id string, key string, value interface{}, ttl time.Duration) error { +func (s *StorageRedis) Set(ctx context.Context, id string, key string, value interface{}, ttl time.Duration) error { return ErrorDisabled } // SetMap batch sets key-value session pairs with map to the storage. // The parameter specifies the TTL for the session id(not for the key-value pair). -func (s *StorageRedis) SetMap(id string, data map[string]interface{}, ttl time.Duration) error { +func (s *StorageRedis) SetMap(ctx context.Context, id string, data map[string]interface{}, ttl time.Duration) error { return ErrorDisabled } // Remove deletes key with its value from storage. -func (s *StorageRedis) Remove(id string, key string) error { +func (s *StorageRedis) Remove(ctx context.Context, id string, key string) error { return ErrorDisabled } // RemoveAll deletes all key-value pairs from storage. -func (s *StorageRedis) RemoveAll(id string) error { +func (s *StorageRedis) RemoveAll(ctx context.Context, id string) error { return ErrorDisabled } @@ -115,9 +116,9 @@ func (s *StorageRedis) RemoveAll(id string) error { // and for some storage it might be nil if memory storage is disabled. // // This function is called ever when session starts. -func (s *StorageRedis) GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) { - intlog.Printf("StorageRedis.GetSession: %s, %v", id, ttl) - r, err := s.redis.DoVar("GET", s.key(id)) +func (s *StorageRedis) GetSession(ctx context.Context, id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) { + intlog.Printf(ctx, "StorageRedis.GetSession: %s, %v", id, ttl) + r, err := s.redis.Ctx(ctx).DoVar("GET", s.key(id)) if err != nil { return nil, err } @@ -143,21 +144,21 @@ func (s *StorageRedis) GetSession(id string, ttl time.Duration, data *gmap.StrAn // SetSession updates the data map for specified session id. // This function is called ever after session, which is changed dirty, is closed. // This copy all session data map from memory to storage. -func (s *StorageRedis) SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error { - intlog.Printf("StorageRedis.SetSession: %s, %v, %v", id, data, ttl) +func (s *StorageRedis) SetSession(ctx context.Context, id string, data *gmap.StrAnyMap, ttl time.Duration) error { + intlog.Printf(ctx, "StorageRedis.SetSession: %s, %v, %v", id, data, ttl) content, err := json.Marshal(data) if err != nil { return err } - _, err = s.redis.DoVar("SETEX", s.key(id), int64(ttl.Seconds()), content) + _, err = s.redis.Ctx(ctx).DoVar("SETEX", s.key(id), int64(ttl.Seconds()), content) return err } // UpdateTTL updates the TTL for specified session id. // This function is called ever after session, which is not dirty, is closed. // It just adds the session id to the async handling queue. -func (s *StorageRedis) UpdateTTL(id string, ttl time.Duration) error { - intlog.Printf("StorageRedis.UpdateTTL: %s, %v", id, ttl) +func (s *StorageRedis) UpdateTTL(ctx context.Context, id string, ttl time.Duration) error { + intlog.Printf(ctx, "StorageRedis.UpdateTTL: %s, %v", id, ttl) if ttl >= DefaultStorageRedisLoopInterval { s.updatingIdMap.Set(id, int(ttl.Seconds())) } @@ -165,9 +166,9 @@ func (s *StorageRedis) UpdateTTL(id string, ttl time.Duration) error { } // doUpdateTTL updates the TTL for session id. -func (s *StorageRedis) doUpdateTTL(id string, ttlSeconds int) error { - intlog.Printf("StorageRedis.doUpdateTTL: %s, %d", id, ttlSeconds) - _, err := s.redis.DoVar("EXPIRE", s.key(id), ttlSeconds) +func (s *StorageRedis) doUpdateTTL(ctx context.Context, id string, ttlSeconds int) error { + intlog.Printf(ctx, "StorageRedis.doUpdateTTL: %s, %d", id, ttlSeconds) + _, err := s.redis.Ctx(ctx).DoVar("EXPIRE", s.key(id), ttlSeconds) return err } diff --git a/os/gsession/gsession_storage_redis_hashtable.go b/os/gsession/gsession_storage_redis_hashtable.go index 15eb825a3..352e5f8b7 100644 --- a/os/gsession/gsession_storage_redis_hashtable.go +++ b/os/gsession/gsession_storage_redis_hashtable.go @@ -7,6 +7,7 @@ package gsession import ( + "context" "time" "github.com/gogf/gf/container/gmap" @@ -38,14 +39,14 @@ func NewStorageRedisHashTable(redis *gredis.Redis, prefix ...string) *StorageRed // New creates a session id. // This function can be used for custom session creation. -func (s *StorageRedisHashTable) New(ttl time.Duration) (id string) { +func (s *StorageRedisHashTable) New(ctx context.Context, ttl time.Duration) (id string) { return "" } // Get retrieves session value with given key. // It returns nil if the key does not exist in the session. -func (s *StorageRedisHashTable) Get(id string, key string) interface{} { - r, _ := s.redis.Do("HGET", s.key(id), key) +func (s *StorageRedisHashTable) Get(ctx context.Context, id string, key string) interface{} { + r, _ := s.redis.Ctx(ctx).Do("HGET", s.key(id), key) if r != nil { return gconv.String(r) } @@ -53,8 +54,8 @@ func (s *StorageRedisHashTable) Get(id string, key string) interface{} { } // GetMap retrieves all key-value pairs as map from storage. -func (s *StorageRedisHashTable) GetMap(id string) map[string]interface{} { - r, err := s.redis.DoVar("HGETALL", s.key(id)) +func (s *StorageRedisHashTable) GetMap(ctx context.Context, id string) map[string]interface{} { + r, err := s.redis.Ctx(ctx).DoVar("HGETALL", s.key(id)) if err != nil { return nil } @@ -71,21 +72,21 @@ func (s *StorageRedisHashTable) GetMap(id string) map[string]interface{} { } // GetSize retrieves the size of key-value pairs from storage. -func (s *StorageRedisHashTable) GetSize(id string) int { - r, _ := s.redis.DoVar("HLEN", s.key(id)) +func (s *StorageRedisHashTable) GetSize(ctx context.Context, id string) int { + r, _ := s.redis.Ctx(ctx).DoVar("HLEN", s.key(id)) return r.Int() } // Set sets key-value session pair to the storage. // The parameter specifies the TTL for the session id (not for the key-value pair). -func (s *StorageRedisHashTable) Set(id string, key string, value interface{}, ttl time.Duration) error { - _, err := s.redis.Do("HSET", s.key(id), key, value) +func (s *StorageRedisHashTable) Set(ctx context.Context, id string, key string, value interface{}, ttl time.Duration) error { + _, err := s.redis.Ctx(ctx).Do("HSET", s.key(id), key, value) return err } // SetMap batch sets key-value session pairs with map to the storage. // The parameter specifies the TTL for the session id(not for the key-value pair). -func (s *StorageRedisHashTable) SetMap(id string, data map[string]interface{}, ttl time.Duration) error { +func (s *StorageRedisHashTable) SetMap(ctx context.Context, id string, data map[string]interface{}, ttl time.Duration) error { array := make([]interface{}, len(data)*2+1) array[0] = s.key(id) @@ -95,19 +96,19 @@ func (s *StorageRedisHashTable) SetMap(id string, data map[string]interface{}, t array[index+1] = v index += 2 } - _, err := s.redis.Do("HMSET", array...) + _, err := s.redis.Ctx(ctx).Do("HMSET", array...) return err } // Remove deletes key with its value from storage. -func (s *StorageRedisHashTable) Remove(id string, key string) error { - _, err := s.redis.Do("HDEL", s.key(id), key) +func (s *StorageRedisHashTable) Remove(ctx context.Context, id string, key string) error { + _, err := s.redis.Ctx(ctx).Do("HDEL", s.key(id), key) return err } // RemoveAll deletes all key-value pairs from storage. -func (s *StorageRedisHashTable) RemoveAll(id string) error { - _, err := s.redis.Do("DEL", s.key(id)) +func (s *StorageRedisHashTable) RemoveAll(ctx context.Context, id string) error { + _, err := s.redis.Ctx(ctx).Do("DEL", s.key(id)) return err } @@ -118,9 +119,9 @@ func (s *StorageRedisHashTable) RemoveAll(id string) error { // and for some storage it might be nil if memory storage is disabled. // // This function is called ever when session starts. -func (s *StorageRedisHashTable) GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) { - intlog.Printf("StorageRedisHashTable.GetSession: %s, %v", id, ttl) - r, err := s.redis.DoVar("EXISTS", s.key(id)) +func (s *StorageRedisHashTable) GetSession(ctx context.Context, id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) { + intlog.Printf(ctx, "StorageRedisHashTable.GetSession: %s, %v", id, ttl) + r, err := s.redis.Ctx(ctx).DoVar("EXISTS", s.key(id)) if err != nil { return nil, err } @@ -133,17 +134,17 @@ func (s *StorageRedisHashTable) GetSession(id string, ttl time.Duration, data *g // SetSession updates the data map for specified session id. // This function is called ever after session, which is changed dirty, is closed. // This copy all session data map from memory to storage. -func (s *StorageRedisHashTable) SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error { - intlog.Printf("StorageRedisHashTable.SetSession: %s, %v", id, ttl) - _, err := s.redis.Do("EXPIRE", s.key(id), int64(ttl.Seconds())) +func (s *StorageRedisHashTable) SetSession(ctx context.Context, id string, data *gmap.StrAnyMap, ttl time.Duration) error { + intlog.Printf(ctx, "StorageRedisHashTable.SetSession: %s, %v", id, ttl) + _, err := s.redis.Ctx(ctx).Do("EXPIRE", s.key(id), int64(ttl.Seconds())) return err } // UpdateTTL updates the TTL for specified session id. // This function is called ever after session, which is not dirty, is closed. // It just adds the session id to the async handling queue. -func (s *StorageRedisHashTable) UpdateTTL(id string, ttl time.Duration) error { - intlog.Printf("StorageRedisHashTable.UpdateTTL: %s, %v", id, ttl) +func (s *StorageRedisHashTable) UpdateTTL(ctx context.Context, id string, ttl time.Duration) error { + intlog.Printf(ctx, "StorageRedisHashTable.UpdateTTL: %s, %v", id, ttl) _, err := s.redis.Do("EXPIRE", s.key(id), int64(ttl.Seconds())) return err } diff --git a/os/gsession/gsession_unit_storage_file_test.go b/os/gsession/gsession_unit_storage_file_test.go index edd872b06..3a7c7ebde 100644 --- a/os/gsession/gsession_unit_storage_file_test.go +++ b/os/gsession/gsession_unit_storage_file_test.go @@ -7,6 +7,7 @@ package gsession_test import ( + "context" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gsession" "testing" @@ -20,7 +21,7 @@ func Test_StorageFile(t *testing.T) { manager := gsession.New(time.Second, storage) sessionId := "" gtest.C(t, func(t *gtest.T) { - s := manager.New() + s := manager.New(context.TODO()) defer s.Close() s.Set("k1", "v1") s.Set("k2", "v2") @@ -34,7 +35,7 @@ func Test_StorageFile(t *testing.T) { time.Sleep(500 * time.Millisecond) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Get("k1"), "v1") t.Assert(s.Get("k2"), "v2") t.Assert(s.Get("k3"), "v3") @@ -66,7 +67,7 @@ func Test_StorageFile(t *testing.T) { time.Sleep(1000 * time.Millisecond) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Size(), 0) t.Assert(s.Get("k5"), nil) t.Assert(s.Get("k6"), nil) diff --git a/os/gsession/gsession_unit_storage_memory_test.go b/os/gsession/gsession_unit_storage_memory_test.go index 054e9948d..c42413d34 100644 --- a/os/gsession/gsession_unit_storage_memory_test.go +++ b/os/gsession/gsession_unit_storage_memory_test.go @@ -7,6 +7,7 @@ package gsession_test import ( + "context" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gsession" "testing" @@ -20,7 +21,7 @@ func Test_StorageMemory(t *testing.T) { manager := gsession.New(time.Second, storage) sessionId := "" gtest.C(t, func(t *gtest.T) { - s := manager.New() + s := manager.New(context.TODO()) defer s.Close() s.Set("k1", "v1") s.Set("k2", "v2") @@ -34,7 +35,7 @@ func Test_StorageMemory(t *testing.T) { time.Sleep(500 * time.Millisecond) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Get("k1"), "v1") t.Assert(s.Get("k2"), "v2") t.Assert(s.Get("k3"), "v3") @@ -66,7 +67,7 @@ func Test_StorageMemory(t *testing.T) { time.Sleep(1000 * time.Millisecond) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Size(), 0) t.Assert(s.Get("k5"), nil) t.Assert(s.Get("k6"), nil) diff --git a/os/gsession/gsession_unit_storage_redis_hashtable_test.go b/os/gsession/gsession_unit_storage_redis_hashtable_test.go index 6ce58ef38..20d7ff082 100644 --- a/os/gsession/gsession_unit_storage_redis_hashtable_test.go +++ b/os/gsession/gsession_unit_storage_redis_hashtable_test.go @@ -7,6 +7,7 @@ package gsession_test import ( + "context" "github.com/gogf/gf/database/gredis" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gsession" @@ -26,7 +27,7 @@ func Test_StorageRedisHashTable(t *testing.T) { manager := gsession.New(time.Second, storage) sessionId := "" gtest.C(t, func(t *gtest.T) { - s := manager.New() + s := manager.New(context.TODO()) defer s.Close() s.Set("k1", "v1") s.Set("k2", "v2") @@ -38,7 +39,7 @@ func Test_StorageRedisHashTable(t *testing.T) { sessionId = s.Id() }) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Get("k1"), "v1") t.Assert(s.Get("k2"), "v2") t.Assert(s.Get("k3"), "v3") @@ -71,7 +72,7 @@ func Test_StorageRedisHashTable(t *testing.T) { time.Sleep(1500 * time.Millisecond) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Size(), 0) t.Assert(s.Get("k5"), nil) t.Assert(s.Get("k6"), nil) @@ -89,7 +90,7 @@ func Test_StorageRedisHashTablePrefix(t *testing.T) { manager := gsession.New(time.Second, storage) sessionId := "" gtest.C(t, func(t *gtest.T) { - s := manager.New() + s := manager.New(context.TODO()) defer s.Close() s.Set("k1", "v1") s.Set("k2", "v2") @@ -101,7 +102,7 @@ func Test_StorageRedisHashTablePrefix(t *testing.T) { sessionId = s.Id() }) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Get("k1"), "v1") t.Assert(s.Get("k2"), "v2") t.Assert(s.Get("k3"), "v3") @@ -134,7 +135,7 @@ func Test_StorageRedisHashTablePrefix(t *testing.T) { time.Sleep(1500 * time.Millisecond) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Size(), 0) t.Assert(s.Get("k5"), nil) t.Assert(s.Get("k6"), nil) diff --git a/os/gsession/gsession_unit_storage_redis_test.go b/os/gsession/gsession_unit_storage_redis_test.go index f0fd9f3b2..53a5e754b 100644 --- a/os/gsession/gsession_unit_storage_redis_test.go +++ b/os/gsession/gsession_unit_storage_redis_test.go @@ -7,6 +7,7 @@ package gsession_test import ( + "context" "github.com/gogf/gf/database/gredis" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gsession" @@ -24,7 +25,7 @@ func Test_StorageRedis(t *testing.T) { manager := gsession.New(time.Second, storage) sessionId := "" gtest.C(t, func(t *gtest.T) { - s := manager.New() + s := manager.New(context.TODO()) defer s.Close() s.Set("k1", "v1") s.Set("k2", "v2") @@ -38,7 +39,7 @@ func Test_StorageRedis(t *testing.T) { time.Sleep(500 * time.Millisecond) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Get("k1"), "v1") t.Assert(s.Get("k2"), "v2") t.Assert(s.Get("k3"), "v3") @@ -70,7 +71,7 @@ func Test_StorageRedis(t *testing.T) { time.Sleep(1000 * time.Millisecond) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Size(), 0) t.Assert(s.Get("k5"), nil) t.Assert(s.Get("k6"), nil) @@ -86,7 +87,7 @@ func Test_StorageRedisPrefix(t *testing.T) { manager := gsession.New(time.Second, storage) sessionId := "" gtest.C(t, func(t *gtest.T) { - s := manager.New() + s := manager.New(context.TODO()) defer s.Close() s.Set("k1", "v1") s.Set("k2", "v2") @@ -100,7 +101,7 @@ func Test_StorageRedisPrefix(t *testing.T) { time.Sleep(500 * time.Millisecond) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Get("k1"), "v1") t.Assert(s.Get("k2"), "v2") t.Assert(s.Get("k3"), "v3") @@ -132,7 +133,7 @@ func Test_StorageRedisPrefix(t *testing.T) { time.Sleep(1000 * time.Millisecond) gtest.C(t, func(t *gtest.T) { - s := manager.New(sessionId) + s := manager.New(context.TODO(), sessionId) t.Assert(s.Size(), 0) t.Assert(s.Get("k5"), nil) t.Assert(s.Get("k6"), nil) diff --git a/os/gspath/gspath.go b/os/gspath/gspath.go index d17ce2658..ca3c7c290 100644 --- a/os/gspath/gspath.go +++ b/os/gspath/gspath.go @@ -12,6 +12,7 @@ package gspath import ( + "context" "errors" "fmt" "github.com/gogf/gf/internal/intlog" @@ -113,7 +114,7 @@ func (sp *SPath) Set(path string) (realPath string, err error) { sp.removeMonitorByPath(v) } } - intlog.Print("paths clear:", sp.paths) + intlog.Print(context.TODO(), "paths clear:", sp.paths) sp.paths.Clear() if sp.cache != nil { sp.cache.Clear() diff --git a/os/gview/gview.go b/os/gview/gview.go index 26dbd393e..1b072f6e3 100644 --- a/os/gview/gview.go +++ b/os/gview/gview.go @@ -72,14 +72,14 @@ func New(path ...string) *View { } if len(path) > 0 && len(path[0]) > 0 { if err := view.SetPath(path[0]); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } } else { // Customized dir path from env/cmd. if envPath := gcmd.GetOptWithEnv(commandEnvKeyForPath).String(); envPath != "" { if gfile.Exists(envPath) { if err := view.SetPath(envPath); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } } else { if errorPrint() { @@ -89,18 +89,18 @@ func New(path ...string) *View { } else { // Dir path of working dir. if err := view.SetPath(gfile.Pwd()); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } // Dir path of binary. if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { if err := view.AddPath(selfPath); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } } // Dir path of main package. if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) { if err := view.AddPath(mainPath); err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } } } diff --git a/os/gview/gview_config.go b/os/gview/gview_config.go index 508344a0a..4e68da421 100644 --- a/os/gview/gview_config.go +++ b/os/gview/gview_config.go @@ -7,6 +7,7 @@ package gview import ( + "context" "errors" "fmt" "github.com/gogf/gf/i18n/gi18n" @@ -67,7 +68,7 @@ func (view *View) SetConfig(config Config) error { // It's just cache, do not hesitate clearing it. templates.Clear() - intlog.Printf("SetConfig: %+v", view.config) + intlog.Printf(context.TODO(), "SetConfig: %+v", view.config) return nil } diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index f69d7e08b..e7675413a 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -83,7 +83,7 @@ func (view *View) Parse(ctx context.Context, file string, params ...Params) (res templates.Clear() gfsnotify.Exit() }); err != nil { - intlog.Error(err) + intlog.Error(ctx, err) } } return &fileCacheItem{ diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index be6b027ac..eb3480a24 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -46,238 +46,254 @@ var ( StructTagPriority = []string{"gconv", "param", "params", "c", "p", "json"} ) -// Convert converts the variable `any` to the type `t`, the type `t` is specified by string. -// The optional parameter `params` is used for additional necessary parameter for this conversion. -// It supports common types conversion as its conversion based on type name string. -func Convert(any interface{}, t string, params ...interface{}) interface{} { - switch t { +type doConvertInput struct { + FromValue interface{} // Value that is converted from. + ToTypeName string // Target value type name in string. + ReferValue interface{} // Referred value, a value in type `ToTypeName`. + Extra []interface{} // Extra values for implementing the converting. +} + +func doConvert(input doConvertInput) interface{} { + switch input.ToTypeName { case "int": - return Int(any) + return Int(input.FromValue) case "*int": - if _, ok := any.(*int); ok { - return any + if _, ok := input.FromValue.(*int); ok { + return input.FromValue } - v := Int(any) + v := Int(input.FromValue) return &v case "int8": - return Int8(any) + return Int8(input.FromValue) case "*int8": - if _, ok := any.(*int8); ok { - return any + if _, ok := input.FromValue.(*int8); ok { + return input.FromValue } - v := Int8(any) + v := Int8(input.FromValue) return &v case "int16": - return Int16(any) + return Int16(input.FromValue) case "*int16": - if _, ok := any.(*int16); ok { - return any + if _, ok := input.FromValue.(*int16); ok { + return input.FromValue } - v := Int16(any) + v := Int16(input.FromValue) return &v case "int32": - return Int32(any) + return Int32(input.FromValue) case "*int32": - if _, ok := any.(*int32); ok { - return any + if _, ok := input.FromValue.(*int32); ok { + return input.FromValue } - v := Int32(any) + v := Int32(input.FromValue) return &v case "int64": - return Int64(any) + return Int64(input.FromValue) case "*int64": - if _, ok := any.(*int64); ok { - return any + if _, ok := input.FromValue.(*int64); ok { + return input.FromValue } - v := Int64(any) + v := Int64(input.FromValue) return &v case "uint": - return Uint(any) + return Uint(input.FromValue) case "*uint": - if _, ok := any.(*uint); ok { - return any + if _, ok := input.FromValue.(*uint); ok { + return input.FromValue } - v := Uint(any) + v := Uint(input.FromValue) return &v case "uint8": - return Uint8(any) + return Uint8(input.FromValue) case "*uint8": - if _, ok := any.(*uint8); ok { - return any + if _, ok := input.FromValue.(*uint8); ok { + return input.FromValue } - v := Uint8(any) + v := Uint8(input.FromValue) return &v case "uint16": - return Uint16(any) + return Uint16(input.FromValue) case "*uint16": - if _, ok := any.(*uint16); ok { - return any + if _, ok := input.FromValue.(*uint16); ok { + return input.FromValue } - v := Uint16(any) + v := Uint16(input.FromValue) return &v case "uint32": - return Uint32(any) + return Uint32(input.FromValue) case "*uint32": - if _, ok := any.(*uint32); ok { - return any + if _, ok := input.FromValue.(*uint32); ok { + return input.FromValue } - v := Uint32(any) + v := Uint32(input.FromValue) return &v case "uint64": - return Uint64(any) + return Uint64(input.FromValue) case "*uint64": - if _, ok := any.(*uint64); ok { - return any + if _, ok := input.FromValue.(*uint64); ok { + return input.FromValue } - v := Uint64(any) + v := Uint64(input.FromValue) return &v case "float32": - return Float32(any) + return Float32(input.FromValue) case "*float32": - if _, ok := any.(*float32); ok { - return any + if _, ok := input.FromValue.(*float32); ok { + return input.FromValue } - v := Float32(any) + v := Float32(input.FromValue) return &v case "float64": - return Float64(any) + return Float64(input.FromValue) case "*float64": - if _, ok := any.(*float64); ok { - return any + if _, ok := input.FromValue.(*float64); ok { + return input.FromValue } - v := Float64(any) + v := Float64(input.FromValue) return &v case "bool": - return Bool(any) + return Bool(input.FromValue) case "*bool": - if _, ok := any.(*bool); ok { - return any + if _, ok := input.FromValue.(*bool); ok { + return input.FromValue } - v := Bool(any) + v := Bool(input.FromValue) return &v case "string": - return String(any) + return String(input.FromValue) case "*string": - if _, ok := any.(*string); ok { - return any + if _, ok := input.FromValue.(*string); ok { + return input.FromValue } - v := String(any) + v := String(input.FromValue) return &v case "[]byte": - return Bytes(any) + return Bytes(input.FromValue) case "[]int": - return Ints(any) + return Ints(input.FromValue) case "[]int32": - return Int32s(any) + return Int32s(input.FromValue) case "[]int64": - return Int64s(any) + return Int64s(input.FromValue) case "[]uint": - return Uints(any) + return Uints(input.FromValue) case "[]uint32": - return Uint32s(any) + return Uint32s(input.FromValue) case "[]uint64": - return Uint64s(any) + return Uint64s(input.FromValue) case "[]float32": - return Float32s(any) + return Float32s(input.FromValue) case "[]float64": - return Float64s(any) + return Float64s(input.FromValue) case "[]string": - return Strings(any) + return Strings(input.FromValue) case "Time", "time.Time": - if len(params) > 0 { - return Time(any, String(params[0])) + if len(input.Extra) > 0 { + return Time(input.FromValue, String(input.Extra[0])) } - return Time(any) + return Time(input.FromValue) case "*time.Time": var v interface{} - if len(params) > 0 { - v = Time(any, String(params[0])) + if len(input.Extra) > 0 { + v = Time(input.FromValue, String(input.Extra[0])) } else { - if _, ok := any.(*time.Time); ok { - return any + if _, ok := input.FromValue.(*time.Time); ok { + return input.FromValue } - v = Time(any) + v = Time(input.FromValue) } return &v case "GTime", "gtime.Time": - if len(params) > 0 { - if v := GTime(any, String(params[0])); v != nil { + if len(input.Extra) > 0 { + if v := GTime(input.FromValue, String(input.Extra[0])); v != nil { return *v } else { return *gtime.New() } } - if v := GTime(any); v != nil { + if v := GTime(input.FromValue); v != nil { return *v } else { return *gtime.New() } case "*gtime.Time": - if len(params) > 0 { - if v := GTime(any, String(params[0])); v != nil { + if len(input.Extra) > 0 { + if v := GTime(input.FromValue, String(input.Extra[0])); v != nil { return v } else { return gtime.New() } } - if v := GTime(any); v != nil { + if v := GTime(input.FromValue); v != nil { return v } else { return gtime.New() } case "Duration", "time.Duration": - return Duration(any) + return Duration(input.FromValue) case "*time.Duration": - if _, ok := any.(*time.Duration); ok { - return any + if _, ok := input.FromValue.(*time.Duration); ok { + return input.FromValue } - v := Duration(any) + v := Duration(input.FromValue) return &v case "map[string]string": - return MapStrStr(any) + return MapStrStr(input.FromValue) case "map[string]interface{}": - return Map(any) + return Map(input.FromValue) case "[]map[string]interface{}": - return Maps(any) - - //case "gvar.Var": - // // TODO remove reflect usage to create gvar.Var, considering using unsafe pointer - // rv := reflect.New(intstore.ReflectTypeVarImp) - // ri := rv.Interface() - // if v, ok := ri.(apiSet); ok { - // v.Set(any) - // } else if v, ok := ri.(apiUnmarshalValue); ok { - // v.UnmarshalValue(any) - // } else { - // rv.Set(reflect.ValueOf(any)) - // } - // return ri + return Maps(input.FromValue) default: - return any + if input.ReferValue != nil { + var ( + referReflectValue reflect.Value + ) + if v, ok := input.ReferValue.(reflect.Value); ok { + referReflectValue = v + } else { + referReflectValue = reflect.ValueOf(input.ReferValue) + } + input.ToTypeName = referReflectValue.Kind().String() + input.ReferValue = nil + return doConvert(input) + } + return input.FromValue } } +// Convert converts the variable `fromValue` to the type `toTypeName`, the type `toTypeName` is specified by string. +// The optional parameter `extraParams` is used for additional necessary parameter for this conversion. +// It supports common types conversion as its conversion based on type name string. +func Convert(fromValue interface{}, toTypeName string, extraParams ...interface{}) interface{} { + return doConvert(doConvertInput{ + FromValue: fromValue, + ToTypeName: toTypeName, + ReferValue: nil, + Extra: extraParams, + }) +} + // Byte converts `any` to byte. func Byte(any interface{}) byte { if v, ok := any.(byte); ok { diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index 6685433b2..f2878ce56 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -166,7 +166,7 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string] dataMap[String(reflectValue.Index(i).Interface())] = nil } } - case reflect.Map, reflect.Struct: + case reflect.Map, reflect.Struct, reflect.Interface: convertedValue := doMapConvertForMapOrStructValue(true, value, recursive, newTags...) if m, ok := convertedValue.(map[string]interface{}); ok { return m diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 4b481057b..1dd76832a 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -32,6 +32,7 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) switch pointerElemKind { case reflect.Map: return MapToMap(params, pointer, mapping...) + case reflect.Array, reflect.Slice: var ( sliceElem = pointerElem.Elem() diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 09920e1f6..354905ad2 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -107,6 +107,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string var ( paramsReflectValue reflect.Value + paramsInterface interface{} // DO NOT use `params` directly as it might be type of `reflect.Value` pointerReflectValue reflect.Value pointerReflectKind reflect.Kind pointerElemReflectValue reflect.Value // The pointed element. @@ -116,6 +117,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string } else { paramsReflectValue = reflect.ValueOf(params) } + paramsInterface = paramsReflectValue.Interface() if v, ok := pointer.(reflect.Value); ok { pointerReflectValue = v pointerElemReflectValue = v @@ -139,7 +141,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string } // Normal unmarshalling interfaces checks. - if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, params); ok { + if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok { return err } @@ -154,7 +156,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string // return v.UnmarshalValue(params) //} // Note that it's `pointerElemReflectValue` here not `pointerReflectValue`. - if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, params); ok { + if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, paramsInterface); ok { return err } // Retrieve its element, may be struct at last. @@ -163,7 +165,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string // paramsMap is the map[string]interface{} type variable for params. // DO NOT use MapDeep here. - paramsMap := Map(params) + paramsMap := Map(paramsInterface) if paramsMap == nil { return gerror.Newf("convert params to map failed: %v", params) } @@ -304,7 +306,7 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, map return nil } defer func() { - if e := recover(); e != nil { + if exception := recover(); exception != nil { if err = bindVarToReflectValue(structFieldValue, value, mapping, priorityTag); err != nil { err = gerror.Wrapf(err, `error binding value to attribute "%s"`, name) } @@ -314,7 +316,13 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, map if empty.IsNil(value) { structFieldValue.Set(reflect.Zero(structFieldValue.Type())) } else { - structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String()))) + structFieldValue.Set(reflect.ValueOf(doConvert( + doConvertInput{ + FromValue: value, + ToTypeName: structFieldValue.Type().String(), + ReferValue: structFieldValue, + }, + ))) } return nil } @@ -335,9 +343,11 @@ func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value i } pointer = reflectValue.Interface() } + // UnmarshalValue. if v, ok := pointer.(apiUnmarshalValue); ok { return v.UnmarshalValue(value), ok } + // UnmarshalText. if v, ok := pointer.(apiUnmarshalText); ok { if s, ok := value.(string); ok { return v.UnmarshalText([]byte(s)), ok @@ -450,11 +460,12 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma default: defer func() { - if e := recover(); e != nil { + if exception := recover(); exception != nil { err = gerror.New( - fmt.Sprintf(`cannot convert value "%+v" to type "%s"`, + fmt.Sprintf(`cannot convert value "%+v" to type "%s":%+v`, value, structFieldValue.Type().String(), + exception, ), ) } diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index 8d3626d36..d8aafef79 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -100,13 +100,34 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin } } // Converting `params` to map slice. - paramsMaps := Maps(params) + var ( + paramsList []interface{} + paramsRv = reflect.ValueOf(params) + paramsKind = paramsRv.Kind() + ) + for paramsKind == reflect.Ptr { + paramsRv = paramsRv.Elem() + paramsKind = paramsRv.Kind() + } + switch paramsKind { + case reflect.Slice, reflect.Array: + paramsList = make([]interface{}, paramsRv.Len()) + for i := 0; i < paramsRv.Len(); i++ { + paramsList[i] = paramsRv.Index(i) + } + default: + var paramsMaps = Maps(params) + paramsList = make([]interface{}, len(paramsMaps)) + for i := 0; i < len(paramsMaps); i++ { + paramsList[i] = paramsMaps[i] + } + } // If `params` is an empty slice, no conversion. - if len(paramsMaps) == 0 { + if len(paramsList) == 0 { return nil } var ( - reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsMaps), len(paramsMaps)) + reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsList), len(paramsList)) itemType = reflectElemArray.Index(0).Type() itemTypeKind = itemType.Kind() pointerRvElem = pointerRv.Elem() @@ -114,7 +135,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin ) if itemTypeKind == reflect.Ptr { // Pointer element. - for i := 0; i < len(paramsMaps); i++ { + for i := 0; i < len(paramsList); i++ { var tempReflectValue reflect.Value if i < pointerRvLength { // Might be nil. @@ -123,21 +144,21 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin if !tempReflectValue.IsValid() { tempReflectValue = reflect.New(itemType.Elem()).Elem() } - if err = doStruct(paramsMaps[i], tempReflectValue, mapping, priorityTag); err != nil { + if err = doStruct(paramsList[i], tempReflectValue, mapping, priorityTag); err != nil { return err } reflectElemArray.Index(i).Set(tempReflectValue.Addr()) } } else { // Struct element. - for i := 0; i < len(paramsMaps); i++ { + for i := 0; i < len(paramsList); i++ { var tempReflectValue reflect.Value if i < pointerRvLength { tempReflectValue = pointerRvElem.Index(i) } else { tempReflectValue = reflect.New(itemType).Elem() } - if err = doStruct(paramsMaps[i], tempReflectValue, mapping, priorityTag); err != nil { + if err = doStruct(paramsList[i], tempReflectValue, mapping, priorityTag); err != nil { return err } reflectElemArray.Index(i).Set(tempReflectValue) diff --git a/util/gconv/gconv_z_unit_scan_test.go b/util/gconv/gconv_z_unit_scan_test.go index d80448818..541baf7dc 100644 --- a/util/gconv/gconv_z_unit_scan_test.go +++ b/util/gconv/gconv_z_unit_scan_test.go @@ -58,7 +58,7 @@ func Test_Scan_StructStructs(t *testing.T) { } ) err := gconv.Scan(params, &users) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(users, g.Slice{ &User{ Uid: 1, From 8210f40469b70981b8a1ed61827a3d7e5228b741 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 26 Jun 2021 16:46:36 +0800 Subject: [PATCH 347/492] improve Record/Result converting for package gdb --- util/gconv/gconv.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index eb3480a24..77e492fe8 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -53,6 +53,7 @@ type doConvertInput struct { Extra []interface{} // Extra values for implementing the converting. } +// doConvert does common used types converting. func doConvert(input doConvertInput) interface{} { switch input.ToTypeName { case "int": @@ -191,6 +192,8 @@ func doConvert(input doConvertInput) interface{} { return Int64s(input.FromValue) case "[]uint": return Uints(input.FromValue) + case "[]uint8": + return Bytes(input.FromValue) case "[]uint32": return Uint32s(input.FromValue) case "[]uint64": @@ -276,7 +279,7 @@ func doConvert(input doConvertInput) interface{} { } input.ToTypeName = referReflectValue.Kind().String() input.ReferValue = nil - return doConvert(input) + return reflect.ValueOf(doConvert(input)).Convert(referReflectValue.Type()).Interface() } return input.FromValue } From 91ca79b30063eef43d8d78a1a42758f47b77aade Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 26 Jun 2021 16:51:26 +0800 Subject: [PATCH 348/492] add context for intlog --- net/ghttp/ghttp_unit_router_domain_basic_test.go | 6 +++--- os/gbuild/gbuild.go | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/net/ghttp/ghttp_unit_router_domain_basic_test.go b/net/ghttp/ghttp_unit_router_domain_basic_test.go index 5b597cb6f..3db0f35cc 100644 --- a/net/ghttp/ghttp_unit_router_domain_basic_test.go +++ b/net/ghttp/ghttp_unit_router_domain_basic_test.go @@ -335,15 +335,15 @@ func Test_Router_DomainGroup(t *testing.T) { d.Group("/", func(group *ghttp.RouterGroup) { group.Group("/app", func(gApp *ghttp.RouterGroup) { gApp.GET("/{table}/list/{page}.html", func(r *ghttp.Request) { - intlog.Print("/{table}/list/{page}.html") + intlog.Print(r.Context(), "/{table}/list/{page}.html") r.Response.Write(r.Get("table"), "&", r.Get("page")) }) gApp.GET("/order/info/{order_id}", func(r *ghttp.Request) { - intlog.Print("/order/info/{order_id}") + intlog.Print(r.Context(), "/order/info/{order_id}") r.Response.Write(r.Get("order_id")) }) gApp.DELETE("/comment/{id}", func(r *ghttp.Request) { - intlog.Print("/comment/{id}") + intlog.Print(r.Context(), "/comment/{id}") r.Response.Write(r.Get("id")) }) }) diff --git a/os/gbuild/gbuild.go b/os/gbuild/gbuild.go index 5447c0cb7..c44cd57fa 100644 --- a/os/gbuild/gbuild.go +++ b/os/gbuild/gbuild.go @@ -8,6 +8,7 @@ package gbuild import ( + "context" "github.com/gogf/gf" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/encoding/gbase64" @@ -26,13 +27,13 @@ func init() { if builtInVarStr != "" { err := json.UnmarshalUseNumber(gbase64.MustDecodeString(builtInVarStr), &builtInVarMap) if err != nil { - intlog.Error(err) + intlog.Error(context.TODO(), err) } builtInVarMap["gfVersion"] = gf.VERSION builtInVarMap["goVersion"] = runtime.Version() - intlog.Printf("build variables: %+v", builtInVarMap) + intlog.Printf(context.TODO(), "build variables: %+v", builtInVarMap) } else { - intlog.Print("no build variables") + intlog.Print(context.TODO(), "no build variables") } } From 859ea150ed91f285c85a6110f9eadc3e2efc41b0 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 26 Jun 2021 17:00:32 +0800 Subject: [PATCH 349/492] rename constants of package gpage from skake to upper camel case --- net/ghttp/ghttp_request_param_page.go | 12 ++++++------ util/gpage/gpage.go | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/net/ghttp/ghttp_request_param_page.go b/net/ghttp/ghttp_request_param_page.go index 478ae3ed0..20e0c8f55 100644 --- a/net/ghttp/ghttp_request_param_page.go +++ b/net/ghttp/ghttp_request_param_page.go @@ -14,7 +14,7 @@ import ( ) // GetPage creates and returns the pagination object for given and . -// NOTE THAT the page parameter name from client is constantly defined as gpage.PAGE_NAME +// NOTE THAT the page parameter name from client is constantly defined as gpage.DefaultPageName // for simplification and convenience. func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page { // It must has Router object attribute. @@ -27,7 +27,7 @@ func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page { // Check the page variable in the URI. if len(r.Router.RegNames) > 0 { for _, name := range r.Router.RegNames { - if name == gpage.PAGE_NAME { + if name == gpage.DefaultPageName { uriHasPageName = true break } @@ -38,8 +38,8 @@ func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page { urlTemplate = r.Router.Uri for i, name := range r.Router.RegNames { rule := fmt.Sprintf(`[:\*]%s|\{%s\}`, name, name) - if name == gpage.PAGE_NAME { - urlTemplate, _ = gregex.ReplaceString(rule, gpage.PAGE_PLACE_HOLDER, urlTemplate) + if name == gpage.DefaultPageName { + urlTemplate, _ = gregex.ReplaceString(rule, gpage.DefaultPagePlaceHolder, urlTemplate) } else { urlTemplate, _ = gregex.ReplaceString(rule, match[i+1], urlTemplate) } @@ -51,7 +51,7 @@ func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page { // Check the page variable in the query string. if !uriHasPageName { values := url.Query() - values.Set(gpage.PAGE_NAME, gpage.PAGE_PLACE_HOLDER) + values.Set(gpage.DefaultPageName, gpage.DefaultPagePlaceHolder) url.RawQuery = values.Encode() // Replace the encoded "{.page}" to original "{.page}". url.RawQuery = gstr.Replace(url.RawQuery, "%7B.page%7D", "{.page}") @@ -60,5 +60,5 @@ func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page { urlTemplate += "?" + url.RawQuery } - return gpage.New(totalSize, pageSize, r.GetInt(gpage.PAGE_NAME), urlTemplate) + return gpage.New(totalSize, pageSize, r.GetInt(gpage.DefaultPageName), urlTemplate) } diff --git a/util/gpage/gpage.go b/util/gpage/gpage.go index ef9b9e2d3..a0b05ef3c 100644 --- a/util/gpage/gpage.go +++ b/util/gpage/gpage.go @@ -35,8 +35,8 @@ type Page struct { } const ( - PAGE_NAME = "page" // PAGE_NAME defines the default page name. - PAGE_PLACE_HOLDER = "{.page}" // PAGE_PLACE_HOLDER defines the place holder for the url template. + DefaultPageName = "page" // DefaultPageName defines the default page name. + DefaultPagePlaceHolder = "{.page}" // DefaultPagePlaceHolder defines the place holder for the url template. ) // New creates and returns a pagination manager. @@ -206,7 +206,7 @@ func (p *Page) GetContent(mode int) string { // Note that the UrlTemplate attribute can be either an URL or a URI string with "{.page}" // place holder specifying the page number position. func (p *Page) GetUrl(page int) string { - return gstr.Replace(p.UrlTemplate, PAGE_PLACE_HOLDER, gconv.String(page)) + return gstr.Replace(p.UrlTemplate, DefaultPagePlaceHolder, gconv.String(page)) } // GetLink returns the HTML link tag `a` content for given page number. From b9586892645fb9f99cd404abb8c410acf203eba5 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 26 Jun 2021 18:20:55 +0800 Subject: [PATCH 350/492] add CtxStrict feature for package gdb --- database/gdb/gdb.go | 26 +++++++-------- database/gdb/gdb_core.go | 5 +-- database/gdb/gdb_core_config.go | 1 + database/gdb/gdb_core_underlying.go | 41 ++++++++++++++++++++---- database/gdb/gdb_core_utility.go | 8 ----- database/gdb/gdb_driver_mssql.go | 7 +++-- database/gdb/gdb_driver_mysql.go | 4 +-- database/gdb/gdb_driver_oracle.go | 11 +++---- database/gdb/gdb_driver_pgsql.go | 10 ++++-- database/gdb/gdb_driver_sqlite.go | 6 ++-- database/gdb/gdb_statement.go | 8 ++--- database/gdb/gdb_z_driver_test.go | 2 +- database/gdb/gdb_z_init_test.go | 42 +++++++++++++++++++------ database/gdb/gdb_z_mysql_ctx_test.go | 20 ++++++++++++ database/gdb/gdb_z_mysql_method_test.go | 8 ++--- 15 files changed, 133 insertions(+), 66 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index c0ecb2a83..f197e5675 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -70,8 +70,6 @@ type DB interface { // Ctx is a chaining function, which creates and returns a new DB that is a shallow copy // of current DB object and with given context in it. - // Note that this returned DB object can be used only once, so do not assign it to - // a global or package variable for long using. // Also see Core.Ctx. Ctx(ctx context.Context) DB @@ -105,23 +103,21 @@ type DB interface { DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete. DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery. DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec. - DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}) // See Core.DoCommit. + DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) // See Core.DoCommit. DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare. // =========================================================================== // Query APIs for convenience purpose. // =========================================================================== - GetAll(sql string, args ...interface{}) (Result, error) // See Core.GetAll. - GetOne(sql string, args ...interface{}) (Record, error) // See Core.GetOne. - GetValue(sql string, args ...interface{}) (Value, error) // See Core.GetValue. - GetArray(sql string, args ...interface{}) ([]Value, error) // See Core.GetArray. - GetCount(sql string, args ...interface{}) (int, error) // See Core.GetCount. - GetStruct(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetStruct. - GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error // See Core.GetStructs. - GetScan(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan. - Union(unions ...*Model) *Model // See Core.Union. - UnionAll(unions ...*Model) *Model // See Core.UnionAll. + GetAll(sql string, args ...interface{}) (Result, error) // See Core.GetAll. + GetOne(sql string, args ...interface{}) (Record, error) // See Core.GetOne. + GetValue(sql string, args ...interface{}) (Value, error) // See Core.GetValue. + GetArray(sql string, args ...interface{}) ([]Value, error) // See Core.GetArray. + GetCount(sql string, args ...interface{}) (int, error) // See Core.GetCount. + GetScan(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan. + Union(unions ...*Model) *Model // See Core.Union. + UnionAll(unions ...*Model) *Model // See Core.UnionAll. // =========================================================================== // Master/Slave specification support. @@ -173,7 +169,7 @@ type DB interface { GetChars() (charLeft string, charRight string) // See Core.GetChars. Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables. TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields. - FilteredLink() string + FilteredLink() string // FilteredLink is used for filtering sensitive information in `Link` configuration before output it to tracing server. } // Core is the base struct for database management. @@ -270,6 +266,8 @@ const ( ctxTimeoutTypeQuery ctxTimeoutTypePrepare commandEnvKeyForDryRun = "gf.gdb.dryrun" + ctxStrictKeyName = "gf.gdb.CtxStrictEnabled" + ctxStrictErrorStr = "context is required for database operation, did you missing call function Ctx" ) var ( diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index ff518bcdc..ab11e9b66 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -40,6 +40,7 @@ func (c *Core) Ctx(ctx context.Context) DB { if c.ctx != nil { return c.db } + ctx = context.WithValue(ctx, ctxStrictKeyName, 1) // It makes a shallow copy of current db and changes its context for next chaining operation. var ( err error @@ -189,9 +190,9 @@ func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) err k = t.Elem().Kind() switch k { case reflect.Array, reflect.Slice: - return c.db.GetStructs(pointer, sql, args...) + return c.db.GetCore().GetStructs(pointer, sql, args...) case reflect.Struct: - return c.db.GetStruct(pointer, sql, args...) + return c.db.GetCore().GetStruct(pointer, sql, args...) } return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k) } diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index a839ce8ba..0982d6130 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -49,6 +49,7 @@ type ConfigNode struct { UpdatedAt string `json:"updatedAt"` // (Optional) The filed name of table for automatic-filled updated datetime. DeletedAt string `json:"deletedAt"` // (Optional) The filed name of table for automatic-filled updated datetime. TimeMaintainDisabled bool `json:"timeMaintainDisabled"` // (Optional) Disable the automatic time maintaining feature. + CtxStrict bool `json:"ctxStrict"` // (Optional) Strictly require context input for all database operations. } const ( diff --git a/database/gdb/gdb_core_underlying.go b/database/gdb/gdb_core_underlying.go index 848270ac0..ba09b503f 100644 --- a/database/gdb/gdb_core_underlying.go +++ b/database/gdb/gdb_core_underlying.go @@ -10,6 +10,7 @@ package gdb import ( "context" "database/sql" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" ) @@ -33,12 +34,17 @@ func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...inter link = &txLink{tx.tx} } } - // Link execution. - sql, args = formatSql(sql, args) - sql, args = c.db.DoCommit(ctx, link, sql, args) + if c.GetConfig().QueryTimeout > 0 { ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout) } + + // Link execution. + sql, args = formatSql(sql, args) + sql, args, err = c.db.DoCommit(ctx, link, sql, args) + if err != nil { + return nil, err + } mTime1 := gtime.TimestampMilli() rows, err = link.QueryContext(ctx, sql, args...) mTime2 := gtime.TimestampMilli() @@ -85,15 +91,19 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf link = &txLink{tx.tx} } } - // Link execution. - sql, args = formatSql(sql, args) - sql, args = c.db.DoCommit(ctx, link, sql, args) + if c.GetConfig().ExecTimeout > 0 { var cancelFunc context.CancelFunc ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout) defer cancelFunc() } + // Link execution. + sql, args = formatSql(sql, args) + sql, args, err = c.db.DoCommit(ctx, link, sql, args) + if err != nil { + return nil, err + } mTime1 := gtime.TimestampMilli() if !c.db.GetDryRun() { result, err = link.ExecContext(ctx, sql, args...) @@ -120,6 +130,18 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf return result, formatError(err, sql, args...) } +// DoCommit is a hook function, which deals with the sql string before it's committed to underlying driver. +// The parameter `link` specifies the current database connection operation object. You can modify the sql +// string `sql` and its arguments `args` as you wish before they're committed to driver. +func (c *Core) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { + if c.db.GetConfig().CtxStrict { + if v := ctx.Value(ctxStrictKeyName); v == nil { + return sql, args, gerror.New(ctxStrictErrorStr) + } + } + return sql, args, nil +} + // Prepare creates a prepared statement for later queries or executions. // Multiple queries or executions may be run concurrently from the // returned statement. @@ -156,6 +178,13 @@ func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, err // DO NOT USE cancel function in prepare statement. ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout) } + + if c.db.GetConfig().CtxStrict { + if v := ctx.Value(ctxStrictKeyName); v == nil { + return nil, gerror.New(ctxStrictErrorStr) + } + } + var ( mTime1 = gtime.TimestampMilli() stmt, err = link.PrepareContext(ctx, sql) diff --git a/database/gdb/gdb_core_utility.go b/database/gdb/gdb_core_utility.go index 2fe4b1c19..87add6748 100644 --- a/database/gdb/gdb_core_utility.go +++ b/database/gdb/gdb_core_utility.go @@ -63,14 +63,6 @@ func (c *Core) GetChars() (charLeft string, charRight string) { return "", "" } -// DoCommit is a hook function, which deals with the sql string before it's committed to underlying driver. -// The parameter `link` specifies the current database connection operation object. You can modify the sql -// string `sql` and its arguments `args` as you wish before they're committed to driver. -// Also see Core.DoCommit. -func (c *Core) DoCommit(sql string) string { - return sql -} - // Tables retrieves and returns the tables of current schema. // It's mainly used in cli tool chain for automatically generating the models. // diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index 0e91b48d8..24156605c 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -79,7 +79,10 @@ func (d *DriverMssql) GetChars() (charLeft string, charRight string) { } // DoCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverMssql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { +func (d *DriverMssql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { + defer func() { + newSql, newArgs, err = d.Core.DoCommit(ctx, link, newSql, newArgs) + }() var index int // Convert place holder char '?' to string "@px". str, _ := gregex.ReplaceStringFunc("\\?", sql, func(s string) string { @@ -87,7 +90,7 @@ func (d *DriverMssql) DoCommit(ctx context.Context, link Link, sql string, args return fmt.Sprintf("@p%d", index) }) str, _ = gregex.ReplaceString("\"", "", str) - return d.parseSql(str), args + return d.parseSql(str), args, nil } // parseSql does some replacement of the sql before commits it to underlying driver, diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index b03b56839..a9ac18296 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -81,8 +81,8 @@ func (d *DriverMysql) GetChars() (charLeft string, charRight string) { } // DoCommit handles the sql before posts it to database. -func (d *DriverMysql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { - return sql, args +func (d *DriverMysql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { + return d.Core.DoCommit(ctx, link, sql, args) } // Tables retrieves and returns the tables of current schema. diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index 682b5eee9..8c175f228 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -32,11 +32,6 @@ type DriverOracle struct { *Core } -const ( - tableAlias1 = "GFORM1" - tableAlias2 = "GFORM2" -) - // New creates and returns a database object for oracle. // It implements the interface of gdb.Driver for extra database driver installation. func (d *DriverOracle) New(core *Core, node *ConfigNode) (DB, error) { @@ -85,7 +80,11 @@ func (d *DriverOracle) GetChars() (charLeft string, charRight string) { } // DoCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverOracle) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}) { +func (d *DriverOracle) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { + defer func() { + newSql, newArgs, err = d.Core.DoCommit(ctx, link, newSql, newArgs) + }() + var index int // Convert place holder char '?' to string ":vx". newSql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string { diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 28d924e84..6778ed760 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -80,15 +80,19 @@ func (d *DriverPgsql) GetChars() (charLeft string, charRight string) { } // DoCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverPgsql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { +func (d *DriverPgsql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { + defer func() { + newSql, newArgs, err = d.Core.DoCommit(ctx, link, newSql, newArgs) + }() + var index int // Convert place holder char '?' to string "$x". sql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string { index++ return fmt.Sprintf("$%d", index) }) - sql, _ = gregex.ReplaceString(` LIMIT (\d+),\s*(\d+)`, ` LIMIT $2 OFFSET $1`, sql) - return sql, args + newSql, _ = gregex.ReplaceString(` LIMIT (\d+),\s*(\d+)`, ` LIMIT $2 OFFSET $1`, sql) + return newSql, args, nil } // Tables retrieves and returns the tables of current schema. diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index b236a3889..1ad0f68b3 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -67,10 +67,8 @@ func (d *DriverSqlite) GetChars() (charLeft string, charRight string) { } // DoCommit deals with the sql string before commits it to underlying sql driver. -// TODO 需要增加对Save方法的支持,可使用正则来实现替换, -// TODO 将ON DUPLICATE KEY UPDATE触发器修改为两条SQL语句(INSERT OR IGNORE & UPDATE) -func (d *DriverSqlite) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) { - return sql, args +func (d *DriverSqlite) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { + return d.Core.DoCommit(ctx, link, sql, args) } // Tables retrieves and returns the tables of current schema. diff --git a/database/gdb/gdb_statement.go b/database/gdb/gdb_statement.go index 81f14affe..51d8a4b2f 100644 --- a/database/gdb/gdb_statement.go +++ b/database/gdb/gdb_statement.go @@ -37,7 +37,7 @@ const ( ) // doStmtCommit commits statement according to given `stmtType`. -func (s *Stmt) doStmtCommit(stmtType string, ctx context.Context, args ...interface{}) (result interface{}, err error) { +func (s *Stmt) doStmtCommit(ctx context.Context, stmtType string, args ...interface{}) (result interface{}, err error) { var ( cancelFuncForTimeout context.CancelFunc timestampMilli1 = gtime.TimestampMilli() @@ -86,7 +86,7 @@ func (s *Stmt) doStmtCommit(stmtType string, ctx context.Context, args ...interf // ExecContext executes a prepared statement with the given arguments and // returns a Result summarizing the effect of the statement. func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error) { - result, err := s.doStmtCommit(stmtTypeExecContext, ctx, args...) + result, err := s.doStmtCommit(ctx, stmtTypeExecContext, args...) if result != nil { return result.(sql.Result), err } @@ -96,7 +96,7 @@ func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result // QueryContext executes a prepared query statement with the given arguments // and returns the query results as a *Rows. func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*sql.Rows, error) { - result, err := s.doStmtCommit(stmtTypeQueryContext, ctx, args...) + result, err := s.doStmtCommit(ctx, stmtTypeQueryContext, args...) if result != nil { return result.(*sql.Rows), err } @@ -110,7 +110,7 @@ func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*sql.Rows // Otherwise, the *Row's Scan scans the first selected row and discards // the rest. func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *sql.Row { - result, _ := s.doStmtCommit(stmtTypeQueryRowContext, ctx, args...) + result, _ := s.doStmtCommit(ctx, stmtTypeQueryRowContext, args...) if result != nil { return result.(*sql.Row) } diff --git a/database/gdb/gdb_z_driver_test.go b/database/gdb/gdb_z_driver_test.go index 3d6053d5d..848a96a04 100644 --- a/database/gdb/gdb_z_driver_test.go +++ b/database/gdb/gdb_z_driver_test.go @@ -43,7 +43,7 @@ func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) { // DoCommit handles the sql before posts it to database. // It here overwrites the same method of gdb.DriverMysql and makes some custom changes. -func (d *MyDriver) DoCommit(ctx context.Context, link gdb.Link, sql string, args []interface{}) (string, []interface{}) { +func (d *MyDriver) DoCommit(ctx context.Context, link gdb.Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { latestSqlString.Set(sql) return d.DriverMysql.DoCommit(ctx, link, sql, args) } diff --git a/database/gdb/gdb_z_init_test.go b/database/gdb/gdb_z_init_test.go index d541322a7..9e73bf49c 100644 --- a/database/gdb/gdb_z_init_test.go +++ b/database/gdb/gdb_z_init_test.go @@ -7,6 +7,7 @@ package gdb_test import ( + "context" "fmt" "github.com/gogf/gf/container/garray" "github.com/gogf/gf/frame/g" @@ -29,9 +30,10 @@ const ( ) var ( - db gdb.DB - dbPrefix gdb.DB - configNode gdb.ConfigNode + db gdb.DB + dbPrefix gdb.DB + dbCtxStrict gdb.DB + configNode gdb.ConfigNode ) func init() { @@ -56,9 +58,15 @@ func init() { } nodePrefix := configNode nodePrefix.Prefix = TableNamePrefix1 + + nodeCtxStrict := configNode + nodeCtxStrict.CtxStrict = true + gdb.AddConfigNode("test", configNode) gdb.AddConfigNode("prefix", nodePrefix) + gdb.AddConfigNode("ctxstrict", nodeCtxStrict) gdb.AddConfigNode(gdb.DefaultGroupName, configNode) + // Default db. if r, err := gdb.New(); err != nil { gtest.Error(err) @@ -87,6 +95,20 @@ func init() { gtest.Error(err) } dbPrefix.SetSchema(TestSchema1) + + // CtxStrict db. + if r, err := gdb.New("ctxstrict"); err != nil { + gtest.Error(err) + } else { + dbCtxStrict = r + } + if _, err := dbCtxStrict.Ctx(context.TODO()).Exec(fmt.Sprintf(schemaTemplate, TestSchema1)); err != nil { + gtest.Error(err) + } + if _, err := dbCtxStrict.Ctx(context.TODO()).Exec(fmt.Sprintf(schemaTemplate, TestSchema2)); err != nil { + gtest.Error(err) + } + dbCtxStrict.SetSchema(TestSchema1) } func createTable(table ...string) string { @@ -111,7 +133,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) { switch configNode.Type { case "sqlite": - if _, err := db.Exec(fmt.Sprintf(` + if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf(` CREATE TABLE %s ( id bigint unsigned NOT NULL AUTO_INCREMENT, passport varchar(45), @@ -124,7 +146,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) { gtest.Fatal(err) } case "pgsql": - if _, err := db.Exec(fmt.Sprintf(` + if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf(` CREATE TABLE %s ( id bigint NOT NULL, passport varchar(45), @@ -137,7 +159,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) { gtest.Fatal(err) } case "mssql": - if _, err := db.Exec(fmt.Sprintf(` + if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf(` IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='%s' and xtype='U') CREATE TABLE %s ( ID numeric(10,0) NOT NULL, @@ -151,7 +173,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) { gtest.Fatal(err) } case "oracle": - if _, err := db.Exec(fmt.Sprintf(` + if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf(` CREATE TABLE %s ( ID NUMBER(10) NOT NULL, PASSPORT VARCHAR(45) NOT NULL, @@ -164,7 +186,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) { gtest.Fatal(err) } case "mysql": - if _, err := db.Exec(fmt.Sprintf(` + if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf(` CREATE TABLE %s ( id int(10) unsigned NOT NULL AUTO_INCREMENT, passport varchar(45) NULL, @@ -195,7 +217,7 @@ func createInitTableWithDb(db gdb.DB, table ...string) (name string) { }) } - result, err := db.Insert(name, array.Slice()) + result, err := db.Ctx(context.TODO()).Insert(name, array.Slice()) gtest.AssertNil(err) n, e := result.RowsAffected() @@ -205,7 +227,7 @@ func createInitTableWithDb(db gdb.DB, table ...string) (name string) { } func dropTableWithDb(db gdb.DB, table string) { - if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil { + if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil { gtest.Error(err) } } diff --git a/database/gdb/gdb_z_mysql_ctx_test.go b/database/gdb/gdb_z_mysql_ctx_test.go index 33eb23125..0d3cf753a 100644 --- a/database/gdb/gdb_z_mysql_ctx_test.go +++ b/database/gdb/gdb_z_mysql_ctx_test.go @@ -62,3 +62,23 @@ func Test_Ctx_Model(t *testing.T) { db.Model(table).All() }) } + +func Test_Ctx_Strict(t *testing.T) { + table := createInitTableWithDb(dbCtxStrict) + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + _, err := dbCtxStrict.Query("select 1") + t.AssertNE(err, nil) + }) + gtest.C(t, func(t *gtest.T) { + r, err := dbCtxStrict.Model(table).All() + t.AssertNE(err, nil) + t.Assert(len(r), 0) + }) + gtest.C(t, func(t *gtest.T) { + r, err := dbCtxStrict.Model(table).Ctx(context.TODO()).All() + t.AssertNil(err) + t.Assert(len(r), TableSize) + }) +} diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index 86e24a930..5ac4e2730 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -584,7 +584,7 @@ func Test_DB_GetStruct(t *testing.T) { CreateTime gtime.Time } user := new(User) - err := db.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3) + err := db.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3) t.AssertNil(err) t.Assert(user.NickName, "name_3") }) @@ -597,7 +597,7 @@ func Test_DB_GetStruct(t *testing.T) { CreateTime *gtime.Time } user := new(User) - err := db.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3) + err := db.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3) t.AssertNil(err) t.Assert(user.NickName, "name_3") }) @@ -615,7 +615,7 @@ func Test_DB_GetStructs(t *testing.T) { CreateTime gtime.Time } var users []User - err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) + err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) t.AssertNil(err) t.Assert(len(users), TableSize-1) t.Assert(users[0].Id, 2) @@ -635,7 +635,7 @@ func Test_DB_GetStructs(t *testing.T) { CreateTime *gtime.Time } var users []User - err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) + err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1) t.AssertNil(err) t.Assert(len(users), TableSize-1) t.Assert(users[0].Id, 2) From d109706ad3ad068f7888c27fff7eb2f7dc0fd226 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 26 Jun 2021 18:34:26 +0800 Subject: [PATCH 351/492] unify error package to gerror --- container/garray/garray_normal_any.go | 10 +++---- container/garray/garray_normal_int.go | 10 +++---- container/garray/garray_normal_str.go | 11 ++++---- container/gpool/gpool.go | 6 ++--- crypto/gaes/gaes.go | 16 ++++++------ crypto/gdes/gdes.go | 26 +++++++++---------- database/gredis/gredis_conn.go | 6 ++--- encoding/gcharset/gcharset.go | 11 ++++---- encoding/gini/gini.go | 4 +-- encoding/gjson/gjson_api_new_load.go | 4 +-- errors/gerror/gerror.go | 2 +- i18n/gi18n/gi18n_manager.go | 4 +-- net/ghttp/ghttp_request_param_file.go | 5 ++-- net/ghttp/ghttp_server_admin_process.go | 10 +++---- net/ghttp/ghttp_server_graceful.go | 4 +-- net/ghttp/ghttp_server_router.go | 4 +-- net/ghttp/internal/client/client_request.go | 5 ++-- net/gipv4/gipv4_ip.go | 4 +-- net/gtcp/gtcp_server.go | 4 +-- net/gudp/gudp_server.go | 4 +-- os/gcfg/gcfg_config.go | 3 +-- os/gcfg/gcfg_config_api.go | 20 +++++++------- os/gcmd/gcmd_handler.go | 10 +++---- os/gcmd/gcmd_parser.go | 6 ++--- os/gcmd/gcmd_parser_handler.go | 12 ++++----- os/gcron/gcron_cron.go | 5 ++-- os/gcron/gcron_schedule.go | 15 +++++------ os/gfsnotify/gfsnotify.go | 5 ++-- os/gfsnotify/gfsnotify_watcher.go | 5 ++-- os/glog/glog_logger_config.go | 10 +++---- os/glog/glog_logger_level.go | 5 ++-- os/gproc/gproc_comm.go | 4 +-- os/gproc/gproc_comm_send.go | 4 +-- os/gproc/gproc_process.go | 4 +-- os/grpool/grpool.go | 7 +++-- os/gsession/gsession_session.go | 6 ++--- os/gspath/gspath.go | 11 ++++---- os/gview/gview_config.go | 13 +++++----- os/gview/gview_parse.go | 3 +-- ...nv_z_unit_struct_marshal_unmarshal_test.go | 8 +++--- util/gvalid/gvalid_validator_check_value.go | 14 +++++----- 41 files changed, 151 insertions(+), 169 deletions(-) diff --git a/container/garray/garray_normal_any.go b/container/garray/garray_normal_any.go index 985c02fc7..a3b450b54 100644 --- a/container/garray/garray_normal_any.go +++ b/container/garray/garray_normal_any.go @@ -8,8 +8,8 @@ package garray import ( "bytes" - "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/text/gstr" @@ -123,7 +123,7 @@ func (a *Array) Set(index int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array))) + return gerror.Newf("index %d out of array range %d", index, len(a.array)) } a.array[index] = value return nil @@ -176,7 +176,7 @@ func (a *Array) InsertBefore(index int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array))) + return gerror.Newf("index %d out of array range %d", index, len(a.array)) } rear := append([]interface{}{}, a.array[index:]...) a.array = append(a.array[0:index], value) @@ -189,7 +189,7 @@ func (a *Array) InsertAfter(index int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array))) + return gerror.Newf("index %d out of array range %d", index, len(a.array)) } rear := append([]interface{}{}, a.array[index+1:]...) a.array = append(a.array[0:index+1], value) @@ -545,7 +545,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if startIndex < 0 || startIndex > len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array))) + return gerror.Newf("index %d out of array range %d", startIndex, len(a.array)) } for i := startIndex; i < startIndex+num; i++ { if i > len(a.array)-1 { diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index 05a10f0fe..3e43a56db 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -8,8 +8,8 @@ package garray import ( "bytes" - "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "math" "sort" @@ -104,7 +104,7 @@ func (a *IntArray) Set(index int, value int) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array))) + return gerror.Newf("index %d out of array range %d", index, len(a.array)) } a.array[index] = value return nil @@ -175,7 +175,7 @@ func (a *IntArray) InsertBefore(index int, value int) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array))) + return gerror.Newf("index %d out of array range %d", index, len(a.array)) } rear := append([]int{}, a.array[index:]...) a.array = append(a.array[0:index], value) @@ -188,7 +188,7 @@ func (a *IntArray) InsertAfter(index int, value int) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array))) + return gerror.Newf("index %d out of array range %d", index, len(a.array)) } rear := append([]int{}, a.array[index+1:]...) a.array = append(a.array[0:index+1], value) @@ -559,7 +559,7 @@ func (a *IntArray) Fill(startIndex int, num int, value int) error { a.mu.Lock() defer a.mu.Unlock() if startIndex < 0 || startIndex > len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array))) + return gerror.Newf("index %d out of array range %d", startIndex, len(a.array)) } for i := startIndex; i < startIndex+num; i++ { if i > len(a.array)-1 { diff --git a/container/garray/garray_normal_str.go b/container/garray/garray_normal_str.go index f1754e80d..832a70cb6 100644 --- a/container/garray/garray_normal_str.go +++ b/container/garray/garray_normal_str.go @@ -8,8 +8,7 @@ package garray import ( "bytes" - "errors" - "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/text/gstr" "math" @@ -91,7 +90,7 @@ func (a *StrArray) Set(index int, value string) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array))) + return gerror.Newf("index %d out of array range %d", index, len(a.array)) } a.array[index] = value return nil @@ -163,7 +162,7 @@ func (a *StrArray) InsertBefore(index int, value string) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array))) + return gerror.Newf("index %d out of array range %d", index, len(a.array)) } rear := append([]string{}, a.array[index:]...) a.array = append(a.array[0:index], value) @@ -176,7 +175,7 @@ func (a *StrArray) InsertAfter(index int, value string) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array))) + return gerror.Newf("index %d out of array range %d", index, len(a.array)) } rear := append([]string{}, a.array[index+1:]...) a.array = append(a.array[0:index+1], value) @@ -563,7 +562,7 @@ func (a *StrArray) Fill(startIndex int, num int, value string) error { a.mu.Lock() defer a.mu.Unlock() if startIndex < 0 || startIndex > len(a.array) { - return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array))) + return gerror.Newf("index %d out of array range %d", startIndex, len(a.array)) } for i := startIndex; i < startIndex+num; i++ { if i > len(a.array)-1 { diff --git a/container/gpool/gpool.go b/container/gpool/gpool.go index 0ac919139..e4125aaac 100644 --- a/container/gpool/gpool.go +++ b/container/gpool/gpool.go @@ -8,7 +8,7 @@ package gpool import ( - "errors" + "github.com/gogf/gf/errors/gerror" "time" "github.com/gogf/gf/container/glist" @@ -66,7 +66,7 @@ func New(ttl time.Duration, newFunc NewFunc, expireFunc ...ExpireFunc) *Pool { // Put puts an item to pool. func (p *Pool) Put(value interface{}) error { if p.closed.Val() { - return errors.New("pool is closed") + return gerror.New("pool is closed") } item := &poolItem{ value: value, @@ -117,7 +117,7 @@ func (p *Pool) Get() (interface{}, error) { if p.NewFunc != nil { return p.NewFunc() } - return nil, errors.New("pool is empty") + return nil, gerror.New("pool is empty") } // Size returns the count of available items of pool. diff --git a/crypto/gaes/gaes.go b/crypto/gaes/gaes.go index 6e2f84d5a..bdb04d78a 100644 --- a/crypto/gaes/gaes.go +++ b/crypto/gaes/gaes.go @@ -11,7 +11,7 @@ import ( "bytes" "crypto/aes" "crypto/cipher" - "errors" + "github.com/gogf/gf/errors/gerror" ) var ( @@ -63,7 +63,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) { } blockSize := block.BlockSize() if len(cipherText) < blockSize { - return nil, errors.New("cipherText too short") + return nil, gerror.New("cipherText too short") } ivValue := ([]byte)(nil) if len(iv) > 0 { @@ -72,7 +72,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) { ivValue = []byte(IVDefaultValue) } if len(cipherText)%blockSize != 0 { - return nil, errors.New("cipherText is not a multiple of the block size") + return nil, gerror.New("cipherText is not a multiple of the block size") } blockModel := cipher.NewCBCDecrypter(block, ivValue) plainText := make([]byte, len(cipherText)) @@ -93,22 +93,22 @@ func PKCS5Padding(src []byte, blockSize int) []byte { func PKCS5UnPadding(src []byte, blockSize int) ([]byte, error) { length := len(src) if blockSize <= 0 { - return nil, errors.New("invalid blocklen") + return nil, gerror.New("invalid blocklen") } if length%blockSize != 0 || length == 0 { - return nil, errors.New("invalid data len") + return nil, gerror.New("invalid data len") } unpadding := int(src[length-1]) if unpadding > blockSize || unpadding == 0 { - return nil, errors.New("invalid padding") + return nil, gerror.New("invalid padding") } padding := src[length-unpadding:] for i := 0; i < unpadding; i++ { if padding[i] != byte(unpadding) { - return nil, errors.New("invalid padding") + return nil, gerror.New("invalid padding") } } @@ -146,7 +146,7 @@ func DecryptCFB(cipherText []byte, key []byte, unPadding int, iv ...[]byte) ([]b return nil, err } if len(cipherText) < aes.BlockSize { - return nil, errors.New("cipherText too short") + return nil, gerror.New("cipherText too short") } ivValue := ([]byte)(nil) if len(iv) > 0 { diff --git a/crypto/gdes/gdes.go b/crypto/gdes/gdes.go index 55d936dfe..c60f7c190 100644 --- a/crypto/gdes/gdes.go +++ b/crypto/gdes/gdes.go @@ -11,7 +11,7 @@ import ( "bytes" "crypto/cipher" "crypto/des" - "errors" + "github.com/gogf/gf/errors/gerror" ) const ( @@ -66,7 +66,7 @@ func DecryptECB(cipherText []byte, key []byte, padding int) ([]byte, error) { // The length of the should be either 16 or 24 bytes. func EncryptECBTriple(plainText []byte, key []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, errors.New("key length error") + return nil, gerror.New("key length error") } text, err := Padding(plainText, padding) @@ -100,7 +100,7 @@ func EncryptECBTriple(plainText []byte, key []byte, padding int) ([]byte, error) // The length of the should be either 16 or 24 bytes. func DecryptECBTriple(cipherText []byte, key []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, errors.New("key length error") + return nil, gerror.New("key length error") } var newKey []byte @@ -138,7 +138,7 @@ func EncryptCBC(plainText []byte, key []byte, iv []byte, padding int) ([]byte, e } if len(iv) != block.BlockSize() { - return nil, errors.New("iv length invalid") + return nil, gerror.New("iv length invalid") } text, err := Padding(plainText, padding) @@ -161,7 +161,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv []byte, padding int) ([]byte, } if len(iv) != block.BlockSize() { - return nil, errors.New("iv length invalid") + return nil, gerror.New("iv length invalid") } text := make([]byte, len(cipherText)) @@ -179,7 +179,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv []byte, padding int) ([]byte, // EncryptCBCTriple encrypts using TripleDES and CBC mode. func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, errors.New("key length invalid") + return nil, gerror.New("key length invalid") } var newKey []byte @@ -196,7 +196,7 @@ func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]b } if len(iv) != block.BlockSize() { - return nil, errors.New("iv length invalid") + return nil, gerror.New("iv length invalid") } text, err := Padding(plainText, padding) @@ -214,7 +214,7 @@ func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]b // DecryptCBCTriple decrypts <cipherText> using TripleDES and CBC mode. func DecryptCBCTriple(cipherText []byte, key []byte, iv []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, errors.New("key length invalid") + return nil, gerror.New("key length invalid") } var newKey []byte @@ -231,7 +231,7 @@ func DecryptCBCTriple(cipherText []byte, key []byte, iv []byte, padding int) ([] } if len(iv) != block.BlockSize() { - return nil, errors.New("iv length invalid") + return nil, gerror.New("iv length invalid") } text := make([]byte, len(cipherText)) @@ -262,12 +262,12 @@ func Padding(text []byte, padding int) ([]byte, error) { switch padding { case NOPADDING: if len(text)%8 != 0 { - return nil, errors.New("text length invalid") + return nil, gerror.New("text length invalid") } case PKCS5PADDING: return PaddingPKCS5(text, 8), nil default: - return nil, errors.New("padding type error") + return nil, gerror.New("padding type error") } return text, nil @@ -277,12 +277,12 @@ func UnPadding(text []byte, padding int) ([]byte, error) { switch padding { case NOPADDING: if len(text)%8 != 0 { - return nil, errors.New("text length invalid") + return nil, gerror.New("text length invalid") } case PKCS5PADDING: return UnPaddingPKCS5(text), nil default: - return nil, errors.New("padding type error") + return nil, gerror.New("padding type error") } return text, nil } diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index f33d4528e..2826affcd 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -8,8 +8,8 @@ package gredis import ( "context" - "errors" "github.com/gogf/gf/container/gvar" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" @@ -50,7 +50,7 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} if timeout > 0 { conn, ok := c.Conn.(redis.ConnWithTimeout) if !ok { - return gvar.New(nil), errors.New(`current connection does not support "ConnWithTimeout"`) + return gvar.New(nil), gerror.New(`current connection does not support "ConnWithTimeout"`) } return conn.DoWithTimeout(timeout, commandName, args...) } @@ -107,7 +107,7 @@ func (c *Conn) ReceiveVar() (*gvar.Var, error) { func (c *Conn) ReceiveVarWithTimeout(timeout time.Duration) (*gvar.Var, error) { conn, ok := c.Conn.(redis.ConnWithTimeout) if !ok { - return gvar.New(nil), errors.New(`current connection does not support "ConnWithTimeout"`) + return gvar.New(nil), gerror.New(`current connection does not support "ConnWithTimeout"`) } return resultToVar(conn.ReceiveWithTimeout(timeout)) } diff --git a/encoding/gcharset/gcharset.go b/encoding/gcharset/gcharset.go index c976fa606..0aac86b27 100644 --- a/encoding/gcharset/gcharset.go +++ b/encoding/gcharset/gcharset.go @@ -21,8 +21,7 @@ package gcharset import ( "bytes" - "errors" - "fmt" + "github.com/gogf/gf/errors/gerror" "io/ioutil" "golang.org/x/text/encoding" @@ -60,11 +59,11 @@ func Convert(dstCharset string, srcCharset string, src string) (dst string, err transform.NewReader(bytes.NewReader([]byte(src)), e.NewDecoder()), ) if err != nil { - return "", fmt.Errorf("%s to utf8 failed. %v", srcCharset, err) + return "", gerror.Newf("%s to utf8 failed. %v", srcCharset, err) } src = string(tmp) } else { - return dst, errors.New(fmt.Sprintf("unsupport srcCharset: %s", srcCharset)) + return dst, gerror.Newf("unsupport srcCharset: %s", srcCharset) } } // Do the converting from UTF-8 to <dstCharset>. @@ -74,11 +73,11 @@ func Convert(dstCharset string, srcCharset string, src string) (dst string, err transform.NewReader(bytes.NewReader([]byte(src)), e.NewEncoder()), ) if err != nil { - return "", fmt.Errorf("utf to %s failed. %v", dstCharset, err) + return "", gerror.Newf("utf to %s failed. %v", dstCharset, err) } dst = string(tmp) } else { - return dst, errors.New(fmt.Sprintf("unsupport dstCharset: %s", dstCharset)) + return dst, gerror.Newf("unsupport dstCharset: %s", dstCharset) } } else { dst = src diff --git a/encoding/gini/gini.go b/encoding/gini/gini.go index 8ca3770bb..6394d2393 100644 --- a/encoding/gini/gini.go +++ b/encoding/gini/gini.go @@ -10,8 +10,8 @@ package gini import ( "bufio" "bytes" - "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "io" "strings" @@ -70,7 +70,7 @@ func Decode(data []byte) (res map[string]interface{}, err error) { } if haveSection == false { - return nil, errors.New("failed to parse INI file, section not found") + return nil, gerror.New("failed to parse INI file, section not found") } return res, nil } diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index bd6dcb504..780b3a16f 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -8,8 +8,8 @@ package gjson import ( "bytes" - "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "reflect" "github.com/gogf/gf/internal/json" @@ -264,7 +264,7 @@ func doLoadContentWithOptions(dataType string, data []byte, options Options) (*J return nil, err } default: - err = errors.New("unsupported type for loading") + err = gerror.New("unsupported type for loading") } if err != nil { return nil, err diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index 99b8b45fd..8b029b23f 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package errors provides simple functions to manipulate errors. +// Package gerror provides simple functions to manipulate errors. // // Very note that, this package is quite a base package, which should not import extra // packages except standard packages, to avoid cycle imports. diff --git a/i18n/gi18n/gi18n_manager.go b/i18n/gi18n/gi18n_manager.go index d289e2053..cc223e32d 100644 --- a/i18n/gi18n/gi18n_manager.go +++ b/i18n/gi18n/gi18n_manager.go @@ -8,8 +8,8 @@ package gi18n import ( "context" - "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "strings" "sync" @@ -101,7 +101,7 @@ func (m *Manager) SetPath(path string) error { } else { realPath, _ := gfile.Search(path) if realPath == "" { - return errors.New(fmt.Sprintf(`%s does not exist`, path)) + return gerror.Newf(`%s does not exist`, path) } m.options.Path = realPath } diff --git a/net/ghttp/ghttp_request_param_file.go b/net/ghttp/ghttp_request_param_file.go index 47811dfde..997b82ed4 100644 --- a/net/ghttp/ghttp_request_param_file.go +++ b/net/ghttp/ghttp_request_param_file.go @@ -8,7 +8,6 @@ package ghttp import ( "context" - "errors" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfile" @@ -36,7 +35,7 @@ type UploadFiles []*UploadFile // Note that it will OVERWRITE the target file if there's already a same name file exist. func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename string, err error) { if f == nil { - return "", errors.New("file is empty, maybe you retrieve it from invalid field name or form enctype") + return "", gerror.New("file is empty, maybe you retrieve it from invalid field name or form enctype") } if !gfile.Exists(dirPath) { if err = gfile.Mkdir(dirPath); err != nil { @@ -77,7 +76,7 @@ func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename stri // The parameter <randomlyRename> specifies whether randomly renames all the file names. func (fs UploadFiles) Save(dirPath string, randomlyRename ...bool) (filenames []string, err error) { if len(fs) == 0 { - return nil, errors.New("file array is empty, maybe you retrieve it from invalid field name or form enctype") + return nil, gerror.New("file array is empty, maybe you retrieve it from invalid field name or form enctype") } for _, f := range fs { if filename, err := f.Save(dirPath, randomlyRename...); err != nil { diff --git a/net/ghttp/ghttp_server_admin_process.go b/net/ghttp/ghttp_server_admin_process.go index 6cb27d05f..e6fa2e622 100644 --- a/net/ghttp/ghttp_server_admin_process.go +++ b/net/ghttp/ghttp_server_admin_process.go @@ -9,8 +9,8 @@ package ghttp import ( "bytes" "context" - "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/text/gstr" "os" @@ -52,7 +52,7 @@ var serverProcessStatus = gtype.NewInt() // The optional parameter <newExeFilePath> specifies the new binary file for creating process. func RestartAllServer(newExeFilePath ...string) error { if !gracefulEnabled { - return errors.New("graceful reload feature is disabled") + return gerror.New("graceful reload feature is disabled") } serverActionLocker.Lock() defer serverActionLocker.Unlock() @@ -85,9 +85,9 @@ func checkProcessStatus() error { if status > 0 { switch status { case adminActionRestarting: - return errors.New("server is restarting") + return gerror.New("server is restarting") case adminActionShuttingDown: - return errors.New("server is shutting down") + return gerror.New("server is shutting down") } } return nil @@ -98,7 +98,7 @@ func checkProcessStatus() error { func checkActionFrequency() error { interval := gtime.TimestampMilli() - serverActionLastTime.Val() if interval < adminActionIntervalLimit { - return errors.New(fmt.Sprintf("too frequent action, please retry in %d ms", adminActionIntervalLimit-interval)) + return gerror.Newf("too frequent action, please retry in %d ms", adminActionIntervalLimit-interval) } serverActionLastTime.Set(gtime.TimestampMilli()) return nil diff --git a/net/ghttp/ghttp_server_graceful.go b/net/ghttp/ghttp_server_graceful.go index e4ca2553c..2e6f82390 100644 --- a/net/ghttp/ghttp_server_graceful.go +++ b/net/ghttp/ghttp_server_graceful.go @@ -9,8 +9,8 @@ package ghttp import ( "context" "crypto/tls" - "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gproc" "github.com/gogf/gf/os/gres" "github.com/gogf/gf/text/gstr" @@ -122,7 +122,7 @@ func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig . } if err != nil { - return errors.New(fmt.Sprintf(`open cert file "%s","%s" failed: %s`, certFile, keyFile, err.Error())) + return gerror.Newf(`open cert file "%s","%s" failed: %s`, certFile, keyFile, err.Error()) } ln, err := s.getNetListener() if err != nil { diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index cc7d883b3..6d31d5e78 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -7,9 +7,9 @@ package ghttp import ( - "errors" "fmt" "github.com/gogf/gf/container/gtype" + "github.com/gogf/gf/errors/gerror" "strings" "github.com/gogf/gf/debug/gdebug" @@ -53,7 +53,7 @@ func (s *Server) parsePattern(pattern string) (domain, method, path string, err } } if path == "" { - err = errors.New("invalid pattern: URI should not be empty") + err = gerror.New("invalid pattern: URI should not be empty") } if path != "/" { path = strings.TrimRight(path, "/") diff --git a/net/ghttp/internal/client/client_request.go b/net/ghttp/internal/client/client_request.go index 2da8b6043..90b8970b9 100644 --- a/net/ghttp/internal/client/client_request.go +++ b/net/ghttp/internal/client/client_request.go @@ -9,8 +9,7 @@ package client import ( "bytes" "context" - "errors" - "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/internal/utils" @@ -189,7 +188,7 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h if len(array[1]) > 6 && strings.Compare(array[1][0:6], "@file:") == 0 { path := array[1][6:] if !gfile.Exists(path) { - return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path)) + return nil, gerror.Newf(`"%s" does not exist`, path) } if file, err := writer.CreateFormFile(array[0], gfile.Basename(path)); err == nil { if f, err := os.Open(path); err == nil { diff --git a/net/gipv4/gipv4_ip.go b/net/gipv4/gipv4_ip.go index 46d2aab8e..29c113d23 100644 --- a/net/gipv4/gipv4_ip.go +++ b/net/gipv4/gipv4_ip.go @@ -8,7 +8,7 @@ package gipv4 import ( - "errors" + "github.com/gogf/gf/errors/gerror" "net" "strconv" "strings" @@ -38,7 +38,7 @@ func GetIntranetIp() (ip string, err error) { return "", err } if len(ips) == 0 { - return "", errors.New("no intranet ip found") + return "", gerror.New("no intranet ip found") } return ips[0], nil } diff --git a/net/gtcp/gtcp_server.go b/net/gtcp/gtcp_server.go index f4ec7363c..d8c4f22fd 100644 --- a/net/gtcp/gtcp_server.go +++ b/net/gtcp/gtcp_server.go @@ -8,7 +8,7 @@ package gtcp import ( "crypto/tls" - "errors" + "github.com/gogf/gf/errors/gerror" "net" "sync" @@ -116,7 +116,7 @@ func (s *Server) Close() error { // Run starts running the TCP Server. func (s *Server) Run() (err error) { if s.handler == nil { - err = errors.New("start running failed: socket handler not defined") + err = gerror.New("start running failed: socket handler not defined") glog.Error(err) return } diff --git a/net/gudp/gudp_server.go b/net/gudp/gudp_server.go index db7dca6a4..4987787c9 100644 --- a/net/gudp/gudp_server.go +++ b/net/gudp/gudp_server.go @@ -7,7 +7,7 @@ package gudp import ( - "errors" + "github.com/gogf/gf/errors/gerror" "net" "github.com/gogf/gf/container/gmap" @@ -78,7 +78,7 @@ func (s *Server) Close() error { // Run starts listening UDP connection. func (s *Server) Run() error { if s.handler == nil { - err := errors.New("start running failed: socket handler not defined") + err := gerror.New("start running failed: socket handler not defined") glog.Error(err) return err } diff --git a/os/gcfg/gcfg_config.go b/os/gcfg/gcfg_config.go index d21f91978..425a09df5 100644 --- a/os/gcfg/gcfg_config.go +++ b/os/gcfg/gcfg_config.go @@ -9,7 +9,6 @@ package gcfg import ( "bytes" "context" - "errors" "fmt" "github.com/gogf/gf/container/garray" "github.com/gogf/gf/container/gmap" @@ -143,7 +142,7 @@ func (c *Config) SetPath(path string) error { } else { buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path)) } - err := errors.New(buffer.String()) + err := gerror.New(buffer.String()) if errorPrint() { glog.Error(err) } diff --git a/os/gcfg/gcfg_config_api.go b/os/gcfg/gcfg_config_api.go index aee7804a3..7e1f0625e 100644 --- a/os/gcfg/gcfg_config_api.go +++ b/os/gcfg/gcfg_config_api.go @@ -7,7 +7,7 @@ package gcfg import ( - "errors" + "github.com/gogf/gf/errors/gerror" "time" "github.com/gogf/gf/encoding/gjson" @@ -295,7 +295,7 @@ func (c *Config) GetStruct(pattern string, pointer interface{}, mapping ...map[s if j := c.getJson(); j != nil { return j.GetStruct(pattern, pointer, mapping...) } - return errors.New("configuration not found") + return gerror.New("configuration not found") } // GetStructs converts any slice to given struct slice. @@ -303,7 +303,7 @@ func (c *Config) GetStructs(pattern string, pointer interface{}, mapping ...map[ if j := c.getJson(); j != nil { return j.GetStructs(pattern, pointer, mapping...) } - return errors.New("configuration not found") + return gerror.New("configuration not found") } // GetMapToMap retrieves the value by specified `pattern` and converts it to specified map variable. @@ -312,7 +312,7 @@ func (c *Config) GetMapToMap(pattern string, pointer interface{}, mapping ...map if j := c.getJson(); j != nil { return j.GetMapToMap(pattern, pointer, mapping...) } - return errors.New("configuration not found") + return gerror.New("configuration not found") } // GetMapToMaps retrieves the value by specified `pattern` and converts it to specified map slice @@ -322,7 +322,7 @@ func (c *Config) GetMapToMaps(pattern string, pointer interface{}, mapping ...ma if j := c.getJson(); j != nil { return j.GetMapToMaps(pattern, pointer, mapping...) } - return errors.New("configuration not found") + return gerror.New("configuration not found") } // GetMapToMapsDeep retrieves the value by specified `pattern` and converts it to specified map slice @@ -332,7 +332,7 @@ func (c *Config) GetMapToMapsDeep(pattern string, pointer interface{}, mapping . if j := c.getJson(); j != nil { return j.GetMapToMapsDeep(pattern, pointer, mapping...) } - return errors.New("configuration not found") + return gerror.New("configuration not found") } // Map converts current Json object to map[string]interface{}. It returns nil if fails. @@ -358,7 +358,7 @@ func (c *Config) Struct(pointer interface{}, mapping ...map[string]string) error if j := c.getJson(); j != nil { return j.Struct(pointer, mapping...) } - return errors.New("configuration not found") + return gerror.New("configuration not found") } // Structs converts current Json object to specified object slice. @@ -367,7 +367,7 @@ func (c *Config) Structs(pointer interface{}, mapping ...map[string]string) erro if j := c.getJson(); j != nil { return j.Structs(pointer, mapping...) } - return errors.New("configuration not found") + return gerror.New("configuration not found") } // MapToMap converts current Json object to specified map variable. @@ -376,7 +376,7 @@ func (c *Config) MapToMap(pointer interface{}, mapping ...map[string]string) err if j := c.getJson(); j != nil { return j.MapToMap(pointer, mapping...) } - return errors.New("configuration not found") + return gerror.New("configuration not found") } // MapToMaps converts current Json object to specified map variable slice. @@ -385,7 +385,7 @@ func (c *Config) MapToMaps(pointer interface{}, mapping ...map[string]string) er if j := c.getJson(); j != nil { return j.MapToMaps(pointer, mapping...) } - return errors.New("configuration not found") + return gerror.New("configuration not found") } // Clear removes all parsed configuration files content cache, diff --git a/os/gcmd/gcmd_handler.go b/os/gcmd/gcmd_handler.go index 2bcb7a905..73d6c73c3 100644 --- a/os/gcmd/gcmd_handler.go +++ b/os/gcmd/gcmd_handler.go @@ -8,13 +8,13 @@ package gcmd import ( - "errors" + "github.com/gogf/gf/errors/gerror" ) // BindHandle registers callback function <f> with <cmd>. func BindHandle(cmd string, f func()) error { if _, ok := defaultCommandFuncMap[cmd]; ok { - return errors.New("duplicated handle for command:" + cmd) + return gerror.New("duplicated handle for command:" + cmd) } else { defaultCommandFuncMap[cmd] = f } @@ -37,7 +37,7 @@ func RunHandle(cmd string) error { if handle, ok := defaultCommandFuncMap[cmd]; ok { handle() } else { - return errors.New("no handle found for command:" + cmd) + return gerror.New("no handle found for command:" + cmd) } return nil } @@ -49,10 +49,10 @@ func AutoRun() error { if handle, ok := defaultCommandFuncMap[cmd]; ok { handle() } else { - return errors.New("no handle found for command:" + cmd) + return gerror.New("no handle found for command:" + cmd) } } else { - return errors.New("no command found") + return gerror.New("no command found") } return nil } diff --git a/os/gcmd/gcmd_parser.go b/os/gcmd/gcmd_parser.go index f18e5093e..1ac003ae2 100644 --- a/os/gcmd/gcmd_parser.go +++ b/os/gcmd/gcmd_parser.go @@ -8,15 +8,13 @@ package gcmd import ( - "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "os" "strings" "github.com/gogf/gf/text/gstr" - "errors" - "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/text/gregex" @@ -96,7 +94,7 @@ func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bo i++ continue } else if parser.strict { - return nil, errors.New(fmt.Sprintf(`invalid option '%s'`, args[i])) + return nil, gerror.Newf(`invalid option '%s'`, args[i]) } } } diff --git a/os/gcmd/gcmd_parser_handler.go b/os/gcmd/gcmd_parser_handler.go index a2ab79eee..6e61712f4 100644 --- a/os/gcmd/gcmd_parser_handler.go +++ b/os/gcmd/gcmd_parser_handler.go @@ -8,20 +8,20 @@ package gcmd import ( - "errors" + "github.com/gogf/gf/errors/gerror" ) // BindHandle registers callback function <f> with <cmd>. func (p *Parser) BindHandle(cmd string, f func()) error { if _, ok := p.commandFuncMap[cmd]; ok { - return errors.New("duplicated handle for command:" + cmd) + return gerror.New("duplicated handle for command:" + cmd) } else { p.commandFuncMap[cmd] = f } return nil } -// BindHandle registers callback function with map <m>. +// BindHandleMap registers callback function with map <m>. func (p *Parser) BindHandleMap(m map[string]func()) error { var err error for k, v := range m { @@ -37,7 +37,7 @@ func (p *Parser) RunHandle(cmd string) error { if handle, ok := p.commandFuncMap[cmd]; ok { handle() } else { - return errors.New("no handle found for command:" + cmd) + return gerror.New("no handle found for command:" + cmd) } return nil } @@ -49,10 +49,10 @@ func (p *Parser) AutoRun() error { if handle, ok := p.commandFuncMap[cmd]; ok { handle() } else { - return errors.New("no handle found for command:" + cmd) + return gerror.New("no handle found for command:" + cmd) } } else { - return errors.New("no command found") + return gerror.New("no command found") } return nil } diff --git a/os/gcron/gcron_cron.go b/os/gcron/gcron_cron.go index 28e2b8522..0e148b970 100644 --- a/os/gcron/gcron_cron.go +++ b/os/gcron/gcron_cron.go @@ -7,8 +7,7 @@ package gcron import ( - "errors" - "fmt" + "github.com/gogf/gf/errors/gerror" "time" "github.com/gogf/gf/container/garray" @@ -63,7 +62,7 @@ func (c *Cron) GetLogLevel() int { func (c *Cron) Add(pattern string, job func(), name ...string) (*Entry, error) { if len(name) > 0 { if c.Search(name[0]) != nil { - return nil, errors.New(fmt.Sprintf(`cron job "%s" already exists`, name[0])) + return nil, gerror.Newf(`cron job "%s" already exists`, name[0]) } } return c.addEntry(pattern, job, false, name...) diff --git a/os/gcron/gcron_schedule.go b/os/gcron/gcron_schedule.go index 017875a1e..a960843e3 100644 --- a/os/gcron/gcron_schedule.go +++ b/os/gcron/gcron_schedule.go @@ -7,8 +7,7 @@ package gcron import ( - "errors" - "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" "strconv" "strings" @@ -91,7 +90,7 @@ func newSchedule(pattern string) (*cronSchedule, error) { }, nil } } else { - return nil, errors.New(fmt.Sprintf(`invalid pattern: "%s"`, pattern)) + return nil, gerror.Newf(`invalid pattern: "%s"`, pattern) } } // Handle the common cron pattern, like: @@ -140,7 +139,7 @@ func newSchedule(pattern string) (*cronSchedule, error) { } return schedule, nil } else { - return nil, errors.New(fmt.Sprintf(`invalid pattern: "%s"`, pattern)) + return nil, gerror.Newf(`invalid pattern: "%s"`, pattern) } } @@ -157,7 +156,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s intervalArray := strings.Split(item, "/") if len(intervalArray) == 2 { if i, err := strconv.Atoi(intervalArray[1]); err != nil { - return nil, errors.New(fmt.Sprintf(`invalid pattern item: "%s"`, item)) + return nil, gerror.Newf(`invalid pattern item: "%s"`, item) } else { interval = i } @@ -179,7 +178,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s // Eg: */5 if rangeArray[0] != "*" { if i, err := parseItemValue(rangeArray[0], fieldType); err != nil { - return nil, errors.New(fmt.Sprintf(`invalid pattern item: "%s"`, item)) + return nil, gerror.Newf(`invalid pattern item: "%s"`, item) } else { rangeMin = i rangeMax = i @@ -187,7 +186,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s } if len(rangeArray) == 2 { if i, err := parseItemValue(rangeArray[1], fieldType); err != nil { - return nil, errors.New(fmt.Sprintf(`invalid pattern item: "%s"`, item)) + return nil, gerror.Newf(`invalid pattern item: "%s"`, item) } else { rangeMax = i } @@ -221,7 +220,7 @@ func parseItemValue(value string, fieldType byte) (int, error) { } } } - return 0, errors.New(fmt.Sprintf(`invalid pattern value: "%s"`, value)) + return 0, gerror.Newf(`invalid pattern value: "%s"`, value) } // meet checks if the given time <t> meets the runnable point for the job. diff --git a/os/gfsnotify/gfsnotify.go b/os/gfsnotify/gfsnotify.go index 670370b33..0224b8ba0 100644 --- a/os/gfsnotify/gfsnotify.go +++ b/os/gfsnotify/gfsnotify.go @@ -9,9 +9,8 @@ package gfsnotify import ( "context" - "errors" - "fmt" "github.com/gogf/gf/container/gset" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "sync" "time" @@ -140,7 +139,7 @@ func RemoveCallback(callbackId int) error { callback = r.(*Callback) } if callback == nil { - return errors.New(fmt.Sprintf(`callback for id %d not found`, callbackId)) + return gerror.Newf(`callback for id %d not found`, callbackId) } w.RemoveCallback(callbackId) return nil diff --git a/os/gfsnotify/gfsnotify_watcher.go b/os/gfsnotify/gfsnotify_watcher.go index ab0f92eec..3219c26c8 100644 --- a/os/gfsnotify/gfsnotify_watcher.go +++ b/os/gfsnotify/gfsnotify_watcher.go @@ -8,8 +8,7 @@ package gfsnotify import ( "context" - "errors" - "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/container/glist" @@ -66,7 +65,7 @@ func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), re func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { // Check and convert the given path to absolute path. if t := fileRealPath(path); t == "" { - return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path)) + return nil, gerror.Newf(`"%s" does not exist`, path) } else { path = t } diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index ac295c1c5..54322a7f8 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -7,8 +7,6 @@ package glog import ( - "errors" - "fmt" "io" "strings" "time" @@ -82,7 +80,7 @@ func (l *Logger) SetConfig(config Config) error { // SetConfigWithMap set configurations with map for the logger. func (l *Logger) SetConfigWithMap(m map[string]interface{}) error { if m == nil || len(m) == 0 { - return errors.New("configuration cannot be empty") + return gerror.New("configuration cannot be empty") } // The m now is a shallow copy of m. // A little tricky, isn't it? @@ -93,7 +91,7 @@ func (l *Logger) SetConfigWithMap(m map[string]interface{}) error { if level, ok := levelStringMap[strings.ToUpper(gconv.String(levelValue))]; ok { m[levelKey] = level } else { - return errors.New(fmt.Sprintf(`invalid level string: %v`, levelValue)) + return gerror.Newf(`invalid level string: %v`, levelValue) } } // Change string configuration to int value for file rotation size. @@ -101,7 +99,7 @@ func (l *Logger) SetConfigWithMap(m map[string]interface{}) error { if rotateSizeValue != nil { m[rotateSizeKey] = gfile.StrToSize(gconv.String(rotateSizeValue)) if m[rotateSizeKey] == -1 { - return errors.New(fmt.Sprintf(`invalid rotate size: %v`, rotateSizeValue)) + return gerror.Newf(`invalid rotate size: %v`, rotateSizeValue) } } if err := gconv.Struct(m, &l.config); err != nil { @@ -206,7 +204,7 @@ func (l *Logger) GetWriter() io.Writer { // SetPath sets the directory path for file logging. func (l *Logger) SetPath(path string) error { if path == "" { - return errors.New("logging path is empty") + return gerror.New("logging path is empty") } if !gfile.Exists(path) { if err := gfile.Mkdir(path); err != nil { diff --git a/os/glog/glog_logger_level.go b/os/glog/glog_logger_level.go index fe1cfdb4e..9e9ba02ca 100644 --- a/os/glog/glog_logger_level.go +++ b/os/glog/glog_logger_level.go @@ -7,8 +7,7 @@ package glog import ( - "errors" - "fmt" + "github.com/gogf/gf/errors/gerror" "strings" ) @@ -78,7 +77,7 @@ func (l *Logger) SetLevelStr(levelStr string) error { if level, ok := levelStringMap[strings.ToUpper(levelStr)]; ok { l.config.Level = level } else { - return errors.New(fmt.Sprintf(`invalid level string: %s`, levelStr)) + return gerror.Newf(`invalid level string: %s`, levelStr) } return nil } diff --git a/os/gproc/gproc_comm.go b/os/gproc/gproc_comm.go index ca9911c83..fc013eec9 100644 --- a/os/gproc/gproc_comm.go +++ b/os/gproc/gproc_comm.go @@ -7,9 +7,9 @@ package gproc import ( - "errors" "fmt" "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/net/gtcp" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/util/gconv" @@ -65,7 +65,7 @@ func getConnByPid(pid int) (*gtcp.PoolConn, error) { return nil, err } } - return nil, errors.New(fmt.Sprintf("could not find port for pid: %d", pid)) + return nil, gerror.Newf("could not find port for pid: %d", pid) } // getPortByPid returns the listening port for specified pid. diff --git a/os/gproc/gproc_comm_send.go b/os/gproc/gproc_comm_send.go index b53c5132e..7946c9a81 100644 --- a/os/gproc/gproc_comm_send.go +++ b/os/gproc/gproc_comm_send.go @@ -7,7 +7,7 @@ package gproc import ( - "errors" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/net/gtcp" "io" @@ -46,7 +46,7 @@ func Send(pid int, data []byte, group ...string) error { err = json.UnmarshalUseNumber(result, response) if err == nil { if response.Code != 1 { - err = errors.New(response.Message) + err = gerror.New(response.Message) } } } diff --git a/os/gproc/gproc_process.go b/os/gproc/gproc_process.go index eb386345e..51fa28d70 100644 --- a/os/gproc/gproc_process.go +++ b/os/gproc/gproc_process.go @@ -8,8 +8,8 @@ package gproc import ( "context" - "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "os" "os/exec" @@ -101,7 +101,7 @@ func (p *Process) Send(data []byte) error { if p.Process != nil { return Send(p.Process.Pid, data) } - return errors.New("invalid process") + return gerror.New("invalid process") } // Release releases any resources associated with the Process p, diff --git a/os/grpool/grpool.go b/os/grpool/grpool.go index 136484367..707eb1304 100644 --- a/os/grpool/grpool.go +++ b/os/grpool/grpool.go @@ -8,8 +8,7 @@ package grpool import ( - "errors" - "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/container/glist" "github.com/gogf/gf/container/gtype" @@ -70,7 +69,7 @@ func Jobs() int { // The job will be executed asynchronously. func (p *Pool) Add(f func()) error { for p.closed.Val() { - return errors.New("pool closed") + return gerror.New("pool closed") } p.list.PushFront(f) // Check whether fork new goroutine or not. @@ -99,7 +98,7 @@ func (p *Pool) AddWithRecover(userFunc func(), recoverFunc ...func(err error)) e defer func() { if err := recover(); err != nil { if len(recoverFunc) > 0 && recoverFunc[0] != nil { - recoverFunc[0](errors.New(fmt.Sprintf(`%v`, err))) + recoverFunc[0](gerror.Newf(`%v`, err)) } } }() diff --git a/os/gsession/gsession_session.go b/os/gsession/gsession_session.go index 85d4d1995..2067d5b35 100644 --- a/os/gsession/gsession_session.go +++ b/os/gsession/gsession_session.go @@ -8,7 +8,7 @@ package gsession import ( "context" - "errors" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "time" @@ -182,7 +182,7 @@ func (s *Session) Id() string { // It returns error if it is called after session starts. func (s *Session) SetId(id string) error { if s.start { - return errors.New("session already started") + return gerror.New("session already started") } s.id = id return nil @@ -192,7 +192,7 @@ func (s *Session) SetId(id string) error { // It returns error if it is called after session starts. func (s *Session) SetIdFunc(f func(ttl time.Duration) string) error { if s.start { - return errors.New("session already started") + return gerror.New("session already started") } s.idFunc = f return nil diff --git a/os/gspath/gspath.go b/os/gspath/gspath.go index ca3c7c290..e54300629 100644 --- a/os/gspath/gspath.go +++ b/os/gspath/gspath.go @@ -13,8 +13,7 @@ package gspath import ( "context" - "errors" - "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "os" "sort" @@ -104,7 +103,7 @@ func (sp *SPath) Set(path string) (realPath string, err error) { } } if realPath == "" { - return realPath, errors.New(fmt.Sprintf(`path "%s" does not exist`, path)) + return realPath, gerror.Newf(`path "%s" does not exist`, path) } // The set path must be a directory. if gfile.IsDir(realPath) { @@ -124,7 +123,7 @@ func (sp *SPath) Set(path string) (realPath string, err error) { sp.addMonitorByPath(realPath) return realPath, nil } else { - return "", errors.New(path + " should be a folder") + return "", gerror.New(path + " should be a folder") } } @@ -139,7 +138,7 @@ func (sp *SPath) Add(path string) (realPath string, err error) { } } if realPath == "" { - return realPath, errors.New(fmt.Sprintf(`path "%s" does not exist`, path)) + return realPath, gerror.Newf(`path "%s" does not exist`, path) } // The added path must be a directory. if gfile.IsDir(realPath) { @@ -153,7 +152,7 @@ func (sp *SPath) Add(path string) (realPath string, err error) { } return realPath, nil } else { - return "", errors.New(path + " should be a folder") + return "", gerror.New(path + " should be a folder") } } diff --git a/os/gview/gview_config.go b/os/gview/gview_config.go index 4e68da421..e596b07bf 100644 --- a/os/gview/gview_config.go +++ b/os/gview/gview_config.go @@ -8,8 +8,7 @@ package gview import ( "context" - "errors" - "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/i18n/gi18n" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfile" @@ -75,7 +74,7 @@ func (view *View) SetConfig(config Config) error { // SetConfigWithMap set configurations with map for the view. func (view *View) SetConfigWithMap(m map[string]interface{}) error { if m == nil || len(m) == 0 { - return errors.New("configuration cannot be empty") + return gerror.New("configuration cannot be empty") } // The m now is a shallow copy of m. // Any changes to m does not affect the original one. @@ -124,7 +123,7 @@ func (view *View) SetPath(path string) error { } // Path not exist. if realPath == "" { - err := errors.New(fmt.Sprintf(`[gview] SetPath failed: path "%s" does not exist`, path)) + err := gerror.Newf(`[gview] SetPath failed: path "%s" does not exist`, path) if errorPrint() { glog.Error(err) } @@ -132,7 +131,7 @@ func (view *View) SetPath(path string) error { } // Should be a directory. if !isDir { - err := errors.New(fmt.Sprintf(`[gview] SetPath failed: path "%s" should be directory type`, path)) + err := gerror.Newf(`[gview] SetPath failed: path "%s" should be directory type`, path) if errorPrint() { glog.Error(err) } @@ -178,7 +177,7 @@ func (view *View) AddPath(path string) error { } // Path not exist. if realPath == "" { - err := errors.New(fmt.Sprintf(`[gview] AddPath failed: path "%s" does not exist`, path)) + err := gerror.Newf(`[gview] AddPath failed: path "%s" does not exist`, path) if errorPrint() { glog.Error(err) } @@ -186,7 +185,7 @@ func (view *View) AddPath(path string) error { } // realPath should be type of folder. if !isDir { - err := errors.New(fmt.Sprintf(`[gview] AddPath failed: path "%s" should be directory type`, path)) + err := gerror.Newf(`[gview] AddPath failed: path "%s" should be directory type`, path) if errorPrint() { glog.Error(err) } diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index e7675413a..ef2e242b9 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -9,7 +9,6 @@ package gview import ( "bytes" "context" - "errors" "fmt" "github.com/gogf/gf/encoding/ghash" "github.com/gogf/gf/errors/gerror" @@ -377,7 +376,7 @@ func (view *View) searchFile(file string) (path string, folder string, resource if errorPrint() { glog.Error(buffer.String()) } - err = errors.New(fmt.Sprintf(`template file "%s" not found`, file)) + err = gerror.Newf(`template file "%s" not found`, file) } return } diff --git a/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go b/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go index 9fc082944..b7c0941f5 100644 --- a/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go +++ b/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go @@ -7,9 +7,9 @@ package gconv_test import ( - "errors" "github.com/gogf/gf/crypto/gcrc32" "github.com/gogf/gf/encoding/gbinary" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" @@ -83,16 +83,16 @@ func (p *Pkg) Marshal() []byte { func (p *Pkg) UnmarshalValue(v interface{}) error { b := gconv.Bytes(v) if len(b) < 6 { - return errors.New("invalid package length") + return gerror.New("invalid package length") } p.Length = gbinary.DecodeToUint16(b[:2]) if len(b) < int(p.Length) { - return errors.New("invalid data length") + return gerror.New("invalid data length") } p.Crc32 = gbinary.DecodeToUint32(b[2:6]) p.Data = b[6:] if gcrc32.Encrypt(p.Data) != p.Crc32 { - return errors.New("crc32 validation failed") + return gerror.New("crc32 validation failed") } return nil } diff --git a/util/gvalid/gvalid_validator_check_value.go b/util/gvalid/gvalid_validator_check_value.go index a72cee3b3..791a7d080 100644 --- a/util/gvalid/gvalid_validator_check_value.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -7,7 +7,7 @@ package gvalid import ( - "errors" + "github.com/gogf/gf/errors/gerror" "strconv" "strings" "time" @@ -175,7 +175,7 @@ func (v *Validator) doCheckBuildInRules( "max-length", "size": if msg := v.checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { - return match, errors.New(msg) + return match, gerror.New(msg) } else { match = true } @@ -186,7 +186,7 @@ func (v *Validator) doCheckBuildInRules( "max", "between": if msg := v.checkRange(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { - return match, errors.New(msg) + return match, gerror.New(msg) } else { match = true } @@ -222,7 +222,7 @@ func (v *Validator) doCheckBuildInRules( var msg string msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":format", rulePattern, -1) - return match, errors.New(msg) + return match, gerror.New(msg) } // Values of two fields should be equal as string. @@ -237,7 +237,7 @@ func (v *Validator) doCheckBuildInRules( var msg string msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":field", rulePattern, -1) - return match, errors.New(msg) + return match, gerror.New(msg) } // Values of two fields should not be equal as string. @@ -253,7 +253,7 @@ func (v *Validator) doCheckBuildInRules( var msg string msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":field", rulePattern, -1) - return match, errors.New(msg) + return match, gerror.New(msg) } // Field value should be in range of. @@ -436,7 +436,7 @@ func (v *Validator) doCheckBuildInRules( match = gregex.IsMatchString(`^([0-9A-Fa-f]{2}[\-:]){5}[0-9A-Fa-f]{2}$`, valueStr) default: - return match, errors.New("Invalid rule name: " + ruleKey) + return match, gerror.New("Invalid rule name: " + ruleKey) } return match, nil } From bb0a3e09d687d0e0d887400f67b591ba8854e94d Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sat, 26 Jun 2021 18:44:59 +0800 Subject: [PATCH 352/492] revert gerror usage for package gvalid --- util/gvalid/gvalid_validator_check_value.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/util/gvalid/gvalid_validator_check_value.go b/util/gvalid/gvalid_validator_check_value.go index 791a7d080..a72cee3b3 100644 --- a/util/gvalid/gvalid_validator_check_value.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -7,7 +7,7 @@ package gvalid import ( - "github.com/gogf/gf/errors/gerror" + "errors" "strconv" "strings" "time" @@ -175,7 +175,7 @@ func (v *Validator) doCheckBuildInRules( "max-length", "size": if msg := v.checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { - return match, gerror.New(msg) + return match, errors.New(msg) } else { match = true } @@ -186,7 +186,7 @@ func (v *Validator) doCheckBuildInRules( "max", "between": if msg := v.checkRange(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { - return match, gerror.New(msg) + return match, errors.New(msg) } else { match = true } @@ -222,7 +222,7 @@ func (v *Validator) doCheckBuildInRules( var msg string msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":format", rulePattern, -1) - return match, gerror.New(msg) + return match, errors.New(msg) } // Values of two fields should be equal as string. @@ -237,7 +237,7 @@ func (v *Validator) doCheckBuildInRules( var msg string msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":field", rulePattern, -1) - return match, gerror.New(msg) + return match, errors.New(msg) } // Values of two fields should not be equal as string. @@ -253,7 +253,7 @@ func (v *Validator) doCheckBuildInRules( var msg string msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":field", rulePattern, -1) - return match, gerror.New(msg) + return match, errors.New(msg) } // Field value should be in range of. @@ -436,7 +436,7 @@ func (v *Validator) doCheckBuildInRules( match = gregex.IsMatchString(`^([0-9A-Fa-f]{2}[\-:]){5}[0-9A-Fa-f]{2}$`, valueStr) default: - return match, gerror.New("Invalid rule name: " + ruleKey) + return match, errors.New("Invalid rule name: " + ruleKey) } return match, nil } From 968e1db94db6d832fbdb018873abd004d926a464 Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Mon, 28 Jun 2021 00:00:44 +0800 Subject: [PATCH 353/492] add log level prefix color --- os/glog/glog_logger.go | 8 ++++---- os/glog/glog_logger_chaining.go | 11 +++++++++++ os/glog/glog_logger_config.go | 5 +++++ os/glog/glog_logger_handler.go | 19 +++++++++++++++++-- os/glog/glog_logger_level.go | 25 +++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 6 deletions(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 7f4dcbc91..7a50d6b2c 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -43,6 +43,7 @@ const ( defaultFilePerm = os.FileMode(0666) defaultFileExpire = time.Minute pathFilterKey = "/os/glog/glog" + bufferStdOut = "stdOut" ) const ( @@ -220,20 +221,19 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { // printToWriter writes buffer to writer. func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { - buffer := input.Buffer() if l.config.Writer == nil { // Output content to disk file. if l.config.Path != "" { - l.printToFile(input.Time, buffer) + l.printToFile(input.Time, input.Buffer()) } // Allow output to stdout? if l.config.StdoutPrint { - if _, err := os.Stdout.Write(buffer.Bytes()); err != nil { + if _, err := os.Stdout.Write(input.Buffer(bufferStdOut).Bytes()); err != nil { intlog.Error(err) } } } else { - if _, err := l.config.Writer.Write(buffer.Bytes()); err != nil { + if _, err := l.config.Writer.Write(input.Buffer().Bytes()); err != nil { // panic(err) intlog.Error(err) } diff --git a/os/glog/glog_logger_chaining.go b/os/glog/glog_logger_chaining.go index 3a5080b62..e96c55ec7 100644 --- a/os/glog/glog_logger_chaining.go +++ b/os/glog/glog_logger_chaining.go @@ -245,3 +245,14 @@ func (l *Logger) Async(enabled ...bool) *Logger { } return logger } + +func (l *Logger) Color(color logColor) *Logger { + logger := (*Logger)(nil) + if l.parent == nil { + logger = l.Clone() + } else { + logger = l + } + logger.SetColor(color) + return logger +} \ No newline at end of file diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 6b00a6afd..00c974705 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -42,6 +42,7 @@ type Config struct { RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration. RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression. RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default. + color logColor `json:"-"` } // DefaultConfig returns the default configuration for logger. @@ -252,3 +253,7 @@ func (l *Logger) SetPrefix(prefix string) { func (l *Logger) SetHandlers(handlers ...Handler) { l.config.Handlers = append(handlers, defaultHandler) } + +func (l *Logger) SetColor(color logColor) { + l.config.color = color +} diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index 69a703992..37c318643 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -9,6 +9,7 @@ package glog import ( "bytes" "context" + "fmt" "time" ) @@ -42,11 +43,15 @@ func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, s string) { buffer.WriteString(s) } -func (i *HandlerInput) Buffer() *bytes.Buffer { +func (i *HandlerInput) Buffer(bufferType ...string) *bytes.Buffer { buffer := bytes.NewBuffer(nil) buffer.WriteString(i.TimeFormat) if i.LevelFormat != "" { - i.addStringToBuffer(buffer, i.LevelFormat) + if len(bufferType) > 0 && bufferType[0] == bufferStdOut { + i.addStringToBuffer(buffer, i.getLevelFormatWithColor()) + } else { + i.addStringToBuffer(buffer, i.LevelFormat) + } } if i.CallerFunc != "" { i.addStringToBuffer(buffer, i.CallerFunc) @@ -67,6 +72,16 @@ func (i *HandlerInput) Buffer() *bytes.Buffer { return buffer } +// getLevelFormatWithColor returns the prefix string with color. +func (i *HandlerInput) getLevelFormatWithColor() string { + s := i.LevelFormat + color := defaultLevelColor[i.Level] + if i.logger.config.color != 0 { + color = i.logger.config.color + } + return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", color, s) +} + func (i *HandlerInput) String() string { return i.Buffer().String() } diff --git a/os/glog/glog_logger_level.go b/os/glog/glog_logger_level.go index a756bc6f8..49a4c1f94 100644 --- a/os/glog/glog_logger_level.go +++ b/os/glog/glog_logger_level.go @@ -29,6 +29,19 @@ const ( LEVEL_FATA // 1024 ) +type logColor int + +const ( + COLOR_BLACK logColor = 30 + iota + COLOR_RED + COLOR_GREEN + COLOR_YELLOW + COLOR_BLUE + COLOR_MAGENTA + COLOR_CYAN + COLOR_WHITE +) + // defaultLevelPrefixes defines the default level and its mapping prefix string. var defaultLevelPrefixes = map[int]string{ LEVEL_DEBU: "DEBU", @@ -41,6 +54,18 @@ var defaultLevelPrefixes = map[int]string{ LEVEL_FATA: "FATA", } +// defaultLevelColor defines the default level and its mapping prefix string. +var defaultLevelColor = map[int]logColor{ + LEVEL_DEBU: COLOR_YELLOW, + LEVEL_INFO: COLOR_GREEN, + LEVEL_NOTI: COLOR_CYAN, + LEVEL_WARN: COLOR_YELLOW, + LEVEL_ERRO: COLOR_RED, + LEVEL_CRIT: COLOR_RED, + LEVEL_PANI: COLOR_RED, + LEVEL_FATA: COLOR_RED, +} + // levelStringMap defines level string name to its level mapping. var levelStringMap = map[string]int{ "ALL": LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT, From a2771c7558f4a0e5462c7d51a734382378b3332e Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Mon, 28 Jun 2021 00:23:25 +0800 Subject: [PATCH 354/492] format code --- os/glog/glog_logger.go | 2 +- os/glog/glog_logger_chaining.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 7a50d6b2c..88475c0e0 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -43,7 +43,7 @@ const ( defaultFilePerm = os.FileMode(0666) defaultFileExpire = time.Minute pathFilterKey = "/os/glog/glog" - bufferStdOut = "stdOut" + bufferStdOut = "stdOut" ) const ( diff --git a/os/glog/glog_logger_chaining.go b/os/glog/glog_logger_chaining.go index e96c55ec7..0ba7565a6 100644 --- a/os/glog/glog_logger_chaining.go +++ b/os/glog/glog_logger_chaining.go @@ -255,4 +255,4 @@ func (l *Logger) Color(color logColor) *Logger { } logger.SetColor(color) return logger -} \ No newline at end of file +} From 03928f19775b6ef8c3977de64aaa2f8288b23270 Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Mon, 28 Jun 2021 00:58:35 +0800 Subject: [PATCH 355/492] add logging level prefix with color or not config --- os/glog/glog_logger.go | 4 ++-- os/glog/glog_logger_config.go | 1 + os/glog/glog_logger_handler.go | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 88475c0e0..5fdc14fed 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -43,7 +43,7 @@ const ( defaultFilePerm = os.FileMode(0666) defaultFileExpire = time.Minute pathFilterKey = "/os/glog/glog" - bufferStdOut = "stdOut" + mustWithColor = true ) const ( @@ -228,7 +228,7 @@ func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { } // Allow output to stdout? if l.config.StdoutPrint { - if _, err := os.Stdout.Write(input.Buffer(bufferStdOut).Bytes()); err != nil { + if _, err := os.Stdout.Write(input.Buffer(mustWithColor).Bytes()); err != nil { intlog.Error(err) } } diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 00c974705..ec970897c 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -42,6 +42,7 @@ type Config struct { RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration. RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression. RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default. + FileColor bool `json:"fileColor"` // Logging level prefix with color or not (false in default). color logColor `json:"-"` } diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index 37c318643..bfb15d94b 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -43,11 +43,11 @@ func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, s string) { buffer.WriteString(s) } -func (i *HandlerInput) Buffer(bufferType ...string) *bytes.Buffer { +func (i *HandlerInput) Buffer(withColor ...bool) *bytes.Buffer { buffer := bytes.NewBuffer(nil) buffer.WriteString(i.TimeFormat) if i.LevelFormat != "" { - if len(bufferType) > 0 && bufferType[0] == bufferStdOut { + if i.logger.config.FileColor || (len(withColor) > 0 && withColor[0] == mustWithColor) { i.addStringToBuffer(buffer, i.getLevelFormatWithColor()) } else { i.addStringToBuffer(buffer, i.LevelFormat) From 083e32fd9e7c445d4fb7e386be981c7acdccb741 Mon Sep 17 00:00:00 2001 From: qinyuguang <qinyuguang@meican.com> Date: Thu, 24 Jun 2021 16:29:55 +0800 Subject: [PATCH 356/492] otel version bump to v1.0.0-RC1 --- go.mod | 6 +++--- go.sum | 18 +++++++--------- net/gtrace/gtrace_baggage.go | 30 ++++++++++++-------------- net/gtrace/gtrace_span.go | 2 +- net/gtrace/gtrace_unit_carrier_test.go | 2 +- 5 files changed, 27 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 5022b6160..549075d90 100644 --- a/go.mod +++ b/go.mod @@ -12,9 +12,9 @@ require ( github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf github.com/mattn/go-runewidth v0.0.10 // indirect github.com/olekukonko/tablewriter v0.0.5 - go.opentelemetry.io/otel v0.19.0 - go.opentelemetry.io/otel/oteltest v0.19.0 - go.opentelemetry.io/otel/trace v0.19.0 + go.opentelemetry.io/otel v1.0.0-RC1 + go.opentelemetry.io/otel/oteltest v1.0.0-RC1 + go.opentelemetry.io/otel/trace v1.0.0-RC1 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 golang.org/x/text v0.3.4 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c diff --git a/go.sum b/go.sum index 582d62e0e..45d8f1356 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= @@ -28,14 +28,12 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng= -go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= -go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg= -go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= -go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q= -go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= -go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc= -go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= +go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc= +go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I= +go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0cljhGk9sB0Yk= +go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4= +go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58= +go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= diff --git a/net/gtrace/gtrace_baggage.go b/net/gtrace/gtrace_baggage.go index 28a175c20..d04834645 100644 --- a/net/gtrace/gtrace_baggage.go +++ b/net/gtrace/gtrace_baggage.go @@ -10,7 +10,7 @@ import ( "context" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" - "go.opentelemetry.io/otel/attribute" + "github.com/gogf/gf/util/gconv" "go.opentelemetry.io/otel/baggage" ) @@ -37,39 +37,37 @@ func (b *Baggage) Ctx() context.Context { // SetValue is a convenient function for adding one key-value pair to baggage. // Note that it uses attribute.Any to set the key-value pair. func (b *Baggage) SetValue(key string, value interface{}) context.Context { - b.ctx = baggage.ContextWithValues(b.ctx, attribute.Any(key, value)) + member, _ := baggage.NewMember(key, gconv.String(value)) + bag, _ := baggage.New(member) + b.ctx = baggage.ContextWithBaggage(b.ctx, bag) return b.ctx } // SetMap is a convenient function for adding map key-value pairs to baggage. // Note that it uses attribute.Any to set the key-value pair. func (b *Baggage) SetMap(data map[string]interface{}) context.Context { - pairs := make([]attribute.KeyValue, 0) + members := make([]baggage.Member, 0) for k, v := range data { - pairs = append(pairs, attribute.Any(k, v)) + member, _ := baggage.NewMember(k, gconv.String(v)) + members = append(members, member) } - b.ctx = baggage.ContextWithValues(b.ctx, pairs...) + bag, _ := baggage.New(members...) + b.ctx = baggage.ContextWithBaggage(b.ctx, bag) return b.ctx } // GetMap retrieves and returns the baggage values as map. func (b *Baggage) GetMap() *gmap.StrAnyMap { m := gmap.NewStrAnyMap() - set := baggage.Set(b.ctx) - if length := set.Len(); length > 0 { - if length == 0 { - return m - } - inter := set.Iter() - for inter.Next() { - m.Set(string(inter.Label().Key), inter.Label().Value.AsInterface()) - } + members := baggage.FromContext(b.ctx).Members() + for i := range members { + m.Set(members[i].Key(), members[i].Value()) } return m } // GetVar retrieves value and returns a *gvar.Var for specified key from baggage. func (b *Baggage) GetVar(key string) *gvar.Var { - value := baggage.Value(b.ctx, attribute.Key(key)) - return gvar.New(value.AsInterface()) + value := baggage.FromContext(b.ctx).Member(key).Value() + return gvar.New(value) } diff --git a/net/gtrace/gtrace_span.go b/net/gtrace/gtrace_span.go index f9163a60f..72f88c418 100644 --- a/net/gtrace/gtrace_span.go +++ b/net/gtrace/gtrace_span.go @@ -16,7 +16,7 @@ type Span struct { } // NewSpan creates a span using default tracer. -func NewSpan(ctx context.Context, spanName string, opts ...trace.SpanOption) (context.Context, *Span) { +func NewSpan(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, *Span) { ctx, span := NewTracer().Start(ctx, spanName, opts...) return ctx, &Span{ Span: span, diff --git a/net/gtrace/gtrace_unit_carrier_test.go b/net/gtrace/gtrace_unit_carrier_test.go index f67e85ae0..a070b2ab4 100644 --- a/net/gtrace/gtrace_unit_carrier_test.go +++ b/net/gtrace/gtrace_unit_carrier_test.go @@ -57,7 +57,7 @@ func TestNewCarrier(t *testing.T) { t.Assert(carrier1.String(), `{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000002-01","tracestate":""}`) ctx = otel.GetTextMapPropagator().Extract(ctx, carrier1) - gotSc := trace.RemoteSpanContextFromContext(ctx) + gotSc := trace.SpanContextFromContext(ctx) t.Assert(gotSc.TraceID().String(), traceID.String()) t.Assert(gotSc.SpanID().String(), "0000000000000002") }) From bfdeb6c4f511455b66d957c3882e29fa76dd3454 Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Tue, 29 Jun 2021 21:05:46 +0800 Subject: [PATCH 357/492] =?UTF-8?q?log=20color=E5=85=BC=E5=AE=B9win?= =?UTF-8?q?=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 1 + os/glog/glog_logger_chaining.go | 7 +++-- os/glog/glog_logger_config.go | 49 ++++++++++++++++----------------- os/glog/glog_logger_handler.go | 12 ++++---- os/glog/glog_logger_level.go | 7 ++--- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/go.mod b/go.mod index 5022b6160..ba8172629 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.14 require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 + github.com/fatih/color v1.12.0 // indirect github.com/fsnotify/fsnotify v1.4.9 github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 github.com/gomodule/redigo v2.0.0+incompatible diff --git a/os/glog/glog_logger_chaining.go b/os/glog/glog_logger_chaining.go index 0ba7565a6..f4ed24201 100644 --- a/os/glog/glog_logger_chaining.go +++ b/os/glog/glog_logger_chaining.go @@ -8,6 +8,7 @@ package glog import ( "context" + "github.com/fatih/color" "github.com/gogf/gf/internal/intlog" "io" @@ -246,13 +247,15 @@ func (l *Logger) Async(enabled ...bool) *Logger { return logger } -func (l *Logger) Color(color logColor) *Logger { +// Color is a chaining function, +// which set level prefix color logging output feature. +func (l *Logger) Color(color color.Attribute) *Logger { logger := (*Logger)(nil) if l.parent == nil { logger = l.Clone() } else { logger = l } - logger.SetColor(color) + logger.config.currentColor = color return logger } diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index ec970897c..620351fd5 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -9,6 +9,7 @@ package glog import ( "errors" "fmt" + "github.com/fatih/color" "io" "strings" "time" @@ -22,28 +23,28 @@ import ( // Config is the configuration object for logger. type Config struct { - Handlers []Handler `json:"-"` // Logger handlers which implement feature similar as middleware. - Writer io.Writer `json:"-"` // Customized io.Writer. - Flags int `json:"flags"` // Extra flags for logging output features. - Path string `json:"path"` // Logging directory path. - File string `json:"file"` // Format for logging file. - Level int `json:"level"` // Output level. - Prefix string `json:"prefix"` // Prefix string for every logging content. - StSkip int `json:"stSkip"` // Skip count for stack. - StStatus int `json:"stStatus"` // Stack status(1: enabled - default; 0: disabled) - StFilter string `json:"stFilter"` // Stack string filter. - CtxKeys []interface{} `json:"ctxKeys"` // Context keys for logging, which is used for value retrieving from context. - HeaderPrint bool `json:"header"` // Print header or not(true in default). - StdoutPrint bool `json:"stdout"` // Output to stdout or not(true in default). - LevelPrefixes map[int]string `json:"levelPrefixes"` // Logging level to its prefix string mapping. - RotateSize int64 `json:"rotateSize"` // Rotate the logging file if its size > 0 in bytes. - RotateExpire time.Duration `json:"rotateExpire"` // Rotate the logging file if its mtime exceeds this duration. - RotateBackupLimit int `json:"rotateBackupLimit"` // Max backup for rotated files, default is 0, means no backups. - RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration. - RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression. - RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default. - FileColor bool `json:"fileColor"` // Logging level prefix with color or not (false in default). - color logColor `json:"-"` + Handlers []Handler `json:"-"` // Logger handlers which implement feature similar as middleware. + Writer io.Writer `json:"-"` // Customized io.Writer. + Flags int `json:"flags"` // Extra flags for logging output features. + Path string `json:"path"` // Logging directory path. + File string `json:"file"` // Format for logging file. + Level int `json:"level"` // Output level. + Prefix string `json:"prefix"` // Prefix string for every logging content. + StSkip int `json:"stSkip"` // Skip count for stack. + StStatus int `json:"stStatus"` // Stack status(1: enabled - default; 0: disabled) + StFilter string `json:"stFilter"` // Stack string filter. + CtxKeys []interface{} `json:"ctxKeys"` // Context keys for logging, which is used for value retrieving from context. + HeaderPrint bool `json:"header"` // Print header or not(true in default). + StdoutPrint bool `json:"stdout"` // Output to stdout or not(true in default). + LevelPrefixes map[int]string `json:"levelPrefixes"` // Logging level to its prefix string mapping. + RotateSize int64 `json:"rotateSize"` // Rotate the logging file if its size > 0 in bytes. + RotateExpire time.Duration `json:"rotateExpire"` // Rotate the logging file if its mtime exceeds this duration. + RotateBackupLimit int `json:"rotateBackupLimit"` // Max backup for rotated files, default is 0, means no backups. + RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration. + RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression. + RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default. + FileColorEnable bool `json:"fileColorEnable"` // Logging level prefix with color or not (false in default). + currentColor color.Attribute `json:"-"` } // DefaultConfig returns the default configuration for logger. @@ -254,7 +255,3 @@ func (l *Logger) SetPrefix(prefix string) { func (l *Logger) SetHandlers(handlers ...Handler) { l.config.Handlers = append(handlers, defaultHandler) } - -func (l *Logger) SetColor(color logColor) { - l.config.color = color -} diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index bfb15d94b..6e1b501ca 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -9,7 +9,7 @@ package glog import ( "bytes" "context" - "fmt" + "github.com/fatih/color" "time" ) @@ -47,7 +47,7 @@ func (i *HandlerInput) Buffer(withColor ...bool) *bytes.Buffer { buffer := bytes.NewBuffer(nil) buffer.WriteString(i.TimeFormat) if i.LevelFormat != "" { - if i.logger.config.FileColor || (len(withColor) > 0 && withColor[0] == mustWithColor) { + if i.logger.config.FileColorEnable || (len(withColor) > 0 && withColor[0] == mustWithColor) { i.addStringToBuffer(buffer, i.getLevelFormatWithColor()) } else { i.addStringToBuffer(buffer, i.LevelFormat) @@ -75,11 +75,11 @@ func (i *HandlerInput) Buffer(withColor ...bool) *bytes.Buffer { // getLevelFormatWithColor returns the prefix string with color. func (i *HandlerInput) getLevelFormatWithColor() string { s := i.LevelFormat - color := defaultLevelColor[i.Level] - if i.logger.config.color != 0 { - color = i.logger.config.color + fg := defaultLevelColor[i.Level] + if i.logger.config.currentColor != 0 { + fg = i.logger.config.currentColor } - return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", color, s) + return color.New(fg).Sprint(s) } func (i *HandlerInput) String() string { diff --git a/os/glog/glog_logger_level.go b/os/glog/glog_logger_level.go index 49a4c1f94..4cdad5624 100644 --- a/os/glog/glog_logger_level.go +++ b/os/glog/glog_logger_level.go @@ -9,6 +9,7 @@ package glog import ( "errors" "fmt" + "github.com/fatih/color" "strings" ) @@ -29,10 +30,8 @@ const ( LEVEL_FATA // 1024 ) -type logColor int - const ( - COLOR_BLACK logColor = 30 + iota + COLOR_BLACK = 30 + iota COLOR_RED COLOR_GREEN COLOR_YELLOW @@ -55,7 +54,7 @@ var defaultLevelPrefixes = map[int]string{ } // defaultLevelColor defines the default level and its mapping prefix string. -var defaultLevelColor = map[int]logColor{ +var defaultLevelColor = map[int]color.Attribute{ LEVEL_DEBU: COLOR_YELLOW, LEVEL_INFO: COLOR_GREEN, LEVEL_NOTI: COLOR_CYAN, From 1e628b9edb8ec2ea2c498a4146280e80bcffdf99 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 30 Jun 2021 00:15:52 +0800 Subject: [PATCH 358/492] move command tool from repo gf-cli to sub folder --- go.mod | 7 +- go.sum | 18 +- tool/gf/README.MD | 63 ++ tool/gf/README_ZH.MD | 65 ++ tool/gf/boot/boot.go | 31 + tool/gf/commands/build/build.go | 341 +++++++++ tool/gf/commands/docker/docker.go | 99 +++ tool/gf/commands/env/env.go | 44 ++ tool/gf/commands/fix/fix.go | 7 + tool/gf/commands/gen/gen.go | 54 ++ tool/gf/commands/gen/gen_dao.go | 699 ++++++++++++++++++ tool/gf/commands/gen/gen_dao_template_dao.go | 73 ++ .../gf/commands/gen/gen_dao_template_model.go | 18 + tool/gf/commands/gen/gen_pb.go | 77 ++ tool/gf/commands/gen/gen_pbentity.go | 447 +++++++++++ tool/gf/commands/gen/gen_pbentity_template.go | 17 + tool/gf/commands/get/get.go | 33 + tool/gf/commands/initialize/initialize.go | 110 +++ tool/gf/commands/install/install.go | 192 +++++ tool/gf/commands/mod/mod.go | 113 +++ tool/gf/commands/pack/pack.go | 80 ++ tool/gf/commands/run/run.go | 212 ++++++ tool/gf/commands/swagger/swagger.go | 154 ++++ tool/gf/commands/update/update.go | 95 +++ tool/gf/config/url.toml | 17 + tool/gf/go.mod | 15 + tool/gf/go.sum | 93 +++ tool/gf/library/allyes/allyes.go | 22 + tool/gf/library/mlog/mlog.go | 63 ++ tool/gf/library/proxy/proxy.go | 33 + tool/gf/library/utils/utils.go | 18 + tool/gf/main.go | 215 ++++++ tool/gf/packed/config.go | 9 + 33 files changed, 3528 insertions(+), 6 deletions(-) create mode 100644 tool/gf/README.MD create mode 100644 tool/gf/README_ZH.MD create mode 100644 tool/gf/boot/boot.go create mode 100644 tool/gf/commands/build/build.go create mode 100644 tool/gf/commands/docker/docker.go create mode 100644 tool/gf/commands/env/env.go create mode 100644 tool/gf/commands/fix/fix.go create mode 100644 tool/gf/commands/gen/gen.go create mode 100644 tool/gf/commands/gen/gen_dao.go create mode 100644 tool/gf/commands/gen/gen_dao_template_dao.go create mode 100644 tool/gf/commands/gen/gen_dao_template_model.go create mode 100644 tool/gf/commands/gen/gen_pb.go create mode 100644 tool/gf/commands/gen/gen_pbentity.go create mode 100644 tool/gf/commands/gen/gen_pbentity_template.go create mode 100644 tool/gf/commands/get/get.go create mode 100644 tool/gf/commands/initialize/initialize.go create mode 100644 tool/gf/commands/install/install.go create mode 100644 tool/gf/commands/mod/mod.go create mode 100644 tool/gf/commands/pack/pack.go create mode 100644 tool/gf/commands/run/run.go create mode 100644 tool/gf/commands/swagger/swagger.go create mode 100644 tool/gf/commands/update/update.go create mode 100644 tool/gf/config/url.toml create mode 100644 tool/gf/go.mod create mode 100644 tool/gf/go.sum create mode 100644 tool/gf/library/allyes/allyes.go create mode 100644 tool/gf/library/mlog/mlog.go create mode 100644 tool/gf/library/proxy/proxy.go create mode 100644 tool/gf/library/utils/utils.go create mode 100644 tool/gf/main.go create mode 100644 tool/gf/packed/config.go diff --git a/go.mod b/go.mod index 549075d90..25bb74983 100644 --- a/go.mod +++ b/go.mod @@ -5,17 +5,20 @@ go 1.14 require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.4.9 github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 github.com/gomodule/redigo v2.0.0+incompatible - github.com/gorilla/websocket v1.4.1 - github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf + github.com/gorilla/websocket v1.4.2 + github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 github.com/mattn/go-runewidth v0.0.10 // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/olekukonko/tablewriter v0.0.5 go.opentelemetry.io/otel v1.0.0-RC1 go.opentelemetry.io/otel/oteltest v1.0.0-RC1 go.opentelemetry.io/otel/trace v1.0.0-RC1 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 golang.org/x/text v0.3.4 + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c ) diff --git a/go.sum b/go.sum index 45d8f1356..61103d738 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUao github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw= @@ -12,13 +14,18 @@ github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNu github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= -github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 h1:h3iGUlU8HyW4baKd6D+h1mwOHnM2kwskSuG6Bv4tSbc= +github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -35,6 +42,7 @@ go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1G go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58= go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= @@ -53,5 +61,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tool/gf/README.MD b/tool/gf/README.MD new file mode 100644 index 000000000..5345ea818 --- /dev/null +++ b/tool/gf/README.MD @@ -0,0 +1,63 @@ +# GF-CLI +English | [简体中文](README_ZH.MD) + +`gf` is a powerful CLI tool for building [GoFrame](https://goframe.org) application with convenience. + +## 1. Install + +> You might need setting the goproxy to make through building. + +1. Latest version + ``` + go install github.com/gogf/gf/tool/gf@latest + ``` +1. Specified version + ``` + go install github.com/gogf/gf/tool/gf@v1.16.0 + ``` +1. Database `sqlite` and `oracle` are not support in `gf gen` command in default as it needs `cgo` and `gcc`, you can manually make some changes to the source codes and do the building. + +## 2. Commands +```html +$ gf +USAGE + gf COMMAND [ARGUMENT] [OPTION] + +COMMAND + env show current Golang environment variables + get install or update GF to system in default... + gen automatically generate go files for ORM models... + mod extra features for go modules... + run running go codes with hot-compiled-like feature... + init initialize an empty GF project at current working directory... + help show more information about a specified command + pack packing any file/directory to a resource file, or a go file... + build cross-building go project for lots of platforms... + docker create a docker image for current GF project... + swagger swagger feature for current project... + update update current gf binary to latest one (might need root/admin permission) + install install gf binary to system (might need root/admin permission) + version show current binary version info + +OPTION + -y all yes for all command without prompt ask + -?,-h show this help or detail for specified command + -v,-i show version information + +ADDITIONAL + Use 'gf help COMMAND' or 'gf COMMAND -h' for detail about a command, which has '...' + in the tail of their comments. +``` + +## 3. FAQ + +### 1). Command `gf run` returns `pipe: too many open files` + +Please use `ulimit -n 65535` to enlarge your system configuration for max open files for current terminal shell session, and then `gf run`. + + + + + + + diff --git a/tool/gf/README_ZH.MD b/tool/gf/README_ZH.MD new file mode 100644 index 000000000..7ad47d8f0 --- /dev/null +++ b/tool/gf/README_ZH.MD @@ -0,0 +1,65 @@ +# GF-CLI +[English](README.MD) | 简体中文 + +`gf`是一款强大的[GoFrame](https://goframe.org)开发工具链,使得我们开发基于`GoFrame`框架的项目更加便捷。 + +## 1. 安装 + +1. 最新版本 + ``` + go install github.com/gogf/gf/tool/gf@latest + ``` +1. 指定版本 + ``` + go install github.com/gogf/gf/tool/gf@v1.16.0 + ``` +> 注意:在`gf gen`命令中,由于`sqlite`和`oracle`数据库需要`cgo`和`gcc`支持,因此预编译的二进制中不提供对这两个数据库的支持,您需要手动修改源码,去掉对应源码文件中指定数据库类型的`import`注释后手动编译支持。 + +## 2. 命令 +```html +$ gf +USAGE + gf COMMAND [ARGUMENT] [OPTION] + +COMMAND + env show current Golang environment variables + get install or update GF to system in default... + gen automatically generate go files for ORM models... + mod extra features for go modules... + run running go codes with hot-compiled-like feature... + init initialize an empty GF project at current working directory... + help show more information about a specified command + pack packing any file/directory to a resource file, or a go file... + build cross-building go project for lots of platforms... + docker create a docker image for current GF project... + swagger swagger feature for current project... + update update current gf binary to latest one (might need root/admin permission) + install install gf binary to system (might need root/admin permission) + version show current binary version info + +OPTION + -y all yes for all command without prompt ask + -?,-h show this help or detail for specified command + -v,-i show version information + +ADDITIONAL + Use 'gf help COMMAND' or 'gf COMMAND -h' for detail about a command, which has '...' + in the tail of their comments. +``` + +## 3. 文档 + +完善详尽的中文文档请参考`GoFrame`官网板块:[开发工具](https://itician.org/pages/viewpage.action?pageId=1114260) + +## 4. FAQ + +### 1). `gf run` 命令报错 `pipe: too many open files` + +请执行`ulimit -n 65535`命令扩展您当前终端会话支持的最大文件打开数,随后再执行`gf run`。需要注意的是该命令仅对当前终端会话有效。 + + + + + + + diff --git a/tool/gf/boot/boot.go b/tool/gf/boot/boot.go new file mode 100644 index 000000000..ef11ced62 --- /dev/null +++ b/tool/gf/boot/boot.go @@ -0,0 +1,31 @@ +package boot + +import ( + "github.com/gogf/gf/os/genv" + _ "github.com/gogf/gf/tool/gf/packed" + + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/text/gstr" +) + +func init() { + // Force using configuration file in current working directory. + // In case of source environment. + genv.Set("GF_GCFG_PATH", gfile.Pwd()) + handleZshAlias() +} + +// zsh alias "git fetch" conflicts checks. +func handleZshAlias() { + home, err := gfile.Home() + if err == nil { + zshPath := gfile.Join(home, ".zshrc") + if gfile.Exists(zshPath) { + aliasCommand := `alias gf=gf` + content := gfile.GetContents(zshPath) + if !gstr.Contains(content, aliasCommand) { + _ = gfile.PutContentsAppend(zshPath, "\n"+aliasCommand+"\n") + } + } + } +} diff --git a/tool/gf/commands/build/build.go b/tool/gf/commands/build/build.go new file mode 100644 index 000000000..f8b157a60 --- /dev/null +++ b/tool/gf/commands/build/build.go @@ -0,0 +1,341 @@ +package build + +import ( + "encoding/json" + "fmt" + "github.com/gogf/gf/encoding/gbase64" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/genv" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gproc" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/text/gregex" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/mlog" + "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/gutil" + "regexp" + "runtime" + "strings" +) + +// https://golang.google.cn/doc/install/source +const platforms = ` + darwin amd64 + darwin arm64 + ios amd64 + ios arm64 + freebsd 386 + freebsd amd64 + freebsd arm + linux 386 + linux amd64 + linux arm + linux arm64 + linux ppc64 + linux ppc64le + linux mips + linux mipsle + linux mips64 + linux mips64le + netbsd 386 + netbsd amd64 + netbsd arm + openbsd 386 + openbsd amd64 + openbsd arm + windows 386 + windows amd64 + android arm + dragonfly amd64 + plan9 386 + plan9 amd64 + solaris amd64 +` + +const ( + nodeNameInConfigFile = "gfcli.build" // nodeNameInConfigFile is the node name for compiler configurations in configuration file. + packedGoFileName = "build_pack_data.go" // packedGoFileName specifies the file name for packing common folders into one single go file. +) + +func Help() { + mlog.Print(gstr.TrimLeft(` +USAGE + gf build FILE [OPTION] + +ARGUMENT + FILE building file path. + +OPTION + -n, --name output binary name + -v, --version output binary version + -a, --arch output binary architecture, multiple arch separated with ',' + -s, --system output binary system, multiple os separated with ',' + -o, --output output binary path, used when building single binary file + -p, --path output binary directory path, default is './bin' + -e, --extra extra custom "go build" options + -m, --mod like "-mod" option of "go build", use "-m none" to disable go module + -c, --cgo enable or disable cgo feature, it's disabled in default + --pack pack specified folder into packed/data.go before building. + --swagger auto parse and pack swagger into packed/swagger.go before building. + +EXAMPLES + gf build main.go + gf build main.go --swagger + gf build main.go --pack public,template + gf build main.go --cgo + gf build main.go -m none + gf build main.go -n my-app -a all -s all + gf build main.go -n my-app -a amd64,386 -s linux -p . + gf build main.go -n my-app -v 1.0 -a amd64,386 -s linux,windows,darwin -p ./docker/bin + +DESCRIPTION + The "build" command is most commonly used command, which is designed as a powerful wrapper for + "go build" command for convenience cross-compiling usage. + It provides much more features for building binary: + 1. Cross-Compiling for many platforms and architectures. + 2. Configuration file support for compiling. + 3. Build-In Variables. + +PLATFORMS + darwin amd64,arm64 + freebsd 386,amd64,arm + linux 386,amd64,arm,arm64,ppc64,ppc64le,mips,mipsle,mips64,mips64le + netbsd 386,amd64,arm + openbsd 386,amd64,arm + windows 386,amd64 +`)) +} + +func Run() { + mlog.SetHeaderPrint(true) + parser, err := gcmd.Parse(g.MapStrBool{ + "n,name": true, + "v,version": true, + "a,arch": true, + "s,system": true, + "o,output": true, + "p,path": true, + "e,extra": true, + "m,mod": true, + "pack": true, + "c,cgo": false, + "swagger": false, + }) + if err != nil { + mlog.Fatal(err) + } + file := parser.GetArg(2) + if len(file) < 1 { + // Check and use the main.go file. + if gfile.Exists("main.go") { + file = "main.go" + } else { + mlog.Fatal("build file path cannot be empty") + } + } + path := getOption(parser, "path", "./bin") + name := getOption(parser, "name", gfile.Name(file)) + if len(name) < 1 || name == "*" { + mlog.Fatal("name cannot be empty") + } + var ( + mod = getOption(parser, "mod") + extra = getOption(parser, "extra") + ) + if mod != "" && mod != "none" { + mlog.Debugf(`mod is %s`, mod) + if extra == "" { + extra = fmt.Sprintf(`-mod=%s`, mod) + } else { + extra = fmt.Sprintf(`-mod=%s %s`, mod, extra) + } + } + if extra != "" { + extra += " " + } + var ( + cgoEnabled = gconv.Bool(getOption(parser, "cgo")) + version = getOption(parser, "version") + outputPath = getOption(parser, "output") + archOption = getOption(parser, "arch") + systemOption = getOption(parser, "system") + packStr = getOption(parser, "pack") + customSystems = gstr.SplitAndTrim(systemOption, ",") + customArches = gstr.SplitAndTrim(archOption, ",") + ) + if !cgoEnabled { + cgoEnabled = parser.ContainsOpt("cgo") + } + if len(version) > 0 { + path += "/" + version + } + // System and arch checks. + var ( + spaceRegex = regexp.MustCompile(`\s+`) + platformMap = make(map[string]map[string]bool) + ) + for _, line := range strings.Split(strings.TrimSpace(platforms), "\n") { + line = gstr.Trim(line) + line = spaceRegex.ReplaceAllString(line, " ") + var ( + array = strings.Split(line, " ") + system = strings.TrimSpace(array[0]) + arch = strings.TrimSpace(array[1]) + ) + if platformMap[system] == nil { + platformMap[system] = make(map[string]bool) + } + platformMap[system][arch] = true + } + // Auto swagger. + if containsOption(parser, "swagger") { + if err := gproc.ShellRun(`gf swagger`); err != nil { + return + } + if gfile.Exists("swagger") { + packCmd := fmt.Sprintf(`gf pack %s packed/%s`, "swagger", packedGoFileName) + mlog.Print(packCmd) + if err := gproc.ShellRun(packCmd); err != nil { + return + } + } + } + + // Auto packing. + if len(packStr) > 0 { + dataFilePath := fmt.Sprintf(`packed/%s`, packedGoFileName) + if !gfile.Exists(dataFilePath) { + // Remove the go file that is automatically packed resource. + defer func() { + gfile.Remove(dataFilePath) + mlog.Printf(`remove the automatically generated resource go file: %s`, dataFilePath) + }() + } + packCmd := fmt.Sprintf(`gf pack %s %s`, packStr, dataFilePath) + mlog.Print(packCmd) + gproc.ShellRun(packCmd) + } + + // Injected information by building flags. + ldFlags := fmt.Sprintf(`-X 'github.com/gogf/gf/os/gbuild.builtInVarStr=%v'`, getBuildInVarStr()) + + // start building + mlog.Print("start building...") + if cgoEnabled { + genv.Set("CGO_ENABLED", "1") + } else { + genv.Set("CGO_ENABLED", "0") + } + var ( + cmd = "" + ext = "" + ) + for system, item := range platformMap { + cmd = "" + ext = "" + if len(customSystems) > 0 && customSystems[0] != "all" && !gstr.InArray(customSystems, system) { + continue + } + for arch, _ := range item { + if len(customArches) > 0 && customArches[0] != "all" && !gstr.InArray(customArches, arch) { + continue + } + if len(customSystems) == 0 && len(customArches) == 0 { + if runtime.GOOS == "windows" { + ext = ".exe" + } + // Single binary building, output the binary to current working folder. + output := "" + if len(outputPath) > 0 { + output = "-o " + outputPath + ext + } else { + output = "-o " + name + ext + } + cmd = fmt.Sprintf(`go build %s -ldflags "%s" %s %s`, output, ldFlags, extra, file) + } else { + // Cross-building, output the compiled binary to specified path. + if system == "windows" { + ext = ".exe" + } + genv.Set("GOOS", system) + genv.Set("GOARCH", arch) + cmd = fmt.Sprintf( + `go build -o %s/%s/%s%s -ldflags "%s" %s%s`, + path, system+"_"+arch, name, ext, ldFlags, extra, file, + ) + } + // It's not necessary printing the complete command string. + cmdShow, _ := gregex.ReplaceString(`\s+(-ldflags ".+?")\s+`, " ", cmd) + mlog.Print(cmdShow) + if _, err := gproc.ShellExec(cmd); err != nil { + mlog.Printf("failed to build, os:%s, arch:%s", system, arch) + } + // single binary building. + if len(customSystems) == 0 && len(customArches) == 0 { + goto buildDone + } + } + } +buildDone: + mlog.Print("done!") +} + +// getOption retrieves option value from parser and configuration file. +// It returns the default value specified by parameter <value> is no value found. +func getOption(parser *gcmd.Parser, name string, value ...string) (result string) { + result = parser.GetOpt(name) + if result == "" && g.Config().Available() { + result = g.Config().GetString(nodeNameInConfigFile + "." + name) + } + if result == "" && len(value) > 0 { + result = value[0] + } + return +} + +// containsOption checks whether the command option or the configuration file containing +// given option name. +func containsOption(parser *gcmd.Parser, name string) bool { + result := parser.ContainsOpt(name) + if !result && g.Config().Available() { + result = g.Config().Contains(nodeNameInConfigFile + "." + name) + } + return result +} + +// getBuildInVarMapJson retrieves and returns the custom build-in variables in configuration +// file as json. +func getBuildInVarStr() string { + buildInVarMap := g.Map{} + if g.Config().Available() { + configMap := g.Config().GetMap(nodeNameInConfigFile) + if len(configMap) > 0 { + _, v := gutil.MapPossibleItemByKey(configMap, "VarMap") + if v != nil { + buildInVarMap = gconv.Map(v) + } + } + } + buildInVarMap["builtGit"] = getGitCommit() + buildInVarMap["builtTime"] = gtime.Now().String() + b, err := json.Marshal(buildInVarMap) + if err != nil { + mlog.Fatal(err) + } + return gbase64.EncodeToString(b) +} + +// getGitCommit retrieves and returns the latest git commit hash string if present. +func getGitCommit() string { + if gproc.SearchBinary("git") == "" { + return "" + } + if s, _ := gproc.ShellExec("git rev-list -1 HEAD"); s != "" { + if !gstr.Contains(s, " ") && !gstr.Contains(s, "fatal") { + return gstr.Trim(s) + } + } + return "" +} diff --git a/tool/gf/commands/docker/docker.go b/tool/gf/commands/docker/docker.go new file mode 100644 index 000000000..69e0c9d95 --- /dev/null +++ b/tool/gf/commands/docker/docker.go @@ -0,0 +1,99 @@ +package docker + +import ( + "fmt" + "github.com/gogf/gf/container/garray" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gproc" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/mlog" + "os" + "strings" +) + +func Help() { + mlog.Print(gstr.TrimLeft(` +USAGE + gf docker [FILE] [OPTION] + +ARGUMENT + FILE file path for "gf build", it's "main.go" in default. + OPTION the same options as "docker build" except some options as follows defined + +OPTION + -p, --push auto push the docker image to docker registry if "-t" option passed + +EXAMPLES + gf docker + gf docker -t hub.docker.com/john/image:tag + gf docker -p -t hub.docker.com/john/image:tag + gf docker main.go + gf docker main.go -t hub.docker.com/john/image:tag + gf docker main.go -t hub.docker.com/john/image:tag + gf docker main.go -p -t hub.docker.com/john/image:tag + +DESCRIPTION + The "docker" command builds the GF project to a docker images. + It runs "gf build" firstly to compile the project to binary file. + It then runs "docker build" command automatically to generate the docker image. + You should have docker installed, and there must be a Dockerfile in the root of the project. + +`)) +} + +func Run() { + var err error + autoPush := false + array := garray.NewStrArrayFromCopy(os.Args) + index := array.Search("--push") + if index < 0 { + index = array.Search("-p") + } + if index != -1 { + array.Remove(index) + autoPush = true + } + file := "main.go" + extraOptions := "" + if array.Len() > 2 { + v, _ := array.Get(2) + if gfile.ExtName(v) == "go" { + file, _ = array.Get(2) + if array.Len() > 3 { + extraOptions = strings.Join(array.SubSlice(3), " ") + } + } else { + extraOptions = strings.Join(array.SubSlice(2), " ") + } + } + // Binary build. + err = gproc.ShellRun(fmt.Sprintf(`gf build %s -a amd64 -s linux`, file)) + if err != nil { + return + } + // Docker build. + err = gproc.ShellRun(fmt.Sprintf(`docker build . %s`, extraOptions)) + if err != nil { + return + } + // Docker push. + if !autoPush { + return + } + parser, err := gcmd.Parse(g.MapStrBool{ + "t,tag": true, + }) + if err != nil { + mlog.Fatal(err) + } + tag := parser.GetOpt("t") + if tag == "" { + return + } + err = gproc.ShellRun(fmt.Sprintf(`docker push %s`, tag)) + if err != nil { + return + } +} diff --git a/tool/gf/commands/env/env.go b/tool/gf/commands/env/env.go new file mode 100644 index 000000000..06a163b92 --- /dev/null +++ b/tool/gf/commands/env/env.go @@ -0,0 +1,44 @@ +package env + +import ( + "bytes" + "github.com/gogf/gf/os/gproc" + "github.com/gogf/gf/text/gregex" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/mlog" + "github.com/olekukonko/tablewriter" +) + +func Run() { + result, err := gproc.ShellExec("go env") + if err != nil { + mlog.Fatal(err) + } + if result == "" { + mlog.Fatal(`retrieving Golang environment variables failed, did you install Golang?`) + } + var ( + lines = gstr.Split(result, "\n") + buffer = bytes.NewBuffer(nil) + ) + array := make([][]string, 0) + for _, line := range lines { + line = gstr.Trim(line) + if line == "" { + continue + } + if gstr.Pos(line, "set ") == 0 { + line = line[4:] + } + match, _ := gregex.MatchString(`(.+?)=(.*)`, line) + if len(match) < 3 { + mlog.Fatalf(`invalid Golang environment variable: "%s"`, line) + } + array = append(array, []string{gstr.Trim(match[1]), gstr.Trim(match[2])}) + } + tw := tablewriter.NewWriter(buffer) + tw.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT}) + tw.AppendBulk(array) + tw.Render() + mlog.Print(buffer.String()) +} diff --git a/tool/gf/commands/fix/fix.go b/tool/gf/commands/fix/fix.go new file mode 100644 index 000000000..1bcfa7463 --- /dev/null +++ b/tool/gf/commands/fix/fix.go @@ -0,0 +1,7 @@ +package fix + +import "github.com/gogf/gf/tool/gf/library/mlog" + +func Run() { + mlog.Print("this feature is not completed yet") +} diff --git a/tool/gf/commands/gen/gen.go b/tool/gf/commands/gen/gen.go new file mode 100644 index 000000000..1a5a03d27 --- /dev/null +++ b/tool/gf/commands/gen/gen.go @@ -0,0 +1,54 @@ +package gen + +import ( + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/mlog" +) + +func Help() { + switch gcmd.GetArg(2) { + case "dao": + HelpDao() + + case "pb": + HelpPb() + + case "pbentity": + HelpPbEntity() + + default: + mlog.Print(gstr.TrimLeft(` +USAGE + gf gen TYPE [OPTION] + +TYPE + dao generate dao and model files. + pb parse proto files and generate protobuf go files. + pbentity generate entity message files in protobuf3 format. + +DESCRIPTION + The "gen" command is designed for multiple generating purposes. + It's currently supporting generating go files for ORM models, protobuf and protobuf entity files. + Please use "gf gen dao -h" or "gf gen model -h" for specified type help. +`)) + } +} + +func Run() { + genType := gcmd.GetArg(2) + if genType == "" { + mlog.Print("generating type cannot be empty") + return + } + switch genType { + case "dao": + doGenDao() + + case "pb": + doGenPb() + + case "pbentity": + doGenPbEntity() + } +} diff --git a/tool/gf/commands/gen/gen_dao.go b/tool/gf/commands/gen/gen_dao.go new file mode 100644 index 000000000..73701fef7 --- /dev/null +++ b/tool/gf/commands/gen/gen_dao.go @@ -0,0 +1,699 @@ +package gen + +import ( + "bytes" + "context" + "fmt" + "github.com/gogf/gf/container/garray" + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/text/gregex" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/mlog" + "github.com/gogf/gf/tool/gf/library/utils" + "github.com/gogf/gf/util/gconv" + "github.com/olekukonko/tablewriter" + "strings" + + _ "github.com/denisenkom/go-mssqldb" + _ "github.com/lib/pq" + //_ "github.com/mattn/go-oci8" + //_ "github.com/mattn/go-sqlite3" +) + +// generateDaoReq is the input parameter for generating dao. +type generateDaoReq struct { + TableName string // TableName specifies the table name of the table. + NewTableName string // NewTableName specifies the prefix-stripped name of the table. + PrefixName string // PrefixName specifies the custom prefix name for generated dao and model struct. + GroupName string // GroupName specifies the group name of database configuration node for generated DAO. + ModName string // ModName specifies the module name of current golang project, which is used for import purpose. + JsonCase string // JsonCase specifies the case of generated 'json' tag for model struct, value from gstr.Case* function names. + DirPath string // DirPath specifies the directory path for generated files. + StdTime bool // StdTime defines using time.Time from stdlib instead of gtime.Time for generated time/date fields of tables. + ModelIndexFileName string // Custom name for storing generated model content. + TplDaoIndexPath string // TplDaoIndexPath specifies the file path for generating dao index files. + TplDaoInternalPath string // TplDaoInternalPath specifies the file path for generating dao internal files. + TplModelIndexPath string // TplModelIndexPath specifies the file path for generating model index content. + TplModelStructPath string // TplModelStructPath specifies the file path for generating model struct content. +} + +const ( + genDaoDefaultPath = "./app" + nodeNameGenDaoInConfigFile = "gfcli.gen.dao" + defaultModelIndexFileName = "model.go" +) + +func HelpDao() { + mlog.Print(gstr.TrimLeft(` +USAGE + gf gen dao [OPTION] + +OPTION + -/--path directory path for generated files. + -l, --link database configuration, the same as the ORM configuration of GoFrame. + -t, --tables generate models only for given tables, multiple table names separated with ',' + -e, --tablesEx generate models excluding given tables, multiple table names separated with ',' + -g, --group specifying the configuration group name of database for generated ORM instance, + it's not necessary and the default value is "default" + -p, --prefix add prefix for all table of specified link/database tables. + -r, --removePrefix remove specified prefix of the table, multiple prefix separated with ',' + -m, --mod module name for generated golang file imports. + -j, --jsonCase generated json tag case for model struct, cases are as follows: + | Case | Example | + |---------------- |--------------------| + | Camel | AnyKindOfString | + | CamelLower | anyKindOfString | default + | Snake | any_kind_of_string | + | SnakeScreaming | ANY_KIND_OF_STRING | + | SnakeFirstUpper | rgb_code_md5 | + | Kebab | any-kind-of-string | + | KebabScreaming | ANY-KIND-OF-STRING | + -/--stdTime use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables. + -/--modelFile custom file name for storing generated model content. + -/--tplDaoIndex template content for Dao index files generating. + -/--tplDaoInternal template content for Dao internal files generating. + -/--tplModelIndex template content for Model index files generating. + -/--tplModelStruct template content for Model internal files generating. + +CONFIGURATION SUPPORT + Options are also supported by configuration file. + It's suggested using configuration file instead of command line arguments making producing. + The configuration node name is "gf.gen.dao", which also supports multiple databases, for example: + [gfcli] + [[gfcli.gen.dao]] + link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + tables = "order,products" + jsonCase = "CamelLower" + [[gfcli.gen.dao]] + link = "mysql:root:12345678@tcp(127.0.0.1:3306)/primary" + path = "./my-app" + prefix = "primary_" + tables = "user, userDetail" + +EXAMPLES + gf gen dao + gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + gf gen dao -path ./model -c config.yaml -g user-center -t user,user_detail,user_login + gf gen dao -r user_ +`)) +} + +// doGenDao implements the "gen dao" command. +func doGenDao() { + parser, err := gcmd.Parse(g.MapStrBool{ + "path": true, + "m,mod": true, + "l,link": true, + "t,tables": true, + "e,tablesEx": true, + "g,group": true, + "c,config": true, + "p,prefix": true, + "r,removePrefix": true, + "j,jsonCase": true, + "stdTime": false, + "modelFile": true, + "tplDaoIndex": true, + "tplDaoInternal": true, + "tplModelIndex": true, + "tplModelStruct": true, + }) + if err != nil { + mlog.Fatal(err) + } + config := g.Cfg() + if config.Available() { + v := config.GetVar(nodeNameGenDaoInConfigFile) + if v.IsEmpty() && g.IsEmpty(parser.GetOptAll()) { + mlog.Fatal(`command arguments and configurations not found for generating dao files`) + } + if v.IsSlice() { + for i := 0; i < len(v.Interfaces()); i++ { + doGenDaoForArray(i, parser) + } + } else { + doGenDaoForArray(-1, parser) + } + } else { + doGenDaoForArray(-1, parser) + } + mlog.Print("done!") +} + +// doGenDaoForArray implements the "gen dao" command for configuration array. +func doGenDaoForArray(index int, parser *gcmd.Parser) { + var ( + err error + db gdb.DB + modName = getOptionOrConfigForDao(index, parser, "mod") // Go module name, eg: github.com/gogf/gf. + dirPath = getOptionOrConfigForDao(index, parser, "path", genDaoDefaultPath) // Generated directory path. + tablesStr = getOptionOrConfigForDao(index, parser, "tables") // Tables that will be generated. + tablesEx = getOptionOrConfigForDao(index, parser, "tablesEx") // Tables that will be excluded for generating. + prefixName = getOptionOrConfigForDao(index, parser, "prefix") // Add prefix to DAO and Model struct name. + linkInfo = getOptionOrConfigForDao(index, parser, "link") // Custom database link. + configPath = getOptionOrConfigForDao(index, parser, "config") // Config file path, eg: ./config/db.toml. + configGroup = getOptionOrConfigForDao(index, parser, "group", "default") // Group name of database configuration node for generated DAO. + removePrefix = getOptionOrConfigForDao(index, parser, "removePrefix") // Remove prefix from table name. + jsonCase = getOptionOrConfigForDao(index, parser, "jsonCase", "CamelLower") // Case configuration for 'json' tag. + stdTime = getOptionOrConfigForDao(index, parser, "stdTime", "false") // Use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables. + modelFileName = getOptionOrConfigForDao(index, parser, "modelFile", defaultModelIndexFileName) // Custom file name for storing generated model content. + tplDaoIndexPath = getOptionOrConfigForDao(index, parser, "tplDaoIndex") // Template file path for generating dao index files. + tplDaoInternalPath = getOptionOrConfigForDao(index, parser, "tplDaoInternal") // Template file path for generating dao internal files. + tplModelIndexPath = getOptionOrConfigForDao(index, parser, "tplModelIndex") // Template file path for generating model index files. + tplModelStructPath = getOptionOrConfigForDao(index, parser, "tplModelStruct") // Template file path for generating model internal files. + ) + if tplDaoIndexPath != "" && (!gfile.Exists(tplDaoIndexPath) || !gfile.IsReadable(tplDaoIndexPath)) { + mlog.Fatalf("template file for dao index files generating does not exist or is not readable: %s", tplDaoIndexPath) + } + if tplDaoInternalPath != "" && (!gfile.Exists(tplDaoInternalPath) || !gfile.IsReadable(tplDaoInternalPath)) { + mlog.Fatalf("template internal for dao internal files generating does not exist or is not readable: %s: %s", tplDaoInternalPath) + } + if tplModelIndexPath != "" && (!gfile.Exists(tplModelIndexPath) || !gfile.IsReadable(tplModelIndexPath)) { + mlog.Fatalf("template file for model index files generating does not exist or is not readable: %s: %s", tplModelIndexPath) + } + if tplModelStructPath != "" && (!gfile.Exists(tplModelStructPath) || !gfile.IsReadable(tplModelStructPath)) { + mlog.Fatalf("template file for model internal files generating does not exist or is not readable: %s: %s", tplModelStructPath) + } + // Make it compatible with old CLI version for option name: remove-prefix + if removePrefix == "" { + removePrefix = getOptionOrConfigForDao(index, parser, "remove-prefix") + } + removePrefixArray := gstr.SplitAndTrim(removePrefix, ",") + if modName == "" { + if !gfile.Exists("go.mod") { + mlog.Fatal("go.mod does not exist in current working directory") + } + var ( + goModContent = gfile.GetContents("go.mod") + match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent) + ) + if len(match) > 1 { + modName = gstr.Trim(match[1]) + } else { + mlog.Fatal("module name does not found in go.mod") + } + } + // It reads database configuration from project configuration file. + if configPath != "" { + path, err := gfile.Search(configPath) + if err != nil { + mlog.Fatalf("search configuration file '%s' failed: %v", configPath, err) + } + if err := g.Cfg().SetPath(gfile.Dir(path)); err != nil { + mlog.Fatalf("set configuration path '%s' failed: %v", path, err) + } + g.Cfg().SetFileName(gfile.Basename(path)) + } + // It uses user passed database configuration. + if linkInfo != "" { + tempGroup := gtime.TimestampNanoStr() + match, _ := gregex.MatchString(`([a-z]+):(.+)`, linkInfo) + if len(match) == 3 { + gdb.AddConfigNode(tempGroup, gdb.ConfigNode{ + Type: gstr.Trim(match[1]), + LinkInfo: gstr.Trim(match[2]), + }) + db, _ = gdb.Instance(tempGroup) + } + } else { + db = g.DB(configGroup) + } + if db == nil { + mlog.Fatal("database initialization failed") + } + + var tableNames []string + if tablesStr != "" { + tableNames = gstr.SplitAndTrim(tablesStr, ",") + } else { + tableNames, err = db.Tables(context.TODO()) + if err != nil { + mlog.Fatalf("fetching tables failed: \n %v", err) + } + } + // Table excluding. + if tablesEx != "" { + array := garray.NewStrArrayFrom(tableNames) + for _, v := range gstr.SplitAndTrim(tablesEx, ",") { + array.RemoveValue(v) + } + tableNames = array.Slice() + } + + // Generating dao & model go files one by one according to given table name. + newTableNames := make([]string, len(tableNames)) + for i, tableName := range tableNames { + newTableName := tableName + for _, v := range removePrefixArray { + newTableName = gstr.TrimLeftStr(newTableName, v, 1) + } + newTableNames[i] = newTableName + generateDaoContentFile(db, generateDaoReq{ + TableName: tableName, + NewTableName: newTableName, + PrefixName: prefixName, + GroupName: configGroup, + ModName: modName, + JsonCase: jsonCase, + DirPath: dirPath, + StdTime: gconv.Bool(stdTime), + TplDaoIndexPath: tplDaoIndexPath, + TplDaoInternalPath: tplDaoInternalPath, + TplModelIndexPath: tplModelIndexPath, + TplModelStructPath: tplModelStructPath, + }) + } + generateDaoModelContentFile(db, tableNames, newTableNames, generateDaoReq{ + JsonCase: jsonCase, + DirPath: dirPath, + StdTime: gconv.Bool(stdTime), + ModelIndexFileName: modelFileName, + TplModelIndexPath: tplModelIndexPath, + TplModelStructPath: tplModelStructPath, + }) +} + +// generateDaoContentFile generates the dao and model content of given table. +func generateDaoContentFile(db gdb.DB, req generateDaoReq) { + // Generating table data preparing. + fieldMap, err := db.TableFields(context.TODO(), req.TableName) + if err != nil { + mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", req.TableName, err) + } + // Change the `newTableName` if `prefixName` is given. + newTableName := req.PrefixName + req.NewTableName + var ( + dirPathDao = gstr.Trim(gfile.Join(req.DirPath, "dao"), "./") + tableNameCamelCase = gstr.CaseCamel(newTableName) + tableNameCamelLowerCase = gstr.CaseCamelLower(newTableName) + tableNameSnakeCase = gstr.CaseSnake(newTableName) + importPrefix = "" + dirRealPath = gfile.RealPath(req.DirPath) + ) + if dirRealPath == "" { + dirRealPath = req.DirPath + importPrefix = dirRealPath + importPrefix = gstr.Trim(dirRealPath, "./") + } else { + importPrefix = gstr.Replace(dirRealPath, gfile.Pwd(), "") + } + importPrefix = gstr.Replace(importPrefix, gfile.Separator, "/") + importPrefix = gstr.Join(g.SliceStr{req.ModName, importPrefix}, "/") + importPrefix, _ = gregex.ReplaceString(`\/{2,}`, `/`, gstr.Trim(importPrefix, "/")) + + fileName := gstr.Trim(tableNameSnakeCase, "-_.") + if len(fileName) > 5 && fileName[len(fileName)-5:] == "_test" { + // Add suffix to avoid the table name which contains "_test", + // which would make the go file a testing file. + fileName += "_table" + } + + // dao - index + generateDaoIndex(tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName, req) + + // dao - internal + generateDaoInternal(tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName, fieldMap, req) +} + +func generateDaoModelContentFile(db gdb.DB, tableNames, newTableNames []string, req generateDaoReq) { + var ( + modelContent string + packageImports string + dirPathModel = gstr.Trim(gfile.Join(req.DirPath, "model"), "./") + ) + + // Model content. + for i, tableName := range tableNames { + fieldMap, err := db.TableFields(context.TODO(), tableName) + if err != nil { + mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", req.TableName, err) + } + modelContent += generateDaoModelStructContent( + tableName, + gstr.CaseCamel(newTableNames[i]), + req.TplModelStructPath, + generateStructDefinitionForModel(gstr.CaseCamel(newTableNames[i]), fieldMap, req), + ) + modelContent += "\n" + } + + // Time package recognition. + if strings.Contains(modelContent, "gtime.Time") { + packageImports = gstr.Trim(` +import ( + "github.com/gogf/gf/os/gtime" +)`) + } else if strings.Contains(modelContent, "time.Time") { + packageImports = gstr.Trim(` +import ( + "time" +)`) + } else { + packageImports = "" + } + + // Generate and write content to golang file. + modelContent = gstr.ReplaceByMap(getTplModelIndexContent(req.TplModelIndexPath), g.MapStrStr{ + "{TplPackageImports}": packageImports, + "{TplModelStructs}": modelContent, + }) + path := gfile.Join(dirPathModel, req.ModelIndexFileName) + if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil { + mlog.Fatalf("writing content to '%s' failed: %v", path, err) + } else { + utils.GoFmt(path) + mlog.Print("generated:", path) + } +} + +func generateDaoModelStructContent(tableName, tableNameCamelCase, tplModelStructPath, structDefine string) string { + return gstr.ReplaceByMap(getTplModelStructContent(tplModelStructPath), g.MapStrStr{ + "{TplTableName}": tableName, + "{TplTableNameCamelCase}": tableNameCamelCase, + "{TplStructDefine}": structDefine, + }) +} + +func generateDaoIndex(tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName string, req generateDaoReq) { + path := gfile.Join(dirPathDao, fileName+".go") + if !gfile.Exists(path) { + indexContent := gstr.ReplaceByMap(getTplDaoIndexContent(req.TplDaoIndexPath), g.MapStrStr{ + "{TplImportPrefix}": importPrefix, + "{TplTableName}": req.TableName, + "{TplTableNameCamelCase}": tableNameCamelCase, + "{TplTableNameCamelLowerCase}": tableNameCamelLowerCase, + }) + if err := gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil { + mlog.Fatalf("writing content to '%s' failed: %v", path, err) + } else { + utils.GoFmt(path) + mlog.Print("generated:", path) + } + } +} + +func generateDaoInternal( + tableNameCamelCase, tableNameCamelLowerCase, importPrefix string, + dirPathDao, fileName string, + fieldMap map[string]*gdb.TableField, + req generateDaoReq, +) { + path := gfile.Join(dirPathDao, "internal", fileName+".go") + modelContent := gstr.ReplaceByMap(getTplDaoInternalContent(req.TplDaoInternalPath), g.MapStrStr{ + "{TplImportPrefix}": importPrefix, + "{TplTableName}": req.TableName, + "{TplGroupName}": req.GroupName, + "{TplTableNameCamelCase}": tableNameCamelCase, + "{TplTableNameCamelLowerCase}": tableNameCamelLowerCase, + "{TplColumnDefine}": gstr.Trim(generateColumnDefinitionForDao(fieldMap)), + "{TplColumnNames}": gstr.Trim(generateColumnNamesForDao(fieldMap)), + }) + if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil { + mlog.Fatalf("writing content to '%s' failed: %v", path, err) + } else { + utils.GoFmt(path) + mlog.Print("generated:", path) + } +} + +// generateStructDefinitionForModel generates and returns the struct definition for specified table. +func generateStructDefinitionForModel(structName string, fieldMap map[string]*gdb.TableField, req generateDaoReq) string { + buffer := bytes.NewBuffer(nil) + array := make([][]string, len(fieldMap)) + names := sortFieldKeyForDao(fieldMap) + for index, name := range names { + field := fieldMap[name] + array[index] = generateStructFieldForModel(field, req) + } + tw := tablewriter.NewWriter(buffer) + tw.SetBorder(false) + tw.SetRowLine(false) + tw.SetAutoWrapText(false) + tw.SetColumnSeparator("") + tw.AppendBulk(array) + tw.Render() + stContent := buffer.String() + // Let's do this hack of table writer for indent! + stContent = gstr.Replace(stContent, " #", "") + buffer.Reset() + buffer.WriteString(fmt.Sprintf("type %s struct {\n", structName)) + buffer.WriteString(stContent) + buffer.WriteString("}") + return buffer.String() +} + +// generateStructFieldForModel generates and returns the attribute definition for specified field. +func generateStructFieldForModel(field *gdb.TableField, req generateDaoReq) []string { + var typeName, ormTag, jsonTag string + t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type) + t = gstr.Split(gstr.Trim(t), " ")[0] + t = gstr.ToLower(t) + switch t { + case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob": + typeName = "[]byte" + + case "bit", "int", "int2", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial": + if gstr.ContainsI(field.Type, "unsigned") { + typeName = "uint" + } else { + typeName = "int" + } + + case "int4", "int8", "big_int", "bigint", "bigserial": + if gstr.ContainsI(field.Type, "unsigned") { + typeName = "uint64" + } else { + typeName = "int64" + } + + case "real": + typeName = "float32" + + case "float", "double", "decimal", "smallmoney", "numeric": + typeName = "float64" + + case "bool": + typeName = "bool" + + case "datetime", "timestamp", "date", "time": + if req.StdTime { + typeName = "time.Time" + } else { + typeName = "*gtime.Time" + } + + default: + // Auto detecting type. + switch { + case strings.Contains(t, "int"): + typeName = "int" + case strings.Contains(t, "text") || strings.Contains(t, "char"): + typeName = "string" + case strings.Contains(t, "float") || strings.Contains(t, "double"): + typeName = "float64" + case strings.Contains(t, "bool"): + typeName = "bool" + case strings.Contains(t, "binary") || strings.Contains(t, "blob"): + typeName = "[]byte" + case strings.Contains(t, "date") || strings.Contains(t, "time"): + if req.StdTime { + typeName = "time.Time" + } else { + typeName = "*gtime.Time" + } + default: + typeName = "string" + } + } + ormTag = field.Name + jsonTag = getJsonTagFromCase(field.Name, req.JsonCase) + if gstr.ContainsI(field.Key, "pri") { + ormTag += ",primary" + } + if gstr.ContainsI(field.Key, "uni") { + ormTag += ",unique" + } + return []string{ + " #" + gstr.CaseCamel(field.Name), + " #" + typeName, + " #" + fmt.Sprintf("`"+`orm:"%s"`, ormTag), + " #" + fmt.Sprintf(`json:"%s"`+"`", jsonTag), + " #" + fmt.Sprintf(`// %s`, formatComment(field.Comment)), + } +} + +// formatComment formats the comment string to fit the golang code without any lines. +func formatComment(comment string) string { + comment = gstr.ReplaceByArray(comment, g.SliceStr{ + "\n", " ", + "\r", " ", + }) + comment = gstr.Trim(comment) + comment = gstr.Replace(comment, `\n`, " ") + return comment +} + +// generateColumnDefinitionForDao generates and returns the column names definition for specified table. +func generateColumnDefinitionForDao(fieldMap map[string]*gdb.TableField) string { + var ( + buffer = bytes.NewBuffer(nil) + array = make([][]string, len(fieldMap)) + names = sortFieldKeyForDao(fieldMap) + ) + for index, name := range names { + field := fieldMap[name] + comment := gstr.Trim(gstr.ReplaceByArray(field.Comment, g.SliceStr{ + "\n", " ", + "\r", " ", + })) + array[index] = []string{ + " #" + gstr.CaseCamel(field.Name), + " # " + "string", + " #" + fmt.Sprintf(`// %s`, comment), + } + } + tw := tablewriter.NewWriter(buffer) + tw.SetBorder(false) + tw.SetRowLine(false) + tw.SetAutoWrapText(false) + tw.SetColumnSeparator("") + tw.AppendBulk(array) + tw.Render() + defineContent := buffer.String() + // Let's do this hack of table writer for indent! + defineContent = gstr.Replace(defineContent, " #", "") + buffer.Reset() + buffer.WriteString(defineContent) + return buffer.String() +} + +// generateColumnNamesForDao generates and returns the column names assignment content of column struct +// for specified table. +func generateColumnNamesForDao(fieldMap map[string]*gdb.TableField) string { + var ( + buffer = bytes.NewBuffer(nil) + array = make([][]string, len(fieldMap)) + names = sortFieldKeyForDao(fieldMap) + ) + for index, name := range names { + field := fieldMap[name] + array[index] = []string{ + " #" + gstr.CaseCamel(field.Name) + ":", + fmt.Sprintf(` #"%s",`, field.Name), + } + } + tw := tablewriter.NewWriter(buffer) + tw.SetBorder(false) + tw.SetRowLine(false) + tw.SetAutoWrapText(false) + tw.SetColumnSeparator("") + tw.AppendBulk(array) + tw.Render() + namesContent := buffer.String() + // Let's do this hack of table writer for indent! + namesContent = gstr.Replace(namesContent, " #", "") + buffer.Reset() + buffer.WriteString(namesContent) + return buffer.String() +} + +func getTplDaoIndexContent(tplDaoIndexPath string) string { + if tplDaoIndexPath != "" { + return gfile.GetContents(tplDaoIndexPath) + } + return templateDaoDaoIndexContent +} + +func getTplDaoInternalContent(tplDaoInternalPath string) string { + if tplDaoInternalPath != "" { + return gfile.GetContents(tplDaoInternalPath) + } + return templateDaoDaoInternalContent +} + +func getTplModelIndexContent(tplModelIndexPath string) string { + if tplModelIndexPath != "" { + return gfile.GetContents(tplModelIndexPath) + } + return templateDaoModelIndexContent +} + +func getTplModelStructContent(tplModelStructPath string) string { + if tplModelStructPath != "" { + return gfile.GetContents(tplModelStructPath) + } + return templateDaoModelStructContent +} + +// getJsonTagFromCase call gstr.Case* function to convert the s to specified case. +func getJsonTagFromCase(str, caseStr string) string { + switch gstr.ToLower(caseStr) { + case gstr.ToLower("Camel"): + return gstr.CaseCamel(str) + + case gstr.ToLower("CamelLower"): + return gstr.CaseCamelLower(str) + + case gstr.ToLower("Kebab"): + return gstr.CaseKebab(str) + + case gstr.ToLower("KebabScreaming"): + return gstr.CaseKebabScreaming(str) + + case gstr.ToLower("Snake"): + return gstr.CaseSnake(str) + + case gstr.ToLower("SnakeFirstUpper"): + return gstr.CaseSnakeFirstUpper(str) + + case gstr.ToLower("SnakeScreaming"): + return gstr.CaseSnakeScreaming(str) + } + return str +} + +func sortFieldKeyForDao(fieldMap map[string]*gdb.TableField) []string { + names := make(map[int]string) + for _, field := range fieldMap { + names[field.Index] = field.Name + } + var ( + i = 0 + j = 0 + result = make([]string, len(names)) + ) + for { + if len(names) == 0 { + break + } + if val, ok := names[i]; ok { + result[j] = val + j++ + delete(names, i) + } + i++ + } + return result +} + +// getOptionOrConfigForDao retrieves option value from parser and configuration file. +// It returns the default value specified by parameter <value> is no value found. +func getOptionOrConfigForDao(index int, parser *gcmd.Parser, name string, defaultValue ...string) (result string) { + result = parser.GetOpt(name) + if result == "" && g.Config().Available() { + g.Cfg().SetViolenceCheck(true) + if index >= 0 { + result = g.Cfg().GetString(fmt.Sprintf(`%s.%d.%s`, nodeNameGenDaoInConfigFile, index, name)) + } else { + result = g.Cfg().GetString(fmt.Sprintf(`%s.%s`, nodeNameGenDaoInConfigFile, name)) + } + } + if result == "" && len(defaultValue) > 0 { + result = defaultValue[0] + } + return +} diff --git a/tool/gf/commands/gen/gen_dao_template_dao.go b/tool/gf/commands/gen/gen_dao_template_dao.go new file mode 100644 index 000000000..879875c07 --- /dev/null +++ b/tool/gf/commands/gen/gen_dao_template_dao.go @@ -0,0 +1,73 @@ +package gen + +const templateDaoDaoIndexContent = ` +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package dao + +import ( + "{TplImportPrefix}/dao/internal" +) + +// {TplTableNameCamelLowerCase}Dao is the manager for logic model data accessing and custom defined data operations functions management. +// You can define custom methods on it to extend its functionality as you wish. +type {TplTableNameCamelLowerCase}Dao struct { + *internal.{TplTableNameCamelCase}Dao +} + +var ( + // {TplTableNameCamelCase} is globally public accessible object for table {TplTableName} operations. + {TplTableNameCamelCase} {TplTableNameCamelLowerCase}Dao +) + +func init() { + {TplTableNameCamelCase} = {TplTableNameCamelLowerCase}Dao{ + internal.New{TplTableNameCamelCase}Dao(), + } +} + +// Fill with you ideas below. + +` + +const templateDaoDaoInternalContent = ` +// ========================================================================== +// Code generated by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/frame/gmvc" +) + +// {TplTableNameCamelCase}Dao is the manager for logic model data accessing and custom defined data operations functions management. +type {TplTableNameCamelCase}Dao struct { + gmvc.M // M is the core and embedded struct that inherits all chaining operations from gdb.Model. + C {TplTableNameCamelLowerCase}Columns // C is the short type for Columns, which contains all the column names of Table for convenient usage. + DB gdb.DB // DB is the raw underlying database management object. + Table string // Table is the underlying table name of the DAO. +} + +// {TplTableNameCamelCase}Columns defines and stores column names for table {TplTableName}. +type {TplTableNameCamelLowerCase}Columns struct { + {TplColumnDefine} +} + +// New{TplTableNameCamelCase}Dao creates and returns a new DAO object for table data access. +func New{TplTableNameCamelCase}Dao() *{TplTableNameCamelCase}Dao { + columns := {TplTableNameCamelLowerCase}Columns{ + {TplColumnNames} + } + return &{TplTableNameCamelCase}Dao{ + C: columns, + M: g.DB("{TplGroupName}").Model("{TplTableName}").Safe(), + DB: g.DB("{TplGroupName}"), + Table: "{TplTableName}", + } +} +` diff --git a/tool/gf/commands/gen/gen_dao_template_model.go b/tool/gf/commands/gen/gen_dao_template_model.go new file mode 100644 index 000000000..3eef8d4c0 --- /dev/null +++ b/tool/gf/commands/gen/gen_dao_template_model.go @@ -0,0 +1,18 @@ +package gen + +const templateDaoModelIndexContent = ` +// ================================================================================= +// Code generated by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package model + +{TplPackageImports} + +{TplModelStructs} +` + +const templateDaoModelStructContent = ` +// {TplTableNameCamelCase} is the golang structure for table {TplTableName}. +{TplStructDefine} +` diff --git a/tool/gf/commands/gen/gen_pb.go b/tool/gf/commands/gen/gen_pb.go new file mode 100644 index 000000000..81ee6f1be --- /dev/null +++ b/tool/gf/commands/gen/gen_pb.go @@ -0,0 +1,77 @@ +package gen + +import ( + "fmt" + "github.com/gogf/gf/container/gset" + "github.com/gogf/gf/os/genv" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gproc" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/mlog" +) + +func HelpPb() { + mlog.Print(gstr.TrimLeft(` +USAGE + gf gen pb + +`)) +} + +// doGenPb parses current `proto` files in folder `protocol` and generates `pb` files to `protobuf`. +func doGenPb() { + // protoc search. + protocBinPath := gproc.SearchBinary("protoc") + if protocBinPath == "" { + mlog.Fatal(`"protoc" command not found, install it first to proceed proto files parsing`) + } + // protocol fold checks. + protoFolder := "protocol" + if !gfile.Exists(protoFolder) { + mlog.Fatalf(`proto files folder "%s" does not exist`, protoFolder) + } + // folder scanning. + files, err := gfile.ScanDirFile(protoFolder, "*.proto", true) + if err != nil { + mlog.Fatal(err) + } + if len(files) == 0 { + mlog.Fatalf(`no proto files found in folder "%s"`, protoFolder) + } + dirSet := gset.NewStrSet() + for _, file := range files { + dirSet.Add(gfile.Dir(file)) + } + var ( + servicePath = gfile.RealPath(".") + goPathSrc = gfile.RealPath(gfile.Join(genv.Get("GOPATH"), "src")) + ) + dirSet.Iterator(func(protoDirPath string) bool { + parsingCommand := fmt.Sprintf( + "protoc --gofast_out=plugins=grpc:. %s/*.proto -I%s", + protoDirPath, + servicePath, + ) + if goPathSrc != "" { + parsingCommand += " -I" + goPathSrc + } + mlog.Print(parsingCommand) + if output, err := gproc.ShellExec(parsingCommand); err != nil { + mlog.Print(output) + mlog.Fatal(err) + } + return true + }) + // Custom replacement. + //pbFolder := "protobuf" + //_, _ = gfile.ScanDirFileFunc(pbFolder, "*.go", true, func(path string) string { + // content := gfile.GetContents(path) + // content = gstr.ReplaceByArray(content, g.SliceStr{ + // `gtime "gtime"`, `gtime "github.com/gogf/gf/os/gtime"`, + // }) + // _ = gfile.PutContents(path, content) + // utils.GoFmt(path) + // return path + //}) + mlog.Print("done!") +} diff --git a/tool/gf/commands/gen/gen_pbentity.go b/tool/gf/commands/gen/gen_pbentity.go new file mode 100644 index 000000000..8c270f9e7 --- /dev/null +++ b/tool/gf/commands/gen/gen_pbentity.go @@ -0,0 +1,447 @@ +package gen + +import ( + "bytes" + "context" + "fmt" + _ "github.com/denisenkom/go-mssqldb" + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/text/gregex" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/mlog" + "github.com/gogf/gf/util/gconv" + _ "github.com/lib/pq" + //_ "github.com/mattn/go-oci8" + //_ "github.com/mattn/go-sqlite3" + "github.com/olekukonko/tablewriter" + "strings" +) + +// generatePbEntityReq is the input parameter for generating entity protobuf files. +type generatePbEntityReq struct { + TableName string // TableName specifies the table name of the table. + NewTableName string // NewTableName specifies the prefix-stripped name of the table. + PrefixName string // PrefixName specifies the custom prefix name for generated protobuf entity. + GroupName string // GroupName specifies the group name of database configuration node for generated protobuf entity. + PkgName string // PkgName specifies package name for generated protobuf. + NameCase string // NameCase specifies the case of generated attribute name for entity message, value from gstr.Case* function names. + JsonCase string // JsonCase specifies the case of json tag for attribute name of entity message, value from gstr.Case* function names. + DirPath string // DirPath specifies the directory path for generated files. + OptionContent string // OptionContent specifies the extra option configuration content for protobuf. + TplEntityPath string // TplEntityPath specifies the file path for generating protobuf entity files. +} + +const ( + nodeNameGenPbEntityInConfigFile = "gfcli.gen.pbentity" +) + +func HelpPbEntity() { + mlog.Print(gstr.TrimLeft(` +USAGE + gf gen pbentity [OPTION] + +OPTION + -/--path directory path for generated files. + -/--package package name for all entity proto files. + -l, --link database configuration, the same as the ORM configuration of GoFrame. + -t, --tables generate models only for given tables, multiple table names separated with ',' + -c, --config used to specify the configuration file for database, it's commonly not necessary. + If "-l" is not passed, it will search "./config.toml" and "./config/config.toml" + in current working directory in default. + -p, --prefix add specified prefix for all entity names and entity proto files. + -r, --removePrefix remove specified prefix of the table, multiple prefix separated with ',' + -n, --nameCase case for message attribute names, default is "Camel": + | Case | Example | + |---------------- |--------------------| + | Camel | AnyKindOfString | default + | CamelLower | anyKindOfString | + | Snake | any_kind_of_string | + | SnakeScreaming | ANY_KIND_OF_STRING | + | SnakeFirstUpper | rgb_code_md5 | + | Kebab | any-kind-of-string | + | KebabScreaming | ANY-KIND-OF-STRING | + -j, --jsonCase case for message json tag, cases are the same as "nameCase", default "CamelLower". + set it to "none" to ignore json tag generating. + -o, --option extra protobuf options. + -/--tplEntity template content for protobuf entity files generating. + +CONFIGURATION SUPPORT + Options are also supported by configuration file. + It's suggested using configuration file instead of command line arguments making producing. + The configuration node name is "gf.gen.pbentity", which also supports multiple databases, for example: + [gfcli] + [[gfcli.gen.pbentity]] + link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + path = "protocol/demos/entity" + tables = "order,products" + package = "demos" + [[gfcli.gen.pbentity]] + link = "mysql:root:12345678@tcp(127.0.0.1:3306)/primary" + path = "protocol/demos/entity" + prefix = "primary_" + tables = "user, userDetail" + package = "demos" + option = """ +option go_package = "protobuf/demos"; +option java_package = "protobuf/demos"; +option php_namespace = "protobuf/demos"; +""" + +EXAMPLES + gf gen pbentity + gf gen pbentity -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + gf gen pbentity -path ./protocol/demos/entity -c config.yaml -g user-center -t user,user_detail,user_login + gf gen pbentity -r user_ +`)) +} + +// doGenPbEntity implements the "gen pbentity" command. +func doGenPbEntity() { + parser, err := gcmd.Parse(g.MapStrBool{ + "path": true, + "package": true, + "l,link": true, + "t,tables": true, + "c,config": true, + "p,prefix": true, + "r,removePrefix": true, + "o,option": true, + "n,nameCase": true, + "j,jsonCase": true, + "tplEntity": true, + }) + if err != nil { + mlog.Fatal(err) + } + config := g.Cfg() + if config.Available() { + v := config.GetVar(nodeNameGenPbEntityInConfigFile) + if v.IsEmpty() && g.IsEmpty(parser.GetOptAll()) { + mlog.Fatal(`command arguments and configurations not found for generating protobuf entity files`) + } + if v.IsSlice() { + for i := 0; i < len(v.Interfaces()); i++ { + doGenPbEntityForArray(i, parser) + } + } else { + doGenPbEntityForArray(-1, parser) + } + } else { + doGenPbEntityForArray(-1, parser) + } + mlog.Print("done!") +} + +// doGenPbEntityForArray implements the "gen pbentity" command for configuration array. +func doGenPbEntityForArray(index int, parser *gcmd.Parser) { + var ( + err error + db gdb.DB + dirPath = getOptionOrConfigForPbEntity(index, parser, "path") // Generated directory path. + pkgName = getOptionOrConfigForPbEntity(index, parser, "package") // Package name for protobuf. + tablesStr = getOptionOrConfigForPbEntity(index, parser, "tables") // Tables that will be generated. + prefixName = getOptionOrConfigForPbEntity(index, parser, "prefix") // Add prefix to entity name. + linkInfo = getOptionOrConfigForPbEntity(index, parser, "link") // Custom database link. + configPath = getOptionOrConfigForPbEntity(index, parser, "config") // Config file path, eg: ./config/db.toml. + configGroup = getOptionOrConfigForPbEntity(index, parser, "group", "default") // Group name of database configuration node for generated protobuf entity. + removePrefix = getOptionOrConfigForPbEntity(index, parser, "removePrefix") // Remove prefix from table name. + nameCase = getOptionOrConfigForPbEntity(index, parser, "nameCase", "Camel") // Case configuration for message name. + jsonCase = getOptionOrConfigForPbEntity(index, parser, "jsonCase", "CamelLower") // Case configuration for message json tag. + optionContent = getOptionOrConfigForPbEntity(index, parser, "option") // Option content for protobuf. + tplEntityPath = getOptionOrConfigForPbEntity(index, parser, "tplEntity") // Specifies the file path for generating protobuf entity files. + ) + if tplEntityPath != "" && (!gfile.Exists(tplEntityPath) || !gfile.IsReadable(tplEntityPath)) { + mlog.Fatalf("template file for entity files generating does not exist or is not readable: %s", tplEntityPath) + } + // Make it compatible with old CLI version for option name: remove-prefix + if removePrefix == "" { + removePrefix = getOptionOrConfigForPbEntity(index, parser, "remove-prefix") + } + removePrefixArray := gstr.SplitAndTrim(removePrefix, ",") + if pkgName == "" { + mlog.Fatal("package name should not be empty") + } + // It reads database configuration from project configuration file. + if configPath != "" { + path, err := gfile.Search(configPath) + if err != nil { + mlog.Fatalf("search configuration file '%s' failed: %v", configPath, err) + } + if err := g.Cfg().SetPath(gfile.Dir(path)); err != nil { + mlog.Fatalf("set configuration path '%s' failed: %v", path, err) + } + g.Cfg().SetFileName(gfile.Basename(path)) + } + // It uses user passed database configuration. + if linkInfo != "" { + tempGroup := gtime.TimestampNanoStr() + match, _ := gregex.MatchString(`([a-z]+):(.+)`, linkInfo) + if len(match) == 3 { + gdb.AddConfigNode(tempGroup, gdb.ConfigNode{ + Type: gstr.Trim(match[1]), + LinkInfo: gstr.Trim(match[2]), + }) + db, _ = gdb.Instance(tempGroup) + } + } else { + db = g.DB(configGroup) + } + if db == nil { + mlog.Fatal("database initialization failed") + } + + tableNames := ([]string)(nil) + if tablesStr != "" { + tableNames = gstr.SplitAndTrim(tablesStr, ",") + } else { + tableNames, err = db.Tables(context.TODO()) + if err != nil { + mlog.Fatalf("fetching tables failed: \n %v", err) + } + } + + for _, tableName := range tableNames { + newTableName := tableName + for _, v := range removePrefixArray { + newTableName = gstr.TrimLeftStr(newTableName, v, 1) + } + req := &generatePbEntityReq{ + TableName: tableName, + NewTableName: newTableName, + PrefixName: prefixName, + GroupName: configGroup, + PkgName: pkgName, + NameCase: nameCase, + JsonCase: jsonCase, + DirPath: dirPath, + OptionContent: gstr.Trim(optionContent), + TplEntityPath: tplEntityPath, + } + generatePbEntityContentFile(db, req) + } +} + +// generatePbEntityContentFile generates the protobuf files for given table. +func generatePbEntityContentFile(db gdb.DB, req *generatePbEntityReq) { + fieldMap, err := db.TableFields(db.GetCtx(), req.TableName) + if err != nil { + mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", req.TableName, err) + } + // Change the `newTableName` if `prefixName` is given. + newTableName := "Entity_" + req.PrefixName + req.NewTableName + var ( + tableNameCamelCase = gstr.CaseCamel(newTableName) + tableNameSnakeCase = gstr.CaseSnake(newTableName) + entityMessageDefine = generateEntityMessageDefinition(tableNameCamelCase, fieldMap, req) + fileName = gstr.Trim(tableNameSnakeCase, "-_.") + path = gfile.Join(req.DirPath, fileName+".proto") + ) + entityContent := gstr.ReplaceByMap(getTplPbEntityContent(req.TplEntityPath), g.MapStrStr{ + "{PackageName}": req.PkgName, + "{OptionContent}": req.OptionContent, + "{EntityMessage}": entityMessageDefine, + }) + if err := gfile.PutContents(path, strings.TrimSpace(entityContent)); err != nil { + mlog.Fatalf("writing content to '%s' failed: %v", path, err) + } else { + mlog.Print("generated:", path) + } +} + +// generateEntityMessageDefinition generates and returns the message definition for specified table. +func generateEntityMessageDefinition(name string, fieldMap map[string]*gdb.TableField, req *generatePbEntityReq) string { + var ( + buffer = bytes.NewBuffer(nil) + array = make([][]string, len(fieldMap)) + names = sortFieldKeyForPbEntity(fieldMap) + ) + for index, name := range names { + array[index] = generateMessageFieldForPbEntity(index+1, fieldMap[name], req) + } + tw := tablewriter.NewWriter(buffer) + tw.SetBorder(false) + tw.SetRowLine(false) + tw.SetAutoWrapText(false) + tw.SetColumnSeparator("") + tw.AppendBulk(array) + tw.Render() + stContent := buffer.String() + // Let's do this hack of table writer for indent! + stContent = gstr.Replace(stContent, " #", "") + buffer.Reset() + buffer.WriteString(fmt.Sprintf("message %s {\n", name)) + buffer.WriteString(stContent) + buffer.WriteString("}") + return buffer.String() +} + +// generateMessageFieldForPbEntity generates and returns the message definition for specified field. +func generateMessageFieldForPbEntity(index int, field *gdb.TableField, req *generatePbEntityReq) []string { + var ( + typeName string + comment string + jsonTagStr string + ) + t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type) + t = gstr.Split(gstr.Trim(t), " ")[0] + t = gstr.ToLower(t) + switch t { + case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob": + typeName = "bytes" + + case "bit", "int", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial": + if gstr.ContainsI(field.Type, "unsigned") { + typeName = "uint32" + } else { + typeName = "int32" + } + + case "int8", "big_int", "bigint", "bigserial": + if gstr.ContainsI(field.Type, "unsigned") { + typeName = "uint64" + } else { + typeName = "int64" + } + + case "real": + typeName = "float" + + case "float", "double", "decimal", "smallmoney": + typeName = "double" + + case "bool": + typeName = "bool" + + case "datetime", "timestamp", "date", "time": + typeName = "int64" + + default: + // Auto detecting type. + switch { + case strings.Contains(t, "int"): + typeName = "int" + case strings.Contains(t, "text") || strings.Contains(t, "char"): + typeName = "string" + case strings.Contains(t, "float") || strings.Contains(t, "double"): + typeName = "double" + case strings.Contains(t, "bool"): + typeName = "bool" + case strings.Contains(t, "binary") || strings.Contains(t, "blob"): + typeName = "bytes" + case strings.Contains(t, "date") || strings.Contains(t, "time"): + typeName = "int64" + default: + typeName = "string" + } + } + comment = gstr.ReplaceByArray(field.Comment, g.SliceStr{ + "\n", " ", + "\r", " ", + }) + comment = gstr.Trim(comment) + comment = gstr.Replace(comment, `\n`, " ") + comment, _ = gregex.ReplaceString(`\s{2,}`, ` `, comment) + if jsonTagName := formatCase(field.Name, req.JsonCase); jsonTagName != "" { + jsonTagStr = fmt.Sprintf(`[(gogoproto.jsontag) = "%s"]`, jsonTagName) + // beautiful indent. + if index < 10 { + // 3 spaces + jsonTagStr = " " + jsonTagStr + } else if index < 100 { + // 2 spaces + jsonTagStr = " " + jsonTagStr + } else { + // 1 spaces + jsonTagStr = " " + jsonTagStr + } + } + return []string{ + " #" + typeName, + " #" + formatCase(field.Name, req.NameCase), + " #= " + gconv.String(index) + jsonTagStr + ";", + " #" + fmt.Sprintf(`// %s`, comment), + } +} + +func getTplPbEntityContent(tplEntityPath string) string { + if tplEntityPath != "" { + return gfile.GetContents(tplEntityPath) + } + return templatePbEntityMessageContent +} + +// formatCase call gstr.Case* function to convert the s to specified case. +func formatCase(str, caseStr string) string { + switch gstr.ToLower(caseStr) { + case gstr.ToLower("Camel"): + return gstr.CaseCamel(str) + + case gstr.ToLower("CamelLower"): + return gstr.CaseCamelLower(str) + + case gstr.ToLower("Kebab"): + return gstr.CaseKebab(str) + + case gstr.ToLower("KebabScreaming"): + return gstr.CaseKebabScreaming(str) + + case gstr.ToLower("Snake"): + return gstr.CaseSnake(str) + + case gstr.ToLower("SnakeFirstUpper"): + return gstr.CaseSnakeFirstUpper(str) + + case gstr.ToLower("SnakeScreaming"): + return gstr.CaseSnakeScreaming(str) + + case "none": + return "" + } + return str +} + +// getOptionOrConfigForPbEntity retrieves option value from parser and configuration file. +// It returns the default value specified by parameter <value> is no value found. +func getOptionOrConfigForPbEntity(index int, parser *gcmd.Parser, name string, defaultValue ...string) (result string) { + result = parser.GetOpt(name) + if result == "" && g.Config().Available() { + g.Cfg().SetViolenceCheck(true) + if index >= 0 { + result = g.Cfg().GetString(fmt.Sprintf(`%s.%d.%s`, nodeNameGenPbEntityInConfigFile, index, name)) + } else { + result = g.Cfg().GetString(fmt.Sprintf(`%s.%s`, nodeNameGenPbEntityInConfigFile, name)) + } + } + if result == "" && len(defaultValue) > 0 { + result = defaultValue[0] + } + return +} + +func sortFieldKeyForPbEntity(fieldMap map[string]*gdb.TableField) []string { + names := make(map[int]string) + for _, field := range fieldMap { + names[field.Index] = field.Name + } + var ( + result = make([]string, len(names)) + i = 0 + j = 0 + ) + for { + if len(names) == 0 { + break + } + if val, ok := names[i]; ok { + result[j] = val + j++ + delete(names, i) + } + i++ + } + return result +} diff --git a/tool/gf/commands/gen/gen_pbentity_template.go b/tool/gf/commands/gen/gen_pbentity_template.go new file mode 100644 index 000000000..f73d5c55e --- /dev/null +++ b/tool/gf/commands/gen/gen_pbentity_template.go @@ -0,0 +1,17 @@ +package gen + +const templatePbEntityMessageContent = ` +// ========================================================================== +// Code generated by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +syntax = "proto3"; + +package {PackageName}; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +{OptionContent} + +{EntityMessage} +` diff --git a/tool/gf/commands/get/get.go b/tool/gf/commands/get/get.go new file mode 100644 index 000000000..584b5cf7e --- /dev/null +++ b/tool/gf/commands/get/get.go @@ -0,0 +1,33 @@ +package get + +import ( + "fmt" + "github.com/gogf/gf/os/gproc" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/mlog" + "os" +) + +func Help() { + mlog.Print(gstr.TrimLeft(` +USAGE + gf get PACKAGE + +ARGUMENT + PACKAGE remote golang package path, eg: github.com/gogf/gf + +EXAMPLES + gf get github.com/gogf/gf + gf get github.com/gogf/gf@latest + gf get github.com/gogf/gf@master + gf get golang.org/x/sys +`)) +} + +func Run() { + if len(os.Args) > 2 { + gproc.ShellRun(fmt.Sprintf(`go get -u %s`, gstr.Join(os.Args[2:], " "))) + } else { + mlog.Fatal("please input the package path for get") + } +} diff --git a/tool/gf/commands/initialize/initialize.go b/tool/gf/commands/initialize/initialize.go new file mode 100644 index 000000000..a5ddadd0b --- /dev/null +++ b/tool/gf/commands/initialize/initialize.go @@ -0,0 +1,110 @@ +package initialize + +import ( + "github.com/gogf/gf/encoding/gcompress" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/allyes" + "github.com/gogf/gf/tool/gf/library/mlog" + "strings" +) + +const ( + emptyProject = "github.com/gogf/gf-empty" + emptyProjectName = "gf-empty" +) + +var ( + cdnUrl = g.Config("url").GetString("cdn.url") + homeUrl = g.Config("url").GetString("home.url") +) + +func init() { + if cdnUrl == "" { + mlog.Fatal("CDN configuration cannot be empty") + } + if homeUrl == "" { + mlog.Fatal("Home configuration cannot be empty") + } +} + +func Help() { + mlog.Print(gstr.TrimLeft(` +USAGE + gf init NAME + +ARGUMENT + NAME name for the project. It will create a folder with NAME in current directory. + The NAME will also be the module name for the project. + +EXAMPLES + gf init my-app + gf init my-project-name +`)) +} + +func Run() { + parser, err := gcmd.Parse(nil) + if err != nil { + mlog.Fatal(err) + } + projectName := parser.GetArg(2) + if projectName == "" { + mlog.Fatal("project name should not be empty") + } + dirPath := projectName + if !gfile.IsEmpty(dirPath) && !allyes.Check() { + s := gcmd.Scanf(`the folder "%s" is not empty, files might be overwrote, continue? [y/n]: `, projectName) + if strings.EqualFold(s, "n") { + return + } + } + mlog.Print("initializing...") + // MD5 retrieving. + respMd5, err := g.Client().Get(homeUrl + "/cli/project/md5") + if err != nil { + mlog.Fatalf("get the project zip md5 failed: %s", err.Error()) + } + if respMd5 == nil { + mlog.Fatal("got the project zip md5 failed") + } + defer respMd5.Close() + md5DataStr := respMd5.ReadAllString() + if md5DataStr == "" { + mlog.Fatal("get the project zip md5 failed: empty md5 value. maybe network issue, try again?") + } + + // Zip data retrieving. + respData, err := g.Client().Get(cdnUrl + "/cli/project/zip?" + md5DataStr) + if err != nil { + mlog.Fatalf("got the project zip data failed: %s", err.Error()) + } + if respData == nil { + mlog.Fatal("got the project zip data failed") + } + defer respData.Close() + zipData := respData.ReadAll() + if len(zipData) == 0 { + mlog.Fatal("get the project data failed: empty data value. maybe network issue, try again?") + } + // Current folder. + replacedProjectName := projectName + if replacedProjectName == "." { + replacedProjectName = gfile.Name(gfile.RealPath(".")) + } + // Unzip the zip data. + if err = gcompress.UnZipContent(zipData, dirPath, emptyProjectName+"-master"); err != nil { + mlog.Fatal("unzip project data failed,", err.Error()) + } + // Replace project name. + if err = gfile.ReplaceDir(emptyProject, replacedProjectName, dirPath, "Dockerfile,*.go,*.MD,*.mod", true); err != nil { + mlog.Fatal("content replacing failed,", err.Error()) + } + if err = gfile.ReplaceDir(emptyProjectName, replacedProjectName, dirPath, "Dockerfile,*.go,*.MD,*.mod", true); err != nil { + mlog.Fatal("content replacing failed,", err.Error()) + } + mlog.Print("initialization done! ") + mlog.Print("you can now run 'gf run main.go' to start your journey, enjoy!") +} diff --git a/tool/gf/commands/install/install.go b/tool/gf/commands/install/install.go new file mode 100644 index 000000000..e178c3f3f --- /dev/null +++ b/tool/gf/commands/install/install.go @@ -0,0 +1,192 @@ +package install + +import ( + "github.com/gogf/gf/container/garray" + "github.com/gogf/gf/container/gset" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/genv" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/allyes" + "github.com/gogf/gf/tool/gf/library/mlog" + "github.com/gogf/gf/util/gconv" + "runtime" + "strings" +) + +// installFolderPath contains installFolderPath-related data, +// such as path, writable, binaryFilePath, and installed. +type installFolderPath struct { + path string + writable bool + binaryFilePath string + installed bool +} + +// Run does the installation. +func Run() { + // Ask where to install. + paths := getInstallPathsData() + if len(paths) <= 0 { + mlog.Printf("no path detected, you can manually install gf by copying the binary to path folder.") + return + } + mlog.Printf("I found some installable paths for you(from $PATH): ") + mlog.Printf(" %2s | %8s | %9s | %s", "Id", "Writable", "Installed", "Path") + + // Print all paths status and determine the default selectedID value. + var ( + selectedID = -1 + pathSet = gset.NewStrSet() // Used for repeated items filtering. + ) + for id, aPath := range paths { + if !pathSet.AddIfNotExist(aPath.path) { + continue + } + mlog.Printf(" %2d | %8t | %9t | %s", id, aPath.writable, aPath.installed, aPath.path) + if selectedID == -1 { + // Use the previously installed path as the most priority choice. + if aPath.installed { + selectedID = id + } + } + } + // If there's no previously installed path, use the first writable path. + if selectedID == -1 { + // Order by choosing priority. + commonPaths := garray.NewStrArrayFrom(g.SliceStr{ + `/usr/local/bin`, + `/usr/bin`, + `/usr/sbin`, + `C:\Windows`, + `C:\Windows\system32`, + `C:\Go\bin`, + `C:\Program Files`, + `C:\Program Files (x86)`, + }) + // Check the common installation directories. + commonPaths.Iterator(func(k int, v string) bool { + for id, aPath := range paths { + if strings.EqualFold(aPath.path, v) { + selectedID = id + return false + } + } + return true + }) + if selectedID == -1 { + selectedID = 0 + } + } + + if allyes.Check() { + // Use the default selectedID. + mlog.Printf("please choose one installation destination [default %d]: %d", selectedID, selectedID) + } else { + // Get input and update selectedID. + input := gcmd.Scanf("please choose one installation destination [default %d]: ", selectedID) + if input != "" { + selectedID = gconv.Int(input) + } + } + + // Check if out of range. + if selectedID >= len(paths) || selectedID < 0 { + mlog.Printf("invalid install destination Id: %d", selectedID) + return + } + + // Get selected destination path. + dstPath := paths[selectedID] + + // Install the new binary. + err := gfile.CopyFile(gfile.SelfPath(), dstPath.binaryFilePath) + if err != nil { + mlog.Printf("install gf binary to '%s' failed: %v", dstPath.path, err) + mlog.Printf("you can manually install gf by copying the binary to folder: %s", dstPath.path) + } else { + mlog.Printf("gf binary is successfully installed to: %s", dstPath.path) + } + + // Uninstall the old binary. + for _, aPath := range paths { + // Do not delete myself. + if aPath.binaryFilePath != "" && + aPath.binaryFilePath != dstPath.binaryFilePath && + gfile.SelfPath() != aPath.binaryFilePath { + gfile.Remove(aPath.binaryFilePath) + } + } +} + +// IsInstalled returns whether the binary is installed. +func IsInstalled() bool { + paths := getInstallPathsData() + for _, aPath := range paths { + if aPath.installed { + return true + } + } + return false +} + +// GetInstallPathsData returns the installation paths data for the binary. +func getInstallPathsData() []installFolderPath { + var folderPaths []installFolderPath + // Pre generate binaryFileName. + binaryFileName := "gf" + gfile.Ext(gfile.SelfPath()) + switch runtime.GOOS { + case "darwin": + folderPaths = checkPathAndAppendToInstallFolderPath( + folderPaths, "/usr/local/bin", binaryFileName, + ) + default: + // Search and find the writable directory path. + envPath := genv.Get("PATH", genv.Get("Path")) + if gstr.Contains(envPath, ";") { + for _, v := range gstr.SplitAndTrim(envPath, ";") { + folderPaths = checkPathAndAppendToInstallFolderPath( + folderPaths, v, binaryFileName) + } + } else if gstr.Contains(envPath, ":") { + for _, v := range gstr.SplitAndTrim(envPath, ":") { + folderPaths = checkPathAndAppendToInstallFolderPath( + folderPaths, v, binaryFileName) + } + } else if envPath != "" { + folderPaths = checkPathAndAppendToInstallFolderPath( + folderPaths, envPath, binaryFileName) + } else { + folderPaths = checkPathAndAppendToInstallFolderPath( + folderPaths, "/usr/local/bin", binaryFileName) + } + } + return folderPaths +} + +// checkPathAndAppendToInstallFolderPath checks if <path> is writable and already installed. +// It adds the <path> to <folderPaths> if it is writable or already installed, or else it ignores the <path>. +func checkPathAndAppendToInstallFolderPath(folderPaths []installFolderPath, path string, binaryFileName string) []installFolderPath { + var ( + binaryFilePath = gfile.Join(path, binaryFileName) + writable = gfile.IsWritable(path) + installed = isInstalled(binaryFilePath) + ) + if !writable && !installed { + return folderPaths + } + return append( + folderPaths, + installFolderPath{ + path: path, + writable: writable, + binaryFilePath: binaryFilePath, + installed: installed, + }) +} + +// Check if this gf binary path exists. +func isInstalled(path string) bool { + return gfile.Exists(path) +} diff --git a/tool/gf/commands/mod/mod.go b/tool/gf/commands/mod/mod.go new file mode 100644 index 000000000..216ae0302 --- /dev/null +++ b/tool/gf/commands/mod/mod.go @@ -0,0 +1,113 @@ +package mod + +import ( + "fmt" + "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/genv" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/mlog" +) + +func Help() { + mlog.Print(gstr.TrimLeft(` +USAGE + gf mod ARGUMENT + +ARGUMENT + path copy all packages with its latest version in Go modules, which does not exist + in GOPATH, to GOPATH. This enables your project using GOPATH building, but you + should have GOPATH environment variable configured. + +EXAMPLES + gf mod path +`)) +} + +func Run() { + argument := gcmd.GetArg(2) + switch argument { + case "path": + doPath() + + default: + mlog.Print("argument cannot be empty") + Help() + } +} + +// doPath copies all packages in Go modules, which does not exist in GOPATH, to GOPATH. +// This enables your project using GOPATH building, but you should have GOPATH +// environment variable configured. +func doPath() { + goPathEnv := genv.Get("GOPATH") + if goPathEnv == "" { + mlog.Fatal("GOPATH is not found in your environment") + } + mlog.Print("scanning...") + var ( + copied = false + haveCount = 0 + ) + for _, goPath := range gstr.SplitAndTrim(goPathEnv, ";") { + goModPath := gfile.Join(goPath, "pkg", "mod") + if !gfile.Exists(goModPath) { + continue + } + pathMap := gmap.NewStrStrMap() + _, err := gfile.ScanDirFunc(goModPath, "*.*", true, func(path string) string { + // Ignore the cache folder. + if gstr.Contains(path, gfile.Join(goModPath, "cache")) { + return "" + } + name := gfile.Name(path) + if name == "" { + return "" + } + if !gstr.Contains(name, "@") { + return "" + } + if n := gstr.Count(path, "@"); n > 1 { + return "" + } + if !gfile.IsDir(path) { + return "" + } + array := gstr.Split(path, "@") + if v := pathMap.Get(array[0]); v == "" { + pathMap.Set(array[0], array[1]) + } else { + if gstr.CompareVersionGo(v, array[1]) < 0 { + pathMap.Set(array[0], array[1]) + } + } + return path + }) + if err != nil { + mlog.Fatal(err) + } + haveCount += pathMap.Size() + pathMap.Iterator(func(k string, v string) bool { + src := fmt.Sprintf(`%s@%s`, k, v) + dst := gfile.Join(goPath, "src", gstr.Trim(gstr.Replace(k, goModPath, ""), "\\/")) + if !gfile.Exists(dst) { + mlog.Printf(`copying %s to %s`, src, dst) + if err := gfile.Copy(src, dst); err != nil { + mlog.Fatal(err) + } + copied = true + } + return true + }) + } + if !copied { + if haveCount > 0 { + mlog.Print(`all packages of go modules already exist in GOPATH`) + } else { + mlog.Printf(`no packages found in go module path: %s`, goPathEnv) + } + return + } + mlog.Print("done!") +} diff --git a/tool/gf/commands/pack/pack.go b/tool/gf/commands/pack/pack.go new file mode 100644 index 000000000..7a3efaa9d --- /dev/null +++ b/tool/gf/commands/pack/pack.go @@ -0,0 +1,80 @@ +package pack + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gres" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/allyes" + "github.com/gogf/gf/tool/gf/library/mlog" + "strings" +) + +func Help() { + mlog.Print(gstr.TrimLeft(` +USAGE + gf pack SRC DST + +ARGUMENT + SRC source path for packing, which can be multiple source paths. + DST destination file path for packed file. if extension of the filename is ".go" and "-n" option is given, + it enables packing SRC to go file, or else it packs SRC into a binary file. + +OPTION + -n, --name package name for output go file, it's set as its directory name if no name passed + -p, --prefix prefix for each file packed into the resource file + +EXAMPLES + gf pack public data.bin + gf pack public,template data.bin + gf pack public,template packed/data.go + gf pack public,template,config packed/data.go + gf pack public,template,config packed/data.go -n=packed -p=/var/www/my-app + gf pack /var/www/public packed/data.go -n=packed +`)) +} + +func Run() { + parser, err := gcmd.Parse(g.MapStrBool{ + "n,name": true, + "p,prefix": true, + }) + if err != nil { + mlog.Fatal(err) + } + srcPath := parser.GetArg(2) + dstPath := parser.GetArg(3) + if srcPath == "" { + mlog.Fatal("SRC path cannot be empty") + } + if dstPath == "" { + mlog.Fatal("DST path cannot be empty") + } + if gfile.Exists(dstPath) && gfile.IsDir(dstPath) { + mlog.Fatalf("DST path '%s' cannot be a directory", dstPath) + } + if !gfile.IsEmpty(dstPath) && !allyes.Check() { + s := gcmd.Scanf("path '%s' is not empty, files might be overwrote, continue? [y/n]: ", dstPath) + if strings.EqualFold(s, "n") { + return + } + } + var ( + name = parser.GetOpt("name") + prefix = parser.GetOpt("prefix") + ) + if name == "" && gfile.ExtName(dstPath) == "go" { + name = gfile.Basename(gfile.Dir(dstPath)) + } + if name != "" { + if err := gres.PackToGoFile(srcPath, dstPath, name, prefix); err != nil { + mlog.Fatalf("pack failed: %v", err) + } + } else { + if err := gres.PackToFile(srcPath, dstPath, prefix); err != nil { + mlog.Fatalf("pack failed: %v", err) + } + } + mlog.Print("done!") +} diff --git a/tool/gf/commands/run/run.go b/tool/gf/commands/run/run.go new file mode 100644 index 000000000..fe9b55d96 --- /dev/null +++ b/tool/gf/commands/run/run.go @@ -0,0 +1,212 @@ +package run + +import ( + "fmt" + "github.com/gogf/gf/container/garray" + "github.com/gogf/gf/container/gtype" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gfsnotify" + "github.com/gogf/gf/os/gproc" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/os/gtimer" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/commands/swagger" + "github.com/gogf/gf/tool/gf/library/mlog" + "os" + "runtime" + "strings" + "time" +) + +type App struct { + File string // Go run file name/path. + Options string // Extra "go run" options. + Args string // Auto parse and pack swagger files. + Swagger bool // Auto parse and pack swagger files. +} + +const ( + gPROXY_CHECK_TIMEOUT = time.Second +) + +var ( + process *gproc.Process + httpClient = ghttp.NewClient() +) + +func init() { + httpClient.SetTimeout(gPROXY_CHECK_TIMEOUT) +} + +func Help() { + mlog.Print(gstr.TrimLeft(` +USAGE + gf run FILE [OPTION] + +ARGUMENT + FILE building file path. + OPTION the same options as "go run"/"go build" except some options as follows defined + +OPTION + -/--args custom process arguments. + -/--swagger auto parse and pack swagger into packed/data-swagger.go before running. + +EXAMPLES + gf run main.go + gf run main.go --swagger + gf run main.go --args "server -p 8080" + gf run main.go -mod=vendor + +DESCRIPTION + The "run" command is used for running go codes with hot-compiled-like feature, + which compiles and runs the go codes asynchronously when codes change. +`)) +} + +func Run() { + parser, err := gcmd.Parse(g.MapStrBool{ + "args": true, + }) + if err != nil { + mlog.Fatal(err) + } + mlog.SetHeaderPrint(true) + file := gcmd.GetArg(2) + if len(file) < 1 { + mlog.Fatal("file path cannot be empty") + } + app := &App{ + File: file, + } + // ================================================================================ + // This command is very special that it supports options of "go run" and "go build" + // from the third parameter of os.Args. That means, we should filter any parameter + // that "go run" and "go build" do not allow. + // ================================================================================ + // Swagger checks. + array := garray.NewStrArrayFrom(os.Args) + index := array.Search("--swagger") + if index < 0 { + index = array.Search("-swagger") + } + if index != -1 { + app.Swagger = true + array.Remove(index) + } + // args checks. + args := parser.GetOpt("args") + if args != "" { + app.Args = args + index := -1 + array.Iterator(func(k int, v string) bool { + if gstr.Contains(v, "-args") { + index = k + return false + } + return true + }) + if index != -1 { + v, _ := array.Get(index) + if gstr.Contains(v, "=") { + array.Remove(index) + } else { + array.Remove(index) + array.Remove(index) + } + } + } + // -y checks + array.RemoveValue("-y") + array.RemoveValue("--y") + if array.Len() > 3 { + app.Options = strings.Join(array.SubSlice(3), " ") + } + dirty := gtype.NewBool() + _, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) { + if gfile.ExtName(event.Path) != "go" { + return + } + // Ignore swagger file. + if gfile.Basename(event.Path) == "data-swagger.go" { + return + } + // Variable <dirty> is used for running the changes only one in one second. + if !dirty.Cas(false, true) { + return + } + // With some delay in case of multiple code changes in very short interval. + gtimer.SetTimeout(1500*gtime.MS, func() { + defer dirty.Set(false) + mlog.Printf(`go file changes: %s`, event.String()) + app.Run() + }) + }) + if err != nil { + mlog.Fatal(err) + } + go app.Run() + select {} +} + +func (app *App) Run() { + // Rebuild and run the codes. + renamePath := "" + mlog.Printf("build: %s", app.File) + outputPath := gfile.Join("bin", gfile.Name(app.File)) + if runtime.GOOS == "windows" { + outputPath += ".exe" + if gfile.Exists(outputPath) { + renamePath = outputPath + "~" + if err := gfile.Rename(outputPath, renamePath); err != nil { + mlog.Print(err) + } + } + } + // Auto swagger. + if app.Swagger { + if err := gproc.ShellRun(`gf swagger`); err != nil { + return + } + if gfile.Exists("swagger") { + packCmd := fmt.Sprintf(`gf pack %s packed/%s -n packed -y`, "swagger", swagger.PackedGoFileName) + mlog.Print(packCmd) + if err := gproc.ShellRun(packCmd); err != nil { + return + } + } + } + // In case of `pipe: too many open files` error. + // Build the app. + buildCommand := fmt.Sprintf(`go build -o %s %s %s`, outputPath, app.Options, app.File) + mlog.Print(buildCommand) + result, err := gproc.ShellExec(buildCommand) + if err != nil { + mlog.Printf("build error: \n%s%s", result, err.Error()) + return + } + // Kill the old process if build successfully. + if process != nil { + if err := process.Kill(); err != nil { + mlog.Debugf("kill process error: %s", err.Error()) + //return + } + } + // Run the binary file. + runCommand := fmt.Sprintf(`%s %s`, outputPath, app.Args) + mlog.Print(runCommand) + if runtime.GOOS == "windows" { + // Special handling for windows platform. + // DO NOT USE "cmd /c" command. + process = gproc.NewProcess(runCommand, nil) + } else { + process = gproc.NewProcessCmd(runCommand, nil) + } + if pid, err := process.Start(); err != nil { + mlog.Printf("build running error: %s", err.Error()) + } else { + mlog.Printf("build running pid: %d", pid) + } +} diff --git a/tool/gf/commands/swagger/swagger.go b/tool/gf/commands/swagger/swagger.go new file mode 100644 index 000000000..2993f200c --- /dev/null +++ b/tool/gf/commands/swagger/swagger.go @@ -0,0 +1,154 @@ +package swagger + +import ( + "errors" + "fmt" + "github.com/gogf/gf/container/gtype" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gfsnotify" + "github.com/gogf/gf/os/gproc" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/os/gtimer" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/tool/gf/library/mlog" + "github.com/gogf/swagger" +) + +const ( + defaultOutput = "./swagger" + swaggoRepoPath = "github.com/swaggo/swag/cmd/swag" + PackedGoFileName = "swagger.go" +) + +func Help() { + mlog.Print(gstr.TrimLeft(` +USAGE + gf swagger [OPTION] + +OPTION + -s, --server start a swagger server at specified address after swagger files + produced + -o, --output the output directory for storage parsed swagger files, + the default output directory is "./swagger" + -/--pack auto parses and packs swagger into packed/swagger.go. + +EXAMPLES + gf swagger + gf swagger --pack + gf swagger -s 8080 + gf swagger -s 127.0.0.1:8080 + gf swagger -o ./document/swagger + + +DESCRIPTION + The "swagger" command parses the current project and produces swagger API description + files, which can be used in swagger API server. If used with "-s/--server" option, it + watches the changes of go files of current project and reproduces the swagger files, + which is quite convenient for local API development. + If it fails in command "swag", please firstly check your system PATH whether containing + go binary path, or you can install the "swag" tool manually referring to: + https://github.com/swaggo/swag +`)) +} + +func Run() { + mlog.SetHeaderPrint(true) + parser, err := gcmd.Parse(g.MapStrBool{ + "s,server": true, + "o,output": true, + "pack": false, + }) + if err != nil { + mlog.Fatal(err) + } + server := parser.GetOpt("server") + output := parser.GetOpt("output", defaultOutput) + // Generate swagger files. + if err := generateSwaggerFiles(output, parser.ContainsOpt("pack")); err != nil { + mlog.Print(err) + } + // Watch the go file changes and regenerate the swagger files. + dirty := gtype.NewBool() + _, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) { + if gfile.ExtName(event.Path) != "go" || gstr.Contains(event.Path, "swagger") { + return + } + // Variable <dirty> is used for running the changes only one in one second. + if !dirty.Cas(false, true) { + return + } + // With some delay in case of multiple code changes in very short interval. + gtimer.SetTimeout(1500*gtime.MS, func() { + mlog.Printf(`go file changes: %s`, event.String()) + mlog.Print(`reproducing swagger files...`) + if err := generateSwaggerFiles(output, parser.ContainsOpt("pack")); err != nil { + mlog.Print(err) + } else { + mlog.Print(`done!`) + } + dirty.Set(false) + }) + }) + if err != nil { + mlog.Fatal(err) + } + // Swagger server starts. + if server != "" { + if gstr.IsNumeric(server) { + server = ":" + server + } + s := g.Server() + s.Plugin(&swagger.Swagger{}) + s.SetAddr(server) + s.Run() + } +} + +// generateSwaggerFiles generates necessary swagger files. +func generateSwaggerFiles(output string, pack bool) error { + mlog.Print(`producing swagger files...`) + // Temporary storing swagger files directory. + tempOutputPath := gfile.Join(gfile.TempDir(), "swagger") + if gfile.Exists(tempOutputPath) { + gfile.Remove(tempOutputPath) + } + gfile.Mkdir(tempOutputPath) + // Check and install swag tool. + swag := gproc.SearchBinary("swag") + if swag == "" { + err := gproc.ShellRun(fmt.Sprintf(`go get -u -v %s`, swaggoRepoPath)) + if err != nil { + return err + } + } + // Generate swagger files using swag. + command := fmt.Sprintf(`swag init -o %s`, tempOutputPath) + result, err := gproc.ShellExec(command) + if err != nil { + return errors.New(result + err.Error()) + } + if !gfile.Exists(gfile.Join(tempOutputPath, "swagger.json")) { + return errors.New("make swagger files failed") + } + if !gfile.Exists(output) { + gfile.Mkdir(output) + } + if err = gfile.CopyFile( + gfile.Join(tempOutputPath, "swagger.json"), + gfile.Join(output, "swagger.json"), + ); err != nil { + return err + } + mlog.Print(`done!`) + // Auto pack into go file. + if pack && gfile.Exists("swagger") { + packCmd := fmt.Sprintf(`gf pack %s packed/%s -n packed`, "swagger", PackedGoFileName) + mlog.Print(packCmd) + if err := gproc.ShellRun(packCmd); err != nil { + return err + } + } + return nil +} diff --git a/tool/gf/commands/update/update.go b/tool/gf/commands/update/update.go new file mode 100644 index 000000000..919e71e9a --- /dev/null +++ b/tool/gf/commands/update/update.go @@ -0,0 +1,95 @@ +package update + +import ( + "fmt" + "github.com/gogf/gf/crypto/gmd5" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/tool/gf/library/mlog" + "runtime" +) + +var ( + cdnUrl = g.Config("url").GetString("cdn.url") + homeUrl = g.Config("url").GetString("home.url") +) + +func init() { + if cdnUrl == "" { + mlog.Fatal("CDN configuration cannot be empty") + } + if homeUrl == "" { + mlog.Fatal("Home configuration cannot be empty") + } +} + +func Run() { + mlog.Print("checking...") + md5Url := homeUrl + `/cli/binary/md5` + latestMd5 := ghttp.GetContent(md5Url, g.Map{ + "os": runtime.GOOS, + "arch": runtime.GOARCH, + }) + if latestMd5 == "" { + mlog.Fatal("get the latest binary md5 failed, may be network issue") + } + localMd5, err := gmd5.EncryptFile(gfile.SelfPath()) + if err != nil { + mlog.Fatal("calculate local binary md5 failed,", err.Error()) + } + if localMd5 != latestMd5 { + mlog.Print("downloading...") + ext := "" + if runtime.GOOS == "windows" { + ext = ".exe" + } + downloadUrl := fmt.Sprintf( + `%s/cli/binary/%s_%s/gf%s?%s`, + cdnUrl, + runtime.GOOS, + runtime.GOARCH, + ext, + latestMd5, + ) + mlog.Debugf("HTTP GET %s", downloadUrl) + res, err := ghttp.Get(downloadUrl) + if err != nil || res.StatusCode != 200 { + mlog.Fatalf( + "downloading failed for %s %s, may be network issue:\n%s", + runtime.GOOS, runtime.GOARCH, res.ReadAllString(), + ) + } + defer res.Close() + data := res.ReadAll() + mlog.Print("installing...") + var ( + binPath = gfile.SelfPath() + binDirPath = gfile.SelfDir() + renamePath = binPath + "~" + ) + if runtime.GOOS == "windows" { + // Rename myself for windows. + if err := gfile.Rename(binPath, renamePath); err != nil { + mlog.Fatal("rename binary file failed:", err.Error()) + } + defer gfile.Remove(renamePath) + } else { + // Remove the binary for other platforms. + if gfile.IsWritable(binDirPath) { + if err := gfile.Remove(binPath); err != nil { + mlog.Fatal("remove binary failed:", err.Error()) + } + } + } + if err := gfile.PutBytes(binPath, data); err != nil { + mlog.Fatal("install binary failed:", err.Error()) + } + if err := gfile.Chmod(binPath, 0777); err != nil { + mlog.Fatal("chmod binary failed:", err.Error()) + } + mlog.Print("gf binary is now updated to the latest version") + } else { + mlog.Print("it's the latest version, no need updates") + } +} diff --git a/tool/gf/config/url.toml b/tool/gf/config/url.toml new file mode 100644 index 000000000..6f965f328 --- /dev/null +++ b/tool/gf/config/url.toml @@ -0,0 +1,17 @@ +# gf pack config packed/config.go + + +# gf home site cdn. +[cdn] + url = "https://gfcdn.johng.cn" + +# home site url. +[home] + url = "https://goframe.org" + +# go proxy for gf get. +[proxy] + urls = [ + "https://goproxy.io/", + "https://goproxy.cn/" + ] \ No newline at end of file diff --git a/tool/gf/go.mod b/tool/gf/go.mod new file mode 100644 index 000000000..940df97cf --- /dev/null +++ b/tool/gf/go.mod @@ -0,0 +1,15 @@ +module github.com/gogf/gf/tool/gf + +go 1.11 + +require ( + github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e + github.com/gogf/gf v1.16.1 + github.com/gogf/swagger v1.0.4 + github.com/gorilla/websocket v1.4.2 // indirect + github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 // indirect + github.com/lib/pq v1.2.0 + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/olekukonko/tablewriter v0.0.5 + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect +) diff --git a/tool/gf/go.sum b/tool/gf/go.sum new file mode 100644 index 000000000..4d7f691bd --- /dev/null +++ b/tool/gf/go.sum @@ -0,0 +1,93 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4= +github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= +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/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e h1:LzwWXEScfcTu7vUZNlDDWDARoSGEtvlDKK2BYHowNeE= +github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/gogf/gf v1.13.8-0.20201010060010-09ce105eeeab/go.mod h1:nGAMjE4ohU2bwj4Gk3h25K6rEkPZMDdvsmyifpFcuMQ= +github.com/gogf/gf v1.16.1 h1:J2kcf8ufbuIIGrbeXfMH/CCkH+hUyC9lrQKrLXZrKVg= +github.com/gogf/gf v1.16.1/go.mod h1:5eEgE9fWeRQW8dJE3GLpCy0KkNitXh6POesdJiBE/lw= +github.com/gogf/swagger v1.0.4 h1:MILniFKPh52/26s+z8taSh8thn1tq2RaeWM7rYX1dRw= +github.com/gogf/swagger v1.0.4/go.mod h1:4rD12TCoDz60jmgtuFnx7ZBWUM92tXc/qtrIrkBIp5Q= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gqcn/structs v1.1.1/go.mod h1:/aBhTBSsKQ2Ec9pbnYdGphtdWXHFn4KrCL0fXM/Adok= +github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 h1:h3iGUlU8HyW4baKd6D+h1mwOHnM2kwskSuG6Bv4tSbc= +github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng= +go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= +go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg= +go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= +go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q= +go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= +go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc= +go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tool/gf/library/allyes/allyes.go b/tool/gf/library/allyes/allyes.go new file mode 100644 index 000000000..e71c51f79 --- /dev/null +++ b/tool/gf/library/allyes/allyes.go @@ -0,0 +1,22 @@ +package allyes + +import ( + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/genv" +) + +const ( + EnvName = "GF_CLI_ALL_YES" +) + +// Init initializes the package manually. +func Init() { + if gcmd.ContainsOpt("y") { + genv.Set(EnvName, "1") + } +} + +// Check checks whether option allow all yes for command. +func Check() bool { + return genv.Get(EnvName) == "1" +} diff --git a/tool/gf/library/mlog/mlog.go b/tool/gf/library/mlog/mlog.go new file mode 100644 index 000000000..1f4c4fe7e --- /dev/null +++ b/tool/gf/library/mlog/mlog.go @@ -0,0 +1,63 @@ +package mlog + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/genv" + "github.com/gogf/gf/os/glog" +) + +const ( + headerPrintEnvName = "GF_CLI_MLOG_HEADER" +) + +var ( + logger = glog.New() +) + +func init() { + logger.SetStack(false) + logger.SetDebug(false) + if genv.Get(headerPrintEnvName) == "1" { + logger.SetHeaderPrint(true) + } else { + logger.SetHeaderPrint(false) + } + if gcmd.ContainsOpt("debug") { + logger.SetDebug(true) + } +} + +// SetHeaderPrint enables/disables header printing to stdout. +func SetHeaderPrint(enabled bool) { + logger.SetHeaderPrint(enabled) + if enabled { + genv.Set(headerPrintEnvName, "1") + } else { + genv.Set(headerPrintEnvName, "0") + } +} + +func Print(v ...interface{}) { + logger.Print(v...) +} + +func Printf(format string, v ...interface{}) { + logger.Printf(format, v...) +} + +func Fatal(v ...interface{}) { + logger.Fatal(append(g.Slice{"Error:"}, v...)...) +} + +func Fatalf(format string, v ...interface{}) { + logger.Fatalf("Error: "+format, v...) +} + +func Debug(v ...interface{}) { + logger.Debug(append(g.Slice{"Debug:"}, v...)...) +} + +func Debugf(format string, v ...interface{}) { + logger.Debugf("Debug: "+format, v...) +} diff --git a/tool/gf/library/proxy/proxy.go b/tool/gf/library/proxy/proxy.go new file mode 100644 index 000000000..bcaba6ea3 --- /dev/null +++ b/tool/gf/library/proxy/proxy.go @@ -0,0 +1,33 @@ +package proxy + +import ( + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/os/genv" + "github.com/gogf/gf/tool/gf/library/mlog" + "time" +) + +var ( + httpClient = ghttp.NewClient() +) + +func init() { + httpClient.SetTimeout(time.Second) +} + +// AutoSet automatically checks and sets the golang proxy. +func AutoSet() { + SetGoModuleEnabled(true) + genv.Set("GOPROXY", "https://goproxy.cn") +} + +// SetGoModuleEnabled enables/disables the go module feature. +func SetGoModuleEnabled(enabled bool) { + if enabled { + mlog.Debug("set GO111MODULE=on") + genv.Set("GO111MODULE", "on") + } else { + mlog.Debug("set GO111MODULE=off") + genv.Set("GO111MODULE", "off") + } +} diff --git a/tool/gf/library/utils/utils.go b/tool/gf/library/utils/utils.go new file mode 100644 index 000000000..ce29dd738 --- /dev/null +++ b/tool/gf/library/utils/utils.go @@ -0,0 +1,18 @@ +package utils + +import ( + "fmt" + "github.com/gogf/gf/os/gproc" +) + +var ( + // gofmtPath is the binary path of command `gofmt`. + gofmtPath = gproc.SearchBinaryPath("gofmt") +) + +// GoFmt formats the source file using command `gofmt -w -s PATH`. +func GoFmt(path string) { + if gofmtPath != "" { + gproc.ShellExec(fmt.Sprintf(`%s -w -s %s`, gofmtPath, path)) + } +} diff --git a/tool/gf/main.go b/tool/gf/main.go new file mode 100644 index 000000000..e985fd2ee --- /dev/null +++ b/tool/gf/main.go @@ -0,0 +1,215 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/text/gregex" + "github.com/gogf/gf/tool/gf/commands/env" + "github.com/gogf/gf/tool/gf/commands/mod" + "strings" + + "github.com/gogf/gf/os/gbuild" + "github.com/gogf/gf/os/gcmd" + "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/text/gstr" + _ "github.com/gogf/gf/tool/gf/boot" + "github.com/gogf/gf/tool/gf/commands/build" + "github.com/gogf/gf/tool/gf/commands/docker" + "github.com/gogf/gf/tool/gf/commands/fix" + "github.com/gogf/gf/tool/gf/commands/gen" + "github.com/gogf/gf/tool/gf/commands/get" + "github.com/gogf/gf/tool/gf/commands/initialize" + "github.com/gogf/gf/tool/gf/commands/install" + "github.com/gogf/gf/tool/gf/commands/pack" + "github.com/gogf/gf/tool/gf/commands/run" + "github.com/gogf/gf/tool/gf/commands/swagger" + "github.com/gogf/gf/tool/gf/commands/update" + "github.com/gogf/gf/tool/gf/library/allyes" + "github.com/gogf/gf/tool/gf/library/mlog" + "github.com/gogf/gf/tool/gf/library/proxy" +) + +func init() { + // Automatically sets the golang proxy for all commands. + proxy.AutoSet() +} + +var ( + helpContent = gstr.TrimLeft(` +USAGE + gf COMMAND [ARGUMENT] [OPTION] + +COMMAND + env show current Golang environment variables + get install or update GF to system in default... + gen automatically generate go files for ORM models... + mod extra features for go modules... + run running go codes with hot-compiled-like feature... + init create and initialize an empty GF project... + help show more information about a specified command + pack packing any file/directory to a resource file, or a go file... + build cross-building go project for lots of platforms... + docker create a docker image for current GF project... + swagger swagger feature for current project... + update update current gf binary to latest one (might need root/admin permission) + install install gf binary to system (might need root/admin permission) + version show current binary version info + +OPTION + -y all yes for all command without prompt ask + -?,-h show this help or detail for specified command + -v,-i show version information + +ADDITIONAL + Use 'gf help COMMAND' or 'gf COMMAND -h' for detail about a command, which has '...' + in the tail of their comments. +`) +) + +func main() { + defer func() { + if exception := recover(); exception != nil { + if err, ok := exception.(error); ok { + mlog.Print(gerror.Current(err).Error()) + } else { + panic(exception) + } + } + }() + + allyes.Init() + + command := gcmd.GetArg(1) + // Help information + if gcmd.ContainsOpt("h") && command != "" { + help(command) + return + } + switch command { + case "help": + help(gcmd.GetArg(2)) + case "version": + version() + case "env": + env.Run() + case "get": + get.Run() + case "gen": + gen.Run() + case "fix": + fix.Run() + case "mod": + mod.Run() + case "init": + initialize.Run() + case "pack": + pack.Run() + case "docker": + docker.Run() + case "swagger": + swagger.Run() + case "update": + update.Run() + case "install": + install.Run() + case "build": + build.Run() + case "run": + run.Run() + default: + for k := range gcmd.GetOptAll() { + switch k { + case "?", "h": + mlog.Print(helpContent) + return + case "i", "v": + version() + return + } + } + // No argument or option, do installation checks. + if !install.IsInstalled() { + mlog.Print("hi, it seams it's the first time you installing gf cli.") + s := gcmd.Scanf("do you want to install gf binary to your system? [y/n]: ") + if strings.EqualFold(s, "y") { + install.Run() + gcmd.Scan("press <Enter> to exit...") + return + } + } + mlog.Print(helpContent) + } +} + +// help shows more information for specified command. +func help(command string) { + switch command { + case "get": + get.Help() + case "gen": + gen.Help() + case "init": + initialize.Help() + case "docker": + docker.Help() + case "swagger": + swagger.Help() + case "build": + build.Help() + case "pack": + pack.Help() + case "run": + run.Help() + case "mod": + mod.Help() + default: + mlog.Print(helpContent) + } +} + +// version prints the version information of the cli tool. +func version() { + info := gbuild.Info() + if info["git"] == "" { + info["git"] = "none" + } + mlog.Printf(`GoFrame CLI Tool %s, https://goframe.org`, gf.VERSION) + gfVersion, err := getGFVersionOfCurrentProject() + if err != nil { + gfVersion = err.Error() + } else { + gfVersion = gfVersion + " in current go.mod" + } + mlog.Printf(`GoFrame Version: %s`, gfVersion) + mlog.Printf(`CLI Installed At: %s`, gfile.SelfPath()) + if info["gf"] == "" { + mlog.Print(`Current is a custom installed version, no installation information.`) + return + } + + mlog.Print(gstr.Trim(fmt.Sprintf(` +CLI Built Detail: + Go Version: %s + GF Version: %s + Git Commit: %s + Build Time: %s +`, info["go"], info["gf"], info["git"], info["time"]))) +} + +// getGFVersionOfCurrentProject checks and returns the GoFrame version current project using. +func getGFVersionOfCurrentProject() (string, error) { + goModPath := gfile.Join(gfile.Pwd(), "go.mod") + if gfile.Exists(goModPath) { + match, err := gregex.MatchString(`github.com/gogf/gf\s+([\w\d\.]+)`, gfile.GetContents(goModPath)) + if err != nil { + return "", err + } + if len(match) > 1 { + return match[1], nil + } + return "", gerror.New("cannot find goframe requirement in go.mod") + } else { + return "", gerror.New("cannot find go.mod") + } +} diff --git a/tool/gf/packed/config.go b/tool/gf/packed/config.go new file mode 100644 index 000000000..f066cc66e --- /dev/null +++ b/tool/gf/packed/config.go @@ -0,0 +1,9 @@ +package packed + +import "github.com/gogf/gf/os/gres" + +func init() { + if err := gres.Add("H4sIAAAAAAAC/wrwZmYRYeBg4GBIi7kQwIAE+Bk4GZLz89Iy0/VLi3L0SvJzc0JDWBkYC9a9iMvpc+xrNuBxvf5DJH7m2xM3+494KolpqLRpHtFgWbm3dmpI1u6E5+//z3sq73HE7pf0nmU7PGYfTonvdO1cYbg5j/F4ENOfKTPmzL/kwFDwTNT2TMypuj+R2hu//bdST5zvs9X+1cxwtfDPW1cb9kcnnL3kEvgm8j2/y9mv7JytJ7devW1/4XDg9VrZTAb9rLPvQx9ONr3Ce56BgeH//wBvdo4D12YyTmNgYPjFwMAA8xsDw+EMwUBkv7HB/Qb20v4Gq3iQZmQlAd6MTCLMiKBBNhgUNDCwpBFE4goohCnYHQEBAgz/He/ATUFyEisbSJqJgYmhmYGBQZIRxAMEAAD//5uozoWyAQAA"); err != nil { + panic("add binary content to resource manager failed: " + err.Error()) + } +} From 012121ea7798dee7168b80c7919b0353e8de6ef5 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 30 Jun 2021 20:00:50 +0800 Subject: [PATCH 359/492] gf cli updates --- .gitignore | 2 + go.mod | 5 +- go.sum | 33 +++++ tool/gf/README.MD | 14 +- tool/gf/README_ZH.MD | 65 ---------- tool/gf/commands/install/install.go | 192 ---------------------------- tool/gf/commands/update/update.go | 96 ++------------ tool/gf/main.go | 42 +----- 8 files changed, 65 insertions(+), 384 deletions(-) delete mode 100644 tool/gf/README_ZH.MD delete mode 100644 tool/gf/commands/install/install.go diff --git a/.gitignore b/.gitignore index 79efb0edc..fdb500de8 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ cbuild **/.DS_Store .vscode/ .example/other/ +main +gf \ No newline at end of file diff --git a/go.mod b/go.mod index 25bb74983..edda9e83b 100644 --- a/go.mod +++ b/go.mod @@ -5,20 +5,17 @@ go 1.14 require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 - github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.4.9 + github.com/gogf/gf/tool/gf v0.0.0-20210629161552-1e628b9edb8e // indirect github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/websocket v1.4.2 github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 - github.com/mattn/go-runewidth v0.0.10 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/olekukonko/tablewriter v0.0.5 go.opentelemetry.io/otel v1.0.0-RC1 go.opentelemetry.io/otel/oteltest v1.0.0-RC1 go.opentelemetry.io/otel/trace v1.0.0-RC1 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 golang.org/x/text v0.3.4 - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c ) diff --git a/go.sum b/go.sum index 61103d738..8cf065292 100644 --- a/go.sum +++ b/go.sum @@ -6,26 +6,49 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 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/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e h1:LzwWXEScfcTu7vUZNlDDWDARoSGEtvlDKK2BYHowNeE= +github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/gogf/gf v1.13.8-0.20201010060010-09ce105eeeab/go.mod h1:nGAMjE4ohU2bwj4Gk3h25K6rEkPZMDdvsmyifpFcuMQ= +github.com/gogf/gf v1.16.1/go.mod h1:5eEgE9fWeRQW8dJE3GLpCy0KkNitXh6POesdJiBE/lw= +github.com/gogf/gf/tool/gf v0.0.0-20210629161552-1e628b9edb8e h1:0yamWn3TAI/nFVDI9pwofWDekdMt4qQLwQdMmE59TNA= +github.com/gogf/gf/tool/gf v0.0.0-20210629161552-1e628b9edb8e/go.mod h1:bCukUi4KQbIKZ4W5xLPKHe3eYparuvqfqBn8ZiYeCnY= github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw= github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8= +github.com/gogf/swagger v1.0.4 h1:MILniFKPh52/26s+z8taSh8thn1tq2RaeWM7rYX1dRw= +github.com/gogf/swagger v1.0.4/go.mod h1:4rD12TCoDz60jmgtuFnx7ZBWUM92tXc/qtrIrkBIp5Q= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gqcn/structs v1.1.1/go.mod h1:/aBhTBSsKQ2Ec9pbnYdGphtdWXHFn4KrCL0fXM/Adok= +github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 h1:h3iGUlU8HyW4baKd6D+h1mwOHnM2kwskSuG6Bv4tSbc= github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -33,26 +56,36 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc= go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I= +go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= +go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0cljhGk9sB0Yk= go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4= +go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58= go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/tool/gf/README.MD b/tool/gf/README.MD index 5345ea818..718ba82ff 100644 --- a/tool/gf/README.MD +++ b/tool/gf/README.MD @@ -1,17 +1,22 @@ -# GF-CLI -English | [简体中文](README_ZH.MD) +# GoFrame CLI Tool -`gf` is a powerful CLI tool for building [GoFrame](https://goframe.org) application with convenience. +Powerful CLI tool for building [GoFrame](https://goframe.org) application with convenience. ## 1. Install > You might need setting the goproxy to make through building. 1. Latest version + > Go version >= v1.16 ``` go install github.com/gogf/gf/tool/gf@latest ``` + > Go version < v1.16 + ``` + go install github.com/gogf/gf/tool/gf + ``` 1. Specified version + > Go version >= v1.16 ``` go install github.com/gogf/gf/tool/gf@v1.16.0 ``` @@ -35,8 +40,7 @@ COMMAND build cross-building go project for lots of platforms... docker create a docker image for current GF project... swagger swagger feature for current project... - update update current gf binary to latest one (might need root/admin permission) - install install gf binary to system (might need root/admin permission) + update update current gf binary to latest one version show current binary version info OPTION diff --git a/tool/gf/README_ZH.MD b/tool/gf/README_ZH.MD deleted file mode 100644 index 7ad47d8f0..000000000 --- a/tool/gf/README_ZH.MD +++ /dev/null @@ -1,65 +0,0 @@ -# GF-CLI -[English](README.MD) | 简体中文 - -`gf`是一款强大的[GoFrame](https://goframe.org)开发工具链,使得我们开发基于`GoFrame`框架的项目更加便捷。 - -## 1. 安装 - -1. 最新版本 - ``` - go install github.com/gogf/gf/tool/gf@latest - ``` -1. 指定版本 - ``` - go install github.com/gogf/gf/tool/gf@v1.16.0 - ``` -> 注意:在`gf gen`命令中,由于`sqlite`和`oracle`数据库需要`cgo`和`gcc`支持,因此预编译的二进制中不提供对这两个数据库的支持,您需要手动修改源码,去掉对应源码文件中指定数据库类型的`import`注释后手动编译支持。 - -## 2. 命令 -```html -$ gf -USAGE - gf COMMAND [ARGUMENT] [OPTION] - -COMMAND - env show current Golang environment variables - get install or update GF to system in default... - gen automatically generate go files for ORM models... - mod extra features for go modules... - run running go codes with hot-compiled-like feature... - init initialize an empty GF project at current working directory... - help show more information about a specified command - pack packing any file/directory to a resource file, or a go file... - build cross-building go project for lots of platforms... - docker create a docker image for current GF project... - swagger swagger feature for current project... - update update current gf binary to latest one (might need root/admin permission) - install install gf binary to system (might need root/admin permission) - version show current binary version info - -OPTION - -y all yes for all command without prompt ask - -?,-h show this help or detail for specified command - -v,-i show version information - -ADDITIONAL - Use 'gf help COMMAND' or 'gf COMMAND -h' for detail about a command, which has '...' - in the tail of their comments. -``` - -## 3. 文档 - -完善详尽的中文文档请参考`GoFrame`官网板块:[开发工具](https://itician.org/pages/viewpage.action?pageId=1114260) - -## 4. FAQ - -### 1). `gf run` 命令报错 `pipe: too many open files` - -请执行`ulimit -n 65535`命令扩展您当前终端会话支持的最大文件打开数,随后再执行`gf run`。需要注意的是该命令仅对当前终端会话有效。 - - - - - - - diff --git a/tool/gf/commands/install/install.go b/tool/gf/commands/install/install.go deleted file mode 100644 index e178c3f3f..000000000 --- a/tool/gf/commands/install/install.go +++ /dev/null @@ -1,192 +0,0 @@ -package install - -import ( - "github.com/gogf/gf/container/garray" - "github.com/gogf/gf/container/gset" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/genv" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/allyes" - "github.com/gogf/gf/tool/gf/library/mlog" - "github.com/gogf/gf/util/gconv" - "runtime" - "strings" -) - -// installFolderPath contains installFolderPath-related data, -// such as path, writable, binaryFilePath, and installed. -type installFolderPath struct { - path string - writable bool - binaryFilePath string - installed bool -} - -// Run does the installation. -func Run() { - // Ask where to install. - paths := getInstallPathsData() - if len(paths) <= 0 { - mlog.Printf("no path detected, you can manually install gf by copying the binary to path folder.") - return - } - mlog.Printf("I found some installable paths for you(from $PATH): ") - mlog.Printf(" %2s | %8s | %9s | %s", "Id", "Writable", "Installed", "Path") - - // Print all paths status and determine the default selectedID value. - var ( - selectedID = -1 - pathSet = gset.NewStrSet() // Used for repeated items filtering. - ) - for id, aPath := range paths { - if !pathSet.AddIfNotExist(aPath.path) { - continue - } - mlog.Printf(" %2d | %8t | %9t | %s", id, aPath.writable, aPath.installed, aPath.path) - if selectedID == -1 { - // Use the previously installed path as the most priority choice. - if aPath.installed { - selectedID = id - } - } - } - // If there's no previously installed path, use the first writable path. - if selectedID == -1 { - // Order by choosing priority. - commonPaths := garray.NewStrArrayFrom(g.SliceStr{ - `/usr/local/bin`, - `/usr/bin`, - `/usr/sbin`, - `C:\Windows`, - `C:\Windows\system32`, - `C:\Go\bin`, - `C:\Program Files`, - `C:\Program Files (x86)`, - }) - // Check the common installation directories. - commonPaths.Iterator(func(k int, v string) bool { - for id, aPath := range paths { - if strings.EqualFold(aPath.path, v) { - selectedID = id - return false - } - } - return true - }) - if selectedID == -1 { - selectedID = 0 - } - } - - if allyes.Check() { - // Use the default selectedID. - mlog.Printf("please choose one installation destination [default %d]: %d", selectedID, selectedID) - } else { - // Get input and update selectedID. - input := gcmd.Scanf("please choose one installation destination [default %d]: ", selectedID) - if input != "" { - selectedID = gconv.Int(input) - } - } - - // Check if out of range. - if selectedID >= len(paths) || selectedID < 0 { - mlog.Printf("invalid install destination Id: %d", selectedID) - return - } - - // Get selected destination path. - dstPath := paths[selectedID] - - // Install the new binary. - err := gfile.CopyFile(gfile.SelfPath(), dstPath.binaryFilePath) - if err != nil { - mlog.Printf("install gf binary to '%s' failed: %v", dstPath.path, err) - mlog.Printf("you can manually install gf by copying the binary to folder: %s", dstPath.path) - } else { - mlog.Printf("gf binary is successfully installed to: %s", dstPath.path) - } - - // Uninstall the old binary. - for _, aPath := range paths { - // Do not delete myself. - if aPath.binaryFilePath != "" && - aPath.binaryFilePath != dstPath.binaryFilePath && - gfile.SelfPath() != aPath.binaryFilePath { - gfile.Remove(aPath.binaryFilePath) - } - } -} - -// IsInstalled returns whether the binary is installed. -func IsInstalled() bool { - paths := getInstallPathsData() - for _, aPath := range paths { - if aPath.installed { - return true - } - } - return false -} - -// GetInstallPathsData returns the installation paths data for the binary. -func getInstallPathsData() []installFolderPath { - var folderPaths []installFolderPath - // Pre generate binaryFileName. - binaryFileName := "gf" + gfile.Ext(gfile.SelfPath()) - switch runtime.GOOS { - case "darwin": - folderPaths = checkPathAndAppendToInstallFolderPath( - folderPaths, "/usr/local/bin", binaryFileName, - ) - default: - // Search and find the writable directory path. - envPath := genv.Get("PATH", genv.Get("Path")) - if gstr.Contains(envPath, ";") { - for _, v := range gstr.SplitAndTrim(envPath, ";") { - folderPaths = checkPathAndAppendToInstallFolderPath( - folderPaths, v, binaryFileName) - } - } else if gstr.Contains(envPath, ":") { - for _, v := range gstr.SplitAndTrim(envPath, ":") { - folderPaths = checkPathAndAppendToInstallFolderPath( - folderPaths, v, binaryFileName) - } - } else if envPath != "" { - folderPaths = checkPathAndAppendToInstallFolderPath( - folderPaths, envPath, binaryFileName) - } else { - folderPaths = checkPathAndAppendToInstallFolderPath( - folderPaths, "/usr/local/bin", binaryFileName) - } - } - return folderPaths -} - -// checkPathAndAppendToInstallFolderPath checks if <path> is writable and already installed. -// It adds the <path> to <folderPaths> if it is writable or already installed, or else it ignores the <path>. -func checkPathAndAppendToInstallFolderPath(folderPaths []installFolderPath, path string, binaryFileName string) []installFolderPath { - var ( - binaryFilePath = gfile.Join(path, binaryFileName) - writable = gfile.IsWritable(path) - installed = isInstalled(binaryFilePath) - ) - if !writable && !installed { - return folderPaths - } - return append( - folderPaths, - installFolderPath{ - path: path, - writable: writable, - binaryFilePath: binaryFilePath, - installed: installed, - }) -} - -// Check if this gf binary path exists. -func isInstalled(path string) bool { - return gfile.Exists(path) -} diff --git a/tool/gf/commands/update/update.go b/tool/gf/commands/update/update.go index 919e71e9a..6ebc9eea0 100644 --- a/tool/gf/commands/update/update.go +++ b/tool/gf/commands/update/update.go @@ -1,95 +1,25 @@ package update import ( - "fmt" - "github.com/gogf/gf/crypto/gmd5" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" - "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/os/gproc" + "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/tool/gf/library/mlog" "runtime" ) -var ( - cdnUrl = g.Config("url").GetString("cdn.url") - homeUrl = g.Config("url").GetString("home.url") -) - -func init() { - if cdnUrl == "" { - mlog.Fatal("CDN configuration cannot be empty") - } - if homeUrl == "" { - mlog.Fatal("Home configuration cannot be empty") - } -} - func Run() { - mlog.Print("checking...") - md5Url := homeUrl + `/cli/binary/md5` - latestMd5 := ghttp.GetContent(md5Url, g.Map{ - "os": runtime.GOOS, - "arch": runtime.GOARCH, - }) - if latestMd5 == "" { - mlog.Fatal("get the latest binary md5 failed, may be network issue") + goBinPath := gproc.SearchBinary("go") + if goBinPath == "" { + mlog.Fatal(`"go" command not found, install it first to step further`) } - localMd5, err := gmd5.EncryptFile(gfile.SelfPath()) - if err != nil { - mlog.Fatal("calculate local binary md5 failed,", err.Error()) - } - if localMd5 != latestMd5 { - mlog.Print("downloading...") - ext := "" - if runtime.GOOS == "windows" { - ext = ".exe" - } - downloadUrl := fmt.Sprintf( - `%s/cli/binary/%s_%s/gf%s?%s`, - cdnUrl, - runtime.GOOS, - runtime.GOARCH, - ext, - latestMd5, - ) - mlog.Debugf("HTTP GET %s", downloadUrl) - res, err := ghttp.Get(downloadUrl) - if err != nil || res.StatusCode != 200 { - mlog.Fatalf( - "downloading failed for %s %s, may be network issue:\n%s", - runtime.GOOS, runtime.GOARCH, res.ReadAllString(), - ) - } - defer res.Close() - data := res.ReadAll() - mlog.Print("installing...") - var ( - binPath = gfile.SelfPath() - binDirPath = gfile.SelfDir() - renamePath = binPath + "~" - ) - if runtime.GOOS == "windows" { - // Rename myself for windows. - if err := gfile.Rename(binPath, renamePath); err != nil { - mlog.Fatal("rename binary file failed:", err.Error()) - } - defer gfile.Remove(renamePath) - } else { - // Remove the binary for other platforms. - if gfile.IsWritable(binDirPath) { - if err := gfile.Remove(binPath); err != nil { - mlog.Fatal("remove binary failed:", err.Error()) - } - } - } - if err := gfile.PutBytes(binPath, data); err != nil { - mlog.Fatal("install binary failed:", err.Error()) - } - if err := gfile.Chmod(binPath, 0777); err != nil { - mlog.Fatal("chmod binary failed:", err.Error()) - } - mlog.Print("gf binary is now updated to the latest version") + var err error + if gstr.CompareVersionGo(runtime.Version(), "go1.16.0") >= 0 { + err = gproc.ShellRun(`go install github.com/gogf/gf/tool/gf@latest`) } else { - mlog.Print("it's the latest version, no need updates") + err = gproc.ShellRun(`go install github.com/gogf/gf/tool/gf`) } + if err != nil { + mlog.Fatalf(`gf binary installation failed: %+v`, err) + } + mlog.Print("gf binary is now updated to the latest version") } diff --git a/tool/gf/main.go b/tool/gf/main.go index e985fd2ee..276691ebc 100644 --- a/tool/gf/main.go +++ b/tool/gf/main.go @@ -1,26 +1,23 @@ package main import ( - "fmt" + _ "github.com/gogf/gf/tool/gf/boot" + "github.com/gogf/gf" "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/text/gregex" - "github.com/gogf/gf/tool/gf/commands/env" - "github.com/gogf/gf/tool/gf/commands/mod" - "strings" - "github.com/gogf/gf/os/gbuild" "github.com/gogf/gf/os/gcmd" "github.com/gogf/gf/os/gfile" + "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" - _ "github.com/gogf/gf/tool/gf/boot" "github.com/gogf/gf/tool/gf/commands/build" "github.com/gogf/gf/tool/gf/commands/docker" + "github.com/gogf/gf/tool/gf/commands/env" "github.com/gogf/gf/tool/gf/commands/fix" "github.com/gogf/gf/tool/gf/commands/gen" "github.com/gogf/gf/tool/gf/commands/get" "github.com/gogf/gf/tool/gf/commands/initialize" - "github.com/gogf/gf/tool/gf/commands/install" + "github.com/gogf/gf/tool/gf/commands/mod" "github.com/gogf/gf/tool/gf/commands/pack" "github.com/gogf/gf/tool/gf/commands/run" "github.com/gogf/gf/tool/gf/commands/swagger" @@ -52,8 +49,7 @@ COMMAND build cross-building go project for lots of platforms... docker create a docker image for current GF project... swagger swagger feature for current project... - update update current gf binary to latest one (might need root/admin permission) - install install gf binary to system (might need root/admin permission) + update update current gf binary to latest one version show current binary version info OPTION @@ -111,8 +107,6 @@ func main() { swagger.Run() case "update": update.Run() - case "install": - install.Run() case "build": build.Run() case "run": @@ -128,16 +122,6 @@ func main() { return } } - // No argument or option, do installation checks. - if !install.IsInstalled() { - mlog.Print("hi, it seams it's the first time you installing gf cli.") - s := gcmd.Scanf("do you want to install gf binary to your system? [y/n]: ") - if strings.EqualFold(s, "y") { - install.Run() - gcmd.Scan("press <Enter> to exit...") - return - } - } mlog.Print(helpContent) } } @@ -183,18 +167,6 @@ func version() { } mlog.Printf(`GoFrame Version: %s`, gfVersion) mlog.Printf(`CLI Installed At: %s`, gfile.SelfPath()) - if info["gf"] == "" { - mlog.Print(`Current is a custom installed version, no installation information.`) - return - } - - mlog.Print(gstr.Trim(fmt.Sprintf(` -CLI Built Detail: - Go Version: %s - GF Version: %s - Git Commit: %s - Build Time: %s -`, info["go"], info["gf"], info["git"], info["time"]))) } // getGFVersionOfCurrentProject checks and returns the GoFrame version current project using. @@ -210,6 +182,6 @@ func getGFVersionOfCurrentProject() (string, error) { } return "", gerror.New("cannot find goframe requirement in go.mod") } else { - return "", gerror.New("cannot find go.mod") + return "", gerror.New("cannot find go.mod in current directory") } } From 84355c1ddd2f675c5aa58f5d80cf130a94959d9b Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 30 Jun 2021 20:07:46 +0800 Subject: [PATCH 360/492] gf cli updates --- tool/gf/README.MD | 3 +++ tool/gf/commands/gen/gen_dao.go | 4 ++-- tool/gf/commands/gen/gen_pbentity.go | 4 ++-- tool/gf/go.mod | 2 +- tool/gf/go.sum | 4 ++++ 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tool/gf/README.MD b/tool/gf/README.MD index 718ba82ff..6d0fb4104 100644 --- a/tool/gf/README.MD +++ b/tool/gf/README.MD @@ -7,6 +7,7 @@ Powerful CLI tool for building [GoFrame](https://goframe.org) application with c > You might need setting the goproxy to make through building. 1. Latest version + > Go version >= v1.16 ``` go install github.com/gogf/gf/tool/gf@latest @@ -15,7 +16,9 @@ Powerful CLI tool for building [GoFrame](https://goframe.org) application with c ``` go install github.com/gogf/gf/tool/gf ``` + 1. Specified version + > Go version >= v1.16 ``` go install github.com/gogf/gf/tool/gf@v1.16.0 diff --git a/tool/gf/commands/gen/gen_dao.go b/tool/gf/commands/gen/gen_dao.go index 73701fef7..1832b17b5 100644 --- a/tool/gf/commands/gen/gen_dao.go +++ b/tool/gf/commands/gen/gen_dao.go @@ -214,8 +214,8 @@ func doGenDaoForArray(index int, parser *gcmd.Parser) { match, _ := gregex.MatchString(`([a-z]+):(.+)`, linkInfo) if len(match) == 3 { gdb.AddConfigNode(tempGroup, gdb.ConfigNode{ - Type: gstr.Trim(match[1]), - LinkInfo: gstr.Trim(match[2]), + Type: gstr.Trim(match[1]), + Link: gstr.Trim(match[2]), }) db, _ = gdb.Instance(tempGroup) } diff --git a/tool/gf/commands/gen/gen_pbentity.go b/tool/gf/commands/gen/gen_pbentity.go index 8c270f9e7..a21d81a61 100644 --- a/tool/gf/commands/gen/gen_pbentity.go +++ b/tool/gf/commands/gen/gen_pbentity.go @@ -182,8 +182,8 @@ func doGenPbEntityForArray(index int, parser *gcmd.Parser) { match, _ := gregex.MatchString(`([a-z]+):(.+)`, linkInfo) if len(match) == 3 { gdb.AddConfigNode(tempGroup, gdb.ConfigNode{ - Type: gstr.Trim(match[1]), - LinkInfo: gstr.Trim(match[2]), + Type: gstr.Trim(match[1]), + Link: gstr.Trim(match[2]), }) db, _ = gdb.Instance(tempGroup) } diff --git a/tool/gf/go.mod b/tool/gf/go.mod index 940df97cf..426054699 100644 --- a/tool/gf/go.mod +++ b/tool/gf/go.mod @@ -4,7 +4,7 @@ go 1.11 require ( github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e - github.com/gogf/gf v1.16.1 + github.com/gogf/gf v1.16.4 github.com/gogf/swagger v1.0.4 github.com/gorilla/websocket v1.4.2 // indirect github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 // indirect diff --git a/tool/gf/go.sum b/tool/gf/go.sum index 4d7f691bd..737ebaa0a 100644 --- a/tool/gf/go.sum +++ b/tool/gf/go.sum @@ -15,6 +15,10 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/gogf/gf v1.13.8-0.20201010060010-09ce105eeeab/go.mod h1:nGAMjE4ohU2bwj4Gk3h25K6rEkPZMDdvsmyifpFcuMQ= github.com/gogf/gf v1.16.1 h1:J2kcf8ufbuIIGrbeXfMH/CCkH+hUyC9lrQKrLXZrKVg= github.com/gogf/gf v1.16.1/go.mod h1:5eEgE9fWeRQW8dJE3GLpCy0KkNitXh6POesdJiBE/lw= +github.com/gogf/gf v1.16.4 h1:1Y8/P1UMp9BmrtUn0wAg3g6ElRAO0R9QHCdUNt9Z5L4= +github.com/gogf/gf v1.16.4/go.mod h1:EjnxZXddTwfFoLPofDE3NokFWx+immofINtSyFCj280= +github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw= +github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8= github.com/gogf/swagger v1.0.4 h1:MILniFKPh52/26s+z8taSh8thn1tq2RaeWM7rYX1dRw= github.com/gogf/swagger v1.0.4/go.mod h1:4rD12TCoDz60jmgtuFnx7ZBWUM92tXc/qtrIrkBIp5Q= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= From 35a81b868ff719112a0aef77354409ee9132429a Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 30 Jun 2021 20:08:25 +0800 Subject: [PATCH 361/492] gf cli updates --- tool/gf/README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/gf/README.MD b/tool/gf/README.MD index 6d0fb4104..904a00475 100644 --- a/tool/gf/README.MD +++ b/tool/gf/README.MD @@ -12,7 +12,7 @@ Powerful CLI tool for building [GoFrame](https://goframe.org) application with c ``` go install github.com/gogf/gf/tool/gf@latest ``` - > Go version < v1.16 + > Go version <= v1.15 ``` go install github.com/gogf/gf/tool/gf ``` From d5fad88c561276d4b699bc1e63f5f04aa178eb73 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 30 Jun 2021 20:16:49 +0800 Subject: [PATCH 362/492] gf cli updates --- tool/gf/go.mod | 2 +- tool/gf/go.sum | 24 +++--------------------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/tool/gf/go.mod b/tool/gf/go.mod index 426054699..34e47d80b 100644 --- a/tool/gf/go.mod +++ b/tool/gf/go.mod @@ -5,7 +5,7 @@ go 1.11 require ( github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e github.com/gogf/gf v1.16.4 - github.com/gogf/swagger v1.0.4 + github.com/gogf/swagger v1.3.0 github.com/gorilla/websocket v1.4.2 // indirect github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 // indirect github.com/lib/pq v1.2.0 diff --git a/tool/gf/go.sum b/tool/gf/go.sum index 737ebaa0a..281fb6356 100644 --- a/tool/gf/go.sum +++ b/tool/gf/go.sum @@ -2,40 +2,30 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4= github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e h1:LzwWXEScfcTu7vUZNlDDWDARoSGEtvlDKK2BYHowNeE= github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/gogf/gf v1.13.8-0.20201010060010-09ce105eeeab/go.mod h1:nGAMjE4ohU2bwj4Gk3h25K6rEkPZMDdvsmyifpFcuMQ= -github.com/gogf/gf v1.16.1 h1:J2kcf8ufbuIIGrbeXfMH/CCkH+hUyC9lrQKrLXZrKVg= -github.com/gogf/gf v1.16.1/go.mod h1:5eEgE9fWeRQW8dJE3GLpCy0KkNitXh6POesdJiBE/lw= github.com/gogf/gf v1.16.4 h1:1Y8/P1UMp9BmrtUn0wAg3g6ElRAO0R9QHCdUNt9Z5L4= github.com/gogf/gf v1.16.4/go.mod h1:EjnxZXddTwfFoLPofDE3NokFWx+immofINtSyFCj280= github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw= github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8= -github.com/gogf/swagger v1.0.4 h1:MILniFKPh52/26s+z8taSh8thn1tq2RaeWM7rYX1dRw= -github.com/gogf/swagger v1.0.4/go.mod h1:4rD12TCoDz60jmgtuFnx7ZBWUM92tXc/qtrIrkBIp5Q= +github.com/gogf/swagger v1.3.0 h1:McpIEwj2DXLF3/ZNN9h9LKza/fsjtHP54nMTYVSCQ74= +github.com/gogf/swagger v1.3.0/go.mod h1:VDpNntu8+8NTTJ3sLOZNPO1LUbfW/ZA84IFJhhrlpoM= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gqcn/structs v1.1.1/go.mod h1:/aBhTBSsKQ2Ec9pbnYdGphtdWXHFn4KrCL0fXM/Adok= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 h1:h3iGUlU8HyW4baKd6D+h1mwOHnM2kwskSuG6Bv4tSbc= github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -44,11 +34,8 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -56,7 +43,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng= @@ -72,18 +58,14 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From 1e78734f2cfdfe39bc2bfe7a12273ae800cc4466 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 30 Jun 2021 20:43:49 +0800 Subject: [PATCH 363/492] remove gf cli from basic framework --- go.mod | 5 +- go.sum | 36 - tool/gf/README.MD | 70 -- tool/gf/boot/boot.go | 31 - tool/gf/commands/build/build.go | 341 --------- tool/gf/commands/docker/docker.go | 99 --- tool/gf/commands/env/env.go | 44 -- tool/gf/commands/fix/fix.go | 7 - tool/gf/commands/gen/gen.go | 54 -- tool/gf/commands/gen/gen_dao.go | 699 ------------------ tool/gf/commands/gen/gen_dao_template_dao.go | 73 -- .../gf/commands/gen/gen_dao_template_model.go | 18 - tool/gf/commands/gen/gen_pb.go | 77 -- tool/gf/commands/gen/gen_pbentity.go | 447 ----------- tool/gf/commands/gen/gen_pbentity_template.go | 17 - tool/gf/commands/get/get.go | 33 - tool/gf/commands/initialize/initialize.go | 110 --- tool/gf/commands/mod/mod.go | 113 --- tool/gf/commands/pack/pack.go | 80 -- tool/gf/commands/run/run.go | 212 ------ tool/gf/commands/swagger/swagger.go | 154 ---- tool/gf/commands/update/update.go | 25 - tool/gf/config/url.toml | 17 - tool/gf/go.mod | 15 - tool/gf/go.sum | 79 -- tool/gf/library/allyes/allyes.go | 22 - tool/gf/library/mlog/mlog.go | 63 -- tool/gf/library/proxy/proxy.go | 33 - tool/gf/library/utils/utils.go | 18 - tool/gf/main.go | 187 ----- tool/gf/packed/config.go | 9 - 31 files changed, 4 insertions(+), 3184 deletions(-) delete mode 100644 tool/gf/README.MD delete mode 100644 tool/gf/boot/boot.go delete mode 100644 tool/gf/commands/build/build.go delete mode 100644 tool/gf/commands/docker/docker.go delete mode 100644 tool/gf/commands/env/env.go delete mode 100644 tool/gf/commands/fix/fix.go delete mode 100644 tool/gf/commands/gen/gen.go delete mode 100644 tool/gf/commands/gen/gen_dao.go delete mode 100644 tool/gf/commands/gen/gen_dao_template_dao.go delete mode 100644 tool/gf/commands/gen/gen_dao_template_model.go delete mode 100644 tool/gf/commands/gen/gen_pb.go delete mode 100644 tool/gf/commands/gen/gen_pbentity.go delete mode 100644 tool/gf/commands/gen/gen_pbentity_template.go delete mode 100644 tool/gf/commands/get/get.go delete mode 100644 tool/gf/commands/initialize/initialize.go delete mode 100644 tool/gf/commands/mod/mod.go delete mode 100644 tool/gf/commands/pack/pack.go delete mode 100644 tool/gf/commands/run/run.go delete mode 100644 tool/gf/commands/swagger/swagger.go delete mode 100644 tool/gf/commands/update/update.go delete mode 100644 tool/gf/config/url.toml delete mode 100644 tool/gf/go.mod delete mode 100644 tool/gf/go.sum delete mode 100644 tool/gf/library/allyes/allyes.go delete mode 100644 tool/gf/library/mlog/mlog.go delete mode 100644 tool/gf/library/proxy/proxy.go delete mode 100644 tool/gf/library/utils/utils.go delete mode 100644 tool/gf/main.go delete mode 100644 tool/gf/packed/config.go diff --git a/go.mod b/go.mod index edda9e83b..25bb74983 100644 --- a/go.mod +++ b/go.mod @@ -5,17 +5,20 @@ go 1.14 require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.4.9 - github.com/gogf/gf/tool/gf v0.0.0-20210629161552-1e628b9edb8e // indirect github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/websocket v1.4.2 github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 + github.com/mattn/go-runewidth v0.0.10 // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/olekukonko/tablewriter v0.0.5 go.opentelemetry.io/otel v1.0.0-RC1 go.opentelemetry.io/otel/oteltest v1.0.0-RC1 go.opentelemetry.io/otel/trace v1.0.0-RC1 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 golang.org/x/text v0.3.4 + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c ) diff --git a/go.sum b/go.sum index 8cf065292..19076b3bf 100644 --- a/go.sum +++ b/go.sum @@ -2,53 +2,29 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4= github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e h1:LzwWXEScfcTu7vUZNlDDWDARoSGEtvlDKK2BYHowNeE= -github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/gogf/gf v1.13.8-0.20201010060010-09ce105eeeab/go.mod h1:nGAMjE4ohU2bwj4Gk3h25K6rEkPZMDdvsmyifpFcuMQ= -github.com/gogf/gf v1.16.1/go.mod h1:5eEgE9fWeRQW8dJE3GLpCy0KkNitXh6POesdJiBE/lw= -github.com/gogf/gf/tool/gf v0.0.0-20210629161552-1e628b9edb8e h1:0yamWn3TAI/nFVDI9pwofWDekdMt4qQLwQdMmE59TNA= -github.com/gogf/gf/tool/gf v0.0.0-20210629161552-1e628b9edb8e/go.mod h1:bCukUi4KQbIKZ4W5xLPKHe3eYparuvqfqBn8ZiYeCnY= github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw= github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8= -github.com/gogf/swagger v1.0.4 h1:MILniFKPh52/26s+z8taSh8thn1tq2RaeWM7rYX1dRw= -github.com/gogf/swagger v1.0.4/go.mod h1:4rD12TCoDz60jmgtuFnx7ZBWUM92tXc/qtrIrkBIp5Q= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gqcn/structs v1.1.1/go.mod h1:/aBhTBSsKQ2Ec9pbnYdGphtdWXHFn4KrCL0fXM/Adok= -github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 h1:h3iGUlU8HyW4baKd6D+h1mwOHnM2kwskSuG6Bv4tSbc= github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -56,43 +32,31 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc= go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I= -go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= -go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0cljhGk9sB0Yk= go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4= -go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58= go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/tool/gf/README.MD b/tool/gf/README.MD deleted file mode 100644 index 904a00475..000000000 --- a/tool/gf/README.MD +++ /dev/null @@ -1,70 +0,0 @@ -# GoFrame CLI Tool - -Powerful CLI tool for building [GoFrame](https://goframe.org) application with convenience. - -## 1. Install - -> You might need setting the goproxy to make through building. - -1. Latest version - - > Go version >= v1.16 - ``` - go install github.com/gogf/gf/tool/gf@latest - ``` - > Go version <= v1.15 - ``` - go install github.com/gogf/gf/tool/gf - ``` - -1. Specified version - - > Go version >= v1.16 - ``` - go install github.com/gogf/gf/tool/gf@v1.16.0 - ``` -1. Database `sqlite` and `oracle` are not support in `gf gen` command in default as it needs `cgo` and `gcc`, you can manually make some changes to the source codes and do the building. - -## 2. Commands -```html -$ gf -USAGE - gf COMMAND [ARGUMENT] [OPTION] - -COMMAND - env show current Golang environment variables - get install or update GF to system in default... - gen automatically generate go files for ORM models... - mod extra features for go modules... - run running go codes with hot-compiled-like feature... - init initialize an empty GF project at current working directory... - help show more information about a specified command - pack packing any file/directory to a resource file, or a go file... - build cross-building go project for lots of platforms... - docker create a docker image for current GF project... - swagger swagger feature for current project... - update update current gf binary to latest one - version show current binary version info - -OPTION - -y all yes for all command without prompt ask - -?,-h show this help or detail for specified command - -v,-i show version information - -ADDITIONAL - Use 'gf help COMMAND' or 'gf COMMAND -h' for detail about a command, which has '...' - in the tail of their comments. -``` - -## 3. FAQ - -### 1). Command `gf run` returns `pipe: too many open files` - -Please use `ulimit -n 65535` to enlarge your system configuration for max open files for current terminal shell session, and then `gf run`. - - - - - - - diff --git a/tool/gf/boot/boot.go b/tool/gf/boot/boot.go deleted file mode 100644 index ef11ced62..000000000 --- a/tool/gf/boot/boot.go +++ /dev/null @@ -1,31 +0,0 @@ -package boot - -import ( - "github.com/gogf/gf/os/genv" - _ "github.com/gogf/gf/tool/gf/packed" - - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/text/gstr" -) - -func init() { - // Force using configuration file in current working directory. - // In case of source environment. - genv.Set("GF_GCFG_PATH", gfile.Pwd()) - handleZshAlias() -} - -// zsh alias "git fetch" conflicts checks. -func handleZshAlias() { - home, err := gfile.Home() - if err == nil { - zshPath := gfile.Join(home, ".zshrc") - if gfile.Exists(zshPath) { - aliasCommand := `alias gf=gf` - content := gfile.GetContents(zshPath) - if !gstr.Contains(content, aliasCommand) { - _ = gfile.PutContentsAppend(zshPath, "\n"+aliasCommand+"\n") - } - } - } -} diff --git a/tool/gf/commands/build/build.go b/tool/gf/commands/build/build.go deleted file mode 100644 index f8b157a60..000000000 --- a/tool/gf/commands/build/build.go +++ /dev/null @@ -1,341 +0,0 @@ -package build - -import ( - "encoding/json" - "fmt" - "github.com/gogf/gf/encoding/gbase64" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/genv" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/gproc" - "github.com/gogf/gf/os/gtime" - "github.com/gogf/gf/text/gregex" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/mlog" - "github.com/gogf/gf/util/gconv" - "github.com/gogf/gf/util/gutil" - "regexp" - "runtime" - "strings" -) - -// https://golang.google.cn/doc/install/source -const platforms = ` - darwin amd64 - darwin arm64 - ios amd64 - ios arm64 - freebsd 386 - freebsd amd64 - freebsd arm - linux 386 - linux amd64 - linux arm - linux arm64 - linux ppc64 - linux ppc64le - linux mips - linux mipsle - linux mips64 - linux mips64le - netbsd 386 - netbsd amd64 - netbsd arm - openbsd 386 - openbsd amd64 - openbsd arm - windows 386 - windows amd64 - android arm - dragonfly amd64 - plan9 386 - plan9 amd64 - solaris amd64 -` - -const ( - nodeNameInConfigFile = "gfcli.build" // nodeNameInConfigFile is the node name for compiler configurations in configuration file. - packedGoFileName = "build_pack_data.go" // packedGoFileName specifies the file name for packing common folders into one single go file. -) - -func Help() { - mlog.Print(gstr.TrimLeft(` -USAGE - gf build FILE [OPTION] - -ARGUMENT - FILE building file path. - -OPTION - -n, --name output binary name - -v, --version output binary version - -a, --arch output binary architecture, multiple arch separated with ',' - -s, --system output binary system, multiple os separated with ',' - -o, --output output binary path, used when building single binary file - -p, --path output binary directory path, default is './bin' - -e, --extra extra custom "go build" options - -m, --mod like "-mod" option of "go build", use "-m none" to disable go module - -c, --cgo enable or disable cgo feature, it's disabled in default - --pack pack specified folder into packed/data.go before building. - --swagger auto parse and pack swagger into packed/swagger.go before building. - -EXAMPLES - gf build main.go - gf build main.go --swagger - gf build main.go --pack public,template - gf build main.go --cgo - gf build main.go -m none - gf build main.go -n my-app -a all -s all - gf build main.go -n my-app -a amd64,386 -s linux -p . - gf build main.go -n my-app -v 1.0 -a amd64,386 -s linux,windows,darwin -p ./docker/bin - -DESCRIPTION - The "build" command is most commonly used command, which is designed as a powerful wrapper for - "go build" command for convenience cross-compiling usage. - It provides much more features for building binary: - 1. Cross-Compiling for many platforms and architectures. - 2. Configuration file support for compiling. - 3. Build-In Variables. - -PLATFORMS - darwin amd64,arm64 - freebsd 386,amd64,arm - linux 386,amd64,arm,arm64,ppc64,ppc64le,mips,mipsle,mips64,mips64le - netbsd 386,amd64,arm - openbsd 386,amd64,arm - windows 386,amd64 -`)) -} - -func Run() { - mlog.SetHeaderPrint(true) - parser, err := gcmd.Parse(g.MapStrBool{ - "n,name": true, - "v,version": true, - "a,arch": true, - "s,system": true, - "o,output": true, - "p,path": true, - "e,extra": true, - "m,mod": true, - "pack": true, - "c,cgo": false, - "swagger": false, - }) - if err != nil { - mlog.Fatal(err) - } - file := parser.GetArg(2) - if len(file) < 1 { - // Check and use the main.go file. - if gfile.Exists("main.go") { - file = "main.go" - } else { - mlog.Fatal("build file path cannot be empty") - } - } - path := getOption(parser, "path", "./bin") - name := getOption(parser, "name", gfile.Name(file)) - if len(name) < 1 || name == "*" { - mlog.Fatal("name cannot be empty") - } - var ( - mod = getOption(parser, "mod") - extra = getOption(parser, "extra") - ) - if mod != "" && mod != "none" { - mlog.Debugf(`mod is %s`, mod) - if extra == "" { - extra = fmt.Sprintf(`-mod=%s`, mod) - } else { - extra = fmt.Sprintf(`-mod=%s %s`, mod, extra) - } - } - if extra != "" { - extra += " " - } - var ( - cgoEnabled = gconv.Bool(getOption(parser, "cgo")) - version = getOption(parser, "version") - outputPath = getOption(parser, "output") - archOption = getOption(parser, "arch") - systemOption = getOption(parser, "system") - packStr = getOption(parser, "pack") - customSystems = gstr.SplitAndTrim(systemOption, ",") - customArches = gstr.SplitAndTrim(archOption, ",") - ) - if !cgoEnabled { - cgoEnabled = parser.ContainsOpt("cgo") - } - if len(version) > 0 { - path += "/" + version - } - // System and arch checks. - var ( - spaceRegex = regexp.MustCompile(`\s+`) - platformMap = make(map[string]map[string]bool) - ) - for _, line := range strings.Split(strings.TrimSpace(platforms), "\n") { - line = gstr.Trim(line) - line = spaceRegex.ReplaceAllString(line, " ") - var ( - array = strings.Split(line, " ") - system = strings.TrimSpace(array[0]) - arch = strings.TrimSpace(array[1]) - ) - if platformMap[system] == nil { - platformMap[system] = make(map[string]bool) - } - platformMap[system][arch] = true - } - // Auto swagger. - if containsOption(parser, "swagger") { - if err := gproc.ShellRun(`gf swagger`); err != nil { - return - } - if gfile.Exists("swagger") { - packCmd := fmt.Sprintf(`gf pack %s packed/%s`, "swagger", packedGoFileName) - mlog.Print(packCmd) - if err := gproc.ShellRun(packCmd); err != nil { - return - } - } - } - - // Auto packing. - if len(packStr) > 0 { - dataFilePath := fmt.Sprintf(`packed/%s`, packedGoFileName) - if !gfile.Exists(dataFilePath) { - // Remove the go file that is automatically packed resource. - defer func() { - gfile.Remove(dataFilePath) - mlog.Printf(`remove the automatically generated resource go file: %s`, dataFilePath) - }() - } - packCmd := fmt.Sprintf(`gf pack %s %s`, packStr, dataFilePath) - mlog.Print(packCmd) - gproc.ShellRun(packCmd) - } - - // Injected information by building flags. - ldFlags := fmt.Sprintf(`-X 'github.com/gogf/gf/os/gbuild.builtInVarStr=%v'`, getBuildInVarStr()) - - // start building - mlog.Print("start building...") - if cgoEnabled { - genv.Set("CGO_ENABLED", "1") - } else { - genv.Set("CGO_ENABLED", "0") - } - var ( - cmd = "" - ext = "" - ) - for system, item := range platformMap { - cmd = "" - ext = "" - if len(customSystems) > 0 && customSystems[0] != "all" && !gstr.InArray(customSystems, system) { - continue - } - for arch, _ := range item { - if len(customArches) > 0 && customArches[0] != "all" && !gstr.InArray(customArches, arch) { - continue - } - if len(customSystems) == 0 && len(customArches) == 0 { - if runtime.GOOS == "windows" { - ext = ".exe" - } - // Single binary building, output the binary to current working folder. - output := "" - if len(outputPath) > 0 { - output = "-o " + outputPath + ext - } else { - output = "-o " + name + ext - } - cmd = fmt.Sprintf(`go build %s -ldflags "%s" %s %s`, output, ldFlags, extra, file) - } else { - // Cross-building, output the compiled binary to specified path. - if system == "windows" { - ext = ".exe" - } - genv.Set("GOOS", system) - genv.Set("GOARCH", arch) - cmd = fmt.Sprintf( - `go build -o %s/%s/%s%s -ldflags "%s" %s%s`, - path, system+"_"+arch, name, ext, ldFlags, extra, file, - ) - } - // It's not necessary printing the complete command string. - cmdShow, _ := gregex.ReplaceString(`\s+(-ldflags ".+?")\s+`, " ", cmd) - mlog.Print(cmdShow) - if _, err := gproc.ShellExec(cmd); err != nil { - mlog.Printf("failed to build, os:%s, arch:%s", system, arch) - } - // single binary building. - if len(customSystems) == 0 && len(customArches) == 0 { - goto buildDone - } - } - } -buildDone: - mlog.Print("done!") -} - -// getOption retrieves option value from parser and configuration file. -// It returns the default value specified by parameter <value> is no value found. -func getOption(parser *gcmd.Parser, name string, value ...string) (result string) { - result = parser.GetOpt(name) - if result == "" && g.Config().Available() { - result = g.Config().GetString(nodeNameInConfigFile + "." + name) - } - if result == "" && len(value) > 0 { - result = value[0] - } - return -} - -// containsOption checks whether the command option or the configuration file containing -// given option name. -func containsOption(parser *gcmd.Parser, name string) bool { - result := parser.ContainsOpt(name) - if !result && g.Config().Available() { - result = g.Config().Contains(nodeNameInConfigFile + "." + name) - } - return result -} - -// getBuildInVarMapJson retrieves and returns the custom build-in variables in configuration -// file as json. -func getBuildInVarStr() string { - buildInVarMap := g.Map{} - if g.Config().Available() { - configMap := g.Config().GetMap(nodeNameInConfigFile) - if len(configMap) > 0 { - _, v := gutil.MapPossibleItemByKey(configMap, "VarMap") - if v != nil { - buildInVarMap = gconv.Map(v) - } - } - } - buildInVarMap["builtGit"] = getGitCommit() - buildInVarMap["builtTime"] = gtime.Now().String() - b, err := json.Marshal(buildInVarMap) - if err != nil { - mlog.Fatal(err) - } - return gbase64.EncodeToString(b) -} - -// getGitCommit retrieves and returns the latest git commit hash string if present. -func getGitCommit() string { - if gproc.SearchBinary("git") == "" { - return "" - } - if s, _ := gproc.ShellExec("git rev-list -1 HEAD"); s != "" { - if !gstr.Contains(s, " ") && !gstr.Contains(s, "fatal") { - return gstr.Trim(s) - } - } - return "" -} diff --git a/tool/gf/commands/docker/docker.go b/tool/gf/commands/docker/docker.go deleted file mode 100644 index 69e0c9d95..000000000 --- a/tool/gf/commands/docker/docker.go +++ /dev/null @@ -1,99 +0,0 @@ -package docker - -import ( - "fmt" - "github.com/gogf/gf/container/garray" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/gproc" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/mlog" - "os" - "strings" -) - -func Help() { - mlog.Print(gstr.TrimLeft(` -USAGE - gf docker [FILE] [OPTION] - -ARGUMENT - FILE file path for "gf build", it's "main.go" in default. - OPTION the same options as "docker build" except some options as follows defined - -OPTION - -p, --push auto push the docker image to docker registry if "-t" option passed - -EXAMPLES - gf docker - gf docker -t hub.docker.com/john/image:tag - gf docker -p -t hub.docker.com/john/image:tag - gf docker main.go - gf docker main.go -t hub.docker.com/john/image:tag - gf docker main.go -t hub.docker.com/john/image:tag - gf docker main.go -p -t hub.docker.com/john/image:tag - -DESCRIPTION - The "docker" command builds the GF project to a docker images. - It runs "gf build" firstly to compile the project to binary file. - It then runs "docker build" command automatically to generate the docker image. - You should have docker installed, and there must be a Dockerfile in the root of the project. - -`)) -} - -func Run() { - var err error - autoPush := false - array := garray.NewStrArrayFromCopy(os.Args) - index := array.Search("--push") - if index < 0 { - index = array.Search("-p") - } - if index != -1 { - array.Remove(index) - autoPush = true - } - file := "main.go" - extraOptions := "" - if array.Len() > 2 { - v, _ := array.Get(2) - if gfile.ExtName(v) == "go" { - file, _ = array.Get(2) - if array.Len() > 3 { - extraOptions = strings.Join(array.SubSlice(3), " ") - } - } else { - extraOptions = strings.Join(array.SubSlice(2), " ") - } - } - // Binary build. - err = gproc.ShellRun(fmt.Sprintf(`gf build %s -a amd64 -s linux`, file)) - if err != nil { - return - } - // Docker build. - err = gproc.ShellRun(fmt.Sprintf(`docker build . %s`, extraOptions)) - if err != nil { - return - } - // Docker push. - if !autoPush { - return - } - parser, err := gcmd.Parse(g.MapStrBool{ - "t,tag": true, - }) - if err != nil { - mlog.Fatal(err) - } - tag := parser.GetOpt("t") - if tag == "" { - return - } - err = gproc.ShellRun(fmt.Sprintf(`docker push %s`, tag)) - if err != nil { - return - } -} diff --git a/tool/gf/commands/env/env.go b/tool/gf/commands/env/env.go deleted file mode 100644 index 06a163b92..000000000 --- a/tool/gf/commands/env/env.go +++ /dev/null @@ -1,44 +0,0 @@ -package env - -import ( - "bytes" - "github.com/gogf/gf/os/gproc" - "github.com/gogf/gf/text/gregex" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/mlog" - "github.com/olekukonko/tablewriter" -) - -func Run() { - result, err := gproc.ShellExec("go env") - if err != nil { - mlog.Fatal(err) - } - if result == "" { - mlog.Fatal(`retrieving Golang environment variables failed, did you install Golang?`) - } - var ( - lines = gstr.Split(result, "\n") - buffer = bytes.NewBuffer(nil) - ) - array := make([][]string, 0) - for _, line := range lines { - line = gstr.Trim(line) - if line == "" { - continue - } - if gstr.Pos(line, "set ") == 0 { - line = line[4:] - } - match, _ := gregex.MatchString(`(.+?)=(.*)`, line) - if len(match) < 3 { - mlog.Fatalf(`invalid Golang environment variable: "%s"`, line) - } - array = append(array, []string{gstr.Trim(match[1]), gstr.Trim(match[2])}) - } - tw := tablewriter.NewWriter(buffer) - tw.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT}) - tw.AppendBulk(array) - tw.Render() - mlog.Print(buffer.String()) -} diff --git a/tool/gf/commands/fix/fix.go b/tool/gf/commands/fix/fix.go deleted file mode 100644 index 1bcfa7463..000000000 --- a/tool/gf/commands/fix/fix.go +++ /dev/null @@ -1,7 +0,0 @@ -package fix - -import "github.com/gogf/gf/tool/gf/library/mlog" - -func Run() { - mlog.Print("this feature is not completed yet") -} diff --git a/tool/gf/commands/gen/gen.go b/tool/gf/commands/gen/gen.go deleted file mode 100644 index 1a5a03d27..000000000 --- a/tool/gf/commands/gen/gen.go +++ /dev/null @@ -1,54 +0,0 @@ -package gen - -import ( - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/mlog" -) - -func Help() { - switch gcmd.GetArg(2) { - case "dao": - HelpDao() - - case "pb": - HelpPb() - - case "pbentity": - HelpPbEntity() - - default: - mlog.Print(gstr.TrimLeft(` -USAGE - gf gen TYPE [OPTION] - -TYPE - dao generate dao and model files. - pb parse proto files and generate protobuf go files. - pbentity generate entity message files in protobuf3 format. - -DESCRIPTION - The "gen" command is designed for multiple generating purposes. - It's currently supporting generating go files for ORM models, protobuf and protobuf entity files. - Please use "gf gen dao -h" or "gf gen model -h" for specified type help. -`)) - } -} - -func Run() { - genType := gcmd.GetArg(2) - if genType == "" { - mlog.Print("generating type cannot be empty") - return - } - switch genType { - case "dao": - doGenDao() - - case "pb": - doGenPb() - - case "pbentity": - doGenPbEntity() - } -} diff --git a/tool/gf/commands/gen/gen_dao.go b/tool/gf/commands/gen/gen_dao.go deleted file mode 100644 index 1832b17b5..000000000 --- a/tool/gf/commands/gen/gen_dao.go +++ /dev/null @@ -1,699 +0,0 @@ -package gen - -import ( - "bytes" - "context" - "fmt" - "github.com/gogf/gf/container/garray" - "github.com/gogf/gf/database/gdb" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/gtime" - "github.com/gogf/gf/text/gregex" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/mlog" - "github.com/gogf/gf/tool/gf/library/utils" - "github.com/gogf/gf/util/gconv" - "github.com/olekukonko/tablewriter" - "strings" - - _ "github.com/denisenkom/go-mssqldb" - _ "github.com/lib/pq" - //_ "github.com/mattn/go-oci8" - //_ "github.com/mattn/go-sqlite3" -) - -// generateDaoReq is the input parameter for generating dao. -type generateDaoReq struct { - TableName string // TableName specifies the table name of the table. - NewTableName string // NewTableName specifies the prefix-stripped name of the table. - PrefixName string // PrefixName specifies the custom prefix name for generated dao and model struct. - GroupName string // GroupName specifies the group name of database configuration node for generated DAO. - ModName string // ModName specifies the module name of current golang project, which is used for import purpose. - JsonCase string // JsonCase specifies the case of generated 'json' tag for model struct, value from gstr.Case* function names. - DirPath string // DirPath specifies the directory path for generated files. - StdTime bool // StdTime defines using time.Time from stdlib instead of gtime.Time for generated time/date fields of tables. - ModelIndexFileName string // Custom name for storing generated model content. - TplDaoIndexPath string // TplDaoIndexPath specifies the file path for generating dao index files. - TplDaoInternalPath string // TplDaoInternalPath specifies the file path for generating dao internal files. - TplModelIndexPath string // TplModelIndexPath specifies the file path for generating model index content. - TplModelStructPath string // TplModelStructPath specifies the file path for generating model struct content. -} - -const ( - genDaoDefaultPath = "./app" - nodeNameGenDaoInConfigFile = "gfcli.gen.dao" - defaultModelIndexFileName = "model.go" -) - -func HelpDao() { - mlog.Print(gstr.TrimLeft(` -USAGE - gf gen dao [OPTION] - -OPTION - -/--path directory path for generated files. - -l, --link database configuration, the same as the ORM configuration of GoFrame. - -t, --tables generate models only for given tables, multiple table names separated with ',' - -e, --tablesEx generate models excluding given tables, multiple table names separated with ',' - -g, --group specifying the configuration group name of database for generated ORM instance, - it's not necessary and the default value is "default" - -p, --prefix add prefix for all table of specified link/database tables. - -r, --removePrefix remove specified prefix of the table, multiple prefix separated with ',' - -m, --mod module name for generated golang file imports. - -j, --jsonCase generated json tag case for model struct, cases are as follows: - | Case | Example | - |---------------- |--------------------| - | Camel | AnyKindOfString | - | CamelLower | anyKindOfString | default - | Snake | any_kind_of_string | - | SnakeScreaming | ANY_KIND_OF_STRING | - | SnakeFirstUpper | rgb_code_md5 | - | Kebab | any-kind-of-string | - | KebabScreaming | ANY-KIND-OF-STRING | - -/--stdTime use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables. - -/--modelFile custom file name for storing generated model content. - -/--tplDaoIndex template content for Dao index files generating. - -/--tplDaoInternal template content for Dao internal files generating. - -/--tplModelIndex template content for Model index files generating. - -/--tplModelStruct template content for Model internal files generating. - -CONFIGURATION SUPPORT - Options are also supported by configuration file. - It's suggested using configuration file instead of command line arguments making producing. - The configuration node name is "gf.gen.dao", which also supports multiple databases, for example: - [gfcli] - [[gfcli.gen.dao]] - link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" - tables = "order,products" - jsonCase = "CamelLower" - [[gfcli.gen.dao]] - link = "mysql:root:12345678@tcp(127.0.0.1:3306)/primary" - path = "./my-app" - prefix = "primary_" - tables = "user, userDetail" - -EXAMPLES - gf gen dao - gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test" - gf gen dao -path ./model -c config.yaml -g user-center -t user,user_detail,user_login - gf gen dao -r user_ -`)) -} - -// doGenDao implements the "gen dao" command. -func doGenDao() { - parser, err := gcmd.Parse(g.MapStrBool{ - "path": true, - "m,mod": true, - "l,link": true, - "t,tables": true, - "e,tablesEx": true, - "g,group": true, - "c,config": true, - "p,prefix": true, - "r,removePrefix": true, - "j,jsonCase": true, - "stdTime": false, - "modelFile": true, - "tplDaoIndex": true, - "tplDaoInternal": true, - "tplModelIndex": true, - "tplModelStruct": true, - }) - if err != nil { - mlog.Fatal(err) - } - config := g.Cfg() - if config.Available() { - v := config.GetVar(nodeNameGenDaoInConfigFile) - if v.IsEmpty() && g.IsEmpty(parser.GetOptAll()) { - mlog.Fatal(`command arguments and configurations not found for generating dao files`) - } - if v.IsSlice() { - for i := 0; i < len(v.Interfaces()); i++ { - doGenDaoForArray(i, parser) - } - } else { - doGenDaoForArray(-1, parser) - } - } else { - doGenDaoForArray(-1, parser) - } - mlog.Print("done!") -} - -// doGenDaoForArray implements the "gen dao" command for configuration array. -func doGenDaoForArray(index int, parser *gcmd.Parser) { - var ( - err error - db gdb.DB - modName = getOptionOrConfigForDao(index, parser, "mod") // Go module name, eg: github.com/gogf/gf. - dirPath = getOptionOrConfigForDao(index, parser, "path", genDaoDefaultPath) // Generated directory path. - tablesStr = getOptionOrConfigForDao(index, parser, "tables") // Tables that will be generated. - tablesEx = getOptionOrConfigForDao(index, parser, "tablesEx") // Tables that will be excluded for generating. - prefixName = getOptionOrConfigForDao(index, parser, "prefix") // Add prefix to DAO and Model struct name. - linkInfo = getOptionOrConfigForDao(index, parser, "link") // Custom database link. - configPath = getOptionOrConfigForDao(index, parser, "config") // Config file path, eg: ./config/db.toml. - configGroup = getOptionOrConfigForDao(index, parser, "group", "default") // Group name of database configuration node for generated DAO. - removePrefix = getOptionOrConfigForDao(index, parser, "removePrefix") // Remove prefix from table name. - jsonCase = getOptionOrConfigForDao(index, parser, "jsonCase", "CamelLower") // Case configuration for 'json' tag. - stdTime = getOptionOrConfigForDao(index, parser, "stdTime", "false") // Use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables. - modelFileName = getOptionOrConfigForDao(index, parser, "modelFile", defaultModelIndexFileName) // Custom file name for storing generated model content. - tplDaoIndexPath = getOptionOrConfigForDao(index, parser, "tplDaoIndex") // Template file path for generating dao index files. - tplDaoInternalPath = getOptionOrConfigForDao(index, parser, "tplDaoInternal") // Template file path for generating dao internal files. - tplModelIndexPath = getOptionOrConfigForDao(index, parser, "tplModelIndex") // Template file path for generating model index files. - tplModelStructPath = getOptionOrConfigForDao(index, parser, "tplModelStruct") // Template file path for generating model internal files. - ) - if tplDaoIndexPath != "" && (!gfile.Exists(tplDaoIndexPath) || !gfile.IsReadable(tplDaoIndexPath)) { - mlog.Fatalf("template file for dao index files generating does not exist or is not readable: %s", tplDaoIndexPath) - } - if tplDaoInternalPath != "" && (!gfile.Exists(tplDaoInternalPath) || !gfile.IsReadable(tplDaoInternalPath)) { - mlog.Fatalf("template internal for dao internal files generating does not exist or is not readable: %s: %s", tplDaoInternalPath) - } - if tplModelIndexPath != "" && (!gfile.Exists(tplModelIndexPath) || !gfile.IsReadable(tplModelIndexPath)) { - mlog.Fatalf("template file for model index files generating does not exist or is not readable: %s: %s", tplModelIndexPath) - } - if tplModelStructPath != "" && (!gfile.Exists(tplModelStructPath) || !gfile.IsReadable(tplModelStructPath)) { - mlog.Fatalf("template file for model internal files generating does not exist or is not readable: %s: %s", tplModelStructPath) - } - // Make it compatible with old CLI version for option name: remove-prefix - if removePrefix == "" { - removePrefix = getOptionOrConfigForDao(index, parser, "remove-prefix") - } - removePrefixArray := gstr.SplitAndTrim(removePrefix, ",") - if modName == "" { - if !gfile.Exists("go.mod") { - mlog.Fatal("go.mod does not exist in current working directory") - } - var ( - goModContent = gfile.GetContents("go.mod") - match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent) - ) - if len(match) > 1 { - modName = gstr.Trim(match[1]) - } else { - mlog.Fatal("module name does not found in go.mod") - } - } - // It reads database configuration from project configuration file. - if configPath != "" { - path, err := gfile.Search(configPath) - if err != nil { - mlog.Fatalf("search configuration file '%s' failed: %v", configPath, err) - } - if err := g.Cfg().SetPath(gfile.Dir(path)); err != nil { - mlog.Fatalf("set configuration path '%s' failed: %v", path, err) - } - g.Cfg().SetFileName(gfile.Basename(path)) - } - // It uses user passed database configuration. - if linkInfo != "" { - tempGroup := gtime.TimestampNanoStr() - match, _ := gregex.MatchString(`([a-z]+):(.+)`, linkInfo) - if len(match) == 3 { - gdb.AddConfigNode(tempGroup, gdb.ConfigNode{ - Type: gstr.Trim(match[1]), - Link: gstr.Trim(match[2]), - }) - db, _ = gdb.Instance(tempGroup) - } - } else { - db = g.DB(configGroup) - } - if db == nil { - mlog.Fatal("database initialization failed") - } - - var tableNames []string - if tablesStr != "" { - tableNames = gstr.SplitAndTrim(tablesStr, ",") - } else { - tableNames, err = db.Tables(context.TODO()) - if err != nil { - mlog.Fatalf("fetching tables failed: \n %v", err) - } - } - // Table excluding. - if tablesEx != "" { - array := garray.NewStrArrayFrom(tableNames) - for _, v := range gstr.SplitAndTrim(tablesEx, ",") { - array.RemoveValue(v) - } - tableNames = array.Slice() - } - - // Generating dao & model go files one by one according to given table name. - newTableNames := make([]string, len(tableNames)) - for i, tableName := range tableNames { - newTableName := tableName - for _, v := range removePrefixArray { - newTableName = gstr.TrimLeftStr(newTableName, v, 1) - } - newTableNames[i] = newTableName - generateDaoContentFile(db, generateDaoReq{ - TableName: tableName, - NewTableName: newTableName, - PrefixName: prefixName, - GroupName: configGroup, - ModName: modName, - JsonCase: jsonCase, - DirPath: dirPath, - StdTime: gconv.Bool(stdTime), - TplDaoIndexPath: tplDaoIndexPath, - TplDaoInternalPath: tplDaoInternalPath, - TplModelIndexPath: tplModelIndexPath, - TplModelStructPath: tplModelStructPath, - }) - } - generateDaoModelContentFile(db, tableNames, newTableNames, generateDaoReq{ - JsonCase: jsonCase, - DirPath: dirPath, - StdTime: gconv.Bool(stdTime), - ModelIndexFileName: modelFileName, - TplModelIndexPath: tplModelIndexPath, - TplModelStructPath: tplModelStructPath, - }) -} - -// generateDaoContentFile generates the dao and model content of given table. -func generateDaoContentFile(db gdb.DB, req generateDaoReq) { - // Generating table data preparing. - fieldMap, err := db.TableFields(context.TODO(), req.TableName) - if err != nil { - mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", req.TableName, err) - } - // Change the `newTableName` if `prefixName` is given. - newTableName := req.PrefixName + req.NewTableName - var ( - dirPathDao = gstr.Trim(gfile.Join(req.DirPath, "dao"), "./") - tableNameCamelCase = gstr.CaseCamel(newTableName) - tableNameCamelLowerCase = gstr.CaseCamelLower(newTableName) - tableNameSnakeCase = gstr.CaseSnake(newTableName) - importPrefix = "" - dirRealPath = gfile.RealPath(req.DirPath) - ) - if dirRealPath == "" { - dirRealPath = req.DirPath - importPrefix = dirRealPath - importPrefix = gstr.Trim(dirRealPath, "./") - } else { - importPrefix = gstr.Replace(dirRealPath, gfile.Pwd(), "") - } - importPrefix = gstr.Replace(importPrefix, gfile.Separator, "/") - importPrefix = gstr.Join(g.SliceStr{req.ModName, importPrefix}, "/") - importPrefix, _ = gregex.ReplaceString(`\/{2,}`, `/`, gstr.Trim(importPrefix, "/")) - - fileName := gstr.Trim(tableNameSnakeCase, "-_.") - if len(fileName) > 5 && fileName[len(fileName)-5:] == "_test" { - // Add suffix to avoid the table name which contains "_test", - // which would make the go file a testing file. - fileName += "_table" - } - - // dao - index - generateDaoIndex(tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName, req) - - // dao - internal - generateDaoInternal(tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName, fieldMap, req) -} - -func generateDaoModelContentFile(db gdb.DB, tableNames, newTableNames []string, req generateDaoReq) { - var ( - modelContent string - packageImports string - dirPathModel = gstr.Trim(gfile.Join(req.DirPath, "model"), "./") - ) - - // Model content. - for i, tableName := range tableNames { - fieldMap, err := db.TableFields(context.TODO(), tableName) - if err != nil { - mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", req.TableName, err) - } - modelContent += generateDaoModelStructContent( - tableName, - gstr.CaseCamel(newTableNames[i]), - req.TplModelStructPath, - generateStructDefinitionForModel(gstr.CaseCamel(newTableNames[i]), fieldMap, req), - ) - modelContent += "\n" - } - - // Time package recognition. - if strings.Contains(modelContent, "gtime.Time") { - packageImports = gstr.Trim(` -import ( - "github.com/gogf/gf/os/gtime" -)`) - } else if strings.Contains(modelContent, "time.Time") { - packageImports = gstr.Trim(` -import ( - "time" -)`) - } else { - packageImports = "" - } - - // Generate and write content to golang file. - modelContent = gstr.ReplaceByMap(getTplModelIndexContent(req.TplModelIndexPath), g.MapStrStr{ - "{TplPackageImports}": packageImports, - "{TplModelStructs}": modelContent, - }) - path := gfile.Join(dirPathModel, req.ModelIndexFileName) - if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil { - mlog.Fatalf("writing content to '%s' failed: %v", path, err) - } else { - utils.GoFmt(path) - mlog.Print("generated:", path) - } -} - -func generateDaoModelStructContent(tableName, tableNameCamelCase, tplModelStructPath, structDefine string) string { - return gstr.ReplaceByMap(getTplModelStructContent(tplModelStructPath), g.MapStrStr{ - "{TplTableName}": tableName, - "{TplTableNameCamelCase}": tableNameCamelCase, - "{TplStructDefine}": structDefine, - }) -} - -func generateDaoIndex(tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName string, req generateDaoReq) { - path := gfile.Join(dirPathDao, fileName+".go") - if !gfile.Exists(path) { - indexContent := gstr.ReplaceByMap(getTplDaoIndexContent(req.TplDaoIndexPath), g.MapStrStr{ - "{TplImportPrefix}": importPrefix, - "{TplTableName}": req.TableName, - "{TplTableNameCamelCase}": tableNameCamelCase, - "{TplTableNameCamelLowerCase}": tableNameCamelLowerCase, - }) - if err := gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil { - mlog.Fatalf("writing content to '%s' failed: %v", path, err) - } else { - utils.GoFmt(path) - mlog.Print("generated:", path) - } - } -} - -func generateDaoInternal( - tableNameCamelCase, tableNameCamelLowerCase, importPrefix string, - dirPathDao, fileName string, - fieldMap map[string]*gdb.TableField, - req generateDaoReq, -) { - path := gfile.Join(dirPathDao, "internal", fileName+".go") - modelContent := gstr.ReplaceByMap(getTplDaoInternalContent(req.TplDaoInternalPath), g.MapStrStr{ - "{TplImportPrefix}": importPrefix, - "{TplTableName}": req.TableName, - "{TplGroupName}": req.GroupName, - "{TplTableNameCamelCase}": tableNameCamelCase, - "{TplTableNameCamelLowerCase}": tableNameCamelLowerCase, - "{TplColumnDefine}": gstr.Trim(generateColumnDefinitionForDao(fieldMap)), - "{TplColumnNames}": gstr.Trim(generateColumnNamesForDao(fieldMap)), - }) - if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil { - mlog.Fatalf("writing content to '%s' failed: %v", path, err) - } else { - utils.GoFmt(path) - mlog.Print("generated:", path) - } -} - -// generateStructDefinitionForModel generates and returns the struct definition for specified table. -func generateStructDefinitionForModel(structName string, fieldMap map[string]*gdb.TableField, req generateDaoReq) string { - buffer := bytes.NewBuffer(nil) - array := make([][]string, len(fieldMap)) - names := sortFieldKeyForDao(fieldMap) - for index, name := range names { - field := fieldMap[name] - array[index] = generateStructFieldForModel(field, req) - } - tw := tablewriter.NewWriter(buffer) - tw.SetBorder(false) - tw.SetRowLine(false) - tw.SetAutoWrapText(false) - tw.SetColumnSeparator("") - tw.AppendBulk(array) - tw.Render() - stContent := buffer.String() - // Let's do this hack of table writer for indent! - stContent = gstr.Replace(stContent, " #", "") - buffer.Reset() - buffer.WriteString(fmt.Sprintf("type %s struct {\n", structName)) - buffer.WriteString(stContent) - buffer.WriteString("}") - return buffer.String() -} - -// generateStructFieldForModel generates and returns the attribute definition for specified field. -func generateStructFieldForModel(field *gdb.TableField, req generateDaoReq) []string { - var typeName, ormTag, jsonTag string - t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type) - t = gstr.Split(gstr.Trim(t), " ")[0] - t = gstr.ToLower(t) - switch t { - case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob": - typeName = "[]byte" - - case "bit", "int", "int2", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial": - if gstr.ContainsI(field.Type, "unsigned") { - typeName = "uint" - } else { - typeName = "int" - } - - case "int4", "int8", "big_int", "bigint", "bigserial": - if gstr.ContainsI(field.Type, "unsigned") { - typeName = "uint64" - } else { - typeName = "int64" - } - - case "real": - typeName = "float32" - - case "float", "double", "decimal", "smallmoney", "numeric": - typeName = "float64" - - case "bool": - typeName = "bool" - - case "datetime", "timestamp", "date", "time": - if req.StdTime { - typeName = "time.Time" - } else { - typeName = "*gtime.Time" - } - - default: - // Auto detecting type. - switch { - case strings.Contains(t, "int"): - typeName = "int" - case strings.Contains(t, "text") || strings.Contains(t, "char"): - typeName = "string" - case strings.Contains(t, "float") || strings.Contains(t, "double"): - typeName = "float64" - case strings.Contains(t, "bool"): - typeName = "bool" - case strings.Contains(t, "binary") || strings.Contains(t, "blob"): - typeName = "[]byte" - case strings.Contains(t, "date") || strings.Contains(t, "time"): - if req.StdTime { - typeName = "time.Time" - } else { - typeName = "*gtime.Time" - } - default: - typeName = "string" - } - } - ormTag = field.Name - jsonTag = getJsonTagFromCase(field.Name, req.JsonCase) - if gstr.ContainsI(field.Key, "pri") { - ormTag += ",primary" - } - if gstr.ContainsI(field.Key, "uni") { - ormTag += ",unique" - } - return []string{ - " #" + gstr.CaseCamel(field.Name), - " #" + typeName, - " #" + fmt.Sprintf("`"+`orm:"%s"`, ormTag), - " #" + fmt.Sprintf(`json:"%s"`+"`", jsonTag), - " #" + fmt.Sprintf(`// %s`, formatComment(field.Comment)), - } -} - -// formatComment formats the comment string to fit the golang code without any lines. -func formatComment(comment string) string { - comment = gstr.ReplaceByArray(comment, g.SliceStr{ - "\n", " ", - "\r", " ", - }) - comment = gstr.Trim(comment) - comment = gstr.Replace(comment, `\n`, " ") - return comment -} - -// generateColumnDefinitionForDao generates and returns the column names definition for specified table. -func generateColumnDefinitionForDao(fieldMap map[string]*gdb.TableField) string { - var ( - buffer = bytes.NewBuffer(nil) - array = make([][]string, len(fieldMap)) - names = sortFieldKeyForDao(fieldMap) - ) - for index, name := range names { - field := fieldMap[name] - comment := gstr.Trim(gstr.ReplaceByArray(field.Comment, g.SliceStr{ - "\n", " ", - "\r", " ", - })) - array[index] = []string{ - " #" + gstr.CaseCamel(field.Name), - " # " + "string", - " #" + fmt.Sprintf(`// %s`, comment), - } - } - tw := tablewriter.NewWriter(buffer) - tw.SetBorder(false) - tw.SetRowLine(false) - tw.SetAutoWrapText(false) - tw.SetColumnSeparator("") - tw.AppendBulk(array) - tw.Render() - defineContent := buffer.String() - // Let's do this hack of table writer for indent! - defineContent = gstr.Replace(defineContent, " #", "") - buffer.Reset() - buffer.WriteString(defineContent) - return buffer.String() -} - -// generateColumnNamesForDao generates and returns the column names assignment content of column struct -// for specified table. -func generateColumnNamesForDao(fieldMap map[string]*gdb.TableField) string { - var ( - buffer = bytes.NewBuffer(nil) - array = make([][]string, len(fieldMap)) - names = sortFieldKeyForDao(fieldMap) - ) - for index, name := range names { - field := fieldMap[name] - array[index] = []string{ - " #" + gstr.CaseCamel(field.Name) + ":", - fmt.Sprintf(` #"%s",`, field.Name), - } - } - tw := tablewriter.NewWriter(buffer) - tw.SetBorder(false) - tw.SetRowLine(false) - tw.SetAutoWrapText(false) - tw.SetColumnSeparator("") - tw.AppendBulk(array) - tw.Render() - namesContent := buffer.String() - // Let's do this hack of table writer for indent! - namesContent = gstr.Replace(namesContent, " #", "") - buffer.Reset() - buffer.WriteString(namesContent) - return buffer.String() -} - -func getTplDaoIndexContent(tplDaoIndexPath string) string { - if tplDaoIndexPath != "" { - return gfile.GetContents(tplDaoIndexPath) - } - return templateDaoDaoIndexContent -} - -func getTplDaoInternalContent(tplDaoInternalPath string) string { - if tplDaoInternalPath != "" { - return gfile.GetContents(tplDaoInternalPath) - } - return templateDaoDaoInternalContent -} - -func getTplModelIndexContent(tplModelIndexPath string) string { - if tplModelIndexPath != "" { - return gfile.GetContents(tplModelIndexPath) - } - return templateDaoModelIndexContent -} - -func getTplModelStructContent(tplModelStructPath string) string { - if tplModelStructPath != "" { - return gfile.GetContents(tplModelStructPath) - } - return templateDaoModelStructContent -} - -// getJsonTagFromCase call gstr.Case* function to convert the s to specified case. -func getJsonTagFromCase(str, caseStr string) string { - switch gstr.ToLower(caseStr) { - case gstr.ToLower("Camel"): - return gstr.CaseCamel(str) - - case gstr.ToLower("CamelLower"): - return gstr.CaseCamelLower(str) - - case gstr.ToLower("Kebab"): - return gstr.CaseKebab(str) - - case gstr.ToLower("KebabScreaming"): - return gstr.CaseKebabScreaming(str) - - case gstr.ToLower("Snake"): - return gstr.CaseSnake(str) - - case gstr.ToLower("SnakeFirstUpper"): - return gstr.CaseSnakeFirstUpper(str) - - case gstr.ToLower("SnakeScreaming"): - return gstr.CaseSnakeScreaming(str) - } - return str -} - -func sortFieldKeyForDao(fieldMap map[string]*gdb.TableField) []string { - names := make(map[int]string) - for _, field := range fieldMap { - names[field.Index] = field.Name - } - var ( - i = 0 - j = 0 - result = make([]string, len(names)) - ) - for { - if len(names) == 0 { - break - } - if val, ok := names[i]; ok { - result[j] = val - j++ - delete(names, i) - } - i++ - } - return result -} - -// getOptionOrConfigForDao retrieves option value from parser and configuration file. -// It returns the default value specified by parameter <value> is no value found. -func getOptionOrConfigForDao(index int, parser *gcmd.Parser, name string, defaultValue ...string) (result string) { - result = parser.GetOpt(name) - if result == "" && g.Config().Available() { - g.Cfg().SetViolenceCheck(true) - if index >= 0 { - result = g.Cfg().GetString(fmt.Sprintf(`%s.%d.%s`, nodeNameGenDaoInConfigFile, index, name)) - } else { - result = g.Cfg().GetString(fmt.Sprintf(`%s.%s`, nodeNameGenDaoInConfigFile, name)) - } - } - if result == "" && len(defaultValue) > 0 { - result = defaultValue[0] - } - return -} diff --git a/tool/gf/commands/gen/gen_dao_template_dao.go b/tool/gf/commands/gen/gen_dao_template_dao.go deleted file mode 100644 index 879875c07..000000000 --- a/tool/gf/commands/gen/gen_dao_template_dao.go +++ /dev/null @@ -1,73 +0,0 @@ -package gen - -const templateDaoDaoIndexContent = ` -// ================================================================================= -// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. -// ================================================================================= - -package dao - -import ( - "{TplImportPrefix}/dao/internal" -) - -// {TplTableNameCamelLowerCase}Dao is the manager for logic model data accessing and custom defined data operations functions management. -// You can define custom methods on it to extend its functionality as you wish. -type {TplTableNameCamelLowerCase}Dao struct { - *internal.{TplTableNameCamelCase}Dao -} - -var ( - // {TplTableNameCamelCase} is globally public accessible object for table {TplTableName} operations. - {TplTableNameCamelCase} {TplTableNameCamelLowerCase}Dao -) - -func init() { - {TplTableNameCamelCase} = {TplTableNameCamelLowerCase}Dao{ - internal.New{TplTableNameCamelCase}Dao(), - } -} - -// Fill with you ideas below. - -` - -const templateDaoDaoInternalContent = ` -// ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ========================================================================== - -package internal - -import ( - "github.com/gogf/gf/database/gdb" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/frame/gmvc" -) - -// {TplTableNameCamelCase}Dao is the manager for logic model data accessing and custom defined data operations functions management. -type {TplTableNameCamelCase}Dao struct { - gmvc.M // M is the core and embedded struct that inherits all chaining operations from gdb.Model. - C {TplTableNameCamelLowerCase}Columns // C is the short type for Columns, which contains all the column names of Table for convenient usage. - DB gdb.DB // DB is the raw underlying database management object. - Table string // Table is the underlying table name of the DAO. -} - -// {TplTableNameCamelCase}Columns defines and stores column names for table {TplTableName}. -type {TplTableNameCamelLowerCase}Columns struct { - {TplColumnDefine} -} - -// New{TplTableNameCamelCase}Dao creates and returns a new DAO object for table data access. -func New{TplTableNameCamelCase}Dao() *{TplTableNameCamelCase}Dao { - columns := {TplTableNameCamelLowerCase}Columns{ - {TplColumnNames} - } - return &{TplTableNameCamelCase}Dao{ - C: columns, - M: g.DB("{TplGroupName}").Model("{TplTableName}").Safe(), - DB: g.DB("{TplGroupName}"), - Table: "{TplTableName}", - } -} -` diff --git a/tool/gf/commands/gen/gen_dao_template_model.go b/tool/gf/commands/gen/gen_dao_template_model.go deleted file mode 100644 index 3eef8d4c0..000000000 --- a/tool/gf/commands/gen/gen_dao_template_model.go +++ /dev/null @@ -1,18 +0,0 @@ -package gen - -const templateDaoModelIndexContent = ` -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package model - -{TplPackageImports} - -{TplModelStructs} -` - -const templateDaoModelStructContent = ` -// {TplTableNameCamelCase} is the golang structure for table {TplTableName}. -{TplStructDefine} -` diff --git a/tool/gf/commands/gen/gen_pb.go b/tool/gf/commands/gen/gen_pb.go deleted file mode 100644 index 81ee6f1be..000000000 --- a/tool/gf/commands/gen/gen_pb.go +++ /dev/null @@ -1,77 +0,0 @@ -package gen - -import ( - "fmt" - "github.com/gogf/gf/container/gset" - "github.com/gogf/gf/os/genv" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/gproc" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/mlog" -) - -func HelpPb() { - mlog.Print(gstr.TrimLeft(` -USAGE - gf gen pb - -`)) -} - -// doGenPb parses current `proto` files in folder `protocol` and generates `pb` files to `protobuf`. -func doGenPb() { - // protoc search. - protocBinPath := gproc.SearchBinary("protoc") - if protocBinPath == "" { - mlog.Fatal(`"protoc" command not found, install it first to proceed proto files parsing`) - } - // protocol fold checks. - protoFolder := "protocol" - if !gfile.Exists(protoFolder) { - mlog.Fatalf(`proto files folder "%s" does not exist`, protoFolder) - } - // folder scanning. - files, err := gfile.ScanDirFile(protoFolder, "*.proto", true) - if err != nil { - mlog.Fatal(err) - } - if len(files) == 0 { - mlog.Fatalf(`no proto files found in folder "%s"`, protoFolder) - } - dirSet := gset.NewStrSet() - for _, file := range files { - dirSet.Add(gfile.Dir(file)) - } - var ( - servicePath = gfile.RealPath(".") - goPathSrc = gfile.RealPath(gfile.Join(genv.Get("GOPATH"), "src")) - ) - dirSet.Iterator(func(protoDirPath string) bool { - parsingCommand := fmt.Sprintf( - "protoc --gofast_out=plugins=grpc:. %s/*.proto -I%s", - protoDirPath, - servicePath, - ) - if goPathSrc != "" { - parsingCommand += " -I" + goPathSrc - } - mlog.Print(parsingCommand) - if output, err := gproc.ShellExec(parsingCommand); err != nil { - mlog.Print(output) - mlog.Fatal(err) - } - return true - }) - // Custom replacement. - //pbFolder := "protobuf" - //_, _ = gfile.ScanDirFileFunc(pbFolder, "*.go", true, func(path string) string { - // content := gfile.GetContents(path) - // content = gstr.ReplaceByArray(content, g.SliceStr{ - // `gtime "gtime"`, `gtime "github.com/gogf/gf/os/gtime"`, - // }) - // _ = gfile.PutContents(path, content) - // utils.GoFmt(path) - // return path - //}) - mlog.Print("done!") -} diff --git a/tool/gf/commands/gen/gen_pbentity.go b/tool/gf/commands/gen/gen_pbentity.go deleted file mode 100644 index a21d81a61..000000000 --- a/tool/gf/commands/gen/gen_pbentity.go +++ /dev/null @@ -1,447 +0,0 @@ -package gen - -import ( - "bytes" - "context" - "fmt" - _ "github.com/denisenkom/go-mssqldb" - "github.com/gogf/gf/database/gdb" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/gtime" - "github.com/gogf/gf/text/gregex" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/mlog" - "github.com/gogf/gf/util/gconv" - _ "github.com/lib/pq" - //_ "github.com/mattn/go-oci8" - //_ "github.com/mattn/go-sqlite3" - "github.com/olekukonko/tablewriter" - "strings" -) - -// generatePbEntityReq is the input parameter for generating entity protobuf files. -type generatePbEntityReq struct { - TableName string // TableName specifies the table name of the table. - NewTableName string // NewTableName specifies the prefix-stripped name of the table. - PrefixName string // PrefixName specifies the custom prefix name for generated protobuf entity. - GroupName string // GroupName specifies the group name of database configuration node for generated protobuf entity. - PkgName string // PkgName specifies package name for generated protobuf. - NameCase string // NameCase specifies the case of generated attribute name for entity message, value from gstr.Case* function names. - JsonCase string // JsonCase specifies the case of json tag for attribute name of entity message, value from gstr.Case* function names. - DirPath string // DirPath specifies the directory path for generated files. - OptionContent string // OptionContent specifies the extra option configuration content for protobuf. - TplEntityPath string // TplEntityPath specifies the file path for generating protobuf entity files. -} - -const ( - nodeNameGenPbEntityInConfigFile = "gfcli.gen.pbentity" -) - -func HelpPbEntity() { - mlog.Print(gstr.TrimLeft(` -USAGE - gf gen pbentity [OPTION] - -OPTION - -/--path directory path for generated files. - -/--package package name for all entity proto files. - -l, --link database configuration, the same as the ORM configuration of GoFrame. - -t, --tables generate models only for given tables, multiple table names separated with ',' - -c, --config used to specify the configuration file for database, it's commonly not necessary. - If "-l" is not passed, it will search "./config.toml" and "./config/config.toml" - in current working directory in default. - -p, --prefix add specified prefix for all entity names and entity proto files. - -r, --removePrefix remove specified prefix of the table, multiple prefix separated with ',' - -n, --nameCase case for message attribute names, default is "Camel": - | Case | Example | - |---------------- |--------------------| - | Camel | AnyKindOfString | default - | CamelLower | anyKindOfString | - | Snake | any_kind_of_string | - | SnakeScreaming | ANY_KIND_OF_STRING | - | SnakeFirstUpper | rgb_code_md5 | - | Kebab | any-kind-of-string | - | KebabScreaming | ANY-KIND-OF-STRING | - -j, --jsonCase case for message json tag, cases are the same as "nameCase", default "CamelLower". - set it to "none" to ignore json tag generating. - -o, --option extra protobuf options. - -/--tplEntity template content for protobuf entity files generating. - -CONFIGURATION SUPPORT - Options are also supported by configuration file. - It's suggested using configuration file instead of command line arguments making producing. - The configuration node name is "gf.gen.pbentity", which also supports multiple databases, for example: - [gfcli] - [[gfcli.gen.pbentity]] - link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" - path = "protocol/demos/entity" - tables = "order,products" - package = "demos" - [[gfcli.gen.pbentity]] - link = "mysql:root:12345678@tcp(127.0.0.1:3306)/primary" - path = "protocol/demos/entity" - prefix = "primary_" - tables = "user, userDetail" - package = "demos" - option = """ -option go_package = "protobuf/demos"; -option java_package = "protobuf/demos"; -option php_namespace = "protobuf/demos"; -""" - -EXAMPLES - gf gen pbentity - gf gen pbentity -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test" - gf gen pbentity -path ./protocol/demos/entity -c config.yaml -g user-center -t user,user_detail,user_login - gf gen pbentity -r user_ -`)) -} - -// doGenPbEntity implements the "gen pbentity" command. -func doGenPbEntity() { - parser, err := gcmd.Parse(g.MapStrBool{ - "path": true, - "package": true, - "l,link": true, - "t,tables": true, - "c,config": true, - "p,prefix": true, - "r,removePrefix": true, - "o,option": true, - "n,nameCase": true, - "j,jsonCase": true, - "tplEntity": true, - }) - if err != nil { - mlog.Fatal(err) - } - config := g.Cfg() - if config.Available() { - v := config.GetVar(nodeNameGenPbEntityInConfigFile) - if v.IsEmpty() && g.IsEmpty(parser.GetOptAll()) { - mlog.Fatal(`command arguments and configurations not found for generating protobuf entity files`) - } - if v.IsSlice() { - for i := 0; i < len(v.Interfaces()); i++ { - doGenPbEntityForArray(i, parser) - } - } else { - doGenPbEntityForArray(-1, parser) - } - } else { - doGenPbEntityForArray(-1, parser) - } - mlog.Print("done!") -} - -// doGenPbEntityForArray implements the "gen pbentity" command for configuration array. -func doGenPbEntityForArray(index int, parser *gcmd.Parser) { - var ( - err error - db gdb.DB - dirPath = getOptionOrConfigForPbEntity(index, parser, "path") // Generated directory path. - pkgName = getOptionOrConfigForPbEntity(index, parser, "package") // Package name for protobuf. - tablesStr = getOptionOrConfigForPbEntity(index, parser, "tables") // Tables that will be generated. - prefixName = getOptionOrConfigForPbEntity(index, parser, "prefix") // Add prefix to entity name. - linkInfo = getOptionOrConfigForPbEntity(index, parser, "link") // Custom database link. - configPath = getOptionOrConfigForPbEntity(index, parser, "config") // Config file path, eg: ./config/db.toml. - configGroup = getOptionOrConfigForPbEntity(index, parser, "group", "default") // Group name of database configuration node for generated protobuf entity. - removePrefix = getOptionOrConfigForPbEntity(index, parser, "removePrefix") // Remove prefix from table name. - nameCase = getOptionOrConfigForPbEntity(index, parser, "nameCase", "Camel") // Case configuration for message name. - jsonCase = getOptionOrConfigForPbEntity(index, parser, "jsonCase", "CamelLower") // Case configuration for message json tag. - optionContent = getOptionOrConfigForPbEntity(index, parser, "option") // Option content for protobuf. - tplEntityPath = getOptionOrConfigForPbEntity(index, parser, "tplEntity") // Specifies the file path for generating protobuf entity files. - ) - if tplEntityPath != "" && (!gfile.Exists(tplEntityPath) || !gfile.IsReadable(tplEntityPath)) { - mlog.Fatalf("template file for entity files generating does not exist or is not readable: %s", tplEntityPath) - } - // Make it compatible with old CLI version for option name: remove-prefix - if removePrefix == "" { - removePrefix = getOptionOrConfigForPbEntity(index, parser, "remove-prefix") - } - removePrefixArray := gstr.SplitAndTrim(removePrefix, ",") - if pkgName == "" { - mlog.Fatal("package name should not be empty") - } - // It reads database configuration from project configuration file. - if configPath != "" { - path, err := gfile.Search(configPath) - if err != nil { - mlog.Fatalf("search configuration file '%s' failed: %v", configPath, err) - } - if err := g.Cfg().SetPath(gfile.Dir(path)); err != nil { - mlog.Fatalf("set configuration path '%s' failed: %v", path, err) - } - g.Cfg().SetFileName(gfile.Basename(path)) - } - // It uses user passed database configuration. - if linkInfo != "" { - tempGroup := gtime.TimestampNanoStr() - match, _ := gregex.MatchString(`([a-z]+):(.+)`, linkInfo) - if len(match) == 3 { - gdb.AddConfigNode(tempGroup, gdb.ConfigNode{ - Type: gstr.Trim(match[1]), - Link: gstr.Trim(match[2]), - }) - db, _ = gdb.Instance(tempGroup) - } - } else { - db = g.DB(configGroup) - } - if db == nil { - mlog.Fatal("database initialization failed") - } - - tableNames := ([]string)(nil) - if tablesStr != "" { - tableNames = gstr.SplitAndTrim(tablesStr, ",") - } else { - tableNames, err = db.Tables(context.TODO()) - if err != nil { - mlog.Fatalf("fetching tables failed: \n %v", err) - } - } - - for _, tableName := range tableNames { - newTableName := tableName - for _, v := range removePrefixArray { - newTableName = gstr.TrimLeftStr(newTableName, v, 1) - } - req := &generatePbEntityReq{ - TableName: tableName, - NewTableName: newTableName, - PrefixName: prefixName, - GroupName: configGroup, - PkgName: pkgName, - NameCase: nameCase, - JsonCase: jsonCase, - DirPath: dirPath, - OptionContent: gstr.Trim(optionContent), - TplEntityPath: tplEntityPath, - } - generatePbEntityContentFile(db, req) - } -} - -// generatePbEntityContentFile generates the protobuf files for given table. -func generatePbEntityContentFile(db gdb.DB, req *generatePbEntityReq) { - fieldMap, err := db.TableFields(db.GetCtx(), req.TableName) - if err != nil { - mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", req.TableName, err) - } - // Change the `newTableName` if `prefixName` is given. - newTableName := "Entity_" + req.PrefixName + req.NewTableName - var ( - tableNameCamelCase = gstr.CaseCamel(newTableName) - tableNameSnakeCase = gstr.CaseSnake(newTableName) - entityMessageDefine = generateEntityMessageDefinition(tableNameCamelCase, fieldMap, req) - fileName = gstr.Trim(tableNameSnakeCase, "-_.") - path = gfile.Join(req.DirPath, fileName+".proto") - ) - entityContent := gstr.ReplaceByMap(getTplPbEntityContent(req.TplEntityPath), g.MapStrStr{ - "{PackageName}": req.PkgName, - "{OptionContent}": req.OptionContent, - "{EntityMessage}": entityMessageDefine, - }) - if err := gfile.PutContents(path, strings.TrimSpace(entityContent)); err != nil { - mlog.Fatalf("writing content to '%s' failed: %v", path, err) - } else { - mlog.Print("generated:", path) - } -} - -// generateEntityMessageDefinition generates and returns the message definition for specified table. -func generateEntityMessageDefinition(name string, fieldMap map[string]*gdb.TableField, req *generatePbEntityReq) string { - var ( - buffer = bytes.NewBuffer(nil) - array = make([][]string, len(fieldMap)) - names = sortFieldKeyForPbEntity(fieldMap) - ) - for index, name := range names { - array[index] = generateMessageFieldForPbEntity(index+1, fieldMap[name], req) - } - tw := tablewriter.NewWriter(buffer) - tw.SetBorder(false) - tw.SetRowLine(false) - tw.SetAutoWrapText(false) - tw.SetColumnSeparator("") - tw.AppendBulk(array) - tw.Render() - stContent := buffer.String() - // Let's do this hack of table writer for indent! - stContent = gstr.Replace(stContent, " #", "") - buffer.Reset() - buffer.WriteString(fmt.Sprintf("message %s {\n", name)) - buffer.WriteString(stContent) - buffer.WriteString("}") - return buffer.String() -} - -// generateMessageFieldForPbEntity generates and returns the message definition for specified field. -func generateMessageFieldForPbEntity(index int, field *gdb.TableField, req *generatePbEntityReq) []string { - var ( - typeName string - comment string - jsonTagStr string - ) - t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type) - t = gstr.Split(gstr.Trim(t), " ")[0] - t = gstr.ToLower(t) - switch t { - case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob": - typeName = "bytes" - - case "bit", "int", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial": - if gstr.ContainsI(field.Type, "unsigned") { - typeName = "uint32" - } else { - typeName = "int32" - } - - case "int8", "big_int", "bigint", "bigserial": - if gstr.ContainsI(field.Type, "unsigned") { - typeName = "uint64" - } else { - typeName = "int64" - } - - case "real": - typeName = "float" - - case "float", "double", "decimal", "smallmoney": - typeName = "double" - - case "bool": - typeName = "bool" - - case "datetime", "timestamp", "date", "time": - typeName = "int64" - - default: - // Auto detecting type. - switch { - case strings.Contains(t, "int"): - typeName = "int" - case strings.Contains(t, "text") || strings.Contains(t, "char"): - typeName = "string" - case strings.Contains(t, "float") || strings.Contains(t, "double"): - typeName = "double" - case strings.Contains(t, "bool"): - typeName = "bool" - case strings.Contains(t, "binary") || strings.Contains(t, "blob"): - typeName = "bytes" - case strings.Contains(t, "date") || strings.Contains(t, "time"): - typeName = "int64" - default: - typeName = "string" - } - } - comment = gstr.ReplaceByArray(field.Comment, g.SliceStr{ - "\n", " ", - "\r", " ", - }) - comment = gstr.Trim(comment) - comment = gstr.Replace(comment, `\n`, " ") - comment, _ = gregex.ReplaceString(`\s{2,}`, ` `, comment) - if jsonTagName := formatCase(field.Name, req.JsonCase); jsonTagName != "" { - jsonTagStr = fmt.Sprintf(`[(gogoproto.jsontag) = "%s"]`, jsonTagName) - // beautiful indent. - if index < 10 { - // 3 spaces - jsonTagStr = " " + jsonTagStr - } else if index < 100 { - // 2 spaces - jsonTagStr = " " + jsonTagStr - } else { - // 1 spaces - jsonTagStr = " " + jsonTagStr - } - } - return []string{ - " #" + typeName, - " #" + formatCase(field.Name, req.NameCase), - " #= " + gconv.String(index) + jsonTagStr + ";", - " #" + fmt.Sprintf(`// %s`, comment), - } -} - -func getTplPbEntityContent(tplEntityPath string) string { - if tplEntityPath != "" { - return gfile.GetContents(tplEntityPath) - } - return templatePbEntityMessageContent -} - -// formatCase call gstr.Case* function to convert the s to specified case. -func formatCase(str, caseStr string) string { - switch gstr.ToLower(caseStr) { - case gstr.ToLower("Camel"): - return gstr.CaseCamel(str) - - case gstr.ToLower("CamelLower"): - return gstr.CaseCamelLower(str) - - case gstr.ToLower("Kebab"): - return gstr.CaseKebab(str) - - case gstr.ToLower("KebabScreaming"): - return gstr.CaseKebabScreaming(str) - - case gstr.ToLower("Snake"): - return gstr.CaseSnake(str) - - case gstr.ToLower("SnakeFirstUpper"): - return gstr.CaseSnakeFirstUpper(str) - - case gstr.ToLower("SnakeScreaming"): - return gstr.CaseSnakeScreaming(str) - - case "none": - return "" - } - return str -} - -// getOptionOrConfigForPbEntity retrieves option value from parser and configuration file. -// It returns the default value specified by parameter <value> is no value found. -func getOptionOrConfigForPbEntity(index int, parser *gcmd.Parser, name string, defaultValue ...string) (result string) { - result = parser.GetOpt(name) - if result == "" && g.Config().Available() { - g.Cfg().SetViolenceCheck(true) - if index >= 0 { - result = g.Cfg().GetString(fmt.Sprintf(`%s.%d.%s`, nodeNameGenPbEntityInConfigFile, index, name)) - } else { - result = g.Cfg().GetString(fmt.Sprintf(`%s.%s`, nodeNameGenPbEntityInConfigFile, name)) - } - } - if result == "" && len(defaultValue) > 0 { - result = defaultValue[0] - } - return -} - -func sortFieldKeyForPbEntity(fieldMap map[string]*gdb.TableField) []string { - names := make(map[int]string) - for _, field := range fieldMap { - names[field.Index] = field.Name - } - var ( - result = make([]string, len(names)) - i = 0 - j = 0 - ) - for { - if len(names) == 0 { - break - } - if val, ok := names[i]; ok { - result[j] = val - j++ - delete(names, i) - } - i++ - } - return result -} diff --git a/tool/gf/commands/gen/gen_pbentity_template.go b/tool/gf/commands/gen/gen_pbentity_template.go deleted file mode 100644 index f73d5c55e..000000000 --- a/tool/gf/commands/gen/gen_pbentity_template.go +++ /dev/null @@ -1,17 +0,0 @@ -package gen - -const templatePbEntityMessageContent = ` -// ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ========================================================================== - -syntax = "proto3"; - -package {PackageName}; - -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -{OptionContent} - -{EntityMessage} -` diff --git a/tool/gf/commands/get/get.go b/tool/gf/commands/get/get.go deleted file mode 100644 index 584b5cf7e..000000000 --- a/tool/gf/commands/get/get.go +++ /dev/null @@ -1,33 +0,0 @@ -package get - -import ( - "fmt" - "github.com/gogf/gf/os/gproc" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/mlog" - "os" -) - -func Help() { - mlog.Print(gstr.TrimLeft(` -USAGE - gf get PACKAGE - -ARGUMENT - PACKAGE remote golang package path, eg: github.com/gogf/gf - -EXAMPLES - gf get github.com/gogf/gf - gf get github.com/gogf/gf@latest - gf get github.com/gogf/gf@master - gf get golang.org/x/sys -`)) -} - -func Run() { - if len(os.Args) > 2 { - gproc.ShellRun(fmt.Sprintf(`go get -u %s`, gstr.Join(os.Args[2:], " "))) - } else { - mlog.Fatal("please input the package path for get") - } -} diff --git a/tool/gf/commands/initialize/initialize.go b/tool/gf/commands/initialize/initialize.go deleted file mode 100644 index a5ddadd0b..000000000 --- a/tool/gf/commands/initialize/initialize.go +++ /dev/null @@ -1,110 +0,0 @@ -package initialize - -import ( - "github.com/gogf/gf/encoding/gcompress" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/allyes" - "github.com/gogf/gf/tool/gf/library/mlog" - "strings" -) - -const ( - emptyProject = "github.com/gogf/gf-empty" - emptyProjectName = "gf-empty" -) - -var ( - cdnUrl = g.Config("url").GetString("cdn.url") - homeUrl = g.Config("url").GetString("home.url") -) - -func init() { - if cdnUrl == "" { - mlog.Fatal("CDN configuration cannot be empty") - } - if homeUrl == "" { - mlog.Fatal("Home configuration cannot be empty") - } -} - -func Help() { - mlog.Print(gstr.TrimLeft(` -USAGE - gf init NAME - -ARGUMENT - NAME name for the project. It will create a folder with NAME in current directory. - The NAME will also be the module name for the project. - -EXAMPLES - gf init my-app - gf init my-project-name -`)) -} - -func Run() { - parser, err := gcmd.Parse(nil) - if err != nil { - mlog.Fatal(err) - } - projectName := parser.GetArg(2) - if projectName == "" { - mlog.Fatal("project name should not be empty") - } - dirPath := projectName - if !gfile.IsEmpty(dirPath) && !allyes.Check() { - s := gcmd.Scanf(`the folder "%s" is not empty, files might be overwrote, continue? [y/n]: `, projectName) - if strings.EqualFold(s, "n") { - return - } - } - mlog.Print("initializing...") - // MD5 retrieving. - respMd5, err := g.Client().Get(homeUrl + "/cli/project/md5") - if err != nil { - mlog.Fatalf("get the project zip md5 failed: %s", err.Error()) - } - if respMd5 == nil { - mlog.Fatal("got the project zip md5 failed") - } - defer respMd5.Close() - md5DataStr := respMd5.ReadAllString() - if md5DataStr == "" { - mlog.Fatal("get the project zip md5 failed: empty md5 value. maybe network issue, try again?") - } - - // Zip data retrieving. - respData, err := g.Client().Get(cdnUrl + "/cli/project/zip?" + md5DataStr) - if err != nil { - mlog.Fatalf("got the project zip data failed: %s", err.Error()) - } - if respData == nil { - mlog.Fatal("got the project zip data failed") - } - defer respData.Close() - zipData := respData.ReadAll() - if len(zipData) == 0 { - mlog.Fatal("get the project data failed: empty data value. maybe network issue, try again?") - } - // Current folder. - replacedProjectName := projectName - if replacedProjectName == "." { - replacedProjectName = gfile.Name(gfile.RealPath(".")) - } - // Unzip the zip data. - if err = gcompress.UnZipContent(zipData, dirPath, emptyProjectName+"-master"); err != nil { - mlog.Fatal("unzip project data failed,", err.Error()) - } - // Replace project name. - if err = gfile.ReplaceDir(emptyProject, replacedProjectName, dirPath, "Dockerfile,*.go,*.MD,*.mod", true); err != nil { - mlog.Fatal("content replacing failed,", err.Error()) - } - if err = gfile.ReplaceDir(emptyProjectName, replacedProjectName, dirPath, "Dockerfile,*.go,*.MD,*.mod", true); err != nil { - mlog.Fatal("content replacing failed,", err.Error()) - } - mlog.Print("initialization done! ") - mlog.Print("you can now run 'gf run main.go' to start your journey, enjoy!") -} diff --git a/tool/gf/commands/mod/mod.go b/tool/gf/commands/mod/mod.go deleted file mode 100644 index 216ae0302..000000000 --- a/tool/gf/commands/mod/mod.go +++ /dev/null @@ -1,113 +0,0 @@ -package mod - -import ( - "fmt" - "github.com/gogf/gf/container/gmap" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/genv" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/mlog" -) - -func Help() { - mlog.Print(gstr.TrimLeft(` -USAGE - gf mod ARGUMENT - -ARGUMENT - path copy all packages with its latest version in Go modules, which does not exist - in GOPATH, to GOPATH. This enables your project using GOPATH building, but you - should have GOPATH environment variable configured. - -EXAMPLES - gf mod path -`)) -} - -func Run() { - argument := gcmd.GetArg(2) - switch argument { - case "path": - doPath() - - default: - mlog.Print("argument cannot be empty") - Help() - } -} - -// doPath copies all packages in Go modules, which does not exist in GOPATH, to GOPATH. -// This enables your project using GOPATH building, but you should have GOPATH -// environment variable configured. -func doPath() { - goPathEnv := genv.Get("GOPATH") - if goPathEnv == "" { - mlog.Fatal("GOPATH is not found in your environment") - } - mlog.Print("scanning...") - var ( - copied = false - haveCount = 0 - ) - for _, goPath := range gstr.SplitAndTrim(goPathEnv, ";") { - goModPath := gfile.Join(goPath, "pkg", "mod") - if !gfile.Exists(goModPath) { - continue - } - pathMap := gmap.NewStrStrMap() - _, err := gfile.ScanDirFunc(goModPath, "*.*", true, func(path string) string { - // Ignore the cache folder. - if gstr.Contains(path, gfile.Join(goModPath, "cache")) { - return "" - } - name := gfile.Name(path) - if name == "" { - return "" - } - if !gstr.Contains(name, "@") { - return "" - } - if n := gstr.Count(path, "@"); n > 1 { - return "" - } - if !gfile.IsDir(path) { - return "" - } - array := gstr.Split(path, "@") - if v := pathMap.Get(array[0]); v == "" { - pathMap.Set(array[0], array[1]) - } else { - if gstr.CompareVersionGo(v, array[1]) < 0 { - pathMap.Set(array[0], array[1]) - } - } - return path - }) - if err != nil { - mlog.Fatal(err) - } - haveCount += pathMap.Size() - pathMap.Iterator(func(k string, v string) bool { - src := fmt.Sprintf(`%s@%s`, k, v) - dst := gfile.Join(goPath, "src", gstr.Trim(gstr.Replace(k, goModPath, ""), "\\/")) - if !gfile.Exists(dst) { - mlog.Printf(`copying %s to %s`, src, dst) - if err := gfile.Copy(src, dst); err != nil { - mlog.Fatal(err) - } - copied = true - } - return true - }) - } - if !copied { - if haveCount > 0 { - mlog.Print(`all packages of go modules already exist in GOPATH`) - } else { - mlog.Printf(`no packages found in go module path: %s`, goPathEnv) - } - return - } - mlog.Print("done!") -} diff --git a/tool/gf/commands/pack/pack.go b/tool/gf/commands/pack/pack.go deleted file mode 100644 index 7a3efaa9d..000000000 --- a/tool/gf/commands/pack/pack.go +++ /dev/null @@ -1,80 +0,0 @@ -package pack - -import ( - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/gres" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/allyes" - "github.com/gogf/gf/tool/gf/library/mlog" - "strings" -) - -func Help() { - mlog.Print(gstr.TrimLeft(` -USAGE - gf pack SRC DST - -ARGUMENT - SRC source path for packing, which can be multiple source paths. - DST destination file path for packed file. if extension of the filename is ".go" and "-n" option is given, - it enables packing SRC to go file, or else it packs SRC into a binary file. - -OPTION - -n, --name package name for output go file, it's set as its directory name if no name passed - -p, --prefix prefix for each file packed into the resource file - -EXAMPLES - gf pack public data.bin - gf pack public,template data.bin - gf pack public,template packed/data.go - gf pack public,template,config packed/data.go - gf pack public,template,config packed/data.go -n=packed -p=/var/www/my-app - gf pack /var/www/public packed/data.go -n=packed -`)) -} - -func Run() { - parser, err := gcmd.Parse(g.MapStrBool{ - "n,name": true, - "p,prefix": true, - }) - if err != nil { - mlog.Fatal(err) - } - srcPath := parser.GetArg(2) - dstPath := parser.GetArg(3) - if srcPath == "" { - mlog.Fatal("SRC path cannot be empty") - } - if dstPath == "" { - mlog.Fatal("DST path cannot be empty") - } - if gfile.Exists(dstPath) && gfile.IsDir(dstPath) { - mlog.Fatalf("DST path '%s' cannot be a directory", dstPath) - } - if !gfile.IsEmpty(dstPath) && !allyes.Check() { - s := gcmd.Scanf("path '%s' is not empty, files might be overwrote, continue? [y/n]: ", dstPath) - if strings.EqualFold(s, "n") { - return - } - } - var ( - name = parser.GetOpt("name") - prefix = parser.GetOpt("prefix") - ) - if name == "" && gfile.ExtName(dstPath) == "go" { - name = gfile.Basename(gfile.Dir(dstPath)) - } - if name != "" { - if err := gres.PackToGoFile(srcPath, dstPath, name, prefix); err != nil { - mlog.Fatalf("pack failed: %v", err) - } - } else { - if err := gres.PackToFile(srcPath, dstPath, prefix); err != nil { - mlog.Fatalf("pack failed: %v", err) - } - } - mlog.Print("done!") -} diff --git a/tool/gf/commands/run/run.go b/tool/gf/commands/run/run.go deleted file mode 100644 index fe9b55d96..000000000 --- a/tool/gf/commands/run/run.go +++ /dev/null @@ -1,212 +0,0 @@ -package run - -import ( - "fmt" - "github.com/gogf/gf/container/garray" - "github.com/gogf/gf/container/gtype" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/gfsnotify" - "github.com/gogf/gf/os/gproc" - "github.com/gogf/gf/os/gtime" - "github.com/gogf/gf/os/gtimer" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/commands/swagger" - "github.com/gogf/gf/tool/gf/library/mlog" - "os" - "runtime" - "strings" - "time" -) - -type App struct { - File string // Go run file name/path. - Options string // Extra "go run" options. - Args string // Auto parse and pack swagger files. - Swagger bool // Auto parse and pack swagger files. -} - -const ( - gPROXY_CHECK_TIMEOUT = time.Second -) - -var ( - process *gproc.Process - httpClient = ghttp.NewClient() -) - -func init() { - httpClient.SetTimeout(gPROXY_CHECK_TIMEOUT) -} - -func Help() { - mlog.Print(gstr.TrimLeft(` -USAGE - gf run FILE [OPTION] - -ARGUMENT - FILE building file path. - OPTION the same options as "go run"/"go build" except some options as follows defined - -OPTION - -/--args custom process arguments. - -/--swagger auto parse and pack swagger into packed/data-swagger.go before running. - -EXAMPLES - gf run main.go - gf run main.go --swagger - gf run main.go --args "server -p 8080" - gf run main.go -mod=vendor - -DESCRIPTION - The "run" command is used for running go codes with hot-compiled-like feature, - which compiles and runs the go codes asynchronously when codes change. -`)) -} - -func Run() { - parser, err := gcmd.Parse(g.MapStrBool{ - "args": true, - }) - if err != nil { - mlog.Fatal(err) - } - mlog.SetHeaderPrint(true) - file := gcmd.GetArg(2) - if len(file) < 1 { - mlog.Fatal("file path cannot be empty") - } - app := &App{ - File: file, - } - // ================================================================================ - // This command is very special that it supports options of "go run" and "go build" - // from the third parameter of os.Args. That means, we should filter any parameter - // that "go run" and "go build" do not allow. - // ================================================================================ - // Swagger checks. - array := garray.NewStrArrayFrom(os.Args) - index := array.Search("--swagger") - if index < 0 { - index = array.Search("-swagger") - } - if index != -1 { - app.Swagger = true - array.Remove(index) - } - // args checks. - args := parser.GetOpt("args") - if args != "" { - app.Args = args - index := -1 - array.Iterator(func(k int, v string) bool { - if gstr.Contains(v, "-args") { - index = k - return false - } - return true - }) - if index != -1 { - v, _ := array.Get(index) - if gstr.Contains(v, "=") { - array.Remove(index) - } else { - array.Remove(index) - array.Remove(index) - } - } - } - // -y checks - array.RemoveValue("-y") - array.RemoveValue("--y") - if array.Len() > 3 { - app.Options = strings.Join(array.SubSlice(3), " ") - } - dirty := gtype.NewBool() - _, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) { - if gfile.ExtName(event.Path) != "go" { - return - } - // Ignore swagger file. - if gfile.Basename(event.Path) == "data-swagger.go" { - return - } - // Variable <dirty> is used for running the changes only one in one second. - if !dirty.Cas(false, true) { - return - } - // With some delay in case of multiple code changes in very short interval. - gtimer.SetTimeout(1500*gtime.MS, func() { - defer dirty.Set(false) - mlog.Printf(`go file changes: %s`, event.String()) - app.Run() - }) - }) - if err != nil { - mlog.Fatal(err) - } - go app.Run() - select {} -} - -func (app *App) Run() { - // Rebuild and run the codes. - renamePath := "" - mlog.Printf("build: %s", app.File) - outputPath := gfile.Join("bin", gfile.Name(app.File)) - if runtime.GOOS == "windows" { - outputPath += ".exe" - if gfile.Exists(outputPath) { - renamePath = outputPath + "~" - if err := gfile.Rename(outputPath, renamePath); err != nil { - mlog.Print(err) - } - } - } - // Auto swagger. - if app.Swagger { - if err := gproc.ShellRun(`gf swagger`); err != nil { - return - } - if gfile.Exists("swagger") { - packCmd := fmt.Sprintf(`gf pack %s packed/%s -n packed -y`, "swagger", swagger.PackedGoFileName) - mlog.Print(packCmd) - if err := gproc.ShellRun(packCmd); err != nil { - return - } - } - } - // In case of `pipe: too many open files` error. - // Build the app. - buildCommand := fmt.Sprintf(`go build -o %s %s %s`, outputPath, app.Options, app.File) - mlog.Print(buildCommand) - result, err := gproc.ShellExec(buildCommand) - if err != nil { - mlog.Printf("build error: \n%s%s", result, err.Error()) - return - } - // Kill the old process if build successfully. - if process != nil { - if err := process.Kill(); err != nil { - mlog.Debugf("kill process error: %s", err.Error()) - //return - } - } - // Run the binary file. - runCommand := fmt.Sprintf(`%s %s`, outputPath, app.Args) - mlog.Print(runCommand) - if runtime.GOOS == "windows" { - // Special handling for windows platform. - // DO NOT USE "cmd /c" command. - process = gproc.NewProcess(runCommand, nil) - } else { - process = gproc.NewProcessCmd(runCommand, nil) - } - if pid, err := process.Start(); err != nil { - mlog.Printf("build running error: %s", err.Error()) - } else { - mlog.Printf("build running pid: %d", pid) - } -} diff --git a/tool/gf/commands/swagger/swagger.go b/tool/gf/commands/swagger/swagger.go deleted file mode 100644 index 2993f200c..000000000 --- a/tool/gf/commands/swagger/swagger.go +++ /dev/null @@ -1,154 +0,0 @@ -package swagger - -import ( - "errors" - "fmt" - "github.com/gogf/gf/container/gtype" - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/gfsnotify" - "github.com/gogf/gf/os/gproc" - "github.com/gogf/gf/os/gtime" - "github.com/gogf/gf/os/gtimer" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/mlog" - "github.com/gogf/swagger" -) - -const ( - defaultOutput = "./swagger" - swaggoRepoPath = "github.com/swaggo/swag/cmd/swag" - PackedGoFileName = "swagger.go" -) - -func Help() { - mlog.Print(gstr.TrimLeft(` -USAGE - gf swagger [OPTION] - -OPTION - -s, --server start a swagger server at specified address after swagger files - produced - -o, --output the output directory for storage parsed swagger files, - the default output directory is "./swagger" - -/--pack auto parses and packs swagger into packed/swagger.go. - -EXAMPLES - gf swagger - gf swagger --pack - gf swagger -s 8080 - gf swagger -s 127.0.0.1:8080 - gf swagger -o ./document/swagger - - -DESCRIPTION - The "swagger" command parses the current project and produces swagger API description - files, which can be used in swagger API server. If used with "-s/--server" option, it - watches the changes of go files of current project and reproduces the swagger files, - which is quite convenient for local API development. - If it fails in command "swag", please firstly check your system PATH whether containing - go binary path, or you can install the "swag" tool manually referring to: - https://github.com/swaggo/swag -`)) -} - -func Run() { - mlog.SetHeaderPrint(true) - parser, err := gcmd.Parse(g.MapStrBool{ - "s,server": true, - "o,output": true, - "pack": false, - }) - if err != nil { - mlog.Fatal(err) - } - server := parser.GetOpt("server") - output := parser.GetOpt("output", defaultOutput) - // Generate swagger files. - if err := generateSwaggerFiles(output, parser.ContainsOpt("pack")); err != nil { - mlog.Print(err) - } - // Watch the go file changes and regenerate the swagger files. - dirty := gtype.NewBool() - _, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) { - if gfile.ExtName(event.Path) != "go" || gstr.Contains(event.Path, "swagger") { - return - } - // Variable <dirty> is used for running the changes only one in one second. - if !dirty.Cas(false, true) { - return - } - // With some delay in case of multiple code changes in very short interval. - gtimer.SetTimeout(1500*gtime.MS, func() { - mlog.Printf(`go file changes: %s`, event.String()) - mlog.Print(`reproducing swagger files...`) - if err := generateSwaggerFiles(output, parser.ContainsOpt("pack")); err != nil { - mlog.Print(err) - } else { - mlog.Print(`done!`) - } - dirty.Set(false) - }) - }) - if err != nil { - mlog.Fatal(err) - } - // Swagger server starts. - if server != "" { - if gstr.IsNumeric(server) { - server = ":" + server - } - s := g.Server() - s.Plugin(&swagger.Swagger{}) - s.SetAddr(server) - s.Run() - } -} - -// generateSwaggerFiles generates necessary swagger files. -func generateSwaggerFiles(output string, pack bool) error { - mlog.Print(`producing swagger files...`) - // Temporary storing swagger files directory. - tempOutputPath := gfile.Join(gfile.TempDir(), "swagger") - if gfile.Exists(tempOutputPath) { - gfile.Remove(tempOutputPath) - } - gfile.Mkdir(tempOutputPath) - // Check and install swag tool. - swag := gproc.SearchBinary("swag") - if swag == "" { - err := gproc.ShellRun(fmt.Sprintf(`go get -u -v %s`, swaggoRepoPath)) - if err != nil { - return err - } - } - // Generate swagger files using swag. - command := fmt.Sprintf(`swag init -o %s`, tempOutputPath) - result, err := gproc.ShellExec(command) - if err != nil { - return errors.New(result + err.Error()) - } - if !gfile.Exists(gfile.Join(tempOutputPath, "swagger.json")) { - return errors.New("make swagger files failed") - } - if !gfile.Exists(output) { - gfile.Mkdir(output) - } - if err = gfile.CopyFile( - gfile.Join(tempOutputPath, "swagger.json"), - gfile.Join(output, "swagger.json"), - ); err != nil { - return err - } - mlog.Print(`done!`) - // Auto pack into go file. - if pack && gfile.Exists("swagger") { - packCmd := fmt.Sprintf(`gf pack %s packed/%s -n packed`, "swagger", PackedGoFileName) - mlog.Print(packCmd) - if err := gproc.ShellRun(packCmd); err != nil { - return err - } - } - return nil -} diff --git a/tool/gf/commands/update/update.go b/tool/gf/commands/update/update.go deleted file mode 100644 index 6ebc9eea0..000000000 --- a/tool/gf/commands/update/update.go +++ /dev/null @@ -1,25 +0,0 @@ -package update - -import ( - "github.com/gogf/gf/os/gproc" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/library/mlog" - "runtime" -) - -func Run() { - goBinPath := gproc.SearchBinary("go") - if goBinPath == "" { - mlog.Fatal(`"go" command not found, install it first to step further`) - } - var err error - if gstr.CompareVersionGo(runtime.Version(), "go1.16.0") >= 0 { - err = gproc.ShellRun(`go install github.com/gogf/gf/tool/gf@latest`) - } else { - err = gproc.ShellRun(`go install github.com/gogf/gf/tool/gf`) - } - if err != nil { - mlog.Fatalf(`gf binary installation failed: %+v`, err) - } - mlog.Print("gf binary is now updated to the latest version") -} diff --git a/tool/gf/config/url.toml b/tool/gf/config/url.toml deleted file mode 100644 index 6f965f328..000000000 --- a/tool/gf/config/url.toml +++ /dev/null @@ -1,17 +0,0 @@ -# gf pack config packed/config.go - - -# gf home site cdn. -[cdn] - url = "https://gfcdn.johng.cn" - -# home site url. -[home] - url = "https://goframe.org" - -# go proxy for gf get. -[proxy] - urls = [ - "https://goproxy.io/", - "https://goproxy.cn/" - ] \ No newline at end of file diff --git a/tool/gf/go.mod b/tool/gf/go.mod deleted file mode 100644 index 34e47d80b..000000000 --- a/tool/gf/go.mod +++ /dev/null @@ -1,15 +0,0 @@ -module github.com/gogf/gf/tool/gf - -go 1.11 - -require ( - github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e - github.com/gogf/gf v1.16.4 - github.com/gogf/swagger v1.3.0 - github.com/gorilla/websocket v1.4.2 // indirect - github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 // indirect - github.com/lib/pq v1.2.0 - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/olekukonko/tablewriter v0.0.5 - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect -) diff --git a/tool/gf/go.sum b/tool/gf/go.sum deleted file mode 100644 index 281fb6356..000000000 --- a/tool/gf/go.sum +++ /dev/null @@ -1,79 +0,0 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4= -github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e h1:LzwWXEScfcTu7vUZNlDDWDARoSGEtvlDKK2BYHowNeE= -github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gogf/gf v1.16.4 h1:1Y8/P1UMp9BmrtUn0wAg3g6ElRAO0R9QHCdUNt9Z5L4= -github.com/gogf/gf v1.16.4/go.mod h1:EjnxZXddTwfFoLPofDE3NokFWx+immofINtSyFCj280= -github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw= -github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8= -github.com/gogf/swagger v1.3.0 h1:McpIEwj2DXLF3/ZNN9h9LKza/fsjtHP54nMTYVSCQ74= -github.com/gogf/swagger v1.3.0/go.mod h1:VDpNntu8+8NTTJ3sLOZNPO1LUbfW/ZA84IFJhhrlpoM= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= -github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119 h1:h3iGUlU8HyW4baKd6D+h1mwOHnM2kwskSuG6Bv4tSbc= -github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng= -go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= -go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg= -go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= -go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q= -go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= -go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc= -go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -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.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -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/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tool/gf/library/allyes/allyes.go b/tool/gf/library/allyes/allyes.go deleted file mode 100644 index e71c51f79..000000000 --- a/tool/gf/library/allyes/allyes.go +++ /dev/null @@ -1,22 +0,0 @@ -package allyes - -import ( - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/genv" -) - -const ( - EnvName = "GF_CLI_ALL_YES" -) - -// Init initializes the package manually. -func Init() { - if gcmd.ContainsOpt("y") { - genv.Set(EnvName, "1") - } -} - -// Check checks whether option allow all yes for command. -func Check() bool { - return genv.Get(EnvName) == "1" -} diff --git a/tool/gf/library/mlog/mlog.go b/tool/gf/library/mlog/mlog.go deleted file mode 100644 index 1f4c4fe7e..000000000 --- a/tool/gf/library/mlog/mlog.go +++ /dev/null @@ -1,63 +0,0 @@ -package mlog - -import ( - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/genv" - "github.com/gogf/gf/os/glog" -) - -const ( - headerPrintEnvName = "GF_CLI_MLOG_HEADER" -) - -var ( - logger = glog.New() -) - -func init() { - logger.SetStack(false) - logger.SetDebug(false) - if genv.Get(headerPrintEnvName) == "1" { - logger.SetHeaderPrint(true) - } else { - logger.SetHeaderPrint(false) - } - if gcmd.ContainsOpt("debug") { - logger.SetDebug(true) - } -} - -// SetHeaderPrint enables/disables header printing to stdout. -func SetHeaderPrint(enabled bool) { - logger.SetHeaderPrint(enabled) - if enabled { - genv.Set(headerPrintEnvName, "1") - } else { - genv.Set(headerPrintEnvName, "0") - } -} - -func Print(v ...interface{}) { - logger.Print(v...) -} - -func Printf(format string, v ...interface{}) { - logger.Printf(format, v...) -} - -func Fatal(v ...interface{}) { - logger.Fatal(append(g.Slice{"Error:"}, v...)...) -} - -func Fatalf(format string, v ...interface{}) { - logger.Fatalf("Error: "+format, v...) -} - -func Debug(v ...interface{}) { - logger.Debug(append(g.Slice{"Debug:"}, v...)...) -} - -func Debugf(format string, v ...interface{}) { - logger.Debugf("Debug: "+format, v...) -} diff --git a/tool/gf/library/proxy/proxy.go b/tool/gf/library/proxy/proxy.go deleted file mode 100644 index bcaba6ea3..000000000 --- a/tool/gf/library/proxy/proxy.go +++ /dev/null @@ -1,33 +0,0 @@ -package proxy - -import ( - "github.com/gogf/gf/net/ghttp" - "github.com/gogf/gf/os/genv" - "github.com/gogf/gf/tool/gf/library/mlog" - "time" -) - -var ( - httpClient = ghttp.NewClient() -) - -func init() { - httpClient.SetTimeout(time.Second) -} - -// AutoSet automatically checks and sets the golang proxy. -func AutoSet() { - SetGoModuleEnabled(true) - genv.Set("GOPROXY", "https://goproxy.cn") -} - -// SetGoModuleEnabled enables/disables the go module feature. -func SetGoModuleEnabled(enabled bool) { - if enabled { - mlog.Debug("set GO111MODULE=on") - genv.Set("GO111MODULE", "on") - } else { - mlog.Debug("set GO111MODULE=off") - genv.Set("GO111MODULE", "off") - } -} diff --git a/tool/gf/library/utils/utils.go b/tool/gf/library/utils/utils.go deleted file mode 100644 index ce29dd738..000000000 --- a/tool/gf/library/utils/utils.go +++ /dev/null @@ -1,18 +0,0 @@ -package utils - -import ( - "fmt" - "github.com/gogf/gf/os/gproc" -) - -var ( - // gofmtPath is the binary path of command `gofmt`. - gofmtPath = gproc.SearchBinaryPath("gofmt") -) - -// GoFmt formats the source file using command `gofmt -w -s PATH`. -func GoFmt(path string) { - if gofmtPath != "" { - gproc.ShellExec(fmt.Sprintf(`%s -w -s %s`, gofmtPath, path)) - } -} diff --git a/tool/gf/main.go b/tool/gf/main.go deleted file mode 100644 index 276691ebc..000000000 --- a/tool/gf/main.go +++ /dev/null @@ -1,187 +0,0 @@ -package main - -import ( - _ "github.com/gogf/gf/tool/gf/boot" - - "github.com/gogf/gf" - "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/os/gbuild" - "github.com/gogf/gf/os/gcmd" - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/text/gregex" - "github.com/gogf/gf/text/gstr" - "github.com/gogf/gf/tool/gf/commands/build" - "github.com/gogf/gf/tool/gf/commands/docker" - "github.com/gogf/gf/tool/gf/commands/env" - "github.com/gogf/gf/tool/gf/commands/fix" - "github.com/gogf/gf/tool/gf/commands/gen" - "github.com/gogf/gf/tool/gf/commands/get" - "github.com/gogf/gf/tool/gf/commands/initialize" - "github.com/gogf/gf/tool/gf/commands/mod" - "github.com/gogf/gf/tool/gf/commands/pack" - "github.com/gogf/gf/tool/gf/commands/run" - "github.com/gogf/gf/tool/gf/commands/swagger" - "github.com/gogf/gf/tool/gf/commands/update" - "github.com/gogf/gf/tool/gf/library/allyes" - "github.com/gogf/gf/tool/gf/library/mlog" - "github.com/gogf/gf/tool/gf/library/proxy" -) - -func init() { - // Automatically sets the golang proxy for all commands. - proxy.AutoSet() -} - -var ( - helpContent = gstr.TrimLeft(` -USAGE - gf COMMAND [ARGUMENT] [OPTION] - -COMMAND - env show current Golang environment variables - get install or update GF to system in default... - gen automatically generate go files for ORM models... - mod extra features for go modules... - run running go codes with hot-compiled-like feature... - init create and initialize an empty GF project... - help show more information about a specified command - pack packing any file/directory to a resource file, or a go file... - build cross-building go project for lots of platforms... - docker create a docker image for current GF project... - swagger swagger feature for current project... - update update current gf binary to latest one - version show current binary version info - -OPTION - -y all yes for all command without prompt ask - -?,-h show this help or detail for specified command - -v,-i show version information - -ADDITIONAL - Use 'gf help COMMAND' or 'gf COMMAND -h' for detail about a command, which has '...' - in the tail of their comments. -`) -) - -func main() { - defer func() { - if exception := recover(); exception != nil { - if err, ok := exception.(error); ok { - mlog.Print(gerror.Current(err).Error()) - } else { - panic(exception) - } - } - }() - - allyes.Init() - - command := gcmd.GetArg(1) - // Help information - if gcmd.ContainsOpt("h") && command != "" { - help(command) - return - } - switch command { - case "help": - help(gcmd.GetArg(2)) - case "version": - version() - case "env": - env.Run() - case "get": - get.Run() - case "gen": - gen.Run() - case "fix": - fix.Run() - case "mod": - mod.Run() - case "init": - initialize.Run() - case "pack": - pack.Run() - case "docker": - docker.Run() - case "swagger": - swagger.Run() - case "update": - update.Run() - case "build": - build.Run() - case "run": - run.Run() - default: - for k := range gcmd.GetOptAll() { - switch k { - case "?", "h": - mlog.Print(helpContent) - return - case "i", "v": - version() - return - } - } - mlog.Print(helpContent) - } -} - -// help shows more information for specified command. -func help(command string) { - switch command { - case "get": - get.Help() - case "gen": - gen.Help() - case "init": - initialize.Help() - case "docker": - docker.Help() - case "swagger": - swagger.Help() - case "build": - build.Help() - case "pack": - pack.Help() - case "run": - run.Help() - case "mod": - mod.Help() - default: - mlog.Print(helpContent) - } -} - -// version prints the version information of the cli tool. -func version() { - info := gbuild.Info() - if info["git"] == "" { - info["git"] = "none" - } - mlog.Printf(`GoFrame CLI Tool %s, https://goframe.org`, gf.VERSION) - gfVersion, err := getGFVersionOfCurrentProject() - if err != nil { - gfVersion = err.Error() - } else { - gfVersion = gfVersion + " in current go.mod" - } - mlog.Printf(`GoFrame Version: %s`, gfVersion) - mlog.Printf(`CLI Installed At: %s`, gfile.SelfPath()) -} - -// getGFVersionOfCurrentProject checks and returns the GoFrame version current project using. -func getGFVersionOfCurrentProject() (string, error) { - goModPath := gfile.Join(gfile.Pwd(), "go.mod") - if gfile.Exists(goModPath) { - match, err := gregex.MatchString(`github.com/gogf/gf\s+([\w\d\.]+)`, gfile.GetContents(goModPath)) - if err != nil { - return "", err - } - if len(match) > 1 { - return match[1], nil - } - return "", gerror.New("cannot find goframe requirement in go.mod") - } else { - return "", gerror.New("cannot find go.mod in current directory") - } -} diff --git a/tool/gf/packed/config.go b/tool/gf/packed/config.go deleted file mode 100644 index f066cc66e..000000000 --- a/tool/gf/packed/config.go +++ /dev/null @@ -1,9 +0,0 @@ -package packed - -import "github.com/gogf/gf/os/gres" - -func init() { - if err := gres.Add("H4sIAAAAAAAC/wrwZmYRYeBg4GBIi7kQwIAE+Bk4GZLz89Iy0/VLi3L0SvJzc0JDWBkYC9a9iMvpc+xrNuBxvf5DJH7m2xM3+494KolpqLRpHtFgWbm3dmpI1u6E5+//z3sq73HE7pf0nmU7PGYfTonvdO1cYbg5j/F4ENOfKTPmzL/kwFDwTNT2TMypuj+R2hu//bdST5zvs9X+1cxwtfDPW1cb9kcnnL3kEvgm8j2/y9mv7JytJ7devW1/4XDg9VrZTAb9rLPvQx9ONr3Ce56BgeH//wBvdo4D12YyTmNgYPjFwMAA8xsDw+EMwUBkv7HB/Qb20v4Gq3iQZmQlAd6MTCLMiKBBNhgUNDCwpBFE4goohCnYHQEBAgz/He/ATUFyEisbSJqJgYmhmYGBQZIRxAMEAAD//5uozoWyAQAA"); err != nil { - panic("add binary content to resource manager failed: " + err.Error()) - } -} From e5ae1cb85c69fff94a30faafdea9e052b7f10681 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 6 Jul 2021 09:53:35 +0800 Subject: [PATCH 364/492] improve session variable map in template parsing --- container/gvar/gvar_map.go | 2 +- net/ghttp/ghttp_response_view.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/container/gvar/gvar_map.go b/container/gvar/gvar_map.go index 090dd372a..00a751a23 100644 --- a/container/gvar/gvar_map.go +++ b/container/gvar/gvar_map.go @@ -41,7 +41,7 @@ func (v *Var) MapDeep(tags ...string) map[string]interface{} { return gconv.MapDeep(v.Val(), tags...) } -// MapDeep converts and returns <v> as map[string]string recursively. +// MapStrStrDeep converts and returns <v> as map[string]string recursively. func (v *Var) MapStrStrDeep(tags ...string) map[string]string { return gconv.MapStrStrDeep(v.Val(), tags...) } diff --git a/net/ghttp/ghttp_response_view.go b/net/ghttp/ghttp_response_view.go index 194d04c0d..04f14a072 100644 --- a/net/ghttp/ghttp_response_view.go +++ b/net/ghttp/ghttp_response_view.go @@ -10,6 +10,7 @@ package ghttp import ( "github.com/gogf/gf/os/gcfg" "github.com/gogf/gf/os/gview" + "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gmode" "github.com/gogf/gf/util/gutil" ) @@ -62,7 +63,7 @@ func (r *Response) ParseTpl(tpl string, params ...gview.Params) (string, error) return r.Request.GetView().Parse(r.Request.Context(), tpl, r.buildInVars(params...)) } -// ParseDefault parses the default template file with params. +// ParseTplDefault parses the default template file with params. func (r *Response) ParseTplDefault(params ...gview.Params) (string, error) { return r.Request.GetView().ParseDefault(r.Request.Context(), r.buildInVars(params...)) } @@ -86,7 +87,7 @@ func (r *Response) buildInVars(params ...map[string]interface{}) map[string]inte "Query": r.Request.GetQueryMap(), "Request": r.Request.GetMap(), "Cookie": r.Request.Cookie.Map(), - "Session": r.Request.Session.Map(), + "Session": gconv.MapDeep(r.Request.Session.Map()), }) // Note that it should assign no Config variable to template // if there's no configuration file. From 7e2605188d8cea718ce3615283a94a9216b6311e Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 6 Jul 2021 09:58:25 +0800 Subject: [PATCH 365/492] improve session variable map in template parsing --- net/ghttp/ghttp_response_view.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/ghttp/ghttp_response_view.go b/net/ghttp/ghttp_response_view.go index 04f14a072..47da241dc 100644 --- a/net/ghttp/ghttp_response_view.go +++ b/net/ghttp/ghttp_response_view.go @@ -82,17 +82,18 @@ func (r *Response) buildInVars(params ...map[string]interface{}) map[string]inte gutil.MapMerge(m, params[0]) } // Retrieve custom template variables from request object. + sessionMap := gconv.MapDeep(r.Request.Session.Map()) gutil.MapMerge(m, map[string]interface{}{ "Form": r.Request.GetFormMap(), "Query": r.Request.GetQueryMap(), "Request": r.Request.GetMap(), "Cookie": r.Request.Cookie.Map(), - "Session": gconv.MapDeep(r.Request.Session.Map()), + "Session": sessionMap, }) // Note that it should assign no Config variable to template // if there's no configuration file. if c := gcfg.Instance(); c.Available() { - m["Config"] = c.GetMap(".") + m["Config"] = c.Map() } return m } From 50e5dd5bd0c8b091f54d2d11510512b9748c4a83 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 6 Jul 2021 13:14:33 +0800 Subject: [PATCH 366/492] fix issue in fields filtering for table name with as statement --- database/gdb/gdb_model.go | 2 +- database/gdb/gdb_model_fields.go | 4 ++-- database/gdb/gdb_model_select.go | 2 +- database/gdb/gdb_model_utility.go | 4 ++-- database/gdb/gdb_model_with.go | 5 ++++- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 245398260..909103bf7 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -206,7 +206,7 @@ func (m *Model) As(as string) *Model { if m.tables != "" { model := m.getModel() split := " JOIN " - if gstr.Contains(model.tables, split) { + if gstr.ContainsI(model.tables, split) { // For join table. array := gstr.Split(model.tables, split) array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1]) diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index 46d7af223..88d93ce8e 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -96,7 +96,7 @@ func (m *Model) GetFieldsStr(prefix ...string) string { if len(prefix) > 0 { prefixStr = prefix[0] } - tableFields, err := m.TableFields(m.tables) + tableFields, err := m.TableFields(m.tablesInit) if err != nil { panic(err) } @@ -164,7 +164,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string { // HasField determine whether the field exists in the table. func (m *Model) HasField(field string) (bool, error) { - tableFields, err := m.TableFields(m.tables) + tableFields, err := m.TableFields(m.tablesInit) if err != nil { return false, err } diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index cd39c4332..5da09222a 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -76,7 +76,7 @@ func (m *Model) getFieldsFiltered() string { panic("function FieldsEx supports only single table operations") } // Filter table fields with fieldEx. - tableFields, err := m.TableFields(m.tables) + tableFields, err := m.TableFields(m.tablesInit) if err != nil { panic(err) } diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 7b1c36b60..4c6823850 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -47,7 +47,7 @@ func (m *Model) getModel() *Model { // ID -> id // NICK_Name -> nickname func (m *Model) mappingAndFilterToTableFields(fields []string, filter bool) []string { - fieldsMap, err := m.TableFields(m.tables) + fieldsMap, err := m.TableFields(m.tablesInit) if err != nil || len(fieldsMap) == 0 { return fields } @@ -201,7 +201,7 @@ func (m *Model) getLink(master bool) Link { // It parses m.tables to retrieve the primary table name, supporting m.tables like: // "user", "user u", "user as u, user_detail as ud". func (m *Model) getPrimaryKey() string { - table := gstr.SplitAndTrim(m.tables, " ")[0] + table := gstr.SplitAndTrim(m.tablesInit, " ")[0] tableFields, err := m.TableFields(table) if err != nil { return "" diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 1fbd891d3..ea30abd39 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -39,7 +39,10 @@ func (m *Model) With(objects ...interface{}) *Model { model := m.getModel() for _, object := range objects { if m.tables == "" { - m.tables = m.db.GetCore().QuotePrefixTableName(getTableNameFromOrmTag(object)) + m.tablesInit = m.db.GetCore().QuotePrefixTableName( + getTableNameFromOrmTag(object), + ) + m.tables = m.tablesInit return model } model.withArray = append(model.withArray, object) From 8e76d7a8edfbd9906b756ccae21a0fec11597838 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 6 Jul 2021 20:59:09 +0800 Subject: [PATCH 367/492] improve function TableFileds for gussing table name from table string;add overwrote function DoInsert for mssql/pgsql/oracle/sqlite for unsupported features --- database/gdb/gdb_core.go | 2 +- database/gdb/gdb_driver_mssql.go | 14 ++++++++++++++ database/gdb/gdb_driver_oracle.go | 20 ++++++++++++++++++++ database/gdb/gdb_driver_pgsql.go | 14 ++++++++++++++ database/gdb/gdb_driver_sqlite.go | 14 ++++++++++++++ database/gdb/gdb_func.go | 25 +++++++++++++++++++++++++ database/gdb/gdb_model_fields.go | 2 +- database/gdb/gdb_model_time.go | 20 +++----------------- database/gdb/gdb_model_utility.go | 13 +++++-------- 9 files changed, 97 insertions(+), 27 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index ab11e9b66..675dc809f 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -353,7 +353,7 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e return c.Model(table).Data(data).Save() } -// DoInsert inserts or updates data for given table. +// DoInsert inserts or updates data forF given table. // This function is usually used for custom interface definition, you do not need call it manually. // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. // Eg: diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index 24156605c..f56c2f966 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -287,3 +287,17 @@ ORDER BY a.id,a.colorder`, } return } + +// DoInsert is not supported in mssql. +func (d *DriverMssql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { + switch option.InsertOption { + case insertOptionSave: + return nil, gerror.New(`Save operation is not supported by mssql driver`) + + case insertOptionReplace: + return nil, gerror.New(`Replace operation is not supported by mssql driver`) + + default: + return d.Core.DoInsert(ctx, link, table, list, option) + } +} diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index 8c175f228..7ea5fcd54 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -263,7 +263,27 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[ return } +// DoInsert inserts or updates data for given table. +// This function is usually used for custom interface definition, you do not need call it manually. +// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. +// Eg: +// Data(g.Map{"uid": 10000, "name":"john"}) +// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) +// +// The parameter `option` values are as follows: +// 0: insert: just insert, if there's unique/primary key in the data, it returns error; +// 1: replace: if there's unique/primary key in the data, it deletes it from table and inserts a new one; +// 2: save: if there's unique/primary key in the data, it updates it or else inserts a new one; +// 3: ignore: if there's unique/primary key in the data, it ignores the inserting; func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { + switch option.InsertOption { + case insertOptionSave: + return nil, gerror.New(`Save operation is not supported by mssql driver`) + + case insertOptionReplace: + return nil, gerror.New(`Replace operation is not supported by mssql driver`) + } + var ( keys []string values []string diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 6778ed760..961242426 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -172,3 +172,17 @@ ORDER BY a.attnum`, } return } + +// DoInsert is not supported in pgsql. +func (d *DriverPgsql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { + switch option.InsertOption { + case insertOptionSave: + return nil, gerror.New(`Save operation is not supported by pgsql driver`) + + case insertOptionReplace: + return nil, gerror.New(`Replace operation is not supported by pgsql driver`) + + default: + return d.Core.DoInsert(ctx, link, table, list, option) + } +} diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index 1ad0f68b3..132fe5c32 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -136,3 +136,17 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ... } return } + +// DoInsert is not supported in sqlite. +func (d *DriverSqlite) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { + switch option.InsertOption { + case insertOptionSave: + return nil, gerror.New(`Save operation is not supported by sqlite driver`) + + case insertOptionReplace: + return nil, gerror.New(`Replace operation is not supported by sqlite driver`) + + default: + return d.Core.DoInsert(ctx, link, table, list, option) + } +} diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index e2a0b4c17..9d9b35899 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -70,6 +70,31 @@ var ( structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...) ) +// guessPrimaryTableName parses and returns the primary table name. +func (m *Model) guessPrimaryTableName(tableStr string) string { + if tableStr == "" { + return "" + } + var ( + guessedTableName = "" + array1 = gstr.SplitAndTrim(tableStr, ",") + array2 = gstr.SplitAndTrim(array1[0], " ") + array3 = gstr.SplitAndTrim(array2[0], ".") + ) + if len(array3) >= 2 { + guessedTableName = array3[1] + } + guessedTableName = array3[0] + charL, charR := m.db.GetChars() + if charL != "" || charR != "" { + guessedTableName = gstr.Trim(guessedTableName, charL+charR) + } + if !gregex.IsMatchString(regularFieldNameRegPattern, guessedTableName) { + return "" + } + return guessedTableName +} + // getTableNameFromOrmTag retrieves and returns the table name from struct object. func getTableNameFromOrmTag(object interface{}) string { var tableName string diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index 88d93ce8e..d959a0224 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -136,7 +136,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string { if len(prefix) > 0 { prefixStr = prefix[0] } - tableFields, err := m.TableFields(m.tables) + tableFields, err := m.TableFields(m.tablesInit) if err != nil { panic(err) } diff --git a/database/gdb/gdb_model_time.go b/database/gdb/gdb_model_time.go index 76c1f0667..0ca2a6877 100644 --- a/database/gdb/gdb_model_time.go +++ b/database/gdb/gdb_model_time.go @@ -40,7 +40,7 @@ func (m *Model) getSoftFieldNameCreated(table ...string) string { if len(table) > 0 { tableName = table[0] } else { - tableName = m.getPrimaryTableName() + tableName = m.tablesInit } config := m.db.GetConfig() if config.CreatedAt != "" { @@ -61,7 +61,7 @@ func (m *Model) getSoftFieldNameUpdated(table ...string) (field string) { if len(table) > 0 { tableName = table[0] } else { - tableName = m.getPrimaryTableName() + tableName = m.tablesInit } config := m.db.GetConfig() if config.UpdatedAt != "" { @@ -82,7 +82,7 @@ func (m *Model) getSoftFieldNameDeleted(table ...string) (field string) { if len(table) > 0 { tableName = table[0] } else { - tableName = m.getPrimaryTableName() + tableName = m.tablesInit } config := m.db.GetConfig() if config.UpdatedAt != "" { @@ -170,17 +170,3 @@ func (m *Model) getConditionOfTableStringForSoftDeleting(s string) string { } return fmt.Sprintf(`%s.%s IS NULL`, m.db.GetCore().QuoteWord(table), m.db.GetCore().QuoteWord(field)) } - -// getPrimaryTableName parses and returns the primary table name. -func (m *Model) getPrimaryTableName() string { - if m.tables == "" { - return "" - } - array1 := gstr.SplitAndTrim(m.tables, ",") - array2 := gstr.SplitAndTrim(array1[0], " ") - array3 := gstr.SplitAndTrim(array2[0], ".") - if len(array3) >= 2 { - return array3[1] - } - return array3[0] -} diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 4c6823850..03f5e1ed3 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -21,15 +21,12 @@ import ( // schema. // // Also see DriverMysql.TableFields. -func (m *Model) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) { - charL, charR := m.db.GetChars() - if charL != "" || charR != "" { - table = gstr.Trim(table, charL+charR) +func (m *Model) TableFields(tableStr string, schema ...string) (fields map[string]*TableField, err error) { + useSchema := m.schema + if len(schema) > 0 && schema[0] != "" { + useSchema = schema[0] } - if !gregex.IsMatchString(regularFieldNameRegPattern, table) { - return nil, nil - } - return m.db.TableFields(m.GetCtx(), table, schema...) + return m.db.TableFields(m.GetCtx(), m.guessPrimaryTableName(tableStr), useSchema) } // getModel creates and returns a cloned model of current model if `safe` is true, or else it returns From 2970864158bd5ae694659f5bed46d11275803b38 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 6 Jul 2021 21:18:26 +0800 Subject: [PATCH 368/492] fix issue #1240 --- database/gdb/gdb_func.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 9d9b35899..8a28250dd 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -264,7 +264,7 @@ func DataToMapDeep(value interface{}) map[string]interface{} { name = "" fieldTag = rtField.Tag for _, tag := range structTagPriority { - if s := fieldTag.Get(tag); s != "" && gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, s) { + if s := fieldTag.Get(tag); s != "" { name = s break } From 2a1634fd6fdb849957f1d3188b52e51d6167de31 Mon Sep 17 00:00:00 2001 From: Eagle <492617424@qq.com> Date: Thu, 8 Jul 2021 15:42:51 +0800 Subject: [PATCH 369/492] Update gdb_model_fields.go annotation typo fix --- database/gdb/gdb_model_fields.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index d959a0224..1d3b8a626 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -90,7 +90,7 @@ func (m *Model) FieldsStr(prefix ...string) string { } // GetFieldsStr retrieves and returns all fields from the table, joined with char ','. -// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsStr("u."). +// The optional parameter `prefix` specifies the prefix for each field, eg: GetFieldsStr("u."). func (m *Model) GetFieldsStr(prefix ...string) string { prefixStr := "" if len(prefix) > 0 { From 9df860a202871c40002f51775a1e5866ce542cce Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 8 Jul 2021 21:02:36 +0800 Subject: [PATCH 370/492] fix issue #1292 --- database/gdb/gdb_model_select.go | 4 +- database/gdb/gdb_model_with.go | 4 + database/gdb/gdb_type_result_scanlist.go | 88 +++++++--- .../gdb/gdb_z_mysql_association_with_test.go | 161 ++++++++++++++++++ .../gdb/testdata/with_multiple_depends.sql | 33 ++++ 5 files changed, 260 insertions(+), 30 deletions(-) create mode 100644 database/gdb/testdata/with_multiple_depends.sql diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 5da09222a..897eae92f 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -358,11 +358,11 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error { // parameter. // See the example or unit testing cases for clear understanding for this function. func (m *Model) ScanList(listPointer interface{}, attributeName string, relation ...string) (err error) { - all, err := m.All() + result, err := m.All() if err != nil { return err } - return all.ScanList(listPointer, attributeName, relation...) + return doScanList(m, result, listPointer, attributeName, relation...) } // Count does "SELECT COUNT(x) FROM ..." statement for the model. diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index ea30abd39..12ab46f55 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -166,6 +166,10 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { // doWithScanStructs handles model association operations feature for struct slice. // Also see doWithScanStruct. func (m *Model) doWithScanStructs(pointer interface{}) error { + if v, ok := pointer.(reflect.Value); ok { + pointer = v.Interface() + } + var ( err error allowedTypeStrArray = make([]string, 0) diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index 9eed29c13..18887471e 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -42,21 +42,21 @@ import ( // // See the example or unit testing cases for clear understanding for this function. func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) { - if r.IsEmpty() { + return doScanList(nil, r, listPointer, bindToAttrName, relationKV...) +} + +// doScanList converts `result` to struct slice which contains other complex struct attributes recursively. +// The parameter `model` is used for recursively scanning purpose, which means, it can scans the attribute struct/structs recursively but +// it needs the Model for database accessing. +// Note that the parameter `listPointer` should be type of *[]struct/*[]*struct. +func doScanList(model *Model, result Result, listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) { + if result.IsEmpty() { return nil } // Necessary checks for parameters. if bindToAttrName == "" { return gerror.New(`bindToAttrName should not be empty`) } - //if len(relation) > 0 { - // if len(relation) < 2 { - // return gerror.New(`relation name and key should are both necessary`) - // } - // if relation[0] == "" || relation[1] == "" { - // return gerror.New(`relation name and key should not be empty`) - // } - //} var ( reflectValue = reflect.ValueOf(listPointer) @@ -67,14 +67,14 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio reflectKind = reflectValue.Kind() } if reflectKind != reflect.Ptr { - return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) + return gerror.Newf("listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind) } reflectValue = reflectValue.Elem() reflectKind = reflectValue.Kind() if reflectKind != reflect.Slice && reflectKind != reflect.Array { - return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) + return gerror.Newf("listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind) } - length := len(r) + length := len(result) if length == 0 { // The pointed slice is not empty. if reflectValue.Len() > 0 { @@ -134,7 +134,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // uid:UserId relationResultFieldName = array[0] relationBindToSubAttrName = array[1] - if key, _ := gutil.MapPossibleItemByKey(r[0].Map(), relationResultFieldName); key == "" { + if key, _ := gutil.MapPossibleItemByKey(result[0].Map(), relationResultFieldName); key == "" { return gerror.Newf( `cannot find possible related table field name "%s" from given relation key "%s"`, relationResultFieldName, @@ -147,7 +147,8 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`) } if relationResultFieldName != "" { - relationDataMap = r.MapKeyValue(relationResultFieldName) + // Note that the value might be type of slice. + relationDataMap = result.MapKeyValue(relationResultFieldName) } if len(relationDataMap) == 0 { return gerror.Newf(`cannot find the relation data map, maybe invalid relation given "%v"`, relationKV) @@ -239,12 +240,19 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio if len(relationDataMap) > 0 { relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) if relationFromAttrField.IsValid() { - if err = gconv.Structs( - relationDataMap[gconv.String(relationFromAttrField.Interface())], - bindToAttrValue.Addr(), - ); err != nil { + results := make(Result, 0) + for _, v := range relationDataMap[gconv.String(relationFromAttrField.Interface())].Slice() { + results = append(results, v.(Record)) + } + if err = results.Structs(bindToAttrValue.Addr()); err != nil { return err } + // Recursively Scan. + if model != nil { + if err = model.doWithScanStructs(bindToAttrValue.Addr()); err != nil { + return nil + } + } } else { // May be the attribute does not exist yet. return gerror.Newf(`invalid relation specified: "%v"`, relationKV) @@ -268,24 +276,36 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // There's no relational data. continue } - if err = gconv.Struct(v, element); err != nil { - return err + if v.IsSlice() { + if err = v.Slice()[0].(Record).Struct(element); err != nil { + return err + } + } else { + if err = v.Val().(Record).Struct(element); err != nil { + return err + } } } else { // May be the attribute does not exist yet. return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } } else { - if i >= len(r) { + if i >= len(result) { // There's no relational data. continue } - v := r[i] + v := result[i] if v == nil { // There's no relational data. continue } - if err = gconv.Struct(v, element); err != nil { + if err = v.Struct(element); err != nil { + return err + } + } + // Recursively Scan. + if model != nil { + if err = model.doWithScanStruct(element); err != nil { return err } } @@ -300,24 +320,36 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // There's no relational data. continue } - if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil { - return err + if relationDataItem.IsSlice() { + if err = relationDataItem.Slice()[0].(Record).Struct(bindToAttrValue); err != nil { + return err + } + } else { + if err = relationDataItem.Val().(Record).Struct(bindToAttrValue); err != nil { + return err + } } } else { // May be the attribute does not exist yet. return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } } else { - if i >= len(r) { + if i >= len(result) { // There's no relational data. continue } - relationDataItem := r[i] + relationDataItem := result[i] if relationDataItem == nil { // There's no relational data. continue } - if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil { + if err = relationDataItem.Struct(bindToAttrValue); err != nil { + return err + } + } + // Recursively Scan. + if model != nil { + if err = model.doWithScanStruct(bindToAttrValue); err != nil { return err } } diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index 43a0f3ab7..8413b0440 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -8,8 +8,11 @@ package gdb_test import ( "fmt" + "github.com/gogf/gf/debug/gdebug" "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/test/gtest" + "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gmeta" "testing" ) @@ -1189,3 +1192,161 @@ PRIMARY KEY (id) t.Assert(user.UserDetail.UserScores[4].Score, 5) }) } + +func Test_Table_Relation_With_MultipleDepends1(t *testing.T) { + defer func() { + dropTable("table_a") + dropTable("table_b") + dropTable("table_c") + }() + for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") { + if _, err := db.Exec(v); err != nil { + gtest.Error(err) + } + } + + type TableC struct { + gmeta.Meta `orm:"table_c"` + Id int `orm:"id,primary" json:"id"` + TableBId int `orm:"table_b_id" json:"table_b_id"` + } + + type TableB struct { + gmeta.Meta `orm:"table_b"` + Id int `orm:"id,primary" json:"id"` + TableAId int `orm:"table_a_id" json:"table_a_id"` + TableC *TableC `orm:"with:table_b_id=id" json:"table_c"` + } + + type TableA struct { + gmeta.Meta `orm:"table_a"` + Id int `orm:"id,primary" json:"id"` + TableB *TableB `orm:"with:table_a_id=id" json:"table_b"` + } + + db.SetDebug(true) + defer db.SetDebug(false) + + // Struct. + gtest.C(t, func(t *gtest.T) { + var tableA *TableA + err := db.Model("table_a").WithAll().Scan(&tableA) + //g.Dump(tableA) + t.AssertNil(err) + t.AssertNE(tableA, nil) + t.Assert(tableA.Id, 1) + + t.AssertNE(tableA.TableB, nil) + t.AssertNE(tableA.TableB.TableC, nil) + t.Assert(tableA.TableB.TableAId, 1) + t.Assert(tableA.TableB.TableC.Id, 100) + t.Assert(tableA.TableB.TableC.TableBId, 10) + }) + + // Structs + gtest.C(t, func(t *gtest.T) { + var tableA []*TableA + err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA) + //g.Dump(tableA) + t.AssertNil(err) + t.Assert(len(tableA), 2) + t.AssertNE(tableA[0].TableB, nil) + t.AssertNE(tableA[1].TableB, nil) + t.AssertNE(tableA[0].TableB.TableC, nil) + t.AssertNE(tableA[1].TableB.TableC, nil) + + t.Assert(tableA[0].Id, 1) + t.Assert(tableA[0].TableB.Id, 10) + t.Assert(tableA[0].TableB.TableC.Id, 100) + + t.Assert(tableA[1].Id, 2) + t.Assert(tableA[1].TableB.Id, 20) + t.Assert(tableA[1].TableB.TableC.Id, 300) + }) +} +func Test_Table_Relation_With_MultipleDepends2(t *testing.T) { + defer func() { + dropTable("table_a") + dropTable("table_b") + dropTable("table_c") + }() + for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") { + if _, err := db.Exec(v); err != nil { + gtest.Error(err) + } + } + + type TableC struct { + gmeta.Meta `orm:"table_c"` + Id int `orm:"id,primary" json:"id"` + TableBId int `orm:"table_b_id" json:"table_b_id"` + } + + type TableB struct { + gmeta.Meta `orm:"table_b"` + Id int `orm:"id,primary" json:"id"` + TableAId int `orm:"table_a_id" json:"table_a_id"` + TableC []*TableC `orm:"with:table_b_id=id" json:"table_c"` + } + + type TableA struct { + gmeta.Meta `orm:"table_a"` + Id int `orm:"id,primary" json:"id"` + TableB []*TableB `orm:"with:table_a_id=id" json:"table_b"` + } + + db.SetDebug(true) + defer db.SetDebug(false) + + // Struct. + gtest.C(t, func(t *gtest.T) { + var tableA *TableA + err := db.Model("table_a").WithAll().Scan(&tableA) + //g.Dump(tableA) + t.AssertNil(err) + t.AssertNE(tableA, nil) + t.Assert(tableA.Id, 1) + + t.Assert(len(tableA.TableB), 2) + t.Assert(tableA.TableB[0].Id, 10) + t.Assert(tableA.TableB[1].Id, 30) + + t.Assert(len(tableA.TableB[0].TableC), 2) + t.Assert(len(tableA.TableB[1].TableC), 1) + t.Assert(tableA.TableB[0].TableC[0].Id, 100) + t.Assert(tableA.TableB[0].TableC[0].TableBId, 10) + t.Assert(tableA.TableB[0].TableC[1].Id, 200) + t.Assert(tableA.TableB[0].TableC[1].TableBId, 10) + t.Assert(tableA.TableB[1].TableC[0].Id, 400) + t.Assert(tableA.TableB[1].TableC[0].TableBId, 30) + }) + + // Structs + gtest.C(t, func(t *gtest.T) { + var tableA []*TableA + err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA) + //g.Dump(tableA) + t.AssertNil(err) + t.Assert(len(tableA), 2) + + t.Assert(len(tableA[0].TableB), 2) + t.Assert(tableA[0].TableB[0].Id, 10) + t.Assert(tableA[0].TableB[1].Id, 30) + + t.Assert(len(tableA[0].TableB[0].TableC), 2) + t.Assert(len(tableA[0].TableB[1].TableC), 1) + t.Assert(tableA[0].TableB[0].TableC[0].Id, 100) + t.Assert(tableA[0].TableB[0].TableC[0].TableBId, 10) + t.Assert(tableA[0].TableB[0].TableC[1].Id, 200) + t.Assert(tableA[0].TableB[0].TableC[1].TableBId, 10) + t.Assert(tableA[0].TableB[1].TableC[0].Id, 400) + t.Assert(tableA[0].TableB[1].TableC[0].TableBId, 30) + + t.Assert(tableA[1].TableB[0].TableC[0].Id, 300) + t.Assert(tableA[1].TableB[0].TableC[0].TableBId, 20) + + t.Assert(tableA[1].TableB[1].Id, 40) + t.Assert(tableA[1].TableB[1].TableAId, 2) + t.Assert(tableA[1].TableB[1].TableC, nil) + }) +} diff --git a/database/gdb/testdata/with_multiple_depends.sql b/database/gdb/testdata/with_multiple_depends.sql new file mode 100644 index 000000000..7980d7f53 --- /dev/null +++ b/database/gdb/testdata/with_multiple_depends.sql @@ -0,0 +1,33 @@ + +CREATE TABLE `table_a` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +INSERT INTO `table_a` VALUES (1, 'table_a_test1'); +INSERT INTO `table_a` VALUES (2, 'table_a_test2'); + +CREATE TABLE `table_b` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `table_a_id` int(11) NOT NULL, + `alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +INSERT INTO `table_b` VALUES (10, 1, 'table_b_test1'); +INSERT INTO `table_b` VALUES (20, 2, 'table_b_test2'); +INSERT INTO `table_b` VALUES (30, 1, 'table_b_test3'); +INSERT INTO `table_b` VALUES (40, 2, 'table_b_test4'); + +CREATE TABLE `table_c` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `table_b_id` int(11) NOT NULL, + `alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +INSERT INTO `table_c` VALUES (100, 10, 'table_c_test1'); +INSERT INTO `table_c` VALUES (200, 10, 'table_c_test2'); +INSERT INTO `table_c` VALUES (300, 20, 'table_c_test3'); +INSERT INTO `table_c` VALUES (400, 30, 'table_c_test4'); \ No newline at end of file From a63c4b644160372efc463b513cf7e27b0040fe80 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 8 Jul 2021 21:06:49 +0800 Subject: [PATCH 371/492] add more unit testing case for package gdb --- .../gdb/gdb_z_mysql_association_with_test.go | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index 8413b0440..2195ed00a 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -1350,3 +1350,74 @@ func Test_Table_Relation_With_MultipleDepends2(t *testing.T) { t.Assert(tableA[1].TableB[1].TableC, nil) }) } +func Test_Table_Relation_With_MultipleDepends_Embedded(t *testing.T) { + defer func() { + dropTable("table_a") + dropTable("table_b") + dropTable("table_c") + }() + for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") { + if _, err := db.Exec(v); err != nil { + gtest.Error(err) + } + } + + type TableC struct { + gmeta.Meta `orm:"table_c"` + Id int `orm:"id,primary" json:"id"` + TableBId int `orm:"table_b_id" json:"table_b_id"` + } + + type TableB struct { + gmeta.Meta `orm:"table_b"` + Id int `orm:"id,primary" json:"id"` + TableAId int `orm:"table_a_id" json:"table_a_id"` + *TableC `orm:"with:table_b_id=id" json:"table_c"` + } + + type TableA struct { + gmeta.Meta `orm:"table_a"` + Id int `orm:"id,primary" json:"id"` + *TableB `orm:"with:table_a_id=id" json:"table_b"` + } + + db.SetDebug(true) + defer db.SetDebug(false) + + // Struct. + gtest.C(t, func(t *gtest.T) { + var tableA *TableA + err := db.Model("table_a").WithAll().Scan(&tableA) + //g.Dump(tableA) + t.AssertNil(err) + t.AssertNE(tableA, nil) + t.Assert(tableA.Id, 1) + + t.AssertNE(tableA.TableB, nil) + t.AssertNE(tableA.TableB.TableC, nil) + t.Assert(tableA.TableB.TableAId, 1) + t.Assert(tableA.TableB.TableC.Id, 100) + t.Assert(tableA.TableB.TableC.TableBId, 10) + }) + + // Structs + gtest.C(t, func(t *gtest.T) { + var tableA []*TableA + err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA) + //g.Dump(tableA) + t.AssertNil(err) + t.Assert(len(tableA), 2) + t.AssertNE(tableA[0].TableB, nil) + t.AssertNE(tableA[1].TableB, nil) + t.AssertNE(tableA[0].TableB.TableC, nil) + t.AssertNE(tableA[1].TableB.TableC, nil) + + t.Assert(tableA[0].Id, 1) + t.Assert(tableA[0].TableB.Id, 10) + t.Assert(tableA[0].TableB.TableC.Id, 100) + + t.Assert(tableA[1].Id, 2) + t.Assert(tableA[1].TableB.Id, 20) + t.Assert(tableA[1].TableB.TableC.Id, 300) + }) +} From 2d319d0856c79c19bf64d313e37e226898b5d287 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 8 Jul 2021 21:46:21 +0800 Subject: [PATCH 372/492] update sql of unit testing cases for package gdb --- database/gdb/testdata/with_multiple_depends.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/database/gdb/testdata/with_multiple_depends.sql b/database/gdb/testdata/with_multiple_depends.sql index 7980d7f53..1ae9e8c5d 100644 --- a/database/gdb/testdata/with_multiple_depends.sql +++ b/database/gdb/testdata/with_multiple_depends.sql @@ -3,7 +3,7 @@ CREATE TABLE `table_a` ( `id` int(11) NOT NULL AUTO_INCREMENT, `alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB; INSERT INTO `table_a` VALUES (1, 'table_a_test1'); INSERT INTO `table_a` VALUES (2, 'table_a_test2'); @@ -13,7 +13,7 @@ CREATE TABLE `table_b` ( `table_a_id` int(11) NOT NULL, `alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB; INSERT INTO `table_b` VALUES (10, 1, 'table_b_test1'); INSERT INTO `table_b` VALUES (20, 2, 'table_b_test2'); @@ -25,7 +25,7 @@ CREATE TABLE `table_c` ( `table_b_id` int(11) NOT NULL, `alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; +) ENGINE = InnoDB; INSERT INTO `table_c` VALUES (100, 10, 'table_c_test1'); INSERT INTO `table_c` VALUES (200, 10, 'table_c_test2'); From 82ad7e2acc922acc1b8ad981db1e2da167d7fef3 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 8 Jul 2021 22:08:45 +0800 Subject: [PATCH 373/492] add more unit testing case for package gdb --- .../gdb/gdb_z_mysql_association_with_test.go | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index 2195ed00a..94827ac43 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -785,6 +785,122 @@ PRIMARY KEY (id) }) } +func Test_Table_Relation_WithAll_Embedded_WithoutMeta(t *testing.T) { + var ( + tableUser = "user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +uid int(10) unsigned NOT NULL AUTO_INCREMENT, +address varchar(45) NOT NULL, +PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +uid int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetailBase struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserDetail struct { + UserDetailBase + } + + type UserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + *UserDetail `orm:"with:uid=id"` + Id int `json:"id"` + Name string `json:"name"` + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} + func Test_Table_Relation_WithAll_AttributeStructAlsoHasWithTag(t *testing.T) { var ( tableUser = "user" From 5e92747737ba4c000e18ca99eb3b6e6ad706527b Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 8 Jul 2021 22:44:16 +0800 Subject: [PATCH 374/492] fix issue #1291 --- container/gvar/gvar_is.go | 81 +++------- database/gdb/gdb_result.go | 12 +- database/gdb/gdb_type_record.go | 5 + database/gdb/gdb_type_result.go | 5 + encoding/gjson/gjson.go | 13 ++ encoding/gjson/gjson_api.go | 6 + encoding/gjson/gjson_z_unit_basic_test.go | 14 ++ internal/utils/utils_is.go | 98 +++++++++++++ internal/utils/utils_z_is_test.go | 171 ++++++++++++++++++++++ 9 files changed, 339 insertions(+), 66 deletions(-) create mode 100644 internal/utils/utils_is.go create mode 100644 internal/utils/utils_z_is_test.go diff --git a/container/gvar/gvar_is.go b/container/gvar/gvar_is.go index a7d2f14c7..b7ac9af48 100644 --- a/container/gvar/gvar_is.go +++ b/container/gvar/gvar_is.go @@ -7,92 +7,45 @@ package gvar import ( - "github.com/gogf/gf/internal/empty" - "reflect" + "github.com/gogf/gf/internal/utils" ) -// IsNil checks whether <v> is nil. +// IsNil checks whether `v` is nil. func (v *Var) IsNil() bool { - return v.Val() == nil + return utils.IsNil(v.Val()) } -// IsEmpty checks whether <v> is empty. +// IsEmpty checks whether `v` is empty. func (v *Var) IsEmpty() bool { - return empty.IsEmpty(v.Val()) + return utils.IsEmpty(v.Val()) } -// IsInt checks whether <v> is type of int. +// IsInt checks whether `v` is type of int. func (v *Var) IsInt() bool { - switch v.Val().(type) { - case int, *int, int8, *int8, int16, *int16, int32, *int32, int64, *int64: - return true - } - return false + return utils.IsInt(v.Val()) } -// IsUint checks whether <v> is type of uint. +// IsUint checks whether `v` is type of uint. func (v *Var) IsUint() bool { - switch v.Val().(type) { - case uint, *uint, uint8, *uint8, uint16, *uint16, uint32, *uint32, uint64, *uint64: - return true - } - return false + return utils.IsUint(v.Val()) } -// IsFloat checks whether <v> is type of float. +// IsFloat checks whether `v` is type of float. func (v *Var) IsFloat() bool { - switch v.Val().(type) { - case float32, *float32, float64, *float64: - return true - } - return false + return utils.IsFloat(v.Val()) } -// IsSlice checks whether <v> is type of slice. +// IsSlice checks whether `v` is type of slice. func (v *Var) IsSlice() bool { - var ( - reflectValue = reflect.ValueOf(v.Val()) - reflectKind = reflectValue.Kind() - ) - for reflectKind == reflect.Ptr { - reflectValue = reflectValue.Elem() - } - switch reflectKind { - case reflect.Slice, reflect.Array: - return true - } - return false + return utils.IsSlice(v.Val()) } -// IsMap checks whether <v> is type of map. +// IsMap checks whether `v` is type of map. func (v *Var) IsMap() bool { - var ( - reflectValue = reflect.ValueOf(v.Val()) - reflectKind = reflectValue.Kind() - ) - for reflectKind == reflect.Ptr { - reflectValue = reflectValue.Elem() - } - switch reflectKind { - case reflect.Map: - return true - } - return false + return utils.IsMap(v.Val()) } -// IsStruct checks whether <v> is type of struct. +// IsStruct checks whether `v` is type of struct. func (v *Var) IsStruct() bool { - var ( - reflectValue = reflect.ValueOf(v.Val()) - reflectKind = reflectValue.Kind() - ) - for reflectKind == reflect.Ptr { - reflectValue = reflectValue.Elem() - reflectKind = reflectValue.Kind() - } - switch reflectKind { - case reflect.Struct: - return true - } - return false + return utils.IsStruct(v.Val()) } diff --git a/database/gdb/gdb_result.go b/database/gdb/gdb_result.go index acfbd3f1c..63134bb09 100644 --- a/database/gdb/gdb_result.go +++ b/database/gdb/gdb_result.go @@ -33,7 +33,10 @@ func (r *SqlResult) MustGetInsertId() int64 { return id } -// see sql.Result.RowsAffected +// RowsAffected returns the number of rows affected by an +// update, insert, or delete. Not every database or database +// driver may support this. +// Also See sql.Result. func (r *SqlResult) RowsAffected() (int64, error) { if r.affected > 0 { return r.affected, nil @@ -44,7 +47,12 @@ func (r *SqlResult) RowsAffected() (int64, error) { return r.result.RowsAffected() } -// see sql.Result.LastInsertId +// LastInsertId returns the integer generated by the database +// in response to a command. Typically this will be from an +// "auto increment" column when inserting a new row. Not all +// databases support this feature, and the syntax of such +// statements varies. +// Also See sql.Result. func (r *SqlResult) LastInsertId() (int64, error) { if r.result == nil { return 0, nil diff --git a/database/gdb/gdb_type_record.go b/database/gdb/gdb_type_record.go index a660ec4ef..eff35a1f4 100644 --- a/database/gdb/gdb_type_record.go +++ b/database/gdb/gdb_type_record.go @@ -14,6 +14,11 @@ import ( "github.com/gogf/gf/util/gconv" ) +// Interface converts and returns `r` as type of interface{}. +func (r Record) Interface() interface{} { + return r +} + // Json converts `r` to JSON format content. func (r Record) Json() string { content, _ := gparser.VarToJson(r.Map()) diff --git a/database/gdb/gdb_type_result.go b/database/gdb/gdb_type_result.go index 489e3bc47..dd2764880 100644 --- a/database/gdb/gdb_type_result.go +++ b/database/gdb/gdb_type_result.go @@ -13,6 +13,11 @@ import ( "math" ) +// Interface converts and returns `r` as type of interface{}. +func (r Result) Interface() interface{} { + return r +} + // IsEmpty checks and returns whether `r` is empty. func (r Result) IsEmpty() bool { return r.Len() == 0 diff --git a/encoding/gjson/gjson.go b/encoding/gjson/gjson.go index 573acce39..887b0f232 100644 --- a/encoding/gjson/gjson.go +++ b/encoding/gjson/gjson.go @@ -8,6 +8,7 @@ package gjson import ( + "github.com/gogf/gf/internal/utils" "reflect" "strconv" "strings" @@ -37,11 +38,23 @@ type Options struct { StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64. } +// apiInterface is used for type assert api for Interface(). +type apiInterface interface { + Interface() interface{} +} + // setValue sets <value> to <j> by <pattern>. // Note: // 1. If value is nil and removed is true, means deleting this value; // 2. It's quite complicated in hierarchical data search, node creating and data assignment; func (j *Json) setValue(pattern string, value interface{}, removed bool) error { + if value != nil { + if utils.IsStruct(value) { + if v, ok := value.(apiInterface); ok { + value = v.Interface() + } + } + } array := strings.Split(pattern, string(j.c)) length := len(array) value = j.convertValue(value) diff --git a/encoding/gjson/gjson_api.go b/encoding/gjson/gjson_api.go index a45f6a6de..642990201 100644 --- a/encoding/gjson/gjson_api.go +++ b/encoding/gjson/gjson_api.go @@ -18,7 +18,13 @@ import ( ) // Value returns the json value. +// Deprecated, use Interface instead. func (j *Json) Value() interface{} { + return j.Interface() +} + +// Interface returns the json value. +func (j *Json) Interface() interface{} { j.mu.RLock() defer j.mu.RUnlock() return *(j.p) diff --git a/encoding/gjson/gjson_z_unit_basic_test.go b/encoding/gjson/gjson_z_unit_basic_test.go index 35b9538f0..7013f2fbf 100644 --- a/encoding/gjson/gjson_z_unit_basic_test.go +++ b/encoding/gjson/gjson_z_unit_basic_test.go @@ -485,3 +485,17 @@ func TestJson_IsNil(t *testing.T) { t.Assert(j.IsNil(), true) }) } + +func TestJson_Set_With_Struct(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + v := gjson.New(g.Map{ + "user1": g.Map{"name": "user1"}, + "user2": g.Map{"name": "user2"}, + "user3": g.Map{"name": "user3"}, + }) + user1 := v.GetJson("user1") + user1.Set("id", 111) + v.Set("user1", user1) + t.Assert(v.Get("user1.id"), 111) + }) +} diff --git a/internal/utils/utils_is.go b/internal/utils/utils_is.go new file mode 100644 index 000000000..7e5757a8d --- /dev/null +++ b/internal/utils/utils_is.go @@ -0,0 +1,98 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package utils + +import ( + "github.com/gogf/gf/internal/empty" + "reflect" +) + +// IsNil checks whether `value` is nil. +func IsNil(value interface{}) bool { + return value == nil +} + +// IsEmpty checks whether `value` is empty. +func IsEmpty(value interface{}) bool { + return empty.IsEmpty(value) +} + +// IsInt checks whether `value` is type of int. +func IsInt(value interface{}) bool { + switch value.(type) { + case int, *int, int8, *int8, int16, *int16, int32, *int32, int64, *int64: + return true + } + return false +} + +// IsUint checks whether `value` is type of uint. +func IsUint(value interface{}) bool { + switch value.(type) { + case uint, *uint, uint8, *uint8, uint16, *uint16, uint32, *uint32, uint64, *uint64: + return true + } + return false +} + +// IsFloat checks whether `value` is type of float. +func IsFloat(value interface{}) bool { + switch value.(type) { + case float32, *float32, float64, *float64: + return true + } + return false +} + +// IsSlice checks whether `value` is type of slice. +func IsSlice(value interface{}) bool { + var ( + reflectValue = reflect.ValueOf(value) + reflectKind = reflectValue.Kind() + ) + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + } + switch reflectKind { + case reflect.Slice, reflect.Array: + return true + } + return false +} + +// IsMap checks whether `value` is type of map. +func IsMap(value interface{}) bool { + var ( + reflectValue = reflect.ValueOf(value) + reflectKind = reflectValue.Kind() + ) + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + } + switch reflectKind { + case reflect.Map: + return true + } + return false +} + +// IsStruct checks whether `value` is type of struct. +func IsStruct(value interface{}) bool { + var ( + reflectValue = reflect.ValueOf(value) + reflectKind = reflectValue.Kind() + ) + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + switch reflectKind { + case reflect.Struct: + return true + } + return false +} diff --git a/internal/utils/utils_z_is_test.go b/internal/utils/utils_z_is_test.go new file mode 100644 index 000000000..77df365c4 --- /dev/null +++ b/internal/utils/utils_z_is_test.go @@ -0,0 +1,171 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package utils_test + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/internal/utils" + "github.com/gogf/gf/test/gtest" + "testing" +) + +func TestVar_IsNil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsNil(0), false) + t.Assert(utils.IsNil(nil), true) + t.Assert(utils.IsNil(g.Map{}), false) + t.Assert(utils.IsNil(g.Slice{}), false) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsNil(1), false) + t.Assert(utils.IsNil(0.1), false) + t.Assert(utils.IsNil(g.Map{"k": "v"}), false) + t.Assert(utils.IsNil(g.Slice{0}), false) + }) +} + +func TestVar_IsEmpty(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsEmpty(0), true) + t.Assert(utils.IsEmpty(nil), true) + t.Assert(utils.IsEmpty(g.Map{}), true) + t.Assert(utils.IsEmpty(g.Slice{}), true) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsEmpty(1), false) + t.Assert(utils.IsEmpty(0.1), false) + t.Assert(utils.IsEmpty(g.Map{"k": "v"}), false) + t.Assert(utils.IsEmpty(g.Slice{0}), false) + }) +} + +func TestVar_IsInt(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsInt(0), true) + t.Assert(utils.IsInt(nil), false) + t.Assert(utils.IsInt(g.Map{}), false) + t.Assert(utils.IsInt(g.Slice{}), false) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsInt(1), true) + t.Assert(utils.IsInt(-1), true) + t.Assert(utils.IsInt(0.1), false) + t.Assert(utils.IsInt(g.Map{"k": "v"}), false) + t.Assert(utils.IsInt(g.Slice{0}), false) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsInt(int8(1)), true) + t.Assert(utils.IsInt(uint8(1)), false) + }) +} + +func TestVar_IsUint(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsUint(0), false) + t.Assert(utils.IsUint(nil), false) + t.Assert(utils.IsUint(g.Map{}), false) + t.Assert(utils.IsUint(g.Slice{}), false) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsUint(1), false) + t.Assert(utils.IsUint(-1), false) + t.Assert(utils.IsUint(0.1), false) + t.Assert(utils.IsUint(g.Map{"k": "v"}), false) + t.Assert(utils.IsUint(g.Slice{0}), false) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsUint(int8(1)), false) + t.Assert(utils.IsUint(uint8(1)), true) + }) +} + +func TestVar_IsFloat(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsFloat(0), false) + t.Assert(utils.IsFloat(nil), false) + t.Assert(utils.IsFloat(g.Map{}), false) + t.Assert(utils.IsFloat(g.Slice{}), false) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsFloat(1), false) + t.Assert(utils.IsFloat(-1), false) + t.Assert(utils.IsFloat(0.1), true) + t.Assert(utils.IsFloat(float64(1)), true) + t.Assert(utils.IsFloat(g.Map{"k": "v"}), false) + t.Assert(utils.IsFloat(g.Slice{0}), false) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsFloat(int8(1)), false) + t.Assert(utils.IsFloat(uint8(1)), false) + }) +} + +func TestVar_IsSlice(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsSlice(0), false) + t.Assert(utils.IsSlice(nil), false) + t.Assert(utils.IsSlice(g.Map{}), false) + t.Assert(utils.IsSlice(g.Slice{}), true) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsSlice(1), false) + t.Assert(utils.IsSlice(-1), false) + t.Assert(utils.IsSlice(0.1), false) + t.Assert(utils.IsSlice(float64(1)), false) + t.Assert(utils.IsSlice(g.Map{"k": "v"}), false) + t.Assert(utils.IsSlice(g.Slice{0}), true) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsSlice(int8(1)), false) + t.Assert(utils.IsSlice(uint8(1)), false) + }) +} + +func TestVar_IsMap(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsMap(0), false) + t.Assert(utils.IsMap(nil), false) + t.Assert(utils.IsMap(g.Map{}), true) + t.Assert(utils.IsMap(g.Slice{}), false) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsMap(1), false) + t.Assert(utils.IsMap(-1), false) + t.Assert(utils.IsMap(0.1), false) + t.Assert(utils.IsMap(float64(1)), false) + t.Assert(utils.IsMap(g.Map{"k": "v"}), true) + t.Assert(utils.IsMap(g.Slice{0}), false) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsMap(int8(1)), false) + t.Assert(utils.IsMap(uint8(1)), false) + }) +} + +func TestVar_IsStruct(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsStruct(0), false) + t.Assert(utils.IsStruct(nil), false) + t.Assert(utils.IsStruct(g.Map{}), false) + t.Assert(utils.IsStruct(g.Slice{}), false) + }) + gtest.C(t, func(t *gtest.T) { + t.Assert(utils.IsStruct(1), false) + t.Assert(utils.IsStruct(-1), false) + t.Assert(utils.IsStruct(0.1), false) + t.Assert(utils.IsStruct(float64(1)), false) + t.Assert(utils.IsStruct(g.Map{"k": "v"}), false) + t.Assert(utils.IsStruct(g.Slice{0}), false) + }) + gtest.C(t, func(t *gtest.T) { + a := &struct { + }{} + t.Assert(utils.IsStruct(a), true) + t.Assert(utils.IsStruct(*a), true) + t.Assert(utils.IsStruct(&a), true) + }) +} From 046749566da0bc14c01808d157a3b29120a805fa Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 8 Jul 2021 23:06:58 +0800 Subject: [PATCH 375/492] update sql of unit testing case for package gdb --- database/gdb/testdata/with_multiple_depends.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/database/gdb/testdata/with_multiple_depends.sql b/database/gdb/testdata/with_multiple_depends.sql index 1ae9e8c5d..f4327b947 100644 --- a/database/gdb/testdata/with_multiple_depends.sql +++ b/database/gdb/testdata/with_multiple_depends.sql @@ -1,7 +1,7 @@ CREATE TABLE `table_a` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '', + `alias` varchar(255) NULL DEFAULT '', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB; @@ -11,7 +11,7 @@ INSERT INTO `table_a` VALUES (2, 'table_a_test2'); CREATE TABLE `table_b` ( `id` int(11) NOT NULL AUTO_INCREMENT, `table_a_id` int(11) NOT NULL, - `alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '', + `alias` varchar(255) NULL DEFAULT '', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB; @@ -23,7 +23,7 @@ INSERT INTO `table_b` VALUES (40, 2, 'table_b_test4'); CREATE TABLE `table_c` ( `id` int(11) NOT NULL AUTO_INCREMENT, `table_b_id` int(11) NOT NULL, - `alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '', + `alias` varchar(255) NULL DEFAULT '', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB; From 3d4d3a763a0623532ac604a08f7e8dffdc440865 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 12 Jul 2021 22:05:03 +0800 Subject: [PATCH 376/492] comment update for package gfsnotify --- os/gfsnotify/gfsnotify.go | 14 +++++++------- os/gfsnotify/gfsnotify_filefunc.go | 16 ++++++++-------- os/gfsnotify/gfsnotify_watcher.go | 19 +++++++++---------- os/gfsnotify/gfsnotify_watcher_loop.go | 2 +- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/os/gfsnotify/gfsnotify.go b/os/gfsnotify/gfsnotify.go index 0224b8ba0..9c2f5864f 100644 --- a/os/gfsnotify/gfsnotify.go +++ b/os/gfsnotify/gfsnotify.go @@ -96,8 +96,8 @@ func New() (*Watcher, error) { return w, nil } -// Add monitors <path> using default watcher with callback function <callbackFunc>. -// The optional parameter <recursive> specifies whether monitoring the <path> recursively, which is true in default. +// Add monitors `path` using default watcher with callback function `callbackFunc`. +// The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default. func Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { w, err := getDefaultWatcher() if err != nil { @@ -106,11 +106,11 @@ func Add(path string, callbackFunc func(event *Event), recursive ...bool) (callb return w.Add(path, callbackFunc, recursive...) } -// AddOnce monitors <path> using default watcher with callback function <callbackFunc> only once using unique name <name>. -// If AddOnce is called multiple times with the same <name> parameter, <path> is only added to monitor once. It returns error -// if it's called twice with the same <name>. +// AddOnce monitors `path` using default watcher with callback function `callbackFunc` only once using unique name `name`. +// If AddOnce is called multiple times with the same `name` parameter, `path` is only added to monitor once. It returns error +// if it's called twice with the same `name`. // -// The optional parameter <recursive> specifies whether monitoring the <path> recursively, which is true in default. +// The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default. func AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { w, err := getDefaultWatcher() if err != nil { @@ -119,7 +119,7 @@ func AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bo return w.AddOnce(name, path, callbackFunc, recursive...) } -// Remove removes all monitoring callbacks of given <path> from watcher recursively. +// Remove removes all monitoring callbacks of given `path` from watcher recursively. func Remove(path string) error { w, err := getDefaultWatcher() if err != nil { diff --git a/os/gfsnotify/gfsnotify_filefunc.go b/os/gfsnotify/gfsnotify_filefunc.go index f1d4daef6..3be6fbde8 100644 --- a/os/gfsnotify/gfsnotify_filefunc.go +++ b/os/gfsnotify/gfsnotify_filefunc.go @@ -24,7 +24,7 @@ func fileDir(path string) string { return filepath.Dir(path) } -// fileRealPath converts the given <path> to its absolute path +// fileRealPath converts the given `path` to its absolute path // and checks if the file path exists. // If the file does not exist, return an empty string. func fileRealPath(path string) string { @@ -38,7 +38,7 @@ func fileRealPath(path string) string { return p } -// fileExists checks whether given <path> exist. +// fileExists checks whether given `path` exist. func fileExists(path string) bool { if stat, err := os.Stat(path); stat != nil && !os.IsNotExist(err) { return true @@ -46,7 +46,7 @@ func fileExists(path string) bool { return false } -// fileIsDir checks whether given <path> a directory. +// fileIsDir checks whether given `path` a directory. func fileIsDir(path string) bool { s, err := os.Stat(path) if err != nil { @@ -55,7 +55,7 @@ func fileIsDir(path string) bool { return s.IsDir() } -// fileAllDirs returns all sub-folders including itself of given <path> recursively. +// fileAllDirs returns all sub-folders including itself of given `path` recursively. func fileAllDirs(path string) (list []string) { list = []string{path} file, err := os.Open(path) @@ -78,8 +78,8 @@ func fileAllDirs(path string) (list []string) { return } -// fileScanDir returns all sub-files with absolute paths of given <path>, -// It scans directory recursively if given parameter <recursive> is true. +// fileScanDir returns all sub-files with absolute paths of given `path`, +// It scans directory recursively if given parameter `recursive` is true. func fileScanDir(path string, pattern string, recursive ...bool) ([]string, error) { list, err := doFileScanDir(path, pattern, recursive...) if err != nil { @@ -94,10 +94,10 @@ func fileScanDir(path string, pattern string, recursive ...bool) ([]string, erro // doFileScanDir is an internal method which scans directory // and returns the absolute path list of files that are not sorted. // -// The pattern parameter <pattern> supports multiple file name patterns, +// The pattern parameter `pattern` supports multiple file name patterns, // using the ',' symbol to separate multiple patterns. // -// It scans directory recursively if given parameter <recursive> is true. +// It scans directory recursively if given parameter `recursive` is true. func doFileScanDir(path string, pattern string, recursive ...bool) ([]string, error) { list := ([]string)(nil) file, err := os.Open(path) diff --git a/os/gfsnotify/gfsnotify_watcher.go b/os/gfsnotify/gfsnotify_watcher.go index 3219c26c8..777ada43a 100644 --- a/os/gfsnotify/gfsnotify_watcher.go +++ b/os/gfsnotify/gfsnotify_watcher.go @@ -14,19 +14,20 @@ import ( "github.com/gogf/gf/container/glist" ) -// Add monitors <path> with callback function <callbackFunc> to the watcher. -// The optional parameter <recursive> specifies whether monitoring the <path> recursively, +// Add monitors `path` with callback function `callbackFunc` to the watcher. +// The optional parameter `recursive` specifies whether monitoring the `path` recursively, // which is true in default. func (w *Watcher) Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { return w.AddOnce("", path, callbackFunc, recursive...) } -// AddOnce monitors <path> with callback function <callbackFunc> only once using unique name -// <name> to the watcher. If AddOnce is called multiple times with the same <name> parameter, -// <path> is only added to monitor once. -// It returns error if it's called twice with the same <name>. +// AddOnce monitors `path` with callback function `callbackFunc` only once using unique name +// `name` to the watcher. If AddOnce is called multiple times with the same `name` parameter, +// `path` is only added to monitor once. // -// The optional parameter <recursive> specifies whether monitoring the <path> recursively, +// It returns error if it's called twice with the same `name`. +// +// The optional parameter `recursive` specifies whether monitoring the `path` recursively, // which is true in default. func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { w.nameSet.AddIfNotExistFuncLock(name, func() bool { @@ -99,8 +100,6 @@ func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event } // Add the callback to global callback map. callbackIdMap.Set(callback.Id, callback) - - //intlog.Print("addWithCallbackFunc", name, path, callback.recursive) return } @@ -113,7 +112,7 @@ func (w *Watcher) Close() { close(w.closeChan) } -// Remove removes monitor and all callbacks associated with the <path> recursively. +// Remove removes monitor and all callbacks associated with the `path` recursively. func (w *Watcher) Remove(path string) error { // Firstly remove the callbacks of the path. if r := w.callbacks.Remove(path); r != nil { diff --git a/os/gfsnotify/gfsnotify_watcher_loop.go b/os/gfsnotify/gfsnotify_watcher_loop.go index 2618239ce..6eb1e1a7d 100644 --- a/os/gfsnotify/gfsnotify_watcher_loop.go +++ b/os/gfsnotify/gfsnotify_watcher_loop.go @@ -134,7 +134,7 @@ func (w *Watcher) eventLoop() { }() } -// getCallbacks searches and returns all callbacks with given <path>. +// getCallbacks searches and returns all callbacks with given `path`. // It also searches its parents for callbacks if they're recursive. func (w *Watcher) getCallbacks(path string) (callbacks []*Callback) { // Firstly add the callbacks of itself. From fbfc23211c18085cfee89095416fbc92a5863016 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 13 Jul 2021 19:37:16 +0800 Subject: [PATCH 377/492] fix issue in Counter of package gdb --- database/gdb/gdb_core.go | 42 ++++++++++++++----------- database/gdb/gdb_func.go | 5 ++- database/gdb/gdb_z_mysql_method_test.go | 6 ++-- internal/intlog/intlog.go | 3 +- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 675dc809f..2b96dd8ec 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -510,29 +510,34 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter switch kind { case reflect.Map, reflect.Struct: var ( - fields []string - dataMap = ConvertDataForTableRecord(data) + fields []string + dataMap = ConvertDataForTableRecord(data) + counterHandler = func(column string, counter Counter) { + if counter.Value != 0 { + var ( + column = c.QuoteWord(column) + columnRef = c.QuoteWord(counter.Field) + columnVal = counter.Value + operator = "+" + ) + if columnVal < 0 { + operator = "-" + columnVal = -columnVal + } + fields = append(fields, fmt.Sprintf("%s=%s%s?", column, columnRef, operator)) + params = append(params, columnVal) + } + } ) + for k, v := range dataMap { switch value := v.(type) { case *Counter: - if value.Value != 0 { - column := k - if value.Field != "" { - column = c.QuoteWord(value.Field) - } - fields = append(fields, fmt.Sprintf("%s=%s+?", column, column)) - params = append(params, value.Value) - } + counterHandler(k, *value) + case Counter: - if value.Value != 0 { - column := k - if value.Field != "" { - column = c.QuoteWord(value.Field) - } - fields = append(fields, fmt.Sprintf("%s=%s+?", column, column)) - params = append(params, value.Value) - } + counterHandler(k, value) + default: if s, ok := v.(Raw); ok { fields = append(fields, c.QuoteWord(k)+"="+gconv.String(s)) @@ -543,6 +548,7 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter } } updates = strings.Join(fields, ",") + default: updates = gconv.String(data) } diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 8a28250dd..c5454c38f 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -814,7 +814,9 @@ func formatError(err error, sql string, args ...interface{}) error { func FormatSqlWithArgs(sql string, args []interface{}) string { index := -1 newQuery, _ := gregex.ReplaceStringFunc( - `(\?|:v\d+|\$\d+|@p\d+)`, sql, func(s string) string { + `(\?|:v\d+|\$\d+|@p\d+)`, + sql, + func(s string) string { index++ if len(args) > index { if args[index] == nil { @@ -834,6 +836,7 @@ func FormatSqlWithArgs(sql string, args []interface{}) string { switch kind { case reflect.String, reflect.Map, reflect.Slice, reflect.Array: return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'` + case reflect.Struct: if t, ok := args[index].(time.Time); ok { return `'` + t.Format(`2006-01-02 15:04:05`) + `'` diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index 5ac4e2730..b571a1da3 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -1436,7 +1436,7 @@ func Test_DB_UpdateCounter(t *testing.T) { gtest.C(t, func(t *gtest.T) { gdbCounter := &gdb.Counter{ - Field: "views", + Field: "id", Value: 1, } updateData := g.Map{ @@ -1449,7 +1449,7 @@ func Test_DB_UpdateCounter(t *testing.T) { one, err := db.Model(tableName).Where("id", 1).One() t.AssertNil(err) t.Assert(one["id"].Int(), 1) - t.Assert(one["views"].Int(), 1) + t.Assert(one["views"].Int(), 2) }) gtest.C(t, func(t *gtest.T) { @@ -1468,7 +1468,7 @@ func Test_DB_UpdateCounter(t *testing.T) { one, err := db.Model(tableName).Where("id", 1).One() t.AssertNil(err) t.Assert(one["id"].Int(), 1) - t.Assert(one["views"].Int(), 0) + t.Assert(one["views"].Int(), 1) }) } diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index d5ca85d6a..a22207ccf 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -71,8 +71,9 @@ func doPrint(ctx context.Context, content string, stack bool) { buffer.WriteString(now()) buffer.WriteString(" [INTE] ") buffer.WriteString(file()) + buffer.WriteString(" ") if s := traceIdStr(ctx); s != "" { - buffer.WriteString(" " + s) + buffer.WriteString(s + " ") } buffer.WriteString(content) buffer.WriteString("\n") From bc724deb5e7795c9c1861c51b3ccd87ebd30d4d6 Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Tue, 13 Jul 2021 21:43:07 +0800 Subject: [PATCH 378/492] =?UTF-8?q?=E6=97=A5=E5=BF=97=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E5=8C=BA=E5=88=86=E6=96=87=E4=BB=B6=E5=92=8C=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=8F=B0=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/glog/glog_logger.go | 4 +--- os/glog/glog_logger_handler.go | 35 ++++++++++++++++++++++------------ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 5fdc14fed..9bb943fc9 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -228,9 +228,7 @@ func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { } // Allow output to stdout? if l.config.StdoutPrint { - if _, err := os.Stdout.Write(input.Buffer(mustWithColor).Bytes()); err != nil { - intlog.Error(err) - } + input.Stdout() } } else { if _, err := l.config.Writer.Write(input.Buffer().Bytes()); err != nil { diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index 6e1b501ca..bb603ef8e 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -10,6 +10,7 @@ import ( "bytes" "context" "github.com/fatih/color" + "os" "time" ) @@ -43,16 +44,27 @@ func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, s string) { buffer.WriteString(s) } -func (i *HandlerInput) Buffer(withColor ...bool) *bytes.Buffer { +func (i *HandlerInput) Buffer() *bytes.Buffer { buffer := bytes.NewBuffer(nil) buffer.WriteString(i.TimeFormat) - if i.LevelFormat != "" { - if i.logger.config.FileColorEnable || (len(withColor) > 0 && withColor[0] == mustWithColor) { - i.addStringToBuffer(buffer, i.getLevelFormatWithColor()) - } else { - i.addStringToBuffer(buffer, i.LevelFormat) - } - } + i.addStringToBuffer(buffer, i.LevelFormat) + msg := i.GetContent() + i.addStringToBuffer(buffer, msg.String()) + return buffer +} + +// Stdout print log to console +func (i *HandlerInput) Stdout() { + _, _ = os.Stdout.Write([]byte(i.TimeFormat)) + fg := i.getLevelFormatColor() + _, _ = color.New(fg).Print(" " + i.LevelFormat + " ") + msg := i.GetContent() + _, _ = os.Stdout.Write(msg.Bytes()) +} + +// GetContent returns the primary content. +func (i *HandlerInput) GetContent() *bytes.Buffer { + buffer := bytes.NewBuffer(nil) if i.CallerFunc != "" { i.addStringToBuffer(buffer, i.CallerFunc) } @@ -72,14 +84,13 @@ func (i *HandlerInput) Buffer(withColor ...bool) *bytes.Buffer { return buffer } -// getLevelFormatWithColor returns the prefix string with color. -func (i *HandlerInput) getLevelFormatWithColor() string { - s := i.LevelFormat +// getLevelFormatColor returns the prefix string color. +func (i *HandlerInput) getLevelFormatColor() color.Attribute { fg := defaultLevelColor[i.Level] if i.logger.config.currentColor != 0 { fg = i.logger.config.currentColor } - return color.New(fg).Sprint(s) + return fg } func (i *HandlerInput) String() string { From 84aa30d9c29540c5d6be7c4e0e23b8e09d73af1f Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Tue, 13 Jul 2021 22:45:35 +0800 Subject: [PATCH 379/492] =?UTF-8?q?=E5=85=81=E8=AE=B8=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=AD=E6=B7=BB=E5=8A=A0=E9=A2=9C=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/glog/glog_logger_handler.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index bb603ef8e..b632e46bb 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -47,7 +47,12 @@ func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, s string) { func (i *HandlerInput) Buffer() *bytes.Buffer { buffer := bytes.NewBuffer(nil) buffer.WriteString(i.TimeFormat) - i.addStringToBuffer(buffer, i.LevelFormat) + levelString := i.LevelFormat + if i.logger.config.FileColorEnable { + fg := i.getLevelFormatColor() + levelString = color.New(fg).Sprintf(i.LevelFormat) + } + i.addStringToBuffer(buffer, levelString) msg := i.GetContent() i.addStringToBuffer(buffer, msg.String()) return buffer From 0140808460c573c9c255dc2b7f70068899a62015 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 13 Jul 2021 23:01:31 +0800 Subject: [PATCH 380/492] add handler extension feature for package ghttp --- frame/g/g_func.go | 6 ++ net/ghttp/ghttp.go | 27 ++++-- .../ghttp_middleware_handler_response.go | 29 +++++++ net/ghttp/ghttp_request.go | 20 +++++ net/ghttp/ghttp_request_middleware.go | 54 ++++++++++-- net/ghttp/ghttp_response.go | 4 +- net/ghttp/ghttp_server_config.go | 12 ++- net/ghttp/ghttp_server_domain.go | 31 ++----- net/ghttp/ghttp_server_router.go | 8 +- net/ghttp/ghttp_server_router_group.go | 17 +++- net/ghttp/ghttp_server_router_hook.go | 8 +- net/ghttp/ghttp_server_router_middleware.go | 15 +++- net/ghttp/ghttp_server_service_controller.go | 42 ++++++---- net/ghttp/ghttp_server_service_handler.go | 76 ++++++++++++++--- net/ghttp/ghttp_server_service_object.go | 70 ++++++---------- ...ghttp_unit_router_handler_extended_test.go | 82 +++++++++++++++++++ net/ghttp/ghttp_unit_router_names_test.go | 2 +- 17 files changed, 373 insertions(+), 130 deletions(-) create mode 100644 net/ghttp/ghttp_middleware_handler_response.go create mode 100644 net/ghttp/ghttp_unit_router_handler_extended_test.go diff --git a/frame/g/g_func.go b/frame/g/g_func.go index 64b284482..5034a8f08 100644 --- a/frame/g/g_func.go +++ b/frame/g/g_func.go @@ -7,6 +7,7 @@ package g import ( + "context" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/net/ghttp" @@ -75,3 +76,8 @@ func IsNil(value interface{}, traceSource ...bool) bool { func IsEmpty(value interface{}) bool { return empty.IsEmpty(value) } + +// RequestFromCtx retrieves and returns the Request object from context. +func RequestFromCtx(ctx context.Context) *ghttp.Request { + return ghttp.RequestFromCtx(ctx) +} diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index 54e2a4bbd..0f9ed208b 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -58,20 +58,30 @@ type ( handler *handlerItem // The handler. } + // HandlerFunc is request handler function. + HandlerFunc = func(r *Request) + + // handlerFuncInfo contains the HandlerFunc address and its reflect type. + handlerFuncInfo struct { + Func HandlerFunc // Handler function address. + Type reflect.Type // Reflect type information for current handler, which is used for extension of handler feature. + Value reflect.Value // Reflect value information for current handler, which is used for extension of handler feature. + } + // handlerItem is the registered handler for route handling, // including middleware and hook functions. handlerItem struct { itemId int // Unique handler item id mark. itemName string // Handler name, which is automatically retrieved from runtime stack when registered. itemType int // Handler type: object/handler/controller/middleware/hook. - itemFunc HandlerFunc // Handler address. - initFunc HandlerFunc // Initialization function when request enters the object(only available for object register type). - shutFunc HandlerFunc // Shutdown function when request leaves out the object(only available for object register type). + itemInfo handlerFuncInfo // Handler function information. + initFunc HandlerFunc // Initialization function when request enters the object (only available for object register type). + shutFunc HandlerFunc // Shutdown function when request leaves out the object (only available for object register type). middleware []HandlerFunc // Bound middleware array. ctrlInfo *handlerController // Controller information for reflect usage. - hookName string // Hook type name. + hookName string // Hook type name, only available for hook type. router *Router // Router object. - source string // Source file path:line when registering. + source string // Registering source file `path:line`. } // handlerParsedItem is the item parsed from URL.Path. @@ -98,9 +108,6 @@ type ( Stack() string } - // HandlerFunc is request handler function. - HandlerFunc = func(r *Request) - // Listening file descriptor mapping. // The key is either "http" or "https" and the value is its FD. listenerFdMap = map[string]string @@ -126,6 +133,10 @@ const ( exceptionExitAll = "exit_all" exceptionExitHook = "exit_hook" routeCacheDuration = time.Hour + methodNameInit = "Init" + methodNameShut = "Shut" + methodNameExit = "Exit" + ctxKeyForRequest = "gHttpRequestObject" ) var ( diff --git a/net/ghttp/ghttp_middleware_handler_response.go b/net/ghttp/ghttp_middleware_handler_response.go new file mode 100644 index 000000000..d84815275 --- /dev/null +++ b/net/ghttp/ghttp_middleware_handler_response.go @@ -0,0 +1,29 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp + +import ( + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/intlog" +) + +// MiddlewareHandlerResponse is the default middleware handling handler response object and its error. +func MiddlewareHandlerResponse(r *Request) { + r.Middleware.Next() + res, err := r.GetHandlerResponse() + if err != nil { + r.Response.Writef( + `{"code":%d,"message":"%s"}`, + gerror.Code(err), + err.Error(), + ) + return + } + if exception := r.Response.WriteJson(res); exception != nil { + intlog.Error(r.Context(), exception) + } +} diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index fa86d02c5..e01dc1ac8 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -37,6 +37,7 @@ type Request struct { StaticFile *staticFile // Static file object for static file serving. context context.Context // Custom context for internal usage purpose. handlers []*handlerParsedItem // All matched handlers containing handler, hook and middleware for this request. + handlerResponse handlerResponse // Handler response object and its error value. hasHookHandler bool // A bool marking whether there's hook handler in the handlers for performance purpose. hasServeHandler bool // A bool marking whether there's serving handler in the handlers for performance purpose. parsedQuery bool // A bool marking whether the GET parameters parsed. @@ -57,6 +58,11 @@ type Request struct { viewParams gview.Params // Custom template view variables for this response. } +type handlerResponse struct { + Object interface{} + Error error +} + // staticFile is the file struct for static file service. type staticFile struct { File *gres.File // Resource file object. @@ -96,6 +102,15 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { return request } +// RequestFromCtx retrieves and returns the Request object from context. +func RequestFromCtx(ctx context.Context) *Request { + result := ctx.Value(ctxKeyForRequest) + if result != nil { + return result.(*Request) + } + return nil +} + // WebSocket upgrades current request as a websocket request. // It returns a new WebSocket object if success, or the error if failure. // Note that the request should be a websocket request, or it will surely fail upgrading. @@ -236,3 +251,8 @@ func (r *Request) ReloadParam() { r.parsedQuery = false r.bodyContent = nil } + +// GetHandlerResponse retrieves and returns the handler response object and its error. +func (r *Request) GetHandlerResponse() (res interface{}, err error) { + return r.handlerResponse.Object, r.handlerResponse.Error +} diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index caa0e0bb0..75321da0a 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -7,6 +7,7 @@ package ghttp import ( + "context" "github.com/gogf/gf/errors/gerror" "net/http" "reflect" @@ -91,9 +92,7 @@ func (m *middleware) Next() { }) } if !m.request.IsExited() { - niceCallFunc(func() { - item.handler.itemFunc(m.request) - }) + m.callHandlerFunc(item.handler.itemInfo) } if !m.request.IsExited() && item.handler.shutFunc != nil { niceCallFunc(func() { @@ -108,13 +107,13 @@ func (m *middleware) Next() { break } niceCallFunc(func() { - item.handler.itemFunc(m.request) + m.callHandlerFunc(item.handler.itemInfo) }) // Global middleware array. case handlerTypeMiddleware: niceCallFunc(func() { - item.handler.itemFunc(m.request) + item.handler.itemInfo.Func(m.request) }) // It does not continue calling next middleware after another middleware done. // There should be a "Next" function to be called in the middleware in order to manage the workflow. @@ -145,3 +144,48 @@ func (m *middleware) Next() { } } } + +func (m *middleware) callHandlerFunc(funcInfo handlerFuncInfo) { + niceCallFunc(func() { + if funcInfo.Func != nil { + funcInfo.Func(m.request) + } else { + var inputValues = []reflect.Value{ + reflect.ValueOf(context.WithValue( + m.request.Context(), ctxKeyForRequest, m.request, + )), + } + if funcInfo.Type.NumIn() == 2 { + var ( + request reflect.Value + ) + if funcInfo.Type.In(1).Kind() == reflect.Ptr { + request = reflect.New(funcInfo.Type.In(1).Elem()) + m.request.handlerResponse.Error = m.request.Parse(request.Interface()) + } else { + request = reflect.New(funcInfo.Type.In(1).Elem()).Elem() + m.request.handlerResponse.Error = m.request.Parse(request.Addr().Interface()) + } + if m.request.handlerResponse.Error != nil { + return + } + inputValues = append(inputValues, request) + } + + // Call handler with dynamic created parameter values. + results := funcInfo.Value.Call(inputValues) + switch len(results) { + case 1: + m.request.handlerResponse.Error = results[0].Interface().(error) + + case 2: + m.request.handlerResponse.Object = results[0].Interface() + if !results[1].IsNil() { + if v := results[1].Interface(); v != nil { + m.request.handlerResponse.Error = v.(error) + } + } + } + } + }) +} diff --git a/net/ghttp/ghttp_response.go b/net/ghttp/ghttp_response.go index a33613957..04524046a 100644 --- a/net/ghttp/ghttp_response.go +++ b/net/ghttp/ghttp_response.go @@ -110,7 +110,7 @@ func (r *Response) RedirectBack(code ...int) { r.RedirectTo(r.Request.GetReferer(), code...) } -// BufferString returns the buffered content as []byte. +// Buffer returns the buffered content as []byte. func (r *Response) Buffer() []byte { return r.buffer.Bytes() } @@ -136,7 +136,7 @@ func (r *Response) ClearBuffer() { r.buffer.Reset() } -// Output outputs the buffer content to the client and clears the buffer. +// Flush outputs the buffer content to the client and clears the buffer. func (r *Response) Flush() { if r.Server.config.ServerAgent != "" { r.Header().Set("Server", r.Server.config.ServerAgent) diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 5021c26d8..9f2e8be33 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -30,10 +30,14 @@ import ( const ( defaultHttpAddr = ":80" // Default listening port for HTTP. defaultHttpsAddr = ":443" // Default listening port for HTTPS. - URI_TYPE_DEFAULT = 0 // Method name to URI converting type, which converts name to its lower case and joins the words using char '-'. - URI_TYPE_FULLNAME = 1 // Method name to URI converting type, which does no converting to the method name. - URI_TYPE_ALLLOWER = 2 // Method name to URI converting type, which converts name to its lower case. - URI_TYPE_CAMEL = 3 // Method name to URI converting type, which converts name to its camel case. + URI_TYPE_DEFAULT = 0 // Deprecated, please use UriTypeDefault instead. + URI_TYPE_FULLNAME = 1 // Deprecated, please use UriTypeFullName instead. + URI_TYPE_ALLLOWER = 2 // Deprecated, please use UriTypeAllLower instead. + URI_TYPE_CAMEL = 3 // Deprecated, please use UriTypeCamel instead. + UriTypeDefault = 0 // Method name to URI converting type, which converts name to its lower case and joins the words using char '-'. + UriTypeFullName = 1 // Method name to URI converting type, which does no converting to the method name. + UriTypeAllLower = 2 // Method name to URI converting type, which converts name to its lower case. + UriTypeCamel = 3 // Method name to URI converting type, which converts name to its camel case. ) // ServerConfig is the HTTP Server configuration manager. diff --git a/net/ghttp/ghttp_server_domain.go b/net/ghttp/ghttp_server_domain.go index 3fcb066e0..17a1e6e68 100644 --- a/net/ghttp/ghttp_server_domain.go +++ b/net/ghttp/ghttp_server_domain.go @@ -28,18 +28,15 @@ func (s *Server) Domain(domains string) *Domain { return d } -func (d *Domain) BindHandler(pattern string, handler HandlerFunc) { +func (d *Domain) BindHandler(pattern string, handler interface{}) { for domain, _ := range d.domains { d.server.BindHandler(pattern+"@"+domain, handler) } } -func (d *Domain) doBindHandler( - pattern string, handler HandlerFunc, - middleware []HandlerFunc, source string, -) { +func (d *Domain) doBindHandler(pattern string, funcInfo handlerFuncInfo, middleware []HandlerFunc, source string) { for domain, _ := range d.domains { - d.server.doBindHandler(pattern+"@"+domain, handler, middleware, source) + d.server.doBindHandler(pattern+"@"+domain, funcInfo, middleware, source) } } @@ -49,10 +46,7 @@ func (d *Domain) BindObject(pattern string, obj interface{}, methods ...string) } } -func (d *Domain) doBindObject( - pattern string, obj interface{}, methods string, - middleware []HandlerFunc, source string, -) { +func (d *Domain) doBindObject(pattern string, obj interface{}, methods string, middleware []HandlerFunc, source string) { for domain, _ := range d.domains { d.server.doBindObject(pattern+"@"+domain, obj, methods, middleware, source) } @@ -79,10 +73,7 @@ func (d *Domain) BindObjectRest(pattern string, obj interface{}) { } } -func (d *Domain) doBindObjectRest( - pattern string, obj interface{}, - middleware []HandlerFunc, source string, -) { +func (d *Domain) doBindObjectRest(pattern string, obj interface{}, middleware []HandlerFunc, source string) { for domain, _ := range d.domains { d.server.doBindObjectRest(pattern+"@"+domain, obj, middleware, source) } @@ -94,10 +85,7 @@ func (d *Domain) BindController(pattern string, c Controller, methods ...string) } } -func (d *Domain) doBindController( - pattern string, c Controller, methods string, - middleware []HandlerFunc, source string, -) { +func (d *Domain) doBindController(pattern string, c Controller, methods string, middleware []HandlerFunc, source string) { for domain, _ := range d.domains { d.server.doBindController(pattern+"@"+domain, c, methods, middleware, source) } @@ -109,10 +97,7 @@ func (d *Domain) BindControllerMethod(pattern string, c Controller, method strin } } -func (d *Domain) doBindControllerMethod( - pattern string, c Controller, method string, - middleware []HandlerFunc, source string, -) { +func (d *Domain) doBindControllerMethod(pattern string, c Controller, method string, middleware []HandlerFunc, source string) { for domain, _ := range d.domains { d.server.doBindControllerMethod(pattern+"@"+domain, c, method, middleware, source) } @@ -171,7 +156,7 @@ func (d *Domain) BindMiddleware(pattern string, handlers ...HandlerFunc) { func (d *Domain) BindMiddlewareDefault(handlers ...HandlerFunc) { for domain, _ := range d.domains { - d.server.BindMiddleware(gDEFAULT_MIDDLEWARE_PATTERN+"@"+domain, handlers...) + d.server.BindMiddleware(defaultMiddlewarePattern+"@"+domain, handlers...) } } diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index 6d31d5e78..85b6480a5 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -234,7 +234,7 @@ func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerIte // Compare the length of their URI, // but the fuzzy and named parts of the URI are not calculated to the result. - // Eg: + // Example: // /admin-goods-{page} > /admin-{page} // /{hash}.{type} > /{hash} var uriNew, uriOld string @@ -252,7 +252,7 @@ func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerIte } // Route type checks: {xxx} > :xxx > *xxx. - // Eg: + // Example: // /name/act > /{name}/:act var ( fuzzyCountFieldNew int @@ -321,9 +321,7 @@ func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerIte // If they have different router type, // the new router item has more priority than the other one. - if newItem.itemType == handlerTypeHandler || - newItem.itemType == handlerTypeObject || - newItem.itemType == handlerTypeController { + if newItem.itemType == handlerTypeHandler || newItem.itemType == handlerTypeObject || newItem.itemType == handlerTypeController { return true } diff --git a/net/ghttp/ghttp_server_router_group.go b/net/ghttp/ghttp_server_router_group.go index 3b4d01d0b..09b1ea84c 100644 --- a/net/ghttp/ghttp_server_router_group.go +++ b/net/ghttp/ghttp_server_router_group.go @@ -155,8 +155,10 @@ func (g *RouterGroup) Bind(items []GroupItem) *RouterGroup { switch bindType { case "REST": group.preBindToLocalArray("REST", gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) + case "MIDDLEWARE": group.preBindToLocalArray("MIDDLEWARE", gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) + default: if strings.EqualFold(bindType, "ALL") { bindType = "" @@ -309,11 +311,16 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { } switch bindType { case "HANDLER": - if h, ok := object.(HandlerFunc); ok { + if reflect.ValueOf(object).Kind() == reflect.Func { + funcInfo, err := g.server.checkAndCreateFuncInfo(object, "", "", "") + if err != nil { + g.server.Logger().Error(err.Error()) + return g + } if g.server != nil { - g.server.doBindHandler(pattern, h, g.middleware, source) + g.server.doBindHandler(pattern, funcInfo, g.middleware, source) } else { - g.domain.doBindHandler(pattern, h, g.middleware, source) + g.domain.doBindHandler(pattern, funcInfo, g.middleware, source) } } else if g.isController(object) { if len(extras) > 0 { @@ -373,6 +380,7 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { } } } else { + // At last, it treats the `object` as Object registering type. if g.server != nil { g.server.doBindObject(pattern, object, "", g.middleware, source) } else { @@ -380,6 +388,7 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { } } } + case "REST": if g.isController(object) { if g.server != nil { @@ -398,6 +407,7 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { g.domain.doBindObjectRest(pattern, object, g.middleware, source) } } + case "HOOK": if h, ok := object.(HandlerFunc); ok { if g.server != nil { @@ -414,6 +424,7 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { // isController checks and returns whether given <value> is a controller. // A controller should contains attributes: Request/Response/Server/Cookie/Session/View. +// Deprecated. func (g *RouterGroup) isController(value interface{}) bool { // Whether implements interface Controller. if _, ok := value.(Controller); !ok { diff --git a/net/ghttp/ghttp_server_router_hook.go b/net/ghttp/ghttp_server_router_hook.go index 4da68ad5e..7963a7531 100644 --- a/net/ghttp/ghttp_server_router_hook.go +++ b/net/ghttp/ghttp_server_router_hook.go @@ -9,6 +9,7 @@ package ghttp import ( "github.com/gogf/gf/debug/gdebug" "net/http" + "reflect" ) // BindHookHandler registers handler for specified hook. @@ -20,7 +21,10 @@ func (s *Server) doBindHookHandler(pattern string, hook string, handler HandlerF s.setHandler(pattern, &handlerItem{ itemType: handlerTypeHook, itemName: gdebug.FuncPath(handler), - itemFunc: handler, + itemInfo: handlerFuncInfo{ + Func: handler, + Type: reflect.TypeOf(handler), + }, hookName: hook, source: source, }) @@ -43,7 +47,7 @@ func (s *Server) callHookHandler(hook string, r *Request) { // DO NOT USE the router of the hook handler, // which can overwrite the router of serving handler. // r.Router = item.handler.router - if err := s.niceCallHookHandler(item.handler.itemFunc, r); err != nil { + if err := s.niceCallHookHandler(item.handler.itemInfo.Func, r); err != nil { switch err { case exceptionExit: break diff --git a/net/ghttp/ghttp_server_router_middleware.go b/net/ghttp/ghttp_server_router_middleware.go index 699ccd9d5..09faf7d1e 100644 --- a/net/ghttp/ghttp_server_router_middleware.go +++ b/net/ghttp/ghttp_server_router_middleware.go @@ -8,11 +8,12 @@ package ghttp import ( "github.com/gogf/gf/debug/gdebug" + "reflect" ) const ( // The default route pattern for global middleware. - gDEFAULT_MIDDLEWARE_PATTERN = "/*" + defaultMiddlewarePattern = "/*" ) // BindMiddleware registers one or more global middleware to the server. @@ -24,7 +25,10 @@ func (s *Server) BindMiddleware(pattern string, handlers ...HandlerFunc) { s.setHandler(pattern, &handlerItem{ itemType: handlerTypeMiddleware, itemName: gdebug.FuncPath(handler), - itemFunc: handler, + itemInfo: handlerFuncInfo{ + Func: handler, + Type: reflect.TypeOf(handler), + }, }) } } @@ -34,10 +38,13 @@ func (s *Server) BindMiddleware(pattern string, handlers ...HandlerFunc) { // before or after service handler. func (s *Server) BindMiddlewareDefault(handlers ...HandlerFunc) { for _, handler := range handlers { - s.setHandler(gDEFAULT_MIDDLEWARE_PATTERN, &handlerItem{ + s.setHandler(defaultMiddlewarePattern, &handlerItem{ itemType: handlerTypeMiddleware, itemName: gdebug.FuncPath(handler), - itemFunc: handler, + itemInfo: handlerFuncInfo{ + Func: handler, + Type: reflect.TypeOf(handler), + }, }) } } diff --git a/net/ghttp/ghttp_server_service_controller.go b/net/ghttp/ghttp_server_service_controller.go index 94f0940c1..b2df34ec4 100644 --- a/net/ghttp/ghttp_server_service_controller.go +++ b/net/ghttp/ghttp_server_service_controller.go @@ -73,18 +73,20 @@ func (s *Server) doBindController( pattern = s.serveHandlerKey("", path, domain) } // Retrieve a list of methods, create construct corresponding URI. - m := make(map[string]*handlerItem) - v := reflect.ValueOf(controller) - t := v.Type() - pkgPath := t.Elem().PkgPath() - pkgName := gfile.Basename(pkgPath) - structName := t.Elem().Name() + var ( + m = make(map[string]*handlerItem) + v = reflect.ValueOf(controller) + t = v.Type() + pkgPath = t.Elem().PkgPath() + pkgName = gfile.Basename(pkgPath) + structName = t.Elem().Name() + ) for i := 0; i < v.NumMethod(); i++ { methodName := t.Method(i).Name if methodMap != nil && !methodMap[methodName] { continue } - if methodName == "Init" || methodName == "Shut" || methodName == "Exit" { + if methodName == methodNameInit || methodName == methodNameShut || methodName == methodNameExit { continue } ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") @@ -153,12 +155,14 @@ func (s *Server) doBindControllerMethod( middleware []HandlerFunc, source string, ) { - m := make(map[string]*handlerItem) - v := reflect.ValueOf(controller) - t := v.Type() - structName := t.Elem().Name() - methodName := strings.TrimSpace(method) - methodValue := v.MethodByName(methodName) + var ( + m = make(map[string]*handlerItem) + v = reflect.ValueOf(controller) + t = v.Type() + structName = t.Elem().Name() + methodName = strings.TrimSpace(method) + methodValue = v.MethodByName(methodName) + ) if !methodValue.IsValid() { s.Logger().Fatal("invalid method name: " + methodName) return @@ -194,11 +198,13 @@ func (s *Server) doBindControllerRest( pattern string, controller Controller, middleware []HandlerFunc, source string, ) { - m := make(map[string]*handlerItem) - v := reflect.ValueOf(controller) - t := v.Type() - pkgPath := t.Elem().PkgPath() - structName := t.Elem().Name() + var ( + m = make(map[string]*handlerItem) + v = reflect.ValueOf(controller) + t = v.Type() + pkgPath = t.Elem().PkgPath() + structName = t.Elem().Name() + ) for i := 0; i < v.NumMethod(); i++ { methodName := t.Method(i).Name if _, ok := methodsMap[strings.ToUpper(methodName)]; !ok { diff --git a/net/ghttp/ghttp_server_service_handler.go b/net/ghttp/ghttp_server_service_handler.go index c81ee3900..36a1a1c66 100644 --- a/net/ghttp/ghttp_server_service_handler.go +++ b/net/ghttp/ghttp_server_service_handler.go @@ -9,27 +9,37 @@ package ghttp import ( "bytes" "github.com/gogf/gf/debug/gdebug" + "github.com/gogf/gf/errors/gerror" + "reflect" "strings" "github.com/gogf/gf/text/gstr" ) // BindHandler registers a handler function to server with given pattern. -func (s *Server) BindHandler(pattern string, handler HandlerFunc) { - s.doBindHandler(pattern, handler, nil, "") +// The parameter `handler` can be type of: +// func(*ghttp.Request) +// func(context.Context) +// func(context.Context,TypeRequest) +// func(context.Context,TypeRequest) error +// func(context.Context,TypeRequest)(TypeResponse,error) +func (s *Server) BindHandler(pattern string, handler interface{}) { + funcInfo, err := s.checkAndCreateFuncInfo(handler, "", "", "") + if err != nil { + s.Logger().Error(err.Error()) + return + } + s.doBindHandler(pattern, funcInfo, nil, "") } // doBindHandler registers a handler function to server with given pattern. // The parameter <pattern> is like: // /user/list, put:/user, delete:/user, post:/user@goframe.org -func (s *Server) doBindHandler( - pattern string, handler HandlerFunc, - middleware []HandlerFunc, source string, -) { +func (s *Server) doBindHandler(pattern string, funcInfo handlerFuncInfo, middleware []HandlerFunc, source string) { s.setHandler(pattern, &handlerItem{ - itemName: gdebug.FuncPath(handler), + itemName: gdebug.FuncPath(funcInfo.Func), itemType: handlerTypeHandler, - itemFunc: handler, + itemInfo: funcInfo, middleware: middleware, source: source, }) @@ -77,13 +87,13 @@ func (s *Server) mergeBuildInNameToPattern(pattern string, structName, methodNam // Rule 3: Use camel case naming. func (s *Server) nameToUri(name string) string { switch s.config.NameToUriType { - case URI_TYPE_FULLNAME: + case UriTypeFullName: return name - case URI_TYPE_ALLLOWER: + case UriTypeAllLower: return strings.ToLower(name) - case URI_TYPE_CAMEL: + case UriTypeCamel: part := bytes.NewBuffer(nil) if gstr.IsLetterUpper(name[0]) { part.WriteByte(name[0] + 32) @@ -93,8 +103,9 @@ func (s *Server) nameToUri(name string) string { part.WriteString(name[1:]) return part.String() - case URI_TYPE_DEFAULT: + case UriTypeDefault: fallthrough + default: part := bytes.NewBuffer(nil) for i := 0; i < len(name); i++ { @@ -110,3 +121,44 @@ func (s *Server) nameToUri(name string) string { return part.String() } } + +func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, objName, methodName string) (info handlerFuncInfo, err error) { + handlerFunc, ok := f.(HandlerFunc) + if !ok { + reflectType := reflect.TypeOf(f) + if reflectType.NumIn() == 0 || reflectType.NumIn() > 2 || reflectType.NumOut() > 2 { + if pkgPath != "" { + err = gerror.Newf( + `invalid handler: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" or "func(context.Context)/func(context.Context,Request)/func(context.Context,Request) error/func(context.Context,Request)(Response,error)" is required`, + pkgPath, objName, methodName, reflect.TypeOf(f).String(), + ) + } else { + err = gerror.Newf( + `invalid handler: defined as "%s", but "func(*ghttp.Request)" or "func(context.Context)/func(context.Context,Request)/func(context.Context,Request) error/func(context.Context,Request)(Response,error)" is required`, + reflect.TypeOf(f).String(), + ) + } + return + } + + if reflectType.In(0).String() != "context.Context" { + err = gerror.Newf( + `invalid handler: defined as "%s", but the first input parameter should be type of "context.Context"`, + reflect.TypeOf(f).String(), + ) + return + } + + if reflectType.NumOut() > 0 && reflectType.Out(reflectType.NumOut()-1).String() != "error" { + err = gerror.Newf( + `invalid handler: defined as "%s", but the last output parameter should be type of "error"`, + reflect.TypeOf(f).String(), + ) + return + } + } + info.Func = handlerFunc + info.Type = reflect.TypeOf(f) + info.Value = reflect.ValueOf(f) + return +} diff --git a/net/ghttp/ghttp_server_service_object.go b/net/ghttp/ghttp_server_service_object.go index d86bf24e2..872cf179c 100644 --- a/net/ghttp/ghttp_server_service_object.go +++ b/net/ghttp/ghttp_server_service_object.go @@ -46,10 +46,7 @@ func (s *Server) BindObjectRest(pattern string, object interface{}) { s.doBindObjectRest(pattern, object, nil, "") } -func (s *Server) doBindObject( - pattern string, object interface{}, method string, - middleware []HandlerFunc, source string, -) { +func (s *Server) doBindObject(pattern string, object interface{}, method string, middleware []HandlerFunc, source string) { // Convert input method to map for convenience and high performance searching purpose. var methodMap map[string]bool if len(method) > 0 { @@ -104,26 +101,18 @@ func (s *Server) doBindObject( if objName[0] == '*' { objName = fmt.Sprintf(`(%s)`, objName) } - itemFunc, ok := v.Method(i).Interface().(func(*Request)) - if !ok { - if len(methodMap) > 0 { - s.Logger().Errorf( - `invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`, - pkgPath, objName, methodName, v.Method(i).Type().String(), - ) - } else { - s.Logger().Debugf( - `ignore route method: %s.%s.%s defined as "%s", no match "func(*ghttp.Request)" for object registry`, - pkgPath, objName, methodName, v.Method(i).Type().String(), - ) - } - continue + + funcInfo, err := s.checkAndCreateFuncInfo(v.Method(i).Interface(), pkgPath, objName, methodName) + if err != nil { + s.Logger().Error(err.Error()) + return } + key := s.mergeBuildInNameToPattern(pattern, structName, methodName, true) m[key] = &handlerItem{ itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), itemType: handlerTypeObject, - itemFunc: itemFunc, + itemInfo: funcInfo, initFunc: initFunc, shutFunc: shutFunc, middleware: middleware, @@ -145,7 +134,7 @@ func (s *Server) doBindObject( m[k] = &handlerItem{ itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), itemType: handlerTypeObject, - itemFunc: itemFunc, + itemInfo: funcInfo, initFunc: initFunc, shutFunc: shutFunc, middleware: middleware, @@ -194,19 +183,18 @@ func (s *Server) doBindObjectMethod( if objName[0] == '*' { objName = fmt.Sprintf(`(%s)`, objName) } - itemFunc, ok := methodValue.Interface().(func(*Request)) - if !ok { - s.Logger().Errorf( - `invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`, - pkgPath, objName, methodName, methodValue.Type().String(), - ) + + funcInfo, err := s.checkAndCreateFuncInfo(methodValue.Interface(), pkgPath, objName, methodName) + if err != nil { + s.Logger().Error(err.Error()) return } + key := s.mergeBuildInNameToPattern(pattern, structName, methodName, false) m[key] = &handlerItem{ itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), itemType: handlerTypeObject, - itemFunc: itemFunc, + itemInfo: funcInfo, initFunc: initFunc, shutFunc: shutFunc, middleware: middleware, @@ -216,10 +204,7 @@ func (s *Server) doBindObjectMethod( s.bindHandlerByMap(m) } -func (s *Server) doBindObjectRest( - pattern string, object interface{}, - middleware []HandlerFunc, source string, -) { +func (s *Server) doBindObjectRest(pattern string, object interface{}, middleware []HandlerFunc, source string) { var ( m = make(map[string]*handlerItem) v = reflect.ValueOf(object) @@ -236,11 +221,11 @@ func (s *Server) doBindObjectRest( t = v.Type() } structName := t.Elem().Name() - if v.MethodByName("Init").IsValid() { - initFunc = v.MethodByName("Init").Interface().(func(*Request)) + if v.MethodByName(methodNameInit).IsValid() { + initFunc = v.MethodByName(methodNameInit).Interface().(func(*Request)) } - if v.MethodByName("Shut").IsValid() { - shutFunc = v.MethodByName("Shut").Interface().(func(*Request)) + if v.MethodByName(methodNameShut).IsValid() { + shutFunc = v.MethodByName(methodNameShut).Interface().(func(*Request)) } pkgPath := t.Elem().PkgPath() for i := 0; i < v.NumMethod(); i++ { @@ -253,19 +238,18 @@ func (s *Server) doBindObjectRest( if objName[0] == '*' { objName = fmt.Sprintf(`(%s)`, objName) } - itemFunc, ok := v.Method(i).Interface().(func(*Request)) - if !ok { - s.Logger().Errorf( - `invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`, - pkgPath, objName, methodName, v.Method(i).Type().String(), - ) - continue + + funcInfo, err := s.checkAndCreateFuncInfo(v.Method(i).Interface(), pkgPath, objName, methodName) + if err != nil { + s.Logger().Error(err.Error()) + return } + key := s.mergeBuildInNameToPattern(methodName+":"+pattern, structName, methodName, false) m[key] = &handlerItem{ itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), itemType: handlerTypeObject, - itemFunc: itemFunc, + itemInfo: funcInfo, initFunc: initFunc, shutFunc: shutFunc, middleware: middleware, diff --git a/net/ghttp/ghttp_unit_router_handler_extended_test.go b/net/ghttp/ghttp_unit_router_handler_extended_test.go new file mode 100644 index 000000000..ddcf4da3d --- /dev/null +++ b/net/ghttp/ghttp_unit_router_handler_extended_test.go @@ -0,0 +1,82 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp_test + +import ( + "context" + "fmt" + "github.com/gogf/gf/errors/gerror" + "testing" + "time" + + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/test/gtest" +) + +func Test_Router_Handler_Extended_Handler_Basic(t *testing.T) { + p, _ := ports.PopRand() + s := g.Server(p) + s.BindHandler("/test", func(ctx context.Context) { + r := g.RequestFromCtx(ctx) + r.Response.Write("test") + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + client := g.Client() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + + t.Assert(client.GetContent("/test"), "test") + }) +} + +func Test_Router_Handler_Extended_Handler_WithObject(t *testing.T) { + type TestReq struct { + Age int + Name string + } + type TestRes struct { + Id int + Age int + Name string + } + p, _ := ports.PopRand() + s := g.Server(p) + s.Use(ghttp.MiddlewareHandlerResponse) + s.BindHandler("/test", func(ctx context.Context, req *TestReq) (res *TestRes, err error) { + return &TestRes{ + Id: 1, + Age: req.Age, + Name: req.Name, + }, nil + }) + s.BindHandler("/test/error", func(ctx context.Context, req *TestReq) (res *TestRes, err error) { + return &TestRes{ + Id: 1, + Age: req.Age, + Name: req.Name, + }, gerror.New("error") + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + client := g.Client() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + + t.Assert(client.GetContent("/test?age=18&name=john"), `{"Id":1,"Age":18,"Name":"john"}`) + t.Assert(client.GetContent("/test/error"), `{"code":-1,"message":"error"}`) + }) +} diff --git a/net/ghttp/ghttp_unit_router_names_test.go b/net/ghttp/ghttp_unit_router_names_test.go index bf6f40e8d..5c79cd9f8 100644 --- a/net/ghttp/ghttp_unit_router_names_test.go +++ b/net/ghttp/ghttp_unit_router_names_test.go @@ -88,7 +88,7 @@ func Test_NameToUri_Camel(t *testing.T) { func Test_NameToUri_Default(t *testing.T) { p, _ := ports.PopRand() s := g.Server(p) - s.SetNameToUriType(ghttp.URI_TYPE_DEFAULT) + s.SetNameToUriType(ghttp.UriTypeDefault) s.BindObject("/{.struct}/{.method}", new(NamesObject)) s.SetPort(p) s.SetDumpRouterMap(false) From 30dbccf99ea2969685b00da820249e7ab115d56b Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Wed, 14 Jul 2021 21:28:23 +0800 Subject: [PATCH 381/492] =?UTF-8?q?=E8=BE=93=E5=87=BA=E5=AF=B9=E5=BA=94err?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/glog/glog_logger.go | 4 +++- os/glog/glog_logger_handler.go | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 8e5f11ed3..891bd2ab3 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -228,7 +228,9 @@ func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { } // Allow output to stdout? if l.config.StdoutPrint { - input.Stdout() + if err := input.Stdout(); err != nil { + intlog.Error(ctx, err) + } } } else { if _, err := l.config.Writer.Write(input.Buffer().Bytes()); err != nil { diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index 53e090ca8..152629e90 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -10,6 +10,7 @@ import ( "bytes" "context" "github.com/fatih/color" + "github.com/gogf/gf/internal/intlog" "os" "time" ) @@ -59,12 +60,22 @@ func (i *HandlerInput) Buffer() *bytes.Buffer { } // Stdout print log to console -func (i *HandlerInput) Stdout() { - _, _ = os.Stdout.Write([]byte(i.TimeFormat)) +func (i *HandlerInput) Stdout() error { + if _, err := os.Stdout.Write([]byte(i.TimeFormat)); err != nil { + intlog.Error(i.Ctx, err) + return err + } fg := i.getLevelFormatColor() - _, _ = color.New(fg).Print(" " + i.LevelFormat + " ") + if _, err := color.New(fg).Print(" " + i.LevelFormat + " "); err != nil { + intlog.Error(i.Ctx, err) + return err + } msg := i.GetContent() - _, _ = os.Stdout.Write(msg.Bytes()) + if _, err := os.Stdout.Write(msg.Bytes()); err != nil { + intlog.Error(i.Ctx, err) + return err + } + return nil } // GetContent returns the primary content. From fae4dea37a702ebea32ce7378944c5c8aebdf75a Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 15 Jul 2021 13:31:32 +0800 Subject: [PATCH 382/492] improve color feature for package glog --- .example/os/glog/glog_color.go | 15 +++++++ os/gfile/gfile_contents.go | 2 +- os/glog/glog_chaining.go | 2 +- os/glog/glog_logger.go | 38 +++++++++++++----- os/glog/glog_logger_chaining.go | 14 ------- os/glog/glog_logger_color.go | 41 ++++++++++++++++++++ os/glog/glog_logger_config.go | 44 ++++++++++----------- os/glog/glog_logger_handler.go | 69 ++++++++++----------------------- os/glog/glog_logger_level.go | 29 ++------------ 9 files changed, 130 insertions(+), 124 deletions(-) create mode 100644 .example/os/glog/glog_color.go create mode 100644 os/glog/glog_logger_color.go diff --git a/.example/os/glog/glog_color.go b/.example/os/glog/glog_color.go new file mode 100644 index 000000000..b178db5e0 --- /dev/null +++ b/.example/os/glog/glog_color.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" +) + +func main() { + g.Log().Print("Print") + g.Log().Debug("Debug") + g.Log().Info("Info") + g.Log().Notice("Notice") + g.Log().Warning("Warning") + g.Log().Error("Error") + g.Log().Critical("Critical") +} diff --git a/os/gfile/gfile_contents.go b/os/gfile/gfile_contents.go index 6b3a7c5d6..048e15d76 100644 --- a/os/gfile/gfile_contents.go +++ b/os/gfile/gfile_contents.go @@ -16,7 +16,7 @@ import ( ) var ( - // Buffer size for reading file content. + // DefaultReadBuffer is the buffer size for reading file content. DefaultReadBuffer = 1024 ) diff --git a/os/glog/glog_chaining.go b/os/glog/glog_chaining.go index f55012fa3..db25843ef 100644 --- a/os/glog/glog_chaining.go +++ b/os/glog/glog_chaining.go @@ -78,7 +78,7 @@ func StackWithFilter(filter string) *Logger { return logger.StackWithFilter(filter) } -// StdPrint is a chaining function, +// Stdout is a chaining function, // which enables/disables stdout for the current logging content output. // It's enabled in default. func Stdout(enabled ...bool) *Logger { diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 891bd2ab3..be9a4c210 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -7,7 +7,6 @@ package glog import ( - "bytes" "context" "fmt" "github.com/gogf/gf/container/gtype" @@ -119,6 +118,7 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { index: -1, Ctx: ctx, Time: now, + Color: defaultLevelColor[level], Level: level, } ) @@ -219,30 +219,48 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { } } -// printToWriter writes buffer to writer. -func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { +// doPrint outputs the logging content according configuration. +func (l *Logger) doPrint(ctx context.Context, input *HandlerInput) { if l.config.Writer == nil { // Output content to disk file. if l.config.Path != "" { - l.printToFile(ctx, input.Time, input.Buffer()) + l.printToFile(ctx, input.Time, input) } // Allow output to stdout? if l.config.StdoutPrint { - if err := input.Stdout(); err != nil { - intlog.Error(ctx, err) - } + l.printToStdout(ctx, input) } } else { - if _, err := l.config.Writer.Write(input.Buffer().Bytes()); err != nil { - // panic(err) + // Output to custom writer. + l.printToWriter(ctx, input) + } +} + +// printToWriter writes buffer to writer. +func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { + if l.config.Writer != nil { + var ( + buffer = input.getBuffer(l.config.WriterColorEnable) + ) + if _, err := l.config.Writer.Write(buffer.Bytes()); err != nil { + intlog.Error(ctx, err) + } + } +} + +// printToStdout outputs logging content to stdout. +func (l *Logger) printToStdout(ctx context.Context, input *HandlerInput) { + if l.config.StdoutPrint { + if _, err := os.Stdout.Write(input.getBuffer(true).Bytes()); err != nil { intlog.Error(ctx, err) } } } // printToFile outputs logging content to disk file. -func (l *Logger) printToFile(ctx context.Context, t time.Time, buffer *bytes.Buffer) { +func (l *Logger) printToFile(ctx context.Context, t time.Time, input *HandlerInput) { var ( + buffer = input.getBuffer(l.config.WriterColorEnable) logFilePath = l.getFilePath(t) memoryLockKey = memoryLockPrefixForPrintingToFile + logFilePath ) diff --git a/os/glog/glog_logger_chaining.go b/os/glog/glog_logger_chaining.go index bb9529023..c0213b982 100644 --- a/os/glog/glog_logger_chaining.go +++ b/os/glog/glog_logger_chaining.go @@ -8,7 +8,6 @@ package glog import ( "context" - "github.com/fatih/color" "github.com/gogf/gf/internal/intlog" "io" @@ -246,16 +245,3 @@ func (l *Logger) Async(enabled ...bool) *Logger { } return logger } - -// Color is a chaining function, -// which set level prefix color logging output feature. -func (l *Logger) Color(color color.Attribute) *Logger { - logger := (*Logger)(nil) - if l.parent == nil { - logger = l.Clone() - } else { - logger = l - } - logger.config.currentColor = color - return logger -} diff --git a/os/glog/glog_logger_color.go b/os/glog/glog_logger_color.go new file mode 100644 index 000000000..6ab51a0f3 --- /dev/null +++ b/os/glog/glog_logger_color.go @@ -0,0 +1,41 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package glog + +import "github.com/fatih/color" + +const ( + COLOR_BLACK = 30 + iota + COLOR_RED + COLOR_GREEN + COLOR_YELLOW + COLOR_BLUE + COLOR_MAGENTA + COLOR_CYAN + COLOR_WHITE +) + +// defaultLevelColor defines the default level and its mapping prefix string. +var defaultLevelColor = map[int]int{ + LEVEL_DEBU: COLOR_YELLOW, + LEVEL_INFO: COLOR_GREEN, + LEVEL_NOTI: COLOR_CYAN, + LEVEL_WARN: COLOR_YELLOW, + LEVEL_ERRO: COLOR_RED, + LEVEL_CRIT: COLOR_RED, + LEVEL_PANI: COLOR_RED, + LEVEL_FATA: COLOR_RED, +} + +// getColoredStr returns a string that is colored by given color. +func (l *Logger) getColoredStr(c int, s string) string { + return color.New(color.Attribute(c)).Sprint(s) +} + +func (l *Logger) getColorByLevel(level int) int { + return defaultLevelColor[level] +} diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index d16564662..b4d17b6f7 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -7,7 +7,6 @@ package glog import ( - "github.com/fatih/color" "io" "strings" "time" @@ -21,28 +20,27 @@ import ( // Config is the configuration object for logger. type Config struct { - Handlers []Handler `json:"-"` // Logger handlers which implement feature similar as middleware. - Writer io.Writer `json:"-"` // Customized io.Writer. - Flags int `json:"flags"` // Extra flags for logging output features. - Path string `json:"path"` // Logging directory path. - File string `json:"file"` // Format for logging file. - Level int `json:"level"` // Output level. - Prefix string `json:"prefix"` // Prefix string for every logging content. - StSkip int `json:"stSkip"` // Skip count for stack. - StStatus int `json:"stStatus"` // Stack status(1: enabled - default; 0: disabled) - StFilter string `json:"stFilter"` // Stack string filter. - CtxKeys []interface{} `json:"ctxKeys"` // Context keys for logging, which is used for value retrieving from context. - HeaderPrint bool `json:"header"` // Print header or not(true in default). - StdoutPrint bool `json:"stdout"` // Output to stdout or not(true in default). - LevelPrefixes map[int]string `json:"levelPrefixes"` // Logging level to its prefix string mapping. - RotateSize int64 `json:"rotateSize"` // Rotate the logging file if its size > 0 in bytes. - RotateExpire time.Duration `json:"rotateExpire"` // Rotate the logging file if its mtime exceeds this duration. - RotateBackupLimit int `json:"rotateBackupLimit"` // Max backup for rotated files, default is 0, means no backups. - RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration. - RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression. - RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default. - FileColorEnable bool `json:"fileColorEnable"` // Logging level prefix with color or not (false in default). - currentColor color.Attribute `json:"-"` + Handlers []Handler `json:"-"` // Logger handlers which implement feature similar as middleware. + Writer io.Writer `json:"-"` // Customized io.Writer. + Flags int `json:"flags"` // Extra flags for logging output features. + Path string `json:"path"` // Logging directory path. + File string `json:"file"` // Format for logging file. + Level int `json:"level"` // Output level. + Prefix string `json:"prefix"` // Prefix string for every logging content. + StSkip int `json:"stSkip"` // Skip count for stack. + StStatus int `json:"stStatus"` // Stack status(1: enabled - default; 0: disabled) + StFilter string `json:"stFilter"` // Stack string filter. + CtxKeys []interface{} `json:"ctxKeys"` // Context keys for logging, which is used for value retrieving from context. + HeaderPrint bool `json:"header"` // Print header or not(true in default). + StdoutPrint bool `json:"stdout"` // Output to stdout or not(true in default). + LevelPrefixes map[int]string `json:"levelPrefixes"` // Logging level to its prefix string mapping. + RotateSize int64 `json:"rotateSize"` // Rotate the logging file if its size > 0 in bytes. + RotateExpire time.Duration `json:"rotateExpire"` // Rotate the logging file if its mtime exceeds this duration. + RotateBackupLimit int `json:"rotateBackupLimit"` // Max backup for rotated files, default is 0, means no backups. + RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration. + RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression. + RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronously checks the backups and expiration at intervals. It's 1 hour in default. + WriterColorEnable bool `json:"writerColorEnable"` // Logging level prefix with color to writer or not (false in default). } // DefaultConfig returns the default configuration for logger. diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index 152629e90..e2d990567 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -9,9 +9,6 @@ package glog import ( "bytes" "context" - "github.com/fatih/color" - "github.com/gogf/gf/internal/intlog" - "os" "time" ) @@ -23,6 +20,7 @@ type HandlerInput struct { Ctx context.Context Time time.Time TimeFormat string + Color int Level int LevelFormat string CallerFunc string @@ -35,52 +33,34 @@ type HandlerInput struct { // defaultHandler is the default handler for logger. func defaultHandler(ctx context.Context, input *HandlerInput) { - input.logger.printToWriter(ctx, input) + input.logger.doPrint(ctx, input) } -func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, s string) { - if buffer.Len() > 0 { - buffer.WriteByte(' ') +func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, strings ...string) { + for _, s := range strings { + if buffer.Len() > 0 { + buffer.WriteByte(' ') + } + buffer.WriteString(s) } - buffer.WriteString(s) } func (i *HandlerInput) Buffer() *bytes.Buffer { + return i.getBuffer(false) +} + +func (i *HandlerInput) getBuffer(withColor bool) *bytes.Buffer { buffer := bytes.NewBuffer(nil) buffer.WriteString(i.TimeFormat) - levelString := i.LevelFormat - if i.logger.config.FileColorEnable { - fg := i.getLevelFormatColor() - levelString = color.New(fg).Sprintf(i.LevelFormat) + if i.LevelFormat != "" { + if withColor { + i.addStringToBuffer(buffer, i.logger.getColoredStr( + i.logger.getColorByLevel(i.Level), i.LevelFormat, + )) + } else { + i.addStringToBuffer(buffer, i.LevelFormat) + } } - i.addStringToBuffer(buffer, levelString) - msg := i.GetContent() - i.addStringToBuffer(buffer, msg.String()) - return buffer -} - -// Stdout print log to console -func (i *HandlerInput) Stdout() error { - if _, err := os.Stdout.Write([]byte(i.TimeFormat)); err != nil { - intlog.Error(i.Ctx, err) - return err - } - fg := i.getLevelFormatColor() - if _, err := color.New(fg).Print(" " + i.LevelFormat + " "); err != nil { - intlog.Error(i.Ctx, err) - return err - } - msg := i.GetContent() - if _, err := os.Stdout.Write(msg.Bytes()); err != nil { - intlog.Error(i.Ctx, err) - return err - } - return nil -} - -// GetContent returns the primary content. -func (i *HandlerInput) GetContent() *bytes.Buffer { - buffer := bytes.NewBuffer(nil) if i.CallerFunc != "" { i.addStringToBuffer(buffer, i.CallerFunc) } @@ -100,15 +80,6 @@ func (i *HandlerInput) GetContent() *bytes.Buffer { return buffer } -// getLevelFormatColor returns the prefix string color. -func (i *HandlerInput) getLevelFormatColor() color.Attribute { - fg := defaultLevelColor[i.Level] - if i.logger.config.currentColor != 0 { - fg = i.logger.config.currentColor - } - return fg -} - func (i *HandlerInput) String() string { return i.Buffer().String() } diff --git a/os/glog/glog_logger_level.go b/os/glog/glog_logger_level.go index f726bd7bb..67fae2cd7 100644 --- a/os/glog/glog_logger_level.go +++ b/os/glog/glog_logger_level.go @@ -7,7 +7,6 @@ package glog import ( - "github.com/fatih/color" "github.com/gogf/gf/errors/gerror" "strings" ) @@ -29,17 +28,6 @@ const ( LEVEL_FATA // 1024 ) -const ( - COLOR_BLACK = 30 + iota - COLOR_RED - COLOR_GREEN - COLOR_YELLOW - COLOR_BLUE - COLOR_MAGENTA - COLOR_CYAN - COLOR_WHITE -) - // defaultLevelPrefixes defines the default level and its mapping prefix string. var defaultLevelPrefixes = map[int]string{ LEVEL_DEBU: "DEBU", @@ -52,18 +40,6 @@ var defaultLevelPrefixes = map[int]string{ LEVEL_FATA: "FATA", } -// defaultLevelColor defines the default level and its mapping prefix string. -var defaultLevelColor = map[int]color.Attribute{ - LEVEL_DEBU: COLOR_YELLOW, - LEVEL_INFO: COLOR_GREEN, - LEVEL_NOTI: COLOR_CYAN, - LEVEL_WARN: COLOR_YELLOW, - LEVEL_ERRO: COLOR_RED, - LEVEL_CRIT: COLOR_RED, - LEVEL_PANI: COLOR_RED, - LEVEL_FATA: COLOR_RED, -} - // levelStringMap defines level string name to its level mapping. var levelStringMap = map[string]int{ "ALL": LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT, @@ -125,8 +101,9 @@ func (l *Logger) GetLevelPrefix(level int) string { // getLevelPrefixWithBrackets returns the prefix string with brackets for specified level. func (l *Logger) getLevelPrefixWithBrackets(level int) string { + levelStr := "" if s, ok := l.config.LevelPrefixes[level]; ok { - return "[" + s + "]" + levelStr = "[" + s + "]" } - return "" + return levelStr } From 141ba2e951d04e2bc2929f7bb9587935e4f6ee47 Mon Sep 17 00:00:00 2001 From: jflyfox <zcool321@sina.com> Date: Thu, 15 Jul 2021 14:53:21 +0800 Subject: [PATCH 383/492] update pgsql pri error #1340 --- database/gdb/gdb_driver_pgsql.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 961242426..3503313d9 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -142,9 +142,18 @@ func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...s result Result link, err = d.SlaveLink(useSchema) structureSql = fmt.Sprintf(` -SELECT a.attname AS field, t.typname AS type FROM pg_class c, pg_attribute a -LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,pg_type t -WHERE c.relname = '%s' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid +SELECT a.attname AS field, t.typname AS type,a.attnotnull as null, + (case when d.contype is not null then 'pri' else '' end) as key + ,ic.column_default as default_value,b.description as comment + ,coalesce(character_maximum_length, numeric_precision, -1) as length + ,numeric_scale as scale +FROM pg_attribute a + left join pg_class c on a.attrelid = c.oid + left join pg_constraint d on d.conrelid = c.oid and a.attnum = d.conkey[1] + left join pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid + left join pg_type t ON a.atttypid = t.oid + left join information_schema.columns ic on ic.column_name = a.attname and ic.table_name = c.relname +WHERE c.relname = '%s' and a.attnum > 0 ORDER BY a.attnum`, strings.ToLower(table), ) @@ -160,9 +169,13 @@ ORDER BY a.attnum`, fields = make(map[string]*TableField) for i, m := range result { fields[m["field"].String()] = &TableField{ - Index: i, - Name: m["field"].String(), - Type: m["type"].String(), + Index: i, + Name: m["field"].String(), + Type: m["type"].String(), + Null: m["null"].Bool(), + Key: m["key"].String(), + Default: m["default_value"].Val(), + Comment: m["comment"].String(), } } return fields From 92c3c136f9b2d9a660cbd016591483f1de71ca77 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 15 Jul 2021 21:20:29 +0800 Subject: [PATCH 384/492] improve color feature for package glog --- os/glog/glog_instance.go | 2 +- os/glog/glog_logger.go | 1 - os/glog/glog_logger_color.go | 20 ++++++++++++++++---- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/os/glog/glog_instance.go b/os/glog/glog_instance.go index f53e3e37d..42c741a03 100644 --- a/os/glog/glog_instance.go +++ b/os/glog/glog_instance.go @@ -9,7 +9,7 @@ package glog import "github.com/gogf/gf/container/gmap" const ( - // Default group name for instance usage. + // DefaultName is the default group name for instance usage. DefaultName = "default" ) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index be9a4c210..3a87e9aab 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -43,7 +43,6 @@ const ( defaultFileExpire = time.Minute pathFilterKey = "/os/glog/glog" memoryLockPrefixForPrintingToFile = "glog.printToFile:" - mustWithColor = true ) const ( diff --git a/os/glog/glog_logger_color.go b/os/glog/glog_logger_color.go index 6ab51a0f3..98bf966c5 100644 --- a/os/glog/glog_logger_color.go +++ b/os/glog/glog_logger_color.go @@ -19,16 +19,28 @@ const ( COLOR_WHITE ) +// Foreground Hi-Intensity text colors +const ( + COLOR_HI_BLACK = 90 + iota + COLOR_HI_RED + COLOR_HI_GREEN + COLOR_HI_YELLOW + COLOR_HI_BLUE + COLOR_HI_MAGENTA + COLOR_HI_CYAN + COLOR_HI_WHITE +) + // defaultLevelColor defines the default level and its mapping prefix string. var defaultLevelColor = map[int]int{ LEVEL_DEBU: COLOR_YELLOW, LEVEL_INFO: COLOR_GREEN, LEVEL_NOTI: COLOR_CYAN, - LEVEL_WARN: COLOR_YELLOW, + LEVEL_WARN: COLOR_MAGENTA, LEVEL_ERRO: COLOR_RED, - LEVEL_CRIT: COLOR_RED, - LEVEL_PANI: COLOR_RED, - LEVEL_FATA: COLOR_RED, + LEVEL_CRIT: COLOR_HI_RED, + LEVEL_PANI: COLOR_HI_RED, + LEVEL_FATA: COLOR_HI_RED, } // getColoredStr returns a string that is colored by given color. From 03d51bd18c10c107c9bb6f86778578b338300871 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 15 Jul 2021 21:40:26 +0800 Subject: [PATCH 385/492] add internal logging for package gtimer --- internal/intlog/intlog.go | 18 ++++++++++++++++++ os/gtimer/gtimer_entry.go | 11 +++++++++++ 2 files changed, 29 insertions(+) diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index a22207ccf..fb1f08af0 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -63,6 +63,24 @@ func Errorf(ctx context.Context, format string, v ...interface{}) { doPrint(ctx, fmt.Sprintf(format, v...), true) } +// PrintFunc prints the output from function `f`. +// It only calls function `f` if debug mode is enabled. +func PrintFunc(ctx context.Context, f func() string) { + if !isGFDebug { + return + } + doPrint(ctx, fmt.Sprint(f()), false) +} + +// ErrorFunc prints the output from function `f`. +// It only calls function `f` if debug mode is enabled. +func ErrorFunc(ctx context.Context, f func() string) { + if !isGFDebug { + return + } + doPrint(ctx, fmt.Sprint(f()), true) +} + func doPrint(ctx context.Context, content string, stack bool) { if !isGFDebug { return diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go index beebf21a6..8a7cee2f8 100644 --- a/os/gtimer/gtimer_entry.go +++ b/os/gtimer/gtimer_entry.go @@ -7,8 +7,13 @@ package gtimer import ( + "context" + "fmt" "github.com/gogf/gf/container/gtype" + "github.com/gogf/gf/internal/intlog" "math" + "reflect" + "runtime" ) // Entry is the timing job. @@ -56,7 +61,13 @@ func (entry *Entry) Run() { entry.SetStatus(StatusReady) } }() + intlog.PrintFunc(context.TODO(), func() string { + return fmt.Sprintf(`job start: %s`, runtime.FuncForPC(reflect.ValueOf(entry.job).Pointer()).Name()) + }) entry.job() + intlog.PrintFunc(context.TODO(), func() string { + return fmt.Sprintf(`job done: %s`, runtime.FuncForPC(reflect.ValueOf(entry.job).Pointer()).Name()) + }) }() } From b192b7dd6066efb0d9379646a2221f5e6ee33967 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 15 Jul 2021 21:46:56 +0800 Subject: [PATCH 386/492] remove internal logging for package gtimer --- internal/intlog/intlog.go | 12 ++++++++++-- os/gtimer/gtimer_entry.go | 11 ----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index fb1f08af0..f5f3559c9 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -69,7 +69,11 @@ func PrintFunc(ctx context.Context, f func() string) { if !isGFDebug { return } - doPrint(ctx, fmt.Sprint(f()), false) + s := f() + if s == "" { + return + } + doPrint(ctx, s, false) } // ErrorFunc prints the output from function `f`. @@ -78,7 +82,11 @@ func ErrorFunc(ctx context.Context, f func() string) { if !isGFDebug { return } - doPrint(ctx, fmt.Sprint(f()), true) + s := f() + if s == "" { + return + } + doPrint(ctx, s, true) } func doPrint(ctx context.Context, content string, stack bool) { diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go index 8a7cee2f8..beebf21a6 100644 --- a/os/gtimer/gtimer_entry.go +++ b/os/gtimer/gtimer_entry.go @@ -7,13 +7,8 @@ package gtimer import ( - "context" - "fmt" "github.com/gogf/gf/container/gtype" - "github.com/gogf/gf/internal/intlog" "math" - "reflect" - "runtime" ) // Entry is the timing job. @@ -61,13 +56,7 @@ func (entry *Entry) Run() { entry.SetStatus(StatusReady) } }() - intlog.PrintFunc(context.TODO(), func() string { - return fmt.Sprintf(`job start: %s`, runtime.FuncForPC(reflect.ValueOf(entry.job).Pointer()).Name()) - }) entry.job() - intlog.PrintFunc(context.TODO(), func() string { - return fmt.Sprintf(`job done: %s`, runtime.FuncForPC(reflect.ValueOf(entry.job).Pointer()).Name()) - }) }() } From 88009ee2781f4c8ba2b4eb15045266d51c919017 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 15 Jul 2021 22:19:00 +0800 Subject: [PATCH 387/492] fix issue #1341 --- database/gdb/gdb_func.go | 3 ++- database/gdb/gdb_model_utility.go | 4 +++- go.mod | 6 +++--- go.sum | 18 ++++++++---------- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index c5454c38f..0f8c9541b 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -83,8 +83,9 @@ func (m *Model) guessPrimaryTableName(tableStr string) string { ) if len(array3) >= 2 { guessedTableName = array3[1] + } else { + guessedTableName = array3[0] } - guessedTableName = array3[0] charL, charR := m.db.GetChars() if charL != "" || charR != "" { guessedTableName = gstr.Trim(guessedTableName, charL+charR) diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 03f5e1ed3..631160b19 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -103,7 +103,9 @@ func (m *Model) filterDataForInsertOrUpdate(data interface{}) (interface{}, erro // Note that, it does not filter list item, which is also type of map, for "omit empty" feature. func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEmpty bool) (Map, error) { var err error - data, err = m.db.GetCore().mappingAndFilterData(m.schema, m.tables, data, m.filter) + data, err = m.db.GetCore().mappingAndFilterData( + m.schema, m.guessPrimaryTableName(m.tablesInit), data, m.filter, + ) if err != nil { return nil, err } diff --git a/go.mod b/go.mod index 0ccef1c24..fb2c5c700 100644 --- a/go.mod +++ b/go.mod @@ -13,9 +13,9 @@ require ( github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf github.com/mattn/go-runewidth v0.0.10 // indirect github.com/olekukonko/tablewriter v0.0.5 - go.opentelemetry.io/otel v0.19.0 - go.opentelemetry.io/otel/oteltest v0.19.0 - go.opentelemetry.io/otel/trace v0.19.0 + go.opentelemetry.io/otel v1.0.0-RC1 + go.opentelemetry.io/otel/oteltest v1.0.0-RC1 + go.opentelemetry.io/otel/trace v1.0.0-RC1 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 golang.org/x/text v0.3.4 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c diff --git a/go.sum b/go.sum index bd012e8c6..8e6df8762 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= @@ -34,14 +34,12 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng= -go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg= -go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg= -go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc= -go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q= -go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA= -go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc= -go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg= +go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc= +go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I= +go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0cljhGk9sB0Yk= +go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4= +go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58= +go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= From f72d991c365509fe0e73f1687ff2e6ea9cfbf048 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 19 Jul 2021 20:06:44 +0800 Subject: [PATCH 388/492] rename attribute names from lower-camel case to upper-camel case --- net/ghttp/ghttp.go | 34 +++++------ .../ghttp_middleware_handler_response.go | 35 +++++++++--- net/ghttp/ghttp_request_middleware.go | 30 +++++----- net/ghttp/ghttp_server.go | 16 +++--- net/ghttp/ghttp_server_router.go | 44 +++++++-------- net/ghttp/ghttp_server_router_hook.go | 16 +++--- net/ghttp/ghttp_server_router_middleware.go | 12 ++-- net/ghttp/ghttp_server_router_serve.go | 48 ++++++++-------- net/ghttp/ghttp_server_service_controller.go | 56 +++++++++---------- net/ghttp/ghttp_server_service_handler.go | 10 ++-- net/ghttp/ghttp_server_service_object.go | 56 +++++++++---------- ...ghttp_unit_router_handler_extended_test.go | 4 +- 12 files changed, 190 insertions(+), 171 deletions(-) diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index 0f9ed208b..0ee23127b 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -71,35 +71,35 @@ type ( // handlerItem is the registered handler for route handling, // including middleware and hook functions. handlerItem struct { - itemId int // Unique handler item id mark. - itemName string // Handler name, which is automatically retrieved from runtime stack when registered. - itemType int // Handler type: object/handler/controller/middleware/hook. - itemInfo handlerFuncInfo // Handler function information. - initFunc HandlerFunc // Initialization function when request enters the object (only available for object register type). - shutFunc HandlerFunc // Shutdown function when request leaves out the object (only available for object register type). - middleware []HandlerFunc // Bound middleware array. - ctrlInfo *handlerController // Controller information for reflect usage. - hookName string // Hook type name, only available for hook type. - router *Router // Router object. - source string // Registering source file `path:line`. + Id int // Unique handler item id mark. + Name string // Handler name, which is automatically retrieved from runtime stack when registered. + Type int // Handler type: object/handler/controller/middleware/hook. + Info handlerFuncInfo // Handler function information. + InitFunc HandlerFunc // Initialization function when request enters the object (only available for object register type). + ShutFunc HandlerFunc // Shutdown function when request leaves out the object (only available for object register type). + Middleware []HandlerFunc // Bound middleware array. + CtrlInfo *handlerController // Controller information for reflect usage. + HookName string // Hook type name, only available for hook type. + Router *Router // Router object. + Source string // Registering source file `path:line`. } // handlerParsedItem is the item parsed from URL.Path. handlerParsedItem struct { - handler *handlerItem // Handler information. - values map[string]string // Router values parsed from URL.Path. + Handler *handlerItem // Handler information. + Values map[string]string // Router values parsed from URL.Path. } // handlerController is the controller information used for reflect. handlerController struct { - name string // Handler method name. - reflect reflect.Type // Reflect type of the controller. + Name string // Handler method name. + Type reflect.Type // Reflect type of the controller. } // registeredRouteItem stores the information of the router and is used for route map. registeredRouteItem struct { - source string // Source file path and its line number. - handler *handlerItem // Handler object. + Source string // Source file path and its line number. + Handler *handlerItem // Handler object. } // errorStack is the interface for Stack feature. diff --git a/net/ghttp/ghttp_middleware_handler_response.go b/net/ghttp/ghttp_middleware_handler_response.go index d84815275..1a771e877 100644 --- a/net/ghttp/ghttp_middleware_handler_response.go +++ b/net/ghttp/ghttp_middleware_handler_response.go @@ -11,19 +11,38 @@ import ( "github.com/gogf/gf/internal/intlog" ) +type DefaultHandlerResponse struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data"` +} + // MiddlewareHandlerResponse is the default middleware handling handler response object and its error. func MiddlewareHandlerResponse(r *Request) { r.Middleware.Next() - res, err := r.GetHandlerResponse() + var ( + err error + res interface{} + internalErr error + ) + res, err = r.GetHandlerResponse() if err != nil { - r.Response.Writef( - `{"code":%d,"message":"%s"}`, - gerror.Code(err), - err.Error(), - ) + internalErr = r.Response.WriteJson(DefaultHandlerResponse{ + Code: gerror.Code(err), + Message: err.Error(), + Data: nil, + }) + if internalErr != nil { + intlog.Error(r.Context(), internalErr) + } return } - if exception := r.Response.WriteJson(res); exception != nil { - intlog.Error(r.Context(), exception) + internalErr = r.Response.WriteJson(DefaultHandlerResponse{ + Code: 0, + Message: "", + Data: res, + }) + if internalErr != nil { + intlog.Error(r.Context(), internalErr) } } diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index 75321da0a..fbaf7a7ba 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -35,20 +35,20 @@ func (m *middleware) Next() { } item = m.request.handlers[m.handlerIndex] // Filter the HOOK handlers, which are designed to be called in another standalone procedure. - if item.handler.itemType == handlerTypeHook { + if item.Handler.Type == handlerTypeHook { m.handlerIndex++ continue } // Current router switching. - m.request.Router = item.handler.router + m.request.Router = item.Handler.Router // Router values switching. - m.request.routerMap = item.values + m.request.routerMap = item.Values gutil.TryCatch(func() { // Execute bound middleware array of the item if it's not empty. - if m.handlerMDIndex < len(item.handler.middleware) { - md := item.handler.middleware[m.handlerMDIndex] + if m.handlerMDIndex < len(item.Handler.Middleware) { + md := item.Handler.Middleware[m.handlerMDIndex] m.handlerMDIndex++ niceCallFunc(func() { md(m.request) @@ -58,20 +58,20 @@ func (m *middleware) Next() { } m.handlerIndex++ - switch item.handler.itemType { + switch item.Handler.Type { // Service controller. case handlerTypeController: m.served = true if m.request.IsExited() { break } - c := reflect.New(item.handler.ctrlInfo.reflect) + c := reflect.New(item.Handler.CtrlInfo.Type) niceCallFunc(func() { c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(m.request)}) }) if !m.request.IsExited() { niceCallFunc(func() { - c.MethodByName(item.handler.ctrlInfo.name).Call(nil) + c.MethodByName(item.Handler.CtrlInfo.Name).Call(nil) }) } if !m.request.IsExited() { @@ -86,17 +86,17 @@ func (m *middleware) Next() { if m.request.IsExited() { break } - if item.handler.initFunc != nil { + if item.Handler.InitFunc != nil { niceCallFunc(func() { - item.handler.initFunc(m.request) + item.Handler.InitFunc(m.request) }) } if !m.request.IsExited() { - m.callHandlerFunc(item.handler.itemInfo) + m.callHandlerFunc(item.Handler.Info) } - if !m.request.IsExited() && item.handler.shutFunc != nil { + if !m.request.IsExited() && item.Handler.ShutFunc != nil { niceCallFunc(func() { - item.handler.shutFunc(m.request) + item.Handler.ShutFunc(m.request) }) } @@ -107,13 +107,13 @@ func (m *middleware) Next() { break } niceCallFunc(func() { - m.callHandlerFunc(item.handler.itemInfo) + m.callHandlerFunc(item.Handler.Info) }) // Global middleware array. case handlerTypeMiddleware: niceCallFunc(func() { - item.handler.itemInfo.Func(m.request) + item.Handler.Info.Func(m.request) }) // It does not continue calling next middleware after another middleware done. // There should be a "Next" function to be called in the middleware in order to manage the workflow. diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index ab7738e38..36d6cc07c 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -221,7 +221,7 @@ func (s *Server) dumpRouterMap() { data[2] = item.Address data[3] = item.Method data[4] = item.Route - data[5] = item.handler.itemName + data[5] = item.handler.Name data[6] = item.Middleware table.Append(data) } @@ -248,21 +248,21 @@ func (s *Server) GetRouterArray() []RouterItem { Server: s.name, Address: address, Domain: array[4], - Type: registeredItem.handler.itemType, + Type: registeredItem.Handler.Type, Middleware: array[1], Method: array[2], Route: array[3], Priority: len(registeredItems) - index - 1, - handler: registeredItem.handler, + handler: registeredItem.Handler, } - switch item.handler.itemType { + switch item.handler.Type { case handlerTypeController, handlerTypeObject, handlerTypeHandler: item.IsServiceHandler = true case handlerTypeMiddleware: item.Middleware = "GLOBAL MIDDLEWARE" } - if len(item.handler.middleware) > 0 { - for _, v := range item.handler.middleware { + if len(item.handler.Middleware) > 0 { + for _, v := range item.handler.Middleware { if item.Middleware != "" { item.Middleware += "," } @@ -280,9 +280,9 @@ func (s *Server) GetRouterArray() []RouterItem { if r = strings.Compare(item1.Domain, item2.Domain); r == 0 { if r = strings.Compare(item1.Route, item2.Route); r == 0 { if r = strings.Compare(item1.Method, item2.Method); r == 0 { - if item1.handler.itemType == handlerTypeMiddleware && item2.handler.itemType != handlerTypeMiddleware { + if item1.handler.Type == handlerTypeMiddleware && item2.handler.Type != handlerTypeMiddleware { return -1 - } else if item1.handler.itemType == handlerTypeMiddleware && item2.handler.itemType == handlerTypeMiddleware { + } else if item1.handler.Type == handlerTypeMiddleware && item2.handler.Type == handlerTypeMiddleware { return 1 } else if r = strings.Compare(item1.Middleware, item2.Middleware); r == 0 { r = item2.Priority - item1.Priority diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index 85b6480a5..bee668ddd 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -66,10 +66,10 @@ func (s *Server) parsePattern(pattern string) (domain, method, path string, err // This function is called during server starts up, which cares little about the performance. What really cares // is the well designed router storage structure for router searching when the request is under serving. func (s *Server) setHandler(pattern string, handler *handlerItem) { - handler.itemId = handlerIdGenerator.Add(1) - if handler.source == "" { + handler.Id = handlerIdGenerator.Add(1) + if handler.Source == "" { _, file, line := gdebug.CallerWithFilter(stackFilterKey) - handler.source = fmt.Sprintf(`%s:%d`, file, line) + handler.Source = fmt.Sprintf(`%s:%d`, file, line) } domain, method, uri, err := s.parsePattern(pattern) if err != nil { @@ -82,27 +82,27 @@ func (s *Server) setHandler(pattern string, handler *handlerItem) { } // Repeated router checks, this feature can be disabled by server configuration. - routerKey := s.routerMapKey(handler.hookName, method, uri, domain) + routerKey := s.routerMapKey(handler.HookName, method, uri, domain) if !s.config.RouteOverWrite { - switch handler.itemType { + switch handler.Type { case handlerTypeHandler, handlerTypeObject, handlerTypeController: if item, ok := s.routesMap[routerKey]; ok { s.Logger().Fatalf( `duplicated route registry "%s" at %s , already registered at %s`, - pattern, handler.source, item[0].source, + pattern, handler.Source, item[0].Source, ) return } } } // Create a new router by given parameter. - handler.router = &Router{ + handler.Router = &Router{ Uri: uri, Domain: domain, Method: strings.ToUpper(method), Priority: strings.Count(uri[1:], "/"), } - handler.router.RegRule, handler.router.RegNames = s.patternToRegular(uri) + handler.Router.RegRule, handler.Router.RegNames = s.patternToRegular(uri) if _, ok := s.serveTree[domain]; !ok { s.serveTree[domain] = make(map[string]interface{}) @@ -193,10 +193,10 @@ func (s *Server) setHandler(pattern string, handler *handlerItem) { } routeItem := registeredRouteItem{ - source: handler.source, - handler: handler, + Source: handler.Source, + Handler: handler, } - switch handler.itemType { + switch handler.Type { case handlerTypeHandler, handlerTypeObject, handlerTypeController: // Overwrite the route. s.routesMap[routerKey] = []registeredRouteItem{routeItem} @@ -216,18 +216,18 @@ func (s *Server) setHandler(pattern string, handler *handlerItem) { // 3. Route type: {xxx} > :xxx > *xxx. func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerItem) bool { // If they're all type of middleware, the priority is according their registered sequence. - if newItem.itemType == handlerTypeMiddleware && oldItem.itemType == handlerTypeMiddleware { + if newItem.Type == handlerTypeMiddleware && oldItem.Type == handlerTypeMiddleware { return false } // The middleware has the most high priority. - if newItem.itemType == handlerTypeMiddleware && oldItem.itemType != handlerTypeMiddleware { + if newItem.Type == handlerTypeMiddleware && oldItem.Type != handlerTypeMiddleware { return true } // URI: The deeper the higher (simply check the count of char '/' in the URI). - if newItem.router.Priority > oldItem.router.Priority { + if newItem.Router.Priority > oldItem.Router.Priority { return true } - if newItem.router.Priority < oldItem.router.Priority { + if newItem.Router.Priority < oldItem.Router.Priority { return false } @@ -238,8 +238,8 @@ func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerIte // /admin-goods-{page} > /admin-{page} // /{hash}.{type} > /{hash} var uriNew, uriOld string - uriNew, _ = gregex.ReplaceString(`\{[^/]+?\}`, "", newItem.router.Uri) - uriOld, _ = gregex.ReplaceString(`\{[^/]+?\}`, "", oldItem.router.Uri) + uriNew, _ = gregex.ReplaceString(`\{[^/]+?\}`, "", newItem.Router.Uri) + uriOld, _ = gregex.ReplaceString(`\{[^/]+?\}`, "", oldItem.Router.Uri) uriNew, _ = gregex.ReplaceString(`:[^/]+?`, "", uriNew) uriOld, _ = gregex.ReplaceString(`:[^/]+?`, "", uriOld) uriNew, _ = gregex.ReplaceString(`\*[^/]*`, "", uriNew) // Replace "/*" and "/*any". @@ -264,7 +264,7 @@ func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerIte fuzzyCountTotalNew int fuzzyCountTotalOld int ) - for _, v := range newItem.router.Uri { + for _, v := range newItem.Router.Uri { switch v { case '{': fuzzyCountFieldNew++ @@ -274,7 +274,7 @@ func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerIte fuzzyCountAnyNew++ } } - for _, v := range oldItem.router.Uri { + for _, v := range oldItem.Router.Uri { switch v { case '{': fuzzyCountFieldOld++ @@ -312,16 +312,16 @@ func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerIte // It then compares the accuracy of their http method, // the more accurate the more priority. - if newItem.router.Method != defaultMethod { + if newItem.Router.Method != defaultMethod { return true } - if oldItem.router.Method != defaultMethod { + if oldItem.Router.Method != defaultMethod { return true } // If they have different router type, // the new router item has more priority than the other one. - if newItem.itemType == handlerTypeHandler || newItem.itemType == handlerTypeObject || newItem.itemType == handlerTypeController { + if newItem.Type == handlerTypeHandler || newItem.Type == handlerTypeObject || newItem.Type == handlerTypeController { return true } diff --git a/net/ghttp/ghttp_server_router_hook.go b/net/ghttp/ghttp_server_router_hook.go index 7963a7531..b8486c082 100644 --- a/net/ghttp/ghttp_server_router_hook.go +++ b/net/ghttp/ghttp_server_router_hook.go @@ -19,14 +19,14 @@ func (s *Server) BindHookHandler(pattern string, hook string, handler HandlerFun func (s *Server) doBindHookHandler(pattern string, hook string, handler HandlerFunc, source string) { s.setHandler(pattern, &handlerItem{ - itemType: handlerTypeHook, - itemName: gdebug.FuncPath(handler), - itemInfo: handlerFuncInfo{ + Type: handlerTypeHook, + Name: gdebug.FuncPath(handler), + Info: handlerFuncInfo{ Func: handler, Type: reflect.TypeOf(handler), }, - hookName: hook, - source: source, + HookName: hook, + Source: source, }) } @@ -43,11 +43,11 @@ func (s *Server) callHookHandler(hook string, r *Request) { // Backup the old router variable map. oldRouterMap := r.routerMap for _, item := range hookItems { - r.routerMap = item.values + r.routerMap = item.Values // DO NOT USE the router of the hook handler, // which can overwrite the router of serving handler. // r.Router = item.handler.router - if err := s.niceCallHookHandler(item.handler.itemInfo.Func, r); err != nil { + if err := s.niceCallHookHandler(item.Handler.Info.Func, r); err != nil { switch err { case exceptionExit: break @@ -73,7 +73,7 @@ func (r *Request) getHookHandlers(hook string) []*handlerParsedItem { } parsedItems := make([]*handlerParsedItem, 0, 4) for _, v := range r.handlers { - if v.handler.hookName != hook { + if v.Handler.HookName != hook { continue } item := v diff --git a/net/ghttp/ghttp_server_router_middleware.go b/net/ghttp/ghttp_server_router_middleware.go index 09faf7d1e..4f90d8288 100644 --- a/net/ghttp/ghttp_server_router_middleware.go +++ b/net/ghttp/ghttp_server_router_middleware.go @@ -23,9 +23,9 @@ const ( func (s *Server) BindMiddleware(pattern string, handlers ...HandlerFunc) { for _, handler := range handlers { s.setHandler(pattern, &handlerItem{ - itemType: handlerTypeMiddleware, - itemName: gdebug.FuncPath(handler), - itemInfo: handlerFuncInfo{ + Type: handlerTypeMiddleware, + Name: gdebug.FuncPath(handler), + Info: handlerFuncInfo{ Func: handler, Type: reflect.TypeOf(handler), }, @@ -39,9 +39,9 @@ func (s *Server) BindMiddleware(pattern string, handlers ...HandlerFunc) { func (s *Server) BindMiddlewareDefault(handlers ...HandlerFunc) { for _, handler := range handlers { s.setHandler(defaultMiddlewarePattern, &handlerItem{ - itemType: handlerTypeMiddleware, - itemName: gdebug.FuncPath(handler), - itemInfo: handlerFuncInfo{ + Type: handlerTypeMiddleware, + Name: gdebug.FuncPath(handler), + Info: handlerFuncInfo{ Func: handler, Type: reflect.TypeOf(handler), }, diff --git a/net/ghttp/ghttp_server_router_serve.go b/net/ghttp/ghttp_server_router_serve.go index 9babf9a14..2f36302e1 100644 --- a/net/ghttp/ghttp_server_router_serve.go +++ b/net/ghttp/ghttp_server_router_serve.go @@ -141,34 +141,34 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han item := e.Value.(*handlerItem) // Filter repeated handler item, especially the middleware and hook handlers. // It is necessary, do not remove this checks logic unless you really know how it is necessary. - if _, ok := repeatHandlerCheckMap[item.itemId]; ok { + if _, ok := repeatHandlerCheckMap[item.Id]; ok { continue } else { - repeatHandlerCheckMap[item.itemId] = struct{}{} + repeatHandlerCheckMap[item.Id] = struct{}{} } // Serving handler can only be added to the handler array just once. if hasServe { - switch item.itemType { + switch item.Type { case handlerTypeHandler, handlerTypeObject, handlerTypeController: continue } } - if item.router.Method == defaultMethod || item.router.Method == method { + if item.Router.Method == defaultMethod || item.Router.Method == method { // Note the rule having no fuzzy rules: len(match) == 1 - if match, err := gregex.MatchString(item.router.RegRule, path); err == nil && len(match) > 0 { + if match, err := gregex.MatchString(item.Router.RegRule, path); err == nil && len(match) > 0 { parsedItem := &handlerParsedItem{item, nil} // If the rule contains fuzzy names, // it needs paring the URL to retrieve the values for the names. - if len(item.router.RegNames) > 0 { - if len(match) > len(item.router.RegNames) { - parsedItem.values = make(map[string]string) + if len(item.Router.RegNames) > 0 { + if len(match) > len(item.Router.RegNames) { + parsedItem.Values = make(map[string]string) // It there repeated names, it just overwrites the same one. - for i, name := range item.router.RegNames { - parsedItem.values[name] = match[i+1] + for i, name := range item.Router.RegNames { + parsedItem.Values[name] = match[i+1] } } } - switch item.itemType { + switch item.Type { // The serving handler can be only added just once. case handlerTypeHandler, handlerTypeObject, handlerTypeController: hasServe = true @@ -190,7 +190,7 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han parsedItemList.PushBack(parsedItem) default: - panic(fmt.Sprintf(`invalid handler type %d`, item.itemType)) + panic(fmt.Sprintf(`invalid handler type %d`, item.Type)) } } } @@ -210,33 +210,33 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han // MarshalJSON implements the interface MarshalJSON for json.Marshal. func (item *handlerItem) MarshalJSON() ([]byte, error) { - switch item.itemType { + switch item.Type { case handlerTypeHook: return json.Marshal( fmt.Sprintf( `%s %s:%s (%s)`, - item.router.Uri, - item.router.Domain, - item.router.Method, - item.hookName, + item.Router.Uri, + item.Router.Domain, + item.Router.Method, + item.HookName, ), ) case handlerTypeMiddleware: return json.Marshal( fmt.Sprintf( `%s %s:%s (MIDDLEWARE)`, - item.router.Uri, - item.router.Domain, - item.router.Method, + item.Router.Uri, + item.Router.Domain, + item.Router.Method, ), ) default: return json.Marshal( fmt.Sprintf( `%s %s:%s`, - item.router.Uri, - item.router.Domain, - item.router.Method, + item.Router.Uri, + item.Router.Domain, + item.Router.Method, ), ) } @@ -244,5 +244,5 @@ func (item *handlerItem) MarshalJSON() ([]byte, error) { // MarshalJSON implements the interface MarshalJSON for json.Marshal. func (item *handlerParsedItem) MarshalJSON() ([]byte, error) { - return json.Marshal(item.handler) + return json.Marshal(item.Handler) } diff --git a/net/ghttp/ghttp_server_service_controller.go b/net/ghttp/ghttp_server_service_controller.go index b2df34ec4..3ad472f03 100644 --- a/net/ghttp/ghttp_server_service_controller.go +++ b/net/ghttp/ghttp_server_service_controller.go @@ -111,14 +111,14 @@ func (s *Server) doBindController( } key := s.mergeBuildInNameToPattern(pattern, structName, methodName, true) m[key] = &handlerItem{ - itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), - itemType: handlerTypeController, - ctrlInfo: &handlerController{ - name: methodName, - reflect: v.Elem().Type(), + Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), + Type: handlerTypeController, + CtrlInfo: &handlerController{ + Name: methodName, + Type: v.Elem().Type(), }, - middleware: middleware, - source: source, + Middleware: middleware, + Source: source, } // If there's "Index" method, then an additional route is automatically added // to match the main URI, for example: @@ -134,14 +134,14 @@ func (s *Server) doBindController( k = "/" + k } m[k] = &handlerItem{ - itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), - itemType: handlerTypeController, - ctrlInfo: &handlerController{ - name: methodName, - reflect: v.Elem().Type(), + Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), + Type: handlerTypeController, + CtrlInfo: &handlerController{ + Name: methodName, + Type: v.Elem().Type(), }, - middleware: middleware, - source: source, + Middleware: middleware, + Source: source, } } } @@ -182,14 +182,14 @@ func (s *Server) doBindControllerMethod( } key := s.mergeBuildInNameToPattern(pattern, structName, methodName, false) m[key] = &handlerItem{ - itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), - itemType: handlerTypeController, - ctrlInfo: &handlerController{ - name: methodName, - reflect: v.Elem().Type(), + Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), + Type: handlerTypeController, + CtrlInfo: &handlerController{ + Name: methodName, + Type: v.Elem().Type(), }, - middleware: middleware, - source: source, + Middleware: middleware, + Source: source, } s.bindHandlerByMap(m) } @@ -224,14 +224,14 @@ func (s *Server) doBindControllerRest( } key := s.mergeBuildInNameToPattern(methodName+":"+pattern, structName, methodName, false) m[key] = &handlerItem{ - itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), - itemType: handlerTypeController, - ctrlInfo: &handlerController{ - name: methodName, - reflect: v.Elem().Type(), + Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), + Type: handlerTypeController, + CtrlInfo: &handlerController{ + Name: methodName, + Type: v.Elem().Type(), }, - middleware: middleware, - source: source, + Middleware: middleware, + Source: source, } } s.bindHandlerByMap(m) diff --git a/net/ghttp/ghttp_server_service_handler.go b/net/ghttp/ghttp_server_service_handler.go index 36a1a1c66..2dbadb2ea 100644 --- a/net/ghttp/ghttp_server_service_handler.go +++ b/net/ghttp/ghttp_server_service_handler.go @@ -37,11 +37,11 @@ func (s *Server) BindHandler(pattern string, handler interface{}) { // /user/list, put:/user, delete:/user, post:/user@goframe.org func (s *Server) doBindHandler(pattern string, funcInfo handlerFuncInfo, middleware []HandlerFunc, source string) { s.setHandler(pattern, &handlerItem{ - itemName: gdebug.FuncPath(funcInfo.Func), - itemType: handlerTypeHandler, - itemInfo: funcInfo, - middleware: middleware, - source: source, + Name: gdebug.FuncPath(funcInfo.Func), + Type: handlerTypeHandler, + Info: funcInfo, + Middleware: middleware, + Source: source, }) } diff --git a/net/ghttp/ghttp_server_service_object.go b/net/ghttp/ghttp_server_service_object.go index 872cf179c..5d4330ac8 100644 --- a/net/ghttp/ghttp_server_service_object.go +++ b/net/ghttp/ghttp_server_service_object.go @@ -110,13 +110,13 @@ func (s *Server) doBindObject(pattern string, object interface{}, method string, key := s.mergeBuildInNameToPattern(pattern, structName, methodName, true) m[key] = &handlerItem{ - itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), - itemType: handlerTypeObject, - itemInfo: funcInfo, - initFunc: initFunc, - shutFunc: shutFunc, - middleware: middleware, - source: source, + Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), + Type: handlerTypeObject, + Info: funcInfo, + InitFunc: initFunc, + ShutFunc: shutFunc, + Middleware: middleware, + Source: source, } // If there's "Index" method, then an additional route is automatically added // to match the main URI, for example: @@ -132,13 +132,13 @@ func (s *Server) doBindObject(pattern string, object interface{}, method string, k = "/" + k } m[k] = &handlerItem{ - itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), - itemType: handlerTypeObject, - itemInfo: funcInfo, - initFunc: initFunc, - shutFunc: shutFunc, - middleware: middleware, - source: source, + Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), + Type: handlerTypeObject, + Info: funcInfo, + InitFunc: initFunc, + ShutFunc: shutFunc, + Middleware: middleware, + Source: source, } } } @@ -192,13 +192,13 @@ func (s *Server) doBindObjectMethod( key := s.mergeBuildInNameToPattern(pattern, structName, methodName, false) m[key] = &handlerItem{ - itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), - itemType: handlerTypeObject, - itemInfo: funcInfo, - initFunc: initFunc, - shutFunc: shutFunc, - middleware: middleware, - source: source, + Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), + Type: handlerTypeObject, + Info: funcInfo, + InitFunc: initFunc, + ShutFunc: shutFunc, + Middleware: middleware, + Source: source, } s.bindHandlerByMap(m) @@ -247,13 +247,13 @@ func (s *Server) doBindObjectRest(pattern string, object interface{}, middleware key := s.mergeBuildInNameToPattern(methodName+":"+pattern, structName, methodName, false) m[key] = &handlerItem{ - itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), - itemType: handlerTypeObject, - itemInfo: funcInfo, - initFunc: initFunc, - shutFunc: shutFunc, - middleware: middleware, - source: source, + Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), + Type: handlerTypeObject, + Info: funcInfo, + InitFunc: initFunc, + ShutFunc: shutFunc, + Middleware: middleware, + Source: source, } } s.bindHandlerByMap(m) diff --git a/net/ghttp/ghttp_unit_router_handler_extended_test.go b/net/ghttp/ghttp_unit_router_handler_extended_test.go index ddcf4da3d..cab1d390b 100644 --- a/net/ghttp/ghttp_unit_router_handler_extended_test.go +++ b/net/ghttp/ghttp_unit_router_handler_extended_test.go @@ -76,7 +76,7 @@ func Test_Router_Handler_Extended_Handler_WithObject(t *testing.T) { client := g.Client() client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - t.Assert(client.GetContent("/test?age=18&name=john"), `{"Id":1,"Age":18,"Name":"john"}`) - t.Assert(client.GetContent("/test/error"), `{"code":-1,"message":"error"}`) + t.Assert(client.GetContent("/test?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18,"Name":"john"}}`) + t.Assert(client.GetContent("/test/error"), `{"code":-1,"message":"error","data":null}`) }) } From 906c54ce61238db143d13809b16146c7ef39e8c5 Mon Sep 17 00:00:00 2001 From: starliu <starliu@thecover.co> Date: Mon, 19 Jul 2021 23:20:51 +0800 Subject: [PATCH 389/492] add basic action scripts --- .github/ workflows/go.yml | 77 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 .github/ workflows/go.yml diff --git a/.github/ workflows/go.yml b/.github/ workflows/go.yml new file mode 100644 index 000000000..4ddc49ffc --- /dev/null +++ b/.github/ workflows/go.yml @@ -0,0 +1,77 @@ +name: Go + +on: + push: + branches: + - master + - main + - develop + - staging + pull_request: + branches: [ master ] +env: + GF_DEBUG: 1 + +jobs: + + code-test: + runs-on: ubuntu-latest + # Service containers to run with `code-test` + services: + redis: + # Docker Hub image + image: redis + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + # Maps tcp port 6379 on service container to the host + - 6379:6379 + postgres: + image: postgres:9 + # Provide the password for postgres + env: + POSTGRES_PASSWORD: postgres + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + # Maps tcp port 5432 on service container to the host + - 5432:5432 + mysql: + image: mysql:5.7 + env: + MYSQL_ROOT_PASSWORD: 12345678 + MYSQL_DATABASE: test + ports: + # Maps tcp port 3306 on service container to the host + - 3306:3306 + steps: + + # - uses: szenius/set-timezone@v1.0 + # with: + # timezoneLinux: "Asia/Shanghai" + + - uses: actions/checkout@v2 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.16 + + - name: Before script + run: | + find . -name "*.go" | xargs gofmt -w + git diff --name-only --exit-code || exit 1 + sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts + + # - name: Build + # run: go build -v ./... + + - name: Test + run: go test -v ./... From f8486474aa54dabf7711d7ba24981f552d54439f Mon Sep 17 00:00:00 2001 From: starliu <starliu@thecover.co> Date: Mon, 19 Jul 2021 23:23:24 +0800 Subject: [PATCH 390/492] fix scripts path --- .github/{ workflows => workflows}/go.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{ workflows => workflows}/go.yml (100%) diff --git a/.github/ workflows/go.yml b/.github/workflows/go.yml similarity index 100% rename from .github/ workflows/go.yml rename to .github/workflows/go.yml From 0ddacdd7e2b664f0259ef20fc1b022d6832eba70 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 20 Jul 2021 23:02:02 +0800 Subject: [PATCH 391/492] add error code for components --- container/garray/garray_normal_any.go | 8 ++-- container/garray/garray_normal_int.go | 8 ++-- container/garray/garray_normal_str.go | 8 ++-- container/gpool/gpool.go | 4 +- crypto/gaes/gaes.go | 14 +++--- crypto/gdes/gdes.go | 24 +++++----- database/gdb/gdb.go | 16 ++++--- database/gdb/gdb_core.go | 4 +- database/gdb/gdb_core_underlying.go | 4 +- database/gdb/gdb_driver_mssql.go | 6 +-- database/gdb/gdb_driver_mysql.go | 2 +- database/gdb/gdb_driver_oracle.go | 6 +-- database/gdb/gdb_driver_pgsql.go | 6 +-- database/gdb/gdb_driver_sqlite.go | 6 +-- database/gdb/gdb_func.go | 2 +- database/gdb/gdb_model_delete.go | 2 +- database/gdb/gdb_model_insert.go | 19 +++++--- database/gdb/gdb_model_select.go | 7 ++- database/gdb/gdb_model_update.go | 4 +- database/gdb/gdb_model_with.go | 6 ++- database/gdb/gdb_statement.go | 2 +- database/gdb/gdb_type_result_scanlist.go | 44 ++++++++++++------- database/gdb/gdb_z_mysql_struct_test.go | 2 +- database/gredis/gredis_config.go | 2 +- database/gredis/gredis_conn.go | 4 +- encoding/gcharset/gcharset.go | 8 ++-- encoding/gini/gini.go | 2 +- encoding/gjson/gjson_api_new_load.go | 2 +- errors/gerror/gerror.go | 8 ++-- errors/gerror/gerror_code.go | 25 +++++++++++ errors/gerror/gerror_error.go | 3 ++ errors/gerror/gerror_option.go | 28 ++++++++++++ frame/gins/gins_database.go | 9 ++-- i18n/gi18n/gi18n_manager.go | 2 +- internal/structs/structs_type.go | 11 +++-- net/ghttp/ghttp_func.go | 9 +++- .../ghttp_middleware_handler_response.go | 8 +++- net/ghttp/ghttp_request_middleware.go | 2 +- net/ghttp/ghttp_request_param_file.go | 12 +++-- net/ghttp/ghttp_server.go | 9 ++-- net/ghttp/ghttp_server_admin_process.go | 13 ++++-- net/ghttp/ghttp_server_graceful.go | 2 +- net/ghttp/ghttp_server_handler.go | 8 +++- net/ghttp/ghttp_server_router.go | 2 +- net/ghttp/ghttp_server_service_handler.go | 12 +++-- net/ghttp/internal/client/client.go | 10 ++--- net/ghttp/internal/client/client_request.go | 2 +- net/gipv4/gipv4_ip.go | 2 +- net/gtcp/gtcp_server.go | 2 +- net/gudp/gudp_server.go | 2 +- os/gcfg/gcfg_config.go | 8 ++-- os/gcfg/gcfg_config_api.go | 18 ++++---- os/gcmd/gcmd_handler.go | 10 ++--- os/gcmd/gcmd_parser.go | 2 +- os/gcmd/gcmd_parser_handler.go | 8 ++-- os/gcron/gcron_cron.go | 2 +- os/gcron/gcron_schedule.go | 12 ++--- os/gfile/gfile_copy.go | 14 +++--- os/gfile/gfile_home.go | 6 +-- os/gfile/gfile_scan.go | 2 +- os/gfile/gfile_search.go | 4 +- os/gfpool/gfpool_file.go | 4 +- os/gfsnotify/gfsnotify.go | 2 +- os/gfsnotify/gfsnotify_watcher.go | 2 +- os/glog/glog_logger_config.go | 11 +++-- os/glog/glog_logger_level.go | 2 +- os/gproc/gproc_comm.go | 2 +- os/gproc/gproc_comm_send.go | 3 +- os/gproc/gproc_process.go | 2 +- os/grpool/grpool.go | 10 +++-- os/gsession/gsession.go | 7 ++- os/gsession/gsession_session.go | 4 +- os/gsession/gsession_storage_file.go | 6 +-- os/gspath/gspath.go | 8 ++-- os/gtime/gtime.go | 6 +-- os/gtime/gtime_time.go | 2 +- os/gview/gview_config.go | 10 ++--- os/gview/gview_parse.go | 6 +-- util/gconv/gconv_maptomap.go | 4 +- util/gconv/gconv_maptomaps.go | 10 ++--- util/gconv/gconv_scan.go | 4 +- util/gconv/gconv_struct.go | 25 +++++------ util/gconv/gconv_structs.go | 6 +-- util/gvalid/gvalid_error.go | 19 ++++++-- util/gvalid/gvalid_validator_check_map.go | 3 +- util/gvalid/gvalid_validator_check_struct.go | 3 +- util/gvalid/gvalid_validator_check_value.go | 34 ++++++++++---- util/gvalid/gvalid_z_unit_basic_all_test.go | 13 ++++++ 88 files changed, 434 insertions(+), 263 deletions(-) create mode 100644 errors/gerror/gerror_code.go create mode 100644 errors/gerror/gerror_option.go diff --git a/container/garray/garray_normal_any.go b/container/garray/garray_normal_any.go index a3b450b54..a87cb8b41 100644 --- a/container/garray/garray_normal_any.go +++ b/container/garray/garray_normal_any.go @@ -123,7 +123,7 @@ func (a *Array) Set(index int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.Newf("index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } a.array[index] = value return nil @@ -176,7 +176,7 @@ func (a *Array) InsertBefore(index int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.Newf("index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]interface{}{}, a.array[index:]...) a.array = append(a.array[0:index], value) @@ -189,7 +189,7 @@ func (a *Array) InsertAfter(index int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.Newf("index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]interface{}{}, a.array[index+1:]...) a.array = append(a.array[0:index+1], value) @@ -545,7 +545,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if startIndex < 0 || startIndex > len(a.array) { - return gerror.Newf("index %d out of array range %d", startIndex, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) } for i := startIndex; i < startIndex+num; i++ { if i > len(a.array)-1 { diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index 3e43a56db..c58020eb2 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -104,7 +104,7 @@ func (a *IntArray) Set(index int, value int) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.Newf("index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } a.array[index] = value return nil @@ -175,7 +175,7 @@ func (a *IntArray) InsertBefore(index int, value int) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.Newf("index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]int{}, a.array[index:]...) a.array = append(a.array[0:index], value) @@ -188,7 +188,7 @@ func (a *IntArray) InsertAfter(index int, value int) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.Newf("index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]int{}, a.array[index+1:]...) a.array = append(a.array[0:index+1], value) @@ -559,7 +559,7 @@ func (a *IntArray) Fill(startIndex int, num int, value int) error { a.mu.Lock() defer a.mu.Unlock() if startIndex < 0 || startIndex > len(a.array) { - return gerror.Newf("index %d out of array range %d", startIndex, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) } for i := startIndex; i < startIndex+num; i++ { if i > len(a.array)-1 { diff --git a/container/garray/garray_normal_str.go b/container/garray/garray_normal_str.go index 832a70cb6..be23bba9d 100644 --- a/container/garray/garray_normal_str.go +++ b/container/garray/garray_normal_str.go @@ -90,7 +90,7 @@ func (a *StrArray) Set(index int, value string) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.Newf("index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } a.array[index] = value return nil @@ -162,7 +162,7 @@ func (a *StrArray) InsertBefore(index int, value string) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.Newf("index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]string{}, a.array[index:]...) a.array = append(a.array[0:index], value) @@ -175,7 +175,7 @@ func (a *StrArray) InsertAfter(index int, value string) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.Newf("index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]string{}, a.array[index+1:]...) a.array = append(a.array[0:index+1], value) @@ -562,7 +562,7 @@ func (a *StrArray) Fill(startIndex int, num int, value string) error { a.mu.Lock() defer a.mu.Unlock() if startIndex < 0 || startIndex > len(a.array) { - return gerror.Newf("index %d out of array range %d", startIndex, len(a.array)) + return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) } for i := startIndex; i < startIndex+num; i++ { if i > len(a.array)-1 { diff --git a/container/gpool/gpool.go b/container/gpool/gpool.go index e4125aaac..0022cfb28 100644 --- a/container/gpool/gpool.go +++ b/container/gpool/gpool.go @@ -66,7 +66,7 @@ func New(ttl time.Duration, newFunc NewFunc, expireFunc ...ExpireFunc) *Pool { // Put puts an item to pool. func (p *Pool) Put(value interface{}) error { if p.closed.Val() { - return gerror.New("pool is closed") + return gerror.NewCode(gerror.CodeInvalidOperation, "pool is closed") } item := &poolItem{ value: value, @@ -117,7 +117,7 @@ func (p *Pool) Get() (interface{}, error) { if p.NewFunc != nil { return p.NewFunc() } - return nil, gerror.New("pool is empty") + return nil, gerror.NewCode(gerror.CodeInvalidOperation, "pool is empty") } // Size returns the count of available items of pool. diff --git a/crypto/gaes/gaes.go b/crypto/gaes/gaes.go index bdb04d78a..2fba47c19 100644 --- a/crypto/gaes/gaes.go +++ b/crypto/gaes/gaes.go @@ -63,7 +63,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) { } blockSize := block.BlockSize() if len(cipherText) < blockSize { - return nil, gerror.New("cipherText too short") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "cipherText too short") } ivValue := ([]byte)(nil) if len(iv) > 0 { @@ -72,7 +72,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) { ivValue = []byte(IVDefaultValue) } if len(cipherText)%blockSize != 0 { - return nil, gerror.New("cipherText is not a multiple of the block size") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "cipherText is not a multiple of the block size") } blockModel := cipher.NewCBCDecrypter(block, ivValue) plainText := make([]byte, len(cipherText)) @@ -93,22 +93,22 @@ func PKCS5Padding(src []byte, blockSize int) []byte { func PKCS5UnPadding(src []byte, blockSize int) ([]byte, error) { length := len(src) if blockSize <= 0 { - return nil, gerror.New("invalid blocklen") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid blocklen") } if length%blockSize != 0 || length == 0 { - return nil, gerror.New("invalid data len") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid data len") } unpadding := int(src[length-1]) if unpadding > blockSize || unpadding == 0 { - return nil, gerror.New("invalid padding") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid padding") } padding := src[length-unpadding:] for i := 0; i < unpadding; i++ { if padding[i] != byte(unpadding) { - return nil, gerror.New("invalid padding") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid padding") } } @@ -146,7 +146,7 @@ func DecryptCFB(cipherText []byte, key []byte, unPadding int, iv ...[]byte) ([]b return nil, err } if len(cipherText) < aes.BlockSize { - return nil, gerror.New("cipherText too short") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "cipherText too short") } ivValue := ([]byte)(nil) if len(iv) > 0 { diff --git a/crypto/gdes/gdes.go b/crypto/gdes/gdes.go index c60f7c190..e12113719 100644 --- a/crypto/gdes/gdes.go +++ b/crypto/gdes/gdes.go @@ -66,7 +66,7 @@ func DecryptECB(cipherText []byte, key []byte, padding int) ([]byte, error) { // The length of the <key> should be either 16 or 24 bytes. func EncryptECBTriple(plainText []byte, key []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, gerror.New("key length error") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length error") } text, err := Padding(plainText, padding) @@ -100,7 +100,7 @@ func EncryptECBTriple(plainText []byte, key []byte, padding int) ([]byte, error) // The length of the <key> should be either 16 or 24 bytes. func DecryptECBTriple(cipherText []byte, key []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, gerror.New("key length error") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length error") } var newKey []byte @@ -138,7 +138,7 @@ func EncryptCBC(plainText []byte, key []byte, iv []byte, padding int) ([]byte, e } if len(iv) != block.BlockSize() { - return nil, gerror.New("iv length invalid") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid") } text, err := Padding(plainText, padding) @@ -161,7 +161,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv []byte, padding int) ([]byte, } if len(iv) != block.BlockSize() { - return nil, gerror.New("iv length invalid") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid") } text := make([]byte, len(cipherText)) @@ -179,7 +179,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv []byte, padding int) ([]byte, // EncryptCBCTriple encrypts <plainText> using TripleDES and CBC mode. func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, gerror.New("key length invalid") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length invalid") } var newKey []byte @@ -196,7 +196,7 @@ func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]b } if len(iv) != block.BlockSize() { - return nil, gerror.New("iv length invalid") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid") } text, err := Padding(plainText, padding) @@ -214,7 +214,7 @@ func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]b // DecryptCBCTriple decrypts <cipherText> using TripleDES and CBC mode. func DecryptCBCTriple(cipherText []byte, key []byte, iv []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, gerror.New("key length invalid") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length invalid") } var newKey []byte @@ -231,7 +231,7 @@ func DecryptCBCTriple(cipherText []byte, key []byte, iv []byte, padding int) ([] } if len(iv) != block.BlockSize() { - return nil, gerror.New("iv length invalid") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid") } text := make([]byte, len(cipherText)) @@ -262,12 +262,12 @@ func Padding(text []byte, padding int) ([]byte, error) { switch padding { case NOPADDING: if len(text)%8 != 0 { - return nil, gerror.New("text length invalid") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "text length invalid") } case PKCS5PADDING: return PaddingPKCS5(text, 8), nil default: - return nil, gerror.New("padding type error") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "padding type error") } return text, nil @@ -277,12 +277,12 @@ func UnPadding(text []byte, padding int) ([]byte, error) { switch padding { case NOPADDING: if len(text)%8 != 0 { - return nil, gerror.New("text length invalid") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "text length invalid") } case PKCS5PADDING: return UnPaddingPKCS5(text), nil default: - return nil, gerror.New("padding type error") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "padding type error") } return text, nil } diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index f197e5675..dd5232d08 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -10,7 +10,6 @@ package gdb import ( "context" "database/sql" - "fmt" "time" "github.com/gogf/gf/errors/gerror" @@ -333,7 +332,10 @@ func New(group ...string) (db DB, err error) { defer configs.RUnlock() if len(configs.config) < 1 { - return nil, gerror.New("database configuration is empty, please set the database configuration before using") + return nil, gerror.NewCode( + gerror.CodeInvalidConfiguration, + "database configuration is empty, please set the database configuration before using", + ) } if _, ok := configs.config[groupName]; ok { if node, err := getConfigNodeByGroup(groupName, true); err == nil { @@ -352,7 +354,8 @@ func New(group ...string) (db DB, err error) { } return c.db, nil } else { - return nil, gerror.Newf( + return nil, gerror.NewCodef( + gerror.CodeInvalidConfiguration, `cannot find database driver for specified database type "%s", did you misspell type name "%s" or forget importing the database driver?`, node.Type, node.Type, ) @@ -361,7 +364,8 @@ func New(group ...string) (db DB, err error) { return nil, err } } else { - return nil, gerror.Newf( + return nil, gerror.NewCodef( + gerror.CodeInvalidConfiguration, `database configuration node "%s" is not found, did you misspell group name "%s" or miss the database configuration?`, groupName, groupName, ) @@ -404,7 +408,7 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) { } } if len(masterList) < 1 { - return nil, gerror.New("at least one master node configuration's need to make sense") + return nil, gerror.NewCode(gerror.CodeInvalidConfiguration, "at least one master node configuration's need to make sense") } if len(slaveList) < 1 { slaveList = masterList @@ -415,7 +419,7 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) { return getConfigNodeByWeight(slaveList), nil } } else { - return nil, gerror.New(fmt.Sprintf("empty database configuration for item name '%s'", group)) + return nil, gerror.NewCodef(gerror.CodeInvalidConfiguration, "empty database configuration for item name '%s'", group) } } diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 2b96dd8ec..3b0cbe7f5 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -89,7 +89,7 @@ func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Cont return context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout) } default: - panic(gerror.Newf("invalid context timeout type: %d", timeoutType)) + panic(gerror.NewCodef(gerror.CodeInvalidParameter, "invalid context timeout type: %d", timeoutType)) } return ctx, func() {} } @@ -553,7 +553,7 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter updates = gconv.String(data) } if len(updates) == 0 { - return nil, gerror.New("data cannot be empty") + return nil, gerror.NewCode(gerror.CodeMissingParameter, "data cannot be empty") } if len(params) > 0 { args = append(params, args...) diff --git a/database/gdb/gdb_core_underlying.go b/database/gdb/gdb_core_underlying.go index ba09b503f..80dd32d58 100644 --- a/database/gdb/gdb_core_underlying.go +++ b/database/gdb/gdb_core_underlying.go @@ -136,7 +136,7 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf func (c *Core) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { if c.db.GetConfig().CtxStrict { if v := ctx.Value(ctxStrictKeyName); v == nil { - return sql, args, gerror.New(ctxStrictErrorStr) + return sql, args, gerror.NewCode(gerror.CodeMissingParameter, ctxStrictErrorStr) } } return sql, args, nil @@ -181,7 +181,7 @@ func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, err if c.db.GetConfig().CtxStrict { if v := ctx.Value(ctxStrictKeyName); v == nil { - return nil, gerror.New(ctxStrictErrorStr) + return nil, gerror.NewCode(gerror.CodeMissingParameter, ctxStrictErrorStr) } } diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index f56c2f966..c5ac9be8a 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -214,7 +214,7 @@ func (d *DriverMssql) TableFields(ctx context.Context, table string, schema ...s charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { - return nil, gerror.New("function TableFields supports only single table operations") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations") } useSchema := d.db.GetSchema() if len(schema) > 0 && schema[0] != "" { @@ -292,10 +292,10 @@ ORDER BY a.id,a.colorder`, func (d *DriverMssql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { switch option.InsertOption { case insertOptionSave: - return nil, gerror.New(`Save operation is not supported by mssql driver`) + return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by mssql driver`) case insertOptionReplace: - return nil, gerror.New(`Replace operation is not supported by mssql driver`) + return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by mssql driver`) default: return d.Core.DoInsert(ctx, link, table, list, option) diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index a9ac18296..d4c5e53f0 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -121,7 +121,7 @@ func (d *DriverMysql) TableFields(ctx context.Context, table string, schema ...s charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { - return nil, gerror.New("function TableFields supports only single table operations") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations") } useSchema := d.schema.Val() if len(schema) > 0 && schema[0] != "" { diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index 7ea5fcd54..d42cd1974 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -186,7 +186,7 @@ func (d *DriverOracle) TableFields(ctx context.Context, table string, schema ... charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { - return nil, gerror.New("function TableFields supports only single table operations") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations") } useSchema := d.db.GetSchema() if len(schema) > 0 && schema[0] != "" { @@ -278,10 +278,10 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[ func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { switch option.InsertOption { case insertOptionSave: - return nil, gerror.New(`Save operation is not supported by mssql driver`) + return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by mssql driver`) case insertOptionReplace: - return nil, gerror.New(`Replace operation is not supported by mssql driver`) + return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by mssql driver`) } var ( diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 3503313d9..07a8029fc 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -126,7 +126,7 @@ func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...s charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { - return nil, gerror.New("function TableFields supports only single table operations") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations") } table, _ = gregex.ReplaceString("\"", "", table) useSchema := d.db.GetSchema() @@ -190,10 +190,10 @@ ORDER BY a.attnum`, func (d *DriverPgsql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { switch option.InsertOption { case insertOptionSave: - return nil, gerror.New(`Save operation is not supported by pgsql driver`) + return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by pgsql driver`) case insertOptionReplace: - return nil, gerror.New(`Replace operation is not supported by pgsql driver`) + return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by pgsql driver`) default: return d.Core.DoInsert(ctx, link, table, list, option) diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index 132fe5c32..61703aa43 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -99,7 +99,7 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ... charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { - return nil, gerror.New("function TableFields supports only single table operations") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations") } useSchema := d.db.GetSchema() if len(schema) > 0 && schema[0] != "" { @@ -141,10 +141,10 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ... func (d *DriverSqlite) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { switch option.InsertOption { case insertOptionSave: - return nil, gerror.New(`Save operation is not supported by sqlite driver`) + return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by sqlite driver`) case insertOptionReplace: - return nil, gerror.New(`Replace operation is not supported by sqlite driver`) + return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by sqlite driver`) default: return d.Core.DoInsert(ctx, link, table, list, option) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 0f8c9541b..d88c81aa1 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -805,7 +805,7 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i // formatError customizes and returns the SQL error. func formatError(err error, sql string, args ...interface{}) error { if err != nil && err != ErrNoRows { - return gerror.New(fmt.Sprintf("%s, %s\n", err.Error(), FormatSqlWithArgs(sql, args))) + return gerror.NewCodef(gerror.CodeDbOperationError, "%s, %s\n", err.Error(), FormatSqlWithArgs(sql, args)) } return err } diff --git a/database/gdb/gdb_model_delete.go b/database/gdb/gdb_model_delete.go index 1e08d68da..43af4544c 100644 --- a/database/gdb/gdb_model_delete.go +++ b/database/gdb/gdb_model_delete.go @@ -44,7 +44,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) { } conditionStr := conditionWhere + conditionExtra if !gstr.ContainsI(conditionStr, " WHERE ") { - return nil, gerror.New("there should be WHERE condition statement for DELETE operation") + return nil, gerror.NewCode(gerror.CodeMissingParameter, "there should be WHERE condition statement for DELETE operation") } return m.db.DoDelete(m.GetCtx(), m.getLink(true), m.tables, conditionStr, conditionArgs...) } diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 02a60d373..6f29229d4 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -8,7 +8,6 @@ package gdb import ( "database/sql" - "fmt" "github.com/gogf/gf/container/gset" "reflect" @@ -211,7 +210,7 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err } }() if m.data == nil { - return nil, gerror.New("inserting into table with empty data") + return nil, gerror.NewCode(gerror.CodeMissingParameter, "inserting into table with empty data") } var ( list List @@ -276,12 +275,12 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err } default: - return result, gerror.New(fmt.Sprint("unsupported list type:", kind)) + return result, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported list type:%v", kind) } } if len(list) < 1 { - return result, gerror.New("data list cannot be empty") + return result, gerror.NewCode(gerror.CodeMissingParameter, "data list cannot be empty") } // Automatic handling for creating/updating time. @@ -366,7 +365,11 @@ func (m *Model) formatDoInsertOption(insertOption int, columnNames []string) (op } default: - return option, gerror.Newf(`unsupported OnDuplicate parameter type "%s"`, reflect.TypeOf(m.onDuplicate)) + return option, gerror.NewCodef( + gerror.CodeInvalidParameter, + `unsupported OnDuplicate parameter type "%s"`, + reflect.TypeOf(m.onDuplicate), + ) } } } else if onDuplicateExKeySet.Size() > 0 { @@ -406,7 +409,11 @@ func (m *Model) formatOnDuplicateExKeys(onDuplicateEx interface{}) ([]string, er return gconv.Strings(onDuplicateEx), nil default: - return nil, gerror.Newf(`unsupported OnDuplicateEx parameter type "%s"`, reflect.TypeOf(onDuplicateEx)) + return nil, gerror.NewCodef( + gerror.CodeInvalidParameter, + `unsupported OnDuplicateEx parameter type "%s"`, + reflect.TypeOf(onDuplicateEx), + ) } } diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 897eae92f..5dc716fad 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -316,7 +316,7 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error { reflectKind = reflectValue.Kind() if reflectKind != reflect.Ptr { - return gerror.New(`the parameter "pointer" for function Scan should type of pointer`) + return gerror.NewCode(gerror.CodeInvalidParameter, `the parameter "pointer" for function Scan should type of pointer`) } for reflectKind == reflect.Ptr { reflectValue = reflectValue.Elem() @@ -331,7 +331,10 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error { return m.doStruct(pointer, where...) default: - return gerror.New(`element of parameter "pointer" for function Scan should type of struct/*struct/[]struct/[]*struct`) + return gerror.NewCode( + gerror.CodeInvalidParameter, + `element of parameter "pointer" for function Scan should type of struct/*struct/[]struct/[]*struct`, + ) } } diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index f72e622ff..80d7ad2a8 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -39,7 +39,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro } }() if m.data == nil { - return nil, gerror.New("updating table with empty data") + return nil, gerror.NewCode(gerror.CodeMissingParameter, "updating table with empty data") } var ( updateData = m.data @@ -80,7 +80,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro } conditionStr := conditionWhere + conditionExtra if !gstr.ContainsI(conditionStr, " WHERE ") { - return nil, gerror.New("there should be WHERE condition statement for UPDATE operation") + return nil, gerror.NewCode(gerror.CodeMissingParameter, "there should be WHERE condition statement for UPDATE operation") } return m.db.DoUpdate( m.GetCtx(), diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 12ab46f55..e9fff363c 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -126,7 +126,8 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { } } if relatedFieldValue == nil { - return gerror.Newf( + return gerror.NewCodef( + gerror.CodeInvalidParameter, `cannot find the related value for attribute name "%s" of with tag "%s"`, relatedAttrName, withTag, ) @@ -238,7 +239,8 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { } } if relatedFieldValue == nil { - return gerror.Newf( + return gerror.NewCodef( + gerror.CodeInvalidParameter, `cannot find the related value for attribute name "%s" of with tag "%s"`, relatedAttrName, withTag, ) diff --git a/database/gdb/gdb_statement.go b/database/gdb/gdb_statement.go index 51d8a4b2f..9bca2980f 100644 --- a/database/gdb/gdb_statement.go +++ b/database/gdb/gdb_statement.go @@ -59,7 +59,7 @@ func (s *Stmt) doStmtCommit(ctx context.Context, stmtType string, args ...interf result = s.Stmt.QueryRowContext(ctx, args...) default: - panic(gerror.Newf(`invalid stmtType: %s`, stmtType)) + panic(gerror.NewCodef(gerror.CodeInvalidParameter, `invalid stmtType: %s`, stmtType)) } var ( timestampMilli2 = gtime.TimestampMilli() diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index 18887471e..ee738ad11 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -55,7 +55,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } // Necessary checks for parameters. if bindToAttrName == "" { - return gerror.New(`bindToAttrName should not be empty`) + return gerror.NewCode(gerror.CodeInvalidParameter, `bindToAttrName should not be empty`) } var ( @@ -67,12 +67,12 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr reflectKind = reflectValue.Kind() } if reflectKind != reflect.Ptr { - return gerror.Newf("listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind) + return gerror.NewCodef(gerror.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind) } reflectValue = reflectValue.Elem() reflectKind = reflectValue.Kind() if reflectKind != reflect.Slice && reflectKind != reflect.Array { - return gerror.Newf("listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind) + return gerror.NewCodef(gerror.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind) } length := len(result) if length == 0 { @@ -135,7 +135,8 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr relationResultFieldName = array[0] relationBindToSubAttrName = array[1] if key, _ := gutil.MapPossibleItemByKey(result[0].Map(), relationResultFieldName); key == "" { - return gerror.Newf( + return gerror.NewCodef( + gerror.CodeInvalidParameter, `cannot find possible related table field name "%s" from given relation key "%s"`, relationResultFieldName, relationKVStr, @@ -144,14 +145,14 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr relationResultFieldName = key } } else { - return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`) + return gerror.NewCode(gerror.CodeInvalidParameter, `parameter relationKV should be format of "ResultFieldName:BindToAttrName"`) } if relationResultFieldName != "" { // Note that the value might be type of slice. relationDataMap = result.MapKeyValue(relationResultFieldName) } if len(relationDataMap) == 0 { - return gerror.Newf(`cannot find the relation data map, maybe invalid relation given "%v"`, relationKV) + return gerror.NewCodef(gerror.CodeInvalidParameter, `cannot find the relation data map, maybe invalid relation given "%v"`, relationKV) } } // Bind to target attribute. @@ -164,11 +165,19 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr ) if arrayItemType.Kind() == reflect.Ptr { if bindToAttrField, ok = arrayItemType.Elem().FieldByName(bindToAttrName); !ok { - return gerror.Newf(`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, bindToAttrName) + return gerror.NewCodef( + gerror.CodeInvalidParameter, + `invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, + bindToAttrName, + ) } } else { if bindToAttrField, ok = arrayItemType.FieldByName(bindToAttrName); !ok { - return gerror.Newf(`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, bindToAttrName) + return gerror.NewCodef( + gerror.CodeInvalidParameter, + `invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, + bindToAttrName, + ) } } bindToAttrType = bindToAttrField.Type @@ -210,7 +219,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr relationFromAttrValue = arrayElemValue } if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() { - return gerror.Newf(`invalid relation specified: "%v"`, relationKV) + return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) } // Check and find possible bind to attribute name. if relationKVStr != "" && !relationBindToSubAttrNameChecked { @@ -224,7 +233,8 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr filedMap[relationFromAttrType.Field(i).Name] = struct{}{} } if key, _ := gutil.MapPossibleItemByKey(filedMap, relationBindToSubAttrName); key == "" { - return gerror.Newf( + return gerror.NewCodef( + gerror.CodeInvalidParameter, `cannot find possible related attribute name "%s" from given relation key "%s"`, relationBindToSubAttrName, relationKVStr, @@ -255,10 +265,14 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } } else { // May be the attribute does not exist yet. - return gerror.Newf(`invalid relation specified: "%v"`, relationKV) + return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) } } else { - return gerror.Newf(`relationKey should not be empty as field "%s" is slice`, bindToAttrName) + return gerror.NewCodef( + gerror.CodeInvalidParameter, + `relationKey should not be empty as field "%s" is slice`, + bindToAttrName, + ) } case reflect.Ptr: @@ -287,7 +301,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } } else { // May be the attribute does not exist yet. - return gerror.Newf(`invalid relation specified: "%v"`, relationKV) + return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) } } else { if i >= len(result) { @@ -331,7 +345,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } } else { // May be the attribute does not exist yet. - return gerror.Newf(`invalid relation specified: "%v"`, relationKV) + return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) } } else { if i >= len(result) { @@ -355,7 +369,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } default: - return gerror.Newf(`unsupported attribute type: %s`, bindToAttrKind.String()) + return gerror.NewCodef(gerror.CodeInvalidParameter, `unsupported attribute type: %s`, bindToAttrKind.String()) } } reflect.ValueOf(listPointer).Elem().Set(arrayValue) diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index d4d7a7373..9f56f4772 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -408,7 +408,7 @@ func (user *User) UnmarshalValue(value interface{}) error { } return nil } - return gerror.Newf(`unsupported value type for UnmarshalValue: %v`, reflect.TypeOf(value)) + return gerror.NewCodef(gerror.CodeInvalidParameter, `unsupported value type for UnmarshalValue: %v`, reflect.TypeOf(value)) } func Test_Model_Scan_UnmarshalValue(t *testing.T) { diff --git a/database/gredis/gredis_config.go b/database/gredis/gredis_config.go index 121b9c4a1..e39a96ddb 100644 --- a/database/gredis/gredis_config.go +++ b/database/gredis/gredis_config.go @@ -114,7 +114,7 @@ func ConfigFromStr(str string) (config *Config, err error) { config.Port = DefaultRedisPort } } else { - err = gerror.Newf(`invalid redis configuration: "%s"`, str) + err = gerror.NewCodef(gerror.CodeInvalidConfiguration, `invalid redis configuration: "%s"`, str) } return } diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index 2826affcd..ed5f90851 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -50,7 +50,7 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} if timeout > 0 { conn, ok := c.Conn.(redis.ConnWithTimeout) if !ok { - return gvar.New(nil), gerror.New(`current connection does not support "ConnWithTimeout"`) + return gvar.New(nil), gerror.NewCode(gerror.CodeNotSupported, `current connection does not support "ConnWithTimeout"`) } return conn.DoWithTimeout(timeout, commandName, args...) } @@ -107,7 +107,7 @@ func (c *Conn) ReceiveVar() (*gvar.Var, error) { func (c *Conn) ReceiveVarWithTimeout(timeout time.Duration) (*gvar.Var, error) { conn, ok := c.Conn.(redis.ConnWithTimeout) if !ok { - return gvar.New(nil), gerror.New(`current connection does not support "ConnWithTimeout"`) + return gvar.New(nil), gerror.NewCode(gerror.CodeNotSupported, `current connection does not support "ConnWithTimeout"`) } return resultToVar(conn.ReceiveWithTimeout(timeout)) } diff --git a/encoding/gcharset/gcharset.go b/encoding/gcharset/gcharset.go index 0aac86b27..af4998fc4 100644 --- a/encoding/gcharset/gcharset.go +++ b/encoding/gcharset/gcharset.go @@ -59,11 +59,11 @@ func Convert(dstCharset string, srcCharset string, src string) (dst string, err transform.NewReader(bytes.NewReader([]byte(src)), e.NewDecoder()), ) if err != nil { - return "", gerror.Newf("%s to utf8 failed. %v", srcCharset, err) + return "", gerror.WrapCodef(gerror.CodeInternalError, err, "%s to utf8 failed", srcCharset) } src = string(tmp) } else { - return dst, gerror.Newf("unsupport srcCharset: %s", srcCharset) + return dst, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported srcCharset: %s", srcCharset) } } // Do the converting from UTF-8 to <dstCharset>. @@ -73,11 +73,11 @@ func Convert(dstCharset string, srcCharset string, src string) (dst string, err transform.NewReader(bytes.NewReader([]byte(src)), e.NewEncoder()), ) if err != nil { - return "", gerror.Newf("utf to %s failed. %v", dstCharset, err) + return "", gerror.WrapCodef(gerror.CodeInternalError, err, "utf to %s failed", dstCharset) } dst = string(tmp) } else { - return dst, gerror.Newf("unsupport dstCharset: %s", dstCharset) + return dst, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported dstCharset: %s", dstCharset) } } else { dst = src diff --git a/encoding/gini/gini.go b/encoding/gini/gini.go index 6394d2393..43ae110da 100644 --- a/encoding/gini/gini.go +++ b/encoding/gini/gini.go @@ -70,7 +70,7 @@ func Decode(data []byte) (res map[string]interface{}, err error) { } if haveSection == false { - return nil, gerror.New("failed to parse INI file, section not found") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "failed to parse INI file, section not found") } return res, nil } diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index 780b3a16f..9c6c46130 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -264,7 +264,7 @@ func doLoadContentWithOptions(dataType string, data []byte, options Options) (*J return nil, err } default: - err = gerror.New("unsupported type for loading") + err = gerror.NewCode(gerror.CodeInvalidParameter, "unsupported type for loading") } if err != nil { return nil, err diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index 8b029b23f..b961be831 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -49,7 +49,7 @@ func New(text string) error { return &Error{ stack: callers(), text: text, - code: -1, + code: CodeNil, } } @@ -58,7 +58,7 @@ func Newf(format string, args ...interface{}) error { return &Error{ stack: callers(), text: fmt.Sprintf(format, args...), - code: -1, + code: CodeNil, } } @@ -68,7 +68,7 @@ func NewSkip(skip int, text string) error { return &Error{ stack: callers(skip), text: text, - code: -1, + code: CodeNil, } } @@ -78,7 +78,7 @@ func NewSkipf(skip int, format string, args ...interface{}) error { return &Error{ stack: callers(skip), text: fmt.Sprintf(format, args...), - code: -1, + code: CodeNil, } } diff --git a/errors/gerror/gerror_code.go b/errors/gerror/gerror_code.go new file mode 100644 index 000000000..6497b3003 --- /dev/null +++ b/errors/gerror/gerror_code.go @@ -0,0 +1,25 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gerror + +// Reserved internal error code of framework: code < 1000. + +const ( + CodeNil = -1 // No error code specified. + CodeOk = 0 // It is OK without error. + CodeInternalError = 50 // An error occurred internally. + CodeValidationFailed = 51 // Data validation failed. + CodeDbOperationError = 52 // Database operation error. + CodeInvalidParameter = 53 // The given parameter for current operation is invalid. + CodeMissingParameter = 54 // Parameter for current operation is missing. + CodeInvalidOperation = 55 // The function cannot be used like this. + CodeInvalidConfiguration = 56 // The configuration is invalid for current operation. + CodeMissingConfiguration = 57 // The configuration is missing for current operation. + CodeNotImplemented = 58 // The operation is not implemented yet. + CodeNotSupported = 59 // The operation is not supported yet. + CodeOperationFailed = 60 // I tried, but I cannot give you what you want. +) diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index 01d593134..045385d84 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -179,6 +179,9 @@ func (err *Error) MarshalJSON() ([]byte, error) { // formatSubStack formats the stack for error. func formatSubStack(st stack, buffer *bytes.Buffer) { + if st == nil { + return + } index := 1 space := " " for _, p := range st { diff --git a/errors/gerror/gerror_option.go b/errors/gerror/gerror_option.go new file mode 100644 index 000000000..26ecc18ad --- /dev/null +++ b/errors/gerror/gerror_option.go @@ -0,0 +1,28 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gerror + +// Option is option for creating error. +type Option struct { + Error error // Wrapped error. + Stack bool // Record stack information into error. + Text string // Error text, which is created by New* functions. + Code int // Error code if necessary. +} + +// NewOption creates and returns an error with Option. +func NewOption(option Option) error { + err := &Error{ + error: option.Error, + text: option.Text, + code: option.Code, + } + if option.Stack { + err.stack = callers() + } + return err +} diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index 1206ee7d4..ec854722e 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -53,21 +53,24 @@ func Database(name ...string) gdb.DB { if configFilePath == "" { exampleFileName := "config.example.toml" if exampleConfigFilePath, _ := Config().GetFilePath(exampleFileName); exampleConfigFilePath != "" { - panic(gerror.Wrapf( + panic(gerror.WrapCodef( + gerror.CodeMissingConfiguration, err, `configuration file "%s" not found, but found "%s", did you miss renaming the example configuration file?`, Config().GetFileName(), exampleFileName, )) } else { - panic(gerror.Wrapf( + panic(gerror.WrapCodef( + gerror.CodeMissingConfiguration, err, `configuration file "%s" not found, did you miss the configuration file or the misspell the configuration file name?`, Config().GetFileName(), )) } } - panic(gerror.Wrapf( + panic(gerror.WrapCodef( + gerror.CodeMissingConfiguration, err, `database initialization failed: "%s" node not found, is configuration file or configuration node missing?`, configNodeNameDatabase, diff --git a/i18n/gi18n/gi18n_manager.go b/i18n/gi18n/gi18n_manager.go index cc223e32d..90ca866d7 100644 --- a/i18n/gi18n/gi18n_manager.go +++ b/i18n/gi18n/gi18n_manager.go @@ -101,7 +101,7 @@ func (m *Manager) SetPath(path string) error { } else { realPath, _ := gfile.Search(path) if realPath == "" { - return gerror.Newf(`%s does not exist`, path) + return gerror.NewCodef(gerror.CodeInvalidParameter, `%s does not exist`, path) } m.options.Path = realPath } diff --git a/internal/structs/structs_type.go b/internal/structs/structs_type.go index 19e20ad40..ed30ecab2 100644 --- a/internal/structs/structs_type.go +++ b/internal/structs/structs_type.go @@ -7,7 +7,8 @@ package structs import ( - "github.com/gogf/gf/errors/gerror" + "errors" + "fmt" "reflect" ) @@ -49,9 +50,11 @@ exitLoop: reflectKind = reflectValue.Kind() } if reflectKind != reflect.Struct { - return nil, gerror.Newf( - `invalid object kind "%s", kind of "struct" is required`, - reflectKind, + return nil, errors.New( + fmt.Sprintf( + `invalid object kind "%s", kind of "struct" is required`, + reflectKind, + ), ) } reflectType = reflectValue.Type() diff --git a/net/ghttp/ghttp_func.go b/net/ghttp/ghttp_func.go index 7047f0317..9b5e09be2 100644 --- a/net/ghttp/ghttp_func.go +++ b/net/ghttp/ghttp_func.go @@ -26,6 +26,7 @@ func niceCallFunc(f func()) { switch exception { case exceptionExit, exceptionExitAll: return + default: if _, ok := exception.(errorStack); ok { // It's already an error that has stack info. @@ -35,9 +36,13 @@ func niceCallFunc(f func()) { // Note that there's a skip pointing the start stacktrace // of the real error point. if err, ok := exception.(error); ok { - panic(gerror.Wrap(err, "")) + if gerror.Code(err) != gerror.CodeNil { + panic(gerror.Wrap(err, "")) + } else { + panic(gerror.WrapCode(gerror.CodeInternalError, err, "")) + } } else { - panic(gerror.NewSkipf(1, "%v", exception)) + panic(gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception)) } } } diff --git a/net/ghttp/ghttp_middleware_handler_response.go b/net/ghttp/ghttp_middleware_handler_response.go index 1a771e877..f8c71c318 100644 --- a/net/ghttp/ghttp_middleware_handler_response.go +++ b/net/ghttp/ghttp_middleware_handler_response.go @@ -27,8 +27,12 @@ func MiddlewareHandlerResponse(r *Request) { ) res, err = r.GetHandlerResponse() if err != nil { + code := gerror.Code(err) + if code == gerror.CodeNil { + code = gerror.CodeInternalError + } internalErr = r.Response.WriteJson(DefaultHandlerResponse{ - Code: gerror.Code(err), + Code: code, Message: err.Error(), Data: nil, }) @@ -38,7 +42,7 @@ func MiddlewareHandlerResponse(r *Request) { return } internalErr = r.Response.WriteJson(DefaultHandlerResponse{ - Code: 0, + Code: gerror.CodeOk, Message: "", Data: res, }) diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index fbaf7a7ba..fe76ca064 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -127,7 +127,7 @@ func (m *middleware) Next() { // Create a new error with stack info. // Note that there's a skip pointing the start stacktrace // of the real error point. - m.request.error = gerror.WrapSkip(1, exception, "") + m.request.error = gerror.WrapCodeSkip(gerror.CodeInternalError, 1, exception, "") } m.request.Response.WriteStatus(http.StatusInternalServerError, exception) loop = false diff --git a/net/ghttp/ghttp_request_param_file.go b/net/ghttp/ghttp_request_param_file.go index 997b82ed4..aea6961cf 100644 --- a/net/ghttp/ghttp_request_param_file.go +++ b/net/ghttp/ghttp_request_param_file.go @@ -35,14 +35,17 @@ type UploadFiles []*UploadFile // Note that it will OVERWRITE the target file if there's already a same name file exist. func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename string, err error) { if f == nil { - return "", gerror.New("file is empty, maybe you retrieve it from invalid field name or form enctype") + return "", gerror.NewCode( + gerror.CodeMissingParameter, + "file is empty, maybe you retrieve it from invalid field name or form enctype", + ) } if !gfile.Exists(dirPath) { if err = gfile.Mkdir(dirPath); err != nil { return } } else if !gfile.IsDir(dirPath) { - return "", gerror.New(`parameter "dirPath" should be a directory path`) + return "", gerror.NewCode(gerror.CodeInvalidParameter, `parameter "dirPath" should be a directory path`) } file, err := f.Open() @@ -76,7 +79,10 @@ func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename stri // The parameter <randomlyRename> specifies whether randomly renames all the file names. func (fs UploadFiles) Save(dirPath string, randomlyRename ...bool) (filenames []string, err error) { if len(fs) == 0 { - return nil, gerror.New("file array is empty, maybe you retrieve it from invalid field name or form enctype") + return nil, gerror.NewCode( + gerror.CodeMissingParameter, + "file array is empty, maybe you retrieve it from invalid field name or form enctype", + ) } for _, f := range fs { if filename, err := f.Save(dirPath, randomlyRename...); err != nil { diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 36d6cc07c..041b182e7 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -125,7 +125,7 @@ func (s *Server) Start() error { // Server can only be run once. if s.Status() == ServerStatusRunning { - return gerror.New("server is already running") + return gerror.NewCode(gerror.CodeInvalidOperation, "server is already running") } // Logging path setting check. @@ -141,7 +141,7 @@ func (s *Server) Start() error { path = gfile.Join(s.config.SessionPath, s.name) if !gfile.Exists(path) { if err := gfile.Mkdir(path); err != nil { - return gerror.Wrapf(err, `mkdir failed for "%s"`, path) + return gerror.WrapCodef(gerror.CodeInternalError, err, `mkdir failed for "%s"`, path) } } } @@ -175,7 +175,10 @@ func (s *Server) Start() error { // If there's no route registered and no static service enabled, // it then returns an error of invalid usage of server. if len(s.routesMap) == 0 && !s.config.FileServerEnabled { - return gerror.New(`there's no route set or static feature enabled, did you forget import the router?`) + return gerror.NewCode( + gerror.CodeInvalidOperation, + `there's no route set or static feature enabled, did you forget import the router?`, + ) } // Start the HTTP server. diff --git a/net/ghttp/ghttp_server_admin_process.go b/net/ghttp/ghttp_server_admin_process.go index e6fa2e622..9fe65b8d3 100644 --- a/net/ghttp/ghttp_server_admin_process.go +++ b/net/ghttp/ghttp_server_admin_process.go @@ -52,7 +52,7 @@ var serverProcessStatus = gtype.NewInt() // The optional parameter <newExeFilePath> specifies the new binary file for creating process. func RestartAllServer(newExeFilePath ...string) error { if !gracefulEnabled { - return gerror.New("graceful reload feature is disabled") + return gerror.NewCode(gerror.CodeInvalidOperation, "graceful reload feature is disabled") } serverActionLocker.Lock() defer serverActionLocker.Unlock() @@ -85,9 +85,10 @@ func checkProcessStatus() error { if status > 0 { switch status { case adminActionRestarting: - return gerror.New("server is restarting") + return gerror.NewCode(gerror.CodeInvalidOperation, "server is restarting") + case adminActionShuttingDown: - return gerror.New("server is shutting down") + return gerror.NewCode(gerror.CodeInvalidOperation, "server is shutting down") } } return nil @@ -98,7 +99,11 @@ func checkProcessStatus() error { func checkActionFrequency() error { interval := gtime.TimestampMilli() - serverActionLastTime.Val() if interval < adminActionIntervalLimit { - return gerror.Newf("too frequent action, please retry in %d ms", adminActionIntervalLimit-interval) + return gerror.NewCodef( + gerror.CodeInvalidOperation, + "too frequent action, please retry in %d ms", + adminActionIntervalLimit-interval, + ) } serverActionLastTime.Set(gtime.TimestampMilli()) return nil diff --git a/net/ghttp/ghttp_server_graceful.go b/net/ghttp/ghttp_server_graceful.go index 2e6f82390..6af3a3368 100644 --- a/net/ghttp/ghttp_server_graceful.go +++ b/net/ghttp/ghttp_server_graceful.go @@ -122,7 +122,7 @@ func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig . } if err != nil { - return gerror.Newf(`open cert file "%s","%s" failed: %s`, certFile, keyFile, err.Error()) + return gerror.WrapCodef(gerror.CodeInternalError, err, `open cert file "%s","%s" failed`, certFile, keyFile) } ln, err := s.getNetListener() if err != nil { diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index 64bdf0ee1..e461a9222 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -67,9 +67,13 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { if exception := recover(); exception != nil { request.Response.WriteStatus(http.StatusInternalServerError) if err, ok := exception.(error); ok { - s.handleErrorLog(gerror.Wrap(err, ""), request) + if code := gerror.Code(err); code != gerror.CodeNil { + s.handleErrorLog(gerror.Wrap(err, ""), request) + } else { + s.handleErrorLog(gerror.WrapCode(gerror.CodeInternalError, err, ""), request) + } } else { - s.handleErrorLog(gerror.Newf("%v", exception), request) + s.handleErrorLog(gerror.NewCodef(gerror.CodeInternalError, "%v", exception), request) } } } diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index bee668ddd..1dd4b630b 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -53,7 +53,7 @@ func (s *Server) parsePattern(pattern string) (domain, method, path string, err } } if path == "" { - err = gerror.New("invalid pattern: URI should not be empty") + err = gerror.NewCode(gerror.CodeInvalidParameter, "invalid pattern: URI should not be empty") } if path != "/" { path = strings.TrimRight(path, "/") diff --git a/net/ghttp/ghttp_server_service_handler.go b/net/ghttp/ghttp_server_service_handler.go index 2dbadb2ea..1b846e7e1 100644 --- a/net/ghttp/ghttp_server_service_handler.go +++ b/net/ghttp/ghttp_server_service_handler.go @@ -128,12 +128,14 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, objName, methodN reflectType := reflect.TypeOf(f) if reflectType.NumIn() == 0 || reflectType.NumIn() > 2 || reflectType.NumOut() > 2 { if pkgPath != "" { - err = gerror.Newf( + err = gerror.NewCodef( + gerror.CodeInvalidParameter, `invalid handler: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" or "func(context.Context)/func(context.Context,Request)/func(context.Context,Request) error/func(context.Context,Request)(Response,error)" is required`, pkgPath, objName, methodName, reflect.TypeOf(f).String(), ) } else { - err = gerror.Newf( + err = gerror.NewCodef( + gerror.CodeInvalidParameter, `invalid handler: defined as "%s", but "func(*ghttp.Request)" or "func(context.Context)/func(context.Context,Request)/func(context.Context,Request) error/func(context.Context,Request)(Response,error)" is required`, reflect.TypeOf(f).String(), ) @@ -142,7 +144,8 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, objName, methodN } if reflectType.In(0).String() != "context.Context" { - err = gerror.Newf( + err = gerror.NewCodef( + gerror.CodeInvalidParameter, `invalid handler: defined as "%s", but the first input parameter should be type of "context.Context"`, reflect.TypeOf(f).String(), ) @@ -150,7 +153,8 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, objName, methodN } if reflectType.NumOut() > 0 && reflectType.Out(reflectType.NumOut()-1).String() != "error" { - err = gerror.Newf( + err = gerror.NewCodef( + gerror.CodeInvalidParameter, `invalid handler: defined as "%s", but the last output parameter should be type of "error"`, reflect.TypeOf(f).String(), ) diff --git a/net/ghttp/internal/client/client.go b/net/ghttp/internal/client/client.go index b1e8620a8..59513a847 100644 --- a/net/ghttp/internal/client/client.go +++ b/net/ghttp/internal/client/client.go @@ -242,27 +242,27 @@ func (c *Client) SetProxy(proxyURL string) { } } -// SetTlsKeyCrt sets the certificate and key file for TLS configuration of client. +// SetTLSKeyCrt sets the certificate and key file for TLS configuration of client. func (c *Client) SetTLSKeyCrt(crtFile, keyFile string) error { tlsConfig, err := LoadKeyCrt(crtFile, keyFile) if err != nil { - return err + return gerror.WrapCode(gerror.CodeInternalError, err, "LoadKeyCrt failed") } if v, ok := c.Transport.(*http.Transport); ok { tlsConfig.InsecureSkipVerify = true v.TLSClientConfig = tlsConfig return nil } - return gerror.New(`cannot set TLSClientConfig for custom Transport of the client`) + return gerror.NewCode(gerror.CodeInternalError, `cannot set TLSClientConfig for custom Transport of the client`) } -// SetTlsConfig sets the TLS configuration of client. +// SetTLSConfig sets the TLS configuration of client. func (c *Client) SetTLSConfig(tlsConfig *tls.Config) error { if v, ok := c.Transport.(*http.Transport); ok { v.TLSClientConfig = tlsConfig return nil } - return gerror.New(`cannot set TLSClientConfig for custom Transport of the client`) + return gerror.NewCode(gerror.CodeInternalError, `cannot set TLSClientConfig for custom Transport of the client`) } // LoadKeyCrt creates and returns a TLS configuration object with given certificate and key files. diff --git a/net/ghttp/internal/client/client_request.go b/net/ghttp/internal/client/client_request.go index 90b8970b9..781c5a2ff 100644 --- a/net/ghttp/internal/client/client_request.go +++ b/net/ghttp/internal/client/client_request.go @@ -188,7 +188,7 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h if len(array[1]) > 6 && strings.Compare(array[1][0:6], "@file:") == 0 { path := array[1][6:] if !gfile.Exists(path) { - return nil, gerror.Newf(`"%s" does not exist`, path) + return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `"%s" does not exist`, path) } if file, err := writer.CreateFormFile(array[0], gfile.Basename(path)); err == nil { if f, err := os.Open(path); err == nil { diff --git a/net/gipv4/gipv4_ip.go b/net/gipv4/gipv4_ip.go index 29c113d23..aab88ccea 100644 --- a/net/gipv4/gipv4_ip.go +++ b/net/gipv4/gipv4_ip.go @@ -38,7 +38,7 @@ func GetIntranetIp() (ip string, err error) { return "", err } if len(ips) == 0 { - return "", gerror.New("no intranet ip found") + return "", gerror.NewCode(gerror.CodeOperationFailed, "no intranet ip found") } return ips[0], nil } diff --git a/net/gtcp/gtcp_server.go b/net/gtcp/gtcp_server.go index d8c4f22fd..6d22539af 100644 --- a/net/gtcp/gtcp_server.go +++ b/net/gtcp/gtcp_server.go @@ -116,7 +116,7 @@ func (s *Server) Close() error { // Run starts running the TCP Server. func (s *Server) Run() (err error) { if s.handler == nil { - err = gerror.New("start running failed: socket handler not defined") + err = gerror.NewCode(gerror.CodeMissingConfiguration, "start running failed: socket handler not defined") glog.Error(err) return } diff --git a/net/gudp/gudp_server.go b/net/gudp/gudp_server.go index 4987787c9..89f4439c2 100644 --- a/net/gudp/gudp_server.go +++ b/net/gudp/gudp_server.go @@ -78,7 +78,7 @@ func (s *Server) Close() error { // Run starts listening UDP connection. func (s *Server) Run() error { if s.handler == nil { - err := gerror.New("start running failed: socket handler not defined") + err := gerror.NewCode(gerror.CodeMissingConfiguration, "start running failed: socket handler not defined") glog.Error(err) return err } diff --git a/os/gcfg/gcfg_config.go b/os/gcfg/gcfg_config.go index 425a09df5..bff1d6e04 100644 --- a/os/gcfg/gcfg_config.go +++ b/os/gcfg/gcfg_config.go @@ -142,7 +142,7 @@ func (c *Config) SetPath(path string) error { } else { buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path)) } - err := gerror.New(buffer.String()) + err := gerror.NewCode(gerror.CodeOperationFailed, buffer.String()) if errorPrint() { glog.Error(err) } @@ -219,14 +219,14 @@ func (c *Config) AddPath(path string) error { } else { buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path)) } - err := gerror.New(buffer.String()) + err := gerror.NewCode(gerror.CodeOperationFailed, buffer.String()) if errorPrint() { glog.Error(err) } return err } if !isDir { - err := gerror.Newf(`[gcfg] AddPath failed: path "%s" should be directory type`, path) + err := gerror.NewCodef(gerror.CodeInvalidParameter, `[gcfg] AddPath failed: path "%s" should be directory type`, path) if errorPrint() { glog.Error(err) } @@ -329,7 +329,7 @@ func (c *Config) GetFilePath(file ...string) (path string, err error) { } else { buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path configured", name)) } - err = gerror.New(buffer.String()) + err = gerror.NewCode(gerror.CodeOperationFailed, buffer.String()) } return } diff --git a/os/gcfg/gcfg_config_api.go b/os/gcfg/gcfg_config_api.go index 7e1f0625e..4e7a2a9e8 100644 --- a/os/gcfg/gcfg_config_api.go +++ b/os/gcfg/gcfg_config_api.go @@ -295,7 +295,7 @@ func (c *Config) GetStruct(pattern string, pointer interface{}, mapping ...map[s if j := c.getJson(); j != nil { return j.GetStruct(pattern, pointer, mapping...) } - return gerror.New("configuration not found") + return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") } // GetStructs converts any slice to given struct slice. @@ -303,7 +303,7 @@ func (c *Config) GetStructs(pattern string, pointer interface{}, mapping ...map[ if j := c.getJson(); j != nil { return j.GetStructs(pattern, pointer, mapping...) } - return gerror.New("configuration not found") + return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") } // GetMapToMap retrieves the value by specified `pattern` and converts it to specified map variable. @@ -312,7 +312,7 @@ func (c *Config) GetMapToMap(pattern string, pointer interface{}, mapping ...map if j := c.getJson(); j != nil { return j.GetMapToMap(pattern, pointer, mapping...) } - return gerror.New("configuration not found") + return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") } // GetMapToMaps retrieves the value by specified `pattern` and converts it to specified map slice @@ -322,7 +322,7 @@ func (c *Config) GetMapToMaps(pattern string, pointer interface{}, mapping ...ma if j := c.getJson(); j != nil { return j.GetMapToMaps(pattern, pointer, mapping...) } - return gerror.New("configuration not found") + return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") } // GetMapToMapsDeep retrieves the value by specified `pattern` and converts it to specified map slice @@ -332,7 +332,7 @@ func (c *Config) GetMapToMapsDeep(pattern string, pointer interface{}, mapping . if j := c.getJson(); j != nil { return j.GetMapToMapsDeep(pattern, pointer, mapping...) } - return gerror.New("configuration not found") + return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") } // Map converts current Json object to map[string]interface{}. It returns nil if fails. @@ -358,7 +358,7 @@ func (c *Config) Struct(pointer interface{}, mapping ...map[string]string) error if j := c.getJson(); j != nil { return j.Struct(pointer, mapping...) } - return gerror.New("configuration not found") + return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") } // Structs converts current Json object to specified object slice. @@ -367,7 +367,7 @@ func (c *Config) Structs(pointer interface{}, mapping ...map[string]string) erro if j := c.getJson(); j != nil { return j.Structs(pointer, mapping...) } - return gerror.New("configuration not found") + return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") } // MapToMap converts current Json object to specified map variable. @@ -376,7 +376,7 @@ func (c *Config) MapToMap(pointer interface{}, mapping ...map[string]string) err if j := c.getJson(); j != nil { return j.MapToMap(pointer, mapping...) } - return gerror.New("configuration not found") + return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") } // MapToMaps converts current Json object to specified map variable slice. @@ -385,7 +385,7 @@ func (c *Config) MapToMaps(pointer interface{}, mapping ...map[string]string) er if j := c.getJson(); j != nil { return j.MapToMaps(pointer, mapping...) } - return gerror.New("configuration not found") + return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") } // Clear removes all parsed configuration files content cache, diff --git a/os/gcmd/gcmd_handler.go b/os/gcmd/gcmd_handler.go index 73d6c73c3..a0a4b4c8c 100644 --- a/os/gcmd/gcmd_handler.go +++ b/os/gcmd/gcmd_handler.go @@ -14,14 +14,14 @@ import ( // BindHandle registers callback function <f> with <cmd>. func BindHandle(cmd string, f func()) error { if _, ok := defaultCommandFuncMap[cmd]; ok { - return gerror.New("duplicated handle for command:" + cmd) + return gerror.NewCode(gerror.CodeInvalidOperation, "duplicated handle for command:"+cmd) } else { defaultCommandFuncMap[cmd] = f } return nil } -// BindHandle registers callback function with map <m>. +// BindHandleMap registers callback function with map <m>. func BindHandleMap(m map[string]func()) error { var err error for k, v := range m { @@ -37,7 +37,7 @@ func RunHandle(cmd string) error { if handle, ok := defaultCommandFuncMap[cmd]; ok { handle() } else { - return gerror.New("no handle found for command:" + cmd) + return gerror.NewCode(gerror.CodeMissingConfiguration, "no handle found for command:"+cmd) } return nil } @@ -49,10 +49,10 @@ func AutoRun() error { if handle, ok := defaultCommandFuncMap[cmd]; ok { handle() } else { - return gerror.New("no handle found for command:" + cmd) + return gerror.NewCode(gerror.CodeMissingConfiguration, "no handle found for command:"+cmd) } } else { - return gerror.New("no command found") + return gerror.NewCode(gerror.CodeMissingParameter, "no command found") } return nil } diff --git a/os/gcmd/gcmd_parser.go b/os/gcmd/gcmd_parser.go index 1ac003ae2..e6b20b804 100644 --- a/os/gcmd/gcmd_parser.go +++ b/os/gcmd/gcmd_parser.go @@ -94,7 +94,7 @@ func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bo i++ continue } else if parser.strict { - return nil, gerror.Newf(`invalid option '%s'`, args[i]) + return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid option '%s'`, args[i]) } } } diff --git a/os/gcmd/gcmd_parser_handler.go b/os/gcmd/gcmd_parser_handler.go index 6e61712f4..03e1ed093 100644 --- a/os/gcmd/gcmd_parser_handler.go +++ b/os/gcmd/gcmd_parser_handler.go @@ -14,7 +14,7 @@ import ( // BindHandle registers callback function <f> with <cmd>. func (p *Parser) BindHandle(cmd string, f func()) error { if _, ok := p.commandFuncMap[cmd]; ok { - return gerror.New("duplicated handle for command:" + cmd) + return gerror.NewCode(gerror.CodeInvalidOperation, "duplicated handle for command:"+cmd) } else { p.commandFuncMap[cmd] = f } @@ -37,7 +37,7 @@ func (p *Parser) RunHandle(cmd string) error { if handle, ok := p.commandFuncMap[cmd]; ok { handle() } else { - return gerror.New("no handle found for command:" + cmd) + return gerror.NewCode(gerror.CodeMissingConfiguration, "no handle found for command:"+cmd) } return nil } @@ -49,10 +49,10 @@ func (p *Parser) AutoRun() error { if handle, ok := p.commandFuncMap[cmd]; ok { handle() } else { - return gerror.New("no handle found for command:" + cmd) + return gerror.NewCode(gerror.CodeMissingConfiguration, "no handle found for command:"+cmd) } } else { - return gerror.New("no command found") + return gerror.NewCode(gerror.CodeMissingParameter, "no command found") } return nil } diff --git a/os/gcron/gcron_cron.go b/os/gcron/gcron_cron.go index 0e148b970..83a5ea8b2 100644 --- a/os/gcron/gcron_cron.go +++ b/os/gcron/gcron_cron.go @@ -62,7 +62,7 @@ func (c *Cron) GetLogLevel() int { func (c *Cron) Add(pattern string, job func(), name ...string) (*Entry, error) { if len(name) > 0 { if c.Search(name[0]) != nil { - return nil, gerror.Newf(`cron job "%s" already exists`, name[0]) + return nil, gerror.NewCodef(gerror.CodeInvalidOperation, `cron job "%s" already exists`, name[0]) } } return c.addEntry(pattern, job, false, name...) diff --git a/os/gcron/gcron_schedule.go b/os/gcron/gcron_schedule.go index a960843e3..bd50c4c1e 100644 --- a/os/gcron/gcron_schedule.go +++ b/os/gcron/gcron_schedule.go @@ -90,7 +90,7 @@ func newSchedule(pattern string) (*cronSchedule, error) { }, nil } } else { - return nil, gerror.Newf(`invalid pattern: "%s"`, pattern) + return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern: "%s"`, pattern) } } // Handle the common cron pattern, like: @@ -139,7 +139,7 @@ func newSchedule(pattern string) (*cronSchedule, error) { } return schedule, nil } else { - return nil, gerror.Newf(`invalid pattern: "%s"`, pattern) + return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern: "%s"`, pattern) } } @@ -156,7 +156,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s intervalArray := strings.Split(item, "/") if len(intervalArray) == 2 { if i, err := strconv.Atoi(intervalArray[1]); err != nil { - return nil, gerror.Newf(`invalid pattern item: "%s"`, item) + return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { interval = i } @@ -178,7 +178,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s // Eg: */5 if rangeArray[0] != "*" { if i, err := parseItemValue(rangeArray[0], fieldType); err != nil { - return nil, gerror.Newf(`invalid pattern item: "%s"`, item) + return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { rangeMin = i rangeMax = i @@ -186,7 +186,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s } if len(rangeArray) == 2 { if i, err := parseItemValue(rangeArray[1], fieldType); err != nil { - return nil, gerror.Newf(`invalid pattern item: "%s"`, item) + return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { rangeMax = i } @@ -220,7 +220,7 @@ func parseItemValue(value string, fieldType byte) (int, error) { } } } - return 0, gerror.Newf(`invalid pattern value: "%s"`, value) + return 0, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern value: "%s"`, value) } // meet checks if the given time <t> meets the runnable point for the job. diff --git a/os/gfile/gfile_copy.go b/os/gfile/gfile_copy.go index 32e9f1320..b8f30c68e 100644 --- a/os/gfile/gfile_copy.go +++ b/os/gfile/gfile_copy.go @@ -7,8 +7,8 @@ package gfile import ( - "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "io" "io/ioutil" "os" @@ -21,10 +21,10 @@ import ( // or else it calls CopyDir. func Copy(src string, dst string) error { if src == "" { - return errors.New("source path cannot be empty") + return gerror.NewCode(gerror.CodeInvalidParameter, "source path cannot be empty") } if dst == "" { - return errors.New("destination path cannot be empty") + return gerror.NewCode(gerror.CodeInvalidParameter, "destination path cannot be empty") } if IsFile(src) { return CopyFile(src, dst) @@ -40,10 +40,10 @@ func Copy(src string, dst string) error { // Thanks: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04 func CopyFile(src, dst string) (err error) { if src == "" { - return errors.New("source file cannot be empty") + return gerror.NewCode(gerror.CodeInvalidParameter, "source file cannot be empty") } if dst == "" { - return errors.New("destination file cannot be empty") + return gerror.NewCode(gerror.CodeInvalidParameter, "destination file cannot be empty") } // If src and dst are the same path, it does nothing. if src == dst { @@ -87,10 +87,10 @@ func CopyFile(src, dst string) (err error) { // Note that, the Source directory must exist and symlinks are ignored and skipped. func CopyDir(src string, dst string) (err error) { if src == "" { - return errors.New("source directory cannot be empty") + return gerror.NewCode(gerror.CodeInvalidParameter, "source directory cannot be empty") } if dst == "" { - return errors.New("destination directory cannot be empty") + return gerror.NewCode(gerror.CodeInvalidParameter, "destination directory cannot be empty") } // If src and dst are the same path, it does nothing. if src == dst { diff --git a/os/gfile/gfile_home.go b/os/gfile/gfile_home.go index 5e5d42fca..15819b550 100644 --- a/os/gfile/gfile_home.go +++ b/os/gfile/gfile_home.go @@ -8,7 +8,7 @@ package gfile import ( "bytes" - "errors" + "github.com/gogf/gf/errors/gerror" "os" "os/exec" "os/user" @@ -56,7 +56,7 @@ func homeUnix() (string, error) { result := strings.TrimSpace(stdout.String()) if result == "" { - return "", errors.New("blank output when reading home directory") + return "", gerror.NewCode(gerror.CodeInternalError, "blank output when reading home directory") } return result, nil @@ -73,7 +73,7 @@ func homeWindows() (string, error) { home = os.Getenv("USERPROFILE") } if home == "" { - return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank") + return "", gerror.NewCode(gerror.CodeOperationFailed, "HOMEDRIVE, HOMEPATH, and USERPROFILE are blank") } return home, nil diff --git a/os/gfile/gfile_scan.go b/os/gfile/gfile_scan.go index 3b488f776..a14672fe9 100644 --- a/os/gfile/gfile_scan.go +++ b/os/gfile/gfile_scan.go @@ -137,7 +137,7 @@ func ScanDirFileFunc(path string, pattern string, recursive bool, handler func(p // string, or else it appends the sub-file path to result slice. func doScanDir(depth int, path string, pattern string, recursive bool, handler func(path string) string) ([]string, error) { if depth >= maxScanDepth { - return nil, gerror.Newf("directory scanning exceeds max recursive depth: %d", maxScanDepth) + return nil, gerror.NewCodef(gerror.CodeOperationFailed, "directory scanning exceeds max recursive depth: %d", maxScanDepth) } list := ([]string)(nil) file, err := os.Open(path) diff --git a/os/gfile/gfile_search.go b/os/gfile/gfile_search.go index 08d3d01fc..7dcc9d77d 100644 --- a/os/gfile/gfile_search.go +++ b/os/gfile/gfile_search.go @@ -8,8 +8,8 @@ package gfile import ( "bytes" - "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/container/garray" ) @@ -52,7 +52,7 @@ func Search(name string, prioritySearchPaths ...string) (realPath string, err er buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) } }) - err = errors.New(buffer.String()) + err = gerror.NewCode(gerror.CodeOperationFailed, buffer.String()) } return } diff --git a/os/gfpool/gfpool_file.go b/os/gfpool/gfpool_file.go index 8a734ecde..aab691279 100644 --- a/os/gfpool/gfpool_file.go +++ b/os/gfpool/gfpool_file.go @@ -7,8 +7,8 @@ package gfpool import ( - "errors" "fmt" + "github.com/gogf/gf/errors/gerror" "os" "time" ) @@ -41,7 +41,7 @@ func Open(path string, flag int, perm os.FileMode, ttl ...time.Duration) (file * // Stat returns the FileInfo structure describing file. func (f *File) Stat() (os.FileInfo, error) { if f.stat == nil { - return nil, errors.New("file stat is empty") + return nil, gerror.NewCode(gerror.CodeInternalError, "file stat is empty") } return f.stat, nil } diff --git a/os/gfsnotify/gfsnotify.go b/os/gfsnotify/gfsnotify.go index 9c2f5864f..89fb6b958 100644 --- a/os/gfsnotify/gfsnotify.go +++ b/os/gfsnotify/gfsnotify.go @@ -139,7 +139,7 @@ func RemoveCallback(callbackId int) error { callback = r.(*Callback) } if callback == nil { - return gerror.Newf(`callback for id %d not found`, callbackId) + return gerror.NewCodef(gerror.CodeInvalidParameter, `callback for id %d not found`, callbackId) } w.RemoveCallback(callbackId) return nil diff --git a/os/gfsnotify/gfsnotify_watcher.go b/os/gfsnotify/gfsnotify_watcher.go index 777ada43a..af6c2ff22 100644 --- a/os/gfsnotify/gfsnotify_watcher.go +++ b/os/gfsnotify/gfsnotify_watcher.go @@ -66,7 +66,7 @@ func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), re func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { // Check and convert the given path to absolute path. if t := fileRealPath(path); t == "" { - return nil, gerror.Newf(`"%s" does not exist`, path) + return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `"%s" does not exist`, path) } else { path = t } diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index b4d17b6f7..6cc95ef05 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -81,7 +81,7 @@ func (l *Logger) SetConfig(config Config) error { // SetConfigWithMap set configurations with map for the logger. func (l *Logger) SetConfigWithMap(m map[string]interface{}) error { if m == nil || len(m) == 0 { - return gerror.New("configuration cannot be empty") + return gerror.NewCode(gerror.CodeInvalidParameter, "configuration cannot be empty") } // The m now is a shallow copy of m. // A little tricky, isn't it? @@ -92,7 +92,7 @@ func (l *Logger) SetConfigWithMap(m map[string]interface{}) error { if level, ok := levelStringMap[strings.ToUpper(gconv.String(levelValue))]; ok { m[levelKey] = level } else { - return gerror.Newf(`invalid level string: %v`, levelValue) + return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid level string: %v`, levelValue) } } // Change string configuration to int value for file rotation size. @@ -100,7 +100,7 @@ func (l *Logger) SetConfigWithMap(m map[string]interface{}) error { if rotateSizeValue != nil { m[rotateSizeKey] = gfile.StrToSize(gconv.String(rotateSizeValue)) if m[rotateSizeKey] == -1 { - return gerror.Newf(`invalid rotate size: %v`, rotateSizeValue) + return gerror.NewCodef(gerror.CodeInvalidConfiguration, `invalid rotate size: %v`, rotateSizeValue) } } if err := gconv.Struct(m, &l.config); err != nil { @@ -205,12 +205,11 @@ func (l *Logger) GetWriter() io.Writer { // SetPath sets the directory path for file logging. func (l *Logger) SetPath(path string) error { if path == "" { - return gerror.New("logging path is empty") + return gerror.NewCode(gerror.CodeInvalidParameter, "logging path is empty") } if !gfile.Exists(path) { if err := gfile.Mkdir(path); err != nil { - //fmt.Fprintln(os.Stderr, fmt.Sprintf(`[glog] mkdir "%s" failed: %s`, path, err.Error())) - return gerror.Wrapf(err, `Mkdir "%s" failed in PWD "%s"`, path, gfile.Pwd()) + return gerror.WrapCodef(gerror.CodeOperationFailed, err, `Mkdir "%s" failed in PWD "%s"`, path, gfile.Pwd()) } } l.config.Path = strings.TrimRight(path, gfile.Separator) diff --git a/os/glog/glog_logger_level.go b/os/glog/glog_logger_level.go index 67fae2cd7..88c2e374d 100644 --- a/os/glog/glog_logger_level.go +++ b/os/glog/glog_logger_level.go @@ -77,7 +77,7 @@ func (l *Logger) SetLevelStr(levelStr string) error { if level, ok := levelStringMap[strings.ToUpper(levelStr)]; ok { l.config.Level = level } else { - return gerror.Newf(`invalid level string: %s`, levelStr) + return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid level string: %s`, levelStr) } return nil } diff --git a/os/gproc/gproc_comm.go b/os/gproc/gproc_comm.go index fc013eec9..f9014c2a2 100644 --- a/os/gproc/gproc_comm.go +++ b/os/gproc/gproc_comm.go @@ -65,7 +65,7 @@ func getConnByPid(pid int) (*gtcp.PoolConn, error) { return nil, err } } - return nil, gerror.Newf("could not find port for pid: %d", pid) + return nil, gerror.NewCodef(gerror.CodeOperationFailed, "could not find port for pid: %d", pid) } // getPortByPid returns the listening port for specified pid. diff --git a/os/gproc/gproc_comm_send.go b/os/gproc/gproc_comm_send.go index 7946c9a81..762042162 100644 --- a/os/gproc/gproc_comm_send.go +++ b/os/gproc/gproc_comm_send.go @@ -43,8 +43,7 @@ func Send(pid int, data []byte, group ...string) error { }) if len(result) > 0 { response := new(MsgResponse) - err = json.UnmarshalUseNumber(result, response) - if err == nil { + if err = json.UnmarshalUseNumber(result, response); err == nil { if response.Code != 1 { err = gerror.New(response.Message) } diff --git a/os/gproc/gproc_process.go b/os/gproc/gproc_process.go index 51fa28d70..870f89cca 100644 --- a/os/gproc/gproc_process.go +++ b/os/gproc/gproc_process.go @@ -101,7 +101,7 @@ func (p *Process) Send(data []byte) error { if p.Process != nil { return Send(p.Process.Pid, data) } - return gerror.New("invalid process") + return gerror.NewCode(gerror.CodeInvalidParameter, "invalid process") } // Release releases any resources associated with the Process p, diff --git a/os/grpool/grpool.go b/os/grpool/grpool.go index 707eb1304..08d6134cf 100644 --- a/os/grpool/grpool.go +++ b/os/grpool/grpool.go @@ -69,7 +69,7 @@ func Jobs() int { // The job will be executed asynchronously. func (p *Pool) Add(f func()) error { for p.closed.Val() { - return gerror.New("pool closed") + return gerror.NewCode(gerror.CodeInvalidOperation, "pool closed") } p.list.PushFront(f) // Check whether fork new goroutine or not. @@ -96,9 +96,13 @@ func (p *Pool) Add(f func()) error { func (p *Pool) AddWithRecover(userFunc func(), recoverFunc ...func(err error)) error { return p.Add(func() { defer func() { - if err := recover(); err != nil { + if exception := recover(); exception != nil { if len(recoverFunc) > 0 && recoverFunc[0] != nil { - recoverFunc[0](gerror.Newf(`%v`, err)) + if err, ok := exception.(error); ok { + recoverFunc[0](err) + } else { + recoverFunc[0](gerror.NewCodef(gerror.CodeInternalError, `%v`, exception)) + } } } }() diff --git a/os/gsession/gsession.go b/os/gsession/gsession.go index 4d6ac77ad..16b901808 100644 --- a/os/gsession/gsession.go +++ b/os/gsession/gsession.go @@ -8,12 +8,15 @@ package gsession import ( - "errors" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/util/guid" ) var ( - ErrorDisabled = errors.New("this feature is disabled in this storage") + ErrorDisabled = gerror.NewOption(gerror.Option{ + Text: "this feature is disabled in this storage", + Code: gerror.CodeNotSupported, + }) ) // NewSessionId creates and returns a new and unique session id string, diff --git a/os/gsession/gsession_session.go b/os/gsession/gsession_session.go index 2067d5b35..78cdb328f 100644 --- a/os/gsession/gsession_session.go +++ b/os/gsession/gsession_session.go @@ -182,7 +182,7 @@ func (s *Session) Id() string { // It returns error if it is called after session starts. func (s *Session) SetId(id string) error { if s.start { - return gerror.New("session already started") + return gerror.NewCode(gerror.CodeInvalidOperation, "session already started") } s.id = id return nil @@ -192,7 +192,7 @@ func (s *Session) SetId(id string) error { // It returns error if it is called after session starts. func (s *Session) SetIdFunc(f func(ttl time.Duration) string) error { if s.start { - return gerror.New("session already started") + return gerror.NewCode(gerror.CodeInvalidOperation, "session already started") } s.idFunc = f return nil diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index 7bbbaf2ef..98628c301 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -48,15 +48,15 @@ func NewStorageFile(path ...string) *StorageFile { if len(path) > 0 && path[0] != "" { storagePath, _ = gfile.Search(path[0]) if storagePath == "" { - panic(gerror.Newf("'%s' does not exist", path[0])) + panic(gerror.NewCodef(gerror.CodeInvalidParameter, `"%s" does not exist`, path[0])) } if !gfile.IsWritable(storagePath) { - panic(gerror.Newf("'%s' is not writable", path[0])) + panic(gerror.NewCodef(gerror.CodeInvalidParameter, `"%s" is not writable`, path[0])) } } if storagePath != "" { if err := gfile.Mkdir(storagePath); err != nil { - panic(gerror.Wrapf(err, `Mkdir "%s" failed in PWD "%s"`, path, gfile.Pwd())) + panic(gerror.WrapCodef(gerror.CodeInternalError, err, `Mkdir "%s" failed in PWD "%s"`, path, gfile.Pwd())) } } s := &StorageFile{ diff --git a/os/gspath/gspath.go b/os/gspath/gspath.go index e54300629..2e7394086 100644 --- a/os/gspath/gspath.go +++ b/os/gspath/gspath.go @@ -103,7 +103,7 @@ func (sp *SPath) Set(path string) (realPath string, err error) { } } if realPath == "" { - return realPath, gerror.Newf(`path "%s" does not exist`, path) + return realPath, gerror.NewCodef(gerror.CodeInvalidParameter, `path "%s" does not exist`, path) } // The set path must be a directory. if gfile.IsDir(realPath) { @@ -123,7 +123,7 @@ func (sp *SPath) Set(path string) (realPath string, err error) { sp.addMonitorByPath(realPath) return realPath, nil } else { - return "", gerror.New(path + " should be a folder") + return "", gerror.NewCode(gerror.CodeInvalidParameter, path+" should be a folder") } } @@ -138,7 +138,7 @@ func (sp *SPath) Add(path string) (realPath string, err error) { } } if realPath == "" { - return realPath, gerror.Newf(`path "%s" does not exist`, path) + return realPath, gerror.NewCodef(gerror.CodeInvalidParameter, `path "%s" does not exist`, path) } // The added path must be a directory. if gfile.IsDir(realPath) { @@ -152,7 +152,7 @@ func (sp *SPath) Add(path string) (realPath string, err error) { } return realPath, nil } else { - return "", gerror.New(path + " should be a folder") + return "", gerror.NewCode(gerror.CodeInvalidParameter, path+" should be a folder") } } diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index 13b341d36..fe537fb4c 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -276,7 +276,7 @@ func StrToTime(str string, format ...string) (*Time, error) { } return NewFromTime(time.Date(0, time.Month(1), 1, hour, min, sec, nsec, local)), nil } else { - return nil, gerror.New("unsupported time format") + return nil, gerror.NewCode(gerror.CodeInvalidParameter, "unsupported time format") } // Time @@ -311,7 +311,7 @@ func StrToTime(str string, format ...string) (*Time, error) { m, _ := strconv.Atoi(zone[2:4]) s, _ := strconv.Atoi(zone[4:6]) if h > 24 || m > 59 || s > 59 { - return nil, gerror.Newf("invalid zone string: %s", match[6]) + return nil, gerror.NewCodef(gerror.CodeInvalidParameter, "invalid zone string: %s", match[6]) } // Comparing the given time zone whether equals to current time zone, // it converts it to UTC if they does not equal. @@ -350,7 +350,7 @@ func StrToTime(str string, format ...string) (*Time, error) { } } if month <= 0 || day <= 0 { - return nil, gerror.New("invalid time string:" + str) + return nil, gerror.NewCodef(gerror.CodeInvalidParameter, "invalid time string:%s", str) } return NewFromTime(time.Date(year, time.Month(month), day, hour, min, sec, nsec, local)), nil } diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index d3a02594a..785e34a84 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -462,7 +462,7 @@ func (t *Time) UnmarshalText(data []byte) error { *t = *vTime return nil } - return gerror.Newf(`invalid time value: %s`, data) + return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid time value: %s`, data) } // NoValidation marks this struct object will not be validated by package gvalid. diff --git a/os/gview/gview_config.go b/os/gview/gview_config.go index e596b07bf..0bba3cf4b 100644 --- a/os/gview/gview_config.go +++ b/os/gview/gview_config.go @@ -74,7 +74,7 @@ func (view *View) SetConfig(config Config) error { // SetConfigWithMap set configurations with map for the view. func (view *View) SetConfigWithMap(m map[string]interface{}) error { if m == nil || len(m) == 0 { - return gerror.New("configuration cannot be empty") + return gerror.NewCode(gerror.CodeInvalidParameter, "configuration cannot be empty") } // The m now is a shallow copy of m. // Any changes to m does not affect the original one. @@ -123,7 +123,7 @@ func (view *View) SetPath(path string) error { } // Path not exist. if realPath == "" { - err := gerror.Newf(`[gview] SetPath failed: path "%s" does not exist`, path) + err := gerror.NewCodef(gerror.CodeInvalidParameter, `[gview] SetPath failed: path "%s" does not exist`, path) if errorPrint() { glog.Error(err) } @@ -131,7 +131,7 @@ func (view *View) SetPath(path string) error { } // Should be a directory. if !isDir { - err := gerror.Newf(`[gview] SetPath failed: path "%s" should be directory type`, path) + err := gerror.NewCodef(gerror.CodeInvalidParameter, `[gview] SetPath failed: path "%s" should be directory type`, path) if errorPrint() { glog.Error(err) } @@ -177,7 +177,7 @@ func (view *View) AddPath(path string) error { } // Path not exist. if realPath == "" { - err := gerror.Newf(`[gview] AddPath failed: path "%s" does not exist`, path) + err := gerror.NewCodef(gerror.CodeInvalidParameter, `[gview] AddPath failed: path "%s" does not exist`, path) if errorPrint() { glog.Error(err) } @@ -185,7 +185,7 @@ func (view *View) AddPath(path string) error { } // realPath should be type of folder. if !isDir { - err := gerror.Newf(`[gview] AddPath failed: path "%s" should be directory type`, path) + err := gerror.NewCodef(gerror.CodeInvalidParameter, `[gview] AddPath failed: path "%s" should be directory type`, path) if errorPrint() { glog.Error(err) } diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index ef2e242b9..f4da300fa 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -112,7 +112,7 @@ func (view *View) Parse(ctx context.Context, file string, params ...Params) (res tpl, err = tpl.(*texttpl.Template).Parse(item.content) } if err != nil && item.path != "" { - err = gerror.Wrap(err, item.path) + err = gerror.WrapCode(gerror.CodeInternalError, err, item.path) } }) if err != nil { @@ -298,7 +298,7 @@ func (view *View) getTemplate(filePath, folderPath, pattern string) (tpl interfa // formatTemplateObjectCreatingError formats the error that creted from creating template object. func (view *View) formatTemplateObjectCreatingError(filePath, tplName string, err error) error { if err != nil { - return gerror.NewSkip(1, gstr.Replace(err.Error(), tplName, filePath)) + return gerror.NewCodeSkip(gerror.CodeInternalError, 1, gstr.Replace(err.Error(), tplName, filePath)) } return nil } @@ -376,7 +376,7 @@ func (view *View) searchFile(file string) (path string, folder string, resource if errorPrint() { glog.Error(buffer.String()) } - err = gerror.Newf(`template file "%s" not found`, file) + err = gerror.NewCodef(gerror.CodeInvalidParameter, `template file "%s" not found`, file) } return } diff --git a/util/gconv/gconv_maptomap.go b/util/gconv/gconv_maptomap.go index ded5ea744..e61aad53e 100644 --- a/util/gconv/gconv_maptomap.go +++ b/util/gconv/gconv_maptomap.go @@ -86,7 +86,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s pointerKind = pointerRv.Kind() } if pointerKind != reflect.Map { - return gerror.Newf("pointer should be type of *map, but got:%s", pointerKind) + return gerror.NewCodef(gerror.CodeInvalidParameter, "pointer should be type of *map, but got:%s", pointerKind) } defer func() { // Catch the panic, especially the reflect operation panics. @@ -94,7 +94,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s if e, ok := exception.(errorStack); ok { err = e } else { - err = gerror.NewSkipf(1, "%v", exception) + err = gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception) } } }() diff --git a/util/gconv/gconv_maptomaps.go b/util/gconv/gconv_maptomaps.go index f4bebc7dc..b37e1ac31 100644 --- a/util/gconv/gconv_maptomaps.go +++ b/util/gconv/gconv_maptomaps.go @@ -73,7 +73,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] paramsKind = paramsRv.Kind() } if paramsKind != reflect.Array && paramsKind != reflect.Slice { - return gerror.New("params should be type of slice, eg: []map/[]*map/[]struct/[]*struct") + return gerror.NewCode(gerror.CodeInvalidParameter, "params should be type of slice, eg: []map/[]*map/[]struct/[]*struct") } var ( paramsElem = paramsRv.Type().Elem() @@ -84,7 +84,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] paramsElemKind = paramsElem.Kind() } if paramsElemKind != reflect.Map && paramsElemKind != reflect.Struct && paramsElemKind != reflect.Interface { - return gerror.Newf("params element should be type of map/*map/struct/*struct, but got: %s", paramsElemKind) + return gerror.NewCodef(gerror.CodeInvalidParameter, "params element should be type of map/*map/struct/*struct, but got: %s", paramsElemKind) } // Empty slice, no need continue. if paramsRv.Len() == 0 { @@ -100,7 +100,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] pointerKind = pointerRv.Kind() } if pointerKind != reflect.Array && pointerKind != reflect.Slice { - return gerror.New("pointer should be type of *[]map/*[]*map") + return gerror.NewCode(gerror.CodeInvalidParameter, "pointer should be type of *[]map/*[]*map") } var ( pointerElemType = pointerRv.Type().Elem() @@ -110,7 +110,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] pointerElemKind = pointerElemType.Elem().Kind() } if pointerElemKind != reflect.Map { - return gerror.New("pointer element should be type of map/*map") + return gerror.NewCode(gerror.CodeInvalidParameter, "pointer element should be type of map/*map") } defer func() { // Catch the panic, especially the reflect operation panics. @@ -118,7 +118,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] if e, ok := exception.(errorStack); ok { err = e } else { - err = gerror.NewSkipf(1, "%v", exception) + err = gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception) } } }() diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 1dd76832a..125bb1037 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -23,7 +23,7 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) pointerKind = pointerType.Kind() ) if pointerKind != reflect.Ptr { - return gerror.Newf("params should be type of pointer, but got: %v", pointerKind) + return gerror.NewCodef(gerror.CodeInvalidParameter, "params should be type of pointer, but got: %v", pointerKind) } var ( pointerElem = pointerType.Elem() @@ -60,7 +60,7 @@ func ScanDeep(params interface{}, pointer interface{}, mapping ...map[string]str t := reflect.TypeOf(pointer) k := t.Kind() if k != reflect.Ptr { - return gerror.Newf("params should be type of pointer, but got: %v", k) + return gerror.NewCodef(gerror.CodeInvalidParameter, "params should be type of pointer, but got: %v", k) } switch t.Elem().Kind() { case reflect.Array, reflect.Slice: diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 354905ad2..78649056c 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -7,7 +7,6 @@ package gconv import ( - "fmt" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/internal/json" @@ -63,7 +62,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string return nil } if pointer == nil { - return gerror.New("object pointer cannot be nil") + return gerror.NewCode(gerror.CodeInvalidParameter, "object pointer cannot be nil") } defer func() { @@ -72,7 +71,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string if e, ok := exception.(errorStack); ok { err = e } else { - err = gerror.NewSkipf(1, "%v", exception) + err = gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception) } } }() @@ -125,11 +124,11 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string pointerReflectValue = reflect.ValueOf(pointer) pointerReflectKind = pointerReflectValue.Kind() if pointerReflectKind != reflect.Ptr { - return gerror.Newf("object pointer should be type of '*struct', but got '%v'", pointerReflectKind) + return gerror.NewCodef(gerror.CodeInvalidParameter, "object pointer should be type of '*struct', but got '%v'", pointerReflectKind) } // Using IsNil on reflect.Ptr variable is OK. if !pointerReflectValue.IsValid() || pointerReflectValue.IsNil() { - return gerror.New("object pointer cannot be nil") + return gerror.NewCode(gerror.CodeInvalidParameter, "object pointer cannot be nil") } pointerElemReflectValue = pointerReflectValue.Elem() } @@ -167,7 +166,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string // DO NOT use MapDeep here. paramsMap := Map(paramsInterface) if paramsMap == nil { - return gerror.Newf("convert params to map failed: %v", params) + return gerror.NewCodef(gerror.CodeInvalidParameter, "convert params to map failed: %v", params) } // It only performs one converting to the same attribute. @@ -308,7 +307,7 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, map defer func() { if exception := recover(); exception != nil { if err = bindVarToReflectValue(structFieldValue, value, mapping, priorityTag); err != nil { - err = gerror.Wrapf(err, `error binding value to attribute "%s"`, name) + err = gerror.WrapCodef(gerror.CodeInternalError, err, `error binding value to attribute "%s"`, name) } } }() @@ -461,12 +460,12 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma default: defer func() { if exception := recover(); exception != nil { - err = gerror.New( - fmt.Sprintf(`cannot convert value "%+v" to type "%s":%+v`, - value, - structFieldValue.Type().String(), - exception, - ), + err = gerror.NewCodef( + gerror.CodeInternalError, + `cannot convert value "%+v" to type "%s":%+v`, + value, + structFieldValue.Type().String(), + exception, ) } }() diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index d8aafef79..68b4be9ca 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -51,7 +51,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin return nil } if pointer == nil { - return gerror.New("object pointer cannot be nil") + return gerror.NewCode(gerror.CodeInvalidParameter, "object pointer cannot be nil") } if doStructsByDirectReflectSet(params, pointer) { @@ -64,7 +64,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin if e, ok := exception.(errorStack); ok { err = e } else { - err = gerror.NewSkipf(1, "%v", exception) + err = gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception) } } }() @@ -96,7 +96,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin if !ok { pointerRv = reflect.ValueOf(pointer) if kind := pointerRv.Kind(); kind != reflect.Ptr { - return gerror.Newf("pointer should be type of pointer, but got: %v", kind) + return gerror.NewCodef(gerror.CodeInvalidParameter, "pointer should be type of pointer, but got: %v", kind) } } // Converting `params` to map slice. diff --git a/util/gvalid/gvalid_error.go b/util/gvalid/gvalid_error.go index 08c3b498e..fd113e989 100644 --- a/util/gvalid/gvalid_error.go +++ b/util/gvalid/gvalid_error.go @@ -7,7 +7,7 @@ package gvalid import ( - "errors" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" "strings" @@ -15,6 +15,7 @@ import ( // Error is the validation error for validation result. type Error interface { + Code() int Current() error Error() string FirstItem() (key string, messages map[string]string) @@ -29,6 +30,7 @@ type Error interface { // validationError is the validation error for validation result. type validationError struct { + code int // Error code. rules []string // Rules by sequence, which is used for keeping error sequence. errors map[string]map[string]string // Error map:map[field]map[rule]message firstKey string // The first error rule key(empty in default). @@ -36,7 +38,7 @@ type validationError struct { } // newError creates and returns a validation error. -func newError(rules []string, errors map[string]map[string]string) *validationError { +func newError(code int, rules []string, errors map[string]map[string]string) *validationError { for field, m := range errors { for k, v := range m { v = strings.Replace(v, ":attribute", field, -1) @@ -47,6 +49,7 @@ func newError(rules []string, errors map[string]map[string]string) *validationEr errors[field] = m } return &validationError{ + code: code, rules: rules, errors: errors, } @@ -54,13 +57,21 @@ func newError(rules []string, errors map[string]map[string]string) *validationEr // newErrorStr creates and returns a validation error by string. func newErrorStr(key, err string) *validationError { - return newError(nil, map[string]map[string]string{ + return newError(gerror.CodeInternalError, nil, map[string]map[string]string{ internalErrorMapKey: { key: err, }, }) } +// Code returns the error code of current validation error. +func (e *validationError) Code() int { + if e == nil { + return gerror.CodeNil + } + return e.code +} + // Map returns the first error message as map. func (e *validationError) Map() map[string]string { if e == nil { @@ -179,7 +190,7 @@ func (e *validationError) Current() error { return nil } _, err := e.FirstRule() - return errors.New(err) + return gerror.NewCode(e.code, err) } // String returns all error messages as string, multiple error messages joined using char ';'. diff --git a/util/gvalid/gvalid_validator_check_map.go b/util/gvalid/gvalid_validator_check_map.go index e7e88d67e..d48b413e3 100644 --- a/util/gvalid/gvalid_validator_check_map.go +++ b/util/gvalid/gvalid_validator_check_map.go @@ -7,6 +7,7 @@ package gvalid import ( + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/util/gconv" "strings" ) @@ -130,7 +131,7 @@ func (v *Validator) doCheckMap(params interface{}) Error { } } if len(errorMaps) > 0 { - return newError(errorRules, errorMaps) + return newError(gerror.CodeValidationFailed, errorRules, errorMaps) } return nil } diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 58426abfb..868312744 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -7,6 +7,7 @@ package gvalid import ( + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/structs" "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gutil" @@ -249,7 +250,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error { } } if len(errorMaps) > 0 { - return newError(errorRules, errorMaps) + return newError(gerror.CodeValidationFailed, errorRules, errorMaps) } return nil } diff --git a/util/gvalid/gvalid_validator_check_value.go b/util/gvalid/gvalid_validator_check_value.go index a72cee3b3..78377d32d 100644 --- a/util/gvalid/gvalid_validator_check_value.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -7,7 +7,7 @@ package gvalid import ( - "errors" + "github.com/gogf/gf/errors/gerror" "strconv" "strings" "time" @@ -138,7 +138,7 @@ func (v *Validator) doCheckValue( index++ } if len(errorMsgArray) > 0 { - return newError([]string{rules}, map[string]map[string]string{ + return newError(gerror.CodeValidationFailed, []string{rules}, map[string]map[string]string{ key: errorMsgArray, }) } @@ -175,7 +175,10 @@ func (v *Validator) doCheckBuildInRules( "max-length", "size": if msg := v.checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { - return match, errors.New(msg) + return match, gerror.NewOption(gerror.Option{ + Text: msg, + Code: gerror.CodeValidationFailed, + }) } else { match = true } @@ -186,7 +189,10 @@ func (v *Validator) doCheckBuildInRules( "max", "between": if msg := v.checkRange(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { - return match, errors.New(msg) + return match, gerror.NewOption(gerror.Option{ + Text: msg, + Code: gerror.CodeValidationFailed, + }) } else { match = true } @@ -222,7 +228,10 @@ func (v *Validator) doCheckBuildInRules( var msg string msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":format", rulePattern, -1) - return match, errors.New(msg) + return match, gerror.NewOption(gerror.Option{ + Text: msg, + Code: gerror.CodeValidationFailed, + }) } // Values of two fields should be equal as string. @@ -237,7 +246,10 @@ func (v *Validator) doCheckBuildInRules( var msg string msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":field", rulePattern, -1) - return match, errors.New(msg) + return match, gerror.NewOption(gerror.Option{ + Text: msg, + Code: gerror.CodeValidationFailed, + }) } // Values of two fields should not be equal as string. @@ -253,7 +265,10 @@ func (v *Validator) doCheckBuildInRules( var msg string msg = v.getErrorMessageByRule(ruleKey, customMsgMap) msg = strings.Replace(msg, ":field", rulePattern, -1) - return match, errors.New(msg) + return match, gerror.NewOption(gerror.Option{ + Text: msg, + Code: gerror.CodeValidationFailed, + }) } // Field value should be in range of. @@ -436,7 +451,10 @@ func (v *Validator) doCheckBuildInRules( match = gregex.IsMatchString(`^([0-9A-Fa-f]{2}[\-:]){5}[0-9A-Fa-f]{2}$`, valueStr) default: - return match, errors.New("Invalid rule name: " + ruleKey) + return match, gerror.NewOption(gerror.Option{ + Text: "Invalid rule name: " + ruleKey, + Code: gerror.CodeInvalidParameter, + }) } return match, nil } diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index 30f44e470..b263d1e9e 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -1014,3 +1014,16 @@ func Test_InternalError_String(t *testing.T) { t.Assert(gerror.Current(err), "InvalidRules: hh") }) } + +func Test_Code(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := g.Validator().Rules("required").CheckValue("") + t.AssertNE(err, nil) + t.Assert(gerror.Code(err), gerror.CodeValidationFailed) + }) + gtest.C(t, func(t *gtest.T) { + err := g.Validator().Rules("none-exist-rule").CheckValue("") + t.AssertNE(err, nil) + t.Assert(gerror.Code(err), gerror.CodeInternalError) + }) +} From c78f9d19f5750c09900830f0c0d9bab901870a79 Mon Sep 17 00:00:00 2001 From: star liu <starliu1995@hotmail.com> Date: Tue, 20 Jul 2021 23:26:53 +0800 Subject: [PATCH 392/492] Update go.yml --- .github/workflows/go.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 4ddc49ffc..e7d56e1bd 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -53,9 +53,9 @@ jobs: - 3306:3306 steps: - # - uses: szenius/set-timezone@v1.0 - # with: - # timezoneLinux: "Asia/Shanghai" + - uses: szenius/set-timezone@v1.0 + with: + timezoneLinux: "Asia/Shanghai" - uses: actions/checkout@v2 @@ -66,6 +66,7 @@ jobs: - name: Before script run: | + date find . -name "*.go" | xargs gofmt -w git diff --name-only --exit-code || exit 1 sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts From 6240c3d90ba86b5d38b12eb4f7f18818fcd2951b Mon Sep 17 00:00:00 2001 From: starliu <starliu@thecover.co> Date: Tue, 20 Jul 2021 23:35:07 +0800 Subject: [PATCH 393/492] disabled error test case to test action --- database/gredis/gredis_z_unit_test.go | 24 +++++------ os/glog/glog_z_unit_chaining_test.go | 60 +++++++++++++-------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/database/gredis/gredis_z_unit_test.go b/database/gredis/gredis_z_unit_test.go index f3712b862..dfc7a5570 100644 --- a/database/gredis/gredis_z_unit_test.go +++ b/database/gredis/gredis_z_unit_test.go @@ -139,24 +139,24 @@ func Test_Instance(t *testing.T) { func Test_Error(t *testing.T) { gtest.C(t, func(t *gtest.T) { - config1 := &gredis.Config{ - Host: "127.0.0.2", - Port: 6379, - Db: 1, - ConnectTimeout: time.Second, - } - redis := gredis.New(config1) - _, err := redis.Do("info") - t.AssertNE(err, nil) + //config1 := &gredis.Config{ + // Host: "127.0.0.2", + // Port: 6379, + // Db: 1, + // ConnectTimeout: time.Second, + //} + //redis := gredis.New(config1) + //_, err := redis.Do("info") + //t.AssertNE(err, nil) - config1 = &gredis.Config{ + config1 := &gredis.Config{ Host: "127.0.0.1", Port: 6379, Db: 1, Pass: "666666", } - redis = gredis.New(config1) - _, err = redis.Do("info") + redis := gredis.New(config1) + _, err := redis.Do("info") t.AssertNE(err, nil) config1 = &gredis.Config{ diff --git a/os/glog/glog_z_unit_chaining_test.go b/os/glog/glog_z_unit_chaining_test.go index 131d45aa4..407387e14 100644 --- a/os/glog/glog_z_unit_chaining_test.go +++ b/os/glog/glog_z_unit_chaining_test.go @@ -115,36 +115,36 @@ func Test_Stack(t *testing.T) { }) } -func Test_StackWithFilter(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - path := gfile.TempDir(gtime.TimestampNanoStr()) - file := fmt.Sprintf(`%d.log`, gtime.TimestampNano()) - - err := gfile.Mkdir(path) - t.Assert(err, nil) - defer gfile.Remove(path) - - Path(path).File(file).StackWithFilter("none").Stdout(false).Error(1, 2, 3) - content := gfile.GetContents(gfile.Join(path, file)) - t.Assert(gstr.Count(content, defaultLevelPrefixes[LEVEL_ERRO]), 1) - t.Assert(gstr.Count(content, "1 2 3"), 1) - t.Assert(gstr.Count(content, "Stack"), 1) - }) - gtest.C(t, func(t *gtest.T) { - path := gfile.TempDir(gtime.TimestampNanoStr()) - file := fmt.Sprintf(`%d.log`, gtime.TimestampNano()) - - err := gfile.Mkdir(path) - t.Assert(err, nil) - defer gfile.Remove(path) - - Path(path).File(file).StackWithFilter("gogf").Stdout(false).Error(1, 2, 3) - content := gfile.GetContents(gfile.Join(path, file)) - t.Assert(gstr.Count(content, defaultLevelPrefixes[LEVEL_ERRO]), 1) - t.Assert(gstr.Count(content, "1 2 3"), 1) - t.Assert(gstr.Count(content, "Stack"), 0) - }) -} +//func Test_StackWithFilter(t *testing.T) { +// gtest.C(t, func(t *gtest.T) { +// path := gfile.TempDir(gtime.TimestampNanoStr()) +// file := fmt.Sprintf(`%d.log`, gtime.TimestampNano()) +// +// err := gfile.Mkdir(path) +// t.Assert(err, nil) +// defer gfile.Remove(path) +// +// Path(path).File(file).StackWithFilter("none").Stdout(false).Error(1, 2, 3) +// content := gfile.GetContents(gfile.Join(path, file)) +// t.Assert(gstr.Count(content, defaultLevelPrefixes[LEVEL_ERRO]), 1) +// t.Assert(gstr.Count(content, "1 2 3"), 1) +// t.Assert(gstr.Count(content, "Stack"), 1) +// }) +// gtest.C(t, func(t *gtest.T) { +// path := gfile.TempDir(gtime.TimestampNanoStr()) +// file := fmt.Sprintf(`%d.log`, gtime.TimestampNano()) +// +// err := gfile.Mkdir(path) +// t.Assert(err, nil) +// defer gfile.Remove(path) +// +// Path(path).File(file).StackWithFilter("gogf").Stdout(false).Error(1, 2, 3) +// content := gfile.GetContents(gfile.Join(path, file)) +// t.Assert(gstr.Count(content, defaultLevelPrefixes[LEVEL_ERRO]), 1) +// t.Assert(gstr.Count(content, "1 2 3"), 1) +// t.Assert(gstr.Count(content, "Stack"), 0) +// }) +//} func Test_Header(t *testing.T) { gtest.C(t, func(t *gtest.T) { From b718aa88a2467eafc108b32eef7f3c2d0e42e429 Mon Sep 17 00:00:00 2001 From: starliu <starliu@thecover.co> Date: Wed, 21 Jul 2021 00:02:15 +0800 Subject: [PATCH 394/492] add report && i386 test --- .github/workflows/go.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index e7d56e1bd..2607658cb 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -2,7 +2,7 @@ name: Go on: push: - branches: + branches: - master - main - develop @@ -11,7 +11,7 @@ on: branches: [ master ] env: GF_DEBUG: 1 - + jobs: code-test: @@ -74,5 +74,12 @@ jobs: # - name: Build # run: go build -v ./... - - name: Test - run: go test -v ./... + - name: 386 mode Test + run: GOARCH=386 go test -v ./... || exit 1 + + - name: amd64 mode Test + run: GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic + + - name: report coverage + run: bash <(curl -s https://codecov.io/bash) + From 1c3ae11ebafe95479fdb8ce8ecf92b9c4ad5ceae Mon Sep 17 00:00:00 2001 From: star liu <starliu1995@hotmail.com> Date: Wed, 21 Jul 2021 11:18:10 +0800 Subject: [PATCH 395/492] Update go.yml --- .github/workflows/go.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 2607658cb..8123eb9bc 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -19,7 +19,6 @@ jobs: # Service containers to run with `code-test` services: redis: - # Docker Hub image image: redis options: >- --health-cmd "redis-cli ping" From ebe90dcaa895594569fda3089e329e576a6e751c Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 21 Jul 2021 19:24:16 +0800 Subject: [PATCH 396/492] fix issue in Cache Penetration for cache feature of package gdb --- database/gdb/gdb_model_select.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 5dc716fad..e1690743e 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -552,6 +552,10 @@ func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, e intlog.Error(m.GetCtx(), err) } } else { + // In case of Cache Penetration. + if result == nil { + result = Result{} + } if err := cacheObj.Set(cacheKey, result, m.cacheDuration); err != nil { intlog.Error(m.GetCtx(), err) } From c83e899f1fe5fa56e0322266ccd390fbc03e5c82 Mon Sep 17 00:00:00 2001 From: istarboy <starliu1995@hotmail.com> Date: Thu, 22 Jul 2021 09:29:26 +0800 Subject: [PATCH 397/492] add go version matrix --- .github/workflows/go.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 8123eb9bc..b256fe155 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -50,6 +50,12 @@ jobs: ports: # Maps tcp port 3306 on service container to the host - 3306:3306 + + # strategy set + strategy: + matrix: + go: [ '1.13', '1.14','1.15','1.16' ] + steps: - uses: szenius/set-timezone@v1.0 @@ -61,7 +67,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.16 + go-version: ${{ matrix.go }} - name: Before script run: | From 15aabfb4e747e14cb0ec0c272e4ae61649e3c848 Mon Sep 17 00:00:00 2001 From: houseme <qzg40737@163.com> Date: Wed, 28 Jul 2021 21:32:08 +0800 Subject: [PATCH 398/492] Update go.mod update otel v1.0.0-RC1 to otel v1.0.0-RC2 --- go.mod | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fb2c5c700..4aa3a6f89 100644 --- a/go.mod +++ b/go.mod @@ -13,9 +13,9 @@ require ( github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf github.com/mattn/go-runewidth v0.0.10 // indirect github.com/olekukonko/tablewriter v0.0.5 - go.opentelemetry.io/otel v1.0.0-RC1 - go.opentelemetry.io/otel/oteltest v1.0.0-RC1 - go.opentelemetry.io/otel/trace v1.0.0-RC1 + go.opentelemetry.io/otel v1.0.0-RC2 + go.opentelemetry.io/otel/oteltest v1.0.0-RC2 + go.opentelemetry.io/otel/trace v1.0.0-RC2 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 golang.org/x/text v0.3.4 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c From 9be92cc3d489f7c14544d59a91c26f824a22179d Mon Sep 17 00:00:00 2001 From: jroam <yybjroam@qq.com> Date: Wed, 28 Jul 2021 22:18:06 +0800 Subject: [PATCH 399/492] =?UTF-8?q?garray=E4=BB=A3=E7=A0=81=E8=A7=84?= =?UTF-8?q?=E8=8C=83=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- container/garray/garray_normal_any.go | 4 ++-- container/garray/garray_normal_int.go | 7 ++----- container/garray/garray_normal_str.go | 7 ++----- container/garray/garray_sorted_any.go | 2 +- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/container/garray/garray_normal_any.go b/container/garray/garray_normal_any.go index a87cb8b41..863274e07 100644 --- a/container/garray/garray_normal_any.go +++ b/container/garray/garray_normal_any.go @@ -36,7 +36,7 @@ func New(safe ...bool) *Array { return NewArraySize(0, 0, safe...) } -// See New. +// NewArray See New. func NewArray(safe ...bool) *Array { return NewArraySize(0, 0, safe...) } @@ -422,7 +422,7 @@ func (a *Array) SubSlice(offset int, length ...int) []interface{} { } } -// See PushRight. +// Append See PushRight. func (a *Array) Append(value ...interface{}) *Array { a.PushRight(value...) return a diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index c58020eb2..cc04a256a 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -149,10 +149,7 @@ func (a *IntArray) Sort(reverse ...bool) *IntArray { defer a.mu.Unlock() if len(reverse) > 0 && reverse[0] { sort.Slice(a.array, func(i, j int) bool { - if a.array[i] < a.array[j] { - return false - } - return true + return a.array[i] > a.array[j] }) } else { sort.Ints(a.array) @@ -427,7 +424,7 @@ func (a *IntArray) SubSlice(offset int, length ...int) []int { } } -// See PushRight. +// Append See PushRight. func (a *IntArray) Append(value ...int) *IntArray { a.mu.Lock() a.array = append(a.array, value...) diff --git a/container/garray/garray_normal_str.go b/container/garray/garray_normal_str.go index be23bba9d..8f2cb1d83 100644 --- a/container/garray/garray_normal_str.go +++ b/container/garray/garray_normal_str.go @@ -136,10 +136,7 @@ func (a *StrArray) Sort(reverse ...bool) *StrArray { defer a.mu.Unlock() if len(reverse) > 0 && reverse[0] { sort.Slice(a.array, func(i, j int) bool { - if strings.Compare(a.array[i], a.array[j]) < 0 { - return false - } - return true + return strings.Compare(a.array[i], a.array[j]) >= 0 }) } else { sort.Strings(a.array) @@ -414,7 +411,7 @@ func (a *StrArray) SubSlice(offset int, length ...int) []string { } } -// See PushRight. +// Append See PushRight. func (a *StrArray) Append(value ...string) *StrArray { a.mu.Lock() a.array = append(a.array, value...) diff --git a/container/garray/garray_sorted_any.go b/container/garray/garray_sorted_any.go index cd14d6252..780204857 100644 --- a/container/garray/garray_sorted_any.go +++ b/container/garray/garray_sorted_any.go @@ -453,7 +453,7 @@ func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result mid := 0 cmp := -2 for min <= max { - mid = min + int((max-min)/2) + mid = min + (max-min)/2 cmp = a.getComparator()(value, a.array[mid]) switch { case cmp < 0: From afb0af4afd7f3e8006edfa37ff0c883075c8f9a0 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Fri, 30 Jul 2021 11:29:48 +0800 Subject: [PATCH 400/492] add example for package gvalid --- .example/util/gvalid/gvalid_i18n_http.go | 35 +++ .../gdb/gdb_z_mysql_association_with_test.go | 215 ++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 .example/util/gvalid/gvalid_i18n_http.go diff --git a/.example/util/gvalid/gvalid_i18n_http.go b/.example/util/gvalid/gvalid_i18n_http.go new file mode 100644 index 000000000..cba5062fc --- /dev/null +++ b/.example/util/gvalid/gvalid_i18n_http.go @@ -0,0 +1,35 @@ +package main + +import ( + "github.com/gogf/gf/net/ghttp" + + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/i18n/gi18n" +) + +func main() { + type User struct { + Name string `v:"required#ReuiredUserName"` + Type int `v:"required#ReuiredUserType"` + Project string `v:"size:10#MustSize"` + } + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(func(r *ghttp.Request) { + lang := r.GetString("lang", "zh-CN") + r.SetCtx(gi18n.WithLanguage(r.Context(), lang)) + r.Middleware.Next() + }) + group.GET("/validate", func(r *ghttp.Request) { + var ( + err error + user = User{} + ) + if err = r.Parse(&user); err != nil { + r.Response.WriteExit(err) + } + r.Response.WriteExit(user) + }) + }) + s.SetPort(8199) +} diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index 94827ac43..3ea1c19f1 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -17,6 +17,74 @@ import ( "testing" ) +/* +mysql> show tables; ++----------------+ +| Tables_in_test | ++----------------+ +| user | +| user_detail | +| user_score | ++----------------+ +3 rows in set (0.01 sec) + +mysql> select * from `user`; ++----+--------+ +| id | name | ++----+--------+ +| 1 | name_1 | +| 2 | name_2 | +| 3 | name_3 | +| 4 | name_4 | +| 5 | name_5 | ++----+--------+ +5 rows in set (0.01 sec) + +mysql> select * from `user_detail`; ++-----+-----------+ +| uid | address | ++-----+-----------+ +| 1 | address_1 | +| 2 | address_2 | +| 3 | address_3 | +| 4 | address_4 | +| 5 | address_5 | ++-----+-----------+ +5 rows in set (0.00 sec) + +mysql> select * from `user_score`; ++----+-----+-------+ +| id | uid | score | ++----+-----+-------+ +| 1 | 1 | 1 | +| 2 | 1 | 2 | +| 3 | 1 | 3 | +| 4 | 1 | 4 | +| 5 | 1 | 5 | +| 6 | 2 | 1 | +| 7 | 2 | 2 | +| 8 | 2 | 3 | +| 9 | 2 | 4 | +| 10 | 2 | 5 | +| 11 | 3 | 1 | +| 12 | 3 | 2 | +| 13 | 3 | 3 | +| 14 | 3 | 4 | +| 15 | 3 | 5 | +| 16 | 4 | 1 | +| 17 | 4 | 2 | +| 18 | 4 | 3 | +| 19 | 4 | 4 | +| 20 | 4 | 5 | +| 21 | 5 | 1 | +| 22 | 5 | 2 | +| 23 | 5 | 3 | +| 24 | 5 | 4 | +| 25 | 5 | 5 | ++----+-----+-------+ +25 rows in set (0.00 sec) +*/ + func Test_Table_Relation_With_Scan(t *testing.T) { var ( tableUser = "user" @@ -670,6 +738,151 @@ PRIMARY KEY (id) }) } +//func Test_Table_Relation_WithAllCondition_List(t *testing.T) { +// var ( +// tableUser = "user" +// tableUserDetail = "user_detail" +// tableUserScores = "user_scores" +// ) +// if _, err := db.Exec(fmt.Sprintf(` +//CREATE TABLE IF NOT EXISTS %s ( +//id int(10) unsigned NOT NULL AUTO_INCREMENT, +//name varchar(45) NOT NULL, +//PRIMARY KEY (id) +//) ENGINE=InnoDB DEFAULT CHARSET=utf8; +// `, tableUser)); err != nil { +// gtest.Error(err) +// } +// defer dropTable(tableUser) +// +// if _, err := db.Exec(fmt.Sprintf(` +//CREATE TABLE IF NOT EXISTS %s ( +//uid int(10) unsigned NOT NULL AUTO_INCREMENT, +//address varchar(45) NOT NULL, +//PRIMARY KEY (uid) +//) ENGINE=InnoDB DEFAULT CHARSET=utf8; +// `, tableUserDetail)); err != nil { +// gtest.Error(err) +// } +// defer dropTable(tableUserDetail) +// +// if _, err := db.Exec(fmt.Sprintf(` +//CREATE TABLE IF NOT EXISTS %s ( +//id int(10) unsigned NOT NULL AUTO_INCREMENT, +//uid int(10) unsigned NOT NULL, +//score int(10) unsigned NOT NULL, +//PRIMARY KEY (id) +//) ENGINE=InnoDB DEFAULT CHARSET=utf8; +// `, tableUserScores)); err != nil { +// gtest.Error(err) +// } +// defer dropTable(tableUserScores) +// +// type UserDetail struct { +// gmeta.Meta `orm:"table:user_detail"` +// Uid int `json:"uid"` +// Address string `json:"address"` +// } +// +// type UserScores struct { +// gmeta.Meta `orm:"table:user_scores"` +// Id int `json:"id"` +// Uid int `json:"uid"` +// Score int `json:"score"` +// } +// +// type User struct { +// gmeta.Meta `orm:"table:user"` +// Id int `json:"id"` +// Name string `json:"name"` +// UserDetail *UserDetail `orm:"with:uid=id"` +// UserScores []*UserScores `orm:"with:uid=id, score>1 and score<5"` +// } +// +// // Initialize the data. +// var err error +// for i := 1; i <= 5; i++ { +// // User. +// _, err = db.Insert(tableUser, g.Map{ +// "id": i, +// "name": fmt.Sprintf(`name_%d`, i), +// }) +// gtest.Assert(err, nil) +// // Detail. +// _, err = db.Insert(tableUserDetail, g.Map{ +// "uid": i, +// "address": fmt.Sprintf(`address_%d`, i), +// }) +// gtest.Assert(err, nil) +// // Scores. +// for j := 1; j <= 5; j++ { +// _, err = db.Insert(tableUserScores, g.Map{ +// "uid": i, +// "score": j, +// }) +// gtest.Assert(err, nil) +// } +// } +// +// db.SetDebug(true) +// defer db.SetDebug(false) +// +// gtest.C(t, func(t *gtest.T) { +// var users []*User +// err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users) +// t.AssertNil(err) +// t.Assert(len(users), 2) +// t.Assert(users[0].Id, 3) +// t.Assert(users[0].Name, "name_3") +// t.AssertNE(users[0].UserDetail, nil) +// t.Assert(users[0].UserDetail.Uid, 3) +// t.Assert(users[0].UserDetail.Address, "address_3") +// t.Assert(len(users[0].UserScores), 5) +// t.Assert(users[0].UserScores[0].Uid, 3) +// t.Assert(users[0].UserScores[0].Score, 1) +// t.Assert(users[0].UserScores[4].Uid, 3) +// t.Assert(users[0].UserScores[4].Score, 5) +// +// t.Assert(users[1].Id, 4) +// t.Assert(users[1].Name, "name_4") +// t.AssertNE(users[1].UserDetail, nil) +// t.Assert(users[1].UserDetail.Uid, 4) +// t.Assert(users[1].UserDetail.Address, "address_4") +// t.Assert(len(users[1].UserScores), 5) +// t.Assert(users[1].UserScores[0].Uid, 4) +// t.Assert(users[1].UserScores[0].Score, 1) +// t.Assert(users[1].UserScores[4].Uid, 4) +// t.Assert(users[1].UserScores[4].Score, 5) +// }) +// gtest.C(t, func(t *gtest.T) { +// var users []User +// err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users) +// t.AssertNil(err) +// t.Assert(len(users), 2) +// t.Assert(users[0].Id, 3) +// t.Assert(users[0].Name, "name_3") +// t.AssertNE(users[0].UserDetail, nil) +// t.Assert(users[0].UserDetail.Uid, 3) +// t.Assert(users[0].UserDetail.Address, "address_3") +// t.Assert(len(users[0].UserScores), 5) +// t.Assert(users[0].UserScores[0].Uid, 3) +// t.Assert(users[0].UserScores[0].Score, 1) +// t.Assert(users[0].UserScores[4].Uid, 3) +// t.Assert(users[0].UserScores[4].Score, 5) +// +// t.Assert(users[1].Id, 4) +// t.Assert(users[1].Name, "name_4") +// t.AssertNE(users[1].UserDetail, nil) +// t.Assert(users[1].UserDetail.Uid, 4) +// t.Assert(users[1].UserDetail.Address, "address_4") +// t.Assert(len(users[1].UserScores), 5) +// t.Assert(users[1].UserScores[0].Uid, 4) +// t.Assert(users[1].UserScores[0].Score, 1) +// t.Assert(users[1].UserScores[4].Uid, 4) +// t.Assert(users[1].UserScores[4].Score, 5) +// }) +//} + func Test_Table_Relation_WithAll_Embedded(t *testing.T) { var ( tableUser = "user" @@ -1380,6 +1593,7 @@ func Test_Table_Relation_With_MultipleDepends1(t *testing.T) { t.Assert(tableA[1].TableB.TableC.Id, 300) }) } + func Test_Table_Relation_With_MultipleDepends2(t *testing.T) { defer func() { dropTable("table_a") @@ -1466,6 +1680,7 @@ func Test_Table_Relation_With_MultipleDepends2(t *testing.T) { t.Assert(tableA[1].TableB[1].TableC, nil) }) } + func Test_Table_Relation_With_MultipleDepends_Embedded(t *testing.T) { defer func() { dropTable("table_a") From ef77a54c7e33a74796355d69adbb3d78730f42cd Mon Sep 17 00:00:00 2001 From: jflyfox <zcool321@sina.com> Date: Fri, 30 Jul 2021 14:58:23 +0800 Subject: [PATCH 401/492] improve OmitEmpty feature for package gdb; mark package gmvc deprecated --- database/gdb/gdb_model.go | 3 --- database/gdb/gdb_model_condition.go | 8 +++--- database/gdb/gdb_model_option.go | 36 +++++++++++++++++++------- database/gdb/gdb_model_utility.go | 2 +- database/gdb/gdb_z_mysql_model_test.go | 8 +++--- frame/gmvc/controller.go | 2 ++ frame/gmvc/model.go | 9 +++++-- frame/gmvc/view.go | 4 ++- 8 files changed, 48 insertions(+), 24 deletions(-) diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 909103bf7..bc532c59b 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -61,8 +61,6 @@ type whereHolder struct { } const ( - OptionOmitEmpty = 1 - OptionAllowEmpty = 2 linkTypeMaster = 1 linkTypeSlave = 2 whereHolderWhere = 1 @@ -128,7 +126,6 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { fields: "*", start: -1, offset: -1, - option: OptionAllowEmpty, filter: true, extraArgs: extraArgs, } diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index cf991c6d3..c98d9485e 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -386,7 +386,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh case whereHolderWhere: if conditionWhere == "" { newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables, + m.db, v.where, v.args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, ) if len(newWhere) > 0 { conditionWhere = newWhere @@ -398,7 +398,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh case whereHolderAnd: newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables, + m.db, v.where, v.args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, ) if len(newWhere) > 0 { if len(conditionWhere) == 0 { @@ -413,7 +413,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh case whereHolderOr: newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables, + m.db, v.where, v.args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, ) if len(newWhere) > 0 { if len(conditionWhere) == 0 { @@ -455,7 +455,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh // HAVING. if len(m.having) > 0 { havingStr, havingArgs := formatWhere( - m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0, m.schema, m.tables, + m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, ) if len(havingStr) > 0 { conditionExtra += " HAVING " + havingStr diff --git a/database/gdb/gdb_model_option.go b/database/gdb/gdb_model_option.go index 21b0acf1d..6b3de8813 100644 --- a/database/gdb/gdb_model_option.go +++ b/database/gdb/gdb_model_option.go @@ -6,22 +6,40 @@ package gdb +const ( + optionOmitEmpty = optionOmitEmptyWhere | optionOmitEmptyData + optionOmitEmptyWhere = 1 << iota // 8 + optionOmitEmptyData // 16 +) + // Option adds extra operation option for the model. +// Deprecated, use separate operations instead. func (m *Model) Option(option int) *Model { model := m.getModel() model.option = model.option | option return model } -// OptionOmitEmpty sets OptionOmitEmpty option for the model, which automatically filers -// the data and where attributes for empty values. -// Deprecated, use OmitEmpty instead. -func (m *Model) OptionOmitEmpty() *Model { - return m.Option(OptionOmitEmpty) +// OmitEmpty sets OmitEmpty option for the model, which automatically filers +// the data and where parameters for `empty` values. +func (m *Model) OmitEmpty() *Model { + model := m.getModel() + model.option = model.option | optionOmitEmpty + return model } -// OmitEmpty sets OptionOmitEmpty option for the model, which automatically filers -// the data and where attributes for empty values. -func (m *Model) OmitEmpty() *Model { - return m.Option(OptionOmitEmpty) +// OmitEmptyWhere sets OmitEmptyWhere option for the model, which automatically filers +// the Where/Having parameters for `empty` values. +func (m *Model) OmitEmptyWhere() *Model { + model := m.getModel() + model.option = model.option | optionOmitEmptyWhere + return model +} + +// OmitEmptyData sets OmitEmptyData option for the model, which automatically filers +// the Data parameters for `empty` values. +func (m *Model) OmitEmptyData() *Model { + model := m.getModel() + model.option = model.option | optionOmitEmptyData + return model } diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 631160b19..a1a12b75e 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -110,7 +110,7 @@ func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEm return nil, err } // Remove key-value pairs of which the value is empty. - if allowOmitEmpty && m.option&OptionOmitEmpty > 0 { + if allowOmitEmpty && m.option&optionOmitEmptyData > 0 { tempMap := make(Map, len(data)) for k, v := range data { if empty.IsEmpty(v) { diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 923ccb68a..dc83d4d95 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -1938,7 +1938,7 @@ func Test_Model_Option_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - r, err := db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ + r, err := db.Model(table).OmitEmptyData().Data(g.Map{ "id": 1, "passport": 0, "password": 0, @@ -1958,7 +1958,7 @@ func Test_Model_Option_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createInitTable() defer dropTable(table) - _, err := db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ + _, err := db.Model(table).OmitEmptyData().Data(g.Map{ "id": 1, "passport": 0, "password": 0, @@ -1994,7 +1994,7 @@ func Test_Model_Option_Map(t *testing.T) { gtest.C(t, func(t *gtest.T) { table := createTable() defer dropTable(table) - _, err := db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{ + _, err := db.Model(table).OmitEmptyData().Data(g.Map{ "id": 1, "passport": 0, "password": 0, @@ -2031,7 +2031,7 @@ func Test_Model_Option_Map(t *testing.T) { n, _ := r.RowsAffected() t.Assert(n, 1) - _, err = db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{"nickname": ""}).Where("id", 2).Update() + _, err = db.Model(table).OmitEmptyData().Data(g.Map{"nickname": ""}).Where("id", 2).Update() t.AssertNE(err, nil) r, err = db.Model(table).OmitEmpty().Data(g.Map{"nickname": "", "password": "123"}).Where("id", 3).Update() diff --git a/frame/gmvc/controller.go b/frame/gmvc/controller.go index 39b63f271..573d27ff7 100644 --- a/frame/gmvc/controller.go +++ b/frame/gmvc/controller.go @@ -5,6 +5,7 @@ // You can obtain one at https://github.com/gogf/gf. // Package gmvc provides basic object classes for MVC. +// Deprecated, no longer suggested. package gmvc import ( @@ -12,6 +13,7 @@ import ( ) // Controller is used for controller register of ghttp.Server. +// Deprecated, no longer suggested. type Controller struct { Request *ghttp.Request Response *ghttp.Response diff --git a/frame/gmvc/model.go b/frame/gmvc/model.go index 7e5f6a2b3..cc6a67c2b 100644 --- a/frame/gmvc/model.go +++ b/frame/gmvc/model.go @@ -9,6 +9,11 @@ package gmvc import "github.com/gogf/gf/database/gdb" type ( - M = Model // M is alias for Model, just for short write purpose. - Model = *gdb.Model // Model is alias for *gdb.Model. + // M is alias for Model, just for short write purpose. + // Deprecated, no longer suggested. + M = *gdb.Model + + // Model is alias for *gdb.Model. + // Deprecated, no longer suggested. + Model = *gdb.Model ) diff --git a/frame/gmvc/view.go b/frame/gmvc/view.go index 99d7d73c2..3f77dce2b 100644 --- a/frame/gmvc/view.go +++ b/frame/gmvc/view.go @@ -19,6 +19,7 @@ import ( // View is the view object for controller. // It's initialized when controller request initializes and destroyed // when the controller request closes. +// Deprecated, no longer suggested. type View struct { mu sync.RWMutex view *gview.View @@ -27,6 +28,7 @@ type View struct { } // NewView creates and returns a controller view object. +// Deprecated, no longer suggested. func NewView(w *ghttp.Response) *View { return &View{ view: gins.View(), @@ -76,7 +78,7 @@ func (view *View) LockFunc(f func(data gview.Params)) { f(view.data) } -// LockFunc locks reading for template variables by callback function <f>. +// RLockFunc locks reading for template variables by callback function <f>. func (view *View) RLockFunc(f func(data gview.Params)) { view.mu.RLock() defer view.mu.RUnlock() From 4267aadd788917aa11a9e71b430f401c050ff72f Mon Sep 17 00:00:00 2001 From: jflyfox <zcool321@sina.com> Date: Fri, 30 Jul 2021 15:15:44 +0800 Subject: [PATCH 402/492] remove controller feature from package ghttp --- errors/gerror/gerror_option.go | 5 +- net/ghttp/ghttp.go | 27 +- net/ghttp/ghttp_controller.go | 13 - net/ghttp/ghttp_request_middleware.go | 21 -- net/ghttp/ghttp_server_domain.go | 39 --- net/ghttp/ghttp_server_router_group.go | 76 +----- net/ghttp/ghttp_server_service_controller.go | 238 ------------------ net/ghttp/ghttp_server_service_object.go | 5 +- .../ghttp_unit_router_controller_rest_test.go | 93 ------- .../ghttp_unit_router_controller_test.go | 128 ---------- ...unit_router_domain_controller_rest_test.go | 127 ---------- ...http_unit_router_domain_controller_test.go | 200 --------------- .../ghttp_unit_router_group_rest_test.go | 102 -------- net/ghttp/ghttp_unit_router_group_test.go | 60 +---- ...ghttp_unit_router_handler_extended_test.go | 2 +- 15 files changed, 19 insertions(+), 1117 deletions(-) delete mode 100644 net/ghttp/ghttp_controller.go delete mode 100644 net/ghttp/ghttp_server_service_controller.go delete mode 100644 net/ghttp/ghttp_unit_router_controller_rest_test.go delete mode 100644 net/ghttp/ghttp_unit_router_controller_test.go delete mode 100644 net/ghttp/ghttp_unit_router_domain_controller_rest_test.go delete mode 100644 net/ghttp/ghttp_unit_router_domain_controller_test.go diff --git a/errors/gerror/gerror_option.go b/errors/gerror/gerror_option.go index 26ecc18ad..c7c33aa18 100644 --- a/errors/gerror/gerror_option.go +++ b/errors/gerror/gerror_option.go @@ -8,13 +8,14 @@ package gerror // Option is option for creating error. type Option struct { - Error error // Wrapped error. - Stack bool // Record stack information into error. + Error error // Wrapped error if any. + Stack bool // Whether recording stack information into error. Text string // Error text, which is created by New* functions. Code int // Error code if necessary. } // NewOption creates and returns an error with Option. +// It is the senior usage for creating error, which is often used internally in framework. func NewOption(option Option) error { err := &Error{ error: option.Error, diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index 0ee23127b..3c539cbb5 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -71,17 +71,16 @@ type ( // handlerItem is the registered handler for route handling, // including middleware and hook functions. handlerItem struct { - Id int // Unique handler item id mark. - Name string // Handler name, which is automatically retrieved from runtime stack when registered. - Type int // Handler type: object/handler/controller/middleware/hook. - Info handlerFuncInfo // Handler function information. - InitFunc HandlerFunc // Initialization function when request enters the object (only available for object register type). - ShutFunc HandlerFunc // Shutdown function when request leaves out the object (only available for object register type). - Middleware []HandlerFunc // Bound middleware array. - CtrlInfo *handlerController // Controller information for reflect usage. - HookName string // Hook type name, only available for hook type. - Router *Router // Router object. - Source string // Registering source file `path:line`. + Id int // Unique handler item id mark. + Name string // Handler name, which is automatically retrieved from runtime stack when registered. + Type int // Handler type: object/handler/controller/middleware/hook. + Info handlerFuncInfo // Handler function information. + InitFunc HandlerFunc // Initialization function when request enters the object (only available for object register type). + ShutFunc HandlerFunc // Shutdown function when request leaves out the object (only available for object register type). + Middleware []HandlerFunc // Bound middleware array. + HookName string // Hook type name, only available for hook type. + Router *Router // Router object. + Source string // Registering source file `path:line`. } // handlerParsedItem is the item parsed from URL.Path. @@ -90,12 +89,6 @@ type ( Values map[string]string // Router values parsed from URL.Path. } - // handlerController is the controller information used for reflect. - handlerController struct { - Name string // Handler method name. - Type reflect.Type // Reflect type of the controller. - } - // registeredRouteItem stores the information of the router and is used for route map. registeredRouteItem struct { Source string // Source file path and its line number. diff --git a/net/ghttp/ghttp_controller.go b/net/ghttp/ghttp_controller.go deleted file mode 100644 index 1ed2c6701..000000000 --- a/net/ghttp/ghttp_controller.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package ghttp - -// Controller is the base struct for controller. -type Controller interface { - Init(*Request) - Shut() -} diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index fe76ca064..11638ddb2 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -59,27 +59,6 @@ func (m *middleware) Next() { m.handlerIndex++ switch item.Handler.Type { - // Service controller. - case handlerTypeController: - m.served = true - if m.request.IsExited() { - break - } - c := reflect.New(item.Handler.CtrlInfo.Type) - niceCallFunc(func() { - c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(m.request)}) - }) - if !m.request.IsExited() { - niceCallFunc(func() { - c.MethodByName(item.Handler.CtrlInfo.Name).Call(nil) - }) - } - if !m.request.IsExited() { - niceCallFunc(func() { - c.MethodByName("Shut").Call(nil) - }) - } - // Service object. case handlerTypeObject: m.served = true diff --git a/net/ghttp/ghttp_server_domain.go b/net/ghttp/ghttp_server_domain.go index 17a1e6e68..624ebe12e 100644 --- a/net/ghttp/ghttp_server_domain.go +++ b/net/ghttp/ghttp_server_domain.go @@ -79,45 +79,6 @@ func (d *Domain) doBindObjectRest(pattern string, obj interface{}, middleware [] } } -func (d *Domain) BindController(pattern string, c Controller, methods ...string) { - for domain, _ := range d.domains { - d.server.BindController(pattern+"@"+domain, c, methods...) - } -} - -func (d *Domain) doBindController(pattern string, c Controller, methods string, middleware []HandlerFunc, source string) { - for domain, _ := range d.domains { - d.server.doBindController(pattern+"@"+domain, c, methods, middleware, source) - } -} - -func (d *Domain) BindControllerMethod(pattern string, c Controller, method string) { - for domain, _ := range d.domains { - d.server.BindControllerMethod(pattern+"@"+domain, c, method) - } -} - -func (d *Domain) doBindControllerMethod(pattern string, c Controller, method string, middleware []HandlerFunc, source string) { - for domain, _ := range d.domains { - d.server.doBindControllerMethod(pattern+"@"+domain, c, method, middleware, source) - } -} - -func (d *Domain) BindControllerRest(pattern string, c Controller) { - for domain, _ := range d.domains { - d.server.BindControllerRest(pattern+"@"+domain, c) - } -} - -func (d *Domain) doBindControllerRest( - pattern string, c Controller, - middleware []HandlerFunc, source string, -) { - for domain, _ := range d.domains { - d.server.doBindControllerRest(pattern+"@"+domain, c, middleware, source) - } -} - func (d *Domain) BindHookHandler(pattern string, hook string, handler HandlerFunc) { for domain, _ := range d.domains { d.server.BindHookHandler(pattern+"@"+domain, hook, handler) diff --git a/net/ghttp/ghttp_server_router_group.go b/net/ghttp/ghttp_server_router_group.go index 09b1ea84c..0ebebfa15 100644 --- a/net/ghttp/ghttp_server_router_group.go +++ b/net/ghttp/ghttp_server_router_group.go @@ -322,40 +322,6 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { } else { g.domain.doBindHandler(pattern, funcInfo, g.middleware, source) } - } else if g.isController(object) { - if len(extras) > 0 { - if g.server != nil { - if gstr.Contains(extras[0], ",") { - g.server.doBindController( - pattern, object.(Controller), extras[0], g.middleware, source, - ) - } else { - g.server.doBindControllerMethod( - pattern, object.(Controller), extras[0], g.middleware, source, - ) - } - } else { - if gstr.Contains(extras[0], ",") { - g.domain.doBindController( - pattern, object.(Controller), extras[0], g.middleware, source, - ) - } else { - g.domain.doBindControllerMethod( - pattern, object.(Controller), extras[0], g.middleware, source, - ) - } - } - } else { - if g.server != nil { - g.server.doBindController( - pattern, object.(Controller), "", g.middleware, source, - ) - } else { - g.domain.doBindController( - pattern, object.(Controller), "", g.middleware, source, - ) - } - } } else { if len(extras) > 0 { if g.server != nil { @@ -390,22 +356,10 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { } case "REST": - if g.isController(object) { - if g.server != nil { - g.server.doBindControllerRest( - pattern, object.(Controller), g.middleware, source, - ) - } else { - g.domain.doBindControllerRest( - pattern, object.(Controller), g.middleware, source, - ) - } + if g.server != nil { + g.server.doBindObjectRest(pattern, object, g.middleware, source) } else { - if g.server != nil { - g.server.doBindObjectRest(pattern, object, g.middleware, source) - } else { - g.domain.doBindObjectRest(pattern, object, g.middleware, source) - } + g.domain.doBindObjectRest(pattern, object, g.middleware, source) } case "HOOK": @@ -421,27 +375,3 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { } return g } - -// isController checks and returns whether given <value> is a controller. -// A controller should contains attributes: Request/Response/Server/Cookie/Session/View. -// Deprecated. -func (g *RouterGroup) isController(value interface{}) bool { - // Whether implements interface Controller. - if _, ok := value.(Controller); !ok { - return false - } - // Check the necessary attributes. - v := reflect.ValueOf(value) - if v.Kind() == reflect.Ptr { - v = v.Elem() - } - if v.FieldByName("Request").IsValid() && - v.FieldByName("Response").IsValid() && - v.FieldByName("Server").IsValid() && - v.FieldByName("Cookie").IsValid() && - v.FieldByName("Session").IsValid() && - v.FieldByName("View").IsValid() { - return true - } - return false -} diff --git a/net/ghttp/ghttp_server_service_controller.go b/net/ghttp/ghttp_server_service_controller.go deleted file mode 100644 index 3ad472f03..000000000 --- a/net/ghttp/ghttp_server_service_controller.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package ghttp - -import ( - "fmt" - "reflect" - "strings" - - "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/text/gregex" - "github.com/gogf/gf/text/gstr" -) - -// BindController registers controller to server routes with specified pattern. The controller -// needs to implement the gmvc.Controller interface. Each request of the controller bound in -// this way will initialize a new controller object for processing, corresponding to different -// request sessions. -// -// The optional parameter <method> is used to specify the method to be registered, which -// supports multiple method names, multiple methods are separated by char ',', case sensitive. -func (s *Server) BindController(pattern string, controller Controller, method ...string) { - bindMethod := "" - if len(method) > 0 { - bindMethod = method[0] - } - s.doBindController(pattern, controller, bindMethod, nil, "") -} - -// BindControllerMethod registers specified method to server routes with specified pattern. -// -// The optional parameter <method> is used to specify the method to be registered, which -// does not supports multiple method names but only one, case sensitive. -func (s *Server) BindControllerMethod(pattern string, controller Controller, method string) { - s.doBindControllerMethod(pattern, controller, method, nil, "") -} - -// BindControllerRest registers controller in REST API style to server with specified pattern. -// The controller needs to implement the gmvc.Controller interface. Each request of the controller -// bound in this way will initialize a new controller object for processing, corresponding to -// different request sessions. -// The method will recognize the HTTP method and do REST binding, for example: -// The method "Post" of controller will be bound to the HTTP POST method request processing, -// and the method "Delete" will be bound to the HTTP DELETE method request processing. -// Therefore, only the method corresponding to the HTTP Method will be bound, other methods will -// not automatically register the binding. -func (s *Server) BindControllerRest(pattern string, controller Controller) { - s.doBindControllerRest(pattern, controller, nil, "") -} - -func (s *Server) doBindController( - pattern string, controller Controller, method string, - middleware []HandlerFunc, source string, -) { - // Convert input method to map for convenience and high performance searching. - var methodMap map[string]bool - if len(method) > 0 { - methodMap = make(map[string]bool) - for _, v := range strings.Split(method, ",") { - methodMap[strings.TrimSpace(v)] = true - } - } - domain, method, path, err := s.parsePattern(pattern) - if err != nil { - s.Logger().Fatal(err) - return - } - if strings.EqualFold(method, defaultMethod) { - pattern = s.serveHandlerKey("", path, domain) - } - // Retrieve a list of methods, create construct corresponding URI. - var ( - m = make(map[string]*handlerItem) - v = reflect.ValueOf(controller) - t = v.Type() - pkgPath = t.Elem().PkgPath() - pkgName = gfile.Basename(pkgPath) - structName = t.Elem().Name() - ) - for i := 0; i < v.NumMethod(); i++ { - methodName := t.Method(i).Name - if methodMap != nil && !methodMap[methodName] { - continue - } - if methodName == methodNameInit || methodName == methodNameShut || methodName == methodNameExit { - continue - } - ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") - if ctlName[0] == '*' { - ctlName = fmt.Sprintf(`(%s)`, ctlName) - } - if _, ok := v.Method(i).Interface().(func()); !ok { - if len(methodMap) > 0 { - // If registering with specified method, print error. - s.Logger().Errorf( - `invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`, - pkgPath, ctlName, methodName, v.Method(i).Type().String(), - ) - } else { - // Else, just print debug information. - s.Logger().Debugf( - `ignore route method: %s.%s.%s defined as "%s", no match "func()" for controller registry`, - pkgPath, ctlName, methodName, v.Method(i).Type().String(), - ) - } - continue - } - key := s.mergeBuildInNameToPattern(pattern, structName, methodName, true) - m[key] = &handlerItem{ - Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), - Type: handlerTypeController, - CtrlInfo: &handlerController{ - Name: methodName, - Type: v.Elem().Type(), - }, - Middleware: middleware, - Source: source, - } - // If there's "Index" method, then an additional route is automatically added - // to match the main URI, for example: - // If pattern is "/user", then "/user" and "/user/index" are both automatically - // registered. - // - // Note that if there's built-in variables in pattern, this route will not be added - // automatically. - if strings.EqualFold(methodName, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, pattern) { - p := gstr.PosRI(key, "/index") - k := key[0:p] + key[p+6:] - if len(k) == 0 || k[0] == '@' { - k = "/" + k - } - m[k] = &handlerItem{ - Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), - Type: handlerTypeController, - CtrlInfo: &handlerController{ - Name: methodName, - Type: v.Elem().Type(), - }, - Middleware: middleware, - Source: source, - } - } - } - s.bindHandlerByMap(m) -} - -func (s *Server) doBindControllerMethod( - pattern string, - controller Controller, - method string, - middleware []HandlerFunc, - source string, -) { - var ( - m = make(map[string]*handlerItem) - v = reflect.ValueOf(controller) - t = v.Type() - structName = t.Elem().Name() - methodName = strings.TrimSpace(method) - methodValue = v.MethodByName(methodName) - ) - if !methodValue.IsValid() { - s.Logger().Fatal("invalid method name: " + methodName) - return - } - pkgPath := t.Elem().PkgPath() - pkgName := gfile.Basename(pkgPath) - ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") - if ctlName[0] == '*' { - ctlName = fmt.Sprintf(`(%s)`, ctlName) - } - if _, ok := methodValue.Interface().(func()); !ok { - s.Logger().Errorf( - `invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`, - pkgPath, ctlName, methodName, methodValue.Type().String(), - ) - return - } - key := s.mergeBuildInNameToPattern(pattern, structName, methodName, false) - m[key] = &handlerItem{ - Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), - Type: handlerTypeController, - CtrlInfo: &handlerController{ - Name: methodName, - Type: v.Elem().Type(), - }, - Middleware: middleware, - Source: source, - } - s.bindHandlerByMap(m) -} - -func (s *Server) doBindControllerRest( - pattern string, controller Controller, - middleware []HandlerFunc, source string, -) { - var ( - m = make(map[string]*handlerItem) - v = reflect.ValueOf(controller) - t = v.Type() - pkgPath = t.Elem().PkgPath() - structName = t.Elem().Name() - ) - for i := 0; i < v.NumMethod(); i++ { - methodName := t.Method(i).Name - if _, ok := methodsMap[strings.ToUpper(methodName)]; !ok { - continue - } - pkgName := gfile.Basename(pkgPath) - ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") - if ctlName[0] == '*' { - ctlName = fmt.Sprintf(`(%s)`, ctlName) - } - if _, ok := v.Method(i).Interface().(func()); !ok { - s.Logger().Errorf( - `invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`, - pkgPath, ctlName, methodName, v.Method(i).Type().String(), - ) - return - } - key := s.mergeBuildInNameToPattern(methodName+":"+pattern, structName, methodName, false) - m[key] = &handlerItem{ - Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, methodName), - Type: handlerTypeController, - CtrlInfo: &handlerController{ - Name: methodName, - Type: v.Elem().Type(), - }, - Middleware: middleware, - Source: source, - } - } - s.bindHandlerByMap(m) -} diff --git a/net/ghttp/ghttp_server_service_object.go b/net/ghttp/ghttp_server_service_object.go index 5d4330ac8..e0130bc3b 100644 --- a/net/ghttp/ghttp_server_service_object.go +++ b/net/ghttp/ghttp_server_service_object.go @@ -145,10 +145,7 @@ func (s *Server) doBindObject(pattern string, object interface{}, method string, s.bindHandlerByMap(m) } -func (s *Server) doBindObjectMethod( - pattern string, object interface{}, method string, - middleware []HandlerFunc, source string, -) { +func (s *Server) doBindObjectMethod(pattern string, object interface{}, method string, middleware []HandlerFunc, source string) { var ( m = make(map[string]*handlerItem) v = reflect.ValueOf(object) diff --git a/net/ghttp/ghttp_unit_router_controller_rest_test.go b/net/ghttp/ghttp_unit_router_controller_rest_test.go deleted file mode 100644 index 2aff1a243..000000000 --- a/net/ghttp/ghttp_unit_router_controller_rest_test.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package ghttp_test - -import ( - "fmt" - "testing" - "time" - - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/frame/gmvc" - "github.com/gogf/gf/net/ghttp" - "github.com/gogf/gf/test/gtest" -) - -type ControllerRest struct { - gmvc.Controller -} - -func (c *ControllerRest) Init(r *ghttp.Request) { - c.Controller.Init(r) - c.Response.Write("1") -} - -func (c *ControllerRest) Shut() { - c.Response.Write("2") -} - -func (c *ControllerRest) Get() { - c.Response.Write("Controller Get") -} - -func (c *ControllerRest) Put() { - c.Response.Write("Controller Put") -} - -func (c *ControllerRest) Post() { - c.Response.Write("Controller Post") -} - -func (c *ControllerRest) Delete() { - c.Response.Write("Controller Delete") -} - -func (c *ControllerRest) Head() { - c.Response.Header().Set("head-ok", "1") -} - -// 控制器注册测试 -func Test_Router_ControllerRest(t *testing.T) { - p, _ := ports.PopRand() - s := g.Server(p) - s.BindControllerRest("/", new(ControllerRest)) - s.BindControllerRest("/{.struct}/{.method}", new(ControllerRest)) - s.SetPort(p) - s.SetDumpRouterMap(false) - s.Start() - defer s.Shutdown() - - time.Sleep(100 * time.Millisecond) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - - t.Assert(client.GetContent("/"), "1Controller Get2") - t.Assert(client.PutContent("/"), "1Controller Put2") - t.Assert(client.PostContent("/"), "1Controller Post2") - t.Assert(client.DeleteContent("/"), "1Controller Delete2") - resp1, err := client.Head("/") - if err == nil { - defer resp1.Close() - } - t.Assert(err, nil) - t.Assert(resp1.Header.Get("head-ok"), "1") - - t.Assert(client.GetContent("/controller-rest/get"), "1Controller Get2") - t.Assert(client.PutContent("/controller-rest/put"), "1Controller Put2") - t.Assert(client.PostContent("/controller-rest/post"), "1Controller Post2") - t.Assert(client.DeleteContent("/controller-rest/delete"), "1Controller Delete2") - resp2, err := client.Head("/controller-rest/head") - if err == nil { - defer resp2.Close() - } - t.Assert(err, nil) - t.Assert(resp2.Header.Get("head-ok"), "1") - - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) -} diff --git a/net/ghttp/ghttp_unit_router_controller_test.go b/net/ghttp/ghttp_unit_router_controller_test.go deleted file mode 100644 index a3104872c..000000000 --- a/net/ghttp/ghttp_unit_router_controller_test.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package ghttp_test - -import ( - "fmt" - "testing" - "time" - - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/frame/gmvc" - "github.com/gogf/gf/net/ghttp" - "github.com/gogf/gf/test/gtest" -) - -// 控制器 -type Controller struct { - gmvc.Controller -} - -func (c *Controller) Init(r *ghttp.Request) { - c.Controller.Init(r) - c.Response.Write("1") -} - -func (c *Controller) Shut() { - c.Response.Write("2") -} - -func (c *Controller) Index() { - c.Response.Write("Controller Index") -} - -func (c *Controller) Show() { - c.Response.Write("Controller Show") -} - -func (c *Controller) Info() { - c.Response.Write("Controller Info") -} - -func Test_Router_Controller1(t *testing.T) { - p, _ := ports.PopRand() - s := g.Server(p) - s.BindController("/", new(Controller)) - s.BindController("/{.struct}/{.method}", new(Controller)) - s.SetPort(p) - s.SetDumpRouterMap(false) - s.Start() - defer s.Shutdown() - - time.Sleep(100 * time.Millisecond) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - - t.Assert(client.GetContent("/"), "1Controller Index2") - t.Assert(client.GetContent("/init"), "Not Found") - t.Assert(client.GetContent("/shut"), "Not Found") - t.Assert(client.GetContent("/index"), "1Controller Index2") - t.Assert(client.GetContent("/show"), "1Controller Show2") - - t.Assert(client.GetContent("/controller"), "Not Found") - t.Assert(client.GetContent("/controller/init"), "Not Found") - t.Assert(client.GetContent("/controller/shut"), "Not Found") - t.Assert(client.GetContent("/controller/index"), "1Controller Index2") - t.Assert(client.GetContent("/controller/show"), "1Controller Show2") - - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) -} - -func Test_Router_Controller2(t *testing.T) { - p, _ := ports.PopRand() - s := g.Server(p) - s.BindController("/controller", new(Controller), "Show, Info") - s.SetPort(p) - s.SetDumpRouterMap(false) - s.Start() - defer s.Shutdown() - - time.Sleep(100 * time.Millisecond) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - - t.Assert(client.GetContent("/"), "Not Found") - t.Assert(client.GetContent("/controller"), "Not Found") - t.Assert(client.GetContent("/controller/init"), "Not Found") - t.Assert(client.GetContent("/controller/shut"), "Not Found") - t.Assert(client.GetContent("/controller/index"), "Not Found") - t.Assert(client.GetContent("/controller/show"), "1Controller Show2") - t.Assert(client.GetContent("/controller/info"), "1Controller Info2") - - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) -} - -func Test_Router_ControllerMethod(t *testing.T) { - p, _ := ports.PopRand() - s := g.Server(p) - s.BindControllerMethod("/controller-info", new(Controller), "Info") - s.SetPort(p) - s.SetDumpRouterMap(false) - s.Start() - defer s.Shutdown() - - time.Sleep(100 * time.Millisecond) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - - t.Assert(client.GetContent("/"), "Not Found") - t.Assert(client.GetContent("/controller"), "Not Found") - t.Assert(client.GetContent("/controller/init"), "Not Found") - t.Assert(client.GetContent("/controller/shut"), "Not Found") - t.Assert(client.GetContent("/controller/index"), "Not Found") - t.Assert(client.GetContent("/controller/show"), "Not Found") - t.Assert(client.GetContent("/controller/info"), "Not Found") - t.Assert(client.GetContent("/controller-info"), "1Controller Info2") - - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) -} diff --git a/net/ghttp/ghttp_unit_router_domain_controller_rest_test.go b/net/ghttp/ghttp_unit_router_domain_controller_rest_test.go deleted file mode 100644 index be0cdbf1f..000000000 --- a/net/ghttp/ghttp_unit_router_domain_controller_rest_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package ghttp_test - -import ( - "fmt" - "testing" - "time" - - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/frame/gmvc" - "github.com/gogf/gf/net/ghttp" - "github.com/gogf/gf/test/gtest" -) - -type DomainControllerRest struct { - gmvc.Controller -} - -func (c *DomainControllerRest) Init(r *ghttp.Request) { - c.Controller.Init(r) - c.Response.Write("1") -} - -func (c *DomainControllerRest) Shut() { - c.Response.Write("2") -} - -func (c *DomainControllerRest) Get() { - c.Response.Write("Controller Get") -} - -func (c *DomainControllerRest) Put() { - c.Response.Write("Controller Put") -} - -func (c *DomainControllerRest) Post() { - c.Response.Write("Controller Post") -} - -func (c *DomainControllerRest) Delete() { - c.Response.Write("Controller Delete") -} - -func (c *DomainControllerRest) Patch() { - c.Response.Write("Controller Patch") -} - -func (c *DomainControllerRest) Options() { - c.Response.Write("Controller Options") -} - -func (c *DomainControllerRest) Head() { - c.Response.Header().Set("head-ok", "1") -} - -// 控制器注册测试 -func Test_Router_DomainControllerRest(t *testing.T) { - p, _ := ports.PopRand() - s := g.Server(p) - d := s.Domain("localhost, local") - d.BindControllerRest("/", new(DomainControllerRest)) - s.SetPort(p) - s.SetDumpRouterMap(false) - s.Start() - defer s.Shutdown() - - time.Sleep(100 * time.Millisecond) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - - t.Assert(client.GetContent("/"), "Not Found") - t.Assert(client.PutContent("/"), "Not Found") - t.Assert(client.PostContent("/"), "Not Found") - t.Assert(client.DeleteContent("/"), "Not Found") - t.Assert(client.PatchContent("/"), "Not Found") - t.Assert(client.OptionsContent("/"), "Not Found") - resp1, err := client.Head("/") - if err == nil { - defer resp1.Close() - } - t.Assert(err, nil) - t.Assert(resp1.Header.Get("head-ok"), "") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://localhost:%d", p)) - - t.Assert(client.GetContent("/"), "1Controller Get2") - t.Assert(client.PutContent("/"), "1Controller Put2") - t.Assert(client.PostContent("/"), "1Controller Post2") - t.Assert(client.DeleteContent("/"), "1Controller Delete2") - t.Assert(client.PatchContent("/"), "1Controller Patch2") - t.Assert(client.OptionsContent("/"), "1Controller Options2") - resp1, err := client.Head("/") - if err == nil { - defer resp1.Close() - } - t.Assert(err, nil) - t.Assert(resp1.Header.Get("head-ok"), "1") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://local:%d", p)) - - t.Assert(client.GetContent("/"), "1Controller Get2") - t.Assert(client.PutContent("/"), "1Controller Put2") - t.Assert(client.PostContent("/"), "1Controller Post2") - t.Assert(client.DeleteContent("/"), "1Controller Delete2") - t.Assert(client.PatchContent("/"), "1Controller Patch2") - t.Assert(client.OptionsContent("/"), "1Controller Options2") - resp1, err := client.Head("/") - if err == nil { - defer resp1.Close() - } - t.Assert(err, nil) - t.Assert(resp1.Header.Get("head-ok"), "1") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) -} diff --git a/net/ghttp/ghttp_unit_router_domain_controller_test.go b/net/ghttp/ghttp_unit_router_domain_controller_test.go deleted file mode 100644 index 72983e2d3..000000000 --- a/net/ghttp/ghttp_unit_router_domain_controller_test.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package ghttp_test - -import ( - "fmt" - "testing" - "time" - - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/frame/gmvc" - "github.com/gogf/gf/net/ghttp" - "github.com/gogf/gf/test/gtest" -) - -type DomainController struct { - gmvc.Controller -} - -func (c *DomainController) Init(r *ghttp.Request) { - c.Controller.Init(r) - c.Response.Write("1") -} - -func (c *DomainController) Shut() { - c.Response.Write("2") -} - -func (c *DomainController) Index() { - c.Response.Write("Controller Index") -} - -func (c *DomainController) Show() { - c.Response.Write("Controller Show") -} - -func (c *DomainController) Info() { - c.Response.Write("Controller Info") -} - -func Test_Router_DomainController1(t *testing.T) { - p, _ := ports.PopRand() - s := g.Server(p) - s.Domain("localhost, local").BindController("/", new(DomainController)) - s.SetPort(p) - s.SetDumpRouterMap(false) - s.Start() - defer s.Shutdown() - - time.Sleep(100 * time.Millisecond) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - - t.Assert(client.GetContent("/"), "Not Found") - t.Assert(client.GetContent("/init"), "Not Found") - t.Assert(client.GetContent("/shut"), "Not Found") - t.Assert(client.GetContent("/index"), "Not Found") - t.Assert(client.GetContent("/show"), "Not Found") - t.Assert(client.GetContent("/info"), "Not Found") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) - - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://localhost:%d", p)) - - t.Assert(client.GetContent("/"), "1Controller Index2") - t.Assert(client.GetContent("/init"), "Not Found") - t.Assert(client.GetContent("/shut"), "Not Found") - t.Assert(client.GetContent("/index"), "1Controller Index2") - t.Assert(client.GetContent("/show"), "1Controller Show2") - t.Assert(client.GetContent("/info"), "1Controller Info2") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) - - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://local:%d", p)) - - t.Assert(client.GetContent("/"), "1Controller Index2") - t.Assert(client.GetContent("/init"), "Not Found") - t.Assert(client.GetContent("/shut"), "Not Found") - t.Assert(client.GetContent("/index"), "1Controller Index2") - t.Assert(client.GetContent("/show"), "1Controller Show2") - t.Assert(client.GetContent("/info"), "1Controller Info2") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) -} - -func Test_Router_DomainController2(t *testing.T) { - p, _ := ports.PopRand() - s := g.Server(p) - s.Domain("localhost, local").BindController("/controller", new(DomainController), "Show, Info") - s.SetPort(p) - s.SetDumpRouterMap(false) - s.Start() - defer s.Shutdown() - - time.Sleep(100 * time.Millisecond) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - - t.Assert(client.GetContent("/"), "Not Found") - t.Assert(client.GetContent("/controller"), "Not Found") - t.Assert(client.GetContent("/controller/init"), "Not Found") - t.Assert(client.GetContent("/controller/shut"), "Not Found") - t.Assert(client.GetContent("/controller/index"), "Not Found") - t.Assert(client.GetContent("/controller/show"), "Not Found") - t.Assert(client.GetContent("/controller/info"), "Not Found") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) - - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://localhost:%d", p)) - - t.Assert(client.GetContent("/"), "Not Found") - t.Assert(client.GetContent("/controller"), "Not Found") - t.Assert(client.GetContent("/controller/init"), "Not Found") - t.Assert(client.GetContent("/controller/shut"), "Not Found") - t.Assert(client.GetContent("/controller/index"), "Not Found") - t.Assert(client.GetContent("/controller/show"), "1Controller Show2") - t.Assert(client.GetContent("/controller/info"), "1Controller Info2") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) - - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://local:%d", p)) - - t.Assert(client.GetContent("/"), "Not Found") - t.Assert(client.GetContent("/controller"), "Not Found") - t.Assert(client.GetContent("/controller/init"), "Not Found") - t.Assert(client.GetContent("/controller/shut"), "Not Found") - t.Assert(client.GetContent("/controller/index"), "Not Found") - t.Assert(client.GetContent("/controller/show"), "1Controller Show2") - t.Assert(client.GetContent("/controller/info"), "1Controller Info2") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) -} - -func Test_Router_DomainControllerMethod(t *testing.T) { - p, _ := ports.PopRand() - s := g.Server(p) - s.Domain("localhost, local").BindControllerMethod("/controller-info", new(DomainController), "Info") - s.SetPort(p) - s.SetDumpRouterMap(false) - s.Start() - defer s.Shutdown() - - time.Sleep(100 * time.Millisecond) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - - t.Assert(client.GetContent("/"), "Not Found") - t.Assert(client.GetContent("/controller"), "Not Found") - t.Assert(client.GetContent("/controller/init"), "Not Found") - t.Assert(client.GetContent("/controller/shut"), "Not Found") - t.Assert(client.GetContent("/controller/index"), "Not Found") - t.Assert(client.GetContent("/controller/show"), "Not Found") - t.Assert(client.GetContent("/controller/info"), "Not Found") - t.Assert(client.GetContent("/controller-info"), "Not Found") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://localhost:%d", p)) - - t.Assert(client.GetContent("/"), "Not Found") - t.Assert(client.GetContent("/controller"), "Not Found") - t.Assert(client.GetContent("/controller/init"), "Not Found") - t.Assert(client.GetContent("/controller/shut"), "Not Found") - t.Assert(client.GetContent("/controller/index"), "Not Found") - t.Assert(client.GetContent("/controller/show"), "Not Found") - t.Assert(client.GetContent("/controller/info"), "Not Found") - t.Assert(client.GetContent("/controller-info"), "1Controller Info2") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) - gtest.C(t, func(t *gtest.T) { - client := g.Client() - client.SetPrefix(fmt.Sprintf("http://local:%d", p)) - - t.Assert(client.GetContent("/"), "Not Found") - t.Assert(client.GetContent("/controller"), "Not Found") - t.Assert(client.GetContent("/controller/init"), "Not Found") - t.Assert(client.GetContent("/controller/shut"), "Not Found") - t.Assert(client.GetContent("/controller/index"), "Not Found") - t.Assert(client.GetContent("/controller/show"), "Not Found") - t.Assert(client.GetContent("/controller/info"), "Not Found") - t.Assert(client.GetContent("/controller-info"), "1Controller Info2") - t.Assert(client.GetContent("/none-exist"), "Not Found") - }) -} diff --git a/net/ghttp/ghttp_unit_router_group_rest_test.go b/net/ghttp/ghttp_unit_router_group_rest_test.go index c127eb554..e31ce2b79 100644 --- a/net/ghttp/ghttp_unit_router_group_rest_test.go +++ b/net/ghttp/ghttp_unit_router_group_rest_test.go @@ -13,52 +13,10 @@ import ( "time" "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/frame/gmvc" "github.com/gogf/gf/net/ghttp" "github.com/gogf/gf/test/gtest" ) -type GroupCtlRest struct { - gmvc.Controller -} - -func (c *GroupCtlRest) Init(r *ghttp.Request) { - c.Controller.Init(r) - c.Response.Write("1") -} - -func (c *GroupCtlRest) Shut() { - c.Response.Write("2") -} - -func (c *GroupCtlRest) Get() { - c.Response.Write("Controller Get") -} - -func (c *GroupCtlRest) Put() { - c.Response.Write("Controller Put") -} - -func (c *GroupCtlRest) Post() { - c.Response.Write("Controller Post") -} - -func (c *GroupCtlRest) Delete() { - c.Response.Write("Controller Delete") -} - -func (c *GroupCtlRest) Patch() { - c.Response.Write("Controller Patch") -} - -func (c *GroupCtlRest) Options() { - c.Response.Write("Controller Options") -} - -func (c *GroupCtlRest) Head() { - c.Response.Header().Set("head-ok", "1") -} - type GroupObjRest struct{} func (o *GroupObjRest) Init(r *ghttp.Request) { @@ -101,11 +59,8 @@ func Test_Router_GroupRest1(t *testing.T) { p, _ := ports.PopRand() s := g.Server(p) group := s.Group("/api") - ctl := new(GroupCtlRest) obj := new(GroupObjRest) - group.REST("/ctl", ctl) group.REST("/obj", obj) - group.REST("/{.struct}/{.method}", ctl) group.REST("/{.struct}/{.method}", obj) s.SetPort(p) s.SetDumpRouterMap(false) @@ -117,19 +72,6 @@ func Test_Router_GroupRest1(t *testing.T) { client := g.Client() client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - t.Assert(client.GetContent("/api/ctl"), "1Controller Get2") - t.Assert(client.PutContent("/api/ctl"), "1Controller Put2") - t.Assert(client.PostContent("/api/ctl"), "1Controller Post2") - t.Assert(client.DeleteContent("/api/ctl"), "1Controller Delete2") - t.Assert(client.PatchContent("/api/ctl"), "1Controller Patch2") - t.Assert(client.OptionsContent("/api/ctl"), "1Controller Options2") - resp1, err := client.Head("/api/ctl") - if err == nil { - defer resp1.Close() - } - t.Assert(err, nil) - t.Assert(resp1.Header.Get("head-ok"), "1") - t.Assert(client.GetContent("/api/obj"), "1Object Get2") t.Assert(client.PutContent("/api/obj"), "1Object Put2") t.Assert(client.PostContent("/api/obj"), "1Object Post2") @@ -143,20 +85,6 @@ func Test_Router_GroupRest1(t *testing.T) { t.Assert(err, nil) t.Assert(resp2.Header.Get("head-ok"), "1") - t.Assert(client.GetContent("/api/group-ctl-rest"), "Not Found") - t.Assert(client.GetContent("/api/group-ctl-rest/get"), "1Controller Get2") - t.Assert(client.PutContent("/api/group-ctl-rest/put"), "1Controller Put2") - t.Assert(client.PostContent("/api/group-ctl-rest/post"), "1Controller Post2") - t.Assert(client.DeleteContent("/api/group-ctl-rest/delete"), "1Controller Delete2") - t.Assert(client.PatchContent("/api/group-ctl-rest/patch"), "1Controller Patch2") - t.Assert(client.OptionsContent("/api/group-ctl-rest/options"), "1Controller Options2") - resp3, err := client.Head("/api/group-ctl-rest/head") - if err == nil { - defer resp3.Close() - } - t.Assert(err, nil) - t.Assert(resp3.Header.Get("head-ok"), "1") - t.Assert(client.GetContent("/api/group-obj-rest"), "Not Found") t.Assert(client.GetContent("/api/group-obj-rest/get"), "1Object Get2") t.Assert(client.PutContent("/api/group-obj-rest/put"), "1Object Put2") @@ -177,11 +105,8 @@ func Test_Router_GroupRest2(t *testing.T) { p, _ := ports.PopRand() s := g.Server(p) s.Group("/api", func(group *ghttp.RouterGroup) { - ctl := new(GroupCtlRest) obj := new(GroupObjRest) - group.REST("/ctl", ctl) group.REST("/obj", obj) - group.REST("/{.struct}/{.method}", ctl) group.REST("/{.struct}/{.method}", obj) }) s.SetPort(p) @@ -194,19 +119,6 @@ func Test_Router_GroupRest2(t *testing.T) { client := g.Client() client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - t.Assert(client.GetContent("/api/ctl"), "1Controller Get2") - t.Assert(client.PutContent("/api/ctl"), "1Controller Put2") - t.Assert(client.PostContent("/api/ctl"), "1Controller Post2") - t.Assert(client.DeleteContent("/api/ctl"), "1Controller Delete2") - t.Assert(client.PatchContent("/api/ctl"), "1Controller Patch2") - t.Assert(client.OptionsContent("/api/ctl"), "1Controller Options2") - resp1, err := client.Head("/api/ctl") - if err == nil { - defer resp1.Close() - } - t.Assert(err, nil) - t.Assert(resp1.Header.Get("head-ok"), "1") - t.Assert(client.GetContent("/api/obj"), "1Object Get2") t.Assert(client.PutContent("/api/obj"), "1Object Put2") t.Assert(client.PostContent("/api/obj"), "1Object Post2") @@ -220,20 +132,6 @@ func Test_Router_GroupRest2(t *testing.T) { t.Assert(err, nil) t.Assert(resp2.Header.Get("head-ok"), "1") - t.Assert(client.GetContent("/api/group-ctl-rest"), "Not Found") - t.Assert(client.GetContent("/api/group-ctl-rest/get"), "1Controller Get2") - t.Assert(client.PutContent("/api/group-ctl-rest/put"), "1Controller Put2") - t.Assert(client.PostContent("/api/group-ctl-rest/post"), "1Controller Post2") - t.Assert(client.DeleteContent("/api/group-ctl-rest/delete"), "1Controller Delete2") - t.Assert(client.PatchContent("/api/group-ctl-rest/patch"), "1Controller Patch2") - t.Assert(client.OptionsContent("/api/group-ctl-rest/options"), "1Controller Options2") - resp3, err := client.Head("/api/group-ctl-rest/head") - if err == nil { - defer resp3.Close() - } - t.Assert(err, nil) - t.Assert(resp3.Header.Get("head-ok"), "1") - t.Assert(client.GetContent("/api/group-obj-rest"), "Not Found") t.Assert(client.GetContent("/api/group-obj-rest/get"), "1Object Get2") t.Assert(client.PutContent("/api/group-obj-rest/put"), "1Object Put2") diff --git a/net/ghttp/ghttp_unit_router_group_test.go b/net/ghttp/ghttp_unit_router_group_test.go index 619055356..9f093b2cc 100644 --- a/net/ghttp/ghttp_unit_router_group_test.go +++ b/net/ghttp/ghttp_unit_router_group_test.go @@ -12,7 +12,6 @@ import ( "time" "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/frame/gmvc" "github.com/gogf/gf/net/ghttp" "github.com/gogf/gf/test/gtest" ) @@ -40,32 +39,6 @@ func (o *GroupObject) Delete(r *ghttp.Request) { r.Response.Write("Object Delete") } -// 控制器 -type GroupController struct { - gmvc.Controller -} - -func (c *GroupController) Init(r *ghttp.Request) { - c.Controller.Init(r) - c.Response.Write("1") -} - -func (c *GroupController) Shut() { - c.Response.Write("2") -} - -func (c *GroupController) Index() { - c.Response.Write("Controller Index") -} - -func (c *GroupController) Show() { - c.Response.Write("Controller Show") -} - -func (c *GroupController) Post() { - c.Response.Write("Controller Post") -} - func Handler(r *ghttp.Request) { r.Response.Write("Handler") } @@ -74,13 +47,9 @@ func Test_Router_GroupBasic1(t *testing.T) { p, _ := ports.PopRand() s := g.Server(p) obj := new(GroupObject) - ctl := new(GroupController) // 分组路由方法注册 group := s.Group("/api") group.ALL("/handler", Handler) - group.ALL("/ctl", ctl) - group.GET("/ctl/my-show", ctl, "Show") - group.REST("/ctl/rest", ctl) group.ALL("/obj", obj) group.GET("/obj/my-show", obj, "Show") group.REST("/obj/rest", obj) @@ -96,14 +65,6 @@ func Test_Router_GroupBasic1(t *testing.T) { t.Assert(client.GetContent("/api/handler"), "Handler") - t.Assert(client.GetContent("/api/ctl"), "1Controller Index2") - t.Assert(client.GetContent("/api/ctl/"), "1Controller Index2") - t.Assert(client.GetContent("/api/ctl/index"), "1Controller Index2") - t.Assert(client.GetContent("/api/ctl/my-show"), "1Controller Show2") - t.Assert(client.GetContent("/api/ctl/post"), "1Controller Post2") - t.Assert(client.GetContent("/api/ctl/show"), "1Controller Show2") - t.Assert(client.PostContent("/api/ctl/rest"), "1Controller Post2") - t.Assert(client.GetContent("/api/obj"), "1Object Index2") t.Assert(client.GetContent("/api/obj/"), "1Object Index2") t.Assert(client.GetContent("/api/obj/index"), "1Object Index2") @@ -121,13 +82,9 @@ func Test_Router_GroupBasic2(t *testing.T) { p, _ := ports.PopRand() s := g.Server(p) obj := new(GroupObject) - ctl := new(GroupController) // 分组路由批量注册 s.Group("/api").Bind([]g.Slice{ {"ALL", "/handler", Handler}, - {"ALL", "/ctl", ctl}, - {"GET", "/ctl/my-show", ctl, "Show"}, - {"REST", "/ctl/rest", ctl}, {"ALL", "/obj", obj}, {"GET", "/obj/my-show", obj, "Show"}, {"REST", "/obj/rest", obj}, @@ -144,11 +101,6 @@ func Test_Router_GroupBasic2(t *testing.T) { t.Assert(client.GetContent("/api/handler"), "Handler") - t.Assert(client.GetContent("/api/ctl/my-show"), "1Controller Show2") - t.Assert(client.GetContent("/api/ctl/post"), "1Controller Post2") - t.Assert(client.GetContent("/api/ctl/show"), "1Controller Show2") - t.Assert(client.PostContent("/api/ctl/rest"), "1Controller Post2") - t.Assert(client.GetContent("/api/obj/delete"), "1Object Delete2") t.Assert(client.GetContent("/api/obj/my-show"), "1Object Show2") t.Assert(client.GetContent("/api/obj/show"), "1Object Show2") @@ -163,10 +115,8 @@ func Test_Router_GroupBuildInVar(t *testing.T) { p, _ := ports.PopRand() s := g.Server(p) obj := new(GroupObject) - ctl := new(GroupController) // 分组路由方法注册 group := s.Group("/api") - group.ALL("/{.struct}/{.method}", ctl) group.ALL("/{.struct}/{.method}", obj) s.SetPort(p) s.SetDumpRouterMap(false) @@ -178,10 +128,6 @@ func Test_Router_GroupBuildInVar(t *testing.T) { client := g.Client() client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - t.Assert(client.GetContent("/api/group-controller/index"), "1Controller Index2") - t.Assert(client.GetContent("/api/group-controller/post"), "1Controller Post2") - t.Assert(client.GetContent("/api/group-controller/show"), "1Controller Show2") - t.Assert(client.GetContent("/api/group-object/index"), "1Object Index2") t.Assert(client.GetContent("/api/group-object/delete"), "1Object Delete2") t.Assert(client.GetContent("/api/group-object/show"), "1Object Show2") @@ -191,14 +137,12 @@ func Test_Router_GroupBuildInVar(t *testing.T) { }) } -func Test_Router_Group_Mthods(t *testing.T) { +func Test_Router_Group_Methods(t *testing.T) { p, _ := ports.PopRand() s := g.Server(p) obj := new(GroupObject) - ctl := new(GroupController) group := s.Group("/") group.ALL("/obj", obj, "Show, Delete") - group.ALL("/ctl", ctl, "Show, Post") s.SetPort(p) s.SetDumpRouterMap(false) s.Start() @@ -208,8 +152,6 @@ func Test_Router_Group_Mthods(t *testing.T) { gtest.C(t, func(t *gtest.T) { client := g.Client() client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) - t.Assert(client.GetContent("/ctl/show"), "1Controller Show2") - t.Assert(client.GetContent("/ctl/post"), "1Controller Post2") t.Assert(client.GetContent("/obj/show"), "1Object Show2") t.Assert(client.GetContent("/obj/delete"), "1Object Delete2") }) diff --git a/net/ghttp/ghttp_unit_router_handler_extended_test.go b/net/ghttp/ghttp_unit_router_handler_extended_test.go index cab1d390b..77d50c8f2 100644 --- a/net/ghttp/ghttp_unit_router_handler_extended_test.go +++ b/net/ghttp/ghttp_unit_router_handler_extended_test.go @@ -77,6 +77,6 @@ func Test_Router_Handler_Extended_Handler_WithObject(t *testing.T) { client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) t.Assert(client.GetContent("/test?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18,"Name":"john"}}`) - t.Assert(client.GetContent("/test/error"), `{"code":-1,"message":"error","data":null}`) + t.Assert(client.GetContent("/test/error"), `{"code":50,"message":"error","data":null}`) }) } From 9f096fc63d28e52550f94c7d9d87b27c60eaeb45 Mon Sep 17 00:00:00 2001 From: jflyfox <zcool321@sina.com> Date: Fri, 30 Jul 2021 16:21:45 +0800 Subject: [PATCH 403/492] improve error code for package gerror --- errors/gerror/gerror_code.go | 41 ++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/errors/gerror/gerror_code.go b/errors/gerror/gerror_code.go index 6497b3003..055ea0ab0 100644 --- a/errors/gerror/gerror_code.go +++ b/errors/gerror/gerror_code.go @@ -9,17 +9,32 @@ package gerror // Reserved internal error code of framework: code < 1000. const ( - CodeNil = -1 // No error code specified. - CodeOk = 0 // It is OK without error. - CodeInternalError = 50 // An error occurred internally. - CodeValidationFailed = 51 // Data validation failed. - CodeDbOperationError = 52 // Database operation error. - CodeInvalidParameter = 53 // The given parameter for current operation is invalid. - CodeMissingParameter = 54 // Parameter for current operation is missing. - CodeInvalidOperation = 55 // The function cannot be used like this. - CodeInvalidConfiguration = 56 // The configuration is invalid for current operation. - CodeMissingConfiguration = 57 // The configuration is missing for current operation. - CodeNotImplemented = 58 // The operation is not implemented yet. - CodeNotSupported = 59 // The operation is not supported yet. - CodeOperationFailed = 60 // I tried, but I cannot give you what you want. + // =============================================================================== + // Common system codes. + // =============================================================================== + + CodeNil = -1 // No error code specified. + CodeOk = 0 // It is OK. + CodeInternalError = 50 + iota // An error occurred internally. + CodeValidationFailed // Data validation failed. + CodeDbOperationError // Database operation error. + CodeInvalidParameter // The given parameter for current operation is invalid. + CodeMissingParameter // Parameter for current operation is missing. + CodeInvalidOperation // The function cannot be used like this. + CodeInvalidConfiguration // The configuration is invalid for current operation. + CodeMissingConfiguration // The configuration is missing for current operation. + CodeNotImplemented // The operation is not implemented yet. + CodeNotSupported // The operation is not supported yet. + CodeOperationFailed // I tried, but I cannot give you what you want. + CodeNotAuthorized // Not Authorized. + CodeSecurityReason // Security Reason. + CodeServerBusy // Server is busy, please try again later. + CodeUnknown // Unknown error. + CodeResourceNotExist // Resource does not exist. + + // =============================================================================== + // Common business codes. + // =============================================================================== + + CodeBusinessValidationFailed = 300 + iota // Business validation failed. ) From 5a6c2c27df1d47da6f2fa076061db1af1c5e9b0e Mon Sep 17 00:00:00 2001 From: jflyfox <zcool321@sina.com> Date: Fri, 30 Jul 2021 17:17:13 +0800 Subject: [PATCH 404/492] inject Request object into context --- net/ghttp/ghttp_request.go | 6 ++++++ net/ghttp/ghttp_request_middleware.go | 5 +---- net/ghttp/ghttp_unit_router_handler_extended_test.go | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index e01dc1ac8..5aac73d46 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -78,6 +78,12 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { Response: newResponse(s, w), EnterTime: gtime.TimestampMilli(), } + // Inject Request object into context. + request.context = context.WithValue( + request.Context(), + ctxKeyForRequest, + request, + ) request.Cookie = GetCookie(request) request.Session = s.sessionManager.New( r.Context(), diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index 11638ddb2..ff8c5a539 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -7,7 +7,6 @@ package ghttp import ( - "context" "github.com/gogf/gf/errors/gerror" "net/http" "reflect" @@ -130,9 +129,7 @@ func (m *middleware) callHandlerFunc(funcInfo handlerFuncInfo) { funcInfo.Func(m.request) } else { var inputValues = []reflect.Value{ - reflect.ValueOf(context.WithValue( - m.request.Context(), ctxKeyForRequest, m.request, - )), + reflect.ValueOf(m.request.Context()), } if funcInfo.Type.NumIn() == 2 { var ( diff --git a/net/ghttp/ghttp_unit_router_handler_extended_test.go b/net/ghttp/ghttp_unit_router_handler_extended_test.go index 77d50c8f2..49f00095c 100644 --- a/net/ghttp/ghttp_unit_router_handler_extended_test.go +++ b/net/ghttp/ghttp_unit_router_handler_extended_test.go @@ -77,6 +77,6 @@ func Test_Router_Handler_Extended_Handler_WithObject(t *testing.T) { client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) t.Assert(client.GetContent("/test?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18,"Name":"john"}}`) - t.Assert(client.GetContent("/test/error"), `{"code":50,"message":"error","data":null}`) + t.Assert(client.GetContent("/test/error"), `{"code":52,"message":"error","data":null}`) }) } From bb57dc1ae7e70ef3b23b1ef5c958a4e64b73e644 Mon Sep 17 00:00:00 2001 From: jflyfox <zcool321@sina.com> Date: Sun, 1 Aug 2021 09:17:37 +0800 Subject: [PATCH 405/492] improve request context feature for package ghttp --- net/ghttp/ghttp_request.go | 15 --------------- net/ghttp/ghttp_request_param_ctx.go | 12 ++++++++++++ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index 5aac73d46..97d33fe25 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -78,12 +78,6 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { Response: newResponse(s, w), EnterTime: gtime.TimestampMilli(), } - // Inject Request object into context. - request.context = context.WithValue( - request.Context(), - ctxKeyForRequest, - request, - ) request.Cookie = GetCookie(request) request.Session = s.sessionManager.New( r.Context(), @@ -108,15 +102,6 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { return request } -// RequestFromCtx retrieves and returns the Request object from context. -func RequestFromCtx(ctx context.Context) *Request { - result := ctx.Value(ctxKeyForRequest) - if result != nil { - return result.(*Request) - } - return nil -} - // WebSocket upgrades current request as a websocket request. // It returns a new WebSocket object if success, or the error if failure. // Note that the request should be a websocket request, or it will surely fail upgrading. diff --git a/net/ghttp/ghttp_request_param_ctx.go b/net/ghttp/ghttp_request_param_ctx.go index 395d6c0e6..efcafba2e 100644 --- a/net/ghttp/ghttp_request_param_ctx.go +++ b/net/ghttp/ghttp_request_param_ctx.go @@ -11,6 +11,14 @@ import ( "github.com/gogf/gf/container/gvar" ) +// RequestFromCtx retrieves and returns the Request object from context. +func RequestFromCtx(ctx context.Context) *Request { + if v := ctx.Value(ctxKeyForRequest); v != nil { + return v.(*Request) + } + return nil +} + // Context is alias for function GetCtx. // This function overwrites the http.Request.Context function. // See GetCtx. @@ -18,6 +26,10 @@ func (r *Request) Context() context.Context { if r.context == nil { r.context = r.Request.Context() } + // Inject Request object into context. + if RequestFromCtx(r.context) == nil { + r.context = context.WithValue(r.context, ctxKeyForRequest, r) + } return r.context } From 2472dd5fac86b872619fbe194a1bb17f4b4ee9f7 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sun, 1 Aug 2021 09:33:12 +0800 Subject: [PATCH 406/492] github action updates --- .github/workflows/go.yml | 53 +++++++++++++--------------------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b256fe155..86fb55226 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,52 +1,35 @@ -name: Go +name: GoFrame CI on: push: branches: - master - - main - develop - - staging pull_request: - branches: [ master ] + branches: [master, develop] env: GF_DEBUG: 1 jobs: - code-test: runs-on: ubuntu-latest # Service containers to run with `code-test` services: redis: - image: redis + image : redis options: >- - --health-cmd "redis-cli ping" + --health-cmd "redis-cli ping" --health-interval 10s - --health-timeout 5s - --health-retries 5 + --health-timeout 5s + --health-retries 5 ports: # Maps tcp port 6379 on service container to the host - 6379:6379 - postgres: - image: postgres:9 - # Provide the password for postgres - env: - POSTGRES_PASSWORD: postgres - # Set health checks to wait until postgres has started - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - # Maps tcp port 5432 on service container to the host - - 5432:5432 mysql: image: mysql:5.7 env: + MYSQL_DATABASE : test MYSQL_ROOT_PASSWORD: 12345678 - MYSQL_DATABASE: test ports: # Maps tcp port 3306 on service container to the host - 3306:3306 @@ -54,37 +37,35 @@ jobs: # strategy set strategy: matrix: - go: [ '1.13', '1.14','1.15','1.16' ] + go: ["1.14", "1.15", "1.16"] steps: - - - uses: szenius/set-timezone@v1.0 + - name: Set Up Timezone + uses: szenius/set-timezone@v1.0 with: timezoneLinux: "Asia/Shanghai" - - uses: actions/checkout@v2 + - name: Checkout Repositary + uses: actions/checkout@v2 - - name: Set up Go + - name: Set Up Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - - name: Before script + - name: Before Script run: | date find . -name "*.go" | xargs gofmt -w git diff --name-only --exit-code || exit 1 sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts - # - name: Build - # run: go build -v ./... - - - name: 386 mode Test + - name: Run i386 Arch Test run: GOARCH=386 go test -v ./... || exit 1 - - name: amd64 mode Test + - name: Run amd64 Arch Test run: GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic - - name: report coverage + - name: Report Coverage run: bash <(curl -s https://codecov.io/bash) From 2b5244a54bb51a73e750794d29092a669e0debeb Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sun, 1 Aug 2021 09:45:46 +0800 Subject: [PATCH 407/492] unit testing cases update for package gredis/glog --- database/gredis/gredis_z_unit_test.go | 18 ++------ os/glog/glog_z_unit_chaining_test.go | 62 ++++++++++++++------------- 2 files changed, 36 insertions(+), 44 deletions(-) diff --git a/database/gredis/gredis_z_unit_test.go b/database/gredis/gredis_z_unit_test.go index dfc7a5570..bf24e991f 100644 --- a/database/gredis/gredis_z_unit_test.go +++ b/database/gredis/gredis_z_unit_test.go @@ -139,21 +139,11 @@ func Test_Instance(t *testing.T) { func Test_Error(t *testing.T) { gtest.C(t, func(t *gtest.T) { - //config1 := &gredis.Config{ - // Host: "127.0.0.2", - // Port: 6379, - // Db: 1, - // ConnectTimeout: time.Second, - //} - //redis := gredis.New(config1) - //_, err := redis.Do("info") - //t.AssertNE(err, nil) - config1 := &gredis.Config{ - Host: "127.0.0.1", - Port: 6379, - Db: 1, - Pass: "666666", + Host: "192.111.0.2", + Port: 6379, + Db: 1, + ConnectTimeout: time.Second, } redis := gredis.New(config1) _, err := redis.Do("info") diff --git a/os/glog/glog_z_unit_chaining_test.go b/os/glog/glog_z_unit_chaining_test.go index 407387e14..f6110e491 100644 --- a/os/glog/glog_z_unit_chaining_test.go +++ b/os/glog/glog_z_unit_chaining_test.go @@ -115,36 +115,38 @@ func Test_Stack(t *testing.T) { }) } -//func Test_StackWithFilter(t *testing.T) { -// gtest.C(t, func(t *gtest.T) { -// path := gfile.TempDir(gtime.TimestampNanoStr()) -// file := fmt.Sprintf(`%d.log`, gtime.TimestampNano()) -// -// err := gfile.Mkdir(path) -// t.Assert(err, nil) -// defer gfile.Remove(path) -// -// Path(path).File(file).StackWithFilter("none").Stdout(false).Error(1, 2, 3) -// content := gfile.GetContents(gfile.Join(path, file)) -// t.Assert(gstr.Count(content, defaultLevelPrefixes[LEVEL_ERRO]), 1) -// t.Assert(gstr.Count(content, "1 2 3"), 1) -// t.Assert(gstr.Count(content, "Stack"), 1) -// }) -// gtest.C(t, func(t *gtest.T) { -// path := gfile.TempDir(gtime.TimestampNanoStr()) -// file := fmt.Sprintf(`%d.log`, gtime.TimestampNano()) -// -// err := gfile.Mkdir(path) -// t.Assert(err, nil) -// defer gfile.Remove(path) -// -// Path(path).File(file).StackWithFilter("gogf").Stdout(false).Error(1, 2, 3) -// content := gfile.GetContents(gfile.Join(path, file)) -// t.Assert(gstr.Count(content, defaultLevelPrefixes[LEVEL_ERRO]), 1) -// t.Assert(gstr.Count(content, "1 2 3"), 1) -// t.Assert(gstr.Count(content, "Stack"), 0) -// }) -//} +func Test_StackWithFilter(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + path := gfile.TempDir(gtime.TimestampNanoStr()) + file := fmt.Sprintf(`%d.log`, gtime.TimestampNano()) + + err := gfile.Mkdir(path) + t.Assert(err, nil) + defer gfile.Remove(path) + + Path(path).File(file).StackWithFilter("none").Stdout(false).Error(1, 2, 3) + content := gfile.GetContents(gfile.Join(path, file)) + t.Assert(gstr.Count(content, defaultLevelPrefixes[LEVEL_ERRO]), 1) + t.Assert(gstr.Count(content, "1 2 3"), 1) + t.Assert(gstr.Count(content, "Stack"), 1) + fmt.Println("Content:") + fmt.Println(content) + }) + gtest.C(t, func(t *gtest.T) { + path := gfile.TempDir(gtime.TimestampNanoStr()) + file := fmt.Sprintf(`%d.log`, gtime.TimestampNano()) + + err := gfile.Mkdir(path) + t.Assert(err, nil) + defer gfile.Remove(path) + + Path(path).File(file).StackWithFilter("gogf").Stdout(false).Error(1, 2, 3) + content := gfile.GetContents(gfile.Join(path, file)) + t.Assert(gstr.Count(content, defaultLevelPrefixes[LEVEL_ERRO]), 1) + t.Assert(gstr.Count(content, "1 2 3"), 1) + t.Assert(gstr.Count(content, "Stack"), 0) + }) +} func Test_Header(t *testing.T) { gtest.C(t, func(t *gtest.T) { From 0acd118c03e623b16eee38785e615e07127e11a3 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sun, 1 Aug 2021 10:17:03 +0800 Subject: [PATCH 408/492] add ModelHandler feature for package gdb --- database/gdb/gdb_model.go | 13 +++++++++++++ database/gdb/gdb_z_mysql_model_test.go | 24 ++++++++++++++++++++++++ os/glog/glog_z_unit_chaining_test.go | 2 ++ 3 files changed, 39 insertions(+) diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index bc532c59b..cd7900d9b 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -53,6 +53,9 @@ type Model struct { onDuplicateEx interface{} // onDuplicateEx is used for excluding some columns ON "DUPLICATE KEY UPDATE" statement. } +// ModelHandler is a function that handles given Model and returns a new Model that is custom modified. +type ModelHandler func(m *Model) *Model + // whereHolder is the holder for where condition preparing. type whereHolder struct { operator int // Operator for this holder. @@ -297,3 +300,13 @@ func (m *Model) Args(args ...interface{}) *Model { model.extraArgs = append(model.extraArgs, args) return model } + +// Handler calls each of `handlers` on current Model and returns a new Model. +// ModelHandler is a function that handles given Model and returns a new Model that is custom modified. +func (m *Model) Handler(handlers ...ModelHandler) *Model { + model := m.getModel() + for _, handler := range handlers { + model = handler(model) + } + return model +} diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index dc83d4d95..8121c2d49 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -3729,3 +3729,27 @@ func Test_Model_Raw(t *testing.T) { t.Assert(count, 6) }) } + +func Test_Model_Handler(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + m := db.Model(table).Safe().Handler( + func(m *gdb.Model) *gdb.Model { + return m.Page(0, 3) + }, + func(m *gdb.Model) *gdb.Model { + return m.Where("id", g.Slice{1, 2, 3, 4, 5, 6}) + }, + func(m *gdb.Model) *gdb.Model { + return m.OrderDesc("id") + }, + ) + all, err := m.All() + t.AssertNil(err) + t.Assert(len(all), 3) + t.Assert(all[0]["id"], 6) + t.Assert(all[2]["id"], 4) + }) +} diff --git a/os/glog/glog_z_unit_chaining_test.go b/os/glog/glog_z_unit_chaining_test.go index f6110e491..1be9d3a86 100644 --- a/os/glog/glog_z_unit_chaining_test.go +++ b/os/glog/glog_z_unit_chaining_test.go @@ -145,6 +145,8 @@ func Test_StackWithFilter(t *testing.T) { t.Assert(gstr.Count(content, defaultLevelPrefixes[LEVEL_ERRO]), 1) t.Assert(gstr.Count(content, "1 2 3"), 1) t.Assert(gstr.Count(content, "Stack"), 0) + fmt.Println("Content:") + fmt.Println(content) }) } From fa64df6f918f3affe18ed9b1370454ba30845bc3 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sun, 1 Aug 2021 10:33:33 +0800 Subject: [PATCH 409/492] improve package gdb/glog --- database/gdb/gdb_model.go | 4 ++++ database/gdb/gdb_model_select.go | 12 ++++++------ database/gdb/gdb_type_result.go | 1 + os/glog/glog_z_unit_chaining_test.go | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index cd7900d9b..195e551cd 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -56,6 +56,10 @@ type Model struct { // ModelHandler is a function that handles given Model and returns a new Model that is custom modified. type ModelHandler func(m *Model) *Model +// ChunkHandler is a function that is used in function Chunk, which handles given Result and error. +// It returns true if it wants continue chunking, or else it returns false to stop chunking. +type ChunkHandler func(result Result, err error) bool + // whereHolder is the holder for where condition preparing. type whereHolder struct { operator int // Operator for this holder. diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index e1690743e..78fc25735 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -101,27 +101,27 @@ func (m *Model) getFieldsFiltered() string { return newFields } -// Chunk iterates the query result with given size and callback function. -func (m *Model) Chunk(limit int, callback func(result Result, err error) bool) { +// Chunk iterates the query result with given `size` and `handler` function. +func (m *Model) Chunk(size int, handler ChunkHandler) { page := m.start if page <= 0 { page = 1 } model := m for { - model = model.Page(page, limit) + model = model.Page(page, size) data, err := model.All() if err != nil { - callback(nil, err) + handler(nil, err) break } if len(data) == 0 { break } - if callback(data, err) == false { + if handler(data, err) == false { break } - if len(data) < limit { + if len(data) < size { break } page++ diff --git a/database/gdb/gdb_type_result.go b/database/gdb/gdb_type_result.go index dd2764880..cfe1ba92a 100644 --- a/database/gdb/gdb_type_result.go +++ b/database/gdb/gdb_type_result.go @@ -77,6 +77,7 @@ func (r Result) List() List { // Array retrieves and returns specified column values as slice. // The parameter `field` is optional is the column field is only one. +// The default `field` is the first field name of the first item in `Result` if parameter `field` is not given. func (r Result) Array(field ...string) []Value { array := make([]Value, len(r)) if len(r) == 0 { diff --git a/os/glog/glog_z_unit_chaining_test.go b/os/glog/glog_z_unit_chaining_test.go index 1be9d3a86..f72f88180 100644 --- a/os/glog/glog_z_unit_chaining_test.go +++ b/os/glog/glog_z_unit_chaining_test.go @@ -140,7 +140,7 @@ func Test_StackWithFilter(t *testing.T) { t.Assert(err, nil) defer gfile.Remove(path) - Path(path).File(file).StackWithFilter("gogf").Stdout(false).Error(1, 2, 3) + Path(path).File(file).StackWithFilter("/gf/").Stdout(false).Error(1, 2, 3) content := gfile.GetContents(gfile.Join(path, file)) t.Assert(gstr.Count(content, defaultLevelPrefixes[LEVEL_ERRO]), 1) t.Assert(gstr.Count(content, "1 2 3"), 1) From 248e6ff1344ffea94a1fdef80150c65454b63db3 Mon Sep 17 00:00:00 2001 From: jroam <yybjroam@qq.com> Date: Sun, 1 Aug 2021 15:19:48 +0800 Subject: [PATCH 410/492] bugfix garray Sort --- container/garray/garray_normal_int.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index cc04a256a..83c2624d5 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -149,7 +149,7 @@ func (a *IntArray) Sort(reverse ...bool) *IntArray { defer a.mu.Unlock() if len(reverse) > 0 && reverse[0] { sort.Slice(a.array, func(i, j int) bool { - return a.array[i] > a.array[j] + return a.array[i] >= a.array[j] }) } else { sort.Ints(a.array) From 76785402709e4bf013655531271b632f800e1fc0 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sun, 1 Aug 2021 22:12:44 +0800 Subject: [PATCH 411/492] add bail rule for package gvalid --- util/gvalid/gvalid.go | 13 ++++- util/gvalid/gvalid_validator_check_struct.go | 42 +++++++++------- util/gvalid/gvalid_validator_check_value.go | 34 ++++++++++--- util/gvalid/gvalid_z_unit_basic_all_test.go | 51 ++++++++++++++++++++ 4 files changed, 114 insertions(+), 26 deletions(-) diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 5b390a125..834d6a82c 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -15,7 +15,9 @@ import ( "github.com/gogf/gf/text/gregex" ) -// Refer to Laravel validation: https://laravel.com/docs/5.5/validation#available-validation-rules +// Refer to Laravel validation: +// https://laravel.com/docs/5.5/validation#available-validation-rules +// https://learnku.com/docs/laravel/5.4/validation // // All supported rules: // required format: required brief: Required. @@ -25,6 +27,7 @@ import ( // required-with-all format: required-with-all:field1,field2,... brief: Required if all of given fields are not empty. // required-without format: required-without:field1,field2,... brief: Required if any of given fields are empty. // required-without-all format: required-without-all:field1,field2,...brief: Required if all of given fields are empty. +// bail format: bail brief: Stop validating when this field's validation failed. // date format: date brief: Standard date, like: 2006-01-02, 20060102, 2006.01.02 // date-format format: date-format:format brief: Custom date format. // email format: email brief: Email address. @@ -79,6 +82,7 @@ const ( internalDefaultRuleName = "__default__" // default rule name for i18n error message format if no i18n message found for specified error rule. ruleMessagePrefixForI18n = "gf.gvalid.rule." // prefix string for each rule configuration in i18n content. noValidationTagName = "nv" // no validation tag name for struct attribute. + bailRuleName = "bail" // the name for rule "bail" ) var ( @@ -121,6 +125,7 @@ var ( "required-with-all": {}, "required-without": {}, "required-without-all": {}, + "bail": {}, "date": {}, "date-format": {}, "email": {}, @@ -219,6 +224,12 @@ var ( "regex": "The :attribute value is invalid", internalDefaultRuleName: "The :attribute value is invalid", } + // markedRuleMap defines all rules that are just marked rules which have neither functional meaning + // nor error messages. + markedRuleMap = map[string]bool{ + bailRuleName: true, + "nullable": true, + } ) // CheckValue checks single value with specified rules. diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 868312744..50822e89e 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -62,11 +62,11 @@ func (v *Validator) doCheckStruct(object interface{}) Error { } var ( - inputParamMap map[string]interface{} - checkRules = make(map[string]string) - customMessage = make(CustomMsg) - checkValueData = v.data - errorRules = make([]string, 0) // Sequence rules. + inputParamMap map[string]interface{} + checkRuleStrMap = make(map[string]string) // Complete rules map of struct: map[name]rule, the rule is complete pattern like: Name@RuleStr#Message + customMessage = make(CustomMsg) // Custom rule error message map. + checkValueData = v.data // Ready to be validated data, which can be type of . + errorRules = make([]string, 0) // Sequence rules. ) if checkValueData == nil { checkValueData = object @@ -101,17 +101,17 @@ func (v *Validator) doCheckStruct(object interface{}) Error { customMessage[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k]) } } - checkRules[name] = rule + checkRuleStrMap[name] = rule errorRules = append(errorRules, name+"@"+rule) } // Map type rules does not support sequence. // Format: map[key]rule case map[string]string: - checkRules = v + checkRuleStrMap = v } // If there's no struct tag and validation rules, it does nothing and returns quickly. - if len(tagField) == 0 && len(checkRules) == 0 { + if len(tagField) == 0 && len(checkRuleStrMap) == 0 { return nil } // Input parameter map handling. @@ -161,14 +161,14 @@ func (v *Validator) doCheckStruct(object interface{}) Error { } } } - if _, ok := checkRules[name]; !ok { - if _, ok := checkRules[fieldName]; ok { + if _, ok := checkRuleStrMap[name]; !ok { + if _, ok := checkRuleStrMap[fieldName]; ok { // If there's alias name, // use alias name as its key and remove the field name key. - checkRules[name] = checkRules[fieldName] - delete(checkRules, fieldName) + checkRuleStrMap[name] = checkRuleStrMap[fieldName] + delete(checkRuleStrMap, fieldName) } else { - checkRules[name] = rule + checkRuleStrMap[name] = rule } errorRules = append(errorRules, name+"@"+rule) } else { @@ -211,13 +211,16 @@ func (v *Validator) doCheckStruct(object interface{}) Error { } } - // The following logic is the same as some of CheckMap. - var value interface{} - for key, rule := range checkRules { + // The following logic is the same as some of CheckMap but with sequence support. + var ( + value interface{} + hasBailRule bool + ) + for key, rule := range checkRuleStrMap { _, value = gutil.MapPossibleItemByKey(inputParamMap, key) // It checks each rule and its value in loop. - if e := v.doCheckValue(key, value, rule, customMessage[key], checkValueData, inputParamMap); e != nil { - _, item := e.FirstItem() + if validatedError := v.doCheckValue(key, value, rule, customMessage[key], checkValueData, inputParamMap); validatedError != nil { + _, item := validatedError.FirstItem() // =================================================================== // Only in map and struct validations, if value is nil or empty string // and has no required* rules, it clears the error message. @@ -247,6 +250,9 @@ func (v *Validator) doCheckStruct(object interface{}) Error { for k, v := range item { errorMaps[key][k] = v } + if hasBailRule { + break + } } } if len(errorMaps) > 0 { diff --git a/util/gvalid/gvalid_validator_check_value.go b/util/gvalid/gvalid_validator_check_value.go index 78377d32d..ff5c507f0 100644 --- a/util/gvalid/gvalid_validator_check_value.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -34,13 +34,14 @@ func (v *Validator) CheckValue(value interface{}) Error { // doCheckSingleValue does the really rules validation for single key-value. // -// The parameter `rules` specifies the validation rules string, like "required", "required|between:1,100", etc. +// The parameter `name` specifies the name of parameter `value`. // The parameter `value` specifies the value for this rules to be validated. +// The parameter `rules` specifies the validation rules string, like "required", "required|between:1,100", etc. // The parameter `messages` specifies the custom error messages for this rule, which is usually type of map/slice. // The parameter `dataRaw` specifies the `raw data` which is passed to the Validator. It might be type of map/struct or a nil value. // The parameter `dataMap` specifies the map that is converted from `dataRaw`. It is usually used internally func (v *Validator) doCheckValue( - key string, + name string, value interface{}, rules string, messages interface{}, @@ -92,15 +93,29 @@ func (v *Validator) doCheckValue( break } } + var ( + hasBailRule = false + ) for index := 0; index < len(ruleItems); { var ( err error - match = false - results = ruleRegex.FindStringSubmatch(ruleItems[index]) - ruleKey = strings.TrimSpace(results[1]) - rulePattern = strings.TrimSpace(results[2]) + match = false // whether this rule is matched(has no error) + results = ruleRegex.FindStringSubmatch(ruleItems[index]) // split single rule. + ruleKey = strings.TrimSpace(results[1]) // rule name like "max" in rule "max: 6" + rulePattern = strings.TrimSpace(results[2]) // rule value if any like "6" in rule:"max:6" customRuleFunc RuleFunc ) + + if !hasBailRule && ruleKey == bailRuleName { + hasBailRule = true + } + + // Ignore logic executing for marked rules. + if markedRuleMap[ruleKey] { + index++ + continue + } + if len(msgArray) > index { customMsgMap[ruleKey] = strings.TrimSpace(msgArray[index]) } @@ -134,12 +149,17 @@ func (v *Validator) doCheckValue( if _, ok := errorMsgArray[ruleKey]; !ok { errorMsgArray[ruleKey] = v.getErrorMessageByRule(ruleKey, customMsgMap) } + // If it is with error and there's bail rule, + // it then does not continue validating for left rules. + if hasBailRule { + break + } } index++ } if len(errorMsgArray) > 0 { return newError(gerror.CodeValidationFailed, []string{rules}, map[string]map[string]string{ - key: errorMsgArray, + name: errorMsgArray, }) } return nil diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index b263d1e9e..2997913d2 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -1027,3 +1027,54 @@ func Test_Code(t *testing.T) { t.Assert(gerror.Code(err), gerror.CodeInternalError) }) } + +func Test_Bail(t *testing.T) { + // check value with no bail + gtest.C(t, func(t *gtest.T) { + err := g.Validator(). + Rules("required|min:1|between:1,100"). + Messages("|min number is 1|size is between 1 and 100"). + CheckValue(-1) + t.AssertNE(err, nil) + t.Assert(err.Error(), "min number is 1; size is between 1 and 100") + }) + + // check value with bail + gtest.C(t, func(t *gtest.T) { + err := g.Validator(). + Rules("bail|required|min:1|between:1,100"). + Messages("||min number is 1|size is between 1 and 100"). + CheckValue(-1) + t.AssertNE(err, nil) + t.Assert(err.Error(), "min number is 1") + }) + + // struct with no bail + gtest.C(t, func(t *gtest.T) { + type Params struct { + Page int `v:"required|min:1"` + Size int `v:"required|min:1|between:1,100 # |min number is 1|size is between 1 and 100"` + } + obj := &Params{ + Page: 1, + Size: -1, + } + err := g.Validator().CheckStruct(obj) + t.AssertNE(err, nil) + t.Assert(err.Error(), "min number is 1; size is between 1 and 100") + }) + // struct with bail + gtest.C(t, func(t *gtest.T) { + type Params struct { + Page int `v:"required|min:1"` + Size int `v:"bail|required|min:1|between:1,100 # ||min number is 1|size is between 1 and 100"` + } + obj := &Params{ + Page: 1, + Size: -1, + } + err := g.Validator().CheckStruct(obj) + t.AssertNE(err, nil) + t.Assert(err.Error(), "min number is 1") + }) +} From ab5f809074d265a465cb262e7101401dc403a469 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sun, 1 Aug 2021 22:13:20 +0800 Subject: [PATCH 412/492] go.sum updates --- go.sum | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/go.sum b/go.sum index 8e6df8762..21565402e 100644 --- a/go.sum +++ b/go.sum @@ -34,12 +34,12 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc= -go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I= -go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0cljhGk9sB0Yk= -go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4= -go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58= -go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg= +go.opentelemetry.io/otel v1.0.0-RC2 h1:SHhxSjB+omnGZPgGlKe+QMp3MyazcOHdQ8qwo89oKbg= +go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM= +go.opentelemetry.io/otel/oteltest v1.0.0-RC2 h1:xNKqMhlZYkASSyvF4JwObZFMq0jhFN3c3SP+2rCzVPk= +go.opentelemetry.io/otel/oteltest v1.0.0-RC2/go.mod h1:kiQ4tw5tAL4JLTbcOYwK1CWI1HkT5aiLzHovgOVnz/A= +go.opentelemetry.io/otel/trace v1.0.0-RC2 h1:dunAP0qDULMIT82atj34m5RgvsIK6LcsXf1c/MsYg1w= +go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= From f02372cf5885f47d7c49e611af86efdeaba68f97 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sun, 1 Aug 2021 23:50:44 +0800 Subject: [PATCH 413/492] improve package gvalid --- util/gvalid/gvalid.go | 8 +- util/gvalid/gvalid_error.go | 26 ++-- util/gvalid/gvalid_validator.go | 8 ++ util/gvalid/gvalid_validator_check_map.go | 82 +++++++----- util/gvalid/gvalid_validator_check_struct.go | 92 ++++++++----- util/gvalid/gvalid_validator_check_value.go | 128 ++++++++++--------- util/gvalid/gvalid_z_unit_checkmap_test.go | 36 ++++++ 7 files changed, 242 insertions(+), 138 deletions(-) diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 834d6a82c..24160e741 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -68,6 +68,12 @@ import ( // like: map[field] => string|map[rule]string type CustomMsg = map[string]interface{} +// fieldRule defined the alias name and rule string for specified field. +type fieldRule struct { + Name string // Alias name for the field. + Rule string // Rule string like: "max:6" +} + // apiNoValidation is an interface that marks current struct not validated by package `gvalid`. type apiNoValidation interface { NoValidation() @@ -228,7 +234,7 @@ var ( // nor error messages. markedRuleMap = map[string]bool{ bailRuleName: true, - "nullable": true, + //"nullable": true, } ) diff --git a/util/gvalid/gvalid_error.go b/util/gvalid/gvalid_error.go index fd113e989..4f4a93fef 100644 --- a/util/gvalid/gvalid_error.go +++ b/util/gvalid/gvalid_error.go @@ -31,14 +31,14 @@ type Error interface { // validationError is the validation error for validation result. type validationError struct { code int // Error code. - rules []string // Rules by sequence, which is used for keeping error sequence. + rules []fieldRule // Rules by sequence, which is used for keeping error sequence. errors map[string]map[string]string // Error map:map[field]map[rule]message firstKey string // The first error rule key(empty in default). firstItem map[string]string // The first error rule value(nil in default). } // newError creates and returns a validation error. -func newError(code int, rules []string, errors map[string]map[string]string) *validationError { +func newError(code int, rules []fieldRule, errors map[string]map[string]string) *validationError { for field, m := range errors { for k, v := range m { v = strings.Replace(v, ":attribute", field, -1) @@ -99,10 +99,9 @@ func (e *validationError) Items() (items []map[string]map[string]string) { // By sequence. if len(e.rules) > 0 { for _, v := range e.rules { - name, _, _ := parseSequenceTag(v) - if errorItemMap, ok := e.errors[name]; ok { + if errorItemMap, ok := e.errors[v.Name]; ok { items = append(items, map[string]map[string]string{ - name: errorItemMap, + v.Name: errorItemMap, }) } } @@ -128,11 +127,10 @@ func (e *validationError) FirstItem() (key string, messages map[string]string) { // By sequence. if len(e.rules) > 0 { for _, v := range e.rules { - name, _, _ := parseSequenceTag(v) - if errorItemMap, ok := e.errors[name]; ok { - e.firstKey = name + if errorItemMap, ok := e.errors[v.Name]; ok { + e.firstKey = v.Name e.firstItem = errorItemMap - return name, errorItemMap + return v.Name, errorItemMap } } } @@ -153,9 +151,8 @@ func (e *validationError) FirstRule() (rule string, err string) { // By sequence. if len(e.rules) > 0 { for _, v := range e.rules { - name, ruleStr, _ := parseSequenceTag(v) - if errorItemMap, ok := e.errors[name]; ok { - for _, ruleItem := range strings.Split(ruleStr, "|") { + if errorItemMap, ok := e.errors[v.Name]; ok { + for _, ruleItem := range strings.Split(v.Rule, "|") { array := strings.Split(ruleItem, ":") ruleItem = strings.TrimSpace(array[0]) if err, ok = errorItemMap[ruleItem]; ok { @@ -218,10 +215,9 @@ func (e *validationError) Strings() (errs []string) { // By sequence. if len(e.rules) > 0 { for _, v := range e.rules { - name, ruleStr, _ := parseSequenceTag(v) - if errorItemMap, ok := e.errors[name]; ok { + if errorItemMap, ok := e.errors[v.Name]; ok { // validation error checks. - for _, ruleItem := range strings.Split(ruleStr, "|") { + for _, ruleItem := range strings.Split(v.Rule, "|") { ruleItem = strings.TrimSpace(strings.Split(ruleItem, ":")[0]) if err, ok := errorItemMap[ruleItem]; ok { errs = append(errs, err) diff --git a/util/gvalid/gvalid_validator.go b/util/gvalid/gvalid_validator.go index e46c70295..33ea9dfd0 100644 --- a/util/gvalid/gvalid_validator.go +++ b/util/gvalid/gvalid_validator.go @@ -22,6 +22,7 @@ type Validator struct { messages interface{} // Custom validation error messages, which can be string or type of CustomMsg. ruleFuncMap map[string]RuleFunc // ruleFuncMap stores custom rule functions for current Validator. useDataInsteadOfObjectAttributes bool // Using `data` as its validation source instead of attribute values from `Object`. + bail bool // Stop validation after the first validation error. } // New creates and returns a new Validator. @@ -54,6 +55,13 @@ func (v *Validator) Ctx(ctx context.Context) *Validator { return newValidator } +// Bail sets the mark for stopping validation after the first validation error. +func (v *Validator) Bail() *Validator { + newValidator := v.Clone() + newValidator.bail = true + return newValidator +} + // Data is a chaining operation function, which sets validation data for current operation. // The parameter `data` usually be type of map, which specifies the parameter map used in validation. // Calling this function also sets `useDataInsteadOfObjectAttributes` true no mather the `data` is nil or not. diff --git a/util/gvalid/gvalid_validator_check_map.go b/util/gvalid/gvalid_validator_check_map.go index d48b413e3..ccbc66bc9 100644 --- a/util/gvalid/gvalid_validator_check_map.go +++ b/util/gvalid/gvalid_validator_check_map.go @@ -24,16 +24,15 @@ func (v *Validator) doCheckMap(params interface{}) Error { return nil } var ( - checkRules = make(map[string]string) - customMsgs = make(CustomMsg) - errorRules = make([]string, 0) - errorMaps = make(map[string]map[string]string) + checkRules = make([]fieldRule, 0) + customMessage = make(CustomMsg) // map[RuleKey]ErrorMsg. + errorMaps = make(map[string]map[string]string) ) - switch v := v.rules.(type) { + switch assertValue := v.rules.(type) { // Sequence tag: []sequence tag // Sequence has order for error results. case []string: - for _, tag := range v { + for _, tag := range assertValue { name, rule, msg := parseSequenceTag(tag) if len(name) == 0 { continue @@ -43,7 +42,7 @@ func (v *Validator) doCheckMap(params interface{}) Error { msgArray = strings.Split(msg, "|") ruleArray = strings.Split(rule, "|") ) - for k, v := range ruleArray { + for k, ruleItem := range ruleArray { // If length of custom messages is lesser than length of rules, // the rest rules use the default error messages. if len(msgArray) <= k { @@ -52,20 +51,27 @@ func (v *Validator) doCheckMap(params interface{}) Error { if len(msgArray[k]) == 0 { continue } - array := strings.Split(v, ":") - if _, ok := customMsgs[name]; !ok { - customMsgs[name] = make(map[string]string) + array := strings.Split(ruleItem, ":") + if _, ok := customMessage[name]; !ok { + customMessage[name] = make(map[string]string) } - customMsgs[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k]) + customMessage[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k]) } } - checkRules[name] = rule - errorRules = append(errorRules, name+"@"+rule) + checkRules = append(checkRules, fieldRule{ + Name: name, + Rule: rule, + }) } // No sequence rules: map[field]rule case map[string]string: - checkRules = v + for name, rule := range assertValue { + checkRules = append(checkRules, fieldRule{ + Name: name, + Rule: rule, + }) + } } // If there's no validation rules, it does nothing and returns quickly. if len(checkRules) == 0 { @@ -79,26 +85,35 @@ func (v *Validator) doCheckMap(params interface{}) Error { ) } if msg, ok := v.messages.(CustomMsg); ok && len(msg) > 0 { - if len(customMsgs) > 0 { + if len(customMessage) > 0 { for k, v := range msg { - customMsgs[k] = v + customMessage[k] = v } } else { - customMsgs = msg + customMessage = msg } } - var value interface{} - for key, rule := range checkRules { - if len(rule) == 0 { + var ( + value interface{} + ) + for _, checkRuleItem := range checkRules { + if len(checkRuleItem.Rule) == 0 { continue } value = nil - if v, ok := data[key]; ok { - value = v + if valueItem, ok := data[checkRuleItem.Name]; ok { + value = valueItem } // It checks each rule and its value in loop. - if e := v.doCheckValue(key, value, rule, customMsgs[key], params, data); e != nil { - _, item := e.FirstItem() + if validatedError := v.doCheckValue(doCheckValueInput{ + Name: checkRuleItem.Name, + Value: value, + Rule: checkRuleItem.Rule, + Messages: customMessage[checkRuleItem.Name], + DataRaw: params, + DataMap: data, + }); validatedError != nil { + _, errorItem := validatedError.FirstItem() // =========================================================== // Only in map and struct validations, if value is nil or empty // string and has no required* rules, it clears the error message. @@ -106,14 +121,14 @@ func (v *Validator) doCheckMap(params interface{}) Error { if gconv.String(value) == "" { required := false // rule => error - for k := range item { + for ruleKey := range errorItem { // Default required rules. - if _, ok := mustCheckRulesEvenValueEmpty[k]; ok { + if _, ok := mustCheckRulesEvenValueEmpty[ruleKey]; ok { required = true break } // Custom rules are also required in default. - if f := v.getRuleFunc(k); f != nil { + if f := v.getRuleFunc(ruleKey); f != nil { required = true break } @@ -122,16 +137,19 @@ func (v *Validator) doCheckMap(params interface{}) Error { continue } } - if _, ok := errorMaps[key]; !ok { - errorMaps[key] = make(map[string]string) + if _, ok := errorMaps[checkRuleItem.Name]; !ok { + errorMaps[checkRuleItem.Name] = make(map[string]string) } - for k, v := range item { - errorMaps[key][k] = v + for ruleKey, errorItemMsgMap := range errorItem { + errorMaps[checkRuleItem.Name][ruleKey] = errorItemMsgMap + } + if v.bail { + break } } } if len(errorMaps) > 0 { - return newError(gerror.CodeValidationFailed, errorRules, errorMaps) + return newError(gerror.CodeValidationFailed, checkRules, errorMaps) } return nil } diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 50822e89e..6e5e1a568 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -62,20 +62,20 @@ func (v *Validator) doCheckStruct(object interface{}) Error { } var ( - inputParamMap map[string]interface{} - checkRuleStrMap = make(map[string]string) // Complete rules map of struct: map[name]rule, the rule is complete pattern like: Name@RuleStr#Message - customMessage = make(CustomMsg) // Custom rule error message map. - checkValueData = v.data // Ready to be validated data, which can be type of . - errorRules = make([]string, 0) // Sequence rules. + inputParamMap map[string]interface{} + checkRules = make([]fieldRule, 0) + nameToRuleMap = make(map[string]string) // just for internally searching index purpose. + customMessage = make(CustomMsg) // Custom rule error message map. + checkValueData = v.data // Ready to be validated data, which can be type of . ) if checkValueData == nil { checkValueData = object } - switch v := v.rules.(type) { + switch assertValue := v.rules.(type) { // Sequence tag: []sequence tag // Sequence has order for error results. case []string: - for _, tag := range v { + for _, tag := range assertValue { name, rule, msg := parseSequenceTag(tag) if len(name) == 0 { continue @@ -101,17 +101,26 @@ func (v *Validator) doCheckStruct(object interface{}) Error { customMessage[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k]) } } - checkRuleStrMap[name] = rule - errorRules = append(errorRules, name+"@"+rule) + nameToRuleMap[name] = rule + checkRules = append(checkRules, fieldRule{ + Name: name, + Rule: rule, + }) } // Map type rules does not support sequence. // Format: map[key]rule case map[string]string: - checkRuleStrMap = v + nameToRuleMap = assertValue + for name, rule := range assertValue { + checkRules = append(checkRules, fieldRule{ + Name: name, + Rule: rule, + }) + } } // If there's no struct tag and validation rules, it does nothing and returns quickly. - if len(tagField) == 0 && len(checkRuleStrMap) == 0 { + if len(tagField) == 0 && len(checkRules) == 0 { return nil } // Input parameter map handling. @@ -129,6 +138,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error { } } } + // Merge the custom validation rules with rules in struct tag. // The custom rules has the most high priority that can overwrite the struct tag rules. for _, field := range tagField { @@ -161,20 +171,32 @@ func (v *Validator) doCheckStruct(object interface{}) Error { } } } - if _, ok := checkRuleStrMap[name]; !ok { - if _, ok := checkRuleStrMap[fieldName]; ok { + + if _, ok := nameToRuleMap[name]; !ok { + if _, ok := nameToRuleMap[fieldName]; ok { // If there's alias name, // use alias name as its key and remove the field name key. - checkRuleStrMap[name] = checkRuleStrMap[fieldName] - delete(checkRuleStrMap, fieldName) + nameToRuleMap[name] = nameToRuleMap[fieldName] + delete(nameToRuleMap, fieldName) + for index, checkRuleItem := range checkRules { + if fieldName == checkRuleItem.Name { + checkRuleItem.Name = name + checkRules[index] = checkRuleItem + break + } + } } else { - checkRuleStrMap[name] = rule + nameToRuleMap[name] = rule + checkRules = append(checkRules, fieldRule{ + Name: name, + Rule: rule, + }) } - errorRules = append(errorRules, name+"@"+rule) } else { // The input rules can overwrite the rules in struct tag. continue } + if len(msg) > 0 { var ( msgArray = strings.Split(msg, "|") @@ -213,14 +235,20 @@ func (v *Validator) doCheckStruct(object interface{}) Error { // The following logic is the same as some of CheckMap but with sequence support. var ( - value interface{} - hasBailRule bool + value interface{} ) - for key, rule := range checkRuleStrMap { - _, value = gutil.MapPossibleItemByKey(inputParamMap, key) + for _, checkRuleItem := range checkRules { + _, value = gutil.MapPossibleItemByKey(inputParamMap, checkRuleItem.Name) // It checks each rule and its value in loop. - if validatedError := v.doCheckValue(key, value, rule, customMessage[key], checkValueData, inputParamMap); validatedError != nil { - _, item := validatedError.FirstItem() + if validatedError := v.doCheckValue(doCheckValueInput{ + Name: checkRuleItem.Name, + Value: value, + Rule: checkRuleItem.Rule, + Messages: customMessage[checkRuleItem.Name], + DataRaw: checkValueData, + DataMap: inputParamMap, + }); validatedError != nil { + _, errorItem := validatedError.FirstItem() // =================================================================== // Only in map and struct validations, if value is nil or empty string // and has no required* rules, it clears the error message. @@ -228,14 +256,14 @@ func (v *Validator) doCheckStruct(object interface{}) Error { if value == nil || gconv.String(value) == "" { required := false // rule => error - for k := range item { + for ruleKey := range errorItem { // Default required rules. - if _, ok := mustCheckRulesEvenValueEmpty[k]; ok { + if _, ok := mustCheckRulesEvenValueEmpty[ruleKey]; ok { required = true break } // Custom rules are also required in default. - if f := v.getRuleFunc(k); f != nil { + if f := v.getRuleFunc(ruleKey); f != nil { required = true break } @@ -244,19 +272,19 @@ func (v *Validator) doCheckStruct(object interface{}) Error { continue } } - if _, ok := errorMaps[key]; !ok { - errorMaps[key] = make(map[string]string) + if _, ok := errorMaps[checkRuleItem.Name]; !ok { + errorMaps[checkRuleItem.Name] = make(map[string]string) } - for k, v := range item { - errorMaps[key][k] = v + for ruleKey, errorItemMsgMap := range errorItem { + errorMaps[checkRuleItem.Name][ruleKey] = errorItemMsgMap } - if hasBailRule { + if v.bail { break } } } if len(errorMaps) > 0 { - return newError(gerror.CodeValidationFailed, errorRules, errorMaps) + return newError(gerror.CodeValidationFailed, checkRules, errorMaps) } return nil } diff --git a/util/gvalid/gvalid_validator_check_value.go b/util/gvalid/gvalid_validator_check_value.go index ff5c507f0..6d8d99684 100644 --- a/util/gvalid/gvalid_validator_check_value.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -29,27 +29,29 @@ type apiTime interface { // CheckValue checks single value with specified rules. // It returns nil if successful validation. func (v *Validator) CheckValue(value interface{}) Error { - return v.doCheckValue("", value, gconv.String(v.rules), v.messages, v.data, gconv.Map(v.data)) + return v.doCheckValue(doCheckValueInput{ + Name: "", + Value: value, + Rule: gconv.String(v.rules), + Messages: v.messages, + DataRaw: v.data, + DataMap: gconv.Map(v.data), + }) +} + +type doCheckValueInput struct { + Name string // Name specifies the name of parameter `value`. + Value interface{} // Value specifies the value for this rules to be validated. + Rule string // Rule specifies the validation rules string, like "required", "required|between:1,100", etc. + Messages interface{} // Messages specifies the custom error messages for this rule, which is usually type of map/slice. + DataRaw interface{} // DataRaw specifies the `raw data` which is passed to the Validator. It might be type of map/struct or a nil value. + DataMap map[string]interface{} // DataMap specifies the map that is converted from `dataRaw`. It is usually used internally } // doCheckSingleValue does the really rules validation for single key-value. -// -// The parameter `name` specifies the name of parameter `value`. -// The parameter `value` specifies the value for this rules to be validated. -// The parameter `rules` specifies the validation rules string, like "required", "required|between:1,100", etc. -// The parameter `messages` specifies the custom error messages for this rule, which is usually type of map/slice. -// The parameter `dataRaw` specifies the `raw data` which is passed to the Validator. It might be type of map/struct or a nil value. -// The parameter `dataMap` specifies the map that is converted from `dataRaw`. It is usually used internally -func (v *Validator) doCheckValue( - name string, - value interface{}, - rules string, - messages interface{}, - dataRaw interface{}, - dataMap map[string]interface{}, -) Error { +func (v *Validator) doCheckValue(input doCheckValueInput) Error { // If there's no validation rules, it does nothing and returns quickly. - if rules == "" { + if input.Rule == "" { return nil } // It converts value to string and then does the validation. @@ -62,17 +64,17 @@ func (v *Validator) doCheckValue( msgArray = make([]string, 0) customMsgMap = make(map[string]string) ) - switch v := messages.(type) { + switch v := input.Messages.(type) { case string: msgArray = strings.Split(v, "|") default: - for k, v := range gconv.Map(messages) { + for k, v := range gconv.Map(input.Messages) { customMsgMap[k] = gconv.String(v) } } // Handle the char '|' in the rule, // which makes this rule separated into multiple rules. - ruleItems := strings.Split(strings.TrimSpace(rules), "|") + ruleItems := strings.Split(strings.TrimSpace(input.Rule), "|") for i := 0; ; { array := strings.Split(ruleItems[i], ":") _, ok := allSupportedRules[array[0]] @@ -83,7 +85,7 @@ func (v *Validator) doCheckValue( } else { return newErrorStr( internalRulesErrRuleName, - internalRulesErrRuleName+": "+rules, + internalRulesErrRuleName+": "+input.Rule, ) } } else { @@ -128,7 +130,7 @@ func (v *Validator) doCheckValue( if customRuleFunc != nil { // It checks custom validation rules with most priority. message := v.getErrorMessageByRule(ruleKey, customMsgMap) - if err := customRuleFunc(v.ctx, ruleItems[index], value, message, dataRaw); err != nil { + if err := customRuleFunc(v.ctx, ruleItems[index], input.Value, message, input.DataRaw); err != nil { match = false errorMsgArray[ruleKey] = err.Error() } else { @@ -136,7 +138,15 @@ func (v *Validator) doCheckValue( } } else { // It checks build-in validation rules if there's no custom rule. - match, err = v.doCheckBuildInRules(index, value, ruleKey, rulePattern, ruleItems, dataMap, customMsgMap) + match, err = v.doCheckBuildInRules(doCheckBuildInRulesInput{ + Index: index, + Value: input.Value, + RuleKey: ruleKey, + RulePattern: rulePattern, + RuleItems: ruleItems, + DataMap: input.DataMap, + CustomMsgMap: customMsgMap, + }) if !match && err != nil { errorMsgArray[ruleKey] = err.Error() } @@ -158,24 +168,26 @@ func (v *Validator) doCheckValue( index++ } if len(errorMsgArray) > 0 { - return newError(gerror.CodeValidationFailed, []string{rules}, map[string]map[string]string{ - name: errorMsgArray, + return newError(gerror.CodeValidationFailed, []fieldRule{{Name: input.Name, Rule: input.Rule}}, map[string]map[string]string{ + input.Name: errorMsgArray, }) } return nil } -func (v *Validator) doCheckBuildInRules( - index int, - value interface{}, - ruleKey string, - rulePattern string, - ruleItems []string, - dataMap map[string]interface{}, - customMsgMap map[string]string, -) (match bool, err error) { - valueStr := gconv.String(value) - switch ruleKey { +type doCheckBuildInRulesInput struct { + Index int + Value interface{} + RuleKey string + RulePattern string + RuleItems []string + DataMap map[string]interface{} + CustomMsgMap map[string]string +} + +func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match bool, err error) { + valueStr := gconv.String(input.Value) + switch input.RuleKey { // Required rules. case "required", @@ -185,7 +197,7 @@ func (v *Validator) doCheckBuildInRules( "required-with-all", "required-without", "required-without-all": - match = v.checkRequired(value, ruleKey, rulePattern, dataMap) + match = v.checkRequired(input.Value, input.RuleKey, input.RulePattern, input.DataMap) // Length rules. // It also supports length of unicode string. @@ -194,7 +206,7 @@ func (v *Validator) doCheckBuildInRules( "min-length", "max-length", "size": - if msg := v.checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { + if msg := v.checkLength(valueStr, input.RuleKey, input.RulePattern, input.CustomMsgMap); msg != "" { return match, gerror.NewOption(gerror.Option{ Text: msg, Code: gerror.CodeValidationFailed, @@ -208,7 +220,7 @@ func (v *Validator) doCheckBuildInRules( "min", "max", "between": - if msg := v.checkRange(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" { + if msg := v.checkRange(valueStr, input.RuleKey, input.RulePattern, input.CustomMsgMap); msg != "" { return match, gerror.NewOption(gerror.Option{ Text: msg, Code: gerror.CodeValidationFailed, @@ -220,18 +232,18 @@ func (v *Validator) doCheckBuildInRules( // Custom regular expression. case "regex": // It here should check the rule as there might be special char '|' in it. - for i := index + 1; i < len(ruleItems); i++ { - if !gregex.IsMatchString(singleRulePattern, ruleItems[i]) { - rulePattern += "|" + ruleItems[i] - index++ + for i := input.Index + 1; i < len(input.RuleItems); i++ { + if !gregex.IsMatchString(singleRulePattern, input.RuleItems[i]) { + input.RulePattern += "|" + input.RuleItems[i] + input.Index++ } } - match = gregex.IsMatchString(rulePattern, valueStr) + match = gregex.IsMatchString(input.RulePattern, valueStr) // Date rules. case "date": // support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. - if v, ok := value.(apiTime); ok { + if v, ok := input.Value.(apiTime); ok { return !v.IsZero(), nil } match = gregex.IsMatchString(`\d{4}[\.\-\_/]{0,1}\d{2}[\.\-\_/]{0,1}\d{2}`, valueStr) @@ -239,15 +251,15 @@ func (v *Validator) doCheckBuildInRules( // Date rule with specified format. case "date-format": // support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. - if v, ok := value.(apiTime); ok { + if v, ok := input.Value.(apiTime); ok { return !v.IsZero(), nil } - if _, err := gtime.StrToTimeFormat(valueStr, rulePattern); err == nil { + if _, err := gtime.StrToTimeFormat(valueStr, input.RulePattern); err == nil { match = true } else { var msg string - msg = v.getErrorMessageByRule(ruleKey, customMsgMap) - msg = strings.Replace(msg, ":format", rulePattern, -1) + msg = v.getErrorMessageByRule(input.RuleKey, input.CustomMsgMap) + msg = strings.Replace(msg, ":format", input.RulePattern, -1) return match, gerror.NewOption(gerror.Option{ Text: msg, Code: gerror.CodeValidationFailed, @@ -256,7 +268,7 @@ func (v *Validator) doCheckBuildInRules( // Values of two fields should be equal as string. case "same": - _, foundValue := gutil.MapPossibleItemByKey(dataMap, rulePattern) + _, foundValue := gutil.MapPossibleItemByKey(input.DataMap, input.RulePattern) if foundValue != nil { if strings.Compare(valueStr, gconv.String(foundValue)) == 0 { match = true @@ -264,8 +276,8 @@ func (v *Validator) doCheckBuildInRules( } if !match { var msg string - msg = v.getErrorMessageByRule(ruleKey, customMsgMap) - msg = strings.Replace(msg, ":field", rulePattern, -1) + msg = v.getErrorMessageByRule(input.RuleKey, input.CustomMsgMap) + msg = strings.Replace(msg, ":field", input.RulePattern, -1) return match, gerror.NewOption(gerror.Option{ Text: msg, Code: gerror.CodeValidationFailed, @@ -275,7 +287,7 @@ func (v *Validator) doCheckBuildInRules( // Values of two fields should not be equal as string. case "different": match = true - _, foundValue := gutil.MapPossibleItemByKey(dataMap, rulePattern) + _, foundValue := gutil.MapPossibleItemByKey(input.DataMap, input.RulePattern) if foundValue != nil { if strings.Compare(valueStr, gconv.String(foundValue)) == 0 { match = false @@ -283,8 +295,8 @@ func (v *Validator) doCheckBuildInRules( } if !match { var msg string - msg = v.getErrorMessageByRule(ruleKey, customMsgMap) - msg = strings.Replace(msg, ":field", rulePattern, -1) + msg = v.getErrorMessageByRule(input.RuleKey, input.CustomMsgMap) + msg = strings.Replace(msg, ":field", input.RulePattern, -1) return match, gerror.NewOption(gerror.Option{ Text: msg, Code: gerror.CodeValidationFailed, @@ -293,7 +305,7 @@ func (v *Validator) doCheckBuildInRules( // Field value should be in range of. case "in": - array := strings.Split(rulePattern, ",") + array := strings.Split(input.RulePattern, ",") for _, v := range array { if strings.Compare(valueStr, strings.TrimSpace(v)) == 0 { match = true @@ -304,7 +316,7 @@ func (v *Validator) doCheckBuildInRules( // Field value should not be in range of. case "not-in": match = true - array := strings.Split(rulePattern, ",") + array := strings.Split(input.RulePattern, ",") for _, v := range array { if strings.Compare(valueStr, strings.TrimSpace(v)) == 0 { match = false @@ -472,7 +484,7 @@ func (v *Validator) doCheckBuildInRules( default: return match, gerror.NewOption(gerror.Option{ - Text: "Invalid rule name: " + ruleKey, + Text: "Invalid rule name: " + input.RuleKey, Code: gerror.CodeInvalidParameter, }) } diff --git a/util/gvalid/gvalid_z_unit_checkmap_test.go b/util/gvalid/gvalid_z_unit_checkmap_test.go index dbb693e24..7750e923b 100755 --- a/util/gvalid/gvalid_z_unit_checkmap_test.go +++ b/util/gvalid/gvalid_z_unit_checkmap_test.go @@ -9,6 +9,7 @@ package gvalid_test import ( "context" "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/frame/g" "testing" "github.com/gogf/gf/test/gtest" @@ -210,3 +211,38 @@ func Test_Sequence(t *testing.T) { t.Assert(gerror.Current(err), "账号不能为空") }) } + +func Test_Map_Bail(t *testing.T) { + // global bail + gtest.C(t, func(t *gtest.T) { + params := map[string]interface{}{ + "passport": "", + "password": "123456", + "password2": "1234567", + } + rules := []string{ + "passport@required|length:6,16#账号不能为空|账号长度应当在:min到:max之间", + "password@required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等", + "password2@required|length:6,16#", + } + err := g.Validator().Bail().Rules(rules).CheckMap(params) + t.AssertNE(err, nil) + t.Assert(err.String(), "账号不能为空; 账号长度应当在6到16之间") + }) + // global bail with rule bail + gtest.C(t, func(t *gtest.T) { + params := map[string]interface{}{ + "passport": "", + "password": "123456", + "password2": "1234567", + } + rules := []string{ + "passport@bail|required|length:6,16#|账号不能为空|账号长度应当在:min到:max之间", + "password@required|length:6,16|same:password2#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等", + "password2@required|length:6,16#", + } + err := g.Validator().Bail().Rules(rules).CheckMap(params) + t.AssertNE(err, nil) + t.Assert(err.String(), "账号不能为空") + }) +} From fddc21670a82643d72ecaf0ca8552cd8674fd05f Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 2 Aug 2021 00:38:56 +0800 Subject: [PATCH 414/492] add condition and order-by feature for with tag feature for package gdb --- database/gdb/gdb_func.go | 12 +- database/gdb/gdb_model_with.go | 95 ++++-- .../gdb/gdb_z_mysql_association_with_test.go | 279 +++++++++--------- 3 files changed, 208 insertions(+), 178 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index d88c81aa1..0919432d5 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -55,11 +55,13 @@ type apiTableName interface { } const ( - OrmTagForStruct = "orm" - OrmTagForUnique = "unique" - OrmTagForPrimary = "primary" - OrmTagForTable = "table" - OrmTagForWith = "with" + OrmTagForStruct = "orm" + OrmTagForUnique = "unique" + OrmTagForPrimary = "primary" + OrmTagForTable = "table" + OrmTagForWith = "with" + OrmTagForWithWhere = "where" + OrmTagForWithOrder = "order" ) var ( diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index e9fff363c..f2bae7b1e 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -88,28 +88,20 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { } for _, field := range fieldMap { var ( - withTag string - ormTag = field.Tag(OrmTagForStruct) - fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") - match, _ = gregex.MatchString( - fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith), - ormTag, - ) + fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") + parsedTagOutput = m.parseWithTagInFieldStruct(field) ) - if len(match) > 1 { - withTag = match[1] - } - if withTag == "" { + if parsedTagOutput.With == "" { continue } if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) { continue } - array := gstr.SplitAndTrim(withTag, "=") + array := gstr.SplitAndTrim(parsedTagOutput.With, "=") if len(array) == 1 { // It supports using only one column name // if both tables associates using the same column name. - array = append(array, withTag) + array = append(array, parsedTagOutput.With) } var ( model *Model @@ -129,7 +121,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { return gerror.NewCodef( gerror.CodeInvalidParameter, `cannot find the related value for attribute name "%s" of with tag "%s"`, - relatedAttrName, withTag, + relatedAttrName, parsedTagOutput.With, ) } bindToReflectValue := field.Value @@ -154,6 +146,12 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { } else { model = model.With(m.withArray...) } + if parsedTagOutput.Where != "" { + model = model.Where(parsedTagOutput.Where) + } + if parsedTagOutput.Order != "" { + model = model.Order(parsedTagOutput.Order) + } err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).Scan(bindToReflectValue) if err != nil { @@ -201,28 +199,20 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { for fieldName, field := range fieldMap { var ( - withTag string - ormTag = field.Tag(OrmTagForStruct) - fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") - match, _ = gregex.MatchString( - fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith), - ormTag, - ) + fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") + parsedTagOutput = m.parseWithTagInFieldStruct(field) ) - if len(match) > 1 { - withTag = match[1] - } - if withTag == "" { + if parsedTagOutput.With == "" { continue } if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) { continue } - array := gstr.SplitAndTrim(withTag, "=") + array := gstr.SplitAndTrim(parsedTagOutput.With, "=") if len(array) == 1 { // It supports using only one column name // if both tables associates using the same column name. - array = append(array, withTag) + array = append(array, parsedTagOutput.With) } var ( model *Model @@ -242,7 +232,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { return gerror.NewCodef( gerror.CodeInvalidParameter, `cannot find the related value for attribute name "%s" of with tag "%s"`, - relatedAttrName, withTag, + relatedAttrName, parsedTagOutput.With, ) } @@ -260,11 +250,58 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { } else { model = model.With(m.withArray...) } + if parsedTagOutput.Where != "" { + model = model.Where(parsedTagOutput.Where) + } + if parsedTagOutput.Order != "" { + model = model.Order(parsedTagOutput.Order) + } - err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).ScanList(pointer, fieldName, withTag) + err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).ScanList(pointer, fieldName, parsedTagOutput.With) if err != nil { return err } } return nil } + +type parseWithTagInFieldStructOutput struct { + With string + Where string + Order string +} + +func (m *Model) parseWithTagInFieldStruct(field *structs.Field) (output parseWithTagInFieldStructOutput) { + var ( + match []string + ormTag = field.Tag(OrmTagForStruct) + ) + // with tag. + match, _ = gregex.MatchString( + fmt.Sprintf(`%s\s*:\s*([^,]+),{0,1}`, OrmTagForWith), + ormTag, + ) + if len(match) > 1 { + output.With = match[1] + } + if len(match) > 2 { + output.Where = gstr.Trim(match[2]) + } + // where string. + match, _ = gregex.MatchString( + fmt.Sprintf(`%s\s*:.+,\s*%s:\s*([^,]+),{0,1}`, OrmTagForWith, OrmTagForWithWhere), + ormTag, + ) + if len(match) > 1 { + output.Where = gstr.Trim(match[1]) + } + // order string. + match, _ = gregex.MatchString( + fmt.Sprintf(`%s\s*:.+,\s*%s:\s*([^,]+),{0,1}`, OrmTagForWith, OrmTagForWithOrder), + ormTag, + ) + if len(match) > 1 { + output.Order = gstr.Trim(match[1]) + } + return +} diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index 3ea1c19f1..5a102fa12 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -738,150 +738,141 @@ PRIMARY KEY (id) }) } -//func Test_Table_Relation_WithAllCondition_List(t *testing.T) { -// var ( -// tableUser = "user" -// tableUserDetail = "user_detail" -// tableUserScores = "user_scores" -// ) -// if _, err := db.Exec(fmt.Sprintf(` -//CREATE TABLE IF NOT EXISTS %s ( -//id int(10) unsigned NOT NULL AUTO_INCREMENT, -//name varchar(45) NOT NULL, -//PRIMARY KEY (id) -//) ENGINE=InnoDB DEFAULT CHARSET=utf8; -// `, tableUser)); err != nil { -// gtest.Error(err) -// } -// defer dropTable(tableUser) -// -// if _, err := db.Exec(fmt.Sprintf(` -//CREATE TABLE IF NOT EXISTS %s ( -//uid int(10) unsigned NOT NULL AUTO_INCREMENT, -//address varchar(45) NOT NULL, -//PRIMARY KEY (uid) -//) ENGINE=InnoDB DEFAULT CHARSET=utf8; -// `, tableUserDetail)); err != nil { -// gtest.Error(err) -// } -// defer dropTable(tableUserDetail) -// -// if _, err := db.Exec(fmt.Sprintf(` -//CREATE TABLE IF NOT EXISTS %s ( -//id int(10) unsigned NOT NULL AUTO_INCREMENT, -//uid int(10) unsigned NOT NULL, -//score int(10) unsigned NOT NULL, -//PRIMARY KEY (id) -//) ENGINE=InnoDB DEFAULT CHARSET=utf8; -// `, tableUserScores)); err != nil { -// gtest.Error(err) -// } -// defer dropTable(tableUserScores) -// -// type UserDetail struct { -// gmeta.Meta `orm:"table:user_detail"` -// Uid int `json:"uid"` -// Address string `json:"address"` -// } -// -// type UserScores struct { -// gmeta.Meta `orm:"table:user_scores"` -// Id int `json:"id"` -// Uid int `json:"uid"` -// Score int `json:"score"` -// } -// -// type User struct { -// gmeta.Meta `orm:"table:user"` -// Id int `json:"id"` -// Name string `json:"name"` -// UserDetail *UserDetail `orm:"with:uid=id"` -// UserScores []*UserScores `orm:"with:uid=id, score>1 and score<5"` -// } -// -// // Initialize the data. -// var err error -// for i := 1; i <= 5; i++ { -// // User. -// _, err = db.Insert(tableUser, g.Map{ -// "id": i, -// "name": fmt.Sprintf(`name_%d`, i), -// }) -// gtest.Assert(err, nil) -// // Detail. -// _, err = db.Insert(tableUserDetail, g.Map{ -// "uid": i, -// "address": fmt.Sprintf(`address_%d`, i), -// }) -// gtest.Assert(err, nil) -// // Scores. -// for j := 1; j <= 5; j++ { -// _, err = db.Insert(tableUserScores, g.Map{ -// "uid": i, -// "score": j, -// }) -// gtest.Assert(err, nil) -// } -// } -// -// db.SetDebug(true) -// defer db.SetDebug(false) -// -// gtest.C(t, func(t *gtest.T) { -// var users []*User -// err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users) -// t.AssertNil(err) -// t.Assert(len(users), 2) -// t.Assert(users[0].Id, 3) -// t.Assert(users[0].Name, "name_3") -// t.AssertNE(users[0].UserDetail, nil) -// t.Assert(users[0].UserDetail.Uid, 3) -// t.Assert(users[0].UserDetail.Address, "address_3") -// t.Assert(len(users[0].UserScores), 5) -// t.Assert(users[0].UserScores[0].Uid, 3) -// t.Assert(users[0].UserScores[0].Score, 1) -// t.Assert(users[0].UserScores[4].Uid, 3) -// t.Assert(users[0].UserScores[4].Score, 5) -// -// t.Assert(users[1].Id, 4) -// t.Assert(users[1].Name, "name_4") -// t.AssertNE(users[1].UserDetail, nil) -// t.Assert(users[1].UserDetail.Uid, 4) -// t.Assert(users[1].UserDetail.Address, "address_4") -// t.Assert(len(users[1].UserScores), 5) -// t.Assert(users[1].UserScores[0].Uid, 4) -// t.Assert(users[1].UserScores[0].Score, 1) -// t.Assert(users[1].UserScores[4].Uid, 4) -// t.Assert(users[1].UserScores[4].Score, 5) -// }) -// gtest.C(t, func(t *gtest.T) { -// var users []User -// err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users) -// t.AssertNil(err) -// t.Assert(len(users), 2) -// t.Assert(users[0].Id, 3) -// t.Assert(users[0].Name, "name_3") -// t.AssertNE(users[0].UserDetail, nil) -// t.Assert(users[0].UserDetail.Uid, 3) -// t.Assert(users[0].UserDetail.Address, "address_3") -// t.Assert(len(users[0].UserScores), 5) -// t.Assert(users[0].UserScores[0].Uid, 3) -// t.Assert(users[0].UserScores[0].Score, 1) -// t.Assert(users[0].UserScores[4].Uid, 3) -// t.Assert(users[0].UserScores[4].Score, 5) -// -// t.Assert(users[1].Id, 4) -// t.Assert(users[1].Name, "name_4") -// t.AssertNE(users[1].UserDetail, nil) -// t.Assert(users[1].UserDetail.Uid, 4) -// t.Assert(users[1].UserDetail.Address, "address_4") -// t.Assert(len(users[1].UserScores), 5) -// t.Assert(users[1].UserScores[0].Uid, 4) -// t.Assert(users[1].UserScores[0].Score, 1) -// t.Assert(users[1].UserScores[4].Uid, 4) -// t.Assert(users[1].UserScores[4].Score, 5) -// }) -//} +func Test_Table_Relation_WithAllCondition_List(t *testing.T) { + var ( + tableUser = "user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +uid int(10) unsigned NOT NULL AUTO_INCREMENT, +address varchar(45) NOT NULL, +PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +uid int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:user"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id, where:uid > 3"` + UserScores []*UserScores `orm:"with:uid=id, where:score>1 and score<5, order:score desc"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + + db.SetDebug(true) + defer db.SetDebug(false) + + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.Assert(users[0].UserDetail, nil) + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.AssertNE(users[1].UserDetail, nil) + t.Assert(users[1].UserDetail.Uid, 4) + t.Assert(users[1].UserDetail.Address, "address_4") + t.Assert(len(users[1].UserScores), 3) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 4) + t.Assert(users[1].UserScores[2].Uid, 4) + t.Assert(users[1].UserScores[2].Score, 2) + }) + gtest.C(t, func(t *gtest.T) { + var users []User + err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.Assert(users[0].UserDetail, nil) + + t.Assert(len(users[0].UserScores), 3) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 4) + t.Assert(users[0].UserScores[2].Uid, 3) + t.Assert(users[0].UserScores[2].Score, 2) + + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.AssertNE(users[1].UserDetail, nil) + t.Assert(users[1].UserDetail.Uid, 4) + t.Assert(users[1].UserDetail.Address, "address_4") + t.Assert(len(users[1].UserScores), 3) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 4) + t.Assert(users[1].UserScores[2].Uid, 4) + t.Assert(users[1].UserScores[2].Score, 2) + }) +} func Test_Table_Relation_WithAll_Embedded(t *testing.T) { var ( From ff70e54e3ec022f2e11d5dc56e4f574aafc6eb1c Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 2 Aug 2021 09:50:44 +0800 Subject: [PATCH 415/492] fix unit testing case for package gtrace --- net/gtrace/gtrace_unit_carrier_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/gtrace/gtrace_unit_carrier_test.go b/net/gtrace/gtrace_unit_carrier_test.go index a070b2ab4..095250c92 100644 --- a/net/gtrace/gtrace_unit_carrier_test.go +++ b/net/gtrace/gtrace_unit_carrier_test.go @@ -54,7 +54,7 @@ func TestNewCarrier(t *testing.T) { ctx, _ = oteltest.DefaultTracer().Start(ctx, "inject") carrier1 := gtrace.NewCarrier() otel.GetTextMapPropagator().Inject(ctx, carrier1) - t.Assert(carrier1.String(), `{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000002-01","tracestate":""}`) + t.Assert(carrier1.String(), `{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000002-01"}`) ctx = otel.GetTextMapPropagator().Extract(ctx, carrier1) gotSc := trace.SpanContextFromContext(ctx) From 5a4de529008632aedb8c415c828f923a09ac9599 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 2 Aug 2021 19:58:04 +0800 Subject: [PATCH 416/492] fix invalid separator char in packing with prefix folder string in OS widnows for package gres --- net/ghttp/internal/client/client.go | 2 +- net/ghttp/internal/client/client_chain.go | 4 ++-- os/gres/gres_func_zip.go | 10 +++++----- os/gres/testdata/data/data.go | 2 +- os/gres/testdata/testdata.go | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/net/ghttp/internal/client/client.go b/net/ghttp/internal/client/client.go index 59513a847..2f635af85 100644 --- a/net/ghttp/internal/client/client.go +++ b/net/ghttp/internal/client/client.go @@ -154,7 +154,7 @@ func (c *Client) SetPrefix(prefix string) *Client { return c } -// SetTimeOut sets the request timeout for the client. +// SetTimeout sets the request timeout for the client. func (c *Client) SetTimeout(t time.Duration) *Client { c.Client.Timeout = t return c diff --git a/net/ghttp/internal/client/client_chain.go b/net/ghttp/internal/client/client_chain.go index 467d614f4..81c1f3af3 100644 --- a/net/ghttp/internal/client/client_chain.go +++ b/net/ghttp/internal/client/client_chain.go @@ -33,7 +33,7 @@ func (c *Client) Header(m map[string]string) *Client { return newClient } -// Header is a chaining function, +// HeaderRaw is a chaining function, // which sets custom HTTP header using raw string for next request. func (c *Client) HeaderRaw(headers string) *Client { newClient := c @@ -92,7 +92,7 @@ func (c *Client) ContentXml() *Client { return newClient } -// TimeOut is a chaining function, +// Timeout is a chaining function, // which sets the timeout for next request. func (c *Client) Timeout(t time.Duration) *Client { newClient := c diff --git a/os/gres/gres_func_zip.go b/os/gres/gres_func_zip.go index 706bd02bc..268ac87f0 100644 --- a/os/gres/gres_func_zip.go +++ b/os/gres/gres_func_zip.go @@ -61,14 +61,14 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix if len(prefix) > 0 && prefix[0] != "" { headerPrefix = prefix[0] } - headerPrefix = strings.TrimRight(headerPrefix, "\\/") + headerPrefix = strings.TrimRight(headerPrefix, `\/`) if len(headerPrefix) > 0 && gfile.IsDir(path) { headerPrefix += "/" } if headerPrefix == "" { headerPrefix = gfile.Basename(path) } - headerPrefix = strings.Replace(headerPrefix, "//", "/", -1) + headerPrefix = strings.Replace(headerPrefix, `//`, `/`, -1) for _, file := range files { if exclude == file { intlog.Printf(context.TODO(), `exclude file path: %s`, file) @@ -84,14 +84,14 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix var name string path = headerPrefix for { - name = gfile.Basename(path) + name = strings.Replace(gfile.Basename(path), `\`, `/`, -1) err = zipFileVirtual( fileinfo.New(name, 0, os.ModeDir|os.ModePerm, time.Now()), path, zipWriter, ) if err != nil { return err } - if path == "/" || !strings.Contains(path, "/") { + if path == `/` || !strings.Contains(path, `/`) { break } path = gfile.Dir(path) @@ -103,7 +103,7 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix // zipFile compresses the file of given <path> and writes the content to <zw>. // The parameter <prefix> indicates the path prefix for zip file. func zipFile(path string, prefix string, zw *zip.Writer) error { - prefix = strings.Replace(prefix, "//", "/", -1) + prefix = strings.Replace(prefix, `//`, `/`, -1) file, err := os.Open(path) if err != nil { return nil diff --git a/os/gres/testdata/data/data.go b/os/gres/testdata/data/data.go index ff96670de..53bc0f480 100644 --- a/os/gres/testdata/data/data.go +++ b/os/gres/testdata/data/data.go @@ -3,7 +3,7 @@ package data import "github.com/gogf/gf/os/gres" func init() { - if err := gres.Add("H4sIAAAAAAAC/7RaeTiU6/9+sq8hSyohYpAxtkiyFLKMfYtSGgxxGGHsLShlSUqh0nZQpGObiCMplX3LlrQoCpF9RIR+V4T3HWPJ+X27rvjHc9/3536f7f28tyGakooD0AE6MIh1NASQf+sAPbB1xdk7OqBmfkngXV2czUypwZqOnfaHxCp0kSgt8TLDDDPDuprSGr1GFArVhNKuQpZqi2sjzdvCJHT0ysTPtxsbGhpqlYlXm4N7srLSTTIvZV7KZEnLyOU0SBlSoBEIYYdEm4typhRUAPz8aYimpbv+7IKdOQDAHgCwuDzmeXkuvhKOOMf/V2XG88rM55V9D1RTJFUGwDmZB8ZQZTRzyqYl0WckHv41GPoni5e1YW4w0tbTA+/q8j8z32i+RLP5Ek3V5d4ubz7HApX/g2dgMi9w37xASdr208s/A2ZSgcs+CgAuu92EFUkH6IGdo7sUysPTZnp487uUQyt/klyQ4b/+I/FYD7yUBN4HP++ShF4dUhspIWWqVV2zTbtq25q5MgseXNq5HgDAtiQHwyzHNPY8LnnMEEqVKfZpc5ayjuo35uodk/5vjknDHJMm75jxgur8I3kpV+yY9LRj0nDHFmLqqRBKVuyY9CqWOxOgB45SO3AoLA6+wtvKtarKyvZlmBkOAanLueXEcHdj1YmjrGr0rLPibL2RqnwAAP6VMThhFmPwe7djzK3+c0nEfT55iWPvcg+F3t/1RARBvZVoV5LVaeJ/GayZ5TRy4PeSAgBsWxmnu+dinHmSP22eB29eL3fwtV+m9m9C8fzOZ+fDmXUCwFyNO/L+QgIARJbkY5nl8zuCVNOHU85XSfAurL/jHz3j5VMKxbtKt94kUNHNUnW1lEcIAQC2rJjKdN//IxX5afWLahULkfH3UKSdozsKi1vlWoQgoI5gnZ1dl5qi83PE/auTLQcAgPWPCLxd3Z3tSAjEalDl2kYZZoYMtFCCG2yeCFKCZTxwwvxXD5wwvz3wxSzwIMd44RqaX6f59mKW/NNH+5+QzfgBI5u2I9PY0I9g+4Z4TwmyTG7utnbmmp6dK3bE3XMVjnDCEX47Aj34F1ng8244H9sisnm63j8hmnEDdsOYnRt5GaSbBvZr76P1y86PtVCK6Y1jFYZsWADy2xMnD1fcnNaUjl/XHMMZbwjG8/uD05q5SV2whTFv+VlChm/GGnJ8MxYR5ieMVfa8SQy96ClSvuVNMt23CpO4F4D8NskHMrkJmemlVUYiaKRYdWXNAzPpBhlDY60qdLVBWSU6x0wMqV2fbUjI+UQok80qffDpV30ZZdNrz3/GTJfhLbskNh3PYFCdK5HVjyvSAABg8KfqZiz9j+pEp1er/4z1cuOsvLERyhbU/LPixoV1o/VJxC08A+gg4lZxvWCbHW7v6Iz9390x4DROmJXukTPHfpHO7q6y8441SSMiayhmiTmKKyx+XTRElyRmhRG7e65kK1r0rvHMGDUpDgBALPvqM085c+FYuPgWLHW8alD7mIaC6OHCzfU3DXqN2lKo526ZyoaohxIAAPElidlJiU33/b8voBBd3xNG+mHvL02LzKw9O5DrPFzoN79LlamW12MAAI5Lzlp6qNRVvke4u7riUbYeHqvYb9ZBhqM88L7OWIlZoF9G1RknT++PWhU122qR1f/oZBtLyoklZ9AwxiSaX/4i3GF3t9NSdGtSR3Qil6VHI8XcnBRrseneDgCQXlI8wyy7owvGAbsK+ewwAJSzq4OrxFGcw5x+51jfHQ1/bSpJko4OFdgZ8/KKuqV95WluNYzkiEupsax9Wam4vbB/c4m7IXvzA46yqO0HTAVOK3AQhH5E2ZZ7Rd25Puqu3+9b49lCtGoilvRPHVcZHS1I/zHVGZr5UJaLSjsIgIDb9DQujACwPhr9eAIAfg/lPAoAejBvNn6l1Hl059fZrbL50wsqsDa0p0UpYFjXDRx2Fd3wtdQ9S1KzzZFD5LyqB2LPpAj+ad/usS2TIniGi8GUP4zyk3UTb639zEIdVa7edwbDOsC37tX2zSEJwdtfBxTzBzX+7WRoFiJBx0STwE21ro7LZZjSgVVrJysLV/j504XHjh//u8Dhb9liM24rNJrxbljAFlYxJgrt4C4DbOuEaXLlpZPCY668shdDHS50iUtHMLRMFv9bn/YAz3+B75pKTeRO0eEn55Ir+Q62OnJc4Tu/9pSaGeYnDdvASanhr0zpKjqCHzHqKIFgLawU5qjIhLUC11X1ce+c9yon33RNRaqNGX3RoqRhKxGIdxOPl9jDmXYnPiUtPiUwWPCCA5tWbp4MwwhL7ZNWWZqYvcS6xrfZlJsublC33vhpI7vz4aQR+YmzX/jyqSNUhcd+3nq80/ggpa858ezkB/WTz+l9toYW3/5okWfH0CfGtjfpkEbh1PqJOPcc/0TOU7qJhKOP1OqDEw7/XP+jPwDz88rAra+UPzJV8vg5etIPNlAjHw33UwLVQuEjSv/QDlxKYGRWFHEbE/smln33uEqOmdXuY/u/iBh+OG8g1TMi6P/okKCJf0jB5IYfUwhzeeW2cE+NzGBN54cs53bJ331xZrdAZBBCqb+iQpjuu8WVU/e/jg0EHlJ68C6LutMlLeZcQL5QS0+l09BO+d4z9T69xFHbfehqh7BY3S9+n3FnT3fGWF+xvP4VtBiPCNqJxjIqpLOdORFrcbIgaEtDaWyTpcp9S0kzzk3v/G0GRj1tLryQv/LuMp1S3hfiBjFaxsvZshtx2nlZN/l41BRrA1yCeX3oage8XIWxjGlfXxzGxZVRNDXj+duR8ftedxsHNyU+vxrCVHFK4GpOj5jgq/zN4mlsRW6nLoadl9ZBMPskcTbg9TTDkUUadNdDTc0fv/bj2d8n2xvUxuqlrPl+r4q4bqrpVFXt+Vx1HHugwTmMYEp7bT2P/f0jlba2Aea+xkK1ojnUxm1P1JVjPmhKP5+0Dx6pfsPNJoq3HmY5YthG97ii8lHC54s/0Rt13ANPGoQZD92l4T64Hc8l4XW/uzMymEdPsP5zNI6oPSCa9eNb7SuPqNsPYyY3aGh9OxfCsudBl5tKzfgAJ7fRVlrWf8sRSMcateePqR26jybp+pzXpnFWeJlj4C1QN2b8KL2vy3Qnj8W3soOYASvXTier1Acjz4dFCteF5zHTPnouvEatV0XhtNXU0+exVxEo217CyPqDr03ZB9fE8ZSHWxRdbcV+NBUfxrfey0e/HAzslpI68jk9WG2rAyL8tfdGhNVLxJF0GRz/HlxRXLXTgGoh7RkdQebG8c3vWMYE/rLfiwhutbWQzxp+b8avYt6ZdAurwH30moL63ZHYBrndW77ZMiY+VO090/hXaZ7iplyF4c0Ugc8yD6FTmivUEtZJumP4C4PONjH3X3DbdMdcyzfbVrFRLD3YTaXRssz87OZYmw9GN3duzZ/oiHg5NWnY3MM7gepSNki5b+3ncCi0JrTnrxaLbkKr1jX8mfBu2RG1esPjWt/Kkrne4Scu3vRHsrMfLnuwTpyPT6siyp96b9Lg+0knOf9PVsGWT8ymjoZ5F79FVPh5x9+wdECdEPdlF2QKS6zJf0hv46UwUJyy9uPZ1qvXpjTflkwkRP2o1r1XEnJZPkrjzH2Gc8gHeR9CBh7wlRAc3Xc8aD0sWnUhjeP1KQJBUOmJ//VH/2a/+TYY/OEAPrmA2NYeEEaVesbCy8+8gbdV5s6amvhIjCrv96yjuXL+zpXf3uqe0LGqCh9cr1kduT2EOo+anm+HAFHMXTe2V7Y7ZNL5hoNWC0dkTqgmA2/nUHaO1vGqIy8eN3HXYwf0WPyJQwry3juj5bx0Yt3X+waqZWcR9+7du2dw7yDR4B/VjAkqA8VKxUa7DI0Ck1g6lijXGhHxwZY2HZ4CPZ/cTif5v4bedrzZW/d05Mdf2H+Zmfv/nUAWU5qtiSzkOnhNvomJwyRPmK+Jt1hv+GqjY8qZ0lyHk+PZzR+vySlFoyT5b0uF2Fp3xuTt1+RMbXmJfvr9J+4ae114afERu+T0sMLrz0ZqN6qxvy1h5jPH28l5MngUmCLMivNupFjGD4W3RDJHxOvS7XKV42Rp6XI/0JX2tEJKsXI3Tsze/Ol3RraN1Kh4AxS1nqup31qa4df1+t5HBC9aaX5WEvyk6uIdeCxIILOquaio7ajmg6RUe8XSvlqr5Be6l9udteMmP8VfS1Sy+Hi4P8ZhiznhdbNCnxXrwGQIJaumc0pOfqTAJWk5lvg+PiRdDq/PUC5BgH37tyy7d3c6yj823wwNZXpzopcSm4a0tFH+7vjw8NvJuBc5mA2p7fs9P3DZCoZd+7QjvSj2H8avyfxNW4pu9PTG6j5B5QTFRRhQofXE9V8VvDbl29LULnla6ph6oAlzhEkn00sM1Y6T/WdlPheFKo8l6HhXCe21Fh9bq/T4RKn0gdiha0MFezM6iVqJ16pllQ71fZMx9GqyH0NfrHPLpvia19EynjuWi89hOEuI29Obk3ig5H3QnScnf/a1Do7r+D9L+jtsE7M+YvCu6M2uhNz6HUk3iIPn0o95T17ujWsvKbdoy6ypi4uwa6/YZ56fRHuhJ0tOrQeP2PX85jFqth+3X1XgIseNBZ4k91xPf71rX9KRx12b9bXHGhif3VOfCkg63ch7qOBcqpStMy2nn1Xqeam6E6cD9S3rTekG+i8J+O4KG9DVZbmoFsfxpisrplqR7/ibzYiJXn+Nbo04AX4LDrwquuGgD7+8ZOv+F7Zv171J5E4e7OjNa7dtKOXfYWG+/1Kp35segkSDcTSPVA6LtDd/R62WrX/x83vyDEgComq0Oipt9Iv9du5u4gc7h9AaxBjLPv0K+sz1k6pnfCLW6VrFCShvHf2Kjqw0DQyOjy2XdmMNWc8ewF+y7XqB+u3SzzcUT/+4i7d6zNRw4ABlj0ijoon7seqNvFLvn/Vvf8vgit1IEVcgzG3t074GXyzc1KxuMCD5bqdL2Xes8pdhra3EhJjRwkun8lpHK/kqOx4+aT7WTtTRLcqwE0awFr33eud/LPM2Pnt4j7I1jVjJYDX/CN6xLYdzff9N4UcSacQII8FhZdaJFvcRqUlm6vycQmxrWlZleVLUwA5sa1rLq+or/1w5Zaxj5RlPGH4geWhrltv9CuQ3tOdHYr9MTESFGS4333JTz/HvruZlw7T39BBlNahUn447Ir4eVA0qydK3X7Q3WxuMW9lXG7blpCtlTuC5BZrTazIV7r7XRVzKfekY72I56RSy7YZLG+/B9FeYg9ppWxs0EWfHCzSYX+zrqqR8no4qzzgwYhnRoDigIVSav8tdn3m0NXDkbSHy33X/hBxzOvxI3eGaN+1kbO8BExwhb3xdq/ZRk9PeARd5/vbfrY/L5EgVDuf2xcvUbhVRLvHofxu8rijIz2iC/0nrpoZi3he3sX0uTS+bbT6Xu3oEXUuwdj+bdMpX8b3yZIaQbCD1DoG8kdrebd111zUuptIg+5tNvh+78MICiVgffPhdaf0JtoKNOz584LuBH7Rx88Q7S919OSrvmKVokIiLivxQOF4e1GDyU00gdCrtOA3vFRNOX07pZ2nPR1WL+lraaIqaU9mji/eITaUxM1OYs3DiKr9pfXktx+3F5WpyQu/RLa/YZB+2hH9yskcai4ca7VHXaPii3XhaZTzEcHl0109IvsJKSu1SarJI/66VHaDPeOjW5gNMeNWTNB0NjBs+dqXu/aI7lZ98Ri8qMxpjM8bB0jk+5B5z6DHB5n7owX7aPNNwY6+3seZdpyrrPgtnnscP6Wdq4bJ1COFBisqdZ5411Jz67jC6P7xDHc3ryRdNX3P6htnhfslIbCxRXLuaJzwgU4eloCtMNruyOSKcgVVJga2IpkGjHY0l3mpIKrze945HaNCgzuRSYjYrLtTk+iHpbXVCkZ05Q7d3CVp94tmJeiOcd2IIoR87Ush5P0PTgcYqkOL4fVP9o8Ty3EDZydefej6dfUt1pZhtWGAf5mywDbqAg3oXQ49s37B05AaPHBHWxocjTo/S6S+9d0ejT3TfHIlpLJY+3lCb3QPi3XrUVRjLt9tR+b3GyuhZazyWQXsjy7epnHvZHHKFMVLVg8UtTNk1deRQQJv0Yc24JhU0rfONpM17VJJ4C7Imdlb6pNn272OM7LeJQ6Brzu4dyOlXjtZlQtbeGd/3bIp9ruvXfzsggwmAy8zLNddn3nxwdlgfiSN4yOuteHWtSzE/E9VHB+ngIjBIcUY0T3UjTwbDlmfm0c7WF6cMYvoTDTSL9keEJzw7EBbuYdo+EVi43Sc7icBlTzSk4SvsT/l+92tad+HN8FPyBcwf+BCn+UIL/P069IY2ae+n2ew/UPew0sK/76unPR+9w5PcdAGG+MPBu6tfyPMkyJee8rzR/WTv975JxtmKML3fTc8BAB4u28P/VdEqeje/XgPxWJejzhg8lpwhQ+JGKHORMi2z0kq0dqqhyO9vq2ZaFZXajZRzzYXHMSJAEACwecmXVlYomTPG19Xz9+fEP3t15SMDg7J1xeExjjisO1x/Spq+tl5FJdrMUKe6Zpt2ZSUaaZyS2k4ok8zyGBlldBse9mAm1NZIZn1KSdOvQt9LnW+YCNG+7ZcHAEgtKYaHnBh7V1f8EkoqalDa8zKIoyxui2kwLVbUWv5LGFkNR7AYu8U1lGvp6c5omOnRSGYRj3riF9Ux7n+hfjsAAPXnOmZ+w3UQjPCD3mtDmY6alYqUVWV+IgQa3QsV9bJiYGBgkBO6KqRwz6tvDKcR+u89BuXQ6LE7QnLjrEYZYd3dUQ3vgq6/iDKlFrpQ0CBw581+iU3HQzbIaY+JRhsmGEtXcER5rAsPd4mOIhJ11skQjcMvSzLvLm18tUe2O8kuBnPtYswt66BGkEyVgtt0PATtPFx4gmquyPLqi1uiAADjfzqJpVcxicl4JU123pDMkfk1t995uIqT5HPMCnnIzQ0CfA7M84grNe9aJQ+5Z5+SVl2JrhbV056dhHUVImVVRsYv76e27yntahe27wjm+Hybftud9s5Y/r/mP5NTjXXS/pqD+ks+Hk5yOlwwjqv5PMm/GNb0Dyky/nkOD8tkwe2r1Oud/lC59j9QSS9CRfKkbFR820mpyHf7Z6mWODHWUHBQzmuFxqXWAfq5v0sM/PVziQQXKRA03cQMA7o1D0SS85nHIJ+Dml2YP3crrAELU1FwAdDs0QaYAK+5wUulokjxoFEhDhgeDwVYOr+0VF3MsLoek0KRKY98h3sWo3f3GkpALmkErwaaCOKCVaMKGb5I0ogUDBoFYoCBJc6CwSNFSxlCBTPk+2+AVfggQwXI5Yfg0qE5H7gP7pDhi+SHSMGgAR+4D69mweBBoZX7IEYNSFNBcG7otzUmGDeWGpBPBZFCQOM4cIiSWQjS2M8CFZCEDRxCkQaQT/GQQkDjLCwwiPuzEGSCOStH4aIFi2ZuVv48An+jrGheMsLmZf7voQvjNPASoMkX+NScgiOQi9OQgkFTLnAwazqwXHRm5bU9hYLBYjJwOdAUC1wOBT1YLiZDCgbNq8DBXEnAyMRgVl5bHRQMFniBy4FmUjhhcjgYwDKBF1IsaPgEjnWcBGthpmWpytbCKvsIxSINr5CcG5BgCfxUFWIEKwivkOJBgyNwvOiFeGTCKSsvkpoJLB4+gYuCRj24YaJ0FoCQCZ+QwkHDGXC4LwvhFqZFltqU6GCbUjAzIJfvWPygYIOpeTE7nGy+gxQHmqmA42xbC5YIcJDiQIMSrPDLAwyHJI9BCgONPcCvZ+tZwNIZC1IoaCyBHQZ1gRSKJDWx1KOihz0qRVZANtSw0suNPysgF2qAFwKNGMAv83mQ4WRCDUvpYIDp2MEGyOcTSPZESMcObqkvDGBhPoEUCdopgx/sHrxg0X7fyg92DT5A2mODC4C2wOClBPCBJXtsS7nKCnO1F4q0sIEGFwTtX/HBBMnygxU30Ba8PkE6Ujww1EJyqOQ6Ggv2QUhzCQ6psQWsrLFFCglt5cAhq8hBkutTrPypEATAUh0huDJo1waubIwMzIr8gzZo4JAYQbCy5g8pJLTXAocsIwf5p/5xwvwz2gqWbdnA5UF7KvwweTcWwyLXsiGFhfZP4LB0QmDl7ZmVH8weEFjIxkJN8+sPlIEy8GcDgPDrfQX8XwAAAP//frYA1UM3AAA="); err != nil { + if err := gres.Add("H4sIAAAAAAAC/7RaeTiU6/9+sq8hSyohYpAxtkiyFLKMfYtSGgxxGGHsLShlSUqh0nZQpGObiCMplX3LlrQoCpF9RIR+V4T3HWPJ+X27rvjHc9/3536f7f28tyGakooD0AE6MIh1NASQf+sAPbB1xdk7OqBmfkngXV2czUypwZqOnfaHxCp0kSgt8TLDDDPDuprSGr1GFArVhNKuQpZqi2sjzdvCJHT0ysTPtxsbGhpqlYlXm4N7srLSTTIvZV7KZEnLyOU0SBlSoBEIYYdEm4typhRUAPz8aYimpbv+7IKdOQDAHgCwuDzmeXkuvhKOOMf/V2XG88rM55V9D1RTJFUGwFprChOoMpo5ZdOSXN7TYn4Nhv7J4mVtmBuMtPX0wLu6/M/MN5ov0Wy+RFN1ubfLm8+xQOX/4BmYzAvcNy9Qkrb99PLPgJlU4LKPAoDLbjdhRdIBemDn6C6F8vC0mR7e/C7l0MqfJBdk+K//SDzWAy8lgffBz7skoVeH1EZKSJlqVdds067atmauzIIHl3auBwCwLcnBMMsxjT2PSx4zhFJlin3anKWso/qNuXrHpP+bY9Iwx6TJO2a8oDr/SF7KFTsmPe2YNNyxhZh6KoSSFTsmvYrlzgTogaPUDhwKi4Ov8LZyraqysn0ZZoZDQOpybjkx3N1YdeIoqxo966w4W2+kKh8AgH9lDE6YxRj83u0Yc6v/XBJxn09e4ti73EOh93c9EUFQbyXalWR1mvhfBmtmOY0c+L2kAADbVsbp7rkYZ57kT5vnwZvXyx187Zep/ZtQPL/z2flwZp0AMFfjjry/kAAAkSX5WGb5/I4g1fThlPNVErwL6+/4R894+ZRC8a7SrTcJVHSzVF0t5RFCAIAtK6Yy3ff/SEV+Wv2iWsVCZPw9FGnn6I7C4la5FiEIqCNYZ2fXpabo/Bxx/+pkywEAYP0jAm9Xd2c7EgKxGlS5tlGGmSEDLZTgBpsngpRgGQ+cMP/VAyfMbw98MQs8yDFeuIbm12m+vZgl//TR/idkM37AyKbtyDQ29CPYviHeU4Isk5u7rZ25pmfnih1x91yFI5xwhN+OQA/+RRb4vBvOx7aIbJ6u90+IZtyA3TBm50ZeBummgf3a+2j9svNjLZRieuNYhSEbFoD89sTJwxU3pzWl49c1x3DGG4Lx/P7gtGZuUhdsYcxbfpaQ4ZuxhhzfjEWE+QljlT1vEkMveoqUb3mTTPetwiTuBSC/TfKBTG5CZnpplZEIGilWXVnzwEy6QcbQWKsKXW1QVonOMRNDatdnGxJyPhHKZLNKH3z6VV9G2fTa858x02V4yy6JTcczGFTnSmT144o0AAAY/Km6GUv/ozrR6dXqP2O93Dgrb2yEsgU1/6y4cWHdaH0ScQvPADqIuFVcL9hmh9s7OmP/d3cMOI0TZqV75MyxX6Szu6vsvGNN0ojIGopZYo7iCotfFw3RJYlZYcTunivZiha9azwzRk2KAwAQy776zFPOXDgWLr4FSx2vGtQ+pqEgerhwc/1Ng16jthTquVumsiHqoQQAQHxJYnZSYtN9/+8LKETX94SRftj7S9MiM2vPDuQ6Dxf6ze9SZarl9RgAgOOSs5YeKnWV7xHurq54lK2Hxyr2m3WQ4SgPvK8zVmIW6JdRdcbJ0/ujVkXNtlpk9T862caScmLJGTSMMYnml78Id9jd7bQU3ZrUEZ3IZenRSDE3J8VabLq3AwCklxTPMMvu6IJxwK5CPjsMAOXs6uAqcRTnMKffOdZ3R8Nfm0qSpKNDBXbGvLyibmlfeZpbDSM54lJqLGtfVipuL+zfXOJuyN78gKMsavsBU4HTChwEoR9RtuVeUXeuj7rr9/vWeLYQrZqIJf1Tx1VGRwvSf0x1hmY+lOWi0g4CIOA2PY0LIwCsj0Y/ngCA30M5jwKAHsybjV8pdR7d+XV2q2z+9IIKrA3taVEKGNZ1A4ddRTd8LXXPktRsc+QQOa/qgdgzKYJ/2rd7bMukCJ7hYjDlD6P8ZN3EW2s/s1BHlav3ncGwDvCte7V9c0hC8PbXAcX8QY1/OxmahUjQMdEkcFOtq+NyGaZ0YNXaycrCFX7+dOGx48f/LnD4W7bYjNsKjWa8GxawhVWMiUI7uMsA2zphmlx56aTwmCuv7MVQhwtd4tIRDC2Txf/Wpz3A81/gu6ZSE7lTdPjJueRKvoOtjhxX+M6vPaVmhvlJwzZwUmr4K1O6io7gR4w6SiBYCyuFOSoyYa3AdVV93DvnvcrJN11TkWpjRl+0KGnYSgTi3cTjJfZwpt2JT0mLTwkMFrzgwKaVmyfDMMJS+6RVliZmL7Gu8W025aaLG9StN37ayO58OGlEfuLsF7586ghV4bGftx7vND5I6WtOPDv5Qf3kc3qfraHFtz9a5Nkx9Imx7U06pFE4tX4izj3HP5HzlG4i4egjtfrghMM/1//oD8D8vDJw6yvlj0yVPH6OnvSDDdTIR8P9lEC1UPiI0j+0A5cSGJkVRdzGxL6JZd89rpJjZrX72P4vIoYfzhtI9YwI+j86JGjiH1IwueHHFMJcXrkt3FMjM1jT+SHLuV3yd1+c2S0QGYRQ6q+oEKb7bnHl1P2vYwOBh5QevMui7nRJizkXkC/U0lPpNLRTvvdMvU8vcdR2H7raISxW94vfZ9zZ050x1lcsr38FLcYjgnaisYwK6WxnTsRanCwI2tJQGttkqXLfUtKMc9M7f5uBUU+bCy/kr7y7TKeU94W4QYyW8XK27Eacdl7WTT4eNcXaAJdgXh+62gEvV2EsY9rXF4dxcWUUTc14/nZk/L7X3cbBTYnPr4YwVZwSuJrTIyb4Kn+zeBpbkdupi2HnpXUQzD5JnA14Pc1wZJEG3fVQU/PHr/149vfJ9ga1sXopa77fqyKum2o6VVV7Plcdxx5ocA4jmNJeW89jf/9Ipa1tgLmvsVCtaA61cdsTdeWYD5rSzyftg0eq33CzieKth1mOGLbRPa6ofJTw+eJP9EYd98CTBmHGQ3dpuA9ux3NJeN3v7owM5tETrP8cjSNqD4hm/fhW+8oj6vbDmMkNGlrfzoWw7HnQ5aZSMz7AyW20lZb133IE0rFG7fljaofuo0m6Pue1aZwVXuYYeAvUjRk/Su/rMt3JY/Gt7CBmwMq108kq9cHI82GRwnXhecy0j54Lr1HrVVE4bTX19HnsVQTKtpcwsv7ga1P2wTVxPOXhFkVXW7EfTcWH8a338tEvBwO7paSOfE4PVtvqgAh/7b0RYfUScSRdBse/B1cUV+00oFpIe0ZHkLlxfPM7ljGBv+z3IoJbbS3ks4bfm/GrmHcm3cIqcB+9pqB+dyS2QW73lm+2jIkPVXvPNP5Vmqe4KVdheDNF4LPMQ+iU5gq1hHWS7hj+wqCzTcz9F9w23THX8s22VWwUSw92U2m0LDM/uznW5oPRzZ1b8yc6Il5OTRo29/BOoLqUDVLuW/s5HAqtCe35q8Wim9CqdQ1/JrxbdkSt3vC41reyZK53+ImLN/2R7OyHyx6sE+fj06qI8qfemzT4ftJJzv+TVbDlE7Opo2HexW8RFX7e8TcsHVAnxH3ZBZnCEmvyH9LbeCkMFKes/Xi29eq1Kc23JRMJUT+qde+VhFyWj9I4c5/hHPJB3oeQgQd8JQRH9x0PWg+LVl1I43h9ikAQVHrif/3Rv9lvvg0GfziATy4gtrUHhFGlnrHw8jNv4G2VubOmJj4So8r7Petorpy/c+W3t7ondKyqwgfXa1ZHbg+hzqOm59shQBRz143tle0OmXS+4aDVwhGZE6rJwNs5lJ2jdbzqyIvHTdz12AE9Fn/ikIK8985oOS+dWPf1voFq2VnEvXv37hncO0g0+Ec1Y4LKQLFSsdEuQ6PAJJaOJcq1RkR8sKVNh6dAzye300n+r6G3HW/21j0d+fEX9l9m5v5/J5DFlGZrIgu5Dl6Tb2LiMMkT5mviLdYbvtromHKmNNfh5Hh288drckrRKEn+21IhttadMXn7NTlTW16in37/ibvGXhdeWnzELjk9rPD6s5HajWrsb0uY+czxdnKeDB4Fpgiz4rwbKZbxQ+EtkcwR8bp0u1zlOFlautwPdKU9rZBSrNyNE7M3f/qdkW0jNSreAEWt52rqt5Zm+HW9vvcRwYtWmp+VBD+pungHHgsSyKxqLipqO6r5ICnVXrG0r9Yq+YXu5XZn7bjJT/HXEpUsPh7uj3HYYk543azQZ8U6MBlCyarpnJKTHylwSVqOJb6PD0mXw+szlEsQYN/+Lcvu3Z2O8o/NN0NDmd6c6KXEpiEtbZS/Oz48/HYy7kUOZkNq+37PD1y2gmHXPu1IL4r9h/FrMn/TlqIbPb2xuk9QOUFxEQZUaD1x/VcFr035tjS1S56WOqYeaMIcYdLJ9BJDteNk/1mZz0WhymMJOt5VQnutxcfWKj0+USp9IHbo2lDB3oxOolbitWpZpUN932QMvZrsx9AX69yyKb7mdbSM547l4nMYzhLi9vTmJB4oeR9058nJn32tg+M6/s+S/g7bxKyPGLwrerMrIbd+R9IN4uC59GPek5d749pLyi3aMmvq4iLs2iv2mecn0V7oyZJT68Ejdj2/eYya7cftVxW4yHFjgSfJPdfTX+/al3Tkcddmfe2xBsZn99SnApJON/IeKjiXKmXrTMvpZ5V6XqruxOlAfct6U7qB/ksCvrvCBnR1WS6qxXG86cqKqVbkO/5mM2Ki11+jWyNOgN+CA6+Kbjjowy8v2br/he3bdW8SuZMHO3rz2m0bSvl3WJjvv1Tq96aHINFgHM0jlcMi7c3fUatl61/8/J48A5KAqBqtjkob/WK/nbub+MHOIbQGMcayT7+CPnP9pOoZn4h1ulZxAspbR7+iIytNA4PjY8ul3VhD1rMH8Jdsu16gfrv08w3F0z/u4q0eMzUcOEDZI9KoaOJ+rHojr9T7Z/3b3zK4YjdSxBUIc1v7tK/BFws3NasbDEi+2+lS9h2r/GVYaysxIWa08NKpvNbRSr7KjodPmo+1E3V0izLshBGsRe+93vkfy7yNzx7eo2xNI1YyWM0/gndsy+Fc339T+JFEGjHCSHBYmXWixX1EapKZOj+nENuallVZnhQ1sAPbmtbyqvrKP1dOGetYecYThh9IHtqa5Xa/AvkN7fmR2C8TE1FhhsvNt9zUc/y7q3nZMO09PURZDSrVp+OOiK8HVYNKsvTtF+3N1gbjVvbVhm056UqZE3hugeb0mkyFu+91EZdyXzrGu1hOOoVsu+HSxnsw/RXmoHba1gZNxNnxAg3mF/u6Kimfp6PKMw6MWEY0KA5oCJXm73LXZx5tDRx5W4j8d90/IcecDj9Sd7jmTTsZ23vABEfIG1/Xqn3U5LR3wEWev/136+MyOVKFw7l98TK1W0WUSzz63wavKwryM5rgf9K6qaGY98VtbJ9L08tmm8/lrh5B1xKs3c8mnfJVfK88mSEkG0i9QyBvpLZ3W3fddY2LqTTI/maT78cuvLBAItYHH35XWn+CrWDjjg8f+G7gB23cPPHOUndfjso7ZikaJOKiIj8UjpcHNZj8VBMInUo7TsN7xYTTl1P6WdrzUdWivpY2mqLmVPbo4j1iU2nMzBTmLJy4ym9aX17LcXtxuZqc0Ht0yys22Yct4Z+c7JHG4qFGe9Q1Gr5oN55WGQ8xXB7d9ROSr7CSUruUmizSv2tlB+gzHrq1+QATXvUkTUcD44aPXal7v+hO5Sef0YvKjMbYjHGwdI4PucccekywuR96sJ82zzTc2OttrHnXqcq6z8KZ5/FD+plauGwdQniQonLnmWcNNae+O4zuD+9QR/N68kXT15y+YXa4XzISG0sU167mCQ/I1GEp6AqTza5sjghnYFVSYCuiadBoR2OJtxqSCq/3veMRGjSoM7mUmM2KCzW5fkh6W51QZGfO0O1dglafeHai3gjnnRhC6MeOFHLez9B0oLEKpDh+31T/KLE8N1B28vWnnk9n31JdKWYbFtiHORtsgy7goN7F0CPbNywducEjR4S18eGI06N0+kvv3dHoE903R2Iai6WPN9Rm94B4tx51Fcby7XZUfq+xMnrWGo9l0N7I8m0q5142h1xhjFT1YHELU3ZNHTkU0CZ9WDOuSQVN63wjafMelSTegqyJnZU+abb9+xgj+23iEOias3sHcvqVo3WZkLV3xvc9m2Kf6/r13w7IYALgMvNyzfWZNx+cHdZH4gge8norXl3rUszPRPXRQTq4CAxSnBHNU93Ik8Gw5Zl5tLP1xSmDmP5EA82i/RHhCc8OhIV7mLZPBBZu98lOInDZEw1p+Ar7U77f/ZrWXXgz/JR8AfMHPsRpvtACf78OvaFN2vtpNvsP1D2stPDv++ppz0fv8CQ3XYAh/nDw7uoX8jwJ8qWnPG90P9n7vW+ScbYiTO9303MAgIfL9vB/VbSK3s2v10A81uWoMwaPJWfIkLgRylykTMustBKtnWoo8vvbqplWRaV2I+Vcc+FxjAgQBABsXvKllRVK5ozxdfX8/Tnxz15d+cjAoGxdcXiMIw7rDtefkqavrVdRiTYz1Kmu2aZdWYlGGqekthPKJLM8RkYZ3YaHPZgJtTWSWZ9S0vSr0PdS5xsmQrRv++UBAFJLiuEhJ8be1RW/hJKKGpT2vAziKIvbYhpMixW1lv8SRlbDESzGbnEN5Vp6ujMaZno0klnEo574RXWM+1+o3w4AQP25jpnfcB0EI/yg99pQpqNmpSJlVZmfCIFG90JFvawYGBgY5ISuCinc8+obw2mE/nuPQTk0euyOkNw4q1FGWHd3VMO7oOsvokyphS4UNAjcebNfYtPxkA1y2mOi0YYJxtIVHFEe68LDXaKjiESddTJE4/DLksy7Sxtf7ZHtTrKLwVy7GHPLOqgRJFOl4DYdD0E7DxeeoJorsrz64pYoAMD4n05i6VVMYjJeSZOdNyRzZH7N7XceruIk+RyzQh5yc4MAnwPzPOJKzbtWyUPu2aekVVeiq0X1tGcnYV2FSFmVkfHL+6nte0q72oXtO4I5Pt+m33anvTOW/6/5z+RUY520v+ag/pKPh5OcDheM42o+T/IvhjX9Q4qMf57DwzJZcPsq9XqnP1Su/Q9U0otQkTwpGxXfdlIq8t3+WaolTow1FByU81qhcal1gH7u7xIDf/1cIsFFCgRNNzHDgG7NA5HkfOYxyOegZhfmz90Ka8DCVBRcADR7tAEmwGtu8FKpKFI8aFSIA4bHQwGWzi8tVRczrK7HpFBkyiPf4Z7F6N29hhKQSxrBq4Emgrhg1ahChi+SNCIFg0aBGGBgibNg8EjRUoZQwQz5/htgFT7IUAFy+SG4dGjOB+6DO2T4IvkhUjBowAfuw6tZMHhQaOU+iFED0lQQnBv6bY0Jxo2lBuRTQaQQ0DgOHKJkFoI09rNABSRhA4dQpAHkUzykENA4CwsM4v4sBJlgzspRuGjBopmblT+PwN8oK5qXjLB5mf976MI4DbwEaPIFPjWn4Ajk4jSkYNCUCxzMmg4sF51ZeW1PoWCwmAxcDjTFApdDQQ+Wi8mQgkHzKnAwVxIwMjGYlddWBwWDBV7gcqCZFE6YHA4GsEzghRQLGj6BYx0nwVqYaVmqsrWwyj5CsUjDKyTnBiRYAj9VhRjBCsIrpHjQ4AgcL3ohHplwysqLpGYCi4dP4KKgUQ9umCidBSBkwiekcNBwBhzuy0K4hWmRpTYlOtimFMwMyOU7Fj8o2GBqXswOJ5vvIMWBZirgONvWgiUCHKQ40KAEK/zyAMMhyWOQwkBjD/Dr2XoWsHTGghQKGktgh0FdIIUiSU0s9ajoYY9KkRWQDTWs9HLjzwrIhRrghUAjBvDLfB5kOJlQw1I6GGA6drAB8vkEkj0R0rGDW+oLA1iYTyBFgnbK4Ae7By9YtN+38oNdgw+Q9tjgAqAtMHgpAXxgyR7bUq6ywlzthSItbKDBBUH7V3wwQbL8YMUNtAWvT5COFA8MtZAcKrmOxoJ9ENJcgkNqbAEra2yRQkJbOXDIKnKQ5PoUK38qBAGwVEcIrgzatYErGyMDsyL/oA0aOCRGEKys+UMKCe21wCHLyEH+qX+cMP+MtoJlWzZwedCeCj9M3o3FsMi1bEhhof0TOCydEFh5e2blB7MHBBaysVDT/PoDZaAM/NkAIPx6XwH/FwAA//+hv1N+QzcAAA=="); err != nil { panic("add binary content to resource manager failed: " + err.Error()) } } diff --git a/os/gres/testdata/testdata.go b/os/gres/testdata/testdata.go index 37c21f2a6..ebf318e6c 100644 --- a/os/gres/testdata/testdata.go +++ b/os/gres/testdata/testdata.go @@ -3,7 +3,7 @@ package testdata import "github.com/gogf/gf/os/gres" func init() { - if err := gres.Add("H4sIAAAAAAAC/7SaCTTU6xvH3+xZQpYUIWKQMQyRZCnKMpaxRimNNWKEka0FpSxJKVTaLop07USSrsq+b0lKUYjsIyL0P+VifmNGk+6/c2rq5Hye7/t9n/e3PPNFo6hpOAEDAOCK2y00IPjFDFYDe0dnOw+EjSvW3tHB1IQWrGp9k3wQjaJnIPxB8ggOIgTcxtMD5+rySxIDGLFzhJAESJP+/ZcUztXF+Se1e7v9QYlKXThCS7IcnW6Kbqgtq9VrRiAQLQjtaniZtqQ23KwzVEpHr1zyQpcRGo3WKpesMQP35eSQLbJ1snWy2UhZ+dwmGUMqFAwm6pBgfUnelIoGgO/ff2g10ZBvMwMA2C+rdQMZrS4+Uo5Yx/9UpvGizL2LMqXpu878WiYPkcz/m5foRZEmiyJvPLto+2uRxB30/zDRaFGf2aK+rwHqSsT6lnY444I+W0d3mRUckTUQAMLD03oFp4NvCeTHbzjOzgMnI4Xzxi3aJaXXANeGS8mYaNXUbtGu3rJqYb2FWZe3rwMAsC9biQ1a6WeFRTppcjC16iwHAICFcieRf+ok8r9wEglxEknaSaMl6/WL4Kf+TSeRP51EQp1cStZTzSz9HScdZbZhKXCSWNoaCABu6+i+IIzy7WBfAkHYYf9oRwg4iMN2zs6u0EtVZ4VWdXn53nRT9CiQuQJWzZvm/tnJhvOn4b9fxsvV3dmWqIxELaJC2zDdFM1IT1jmJrsnjLgMRa44Yf4bV5ww/7rig1niSq4R2vfNtkm3xo+l4Q8EFKSOv8k7uJptXnqBvYSFIABg/QpKzjkEKfnToAwjtG+mzWv8fWUdfzBf6NZOK2duAADr73rk7rkCj3hJcf71iPAestA3+dLfrZ8HbVwnf+CVb4b2oj/OxzeJbQQAcK+g3Jw/kFvWfP/kp/c8uxDGQuCP3eeBx+t+2UOcSwv5Hoar6//RYxUU9a9LTh6u2AXdyd0/7qHoObcyf2yvV1HjXb+oPKdVC4egcBNT/q87iWzVObNIVZ0zLXOxqSxzFm1jHEDNElel1DaTvSuwjZ8M6l/bvAkOQ2ZGWlm1oRgKLlFTVZtlimySRRtpVaNqDMqrULmmEnDtxhx0Zu6HzHK57LKsDz/Wml7+88T6zdnrMrZphxTviXRGtYXlsvlyRxgAAAxWpnHO5D/UKP7zjPvNbYb8FBt/TLiKOa3gvMQpUd0ofSKJS3eEFSrxx19XsBnrllIQdtjl7g15FfgwdyO16aNs6ouH3MYLriYAABD87WJOGEovuSEPdjwVgxXr7Owtv+BYmzgutopqvjxnSaW5DABAfNny3CTKu3tScjWbK00rWUB8zXlmhJiRBADAKHynWSz888ySOK1LrhA4tcCuyd2K4oeKNjbeMhgw7EymXXikUUEjHkoBACSXLb+edHmTvf/5WQvW9TlpqB/69vJPqRn154bznMeKfBcvceVqFY0YAIDjsoLXQgT///qRqI4ThlwdMr1IuxlvW5rdY+xH8CRj6CB47Ecrbvmdyu6e5CpT2Ic22/KPwAEAYstW5YJWnWtBSOHFFc/fn366+w+V0j3l26/jaRjmC/a2V4SLAAA2/WZBk73/YcHlnt7dXV1xf/Qe9AOAsPHwWMFFlWcJBOGB83G2k5rH/Vh5g1HSz7uzVmXtlnp4zd86OUbS8hJJ6XRM0QlmVz6Jdtve67EQ35zYHZXAbeHRTLVwsZNot+7bCgBALrsQNqgGRxeMw0ruD+tJYBDOrg6uUkexDgtrcY7x2dZ0hLc0ERkVIrQ9uu6qhoV91RkedYz0uEuZkZx9eZmkvahfa6k7mqM1i7M8cut+E6EzipyZIt8ibSqORd69MeGuP+RT69mOt2zBlw7NnlCdmChM+zbbE5LxUI6bRjsQAP87q+lcmABgezzx/iQAgh4q+VQA9GNeb/hMrfP47o+nS9WNH17QgDUh/e3K/mO6buCQq/j6z2Xu2dKanY6cYhfUPGC7ZsRw/wzunNw0I4ZjvBRE/c2wIEk34faaj6y0kRUag2cxbMMCa19u3RgcH7T1lX+JYGDzX05o02ApBma6eB6atQ3cLmPUDmxa29lYucMunCk6fuLEX4UOf8mVmPJYolBM90L9N7FJMFNpB/Ua2HVMmyRVXT4lOunKL3cpxOFiryQynLF9puRRY2oWTvCiwHXV2ojt4mNPzydVCRzocOS8KnBhzWl1U8x3OvbhUzJjn5nTVHWE32M0EEJBWnYymKNi01aK3Nc0prxy36qeet07G6E+afhJi5qOvVQozk0yTmoXV+rduOTUuOSAIOGLDuxaefmyjOOs9U875Oii9+AbmttyqHkvrdew2vBhA4fzocRxhelznwQKaMPVRCe/336y3egAtY8Z/tzMO41Tz1d7bw4pufPePN+WcVCCfU/iwd1Fs+umY91z/RK4TusmZB59rN4YFH/o+7pvQ/6Y71eHb3+m/pahmi/I2Z92oIkW/nhsiBqoFYkeVv6bfvhyPBOLkpjbpMQXiZx7J1RzTS13Ht/3SQz97oKBTP+4sN/jg8LGfsGFM+u/zcLMFFQ6wzx3ZwRpOj9kPb9D4d6LszuFIgJhykOVlaIMX82vnn7weXI44KBy1pts2h6X1Ojz/gUi7f1VTqPbFQbONnoP4Cds9qJqHEJjdD/5fsSeO9MTbXXV4sZn0G40LmwrHsOkmMZ+9mSM+anCwE1NZTEtFqoPLKRNuXjf+FkPT3haX3yhcPXNFQbl/E/49RL0TFdy5DZgtfOzbwnwqSvV+7sE8Xsz1A8fcxW1Y0r9/OIQNracqqUVJ9gFj9v7qs8oqCXh+bVg5srTQtdy+yWEXxZslExlL3Y7fSn0AlIHxuKdyNWE09MMgxfvZrgRYmL25JUv375BuYHATrZjKppv96hK6qaYzFbXX8jTwHIEGJzHCCd31Tfy2T84XGVj42/mYyRSL55La9T5VEMl+p0m8vmMfdB4zWsednGc1RjrYXQnw5PKqsfxHy99R23QcQ84ZRBqNHqPjufAVhy31LEHfT0RQXx6wo0fo7B47WHx7G9f6l96RN55GD2zfrfWl/PBrLuyet1Ua6eGuXgMN9OzPaqAwR1r1Z8/oXXoO5qo631Bm85ZsS7XwEuoYdLocdpgr8l2PvMv5Qcww5auPU6WKVnjz8fEitaG5bPQP34uukp9QFXxjOXsP89jrsEQNgOZ4+sOvDLhGFkVy1cRZl58rcPuvYnkGK7jfgGqbiSgT0bm8Me0IPXNDrCwV14bYJZ1sMNpsljBXdji2BqnYbUi+rM6wizNUxvfsE4KHbHfAwvqsDFXyB57ayqoataTeNtOkefodUWNe+MxTfI7N32xYUp4qDZwtvlIWb4Sb57i2EaqgGcZB1HJrZXq8Wul3TGCRYHnWliGLrrx3jXT8smxUWqWSAtyU222KDc7tzHG+p3hre2bC6a7w+tmZ9Ct/fzTiF4Vg+QHVr4OB0NqQ/qPtJv3ZXZoXcedDeuTG1dvRJ/Q+lKexP0GN33plh+cg+NQedZaSQEBrcpIP9o9iSNvZ5zk/T5YBlk8NZ09GupV0gar9PWKu2nhgDgp6cMhzByaUFvwcLX1McXhkuQ17891XLs+q9lWOh0f+a1G935p8BWFyN1nHzCeh2flvwsezhIozXR035bVcUi8+mIq56vTmZnCyk/9bjx+lPP6y0jQu/24pEJ8Z5d/KE3KWfNjvmZN/B2yd1fVxkVg1Pi/Zh/Nk/dzrvrSpntSx7I6bGSdZk3E1mDafNrVAtuE8BLuujEDcn3BM843HbTaOSNyQzQZ+XtGc3K1TlQffvGkhafRbliP1Q8/qqjgtT1K/phOjPs6nwD1nGz8nj17do3sGcEb/K2WPk1joFSl1GybvrvQOIaBNdK1VkxypL1Th69Qzzuvx0nhyGhb9+s9Df+Mfzti94iFZejRNLyE2nRVRBH3gesKLcycxvmiAi38JXpj15odk8+W5TmcmsppfX9dXjkKIS14RybYxqonOn+fJldKex3qn6/fsdc5GsLKSg7bJqWFFt14Nl6/QZ2jrZRFwAxnK+/J6FFoAjMtyb+ZbBE3GtYewRIep8uww1Wei7W9131/b+o/lTJKVTuxEvZm/3xlYt9Ai4gzQNDquZr4rqEbe9Wo73VY+JKl5kdl4Q9qLl4BxwOFMqpbi4s7j2pmJabYK5UN1lsmvdC90uWsHTvzIe56grL5+0ND0Q6bzDJftSoOWrINzwRTs2k6J+cWRAhdRsqzxg0KwBly+b1H8zKFOLZ+ybZ9c7e74n3rrZAQ5tcnB6jtUuEW1ipfHR8eapuJfZGLWZ/Stc/zHbeNcOj1D9vSimP+ZvqcJNiyqfhm/0CM7lNEbmBsuAENSk9S/2XhKxOBTS1d0mdkjmsEGLOEG/cw12Fotp0aOif7sThEZTJex6taZI+V5OQa5Scny5D7Y0avjxbuSe/BayVcr5FTPjj4RRZ9rMV+EnWpwS2H6nN+d/tU3mQeLpfxXGbsroHchP2lbwPvPj31fbBjZErH71niX6G8LPqwkXvit3rj8xq3Jd7Ej5xPO+41c2Ugtqu0wrwzo7YhNty2q3KvWUEi/cX+bHn1fhxsx/Nbx2nZv915WYmNmDISeprUfyPt1Y69iYef9G7U155sYnp2X2PWP/FMM//BwvMpMjbO9Fy+likXZBpOngnQt2g0YRgeuizksyN0WFeX9ZJ6LOfr3uzoGiWBE683wqYH/Hb37Y4VEjTnxKmhmg54CypId+x7YdO29nUCT9JI90B+l01TmeA2c7N9l8t8X/dnSjUZRfHJ5LIivQS767Vs/Eqe31dghGfCqidqIlMnPtlv5enDv7N1CKmFTbLu1a9cnbFuRu2sd/haXctYIZXNE59REVUmAUFxMRVIN7bgdRz+gqVbbhRq3Cn7eFPpzLd7OMsnzE3791P3izUrGbsfr9nAL/P22dDWNkZXuw1UsYWiPFbeXatwJaItrRoGw9JvtruUf7VT+TSmtRkfHz1RdPl0fsdElUBV98Onrce78Dq6xem2ojC24rfH3vgdz7iDyxnbpWJFJ1E6UiM4jnPszOVaN3RL9LFUKj7cUHhMhW263X1cZoaFtiC3yK4jNbuqIjFyeJtdR2r7y5qrf189baRj6RmXOZYlfXBzttuDSvgXlOd7/JBsdHilKTavwIK3/8RXV7PyMfr7erDyWkSKd/ddMR8PmibVJOSdF12tVgZTlvY16M7cNOWMaRyPUGtabYbivbe6sMt5dY5xLhYzTsFbbrp08h9Ie4k5oJ26uUkTdm6qcDfLi729VdTP0xAV6fvHLcKblIZ3i5QV7HDXZ5noCBhvK4I/Wvt38HGnQ481HK570c/EDOw3xmbmT63t0D5qfMbL/xLfX3479bEZnCmiYTw+ONn6zWIqpR5DbUFriwN9DacFn3bwNpXwv7hjN+jSUtdq/bHC1SPweryV+7nE0z5Kb1Vm0kXkAmi3CeWP1w9s6Wu4sftSCh18qNX46/GLL8zhsHVBh96UNZ5kL9yw7d07gZu4EWs3T5yzzL26CQXHbCWDBGxkxLuiqYrAJuPv6kIhs6kn6PivGnP5cCGfpT6fUCsebO+kK25N4Ygq2SUxm8rCQmXGyoWt+qL16ZU8zzFuV+OTeo9vH4tJ8maP/zs3Z7y5ZLTZHnGdTiDKja9D1kMCm89w46T0SztpmR3KLeZpX7Vy/PWZDt7euJ8Zp3aKrruJaf373pQ9n3RnC5LO6kVmRGGsJzlZe6ZG3aMPPsm0fhByYIg+3yTM6FhbjFnv6aqGj6IZF3Cj+hla2BydzLBAJZWes8+aak9/dZjYF9atgeL3FIhaXXvmpumhIekIuxi8pHYNX5h/hg5rYW+oXE5Va3gYI5uyInsxXdPuLpQd/nZTYtGNwTd8IiMGDcaXE3LYsCHGNw4itzSIRPTkjt7ZIWz5gW874rVo/slRmH7MeBHXg3RNBzrLAKoTD0z0j+Ir8gLkZl596P9wro3magn7mNBezLkga1QhJ+0Oxn65wTFkxHqPXDG25ofjTo/TVl9+645Cney7NR7dXII80VSf0w/i3Po1VJkqttrS+L6yk9Wz2v1EFuUFr9iier6uNfgqU4SaB6tbqIpryvhB/07kIc3YFlUUvfPNxI27VBP5C7Ont1d5p9oM7WWKGLKOhaFqz+0Zzh1SidJlhtffndr7bJZjYS49dMc/nRmAKyyUvcHOvf9gbe28pQ7jCN5gJWvqXUoEmWneOyCDisEI1VnxfLUNfOmMm56ZRTlbXZo1iB5KMNAs3hceFv9sf2iYh0nXdEDRVu+cxExuezyaTqBoKPnrvc+pfUW3wk4rFLK8E4CdEQgp9PPt1hvl1d5Ht9FvuOFhlbnf4GdPe4HVDk/z0oQY4w4F7ax5ocAXr1B22vNm39M9XwdnmObXhRn4anIeAPCQwvdcnJ3LUWcM7s9eDuchpAwalTREmImVa5mWVaG0U9Bi/369bKpVWaXdTL0wy3oSLQaEAQAbl9XNvbSkM8bH1RMnswL9ImRhCBtXLA7jiLVzh64lOVVfW6+yCmWK1qmp3aJdVYWCGyWndGWWS2d7jE8wuY2NebBk1tdKZ39ITtWvRt1PWZzVidC3DSkAAGSWlSREXpK9qytuGT2VtQjtRTH4CVY3ckpMSpS0fj2iWkbJYTuMLXklFVp6unNK5oaE0tn4o544smqm/C42bgUAIFaqZu4TqibTEDfitSaE+ahpmVh5dcaHzADD+yHixywZGRkZ5UWuiSjePzY4id0d8ug+o0pI1ORdEfkpNsP00L6+yKY3gTdeRJrQilwsbBK6+3qfFO+J4PXy2pPiUeh4I2QlZ6TH2rAwl6hIPF5nrSzeKOyKNMvOsuaXu+T6Em2jMdcvRd+2CmwGSTTJWN4TwSjnsaKTNAtLrai5tCkSADC1shZHrqDFyfqGJNlPRL2zeDr3OY9VcxF9+fhb1Uj1TCa0NxarSSq37vijaqR6Ijm1pgpVI66nPd+iDZVi5dWGRnUPUrp2lfV2idp3B3F+vLN6y92unhjBI4vBAZrJHvofHaq/7LbxklfjgnFcyZf3ossTf/4hQ8JRz7Ex2WyooVV6Az+/xl/zxwWRZAoS7aC1qk8XcUEAzstmGREWpJ0v+JO3Oj3h0FJ3VlFxUpPP2M0POwd2/vgkk7gjj+CAINSJEEsTd/OkOe8IE20CCyQAEgLCSZNIpsSIqYQBtA0QquIqQEk2jhhIGBbjgQDfEwEp0keY7eKA4IKowLJ5s+V2ghGyEyLUgHQyjDxgDQRgDwEQ5Zmg6yHMbvFB1vNgCYRMMowYSRjaYoMguWgA+QgY5f64EmKQK/AnHQJY1h/CRBbUny9LIGTyXsRIwigW1B8rWkA+2EW5P48WMEQprkUAqdzWImAKAiBKcZFXwQ6BqNKBZVNcUFMIo1ZQn8+R4pBKcREjCWNVUOQgSSSJxBblqzWlB8ums6DSCGNTUGk3SXFIpbOIkYQBKSiSjQFQlr6ifLU+S5GQnBVUGmEIihcirYgUh0TOiphImHaCEqVXA4qiVMutlROy1mtLicSZKaJLKkGSCXpj/EgGRSozRUwlTCpBqfsYAcWZKMqXXUeSCsk8QQUSZov4IQJ5mQClmSdiKGEaCAp9Sg66NKS03KJZIYu2ZgbLxIqg0ghTFusg0pKWUpbEOIhphCEeKI2LBfwyN0RMI0zmcENoV0jQiGJAxDDCnA30kYxlDaAk2kMMJMzBrIcAz5MGEoV1ltuJtRCeMitYLktDDCKMr0BBd6Eg4rDMEkUEiRQoSIANLJd9IQYRBj+4IKAoKIhEnIVyFh07+EVShfKHD68FFlEIhdKHszwIgCiEAl0RYRgE+mw/swRCIoSynCY2iKaAtWC5PAnR7Y5gtgpt7zISmKV5EmIe4UwTum9lAuAX81nKbb8jCMjPRKGCCIeV0AV2LYH8rihuiCjzTeDXA0+oOMJJowj0DJOFkRt4LnnlJZgdCkHY24TA70wul9zmCMaAUPAz8mBSQyViMOHQDQpGC4PfmSlSvmdKm8GvJ3hQlYTzNajKYLIwinwlHKVBwePkwZT4SjgVg4JtRcDvzOWW85UX4usoeTDRiA0qlXD6JQqRqiEKfnfERgwnnHRB4TkUwJHkvSA1LJv34vtOURhYMjqjpfvxv6pAFWzhAMD6x6MP+F8AAAD//+csdAyjOgAA"); err != nil { + if err := gres.Add("H4sIAAAAAAAC/7SaCTTU6xvH3+xZQpYUIWKQMQyRZCnKMpaxRimNNWKEka0FpSxJKRRtF0W61ogkXZV935KUohDZR0Tof8rF/MaMJt1/59TUyfk83/f7Pu9veeaLRlHTcAIGAMAVt5toQPCLGawG9o7Odh4IG1esvaODqQktWNX6JuUgGkXPQPiD5BEcRAi4jacHztXllyQGMGLnCCEJkCb9+y8pnKuL809q93b7gxKVunCElmQ5OsMU3VBbVqvXjEAgWhDa1fAybUltuFlnqJSOXrnkhS4jNBqtVS5ZYwbuyckhW2TrZOtks5Gy8rlNMoZUKBhM1CHR+pK8KRUNAN+//9BqoiHfZgYAsF9W6wYyWl18pByxjv+pTONFmXsXZUrTd535tUweIpn/Ny/RiyJNFkVef3bR9tciiTvo/2Gi0aI+s0V9XwPUlYj1Le1wxgV9to7uMis4ImsgAISHp/UKTgffEsiP33CcnQdORgrnjVu0S0qvAa4Nl5Ix0aqp3aJdvWXVwnoLH1zevg4AwL5sJTZopZ8VFumkycHUqrMcAAAWyp1E/qmTyP/CSSTESSRpJ42WrNcvgp/6N51E/nQSCXVyKVlPNav0d5x0lNmGpcBJYmlrIAC4raP7gjDKt4N9CQRhh/2jHSHgIA7bOTu7Qi9VnRVa1eXlezNM0aNA5gpYNW+a+2cnG86fhv9+GS9Xd2dbojIStYgKbcMMUzQjPWGZG+yeMOIyFLnihPlvXHHC/OuKD2aJK7lGaN832ybdGj+Wht8XUJA6/ibv4Gq2eekF9hIWggCA9SsoOecQpORPgzKN0L5ZNq/x95R1/MF8oZs7rZy5AQCsv+uRu+cKPOIlxfnXI8J7yELf5Et/t34etHGd/IFXvpnai/44H98kthEAwL2CcnP+QG5Z8/2Tn9Hz7EIYC4E/dp8HHq/7ZQ9xLi3kexiurv9Hj1VQ1L8uOXm4Yhd0p3T/uIei59zK+rG9XkWNd/yi8pxWLRyCwk1M+b/uJLJV58wiVXXOtKzFprLMWbSNcQA1S1yVUttM9q7ANn4yqH9t8yY4DFmZ6WXVhmIouERNVe0DU2STLNpIqxpVY1Behco1lYBrN+ags3I/ZJXLZZc9+PBjrRnlP0+s35y9LmObdkjxnshgVFtYLpsvd4QBAMBgZRrnTP5DjeI/z7jf3GbIT7Hxx4SrmNMKzkucEtWN0ieSuHRHWKESf/x1BZuxbikFYYdd7t6QV4EPczdSmz7Kpr54yG284GoCAADB3y7mhKH0khtyf8dTMVixzs7e8guOtUnjYquo5stzllSaywAAxJctz02ivLsnJVezudK0kgXE15xnRogZSQAAjMJ3msXCP88sidO65AqBUwvsmtytKH6oaGPjTYMBw84U2oVHGhU04qEUAEBy2fLrSZc32fufn7VgXZ+Thvqhby//lJpZf244z3msyHfxEleuVtGIAQA4Lit4LUTw/68fieo4YcjVIdOLtJvxtqXZPcZ+BE8yhg6Cx3604pbfqezuSa4yhX1osy3/CBwAILZsVS5o1bkWhBReXPH8/emnu/9QKd1VvvU6gYZhvmBve0W4CABg028WNNn7HxZc7und3dUV90fvQT8ACBsPjxVcVHmWQBAeOB9nO6l53I+VNxgl/7w7a1XWbqmH1/ytk2MkLS+RnEHHFJ1oduWTaLft3R4L8c1J3VGJ3BYezVQLFzuJduu+rQAA5LILYYNqcHTBOKzk/rCeBAbh7OrgKnUU67CwFucYn21NR3hLk5BRIULbo+uualjYV53hUcdIj7uUGcnZl5dJ2ov6tZa6ozlaH3CWR27dbyJ0RpEzS+RbpE3Fscg71yfc9Yd8aj3b8ZYt+NKh2ROqExOF6d9me0IyH8px02gHAuB/ezWdCxMAbI8n3p8EQNBDJZ8KgH7M6w2fqXUe3/nxdKm68cMLGrAmpL9d2X9M1w0cchVf/7nMPVtas9ORU+yCmgds14wY7p/BnZObZsRwjJeCqL8ZFiTrJt5a85GVNrJCY/Ashm1YYO3LrRuDE4K2vvIvEQxs/ssJbRosxcBMl8BDs7aB22WM2oFNazsbK3fYhTNFx0+c+KvQ4S+5ElMeSxSK6W6o/yY2CWYq7aBeA7uOaZPkqsunRCdd+eUuhThc7JVEhjO2z5Q8akx7gBO8KBCrWhuxXXzs6fnkKoEDHY6cVwUurDmtbor5Tsc+fEpm7DNzuqqO8HuMBkIoSMtOBnNUbNpKkfuaxpRX7lvVU697ZyPUJw0/aVHTsZcKxbtJxkvt4kq7E5+SFp8SECR80YFdKy9flnGctf5phxxd9B58Q3NbDjXvpfUaVhs+bOBwPpQ0rjB97pNAAW24mujk91tPthsdoPYxw5+beadx6vlq780hJbffm+fbMg5KsO9JOri7aHbddJx7rl8i12ndxKyjj9UbgxIOfV/3bcgf8/3q8K3P1N8yVfMFOfvTDzTRwh+PDVEDtSLRw8p/0w9fTmBiURJzm5T4IpFz94RqrqnlzuP7Pomh310wkOkfF/Z7fFDY2C+4cGb9t1mYmYJKZ5jn7swgTeeHrOd3KNx9cXanUEQgTHmoslKU4av51dP3P08OBxxUfvAmm7bHJS36vH+BSHt/ldPodoWBs43eA/gJm72oGofQGN1Pvh+x5870RFtdtbj+GbQbjQvbiscwKaaznz0ZY36qMHBTU1lMi4XqfQtpUy7eN37WwxOe1hdfKFx9c4VBOf8Tfr0EPdOVHLkNWO387JsCfOpK9f4uQfzeDPXDx1xF7ZjSPr84hI0rp2ppxQl2weP3vuozCmpJfH4tmLnytNC13H4J4ZcFGyXT2IvdTl8KvYDUgbF4J3E14fQ0w+DFuxmuh5iYPXnly7dvUG4gsJPtmIrm2z2qkrqpJrPV9RfyNLAcAQbnMcIpXfWNfPb3D1fZ2Pib+RiJ1Ivn0hp1PtVQiX6niXw+Yx80XvOah10cZzXGehjdyfCksupxwsdL31EbdNwDThmEGo3epeM5sBXHLXXsfl9PRBCfnnDjxygsXntYPPvbl/qXHpG3H0bPrN+t9eV8MOuuB71uqrVTw1w8hpvp2R5VwOCOterPn9A69B1N0vW+oE3nrFiXa+Al1DBp9Dh9sNdkO5/5l/IDmGFL1x4ny9QH48/HxIrWhuWz0D9+LrpKfUBV8Yzl7D/PY67BEDYDWePrDrwy4RhZFcdXEWZefK3D7r2J5Biu414Bqm4koE9G5vDH9CD1zQ6wsFdeG2CWdbDD6bJYwV3Y4rgap2G1IvqzOsIszVMb37BOCh2x3wML6rAxV8gee2sqqGrWk3TLTpHnaKyixt3xmCb5nZu+2DAlPlQbONt8pCxfiTdPcWwjVcCzzIOolNZK9YS10u4YwaLAcy0sQxfdeO+Yafnk2Cg1S6QHuak2W5SbndsYY/3O8Ob2zQXT3eF1szPo1n7+aUSvikHKfStfh4MhtSH9R9rN+7I6tGJxZ8P65MbVG9EntL6UJ3O/wU1fuukH5+A4VP5graSAgFZlpB/tnqSRtzNO8n4fLIMsnprOHg31KmmDVfp6xd+wcECclPThEGYOTawteLja+pjicEnKmvfnOq7Fzmq2lU4nRH6r0b1XGnxFIXL32fuM5+EP8t8FDz8QKM1ydN/2oOOQePXFNM5Xp7OyhJWf+l1//Cjn9ZeRoHf7ccmF+M4u/1Ca1LPmx3zNmvg7ZO+sqo2PwKjxf80+mifv51z1pU33pI5lddjIOs2aiK3BtPm0qwW2CeEl3HVjBuT6gmecbzhotXNG5IZoMvL3jObkap2oPvziSQtPo92wHqsfflRRwWt7lPwxnRj3dT4B6jnZ+D179uwa2TOCN/hbLWOaxkCpSqnZNmN3oXEMA2uka62Y5Eh7pw5foZ53Xo+TwpHRtu7Xexr+Gf92xO4RC8vQo2l4CbXpqogi7gOxCi3MnMb5ogIt/CV6Y9eaHVPOluU5nJrKaX0fK68chZAWvC0TbGPVE52/T5Mrtb0O9c/X79hYjoawspLDtsnpoUXXn43Xb1DnaCtlETDD2cp7MnoUmsBMS/JvpFjEj4a1R7CEx+sy7HCV52Jt73Xf35v2T6WMUtVOrIS92T9fmdg30CLiDRC0eq4mvmvoxl416nsdFr5kqflRWfiDmotXwPFAoczq1uLizqOaD5JS7ZXKBustk1/oXuly1o6b+RAfm6hs/v7QULTDJrOsV62Kg5ZswzPB1Gyazim5BRFCl5HyrPGDAnCGXH7v0bwsIY6tX7Jt39zprnjfejMkhPn1yQFquzS4hbXKV8eHh9pm4l7kYtandu3zfMdtIxwa+2FbenHM30yfkwVbNhXf6B+I0X2KyA2MCzegQelJ6r8sfGUisKmlS/qMzHGNAGOWcOMe5joMzbZTQ+dkPxaHqEwm6HhVi+yxkpxco/zkZBlyf8xo7GjhnowevFZibI2c8sHBL7LoYy32k6hLDW45VJ/zu9un8ibzcLmM57Lidg3kJu4vfRt45+mp74MdI1M6fs+S/grlZdGHjdwVv9mbkNe4LekGfuR8+nGvmSsDcV2lFeadmbUNceG2XZV7zQqS6C/2Z8ur9+NgO57fPE7L/u32y0psxJSR0NPk/uvpr3bsTTr8pHejvvZkE9Ozexqz/klnmvkPFp5PlbFxpufytUy9INNw8kyAvkWjCcPw0GUhnx2hw7q6rJfU4zhf92ZH1ygJnHi9ETY94Le7b3eckKA5J04N1XTAW1BBumPfC5u2ta8TeZJHugfyu2yaygS3mZvtu1zm+7o/S6rJKIpPJpcV6SXYXa9l41fy/J4CIzwLVj1RE5k28cl+K08f/p2tQ0gtbJJ1r37l6sx1M2pnvcPX6lrGCalsnviMiqgyCQiKj6lAurEFr+PwFyzdcr1Q43bZxxtKZ77dxVk+YW7av5+6X6xZydj9eM0Gfpm3z4a2tjG62m2giisU5bHy7lqFKxFtadUwGJZ+s92l/Kudyqcxrc34hOiJosun8zsmqgSquh8+bT3ehdfRLc6wFYWxFb899sbveOZtXM7YLhUrOonSkRrBcZxjZy7XuqGboo+l0vDhhsJjKmzT7e7jMjMstAW5RXYdadlVFUmRw9vsOtLaX9Zc/fvqaSMdS8/4rLEH0gc3Z7vdr4R/QXm+xw/JRodXmmLzCix4+098dTUrH6O/pwcrr0WkenffEfPxoGlSTUbeftHVamUwZWlfg+7MTVfOnMbxCLWm12Yq3n2rC7ucV+cY72Ix4xS85YZLJ/+B9JeYA9ppm5s0YeemCnezvNjbW0X9PB1RkbF/3CK8SWl4t0hZwQ53fZaJjoDxtiL4o7V/Bx93OvRYwyHWi34mZmC/MTYrf2pth/ZR4zNe/pf4/vLbqY/N5EwVDePxwcnWbxZTKfUYagtaWxzoazgt+LSDt6mE/8Vtu0GXlrpW648Vrh6BsQlW7ueSTvsovVWZyRCRC6DdJpQ/Xj+wpa/h+u5LqXTwoVbjr8cvvjCHw9YFHXpT1niSvXDDtnfvBG7gRqzdPHHOMnfrJhQcs5UMErGREe+KpioCm4y/qwuFzKadoOO/aszlw4V8lvZ8Qq14sL2Trrg1lSOqZJfEbBoLC5UZKxe26ovWp1fyPMe4XY1P6j2+dSwm2Zs94e/cnPHmktFme0QsnUCUG1+HrIcENp/h+knpl3bSMjuUW8zTv2rl+OszHby1cT8zTu0UXXcT0/r3val7PunOFiSf1YvMjMJYT3Ky9kyNukcffJJlfT/kwBB9vkmY0bG2GLPe01UNH0UzL+BG9TO1sDk6WWGBSio9Z5811Z7+6jCxL6xbA8XvKRC1uvbMDdNDQ9IRdjF4Se0avjD/TB3Wwt5QuZyq1vAwRjZlRfZiuqbdXSg7/K2mpKLrg2/4REYMGowvJ+awYUOMrx9EbmkQiejJHb29Q9jyA992xGvR/JOjMP2Y8SKu+xmaDnSWAVQn7pvoH8VX5AXIzbz60P/hXBvN1RL2MaG9mHNB1qhCTtodjP1yg2PIiPUeuWJszQ/HnR6nr7781h2FOtl3czy6uQR5oqk+px/Eu/VrqDJVbLWl8X1lJ6tntfuJLMoLXrFF9Xxda/BVpgg1D1a3UBXX1PGD/p3IQ5pxLaooeucbSRt3qSbxF2ZPb6/yTrMZ2ssUMWQdB0PVntsznDukEqXLDK+/M7X32SzHwlx66LZ/BjMAV1goe4Ode//B2tp5Sx3GEbzBStbUu5QIMtO8d0AGFYMRqrPi+Wob+DIYNz0zi3K2ujRrED2UaKBZvC88LOHZ/tAwD5Ou6YCird45SVnc9ng0nUDRUMrXu5/T+opuhp1WKGR5JwA7IxBS6OfbrTfKq72PbqPfcMPDKnO/wc+e9gKrHZ7mpQsxxh8K2lnzQoEvQaHstOeNvqd7vg7OMM2vCzPw1eQ8AOAhhe+5ODuXo84Y3J+9HM5DSBk0KmmIMBMr1zItq0Jpp6LF/v162VSrskq7mXphlvUkWgwIAwA2Lqube2lJZ4yPqydOZgX6RcjCEDauWBzGEWvnDl1LSpq+tl5lFcoUrVNTu0W7qgoFN0pJ7coql872GJ9gchsb82DJqq+Vzv6QkqZfjbqXujirE6FvG1IAAMgsK0mIvCR7V1fcMnoqaxHai2LwE6xu5JSYlChp/XpEtYySw3YYW/JKKrT0dOeUzA0JpbPxRz1xZNVM+V1s3AoAQKxUzdwnVE2WIW7Ea00I81HTMrHy6swPWQGG90LEj1kyMjIyyotcE1G8d2xwErs75NE9RpWQqMk7IvJTbIYZoX19kU1vAq+/iDShFblY2CR05/U+Kd4TwevltSfFo9AJRshKzkiPtWFhLlGReLzOWlm8UdgVaZadZc0vd8n1JdlGY2IvRd+yCmwGyTQpWN4TwSjnsaKTNAtLrai5tCkSADC1shZHrqDFyfqGJNlPRL2zeDr3OY9VcxF9+fhb1Uj1TBa0NxarSSq37vijaqR6IiWtpgpVI66nPd+iDZVi5dWGRnX3U7t2lfV2idp3B3F+vL16y52unhjBI4vBAZrJHvofHaq/7LbxklfjgnFcyZf3ossTf/4hQ8JRz7Ex2WyooVV6Az+/xl/zxwWRZAoS7aC1qk8XcUEA1lhRGRMWpJ0v+JPn8pYes9SdVVSc1OQzdvPDzoGdPz7JJO7IIzggCHUixNLE3TxpzjvCRJvAAgmAxIBw0iSSKTFiKmEAbQOEqrgKUJKNIwYShsV4IMD3RECK9BFmuzgguCAqsGzebLmdYITshAg1IJ0MIw9YAwHYQwBEeSboegizW3yQ9dxfAiGTDCNGEoa22CBILhpAPgJGuT+uhBjkCvzJgACW9YcwkQX158sSCJm8FzGSMIoF9ceKFpAPdlHuz6MFDFGKaxFAKre1CJiCAIhSXORVsEMgqnRg2RQX1BTCqBXU53OkOKRSXMRIwlgVFDlIEkkisUX5ak3pwbLpLKg0wtgUVNoNUhxS6SxiJGFACopkYwCUpa8oX63PUiQkZwWVRhiC4oVIKyLFIZGzIiYSpp2gROnVgKIo1XJr5YSs9dpSInFmiuiSSpBkgt4YP5JBkcpMEVMJk0pQ6j5GQHEmivJl15GkQjJPUIGE2SJ+iEBeJkBp5okYSpgGgkKfkoMuDSktt2hWyKKtmcEysSKoNMKUxTqItOSllCUxDmIaYYgHSuNiAb/MDRHTCJM53BDaFRI0ohgQMYwwZwN9JGNZAyiJ9hADCXMw6yHA86SBRGGd5XZiLYSnzAqWy9IQgwjjK1DQHSiIOCyzRBFBIgUKEmADy2VfiEGEwQ8uCCgKCiIRZ6GcRccOfpFUofzhw2uBRRRCofThLA8CIAqhQFdEGAaBPtvPLIGQCKEsp4kNoilgLVguT0J0uyOYrULbu4wEZmmehJhHONOE7luZAPjFfJZy228LAvIzUaggwmEldIFdSyC/K4obIsp8E/j1wBMqjnDSKAI9w2Rh5AaeS155CWaHQhD2NiHwO5PLJbc5gjEgFPyMPJjUUIkYTDh0g4LRwuB3ZoqU75nSZvDrCR5UJeF8DaoymCyMIl8JR2lQ8Dh5MCW+Ek7FoGBbEfA7c7nlfOWF+DpKHkw0YoNKJZx+iUKkaoiC3x2xEcMJJ11QeA4FcCR5L0gNy+a9+L5TFAaWjM5o6X78rypQBVs4ALD+8egD/hcAAP//X3/+baM6AAA="); err != nil { panic("add binary content to resource manager failed: " + err.Error()) } } From a2bb8ad2f2d6eca00eaef8879111cd311cb93f3d Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 2 Aug 2021 21:13:00 +0800 Subject: [PATCH 417/492] add more unit testing case for package gdb --- database/gdb/gdb_z_mysql_model_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 8121c2d49..ea4e32049 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -2289,6 +2289,21 @@ func Test_Model_Prefix(t *testing.T) { t.Assert(r[0]["id"], "1") t.Assert(r[1]["id"], "2") }) + // Select with alias to struct. + gtest.C(t, func(t *gtest.T) { + type User struct { + Id int + Passport string + Password string + NickName string + } + var users []User + err := db.Model(table+" u").Where("u.id in (?)", g.Slice{1, 5}).Order("u.id asc").Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 1) + t.Assert(users[1].Id, 5) + }) // Select with alias and join statement. gtest.C(t, func(t *gtest.T) { r, err := db.Model(table+" as u1").LeftJoin(table+" as u2", "u2.id=u1.id").Where("u1.id in (?)", g.Slice{1, 2}).Order("u1.id asc").All() From 114cdb23514154feb6004d69596b61e51b81bd8b Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 3 Aug 2021 19:37:25 +0800 Subject: [PATCH 418/492] improve package gtimer --- os/gtimer/gtimer_queue.go | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/os/gtimer/gtimer_queue.go b/os/gtimer/gtimer_queue.go index 9fb6af73d..6dd1dfc5f 100644 --- a/os/gtimer/gtimer_queue.go +++ b/os/gtimer/gtimer_queue.go @@ -18,7 +18,7 @@ import ( // high priority is served before an element with low priority. // priorityQueue is based on heap structure. type priorityQueue struct { - mu sync.RWMutex + mu sync.Mutex // use sync.Mutex instead of sync.RWMutex for performance purpose. heap *priorityQueueHeap // the underlying queue items manager using heap. latestPriority *gtype.Int64 // latestPriority stores the most priority value of the heap, which is used to check if necessary to call the Pop of heap by Timer. } @@ -30,8 +30,8 @@ type priorityQueueHeap struct { // priorityQueueItem stores the queue item which has a `priority` attribute to sort itself in heap. type priorityQueueItem struct { - value interface{} - priority int64 + value interface{} // queue value. + priority int64 // The lesser the `priority` value the higher priority of the `value`, for example: priority of 0 is greater than priority of 1. } // newPriorityQueue creates and returns a priority queue. @@ -48,8 +48,8 @@ func newPriorityQueue() *priorityQueue { // Len retrieves and returns the length of the queue. func (q *priorityQueue) Len() int { - q.mu.RLock() - defer q.mu.RUnlock() + q.mu.Lock() + defer q.mu.Unlock() return q.heap.Len() } @@ -63,42 +63,27 @@ func (q *priorityQueue) LatestPriority() int64 { // The lesser the `priority` value the higher priority of the `value`. func (q *priorityQueue) Push(value interface{}, priority int64) { q.mu.Lock() + defer q.mu.Unlock() heap.Push(q.heap, priorityQueueItem{ value: value, priority: priority, }) - q.mu.Unlock() + // Update the minimum priority using atomic operation. - for { - latestPriority := q.latestPriority.Val() - if priority >= latestPriority { - break - } - if q.latestPriority.Cas(latestPriority, priority) { - break - } + if priority < q.latestPriority.Val() { + q.latestPriority.Set(priority) } } // Pop retrieves, removes and returns the most high priority value from the queue. func (q *priorityQueue) Pop() interface{} { q.mu.Lock() + defer q.mu.Unlock() if v := heap.Pop(q.heap); v != nil { item := v.(priorityQueueItem) - q.mu.Unlock() // Update the minimum priority using atomic operation. - for { - latestPriority := q.latestPriority.Val() - if item.priority >= latestPriority { - break - } - if q.latestPriority.Cas(latestPriority, item.priority) { - break - } - } + q.latestPriority.Set(item.priority) return item.value - } else { - q.mu.Unlock() } return nil } From 28cb0bef25c9b4ad6e810a9a32d16ff8f93788e4 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 3 Aug 2021 20:34:26 +0800 Subject: [PATCH 419/492] change logger of DB from glog.Logger to interface Logger --- database/gdb/gdb.go | 17 ++++++++++------- database/gdb/gdb_core.go | 4 ++-- database/gdb/gdb_core_config.go | 6 ++---- database/gdb/gdb_core_logger.go | 27 +++++++++++++++++++++++++++ database/gdb/gdb_func.go | 7 ++++--- database/gdb/gdb_z_mysql_ctx_test.go | 4 ++-- frame/gins/gins_database.go | 6 ++++-- 7 files changed, 51 insertions(+), 20 deletions(-) create mode 100644 database/gdb/gdb_core_logger.go diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index dd5232d08..e6b88dfd5 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -152,8 +152,8 @@ type DB interface { GetGroup() string // See Core.GetGroup. SetDryRun(enabled bool) // See Core.SetDryRun. GetDryRun() bool // See Core.GetDryRun. - SetLogger(logger *glog.Logger) // See Core.SetLogger. - GetLogger() *glog.Logger // See Core.GetLogger. + SetLogger(logger Logger) // See Core.SetLogger. + GetLogger() Logger // See Core.GetLogger. GetConfig() *ConfigNode // See Core.GetConfig. SetMaxIdleConnCount(n int) // See Core.SetMaxIdleConnCount. SetMaxOpenConnCount(n int) // See Core.SetMaxOpenConnCount. @@ -179,7 +179,7 @@ type Core struct { debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime. cache *gcache.Cache // Cache manager, SQL result cache only. schema *gtype.String // Custom schema for this object. - logger *glog.Logger // Logger. + logger Logger // Logger for logging functionality. config *ConfigNode // Current config node. } @@ -200,6 +200,12 @@ type Link interface { IsTransaction() bool } +// Logger is the logging interface for DB. +type Logger interface { + Error(ctx context.Context, s string) + Debug(ctx context.Context, s string) +} + // Sql is the sql recording struct. type Sql struct { Sql string // SQL string(may contain reserved char '?'). @@ -270,9 +276,6 @@ const ( ) var ( - // ErrNoRows is alias of sql.ErrNoRows. - ErrNoRows = sql.ErrNoRows - // instances is the management map for instances. instances = gmap.NewStrAnyMap(true) @@ -344,7 +347,7 @@ func New(group ...string) (db DB, err error) { debug: gtype.NewBool(), cache: gcache.New(), schema: gtype.NewString(), - logger: glog.New(), + logger: LoggerImp{glog.New()}, config: node, } if v, ok := driverMap[node.Type]; ok { diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 3b0cbe7f5..abbb101f8 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -659,9 +659,9 @@ func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) { s := fmt.Sprintf("[%3d ms] [%s] %s%s", sql.End-sql.Start, sql.Group, transactionIdStr, sql.Format) if sql.Error != nil { s += "\nError: " + sql.Error.Error() - c.logger.Ctx(ctx).Error(s) + c.logger.Error(ctx, s) } else { - c.logger.Ctx(ctx).Debug(s) + c.logger.Debug(ctx, s) } } diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index 0982d6130..3f5fb818e 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -12,8 +12,6 @@ import ( "time" "github.com/gogf/gf/os/gcache" - - "github.com/gogf/gf/os/glog" ) // Config is the configuration management object. @@ -135,12 +133,12 @@ func IsConfigured() bool { } // SetLogger sets the logger for orm. -func (c *Core) SetLogger(logger *glog.Logger) { +func (c *Core) SetLogger(logger Logger) { c.logger = logger } // GetLogger returns the logger of the orm. -func (c *Core) GetLogger() *glog.Logger { +func (c *Core) GetLogger() Logger { return c.logger } diff --git a/database/gdb/gdb_core_logger.go b/database/gdb/gdb_core_logger.go new file mode 100644 index 000000000..e9b5a3fb8 --- /dev/null +++ b/database/gdb/gdb_core_logger.go @@ -0,0 +1,27 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +import ( + "context" + "github.com/gogf/gf/os/glog" +) + +// LoggerImp is the default implementation of interface Logger for DB. +type LoggerImp struct { + *glog.Logger +} + +// Error implements function Error for interface Logger. +func (l LoggerImp) Error(ctx context.Context, s string) { + l.Ctx(ctx).Error(s) +} + +// Debug implements function Debug for interface Logger. +func (l LoggerImp) Debug(ctx context.Context, s string) { + l.Ctx(ctx).Debug(s) +} diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 0919432d5..fab36478e 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -8,6 +8,7 @@ package gdb import ( "bytes" + "database/sql" "fmt" "reflect" "regexp" @@ -805,9 +806,9 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i } // formatError customizes and returns the SQL error. -func formatError(err error, sql string, args ...interface{}) error { - if err != nil && err != ErrNoRows { - return gerror.NewCodef(gerror.CodeDbOperationError, "%s, %s\n", err.Error(), FormatSqlWithArgs(sql, args)) +func formatError(err error, s string, args ...interface{}) error { + if err != nil && err != sql.ErrNoRows { + return gerror.NewCodef(gerror.CodeDbOperationError, "%s, %s\n", err.Error(), FormatSqlWithArgs(s, args)) } return err } diff --git a/database/gdb/gdb_z_mysql_ctx_test.go b/database/gdb/gdb_z_mysql_ctx_test.go index 0d3cf753a..9143bab77 100644 --- a/database/gdb/gdb_z_mysql_ctx_test.go +++ b/database/gdb/gdb_z_mysql_ctx_test.go @@ -30,7 +30,7 @@ func Test_Ctx(t *testing.T) { } func Test_Ctx_Query(t *testing.T) { - db.GetLogger().SetCtxKeys("SpanId", "TraceId") + db.GetLogger().(gdb.LoggerImp).SetCtxKeys("SpanId", "TraceId") gtest.C(t, func(t *gtest.T) { db.SetDebug(true) defer db.SetDebug(false) @@ -48,7 +48,7 @@ func Test_Ctx_Query(t *testing.T) { func Test_Ctx_Model(t *testing.T) { table := createInitTable() defer dropTable(table) - db.GetLogger().SetCtxKeys("SpanId", "TraceId") + db.GetLogger().(gdb.LoggerImp).SetCtxKeys("SpanId", "TraceId") gtest.C(t, func(t *gtest.T) { db.SetDebug(true) defer db.SetDebug(false) diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index ec854722e..ebd65b982 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -132,8 +132,10 @@ func Database(name ...string) gdb.DB { loggerConfigMap = Config().GetMap(configNodeKey) } if len(loggerConfigMap) > 0 { - if err := db.GetLogger().SetConfigWithMap(loggerConfigMap); err != nil { - panic(err) + if logger, ok := db.GetLogger().(gdb.LoggerImp); ok { + if err := logger.SetConfigWithMap(loggerConfigMap); err != nil { + panic(err) + } } } } From a4497ed547ebfc06d4df6d8d9248c083a78f726b Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 3 Aug 2021 21:32:05 +0800 Subject: [PATCH 420/492] improve gdb.Modle for where holder --- database/gdb/gdb_model.go | 86 ++++++++++++++--------------- database/gdb/gdb_model_condition.go | 44 +++++++-------- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 195e551cd..a9d1fe2dd 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -19,38 +19,38 @@ import ( // Model is core struct implementing the DAO for ORM. type Model struct { - db DB // Underlying DB interface. - tx *TX // Underlying TX interface. - rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model. - schema string // Custom database schema. - linkType int // Mark for operation on master or slave. - tablesInit string // Table names when model initialization. - tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud". - fields string // Operation fields, multiple fields joined using char ','. - fieldsEx string // Excluded operation fields, multiple fields joined using char ','. - withArray []interface{} // Arguments for With feature. - withAll bool // Enable model association operations on all objects that have "with" tag in the struct. - extraArgs []interface{} // Extra custom arguments for sql, which are prepended to the arguments before sql committed to underlying driver. - whereHolder []*whereHolder // Condition strings for where operation. - groupBy string // Used for "group by" statement. - orderBy string // Used for "order by" statement. - having []interface{} // Used for "having..." statement. - start int // Used for "select ... start, limit ..." statement. - limit int // Used for "select ... start, limit ..." statement. - option int // Option for extra operation features. - offset int // Offset statement for some databases grammar. - data interface{} // Data for operation, which can be type of map/[]map/struct/*struct/string, etc. - batch int // Batch number for batch Insert/Replace/Save operations. - filter bool // Filter data and where key-value pairs according to the fields of the table. - distinct string // Force the query to only return distinct results. - lockInfo string // Lock for update or in shared lock. - cacheEnabled bool // Enable sql result cache feature. - cacheDuration time.Duration // Cache TTL duration. - cacheName string // Cache name for custom operation. - unscoped bool // Disables soft deleting features when select/delete operations. - safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model. - onDuplicate interface{} // onDuplicate is used for ON "DUPLICATE KEY UPDATE" statement. - onDuplicateEx interface{} // onDuplicateEx is used for excluding some columns ON "DUPLICATE KEY UPDATE" statement. + db DB // Underlying DB interface. + tx *TX // Underlying TX interface. + rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model. + schema string // Custom database schema. + linkType int // Mark for operation on master or slave. + tablesInit string // Table names when model initialization. + tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud". + fields string // Operation fields, multiple fields joined using char ','. + fieldsEx string // Excluded operation fields, multiple fields joined using char ','. + withArray []interface{} // Arguments for With feature. + withAll bool // Enable model association operations on all objects that have "with" tag in the struct. + extraArgs []interface{} // Extra custom arguments for sql, which are prepended to the arguments before sql committed to underlying driver. + whereHolder []ModelWhereHolder // Condition strings for where operation. + groupBy string // Used for "group by" statement. + orderBy string // Used for "order by" statement. + having []interface{} // Used for "having..." statement. + start int // Used for "select ... start, limit ..." statement. + limit int // Used for "select ... start, limit ..." statement. + option int // Option for extra operation features. + offset int // Offset statement for some databases grammar. + data interface{} // Data for operation, which can be type of map/[]map/struct/*struct/string, etc. + batch int // Batch number for batch Insert/Replace/Save operations. + filter bool // Filter data and where key-value pairs according to the fields of the table. + distinct string // Force the query to only return distinct results. + lockInfo string // Lock for update or in shared lock. + cacheEnabled bool // Enable sql result cache feature. + cacheDuration time.Duration // Cache TTL duration (< 1 for removing cache, >= 0 for saving cache). + cacheName string // Cache name for custom operation. + unscoped bool // Disables soft deleting features when select/delete operations. + safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model. + onDuplicate interface{} // onDuplicate is used for ON "DUPLICATE KEY UPDATE" statement. + onDuplicateEx interface{} // onDuplicateEx is used for excluding some columns ON "DUPLICATE KEY UPDATE" statement. } // ModelHandler is a function that handles given Model and returns a new Model that is custom modified. @@ -60,19 +60,19 @@ type ModelHandler func(m *Model) *Model // It returns true if it wants continue chunking, or else it returns false to stop chunking. type ChunkHandler func(result Result, err error) bool -// whereHolder is the holder for where condition preparing. -type whereHolder struct { - operator int // Operator for this holder. - where interface{} // Where parameter. - args []interface{} // Arguments for where parameter. +// ModelWhereHolder is the holder for where condition preparing. +type ModelWhereHolder struct { + Operator int // Operator for this holder. + Where interface{} // Where parameter, which can commonly be type of string/map/struct. + Args []interface{} // Arguments for where parameter. } const ( - linkTypeMaster = 1 - linkTypeSlave = 2 - whereHolderWhere = 1 - whereHolderAnd = 2 - whereHolderOr = 3 + linkTypeMaster = 1 + linkTypeSlave = 2 + whereHolderOperatorWhere = 1 + whereHolderOperatorAnd = 2 + whereHolderOperatorOr = 3 ) // Table is alias of Core.Model. @@ -262,7 +262,7 @@ func (m *Model) Clone() *Model { copy(newModel.extraArgs, m.extraArgs) } if n := len(m.whereHolder); n > 0 { - newModel.whereHolder = make([]*whereHolder, n) + newModel.whereHolder = make([]ModelWhereHolder, n) copy(newModel.whereHolder, m.whereHolder) } if n := len(m.withArray); n > 0 { diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index c98d9485e..933c3ede9 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -27,12 +27,12 @@ import ( func (m *Model) Where(where interface{}, args ...interface{}) *Model { model := m.getModel() if model.whereHolder == nil { - model.whereHolder = make([]*whereHolder, 0) + model.whereHolder = make([]ModelWhereHolder, 0) } - model.whereHolder = append(model.whereHolder, &whereHolder{ - operator: whereHolderWhere, - where: where, - args: args, + model.whereHolder = append(model.whereHolder, ModelWhereHolder{ + Operator: whereHolderOperatorWhere, + Where: where, + Args: args, }) return model } @@ -149,12 +149,12 @@ func (m *Model) WhereNotNull(columns ...string) *Model { func (m *Model) WhereOr(where interface{}, args ...interface{}) *Model { model := m.getModel() if model.whereHolder == nil { - model.whereHolder = make([]*whereHolder, 0) + model.whereHolder = make([]ModelWhereHolder, 0) } - model.whereHolder = append(model.whereHolder, &whereHolder{ - operator: whereHolderOr, - where: where, - args: args, + model.whereHolder = append(model.whereHolder, ModelWhereHolder{ + Operator: whereHolderOperatorOr, + Where: where, + Args: args, }) return model } @@ -248,12 +248,12 @@ func (m *Model) Group(groupBy string) *Model { func (m *Model) And(where interface{}, args ...interface{}) *Model { model := m.getModel() if model.whereHolder == nil { - model.whereHolder = make([]*whereHolder, 0) + model.whereHolder = make([]ModelWhereHolder, 0) } - model.whereHolder = append(model.whereHolder, &whereHolder{ - operator: whereHolderAnd, - where: where, - args: args, + model.whereHolder = append(model.whereHolder, ModelWhereHolder{ + Operator: whereHolderOperatorAnd, + Where: where, + Args: args, }) return model } @@ -382,11 +382,11 @@ func (m *Model) ForPage(page, limit int) *Model { func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) { if len(m.whereHolder) > 0 { for _, v := range m.whereHolder { - switch v.operator { - case whereHolderWhere: + switch v.Operator { + case whereHolderOperatorWhere: if conditionWhere == "" { newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, + m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, ) if len(newWhere) > 0 { conditionWhere = newWhere @@ -396,9 +396,9 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh } fallthrough - case whereHolderAnd: + case whereHolderOperatorAnd: newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, + m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, ) if len(newWhere) > 0 { if len(conditionWhere) == 0 { @@ -411,9 +411,9 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh conditionArgs = append(conditionArgs, newArgs...) } - case whereHolderOr: + case whereHolderOperatorOr: newWhere, newArgs := formatWhere( - m.db, v.where, v.args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, + m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, ) if len(newWhere) > 0 { if len(conditionWhere) == 0 { From 685bf56a30ad987d18c2e4e6be284bfb5397d08a Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 3 Aug 2021 22:21:20 +0800 Subject: [PATCH 421/492] fix issue #1325 --- database/gdb/gdb_model_with.go | 19 ++- .../gdb/gdb_z_mysql_association_with_test.go | 125 +++++++++++++++++- internal/structs/structs_field.go | 61 +++++++-- internal/structs/structs_z_unit_test.go | 12 +- util/gvalid/gvalid_validator_check_struct.go | 6 +- 5 files changed, 202 insertions(+), 21 deletions(-) diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index f2bae7b1e..52ae7d617 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -63,7 +63,11 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { err error allowedTypeStrArray = make([]string, 0) ) - fieldMap, err := structs.FieldMap(pointer, nil, false) + fieldMap, err := structs.FieldMap(structs.FieldMapInput{ + Pointer: pointer, + PriorityTagArray: nil, + RecursiveOption: structs.RecursiveOptionEmbeddedNoTag, + }) if err != nil { return err } @@ -79,7 +83,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") withItemReflectValueTypeStr = gstr.TrimAll(withItemReflectValueType.String(), "*[]") ) - // It does select operation if the field type is in the specified with type array. + // It does select operation if the field type is in the specified "with" type array. if gstr.Compare(fieldTypeStr, withItemReflectValueTypeStr) == 0 { allowedTypeStrArray = append(allowedTypeStrArray, fieldTypeStr) } @@ -94,6 +98,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { if parsedTagOutput.With == "" { continue } + // Just handler "with" type attribute struct. if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) { continue } @@ -120,8 +125,8 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { if relatedFieldValue == nil { return gerror.NewCodef( gerror.CodeInvalidParameter, - `cannot find the related value for attribute name "%s" of with tag "%s"`, - relatedAttrName, parsedTagOutput.With, + `cannot find the related value of attribute name "%s" in with tag "%s" for attribute "%s.%s"`, + relatedAttrName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(), ) } bindToReflectValue := field.Value @@ -173,7 +178,11 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { err error allowedTypeStrArray = make([]string, 0) ) - fieldMap, err := structs.FieldMap(pointer, nil, false) + fieldMap, err := structs.FieldMap(structs.FieldMapInput{ + Pointer: pointer, + PriorityTagArray: nil, + RecursiveOption: structs.RecursiveOptionEmbeddedNoTag, + }) if err != nil { return err } diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index 5a102fa12..d02316e41 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -874,7 +874,7 @@ PRIMARY KEY (id) }) } -func Test_Table_Relation_WithAll_Embedded(t *testing.T) { +func Test_Table_Relation_WithAll_Embedded_With_SelfMaintained_Attributes(t *testing.T) { var ( tableUser = "user" tableUserDetail = "user_detail" @@ -989,6 +989,129 @@ PRIMARY KEY (id) }) } +func Test_Table_Relation_WithAll_Embedded_Without_SelfMaintained_Attributes(t *testing.T) { + var ( + tableUser = "user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +uid int(10) unsigned NOT NULL AUTO_INCREMENT, +address varchar(45) NOT NULL, +PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +uid int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + // For Test Only + type UserEmbedded struct { + Id int `json:"id"` + Name string `json:"name"` + } + + type User struct { + gmeta.Meta `orm:"table:user"` + *UserDetail `orm:"with:uid=id"` + UserEmbedded + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + db.SetDebug(true) + defer db.SetDebug(false) + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} + func Test_Table_Relation_WithAll_Embedded_WithoutMeta(t *testing.T) { var ( tableUser = "user" diff --git a/internal/structs/structs_field.go b/internal/structs/structs_field.go index 81ae1d960..1af783adf 100644 --- a/internal/structs/structs_field.go +++ b/internal/structs/structs_field.go @@ -29,6 +29,11 @@ func (f *Field) IsEmbedded() bool { return f.Field.Anonymous } +// TagStr returns the tag string of the field. +func (f *Field) TagStr() string { + return string(f.Field.Tag) +} + // IsExported returns true if the given field is exported. func (f *Field) IsExported() bool { return f.Field.PkgPath == "" @@ -64,6 +69,25 @@ func (f *Field) OriginalKind() reflect.Kind { return kind } +const ( + RecursiveOptionNone = 0 // No recursively retrieving fields as map if the field is an embedded struct. + RecursiveOptionEmbedded = 1 // Recursively retrieving fields as map if the field is an embedded struct. + RecursiveOptionEmbeddedNoTag = 2 // Recursively retrieving fields as map if the field is an embedded struct and the field has no tag. +) + +type FieldMapInput struct { + // Pointer should be type of struct/*struct. + Pointer interface{} + + // PriorityTagArray specifies the priority tag array for retrieving from high to low. + // If it's given `nil`, it returns map[name]*Field, of which the `name` is attribute name. + PriorityTagArray []string + + // RecursiveOption specifies the way retrieving the fields recursively if the attribute + // is an embedded struct. It is RecursiveOptionNone in default. + RecursiveOption int +} + // FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`. // // The parameter `pointer` should be type of struct/*struct. @@ -75,8 +99,8 @@ func (f *Field) OriginalKind() reflect.Kind { // is an embedded struct. // // Note that it only retrieves the exported attributes with first letter up-case from struct. -func FieldMap(pointer interface{}, priority []string, recursive bool) (map[string]*Field, error) { - fields, err := getFieldValues(pointer) +func FieldMap(input FieldMapInput) (map[string]*Field, error) { + fields, err := getFieldValues(input.Pointer) if err != nil { return nil, err } @@ -90,7 +114,7 @@ func FieldMap(pointer interface{}, priority []string, recursive bool) (map[strin continue } tagValue = "" - for _, p := range priority { + for _, p := range input.PriorityTagArray { tagValue = field.Tag(p) if tagValue != "" && tagValue != "-" { break @@ -101,15 +125,28 @@ func FieldMap(pointer interface{}, priority []string, recursive bool) (map[strin if tagValue != "" { mapField[tagValue] = tempField } else { - if recursive && field.IsEmbedded() { - m, err := FieldMap(field.Value, priority, recursive) - if err != nil { - return nil, err - } - for k, v := range m { - if _, ok := mapField[k]; !ok { - tempV := v - mapField[k] = tempV + if input.RecursiveOption != RecursiveOptionNone && field.IsEmbedded() { + switch input.RecursiveOption { + case RecursiveOptionEmbeddedNoTag: + if field.TagStr() != "" { + mapField[field.Name()] = tempField + break + } + fallthrough + case RecursiveOptionEmbedded: + m, err := FieldMap(FieldMapInput{ + Pointer: field.Value, + PriorityTagArray: input.PriorityTagArray, + RecursiveOption: input.RecursiveOption, + }) + if err != nil { + return nil, err + } + for k, v := range m { + if _, ok := mapField[k]; !ok { + tempV := v + mapField[k] = tempV + } } } } else { diff --git a/internal/structs/structs_z_unit_test.go b/internal/structs/structs_z_unit_test.go index 4268f473a..825d095bb 100644 --- a/internal/structs/structs_z_unit_test.go +++ b/internal/structs/structs_z_unit_test.go @@ -110,7 +110,11 @@ func Test_FieldMap(t *testing.T) { Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"` } var user *User - m, _ := structs.FieldMap(user, []string{"params"}, true) + m, _ := structs.FieldMap(structs.FieldMapInput{ + Pointer: user, + PriorityTagArray: []string{"params"}, + RecursiveOption: structs.RecursiveOptionEmbedded, + }) t.Assert(len(m), 3) _, ok := m["Id"] t.Assert(ok, true) @@ -130,7 +134,11 @@ func Test_FieldMap(t *testing.T) { Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"` } var user *User - m, _ := structs.FieldMap(user, nil, true) + m, _ := structs.FieldMap(structs.FieldMapInput{ + Pointer: user, + PriorityTagArray: nil, + RecursiveOption: structs.RecursiveOptionEmbedded, + }) t.Assert(len(m), 3) _, ok := m["Id"] t.Assert(ok, true) diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 6e5e1a568..341afe542 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -25,7 +25,11 @@ func (v *Validator) doCheckStruct(object interface{}) Error { errorMaps = make(map[string]map[string]string) // Returning error. fieldToAliasNameMap = make(map[string]string) // Field name to alias name map. ) - fieldMap, err := structs.FieldMap(object, aliasNameTagPriority, true) + fieldMap, err := structs.FieldMap(structs.FieldMapInput{ + Pointer: object, + PriorityTagArray: aliasNameTagPriority, + RecursiveOption: structs.RecursiveOptionEmbedded, + }) if err != nil { return newErrorStr(internalObjectErrRuleName, err.Error()) } From 3043645605159d46729830c84509b9abf2058373 Mon Sep 17 00:00:00 2001 From: star liu <starliu1995@hotmail.com> Date: Tue, 3 Aug 2021 23:29:34 +0800 Subject: [PATCH 422/492] Update Readme ,Github Action badge --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 4b7d82f95..dda9ad1a5 100644 --- a/README.MD +++ b/README.MD @@ -1,7 +1,7 @@ # GoFrame [![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf) -[![Build Status](https://travis-ci.com/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) +[![GoFrame CI](https://github.com/gogf/gf/actions/workflows/go.yml/badge.svg)](https://github.com/gogf/gf/actions/workflows/go.yml) [![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf) [![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master) [![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf) From 65fff6feae09ca74678c57e0f5b9820cb693e61f Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 4 Aug 2021 13:29:06 +0800 Subject: [PATCH 423/492] version updates --- database/gdb/gdb_func.go | 17 +++++++++-------- version.go | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index fab36478e..a75b61525 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -357,14 +357,15 @@ func doQuoteWord(s, charLeft, charRight string) string { return s } -// doQuoteString quotes string with quote chars. It handles strings like: -// "user", -// "user u", -// "user,user_detail", -// "user u, user_detail ut", -// "user.user u, user.user_detail ut", -// "u.id, u.name, u.age", -// "u.id asc". +// doQuoteString quotes string with quote chars. +// For example, if quote char is '`': +// "user" => "`user`" +// "user u" => "`user` u" +// "user,user_detail" => "`user`,`user_detail`" +// "user u, user_detail ut" => "`user` u,`user_detail` ut" +// "user.user u, user.user_detail ut" => "`user`.`user` u,`user`.`user_detail` ut" +// "u.id, u.name, u.age" => "`u`.`id`,`u`.`name`,`u`.`age`" +// "u.id asc" => "`u`.`id` asc" func doQuoteString(s, charLeft, charRight string) string { array1 := gstr.SplitAndTrim(s, ",") for k1, v1 := range array1 { diff --git a/version.go b/version.go index bd9e356f1..882ec7b9e 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.16.4" +const VERSION = "v1.16.5" const AUTHORS = "john<john@goframe.org>" From 4c54b1cfbc9fd97d356629a5238486fd64464f94 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 4 Aug 2021 20:50:45 +0800 Subject: [PATCH 424/492] improve errcode feature for package gerror --- errors/gerror/gerror.go | 22 ++++- errors/gerror/gerror_code.go | 84 ++++++++++++------- errors/gerror/gerror_error.go | 5 +- errors/gerror/gerror_z_unit_test.go | 37 +++++++- .../ghttp_middleware_handler_response.go | 6 +- net/ghttp/ghttp_response_write.go | 2 +- ...ghttp_unit_router_handler_extended_test.go | 2 +- util/gconv/gconv.go | 6 +- 8 files changed, 122 insertions(+), 42 deletions(-) diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index b961be831..29e14bb14 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -142,10 +142,14 @@ func WrapSkipf(skip int, err error, format string, args ...interface{}) error { } // NewCode creates and returns an error that has error code and given text. -func NewCode(code int, text string) error { +func NewCode(code int, text ...string) error { + errText := "" + if len(text) > 0 { + errText = text[0] + } return &Error{ stack: callers(), - text: text, + text: errText, code: code, } } @@ -238,14 +242,24 @@ func WrapCodeSkipf(code, skip int, err error, format string, args ...interface{} } // Code returns the error code of current error. -// It returns -1 if it has no error code or it does not implements interface Code. +// It returns CodeNil if it has no error code or it does not implements interface Code. func Code(err error) int { if err != nil { if e, ok := err.(apiCode); ok { return e.Code() } } - return -1 + return CodeNil +} + +// CodeMessage retrieves and returns the error code message from given error. +func CodeMessage(err error) string { + return Message(Code(err)) +} + +// Message returns the message string for specified code. +func Message(code int) string { + return codeMessageMap[code] } // Cause returns the root cause error of <err>. diff --git a/errors/gerror/gerror_code.go b/errors/gerror/gerror_code.go index 055ea0ab0..c0eecc0f4 100644 --- a/errors/gerror/gerror_code.go +++ b/errors/gerror/gerror_code.go @@ -9,32 +9,60 @@ package gerror // Reserved internal error code of framework: code < 1000. const ( - // =============================================================================== - // Common system codes. - // =============================================================================== - - CodeNil = -1 // No error code specified. - CodeOk = 0 // It is OK. - CodeInternalError = 50 + iota // An error occurred internally. - CodeValidationFailed // Data validation failed. - CodeDbOperationError // Database operation error. - CodeInvalidParameter // The given parameter for current operation is invalid. - CodeMissingParameter // Parameter for current operation is missing. - CodeInvalidOperation // The function cannot be used like this. - CodeInvalidConfiguration // The configuration is invalid for current operation. - CodeMissingConfiguration // The configuration is missing for current operation. - CodeNotImplemented // The operation is not implemented yet. - CodeNotSupported // The operation is not supported yet. - CodeOperationFailed // I tried, but I cannot give you what you want. - CodeNotAuthorized // Not Authorized. - CodeSecurityReason // Security Reason. - CodeServerBusy // Server is busy, please try again later. - CodeUnknown // Unknown error. - CodeResourceNotExist // Resource does not exist. - - // =============================================================================== - // Common business codes. - // =============================================================================== - - CodeBusinessValidationFailed = 300 + iota // Business validation failed. + CodeNil = -1 // No error code specified. + CodeOk = 0 // It is OK. + CodeInternalError = 50 // An error occurred internally. + CodeValidationFailed = 51 // Data validation failed. + CodeDbOperationError = 52 // Database operation error. + CodeInvalidParameter = 53 // The given parameter for current operation is invalid. + CodeMissingParameter = 54 // Parameter for current operation is missing. + CodeInvalidOperation = 55 // The function cannot be used like this. + CodeInvalidConfiguration = 56 // The configuration is invalid for current operation. + CodeMissingConfiguration = 57 // The configuration is missing for current operation. + CodeNotImplemented = 58 // The operation is not implemented yet. + CodeNotSupported = 59 // The operation is not supported yet. + CodeOperationFailed = 60 // I tried, but I cannot give you what you want. + CodeNotAuthorized = 61 // Not Authorized. + CodeSecurityReason = 62 // Security Reason. + CodeServerBusy = 63 // Server is busy, please try again later. + CodeUnknown = 64 // Unknown error. + CodeResourceNotExist = 65 // Resource does not exist. + CodeBusinessValidationFailed = 300 // Business validation failed. ) + +var ( + // codeMessageMap is the mapping from code to according string message. + codeMessageMap = map[int]string{ + CodeNil: "", + CodeOk: "OK", + CodeInternalError: "Internal Error", + CodeValidationFailed: "Validation Failed", + CodeDbOperationError: "Database Operation Error", + CodeInvalidParameter: "Invalid Parameter", + CodeMissingParameter: "Missing Parameter", + CodeInvalidOperation: "Invalid Operation", + CodeInvalidConfiguration: "Invalid Configuration", + CodeMissingConfiguration: "Missing Configuration", + CodeNotImplemented: "Not Implemented", + CodeNotSupported: "Not Supported", + CodeOperationFailed: "Operation Failed", + CodeNotAuthorized: "Not Authorized", + CodeSecurityReason: "Security Reason", + CodeServerBusy: "Server Is Busy", + CodeUnknown: "Unknown Error", + CodeResourceNotExist: "Resource Not Exist", + CodeBusinessValidationFailed: "Business Validation Failed", + } +) + +// RegisterCode registers custom error code to global error codes for gerror recognition. +func RegisterCode(code int, message string) { + codeMessageMap[code] = message +} + +// RegisterCodeMap registers custom error codes to global error codes for gerror recognition using map. +func RegisterCodeMap(codes map[int]string) { + for k, v := range codes { + codeMessageMap[k] = v + } +} diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index 045385d84..f0953f7ce 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -57,10 +57,10 @@ func (err *Error) Error() string { } // Code returns the error code. -// It returns -1 if it has no error code. +// It returns CodeNil if it has no error code. func (err *Error) Code() int { if err == nil { - return -1 + return CodeNil } return err.code } @@ -159,6 +159,7 @@ func (err *Error) Current() error { error: nil, stack: err.stack, text: err.text, + code: err.code, } } diff --git a/errors/gerror/gerror_z_unit_test.go b/errors/gerror/gerror_z_unit_test.go index e60243aa7..0bffed596 100644 --- a/errors/gerror/gerror_z_unit_test.go +++ b/errors/gerror/gerror_z_unit_test.go @@ -261,8 +261,8 @@ func Test_Code(t *testing.T) { t.Assert(err.Error(), "123") }) gtest.C(t, func(t *gtest.T) { - err := gerror.NewCode(1, "123") - t.Assert(gerror.Code(err), 1) + err := gerror.NewCode(gerror.CodeUnknown, "123") + t.Assert(gerror.Code(err), gerror.CodeUnknown) t.Assert(err.Error(), "123") }) gtest.C(t, func(t *gtest.T) { @@ -318,3 +318,36 @@ func Test_Json(t *testing.T) { t.Assert(string(b), `"2: 1"`) }) } + +func Test_Message(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gerror.Message(-100), ``) + t.Assert(gerror.Message(gerror.CodeNil), ``) + t.Assert(gerror.Message(gerror.CodeOk), `OK`) + }) +} + +func Test_CodeMessage(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := gerror.NewCode(gerror.CodeUnknown) + t.Assert(gerror.CodeMessage(err), `Unknown Error`) + }) +} + +func Test_RegisterCode(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + gerror.RegisterCode(10086, "MobileTelecom") + t.Assert(gerror.Message(10086), `MobileTelecom`) + }) +} + +func Test_RegisterCodeMap(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + gerror.RegisterCodeMap(map[int]string{ + 10087: "MobileTelecom 10087", + 10088: "MobileTelecom 10088", + }) + t.Assert(gerror.Message(10087), `MobileTelecom 10087`) + t.Assert(gerror.Message(10088), `MobileTelecom 10088`) + }) +} diff --git a/net/ghttp/ghttp_middleware_handler_response.go b/net/ghttp/ghttp_middleware_handler_response.go index f8c71c318..892ac796b 100644 --- a/net/ghttp/ghttp_middleware_handler_response.go +++ b/net/ghttp/ghttp_middleware_handler_response.go @@ -31,9 +31,13 @@ func MiddlewareHandlerResponse(r *Request) { if code == gerror.CodeNil { code = gerror.CodeInternalError } + message := err.Error() + if message == "" { + message = gerror.Message(code) + } internalErr = r.Response.WriteJson(DefaultHandlerResponse{ Code: code, - Message: err.Error(), + Message: message, Data: nil, }) if internalErr != nil { diff --git a/net/ghttp/ghttp_response_write.go b/net/ghttp/ghttp_response_write.go index f8aca67a6..c7dbd10f8 100644 --- a/net/ghttp/ghttp_response_write.go +++ b/net/ghttp/ghttp_response_write.go @@ -71,7 +71,7 @@ func (r *Response) WritefExit(format string, params ...interface{}) { r.Request.Exit() } -// Writef writes the response with <content> and new line. +// Writeln writes the response with <content> and new line. func (r *Response) Writeln(content ...interface{}) { if len(content) == 0 { r.Write("\n") diff --git a/net/ghttp/ghttp_unit_router_handler_extended_test.go b/net/ghttp/ghttp_unit_router_handler_extended_test.go index 49f00095c..77d50c8f2 100644 --- a/net/ghttp/ghttp_unit_router_handler_extended_test.go +++ b/net/ghttp/ghttp_unit_router_handler_extended_test.go @@ -77,6 +77,6 @@ func Test_Router_Handler_Extended_Handler_WithObject(t *testing.T) { client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) t.Assert(client.GetContent("/test?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18,"Name":"john"}}`) - t.Assert(client.GetContent("/test/error"), `{"code":52,"message":"error","data":null}`) + t.Assert(client.GetContent("/test/error"), `{"code":50,"message":"error","data":null}`) }) } diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 77e492fe8..0a72171f8 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -53,7 +53,7 @@ type doConvertInput struct { Extra []interface{} // Extra values for implementing the converting. } -// doConvert does common used types converting. +// doConvert does commonly used types converting. func doConvert(input doConvertInput) interface{} { switch input.ToTypeName { case "int": @@ -366,7 +366,7 @@ func Runes(any interface{}) []rune { } // String converts `any` to string. -// It's most common used converting function. +// It's most commonly used converting function. func String(any interface{}) string { if any == nil { return "" @@ -459,7 +459,7 @@ func String(any interface{}) string { if kind == reflect.Ptr { return String(rv.Elem().Interface()) } - // Finally we use json.Marshal to convert. + // Finally, we use json.Marshal to convert. if jsonContent, err := json.Marshal(value); err != nil { return fmt.Sprint(value) } else { From 537cbf983e9a0e7ebfd0d7ea29850dc8f0c4e351 Mon Sep 17 00:00:00 2001 From: jroam <yybjroam@qq.com> Date: Wed, 4 Aug 2021 23:35:29 +0800 Subject: [PATCH 425/492] edit some function annotations --- container/garray/garray_normal_any.go | 4 ++-- container/garray/garray_normal_int.go | 2 +- container/garray/garray_normal_str.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/container/garray/garray_normal_any.go b/container/garray/garray_normal_any.go index 863274e07..0e16736de 100644 --- a/container/garray/garray_normal_any.go +++ b/container/garray/garray_normal_any.go @@ -36,7 +36,7 @@ func New(safe ...bool) *Array { return NewArraySize(0, 0, safe...) } -// NewArray See New. +// NewArray is alias of New, please see New. func NewArray(safe ...bool) *Array { return NewArraySize(0, 0, safe...) } @@ -422,7 +422,7 @@ func (a *Array) SubSlice(offset int, length ...int) []interface{} { } } -// Append See PushRight. +// Append is alias of PushRight, please See PushRight. func (a *Array) Append(value ...interface{}) *Array { a.PushRight(value...) return a diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index 83c2624d5..44e577383 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -424,7 +424,7 @@ func (a *IntArray) SubSlice(offset int, length ...int) []int { } } -// Append See PushRight. +// Append is alias of PushRight,please See PushRight. func (a *IntArray) Append(value ...int) *IntArray { a.mu.Lock() a.array = append(a.array, value...) diff --git a/container/garray/garray_normal_str.go b/container/garray/garray_normal_str.go index 8f2cb1d83..61fd0e8d0 100644 --- a/container/garray/garray_normal_str.go +++ b/container/garray/garray_normal_str.go @@ -411,7 +411,7 @@ func (a *StrArray) SubSlice(offset int, length ...int) []string { } } -// Append See PushRight. +// Append is alias of PushRight,please See PushRight. func (a *StrArray) Append(value ...string) *StrArray { a.mu.Lock() a.array = append(a.array, value...) From 00d7ee93b240f541e28bd173a0a2b577b8e1c74e Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 5 Aug 2021 11:40:31 +0800 Subject: [PATCH 426/492] improve package gerror --- errors/gerror/gerror_error.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index f0953f7ce..0c15b0410 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -47,6 +47,9 @@ func (err *Error) Error() string { return "" } errStr := err.text + if errStr == "" { + errStr = Message(err.code) + } if err.error != nil { if err.text != "" { errStr += ": " From 75ca8669913b86e97575ffa3b2ea14a4dcfffa6c Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 5 Aug 2021 19:15:18 +0800 Subject: [PATCH 427/492] fix issue in ghttp.Server --- .../ghttp_middleware_handler_response.go | 6 +----- net/ghttp/ghttp_request_middleware.go | 20 +++++++++---------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/net/ghttp/ghttp_middleware_handler_response.go b/net/ghttp/ghttp_middleware_handler_response.go index 892ac796b..f8c71c318 100644 --- a/net/ghttp/ghttp_middleware_handler_response.go +++ b/net/ghttp/ghttp_middleware_handler_response.go @@ -31,13 +31,9 @@ func MiddlewareHandlerResponse(r *Request) { if code == gerror.CodeNil { code = gerror.CodeInternalError } - message := err.Error() - if message == "" { - message = gerror.Message(code) - } internalErr = r.Response.WriteJson(DefaultHandlerResponse{ Code: code, - Message: message, + Message: err.Error(), Data: nil, }) if internalErr != nil { diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index ff8c5a539..8554b4f3f 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -133,33 +133,33 @@ func (m *middleware) callHandlerFunc(funcInfo handlerFuncInfo) { } if funcInfo.Type.NumIn() == 2 { var ( - request reflect.Value + inputObject reflect.Value ) if funcInfo.Type.In(1).Kind() == reflect.Ptr { - request = reflect.New(funcInfo.Type.In(1).Elem()) - m.request.handlerResponse.Error = m.request.Parse(request.Interface()) + inputObject = reflect.New(funcInfo.Type.In(1).Elem()) + m.request.handlerResponse.Error = m.request.Parse(inputObject.Interface()) } else { - request = reflect.New(funcInfo.Type.In(1).Elem()).Elem() - m.request.handlerResponse.Error = m.request.Parse(request.Addr().Interface()) + inputObject = reflect.New(funcInfo.Type.In(1).Elem()).Elem() + m.request.handlerResponse.Error = m.request.Parse(inputObject.Addr().Interface()) } if m.request.handlerResponse.Error != nil { return } - inputValues = append(inputValues, request) + inputValues = append(inputValues, inputObject) } // Call handler with dynamic created parameter values. results := funcInfo.Value.Call(inputValues) switch len(results) { case 1: - m.request.handlerResponse.Error = results[0].Interface().(error) + if !results[0].IsNil() { + m.request.handlerResponse.Error = results[0].Interface().(error) + } case 2: m.request.handlerResponse.Object = results[0].Interface() if !results[1].IsNil() { - if v := results[1].Interface(); v != nil { - m.request.handlerResponse.Error = v.(error) - } + m.request.handlerResponse.Error = results[1].Interface().(error) } } } From 0e158903c26498cfb0435cedb70bb70652045683 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Fri, 6 Aug 2021 12:08:49 +0800 Subject: [PATCH 428/492] improve package gtimer for priority checks --- container/garray/garray_sorted_any.go | 4 +- container/garray/garray_z_bench_any_test.go | 39 ++++++++++++ frame/gins/gins_database.go | 2 +- os/gtime/gtime.go | 1 + os/gtime/gtime_time_zone.go | 1 + os/gtimer/gtimer_entry.go | 4 +- os/gtimer/gtimer_queue.go | 59 ++++++------------- os/gtimer/gtimer_queue_heap.go | 1 + os/gtimer/gtimer_timer_loop.go | 9 ++- .../gtimer_z_unit_timer_internal_test.go | 16 +++++ 10 files changed, 85 insertions(+), 51 deletions(-) create mode 100644 container/garray/garray_z_bench_any_test.go diff --git a/container/garray/garray_sorted_any.go b/container/garray/garray_sorted_any.go index 780204857..f120809a5 100644 --- a/container/garray/garray_sorted_any.go +++ b/container/garray/garray_sorted_any.go @@ -156,9 +156,7 @@ func (a *SortedArray) Append(values ...interface{}) *SortedArray { if cmp > 0 { index++ } - rear := append([]interface{}{}, a.array[index:]...) - a.array = append(a.array[0:index], value) - a.array = append(a.array, rear...) + a.array = append(a.array[:index], append([]interface{}{value}, a.array[index:]...)...) } return a } diff --git a/container/garray/garray_z_bench_any_test.go b/container/garray/garray_z_bench_any_test.go new file mode 100644 index 000000000..477c18a19 --- /dev/null +++ b/container/garray/garray_z_bench_any_test.go @@ -0,0 +1,39 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package garray_test + +import ( + "github.com/gogf/gf/container/garray" + "testing" +) + +type anySortedArrayItem struct { + priority int64 + value interface{} +} + +var ( + anyArray = garray.NewArray() + anySortedArray = garray.NewSortedArray(func(a, b interface{}) int { + return int(a.(anySortedArrayItem).priority - b.(anySortedArrayItem).priority) + }) +) + +func Benchmark_AnyArray_Add(b *testing.B) { + for i := 0; i < b.N; i++ { + anyArray.Append(i) + } +} + +func Benchmark_AnySortedArray_Add(b *testing.B) { + for i := 0; i < b.N; i++ { + anySortedArray.Add(anySortedArrayItem{ + priority: int64(i), + value: i, + }) + } +} diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index ec854722e..dc49a18a6 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -139,7 +139,7 @@ func Database(name ...string) gdb.DB { } return db } else { - // It panics often because it dose not find its configuration for given group. + // If panics, often because it does not find its configuration for given group. panic(err) } return nil diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index fe537fb4c..3cb587369 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -24,6 +24,7 @@ import ( const ( // Short writes for common usage durations. + D = 24 * time.Hour H = time.Hour M = time.Minute diff --git a/os/gtime/gtime_time_zone.go b/os/gtime/gtime_time_zone.go index d7a50eb2c..8b828bee8 100644 --- a/os/gtime/gtime_time_zone.go +++ b/os/gtime/gtime_time_zone.go @@ -15,6 +15,7 @@ var ( // locationMap is time zone name to its location object. // Time zone name is like: Asia/Shanghai. locationMap = make(map[string]*time.Location) + // locationMu is used for concurrent safety for `locationMap`. locationMu = sync.RWMutex{} ) diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go index beebf21a6..65aac5e3d 100644 --- a/os/gtimer/gtimer_entry.go +++ b/os/gtimer/gtimer_entry.go @@ -33,12 +33,12 @@ func (entry *Entry) Status() int { // Run runs the timer job asynchronously. func (entry *Entry) Run() { leftRunningTimes := entry.times.Add(-1) - // Running times exceeding checks. + // It checks its running times exceeding. if leftRunningTimes < 0 { entry.status.Set(StatusClosed) return } - // This means it does not limit the running times. + // This means it has no limit in running times. if leftRunningTimes == math.MaxInt32-1 { entry.times.Set(math.MaxInt32) } diff --git a/os/gtimer/gtimer_queue.go b/os/gtimer/gtimer_queue.go index 9fb6af73d..54443bdf9 100644 --- a/os/gtimer/gtimer_queue.go +++ b/os/gtimer/gtimer_queue.go @@ -18,9 +18,9 @@ import ( // high priority is served before an element with low priority. // priorityQueue is based on heap structure. type priorityQueue struct { - mu sync.RWMutex - heap *priorityQueueHeap // the underlying queue items manager using heap. - latestPriority *gtype.Int64 // latestPriority stores the most priority value of the heap, which is used to check if necessary to call the Pop of heap by Timer. + mu sync.Mutex + heap *priorityQueueHeap // the underlying queue items manager using heap. + nextPriority *gtype.Int64 // nextPriority stores the next priority value of the heap, which is used to check if necessary to call the Pop of heap by Timer. } // priorityQueueHeap is a heap manager, of which the underlying `array` is a array implementing a heap structure. @@ -37,25 +37,16 @@ type priorityQueueItem struct { // newPriorityQueue creates and returns a priority queue. func newPriorityQueue() *priorityQueue { queue := &priorityQueue{ - heap: &priorityQueueHeap{ - array: make([]priorityQueueItem, 0), - }, - latestPriority: gtype.NewInt64(math.MaxInt64), + heap: &priorityQueueHeap{array: make([]priorityQueueItem, 0)}, + nextPriority: gtype.NewInt64(math.MaxInt64), } heap.Init(queue.heap) return queue } -// Len retrieves and returns the length of the queue. -func (q *priorityQueue) Len() int { - q.mu.RLock() - defer q.mu.RUnlock() - return q.heap.Len() -} - -// LatestPriority retrieves and returns the minimum and the most priority value of the queue. -func (q *priorityQueue) LatestPriority() int64 { - return q.latestPriority.Val() +// NextPriority retrieves and returns the minimum and the most priority value of the queue. +func (q *priorityQueue) NextPriority() int64 { + return q.nextPriority.Val() } // Push pushes a value to the queue. @@ -63,42 +54,30 @@ func (q *priorityQueue) LatestPriority() int64 { // The lesser the `priority` value the higher priority of the `value`. func (q *priorityQueue) Push(value interface{}, priority int64) { q.mu.Lock() + defer q.mu.Unlock() heap.Push(q.heap, priorityQueueItem{ value: value, priority: priority, }) - q.mu.Unlock() // Update the minimum priority using atomic operation. - for { - latestPriority := q.latestPriority.Val() - if priority >= latestPriority { - break - } - if q.latestPriority.Cas(latestPriority, priority) { - break - } + nextPriority := q.nextPriority.Val() + if priority >= nextPriority { + return } + q.nextPriority.Set(priority) } // Pop retrieves, removes and returns the most high priority value from the queue. func (q *priorityQueue) Pop() interface{} { q.mu.Lock() + defer q.mu.Unlock() if v := heap.Pop(q.heap); v != nil { - item := v.(priorityQueueItem) - q.mu.Unlock() - // Update the minimum priority using atomic operation. - for { - latestPriority := q.latestPriority.Val() - if item.priority >= latestPriority { - break - } - if q.latestPriority.Cas(latestPriority, item.priority) { - break - } + var nextPriority int64 = math.MaxInt64 + if len(q.heap.array) > 0 { + nextPriority = q.heap.array[0].priority } - return item.value - } else { - q.mu.Unlock() + q.nextPriority.Set(nextPriority) + return v.(priorityQueueItem).value } return nil } diff --git a/os/gtimer/gtimer_queue_heap.go b/os/gtimer/gtimer_queue_heap.go index 30879f770..c4b2f5dbe 100644 --- a/os/gtimer/gtimer_queue_heap.go +++ b/os/gtimer/gtimer_queue_heap.go @@ -12,6 +12,7 @@ func (h *priorityQueueHeap) Len() int { } // Less is used to implement the interface of sort.Interface. +// The least one is placed to the top of the heap. func (h *priorityQueueHeap) Less(i, j int) bool { return h.array[i].priority < h.array[j].priority } diff --git a/os/gtimer/gtimer_timer_loop.go b/os/gtimer/gtimer_timer_loop.go index 61e2ee672..ae94bd311 100644 --- a/os/gtimer/gtimer_timer_loop.go +++ b/os/gtimer/gtimer_timer_loop.go @@ -23,8 +23,7 @@ func (t *Timer) loop() { switch t.status.Val() { case StatusRunning: // Timer proceeding. - currentTimerTicks = t.ticks.Add(1) - if currentTimerTicks >= t.queue.LatestPriority() { + if currentTimerTicks = t.ticks.Add(1); currentTimerTicks >= t.queue.NextPriority() { t.proceed(currentTimerTicks) } @@ -40,7 +39,7 @@ func (t *Timer) loop() { }() } -// proceed proceeds the timer job checking and running logic. +// proceed function proceeds the timer job checking and running logic. func (t *Timer) proceed(currentTimerTicks int64) { var ( value interface{} @@ -51,9 +50,9 @@ func (t *Timer) proceed(currentTimerTicks int64) { break } entry := value.(*Entry) - // It checks if it meets the ticks requirement. + // It checks if it meets the ticks' requirement. if jobNextTicks := entry.nextTicks.Val(); currentTimerTicks < jobNextTicks { - // It push the job back if current ticks does not meet its running ticks requirement. + // It pushes the job back if current ticks does not meet its running ticks requirement. t.queue.Push(entry, entry.nextTicks.Val()) break } diff --git a/os/gtimer/gtimer_z_unit_timer_internal_test.go b/os/gtimer/gtimer_z_unit_timer_internal_test.go index 9882d21c3..e7c6d9777 100644 --- a/os/gtimer/gtimer_z_unit_timer_internal_test.go +++ b/os/gtimer/gtimer_z_unit_timer_internal_test.go @@ -46,3 +46,19 @@ func TestTimer_Proceed(t *testing.T) { t.Assert(array.Len(), 2) }) } + +func TestTimer_PriorityQueue(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + queue := newPriorityQueue() + queue.Push(1, 1) + queue.Push(4, 4) + queue.Push(5, 5) + queue.Push(2, 2) + queue.Push(3, 3) + t.Assert(queue.Pop(), 1) + t.Assert(queue.Pop(), 2) + t.Assert(queue.Pop(), 3) + t.Assert(queue.Pop(), 4) + t.Assert(queue.Pop(), 5) + }) +} From 0f6820df9e24070126a74ffe080bd0ced1011845 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Fri, 6 Aug 2021 14:13:37 +0800 Subject: [PATCH 429/492] add more unit testing case for package gtimer --- .../gtimer_z_unit_timer_internal_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/os/gtimer/gtimer_z_unit_timer_internal_test.go b/os/gtimer/gtimer_z_unit_timer_internal_test.go index e7c6d9777..785fd9823 100644 --- a/os/gtimer/gtimer_z_unit_timer_internal_test.go +++ b/os/gtimer/gtimer_z_unit_timer_internal_test.go @@ -62,3 +62,22 @@ func TestTimer_PriorityQueue(t *testing.T) { t.Assert(queue.Pop(), 5) }) } + +func TestTimer_PriorityQueue_FirstOneInArrayIsTheLeast(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + size = 1000000 + array = garray.NewIntArrayRange(0, size, 1) + ) + array.Shuffle() + queue := newPriorityQueue() + array.Iterator(func(k int, v int) bool { + queue.Push(v, int64(v)) + return true + }) + for i := 0; i < size; i++ { + t.Assert(queue.Pop(), i) + t.Assert(queue.heap.array[0].priority, i+1) + } + }) +} From cd3593182ab6e631756f8550604549b01a179bb7 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sat, 7 Aug 2021 10:44:57 +0800 Subject: [PATCH 430/492] improve package gerror/ghttp for error code handling --- errors/gerror/gerror.go | 24 ++++++++++++++++++------ errors/gerror/gerror_code.go | 2 ++ errors/gerror/gerror_error.go | 2 +- net/ghttp/ghttp_func.go | 6 +++--- net/ghttp/ghttp_request_param.go | 9 +++++---- net/ghttp/ghttp_request_param_page.go | 19 +++++++++++++------ net/ghttp/ghttp_server.go | 2 +- net/ghttp/ghttp_server_handler.go | 6 +++--- net/ghttp/ghttp_server_router_serve.go | 7 ++++--- net/ghttp/ghttp_unit_config_test.go | 2 +- 10 files changed, 51 insertions(+), 28 deletions(-) diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index 29e14bb14..7e1e9af03 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -165,10 +165,14 @@ func NewCodef(code int, format string, args ...interface{}) error { // NewCodeSkip creates and returns an error which has error code and is formatted from given text. // The parameter <skip> specifies the stack callers skipped amount. -func NewCodeSkip(code, skip int, text string) error { +func NewCodeSkip(code, skip int, text ...string) error { + errText := "" + if len(text) > 0 { + errText = text[0] + } return &Error{ stack: callers(skip), - text: text, + text: errText, code: code, } } @@ -185,14 +189,18 @@ func NewCodeSkipf(code, skip int, format string, args ...interface{}) error { // WrapCode wraps error with code and text. // It returns nil if given err is nil. -func WrapCode(code int, err error, text string) error { +func WrapCode(code int, err error, text ...string) error { if err == nil { return nil } + errText := "" + if len(text) > 0 { + errText = text[0] + } return &Error{ error: err, stack: callers(), - text: text, + text: errText, code: code, } } @@ -214,14 +222,18 @@ func WrapCodef(code int, err error, format string, args ...interface{}) error { // WrapCodeSkip wraps error with code and text. // It returns nil if given err is nil. // The parameter <skip> specifies the stack callers skipped amount. -func WrapCodeSkip(code, skip int, err error, text string) error { +func WrapCodeSkip(code, skip int, err error, text ...string) error { if err == nil { return nil } + errText := "" + if len(text) > 0 { + errText = text[0] + } return &Error{ error: err, stack: callers(skip), - text: text, + text: errText, code: code, } } diff --git a/errors/gerror/gerror_code.go b/errors/gerror/gerror_code.go index c0eecc0f4..699deaa11 100644 --- a/errors/gerror/gerror_code.go +++ b/errors/gerror/gerror_code.go @@ -27,6 +27,7 @@ const ( CodeServerBusy = 63 // Server is busy, please try again later. CodeUnknown = 64 // Unknown error. CodeResourceNotExist = 65 // Resource does not exist. + CodeInvalidRequest = 66 // Invalid request. CodeBusinessValidationFailed = 300 // Business validation failed. ) @@ -51,6 +52,7 @@ var ( CodeServerBusy: "Server Is Busy", CodeUnknown: "Unknown Error", CodeResourceNotExist: "Resource Not Exist", + CodeInvalidRequest: "Invalid Request", CodeBusinessValidationFailed: "Business Validation Failed", } ) diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index 0c15b0410..97e22f0cd 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -51,7 +51,7 @@ func (err *Error) Error() string { errStr = Message(err.code) } if err.error != nil { - if err.text != "" { + if errStr != "" { errStr += ": " } errStr += err.error.Error() diff --git a/net/ghttp/ghttp_func.go b/net/ghttp/ghttp_func.go index 9b5e09be2..2c569c0ac 100644 --- a/net/ghttp/ghttp_func.go +++ b/net/ghttp/ghttp_func.go @@ -37,12 +37,12 @@ func niceCallFunc(f func()) { // of the real error point. if err, ok := exception.(error); ok { if gerror.Code(err) != gerror.CodeNil { - panic(gerror.Wrap(err, "")) + panic(err) } else { - panic(gerror.WrapCode(gerror.CodeInternalError, err, "")) + panic(gerror.WrapCodeSkip(gerror.CodeInternalError, 1, err, "")) } } else { - panic(gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception)) + panic(gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%+v", exception)) } } } diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index 1da2af60a..e0381e854 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -13,6 +13,7 @@ import ( "github.com/gogf/gf/encoding/gjson" "github.com/gogf/gf/encoding/gurl" "github.com/gogf/gf/encoding/gxml" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/internal/utils" "github.com/gogf/gf/text/gregex" @@ -299,7 +300,7 @@ func (r *Request) parseQuery() { var err error r.queryMap, err = gstr.Parse(r.URL.RawQuery) if err != nil { - panic(err) + panic(gerror.WrapCode(gerror.CodeInvalidParameter, err, "")) } } } @@ -354,12 +355,12 @@ func (r *Request) parseForm() { if gstr.Contains(contentType, "multipart/") { // multipart/form-data, multipart/mixed if err = r.ParseMultipartForm(r.Server.config.FormParsingMemory); err != nil { - panic(err) + panic(gerror.WrapCode(gerror.CodeInvalidRequest, err, "")) } } else if gstr.Contains(contentType, "form") { // application/x-www-form-urlencoded if err = r.Request.ParseForm(); err != nil { - panic(err) + panic(gerror.WrapCode(gerror.CodeInvalidRequest, err, "")) } } if len(r.PostForm) > 0 { @@ -402,7 +403,7 @@ func (r *Request) parseForm() { } if params != "" { if r.formMap, err = gstr.Parse(params); err != nil { - panic(err) + panic(gerror.WrapCode(gerror.CodeInvalidParameter, err, "")) } } } diff --git a/net/ghttp/ghttp_request_param_page.go b/net/ghttp/ghttp_request_param_page.go index 20e0c8f55..b63a290a0 100644 --- a/net/ghttp/ghttp_request_param_page.go +++ b/net/ghttp/ghttp_request_param_page.go @@ -17,13 +17,15 @@ import ( // NOTE THAT the page parameter name from client is constantly defined as gpage.DefaultPageName // for simplification and convenience. func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page { - // It must has Router object attribute. + // It must have Router object attribute. if r.Router == nil { panic("Router object not found") } - url := *r.URL - urlTemplate := url.Path - uriHasPageName := false + var ( + url = *r.URL + urlTemplate = url.Path + uriHasPageName = false + ) // Check the page variable in the URI. if len(r.Router.RegNames) > 0 { for _, name := range r.Router.RegNames { @@ -39,12 +41,17 @@ func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page { for i, name := range r.Router.RegNames { rule := fmt.Sprintf(`[:\*]%s|\{%s\}`, name, name) if name == gpage.DefaultPageName { - urlTemplate, _ = gregex.ReplaceString(rule, gpage.DefaultPagePlaceHolder, urlTemplate) + urlTemplate, err = gregex.ReplaceString(rule, gpage.DefaultPagePlaceHolder, urlTemplate) } else { - urlTemplate, _ = gregex.ReplaceString(rule, match[i+1], urlTemplate) + urlTemplate, err = gregex.ReplaceString(rule, match[i+1], urlTemplate) + } + if err != nil { + panic(err) } } } + } else { + panic(err) } } } diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 041b182e7..a0153aa0c 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -107,7 +107,7 @@ func GetServer(name ...interface{}) *Server { } // Initialize the server using default configurations. if err := s.SetConfig(NewConfig()); err != nil { - panic(err) + panic(gerror.WrapCode(gerror.CodeInvalidConfiguration, err, "")) } // Record the server to internal server mapping by name. serverMapping.Set(serverName, s) diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index e461a9222..7803a295d 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -68,12 +68,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { request.Response.WriteStatus(http.StatusInternalServerError) if err, ok := exception.(error); ok { if code := gerror.Code(err); code != gerror.CodeNil { - s.handleErrorLog(gerror.Wrap(err, ""), request) + s.handleErrorLog(err, request) } else { - s.handleErrorLog(gerror.WrapCode(gerror.CodeInternalError, err, ""), request) + s.handleErrorLog(gerror.WrapCodeSkip(gerror.CodeInternalError, 1, err, ""), request) } } else { - s.handleErrorLog(gerror.NewCodef(gerror.CodeInternalError, "%v", exception), request) + s.handleErrorLog(gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%+v", exception), request) } } } diff --git a/net/ghttp/ghttp_server_router_serve.go b/net/ghttp/ghttp_server_router_serve.go index 2f36302e1..d3614312e 100644 --- a/net/ghttp/ghttp_server_router_serve.go +++ b/net/ghttp/ghttp_server_router_serve.go @@ -8,6 +8,7 @@ package ghttp import ( "fmt" + "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "strings" @@ -175,8 +176,8 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han parsedItemList.PushBack(parsedItem) // The middleware is inserted before the serving handler. - // If there're multiple middleware, they're inserted into the result list by their registering order. - // The middleware are also executed by their registered order. + // If there are multiple middleware, they're inserted into the result list by their registering order. + // The middleware is also executed by their registered order. case handlerTypeMiddleware: if lastMiddlewareElem == nil { lastMiddlewareElem = parsedItemList.PushFront(parsedItem) @@ -190,7 +191,7 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han parsedItemList.PushBack(parsedItem) default: - panic(fmt.Sprintf(`invalid handler type %d`, item.Type)) + panic(gerror.NewCodef(gerror.CodeInternalError, `invalid handler type %d`, item.Type)) } } } diff --git a/net/ghttp/ghttp_unit_config_test.go b/net/ghttp/ghttp_unit_config_test.go index 95ba5abfb..cd5949b97 100644 --- a/net/ghttp/ghttp_unit_config_test.go +++ b/net/ghttp/ghttp_unit_config_test.go @@ -152,7 +152,7 @@ func Test_ClientMaxBodySize_File(t *testing.T) { defer gfile.Remove(path) t.Assert( gstr.Trim(c.PostContent("/", "name=john&file=@file:"+path)), - "http: request body too large", + "Invalid Request: http: request body too large", ) }) } From 9cd944fd775a4f0e5f247870be0a19c3a27ec157 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sat, 7 Aug 2021 11:01:15 +0800 Subject: [PATCH 431/492] improve time maintaining feature for package gdb --- database/gdb/gdb_core.go | 9 ++++----- database/gdb/gdb_model_insert.go | 2 -- database/gdb/gdb_model_update.go | 4 ---- database/gdb/gdb_z_mysql_time_maintain_test.go | 11 +++++++---- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 3b0cbe7f5..48512fc39 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -455,9 +455,8 @@ func (c *Core) formatOnDuplicate(columns []string, option DoInsertOption) string } } else { for _, column := range columns { - // If it's SAVE operation, - // do not automatically update the creating time. - if c.isSoftCreatedFilledName(column) { + // If it's SAVE operation, do not automatically update the creating time. + if c.isSoftCreatedFieldName(column) { continue } if len(onDuplicateStr) > 0 { @@ -679,8 +678,8 @@ func (c *Core) HasTable(name string) (bool, error) { return false, nil } -// isSoftCreatedFilledName checks and returns whether given filed name is an automatic-filled created time. -func (c *Core) isSoftCreatedFilledName(fieldName string) bool { +// isSoftCreatedFieldName checks and returns whether given filed name is an automatic-filled created time. +func (c *Core) isSoftCreatedFieldName(fieldName string) bool { if fieldName == "" { return false } diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 6f29229d4..90250c81b 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -217,7 +217,6 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err nowString = gtime.Now().String() fieldNameCreate = m.getSoftFieldNameCreated() fieldNameUpdate = m.getSoftFieldNameUpdated() - fieldNameDelete = m.getSoftFieldNameDeleted() ) newData, err := m.filterDataForInsertOrUpdate(m.data) if err != nil { @@ -286,7 +285,6 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err // Automatic handling for creating/updating time. if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") { for k, v := range list { - gutil.MapDelete(v, fieldNameCreate, fieldNameUpdate, fieldNameDelete) if fieldNameCreate != "" { v[fieldNameCreate] = nowString } diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index 80d7ad2a8..12f2ab492 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -15,7 +15,6 @@ import ( "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" - "github.com/gogf/gf/util/gutil" ) // Update does "UPDATE ... " statement for the model. @@ -43,9 +42,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro } var ( updateData = m.data - fieldNameCreate = m.getSoftFieldNameCreated() fieldNameUpdate = m.getSoftFieldNameUpdated() - fieldNameDelete = m.getSoftFieldNameDeleted() conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false, false) ) // Automatically update the record updating time. @@ -61,7 +58,6 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro switch refKind { case reflect.Map, reflect.Struct: dataMap := ConvertDataForTableRecord(m.data) - gutil.MapDelete(dataMap, fieldNameCreate, fieldNameUpdate, fieldNameDelete) if fieldNameUpdate != "" { dataMap[fieldNameUpdate] = gtime.Now().String() } diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index 120a4cd55..860518fc7 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -663,6 +663,9 @@ CREATE TABLE %s ( } defer dropTable(table) + //db.SetDebug(true) + //defer db.SetDebug(false) + type Entity struct { Id uint64 `orm:"id,primary" json:"id"` Name string `orm:"name" json:"name"` @@ -679,7 +682,7 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err := db.Model(table).Data(dataInsert).Insert() + r, err := db.Model(table).Data(dataInsert).OmitEmpty().Insert() t.AssertNil(err) n, _ := r.RowsAffected() t.Assert(n, 1) @@ -702,7 +705,7 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err = db.Model(table).Data(dataSave).Save() + r, err = db.Model(table).Data(dataSave).OmitEmpty().Save() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) @@ -726,7 +729,7 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update() + r, err = db.Model(table).Data(dataUpdate).WherePri(1).OmitEmpty().Update() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) @@ -747,7 +750,7 @@ CREATE TABLE %s ( UpdateAt: nil, DeleteAt: nil, } - r, err = db.Model(table).Data(dataReplace).Replace() + r, err = db.Model(table).Data(dataReplace).OmitEmpty().Replace() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 2) From 4eb057227c8f55bed5d9d416786a4dbc48501043 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sat, 7 Aug 2021 11:59:30 +0800 Subject: [PATCH 432/492] add SizeFormat function for package gfile; add more internal logging for rotation feature for pacakge glog --- os/gfile/gfile_size.go | 5 +++++ os/gfile/gfile_z_size_test.go | 19 +++++++++++++++++++ os/glog/glog_logger_rotate.go | 8 ++++++++ os/gmlock/gmlock_locker.go | 10 +++++----- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/os/gfile/gfile_size.go b/os/gfile/gfile_size.go index 615b9e765..9e3d8f0a6 100644 --- a/os/gfile/gfile_size.go +++ b/os/gfile/gfile_size.go @@ -22,6 +22,11 @@ func Size(path string) int64 { return s.Size() } +// SizeFormat returns the size of file specified by <path> in format string. +func SizeFormat(path string) string { + return FormatSize(Size(path)) +} + // ReadableSize formats size of file given by <path>, for more human readable. func ReadableSize(path string) string { return FormatSize(Size(path)) diff --git a/os/gfile/gfile_z_size_test.go b/os/gfile/gfile_z_size_test.go index 481f84ef7..9425fa866 100644 --- a/os/gfile/gfile_z_size_test.go +++ b/os/gfile/gfile_z_size_test.go @@ -33,6 +33,25 @@ func Test_Size(t *testing.T) { }) } +func Test_SizeFormat(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + paths1 = "/testfile_t1.txt" + sizes string + ) + + createTestFile(paths1, "abcdefghijklmn") + defer delTestFiles(paths1) + + sizes = gfile.SizeFormat(testpath() + paths1) + t.Assert(sizes, "14.00B") + + sizes = gfile.SizeFormat("") + t.Assert(sizes, "0.00B") + + }) +} + func Test_StrToSize(t *testing.T) { gtest.C(t, func(t *gtest.T) { t.Assert(gfile.StrToSize("0.00B"), 0) diff --git a/os/glog/glog_logger_rotate.go b/os/glog/glog_logger_rotate.go index 697793854..9262b989b 100644 --- a/os/glog/glog_logger_rotate.go +++ b/os/glog/glog_logger_rotate.go @@ -43,6 +43,13 @@ func (l *Logger) doRotateFile(filePath string) error { } defer gmlock.Unlock(memoryLockKey) + intlog.PrintFunc(l.ctx, func() string { + return fmt.Sprintf(`start rotating file by size: %s, file: %s`, gfile.SizeFormat(filePath), filePath) + }) + defer intlog.PrintFunc(l.ctx, func() string { + return fmt.Sprintf(`done rotating file by size: %s, size: %s`, gfile.SizeFormat(filePath), filePath) + }) + // No backups, it then just removes the current logging file. if l.config.RotateBackupLimit == 0 { if err := gfile.Remove(filePath); err != nil { @@ -90,6 +97,7 @@ func (l *Logger) doRotateFile(filePath string) error { intlog.Printf(l.ctx, `rotation file exists, continue: %s`, newFilePath) } } + intlog.Printf(l.ctx, "rotating file by size from %s to %s", filePath, newFilePath) if err := gfile.Rename(filePath, newFilePath); err != nil { return err } diff --git a/os/gmlock/gmlock_locker.go b/os/gmlock/gmlock_locker.go index 11f195057..075d139e2 100644 --- a/os/gmlock/gmlock_locker.go +++ b/os/gmlock/gmlock_locker.go @@ -11,9 +11,9 @@ import ( "github.com/gogf/gf/os/gmutex" ) -// Memory locker. +// Locker is a memory based locker. // Note that there's no cache expire mechanism for mutex in locker. -// You need remove certain mutex manually when you do not want use it any more. +// You need remove certain mutex manually when you do not want use it anymore. type Locker struct { m *gmap.StrAnyMap } @@ -28,7 +28,7 @@ func New() *Locker { // Lock locks the <key> with writing lock. // If there's a write/reading lock the <key>, -// it will blocks until the lock is released. +// it will block until the lock is released. func (l *Locker) Lock(key string) { l.getOrNewMutex(key).Lock() } @@ -68,7 +68,7 @@ func (l *Locker) RUnlock(key string) { // LockFunc locks the <key> with writing lock and callback function <f>. // If there's a write/reading lock the <key>, -// it will blocks until the lock is released. +// it will block until the lock is released. // // It releases the lock after <f> is executed. func (l *Locker) LockFunc(key string, f func()) { @@ -79,7 +79,7 @@ func (l *Locker) LockFunc(key string, f func()) { // RLockFunc locks the <key> with reading lock and callback function <f>. // If there's a writing lock the <key>, -// it will blocks until the lock is released. +// it will block until the lock is released. // // It releases the lock after <f> is executed. func (l *Locker) RLockFunc(key string, f func()) { From fc1dfb7ff986eacf85d3913aaa166c86eb4bb2c5 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sun, 8 Aug 2021 13:10:21 +0800 Subject: [PATCH 433/492] comment update;standardize const naming for package gtcp/gudp --- internal/command/command.go | 2 +- internal/empty/empty.go | 7 ++-- internal/structs/structs.go | 2 +- internal/structs/structs_field.go | 2 +- internal/structs/structs_tag.go | 3 +- internal/structs/structs_type.go | 5 ++- internal/utils/utils_io.go | 4 +-- net/gtcp/gtcp_conn.go | 54 +++++++++++++++---------------- net/gtcp/gtcp_conn_pkg.go | 22 ++++++------- net/gtcp/gtcp_func.go | 10 +++--- net/gtcp/gtcp_pool.go | 43 ++++++++++++------------ net/gtcp/gtcp_pool_pkg.go | 16 ++++----- net/gtcp/gtcp_server.go | 12 +++---- net/gudp/gudp.go | 2 +- net/gudp/gudp_conn.go | 36 ++++++++++----------- net/gudp/gudp_server.go | 4 +-- os/glog/glog_chaining.go | 2 +- 17 files changed, 114 insertions(+), 112 deletions(-) diff --git a/internal/command/command.go b/internal/command/command.go index 824f83ca3..87fce610c 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -20,7 +20,7 @@ var ( argumentRegex = regexp.MustCompile(`^\-{1,2}([\w\?\.\-]+)(=){0,1}(.*)$`) ) -// Custom initialization. +// Init does custom initialization. func Init(args ...string) { if len(args) == 0 { if len(defaultParsedArgs) == 0 && len(defaultParsedOptions) == 0 { diff --git a/internal/empty/empty.go b/internal/empty/empty.go index 023f759a9..af1b7fe75 100644 --- a/internal/empty/empty.go +++ b/internal/empty/empty.go @@ -286,10 +286,9 @@ func IsEmpty(value interface{}) bool { //} // IsNil checks whether given `value` is nil. -// Parameter `traceSource` is used for tracing to the source variable if given `value` is type -// of a pinter that also points to a pointer. It returns nil if the source is nil when `traceSource` -// is true. -// Note that it might use reflect feature which affects performance a little bit. +// Parameter `traceSource` is used for tracing to the source variable if given `value` is type of pinter +// that also points to a pointer. It returns nil if the source is nil when `traceSource` is true. +// Note that it might use reflect feature which affects performance a little. func IsNil(value interface{}, traceSource ...bool) bool { if value == nil { return true diff --git a/internal/structs/structs.go b/internal/structs/structs.go index d6204497c..d9dbfa83c 100644 --- a/internal/structs/structs.go +++ b/internal/structs/structs.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package structs provides functions for struct conversion. +// Package structs provides functions for struct information retrieving and struct conversion. // // Inspired and improved from: https://github.com/fatih/structs package structs diff --git a/internal/structs/structs_field.go b/internal/structs/structs_field.go index 1af783adf..f72bc83e6 100644 --- a/internal/structs/structs_field.go +++ b/internal/structs/structs_field.go @@ -16,7 +16,7 @@ func (f *Field) Tag(key string) string { // TagLookup returns the value associated with key in the tag string. // If the key is present in the tag the value (which may be empty) -// is returned. Otherwise the returned value will be the empty string. +// is returned. Otherwise, the returned value will be the empty string. // The ok return value reports whether the value was explicitly set in // the tag string. If the tag does not have the conventional format, // the value returned by Lookup is unspecified. diff --git a/internal/structs/structs_tag.go b/internal/structs/structs_tag.go index a167e07e4..f7d8c85c7 100644 --- a/internal/structs/structs_tag.go +++ b/internal/structs/structs_tag.go @@ -53,7 +53,7 @@ func ParseTag(tag string) map[string]string { if i >= len(tag) { break } - quotedValue := string(tag[:i+1]) + quotedValue := tag[:i+1] tag = tag[i+1:] value, err := strconv.Unquote(quotedValue) if err != nil { @@ -137,6 +137,7 @@ func getFieldValues(value interface{}) ([]*Field, error) { goto exitLoop } } + exitLoop: for reflectKind == reflect.Ptr { reflectValue = reflectValue.Elem() diff --git a/internal/structs/structs_type.go b/internal/structs/structs_type.go index ed30ecab2..40cb02050 100644 --- a/internal/structs/structs_type.go +++ b/internal/structs/structs_type.go @@ -37,13 +37,16 @@ func StructType(object interface{}) (*Type, error) { reflectValue = reflectValue.Elem() reflectKind = reflectValue.Kind() } + case reflect.Array, reflect.Slice: reflectValue = reflect.New(reflectValue.Type().Elem()).Elem() reflectKind = reflectValue.Kind() + default: goto exitLoop } } + exitLoop: for reflectKind == reflect.Ptr { reflectValue = reflectValue.Elem() @@ -63,7 +66,7 @@ exitLoop: }, nil } -// Signature returns an unique string as this type. +// Signature returns a unique string as this type. func (t Type) Signature() string { return t.PkgPath() + "/" + t.String() } diff --git a/internal/utils/utils_io.go b/internal/utils/utils_io.go index 1d227bd79..9da382d4e 100644 --- a/internal/utils/utils_io.go +++ b/internal/utils/utils_io.go @@ -21,7 +21,7 @@ type ReadCloser struct { repeatable bool } -// NewRepeatReadCloser creates and returns a RepeatReadCloser object. +// NewReadCloser creates and returns a RepeatReadCloser object. func NewReadCloser(content []byte, repeatable bool) io.ReadCloser { return &ReadCloser{ content: content, @@ -29,7 +29,7 @@ func NewReadCloser(content []byte, repeatable bool) io.ReadCloser { } } -// NewRepeatReadCloserWithReadCloser creates and returns a RepeatReadCloser object +// NewReadCloserWithReadCloser creates and returns a RepeatReadCloser object // with given io.ReadCloser. func NewReadCloserWithReadCloser(r io.ReadCloser, repeatable bool) (io.ReadCloser, error) { content, err := ioutil.ReadAll(r) diff --git a/net/gtcp/gtcp_conn.go b/net/gtcp/gtcp_conn.go index 2a2c8e61b..ff0154c13 100644 --- a/net/gtcp/gtcp_conn.go +++ b/net/gtcp/gtcp_conn.go @@ -15,18 +15,18 @@ import ( "time" ) -// TCP connection object. +// Conn is the TCP connection object. type Conn struct { - net.Conn // Underlying TCP connection object. - reader *bufio.Reader // Buffer reader for connection. - recvDeadline time.Time // Timeout point for reading. - sendDeadline time.Time // Timeout point for writing. - recvBufferWait time.Duration // Interval duration for reading buffer. + net.Conn // Underlying TCP connection object. + reader *bufio.Reader // Buffer reader for connection. + receiveDeadline time.Time // Timeout point for reading. + sendDeadline time.Time // Timeout point for writing. + receiveBufferWait time.Duration // Interval duration for reading buffer. } const ( // Default interval for reading buffer. - gRECV_ALL_WAIT_TIMEOUT = time.Millisecond + receiveAllWaitTimeout = time.Millisecond ) // NewConn creates and returns a new connection with given address. @@ -61,11 +61,11 @@ func NewConnKeyCrt(addr, crtFile, keyFile string) (*Conn, error) { // NewConnByNetConn creates and returns a TCP connection object with given net.Conn object. func NewConnByNetConn(conn net.Conn) *Conn { return &Conn{ - Conn: conn, - reader: bufio.NewReader(conn), - recvDeadline: time.Time{}, - sendDeadline: time.Time{}, - recvBufferWait: gRECV_ALL_WAIT_TIMEOUT, + Conn: conn, + reader: bufio.NewReader(conn), + receiveDeadline: time.Time{}, + sendDeadline: time.Time{}, + receiveBufferWait: receiveAllWaitTimeout, } } @@ -84,7 +84,7 @@ func (c *Conn) Send(data []byte, retry ...Retry) error { if len(retry) > 0 { retry[0].Count-- if retry[0].Interval == 0 { - retry[0].Interval = gDEFAULT_RETRY_INTERVAL + retry[0].Interval = defaultRetryInternal } time.Sleep(retry[0].Interval) } @@ -113,13 +113,13 @@ func (c *Conn) Recv(length int, retry ...Retry) ([]byte, error) { if length > 0 { buffer = make([]byte, length) } else { - buffer = make([]byte, gDEFAULT_READ_BUFFER_SIZE) + buffer = make([]byte, defaultReadBufferSize) } for { if length < 0 && index > 0 { bufferWait = true - if err = c.SetReadDeadline(time.Now().Add(c.recvBufferWait)); err != nil { + if err = c.SetReadDeadline(time.Now().Add(c.receiveBufferWait)); err != nil { return nil, err } } @@ -132,9 +132,9 @@ func (c *Conn) Recv(length int, retry ...Retry) ([]byte, error) { break } } else { - if index >= gDEFAULT_READ_BUFFER_SIZE { + if index >= defaultReadBufferSize { // If it exceeds the buffer size, it then automatically increases its buffer size. - buffer = append(buffer, make([]byte, gDEFAULT_READ_BUFFER_SIZE)...) + buffer = append(buffer, make([]byte, defaultReadBufferSize)...) } else { // It returns immediately if received size is lesser than buffer size. if !bufferWait { @@ -150,7 +150,7 @@ func (c *Conn) Recv(length int, retry ...Retry) ([]byte, error) { } // Re-set the timeout when reading data. if bufferWait && isTimeout(err) { - if err = c.SetReadDeadline(c.recvDeadline); err != nil { + if err = c.SetReadDeadline(c.receiveDeadline); err != nil { return nil, err } err = nil @@ -163,7 +163,7 @@ func (c *Conn) Recv(length int, retry ...Retry) ([]byte, error) { } retry[0].Count-- if retry[0].Interval == 0 { - retry[0].Interval = gDEFAULT_RETRY_INTERVAL + retry[0].Interval = defaultRetryInternal } time.Sleep(retry[0].Interval) continue @@ -230,10 +230,10 @@ func (c *Conn) RecvTil(til []byte, retry ...Retry) ([]byte, error) { // RecvWithTimeout reads data from the connection with timeout. func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) (data []byte, err error) { - if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil { + if err := c.SetreceiveDeadline(time.Now().Add(timeout)); err != nil { return nil, err } - defer c.SetRecvDeadline(time.Time{}) + defer c.SetreceiveDeadline(time.Time{}) data, err = c.Recv(length, retry...) return } @@ -269,16 +269,16 @@ func (c *Conn) SendRecvWithTimeout(data []byte, length int, timeout time.Duratio func (c *Conn) SetDeadline(t time.Time) error { err := c.Conn.SetDeadline(t) if err == nil { - c.recvDeadline = t + c.receiveDeadline = t c.sendDeadline = t } return err } -func (c *Conn) SetRecvDeadline(t time.Time) error { +func (c *Conn) SetreceiveDeadline(t time.Time) error { err := c.SetReadDeadline(t) if err == nil { - c.recvDeadline = t + c.receiveDeadline = t } return err } @@ -291,8 +291,8 @@ func (c *Conn) SetSendDeadline(t time.Time) error { return err } -// SetRecvBufferWait sets the buffer waiting timeout when reading all data from connection. +// SetreceiveBufferWait sets the buffer waiting timeout when reading all data from connection. // The waiting duration cannot be too long which might delay receiving data from remote address. -func (c *Conn) SetRecvBufferWait(bufferWaitDuration time.Duration) { - c.recvBufferWait = bufferWaitDuration +func (c *Conn) SetreceiveBufferWait(bufferWaitDuration time.Duration) { + c.receiveBufferWait = bufferWaitDuration } diff --git a/net/gtcp/gtcp_conn_pkg.go b/net/gtcp/gtcp_conn_pkg.go index a13a036f6..ef090adcf 100644 --- a/net/gtcp/gtcp_conn_pkg.go +++ b/net/gtcp/gtcp_conn_pkg.go @@ -13,11 +13,11 @@ import ( ) const ( - gPKG_HEADER_SIZE_DEFAULT = 2 // Header size for simple package protocol. - gPKG_HEADER_SIZE_MAX = 4 // Max header size for simple package protocol. + pkgHeaderSizeDefault = 2 // Header size for simple package protocol. + pkgHeaderSizeMax = 4 // Max header size for simple package protocol. ) -// Package option for simple protocol. +// PkgOption is package option for simple protocol. type PkgOption struct { // HeaderSize is used to mark the data length for next data receiving. // It's 2 bytes in default, 4 bytes max, which stands for the max data length @@ -51,10 +51,10 @@ func (c *Conn) SendPkg(data []byte, option ...PkgOption) error { length, pkgOption.MaxDataSize, ) } - offset := gPKG_HEADER_SIZE_MAX - pkgOption.HeaderSize - buffer := make([]byte, gPKG_HEADER_SIZE_MAX+len(data)) + offset := pkgHeaderSizeMax - pkgOption.HeaderSize + buffer := make([]byte, pkgHeaderSizeMax+len(data)) binary.BigEndian.PutUint32(buffer[0:], uint32(length)) - copy(buffer[gPKG_HEADER_SIZE_MAX:], data) + copy(buffer[pkgHeaderSizeMax:], data) if pkgOption.Retry.Count > 0 { return c.Send(buffer[offset:], pkgOption.Retry) } @@ -128,10 +128,10 @@ func (c *Conn) RecvPkg(option ...PkgOption) (result []byte, err error) { // RecvPkgWithTimeout reads data from connection with timeout using simple package protocol. func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (data []byte, err error) { - if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil { + if err := c.SetreceiveDeadline(time.Now().Add(timeout)); err != nil { return nil, err } - defer c.SetRecvDeadline(time.Time{}) + defer c.SetreceiveDeadline(time.Time{}) data, err = c.RecvPkg(option...) return } @@ -144,12 +144,12 @@ func getPkgOption(option ...PkgOption) (*PkgOption, error) { pkgOption = option[0] } if pkgOption.HeaderSize == 0 { - pkgOption.HeaderSize = gPKG_HEADER_SIZE_DEFAULT + pkgOption.HeaderSize = pkgHeaderSizeDefault } - if pkgOption.HeaderSize > gPKG_HEADER_SIZE_MAX { + if pkgOption.HeaderSize > pkgHeaderSizeMax { return nil, fmt.Errorf( `package header size %d definition exceeds max header size %d`, - pkgOption.HeaderSize, gPKG_HEADER_SIZE_MAX, + pkgOption.HeaderSize, pkgHeaderSizeMax, ) } if pkgOption.MaxDataSize == 0 { diff --git a/net/gtcp/gtcp_func.go b/net/gtcp/gtcp_func.go index 7643f918b..c2d520f49 100644 --- a/net/gtcp/gtcp_func.go +++ b/net/gtcp/gtcp_func.go @@ -16,9 +16,9 @@ import ( ) const ( - gDEFAULT_CONN_TIMEOUT = 30 * time.Second // Default connection timeout. - gDEFAULT_RETRY_INTERVAL = 100 * time.Millisecond // Default retry interval. - gDEFAULT_READ_BUFFER_SIZE = 128 // (Byte) Buffer size for reading. + defaultConnTimeout = 30 * time.Second // Default connection timeout. + defaultRetryInternal = 100 * time.Millisecond // Default retry interval. + defaultReadBufferSize = 128 // (Byte) Buffer size for reading. ) type Retry struct { @@ -29,7 +29,7 @@ type Retry struct { // NewNetConn creates and returns a net.Conn with given address like "127.0.0.1:80". // The optional parameter <timeout> specifies the timeout for dialing connection. func NewNetConn(addr string, timeout ...time.Duration) (net.Conn, error) { - d := gDEFAULT_CONN_TIMEOUT + d := defaultConnTimeout if len(timeout) > 0 { d = timeout[0] } @@ -40,7 +40,7 @@ func NewNetConn(addr string, timeout ...time.Duration) (net.Conn, error) { // The optional parameter <timeout> specifies the timeout for dialing connection. func NewNetConnTLS(addr string, tlsConfig *tls.Config, timeout ...time.Duration) (net.Conn, error) { dialer := &net.Dialer{ - Timeout: gDEFAULT_CONN_TIMEOUT, + Timeout: defaultConnTimeout, } if len(timeout) > 0 { dialer.Timeout = timeout[0] diff --git a/net/gtcp/gtcp_pool.go b/net/gtcp/gtcp_pool.go index eda264b38..49a5cf33e 100644 --- a/net/gtcp/gtcp_pool.go +++ b/net/gtcp/gtcp_pool.go @@ -14,19 +14,18 @@ import ( ) // PoolConn is a connection with pool feature for TCP. -// Note that it is NOT a pool or connection manager, -// it is just a TCP connection object. +// Note that it is NOT a pool or connection manager, it is just a TCP connection object. type PoolConn struct { *Conn // Underlying connection object. - pool *gpool.Pool // Connection pool, which is not a really connection pool, but a connection reusable pool. + pool *gpool.Pool // Connection pool, which is not a real connection pool, but a connection reusable pool. status int // Status of current connection, which is used to mark this connection usable or not. } const ( - gDEFAULT_POOL_EXPIRE = 10 * time.Second // Default TTL for connection in the pool. - gCONN_STATUS_UNKNOWN = 0 // Means it is unknown it's connective or not. - gCONN_STATUS_ACTIVE = 1 // Means it is now connective. - gCONN_STATUS_ERROR = 2 // Means it should be closed and removed from pool. + defaultPoolExpire = 10 * time.Second // Default TTL for connection in the pool. + connStatusUnknown = 0 // Means it is unknown it's connective or not. + connStatusActive = 1 // Means it is now connective. + connStatusError = 2 // Means it should be closed and removed from pool. ) var ( @@ -38,9 +37,9 @@ var ( func NewPoolConn(addr string, timeout ...time.Duration) (*PoolConn, error) { v := addressPoolMap.GetOrSetFuncLock(addr, func() interface{} { var pool *gpool.Pool - pool = gpool.New(gDEFAULT_POOL_EXPIRE, func() (interface{}, error) { + pool = gpool.New(defaultPoolExpire, func() (interface{}, error) { if conn, err := NewConn(addr, timeout...); err == nil { - return &PoolConn{conn, pool, gCONN_STATUS_ACTIVE}, nil + return &PoolConn{conn, pool, connStatusActive}, nil } else { return nil, err } @@ -60,8 +59,8 @@ func NewPoolConn(addr string, timeout ...time.Duration) (*PoolConn, error) { // Note that, if <c> calls Close function closing itself, <c> can not // be used again. func (c *PoolConn) Close() error { - if c.pool != nil && c.status == gCONN_STATUS_ACTIVE { - c.status = gCONN_STATUS_UNKNOWN + if c.pool != nil && c.status == connStatusActive { + c.status = connStatusUnknown c.pool.Put(c) } else { return c.Conn.Close() @@ -73,7 +72,7 @@ func (c *PoolConn) Close() error { // writing data. func (c *PoolConn) Send(data []byte, retry ...Retry) error { err := c.Conn.Send(data, retry...) - if err != nil && c.status == gCONN_STATUS_UNKNOWN { + if err != nil && c.status == connStatusUnknown { if v, e := c.pool.Get(); e == nil { c.Conn = v.(*PoolConn).Conn err = c.Send(data, retry...) @@ -82,9 +81,9 @@ func (c *PoolConn) Send(data []byte, retry ...Retry) error { } } if err != nil { - c.status = gCONN_STATUS_ERROR + c.status = connStatusError } else { - c.status = gCONN_STATUS_ACTIVE + c.status = connStatusActive } return err } @@ -93,9 +92,9 @@ func (c *PoolConn) Send(data []byte, retry ...Retry) error { func (c *PoolConn) Recv(length int, retry ...Retry) ([]byte, error) { data, err := c.Conn.Recv(length, retry...) if err != nil { - c.status = gCONN_STATUS_ERROR + c.status = connStatusError } else { - c.status = gCONN_STATUS_ACTIVE + c.status = connStatusActive } return data, err } @@ -105,9 +104,9 @@ func (c *PoolConn) Recv(length int, retry ...Retry) ([]byte, error) { func (c *PoolConn) RecvLine(retry ...Retry) ([]byte, error) { data, err := c.Conn.RecvLine(retry...) if err != nil { - c.status = gCONN_STATUS_ERROR + c.status = connStatusError } else { - c.status = gCONN_STATUS_ACTIVE + c.status = connStatusActive } return data, err } @@ -117,19 +116,19 @@ func (c *PoolConn) RecvLine(retry ...Retry) ([]byte, error) { func (c *PoolConn) RecvTil(til []byte, retry ...Retry) ([]byte, error) { data, err := c.Conn.RecvTil(til, retry...) if err != nil { - c.status = gCONN_STATUS_ERROR + c.status = connStatusError } else { - c.status = gCONN_STATUS_ACTIVE + c.status = connStatusActive } return data, err } // RecvWithTimeout reads data from the connection with timeout. func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) (data []byte, err error) { - if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil { + if err := c.SetreceiveDeadline(time.Now().Add(timeout)); err != nil { return nil, err } - defer c.SetRecvDeadline(time.Time{}) + defer c.SetreceiveDeadline(time.Time{}) data, err = c.Recv(length, retry...) return } diff --git a/net/gtcp/gtcp_pool_pkg.go b/net/gtcp/gtcp_pool_pkg.go index 7412e829c..ba64af00e 100644 --- a/net/gtcp/gtcp_pool_pkg.go +++ b/net/gtcp/gtcp_pool_pkg.go @@ -13,7 +13,7 @@ import ( // SendPkg sends a package containing <data> to the connection. // The optional parameter <option> specifies the package options for sending. func (c *PoolConn) SendPkg(data []byte, option ...PkgOption) (err error) { - if err = c.Conn.SendPkg(data, option...); err != nil && c.status == gCONN_STATUS_UNKNOWN { + if err = c.Conn.SendPkg(data, option...); err != nil && c.status == connStatusUnknown { if v, e := c.pool.NewFunc(); e == nil { c.Conn = v.(*PoolConn).Conn err = c.Conn.SendPkg(data, option...) @@ -22,9 +22,9 @@ func (c *PoolConn) SendPkg(data []byte, option ...PkgOption) (err error) { } } if err != nil { - c.status = gCONN_STATUS_ERROR + c.status = connStatusError } else { - c.status = gCONN_STATUS_ACTIVE + c.status = connStatusActive } return err } @@ -34,19 +34,19 @@ func (c *PoolConn) SendPkg(data []byte, option ...PkgOption) (err error) { func (c *PoolConn) RecvPkg(option ...PkgOption) ([]byte, error) { data, err := c.Conn.RecvPkg(option...) if err != nil { - c.status = gCONN_STATUS_ERROR + c.status = connStatusError } else { - c.status = gCONN_STATUS_ACTIVE + c.status = connStatusActive } return data, err } // RecvPkgWithTimeout reads data from connection with timeout using simple package protocol. func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (data []byte, err error) { - if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil { + if err := c.SetreceiveDeadline(time.Now().Add(timeout)); err != nil { return nil, err } - defer c.SetRecvDeadline(time.Time{}) + defer c.SetreceiveDeadline(time.Time{}) data, err = c.RecvPkg(option...) return } @@ -70,7 +70,7 @@ func (c *PoolConn) SendRecvPkg(data []byte, option ...PkgOption) ([]byte, error) } } -// RecvPkgWithTimeout reads data from connection with timeout using simple package protocol. +// SendRecvPkgWithTimeout reads data from connection with timeout using simple package protocol. func (c *PoolConn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) ([]byte, error) { if err := c.SendPkg(data, option...); err == nil { return c.RecvPkgWithTimeout(timeout, option...) diff --git a/net/gtcp/gtcp_server.go b/net/gtcp/gtcp_server.go index 6d22539af..57f21e62c 100644 --- a/net/gtcp/gtcp_server.go +++ b/net/gtcp/gtcp_server.go @@ -18,11 +18,11 @@ import ( ) const ( - // Default TCP server name. - gDEFAULT_SERVER = "default" + // defaultServer is the default TCP server name. + defaultServer = "default" ) -// TCP Server. +// Server is a TCP server. type Server struct { mu sync.Mutex // Used for Server.listen concurrent safety. listen net.Listener // Listener. @@ -38,7 +38,7 @@ var serverMapping = gmap.NewStrAnyMap(true) // or it returns a new normal TCP server named <name> if it does not exist. // The parameter <name> is used to specify the TCP server func GetServer(name ...interface{}) *Server { - serverName := gDEFAULT_SERVER + serverName := defaultServer if len(name) > 0 && name[0] != "" { serverName = gconv.String(name[0]) } @@ -88,7 +88,7 @@ func (s *Server) SetHandler(handler func(*Conn)) { s.handler = handler } -// SetTlsKeyCrt sets the certificate and key file for TLS configuration of server. +// SetTLSKeyCrt sets the certificate and key file for TLS configuration of server. func (s *Server) SetTLSKeyCrt(crtFile, keyFile string) error { tlsConfig, err := LoadKeyCrt(crtFile, keyFile) if err != nil { @@ -98,7 +98,7 @@ func (s *Server) SetTLSKeyCrt(crtFile, keyFile string) error { return nil } -// SetTlsConfig sets the TLS configuration of server. +// SetTLSConfig sets the TLS configuration of server. func (s *Server) SetTLSConfig(tlsConfig *tls.Config) { s.tlsConfig = tlsConfig } diff --git a/net/gudp/gudp.go b/net/gudp/gudp.go index 8873d5904..1f0dc99d6 100644 --- a/net/gudp/gudp.go +++ b/net/gudp/gudp.go @@ -4,5 +4,5 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gtcp provides UDP server and client implementations. +// Package gudp provides UDP server and client implementations. package gudp diff --git a/net/gudp/gudp_conn.go b/net/gudp/gudp_conn.go index 52aaccfab..ff3d5341a 100644 --- a/net/gudp/gudp_conn.go +++ b/net/gudp/gudp_conn.go @@ -14,17 +14,17 @@ import ( // Conn handles the UDP connection. type Conn struct { - *net.UDPConn // Underlying UDP connection. - remoteAddr *net.UDPAddr // Remote address. - recvDeadline time.Time // Timeout point for reading data. - sendDeadline time.Time // Timeout point for writing data. - recvBufferWait time.Duration // Interval duration for reading buffer. + *net.UDPConn // Underlying UDP connection. + remoteAddr *net.UDPAddr // Remote address. + receiveDeadline time.Time // Timeout point for reading data. + sendDeadline time.Time // Timeout point for writing data. + receiveBufferWait time.Duration // Interval duration for reading buffer. } const ( - gDEFAULT_RETRY_INTERVAL = 100 * time.Millisecond // Retry interval. - gDEFAULT_READ_BUFFER_SIZE = 1024 // (Byte)Buffer size. - gRECV_ALL_WAIT_TIMEOUT = time.Millisecond // Default interval for reading buffer. + defaultRetryInterval = 100 * time.Millisecond // Retry interval. + defaultReadBufferSize = 1024 // (Byte)Buffer size. + receiveAllWaitTimeout = time.Millisecond // Default interval for reading buffer. ) type Retry struct { @@ -45,10 +45,10 @@ func NewConn(remoteAddress string, localAddress ...string) (*Conn, error) { // NewConnByNetConn creates a UDP connection object with given *net.UDPConn object. func NewConnByNetConn(udp *net.UDPConn) *Conn { return &Conn{ - UDPConn: udp, - recvDeadline: time.Time{}, - sendDeadline: time.Time{}, - recvBufferWait: gRECV_ALL_WAIT_TIMEOUT, + UDPConn: udp, + receiveDeadline: time.Time{}, + sendDeadline: time.Time{}, + receiveBufferWait: receiveAllWaitTimeout, } } @@ -72,7 +72,7 @@ func (c *Conn) Send(data []byte, retry ...Retry) (err error) { if len(retry) > 0 { retry[0].Count-- if retry[0].Interval == 0 { - retry[0].Interval = gDEFAULT_RETRY_INTERVAL + retry[0].Interval = defaultRetryInterval } time.Sleep(retry[0].Interval) } @@ -97,7 +97,7 @@ func (c *Conn) Recv(buffer int, retry ...Retry) ([]byte, error) { if buffer > 0 { data = make([]byte, buffer) } else { - data = make([]byte, gDEFAULT_READ_BUFFER_SIZE) + data = make([]byte, defaultReadBufferSize) } for { size, remoteAddr, err = c.ReadFromUDP(data) @@ -116,7 +116,7 @@ func (c *Conn) Recv(buffer int, retry ...Retry) ([]byte, error) { } retry[0].Count-- if retry[0].Interval == 0 { - retry[0].Interval = gDEFAULT_RETRY_INTERVAL + retry[0].Interval = defaultRetryInterval } time.Sleep(retry[0].Interval) continue @@ -169,7 +169,7 @@ func (c *Conn) SendRecvWithTimeout(data []byte, receive int, timeout time.Durati func (c *Conn) SetDeadline(t time.Time) error { err := c.UDPConn.SetDeadline(t) if err == nil { - c.recvDeadline = t + c.receiveDeadline = t c.sendDeadline = t } return err @@ -178,7 +178,7 @@ func (c *Conn) SetDeadline(t time.Time) error { func (c *Conn) SetRecvDeadline(t time.Time) error { err := c.SetReadDeadline(t) if err == nil { - c.recvDeadline = t + c.receiveDeadline = t } return err } @@ -194,7 +194,7 @@ func (c *Conn) SetSendDeadline(t time.Time) error { // SetRecvBufferWait sets the buffer waiting timeout when reading all data from connection. // The waiting duration cannot be too long which might delay receiving data from remote address. func (c *Conn) SetRecvBufferWait(d time.Duration) { - c.recvBufferWait = d + c.receiveBufferWait = d } // RemoteAddr returns the remote address of current UDP connection. diff --git a/net/gudp/gudp_server.go b/net/gudp/gudp_server.go index 89f4439c2..64d5180c1 100644 --- a/net/gudp/gudp_server.go +++ b/net/gudp/gudp_server.go @@ -16,7 +16,7 @@ import ( ) const ( - gDEFAULT_SERVER = "default" + defaultServer = "default" ) // Server is the UDP server. @@ -33,7 +33,7 @@ var ( // GetServer creates and returns a UDP server instance with given name. func GetServer(name ...interface{}) *Server { - serverName := gDEFAULT_SERVER + serverName := defaultServer if len(name) > 0 && name[0] != "" { serverName = gconv.String(name[0]) } diff --git a/os/glog/glog_chaining.go b/os/glog/glog_chaining.go index db25843ef..900e3bbea 100644 --- a/os/glog/glog_chaining.go +++ b/os/glog/glog_chaining.go @@ -11,7 +11,7 @@ import ( "io" ) -// Expose returns the default logger of glog. +// Expose returns the default logger of package glog. func Expose() *Logger { return logger } From f1857df5e2fb1bb2e99ea2e628c902df26c0d961 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sun, 8 Aug 2021 13:56:26 +0800 Subject: [PATCH 434/492] comment update for package glog --- os/gcron/gcron_entry.go | 34 +++++++++++++++++++++------------ os/gcron/gcron_schedule.go | 4 ++-- os/glog/glog_api.go | 8 ++++---- os/glog/glog_chaining.go | 12 ++++++------ os/glog/glog_config.go | 14 +++++++------- os/glog/glog_instance.go | 2 +- os/glog/glog_logger.go | 12 ++++++------ os/glog/glog_logger_api.go | 10 +++++----- os/glog/glog_logger_chaining.go | 20 +++++++++---------- os/glog/glog_logger_config.go | 10 +++++----- 10 files changed, 68 insertions(+), 58 deletions(-) diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index 68fd6d14c..0c4952faf 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -109,8 +109,10 @@ func (entry *Entry) Close() { // gcron.Entry relies on gtimer to implement a scheduled task check for gcron.Entry per second. func (entry *Entry) check() { if entry.schedule.meet(time.Now()) { - path := entry.cron.GetLogPath() - level := entry.cron.GetLogLevel() + var ( + path = entry.cron.GetLogPath() + level = entry.cron.GetLogLevel() + ) switch entry.cron.status.Val() { case StatusStopped: return @@ -122,6 +124,23 @@ func (entry *Entry) check() { case StatusReady: fallthrough case StatusRunning: + defer func() { + if err := recover(); err != nil { + glog.Path(path).Level(level).Errorf( + "[gcron] %s(%s) %s end with error: %+v", + entry.Name, entry.schedule.pattern, entry.jobName, err, + ) + } else { + glog.Path(path).Level(level).Debugf( + "[gcron] %s(%s) %s end", + entry.Name, entry.schedule.pattern, entry.jobName, + ) + } + if entry.entry.Status() == StatusClosed { + entry.Close() + } + }() + // Running times check. times := entry.times.Add(-1) if times <= 0 { @@ -133,16 +152,7 @@ func (entry *Entry) check() { entry.times.Set(defaultTimes) } glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s start", entry.Name, entry.schedule.pattern, entry.jobName) - defer func() { - if err := recover(); err != nil { - glog.Path(path).Level(level).Errorf("[gcron] %s(%s) %s end with error: %v", entry.Name, entry.schedule.pattern, entry.jobName, err) - } else { - glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s end", entry.Name, entry.schedule.pattern, entry.jobName) - } - if entry.entry.Status() == StatusClosed { - entry.Close() - } - }() + entry.Job() } diff --git a/os/gcron/gcron_schedule.go b/os/gcron/gcron_schedule.go index bd50c4c1e..146c78547 100644 --- a/os/gcron/gcron_schedule.go +++ b/os/gcron/gcron_schedule.go @@ -31,7 +31,7 @@ type cronSchedule struct { const ( // regular expression for cron pattern, which contains 6 parts of time units. - gREGEX_FOR_CRON = `^([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,A-Za-z]+)\s+([\-/\d\*\?,A-Za-z]+)$` + regexForCron = `^([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,A-Za-z]+)\s+([\-/\d\*\?,A-Za-z]+)$` ) var ( @@ -95,7 +95,7 @@ func newSchedule(pattern string) (*cronSchedule, error) { } // Handle the common cron pattern, like: // 0 0 0 1 1 2 - if match, _ := gregex.MatchString(gREGEX_FOR_CRON, pattern); len(match) == 7 { + if match, _ := gregex.MatchString(regexForCron, pattern); len(match) == 7 { schedule := &cronSchedule{ create: time.Now().Unix(), every: 0, diff --git a/os/glog/glog_api.go b/os/glog/glog_api.go index 243a7b993..4a26d84b7 100644 --- a/os/glog/glog_api.go +++ b/os/glog/glog_api.go @@ -6,14 +6,14 @@ package glog -// Print prints <v> with newline using fmt.Sprintln. -// The parameter <v> can be multiple variables. +// Print prints `v` with newline using fmt.Sprintln. +// The parameter `v` can be multiple variables. func Print(v ...interface{}) { logger.Print(v...) } -// Printf prints <v> with format <format> using fmt.Sprintf. -// The parameter <v> can be multiple variables. +// Printf prints `v` with format `format` using fmt.Sprintf. +// The parameter `v` can be multiple variables. func Printf(format string, v ...interface{}) { logger.Printf(format, v...) } diff --git a/os/glog/glog_chaining.go b/os/glog/glog_chaining.go index 900e3bbea..90d93c0af 100644 --- a/os/glog/glog_chaining.go +++ b/os/glog/glog_chaining.go @@ -18,31 +18,31 @@ func Expose() *Logger { // Ctx is a chaining function, // which sets the context for current logging. -// The parameter <keys> specifies the context keys for retrieving values. +// The parameter `keys` specifies the context keys for retrieving values. func Ctx(ctx context.Context, keys ...interface{}) *Logger { return logger.Ctx(ctx, keys...) } // To is a chaining function, -// which redirects current logging content output to the sepecified <writer>. +// which redirects current logging content output to the sepecified `writer`. func To(writer io.Writer) *Logger { return logger.To(writer) } // Path is a chaining function, -// which sets the directory path to <path> for current logging content output. +// which sets the directory path to `path` for current logging content output. func Path(path string) *Logger { return logger.Path(path) } // Cat is a chaining function, -// which sets the category to <category> for current logging content output. +// which sets the category to `category` for current logging content output. func Cat(category string) *Logger { return logger.Cat(category) } // File is a chaining function, -// which sets file name <pattern> for the current logging content output. +// which sets file name `pattern` for the current logging content output. func File(pattern string) *Logger { return logger.File(pattern) } @@ -94,7 +94,7 @@ func Header(enabled ...bool) *Logger { // Line is a chaining function, // which enables/disables printing its caller file along with its line number. -// The parameter <long> specified whether print the long absolute file path, eg: /a/b/c/d.go:23. +// The parameter `long` specified whether print the long absolute file path, eg: /a/b/c/d.go:23. func Line(long ...bool) *Logger { return logger.Line(long...) } diff --git a/os/glog/glog_config.go b/os/glog/glog_config.go index 6e4402dee..e50c3218f 100644 --- a/os/glog/glog_config.go +++ b/os/glog/glog_config.go @@ -31,8 +31,8 @@ func GetPath() string { return logger.GetPath() } -// SetFile sets the file name <pattern> for file logging. -// Datetime pattern can be used in <pattern>, eg: access-{Ymd}.log. +// SetFile sets the file name `pattern` for file logging. +// Datetime pattern can be used in `pattern`, eg: access-{Ymd}.log. // The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log func SetFile(pattern string) { logger.SetFile(pattern) @@ -48,9 +48,9 @@ func GetLevel() int { return logger.GetLevel() } -// SetWriter sets the customized logging <writer> for logging. -// The <writer> object should implements the io.Writer interface. -// Developer can use customized logging <writer> to redirect logging output to another service, +// SetWriter sets the customized logging `writer` for logging. +// The `writer` object should implements the io.Writer interface. +// Developer can use customized logging `writer` to redirect logging output to another service, // eg: kafka, mysql, mongodb, etc. func SetWriter(writer io.Writer) { logger.SetWriter(writer) @@ -113,13 +113,13 @@ func GetCtxKeys() []interface{} { } // PrintStack prints the caller stack, -// the optional parameter <skip> specify the skipped stack offset from the end point. +// the optional parameter `skip` specify the skipped stack offset from the end point. func PrintStack(skip ...int) { logger.PrintStack(skip...) } // GetStack returns the caller stack content, -// the optional parameter <skip> specify the skipped stack offset from the end point. +// the optional parameter `skip` specify the skipped stack offset from the end point. func GetStack(skip ...int) string { return logger.GetStack(skip...) } diff --git a/os/glog/glog_instance.go b/os/glog/glog_instance.go index 42c741a03..49ca83264 100644 --- a/os/glog/glog_instance.go +++ b/os/glog/glog_instance.go @@ -19,7 +19,7 @@ var ( ) // Instance returns an instance of Logger with default settings. -// The parameter <name> is the name for the instance. +// The parameter `name` is the name for the instance. func Instance(name ...string) *Logger { key := DefaultName if len(name) > 0 && name[0] != "" { diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 3a87e9aab..fea141fd4 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -93,7 +93,7 @@ func (l *Logger) getFilePath(now time.Time) string { return file } -// print prints <s> to defined writer, logging file or passed <std>. +// print prints `s` to defined writer, logging file or passed `std`. func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { // Lazy initialize for rotation feature. // It uses atomic reading operation to enhance the performance checking. @@ -309,12 +309,12 @@ func (l *Logger) getCtx() context.Context { return context.TODO() } -// printStd prints content <s> without stack. +// printStd prints content `s` without stack. func (l *Logger) printStd(level int, value ...interface{}) { l.print(l.getCtx(), level, value...) } -// printStd prints content <s> with stack check. +// printStd prints content `s` with stack check. func (l *Logger) printErr(level int, value ...interface{}) { if l.config.StStatus == 1 { if s := l.GetStack(); s != "" { @@ -325,13 +325,13 @@ func (l *Logger) printErr(level int, value ...interface{}) { l.print(l.getCtx(), level, value...) } -// format formats <values> using fmt.Sprintf. +// format formats `values` using fmt.Sprintf. func (l *Logger) format(format string, value ...interface{}) string { return fmt.Sprintf(format, value...) } // PrintStack prints the caller stack, -// the optional parameter <skip> specify the skipped stack offset from the end point. +// the optional parameter `skip` specify the skipped stack offset from the end point. func (l *Logger) PrintStack(skip ...int) { if s := l.GetStack(skip...); s != "" { l.Println("Stack:\n" + s) @@ -341,7 +341,7 @@ func (l *Logger) PrintStack(skip ...int) { } // GetStack returns the caller stack content, -// the optional parameter <skip> specify the skipped stack offset from the end point. +// the optional parameter `skip` specify the skipped stack offset from the end point. func (l *Logger) GetStack(skip ...int) string { stackSkip := l.config.StSkip if len(skip) > 0 { diff --git a/os/glog/glog_logger_api.go b/os/glog/glog_logger_api.go index 321da0752..8b211bc1c 100644 --- a/os/glog/glog_logger_api.go +++ b/os/glog/glog_logger_api.go @@ -11,14 +11,14 @@ import ( "os" ) -// Print prints <v> with newline using fmt.Sprintln. -// The parameter <v> can be multiple variables. +// Print prints `v` with newline using fmt.Sprintln. +// The parameter `v` can be multiple variables. func (l *Logger) Print(v ...interface{}) { l.printStd(LEVEL_NONE, v...) } -// Printf prints <v> with format <format> using fmt.Sprintf. -// The parameter <v> can be multiple variables. +// Printf prints `v` with format `format` using fmt.Sprintf. +// The parameter `v` can be multiple variables. func (l *Logger) Printf(format string, v ...interface{}) { l.printStd(LEVEL_NONE, l.format(format, v...)) } @@ -145,7 +145,7 @@ func (l *Logger) Criticalf(format string, v ...interface{}) { } } -// checkLevel checks whether the given <level> could be output. +// checkLevel checks whether the given `level` could be output. func (l *Logger) checkLevel(level int) bool { return l.config.Level&level > 0 } diff --git a/os/glog/glog_logger_chaining.go b/os/glog/glog_logger_chaining.go index c0213b982..89351df3f 100644 --- a/os/glog/glog_logger_chaining.go +++ b/os/glog/glog_logger_chaining.go @@ -34,7 +34,7 @@ func (l *Logger) Ctx(ctx context.Context, keys ...interface{}) *Logger { } // To is a chaining function, -// which redirects current logging content output to the specified <writer>. +// which redirects current logging content output to the specified `writer`. func (l *Logger) To(writer io.Writer) *Logger { logger := (*Logger)(nil) if l.parent == nil { @@ -47,9 +47,9 @@ func (l *Logger) To(writer io.Writer) *Logger { } // Path is a chaining function, -// which sets the directory path to <path> for current logging content output. +// which sets the directory path to `path` for current logging content output. // -// Note that the parameter <path> is a directory path, not a file path. +// Note that the parameter `path` is a directory path, not a file path. func (l *Logger) Path(path string) *Logger { logger := (*Logger)(nil) if l.parent == nil { @@ -67,8 +67,8 @@ func (l *Logger) Path(path string) *Logger { } // Cat is a chaining function, -// which sets the category to <category> for current logging content output. -// Param <category> can be hierarchical, eg: module/user. +// which sets the category to `category` for current logging content output. +// Param `category` can be hierarchical, eg: module/user. func (l *Logger) Cat(category string) *Logger { logger := (*Logger)(nil) if l.parent == nil { @@ -86,7 +86,7 @@ func (l *Logger) Cat(category string) *Logger { } // File is a chaining function, -// which sets file name <pattern> for the current logging content output. +// which sets file name `pattern` for the current logging content output. func (l *Logger) File(file string) *Logger { logger := (*Logger)(nil) if l.parent == nil { @@ -181,7 +181,7 @@ func (l *Logger) Stdout(enabled ...bool) *Logger { } else { logger = l } - // stdout printing is enabled if <enabled> is not passed. + // stdout printing is enabled if `enabled` is not passed. if len(enabled) > 0 && !enabled[0] { logger.config.StdoutPrint = false } else { @@ -200,7 +200,7 @@ func (l *Logger) Header(enabled ...bool) *Logger { } else { logger = l } - // header is enabled if <enabled> is not passed. + // header is enabled if `enabled` is not passed. if len(enabled) > 0 && !enabled[0] { logger.SetHeaderPrint(false) } else { @@ -211,7 +211,7 @@ func (l *Logger) Header(enabled ...bool) *Logger { // Line is a chaining function, // which enables/disables printing its caller file path along with its line number. -// The parameter <long> specified whether print the long absolute file path, eg: /a/b/c/d.go:23, +// The parameter `long` specified whether print the long absolute file path, eg: /a/b/c/d.go:23, // or else short one: d.go:23. func (l *Logger) Line(long ...bool) *Logger { logger := (*Logger)(nil) @@ -237,7 +237,7 @@ func (l *Logger) Async(enabled ...bool) *Logger { } else { logger = l } - // async feature is enabled if <enabled> is not passed. + // async feature is enabled if `enabled` is not passed. if len(enabled) > 0 && !enabled[0] { logger.SetAsync(false) } else { diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 6cc95ef05..33ed77043 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -188,9 +188,9 @@ func (l *Logger) GetCtxKeys() []interface{} { return l.config.CtxKeys } -// SetWriter sets the customized logging <writer> for logging. -// The <writer> object should implements the io.Writer interface. -// Developer can use customized logging <writer> to redirect logging output to another service, +// SetWriter sets the customized logging `writer` for logging. +// The `writer` object should implements the io.Writer interface. +// Developer can use customized logging `writer` to redirect logging output to another service, // eg: kafka, mysql, mongodb, etc. func (l *Logger) SetWriter(writer io.Writer) { l.config.Writer = writer @@ -222,8 +222,8 @@ func (l *Logger) GetPath() string { return l.config.Path } -// SetFile sets the file name <pattern> for file logging. -// Datetime pattern can be used in <pattern>, eg: access-{Ymd}.log. +// SetFile sets the file name `pattern` for file logging. +// Datetime pattern can be used in `pattern`, eg: access-{Ymd}.log. // The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log func (l *Logger) SetFile(pattern string) { l.config.File = pattern From 0d2ca48d16c5356897107d7451b16599992acf98 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Sun, 8 Aug 2021 14:05:27 +0800 Subject: [PATCH 435/492] update comment and standardize const naming for package gproc --- os/gproc/gproc_comm.go | 6 +++--- os/gproc/gproc_comm_receive.go | 6 +++--- os/gproc/gproc_comm_send.go | 2 +- os/gproc/gproc_process.go | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/os/gproc/gproc_comm.go b/os/gproc/gproc_comm.go index f9014c2a2..9d2bf6aa5 100644 --- a/os/gproc/gproc_comm.go +++ b/os/gproc/gproc_comm.go @@ -31,9 +31,9 @@ type MsgResponse struct { } const ( - gPROC_COMM_DEFAULT_GRUOP_NAME = "" // Default group name. - gPROC_DEFAULT_TCP_PORT = 10000 // Starting port number for receiver listening. - gPROC_MSG_QUEUE_MAX_LENGTH = 10000 // Max size for each message queue of the group. + defaultGroupNameFoProcComm = "" // Default group name. + defaultTcpPortForProcComm = 10000 // Starting port number for receiver listening. + maxLengthForProcMsgQueue = 10000 // Max size for each message queue of the group. ) var ( diff --git a/os/gproc/gproc_comm_receive.go b/os/gproc/gproc_comm_receive.go index 18a815553..df4f52412 100644 --- a/os/gproc/gproc_comm_receive.go +++ b/os/gproc/gproc_comm_receive.go @@ -35,10 +35,10 @@ func Receive(group ...string) *MsgRequest { if len(group) > 0 { groupName = group[0] } else { - groupName = gPROC_COMM_DEFAULT_GRUOP_NAME + groupName = defaultGroupNameFoProcComm } queue := commReceiveQueues.GetOrSetFuncLock(groupName, func() interface{} { - return gqueue.New(gPROC_MSG_QUEUE_MAX_LENGTH) + return gqueue.New(maxLengthForProcMsgQueue) }).(*gqueue.Queue) // Blocking receiving. @@ -52,7 +52,7 @@ func Receive(group ...string) *MsgRequest { func receiveTcpListening() { var listen *net.TCPListener // Scan the available port for listening. - for i := gPROC_DEFAULT_TCP_PORT; ; i++ { + for i := defaultTcpPortForProcComm; ; i++ { addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", i)) if err != nil { continue diff --git a/os/gproc/gproc_comm_send.go b/os/gproc/gproc_comm_send.go index 762042162..5a06cef62 100644 --- a/os/gproc/gproc_comm_send.go +++ b/os/gproc/gproc_comm_send.go @@ -18,7 +18,7 @@ func Send(pid int, data []byte, group ...string) error { msg := MsgRequest{ SendPid: Pid(), RecvPid: pid, - Group: gPROC_COMM_DEFAULT_GRUOP_NAME, + Group: defaultGroupNameFoProcComm, Data: data, } if len(group) > 0 { diff --git a/os/gproc/gproc_process.go b/os/gproc/gproc_process.go index 870f89cca..625407364 100644 --- a/os/gproc/gproc_process.go +++ b/os/gproc/gproc_process.go @@ -88,7 +88,7 @@ func (p *Process) Run() error { } } -// PID +// Pid retrieves and returns the PID for the process. func (p *Process) Pid() int { if p.Process != nil { return p.Process.Pid @@ -96,7 +96,7 @@ func (p *Process) Pid() int { return 0 } -// Send send custom data to the process. +// Send sends custom data to the process. func (p *Process) Send(data []byte) error { if p.Process != nil { return Send(p.Process.Pid, data) From 9ee76ecc93dfba4650b6b9b5d5b9d1ade8091518 Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Sun, 8 Aug 2021 16:23:25 +0800 Subject: [PATCH 436/492] fix print log on windows --- os/glog/glog_logger.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 3a87e9aab..7079a75e5 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -9,6 +9,7 @@ package glog import ( "context" "fmt" + "github.com/fatih/color" "github.com/gogf/gf/container/gtype" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfpool" @@ -250,7 +251,7 @@ func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { // printToStdout outputs logging content to stdout. func (l *Logger) printToStdout(ctx context.Context, input *HandlerInput) { if l.config.StdoutPrint { - if _, err := os.Stdout.Write(input.getBuffer(true).Bytes()); err != nil { + if _, err := fmt.Fprintf(color.Output, input.getBuffer(true).String()) ; err != nil { intlog.Error(ctx, err) } } From 9d4382d12eb8388f1249f4a68ceb05fa1ebb2e65 Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Sun, 8 Aug 2021 16:58:15 +0800 Subject: [PATCH 437/492] fmt --- os/glog/glog_logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 7079a75e5..f369db3f9 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -251,7 +251,7 @@ func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { // printToStdout outputs logging content to stdout. func (l *Logger) printToStdout(ctx context.Context, input *HandlerInput) { if l.config.StdoutPrint { - if _, err := fmt.Fprintf(color.Output, input.getBuffer(true).String()) ; err != nil { + if _, err := fmt.Fprintf(color.Output, input.getBuffer(true).String()); err != nil { intlog.Error(ctx, err) } } From ef50eb6d6b9db5f4065e3fc39026f510ab508477 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 9 Aug 2021 19:16:37 +0800 Subject: [PATCH 438/492] comment update for package ghttp --- net/ghttp/ghttp_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ghttp/ghttp_client.go b/net/ghttp/ghttp_client.go index c88e0ef3d..949754bf6 100644 --- a/net/ghttp/ghttp_client.go +++ b/net/ghttp/ghttp_client.go @@ -17,7 +17,7 @@ type ( ClientHandlerFunc = client.HandlerFunc ) -// New creates and returns a new HTTP client object. +// NewClient creates and returns a new HTTP client object. func NewClient() *Client { return client.New() } From efaf3d591c0fdea9686dc0675449beca40f6b489 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 9 Aug 2021 19:25:23 +0800 Subject: [PATCH 439/492] comment update for package glog --- os/glog/glog_logger.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index f369db3f9..e31ab43da 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -251,6 +251,8 @@ func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { // printToStdout outputs logging content to stdout. func (l *Logger) printToStdout(ctx context.Context, input *HandlerInput) { if l.config.StdoutPrint { + // if _, err := os.Stdout.Write(input.getBuffer(true).Bytes()); err != nil { + // For color in windows. if _, err := fmt.Fprintf(color.Output, input.getBuffer(true).String()); err != nil { intlog.Error(ctx, err) } From fdb6e70322334c94998208ea5d8d9e02470fb997 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 9 Aug 2021 19:29:11 +0800 Subject: [PATCH 440/492] comment update for package glog --- os/glog/glog_logger.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index e31ab43da..b8455dec5 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -251,8 +251,9 @@ func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { // printToStdout outputs logging content to stdout. func (l *Logger) printToStdout(ctx context.Context, input *HandlerInput) { if l.config.StdoutPrint { + // This will lose color in Windows os system. // if _, err := os.Stdout.Write(input.getBuffer(true).Bytes()); err != nil { - // For color in windows. + // This will print color in Windows os system. if _, err := fmt.Fprintf(color.Output, input.getBuffer(true).String()); err != nil { intlog.Error(ctx, err) } From 9bc3b44a61cace5042445bf48bf01c07a2f03b48 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 9 Aug 2021 19:30:16 +0800 Subject: [PATCH 441/492] version updates --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 882ec7b9e..f5adfbbf9 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.16.5" +const VERSION = "v1.17.0" const AUTHORS = "john<john@goframe.org>" From 1c600d5b207b959bcd54a03ef9ed0a8425cf9305 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 10 Aug 2021 15:36:15 +0800 Subject: [PATCH 442/492] revert ORM logger from interface to glog.Logger --- database/gdb/gdb.go | 8 ++++---- database/gdb/gdb_core_config.go | 7 ++++--- database/gdb/gdb_core_logger.go | 27 --------------------------- database/gdb/gdb_z_mysql_ctx_test.go | 4 ++-- frame/gins/gins_database.go | 6 ++---- 5 files changed, 12 insertions(+), 40 deletions(-) delete mode 100644 database/gdb/gdb_core_logger.go diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index e6b88dfd5..030d9a336 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -152,8 +152,8 @@ type DB interface { GetGroup() string // See Core.GetGroup. SetDryRun(enabled bool) // See Core.SetDryRun. GetDryRun() bool // See Core.GetDryRun. - SetLogger(logger Logger) // See Core.SetLogger. - GetLogger() Logger // See Core.GetLogger. + SetLogger(logger *glog.Logger) // See Core.SetLogger. + GetLogger() *glog.Logger // See Core.GetLogger. GetConfig() *ConfigNode // See Core.GetConfig. SetMaxIdleConnCount(n int) // See Core.SetMaxIdleConnCount. SetMaxOpenConnCount(n int) // See Core.SetMaxOpenConnCount. @@ -179,7 +179,7 @@ type Core struct { debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime. cache *gcache.Cache // Cache manager, SQL result cache only. schema *gtype.String // Custom schema for this object. - logger Logger // Logger for logging functionality. + logger *glog.Logger // Logger for logging functionality. config *ConfigNode // Current config node. } @@ -347,7 +347,7 @@ func New(group ...string) (db DB, err error) { debug: gtype.NewBool(), cache: gcache.New(), schema: gtype.NewString(), - logger: LoggerImp{glog.New()}, + logger: glog.New(), config: node, } if v, ok := driverMap[node.Type]; ok { diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index 3f5fb818e..7a8e45443 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -8,6 +8,7 @@ package gdb import ( "fmt" + "github.com/gogf/gf/os/glog" "sync" "time" @@ -133,12 +134,12 @@ func IsConfigured() bool { } // SetLogger sets the logger for orm. -func (c *Core) SetLogger(logger Logger) { +func (c *Core) SetLogger(logger *glog.Logger) { c.logger = logger } -// GetLogger returns the logger of the orm. -func (c *Core) GetLogger() Logger { +// GetLogger returns the (logger) of the orm. +func (c *Core) GetLogger() *glog.Logger { return c.logger } diff --git a/database/gdb/gdb_core_logger.go b/database/gdb/gdb_core_logger.go deleted file mode 100644 index e9b5a3fb8..000000000 --- a/database/gdb/gdb_core_logger.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gdb - -import ( - "context" - "github.com/gogf/gf/os/glog" -) - -// LoggerImp is the default implementation of interface Logger for DB. -type LoggerImp struct { - *glog.Logger -} - -// Error implements function Error for interface Logger. -func (l LoggerImp) Error(ctx context.Context, s string) { - l.Ctx(ctx).Error(s) -} - -// Debug implements function Debug for interface Logger. -func (l LoggerImp) Debug(ctx context.Context, s string) { - l.Ctx(ctx).Debug(s) -} diff --git a/database/gdb/gdb_z_mysql_ctx_test.go b/database/gdb/gdb_z_mysql_ctx_test.go index 9143bab77..0d3cf753a 100644 --- a/database/gdb/gdb_z_mysql_ctx_test.go +++ b/database/gdb/gdb_z_mysql_ctx_test.go @@ -30,7 +30,7 @@ func Test_Ctx(t *testing.T) { } func Test_Ctx_Query(t *testing.T) { - db.GetLogger().(gdb.LoggerImp).SetCtxKeys("SpanId", "TraceId") + db.GetLogger().SetCtxKeys("SpanId", "TraceId") gtest.C(t, func(t *gtest.T) { db.SetDebug(true) defer db.SetDebug(false) @@ -48,7 +48,7 @@ func Test_Ctx_Query(t *testing.T) { func Test_Ctx_Model(t *testing.T) { table := createInitTable() defer dropTable(table) - db.GetLogger().(gdb.LoggerImp).SetCtxKeys("SpanId", "TraceId") + db.GetLogger().SetCtxKeys("SpanId", "TraceId") gtest.C(t, func(t *gtest.T) { db.SetDebug(true) defer db.SetDebug(false) diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index 1bb55d049..dc49a18a6 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -132,10 +132,8 @@ func Database(name ...string) gdb.DB { loggerConfigMap = Config().GetMap(configNodeKey) } if len(loggerConfigMap) > 0 { - if logger, ok := db.GetLogger().(gdb.LoggerImp); ok { - if err := logger.SetConfigWithMap(loggerConfigMap); err != nil { - panic(err) - } + if err := db.GetLogger().SetConfigWithMap(loggerConfigMap); err != nil { + panic(err) } } } From 232751e3db13963baed623dd7caee154866a408d Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 10 Aug 2021 15:46:40 +0800 Subject: [PATCH 443/492] improve context key and caller path&func outputs for logger of package glog --- database/gdb/gdb_core.go | 4 ++-- os/glog/glog_logger_handler.go | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index e003bc1d9..48512fc39 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -658,9 +658,9 @@ func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) { s := fmt.Sprintf("[%3d ms] [%s] %s%s", sql.End-sql.Start, sql.Group, transactionIdStr, sql.Format) if sql.Error != nil { s += "\nError: " + sql.Error.Error() - c.logger.Error(ctx, s) + c.logger.Ctx(ctx).Error(s) } else { - c.logger.Debug(ctx, s) + c.logger.Ctx(ctx).Debug(s) } } diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index e2d990567..5b628ee00 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -61,18 +61,18 @@ func (i *HandlerInput) getBuffer(withColor bool) *bytes.Buffer { i.addStringToBuffer(buffer, i.LevelFormat) } } - if i.CallerFunc != "" { - i.addStringToBuffer(buffer, i.CallerFunc) - } - if i.CallerPath != "" { - i.addStringToBuffer(buffer, i.CallerPath) - } if i.Prefix != "" { i.addStringToBuffer(buffer, i.Prefix) } if i.CtxStr != "" { i.addStringToBuffer(buffer, i.CtxStr) } + if i.CallerFunc != "" { + i.addStringToBuffer(buffer, i.CallerFunc) + } + if i.CallerPath != "" { + i.addStringToBuffer(buffer, i.CallerPath) + } if i.Content != "" { i.addStringToBuffer(buffer, i.Content) } From 00f0a743fc632a090fd25502e212409ce1a9e84c Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 10 Aug 2021 17:20:15 +0800 Subject: [PATCH 444/492] add function TestDataContent for package gdebug --- debug/gdebug/gdebug_caller.go | 6 +++--- debug/gdebug/gdebug_stack.go | 4 ++-- debug/gdebug/gdebug_testdata.go | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/debug/gdebug/gdebug_caller.go b/debug/gdebug/gdebug_caller.go index fa9bb2115..b2e2577f3 100644 --- a/debug/gdebug/gdebug_caller.go +++ b/debug/gdebug/gdebug_caller.go @@ -52,7 +52,7 @@ func Caller(skip ...int) (function string, path string, line int) { // CallerWithFilter returns the function name and the absolute file path along with // its line number of the caller. // -// The parameter <filter> is used to filter the path of the caller. +// The parameter `filter` is used to filter the path of the caller. func CallerWithFilter(filter string, skip ...int) (function string, path string, line int) { var ( number = 0 @@ -166,12 +166,12 @@ func CallerFileLineShort() string { return fmt.Sprintf(`%s:%d`, filepath.Base(path), line) } -// FuncPath returns the complete function path of given <f>. +// FuncPath returns the complete function path of given `f`. func FuncPath(f interface{}) string { return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() } -// FuncName returns the function name of given <f>. +// FuncName returns the function name of given `f`. func FuncName(f interface{}) string { path := FuncPath(f) if path == "" { diff --git a/debug/gdebug/gdebug_stack.go b/debug/gdebug/gdebug_stack.go index a9c0a1e9c..1a2f0c37d 100644 --- a/debug/gdebug/gdebug_stack.go +++ b/debug/gdebug/gdebug_stack.go @@ -28,7 +28,7 @@ func Stack(skip ...int) string { // StackWithFilter returns a formatted stack trace of the goroutine that calls it. // It calls runtime.Stack with a large enough buffer to capture the entire trace. // -// The parameter <filter> is used to filter the path of the caller. +// The parameter `filter` is used to filter the path of the caller. func StackWithFilter(filter string, skip ...int) string { return StackWithFilters([]string{filter}, skip...) } @@ -36,7 +36,7 @@ func StackWithFilter(filter string, skip ...int) string { // StackWithFilters returns a formatted stack trace of the goroutine that calls it. // It calls runtime.Stack with a large enough buffer to capture the entire trace. // -// The parameter <filters> is a slice of strings, which are used to filter the path of the +// The parameter `filters` is a slice of strings, which are used to filter the path of the // caller. // // TODO Improve the performance using debug.Stack. diff --git a/debug/gdebug/gdebug_testdata.go b/debug/gdebug/gdebug_testdata.go index e3d2dadad..c87f24b13 100644 --- a/debug/gdebug/gdebug_testdata.go +++ b/debug/gdebug/gdebug_testdata.go @@ -7,12 +7,13 @@ package gdebug import ( + "io/ioutil" "path/filepath" ) // TestDataPath retrieves and returns the testdata path of current package, // which is used for unit testing cases only. -// The optional parameter <names> specifies the its sub-folders/sub-files, +// The optional parameter `names` specifies the sub-folders/sub-files, // which will be joined with current system separator and returned with the path. func TestDataPath(names ...string) string { path := CallerDirectory() + string(filepath.Separator) + "testdata" @@ -21,3 +22,15 @@ func TestDataPath(names ...string) string { } return path } + +// TestDataContent retrieves and returns the file content for specified testdata path of current package +func TestDataContent(names ...string) string { + path := TestDataPath(names...) + if path != "" { + data, err := ioutil.ReadFile(path) + if err == nil { + return string(data) + } + } + return "" +} From 9c5642f1417da63d1a1d6b21b99ee393dcdeb443 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 10 Aug 2021 19:29:54 +0800 Subject: [PATCH 445/492] fix issue in border value checks for gstr.CompareVersion/CompareVersionGo --- text/gstr/gstr_version.go | 8 ++++---- text/gstr/gstr_z_unit_version_test.go | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/text/gstr/gstr_version.go b/text/gstr/gstr_version.go index eaf12f66b..ac74f6423 100644 --- a/text/gstr/gstr_version.go +++ b/text/gstr/gstr_version.go @@ -24,10 +24,10 @@ import ( // 10.2.0 // etc. func CompareVersion(a, b string) int { - if a[0] == 'v' { + if a != "" && a[0] == 'v' { a = a[1:] } - if b[0] == 'v' { + if b != "" && b[0] == 'v' { b = b[1:] } var ( @@ -71,10 +71,10 @@ func CompareVersion(a, b string) int { // v4.20.0+incompatible // etc. func CompareVersionGo(a, b string) int { - if a[0] == 'v' { + if a != "" && a[0] == 'v' { a = a[1:] } - if b[0] == 'v' { + if b != "" && b[0] == 'v' { b = b[1:] } if Count(a, "-") > 1 { diff --git a/text/gstr/gstr_z_unit_version_test.go b/text/gstr/gstr_z_unit_version_test.go index 948c3e2fe..2d4a81210 100644 --- a/text/gstr/gstr_z_unit_version_test.go +++ b/text/gstr/gstr_z_unit_version_test.go @@ -17,6 +17,9 @@ import ( func Test_CompareVersion(t *testing.T) { gtest.C(t, func(t *gtest.T) { + t.AssertEQ(gstr.CompareVersion("1", ""), 1) + t.AssertEQ(gstr.CompareVersion("", ""), 0) + t.AssertEQ(gstr.CompareVersion("", "v0.1"), -1) t.AssertEQ(gstr.CompareVersion("1", "v0.99"), 1) t.AssertEQ(gstr.CompareVersion("v1.0", "v0.99"), 1) t.AssertEQ(gstr.CompareVersion("v1.0.1", "v1.1.0"), -1) @@ -28,6 +31,9 @@ func Test_CompareVersion(t *testing.T) { func Test_CompareVersionGo(t *testing.T) { gtest.C(t, func(t *gtest.T) { + t.AssertEQ(gstr.CompareVersionGo("1", ""), 1) + t.AssertEQ(gstr.CompareVersionGo("", ""), 0) + t.AssertEQ(gstr.CompareVersionGo("", "v0.1"), -1) t.AssertEQ(gstr.CompareVersionGo("v1.0.1", "v1.1.0"), -1) t.AssertEQ(gstr.CompareVersionGo("1.0.1", "v1.1.0"), -1) t.AssertEQ(gstr.CompareVersionGo("1.0.0", "v0.1.0"), 1) From 91dd9e2bf919e56d0b10e74e72936773dd96624e Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 10 Aug 2021 20:30:32 +0800 Subject: [PATCH 446/492] auto update value of time field if given time value is zero for package gdb --- database/gdb/gdb_func.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index a75b61525..7c7e096da 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -195,8 +195,20 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} { } case reflect.Struct: - switch v.(type) { - case time.Time, *time.Time, gtime.Time, *gtime.Time: + switch r := v.(type) { + // If the time is zero, it then updates it to nil, + // which will insert/update the value to database as "null". + case time.Time: + if r.IsZero() { + data[k] = nil + } + + case gtime.Time: + if r.IsZero() { + data[k] = nil + } + + case *time.Time, *gtime.Time: continue case Counter, *Counter: From 3fc96f2bd0b7f1cbae77b2f992098815a8feca8b Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 11 Aug 2021 13:20:00 +0800 Subject: [PATCH 447/492] add package gctx --- frame/g/g.go | 89 +++++++++++++++++++++--------------------- os/gctx/gctx.go | 15 +++++++ os/glog/glog_logger.go | 11 ++++-- 3 files changed, 68 insertions(+), 47 deletions(-) create mode 100644 os/gctx/gctx.go diff --git a/frame/g/g.go b/frame/g/g.go index 2138cbfcf..c8d4163e7 100644 --- a/frame/g/g.go +++ b/frame/g/g.go @@ -6,57 +6,58 @@ package g -import "github.com/gogf/gf/container/gvar" - -// Var is a universal variable interface, like generics. -type Var = gvar.Var - -// Frequently-used map alias. -type ( - Map = map[string]interface{} - MapAnyAny = map[interface{}]interface{} - MapAnyStr = map[interface{}]string - MapAnyInt = map[interface{}]int - MapStrAny = map[string]interface{} - MapStrStr = map[string]string - MapStrInt = map[string]int - MapIntAny = map[int]interface{} - MapIntStr = map[int]string - MapIntInt = map[int]int - MapAnyBool = map[interface{}]bool - MapStrBool = map[string]bool - MapIntBool = map[int]bool +import ( + "context" + "github.com/gogf/gf/container/gvar" ) -// Frequently-used slice alias. type ( - List = []Map - ListAnyAny = []MapAnyAny - ListAnyStr = []MapAnyStr - ListAnyInt = []MapAnyInt - ListStrAny = []MapStrAny - ListStrStr = []MapStrStr - ListStrInt = []MapStrInt - ListIntAny = []MapIntAny - ListIntStr = []MapIntStr - ListIntInt = []MapIntInt - ListAnyBool = []MapAnyBool - ListStrBool = []MapStrBool - ListIntBool = []MapIntBool + Var = gvar.Var // Var is a universal variable interface, like generics. + Ctx = context.Context // Ctx is alias of frequently-used context.Context. ) -// Frequently-used slice alias. type ( - Slice = []interface{} - SliceAny = []interface{} - SliceStr = []string - SliceInt = []int + Map = map[string]interface{} // Map is alias of frequently-used map type map[string]interface{}. + MapAnyAny = map[interface{}]interface{} // MapAnyAny is alias of frequently-used map type map[interface{}]interface{}. + MapAnyStr = map[interface{}]string // MapAnyStr is alias of frequently-used map type map[interface{}]string. + MapAnyInt = map[interface{}]int // MapAnyInt is alias of frequently-used map type map[interface{}]int. + MapStrAny = map[string]interface{} // MapStrAny is alias of frequently-used map type map[string]interface{}. + MapStrStr = map[string]string // MapStrStr is alias of frequently-used map type map[string]string. + MapStrInt = map[string]int // MapStrInt is alias of frequently-used map type map[string]int. + MapIntAny = map[int]interface{} // MapIntAny is alias of frequently-used map type map[int]interface{}. + MapIntStr = map[int]string // MapIntStr is alias of frequently-used map type map[int]string. + MapIntInt = map[int]int // MapIntInt is alias of frequently-used map type map[int]int. + MapAnyBool = map[interface{}]bool // MapAnyBool is alias of frequently-used map type map[interface{}]bool. + MapStrBool = map[string]bool // MapStrBool is alias of frequently-used map type map[string]bool. + MapIntBool = map[int]bool // MapIntBool is alias of frequently-used map type map[int]bool. ) -// Array is alias of Slice. type ( - Array = []interface{} - ArrayAny = []interface{} - ArrayStr = []string - ArrayInt = []int + List = []Map // List is alias of frequently-used slice type []Map. + ListAnyAny = []MapAnyAny // ListAnyAny is alias of frequently-used slice type []MapAnyAny. + ListAnyStr = []MapAnyStr // ListAnyStr is alias of frequently-used slice type []MapAnyStr. + ListAnyInt = []MapAnyInt // ListAnyInt is alias of frequently-used slice type []MapAnyInt. + ListStrAny = []MapStrAny // ListStrAny is alias of frequently-used slice type []MapStrAny. + ListStrStr = []MapStrStr // ListStrStr is alias of frequently-used slice type []MapStrStr. + ListStrInt = []MapStrInt // ListStrInt is alias of frequently-used slice type []MapStrInt. + ListIntAny = []MapIntAny // ListIntAny is alias of frequently-used slice type []MapIntAny. + ListIntStr = []MapIntStr // ListIntStr is alias of frequently-used slice type []MapIntStr. + ListIntInt = []MapIntInt // ListIntInt is alias of frequently-used slice type []MapIntInt. + ListAnyBool = []MapAnyBool // ListAnyBool is alias of frequently-used slice type []MapAnyBool. + ListStrBool = []MapStrBool // ListStrBool is alias of frequently-used slice type []MapStrBool. + ListIntBool = []MapIntBool // ListIntBool is alias of frequently-used slice type []MapIntBool. +) + +type ( + Slice = []interface{} // Slice is alias of frequently-used slice type []interface{}. + SliceAny = []interface{} // SliceAny is alias of frequently-used slice type []interface{}. + SliceStr = []string // SliceStr is alias of frequently-used slice type []string. + SliceInt = []int // SliceInt is alias of frequently-used slice type []int. +) + +type ( + Array = []interface{} // Array is alias of frequently-used slice type []interface{}. + ArrayAny = []interface{} // ArrayAny is alias of frequently-used slice type []interface{}. + ArrayStr = []string // ArrayStr is alias of frequently-used slice type []string. + ArrayInt = []int // ArrayInt is alias of frequently-used slice type []int. ) diff --git a/os/gctx/gctx.go b/os/gctx/gctx.go new file mode 100644 index 000000000..e2fffb48a --- /dev/null +++ b/os/gctx/gctx.go @@ -0,0 +1,15 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package gctx wraps context.Context and provides extra context features. +package gctx + +import "context" + +type ( + Ctx = context.Context // Ctx is short name alias for context.Context. + StrKey string // StrKey is a type for warps basic type string as context key. +) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index bb6987471..04da63b88 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -12,6 +12,7 @@ import ( "github.com/fatih/color" "github.com/gogf/gf/container/gtype" "github.com/gogf/gf/internal/intlog" + "github.com/gogf/gf/os/gctx" "github.com/gogf/gf/os/gfpool" "github.com/gogf/gf/os/gmlock" "github.com/gogf/gf/os/gtimer" @@ -175,12 +176,16 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { // Context values. if len(l.config.CtxKeys) > 0 { ctxStr := "" - for _, key := range l.config.CtxKeys { - if v := ctx.Value(key); v != nil { + for _, ctxKey := range l.config.CtxKeys { + var ctxValue interface{} + if ctxValue = ctx.Value(ctxKey); ctxValue == nil { + ctxValue = ctx.Value(gctx.StrKey(gconv.String(ctxKey))) + } + if ctxValue != nil { if ctxStr != "" { ctxStr += ", " } - ctxStr += fmt.Sprintf("%s: %+v", key, v) + ctxStr += fmt.Sprintf("%s: %+v", ctxKey, ctxValue) } } if ctxStr != "" { From 4cd4559784908df3205bd60515ffd4faeaff8551 Mon Sep 17 00:00:00 2001 From: Anson <77931774@qq.com> Date: Thu, 12 Aug 2021 00:21:36 +0800 Subject: [PATCH 448/492] fix tcp demo error content-length --- .example/net/gtcp/gtcp_conn.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.example/net/gtcp/gtcp_conn.go b/.example/net/gtcp/gtcp_conn.go index a312c4ead..4008053ef 100644 --- a/.example/net/gtcp/gtcp_conn.go +++ b/.example/net/gtcp/gtcp_conn.go @@ -6,7 +6,7 @@ import ( "os" "github.com/gogf/gf/net/gtcp" - "github.com/gogf/gf/util/gconv" + "strconv" ) func main() { @@ -16,7 +16,7 @@ func main() { } defer conn.Close() - if err := conn.Send([]byte("GET / HTTP/1.1\n\n")); err != nil { + if err := conn.Send([]byte("GET / HTTP/1.1\r\n\r\n")); err != nil { panic(err) } @@ -30,13 +30,17 @@ func main() { array := bytes.Split(data, []byte(": ")) // 获得页面内容长度 if contentLength == 0 && len(array) == 2 && bytes.EqualFold([]byte("Content-Length"), array[0]) { - contentLength = gconv.Int(array[1]) + // http 以\r\n换行,需要把\r也去掉 + contentLength, err = strconv.Atoi(string(array[1][:len(array[1])-1])) + if err != nil { + fmt.Println(err) + } } header = append(header, data...) header = append(header, '\n') } - // header读取完毕,读取文本内容 - if contentLength > 0 && len(data) == 0 { + // header读取完毕,读取文本内容, 1为\r + if contentLength > 0 && len(data) == 1 { content, _ = conn.Recv(contentLength) break } From 2bd76dfdded37c0c9da71c20211d17503327099c Mon Sep 17 00:00:00 2001 From: ansionfor <77931774@qq.com> Date: Thu, 12 Aug 2021 09:35:57 +0800 Subject: [PATCH 449/492] go fmt code --- .example/net/gtcp/gtcp_conn.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.example/net/gtcp/gtcp_conn.go b/.example/net/gtcp/gtcp_conn.go index 4008053ef..7cd01001e 100644 --- a/.example/net/gtcp/gtcp_conn.go +++ b/.example/net/gtcp/gtcp_conn.go @@ -32,9 +32,9 @@ func main() { if contentLength == 0 && len(array) == 2 && bytes.EqualFold([]byte("Content-Length"), array[0]) { // http 以\r\n换行,需要把\r也去掉 contentLength, err = strconv.Atoi(string(array[1][:len(array[1])-1])) - if err != nil { - fmt.Println(err) - } + if err != nil { + fmt.Println(err) + } } header = append(header, data...) header = append(header, '\n') From 95a44122ddcb83f04059bf2c0fdbfd50634f4034 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 12 Aug 2021 10:47:05 +0800 Subject: [PATCH 450/492] comment update for package glog --- os/glog/glog_logger_handler.go | 42 +++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index 5b628ee00..71056e848 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -12,28 +12,29 @@ import ( "time" ) -type Handler func(ctx context.Context, input *HandlerInput) +// Handler is function handler for custom logging content outputs. +type Handler func(ctx context.Context, in *HandlerInput) type HandlerInput struct { - logger *Logger - index int - Ctx context.Context - Time time.Time - TimeFormat string - Color int - Level int - LevelFormat string - CallerFunc string - CallerPath string - CtxStr string - Prefix string - Content string - IsAsync bool + logger *Logger // Logger. + index int // Middleware handling index for internal usage. + Ctx context.Context // Context. + Time time.Time // Logging time, which is the time that logging triggers. + TimeFormat string // Formatted time string, like "2016-01-09 12:00:00". + Color int // Using color, like COLOR_RED, COLOR_BLUE, etc. + Level int // Using level, like LEVEL_INFO, LEVEL_ERRO, etc. + LevelFormat string // Formatted level string, like "DEBU", "ERRO", etc. + CallerFunc string // The source function name that calls logging. + CallerPath string // The source file path and its line number that calls logging. + CtxStr string // The retrieved context value string from context. + Prefix string // Custom prefix string for logging content. + Content string // Content is the main logging content that passed by you. + IsAsync bool // IsAsync marks it is in asynchronous logging. } // defaultHandler is the default handler for logger. -func defaultHandler(ctx context.Context, input *HandlerInput) { - input.logger.doPrint(ctx, input) +func defaultHandler(ctx context.Context, in *HandlerInput) { + in.logger.doPrint(ctx, in) } func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, strings ...string) { @@ -45,13 +46,16 @@ func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, strings ...string } } +// Buffer creates and returns a buffer that handled by default logging content handler. func (i *HandlerInput) Buffer() *bytes.Buffer { return i.getBuffer(false) } func (i *HandlerInput) getBuffer(withColor bool) *bytes.Buffer { buffer := bytes.NewBuffer(nil) - buffer.WriteString(i.TimeFormat) + if i.TimeFormat != "" { + buffer.WriteString(i.TimeFormat) + } if i.LevelFormat != "" { if withColor { i.addStringToBuffer(buffer, i.logger.getColoredStr( @@ -80,10 +84,12 @@ func (i *HandlerInput) getBuffer(withColor bool) *bytes.Buffer { return buffer } +// String retrieves and returns the logging content handled by default handler. func (i *HandlerInput) String() string { return i.Buffer().String() } +// Next calls the next logging handler in middleware way. func (i *HandlerInput) Next() { if len(i.logger.config.Handlers)-1 > i.index { i.index++ From 91cd4f96f0b9b9ad464b50c2d8de9bdc34b9e07f Mon Sep 17 00:00:00 2001 From: Anson <77931774@qq.com> Date: Thu, 12 Aug 2021 10:50:46 +0800 Subject: [PATCH 451/492] Update gtcp_conn.go --- .example/net/gtcp/gtcp_conn.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.example/net/gtcp/gtcp_conn.go b/.example/net/gtcp/gtcp_conn.go index 7cd01001e..323595751 100644 --- a/.example/net/gtcp/gtcp_conn.go +++ b/.example/net/gtcp/gtcp_conn.go @@ -6,7 +6,7 @@ import ( "os" "github.com/gogf/gf/net/gtcp" - "strconv" + "github.com/gogf/gf/util/gconv" ) func main() { @@ -31,10 +31,7 @@ func main() { // 获得页面内容长度 if contentLength == 0 && len(array) == 2 && bytes.EqualFold([]byte("Content-Length"), array[0]) { // http 以\r\n换行,需要把\r也去掉 - contentLength, err = strconv.Atoi(string(array[1][:len(array[1])-1])) - if err != nil { - fmt.Println(err) - } + contentLength = gconv.Int(string(array[1][:len(array[1])-1])) } header = append(header, data...) header = append(header, '\n') From 7b9888c004267e92965116aa1e69d8619f1eb7cf Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 12 Aug 2021 19:42:44 +0800 Subject: [PATCH 452/492] add FieldCount/FieldSum/FieldMax/FieldMin/FieldAvg functions for gdb.Model --- database/gdb/gdb_core_utility.go | 2 + database/gdb/gdb_model.go | 3 +- database/gdb/gdb_model_condition.go | 11 ++- database/gdb/gdb_model_fields.go | 100 ++++++++++++++++++++++--- database/gdb/gdb_z_mysql_model_test.go | 52 +++++++++++++ net/ghttp/ghttp_request_middleware.go | 8 +- util/gvalid/gvalid.go | 4 +- 7 files changed, 160 insertions(+), 20 deletions(-) diff --git a/database/gdb/gdb_core_utility.go b/database/gdb/gdb_core_utility.go index 87add6748..5b0f1973a 100644 --- a/database/gdb/gdb_core_utility.go +++ b/database/gdb/gdb_core_utility.go @@ -31,6 +31,7 @@ func (c *Core) SlaveLink(schema ...string) (Link, error) { // QuoteWord checks given string `s` a word, if true quotes it with security chars of the database // and returns the quoted string; or else return `s` without any change. +// The meaning of a `word` can be considered as a column name. func (c *Core) QuoteWord(s string) string { charLeft, charRight := c.db.GetChars() return doQuoteWord(s, charLeft, charRight) @@ -38,6 +39,7 @@ func (c *Core) QuoteWord(s string) string { // QuoteString quotes string with quote chars. Strings like: // "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc". +// The meaning of a `string` can be considered as part of a statement string including columns. func (c *Core) QuoteString(s string) string { charLeft, charRight := c.db.GetChars() return doQuoteString(s, charLeft, charRight) diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index a9d1fe2dd..fe761dca1 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -73,6 +73,7 @@ const ( whereHolderOperatorWhere = 1 whereHolderOperatorAnd = 2 whereHolderOperatorOr = 3 + defaultFields = "*" ) // Table is alias of Core.Model. @@ -130,7 +131,7 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { db: c.db, tablesInit: tableStr, tables: tableStr, - fields: "*", + fields: defaultFields, start: -1, offset: -1, filter: true, diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 933c3ede9..5853fe7df 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -237,10 +237,13 @@ func (m *Model) WhereOrNotNull(columns ...string) *Model { } // Group sets the "GROUP BY" statement for the model. -func (m *Model) Group(groupBy string) *Model { - model := m.getModel() - model.groupBy = m.db.GetCore().QuoteString(groupBy) - return model +func (m *Model) Group(groupBy ...string) *Model { + if len(groupBy) > 0 { + model := m.getModel() + model.groupBy = m.db.GetCore().QuoteString(gstr.Join(groupBy, ",")) + return model + } + return m } // And adds "AND" condition to the where statement. diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index 1d3b8a626..a4115039e 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -14,7 +14,7 @@ import ( "github.com/gogf/gf/util/gutil" ) -// Fields sets the operation fields of the model, multiple fields joined using char ','. +// Fields appends `fieldNamesOrMapStruct` to the operation fields of the model, multiple fields joined using char ','. // The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct. func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model { length := len(fieldNamesOrMapStruct) @@ -24,26 +24,32 @@ func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model { switch { // String slice. case length >= 2: - model := m.getModel() - model.fields = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), ",") - return model - // It need type asserting. + return m.appendFieldsByStr(gstr.Join( + m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), + ",", + )) + // It needs type asserting. case length == 1: - model := m.getModel() switch r := fieldNamesOrMapStruct[0].(type) { case string: - model.fields = gstr.Join(m.mappingAndFilterToTableFields([]string{r}, false), ",") + return m.appendFieldsByStr(gstr.Join( + m.mappingAndFilterToTableFields([]string{r}, false), ",", + )) case []string: - model.fields = gstr.Join(m.mappingAndFilterToTableFields(r, true), ",") + return m.appendFieldsByStr(gstr.Join( + m.mappingAndFilterToTableFields(r, true), ",", + )) default: - model.fields = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",") + return m.appendFieldsByStr(gstr.Join( + m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",", + )) } - return model } return m } -// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','. +// FieldsEx appends `fieldNamesOrMapStruct` to the excluded operation fields of the model, +// multiple fields joined using char ','. // Note that this function supports only single table operations. // The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct. func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model { @@ -70,6 +76,78 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model { return m } +// FieldCount formats and appends commonly used field `COUNT(column)` to the select fields of model. +func (m *Model) FieldCount(column string, as ...string) *Model { + asStr := "" + if len(as) > 0 && as[0] != "" { + asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0])) + } + return m.appendFieldsByStr(fmt.Sprintf(`COUNT(%s)%s`, m.db.GetCore().QuoteWord(column), asStr)) +} + +// FieldSum formats and appends commonly used field `SUM(column)` to the select fields of model. +func (m *Model) FieldSum(column string, as ...string) *Model { + asStr := "" + if len(as) > 0 && as[0] != "" { + asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0])) + } + return m.appendFieldsByStr(fmt.Sprintf(`SUM(%s)%s`, m.db.GetCore().QuoteWord(column), asStr)) +} + +// FieldMin formats and appends commonly used field `MIN(column)` to the select fields of model. +func (m *Model) FieldMin(column string, as ...string) *Model { + asStr := "" + if len(as) > 0 && as[0] != "" { + asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0])) + } + return m.appendFieldsByStr(fmt.Sprintf(`MIN(%s)%s`, m.db.GetCore().QuoteWord(column), asStr)) +} + +// FieldMax formats and appends commonly used field `MAX(column)` to the select fields of model. +func (m *Model) FieldMax(column string, as ...string) *Model { + asStr := "" + if len(as) > 0 && as[0] != "" { + asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0])) + } + return m.appendFieldsByStr(fmt.Sprintf(`MAX(%s)%s`, m.db.GetCore().QuoteWord(column), asStr)) +} + +// FieldAvg formats and appends commonly used field `AVG(column)` to the select fields of model. +func (m *Model) FieldAvg(column string, as ...string) *Model { + asStr := "" + if len(as) > 0 && as[0] != "" { + asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0])) + } + return m.appendFieldsByStr(fmt.Sprintf(`AVG(%s)%s`, m.db.GetCore().QuoteWord(column), asStr)) +} + +func (m *Model) appendFieldsByStr(fields string) *Model { + if fields != "" { + model := m.getModel() + if model.fields == defaultFields { + model.fields = "" + } + if model.fields != "" { + model.fields += "," + } + model.fields += fields + return model + } + return m +} + +func (m *Model) appendFieldsExByStr(fieldsEx string) *Model { + if fieldsEx != "" { + model := m.getModel() + if model.fieldsEx != "" { + model.fieldsEx += "," + } + model.fieldsEx += fieldsEx + return model + } + return m +} + // Filter marks filtering the fields which does not exist in the fields of the operated table. // Note that this function supports only single table operations. // Deprecated, filter feature is automatically enabled from GoFrame v1.16.0, it is so no longer used. diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index ea4e32049..806cc80bc 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -3768,3 +3768,55 @@ func Test_Model_Handler(t *testing.T) { t.Assert(all[2]["id"], 4) }) } + +func Test_Model_FieldCount(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(table).Fields("id").FieldCount("id", "total").Group("id").OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(all), TableSize) + t.Assert(all[0]["id"], 1) + t.Assert(all[0]["total"], 1) + }) +} + +func Test_Model_FieldMax(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(table).Fields("id").FieldMax("id", "total").Group("id").OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(all), TableSize) + t.Assert(all[0]["id"], 1) + t.Assert(all[0]["total"], 1) + }) +} + +func Test_Model_FieldMin(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(table).Fields("id").FieldMin("id", "total").Group("id").OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(all), TableSize) + t.Assert(all[0]["id"], 1) + t.Assert(all[0]["total"], 1) + }) +} + +func Test_Model_FieldAvg(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(table).Fields("id").FieldAvg("id", "total").Group("id").OrderAsc("id").All() + t.AssertNil(err) + t.Assert(len(all), TableSize) + t.Assert(all[0]["id"], 1) + t.Assert(all[0]["total"], 1) + }) +} diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index 8554b4f3f..66dc66275 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -153,13 +153,17 @@ func (m *middleware) callHandlerFunc(funcInfo handlerFuncInfo) { switch len(results) { case 1: if !results[0].IsNil() { - m.request.handlerResponse.Error = results[0].Interface().(error) + if err, ok := results[0].Interface().(error); ok { + m.request.handlerResponse.Error = err + } } case 2: m.request.handlerResponse.Object = results[0].Interface() if !results[1].IsNil() { - m.request.handlerResponse.Error = results[1].Interface().(error) + if err, ok := results[1].Interface().(error); ok { + m.request.handlerResponse.Error = err + } } } } diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 24160e741..a035b9337 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -24,9 +24,9 @@ import ( // required-if format: required-if:field,value,... brief: Required unless all given field and its value are equal. // required-unless format: required-unless:field,value,... brief: Required unless all given field and its value are not equal. // required-with format: required-with:field1,field2,... brief: Required if any of given fields are not empty. -// required-with-all format: required-with-all:field1,field2,... brief: Required if all of given fields are not empty. +// required-with-all format: required-with-all:field1,field2,... brief: Required if all given fields are not empty. // required-without format: required-without:field1,field2,... brief: Required if any of given fields are empty. -// required-without-all format: required-without-all:field1,field2,...brief: Required if all of given fields are empty. +// required-without-all format: required-without-all:field1,field2,...brief: Required if all given fields are empty. // bail format: bail brief: Stop validating when this field's validation failed. // date format: date brief: Standard date, like: 2006-01-02, 20060102, 2006.01.02 // date-format format: date-format:format brief: Custom date format. From 13ecbc263e4b852d5f2a82c67a13386d2611690c Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 12 Aug 2021 21:58:06 +0800 Subject: [PATCH 453/492] improve handler feature for package glog --- .example/os/glog/glog_CtxKeys.go | 14 +++ ...og_config1.go => glog_SetConfigWithMap.go} | 2 +- ...{glog_async1.go => glog_async_chaining.go} | 5 +- ...glog_async2.go => glog_async_configure.go} | 7 +- .example/os/glog/glog_category.go | 5 +- .example/os/glog/glog_ctx.go | 14 --- .example/os/glog/glog_debug.go | 6 +- .example/os/glog/glog_file.go | 21 ++-- .example/os/glog/glog_flags.go | 14 +-- .example/os/glog/glog_gerror.go | 6 +- .example/os/glog/glog_json.go | 5 +- .example/os/glog/glog_level.go | 8 +- .example/os/glog/glog_level_prefix.go | 6 +- .example/os/glog/glog_line.go | 6 +- .example/os/glog/glog_line2.go | 6 +- .example/os/glog/glog_path.go | 5 +- .example/os/glog/glog_pool.go | 6 +- .example/os/glog/glog_prefix.go | 9 +- .example/os/glog/glog_stack.go | 10 +- .example/os/glog/glog_stdout.go | 15 +-- .example/os/glog/glog_writer_hook.go | 4 +- .example/os/glog/handler/glog_handler_json.go | 42 ++++++++ .../gmlock/{locker1.go => 1.lock&unlock.go} | 6 +- .../os/gmlock/{locker3.go => 2.trylock.go} | 0 .../{locker4.go => 3.lock_conflicts.go} | 0 .../{test_locker.go => 4.test_deadlock.go} | 13 +-- .example/os/gmlock/locker2.go | 23 ----- .example/os/gmlock/test_mutex.go | 95 ------------------- os/glog/glog_logger.go | 43 +++++---- os/glog/glog_logger_config.go | 4 +- os/glog/glog_logger_handler.go | 91 ++++++++++-------- os/glog/glog_z_unit_handler_test.go | 4 +- 32 files changed, 214 insertions(+), 281 deletions(-) create mode 100644 .example/os/glog/glog_CtxKeys.go rename .example/os/glog/{glog_config1.go => glog_SetConfigWithMap.go} (81%) rename .example/os/glog/{glog_async1.go => glog_async_chaining.go} (60%) rename .example/os/glog/{glog_async2.go => glog_async_configure.go} (54%) delete mode 100644 .example/os/glog/glog_ctx.go create mode 100644 .example/os/glog/handler/glog_handler_json.go rename .example/os/gmlock/{locker1.go => 1.lock&unlock.go} (86%) rename .example/os/gmlock/{locker3.go => 2.trylock.go} (100%) rename .example/os/gmlock/{locker4.go => 3.lock_conflicts.go} (100%) rename .example/os/gmlock/{test_locker.go => 4.test_deadlock.go} (91%) delete mode 100644 .example/os/gmlock/locker2.go delete mode 100644 .example/os/gmlock/test_mutex.go diff --git a/.example/os/glog/glog_CtxKeys.go b/.example/os/glog/glog_CtxKeys.go new file mode 100644 index 000000000..3eb6fcc13 --- /dev/null +++ b/.example/os/glog/glog_CtxKeys.go @@ -0,0 +1,14 @@ +package main + +import ( + "context" + "github.com/gogf/gf/frame/g" +) + +func main() { + g.Log().SetCtxKeys("TraceId", "SpanId", "Test") + ctx := context.WithValue(context.Background(), "TraceId", "1234567890") + ctx = context.WithValue(ctx, "SpanId", "abcdefg") + + g.Log().Ctx(ctx).Print(1, 2, 3) +} diff --git a/.example/os/glog/glog_config1.go b/.example/os/glog/glog_SetConfigWithMap.go similarity index 81% rename from .example/os/glog/glog_config1.go rename to .example/os/glog/glog_SetConfigWithMap.go index fc3c5a3cb..06361b583 100644 --- a/.example/os/glog/glog_config1.go +++ b/.example/os/glog/glog_SetConfigWithMap.go @@ -6,7 +6,7 @@ import ( ) func main() { - err := glog.SetConfigWithMap(g.Map{ + err := g.Log().SetConfigWithMap(g.Map{ "prefix": "[TEST]", }) if err != nil { diff --git a/.example/os/glog/glog_async1.go b/.example/os/glog/glog_async_chaining.go similarity index 60% rename from .example/os/glog/glog_async1.go rename to .example/os/glog/glog_async_chaining.go index 1f17d1c25..a3dda3cd6 100644 --- a/.example/os/glog/glog_async1.go +++ b/.example/os/glog/glog_async_chaining.go @@ -1,14 +1,13 @@ package main import ( + "github.com/gogf/gf/frame/g" "time" - - "github.com/gogf/gf/os/glog" ) func main() { for i := 0; i < 10; i++ { - glog.Async().Print("async log", i) + g.Log().Async().Print("async log", i) } time.Sleep(time.Second) } diff --git a/.example/os/glog/glog_async2.go b/.example/os/glog/glog_async_configure.go similarity index 54% rename from .example/os/glog/glog_async2.go rename to .example/os/glog/glog_async_configure.go index 63d6f4278..e9cc86bd6 100644 --- a/.example/os/glog/glog_async2.go +++ b/.example/os/glog/glog_async_configure.go @@ -1,15 +1,14 @@ package main import ( + "github.com/gogf/gf/frame/g" "time" - - "github.com/gogf/gf/os/glog" ) func main() { - glog.SetAsync(true) + g.Log().SetAsync(true) for i := 0; i < 10; i++ { - glog.Async().Print("async log", i) + g.Log().Print("async log", i) } time.Sleep(time.Second) } diff --git a/.example/os/glog/glog_category.go b/.example/os/glog/glog_category.go index 47c56ccf4..2be6ee80b 100644 --- a/.example/os/glog/glog_category.go +++ b/.example/os/glog/glog_category.go @@ -3,13 +3,12 @@ package main import ( "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/glog" ) func main() { path := "/tmp/glog-cat" - glog.SetPath(path) - glog.Stdout(false).Cat("cat1").Cat("cat2").Println("test") + g.Log().SetPath(path) + g.Log().Stdout(false).Cat("cat1").Cat("cat2").Println("test") list, err := gfile.ScanDir(path, "*", true) g.Dump(err) g.Dump(list) diff --git a/.example/os/glog/glog_ctx.go b/.example/os/glog/glog_ctx.go deleted file mode 100644 index b4ca21ea2..000000000 --- a/.example/os/glog/glog_ctx.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import ( - "context" - "github.com/gogf/gf/os/glog" -) - -func main() { - glog.SetCtxKeys("Trace-Id", "Span-Id", "Test") - ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890") - ctx = context.WithValue(ctx, "Span-Id", "abcdefg") - - glog.Ctx(ctx).Print(1, 2, 3) -} diff --git a/.example/os/glog/glog_debug.go b/.example/os/glog/glog_debug.go index 64ec9c277..579aefe3b 100644 --- a/.example/os/glog/glog_debug.go +++ b/.example/os/glog/glog_debug.go @@ -1,19 +1,19 @@ package main import ( + "github.com/gogf/gf/frame/g" "time" - "github.com/gogf/gf/os/glog" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/os/gtimer" ) func main() { gtimer.SetTimeout(3*time.Second, func() { - glog.SetDebug(false) + g.Log().SetDebug(false) }) for { - glog.Debug(gtime.Datetime()) + g.Log().Debug(gtime.Datetime()) time.Sleep(time.Second) } } diff --git a/.example/os/glog/glog_file.go b/.example/os/glog/glog_file.go index 6a0843dac..9e4f6b858 100644 --- a/.example/os/glog/glog_file.go +++ b/.example/os/glog/glog_file.go @@ -3,24 +3,25 @@ package main import ( "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/glog" ) // 设置日志等级 func main() { - l := glog.New() path := "/tmp/glog" - l.SetPath(path) - l.SetStdoutPrint(false) + g.Log().SetPath(path) + g.Log().SetStdoutPrint(false) + // 使用默认文件名称格式 - l.Println("标准文件名称格式,使用当前时间时期") + g.Log().Println("标准文件名称格式,使用当前时间时期") + // 通过SetFile设置文件名称格式 - l.SetFile("stdout.log") - l.Println("设置日志输出文件名称格式为同一个文件") + g.Log().SetFile("stdout.log") + g.Log().Println("设置日志输出文件名称格式为同一个文件") + // 链式操作设置文件名称格式 - l.File("stderr.log").Println("支持链式操作") - l.File("error-{Ymd}.log").Println("文件名称支持带gtime日期格式") - l.File("access-{Ymd}.log").Println("文件名称支持带gtime日期格式") + g.Log().File("stderr.log").Println("支持链式操作") + g.Log().File("error-{Ymd}.log").Println("文件名称支持带gtime日期格式") + g.Log().File("access-{Ymd}.log").Println("文件名称支持带gtime日期格式") list, err := gfile.ScanDir(path, "*") g.Dump(err) diff --git a/.example/os/glog/glog_flags.go b/.example/os/glog/glog_flags.go index ebb3894c3..f3ed32ce3 100644 --- a/.example/os/glog/glog_flags.go +++ b/.example/os/glog/glog_flags.go @@ -1,15 +1,15 @@ package main import ( + "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/glog" ) func main() { - l := glog.New() - l.SetFlags(glog.F_TIME_TIME | glog.F_FILE_SHORT) - l.Println("time and short line number") - l.SetFlags(glog.F_TIME_MILLI | glog.F_FILE_LONG) - l.Println("time with millisecond and long line number") - l.SetFlags(glog.F_TIME_STD | glog.F_FILE_LONG) - l.Println("standard time format and long line number") + g.Log().SetFlags(glog.F_TIME_TIME | glog.F_FILE_SHORT) + g.Log().Println("time and short line number") + g.Log().SetFlags(glog.F_TIME_MILLI | glog.F_FILE_LONG) + g.Log().Println("time with millisecond and long line number") + g.Log().SetFlags(glog.F_TIME_STD | glog.F_FILE_LONG) + g.Log().Println("standard time format and long line number") } diff --git a/.example/os/glog/glog_gerror.go b/.example/os/glog/glog_gerror.go index cbcf405c0..6fb54f726 100644 --- a/.example/os/glog/glog_gerror.go +++ b/.example/os/glog/glog_gerror.go @@ -2,9 +2,9 @@ package main import ( "errors" + "github.com/gogf/gf/frame/g" "github.com/gogf/gf/errors/gerror" - "github.com/gogf/gf/os/glog" ) func MakeError() error { @@ -18,8 +18,8 @@ func MakeGError() error { func TestGError() { err1 := MakeError() err2 := MakeGError() - glog.Error(err1) - glog.Error(err2) + g.Log().Error(err1) + g.Log().Error(err2) } func main() { diff --git a/.example/os/glog/glog_json.go b/.example/os/glog/glog_json.go index f1e28944e..1075bf056 100644 --- a/.example/os/glog/glog_json.go +++ b/.example/os/glog/glog_json.go @@ -2,15 +2,14 @@ package main import ( "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/glog" ) func main() { - glog.Debug(g.Map{"uid": 100, "name": "john"}) + g.Log().Debug(g.Map{"uid": 100, "name": "john"}) type User struct { Uid int `json:"uid"` Name string `json:"name"` } - glog.Debug(User{100, "john"}) + g.Log().Debug(User{100, "john"}) } diff --git a/.example/os/glog/glog_level.go b/.example/os/glog/glog_level.go index c1e07f736..e8c7b917b 100644 --- a/.example/os/glog/glog_level.go +++ b/.example/os/glog/glog_level.go @@ -1,13 +1,13 @@ package main import ( + "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/glog" ) // 设置日志等级,过滤掉Info日志信息 func main() { - l := glog.New() - l.Info("info1") - l.SetLevel(glog.LEVEL_ALL ^ glog.LEVEL_INFO) - l.Info("info2") + g.Log().Info("info1") + g.Log().SetLevel(glog.LEVEL_ALL ^ glog.LEVEL_INFO) + g.Log().Info("info2") } diff --git a/.example/os/glog/glog_level_prefix.go b/.example/os/glog/glog_level_prefix.go index cd9a7e9f7..014c7c288 100644 --- a/.example/os/glog/glog_level_prefix.go +++ b/.example/os/glog/glog_level_prefix.go @@ -1,11 +1,11 @@ package main import ( + "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/glog" ) func main() { - l := glog.New() - l.SetLevelPrefix(glog.LEVEL_DEBU, "debug") - l.Debug("test") + g.Log().SetLevelPrefix(glog.LEVEL_DEBU, "debug") + g.Log().Debug("test") } diff --git a/.example/os/glog/glog_line.go b/.example/os/glog/glog_line.go index 4ee2ad1a1..5326cf867 100644 --- a/.example/os/glog/glog_line.go +++ b/.example/os/glog/glog_line.go @@ -1,10 +1,10 @@ package main import ( - "github.com/gogf/gf/os/glog" + "github.com/gogf/gf/frame/g" ) func main() { - glog.Line().Debug("this is the short file name with its line number") - glog.Line(true).Debug("lone file name with line number") + g.Log().Line().Debug("this is the short file name with its line number") + g.Log().Line(true).Debug("lone file name with line number") } diff --git a/.example/os/glog/glog_line2.go b/.example/os/glog/glog_line2.go index b694f7b92..9fd8a60fc 100644 --- a/.example/os/glog/glog_line2.go +++ b/.example/os/glog/glog_line2.go @@ -1,12 +1,12 @@ package main import ( - "github.com/gogf/gf/os/glog" + "github.com/gogf/gf/frame/g" ) func PrintLog(content string) { - glog.Skip(0).Line().Println("line number with skip:", content) - glog.Line(true).Println("line number without skip:", content) + g.Log().Skip(0).Line().Println("line number with skip:", content) + g.Log().Line(true).Println("line number without skip:", content) } func main() { diff --git a/.example/os/glog/glog_path.go b/.example/os/glog/glog_path.go index 913a037ab..a7d0e4367 100644 --- a/.example/os/glog/glog_path.go +++ b/.example/os/glog/glog_path.go @@ -3,14 +3,13 @@ package main import ( "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gfile" - "github.com/gogf/gf/os/glog" ) // 设置日志输出路径 func main() { path := "/tmp/glog" - glog.SetPath(path) - glog.Println("日志内容") + g.Log().SetPath(path) + g.Log().Println("日志内容") list, err := gfile.ScanDir(path, "*") g.Dump(err) g.Dump(list) diff --git a/.example/os/glog/glog_pool.go b/.example/os/glog/glog_pool.go index 383b74367..960e85557 100644 --- a/.example/os/glog/glog_pool.go +++ b/.example/os/glog/glog_pool.go @@ -1,18 +1,18 @@ package main import ( + "github.com/gogf/gf/frame/g" "time" - "github.com/gogf/gf/os/glog" "github.com/gogf/gf/os/gtime" ) // 测试删除日志文件是否会重建日志文件 func main() { path := "/Users/john/Temp/test" - glog.SetPath(path) + g.Log().SetPath(path) for { - glog.Println(gtime.Now().String()) + g.Log().Println(gtime.Now().String()) time.Sleep(time.Second) } } diff --git a/.example/os/glog/glog_prefix.go b/.example/os/glog/glog_prefix.go index 502467128..b803b9a4e 100644 --- a/.example/os/glog/glog_prefix.go +++ b/.example/os/glog/glog_prefix.go @@ -1,12 +1,11 @@ package main import ( - "github.com/gogf/gf/os/glog" + "github.com/gogf/gf/frame/g" ) func main() { - l := glog.New() - l.SetPrefix("[API]") - l.Println("hello world") - l.Error("error occurred") + g.Log().SetPrefix("[API]") + g.Log().Println("hello world") + g.Log().Error("error occurred") } diff --git a/.example/os/glog/glog_stack.go b/.example/os/glog/glog_stack.go index 1232c2bec..1dced6e69 100644 --- a/.example/os/glog/glog_stack.go +++ b/.example/os/glog/glog_stack.go @@ -2,15 +2,11 @@ package main import ( "fmt" - - "github.com/gogf/gf/os/glog" + "github.com/gogf/gf/frame/g" ) func main() { + g.Log().PrintStack() - glog.PrintStack() - glog.New().PrintStack() - - fmt.Println(glog.GetStack()) - fmt.Println(glog.New().GetStack()) + fmt.Println(g.Log().GetStack()) } diff --git a/.example/os/glog/glog_stdout.go b/.example/os/glog/glog_stdout.go index 4d2200028..c4ae4d6ba 100644 --- a/.example/os/glog/glog_stdout.go +++ b/.example/os/glog/glog_stdout.go @@ -1,22 +1,23 @@ package main import ( + "github.com/gogf/gf/frame/g" "sync" - - "github.com/gogf/gf/os/glog" ) func main() { - wg := sync.WaitGroup{} - c := make(chan struct{}) + var ( + wg = sync.WaitGroup{} + ch = make(chan struct{}) + ) wg.Add(3000) for i := 0; i < 3000; i++ { go func() { - <-c - glog.Println("abcdefghijklmnopqrstuvwxyz1234567890") + <-ch + g.Log().Println("abcdefghijklmnopqrstuvwxyz1234567890") wg.Done() }() } - close(c) + close(ch) wg.Wait() } diff --git a/.example/os/glog/glog_writer_hook.go b/.example/os/glog/glog_writer_hook.go index 67e8863ca..365ec28f0 100644 --- a/.example/os/glog/glog_writer_hook.go +++ b/.example/os/glog/glog_writer_hook.go @@ -2,8 +2,8 @@ package main import ( "fmt" + "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/net/ghttp" "github.com/gogf/gf/os/glog" "github.com/gogf/gf/text/gregex" ) @@ -16,7 +16,7 @@ func (w *MyWriter) Write(p []byte) (n int, err error) { s := string(p) if gregex.IsMatchString(`\[(PANI|FATA)\]`, s) { fmt.Println("SERIOUS ISSUE OCCURRED!! I'd better tell monitor in first time!") - ghttp.PostContent("http://monitor.mydomain.com", s) + g.Client().PostContent("http://monitor.mydomain.com", s) } return w.logger.Write(p) } diff --git a/.example/os/glog/handler/glog_handler_json.go b/.example/os/glog/handler/glog_handler_json.go new file mode 100644 index 000000000..ee4bb06e6 --- /dev/null +++ b/.example/os/glog/handler/glog_handler_json.go @@ -0,0 +1,42 @@ +package main + +import ( + "context" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/internal/json" + "github.com/gogf/gf/os/glog" + "github.com/gogf/gf/text/gstr" + "os" +) + +// JsonOutputsForLogger is for JSON marshaling in sequence. +type JsonOutputsForLogger struct { + Time string `json:"time"` + Level string `json:"level"` + Content string `json:"content"` +} + +// LoggingJsonHandler is a example handler for logging JSON format content. +var LoggingJsonHandler glog.Handler = func(ctx context.Context, in *glog.HandlerInput) { + jsonForLogger := JsonOutputsForLogger{ + Time: in.TimeFormat, + Level: in.LevelFormat, + Content: gstr.Trim(in.String()), + } + jsonBytes, err := json.Marshal(jsonForLogger) + if err != nil { + _, _ = os.Stderr.WriteString(err.Error()) + return + } + in.Buffer.Write(jsonBytes) + in.Buffer.WriteString("\n") + in.Next() +} + +func main() { + g.Log().SetHandlers(LoggingJsonHandler) + + g.Log().Debug("Debugging...") + g.Log().Warning("It is warning info") + g.Log().Error("Error occurs, please have a check") +} diff --git a/.example/os/gmlock/locker1.go b/.example/os/gmlock/1.lock&unlock.go similarity index 86% rename from .example/os/gmlock/locker1.go rename to .example/os/gmlock/1.lock&unlock.go index 946ae72b4..71a4d3534 100644 --- a/.example/os/gmlock/locker1.go +++ b/.example/os/gmlock/1.lock&unlock.go @@ -10,8 +10,10 @@ import ( // 内存锁基本使用 func main() { - key := "lock" - wg := sync.WaitGroup{} + var ( + key = "lock" + wg = sync.WaitGroup{} + ) for i := 0; i < 10; i++ { wg.Add(1) go func(i int) { diff --git a/.example/os/gmlock/locker3.go b/.example/os/gmlock/2.trylock.go similarity index 100% rename from .example/os/gmlock/locker3.go rename to .example/os/gmlock/2.trylock.go diff --git a/.example/os/gmlock/locker4.go b/.example/os/gmlock/3.lock_conflicts.go similarity index 100% rename from .example/os/gmlock/locker4.go rename to .example/os/gmlock/3.lock_conflicts.go diff --git a/.example/os/gmlock/test_locker.go b/.example/os/gmlock/4.test_deadlock.go similarity index 91% rename from .example/os/gmlock/test_locker.go rename to .example/os/gmlock/4.test_deadlock.go index 3600e36b6..b67cf7ddd 100644 --- a/.example/os/gmlock/test_locker.go +++ b/.example/os/gmlock/4.test_deadlock.go @@ -11,12 +11,13 @@ import ( // 测试Locker是否会产生死锁 func main() { - l := gmlock.New() - wg := sync.WaitGroup{} - key := "test" - event := make(chan int) - number := 100000 - + var ( + l = gmlock.New() + wg = sync.WaitGroup{} + key = "test" + event = make(chan int) + number = 100000 + ) for i := 0; i < number; i++ { wg.Add(1) go func() { diff --git a/.example/os/gmlock/locker2.go b/.example/os/gmlock/locker2.go deleted file mode 100644 index f340a25c9..000000000 --- a/.example/os/gmlock/locker2.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "sync" - - "github.com/gogf/gf/os/glog" - "github.com/gogf/gf/os/gmlock" -) - -// 内存锁 - 给定过期时间 -func main() { - key := "lock" - wg := sync.WaitGroup{} - for i := 0; i < 10; i++ { - wg.Add(1) - go func(i int) { - gmlock.Lock(key, 1000) - glog.Println(i) - wg.Done() - }(i) - } - wg.Wait() -} diff --git a/.example/os/gmlock/test_mutex.go b/.example/os/gmlock/test_mutex.go deleted file mode 100644 index c03874e70..000000000 --- a/.example/os/gmlock/test_mutex.go +++ /dev/null @@ -1,95 +0,0 @@ -package main - -import ( - "fmt" - "math/rand" - "sync" - "time" - - "github.com/gogf/gf/os/gmlock" -) - -// 测试是否会产生死锁 -func main() { - mu := gmlock.NewMutex() - wg := sync.WaitGroup{} - event := make(chan int) - number := 100000 - - for i := 0; i < number; i++ { - wg.Add(1) - go func() { - <-event - mu.Lock() - //fmt.Println("get lock") - mu.Unlock() - wg.Done() - }() - } - - for i := 0; i < number; i++ { - wg.Add(1) - go func() { - <-event - mu.RLock() - //fmt.Println("get rlock") - mu.RUnlock() - wg.Done() - }() - } - - for i := 0; i < number; i++ { - wg.Add(1) - go func() { - <-event - if mu.TryLock() { - //fmt.Println("get lock") - mu.Unlock() - } - wg.Done() - }() - } - - for i := 0; i < number; i++ { - wg.Add(1) - go func() { - <-event - if mu.TryRLock() { - //fmt.Println("get rlock") - mu.RUnlock() - } - wg.Done() - }() - } - - for i := 0; i < number; i++ { - wg.Add(1) - go func() { - <-event - if mu.TryLock() { - // 模拟业务逻辑的随机处理间隔 - time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) - mu.Unlock() - } - wg.Done() - }() - } - - for i := 0; i < number; i++ { - wg.Add(1) - go func() { - <-event - if mu.TryRLock() { - // 模拟业务逻辑的随机处理间隔 - time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) - mu.RUnlock() - } - wg.Done() - }() - } - // 使用chan作为事件发送测试指令,让所有的goroutine同时执行 - close(event) - wg.Wait() - - fmt.Println("done!") -} diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 04da63b88..8c8dd3716 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -7,6 +7,7 @@ package glog import ( + "bytes" "context" "fmt" "github.com/fatih/color" @@ -77,11 +78,11 @@ func NewWithWriter(writer io.Writer) *Logger { // Clone returns a new logger, which is the clone the current logger. // It's commonly used for chaining operations. func (l *Logger) Clone() *Logger { - logger := New() - logger.ctx = l.ctx - logger.config = l.config - logger.parent = l - return logger + newLogger := New() + newLogger.ctx = l.ctx + newLogger.config = l.config + newLogger.parent = l + return newLogger } // getFilePath returns the logging file path. @@ -115,12 +116,13 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { var ( now = time.Now() input = &HandlerInput{ - logger: l, - index: -1, - Ctx: ctx, - Time: now, - Color: defaultLevelColor[level], - Level: level, + Logger: l, + Buffer: bytes.NewBuffer(nil), + Ctx: ctx, + Time: now, + Color: defaultLevelColor[level], + Level: level, + handlerIndex: -1, } ) if l.config.HeaderPrint { @@ -224,8 +226,8 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { } } -// doPrint outputs the logging content according configuration. -func (l *Logger) doPrint(ctx context.Context, input *HandlerInput) { +// doDefaultPrint outputs the logging content according configuration. +func (l *Logger) doDefaultPrint(ctx context.Context, input *HandlerInput) { if l.config.Writer == nil { // Output content to disk file. if l.config.Path != "" { @@ -245,7 +247,7 @@ func (l *Logger) doPrint(ctx context.Context, input *HandlerInput) { func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { if l.config.Writer != nil { var ( - buffer = input.getBuffer(l.config.WriterColorEnable) + buffer = input.getRealBuffer(l.config.WriterColorEnable) ) if _, err := l.config.Writer.Write(buffer.Bytes()); err != nil { intlog.Error(ctx, err) @@ -257,18 +259,18 @@ func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { func (l *Logger) printToStdout(ctx context.Context, input *HandlerInput) { if l.config.StdoutPrint { // This will lose color in Windows os system. - // if _, err := os.Stdout.Write(input.getBuffer(true).Bytes()); err != nil { + // if _, err := os.Stdout.Write(input.getRealBuffer(true).Bytes()); err != nil { // This will print color in Windows os system. - if _, err := fmt.Fprintf(color.Output, input.getBuffer(true).String()); err != nil { + if _, err := fmt.Fprintf(color.Output, input.getRealBuffer(true).String()); err != nil { intlog.Error(ctx, err) } } } // printToFile outputs logging content to disk file. -func (l *Logger) printToFile(ctx context.Context, t time.Time, input *HandlerInput) { +func (l *Logger) printToFile(ctx context.Context, t time.Time, in *HandlerInput) { var ( - buffer = input.getBuffer(l.config.WriterColorEnable) + buffer = in.getRealBuffer(l.config.WriterColorEnable) logFilePath = l.getFilePath(t) memoryLockKey = memoryLockPrefixForPrintingToFile + logFilePath ) @@ -362,3 +364,8 @@ func (l *Logger) GetStack(skip ...int) string { } return gdebug.StackWithFilters(filters, stackSkip) } + +// GetConfig returns the configuration of current Logger. +func (l *Logger) GetConfig() Config { + return l.config +} diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 33ed77043..705a56b0a 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -24,10 +24,10 @@ type Config struct { Writer io.Writer `json:"-"` // Customized io.Writer. Flags int `json:"flags"` // Extra flags for logging output features. Path string `json:"path"` // Logging directory path. - File string `json:"file"` // Format for logging file. + File string `json:"file"` // Format pattern for logging file. Level int `json:"level"` // Output level. Prefix string `json:"prefix"` // Prefix string for every logging content. - StSkip int `json:"stSkip"` // Skip count for stack. + StSkip int `json:"stSkip"` // Skipping count for stack. StStatus int `json:"stStatus"` // Stack status(1: enabled - default; 0: disabled) StFilter string `json:"stFilter"` // Stack string filter. CtxKeys []interface{} `json:"ctxKeys"` // Context keys for logging, which is used for value retrieving from context. diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index 71056e848..4217f5a42 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -15,51 +15,53 @@ import ( // Handler is function handler for custom logging content outputs. type Handler func(ctx context.Context, in *HandlerInput) +// HandlerInput is the input parameter struct for logging Handler. type HandlerInput struct { - logger *Logger // Logger. - index int // Middleware handling index for internal usage. - Ctx context.Context // Context. - Time time.Time // Logging time, which is the time that logging triggers. - TimeFormat string // Formatted time string, like "2016-01-09 12:00:00". - Color int // Using color, like COLOR_RED, COLOR_BLUE, etc. - Level int // Using level, like LEVEL_INFO, LEVEL_ERRO, etc. - LevelFormat string // Formatted level string, like "DEBU", "ERRO", etc. - CallerFunc string // The source function name that calls logging. - CallerPath string // The source file path and its line number that calls logging. - CtxStr string // The retrieved context value string from context. - Prefix string // Custom prefix string for logging content. - Content string // Content is the main logging content that passed by you. - IsAsync bool // IsAsync marks it is in asynchronous logging. + Logger *Logger // Logger. + Ctx context.Context // Context. + Buffer *bytes.Buffer // Buffer for logging content outputs. + Time time.Time // Logging time, which is the time that logging triggers. + TimeFormat string // Formatted time string, like "2016-01-09 12:00:00". + Color int // Using color, like COLOR_RED, COLOR_BLUE, etc. + Level int // Using level, like LEVEL_INFO, LEVEL_ERRO, etc. + LevelFormat string // Formatted level string, like "DEBU", "ERRO", etc. + CallerFunc string // The source function name that calls logging. + CallerPath string // The source file path and its line number that calls logging. + CtxStr string // The retrieved context value string from context. + Prefix string // Custom prefix string for logging content. + Content string // Content is the main logging content that passed by you. + IsAsync bool // IsAsync marks it is in asynchronous logging. + handlerIndex int // Middleware handling index for internal usage. } -// defaultHandler is the default handler for logger. -func defaultHandler(ctx context.Context, in *HandlerInput) { - in.logger.doPrint(ctx, in) -} - -func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, strings ...string) { - for _, s := range strings { - if buffer.Len() > 0 { - buffer.WriteByte(' ') - } - buffer.WriteString(s) +// Next calls the next logging handler in middleware way. +func (i *HandlerInput) Next() { + if len(i.Logger.config.Handlers)-1 > i.handlerIndex { + i.handlerIndex++ + i.Logger.config.Handlers[i.handlerIndex](i.Ctx, i) + } else { + defaultHandler(i.Ctx, i) } } -// Buffer creates and returns a buffer that handled by default logging content handler. -func (i *HandlerInput) Buffer() *bytes.Buffer { - return i.getBuffer(false) +// String returns the logging content formatted by default logging handler. +func (i *HandlerInput) String(withColor ...bool) string { + formatWithColor := false + if len(withColor) > 0 { + formatWithColor = withColor[0] + } + return i.getDefaultBuffer(formatWithColor).String() } -func (i *HandlerInput) getBuffer(withColor bool) *bytes.Buffer { +func (i *HandlerInput) getDefaultBuffer(withColor bool) *bytes.Buffer { buffer := bytes.NewBuffer(nil) if i.TimeFormat != "" { buffer.WriteString(i.TimeFormat) } if i.LevelFormat != "" { if withColor { - i.addStringToBuffer(buffer, i.logger.getColoredStr( - i.logger.getColorByLevel(i.Level), i.LevelFormat, + i.addStringToBuffer(buffer, i.Logger.getColoredStr( + i.Logger.getColorByLevel(i.Level), i.LevelFormat, )) } else { i.addStringToBuffer(buffer, i.LevelFormat) @@ -84,18 +86,23 @@ func (i *HandlerInput) getBuffer(withColor bool) *bytes.Buffer { return buffer } -// String retrieves and returns the logging content handled by default handler. -func (i *HandlerInput) String() string { - return i.Buffer().String() +func (i *HandlerInput) getRealBuffer(withColor bool) *bytes.Buffer { + if i.Buffer.Len() > 0 { + return i.Buffer + } + return i.getDefaultBuffer(withColor) } -// Next calls the next logging handler in middleware way. -func (i *HandlerInput) Next() { - if len(i.logger.config.Handlers)-1 > i.index { - i.index++ - i.logger.config.Handlers[i.index](i.Ctx, i) - } else { - // The last handler is the default handler. - defaultHandler(i.Ctx, i) +// defaultHandler is the default handler for logger. +func defaultHandler(ctx context.Context, in *HandlerInput) { + in.Logger.doDefaultPrint(ctx, in) +} + +func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, strings ...string) { + for _, s := range strings { + if buffer.Len() > 0 { + buffer.WriteByte(' ') + } + buffer.WriteString(s) } } diff --git a/os/glog/glog_z_unit_handler_test.go b/os/glog/glog_z_unit_handler_test.go index 70acc0f5c..921beb9b5 100644 --- a/os/glog/glog_z_unit_handler_test.go +++ b/os/glog/glog_z_unit_handler_test.go @@ -19,7 +19,7 @@ import ( var arrayForHandlerTest1 = garray.NewStrArray() func customHandler1(ctx context.Context, input *glog.HandlerInput) { - arrayForHandlerTest1.Append(input.String()) + arrayForHandlerTest1.Append(input.String(false)) input.Next() } @@ -51,7 +51,7 @@ func TestLogger_SetHandlers1(t *testing.T) { var arrayForHandlerTest2 = garray.NewStrArray() func customHandler2(ctx context.Context, input *glog.HandlerInput) { - arrayForHandlerTest2.Append(input.String()) + arrayForHandlerTest2.Append(input.String(false)) } func TestLogger_SetHandlers2(t *testing.T) { From 12f219396341dedfd80c00a606e24dce3b0c5ca8 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 12 Aug 2021 22:14:31 +0800 Subject: [PATCH 454/492] improve handler feature for package glog --- .../os/glog/handler/glog_handler_greylog.go | 31 +++++++++++++++ .example/os/glog/handler/glog_handler_json.go | 2 +- os/glog/glog_logger.go | 39 +++++++++++++------ os/glog/glog_logger_handler.go | 5 ++- 4 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 .example/os/glog/handler/glog_handler_greylog.go diff --git a/.example/os/glog/handler/glog_handler_greylog.go b/.example/os/glog/handler/glog_handler_greylog.go new file mode 100644 index 000000000..e690ac309 --- /dev/null +++ b/.example/os/glog/handler/glog_handler_greylog.go @@ -0,0 +1,31 @@ +package main + +//import ( +// "context" +// "github.com/gogf/gf/frame/g" +// "github.com/gogf/gf/os/glog" +// "github.com/robertkowalski/graylog-golang" +//) +// +//var greyLogClient = gelf.New(gelf.Config{ +// GraylogPort: 80, +// GraylogHostname: "graylog-host.com", +// Connection: "wan", +// MaxChunkSizeWan: 42, +// MaxChunkSizeLan: 1337, +//}) +// +//// LoggingGreyLogHandler is an example handler for logging content to remote GreyLog service. +//var LoggingGreyLogHandler glog.Handler = func(ctx context.Context, in *glog.HandlerInput) { +// in.Next() +// greyLogClient.Log(in.Buffer.String()) +//} +// +//func main() { +// g.Log().SetHandlers(LoggingGreyLogHandler) +// +// g.Log().Debug("Debugging...") +// g.Log().Warning("It is warning info") +// g.Log().Error("Error occurs, please have a check") +// glog.Println("test log") +//} diff --git a/.example/os/glog/handler/glog_handler_json.go b/.example/os/glog/handler/glog_handler_json.go index ee4bb06e6..49dc0750b 100644 --- a/.example/os/glog/handler/glog_handler_json.go +++ b/.example/os/glog/handler/glog_handler_json.go @@ -16,7 +16,7 @@ type JsonOutputsForLogger struct { Content string `json:"content"` } -// LoggingJsonHandler is a example handler for logging JSON format content. +// LoggingJsonHandler is an example handler for logging JSON format content. var LoggingJsonHandler glog.Handler = func(ctx context.Context, in *glog.HandlerInput) { jsonForLogger := JsonOutputsForLogger{ Time: in.TimeFormat, diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 8c8dd3716..5c6ea42e7 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -227,24 +227,33 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { } // doDefaultPrint outputs the logging content according configuration. -func (l *Logger) doDefaultPrint(ctx context.Context, input *HandlerInput) { +func (l *Logger) doDefaultPrint(ctx context.Context, input *HandlerInput) *bytes.Buffer { + var buffer *bytes.Buffer if l.config.Writer == nil { - // Output content to disk file. - if l.config.Path != "" { - l.printToFile(ctx, input.Time, input) - } // Allow output to stdout? if l.config.StdoutPrint { - l.printToStdout(ctx, input) + if buf := l.printToStdout(ctx, input); buf != nil { + buffer = buf + } + } + + // Output content to disk file. + if l.config.Path != "" { + if buf := l.printToFile(ctx, input.Time, input); buf != nil { + buffer = buf + } } } else { // Output to custom writer. - l.printToWriter(ctx, input) + if buf := l.printToWriter(ctx, input); buf != nil { + buffer = buf + } } + return buffer } // printToWriter writes buffer to writer. -func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { +func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) *bytes.Buffer { if l.config.Writer != nil { var ( buffer = input.getRealBuffer(l.config.WriterColorEnable) @@ -252,23 +261,30 @@ func (l *Logger) printToWriter(ctx context.Context, input *HandlerInput) { if _, err := l.config.Writer.Write(buffer.Bytes()); err != nil { intlog.Error(ctx, err) } + return buffer } + return nil } // printToStdout outputs logging content to stdout. -func (l *Logger) printToStdout(ctx context.Context, input *HandlerInput) { +func (l *Logger) printToStdout(ctx context.Context, input *HandlerInput) *bytes.Buffer { if l.config.StdoutPrint { + var ( + buffer = input.getRealBuffer(true) + ) // This will lose color in Windows os system. // if _, err := os.Stdout.Write(input.getRealBuffer(true).Bytes()); err != nil { // This will print color in Windows os system. - if _, err := fmt.Fprintf(color.Output, input.getRealBuffer(true).String()); err != nil { + if _, err := fmt.Fprintf(color.Output, buffer.String()); err != nil { intlog.Error(ctx, err) } + return buffer } + return nil } // printToFile outputs logging content to disk file. -func (l *Logger) printToFile(ctx context.Context, t time.Time, in *HandlerInput) { +func (l *Logger) printToFile(ctx context.Context, t time.Time, in *HandlerInput) *bytes.Buffer { var ( buffer = in.getRealBuffer(l.config.WriterColorEnable) logFilePath = l.getFilePath(t) @@ -294,6 +310,7 @@ func (l *Logger) printToFile(ctx context.Context, t time.Time, in *HandlerInput) intlog.Error(ctx, err) } } + return buffer } // getFilePointer retrieves and returns a file pointer from file pool. diff --git a/os/glog/glog_logger_handler.go b/os/glog/glog_logger_handler.go index 4217f5a42..bdeb4cdfc 100644 --- a/os/glog/glog_logger_handler.go +++ b/os/glog/glog_logger_handler.go @@ -95,7 +95,10 @@ func (i *HandlerInput) getRealBuffer(withColor bool) *bytes.Buffer { // defaultHandler is the default handler for logger. func defaultHandler(ctx context.Context, in *HandlerInput) { - in.Logger.doDefaultPrint(ctx, in) + buffer := in.Logger.doDefaultPrint(ctx, in) + if in.Buffer.Len() == 0 { + in.Buffer = buffer + } } func (i *HandlerInput) addStringToBuffer(buffer *bytes.Buffer, strings ...string) { From 903f29a824fdff8a01b15ff620dc134c1dde23ce Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Fri, 13 Aug 2021 11:28:47 +0800 Subject: [PATCH 455/492] add OmitNil feature for package gdb --- database/gdb/gdb_func.go | 60 ++++++++++++------ database/gdb/gdb_model.go | 11 +++- database/gdb/gdb_model_condition.go | 44 +++++++++---- database/gdb/gdb_model_option.go | 33 +++++++++- database/gdb/gdb_model_utility.go | 12 ++++ .../gdb/gdb_z_mysql_association_with_test.go | 1 + database/gdb/gdb_z_mysql_model_test.go | 63 +++++++++++++++++-- 7 files changed, 181 insertions(+), 43 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 7c7e096da..0a8a28cab 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -489,11 +489,20 @@ func formatSql(sql string, args []interface{}) (newSql string, newArgs []interfa return handleArguments(sql, args) } +type formatWhereInput struct { + Where interface{} + Args []interface{} + OmitNil bool + OmitEmpty bool + Schema string + Table string +} + // formatWhere formats where statement and its arguments for `Where` and `Having` statements. -func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, schema, table string) (newWhere string, newArgs []interface{}) { +func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interface{}) { var ( buffer = bytes.NewBuffer(nil) - rv = reflect.ValueOf(where) + rv = reflect.ValueOf(in.Where) kind = rv.Kind() ) for kind == reflect.Ptr { @@ -502,12 +511,17 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, s } switch kind { case reflect.Array, reflect.Slice: - newArgs = formatWhereInterfaces(db, gconv.Interfaces(where), buffer, newArgs) + newArgs = formatWhereInterfaces(db, gconv.Interfaces(in.Where), buffer, newArgs) case reflect.Map: - for key, value := range DataToMapDeep(where) { - if gregex.IsMatchString(regularFieldNameRegPattern, key) && omitEmpty && empty.IsEmpty(value) { - continue + for key, value := range DataToMapDeep(in.Where) { + if gregex.IsMatchString(regularFieldNameRegPattern, key) { + if in.OmitNil && empty.IsNil(value) { + continue + } + if in.OmitEmpty && empty.IsEmpty(value) { + continue + } } newArgs = formatWhereKeyValue(db, buffer, newArgs, key, value) } @@ -517,11 +531,16 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, s // it then uses its Iterate function to iterates its key-value pairs. // For example, ListMap and TreeMap are ordered map, // which implement apiIterator interface and are index-friendly for where conditions. - if iterator, ok := where.(apiIterator); ok { + if iterator, ok := in.Where.(apiIterator); ok { iterator.Iterator(func(key, value interface{}) bool { ketStr := gconv.String(key) - if gregex.IsMatchString(regularFieldNameRegPattern, ketStr) && omitEmpty && empty.IsEmpty(value) { - return true + if gregex.IsMatchString(regularFieldNameRegPattern, ketStr) { + if in.OmitNil && empty.IsNil(value) { + return true + } + if in.OmitEmpty && empty.IsEmpty(value) { + return true + } } newArgs = formatWhereKeyValue(db, buffer, newArgs, ketStr, value) return true @@ -529,12 +548,15 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, s break } // Automatically mapping and filtering the struct attribute. - data := DataToMapDeep(where) - if table != "" { - data, _ = db.GetCore().mappingAndFilterData(schema, table, data, true) + data := DataToMapDeep(in.Where) + if in.Table != "" { + data, _ = db.GetCore().mappingAndFilterData(in.Schema, in.Table, data, true) } for key, value := range data { - if omitEmpty && empty.IsEmpty(value) { + if in.OmitNil && empty.IsNil(value) { + continue + } + if in.OmitEmpty && empty.IsEmpty(value) { continue } newArgs = formatWhereKeyValue(db, buffer, newArgs, key, value) @@ -544,14 +566,14 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, s // Usually a string. var ( i = 0 - whereStr = gconv.String(where) + whereStr = gconv.String(in.Where) ) for { - if i >= len(args) { + if i >= len(in.Args) { break } // Sub query, which is always used along with a string condition. - if model, ok := args[i].(*Model); ok { + if model, ok := in.Args[i].(*Model); ok { var ( index = -1 ) @@ -565,7 +587,7 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, s } return s }) - args = gutil.SliceDelete(args, i) + in.Args = gutil.SliceDelete(in.Args, i) continue } i++ @@ -574,9 +596,9 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, s } if buffer.Len() == 0 { - return "", args + return "", in.Args } - newArgs = append(newArgs, args...) + newArgs = append(newArgs, in.Args...) newWhere = buffer.String() if len(newArgs) > 0 { if gstr.Pos(newWhere, "?") == -1 { diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index fe761dca1..e7d4f823f 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -104,9 +104,14 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { if len(tableNameQueryOrStruct) > 1 { conditionStr := gconv.String(tableNameQueryOrStruct[0]) if gstr.Contains(conditionStr, "?") { - tableStr, extraArgs = formatWhere( - c.db, conditionStr, tableNameQueryOrStruct[1:], false, "", "", - ) + tableStr, extraArgs = formatWhere(c.db, formatWhereInput{ + Where: conditionStr, + Args: tableNameQueryOrStruct[1:], + OmitNil: false, + OmitEmpty: false, + Schema: "", + Table: "", + }) } } // Normal model creation. diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 5853fe7df..2ff16eb4c 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -388,9 +388,14 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh switch v.Operator { case whereHolderOperatorWhere: if conditionWhere == "" { - newWhere, newArgs := formatWhere( - m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, - ) + newWhere, newArgs := formatWhere(m.db, formatWhereInput{ + Where: v.Where, + Args: v.Args, + OmitNil: m.option&optionOmitNilWhere > 0, + OmitEmpty: m.option&optionOmitEmptyWhere > 0, + Schema: m.schema, + Table: m.tables, + }) if len(newWhere) > 0 { conditionWhere = newWhere conditionArgs = newArgs @@ -400,9 +405,14 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh fallthrough case whereHolderOperatorAnd: - newWhere, newArgs := formatWhere( - m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, - ) + newWhere, newArgs := formatWhere(m.db, formatWhereInput{ + Where: v.Where, + Args: v.Args, + OmitNil: m.option&optionOmitNilWhere > 0, + OmitEmpty: m.option&optionOmitEmptyWhere > 0, + Schema: m.schema, + Table: m.tables, + }) if len(newWhere) > 0 { if len(conditionWhere) == 0 { conditionWhere = newWhere @@ -415,9 +425,14 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh } case whereHolderOperatorOr: - newWhere, newArgs := formatWhere( - m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, - ) + newWhere, newArgs := formatWhere(m.db, formatWhereInput{ + Where: v.Where, + Args: v.Args, + OmitNil: m.option&optionOmitNilWhere > 0, + OmitEmpty: m.option&optionOmitEmptyWhere > 0, + Schema: m.schema, + Table: m.tables, + }) if len(newWhere) > 0 { if len(conditionWhere) == 0 { conditionWhere = newWhere @@ -457,9 +472,14 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh } // HAVING. if len(m.having) > 0 { - havingStr, havingArgs := formatWhere( - m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&optionOmitEmptyWhere > 0, m.schema, m.tables, - ) + havingStr, havingArgs := formatWhere(m.db, formatWhereInput{ + Where: m.having[0], + Args: gconv.Interfaces(m.having[1]), + OmitNil: m.option&optionOmitNilWhere > 0, + OmitEmpty: m.option&optionOmitEmptyWhere > 0, + Schema: m.schema, + Table: m.tables, + }) if len(havingStr) > 0 { conditionExtra += " HAVING " + havingStr conditionArgs = append(conditionArgs, havingArgs...) diff --git a/database/gdb/gdb_model_option.go b/database/gdb/gdb_model_option.go index 6b3de8813..68991fce4 100644 --- a/database/gdb/gdb_model_option.go +++ b/database/gdb/gdb_model_option.go @@ -7,9 +7,12 @@ package gdb const ( + optionOmitNil = optionOmitNilWhere | optionOmitNilData optionOmitEmpty = optionOmitEmptyWhere | optionOmitEmptyData optionOmitEmptyWhere = 1 << iota // 8 optionOmitEmptyData // 16 + optionOmitNilWhere // 32 + optionOmitNilData // 64 ) // Option adds extra operation option for the model. @@ -20,7 +23,7 @@ func (m *Model) Option(option int) *Model { return model } -// OmitEmpty sets OmitEmpty option for the model, which automatically filers +// OmitEmpty sets optionOmitEmpty option for the model, which automatically filers // the data and where parameters for `empty` values. func (m *Model) OmitEmpty() *Model { model := m.getModel() @@ -28,7 +31,7 @@ func (m *Model) OmitEmpty() *Model { return model } -// OmitEmptyWhere sets OmitEmptyWhere option for the model, which automatically filers +// OmitEmptyWhere sets optionOmitEmptyWhere option for the model, which automatically filers // the Where/Having parameters for `empty` values. func (m *Model) OmitEmptyWhere() *Model { model := m.getModel() @@ -36,10 +39,34 @@ func (m *Model) OmitEmptyWhere() *Model { return model } -// OmitEmptyData sets OmitEmptyData option for the model, which automatically filers +// OmitEmptyData sets optionOmitEmptyData option for the model, which automatically filers // the Data parameters for `empty` values. func (m *Model) OmitEmptyData() *Model { model := m.getModel() model.option = model.option | optionOmitEmptyData return model } + +// OmitNil sets optionOmitNil option for the model, which automatically filers +// the data and where parameters for `nil` values. +func (m *Model) OmitNil() *Model { + model := m.getModel() + model.option = model.option | optionOmitNil + return model +} + +// OmitNilWhere sets optionOmitNilWhere option for the model, which automatically filers +// the Where/Having parameters for `nil` values. +func (m *Model) OmitNilWhere() *Model { + model := m.getModel() + model.option = model.option | optionOmitNilWhere + return model +} + +// OmitNilData sets optionOmitNilData option for the model, which automatically filers +// the Data parameters for `nil` values. +func (m *Model) OmitNilData() *Model { + model := m.getModel() + model.option = model.option | optionOmitNilData + return model +} diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index a1a12b75e..be9acfd41 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -109,6 +109,18 @@ func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEm if err != nil { return nil, err } + // Remove key-value pairs of which the value is nil. + if allowOmitEmpty && m.option&optionOmitNilData > 0 { + tempMap := make(Map, len(data)) + for k, v := range data { + if empty.IsNil(v) { + continue + } + tempMap[k] = v + } + data = tempMap + } + // Remove key-value pairs of which the value is empty. if allowOmitEmpty && m.option&optionOmitEmptyData > 0 { tempMap := make(Map, len(data)) diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index d02316e41..d1b98727e 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -368,6 +368,7 @@ PRIMARY KEY (id) gtest.Assert(err, nil) } } + gtest.C(t, func(t *gtest.T) { var users []*User err := db.With(User{}). diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 806cc80bc..ee079006a 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -2132,24 +2132,75 @@ func Test_Model_Option_List(t *testing.T) { } func Test_Model_OmitEmpty(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - table := fmt.Sprintf(`table_%s`, gtime.TimestampNanoStr()) - if _, err := db.Exec(fmt.Sprintf(` + table := fmt.Sprintf(`table_%s`, gtime.TimestampNanoStr()) + if _, err := db.Exec(fmt.Sprintf(` CREATE TABLE IF NOT EXISTS %s ( id int(10) unsigned NOT NULL AUTO_INCREMENT, name varchar(45) NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; `, table)); err != nil { - gtest.Error(err) - } - defer dropTable(table) + gtest.Error(err) + } + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { _, err := db.Model(table).OmitEmpty().Data(g.Map{ "id": 1, "name": "", }).Save() t.AssertNE(err, nil) }) + gtest.C(t, func(t *gtest.T) { + _, err := db.Model(table).OmitEmptyData().Data(g.Map{ + "id": 1, + "name": "", + }).Save() + t.AssertNE(err, nil) + }) + gtest.C(t, func(t *gtest.T) { + _, err := db.Model(table).OmitEmptyWhere().Data(g.Map{ + "id": 1, + "name": "", + }).Save() + t.Assert(err, nil) + }) +} + +func Test_Model_OmitNil(t *testing.T) { + table := fmt.Sprintf(`table_%s`, gtime.TimestampNanoStr()) + if _, err := db.Exec(fmt.Sprintf(` + CREATE TABLE IF NOT EXISTS %s ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + name varchar(45) NOT NULL, + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + _, err := db.Model(table).OmitNil().Data(g.Map{ + "id": 1, + "name": nil, + }).Save() + t.AssertNE(err, nil) + }) + gtest.C(t, func(t *gtest.T) { + _, err := db.Model(table).OmitNil().Data(g.Map{ + "id": 1, + "name": "", + }).Save() + t.Assert(err, nil) + }) + gtest.C(t, func(t *gtest.T) { + _, err := db.Model(table).OmitNilWhere().Data(g.Map{ + "id": 1, + "name": "", + }).Save() + t.Assert(err, nil) + }) } func Test_Model_Option_Where(t *testing.T) { From 17b73e7cb33bf321b1265933b589f36964843dba Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Fri, 13 Aug 2021 13:20:33 +0800 Subject: [PATCH 456/492] add func SetWriterColorEnable for file log color --- os/glog/glog_config.go | 5 +++++ os/glog/glog_logger_config.go | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/os/glog/glog_config.go b/os/glog/glog_config.go index 6e4402dee..badfaafd8 100644 --- a/os/glog/glog_config.go +++ b/os/glog/glog_config.go @@ -153,3 +153,8 @@ func GetLevelPrefix(level int) string { func SetHandlers(handlers ...Handler) { logger.SetHandlers(handlers...) } + +//SetWriterColorEnable sets the file logging with color +func SetWriterColorEnable(enabled bool) { + logger.SetWriterColorEnable(enabled) +} \ No newline at end of file diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 6cc95ef05..283a34ce8 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -249,3 +249,8 @@ func (l *Logger) SetPrefix(prefix string) { func (l *Logger) SetHandlers(handlers ...Handler) { l.config.Handlers = handlers } + +//SetWriterColorEnable sets the file logging with color +func (l *Logger) SetWriterColorEnable(enabled bool) { + l.config.WriterColorEnable = enabled +} \ No newline at end of file From f56f689970e970e762cb32c90545a4ce51315876 Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Fri, 13 Aug 2021 13:24:28 +0800 Subject: [PATCH 457/492] fmt --- os/glog/glog_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/glog/glog_config.go b/os/glog/glog_config.go index badfaafd8..173913d73 100644 --- a/os/glog/glog_config.go +++ b/os/glog/glog_config.go @@ -157,4 +157,4 @@ func SetHandlers(handlers ...Handler) { //SetWriterColorEnable sets the file logging with color func SetWriterColorEnable(enabled bool) { logger.SetWriterColorEnable(enabled) -} \ No newline at end of file +} From 31ef4e7b5a85a3dfbf32b6fdd9caa397323a0a54 Mon Sep 17 00:00:00 2001 From: wanna <wanghouwei@medlinker.com> Date: Fri, 13 Aug 2021 13:27:43 +0800 Subject: [PATCH 458/492] fmt --- os/glog/glog_logger_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 283a34ce8..20dec0cd3 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -253,4 +253,4 @@ func (l *Logger) SetHandlers(handlers ...Handler) { //SetWriterColorEnable sets the file logging with color func (l *Logger) SetWriterColorEnable(enabled bool) { l.config.WriterColorEnable = enabled -} \ No newline at end of file +} From db621e38d98f921412c4835916855893db4a50df Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 16 Aug 2021 20:30:58 +0800 Subject: [PATCH 459/492] improve ServeFile/ServeFileDownload for package ghttp --- .../redirect/{back.go => redirect_back.go} | 0 .../net/ghttp/server/redirect/redirect_to.go | 18 ++++++++++++++++++ .../net/ghttp/server/servefile/servefile.go | 15 +++++++++++++++ .../server/servefile/servefiledownload.go | 15 +++++++++++++++ .example/net/ghttp/server/servefile/test.txt | 1 + net/ghttp/ghttp_response.go | 12 ++++++++---- 6 files changed, 57 insertions(+), 4 deletions(-) rename .example/net/ghttp/server/redirect/{back.go => redirect_back.go} (100%) create mode 100644 .example/net/ghttp/server/redirect/redirect_to.go create mode 100644 .example/net/ghttp/server/servefile/servefile.go create mode 100644 .example/net/ghttp/server/servefile/servefiledownload.go create mode 100644 .example/net/ghttp/server/servefile/test.txt diff --git a/.example/net/ghttp/server/redirect/back.go b/.example/net/ghttp/server/redirect/redirect_back.go similarity index 100% rename from .example/net/ghttp/server/redirect/back.go rename to .example/net/ghttp/server/redirect/redirect_back.go diff --git a/.example/net/ghttp/server/redirect/redirect_to.go b/.example/net/ghttp/server/redirect/redirect_to.go new file mode 100644 index 000000000..f7b8f859d --- /dev/null +++ b/.example/net/ghttp/server/redirect/redirect_to.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" +) + +func main() { + s := g.Server() + s.BindHandler("/", func(r *ghttp.Request) { + r.Response.RedirectTo("/login") + }) + s.BindHandler("/login", func(r *ghttp.Request) { + r.Response.Writeln("Login First") + }) + s.SetPort(8199) + s.Run() +} diff --git a/.example/net/ghttp/server/servefile/servefile.go b/.example/net/ghttp/server/servefile/servefile.go new file mode 100644 index 000000000..761cfbe03 --- /dev/null +++ b/.example/net/ghttp/server/servefile/servefile.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" +) + +func main() { + s := g.Server() + s.BindHandler("/", func(r *ghttp.Request) { + r.Response.ServeFile("test.txt") + }) + s.SetPort(8999) + s.Run() +} diff --git a/.example/net/ghttp/server/servefile/servefiledownload.go b/.example/net/ghttp/server/servefile/servefiledownload.go new file mode 100644 index 000000000..a8e734b9b --- /dev/null +++ b/.example/net/ghttp/server/servefile/servefiledownload.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" +) + +func main() { + s := g.Server() + s.BindHandler("/", func(r *ghttp.Request) { + r.Response.ServeFileDownload("test.txt") + }) + s.SetPort(8999) + s.Run() +} diff --git a/.example/net/ghttp/server/servefile/test.txt b/.example/net/ghttp/server/servefile/test.txt new file mode 100644 index 000000000..30d74d258 --- /dev/null +++ b/.example/net/ghttp/server/servefile/test.txt @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/net/ghttp/ghttp_response.go b/net/ghttp/ghttp_response.go index 04524046a..819a3590d 100644 --- a/net/ghttp/ghttp_response.go +++ b/net/ghttp/ghttp_response.go @@ -41,14 +41,16 @@ func newResponse(s *Server, w http.ResponseWriter) *Response { // ServeFile serves the file to the response. func (r *Response) ServeFile(path string, allowIndex ...bool) { - serveFile := (*staticFile)(nil) + var ( + serveFile *staticFile + ) if file := gres.Get(path); file != nil { serveFile = &staticFile{ File: file, IsDir: file.FileInfo().IsDir(), } } else { - path = gfile.RealPath(path) + path, _ = gfile.Search(path) if path == "" { r.WriteStatus(http.StatusNotFound) return @@ -60,7 +62,9 @@ func (r *Response) ServeFile(path string, allowIndex ...bool) { // ServeFileDownload serves file downloading to the response. func (r *Response) ServeFileDownload(path string, name ...string) { - serveFile := (*staticFile)(nil) + var ( + serveFile *staticFile + ) downloadName := "" if len(name) > 0 { downloadName = name[0] @@ -74,7 +78,7 @@ func (r *Response) ServeFileDownload(path string, name ...string) { downloadName = gfile.Basename(file.Name()) } } else { - path = gfile.RealPath(path) + path, _ = gfile.Search(path) if path == "" { r.WriteStatus(http.StatusNotFound) return From ad6c4f5245f480344deada319b143f330b6b8183 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 16 Aug 2021 21:47:45 +0800 Subject: [PATCH 460/492] improve session storage for package gsession --- container/gvar/gvar.go | 54 +++++------ container/gvar/gvar_list.go | 8 +- container/gvar/gvar_map.go | 30 +++--- container/gvar/gvar_slice.go | 18 ++-- container/gvar/gvar_struct.go | 32 ++++--- os/gsession/gsession_manager.go | 2 +- os/gsession/gsession_session.go | 93 +++++++++++-------- os/gsession/gsession_storage.go | 18 ++-- os/gsession/gsession_storage_file.go | 24 ++--- os/gsession/gsession_storage_memory.go | 24 ++--- os/gsession/gsession_storage_redis.go | 24 ++--- .../gsession_storage_redis_hashtable.go | 43 +++++---- util/gconv/gconv_scan.go | 4 +- 13 files changed, 203 insertions(+), 171 deletions(-) diff --git a/container/gvar/gvar.go b/container/gvar/gvar.go index 36480d71a..aa4dcba31 100644 --- a/container/gvar/gvar.go +++ b/container/gvar/gvar.go @@ -22,8 +22,8 @@ type Var struct { safe bool // Concurrent safe or not. } -// New creates and returns a new Var with given <value>. -// The optional parameter <safe> specifies whether Var is used in concurrent-safety, +// New creates and returns a new Var with given `value`. +// The optional parameter `safe` specifies whether Var is used in concurrent-safety, // which is false in default. func New(value interface{}, safe ...bool) *Var { v := Var{} @@ -36,8 +36,8 @@ func New(value interface{}, safe ...bool) *Var { return &v } -// Create creates and returns a new Var with given <value>. -// The optional parameter <safe> specifies whether Var is used in concurrent-safety, +// Create creates and returns a new Var with given `value`. +// The optional parameter `safe` specifies whether Var is used in concurrent-safety, // which is false in default. func Create(value interface{}, safe ...bool) Var { v := Var{} @@ -55,7 +55,7 @@ func (v *Var) Clone() *Var { return New(v.Val(), v.safe) } -// Set sets <value> to <v>, and returns the old value. +// Set sets `value` to `v`, and returns the old value. func (v *Var) Set(value interface{}) (old interface{}) { if v.safe { if t, ok := v.value.(*gtype.Interface); ok { @@ -68,7 +68,7 @@ func (v *Var) Set(value interface{}) (old interface{}) { return } -// Val returns the current value of <v>. +// Val returns the current value of `v`. func (v *Var) Val() interface{} { if v == nil { return nil @@ -86,96 +86,96 @@ func (v *Var) Interface() interface{} { return v.Val() } -// Bytes converts and returns <v> as []byte. +// Bytes converts and returns `v` as []byte. func (v *Var) Bytes() []byte { return gconv.Bytes(v.Val()) } -// String converts and returns <v> as string. +// String converts and returns `v` as string. func (v *Var) String() string { return gconv.String(v.Val()) } -// Bool converts and returns <v> as bool. +// Bool converts and returns `v` as bool. func (v *Var) Bool() bool { return gconv.Bool(v.Val()) } -// Int converts and returns <v> as int. +// Int converts and returns `v` as int. func (v *Var) Int() int { return gconv.Int(v.Val()) } -// Int8 converts and returns <v> as int8. +// Int8 converts and returns `v` as int8. func (v *Var) Int8() int8 { return gconv.Int8(v.Val()) } -// Int16 converts and returns <v> as int16. +// Int16 converts and returns `v` as int16. func (v *Var) Int16() int16 { return gconv.Int16(v.Val()) } -// Int32 converts and returns <v> as int32. +// Int32 converts and returns `v` as int32. func (v *Var) Int32() int32 { return gconv.Int32(v.Val()) } -// Int64 converts and returns <v> as int64. +// Int64 converts and returns `v` as int64. func (v *Var) Int64() int64 { return gconv.Int64(v.Val()) } -// Uint converts and returns <v> as uint. +// Uint converts and returns `v` as uint. func (v *Var) Uint() uint { return gconv.Uint(v.Val()) } -// Uint8 converts and returns <v> as uint8. +// Uint8 converts and returns `v` as uint8. func (v *Var) Uint8() uint8 { return gconv.Uint8(v.Val()) } -// Uint16 converts and returns <v> as uint16. +// Uint16 converts and returns `v` as uint16. func (v *Var) Uint16() uint16 { return gconv.Uint16(v.Val()) } -// Uint32 converts and returns <v> as uint32. +// Uint32 converts and returns `v` as uint32. func (v *Var) Uint32() uint32 { return gconv.Uint32(v.Val()) } -// Uint64 converts and returns <v> as uint64. +// Uint64 converts and returns `v` as uint64. func (v *Var) Uint64() uint64 { return gconv.Uint64(v.Val()) } -// Float32 converts and returns <v> as float32. +// Float32 converts and returns `v` as float32. func (v *Var) Float32() float32 { return gconv.Float32(v.Val()) } -// Float64 converts and returns <v> as float64. +// Float64 converts and returns `v` as float64. func (v *Var) Float64() float64 { return gconv.Float64(v.Val()) } -// Time converts and returns <v> as time.Time. -// The parameter <format> specifies the format of the time string using gtime, +// Time converts and returns `v` as time.Time. +// The parameter `format` specifies the format of the time string using gtime, // eg: Y-m-d H:i:s. func (v *Var) Time(format ...string) time.Time { return gconv.Time(v.Val(), format...) } -// Duration converts and returns <v> as time.Duration. -// If value of <v> is string, then it uses time.ParseDuration for conversion. +// Duration converts and returns `v` as time.Duration. +// If value of `v` is string, then it uses time.ParseDuration for conversion. func (v *Var) Duration() time.Duration { return gconv.Duration(v.Val()) } -// GTime converts and returns <v> as *gtime.Time. -// The parameter <format> specifies the format of the time string using gtime, +// GTime converts and returns `v` as *gtime.Time. +// The parameter `format` specifies the format of the time string using gtime, // eg: Y-m-d H:i:s. func (v *Var) GTime(format ...string) *gtime.Time { return gconv.GTime(v.Val(), format...) diff --git a/container/gvar/gvar_list.go b/container/gvar/gvar_list.go index 7e4c28833..06b6f4ab0 100644 --- a/container/gvar/gvar_list.go +++ b/container/gvar/gvar_list.go @@ -10,15 +10,15 @@ import ( "github.com/gogf/gf/util/gutil" ) -// ListItemValues retrieves and returns the elements of all item struct/map with key <key>. -// Note that the parameter <list> should be type of slice which contains elements of map or struct, +// ListItemValues retrieves and returns the elements of all item struct/map with key `key`. +// Note that the parameter `list` should be type of slice which contains elements of map or struct, // or else it returns an empty slice. func (v *Var) ListItemValues(key interface{}) (values []interface{}) { return gutil.ListItemValues(v.Val(), key) } -// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key <key>. -// Note that the parameter <list> should be type of slice which contains elements of map or struct, +// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key `key`. +// Note that the parameter `list` should be type of slice which contains elements of map or struct, // or else it returns an empty slice. func (v *Var) ListItemValuesUnique(key string) []interface{} { return gutil.ListItemValuesUnique(v.Val(), key) diff --git a/container/gvar/gvar_map.go b/container/gvar/gvar_map.go index 00a751a23..732bae265 100644 --- a/container/gvar/gvar_map.go +++ b/container/gvar/gvar_map.go @@ -8,7 +8,7 @@ package gvar import "github.com/gogf/gf/util/gconv" -// Map converts and returns <v> as map[string]interface{}. +// Map converts and returns `v` as map[string]interface{}. func (v *Var) Map(tags ...string) map[string]interface{} { return gconv.Map(v.Val(), tags...) } @@ -18,12 +18,12 @@ func (v *Var) MapStrAny() map[string]interface{} { return v.Map() } -// MapStrStr converts and returns <v> as map[string]string. +// MapStrStr converts and returns `v` as map[string]string. func (v *Var) MapStrStr(tags ...string) map[string]string { return gconv.MapStrStr(v.Val(), tags...) } -// MapStrVar converts and returns <v> as map[string]Var. +// MapStrVar converts and returns `v` as map[string]Var. func (v *Var) MapStrVar(tags ...string) map[string]*Var { m := v.Map(tags...) if len(m) > 0 { @@ -36,17 +36,17 @@ func (v *Var) MapStrVar(tags ...string) map[string]*Var { return nil } -// MapDeep converts and returns <v> as map[string]interface{} recursively. +// MapDeep converts and returns `v` as map[string]interface{} recursively. func (v *Var) MapDeep(tags ...string) map[string]interface{} { return gconv.MapDeep(v.Val(), tags...) } -// MapStrStrDeep converts and returns <v> as map[string]string recursively. +// MapStrStrDeep converts and returns `v` as map[string]string recursively. func (v *Var) MapStrStrDeep(tags ...string) map[string]string { return gconv.MapStrStrDeep(v.Val(), tags...) } -// MapStrVarDeep converts and returns <v> as map[string]*Var recursively. +// MapStrVarDeep converts and returns `v` as map[string]*Var recursively. func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var { m := v.MapDeep(tags...) if len(m) > 0 { @@ -59,27 +59,33 @@ func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var { return nil } -// Maps converts and returns <v> as map[string]string. +// Maps converts and returns `v` as map[string]string. // See gconv.Maps. func (v *Var) Maps(tags ...string) []map[string]interface{} { return gconv.Maps(v.Val(), tags...) } -// MapToMap converts any map type variable <params> to another map type variable <pointer>. +// MapsDeep converts `value` to []map[string]interface{} recursively. +// See gconv.MapsDeep. +func (v *Var) MapsDeep(tags ...string) []map[string]interface{} { + return gconv.MapsDeep(v.Val(), tags...) +} + +// MapToMap converts any map type variable `params` to another map type variable `pointer`. // See gconv.MapToMap. func (v *Var) MapToMap(pointer interface{}, mapping ...map[string]string) (err error) { return gconv.MapToMap(v.Val(), pointer, mapping...) } -// MapToMaps converts any map type variable <params> to another map type variable <pointer>. +// MapToMaps converts any map type variable `params` to another map type variable `pointer`. // See gconv.MapToMaps. func (v *Var) MapToMaps(pointer interface{}, mapping ...map[string]string) (err error) { return gconv.MapToMaps(v.Val(), pointer, mapping...) } -// MapToMapsDeep converts any map type variable <params> to another map type variable -// <pointer> recursively. +// MapToMapsDeep converts any map type variable `params` to another map type variable +// `pointer` recursively. // See gconv.MapToMapsDeep. func (v *Var) MapToMapsDeep(pointer interface{}, mapping ...map[string]string) (err error) { - return gconv.MapToMapsDeep(v.Val(), pointer, mapping...) + return gconv.MapToMaps(v.Val(), pointer, mapping...) } diff --git a/container/gvar/gvar_slice.go b/container/gvar/gvar_slice.go index e4c2f5272..c9c6556ce 100644 --- a/container/gvar/gvar_slice.go +++ b/container/gvar/gvar_slice.go @@ -8,22 +8,22 @@ package gvar import "github.com/gogf/gf/util/gconv" -// Ints converts and returns <v> as []int. +// Ints converts and returns `v` as []int. func (v *Var) Ints() []int { return gconv.Ints(v.Val()) } -// Int64s converts and returns <v> as []int64. +// Int64s converts and returns `v` as []int64. func (v *Var) Int64s() []int64 { return gconv.Int64s(v.Val()) } -// Uints converts and returns <v> as []uint. +// Uints converts and returns `v` as []uint. func (v *Var) Uints() []uint { return gconv.Uints(v.Val()) } -// Uint64s converts and returns <v> as []uint64. +// Uint64s converts and returns `v` as []uint64. func (v *Var) Uint64s() []uint64 { return gconv.Uint64s(v.Val()) } @@ -33,22 +33,22 @@ func (v *Var) Floats() []float64 { return gconv.Floats(v.Val()) } -// Float32s converts and returns <v> as []float32. +// Float32s converts and returns `v` as []float32. func (v *Var) Float32s() []float32 { return gconv.Float32s(v.Val()) } -// Float64s converts and returns <v> as []float64. +// Float64s converts and returns `v` as []float64. func (v *Var) Float64s() []float64 { return gconv.Float64s(v.Val()) } -// Strings converts and returns <v> as []string. +// Strings converts and returns `v` as []string. func (v *Var) Strings() []string { return gconv.Strings(v.Val()) } -// Interfaces converts and returns <v> as []interfaces{}. +// Interfaces converts and returns `v` as []interfaces{}. func (v *Var) Interfaces() []interface{} { return gconv.Interfaces(v.Val()) } @@ -63,7 +63,7 @@ func (v *Var) Array() []interface{} { return v.Interfaces() } -// Vars converts and returns <v> as []Var. +// Vars converts and returns `v` as []Var. func (v *Var) Vars() []*Var { array := gconv.Interfaces(v.Val()) if len(array) == 0 { diff --git a/container/gvar/gvar_struct.go b/container/gvar/gvar_struct.go index 2300a46ab..26814fcd8 100644 --- a/container/gvar/gvar_struct.go +++ b/container/gvar/gvar_struct.go @@ -10,44 +10,48 @@ import ( "github.com/gogf/gf/util/gconv" ) -// Struct maps value of <v> to <pointer>. -// The parameter <pointer> should be a pointer to a struct instance. -// The parameter <mapping> is used to specify the key-to-attribute mapping rules. +// Struct maps value of `v` to `pointer`. +// The parameter `pointer` should be a pointer to a struct instance. +// The parameter `mapping` is used to specify the key-to-attribute mapping rules. func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error { return gconv.Struct(v.Val(), pointer, mapping...) } -// Struct maps value of <v> to <pointer> recursively. -// The parameter <pointer> should be a pointer to a struct instance. -// The parameter <mapping> is used to specify the key-to-attribute mapping rules. +// StructDeep maps value of `v` to `pointer` recursively. +// The parameter `pointer` should be a pointer to a struct instance. +// The parameter `mapping` is used to specify the key-to-attribute mapping rules. // Deprecated, use Struct instead. func (v *Var) StructDeep(pointer interface{}, mapping ...map[string]string) error { return gconv.StructDeep(v.Val(), pointer, mapping...) } -// Structs converts and returns <v> as given struct slice. +// Structs converts and returns `v` as given struct slice. func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) error { return gconv.Structs(v.Val(), pointer, mapping...) } -// StructsDeep converts and returns <v> as given struct slice recursively. +// StructsDeep converts and returns `v` as given struct slice recursively. // Deprecated, use Struct instead. func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) error { return gconv.StructsDeep(v.Val(), pointer, mapping...) } // Scan automatically calls Struct or Structs function according to the type of parameter -// <pointer> to implement the converting. -// It calls function Struct if <pointer> is type of *struct/**struct to do the converting. -// It calls function Structs if <pointer> is type of *[]struct/*[]*struct to do the converting. +// `pointer` to implement the converting. +// +// It calls function Struct if `pointer` is type of *struct/**struct to do the converting. +// It calls function Structs if `pointer` is type of *[]struct/*[]*struct to do the converting. func (v *Var) Scan(pointer interface{}, mapping ...map[string]string) error { return gconv.Scan(v.Val(), pointer, mapping...) } // ScanDeep automatically calls StructDeep or StructsDeep function according to the type of -// parameter <pointer> to implement the converting. -// It calls function StructDeep if <pointer> is type of *struct/**struct to do the converting. -// It calls function StructsDeep if <pointer> is type of *[]struct/*[]*struct to do the converting. +// parameter `pointer` to implement the converting. +// +// It calls function StructDeep if `pointer` is type of *struct/**struct to do the converting. +// It calls function StructsDeep if `pointer` is type of *[]struct/*[]*struct to do the converting. +// +// Deprecated, use Scan instead. func (v *Var) ScanDeep(pointer interface{}, mapping ...map[string]string) error { return gconv.ScanDeep(v.Val(), pointer, mapping...) } diff --git a/os/gsession/gsession_manager.go b/os/gsession/gsession_manager.go index 7c69c6fe4..81b308d52 100644 --- a/os/gsession/gsession_manager.go +++ b/os/gsession/gsession_manager.go @@ -40,7 +40,7 @@ func New(ttl time.Duration, storage ...Storage) *Manager { } // New creates or fetches the session for given session id. -// The parameter <sessionId> is optional, it creates a new one if not it's passed +// The parameter `sessionId` is optional, it creates a new one if not it's passed // depending on Storage.New. func (m *Manager) New(ctx context.Context, sessionId ...string) *Session { var id string diff --git a/os/gsession/gsession_session.go b/os/gsession/gsession_session.go index 78cdb328f..7dcab726f 100644 --- a/os/gsession/gsession_session.go +++ b/os/gsession/gsession_session.go @@ -15,7 +15,6 @@ import ( "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/os/gtime" - "github.com/gogf/gf/util/gconv" ) // Session struct for storing single session data, which is bound to a single request. @@ -40,8 +39,8 @@ func (s *Session) init() { if s.start { return } + var err error if s.id != "" { - var err error // Retrieve memory session data from manager. if r, _ := s.manager.sessionData.Get(s.id); r != nil { s.data = r.(*gmap.StrAnyMap) @@ -49,8 +48,9 @@ func (s *Session) init() { } // Retrieve stored session data from storage. if s.manager.storage != nil { - if s.data, err = s.manager.storage.GetSession(s.ctx, s.id, s.manager.ttl, s.data); err != nil { + if s.data, err = s.manager.storage.GetSession(s.ctx, s.id, s.manager.ttl, s.data); err != nil && err != ErrorDisabled { intlog.Errorf(s.ctx, "session restoring failed for id '%s': %v", s.id, err) + panic(err) } } } @@ -60,7 +60,11 @@ func (s *Session) init() { } // Use default session id creating function of storage. if s.id == "" { - s.id = s.manager.storage.New(s.ctx, s.manager.ttl) + s.id, err = s.manager.storage.New(s.ctx, s.manager.ttl) + if err != nil && err != ErrorDisabled { + intlog.Errorf(s.ctx, "create session id failed: %v", err) + panic(err) + } } // Use default session id creating function. if s.id == "" { @@ -70,6 +74,7 @@ func (s *Session) init() { s.data = gmap.NewStrAnyMap(true) } s.start = true + } // Close closes current session and updates its ttl in the session manager. @@ -172,7 +177,7 @@ func (s *Session) RemoveAll() error { } // Id returns the session id for this session. -// It create and returns a new session id if the session id is not passed in initialization. +// It creates and returns a new session id if the session id is not passed in initialization. func (s *Session) Id() string { s.init() return s.id @@ -203,7 +208,11 @@ func (s *Session) SetIdFunc(f func(ttl time.Duration) string) error { func (s *Session) Map() map[string]interface{} { if s.id != "" { s.init() - if data := s.manager.storage.GetMap(s.ctx, s.id); data != nil { + data, err := s.manager.storage.GetMap(s.ctx, s.id) + if err != nil && err != ErrorDisabled { + intlog.Error(s.ctx, err) + } + if data != nil { return data } return s.data.Map() @@ -215,7 +224,11 @@ func (s *Session) Map() map[string]interface{} { func (s *Session) Size() int { if s.id != "" { s.init() - if size := s.manager.storage.GetSize(s.ctx, s.id); size >= 0 { + size, err := s.manager.storage.GetSize(s.ctx, s.id) + if err != nil && err != ErrorDisabled { + intlog.Error(s.ctx, err) + } + if size >= 0 { return size } return s.data.Size() @@ -235,14 +248,18 @@ func (s *Session) IsDirty() bool { } // Get retrieves session value with given key. -// It returns <def> if the key does not exist in the session if <def> is given, -// or else it return nil. +// It returns `def` if the key does not exist in the session if `def` is given, +// or else it returns nil. func (s *Session) Get(key string, def ...interface{}) interface{} { if s.id == "" { return nil } s.init() - if v := s.manager.storage.Get(s.ctx, s.id, key); v != nil { + v, err := s.manager.storage.Get(s.ctx, s.id, key) + if err != nil && err != ErrorDisabled { + intlog.Error(s.ctx, err) + } + if v != nil { return v } if v := s.data.Get(key); v != nil { @@ -259,113 +276,113 @@ func (s *Session) GetVar(key string, def ...interface{}) *gvar.Var { } func (s *Session) GetString(key string, def ...interface{}) string { - return gconv.String(s.Get(key, def...)) + return s.GetVar(key, def...).String() } func (s *Session) GetBool(key string, def ...interface{}) bool { - return gconv.Bool(s.Get(key, def...)) + return s.GetVar(key, def...).Bool() } func (s *Session) GetInt(key string, def ...interface{}) int { - return gconv.Int(s.Get(key, def...)) + return s.GetVar(key, def...).Int() } func (s *Session) GetInt8(key string, def ...interface{}) int8 { - return gconv.Int8(s.Get(key, def...)) + return s.GetVar(key, def...).Int8() } func (s *Session) GetInt16(key string, def ...interface{}) int16 { - return gconv.Int16(s.Get(key, def...)) + return s.GetVar(key, def...).Int16() } func (s *Session) GetInt32(key string, def ...interface{}) int32 { - return gconv.Int32(s.Get(key, def...)) + return s.GetVar(key, def...).Int32() } func (s *Session) GetInt64(key string, def ...interface{}) int64 { - return gconv.Int64(s.Get(key, def...)) + return s.GetVar(key, def...).Int64() } func (s *Session) GetUint(key string, def ...interface{}) uint { - return gconv.Uint(s.Get(key, def...)) + return s.GetVar(key, def...).Uint() } func (s *Session) GetUint8(key string, def ...interface{}) uint8 { - return gconv.Uint8(s.Get(key, def...)) + return s.GetVar(key, def...).Uint8() } func (s *Session) GetUint16(key string, def ...interface{}) uint16 { - return gconv.Uint16(s.Get(key, def...)) + return s.GetVar(key, def...).Uint16() } func (s *Session) GetUint32(key string, def ...interface{}) uint32 { - return gconv.Uint32(s.Get(key, def...)) + return s.GetVar(key, def...).Uint32() } func (s *Session) GetUint64(key string, def ...interface{}) uint64 { - return gconv.Uint64(s.Get(key, def...)) + return s.GetVar(key, def...).Uint64() } func (s *Session) GetFloat32(key string, def ...interface{}) float32 { - return gconv.Float32(s.Get(key, def...)) + return s.GetVar(key, def...).Float32() } func (s *Session) GetFloat64(key string, def ...interface{}) float64 { - return gconv.Float64(s.Get(key, def...)) + return s.GetVar(key, def...).Float64() } func (s *Session) GetBytes(key string, def ...interface{}) []byte { - return gconv.Bytes(s.Get(key, def...)) + return s.GetVar(key, def...).Bytes() } func (s *Session) GetInts(key string, def ...interface{}) []int { - return gconv.Ints(s.Get(key, def...)) + return s.GetVar(key, def...).Ints() } func (s *Session) GetFloats(key string, def ...interface{}) []float64 { - return gconv.Floats(s.Get(key, def...)) + return s.GetVar(key, def...).Floats() } func (s *Session) GetStrings(key string, def ...interface{}) []string { - return gconv.Strings(s.Get(key, def...)) + return s.GetVar(key, def...).Strings() } func (s *Session) GetInterfaces(key string, def ...interface{}) []interface{} { - return gconv.Interfaces(s.Get(key, def...)) + return s.GetVar(key, def...).Interfaces() } func (s *Session) GetTime(key string, format ...string) time.Time { - return gconv.Time(s.Get(key), format...) + return s.GetVar(key).Time(format...) } func (s *Session) GetGTime(key string, format ...string) *gtime.Time { - return gconv.GTime(s.Get(key), format...) + return s.GetVar(key).GTime(format...) } func (s *Session) GetDuration(key string, def ...interface{}) time.Duration { - return gconv.Duration(s.Get(key, def...)) + return s.GetVar(key, def...).Duration() } func (s *Session) GetMap(key string, tags ...string) map[string]interface{} { - return gconv.Map(s.Get(key), tags...) + return s.GetVar(key).Map(tags...) } func (s *Session) GetMapDeep(key string, tags ...string) map[string]interface{} { - return gconv.MapDeep(s.Get(key), tags...) + return s.GetVar(key).MapDeep(tags...) } func (s *Session) GetMaps(key string, tags ...string) []map[string]interface{} { - return gconv.Maps(s.Get(key), tags...) + return s.GetVar(key).Maps(tags...) } func (s *Session) GetMapsDeep(key string, tags ...string) []map[string]interface{} { - return gconv.MapsDeep(s.Get(key), tags...) + return s.GetVar(key).MapsDeep(tags...) } func (s *Session) GetStruct(key string, pointer interface{}, mapping ...map[string]string) error { - return gconv.Struct(s.Get(key), pointer, mapping...) + return s.GetVar(key).Struct(pointer, mapping...) } func (s *Session) GetStructs(key string, pointer interface{}, mapping ...map[string]string) error { - return gconv.Structs(s.Get(key), pointer, mapping...) + return s.GetVar(key).Structs(pointer, mapping...) } diff --git a/os/gsession/gsession_storage.go b/os/gsession/gsession_storage.go index 87ef2a669..db5f066dc 100644 --- a/os/gsession/gsession_storage.go +++ b/os/gsession/gsession_storage.go @@ -16,24 +16,24 @@ import ( type Storage interface { // New creates a custom session id. // This function can be used for custom session creation. - New(ctx context.Context, ttl time.Duration) (id string) + New(ctx context.Context, ttl time.Duration) (id string, err error) // Get retrieves and returns session value with given key. // It returns nil if the key does not exist in the session. - Get(ctx context.Context, id string, key string) interface{} + Get(ctx context.Context, id string, key string) (value interface{}, err error) // GetMap retrieves all key-value pairs as map from storage. - GetMap(ctx context.Context, id string) map[string]interface{} + GetMap(ctx context.Context, id string) (data map[string]interface{}, err error) // GetSize retrieves and returns the size of key-value pairs from storage. - GetSize(ctx context.Context, id string) int + GetSize(ctx context.Context, id string) (size int, err error) // Set sets one key-value session pair to the storage. - // The parameter <ttl> specifies the TTL for the session id. + // The parameter `ttl` specifies the TTL for the session id. Set(ctx context.Context, id string, key string, value interface{}, ttl time.Duration) error // SetMap batch sets key-value session pairs as map to the storage. - // The parameter <ttl> specifies the TTL for the session id. + // The parameter `ttl` specifies the TTL for the session id. SetMap(ctx context.Context, id string, data map[string]interface{}, ttl time.Duration) error // Remove deletes key with its value from storage. @@ -42,10 +42,10 @@ type Storage interface { // RemoveAll deletes all key-value pairs from storage. RemoveAll(ctx context.Context, id string) error - // GetSession returns the session data as *gmap.StrAnyMap for given session id from storage. + // GetSession returns the session data as `*gmap.StrAnyMap` for given session id from storage. // - // The parameter <ttl> specifies the TTL for this session. - // The parameter <data> is the current old session data stored in memory, + // The parameter `ttl` specifies the TTL for this session. + // The parameter `data` is the current old session data stored in memory, // and for some storage it might be nil if memory storage is disabled. // // This function is called ever when session starts. It returns nil if the TTL is exceeded. diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index 98628c301..641ad9912 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -105,34 +105,34 @@ func (s *StorageFile) sessionFilePath(id string) string { // New creates a session id. // This function can be used for custom session creation. -func (s *StorageFile) New(ctx context.Context, ttl time.Duration) (id string) { - return "" +func (s *StorageFile) New(ctx context.Context, ttl time.Duration) (id string, err error) { + return "", ErrorDisabled } // Get retrieves session value with given key. // It returns nil if the key does not exist in the session. -func (s *StorageFile) Get(ctx context.Context, id string, key string) interface{} { - return nil +func (s *StorageFile) Get(ctx context.Context, id string, key string) (value interface{}, err error) { + return nil, ErrorDisabled } // GetMap retrieves all key-value pairs as map from storage. -func (s *StorageFile) GetMap(ctx context.Context, id string) map[string]interface{} { - return nil +func (s *StorageFile) GetMap(ctx context.Context, id string) (data map[string]interface{}, err error) { + return nil, ErrorDisabled } // GetSize retrieves the size of key-value pairs from storage. -func (s *StorageFile) GetSize(ctx context.Context, id string) int { - return -1 +func (s *StorageFile) GetSize(ctx context.Context, id string) (size int, err error) { + return -1, ErrorDisabled } // Set sets key-value session pair to the storage. -// The parameter <ttl> specifies the TTL for the session id (not for the key-value pair). +// The parameter `ttl` specifies the TTL for the session id (not for the key-value pair). func (s *StorageFile) Set(ctx context.Context, id string, key string, value interface{}, ttl time.Duration) error { return ErrorDisabled } // SetMap batch sets key-value session pairs with map to the storage. -// The parameter <ttl> specifies the TTL for the session id(not for the key-value pair). +// The parameter `ttl` specifies the TTL for the session id(not for the key-value pair). func (s *StorageFile) SetMap(ctx context.Context, id string, data map[string]interface{}, ttl time.Duration) error { return ErrorDisabled } @@ -149,8 +149,8 @@ func (s *StorageFile) RemoveAll(ctx context.Context, id string) error { // GetSession returns the session data as *gmap.StrAnyMap for given session id from storage. // -// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded. -// The parameter <data> is the current old session data stored in memory, +// The parameter `ttl` specifies the TTL for this session, and it returns nil if the TTL is exceeded. +// The parameter `data` is the current old session data stored in memory, // and for some storage it might be nil if memory storage is disabled. // // This function is called ever when session starts. diff --git a/os/gsession/gsession_storage_memory.go b/os/gsession/gsession_storage_memory.go index 26f217b92..32175fe48 100644 --- a/os/gsession/gsession_storage_memory.go +++ b/os/gsession/gsession_storage_memory.go @@ -22,34 +22,34 @@ func NewStorageMemory() *StorageMemory { // New creates a session id. // This function can be used for custom session creation. -func (s *StorageMemory) New(ctx context.Context, ttl time.Duration) (id string) { - return "" +func (s *StorageMemory) New(ctx context.Context, ttl time.Duration) (id string, err error) { + return "", ErrorDisabled } // Get retrieves session value with given key. // It returns nil if the key does not exist in the session. -func (s *StorageMemory) Get(ctx context.Context, id string, key string) interface{} { - return nil +func (s *StorageMemory) Get(ctx context.Context, id string, key string) (value interface{}, err error) { + return nil, ErrorDisabled } // GetMap retrieves all key-value pairs as map from storage. -func (s *StorageMemory) GetMap(ctx context.Context, id string) map[string]interface{} { - return nil +func (s *StorageMemory) GetMap(ctx context.Context, id string) (data map[string]interface{}, err error) { + return nil, ErrorDisabled } // GetSize retrieves the size of key-value pairs from storage. -func (s *StorageMemory) GetSize(ctx context.Context, id string) int { - return -1 +func (s *StorageMemory) GetSize(ctx context.Context, id string) (size int, err error) { + return -1, ErrorDisabled } // Set sets key-value session pair to the storage. -// The parameter <ttl> specifies the TTL for the session id (not for the key-value pair). +// The parameter `ttl` specifies the TTL for the session id (not for the key-value pair). func (s *StorageMemory) Set(ctx context.Context, id string, key string, value interface{}, ttl time.Duration) error { return ErrorDisabled } // SetMap batch sets key-value session pairs with map to the storage. -// The parameter <ttl> specifies the TTL for the session id(not for the key-value pair). +// The parameter `ttl` specifies the TTL for the session id(not for the key-value pair). func (s *StorageMemory) SetMap(ctx context.Context, id string, data map[string]interface{}, ttl time.Duration) error { return ErrorDisabled } @@ -66,8 +66,8 @@ func (s *StorageMemory) RemoveAll(ctx context.Context, id string) error { // GetSession returns the session data as *gmap.StrAnyMap for given session id from storage. // -// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded. -// The parameter <data> is the current old session data stored in memory, +// The parameter `ttl` specifies the TTL for this session, and it returns nil if the TTL is exceeded. +// The parameter `data` is the current old session data stored in memory, // and for some storage it might be nil if memory storage is disabled. // // This function is called ever when session starts. diff --git a/os/gsession/gsession_storage_redis.go b/os/gsession/gsession_storage_redis.go index a8b8016af..0082093b6 100644 --- a/os/gsession/gsession_storage_redis.go +++ b/os/gsession/gsession_storage_redis.go @@ -67,34 +67,34 @@ func NewStorageRedis(redis *gredis.Redis, prefix ...string) *StorageRedis { // New creates a session id. // This function can be used for custom session creation. -func (s *StorageRedis) New(ctx context.Context, ttl time.Duration) (id string) { - return "" +func (s *StorageRedis) New(ctx context.Context, ttl time.Duration) (id string, err error) { + return "", ErrorDisabled } // Get retrieves session value with given key. // It returns nil if the key does not exist in the session. -func (s *StorageRedis) Get(ctx context.Context, id string, key string) interface{} { - return nil +func (s *StorageRedis) Get(ctx context.Context, id string, key string) (value interface{}, err error) { + return nil, ErrorDisabled } // GetMap retrieves all key-value pairs as map from storage. -func (s *StorageRedis) GetMap(ctx context.Context, id string) map[string]interface{} { - return nil +func (s *StorageRedis) GetMap(ctx context.Context, id string) (data map[string]interface{}, err error) { + return nil, ErrorDisabled } // GetSize retrieves the size of key-value pairs from storage. -func (s *StorageRedis) GetSize(ctx context.Context, id string) int { - return -1 +func (s *StorageRedis) GetSize(ctx context.Context, id string) (size int, err error) { + return -1, ErrorDisabled } // Set sets key-value session pair to the storage. -// The parameter <ttl> specifies the TTL for the session id (not for the key-value pair). +// The parameter `ttl` specifies the TTL for the session id (not for the key-value pair). func (s *StorageRedis) Set(ctx context.Context, id string, key string, value interface{}, ttl time.Duration) error { return ErrorDisabled } // SetMap batch sets key-value session pairs with map to the storage. -// The parameter <ttl> specifies the TTL for the session id(not for the key-value pair). +// The parameter `ttl` specifies the TTL for the session id(not for the key-value pair). func (s *StorageRedis) SetMap(ctx context.Context, id string, data map[string]interface{}, ttl time.Duration) error { return ErrorDisabled } @@ -111,8 +111,8 @@ func (s *StorageRedis) RemoveAll(ctx context.Context, id string) error { // GetSession returns the session data as *gmap.StrAnyMap for given session id from storage. // -// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded. -// The parameter <data> is the current old session data stored in memory, +// The parameter `ttl` specifies the TTL for this session, and it returns nil if the TTL is exceeded. +// The parameter `data` is the current old session data stored in memory, // and for some storage it might be nil if memory storage is disabled. // // This function is called ever when session starts. diff --git a/os/gsession/gsession_storage_redis_hashtable.go b/os/gsession/gsession_storage_redis_hashtable.go index 352e5f8b7..c2a6ba195 100644 --- a/os/gsession/gsession_storage_redis_hashtable.go +++ b/os/gsession/gsession_storage_redis_hashtable.go @@ -39,53 +39,56 @@ func NewStorageRedisHashTable(redis *gredis.Redis, prefix ...string) *StorageRed // New creates a session id. // This function can be used for custom session creation. -func (s *StorageRedisHashTable) New(ctx context.Context, ttl time.Duration) (id string) { - return "" +func (s *StorageRedisHashTable) New(ctx context.Context, ttl time.Duration) (id string, err error) { + return "", ErrorDisabled } // Get retrieves session value with given key. // It returns nil if the key does not exist in the session. -func (s *StorageRedisHashTable) Get(ctx context.Context, id string, key string) interface{} { - r, _ := s.redis.Ctx(ctx).Do("HGET", s.key(id), key) - if r != nil { - return gconv.String(r) +func (s *StorageRedisHashTable) Get(ctx context.Context, id string, key string) (value interface{}, err error) { + value, err = s.redis.Ctx(ctx).Do("HGET", s.key(id), key) + if value != nil { + value = gconv.String(value) } - return r + return } // GetMap retrieves all key-value pairs as map from storage. -func (s *StorageRedisHashTable) GetMap(ctx context.Context, id string) map[string]interface{} { +func (s *StorageRedisHashTable) GetMap(ctx context.Context, id string) (data map[string]interface{}, err error) { r, err := s.redis.Ctx(ctx).DoVar("HGETALL", s.key(id)) if err != nil { - return nil + return nil, err } + data = make(map[string]interface{}) array := r.Interfaces() - m := make(map[string]interface{}) for i := 0; i < len(array); i += 2 { if array[i+1] != nil { - m[gconv.String(array[i])] = gconv.String(array[i+1]) + data[gconv.String(array[i])] = gconv.String(array[i+1]) } else { - m[gconv.String(array[i])] = array[i+1] + data[gconv.String(array[i])] = array[i+1] } } - return m + return data, nil } // GetSize retrieves the size of key-value pairs from storage. -func (s *StorageRedisHashTable) GetSize(ctx context.Context, id string) int { - r, _ := s.redis.Ctx(ctx).DoVar("HLEN", s.key(id)) - return r.Int() +func (s *StorageRedisHashTable) GetSize(ctx context.Context, id string) (size int, err error) { + r, err := s.redis.Ctx(ctx).DoVar("HLEN", s.key(id)) + if err != nil { + return -1, err + } + return r.Int(), nil } // Set sets key-value session pair to the storage. -// The parameter <ttl> specifies the TTL for the session id (not for the key-value pair). +// The parameter `ttl` specifies the TTL for the session id (not for the key-value pair). func (s *StorageRedisHashTable) Set(ctx context.Context, id string, key string, value interface{}, ttl time.Duration) error { _, err := s.redis.Ctx(ctx).Do("HSET", s.key(id), key, value) return err } // SetMap batch sets key-value session pairs with map to the storage. -// The parameter <ttl> specifies the TTL for the session id(not for the key-value pair). +// The parameter `ttl` specifies the TTL for the session id(not for the key-value pair). func (s *StorageRedisHashTable) SetMap(ctx context.Context, id string, data map[string]interface{}, ttl time.Duration) error { array := make([]interface{}, len(data)*2+1) array[0] = s.key(id) @@ -114,8 +117,8 @@ func (s *StorageRedisHashTable) RemoveAll(ctx context.Context, id string) error // GetSession returns the session data as *gmap.StrAnyMap for given session id from storage. // -// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded. -// The parameter <data> is the current old session data stored in memory, +// The parameter `ttl` specifies the TTL for this session, and it returns nil if the TTL is exceeded. +// The parameter `data` is the current old session data stored in memory, // and for some storage it might be nil if memory storage is disabled. // // This function is called ever when session starts. diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 125bb1037..7ad6f240a 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -13,6 +13,7 @@ import ( // Scan automatically calls MapToMap, MapToMaps, Struct or Structs function according to // the type of parameter `pointer` to implement the converting. +// // It calls function MapToMap if `pointer` is type of *map to do the converting. // It calls function MapToMaps if `pointer` is type of *[]map/*[]*map to do the converting. // It calls function Struct if `pointer` is type of *struct/**struct to do the converting. @@ -52,7 +53,8 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) } // ScanDeep automatically calls StructDeep or StructsDeep function according to the type of -// parameter `pointer` to implement the converting.. +// parameter `pointer` to implement the converting. +// // It calls function StructDeep if `pointer` is type of *struct/**struct to do the converting. // It calls function StructsDeep if `pointer` is type of *[]struct/*[]*struct to do the converting. // Deprecated, use Scan instead. From be2fcf7f57db4d527749325ba6272d2161a87397 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 17 Aug 2021 16:30:47 +0800 Subject: [PATCH 461/492] add UnmarshalJSON interface support for package gconv --- util/gconv/gconv_interface.go | 6 ++++++ util/gconv/gconv_struct.go | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/util/gconv/gconv_interface.go b/util/gconv/gconv_interface.go index cce763cc0..9c23f9425 100644 --- a/util/gconv/gconv_interface.go +++ b/util/gconv/gconv_interface.go @@ -90,6 +90,12 @@ type apiUnmarshalText interface { UnmarshalText(text []byte) error } +// apiUnmarshalText is the interface for custom defined types customizing value assignment. +// Note that only pointer can implement interface apiUnmarshalJSON. +type apiUnmarshalJSON interface { + UnmarshalJSON(b []byte) error +} + // apiSet is the interface for custom value assignment. type apiSet interface { Set(value interface{}) (old interface{}) diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 78649056c..76ee3abb9 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -355,6 +355,15 @@ func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value i return v.UnmarshalText(b), ok } } + // UnmarshalJSON. + if v, ok := pointer.(apiUnmarshalJSON); ok { + if s, ok := value.(string); ok { + return v.UnmarshalJSON([]byte(s)), ok + } + if b, ok := value.([]byte); ok { + return v.UnmarshalJSON(b), ok + } + } if v, ok := pointer.(apiSet); ok { v.Set(value) return nil, ok From 457d552fa08a6aa072251baf52b334f6d791079e Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 17 Aug 2021 16:51:29 +0800 Subject: [PATCH 462/492] add UnmarshalJSON interface support for package gconv --- go.mod | 14 +++++--- go.sum | 45 +++++++++++++++----------- util/gconv/gconv_struct.go | 31 +++++++++++++----- util/gconv/gconv_z_unit_struct_test.go | 39 ++++++++++++++++++++++ 4 files changed, 98 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 4aa3a6f89..d07ddc00e 100644 --- a/go.mod +++ b/go.mod @@ -5,18 +5,24 @@ go 1.14 require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.12.0 github.com/fsnotify/fsnotify v1.4.9 github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 github.com/gomodule/redigo v2.0.0+incompatible - github.com/gorilla/websocket v1.4.1 + github.com/gorilla/websocket v1.4.2 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf + github.com/kr/text v0.2.0 // indirect github.com/mattn/go-runewidth v0.0.10 // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/olekukonko/tablewriter v0.0.5 go.opentelemetry.io/otel v1.0.0-RC2 go.opentelemetry.io/otel/oteltest v1.0.0-RC2 go.opentelemetry.io/otel/trace v1.0.0-RC2 - golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 - golang.org/x/text v0.3.4 - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c + golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 + golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect + golang.org/x/text v0.3.6 + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/go.sum b/go.sum index 21565402e..506697682 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,10 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4= github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +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/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= @@ -14,10 +16,14 @@ github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNu github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +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/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= @@ -25,6 +31,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -40,26 +48,25 @@ go.opentelemetry.io/otel/oteltest v1.0.0-RC2 h1:xNKqMhlZYkASSyvF4JwObZFMq0jhFN3c go.opentelemetry.io/otel/oteltest v1.0.0-RC2/go.mod h1:kiQ4tw5tAL4JLTbcOYwK1CWI1HkT5aiLzHovgOVnz/A= go.opentelemetry.io/otel/trace v1.0.0-RC2 h1:dunAP0qDULMIT82atj34m5RgvsIK6LcsXf1c/MsYg1w= go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -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/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE= +golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/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-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -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.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 76ee3abb9..07b115daf 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -348,20 +348,35 @@ func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value i } // UnmarshalText. if v, ok := pointer.(apiUnmarshalText); ok { - if s, ok := value.(string); ok { - return v.UnmarshalText([]byte(s)), ok - } + var valueBytes []byte if b, ok := value.([]byte); ok { - return v.UnmarshalText(b), ok + valueBytes = b + } else if s, ok := value.(string); ok { + valueBytes = []byte(s) + } + if len(valueBytes) > 0 { + return v.UnmarshalText(valueBytes), ok } } // UnmarshalJSON. if v, ok := pointer.(apiUnmarshalJSON); ok { - if s, ok := value.(string); ok { - return v.UnmarshalJSON([]byte(s)), ok - } + var valueBytes []byte if b, ok := value.([]byte); ok { - return v.UnmarshalJSON(b), ok + valueBytes = b + } else if s, ok := value.(string); ok { + valueBytes = []byte(s) + } + + if len(valueBytes) > 0 { + // If it is not a valid JSON string, it then adds char `"` on its both sides to make it is. + if !json.Valid(valueBytes) { + newValueBytes := make([]byte, len(valueBytes)+2) + newValueBytes[0] = '"' + newValueBytes[len(newValueBytes)-1] = '"' + copy(newValueBytes[1:], valueBytes) + valueBytes = newValueBytes + } + return v.UnmarshalJSON(valueBytes), ok } } if v, ok := pointer.(apiSet); ok { diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index faa79d921..94c920a43 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -360,6 +360,45 @@ func Test_Struct_Attr_CustomType2(t *testing.T) { }) } +// From: k8s.io/apimachinery@v0.22.0/pkg/apis/meta/v1/duration.go +type MyDuration struct { + time.Duration +} + +// UnmarshalJSON implements the json.Unmarshaller interface. +func (d *MyDuration) UnmarshalJSON(b []byte) error { + var str string + err := json.Unmarshal(b, &str) + if err != nil { + return err + } + + pd, err := time.ParseDuration(str) + if err != nil { + return err + } + d.Duration = pd + return nil +} + +func Test_Struct_Attr_CustomType3(t *testing.T) { + type Config struct { + D MyDuration + } + gtest.C(t, func(t *gtest.T) { + config := new(Config) + err := gconv.Struct(g.Map{"d": "15s"}, config) + t.AssertNil(err) + t.Assert(config.D, "15s") + }) + gtest.C(t, func(t *gtest.T) { + config := new(Config) + err := gconv.Struct(g.Map{"d": `"15s"`}, config) + t.AssertNil(err) + t.Assert(config.D, "15s") + }) +} + func Test_Struct_PrivateAttribute(t *testing.T) { type User struct { Id int From f03f56ba4e544c9ea3e46e25a4dbfdeba3637023 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 19 Aug 2021 11:28:25 +0800 Subject: [PATCH 463/492] improve gconv.Struct for map attribute converting --- database/gdb/gdb_func.go | 1 + net/ghttp/ghttp_server_router_group.go | 4 +-- os/gcron/gcron_entry.go | 7 ++-- util/gconv/gconv_maptomap.go | 14 +++++--- util/gconv/gconv_scan.go | 45 ++++++++++++++++-------- util/gconv/gconv_struct.go | 9 +++-- util/gconv/gconv_structs.go | 7 ++-- util/gconv/gconv_z_unit_maptomap_test.go | 2 +- util/gconv/gconv_z_unit_struct_test.go | 25 +++++++++++++ 9 files changed, 77 insertions(+), 37 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 0a8a28cab..da566d043 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -548,6 +548,7 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa break } // Automatically mapping and filtering the struct attribute. + // TODO struct fields in sequence data := DataToMapDeep(in.Where) if in.Table != "" { data, _ = db.GetCore().mappingAndFilterData(in.Schema, in.Table, data, true) diff --git a/net/ghttp/ghttp_server_router_group.go b/net/ghttp/ghttp_server_router_group.go index 0ebebfa15..cf952a81c 100644 --- a/net/ghttp/ghttp_server_router_group.go +++ b/net/ghttp/ghttp_server_router_group.go @@ -273,7 +273,7 @@ func (g *RouterGroup) getPrefix() string { return prefix } -// doBindRoutersToServer does really registering for the group. +// doBindRoutersToServer does really register for the group. func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { var ( bindType = item.bindType @@ -289,7 +289,7 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { if err != nil { g.server.Logger().Fatalf("invalid pattern: %s", pattern) } - // If there'a already a domain, unset the domain field in the pattern. + // If there is already a domain, unset the domain field in the pattern. if g.domain != nil { domain = "" } diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index 0c4952faf..0f6ba3549 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -54,9 +54,8 @@ func (c *Cron) addEntry(pattern string, job func(), singleton bool, name ...stri } // When you add a scheduled task, you cannot allow it to run. // It cannot start running when added to gtimer. - // It should start running after the entry is added to the entries map, - // to avoid the task from running during adding where the entries - // does not have the entry information, which might cause panic. + // It should start running after the entry is added to the Cron entries map, to avoid the task + // from running during adding where the entries do not have the entry information, which might cause panic. entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, -1, gtimer.StatusStopped) c.entries.Set(entry.Name, entry) entry.entry.Start() @@ -70,7 +69,7 @@ func (entry *Entry) IsSingleton() bool { // SetSingleton sets the entry running in singleton mode. func (entry *Entry) SetSingleton(enabled bool) { - entry.entry.SetSingleton(true) + entry.entry.SetSingleton(enabled) } // SetTimes sets the times which the entry can run. diff --git a/util/gconv/gconv_maptomap.go b/util/gconv/gconv_maptomap.go index e61aad53e..22524ba9e 100644 --- a/util/gconv/gconv_maptomap.go +++ b/util/gconv/gconv_maptomap.go @@ -22,10 +22,10 @@ func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]str // doMapToMap converts any map type variable `params` to another map type variable `pointer`. // // The parameter `params` can be any type of map, like: -// map[string]string, map[string]struct, , map[string]*struct, etc. +// map[string]string, map[string]struct, map[string]*struct, etc. // // The parameter `pointer` should be type of *map, like: -// map[int]string, map[string]struct, , map[string]*struct, etc. +// map[int]string, map[string]struct, map[string]*struct, etc. // // The optional parameter `mapping` is used for struct attribute to map key mapping, which makes // sense only if the items of original map `params` is type struct. @@ -54,9 +54,13 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s } } var ( - paramsRv reflect.Value - paramsKind reflect.Kind + paramsRv reflect.Value + paramsKind reflect.Kind + keyToAttributeNameMapping map[string]string ) + if len(mapping) > 0 { + keyToAttributeNameMapping = mapping[0] + } if v, ok := params.(reflect.Value); ok { paramsRv = v } else { @@ -113,7 +117,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s e := reflect.New(pointerValueType).Elem() switch pointerValueKind { case reflect.Map, reflect.Struct: - if err = Struct(paramsRv.MapIndex(key).Interface(), e, mapping...); err != nil { + if err = doStruct(paramsRv.MapIndex(key).Interface(), e, keyToAttributeNameMapping, ""); err != nil { return err } default: diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 7ad6f240a..bb5af1f47 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -11,28 +11,41 @@ import ( "reflect" ) -// Scan automatically calls MapToMap, MapToMaps, Struct or Structs function according to -// the type of parameter `pointer` to implement the converting. +// Scan automatically checks the type of `pointer` and converts `params` to `pointer`. It supports `pointer` +// with type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting. // -// It calls function MapToMap if `pointer` is type of *map to do the converting. -// It calls function MapToMaps if `pointer` is type of *[]map/*[]*map to do the converting. -// It calls function Struct if `pointer` is type of *struct/**struct to do the converting. -// It calls function Structs if `pointer` is type of *[]struct/*[]*struct to do the converting. +// It calls function `doMapToMap` internally if `pointer` is type of *map for converting. +// It calls function `doMapToMaps` internally if `pointer` is type of *[]map/*[]*map for converting. +// It calls function `doStruct` internally if `pointer` is type of *struct/**struct for converting. +// It calls function `doStructs` internally if `pointer` is type of *[]struct/*[]*struct for converting. func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { var ( - pointerType = reflect.TypeOf(pointer) - pointerKind = pointerType.Kind() + pointerType reflect.Type + pointerKind reflect.Kind ) + if v, ok := pointer.(reflect.Value); ok { + pointerType = v.Type() + } else { + pointerType = reflect.TypeOf(pointer) + } + if pointerType == nil { + return gerror.NewCode(gerror.CodeInvalidParameter, "parameter pointer should not be nil") + } + pointerKind = pointerType.Kind() if pointerKind != reflect.Ptr { - return gerror.NewCodef(gerror.CodeInvalidParameter, "params should be type of pointer, but got: %v", pointerKind) + return gerror.NewCodef(gerror.CodeInvalidParameter, "params should be type of pointer, but got type: %v", pointerKind) } var ( - pointerElem = pointerType.Elem() - pointerElemKind = pointerElem.Kind() + pointerElem = pointerType.Elem() + pointerElemKind = pointerElem.Kind() + keyToAttributeNameMapping map[string]string ) + if len(mapping) > 0 { + keyToAttributeNameMapping = mapping[0] + } switch pointerElemKind { case reflect.Map: - return MapToMap(params, pointer, mapping...) + return doMapToMap(params, pointer, mapping...) case reflect.Array, reflect.Slice: var ( @@ -44,11 +57,13 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) sliceElemKind = sliceElem.Kind() } if sliceElemKind == reflect.Map { - return MapToMaps(params, pointer, mapping...) + return doMapToMaps(params, pointer, mapping...) } - return Structs(params, pointer, mapping...) + return doStructs(params, pointer, keyToAttributeNameMapping, "") + default: - return Struct(params, pointer, mapping...) + + return doStruct(params, pointer, keyToAttributeNameMapping, "") } } diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 07b115daf..381a75d28 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -31,11 +31,7 @@ import ( // in mapping procedure to do the matching. // It ignores the map key, if it does not match. func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - var keyToAttributeNameMapping map[string]string - if len(mapping) > 0 { - keyToAttributeNameMapping = mapping[0] - } - return doStruct(params, pointer, keyToAttributeNameMapping, "") + return Scan(params, pointer, mapping...) } // StructTag acts as Struct but also with support for priority tag feature, which retrieves the @@ -405,6 +401,9 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma // Converting by kind. switch kind { + case reflect.Map: + return doMapToMap(value, structFieldValue, mapping) + case reflect.Struct: // Recursively converting for struct attribute. if err := doStruct(value, structFieldValue, nil, ""); err != nil { diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index 68b4be9ca..cf2ac64a0 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -13,12 +13,9 @@ import ( ) // Structs converts any slice to given struct slice. +// Also see Scan, Struct. func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { - var keyToAttributeNameMapping map[string]string - if len(mapping) > 0 { - keyToAttributeNameMapping = mapping[0] - } - return doStructs(params, pointer, keyToAttributeNameMapping, "") + return Scan(params, pointer, mapping...) } // StructsTag acts as Structs but also with support for priority tag feature, which retrieves the diff --git a/util/gconv/gconv_z_unit_maptomap_test.go b/util/gconv/gconv_z_unit_maptomap_test.go index cd38f80a3..838d96cd2 100644 --- a/util/gconv/gconv_z_unit_maptomap_test.go +++ b/util/gconv/gconv_z_unit_maptomap_test.go @@ -83,7 +83,7 @@ func Test_MapToMap2(t *testing.T) { gtest.C(t, func(t *gtest.T) { m := make(map[string]User) err := gconv.MapToMap(params, &m) - t.Assert(err, nil) + t.AssertNil(err) t.Assert(len(m), 1) t.Assert(m["key"].Id, 1) t.Assert(m["key"].Name, "john") diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index 94c920a43..d1d12d46d 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -1203,3 +1203,28 @@ func Test_Struct_GVarAttribute(t *testing.T) { }) } + +func Test_Struct_MapAttribute(t *testing.T) { + type NodeStatus struct { + ID int + } + type Nodes map[string]NodeStatus + type Output struct { + Nodes Nodes + } + + gtest.C(t, func(t *gtest.T) { + var ( + out = Output{} + data = g.Map{ + "nodes": g.Map{ + "name": g.Map{ + "id": 10000, + }, + }, + } + ) + err := gconv.Struct(data, &out) + t.AssertNil(err) + }) +} From 8f4ce913617d961ec91a000a9b8c93b682e22616 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 19 Aug 2021 14:09:31 +0800 Subject: [PATCH 464/492] improve condition parameter of struct by sequence for package gdb --- database/gdb/gdb_func.go | 135 ++++++++++----------------------------- util/gconv/gconv_map.go | 2 +- 2 files changed, 34 insertions(+), 103 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index da566d043..1910e9550 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -228,102 +228,26 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} { return data } -// DataToMapDeep converts `value` to map type recursively. +// DataToMapDeep converts `value` to map type recursively(if attribute struct is embedded). // The parameter `value` should be type of *map/map/*struct/struct. // It supports embedded struct definition for struct. func DataToMapDeep(value interface{}) map[string]interface{} { - if v, ok := value.(apiMapStrAny); ok { - return v.MapStrAny() - } - var ( - rvValue reflect.Value - rvField reflect.Value - rvKind reflect.Kind - rtField reflect.StructField - ) - if v, ok := value.(reflect.Value); ok { - rvValue = v - } else { - rvValue = reflect.ValueOf(value) - } - rvKind = rvValue.Kind() - if rvKind == reflect.Ptr { - rvValue = rvValue.Elem() - rvKind = rvValue.Kind() - } - // If given `value` is not a struct, it uses gconv.Map for converting. - if rvKind != reflect.Struct { - return gconv.Map(value, structTagPriority...) - } - // Struct handling. - var ( - fieldTag reflect.StructTag - rvType = rvValue.Type() - name = "" - data = make(map[string]interface{}) - ) - for i := 0; i < rvValue.NumField(); i++ { - rtField = rvType.Field(i) - rvField = rvValue.Field(i) - fieldName := rtField.Name - if !utils.IsLetterUpper(fieldName[0]) { - continue - } - // Struct attribute inherit - if rtField.Anonymous { - for k, v := range DataToMapDeep(rvField) { - data[k] = v - } - continue - } - // Other attributes. - name = "" - fieldTag = rtField.Tag - for _, tag := range structTagPriority { - if s := fieldTag.Get(tag); s != "" { - name = s - break - } - } - if name == "" { - name = fieldName - } else { - // The "orm" tag supports json tag feature: -, omitempty - // The "orm" tag would be like: "id,priority", so it should use splitting handling. - name = gstr.Trim(name) - if name == "-" { - continue - } - array := gstr.SplitAndTrim(name, ",") - if len(array) > 1 { - switch array[1] { - case "omitempty": - if empty.IsEmpty(rvField.Interface()) { - continue - } else { - name = array[0] - } - default: - name = array[0] - } - } - } - - // The underlying driver supports time.Time/*time.Time types. - fieldValue := rvField.Interface() - switch fieldValue.(type) { + m := gconv.Map(value, structTagPriority...) + for k, v := range m { + switch v.(type) { case time.Time, *time.Time, gtime.Time, *gtime.Time: - data[name] = fieldValue + m[k] = v + default: // Use string conversion in default. - if s, ok := fieldValue.(apiString); ok { - data[name] = s.String() + if s, ok := v.(apiString); ok { + m[k] = s.String() } else { - data[name] = fieldValue + m[k] = v } } } - return data + return m } // doHandleTableName adds prefix string and quote chars for the table. It handles table string like: @@ -501,15 +425,15 @@ type formatWhereInput struct { // formatWhere formats where statement and its arguments for `Where` and `Having` statements. func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interface{}) { var ( - buffer = bytes.NewBuffer(nil) - rv = reflect.ValueOf(in.Where) - kind = rv.Kind() + buffer = bytes.NewBuffer(nil) + reflectValue = reflect.ValueOf(in.Where) + reflectKind = reflectValue.Kind() ) - for kind == reflect.Ptr { - rv = rv.Elem() - kind = rv.Kind() + for reflectKind == reflect.Ptr { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() } - switch kind { + switch reflectKind { case reflect.Array, reflect.Slice: newArgs = formatWhereInterfaces(db, gconv.Interfaces(in.Where), buffer, newArgs) @@ -528,7 +452,7 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa case reflect.Struct: // If `where` struct implements apiIterator interface, - // it then uses its Iterate function to iterates its key-value pairs. + // it then uses its Iterate function to iterate its key-value pairs. // For example, ListMap and TreeMap are ordered map, // which implement apiIterator interface and are index-friendly for where conditions. if iterator, ok := in.Where.(apiIterator); ok { @@ -548,19 +472,26 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa break } // Automatically mapping and filtering the struct attribute. - // TODO struct fields in sequence + var ( + reflectType = reflectValue.Type() + structField reflect.StructField + ) data := DataToMapDeep(in.Where) if in.Table != "" { data, _ = db.GetCore().mappingAndFilterData(in.Schema, in.Table, data, true) } - for key, value := range data { - if in.OmitNil && empty.IsNil(value) { - continue + for i := 0; i < reflectType.NumField(); i++ { + structField = reflectType.Field(i) + foundKey, foundValue := gutil.MapPossibleItemByKey(data, structField.Name) + if foundKey != "" { + if in.OmitNil && empty.IsNil(foundValue) { + continue + } + if in.OmitEmpty && empty.IsEmpty(foundValue) { + continue + } + newArgs = formatWhereKeyValue(db, buffer, newArgs, foundKey, foundValue) } - if in.OmitEmpty && empty.IsEmpty(value) { - continue - } - newArgs = formatWhereKeyValue(db, buffer, newArgs, key, value) } default: diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index f2878ce56..0650f0c5c 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -44,7 +44,7 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string] newTags := StructTagPriority switch len(tags) { case 0: - // No need handle. + // No need handling. case 1: newTags = append(strings.Split(tags[0], ","), StructTagPriority...) default: From feefcc98efb132e16cb8129d420969c63586b436 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 19 Aug 2021 14:11:28 +0800 Subject: [PATCH 465/492] improve condition parameter of struct by sequence for package gdb --- database/gdb/gdb_func.go | 1 + 1 file changed, 1 insertion(+) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 1910e9550..1468cca3e 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -480,6 +480,7 @@ func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interfa if in.Table != "" { data, _ = db.GetCore().mappingAndFilterData(in.Schema, in.Table, data, true) } + // Put the struct attributes in sequence in Where statement. for i := 0; i < reflectType.NumField(); i++ { structField = reflectType.Field(i) foundKey, foundValue := gutil.MapPossibleItemByKey(data, structField.Name) From 314bee2b4e348e901761f491d80b883fd1701428 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 19 Aug 2021 15:35:41 +0800 Subject: [PATCH 466/492] remove context key printing into logging content for context feature of package glog --- os/glog/glog_logger.go | 2 +- os/glog/glog_z_unit_ctx_test.go | 4 ---- os/glog/glog_z_unit_handler_test.go | 8 -------- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 5c6ea42e7..33dca1bf9 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -187,7 +187,7 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { if ctxStr != "" { ctxStr += ", " } - ctxStr += fmt.Sprintf("%s: %+v", ctxKey, ctxValue) + ctxStr += gconv.String(ctxValue) } } if ctxStr != "" { diff --git a/os/glog/glog_z_unit_ctx_test.go b/os/glog/glog_z_unit_ctx_test.go index dd1f80932..e56fe59fe 100644 --- a/os/glog/glog_z_unit_ctx_test.go +++ b/os/glog/glog_z_unit_ctx_test.go @@ -25,9 +25,7 @@ func Test_Ctx(t *testing.T) { ctx = context.WithValue(ctx, "Span-Id", "abcdefg") l.Ctx(ctx).Print(1, 2, 3) - t.Assert(gstr.Count(w.String(), "Trace-Id"), 1) t.Assert(gstr.Count(w.String(), "1234567890"), 1) - t.Assert(gstr.Count(w.String(), "Span-Id"), 1) t.Assert(gstr.Count(w.String(), "abcdefg"), 1) t.Assert(gstr.Count(w.String(), "1 2 3"), 1) }) @@ -46,9 +44,7 @@ func Test_Ctx_Config(t *testing.T) { ctx = context.WithValue(ctx, "Span-Id", "abcdefg") l.Ctx(ctx).Print(1, 2, 3) - t.Assert(gstr.Count(w.String(), "Trace-Id"), 1) t.Assert(gstr.Count(w.String(), "1234567890"), 1) - t.Assert(gstr.Count(w.String(), "Span-Id"), 1) t.Assert(gstr.Count(w.String(), "abcdefg"), 1) t.Assert(gstr.Count(w.String(), "1 2 3"), 1) }) diff --git a/os/glog/glog_z_unit_handler_test.go b/os/glog/glog_z_unit_handler_test.go index 921beb9b5..31f922f13 100644 --- a/os/glog/glog_z_unit_handler_test.go +++ b/os/glog/glog_z_unit_handler_test.go @@ -33,16 +33,12 @@ func TestLogger_SetHandlers1(t *testing.T) { ctx = context.WithValue(ctx, "Span-Id", "abcdefg") l.Ctx(ctx).Print(1, 2, 3) - t.Assert(gstr.Count(w.String(), "Trace-Id"), 1) t.Assert(gstr.Count(w.String(), "1234567890"), 1) - t.Assert(gstr.Count(w.String(), "Span-Id"), 1) t.Assert(gstr.Count(w.String(), "abcdefg"), 1) t.Assert(gstr.Count(w.String(), "1 2 3"), 1) t.Assert(arrayForHandlerTest1.Len(), 1) - t.Assert(gstr.Count(arrayForHandlerTest1.At(0), "Trace-Id"), 1) t.Assert(gstr.Count(arrayForHandlerTest1.At(0), "1234567890"), 1) - t.Assert(gstr.Count(arrayForHandlerTest1.At(0), "Span-Id"), 1) t.Assert(gstr.Count(arrayForHandlerTest1.At(0), "abcdefg"), 1) t.Assert(gstr.Count(arrayForHandlerTest1.At(0), "1 2 3"), 1) }) @@ -64,16 +60,12 @@ func TestLogger_SetHandlers2(t *testing.T) { ctx = context.WithValue(ctx, "Span-Id", "abcdefg") l.Ctx(ctx).Print(1, 2, 3) - t.Assert(gstr.Count(w.String(), "Trace-Id"), 0) t.Assert(gstr.Count(w.String(), "1234567890"), 0) - t.Assert(gstr.Count(w.String(), "Span-Id"), 0) t.Assert(gstr.Count(w.String(), "abcdefg"), 0) t.Assert(gstr.Count(w.String(), "1 2 3"), 0) t.Assert(arrayForHandlerTest2.Len(), 1) - t.Assert(gstr.Count(arrayForHandlerTest2.At(0), "Trace-Id"), 1) t.Assert(gstr.Count(arrayForHandlerTest2.At(0), "1234567890"), 1) - t.Assert(gstr.Count(arrayForHandlerTest2.At(0), "Span-Id"), 1) t.Assert(gstr.Count(arrayForHandlerTest2.At(0), "abcdefg"), 1) t.Assert(gstr.Count(arrayForHandlerTest2.At(0), "1 2 3"), 1) }) From a0ef6fce8167a200973d4a0ba754053a4d4fc511 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Thu, 19 Aug 2021 20:59:08 +0800 Subject: [PATCH 467/492] add bacth registering function Map for package ghttp --- net/ghttp/ghttp_server_router_group.go | 62 ++++++++++++++--------- net/ghttp/ghttp_unit_router_group_test.go | 30 +++++++++++ 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/net/ghttp/ghttp_server_router_group.go b/net/ghttp/ghttp_server_router_group.go index cf952a81c..1eaf85b3a 100644 --- a/net/ghttp/ghttp_server_router_group.go +++ b/net/ghttp/ghttp_server_router_group.go @@ -43,6 +43,13 @@ type ( } ) +const ( + groupBindTypeHandler = "HANDLER" + groupBindTypeRest = "REST" + groupBindTypeHook = "HOOK" + groupBindTypeMiddleware = "MIDDLEWARE" +) + var ( preBindItems = make([]*preBindItem, 0, 64) ) @@ -153,11 +160,11 @@ func (g *RouterGroup) Bind(items []GroupItem) *RouterGroup { } bindType := gstr.ToUpper(gconv.String(item[0])) switch bindType { - case "REST": - group.preBindToLocalArray("REST", gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) + case groupBindTypeRest: + group.preBindToLocalArray(groupBindTypeRest, gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) - case "MIDDLEWARE": - group.preBindToLocalArray("MIDDLEWARE", gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) + case groupBindTypeMiddleware: + group.preBindToLocalArray(groupBindTypeMiddleware, gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) default: if strings.EqualFold(bindType, "ALL") { @@ -166,9 +173,9 @@ func (g *RouterGroup) Bind(items []GroupItem) *RouterGroup { bindType += ":" } if len(item) > 3 { - group.preBindToLocalArray("HANDLER", bindType+gconv.String(item[1]), item[2], item[3]) + group.preBindToLocalArray(groupBindTypeHandler, bindType+gconv.String(item[1]), item[2], item[3]) } else { - group.preBindToLocalArray("HANDLER", bindType+gconv.String(item[1]), item[2]) + group.preBindToLocalArray(groupBindTypeHandler, bindType+gconv.String(item[1]), item[2]) } } } @@ -177,7 +184,7 @@ func (g *RouterGroup) Bind(items []GroupItem) *RouterGroup { // ALL registers a http handler to given route pattern and all http methods. func (g *RouterGroup) ALL(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBindToLocalArray("HANDLER", defaultMethod+":"+pattern, object, params...) + return g.Clone().preBindToLocalArray(groupBindTypeHandler, defaultMethod+":"+pattern, object, params...) } // ALLMap registers http handlers for http methods using map. @@ -187,59 +194,66 @@ func (g *RouterGroup) ALLMap(m map[string]interface{}) { } } +// Map registers http handlers for http methods using map. +func (g *RouterGroup) Map(m map[string]interface{}) { + for pattern, object := range m { + g.preBindToLocalArray(groupBindTypeHandler, pattern, object) + } +} + // GET registers a http handler to given route pattern and http method: GET. func (g *RouterGroup) GET(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBindToLocalArray("HANDLER", "GET:"+pattern, object, params...) + return g.Clone().preBindToLocalArray(groupBindTypeHandler, "GET:"+pattern, object, params...) } // PUT registers a http handler to given route pattern and http method: PUT. func (g *RouterGroup) PUT(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBindToLocalArray("HANDLER", "PUT:"+pattern, object, params...) + return g.Clone().preBindToLocalArray(groupBindTypeHandler, "PUT:"+pattern, object, params...) } // POST registers a http handler to given route pattern and http method: POST. func (g *RouterGroup) POST(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBindToLocalArray("HANDLER", "POST:"+pattern, object, params...) + return g.Clone().preBindToLocalArray(groupBindTypeHandler, "POST:"+pattern, object, params...) } // DELETE registers a http handler to given route pattern and http method: DELETE. func (g *RouterGroup) DELETE(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBindToLocalArray("HANDLER", "DELETE:"+pattern, object, params...) + return g.Clone().preBindToLocalArray(groupBindTypeHandler, "DELETE:"+pattern, object, params...) } // PATCH registers a http handler to given route pattern and http method: PATCH. func (g *RouterGroup) PATCH(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBindToLocalArray("HANDLER", "PATCH:"+pattern, object, params...) + return g.Clone().preBindToLocalArray(groupBindTypeHandler, "PATCH:"+pattern, object, params...) } // HEAD registers a http handler to given route pattern and http method: HEAD. func (g *RouterGroup) HEAD(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBindToLocalArray("HANDLER", "HEAD:"+pattern, object, params...) + return g.Clone().preBindToLocalArray(groupBindTypeHandler, "HEAD:"+pattern, object, params...) } // CONNECT registers a http handler to given route pattern and http method: CONNECT. func (g *RouterGroup) CONNECT(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBindToLocalArray("HANDLER", "CONNECT:"+pattern, object, params...) + return g.Clone().preBindToLocalArray(groupBindTypeHandler, "CONNECT:"+pattern, object, params...) } // OPTIONS registers a http handler to given route pattern and http method: OPTIONS. func (g *RouterGroup) OPTIONS(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBindToLocalArray("HANDLER", "OPTIONS:"+pattern, object, params...) + return g.Clone().preBindToLocalArray(groupBindTypeHandler, "OPTIONS:"+pattern, object, params...) } // TRACE registers a http handler to given route pattern and http method: TRACE. func (g *RouterGroup) TRACE(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBindToLocalArray("HANDLER", "TRACE:"+pattern, object, params...) + return g.Clone().preBindToLocalArray(groupBindTypeHandler, "TRACE:"+pattern, object, params...) } // REST registers a http handler to given route pattern according to REST rule. func (g *RouterGroup) REST(pattern string, object interface{}) *RouterGroup { - return g.Clone().preBindToLocalArray("REST", pattern, object) + return g.Clone().preBindToLocalArray(groupBindTypeRest, pattern, object) } // Hook registers a hook to given route pattern. func (g *RouterGroup) Hook(pattern string, hook string, handler HandlerFunc) *RouterGroup { - return g.Clone().preBindToLocalArray("HANDLER", pattern, handler, hook) + return g.Clone().preBindToLocalArray(groupBindTypeHandler, pattern, handler, hook) } // Middleware binds one or more middleware to the router group. @@ -293,7 +307,7 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { if g.domain != nil { domain = "" } - if bindType == "REST" { + if bindType == groupBindTypeRest { pattern = prefix + "/" + strings.TrimLeft(path, "/") } else { pattern = g.server.serveHandlerKey( @@ -303,14 +317,16 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { } // Filter repeated char '/'. pattern = gstr.Replace(pattern, "//", "/") + // Convert params to string array. extras := gconv.Strings(params) + // Check whether it's a hook handler. if _, ok := object.(HandlerFunc); ok && len(extras) > 0 { - bindType = "HOOK" + bindType = groupBindTypeHook } switch bindType { - case "HANDLER": + case groupBindTypeHandler: if reflect.ValueOf(object).Kind() == reflect.Func { funcInfo, err := g.server.checkAndCreateFuncInfo(object, "", "", "") if err != nil { @@ -355,14 +371,14 @@ func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { } } - case "REST": + case groupBindTypeRest: if g.server != nil { g.server.doBindObjectRest(pattern, object, g.middleware, source) } else { g.domain.doBindObjectRest(pattern, object, g.middleware, source) } - case "HOOK": + case groupBindTypeHook: if h, ok := object.(HandlerFunc); ok { if g.server != nil { g.server.doBindHookHandler(pattern, extras[0], h, source) diff --git a/net/ghttp/ghttp_unit_router_group_test.go b/net/ghttp/ghttp_unit_router_group_test.go index 9f093b2cc..3a2b35135 100644 --- a/net/ghttp/ghttp_unit_router_group_test.go +++ b/net/ghttp/ghttp_unit_router_group_test.go @@ -191,3 +191,33 @@ func Test_Router_Group_MultiServer(t *testing.T) { t.Assert(c2.PostContent("/post"), "post2") }) } + +func Test_Router_Group_Map(t *testing.T) { + testFuncGet := func(r *ghttp.Request) { + r.Response.Write("get") + } + testFuncPost := func(r *ghttp.Request) { + r.Response.Write("post") + } + p, _ := ports.PopRand() + s := g.Server(p) + s.Group("/", func(group *ghttp.RouterGroup) { + group.Map(map[string]interface{}{ + "Get: /test": testFuncGet, + "Post:/test": testFuncPost, + }) + }) + s.SetPort(p) + //s.SetDumpRouterMap(false) + gtest.Assert(s.Start(), nil) + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + c := g.Client() + c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + + t.Assert(c.GetContent("/test"), "get") + t.Assert(c.PostContent("/test"), "post") + }) +} From e10240bd7cc09c9c1155b7137df2f6ab06779ba2 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Fri, 20 Aug 2021 11:52:22 +0800 Subject: [PATCH 468/492] improve package gcron --- os/gcron/gcron.go | 32 ++++----- os/gcron/gcron_cron.go | 133 +++++++++++++++++++++++-------------- os/gcron/gcron_entry.go | 127 +++++++++++++++++++++-------------- os/gcron/gcron_schedule.go | 2 +- os/gtimer/gtimer_entry.go | 23 +++---- os/gtimer/gtimer_timer.go | 28 +++++--- 6 files changed, 204 insertions(+), 141 deletions(-) diff --git a/os/gcron/gcron.go b/os/gcron/gcron.go index 7f45c4b2c..ae8ee328c 100644 --- a/os/gcron/gcron.go +++ b/os/gcron/gcron.go @@ -48,63 +48,63 @@ func GetLogLevel() int { } // Add adds a timed task to default cron object. -// A unique <name> can be bound with the timed task. -// It returns and error if the <name> is already used. +// A unique `name` can be bound with the timed task. +// It returns and error if the `name` is already used. func Add(pattern string, job func(), name ...string) (*Entry, error) { return defaultCron.Add(pattern, job, name...) } // AddSingleton adds a singleton timed task, to default cron object. // A singleton timed task is that can only be running one single instance at the same time. -// A unique <name> can be bound with the timed task. -// It returns and error if the <name> is already used. +// A unique `name` can be bound with the timed task. +// It returns and error if the `name` is already used. func AddSingleton(pattern string, job func(), name ...string) (*Entry, error) { return defaultCron.AddSingleton(pattern, job, name...) } // AddOnce adds a timed task which can be run only once, to default cron object. -// A unique <name> can be bound with the timed task. -// It returns and error if the <name> is already used. +// A unique `name` can be bound with the timed task. +// It returns and error if the `name` is already used. func AddOnce(pattern string, job func(), name ...string) (*Entry, error) { return defaultCron.AddOnce(pattern, job, name...) } // AddTimes adds a timed task which can be run specified times, to default cron object. -// A unique <name> can be bound with the timed task. -// It returns and error if the <name> is already used. +// A unique `name` can be bound with the timed task. +// It returns and error if the `name` is already used. func AddTimes(pattern string, times int, job func(), name ...string) (*Entry, error) { return defaultCron.AddTimes(pattern, times, job, name...) } -// DelayAdd adds a timed task to default cron object after <delay> time. +// DelayAdd adds a timed task to default cron object after `delay` time. func DelayAdd(delay time.Duration, pattern string, job func(), name ...string) { defaultCron.DelayAdd(delay, pattern, job, name...) } -// DelayAddSingleton adds a singleton timed task after <delay> time to default cron object. +// DelayAddSingleton adds a singleton timed task after `delay` time to default cron object. func DelayAddSingleton(delay time.Duration, pattern string, job func(), name ...string) { defaultCron.DelayAddSingleton(delay, pattern, job, name...) } -// DelayAddOnce adds a timed task after <delay> time to default cron object. +// DelayAddOnce adds a timed task after `delay` time to default cron object. // This timed task can be run only once. func DelayAddOnce(delay time.Duration, pattern string, job func(), name ...string) { defaultCron.DelayAddOnce(delay, pattern, job, name...) } -// DelayAddTimes adds a timed task after <delay> time to default cron object. +// DelayAddTimes adds a timed task after `delay` time to default cron object. // This timed task can be run specified times. func DelayAddTimes(delay time.Duration, pattern string, times int, job func(), name ...string) { defaultCron.DelayAddTimes(delay, pattern, times, job, name...) } -// Search returns a scheduled task with the specified <name>. +// Search returns a scheduled task with the specified `name`. // It returns nil if no found. func Search(name string) *Entry { return defaultCron.Search(name) } -// Remove deletes scheduled task which named <name>. +// Remove deletes scheduled task which named `name`. func Remove(name string) { defaultCron.Remove(name) } @@ -119,12 +119,12 @@ func Entries() []*Entry { return defaultCron.Entries() } -// Start starts running the specified timed task named <name>. +// Start starts running the specified timed task named `name`. func Start(name string) { defaultCron.Start(name) } -// Stop stops running the specified timed task named <name>. +// Stop stops running the specified timed task named `name`. func Stop(name string) { defaultCron.Stop(name) } diff --git a/os/gcron/gcron_cron.go b/os/gcron/gcron_cron.go index 83a5ea8b2..cace246d2 100644 --- a/os/gcron/gcron_cron.go +++ b/os/gcron/gcron_cron.go @@ -7,7 +7,6 @@ package gcron import ( - "github.com/gogf/gf/errors/gerror" "time" "github.com/gogf/gf/container/garray" @@ -18,11 +17,18 @@ import ( ) type Cron struct { - idGen *gtype.Int64 // Used for unique name generation. - status *gtype.Int // Timed task status(0: Not Start; 1: Running; 2: Stopped; -1: Closed) - entries *gmap.StrAnyMap // All timed task entries. - logPath *gtype.String // Logging path(folder). - logLevel *gtype.Int // Logging level. + idGen *gtype.Int64 // Used for unique name generation. + status *gtype.Int // Timed task status(0: Not Start; 1: Running; 2: Stopped; -1: Closed) + entries *gmap.StrAnyMap // All timed task entries. + logger *glog.Logger // Logger, it is nil in default. + + // Logging path(folder). + // Deprecated, use logger instead. + logPath *gtype.String + + // Logging level. + // Deprecated, use logger instead. + logLevel *gtype.Int } // New returns a new Cron object with default settings. @@ -36,76 +42,101 @@ func New() *Cron { } } +// SetLogger sets the logger for cron. +func (c *Cron) SetLogger(logger *glog.Logger) { + c.logger = logger +} + +// GetLogger returns the logger in the cron. +func (c *Cron) GetLogger() *glog.Logger { + return c.logger +} + // SetLogPath sets the logging folder path. +// Deprecated, use SetLogger instead. func (c *Cron) SetLogPath(path string) { c.logPath.Set(path) } // GetLogPath return the logging folder path. +// Deprecated, use GetLogger instead. func (c *Cron) GetLogPath() string { return c.logPath.Val() } // SetLogLevel sets the logging level. +// Deprecated, use SetLogger instead. func (c *Cron) SetLogLevel(level int) { c.logLevel.Set(level) } // GetLogLevel returns the logging level. +// Deprecated, use GetLogger instead. func (c *Cron) GetLogLevel() int { return c.logLevel.Val() } -// Add adds a timed task. -// A unique <name> can be bound with the timed task. -// It returns and error if the <name> is already used. -func (c *Cron) Add(pattern string, job func(), name ...string) (*Entry, error) { +// AddEntry creates and returns a new Entry object. +func (c *Cron) AddEntry(pattern string, job func(), times int, singleton bool, name ...string) (*Entry, error) { + var ( + entryName = "" + infinite = false + ) if len(name) > 0 { - if c.Search(name[0]) != nil { - return nil, gerror.NewCodef(gerror.CodeInvalidOperation, `cron job "%s" already exists`, name[0]) - } + entryName = name[0] } - return c.addEntry(pattern, job, false, name...) + if times <= 0 { + infinite = true + } + return c.doAddEntry(addEntryInput{ + Name: entryName, + Job: job, + Times: times, + Pattern: pattern, + Singleton: singleton, + Infinite: infinite, + }) +} + +// Add adds a timed task. +// A unique `name` can be bound with the timed task. +// It returns and error if the `name` is already used. +func (c *Cron) Add(pattern string, job func(), name ...string) (*Entry, error) { + return c.AddEntry(pattern, job, -1, false, name...) } // AddSingleton adds a singleton timed task. // A singleton timed task is that can only be running one single instance at the same time. -// A unique <name> can be bound with the timed task. -// It returns and error if the <name> is already used. +// A unique `name` can be bound with the timed task. +// It returns and error if the `name` is already used. func (c *Cron) AddSingleton(pattern string, job func(), name ...string) (*Entry, error) { - if entry, err := c.Add(pattern, job, name...); err != nil { - return nil, err - } else { - entry.SetSingleton(true) - return entry, nil - } -} - -// AddOnce adds a timed task which can be run only once. -// A unique <name> can be bound with the timed task. -// It returns and error if the <name> is already used. -func (c *Cron) AddOnce(pattern string, job func(), name ...string) (*Entry, error) { - if entry, err := c.Add(pattern, job, name...); err != nil { - return nil, err - } else { - entry.SetTimes(1) - return entry, nil - } + return c.AddEntry(pattern, job, -1, true, name...) } // AddTimes adds a timed task which can be run specified times. -// A unique <name> can be bound with the timed task. -// It returns and error if the <name> is already used. +// A unique `name` can be bound with the timed task. +// It returns and error if the `name` is already used. func (c *Cron) AddTimes(pattern string, times int, job func(), name ...string) (*Entry, error) { - if entry, err := c.Add(pattern, job, name...); err != nil { - return nil, err - } else { - entry.SetTimes(times) - return entry, nil - } + return c.AddEntry(pattern, job, times, false, name...) } -// DelayAdd adds a timed task after <delay> time. +// AddOnce adds a timed task which can be run only once. +// A unique `name` can be bound with the timed task. +// It returns and error if the `name` is already used. +func (c *Cron) AddOnce(pattern string, job func(), name ...string) (*Entry, error) { + return c.AddEntry(pattern, job, 1, false, name...) +} + +// DelayAddEntry adds a timed task after `delay` time. +func (c *Cron) DelayAddEntry(delay time.Duration, pattern string, job func(), times int, singleton bool, name ...string) { + gtimer.AddOnce(delay, func() { + if _, err := c.AddEntry(pattern, job, times, singleton, name...); err != nil { + panic(err) + } + }) +} + +// DelayAdd adds a timed task after `delay` time. func (c *Cron) DelayAdd(delay time.Duration, pattern string, job func(), name ...string) { gtimer.AddOnce(delay, func() { if _, err := c.Add(pattern, job, name...); err != nil { @@ -114,7 +145,7 @@ func (c *Cron) DelayAdd(delay time.Duration, pattern string, job func(), name .. }) } -// DelayAddSingleton adds a singleton timed task after <delay> time. +// DelayAddSingleton adds a singleton timed task after `delay` time. func (c *Cron) DelayAddSingleton(delay time.Duration, pattern string, job func(), name ...string) { gtimer.AddOnce(delay, func() { if _, err := c.AddSingleton(pattern, job, name...); err != nil { @@ -123,7 +154,7 @@ func (c *Cron) DelayAddSingleton(delay time.Duration, pattern string, job func() }) } -// DelayAddOnce adds a timed task after <delay> time. +// DelayAddOnce adds a timed task after `delay` time. // This timed task can be run only once. func (c *Cron) DelayAddOnce(delay time.Duration, pattern string, job func(), name ...string) { gtimer.AddOnce(delay, func() { @@ -133,7 +164,7 @@ func (c *Cron) DelayAddOnce(delay time.Duration, pattern string, job func(), nam }) } -// DelayAddTimes adds a timed task after <delay> time. +// DelayAddTimes adds a timed task after `delay` time. // This timed task can be run specified times. func (c *Cron) DelayAddTimes(delay time.Duration, pattern string, times int, job func(), name ...string) { gtimer.AddOnce(delay, func() { @@ -143,8 +174,8 @@ func (c *Cron) DelayAddTimes(delay time.Duration, pattern string, times int, job }) } -// Search returns a scheduled task with the specified <name>. -// It returns nil if no found. +// Search returns a scheduled task with the specified `name`. +// It returns nil if not found. func (c *Cron) Search(name string) *Entry { if v := c.entries.Get(name); v != nil { return v.(*Entry) @@ -152,7 +183,7 @@ func (c *Cron) Search(name string) *Entry { return nil } -// Start starts running the specified timed task named <name>. +// Start starts running the specified timed task named `name`. func (c *Cron) Start(name ...string) { if len(name) > 0 { for _, v := range name { @@ -165,7 +196,7 @@ func (c *Cron) Start(name ...string) { } } -// Stop stops running the specified timed task named <name>. +// Stop stops running the specified timed task named `name`. func (c *Cron) Stop(name ...string) { if len(name) > 0 { for _, v := range name { @@ -178,7 +209,7 @@ func (c *Cron) Stop(name ...string) { } } -// Remove deletes scheduled task which named <name>. +// Remove deletes scheduled task which named `name`. func (c *Cron) Remove(name string) { if v := c.entries.Get(name); v != nil { v.(*Entry).Close() diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index 0f6ba3549..300c74fd4 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -7,117 +7,131 @@ package gcron import ( + "github.com/gogf/gf/errors/gerror" "reflect" "runtime" "time" "github.com/gogf/gf/container/gtype" - "github.com/gogf/gf/os/glog" "github.com/gogf/gf/os/gtimer" "github.com/gogf/gf/util/gconv" ) // Entry is timing task entry. type Entry struct { - cron *Cron // Cron object belonged to. - entry *gtimer.Entry // Associated gtimer.Entry. - schedule *cronSchedule // Timed schedule object. - jobName string // Callback function name(address info). - times *gtype.Int // Running times limit. - Name string // Entry name. - Job func() `json:"-"` // Callback function. - Time time.Time // Registered time. + cron *Cron // Cron object belonged to. + timerEntry *gtimer.Entry // Associated timer Entry. + schedule *cronSchedule // Timed schedule object. + jobName string // Callback function name(address info). + times *gtype.Int // Running times limit. + infinite *gtype.Bool // No times limit. + Name string // Entry name. + Job func() `json:"-"` // Callback function. + Time time.Time // Registered time. } -// addEntry creates and returns a new Entry object. -// Param <job> is the callback function for timed task execution. -// Param <singleton> specifies whether timed task executing in singleton mode. -// Param <name> names this entry for manual control. -func (c *Cron) addEntry(pattern string, job func(), singleton bool, name ...string) (*Entry, error) { - schedule, err := newSchedule(pattern) +type addEntryInput struct { + Name string // Name names this entry for manual control. + Job func() // Job is the callback function for timed task execution. + Times int // Times specifies the running limit times for the entry. + Pattern string // Pattern is the crontab style string for scheduler. + Singleton bool // Singleton specifies whether timed task executing in singleton mode. + Infinite bool // Infinite specifies whether this entry is running with no times limit. +} + +// doAddEntry creates and returns a new Entry object. +func (c *Cron) doAddEntry(in addEntryInput) (*Entry, error) { + if in.Name != "" { + if c.Search(in.Name) != nil { + return nil, gerror.NewCodef(gerror.CodeInvalidOperation, `cron job "%s" already exists`, in.Name) + } + } + + schedule, err := newSchedule(in.Pattern) if err != nil { return nil, err } - // No limit for <times>, for gtimer checking scheduling every second. + // No limit for `times`, for timer checking scheduling every second. entry := &Entry{ cron: c, schedule: schedule, - jobName: runtime.FuncForPC(reflect.ValueOf(job).Pointer()).Name(), - times: gtype.NewInt(defaultTimes), - Job: job, + jobName: runtime.FuncForPC(reflect.ValueOf(in.Job).Pointer()).Name(), + times: gtype.NewInt(in.Times), + infinite: gtype.NewBool(in.Infinite), + Job: in.Job, Time: time.Now(), } - if len(name) > 0 { - entry.Name = name[0] + if in.Name != "" { + entry.Name = in.Name } else { - entry.Name = "gcron-" + gconv.String(c.idGen.Add(1)) + entry.Name = "cron-" + gconv.String(c.idGen.Add(1)) } // When you add a scheduled task, you cannot allow it to run. - // It cannot start running when added to gtimer. + // It cannot start running when added to timer. // It should start running after the entry is added to the Cron entries map, to avoid the task // from running during adding where the entries do not have the entry information, which might cause panic. - entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, -1, gtimer.StatusStopped) + entry.timerEntry = gtimer.AddEntry(time.Second, entry.check, in.Singleton, -1, gtimer.StatusStopped) c.entries.Set(entry.Name, entry) - entry.entry.Start() + entry.timerEntry.Start() return entry, nil } // IsSingleton return whether this entry is a singleton timed task. func (entry *Entry) IsSingleton() bool { - return entry.entry.IsSingleton() + return entry.timerEntry.IsSingleton() } // SetSingleton sets the entry running in singleton mode. func (entry *Entry) SetSingleton(enabled bool) { - entry.entry.SetSingleton(enabled) + entry.timerEntry.SetSingleton(enabled) } // SetTimes sets the times which the entry can run. func (entry *Entry) SetTimes(times int) { entry.times.Set(times) + entry.infinite.Set(false) } // Status returns the status of entry. func (entry *Entry) Status() int { - return entry.entry.Status() + return entry.timerEntry.Status() } // SetStatus sets the status of the entry. func (entry *Entry) SetStatus(status int) int { - return entry.entry.SetStatus(status) + return entry.timerEntry.SetStatus(status) } // Start starts running the entry. func (entry *Entry) Start() { - entry.entry.Start() + entry.timerEntry.Start() } // Stop stops running the entry. func (entry *Entry) Stop() { - entry.entry.Stop() + entry.timerEntry.Stop() } // Close stops and removes the entry from cron. func (entry *Entry) Close() { entry.cron.entries.Remove(entry.Name) - entry.entry.Close() + entry.timerEntry.Close() } -// Timing task check execution. +// check is the core timing task check logic. // The running times limits feature is implemented by gcron.Entry and cannot be implemented by gtimer.Entry. // gcron.Entry relies on gtimer to implement a scheduled task check for gcron.Entry per second. func (entry *Entry) check() { if entry.schedule.meet(time.Now()) { - var ( - path = entry.cron.GetLogPath() - level = entry.cron.GetLogLevel() - ) switch entry.cron.status.Val() { case StatusStopped: return case StatusClosed: - glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s removed", entry.Name, entry.schedule.pattern, entry.jobName) + entry.logDebugf( + "[gcron] %s(%s) %s removed", + entry.Name, entry.schedule.pattern, entry.jobName, + ) entry.Close() case StatusReady: @@ -125,35 +139,48 @@ func (entry *Entry) check() { case StatusRunning: defer func() { if err := recover(); err != nil { - glog.Path(path).Level(level).Errorf( + entry.logErrorf( "[gcron] %s(%s) %s end with error: %+v", entry.Name, entry.schedule.pattern, entry.jobName, err, ) } else { - glog.Path(path).Level(level).Debugf( + entry.logDebugf( "[gcron] %s(%s) %s end", entry.Name, entry.schedule.pattern, entry.jobName, ) } - if entry.entry.Status() == StatusClosed { + + if entry.timerEntry.Status() == StatusClosed { entry.Close() } }() // Running times check. - times := entry.times.Add(-1) - if times <= 0 { - if entry.entry.SetStatus(StatusClosed) == StatusClosed || times < 0 { - return + if !entry.infinite.Val() { + times := entry.times.Add(-1) + if times <= 0 { + if entry.timerEntry.SetStatus(StatusClosed) == StatusClosed || times < 0 { + return + } } } - if times < 2000000000 && times > 1000000000 { - entry.times.Set(defaultTimes) - } - glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s start", entry.Name, entry.schedule.pattern, entry.jobName) + entry.logDebugf( + "[gcron] %s(%s) %s start", + entry.Name, entry.schedule.pattern, entry.jobName, + ) entry.Job() - } } } +func (entry *Entry) logDebugf(format string, v ...interface{}) { + if logger := entry.cron.GetLogger(); logger != nil { + logger.Debugf(format, v...) + } +} + +func (entry *Entry) logErrorf(format string, v ...interface{}) { + if logger := entry.cron.GetLogger(); logger != nil { + logger.Errorf(format, v...) + } +} diff --git a/os/gcron/gcron_schedule.go b/os/gcron/gcron_schedule.go index 146c78547..0fdd83206 100644 --- a/os/gcron/gcron_schedule.go +++ b/os/gcron/gcron_schedule.go @@ -223,7 +223,7 @@ func parseItemValue(value string, fieldType byte) (int, error) { return 0, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern value: "%s"`, value) } -// meet checks if the given time <t> meets the runnable point for the job. +// meet checks if the given time `t` meets the runnable point for the job. func (s *cronSchedule) meet(t time.Time) bool { if s.every != 0 { // It checks using interval. diff --git a/os/gtimer/gtimer_entry.go b/os/gtimer/gtimer_entry.go index 65aac5e3d..3a49e3819 100644 --- a/os/gtimer/gtimer_entry.go +++ b/os/gtimer/gtimer_entry.go @@ -8,18 +8,18 @@ package gtimer import ( "github.com/gogf/gf/container/gtype" - "math" ) // Entry is the timing job. type Entry struct { job JobFunc // The job function. timer *Timer // Belonged timer. - ticks int64 // The job runs every ticks. + ticks int64 // The job runs every tick. times *gtype.Int // Limit running times. status *gtype.Int // Job status. singleton *gtype.Bool // Singleton mode. nextTicks *gtype.Int64 // Next run ticks of the job. + infinite *gtype.Bool // No times limit. } // JobFunc is the job function. @@ -32,15 +32,13 @@ func (entry *Entry) Status() int { // Run runs the timer job asynchronously. func (entry *Entry) Run() { - leftRunningTimes := entry.times.Add(-1) - // It checks its running times exceeding. - if leftRunningTimes < 0 { - entry.status.Set(StatusClosed) - return - } - // This means it has no limit in running times. - if leftRunningTimes == math.MaxInt32-1 { - entry.times.Set(math.MaxInt32) + if !entry.infinite.Val() { + leftRunningTimes := entry.times.Add(-1) + // It checks its running times exceeding. + if leftRunningTimes < 0 { + entry.status.Set(StatusClosed) + return + } } go func() { defer func() { @@ -108,7 +106,7 @@ func (entry *Entry) Close() { entry.status.Set(StatusClosed) } -// Reset reset the job, which resets its ticks for next running. +// Reset resets the job, which resets its ticks for next running. func (entry *Entry) Reset() { entry.nextTicks.Set(entry.timer.ticks.Val() + entry.ticks) } @@ -131,4 +129,5 @@ func (entry *Entry) Job() JobFunc { // SetTimes sets the limit running times for the job. func (entry *Entry) SetTimes(times int) { entry.times.Set(times) + entry.infinite.Set(false) } diff --git a/os/gtimer/gtimer_timer.go b/os/gtimer/gtimer_timer.go index 21bf19112..7022d87a3 100644 --- a/os/gtimer/gtimer_timer.go +++ b/os/gtimer/gtimer_timer.go @@ -118,8 +118,11 @@ func (t *Timer) Close() { // createEntry creates and adds a timing job to the timer. func (t *Timer) createEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry { + var ( + infinite = false + ) if times <= 0 { - times = defaultTimes + infinite = true } var ( intervalTicksOfJob = int64(interval / t.options.Interval) @@ -129,16 +132,19 @@ func (t *Timer) createEntry(interval time.Duration, job JobFunc, singleton bool, // then sets it to one tick, which means it will be run in one interval. intervalTicksOfJob = 1 } - nextTicks := t.ticks.Val() + intervalTicksOfJob - entry := &Entry{ - job: job, - timer: t, - ticks: intervalTicksOfJob, - times: gtype.NewInt(times), - status: gtype.NewInt(status), - singleton: gtype.NewBool(singleton), - nextTicks: gtype.NewInt64(nextTicks), - } + var ( + nextTicks = t.ticks.Val() + intervalTicksOfJob + entry = &Entry{ + job: job, + timer: t, + ticks: intervalTicksOfJob, + times: gtype.NewInt(times), + status: gtype.NewInt(status), + singleton: gtype.NewBool(singleton), + nextTicks: gtype.NewInt64(nextTicks), + infinite: gtype.NewBool(infinite), + } + ) t.queue.Push(entry, nextTicks) return entry } From 43180ef23996886584d4ea9c19222c61c5c7cf7f Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Fri, 20 Aug 2021 14:09:15 +0800 Subject: [PATCH 469/492] improve package gcron --- os/gcron/gcron.go | 17 +++++++++++++++-- os/gtimer/gtimer.go | 2 -- os/gtimer/gtimer_timer.go | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/os/gcron/gcron.go b/os/gcron/gcron.go index ae8ee328c..aeb63bc8b 100644 --- a/os/gcron/gcron.go +++ b/os/gcron/gcron.go @@ -8,7 +8,7 @@ package gcron import ( - "math" + "github.com/gogf/gf/os/glog" "time" "github.com/gogf/gf/os/gtimer" @@ -19,7 +19,6 @@ const ( StatusRunning = gtimer.StatusRunning StatusStopped = gtimer.StatusStopped StatusClosed = gtimer.StatusClosed - defaultTimes = math.MaxInt32 ) var ( @@ -27,22 +26,36 @@ var ( defaultCron = New() ) +// SetLogger sets the logger for cron. +func SetLogger(logger *glog.Logger) { + defaultCron.SetLogger(logger) +} + +// GetLogger returns the logger in the cron. +func GetLogger() *glog.Logger { + return defaultCron.GetLogger() +} + // SetLogPath sets the logging folder path for default cron object. +// Deprecated, use SetLogger instead. func SetLogPath(path string) { defaultCron.SetLogPath(path) } // GetLogPath returns the logging folder path of default cron object. +// Deprecated, use GetLogger instead. func GetLogPath() string { return defaultCron.GetLogPath() } // SetLogLevel sets the logging level for default cron object. +// Deprecated, use SetLogger instead. func SetLogLevel(level int) { defaultCron.SetLogLevel(level) } // GetLogLevel returns the logging level for default cron object. +// Deprecated, use GetLogger instead. func GetLogLevel() int { return defaultCron.GetLogLevel() } diff --git a/os/gtimer/gtimer.go b/os/gtimer/gtimer.go index 5cc849f7b..3491f6567 100644 --- a/os/gtimer/gtimer.go +++ b/os/gtimer/gtimer.go @@ -20,7 +20,6 @@ package gtimer import ( "github.com/gogf/gf/container/gtype" - "math" "sync" "time" @@ -47,7 +46,6 @@ const ( StatusStopped = 2 // Job or Timer is stopped. StatusClosed = -1 // Job or Timer is closed and waiting to be deleted. panicExit = "exit" // panicExit is used for custom job exit with panic. - defaultTimes = math.MaxInt32 // defaultTimes is the default limit running times, a big number. defaultTimerInterval = 100 // defaultTimerInterval is the default timer interval in milliseconds. commandEnvKeyForInterval = "gf.gtimer.interval" // commandEnvKeyForInterval is the key for command argument or environment configuring default interval duration for timer. ) diff --git a/os/gtimer/gtimer_timer.go b/os/gtimer/gtimer_timer.go index 7022d87a3..2c43ba8fd 100644 --- a/os/gtimer/gtimer_timer.go +++ b/os/gtimer/gtimer_timer.go @@ -28,7 +28,7 @@ func New(options ...TimerOptions) *Timer { // Add adds a timing job to the timer, which runs in interval of <interval>. func (t *Timer) Add(interval time.Duration, job JobFunc) *Entry { - return t.createEntry(interval, job, false, defaultTimes, StatusReady) + return t.createEntry(interval, job, false, -1, StatusReady) } // AddEntry adds a timing job to the timer with detailed parameters. @@ -48,7 +48,7 @@ func (t *Timer) AddEntry(interval time.Duration, job JobFunc, singleton bool, ti // AddSingleton is a convenience function for add singleton mode job. func (t *Timer) AddSingleton(interval time.Duration, job JobFunc) *Entry { - return t.createEntry(interval, job, true, defaultTimes, StatusReady) + return t.createEntry(interval, job, true, -1, StatusReady) } // AddOnce is a convenience function for adding a job which only runs once and then exits. From a229960218e9e35a8168da145b2e6e1cbf167b4d Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Fri, 20 Aug 2021 15:44:08 +0800 Subject: [PATCH 470/492] add context id feature for package gctx/glog --- os/gctx/gctx.go | 47 +++++++++++++++++++++++++++++- os/gctx/gctx_test.go | 51 +++++++++++++++++++++++++++++++++ os/glog/glog_logger.go | 31 +++++++++++--------- os/glog/glog_logger_config.go | 3 ++ os/glog/glog_z_unit_ctx_test.go | 11 +++++++ 5 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 os/gctx/gctx_test.go diff --git a/os/gctx/gctx.go b/os/gctx/gctx.go index e2fffb48a..9eb91f5e6 100644 --- a/os/gctx/gctx.go +++ b/os/gctx/gctx.go @@ -7,9 +7,54 @@ // Package gctx wraps context.Context and provides extra context features. package gctx -import "context" +import ( + "context" + "github.com/gogf/gf/util/guid" +) type ( Ctx = context.Context // Ctx is short name alias for context.Context. StrKey string // StrKey is a type for warps basic type string as context key. ) + +const ( + // CtxKey is custom tracing context key for context id. + // The context id a unique string for certain context. + CtxKey StrKey = "GoFrameCtxId" +) + +// New creates and returns a context which contains context id. +func New() context.Context { + return WithCtx(context.Background()) +} + +// WithCtx creates and returns a context containing context id upon given parent context `ctx`. +func WithCtx(ctx context.Context) context.Context { + return WithPrefix(ctx, "") +} + +// WithPrefix creates and returns a context containing context id upon given parent context `ctx`. +// The generated context id has custom prefix string specified by parameter `prefix`. +func WithPrefix(ctx context.Context, prefix string) context.Context { + return WithValue(ctx, prefix+getUniqueID()) +} + +// WithValue creates and returns a context containing context id upon given parent context `ctx`. +// The generated context id value is specified by parameter `value`. +func WithValue(ctx context.Context, value string) context.Context { + if value == "" { + return New() + } + return context.WithValue(ctx, CtxKey, value) +} + +// Value retrieves and returns the context id from context. +func Value(ctx context.Context) string { + s, _ := ctx.Value(CtxKey).(string) + return s +} + +// getUniqueID produces a global unique string. +func getUniqueID() string { + return guid.S() +} diff --git a/os/gctx/gctx_test.go b/os/gctx/gctx_test.go new file mode 100644 index 000000000..d190d5a7a --- /dev/null +++ b/os/gctx/gctx_test.go @@ -0,0 +1,51 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gctx_test + +import ( + "context" + "github.com/gogf/gf/os/gctx" + "github.com/gogf/gf/text/gstr" + "testing" + + "github.com/gogf/gf/test/gtest" +) + +func Test_New(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + ctx := gctx.New() + t.AssertNE(ctx, nil) + t.AssertNE(gctx.Value(ctx), "") + }) +} + +func Test_WithCtx(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + ctx := context.WithValue(context.TODO(), "TEST", 1) + ctx = gctx.WithCtx(ctx) + t.AssertNE(gctx.Value(ctx), "") + t.Assert(ctx.Value("TEST"), 1) + }) +} + +func Test_WithPrefix(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + ctx := context.WithValue(context.TODO(), "TEST", 1) + ctx = gctx.WithPrefix(ctx, "H-") + t.Assert(gstr.Contains(gctx.Value(ctx), "H-"), true) + t.Assert(ctx.Value("TEST"), 1) + }) +} + +func Test_WithValue(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + ctx := context.WithValue(context.TODO(), "TEST", 1) + ctx = gctx.WithValue(ctx, "123") + t.Assert(gctx.Value(ctx), "123") + t.Assert(ctx.Value("TEST"), 1) + }) +} diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 33dca1bf9..28342804b 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -154,13 +154,17 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { if l.config.Flags&(F_FILE_LONG|F_FILE_SHORT|F_CALLER_FN) > 0 { callerFnName, path, line := gdebug.CallerWithFilter(pathFilterKey, l.config.StSkip) if l.config.Flags&F_CALLER_FN > 0 { - input.CallerFunc = fmt.Sprintf(`[%s]`, callerFnName) + if len(callerFnName) > 2 { + input.CallerFunc = fmt.Sprintf(`[%s]`, callerFnName) + } } - if l.config.Flags&F_FILE_LONG > 0 { - input.CallerPath = fmt.Sprintf(`%s:%d:`, path, line) - } - if l.config.Flags&F_FILE_SHORT > 0 { - input.CallerPath = fmt.Sprintf(`%s:%d:`, gfile.Basename(path), line) + if line >= 0 && len(path) > 1 { + if l.config.Flags&F_FILE_LONG > 0 { + input.CallerPath = fmt.Sprintf(`%s:%d:`, path, line) + } + if l.config.Flags&F_FILE_SHORT > 0 { + input.CallerPath = fmt.Sprintf(`%s:%d:`, gfile.Basename(path), line) + } } } // Prefix. @@ -173,26 +177,25 @@ func (l *Logger) print(ctx context.Context, level int, values ...interface{}) { // Tracing values. spanCtx := trace.SpanContextFromContext(ctx) if traceId := spanCtx.TraceID(); traceId.IsValid() { - input.CtxStr = "{" + traceId.String() + "}" + input.CtxStr = traceId.String() } // Context values. if len(l.config.CtxKeys) > 0 { - ctxStr := "" for _, ctxKey := range l.config.CtxKeys { var ctxValue interface{} if ctxValue = ctx.Value(ctxKey); ctxValue == nil { ctxValue = ctx.Value(gctx.StrKey(gconv.String(ctxKey))) } if ctxValue != nil { - if ctxStr != "" { - ctxStr += ", " + if input.CtxStr != "" { + input.CtxStr += ", " } - ctxStr += gconv.String(ctxValue) + input.CtxStr += gconv.String(ctxValue) } } - if ctxStr != "" { - input.CtxStr += "{" + ctxStr + "}" - } + } + if input.CtxStr != "" { + input.CtxStr = "{" + input.CtxStr + "}" } } var tempStr string diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 81dce4604..1ed493531 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -7,6 +7,7 @@ package glog import ( + "github.com/gogf/gf/os/gctx" "io" "strings" "time" @@ -49,6 +50,7 @@ func DefaultConfig() Config { File: defaultFileFormat, Flags: F_TIME_STD, Level: LEVEL_ALL, + CtxKeys: []interface{}{gctx.CtxKey}, StStatus: 1, HeaderPrint: true, StdoutPrint: true, @@ -163,6 +165,7 @@ func (l *Logger) SetStackFilter(filter string) { // Note that multiple calls of this function will overwrite the previous set context keys. func (l *Logger) SetCtxKeys(keys ...interface{}) { l.config.CtxKeys = keys + l.config.CtxKeys = append(l.config.CtxKeys, gctx.CtxKey) } // AppendCtxKeys appends extra keys to logger. diff --git a/os/glog/glog_z_unit_ctx_test.go b/os/glog/glog_z_unit_ctx_test.go index e56fe59fe..e6811df72 100644 --- a/os/glog/glog_z_unit_ctx_test.go +++ b/os/glog/glog_z_unit_ctx_test.go @@ -10,6 +10,7 @@ import ( "bytes" "context" "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gctx" "github.com/gogf/gf/os/glog" "github.com/gogf/gf/test/gtest" "github.com/gogf/gf/text/gstr" @@ -49,3 +50,13 @@ func Test_Ctx_Config(t *testing.T) { t.Assert(gstr.Count(w.String(), "1 2 3"), 1) }) } + +func Test_Ctx_CtxKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + w := bytes.NewBuffer(nil) + l := glog.NewWithWriter(w) + l.Ctx(gctx.WithValue(context.TODO(), "abcdefg")).Print(1, 2, 3) + t.Assert(gstr.Count(w.String(), "abcdefg"), 1) + t.Assert(gstr.Count(w.String(), "1 2 3"), 1) + }) +} From c5345239fc974b266b267441210b2e5e1afc8c24 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Fri, 20 Aug 2021 15:49:45 +0800 Subject: [PATCH 471/492] improve logging content for package gcron --- os/gcron/gcron_entry.go | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index 300c74fd4..81ed8a778 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -128,10 +128,7 @@ func (entry *Entry) check() { return case StatusClosed: - entry.logDebugf( - "[gcron] %s(%s) %s removed", - entry.Name, entry.schedule.pattern, entry.jobName, - ) + entry.logDebugf("[gcron] %s %s removed", entry.schedule.pattern, entry.jobName) entry.Close() case StatusReady: @@ -139,15 +136,9 @@ func (entry *Entry) check() { case StatusRunning: defer func() { if err := recover(); err != nil { - entry.logErrorf( - "[gcron] %s(%s) %s end with error: %+v", - entry.Name, entry.schedule.pattern, entry.jobName, err, - ) + entry.logErrorf("[gcron] %s %s end with error: %+v", entry.schedule.pattern, entry.jobName, err) } else { - entry.logDebugf( - "[gcron] %s(%s) %s end", - entry.Name, entry.schedule.pattern, entry.jobName, - ) + entry.logDebugf("[gcron] %s %s end", entry.schedule.pattern, entry.jobName) } if entry.timerEntry.Status() == StatusClosed { @@ -164,10 +155,7 @@ func (entry *Entry) check() { } } } - entry.logDebugf( - "[gcron] %s(%s) %s start", - entry.Name, entry.schedule.pattern, entry.jobName, - ) + entry.logDebugf("[gcron] %s %s start", entry.schedule.pattern, entry.jobName) entry.Job() } From ef36cf84463b831e48005097198fae580169a75d Mon Sep 17 00:00:00 2001 From: John <john@johng.cn> Date: Fri, 20 Aug 2021 23:15:48 +0800 Subject: [PATCH 472/492] fix issue in example of package gjson --- encoding/gjson/gjson_z_example_conversion_test.go | 5 +++-- go.mod | 7 ------- net/ghttp/ghttp.go | 6 +++--- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/encoding/gjson/gjson_z_example_conversion_test.go b/encoding/gjson/gjson_z_example_conversion_test.go index c55d9c409..d2f4bdd9f 100644 --- a/encoding/gjson/gjson_z_example_conversion_test.go +++ b/encoding/gjson/gjson_z_example_conversion_test.go @@ -19,6 +19,7 @@ func Example_conversionNormalFormats() { "array" : ["John", "Ming"] } }` + if j, err := gjson.DecodeToJson(data); err != nil { panic(err) } else { @@ -48,8 +49,8 @@ func Example_conversionNormalFormats() { // YAML: // users: // array: - // - John - // - Ming + // - John + // - Ming // count: 1 // // ====================== diff --git a/go.mod b/go.mod index d07ddc00e..19fc61f7f 100644 --- a/go.mod +++ b/go.mod @@ -5,24 +5,17 @@ go 1.14 require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 - github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.12.0 github.com/fsnotify/fsnotify v1.4.9 github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/websocket v1.4.2 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf - github.com/kr/text v0.2.0 // indirect - github.com/mattn/go-runewidth v0.0.10 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/olekukonko/tablewriter v0.0.5 go.opentelemetry.io/otel v1.0.0-RC2 go.opentelemetry.io/otel/oteltest v1.0.0-RC2 go.opentelemetry.io/otel/trace v1.0.0-RC2 golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 - golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect golang.org/x/text v0.3.6 - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index 3c539cbb5..f4f55c34b 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -23,12 +23,12 @@ type ( Server struct { name string // Unique name for instance management. config ServerConfig // Configuration. - plugins []Plugin // Plugin array to extends server functionality. + plugins []Plugin // Plugin array to extend server functionality. servers []*gracefulServer // Underlying http.Server array. serverCount *gtype.Int // Underlying http.Server count. closeChan chan struct{} // Used for underlying server closing event notification. serveTree map[string]interface{} // The route map tree. - serveCache *gcache.Cache // Server cache for internal usage. + serveCache *gcache.Cache // Server caches for internal usage. routesMap map[string][]registeredRouteItem // Route map mainly for route dumps and repeated route checks. statusHandlerMap map[string][]HandlerFunc // Custom status handler map. sessionManager *gsession.Manager // Session manager. @@ -142,7 +142,7 @@ var ( serverMapping = gmap.NewStrAnyMap(true) // serverRunning marks the running server count. - // If there no successful server running or all servers shutdown, this value is 0. + // If there is no successful server running or all servers' shutdown, this value is 0. serverRunning = gtype.NewInt() // wsUpGrader is the default up-grader configuration for websocket. From abc8e62d5842f91122a2563276865a21c12a30a6 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 24 Aug 2021 21:18:59 +0800 Subject: [PATCH 473/492] add package gcode and improve error implements for package gerror --- container/garray/garray_normal_any.go | 9 +- container/garray/garray_normal_int.go | 9 +- container/garray/garray_normal_str.go | 9 +- container/gpool/gpool.go | 5 +- crypto/gaes/gaes.go | 15 +-- crypto/gdes/gdes.go | 24 ++--- database/gdb/gdb.go | 11 ++- database/gdb/gdb_core.go | 5 +- database/gdb/gdb_core_underlying.go | 5 +- database/gdb/gdb_driver_mssql.go | 7 +- database/gdb/gdb_driver_mysql.go | 3 +- database/gdb/gdb_driver_oracle.go | 7 +- database/gdb/gdb_driver_pgsql.go | 7 +- database/gdb/gdb_driver_sqlite.go | 7 +- database/gdb/gdb_func.go | 3 +- database/gdb/gdb_model_delete.go | 3 +- database/gdb/gdb_model_insert.go | 11 ++- database/gdb/gdb_model_select.go | 5 +- database/gdb/gdb_model_update.go | 5 +- database/gdb/gdb_model_with.go | 5 +- database/gdb/gdb_statement.go | 3 +- database/gdb/gdb_type_result_scanlist.go | 31 +++--- database/gdb/gdb_z_mysql_struct_test.go | 3 +- database/gredis/gredis_config.go | 3 +- database/gredis/gredis_conn.go | 5 +- encoding/gcharset/gcharset.go | 9 +- encoding/gini/gini.go | 3 +- encoding/gjson/gjson_api_new_load.go | 3 +- errors/gcode/gcode.go | 95 +++++++++++++++++++ errors/gerror/gerror.go | 51 ++++------ errors/gerror/gerror_code.go | 70 -------------- errors/gerror/gerror_error.go | 17 ++-- errors/gerror/gerror_option.go | 10 +- errors/gerror/gerror_z_bench_test.go | 13 +-- errors/gerror/gerror_z_example_test.go | 15 +-- errors/gerror/gerror_z_unit_test.go | 66 ++++--------- frame/gins/gins_database.go | 7 +- i18n/gi18n/gi18n_manager.go | 3 +- net/ghttp/ghttp_func.go | 7 +- .../ghttp_middleware_handler_response.go | 9 +- net/ghttp/ghttp_request_middleware.go | 3 +- net/ghttp/ghttp_request_param.go | 9 +- net/ghttp/ghttp_request_param_file.go | 7 +- net/ghttp/ghttp_server.go | 9 +- net/ghttp/ghttp_server_admin_process.go | 9 +- net/ghttp/ghttp_server_graceful.go | 3 +- net/ghttp/ghttp_server_handler.go | 7 +- net/ghttp/ghttp_server_router.go | 3 +- net/ghttp/ghttp_server_router_serve.go | 3 +- net/ghttp/ghttp_server_service_handler.go | 9 +- net/ghttp/internal/client/client.go | 7 +- net/ghttp/internal/client/client_request.go | 3 +- net/gipv4/gipv4_ip.go | 3 +- net/gtcp/gtcp_server.go | 3 +- net/gudp/gudp_server.go | 3 +- os/gcfg/gcfg_config.go | 9 +- os/gcfg/gcfg_config_api.go | 19 ++-- os/gcmd/gcmd_handler.go | 9 +- os/gcmd/gcmd_parser.go | 3 +- os/gcmd/gcmd_parser_handler.go | 9 +- os/gcron/gcron_entry.go | 2 +- os/gcron/gcron_schedule.go | 12 +-- os/gfile/gfile_copy.go | 13 +-- os/gfile/gfile_home.go | 5 +- os/gfile/gfile_scan.go | 3 +- os/gfile/gfile_search.go | 3 +- os/gfpool/gfpool_file.go | 3 +- os/gfsnotify/gfsnotify.go | 3 +- os/gfsnotify/gfsnotify_watcher.go | 3 +- os/glog/glog_logger_config.go | 11 ++- os/glog/glog_logger_level.go | 3 +- os/gproc/gproc_comm.go | 3 +- os/gproc/gproc_process.go | 3 +- os/grpool/grpool.go | 5 +- os/gsession/gsession.go | 3 +- os/gsession/gsession_session.go | 5 +- os/gsession/gsession_storage_file.go | 7 +- os/gspath/gspath.go | 9 +- os/gtime/gtime.go | 7 +- os/gtime/gtime_time.go | 3 +- os/gview/gview_config.go | 11 ++- os/gview/gview_parse.go | 7 +- util/gconv/gconv_maptomap.go | 5 +- util/gconv/gconv_maptomaps.go | 11 ++- util/gconv/gconv_scan.go | 7 +- util/gconv/gconv_struct.go | 15 +-- util/gconv/gconv_structs.go | 7 +- util/gvalid/gvalid_error.go | 13 +-- util/gvalid/gvalid_validator_check_map.go | 4 +- util/gvalid/gvalid_validator_check_struct.go | 4 +- util/gvalid/gvalid_validator_check_value.go | 15 +-- util/gvalid/gvalid_z_unit_basic_all_test.go | 4 +- 92 files changed, 492 insertions(+), 425 deletions(-) create mode 100644 errors/gcode/gcode.go delete mode 100644 errors/gerror/gerror_code.go diff --git a/container/garray/garray_normal_any.go b/container/garray/garray_normal_any.go index 0e16736de..07b33c813 100644 --- a/container/garray/garray_normal_any.go +++ b/container/garray/garray_normal_any.go @@ -9,6 +9,7 @@ package garray import ( "bytes" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/internal/json" @@ -123,7 +124,7 @@ func (a *Array) Set(index int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } a.array[index] = value return nil @@ -176,7 +177,7 @@ func (a *Array) InsertBefore(index int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]interface{}{}, a.array[index:]...) a.array = append(a.array[0:index], value) @@ -189,7 +190,7 @@ func (a *Array) InsertAfter(index int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]interface{}{}, a.array[index+1:]...) a.array = append(a.array[0:index+1], value) @@ -545,7 +546,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) error { a.mu.Lock() defer a.mu.Unlock() if startIndex < 0 || startIndex > len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) } for i := startIndex; i < startIndex+num; i++ { if i > len(a.array)-1 { diff --git a/container/garray/garray_normal_int.go b/container/garray/garray_normal_int.go index 44e577383..ee3d173ba 100644 --- a/container/garray/garray_normal_int.go +++ b/container/garray/garray_normal_int.go @@ -9,6 +9,7 @@ package garray import ( "bytes" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "math" @@ -104,7 +105,7 @@ func (a *IntArray) Set(index int, value int) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } a.array[index] = value return nil @@ -172,7 +173,7 @@ func (a *IntArray) InsertBefore(index int, value int) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]int{}, a.array[index:]...) a.array = append(a.array[0:index], value) @@ -185,7 +186,7 @@ func (a *IntArray) InsertAfter(index int, value int) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]int{}, a.array[index+1:]...) a.array = append(a.array[0:index+1], value) @@ -556,7 +557,7 @@ func (a *IntArray) Fill(startIndex int, num int, value int) error { a.mu.Lock() defer a.mu.Unlock() if startIndex < 0 || startIndex > len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) } for i := startIndex; i < startIndex+num; i++ { if i > len(a.array)-1 { diff --git a/container/garray/garray_normal_str.go b/container/garray/garray_normal_str.go index 61fd0e8d0..11cb6551b 100644 --- a/container/garray/garray_normal_str.go +++ b/container/garray/garray_normal_str.go @@ -8,6 +8,7 @@ package garray import ( "bytes" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/text/gstr" @@ -90,7 +91,7 @@ func (a *StrArray) Set(index int, value string) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } a.array[index] = value return nil @@ -159,7 +160,7 @@ func (a *StrArray) InsertBefore(index int, value string) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]string{}, a.array[index:]...) a.array = append(a.array[0:index], value) @@ -172,7 +173,7 @@ func (a *StrArray) InsertAfter(index int, value string) error { a.mu.Lock() defer a.mu.Unlock() if index < 0 || index >= len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array)) } rear := append([]string{}, a.array[index+1:]...) a.array = append(a.array[0:index+1], value) @@ -559,7 +560,7 @@ func (a *StrArray) Fill(startIndex int, num int, value string) error { a.mu.Lock() defer a.mu.Unlock() if startIndex < 0 || startIndex > len(a.array) { - return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) + return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array)) } for i := startIndex; i < startIndex+num; i++ { if i > len(a.array)-1 { diff --git a/container/gpool/gpool.go b/container/gpool/gpool.go index 0022cfb28..4b7e5e583 100644 --- a/container/gpool/gpool.go +++ b/container/gpool/gpool.go @@ -8,6 +8,7 @@ package gpool import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "time" @@ -66,7 +67,7 @@ func New(ttl time.Duration, newFunc NewFunc, expireFunc ...ExpireFunc) *Pool { // Put puts an item to pool. func (p *Pool) Put(value interface{}) error { if p.closed.Val() { - return gerror.NewCode(gerror.CodeInvalidOperation, "pool is closed") + return gerror.NewCode(gcode.CodeInvalidOperation, "pool is closed") } item := &poolItem{ value: value, @@ -117,7 +118,7 @@ func (p *Pool) Get() (interface{}, error) { if p.NewFunc != nil { return p.NewFunc() } - return nil, gerror.NewCode(gerror.CodeInvalidOperation, "pool is empty") + return nil, gerror.NewCode(gcode.CodeInvalidOperation, "pool is empty") } // Size returns the count of available items of pool. diff --git a/crypto/gaes/gaes.go b/crypto/gaes/gaes.go index 2fba47c19..43bc0600a 100644 --- a/crypto/gaes/gaes.go +++ b/crypto/gaes/gaes.go @@ -11,6 +11,7 @@ import ( "bytes" "crypto/aes" "crypto/cipher" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" ) @@ -63,7 +64,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) { } blockSize := block.BlockSize() if len(cipherText) < blockSize { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "cipherText too short") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "cipherText too short") } ivValue := ([]byte)(nil) if len(iv) > 0 { @@ -72,7 +73,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) { ivValue = []byte(IVDefaultValue) } if len(cipherText)%blockSize != 0 { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "cipherText is not a multiple of the block size") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "cipherText is not a multiple of the block size") } blockModel := cipher.NewCBCDecrypter(block, ivValue) plainText := make([]byte, len(cipherText)) @@ -93,22 +94,22 @@ func PKCS5Padding(src []byte, blockSize int) []byte { func PKCS5UnPadding(src []byte, blockSize int) ([]byte, error) { length := len(src) if blockSize <= 0 { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid blocklen") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid blocklen") } if length%blockSize != 0 || length == 0 { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid data len") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid data len") } unpadding := int(src[length-1]) if unpadding > blockSize || unpadding == 0 { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid padding") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid padding") } padding := src[length-unpadding:] for i := 0; i < unpadding; i++ { if padding[i] != byte(unpadding) { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid padding") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid padding") } } @@ -146,7 +147,7 @@ func DecryptCFB(cipherText []byte, key []byte, unPadding int, iv ...[]byte) ([]b return nil, err } if len(cipherText) < aes.BlockSize { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "cipherText too short") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "cipherText too short") } ivValue := ([]byte)(nil) if len(iv) > 0 { diff --git a/crypto/gdes/gdes.go b/crypto/gdes/gdes.go index e12113719..00d030e75 100644 --- a/crypto/gdes/gdes.go +++ b/crypto/gdes/gdes.go @@ -66,7 +66,7 @@ func DecryptECB(cipherText []byte, key []byte, padding int) ([]byte, error) { // The length of the <key> should be either 16 or 24 bytes. func EncryptECBTriple(plainText []byte, key []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length error") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "key length error") } text, err := Padding(plainText, padding) @@ -100,7 +100,7 @@ func EncryptECBTriple(plainText []byte, key []byte, padding int) ([]byte, error) // The length of the <key> should be either 16 or 24 bytes. func DecryptECBTriple(cipherText []byte, key []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length error") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "key length error") } var newKey []byte @@ -138,7 +138,7 @@ func EncryptCBC(plainText []byte, key []byte, iv []byte, padding int) ([]byte, e } if len(iv) != block.BlockSize() { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "iv length invalid") } text, err := Padding(plainText, padding) @@ -161,7 +161,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv []byte, padding int) ([]byte, } if len(iv) != block.BlockSize() { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "iv length invalid") } text := make([]byte, len(cipherText)) @@ -179,7 +179,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv []byte, padding int) ([]byte, // EncryptCBCTriple encrypts <plainText> using TripleDES and CBC mode. func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length invalid") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "key length invalid") } var newKey []byte @@ -196,7 +196,7 @@ func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]b } if len(iv) != block.BlockSize() { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "iv length invalid") } text, err := Padding(plainText, padding) @@ -214,7 +214,7 @@ func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]b // DecryptCBCTriple decrypts <cipherText> using TripleDES and CBC mode. func DecryptCBCTriple(cipherText []byte, key []byte, iv []byte, padding int) ([]byte, error) { if len(key) != 16 && len(key) != 24 { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length invalid") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "key length invalid") } var newKey []byte @@ -231,7 +231,7 @@ func DecryptCBCTriple(cipherText []byte, key []byte, iv []byte, padding int) ([] } if len(iv) != block.BlockSize() { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "iv length invalid") } text := make([]byte, len(cipherText)) @@ -262,12 +262,12 @@ func Padding(text []byte, padding int) ([]byte, error) { switch padding { case NOPADDING: if len(text)%8 != 0 { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "text length invalid") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "text length invalid") } case PKCS5PADDING: return PaddingPKCS5(text, 8), nil default: - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "padding type error") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "padding type error") } return text, nil @@ -277,12 +277,12 @@ func UnPadding(text []byte, padding int) ([]byte, error) { switch padding { case NOPADDING: if len(text)%8 != 0 { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "text length invalid") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "text length invalid") } case PKCS5PADDING: return UnPaddingPKCS5(text), nil default: - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "padding type error") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "padding type error") } return text, nil } diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 030d9a336..49dd32c3a 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -10,6 +10,7 @@ package gdb import ( "context" "database/sql" + "github.com/gogf/gf/errors/gcode" "time" "github.com/gogf/gf/errors/gerror" @@ -336,7 +337,7 @@ func New(group ...string) (db DB, err error) { if len(configs.config) < 1 { return nil, gerror.NewCode( - gerror.CodeInvalidConfiguration, + gcode.CodeInvalidConfiguration, "database configuration is empty, please set the database configuration before using", ) } @@ -358,7 +359,7 @@ func New(group ...string) (db DB, err error) { return c.db, nil } else { return nil, gerror.NewCodef( - gerror.CodeInvalidConfiguration, + gcode.CodeInvalidConfiguration, `cannot find database driver for specified database type "%s", did you misspell type name "%s" or forget importing the database driver?`, node.Type, node.Type, ) @@ -368,7 +369,7 @@ func New(group ...string) (db DB, err error) { } } else { return nil, gerror.NewCodef( - gerror.CodeInvalidConfiguration, + gcode.CodeInvalidConfiguration, `database configuration node "%s" is not found, did you misspell group name "%s" or miss the database configuration?`, groupName, groupName, ) @@ -411,7 +412,7 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) { } } if len(masterList) < 1 { - return nil, gerror.NewCode(gerror.CodeInvalidConfiguration, "at least one master node configuration's need to make sense") + return nil, gerror.NewCode(gcode.CodeInvalidConfiguration, "at least one master node configuration's need to make sense") } if len(slaveList) < 1 { slaveList = masterList @@ -422,7 +423,7 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) { return getConfigNodeByWeight(slaveList), nil } } else { - return nil, gerror.NewCodef(gerror.CodeInvalidConfiguration, "empty database configuration for item name '%s'", group) + return nil, gerror.NewCodef(gcode.CodeInvalidConfiguration, "empty database configuration for item name '%s'", group) } } diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 48512fc39..34954d65b 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -11,6 +11,7 @@ import ( "context" "database/sql" "fmt" + "github.com/gogf/gf/errors/gcode" "reflect" "strings" @@ -89,7 +90,7 @@ func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Cont return context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout) } default: - panic(gerror.NewCodef(gerror.CodeInvalidParameter, "invalid context timeout type: %d", timeoutType)) + panic(gerror.NewCodef(gcode.CodeInvalidParameter, "invalid context timeout type: %d", timeoutType)) } return ctx, func() {} } @@ -552,7 +553,7 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter updates = gconv.String(data) } if len(updates) == 0 { - return nil, gerror.NewCode(gerror.CodeMissingParameter, "data cannot be empty") + return nil, gerror.NewCode(gcode.CodeMissingParameter, "data cannot be empty") } if len(params) > 0 { args = append(params, args...) diff --git a/database/gdb/gdb_core_underlying.go b/database/gdb/gdb_core_underlying.go index 80dd32d58..8e88d0938 100644 --- a/database/gdb/gdb_core_underlying.go +++ b/database/gdb/gdb_core_underlying.go @@ -10,6 +10,7 @@ package gdb import ( "context" "database/sql" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" @@ -136,7 +137,7 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf func (c *Core) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { if c.db.GetConfig().CtxStrict { if v := ctx.Value(ctxStrictKeyName); v == nil { - return sql, args, gerror.NewCode(gerror.CodeMissingParameter, ctxStrictErrorStr) + return sql, args, gerror.NewCode(gcode.CodeMissingParameter, ctxStrictErrorStr) } } return sql, args, nil @@ -181,7 +182,7 @@ func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, err if c.db.GetConfig().CtxStrict { if v := ctx.Value(ctxStrictKeyName); v == nil { - return nil, gerror.NewCode(gerror.CodeMissingParameter, ctxStrictErrorStr) + return nil, gerror.NewCode(gcode.CodeMissingParameter, ctxStrictErrorStr) } } diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index c5ac9be8a..48a83590e 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -15,6 +15,7 @@ import ( "context" "database/sql" "fmt" + "github.com/gogf/gf/errors/gcode" "strconv" "strings" @@ -214,7 +215,7 @@ func (d *DriverMssql) TableFields(ctx context.Context, table string, schema ...s charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations") } useSchema := d.db.GetSchema() if len(schema) > 0 && schema[0] != "" { @@ -292,10 +293,10 @@ ORDER BY a.id,a.colorder`, func (d *DriverMssql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { switch option.InsertOption { case insertOptionSave: - return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by mssql driver`) + return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by mssql driver`) case insertOptionReplace: - return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by mssql driver`) + return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by mssql driver`) default: return d.Core.DoInsert(ctx, link, table, list, option) diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index d4c5e53f0..251c37aed 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -10,6 +10,7 @@ import ( "context" "database/sql" "fmt" + "github.com/gogf/gf/errors/gcode" "net/url" "github.com/gogf/gf/errors/gerror" @@ -121,7 +122,7 @@ func (d *DriverMysql) TableFields(ctx context.Context, table string, schema ...s charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations") } useSchema := d.schema.Val() if len(schema) > 0 && schema[0] != "" { diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index d42cd1974..d3e3d157e 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -15,6 +15,7 @@ import ( "context" "database/sql" "fmt" + "github.com/gogf/gf/errors/gcode" "reflect" "strconv" "strings" @@ -186,7 +187,7 @@ func (d *DriverOracle) TableFields(ctx context.Context, table string, schema ... charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations") } useSchema := d.db.GetSchema() if len(schema) > 0 && schema[0] != "" { @@ -278,10 +279,10 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[ func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { switch option.InsertOption { case insertOptionSave: - return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by mssql driver`) + return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by mssql driver`) case insertOptionReplace: - return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by mssql driver`) + return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by mssql driver`) } var ( diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 07a8029fc..c0dbab9c9 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -15,6 +15,7 @@ import ( "context" "database/sql" "fmt" + "github.com/gogf/gf/errors/gcode" "strings" "github.com/gogf/gf/errors/gerror" @@ -126,7 +127,7 @@ func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...s charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations") } table, _ = gregex.ReplaceString("\"", "", table) useSchema := d.db.GetSchema() @@ -190,10 +191,10 @@ ORDER BY a.attnum`, func (d *DriverPgsql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { switch option.InsertOption { case insertOptionSave: - return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by pgsql driver`) + return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by pgsql driver`) case insertOptionReplace: - return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by pgsql driver`) + return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by pgsql driver`) default: return d.Core.DoInsert(ctx, link, table, list, option) diff --git a/database/gdb/gdb_driver_sqlite.go b/database/gdb/gdb_driver_sqlite.go index 61703aa43..405a391a8 100644 --- a/database/gdb/gdb_driver_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -14,6 +14,7 @@ import ( "context" "database/sql" "fmt" + "github.com/gogf/gf/errors/gcode" "strings" "github.com/gogf/gf/errors/gerror" @@ -99,7 +100,7 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ... charL, charR := d.GetChars() table = gstr.Trim(table, charL+charR) if gstr.Contains(table, " ") { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations") } useSchema := d.db.GetSchema() if len(schema) > 0 && schema[0] != "" { @@ -141,10 +142,10 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ... func (d *DriverSqlite) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { switch option.InsertOption { case insertOptionSave: - return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by sqlite driver`) + return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`) case insertOptionReplace: - return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by sqlite driver`) + return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by sqlite driver`) default: return d.Core.DoInsert(ctx, link, table, list, option) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 1468cca3e..37ae7a8c3 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -10,6 +10,7 @@ import ( "bytes" "database/sql" "fmt" + "github.com/gogf/gf/errors/gcode" "reflect" "regexp" "strings" @@ -776,7 +777,7 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i // formatError customizes and returns the SQL error. func formatError(err error, s string, args ...interface{}) error { if err != nil && err != sql.ErrNoRows { - return gerror.NewCodef(gerror.CodeDbOperationError, "%s, %s\n", err.Error(), FormatSqlWithArgs(s, args)) + return gerror.NewCodef(gcode.CodeDbOperationError, "%s, %s\n", err.Error(), FormatSqlWithArgs(s, args)) } return err } diff --git a/database/gdb/gdb_model_delete.go b/database/gdb/gdb_model_delete.go index 43af4544c..b3f264f7d 100644 --- a/database/gdb/gdb_model_delete.go +++ b/database/gdb/gdb_model_delete.go @@ -9,6 +9,7 @@ package gdb import ( "database/sql" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" @@ -44,7 +45,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) { } conditionStr := conditionWhere + conditionExtra if !gstr.ContainsI(conditionStr, " WHERE ") { - return nil, gerror.NewCode(gerror.CodeMissingParameter, "there should be WHERE condition statement for DELETE operation") + return nil, gerror.NewCode(gcode.CodeMissingParameter, "there should be WHERE condition statement for DELETE operation") } return m.db.DoDelete(m.GetCtx(), m.getLink(true), m.tables, conditionStr, conditionArgs...) } diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 90250c81b..5c40a7eba 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -9,6 +9,7 @@ package gdb import ( "database/sql" "github.com/gogf/gf/container/gset" + "github.com/gogf/gf/errors/gcode" "reflect" "github.com/gogf/gf/errors/gerror" @@ -210,7 +211,7 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err } }() if m.data == nil { - return nil, gerror.NewCode(gerror.CodeMissingParameter, "inserting into table with empty data") + return nil, gerror.NewCode(gcode.CodeMissingParameter, "inserting into table with empty data") } var ( list List @@ -274,12 +275,12 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err } default: - return result, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported list type:%v", kind) + return result, gerror.NewCodef(gcode.CodeInvalidParameter, "unsupported list type:%v", kind) } } if len(list) < 1 { - return result, gerror.NewCode(gerror.CodeMissingParameter, "data list cannot be empty") + return result, gerror.NewCode(gcode.CodeMissingParameter, "data list cannot be empty") } // Automatic handling for creating/updating time. @@ -364,7 +365,7 @@ func (m *Model) formatDoInsertOption(insertOption int, columnNames []string) (op default: return option, gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `unsupported OnDuplicate parameter type "%s"`, reflect.TypeOf(m.onDuplicate), ) @@ -408,7 +409,7 @@ func (m *Model) formatOnDuplicateExKeys(onDuplicateEx interface{}) ([]string, er default: return nil, gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `unsupported OnDuplicateEx parameter type "%s"`, reflect.TypeOf(onDuplicateEx), ) diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 78fc25735..eefbec99b 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -8,6 +8,7 @@ package gdb import ( "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "reflect" @@ -316,7 +317,7 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error { reflectKind = reflectValue.Kind() if reflectKind != reflect.Ptr { - return gerror.NewCode(gerror.CodeInvalidParameter, `the parameter "pointer" for function Scan should type of pointer`) + return gerror.NewCode(gcode.CodeInvalidParameter, `the parameter "pointer" for function Scan should type of pointer`) } for reflectKind == reflect.Ptr { reflectValue = reflectValue.Elem() @@ -332,7 +333,7 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error { default: return gerror.NewCode( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `element of parameter "pointer" for function Scan should type of struct/*struct/[]struct/[]*struct`, ) } diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index 12f2ab492..4826b4d43 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -9,6 +9,7 @@ package gdb import ( "database/sql" "fmt" + "github.com/gogf/gf/errors/gcode" "reflect" "github.com/gogf/gf/errors/gerror" @@ -38,7 +39,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro } }() if m.data == nil { - return nil, gerror.NewCode(gerror.CodeMissingParameter, "updating table with empty data") + return nil, gerror.NewCode(gcode.CodeMissingParameter, "updating table with empty data") } var ( updateData = m.data @@ -76,7 +77,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro } conditionStr := conditionWhere + conditionExtra if !gstr.ContainsI(conditionStr, " WHERE ") { - return nil, gerror.NewCode(gerror.CodeMissingParameter, "there should be WHERE condition statement for UPDATE operation") + return nil, gerror.NewCode(gcode.CodeMissingParameter, "there should be WHERE condition statement for UPDATE operation") } return m.db.DoUpdate( m.GetCtx(), diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 52ae7d617..542f302a9 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -8,6 +8,7 @@ package gdb import ( "fmt" + "github.com/gogf/gf/errors/gcode" "reflect" "github.com/gogf/gf/errors/gerror" @@ -124,7 +125,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { } if relatedFieldValue == nil { return gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `cannot find the related value of attribute name "%s" in with tag "%s" for attribute "%s.%s"`, relatedAttrName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(), ) @@ -239,7 +240,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { } if relatedFieldValue == nil { return gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `cannot find the related value for attribute name "%s" of with tag "%s"`, relatedAttrName, parsedTagOutput.With, ) diff --git a/database/gdb/gdb_statement.go b/database/gdb/gdb_statement.go index 9bca2980f..24f755eb7 100644 --- a/database/gdb/gdb_statement.go +++ b/database/gdb/gdb_statement.go @@ -9,6 +9,7 @@ package gdb import ( "context" "database/sql" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" @@ -59,7 +60,7 @@ func (s *Stmt) doStmtCommit(ctx context.Context, stmtType string, args ...interf result = s.Stmt.QueryRowContext(ctx, args...) default: - panic(gerror.NewCodef(gerror.CodeInvalidParameter, `invalid stmtType: %s`, stmtType)) + panic(gerror.NewCodef(gcode.CodeInvalidParameter, `invalid stmtType: %s`, stmtType)) } var ( timestampMilli2 = gtime.TimestampMilli() diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index ee738ad11..4468c3bad 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -8,6 +8,7 @@ package gdb import ( "database/sql" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" @@ -55,7 +56,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } // Necessary checks for parameters. if bindToAttrName == "" { - return gerror.NewCode(gerror.CodeInvalidParameter, `bindToAttrName should not be empty`) + return gerror.NewCode(gcode.CodeInvalidParameter, `bindToAttrName should not be empty`) } var ( @@ -67,12 +68,12 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr reflectKind = reflectValue.Kind() } if reflectKind != reflect.Ptr { - return gerror.NewCodef(gerror.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind) + return gerror.NewCodef(gcode.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind) } reflectValue = reflectValue.Elem() reflectKind = reflectValue.Kind() if reflectKind != reflect.Slice && reflectKind != reflect.Array { - return gerror.NewCodef(gerror.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind) + return gerror.NewCodef(gcode.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind) } length := len(result) if length == 0 { @@ -136,7 +137,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr relationBindToSubAttrName = array[1] if key, _ := gutil.MapPossibleItemByKey(result[0].Map(), relationResultFieldName); key == "" { return gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `cannot find possible related table field name "%s" from given relation key "%s"`, relationResultFieldName, relationKVStr, @@ -145,14 +146,14 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr relationResultFieldName = key } } else { - return gerror.NewCode(gerror.CodeInvalidParameter, `parameter relationKV should be format of "ResultFieldName:BindToAttrName"`) + return gerror.NewCode(gcode.CodeInvalidParameter, `parameter relationKV should be format of "ResultFieldName:BindToAttrName"`) } if relationResultFieldName != "" { // Note that the value might be type of slice. relationDataMap = result.MapKeyValue(relationResultFieldName) } if len(relationDataMap) == 0 { - return gerror.NewCodef(gerror.CodeInvalidParameter, `cannot find the relation data map, maybe invalid relation given "%v"`, relationKV) + return gerror.NewCodef(gcode.CodeInvalidParameter, `cannot find the relation data map, maybe invalid relation given "%v"`, relationKV) } } // Bind to target attribute. @@ -166,7 +167,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr if arrayItemType.Kind() == reflect.Ptr { if bindToAttrField, ok = arrayItemType.Elem().FieldByName(bindToAttrName); !ok { return gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, bindToAttrName, ) @@ -174,7 +175,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } else { if bindToAttrField, ok = arrayItemType.FieldByName(bindToAttrName); !ok { return gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, bindToAttrName, ) @@ -219,7 +220,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr relationFromAttrValue = arrayElemValue } if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() { - return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) + return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) } // Check and find possible bind to attribute name. if relationKVStr != "" && !relationBindToSubAttrNameChecked { @@ -234,7 +235,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } if key, _ := gutil.MapPossibleItemByKey(filedMap, relationBindToSubAttrName); key == "" { return gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `cannot find possible related attribute name "%s" from given relation key "%s"`, relationBindToSubAttrName, relationKVStr, @@ -265,11 +266,11 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } } else { // May be the attribute does not exist yet. - return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) + return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) } } else { return gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `relationKey should not be empty as field "%s" is slice`, bindToAttrName, ) @@ -301,7 +302,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } } else { // May be the attribute does not exist yet. - return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) + return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) } } else { if i >= len(result) { @@ -345,7 +346,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } } else { // May be the attribute does not exist yet. - return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) + return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV) } } else { if i >= len(result) { @@ -369,7 +370,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr } default: - return gerror.NewCodef(gerror.CodeInvalidParameter, `unsupported attribute type: %s`, bindToAttrKind.String()) + return gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported attribute type: %s`, bindToAttrKind.String()) } } reflect.ValueOf(listPointer).Elem().Set(arrayValue) diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index 9f56f4772..5f12869a2 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -9,6 +9,7 @@ package gdb_test import ( "database/sql" "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gtime" @@ -408,7 +409,7 @@ func (user *User) UnmarshalValue(value interface{}) error { } return nil } - return gerror.NewCodef(gerror.CodeInvalidParameter, `unsupported value type for UnmarshalValue: %v`, reflect.TypeOf(value)) + return gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported value type for UnmarshalValue: %v`, reflect.TypeOf(value)) } func Test_Model_Scan_UnmarshalValue(t *testing.T) { diff --git a/database/gredis/gredis_config.go b/database/gredis/gredis_config.go index e39a96ddb..e6fcae37e 100644 --- a/database/gredis/gredis_config.go +++ b/database/gredis/gredis_config.go @@ -8,6 +8,7 @@ package gredis import ( "context" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" @@ -114,7 +115,7 @@ func ConfigFromStr(str string) (config *Config, err error) { config.Port = DefaultRedisPort } } else { - err = gerror.NewCodef(gerror.CodeInvalidConfiguration, `invalid redis configuration: "%s"`, str) + err = gerror.NewCodef(gcode.CodeInvalidConfiguration, `invalid redis configuration: "%s"`, str) } return } diff --git a/database/gredis/gredis_conn.go b/database/gredis/gredis_conn.go index ed5f90851..a56e9ddd7 100644 --- a/database/gredis/gredis_conn.go +++ b/database/gredis/gredis_conn.go @@ -9,6 +9,7 @@ package gredis import ( "context" "github.com/gogf/gf/container/gvar" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/os/gtime" @@ -50,7 +51,7 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{} if timeout > 0 { conn, ok := c.Conn.(redis.ConnWithTimeout) if !ok { - return gvar.New(nil), gerror.NewCode(gerror.CodeNotSupported, `current connection does not support "ConnWithTimeout"`) + return gvar.New(nil), gerror.NewCode(gcode.CodeNotSupported, `current connection does not support "ConnWithTimeout"`) } return conn.DoWithTimeout(timeout, commandName, args...) } @@ -107,7 +108,7 @@ func (c *Conn) ReceiveVar() (*gvar.Var, error) { func (c *Conn) ReceiveVarWithTimeout(timeout time.Duration) (*gvar.Var, error) { conn, ok := c.Conn.(redis.ConnWithTimeout) if !ok { - return gvar.New(nil), gerror.NewCode(gerror.CodeNotSupported, `current connection does not support "ConnWithTimeout"`) + return gvar.New(nil), gerror.NewCode(gcode.CodeNotSupported, `current connection does not support "ConnWithTimeout"`) } return resultToVar(conn.ReceiveWithTimeout(timeout)) } diff --git a/encoding/gcharset/gcharset.go b/encoding/gcharset/gcharset.go index af4998fc4..5d8137087 100644 --- a/encoding/gcharset/gcharset.go +++ b/encoding/gcharset/gcharset.go @@ -21,6 +21,7 @@ package gcharset import ( "bytes" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "io/ioutil" @@ -59,11 +60,11 @@ func Convert(dstCharset string, srcCharset string, src string) (dst string, err transform.NewReader(bytes.NewReader([]byte(src)), e.NewDecoder()), ) if err != nil { - return "", gerror.WrapCodef(gerror.CodeInternalError, err, "%s to utf8 failed", srcCharset) + return "", gerror.WrapCodef(gcode.CodeInternalError, err, "%s to utf8 failed", srcCharset) } src = string(tmp) } else { - return dst, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported srcCharset: %s", srcCharset) + return dst, gerror.NewCodef(gcode.CodeInvalidParameter, "unsupported srcCharset: %s", srcCharset) } } // Do the converting from UTF-8 to <dstCharset>. @@ -73,11 +74,11 @@ func Convert(dstCharset string, srcCharset string, src string) (dst string, err transform.NewReader(bytes.NewReader([]byte(src)), e.NewEncoder()), ) if err != nil { - return "", gerror.WrapCodef(gerror.CodeInternalError, err, "utf to %s failed", dstCharset) + return "", gerror.WrapCodef(gcode.CodeInternalError, err, "utf to %s failed", dstCharset) } dst = string(tmp) } else { - return dst, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported dstCharset: %s", dstCharset) + return dst, gerror.NewCodef(gcode.CodeInvalidParameter, "unsupported dstCharset: %s", dstCharset) } } else { dst = src diff --git a/encoding/gini/gini.go b/encoding/gini/gini.go index 43ae110da..0ff476b46 100644 --- a/encoding/gini/gini.go +++ b/encoding/gini/gini.go @@ -11,6 +11,7 @@ import ( "bufio" "bytes" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "io" @@ -70,7 +71,7 @@ func Decode(data []byte) (res map[string]interface{}, err error) { } if haveSection == false { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "failed to parse INI file, section not found") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "failed to parse INI file, section not found") } return res, nil } diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index 9c6c46130..f9b5c673b 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -9,6 +9,7 @@ package gjson import ( "bytes" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "reflect" @@ -264,7 +265,7 @@ func doLoadContentWithOptions(dataType string, data []byte, options Options) (*J return nil, err } default: - err = gerror.NewCode(gerror.CodeInvalidParameter, "unsupported type for loading") + err = gerror.NewCode(gcode.CodeInvalidParameter, "unsupported type for loading") } if err != nil { return nil, err diff --git a/errors/gcode/gcode.go b/errors/gcode/gcode.go new file mode 100644 index 000000000..6248ed0c7 --- /dev/null +++ b/errors/gcode/gcode.go @@ -0,0 +1,95 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package gcode provides universal error code definition and common error codes implements. +package gcode + +import "fmt" + +// Code is universal error code interface definition. +type Code interface { + // Code returns the integer number of current error code. + Code() int + + // Message returns the brief message for current error code. + Message() string + + // Detail returns the detailed information of current error code, + // which is mainly designed as an extension field for error code. + Detail() interface{} +} + +// localCode is an implementer for interface Code for internal usage only. +type localCode struct { + code int // Error code, usually an integer. + message string // Brief message for this error code. + detail interface{} // As type of interface, it is mainly designed as an extension field for error code. +} + +// ================================================================================================================ +// Common error code definition. +// There are reserved internal error code by framework: code < 1000. +// ================================================================================================================ + +var ( + CodeNil = localCode{-1, "", nil} // No error code specified. + CodeOK = localCode{0, "OK", nil} // It is OK. + CodeInternalError = localCode{50, "Internal Error", nil} // An error occurred internally. + CodeValidationFailed = localCode{51, "Validation Failed", nil} // Data validation failed. + CodeDbOperationError = localCode{52, "Database Operation Error", nil} // Database operation error. + CodeInvalidParameter = localCode{53, "Invalid Parameter", nil} // The given parameter for current operation is invalid. + CodeMissingParameter = localCode{54, "Missing Parameter", nil} // Parameter for current operation is missing. + CodeInvalidOperation = localCode{55, "Invalid Operation", nil} // The function cannot be used like this. + CodeInvalidConfiguration = localCode{56, "Invalid Configuration", nil} // The configuration is invalid for current operation. + CodeMissingConfiguration = localCode{57, "Missing Configuration", nil} // The configuration is missing for current operation. + CodeNotImplemented = localCode{58, "Not Implemented", nil} // The operation is not implemented yet. + CodeNotSupported = localCode{59, "Not Supported", nil} // The operation is not supported yet. + CodeOperationFailed = localCode{60, "Operation Failed", nil} // I tried, but I cannot give you what you want. + CodeNotAuthorized = localCode{61, "Not Authorized", nil} // Not Authorized. + CodeSecurityReason = localCode{62, "Security Reason", nil} // Security Reason. + CodeServerBusy = localCode{63, "Server Is Busy", nil} // Server is busy, please try again later. + CodeUnknown = localCode{64, "Unknown Error", nil} // Unknown error. + CodeResourceNotExist = localCode{65, "Resource Not Exist", nil} // Resource does not exist. + CodeInvalidRequest = localCode{66, "Invalid Request", nil} // Invalid request. + CodeBusinessValidationFailed = localCode{300, "Business Validation Failed", nil} // Business validation failed. +) + +// New creates and returns an error code. +// Note that it returns an interface object of Code. +func New(code int, message string, detail interface{}) Code { + return localCode{ + code: code, + message: message, + detail: detail, + } +} + +// Code returns the integer number of current error code. +func (c localCode) Code() int { + return c.code +} + +// Message returns the brief message for current error code. +func (c localCode) Message() string { + return c.message +} + +// Detail returns the detailed information of current error code, +// which is mainly designed as an extension field for error code. +func (c localCode) Detail() interface{} { + return c.detail +} + +// String returns current error code as a string. +func (c localCode) String() string { + if c.detail != nil { + return fmt.Sprintf(`%d:%s %v`, c.code, c.message, c.detail) + } + if c.message != "" { + return fmt.Sprintf(`%d:%s`, c.code, c.message) + } + return fmt.Sprintf(`%d`, c.code) +} diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index 7e1e9af03..268af2fab 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -12,35 +12,36 @@ package gerror import ( "fmt" + "github.com/gogf/gf/errors/gcode" ) // apiCode is the interface for Code feature. type apiCode interface { - Error() string // It should be an error. - Code() int + Error() string + Code() gcode.Code } // apiStack is the interface for Stack feature. type apiStack interface { - Error() string // It should be an error. + Error() string Stack() string } // apiCause is the interface for Cause feature. type apiCause interface { - Error() string // It should be an error. + Error() string Cause() error } // apiCurrent is the interface for Current feature. type apiCurrent interface { - Error() string // It should be an error. + Error() string Current() error } // apiNext is the interface for Next feature. type apiNext interface { - Error() string // It should be an error. + Error() string Next() error } @@ -49,7 +50,7 @@ func New(text string) error { return &Error{ stack: callers(), text: text, - code: CodeNil, + code: gcode.CodeNil, } } @@ -58,7 +59,7 @@ func Newf(format string, args ...interface{}) error { return &Error{ stack: callers(), text: fmt.Sprintf(format, args...), - code: CodeNil, + code: gcode.CodeNil, } } @@ -68,7 +69,7 @@ func NewSkip(skip int, text string) error { return &Error{ stack: callers(skip), text: text, - code: CodeNil, + code: gcode.CodeNil, } } @@ -78,7 +79,7 @@ func NewSkipf(skip int, format string, args ...interface{}) error { return &Error{ stack: callers(skip), text: fmt.Sprintf(format, args...), - code: CodeNil, + code: gcode.CodeNil, } } @@ -142,7 +143,7 @@ func WrapSkipf(skip int, err error, format string, args ...interface{}) error { } // NewCode creates and returns an error that has error code and given text. -func NewCode(code int, text ...string) error { +func NewCode(code gcode.Code, text ...string) error { errText := "" if len(text) > 0 { errText = text[0] @@ -155,7 +156,7 @@ func NewCode(code int, text ...string) error { } // NewCodef returns an error that has error code and formats as the given format and args. -func NewCodef(code int, format string, args ...interface{}) error { +func NewCodef(code gcode.Code, format string, args ...interface{}) error { return &Error{ stack: callers(), text: fmt.Sprintf(format, args...), @@ -165,7 +166,7 @@ func NewCodef(code int, format string, args ...interface{}) error { // NewCodeSkip creates and returns an error which has error code and is formatted from given text. // The parameter <skip> specifies the stack callers skipped amount. -func NewCodeSkip(code, skip int, text ...string) error { +func NewCodeSkip(code gcode.Code, skip int, text ...string) error { errText := "" if len(text) > 0 { errText = text[0] @@ -179,7 +180,7 @@ func NewCodeSkip(code, skip int, text ...string) error { // NewCodeSkipf returns an error that has error code and formats as the given format and args. // The parameter <skip> specifies the stack callers skipped amount. -func NewCodeSkipf(code, skip int, format string, args ...interface{}) error { +func NewCodeSkipf(code gcode.Code, skip int, format string, args ...interface{}) error { return &Error{ stack: callers(skip), text: fmt.Sprintf(format, args...), @@ -189,7 +190,7 @@ func NewCodeSkipf(code, skip int, format string, args ...interface{}) error { // WrapCode wraps error with code and text. // It returns nil if given err is nil. -func WrapCode(code int, err error, text ...string) error { +func WrapCode(code gcode.Code, err error, text ...string) error { if err == nil { return nil } @@ -207,7 +208,7 @@ func WrapCode(code int, err error, text ...string) error { // WrapCodef wraps error with code and format specifier. // It returns nil if given <err> is nil. -func WrapCodef(code int, err error, format string, args ...interface{}) error { +func WrapCodef(code gcode.Code, err error, format string, args ...interface{}) error { if err == nil { return nil } @@ -222,7 +223,7 @@ func WrapCodef(code int, err error, format string, args ...interface{}) error { // WrapCodeSkip wraps error with code and text. // It returns nil if given err is nil. // The parameter <skip> specifies the stack callers skipped amount. -func WrapCodeSkip(code, skip int, err error, text ...string) error { +func WrapCodeSkip(code gcode.Code, skip int, err error, text ...string) error { if err == nil { return nil } @@ -241,7 +242,7 @@ func WrapCodeSkip(code, skip int, err error, text ...string) error { // WrapCodeSkipf wraps error with code and text that is formatted with given format and args. // It returns nil if given err is nil. // The parameter <skip> specifies the stack callers skipped amount. -func WrapCodeSkipf(code, skip int, err error, format string, args ...interface{}) error { +func WrapCodeSkipf(code gcode.Code, skip int, err error, format string, args ...interface{}) error { if err == nil { return nil } @@ -255,23 +256,13 @@ func WrapCodeSkipf(code, skip int, err error, format string, args ...interface{} // Code returns the error code of current error. // It returns CodeNil if it has no error code or it does not implements interface Code. -func Code(err error) int { +func Code(err error) gcode.Code { if err != nil { if e, ok := err.(apiCode); ok { return e.Code() } } - return CodeNil -} - -// CodeMessage retrieves and returns the error code message from given error. -func CodeMessage(err error) string { - return Message(Code(err)) -} - -// Message returns the message string for specified code. -func Message(code int) string { - return codeMessageMap[code] + return gcode.CodeNil } // Cause returns the root cause error of <err>. diff --git a/errors/gerror/gerror_code.go b/errors/gerror/gerror_code.go deleted file mode 100644 index 699deaa11..000000000 --- a/errors/gerror/gerror_code.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gerror - -// Reserved internal error code of framework: code < 1000. - -const ( - CodeNil = -1 // No error code specified. - CodeOk = 0 // It is OK. - CodeInternalError = 50 // An error occurred internally. - CodeValidationFailed = 51 // Data validation failed. - CodeDbOperationError = 52 // Database operation error. - CodeInvalidParameter = 53 // The given parameter for current operation is invalid. - CodeMissingParameter = 54 // Parameter for current operation is missing. - CodeInvalidOperation = 55 // The function cannot be used like this. - CodeInvalidConfiguration = 56 // The configuration is invalid for current operation. - CodeMissingConfiguration = 57 // The configuration is missing for current operation. - CodeNotImplemented = 58 // The operation is not implemented yet. - CodeNotSupported = 59 // The operation is not supported yet. - CodeOperationFailed = 60 // I tried, but I cannot give you what you want. - CodeNotAuthorized = 61 // Not Authorized. - CodeSecurityReason = 62 // Security Reason. - CodeServerBusy = 63 // Server is busy, please try again later. - CodeUnknown = 64 // Unknown error. - CodeResourceNotExist = 65 // Resource does not exist. - CodeInvalidRequest = 66 // Invalid request. - CodeBusinessValidationFailed = 300 // Business validation failed. -) - -var ( - // codeMessageMap is the mapping from code to according string message. - codeMessageMap = map[int]string{ - CodeNil: "", - CodeOk: "OK", - CodeInternalError: "Internal Error", - CodeValidationFailed: "Validation Failed", - CodeDbOperationError: "Database Operation Error", - CodeInvalidParameter: "Invalid Parameter", - CodeMissingParameter: "Missing Parameter", - CodeInvalidOperation: "Invalid Operation", - CodeInvalidConfiguration: "Invalid Configuration", - CodeMissingConfiguration: "Missing Configuration", - CodeNotImplemented: "Not Implemented", - CodeNotSupported: "Not Supported", - CodeOperationFailed: "Operation Failed", - CodeNotAuthorized: "Not Authorized", - CodeSecurityReason: "Security Reason", - CodeServerBusy: "Server Is Busy", - CodeUnknown: "Unknown Error", - CodeResourceNotExist: "Resource Not Exist", - CodeInvalidRequest: "Invalid Request", - CodeBusinessValidationFailed: "Business Validation Failed", - } -) - -// RegisterCode registers custom error code to global error codes for gerror recognition. -func RegisterCode(code int, message string) { - codeMessageMap[code] = message -} - -// RegisterCodeMap registers custom error codes to global error codes for gerror recognition using map. -func RegisterCodeMap(codes map[int]string) { - for k, v := range codes { - codeMessageMap[k] = v - } -} diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index 97e22f0cd..bbc00bdf1 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -10,6 +10,7 @@ import ( "bytes" "errors" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/internal/utils" "io" "runtime" @@ -18,10 +19,10 @@ import ( // Error is custom error for additional features. type Error struct { - error error // Wrapped error. - stack stack // Stack array, which records the stack information when this error is created or wrapped. - text string // Error text, which is created by New* functions. - code int // Error code if necessary. + error error // Wrapped error. + stack stack // Stack array, which records the stack information when this error is created or wrapped. + text string // Error text, which is created by New* functions. + code gcode.Code // Error code if necessary. } const ( @@ -47,8 +48,8 @@ func (err *Error) Error() string { return "" } errStr := err.text - if errStr == "" { - errStr = Message(err.code) + if errStr == "" && err.code != nil { + errStr = err.code.Message() } if err.error != nil { if errStr != "" { @@ -61,9 +62,9 @@ func (err *Error) Error() string { // Code returns the error code. // It returns CodeNil if it has no error code. -func (err *Error) Code() int { +func (err *Error) Code() gcode.Code { if err == nil { - return CodeNil + return gcode.CodeNil } return err.code } diff --git a/errors/gerror/gerror_option.go b/errors/gerror/gerror_option.go index c7c33aa18..30c2c38e5 100644 --- a/errors/gerror/gerror_option.go +++ b/errors/gerror/gerror_option.go @@ -6,12 +6,14 @@ package gerror +import "github.com/gogf/gf/errors/gcode" + // Option is option for creating error. type Option struct { - Error error // Wrapped error if any. - Stack bool // Whether recording stack information into error. - Text string // Error text, which is created by New* functions. - Code int // Error code if necessary. + Error error // Wrapped error if any. + Stack bool // Whether recording stack information into error. + Text string // Error text, which is created by New* functions. + Code gcode.Code // Error code if necessary. } // NewOption creates and returns an error with Option. diff --git a/errors/gerror/gerror_z_bench_test.go b/errors/gerror/gerror_z_bench_test.go index fea2aaf8f..7e510abdb 100644 --- a/errors/gerror/gerror_z_bench_test.go +++ b/errors/gerror/gerror_z_bench_test.go @@ -8,6 +8,7 @@ package gerror_test import ( "errors" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "testing" ) @@ -55,36 +56,36 @@ func Benchmark_NewSkipf(b *testing.B) { func Benchmark_NewCode(b *testing.B) { for i := 0; i < b.N; i++ { - gerror.NewCode(500, "test") + gerror.NewCode(gcode.New(500, "", nil), "test") } } func Benchmark_NewCodef(b *testing.B) { for i := 0; i < b.N; i++ { - gerror.NewCodef(500, "%s", "test") + gerror.NewCodef(gcode.New(500, "", nil), "%s", "test") } } func Benchmark_NewCodeSkip(b *testing.B) { for i := 0; i < b.N; i++ { - gerror.NewCodeSkip(1, 500, "test") + gerror.NewCodeSkip(gcode.New(1, "", nil), 500, "test") } } func Benchmark_NewCodeSkipf(b *testing.B) { for i := 0; i < b.N; i++ { - gerror.NewCodeSkipf(1, 500, "%s", "test") + gerror.NewCodeSkipf(gcode.New(1, "", nil), 500, "%s", "test") } } func Benchmark_WrapCode(b *testing.B) { for i := 0; i < b.N; i++ { - gerror.WrapCode(500, baseError, "test") + gerror.WrapCode(gcode.New(500, "", nil), baseError, "test") } } func Benchmark_WrapCodef(b *testing.B) { for i := 0; i < b.N; i++ { - gerror.WrapCodef(500, baseError, "test") + gerror.WrapCodef(gcode.New(500, "", nil), baseError, "test") } } diff --git a/errors/gerror/gerror_z_example_test.go b/errors/gerror/gerror_z_example_test.go index 7c627dbbe..f52528854 100644 --- a/errors/gerror/gerror_z_example_test.go +++ b/errors/gerror/gerror_z_example_test.go @@ -9,11 +9,12 @@ package gerror_test import ( "errors" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" ) func ExampleNewCode() { - err := gerror.NewCode(10000, "My Error") + err := gerror.NewCode(gcode.New(10000, "", nil), "My Error") fmt.Println(err.Error()) fmt.Println(gerror.Code(err)) @@ -23,9 +24,9 @@ func ExampleNewCode() { } func ExampleNewCodef() { - err := gerror.NewCodef(10000, "It's %s", "My Error") + err := gerror.NewCodef(gcode.New(10000, "", nil), "It's %s", "My Error") fmt.Println(err.Error()) - fmt.Println(gerror.Code(err)) + fmt.Println(gerror.Code(err).Code()) // Output: // It's My Error @@ -34,9 +35,9 @@ func ExampleNewCodef() { func ExampleWrapCode() { err1 := errors.New("permission denied") - err2 := gerror.WrapCode(10000, err1, "Custom Error") + err2 := gerror.WrapCode(gcode.New(10000, "", nil), err1, "Custom Error") fmt.Println(err2.Error()) - fmt.Println(gerror.Code(err2)) + fmt.Println(gerror.Code(err2).Code()) // Output: // Custom Error: permission denied @@ -45,9 +46,9 @@ func ExampleWrapCode() { func ExampleWrapCodef() { err1 := errors.New("permission denied") - err2 := gerror.WrapCodef(10000, err1, "It's %s", "Custom Error") + err2 := gerror.WrapCodef(gcode.New(10000, "", nil), err1, "It's %s", "Custom Error") fmt.Println(err2.Error()) - fmt.Println(gerror.Code(err2)) + fmt.Println(gerror.Code(err2).Code()) // Output: // It's Custom Error: permission denied diff --git a/errors/gerror/gerror_z_unit_test.go b/errors/gerror/gerror_z_unit_test.go index 0bffed596..05521833c 100644 --- a/errors/gerror/gerror_z_unit_test.go +++ b/errors/gerror/gerror_z_unit_test.go @@ -9,6 +9,7 @@ package gerror_test import ( "errors" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/internal/json" "testing" @@ -261,51 +262,51 @@ func Test_Code(t *testing.T) { t.Assert(err.Error(), "123") }) gtest.C(t, func(t *gtest.T) { - err := gerror.NewCode(gerror.CodeUnknown, "123") - t.Assert(gerror.Code(err), gerror.CodeUnknown) + err := gerror.NewCode(gcode.CodeUnknown, "123") + t.Assert(gerror.Code(err), gcode.CodeUnknown) t.Assert(err.Error(), "123") }) gtest.C(t, func(t *gtest.T) { - err := gerror.NewCodef(1, "%s", "123") - t.Assert(gerror.Code(err), 1) + err := gerror.NewCodef(gcode.New(1, "", nil), "%s", "123") + t.Assert(gerror.Code(err).Code(), 1) t.Assert(err.Error(), "123") }) gtest.C(t, func(t *gtest.T) { - err := gerror.NewCodeSkip(1, 0, "123") - t.Assert(gerror.Code(err), 1) + err := gerror.NewCodeSkip(gcode.New(1, "", nil), 0, "123") + t.Assert(gerror.Code(err).Code(), 1) t.Assert(err.Error(), "123") }) gtest.C(t, func(t *gtest.T) { - err := gerror.NewCodeSkipf(1, 0, "%s", "123") - t.Assert(gerror.Code(err), 1) + err := gerror.NewCodeSkipf(gcode.New(1, "", nil), 0, "%s", "123") + t.Assert(gerror.Code(err).Code(), 1) t.Assert(err.Error(), "123") }) gtest.C(t, func(t *gtest.T) { err := errors.New("1") err = gerror.Wrap(err, "2") - err = gerror.WrapCode(1, err, "3") - t.Assert(gerror.Code(err), 1) + err = gerror.WrapCode(gcode.New(1, "", nil), err, "3") + t.Assert(gerror.Code(err).Code(), 1) t.Assert(err.Error(), "3: 2: 1") }) gtest.C(t, func(t *gtest.T) { err := errors.New("1") err = gerror.Wrap(err, "2") - err = gerror.WrapCodef(1, err, "%s", "3") - t.Assert(gerror.Code(err), 1) + err = gerror.WrapCodef(gcode.New(1, "", nil), err, "%s", "3") + t.Assert(gerror.Code(err).Code(), 1) t.Assert(err.Error(), "3: 2: 1") }) gtest.C(t, func(t *gtest.T) { err := errors.New("1") err = gerror.Wrap(err, "2") - err = gerror.WrapCodeSkip(1, 100, err, "3") - t.Assert(gerror.Code(err), 1) + err = gerror.WrapCodeSkip(gcode.New(1, "", nil), 100, err, "3") + t.Assert(gerror.Code(err).Code(), 1) t.Assert(err.Error(), "3: 2: 1") }) gtest.C(t, func(t *gtest.T) { err := errors.New("1") err = gerror.Wrap(err, "2") - err = gerror.WrapCodeSkipf(1, 100, err, "%s", "3") - t.Assert(gerror.Code(err), 1) + err = gerror.WrapCodeSkipf(gcode.New(1, "", nil), 100, err, "%s", "3") + t.Assert(gerror.Code(err).Code(), 1) t.Assert(err.Error(), "3: 2: 1") }) } @@ -318,36 +319,3 @@ func Test_Json(t *testing.T) { t.Assert(string(b), `"2: 1"`) }) } - -func Test_Message(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - t.Assert(gerror.Message(-100), ``) - t.Assert(gerror.Message(gerror.CodeNil), ``) - t.Assert(gerror.Message(gerror.CodeOk), `OK`) - }) -} - -func Test_CodeMessage(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - err := gerror.NewCode(gerror.CodeUnknown) - t.Assert(gerror.CodeMessage(err), `Unknown Error`) - }) -} - -func Test_RegisterCode(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - gerror.RegisterCode(10086, "MobileTelecom") - t.Assert(gerror.Message(10086), `MobileTelecom`) - }) -} - -func Test_RegisterCodeMap(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - gerror.RegisterCodeMap(map[int]string{ - 10087: "MobileTelecom 10087", - 10088: "MobileTelecom 10088", - }) - t.Assert(gerror.Message(10087), `MobileTelecom 10087`) - t.Assert(gerror.Message(10088), `MobileTelecom 10088`) - }) -} diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index dc49a18a6..e27f7ce73 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -9,6 +9,7 @@ package gins import ( "context" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/text/gstr" @@ -54,7 +55,7 @@ func Database(name ...string) gdb.DB { exampleFileName := "config.example.toml" if exampleConfigFilePath, _ := Config().GetFilePath(exampleFileName); exampleConfigFilePath != "" { panic(gerror.WrapCodef( - gerror.CodeMissingConfiguration, + gcode.CodeMissingConfiguration, err, `configuration file "%s" not found, but found "%s", did you miss renaming the example configuration file?`, Config().GetFileName(), @@ -62,7 +63,7 @@ func Database(name ...string) gdb.DB { )) } else { panic(gerror.WrapCodef( - gerror.CodeMissingConfiguration, + gcode.CodeMissingConfiguration, err, `configuration file "%s" not found, did you miss the configuration file or the misspell the configuration file name?`, Config().GetFileName(), @@ -70,7 +71,7 @@ func Database(name ...string) gdb.DB { } } panic(gerror.WrapCodef( - gerror.CodeMissingConfiguration, + gcode.CodeMissingConfiguration, err, `database initialization failed: "%s" node not found, is configuration file or configuration node missing?`, configNodeNameDatabase, diff --git a/i18n/gi18n/gi18n_manager.go b/i18n/gi18n/gi18n_manager.go index 90ca866d7..e5d18f7bc 100644 --- a/i18n/gi18n/gi18n_manager.go +++ b/i18n/gi18n/gi18n_manager.go @@ -9,6 +9,7 @@ package gi18n import ( "context" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "strings" @@ -101,7 +102,7 @@ func (m *Manager) SetPath(path string) error { } else { realPath, _ := gfile.Search(path) if realPath == "" { - return gerror.NewCodef(gerror.CodeInvalidParameter, `%s does not exist`, path) + return gerror.NewCodef(gcode.CodeInvalidParameter, `%s does not exist`, path) } m.options.Path = realPath } diff --git a/net/ghttp/ghttp_func.go b/net/ghttp/ghttp_func.go index 2c569c0ac..a4d96037e 100644 --- a/net/ghttp/ghttp_func.go +++ b/net/ghttp/ghttp_func.go @@ -7,6 +7,7 @@ package ghttp import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/net/ghttp/internal/httputil" ) @@ -36,13 +37,13 @@ func niceCallFunc(f func()) { // Note that there's a skip pointing the start stacktrace // of the real error point. if err, ok := exception.(error); ok { - if gerror.Code(err) != gerror.CodeNil { + if gerror.Code(err) != gcode.CodeNil { panic(err) } else { - panic(gerror.WrapCodeSkip(gerror.CodeInternalError, 1, err, "")) + panic(gerror.WrapCodeSkip(gcode.CodeInternalError, 1, err, "")) } } else { - panic(gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%+v", exception)) + panic(gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%+v", exception)) } } } diff --git a/net/ghttp/ghttp_middleware_handler_response.go b/net/ghttp/ghttp_middleware_handler_response.go index f8c71c318..e37e92374 100644 --- a/net/ghttp/ghttp_middleware_handler_response.go +++ b/net/ghttp/ghttp_middleware_handler_response.go @@ -7,6 +7,7 @@ package ghttp import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" ) @@ -28,11 +29,11 @@ func MiddlewareHandlerResponse(r *Request) { res, err = r.GetHandlerResponse() if err != nil { code := gerror.Code(err) - if code == gerror.CodeNil { - code = gerror.CodeInternalError + if code == gcode.CodeNil { + code = gcode.CodeInternalError } internalErr = r.Response.WriteJson(DefaultHandlerResponse{ - Code: code, + Code: code.Code(), Message: err.Error(), Data: nil, }) @@ -42,7 +43,7 @@ func MiddlewareHandlerResponse(r *Request) { return } internalErr = r.Response.WriteJson(DefaultHandlerResponse{ - Code: gerror.CodeOk, + Code: gcode.CodeOK.Code(), Message: "", Data: res, }) diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index 66dc66275..7f7956a37 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -7,6 +7,7 @@ package ghttp import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "net/http" "reflect" @@ -105,7 +106,7 @@ func (m *middleware) Next() { // Create a new error with stack info. // Note that there's a skip pointing the start stacktrace // of the real error point. - m.request.error = gerror.WrapCodeSkip(gerror.CodeInternalError, 1, exception, "") + m.request.error = gerror.WrapCodeSkip(gcode.CodeInternalError, 1, exception, "") } m.request.Response.WriteStatus(http.StatusInternalServerError, exception) loop = false diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index e0381e854..8c9689b2e 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -13,6 +13,7 @@ import ( "github.com/gogf/gf/encoding/gjson" "github.com/gogf/gf/encoding/gurl" "github.com/gogf/gf/encoding/gxml" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "github.com/gogf/gf/internal/utils" @@ -300,7 +301,7 @@ func (r *Request) parseQuery() { var err error r.queryMap, err = gstr.Parse(r.URL.RawQuery) if err != nil { - panic(gerror.WrapCode(gerror.CodeInvalidParameter, err, "")) + panic(gerror.WrapCode(gcode.CodeInvalidParameter, err, "")) } } } @@ -355,12 +356,12 @@ func (r *Request) parseForm() { if gstr.Contains(contentType, "multipart/") { // multipart/form-data, multipart/mixed if err = r.ParseMultipartForm(r.Server.config.FormParsingMemory); err != nil { - panic(gerror.WrapCode(gerror.CodeInvalidRequest, err, "")) + panic(gerror.WrapCode(gcode.CodeInvalidRequest, err, "")) } } else if gstr.Contains(contentType, "form") { // application/x-www-form-urlencoded if err = r.Request.ParseForm(); err != nil { - panic(gerror.WrapCode(gerror.CodeInvalidRequest, err, "")) + panic(gerror.WrapCode(gcode.CodeInvalidRequest, err, "")) } } if len(r.PostForm) > 0 { @@ -403,7 +404,7 @@ func (r *Request) parseForm() { } if params != "" { if r.formMap, err = gstr.Parse(params); err != nil { - panic(gerror.WrapCode(gerror.CodeInvalidParameter, err, "")) + panic(gerror.WrapCode(gcode.CodeInvalidParameter, err, "")) } } } diff --git a/net/ghttp/ghttp_request_param_file.go b/net/ghttp/ghttp_request_param_file.go index aea6961cf..7219117e2 100644 --- a/net/ghttp/ghttp_request_param_file.go +++ b/net/ghttp/ghttp_request_param_file.go @@ -8,6 +8,7 @@ package ghttp import ( "context" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfile" @@ -36,7 +37,7 @@ type UploadFiles []*UploadFile func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename string, err error) { if f == nil { return "", gerror.NewCode( - gerror.CodeMissingParameter, + gcode.CodeMissingParameter, "file is empty, maybe you retrieve it from invalid field name or form enctype", ) } @@ -45,7 +46,7 @@ func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename stri return } } else if !gfile.IsDir(dirPath) { - return "", gerror.NewCode(gerror.CodeInvalidParameter, `parameter "dirPath" should be a directory path`) + return "", gerror.NewCode(gcode.CodeInvalidParameter, `parameter "dirPath" should be a directory path`) } file, err := f.Open() @@ -80,7 +81,7 @@ func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename stri func (fs UploadFiles) Save(dirPath string, randomlyRename ...bool) (filenames []string, err error) { if len(fs) == 0 { return nil, gerror.NewCode( - gerror.CodeMissingParameter, + gcode.CodeMissingParameter, "file array is empty, maybe you retrieve it from invalid field name or form enctype", ) } diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index a0153aa0c..1b2eef058 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -10,6 +10,7 @@ import ( "bytes" "context" "github.com/gogf/gf/debug/gdebug" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "net/http" @@ -107,7 +108,7 @@ func GetServer(name ...interface{}) *Server { } // Initialize the server using default configurations. if err := s.SetConfig(NewConfig()); err != nil { - panic(gerror.WrapCode(gerror.CodeInvalidConfiguration, err, "")) + panic(gerror.WrapCode(gcode.CodeInvalidConfiguration, err, "")) } // Record the server to internal server mapping by name. serverMapping.Set(serverName, s) @@ -125,7 +126,7 @@ func (s *Server) Start() error { // Server can only be run once. if s.Status() == ServerStatusRunning { - return gerror.NewCode(gerror.CodeInvalidOperation, "server is already running") + return gerror.NewCode(gcode.CodeInvalidOperation, "server is already running") } // Logging path setting check. @@ -141,7 +142,7 @@ func (s *Server) Start() error { path = gfile.Join(s.config.SessionPath, s.name) if !gfile.Exists(path) { if err := gfile.Mkdir(path); err != nil { - return gerror.WrapCodef(gerror.CodeInternalError, err, `mkdir failed for "%s"`, path) + return gerror.WrapCodef(gcode.CodeInternalError, err, `mkdir failed for "%s"`, path) } } } @@ -176,7 +177,7 @@ func (s *Server) Start() error { // it then returns an error of invalid usage of server. if len(s.routesMap) == 0 && !s.config.FileServerEnabled { return gerror.NewCode( - gerror.CodeInvalidOperation, + gcode.CodeInvalidOperation, `there's no route set or static feature enabled, did you forget import the router?`, ) } diff --git a/net/ghttp/ghttp_server_admin_process.go b/net/ghttp/ghttp_server_admin_process.go index 9fe65b8d3..3f854dcbb 100644 --- a/net/ghttp/ghttp_server_admin_process.go +++ b/net/ghttp/ghttp_server_admin_process.go @@ -10,6 +10,7 @@ import ( "bytes" "context" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/text/gstr" @@ -52,7 +53,7 @@ var serverProcessStatus = gtype.NewInt() // The optional parameter <newExeFilePath> specifies the new binary file for creating process. func RestartAllServer(newExeFilePath ...string) error { if !gracefulEnabled { - return gerror.NewCode(gerror.CodeInvalidOperation, "graceful reload feature is disabled") + return gerror.NewCode(gcode.CodeInvalidOperation, "graceful reload feature is disabled") } serverActionLocker.Lock() defer serverActionLocker.Unlock() @@ -85,10 +86,10 @@ func checkProcessStatus() error { if status > 0 { switch status { case adminActionRestarting: - return gerror.NewCode(gerror.CodeInvalidOperation, "server is restarting") + return gerror.NewCode(gcode.CodeInvalidOperation, "server is restarting") case adminActionShuttingDown: - return gerror.NewCode(gerror.CodeInvalidOperation, "server is shutting down") + return gerror.NewCode(gcode.CodeInvalidOperation, "server is shutting down") } } return nil @@ -100,7 +101,7 @@ func checkActionFrequency() error { interval := gtime.TimestampMilli() - serverActionLastTime.Val() if interval < adminActionIntervalLimit { return gerror.NewCodef( - gerror.CodeInvalidOperation, + gcode.CodeInvalidOperation, "too frequent action, please retry in %d ms", adminActionIntervalLimit-interval, ) diff --git a/net/ghttp/ghttp_server_graceful.go b/net/ghttp/ghttp_server_graceful.go index 6af3a3368..8f6d60b53 100644 --- a/net/ghttp/ghttp_server_graceful.go +++ b/net/ghttp/ghttp_server_graceful.go @@ -10,6 +10,7 @@ import ( "context" "crypto/tls" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gproc" "github.com/gogf/gf/os/gres" @@ -122,7 +123,7 @@ func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig . } if err != nil { - return gerror.WrapCodef(gerror.CodeInternalError, err, `open cert file "%s","%s" failed`, certFile, keyFile) + return gerror.WrapCodef(gcode.CodeInternalError, err, `open cert file "%s","%s" failed`, certFile, keyFile) } ln, err := s.getNetListener() if err != nil { diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index 7803a295d..30d747c80 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -7,6 +7,7 @@ package ghttp import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/internal/intlog" "net/http" "os" @@ -67,13 +68,13 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { if exception := recover(); exception != nil { request.Response.WriteStatus(http.StatusInternalServerError) if err, ok := exception.(error); ok { - if code := gerror.Code(err); code != gerror.CodeNil { + if code := gerror.Code(err); code != gcode.CodeNil { s.handleErrorLog(err, request) } else { - s.handleErrorLog(gerror.WrapCodeSkip(gerror.CodeInternalError, 1, err, ""), request) + s.handleErrorLog(gerror.WrapCodeSkip(gcode.CodeInternalError, 1, err, ""), request) } } else { - s.handleErrorLog(gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%+v", exception), request) + s.handleErrorLog(gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%+v", exception), request) } } } diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index 1dd4b630b..83d061b5e 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -9,6 +9,7 @@ package ghttp import ( "fmt" "github.com/gogf/gf/container/gtype" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "strings" @@ -53,7 +54,7 @@ func (s *Server) parsePattern(pattern string) (domain, method, path string, err } } if path == "" { - err = gerror.NewCode(gerror.CodeInvalidParameter, "invalid pattern: URI should not be empty") + err = gerror.NewCode(gcode.CodeInvalidParameter, "invalid pattern: URI should not be empty") } if path != "/" { path = strings.TrimRight(path, "/") diff --git a/net/ghttp/ghttp_server_router_serve.go b/net/ghttp/ghttp_server_router_serve.go index d3614312e..00987de67 100644 --- a/net/ghttp/ghttp_server_router_serve.go +++ b/net/ghttp/ghttp_server_router_serve.go @@ -8,6 +8,7 @@ package ghttp import ( "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "strings" @@ -191,7 +192,7 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han parsedItemList.PushBack(parsedItem) default: - panic(gerror.NewCodef(gerror.CodeInternalError, `invalid handler type %d`, item.Type)) + panic(gerror.NewCodef(gcode.CodeInternalError, `invalid handler type %d`, item.Type)) } } } diff --git a/net/ghttp/ghttp_server_service_handler.go b/net/ghttp/ghttp_server_service_handler.go index 1b846e7e1..5fc5ca6ab 100644 --- a/net/ghttp/ghttp_server_service_handler.go +++ b/net/ghttp/ghttp_server_service_handler.go @@ -9,6 +9,7 @@ package ghttp import ( "bytes" "github.com/gogf/gf/debug/gdebug" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "reflect" "strings" @@ -129,13 +130,13 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, objName, methodN if reflectType.NumIn() == 0 || reflectType.NumIn() > 2 || reflectType.NumOut() > 2 { if pkgPath != "" { err = gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `invalid handler: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" or "func(context.Context)/func(context.Context,Request)/func(context.Context,Request) error/func(context.Context,Request)(Response,error)" is required`, pkgPath, objName, methodName, reflect.TypeOf(f).String(), ) } else { err = gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `invalid handler: defined as "%s", but "func(*ghttp.Request)" or "func(context.Context)/func(context.Context,Request)/func(context.Context,Request) error/func(context.Context,Request)(Response,error)" is required`, reflect.TypeOf(f).String(), ) @@ -145,7 +146,7 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, objName, methodN if reflectType.In(0).String() != "context.Context" { err = gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `invalid handler: defined as "%s", but the first input parameter should be type of "context.Context"`, reflect.TypeOf(f).String(), ) @@ -154,7 +155,7 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, objName, methodN if reflectType.NumOut() > 0 && reflectType.Out(reflectType.NumOut()-1).String() != "error" { err = gerror.NewCodef( - gerror.CodeInvalidParameter, + gcode.CodeInvalidParameter, `invalid handler: defined as "%s", but the last output parameter should be type of "error"`, reflect.TypeOf(f).String(), ) diff --git a/net/ghttp/internal/client/client.go b/net/ghttp/internal/client/client.go index 2f635af85..c4f649e2f 100644 --- a/net/ghttp/internal/client/client.go +++ b/net/ghttp/internal/client/client.go @@ -12,6 +12,7 @@ import ( "crypto/tls" "fmt" "github.com/gogf/gf" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/text/gstr" @@ -246,14 +247,14 @@ func (c *Client) SetProxy(proxyURL string) { func (c *Client) SetTLSKeyCrt(crtFile, keyFile string) error { tlsConfig, err := LoadKeyCrt(crtFile, keyFile) if err != nil { - return gerror.WrapCode(gerror.CodeInternalError, err, "LoadKeyCrt failed") + return gerror.WrapCode(gcode.CodeInternalError, err, "LoadKeyCrt failed") } if v, ok := c.Transport.(*http.Transport); ok { tlsConfig.InsecureSkipVerify = true v.TLSClientConfig = tlsConfig return nil } - return gerror.NewCode(gerror.CodeInternalError, `cannot set TLSClientConfig for custom Transport of the client`) + return gerror.NewCode(gcode.CodeInternalError, `cannot set TLSClientConfig for custom Transport of the client`) } // SetTLSConfig sets the TLS configuration of client. @@ -262,7 +263,7 @@ func (c *Client) SetTLSConfig(tlsConfig *tls.Config) error { v.TLSClientConfig = tlsConfig return nil } - return gerror.NewCode(gerror.CodeInternalError, `cannot set TLSClientConfig for custom Transport of the client`) + return gerror.NewCode(gcode.CodeInternalError, `cannot set TLSClientConfig for custom Transport of the client`) } // LoadKeyCrt creates and returns a TLS configuration object with given certificate and key files. diff --git a/net/ghttp/internal/client/client_request.go b/net/ghttp/internal/client/client_request.go index 781c5a2ff..3f7b2b85d 100644 --- a/net/ghttp/internal/client/client_request.go +++ b/net/ghttp/internal/client/client_request.go @@ -9,6 +9,7 @@ package client import ( "bytes" "context" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" @@ -188,7 +189,7 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h if len(array[1]) > 6 && strings.Compare(array[1][0:6], "@file:") == 0 { path := array[1][6:] if !gfile.Exists(path) { - return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `"%s" does not exist`, path) + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `"%s" does not exist`, path) } if file, err := writer.CreateFormFile(array[0], gfile.Basename(path)); err == nil { if f, err := os.Open(path); err == nil { diff --git a/net/gipv4/gipv4_ip.go b/net/gipv4/gipv4_ip.go index aab88ccea..e1819e078 100644 --- a/net/gipv4/gipv4_ip.go +++ b/net/gipv4/gipv4_ip.go @@ -8,6 +8,7 @@ package gipv4 import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "net" "strconv" @@ -38,7 +39,7 @@ func GetIntranetIp() (ip string, err error) { return "", err } if len(ips) == 0 { - return "", gerror.NewCode(gerror.CodeOperationFailed, "no intranet ip found") + return "", gerror.NewCode(gcode.CodeOperationFailed, "no intranet ip found") } return ips[0], nil } diff --git a/net/gtcp/gtcp_server.go b/net/gtcp/gtcp_server.go index 57f21e62c..538dd6b2f 100644 --- a/net/gtcp/gtcp_server.go +++ b/net/gtcp/gtcp_server.go @@ -8,6 +8,7 @@ package gtcp import ( "crypto/tls" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "net" "sync" @@ -116,7 +117,7 @@ func (s *Server) Close() error { // Run starts running the TCP Server. func (s *Server) Run() (err error) { if s.handler == nil { - err = gerror.NewCode(gerror.CodeMissingConfiguration, "start running failed: socket handler not defined") + err = gerror.NewCode(gcode.CodeMissingConfiguration, "start running failed: socket handler not defined") glog.Error(err) return } diff --git a/net/gudp/gudp_server.go b/net/gudp/gudp_server.go index 64d5180c1..8e9bb8846 100644 --- a/net/gudp/gudp_server.go +++ b/net/gudp/gudp_server.go @@ -7,6 +7,7 @@ package gudp import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "net" @@ -78,7 +79,7 @@ func (s *Server) Close() error { // Run starts listening UDP connection. func (s *Server) Run() error { if s.handler == nil { - err := gerror.NewCode(gerror.CodeMissingConfiguration, "start running failed: socket handler not defined") + err := gerror.NewCode(gcode.CodeMissingConfiguration, "start running failed: socket handler not defined") glog.Error(err) return err } diff --git a/os/gcfg/gcfg_config.go b/os/gcfg/gcfg_config.go index bff1d6e04..f7db8f603 100644 --- a/os/gcfg/gcfg_config.go +++ b/os/gcfg/gcfg_config.go @@ -13,6 +13,7 @@ import ( "github.com/gogf/gf/container/garray" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/encoding/gjson" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gcmd" @@ -142,7 +143,7 @@ func (c *Config) SetPath(path string) error { } else { buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path)) } - err := gerror.NewCode(gerror.CodeOperationFailed, buffer.String()) + err := gerror.NewCode(gcode.CodeOperationFailed, buffer.String()) if errorPrint() { glog.Error(err) } @@ -219,14 +220,14 @@ func (c *Config) AddPath(path string) error { } else { buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path)) } - err := gerror.NewCode(gerror.CodeOperationFailed, buffer.String()) + err := gerror.NewCode(gcode.CodeOperationFailed, buffer.String()) if errorPrint() { glog.Error(err) } return err } if !isDir { - err := gerror.NewCodef(gerror.CodeInvalidParameter, `[gcfg] AddPath failed: path "%s" should be directory type`, path) + err := gerror.NewCodef(gcode.CodeInvalidParameter, `[gcfg] AddPath failed: path "%s" should be directory type`, path) if errorPrint() { glog.Error(err) } @@ -329,7 +330,7 @@ func (c *Config) GetFilePath(file ...string) (path string, err error) { } else { buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path configured", name)) } - err = gerror.NewCode(gerror.CodeOperationFailed, buffer.String()) + err = gerror.NewCode(gcode.CodeOperationFailed, buffer.String()) } return } diff --git a/os/gcfg/gcfg_config_api.go b/os/gcfg/gcfg_config_api.go index 4e7a2a9e8..f3adb7c9c 100644 --- a/os/gcfg/gcfg_config_api.go +++ b/os/gcfg/gcfg_config_api.go @@ -7,6 +7,7 @@ package gcfg import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "time" @@ -295,7 +296,7 @@ func (c *Config) GetStruct(pattern string, pointer interface{}, mapping ...map[s if j := c.getJson(); j != nil { return j.GetStruct(pattern, pointer, mapping...) } - return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") + return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") } // GetStructs converts any slice to given struct slice. @@ -303,7 +304,7 @@ func (c *Config) GetStructs(pattern string, pointer interface{}, mapping ...map[ if j := c.getJson(); j != nil { return j.GetStructs(pattern, pointer, mapping...) } - return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") + return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") } // GetMapToMap retrieves the value by specified `pattern` and converts it to specified map variable. @@ -312,7 +313,7 @@ func (c *Config) GetMapToMap(pattern string, pointer interface{}, mapping ...map if j := c.getJson(); j != nil { return j.GetMapToMap(pattern, pointer, mapping...) } - return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") + return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") } // GetMapToMaps retrieves the value by specified `pattern` and converts it to specified map slice @@ -322,7 +323,7 @@ func (c *Config) GetMapToMaps(pattern string, pointer interface{}, mapping ...ma if j := c.getJson(); j != nil { return j.GetMapToMaps(pattern, pointer, mapping...) } - return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") + return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") } // GetMapToMapsDeep retrieves the value by specified `pattern` and converts it to specified map slice @@ -332,7 +333,7 @@ func (c *Config) GetMapToMapsDeep(pattern string, pointer interface{}, mapping . if j := c.getJson(); j != nil { return j.GetMapToMapsDeep(pattern, pointer, mapping...) } - return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") + return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") } // Map converts current Json object to map[string]interface{}. It returns nil if fails. @@ -358,7 +359,7 @@ func (c *Config) Struct(pointer interface{}, mapping ...map[string]string) error if j := c.getJson(); j != nil { return j.Struct(pointer, mapping...) } - return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") + return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") } // Structs converts current Json object to specified object slice. @@ -367,7 +368,7 @@ func (c *Config) Structs(pointer interface{}, mapping ...map[string]string) erro if j := c.getJson(); j != nil { return j.Structs(pointer, mapping...) } - return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") + return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") } // MapToMap converts current Json object to specified map variable. @@ -376,7 +377,7 @@ func (c *Config) MapToMap(pointer interface{}, mapping ...map[string]string) err if j := c.getJson(); j != nil { return j.MapToMap(pointer, mapping...) } - return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") + return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") } // MapToMaps converts current Json object to specified map variable slice. @@ -385,7 +386,7 @@ func (c *Config) MapToMaps(pointer interface{}, mapping ...map[string]string) er if j := c.getJson(); j != nil { return j.MapToMaps(pointer, mapping...) } - return gerror.NewCode(gerror.CodeMissingConfiguration, "configuration not found") + return gerror.NewCode(gcode.CodeMissingConfiguration, "configuration not found") } // Clear removes all parsed configuration files content cache, diff --git a/os/gcmd/gcmd_handler.go b/os/gcmd/gcmd_handler.go index a0a4b4c8c..9853a2019 100644 --- a/os/gcmd/gcmd_handler.go +++ b/os/gcmd/gcmd_handler.go @@ -8,13 +8,14 @@ package gcmd import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" ) // BindHandle registers callback function <f> with <cmd>. func BindHandle(cmd string, f func()) error { if _, ok := defaultCommandFuncMap[cmd]; ok { - return gerror.NewCode(gerror.CodeInvalidOperation, "duplicated handle for command:"+cmd) + return gerror.NewCode(gcode.CodeInvalidOperation, "duplicated handle for command:"+cmd) } else { defaultCommandFuncMap[cmd] = f } @@ -37,7 +38,7 @@ func RunHandle(cmd string) error { if handle, ok := defaultCommandFuncMap[cmd]; ok { handle() } else { - return gerror.NewCode(gerror.CodeMissingConfiguration, "no handle found for command:"+cmd) + return gerror.NewCode(gcode.CodeMissingConfiguration, "no handle found for command:"+cmd) } return nil } @@ -49,10 +50,10 @@ func AutoRun() error { if handle, ok := defaultCommandFuncMap[cmd]; ok { handle() } else { - return gerror.NewCode(gerror.CodeMissingConfiguration, "no handle found for command:"+cmd) + return gerror.NewCode(gcode.CodeMissingConfiguration, "no handle found for command:"+cmd) } } else { - return gerror.NewCode(gerror.CodeMissingParameter, "no command found") + return gerror.NewCode(gcode.CodeMissingParameter, "no command found") } return nil } diff --git a/os/gcmd/gcmd_parser.go b/os/gcmd/gcmd_parser.go index e6b20b804..a53868746 100644 --- a/os/gcmd/gcmd_parser.go +++ b/os/gcmd/gcmd_parser.go @@ -8,6 +8,7 @@ package gcmd import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "os" @@ -94,7 +95,7 @@ func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bo i++ continue } else if parser.strict { - return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid option '%s'`, args[i]) + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid option '%s'`, args[i]) } } } diff --git a/os/gcmd/gcmd_parser_handler.go b/os/gcmd/gcmd_parser_handler.go index 03e1ed093..c30ecbdad 100644 --- a/os/gcmd/gcmd_parser_handler.go +++ b/os/gcmd/gcmd_parser_handler.go @@ -8,13 +8,14 @@ package gcmd import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" ) // BindHandle registers callback function <f> with <cmd>. func (p *Parser) BindHandle(cmd string, f func()) error { if _, ok := p.commandFuncMap[cmd]; ok { - return gerror.NewCode(gerror.CodeInvalidOperation, "duplicated handle for command:"+cmd) + return gerror.NewCode(gcode.CodeInvalidOperation, "duplicated handle for command:"+cmd) } else { p.commandFuncMap[cmd] = f } @@ -37,7 +38,7 @@ func (p *Parser) RunHandle(cmd string) error { if handle, ok := p.commandFuncMap[cmd]; ok { handle() } else { - return gerror.NewCode(gerror.CodeMissingConfiguration, "no handle found for command:"+cmd) + return gerror.NewCode(gcode.CodeMissingConfiguration, "no handle found for command:"+cmd) } return nil } @@ -49,10 +50,10 @@ func (p *Parser) AutoRun() error { if handle, ok := p.commandFuncMap[cmd]; ok { handle() } else { - return gerror.NewCode(gerror.CodeMissingConfiguration, "no handle found for command:"+cmd) + return gerror.NewCode(gcode.CodeMissingConfiguration, "no handle found for command:"+cmd) } } else { - return gerror.NewCode(gerror.CodeMissingParameter, "no command found") + return gerror.NewCode(gcode.CodeMissingParameter, "no command found") } return nil } diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index 81ed8a778..60aa64931 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -43,7 +43,7 @@ type addEntryInput struct { func (c *Cron) doAddEntry(in addEntryInput) (*Entry, error) { if in.Name != "" { if c.Search(in.Name) != nil { - return nil, gerror.NewCodef(gerror.CodeInvalidOperation, `cron job "%s" already exists`, in.Name) + return nil, gerror.NewCodef(gcode.CodeInvalidOperation, `cron job "%s" already exists`, in.Name) } } diff --git a/os/gcron/gcron_schedule.go b/os/gcron/gcron_schedule.go index 0fdd83206..0b92fec15 100644 --- a/os/gcron/gcron_schedule.go +++ b/os/gcron/gcron_schedule.go @@ -90,7 +90,7 @@ func newSchedule(pattern string) (*cronSchedule, error) { }, nil } } else { - return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern: "%s"`, pattern) + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern: "%s"`, pattern) } } // Handle the common cron pattern, like: @@ -139,7 +139,7 @@ func newSchedule(pattern string) (*cronSchedule, error) { } return schedule, nil } else { - return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern: "%s"`, pattern) + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern: "%s"`, pattern) } } @@ -156,7 +156,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s intervalArray := strings.Split(item, "/") if len(intervalArray) == 2 { if i, err := strconv.Atoi(intervalArray[1]); err != nil { - return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern item: "%s"`, item) + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { interval = i } @@ -178,7 +178,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s // Eg: */5 if rangeArray[0] != "*" { if i, err := parseItemValue(rangeArray[0], fieldType); err != nil { - return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern item: "%s"`, item) + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { rangeMin = i rangeMax = i @@ -186,7 +186,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s } if len(rangeArray) == 2 { if i, err := parseItemValue(rangeArray[1], fieldType); err != nil { - return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern item: "%s"`, item) + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { rangeMax = i } @@ -220,7 +220,7 @@ func parseItemValue(value string, fieldType byte) (int, error) { } } } - return 0, gerror.NewCodef(gerror.CodeInvalidParameter, `invalid pattern value: "%s"`, value) + return 0, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern value: "%s"`, value) } // meet checks if the given time `t` meets the runnable point for the job. diff --git a/os/gfile/gfile_copy.go b/os/gfile/gfile_copy.go index b8f30c68e..26ace7bcf 100644 --- a/os/gfile/gfile_copy.go +++ b/os/gfile/gfile_copy.go @@ -8,6 +8,7 @@ package gfile import ( "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "io" "io/ioutil" @@ -21,10 +22,10 @@ import ( // or else it calls CopyDir. func Copy(src string, dst string) error { if src == "" { - return gerror.NewCode(gerror.CodeInvalidParameter, "source path cannot be empty") + return gerror.NewCode(gcode.CodeInvalidParameter, "source path cannot be empty") } if dst == "" { - return gerror.NewCode(gerror.CodeInvalidParameter, "destination path cannot be empty") + return gerror.NewCode(gcode.CodeInvalidParameter, "destination path cannot be empty") } if IsFile(src) { return CopyFile(src, dst) @@ -40,10 +41,10 @@ func Copy(src string, dst string) error { // Thanks: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04 func CopyFile(src, dst string) (err error) { if src == "" { - return gerror.NewCode(gerror.CodeInvalidParameter, "source file cannot be empty") + return gerror.NewCode(gcode.CodeInvalidParameter, "source file cannot be empty") } if dst == "" { - return gerror.NewCode(gerror.CodeInvalidParameter, "destination file cannot be empty") + return gerror.NewCode(gcode.CodeInvalidParameter, "destination file cannot be empty") } // If src and dst are the same path, it does nothing. if src == dst { @@ -87,10 +88,10 @@ func CopyFile(src, dst string) (err error) { // Note that, the Source directory must exist and symlinks are ignored and skipped. func CopyDir(src string, dst string) (err error) { if src == "" { - return gerror.NewCode(gerror.CodeInvalidParameter, "source directory cannot be empty") + return gerror.NewCode(gcode.CodeInvalidParameter, "source directory cannot be empty") } if dst == "" { - return gerror.NewCode(gerror.CodeInvalidParameter, "destination directory cannot be empty") + return gerror.NewCode(gcode.CodeInvalidParameter, "destination directory cannot be empty") } // If src and dst are the same path, it does nothing. if src == dst { diff --git a/os/gfile/gfile_home.go b/os/gfile/gfile_home.go index 15819b550..44e0da4ad 100644 --- a/os/gfile/gfile_home.go +++ b/os/gfile/gfile_home.go @@ -8,6 +8,7 @@ package gfile import ( "bytes" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "os" "os/exec" @@ -56,7 +57,7 @@ func homeUnix() (string, error) { result := strings.TrimSpace(stdout.String()) if result == "" { - return "", gerror.NewCode(gerror.CodeInternalError, "blank output when reading home directory") + return "", gerror.NewCode(gcode.CodeInternalError, "blank output when reading home directory") } return result, nil @@ -73,7 +74,7 @@ func homeWindows() (string, error) { home = os.Getenv("USERPROFILE") } if home == "" { - return "", gerror.NewCode(gerror.CodeOperationFailed, "HOMEDRIVE, HOMEPATH, and USERPROFILE are blank") + return "", gerror.NewCode(gcode.CodeOperationFailed, "HOMEDRIVE, HOMEPATH, and USERPROFILE are blank") } return home, nil diff --git a/os/gfile/gfile_scan.go b/os/gfile/gfile_scan.go index a14672fe9..05f46aee8 100644 --- a/os/gfile/gfile_scan.go +++ b/os/gfile/gfile_scan.go @@ -7,6 +7,7 @@ package gfile import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/text/gstr" "os" @@ -137,7 +138,7 @@ func ScanDirFileFunc(path string, pattern string, recursive bool, handler func(p // string, or else it appends the sub-file path to result slice. func doScanDir(depth int, path string, pattern string, recursive bool, handler func(path string) string) ([]string, error) { if depth >= maxScanDepth { - return nil, gerror.NewCodef(gerror.CodeOperationFailed, "directory scanning exceeds max recursive depth: %d", maxScanDepth) + return nil, gerror.NewCodef(gcode.CodeOperationFailed, "directory scanning exceeds max recursive depth: %d", maxScanDepth) } list := ([]string)(nil) file, err := os.Open(path) diff --git a/os/gfile/gfile_search.go b/os/gfile/gfile_search.go index 7dcc9d77d..97ab2deac 100644 --- a/os/gfile/gfile_search.go +++ b/os/gfile/gfile_search.go @@ -9,6 +9,7 @@ package gfile import ( "bytes" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/container/garray" @@ -52,7 +53,7 @@ func Search(name string, prioritySearchPaths ...string) (realPath string, err er buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) } }) - err = gerror.NewCode(gerror.CodeOperationFailed, buffer.String()) + err = gerror.NewCode(gcode.CodeOperationFailed, buffer.String()) } return } diff --git a/os/gfpool/gfpool_file.go b/os/gfpool/gfpool_file.go index aab691279..9519322e2 100644 --- a/os/gfpool/gfpool_file.go +++ b/os/gfpool/gfpool_file.go @@ -8,6 +8,7 @@ package gfpool import ( "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "os" "time" @@ -41,7 +42,7 @@ func Open(path string, flag int, perm os.FileMode, ttl ...time.Duration) (file * // Stat returns the FileInfo structure describing file. func (f *File) Stat() (os.FileInfo, error) { if f.stat == nil { - return nil, gerror.NewCode(gerror.CodeInternalError, "file stat is empty") + return nil, gerror.NewCode(gcode.CodeInternalError, "file stat is empty") } return f.stat, nil } diff --git a/os/gfsnotify/gfsnotify.go b/os/gfsnotify/gfsnotify.go index 89fb6b958..e66377d2c 100644 --- a/os/gfsnotify/gfsnotify.go +++ b/os/gfsnotify/gfsnotify.go @@ -10,6 +10,7 @@ package gfsnotify import ( "context" "github.com/gogf/gf/container/gset" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "sync" @@ -139,7 +140,7 @@ func RemoveCallback(callbackId int) error { callback = r.(*Callback) } if callback == nil { - return gerror.NewCodef(gerror.CodeInvalidParameter, `callback for id %d not found`, callbackId) + return gerror.NewCodef(gcode.CodeInvalidParameter, `callback for id %d not found`, callbackId) } w.RemoveCallback(callbackId) return nil diff --git a/os/gfsnotify/gfsnotify_watcher.go b/os/gfsnotify/gfsnotify_watcher.go index af6c2ff22..810e8c1aa 100644 --- a/os/gfsnotify/gfsnotify_watcher.go +++ b/os/gfsnotify/gfsnotify_watcher.go @@ -8,6 +8,7 @@ package gfsnotify import ( "context" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" @@ -66,7 +67,7 @@ func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), re func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { // Check and convert the given path to absolute path. if t := fileRealPath(path); t == "" { - return nil, gerror.NewCodef(gerror.CodeInvalidParameter, `"%s" does not exist`, path) + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `"%s" does not exist`, path) } else { path = t } diff --git a/os/glog/glog_logger_config.go b/os/glog/glog_logger_config.go index 1ed493531..a14181e67 100644 --- a/os/glog/glog_logger_config.go +++ b/os/glog/glog_logger_config.go @@ -7,6 +7,7 @@ package glog import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/os/gctx" "io" "strings" @@ -83,7 +84,7 @@ func (l *Logger) SetConfig(config Config) error { // SetConfigWithMap set configurations with map for the logger. func (l *Logger) SetConfigWithMap(m map[string]interface{}) error { if m == nil || len(m) == 0 { - return gerror.NewCode(gerror.CodeInvalidParameter, "configuration cannot be empty") + return gerror.NewCode(gcode.CodeInvalidParameter, "configuration cannot be empty") } // The m now is a shallow copy of m. // A little tricky, isn't it? @@ -94,7 +95,7 @@ func (l *Logger) SetConfigWithMap(m map[string]interface{}) error { if level, ok := levelStringMap[strings.ToUpper(gconv.String(levelValue))]; ok { m[levelKey] = level } else { - return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid level string: %v`, levelValue) + return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid level string: %v`, levelValue) } } // Change string configuration to int value for file rotation size. @@ -102,7 +103,7 @@ func (l *Logger) SetConfigWithMap(m map[string]interface{}) error { if rotateSizeValue != nil { m[rotateSizeKey] = gfile.StrToSize(gconv.String(rotateSizeValue)) if m[rotateSizeKey] == -1 { - return gerror.NewCodef(gerror.CodeInvalidConfiguration, `invalid rotate size: %v`, rotateSizeValue) + return gerror.NewCodef(gcode.CodeInvalidConfiguration, `invalid rotate size: %v`, rotateSizeValue) } } if err := gconv.Struct(m, &l.config); err != nil { @@ -208,11 +209,11 @@ func (l *Logger) GetWriter() io.Writer { // SetPath sets the directory path for file logging. func (l *Logger) SetPath(path string) error { if path == "" { - return gerror.NewCode(gerror.CodeInvalidParameter, "logging path is empty") + return gerror.NewCode(gcode.CodeInvalidParameter, "logging path is empty") } if !gfile.Exists(path) { if err := gfile.Mkdir(path); err != nil { - return gerror.WrapCodef(gerror.CodeOperationFailed, err, `Mkdir "%s" failed in PWD "%s"`, path, gfile.Pwd()) + return gerror.WrapCodef(gcode.CodeOperationFailed, err, `Mkdir "%s" failed in PWD "%s"`, path, gfile.Pwd()) } } l.config.Path = strings.TrimRight(path, gfile.Separator) diff --git a/os/glog/glog_logger_level.go b/os/glog/glog_logger_level.go index 88c2e374d..5d9b0ad5d 100644 --- a/os/glog/glog_logger_level.go +++ b/os/glog/glog_logger_level.go @@ -7,6 +7,7 @@ package glog import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "strings" ) @@ -77,7 +78,7 @@ func (l *Logger) SetLevelStr(levelStr string) error { if level, ok := levelStringMap[strings.ToUpper(levelStr)]; ok { l.config.Level = level } else { - return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid level string: %s`, levelStr) + return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid level string: %s`, levelStr) } return nil } diff --git a/os/gproc/gproc_comm.go b/os/gproc/gproc_comm.go index 9d2bf6aa5..51a40f2bd 100644 --- a/os/gproc/gproc_comm.go +++ b/os/gproc/gproc_comm.go @@ -9,6 +9,7 @@ package gproc import ( "fmt" "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/net/gtcp" "github.com/gogf/gf/os/gfile" @@ -65,7 +66,7 @@ func getConnByPid(pid int) (*gtcp.PoolConn, error) { return nil, err } } - return nil, gerror.NewCodef(gerror.CodeOperationFailed, "could not find port for pid: %d", pid) + return nil, gerror.NewCodef(gcode.CodeOperationFailed, "could not find port for pid: %d", pid) } // getPortByPid returns the listening port for specified pid. diff --git a/os/gproc/gproc_process.go b/os/gproc/gproc_process.go index 625407364..a560c0eb8 100644 --- a/os/gproc/gproc_process.go +++ b/os/gproc/gproc_process.go @@ -9,6 +9,7 @@ package gproc import ( "context" "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "os" @@ -101,7 +102,7 @@ func (p *Process) Send(data []byte) error { if p.Process != nil { return Send(p.Process.Pid, data) } - return gerror.NewCode(gerror.CodeInvalidParameter, "invalid process") + return gerror.NewCode(gcode.CodeInvalidParameter, "invalid process") } // Release releases any resources associated with the Process p, diff --git a/os/grpool/grpool.go b/os/grpool/grpool.go index 08d6134cf..f9c19c356 100644 --- a/os/grpool/grpool.go +++ b/os/grpool/grpool.go @@ -8,6 +8,7 @@ package grpool import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/container/glist" @@ -69,7 +70,7 @@ func Jobs() int { // The job will be executed asynchronously. func (p *Pool) Add(f func()) error { for p.closed.Val() { - return gerror.NewCode(gerror.CodeInvalidOperation, "pool closed") + return gerror.NewCode(gcode.CodeInvalidOperation, "pool closed") } p.list.PushFront(f) // Check whether fork new goroutine or not. @@ -101,7 +102,7 @@ func (p *Pool) AddWithRecover(userFunc func(), recoverFunc ...func(err error)) e if err, ok := exception.(error); ok { recoverFunc[0](err) } else { - recoverFunc[0](gerror.NewCodef(gerror.CodeInternalError, `%v`, exception)) + recoverFunc[0](gerror.NewCodef(gcode.CodeInternalError, `%v`, exception)) } } } diff --git a/os/gsession/gsession.go b/os/gsession/gsession.go index 16b901808..ee1d15fc9 100644 --- a/os/gsession/gsession.go +++ b/os/gsession/gsession.go @@ -8,6 +8,7 @@ package gsession import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/util/guid" ) @@ -15,7 +16,7 @@ import ( var ( ErrorDisabled = gerror.NewOption(gerror.Option{ Text: "this feature is disabled in this storage", - Code: gerror.CodeNotSupported, + Code: gcode.CodeNotSupported, }) ) diff --git a/os/gsession/gsession_session.go b/os/gsession/gsession_session.go index 7dcab726f..edc70ff9d 100644 --- a/os/gsession/gsession_session.go +++ b/os/gsession/gsession_session.go @@ -8,6 +8,7 @@ package gsession import ( "context" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "time" @@ -187,7 +188,7 @@ func (s *Session) Id() string { // It returns error if it is called after session starts. func (s *Session) SetId(id string) error { if s.start { - return gerror.NewCode(gerror.CodeInvalidOperation, "session already started") + return gerror.NewCode(gcode.CodeInvalidOperation, "session already started") } s.id = id return nil @@ -197,7 +198,7 @@ func (s *Session) SetId(id string) error { // It returns error if it is called after session starts. func (s *Session) SetIdFunc(f func(ttl time.Duration) string) error { if s.start { - return gerror.NewCode(gerror.CodeInvalidOperation, "session already started") + return gerror.NewCode(gcode.CodeInvalidOperation, "session already started") } s.idFunc = f return nil diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index 641ad9912..12fd4e2af 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -9,6 +9,7 @@ package gsession import ( "context" "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/internal/json" @@ -48,15 +49,15 @@ func NewStorageFile(path ...string) *StorageFile { if len(path) > 0 && path[0] != "" { storagePath, _ = gfile.Search(path[0]) if storagePath == "" { - panic(gerror.NewCodef(gerror.CodeInvalidParameter, `"%s" does not exist`, path[0])) + panic(gerror.NewCodef(gcode.CodeInvalidParameter, `"%s" does not exist`, path[0])) } if !gfile.IsWritable(storagePath) { - panic(gerror.NewCodef(gerror.CodeInvalidParameter, `"%s" is not writable`, path[0])) + panic(gerror.NewCodef(gcode.CodeInvalidParameter, `"%s" is not writable`, path[0])) } } if storagePath != "" { if err := gfile.Mkdir(storagePath); err != nil { - panic(gerror.WrapCodef(gerror.CodeInternalError, err, `Mkdir "%s" failed in PWD "%s"`, path, gfile.Pwd())) + panic(gerror.WrapCodef(gcode.CodeInternalError, err, `Mkdir "%s" failed in PWD "%s"`, path, gfile.Pwd())) } } s := &StorageFile{ diff --git a/os/gspath/gspath.go b/os/gspath/gspath.go index 2e7394086..fb0a8818d 100644 --- a/os/gspath/gspath.go +++ b/os/gspath/gspath.go @@ -13,6 +13,7 @@ package gspath import ( "context" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "os" @@ -103,7 +104,7 @@ func (sp *SPath) Set(path string) (realPath string, err error) { } } if realPath == "" { - return realPath, gerror.NewCodef(gerror.CodeInvalidParameter, `path "%s" does not exist`, path) + return realPath, gerror.NewCodef(gcode.CodeInvalidParameter, `path "%s" does not exist`, path) } // The set path must be a directory. if gfile.IsDir(realPath) { @@ -123,7 +124,7 @@ func (sp *SPath) Set(path string) (realPath string, err error) { sp.addMonitorByPath(realPath) return realPath, nil } else { - return "", gerror.NewCode(gerror.CodeInvalidParameter, path+" should be a folder") + return "", gerror.NewCode(gcode.CodeInvalidParameter, path+" should be a folder") } } @@ -138,7 +139,7 @@ func (sp *SPath) Add(path string) (realPath string, err error) { } } if realPath == "" { - return realPath, gerror.NewCodef(gerror.CodeInvalidParameter, `path "%s" does not exist`, path) + return realPath, gerror.NewCodef(gcode.CodeInvalidParameter, `path "%s" does not exist`, path) } // The added path must be a directory. if gfile.IsDir(realPath) { @@ -152,7 +153,7 @@ func (sp *SPath) Add(path string) (realPath string, err error) { } return realPath, nil } else { - return "", gerror.NewCode(gerror.CodeInvalidParameter, path+" should be a folder") + return "", gerror.NewCode(gcode.CodeInvalidParameter, path+" should be a folder") } } diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index 3cb587369..cb164da44 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -11,6 +11,7 @@ package gtime import ( "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/utils" "os" @@ -277,7 +278,7 @@ func StrToTime(str string, format ...string) (*Time, error) { } return NewFromTime(time.Date(0, time.Month(1), 1, hour, min, sec, nsec, local)), nil } else { - return nil, gerror.NewCode(gerror.CodeInvalidParameter, "unsupported time format") + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "unsupported time format") } // Time @@ -312,7 +313,7 @@ func StrToTime(str string, format ...string) (*Time, error) { m, _ := strconv.Atoi(zone[2:4]) s, _ := strconv.Atoi(zone[4:6]) if h > 24 || m > 59 || s > 59 { - return nil, gerror.NewCodef(gerror.CodeInvalidParameter, "invalid zone string: %s", match[6]) + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, "invalid zone string: %s", match[6]) } // Comparing the given time zone whether equals to current time zone, // it converts it to UTC if they does not equal. @@ -351,7 +352,7 @@ func StrToTime(str string, format ...string) (*Time, error) { } } if month <= 0 || day <= 0 { - return nil, gerror.NewCodef(gerror.CodeInvalidParameter, "invalid time string:%s", str) + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, "invalid time string:%s", str) } return NewFromTime(time.Date(year, time.Month(month), day, hour, min, sec, nsec, local)), nil } diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 785e34a84..185c9dbd9 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -8,6 +8,7 @@ package gtime import ( "bytes" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "strconv" "time" @@ -462,7 +463,7 @@ func (t *Time) UnmarshalText(data []byte) error { *t = *vTime return nil } - return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid time value: %s`, data) + return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid time value: %s`, data) } // NoValidation marks this struct object will not be validated by package gvalid. diff --git a/os/gview/gview_config.go b/os/gview/gview_config.go index 0bba3cf4b..79dd6eced 100644 --- a/os/gview/gview_config.go +++ b/os/gview/gview_config.go @@ -8,6 +8,7 @@ package gview import ( "context" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/i18n/gi18n" "github.com/gogf/gf/internal/intlog" @@ -74,7 +75,7 @@ func (view *View) SetConfig(config Config) error { // SetConfigWithMap set configurations with map for the view. func (view *View) SetConfigWithMap(m map[string]interface{}) error { if m == nil || len(m) == 0 { - return gerror.NewCode(gerror.CodeInvalidParameter, "configuration cannot be empty") + return gerror.NewCode(gcode.CodeInvalidParameter, "configuration cannot be empty") } // The m now is a shallow copy of m. // Any changes to m does not affect the original one. @@ -123,7 +124,7 @@ func (view *View) SetPath(path string) error { } // Path not exist. if realPath == "" { - err := gerror.NewCodef(gerror.CodeInvalidParameter, `[gview] SetPath failed: path "%s" does not exist`, path) + err := gerror.NewCodef(gcode.CodeInvalidParameter, `[gview] SetPath failed: path "%s" does not exist`, path) if errorPrint() { glog.Error(err) } @@ -131,7 +132,7 @@ func (view *View) SetPath(path string) error { } // Should be a directory. if !isDir { - err := gerror.NewCodef(gerror.CodeInvalidParameter, `[gview] SetPath failed: path "%s" should be directory type`, path) + err := gerror.NewCodef(gcode.CodeInvalidParameter, `[gview] SetPath failed: path "%s" should be directory type`, path) if errorPrint() { glog.Error(err) } @@ -177,7 +178,7 @@ func (view *View) AddPath(path string) error { } // Path not exist. if realPath == "" { - err := gerror.NewCodef(gerror.CodeInvalidParameter, `[gview] AddPath failed: path "%s" does not exist`, path) + err := gerror.NewCodef(gcode.CodeInvalidParameter, `[gview] AddPath failed: path "%s" does not exist`, path) if errorPrint() { glog.Error(err) } @@ -185,7 +186,7 @@ func (view *View) AddPath(path string) error { } // realPath should be type of folder. if !isDir { - err := gerror.NewCodef(gerror.CodeInvalidParameter, `[gview] AddPath failed: path "%s" should be directory type`, path) + err := gerror.NewCodef(gcode.CodeInvalidParameter, `[gview] AddPath failed: path "%s" should be directory type`, path) if errorPrint() { glog.Error(err) } diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index f4da300fa..cd50e7a8b 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -11,6 +11,7 @@ import ( "context" "fmt" "github.com/gogf/gf/encoding/ghash" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/os/gfsnotify" @@ -112,7 +113,7 @@ func (view *View) Parse(ctx context.Context, file string, params ...Params) (res tpl, err = tpl.(*texttpl.Template).Parse(item.content) } if err != nil && item.path != "" { - err = gerror.WrapCode(gerror.CodeInternalError, err, item.path) + err = gerror.WrapCode(gcode.CodeInternalError, err, item.path) } }) if err != nil { @@ -298,7 +299,7 @@ func (view *View) getTemplate(filePath, folderPath, pattern string) (tpl interfa // formatTemplateObjectCreatingError formats the error that creted from creating template object. func (view *View) formatTemplateObjectCreatingError(filePath, tplName string, err error) error { if err != nil { - return gerror.NewCodeSkip(gerror.CodeInternalError, 1, gstr.Replace(err.Error(), tplName, filePath)) + return gerror.NewCodeSkip(gcode.CodeInternalError, 1, gstr.Replace(err.Error(), tplName, filePath)) } return nil } @@ -376,7 +377,7 @@ func (view *View) searchFile(file string) (path string, folder string, resource if errorPrint() { glog.Error(buffer.String()) } - err = gerror.NewCodef(gerror.CodeInvalidParameter, `template file "%s" not found`, file) + err = gerror.NewCodef(gcode.CodeInvalidParameter, `template file "%s" not found`, file) } return } diff --git a/util/gconv/gconv_maptomap.go b/util/gconv/gconv_maptomap.go index 22524ba9e..973f12e52 100644 --- a/util/gconv/gconv_maptomap.go +++ b/util/gconv/gconv_maptomap.go @@ -7,6 +7,7 @@ package gconv import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "reflect" @@ -90,7 +91,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s pointerKind = pointerRv.Kind() } if pointerKind != reflect.Map { - return gerror.NewCodef(gerror.CodeInvalidParameter, "pointer should be type of *map, but got:%s", pointerKind) + return gerror.NewCodef(gcode.CodeInvalidParameter, "pointer should be type of *map, but got:%s", pointerKind) } defer func() { // Catch the panic, especially the reflect operation panics. @@ -98,7 +99,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s if e, ok := exception.(errorStack); ok { err = e } else { - err = gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception) + err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception) } } }() diff --git a/util/gconv/gconv_maptomaps.go b/util/gconv/gconv_maptomaps.go index b37e1ac31..a3c0928b5 100644 --- a/util/gconv/gconv_maptomaps.go +++ b/util/gconv/gconv_maptomaps.go @@ -7,6 +7,7 @@ package gconv import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "reflect" @@ -73,7 +74,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] paramsKind = paramsRv.Kind() } if paramsKind != reflect.Array && paramsKind != reflect.Slice { - return gerror.NewCode(gerror.CodeInvalidParameter, "params should be type of slice, eg: []map/[]*map/[]struct/[]*struct") + return gerror.NewCode(gcode.CodeInvalidParameter, "params should be type of slice, eg: []map/[]*map/[]struct/[]*struct") } var ( paramsElem = paramsRv.Type().Elem() @@ -84,7 +85,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] paramsElemKind = paramsElem.Kind() } if paramsElemKind != reflect.Map && paramsElemKind != reflect.Struct && paramsElemKind != reflect.Interface { - return gerror.NewCodef(gerror.CodeInvalidParameter, "params element should be type of map/*map/struct/*struct, but got: %s", paramsElemKind) + return gerror.NewCodef(gcode.CodeInvalidParameter, "params element should be type of map/*map/struct/*struct, but got: %s", paramsElemKind) } // Empty slice, no need continue. if paramsRv.Len() == 0 { @@ -100,7 +101,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] pointerKind = pointerRv.Kind() } if pointerKind != reflect.Array && pointerKind != reflect.Slice { - return gerror.NewCode(gerror.CodeInvalidParameter, "pointer should be type of *[]map/*[]*map") + return gerror.NewCode(gcode.CodeInvalidParameter, "pointer should be type of *[]map/*[]*map") } var ( pointerElemType = pointerRv.Type().Elem() @@ -110,7 +111,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] pointerElemKind = pointerElemType.Elem().Kind() } if pointerElemKind != reflect.Map { - return gerror.NewCode(gerror.CodeInvalidParameter, "pointer element should be type of map/*map") + return gerror.NewCode(gcode.CodeInvalidParameter, "pointer element should be type of map/*map") } defer func() { // Catch the panic, especially the reflect operation panics. @@ -118,7 +119,7 @@ func doMapToMaps(params interface{}, pointer interface{}, mapping ...map[string] if e, ok := exception.(errorStack); ok { err = e } else { - err = gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception) + err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception) } } }() diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index bb5af1f47..9f32740b0 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -7,6 +7,7 @@ package gconv import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "reflect" ) @@ -29,11 +30,11 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) pointerType = reflect.TypeOf(pointer) } if pointerType == nil { - return gerror.NewCode(gerror.CodeInvalidParameter, "parameter pointer should not be nil") + return gerror.NewCode(gcode.CodeInvalidParameter, "parameter pointer should not be nil") } pointerKind = pointerType.Kind() if pointerKind != reflect.Ptr { - return gerror.NewCodef(gerror.CodeInvalidParameter, "params should be type of pointer, but got type: %v", pointerKind) + return gerror.NewCodef(gcode.CodeInvalidParameter, "params should be type of pointer, but got type: %v", pointerKind) } var ( pointerElem = pointerType.Elem() @@ -77,7 +78,7 @@ func ScanDeep(params interface{}, pointer interface{}, mapping ...map[string]str t := reflect.TypeOf(pointer) k := t.Kind() if k != reflect.Ptr { - return gerror.NewCodef(gerror.CodeInvalidParameter, "params should be type of pointer, but got: %v", k) + return gerror.NewCodef(gcode.CodeInvalidParameter, "params should be type of pointer, but got: %v", k) } switch t.Elem().Kind() { case reflect.Array, reflect.Slice: diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 381a75d28..bb959daa1 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -7,6 +7,7 @@ package gconv import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/internal/json" @@ -58,7 +59,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string return nil } if pointer == nil { - return gerror.NewCode(gerror.CodeInvalidParameter, "object pointer cannot be nil") + return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil") } defer func() { @@ -67,7 +68,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string if e, ok := exception.(errorStack); ok { err = e } else { - err = gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception) + err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception) } } }() @@ -120,11 +121,11 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string pointerReflectValue = reflect.ValueOf(pointer) pointerReflectKind = pointerReflectValue.Kind() if pointerReflectKind != reflect.Ptr { - return gerror.NewCodef(gerror.CodeInvalidParameter, "object pointer should be type of '*struct', but got '%v'", pointerReflectKind) + return gerror.NewCodef(gcode.CodeInvalidParameter, "object pointer should be type of '*struct', but got '%v'", pointerReflectKind) } // Using IsNil on reflect.Ptr variable is OK. if !pointerReflectValue.IsValid() || pointerReflectValue.IsNil() { - return gerror.NewCode(gerror.CodeInvalidParameter, "object pointer cannot be nil") + return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil") } pointerElemReflectValue = pointerReflectValue.Elem() } @@ -162,7 +163,7 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string // DO NOT use MapDeep here. paramsMap := Map(paramsInterface) if paramsMap == nil { - return gerror.NewCodef(gerror.CodeInvalidParameter, "convert params to map failed: %v", params) + return gerror.NewCodef(gcode.CodeInvalidParameter, "convert params to map failed: %v", params) } // It only performs one converting to the same attribute. @@ -303,7 +304,7 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, map defer func() { if exception := recover(); exception != nil { if err = bindVarToReflectValue(structFieldValue, value, mapping, priorityTag); err != nil { - err = gerror.WrapCodef(gerror.CodeInternalError, err, `error binding value to attribute "%s"`, name) + err = gerror.WrapCodef(gcode.CodeInternalError, err, `error binding value to attribute "%s"`, name) } } }() @@ -484,7 +485,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma defer func() { if exception := recover(); exception != nil { err = gerror.NewCodef( - gerror.CodeInternalError, + gcode.CodeInternalError, `cannot convert value "%+v" to type "%s":%+v`, value, structFieldValue.Type().String(), diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index cf2ac64a0..2bb370af8 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -7,6 +7,7 @@ package gconv import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/json" "reflect" @@ -48,7 +49,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin return nil } if pointer == nil { - return gerror.NewCode(gerror.CodeInvalidParameter, "object pointer cannot be nil") + return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil") } if doStructsByDirectReflectSet(params, pointer) { @@ -61,7 +62,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin if e, ok := exception.(errorStack); ok { err = e } else { - err = gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception) + err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception) } } }() @@ -93,7 +94,7 @@ func doStructs(params interface{}, pointer interface{}, mapping map[string]strin if !ok { pointerRv = reflect.ValueOf(pointer) if kind := pointerRv.Kind(); kind != reflect.Ptr { - return gerror.NewCodef(gerror.CodeInvalidParameter, "pointer should be type of pointer, but got: %v", kind) + return gerror.NewCodef(gcode.CodeInvalidParameter, "pointer should be type of pointer, but got: %v", kind) } } // Converting `params` to map slice. diff --git a/util/gvalid/gvalid_error.go b/util/gvalid/gvalid_error.go index 4f4a93fef..5fe38875c 100644 --- a/util/gvalid/gvalid_error.go +++ b/util/gvalid/gvalid_error.go @@ -7,6 +7,7 @@ package gvalid import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" @@ -15,7 +16,7 @@ import ( // Error is the validation error for validation result. type Error interface { - Code() int + Code() gcode.Code Current() error Error() string FirstItem() (key string, messages map[string]string) @@ -30,7 +31,7 @@ type Error interface { // validationError is the validation error for validation result. type validationError struct { - code int // Error code. + code gcode.Code // Error code. rules []fieldRule // Rules by sequence, which is used for keeping error sequence. errors map[string]map[string]string // Error map:map[field]map[rule]message firstKey string // The first error rule key(empty in default). @@ -38,7 +39,7 @@ type validationError struct { } // newError creates and returns a validation error. -func newError(code int, rules []fieldRule, errors map[string]map[string]string) *validationError { +func newError(code gcode.Code, rules []fieldRule, errors map[string]map[string]string) *validationError { for field, m := range errors { for k, v := range m { v = strings.Replace(v, ":attribute", field, -1) @@ -57,7 +58,7 @@ func newError(code int, rules []fieldRule, errors map[string]map[string]string) // newErrorStr creates and returns a validation error by string. func newErrorStr(key, err string) *validationError { - return newError(gerror.CodeInternalError, nil, map[string]map[string]string{ + return newError(gcode.CodeInternalError, nil, map[string]map[string]string{ internalErrorMapKey: { key: err, }, @@ -65,9 +66,9 @@ func newErrorStr(key, err string) *validationError { } // Code returns the error code of current validation error. -func (e *validationError) Code() int { +func (e *validationError) Code() gcode.Code { if e == nil { - return gerror.CodeNil + return gcode.CodeNil } return e.code } diff --git a/util/gvalid/gvalid_validator_check_map.go b/util/gvalid/gvalid_validator_check_map.go index ccbc66bc9..9db900232 100644 --- a/util/gvalid/gvalid_validator_check_map.go +++ b/util/gvalid/gvalid_validator_check_map.go @@ -7,7 +7,7 @@ package gvalid import ( - "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/util/gconv" "strings" ) @@ -149,7 +149,7 @@ func (v *Validator) doCheckMap(params interface{}) Error { } } if len(errorMaps) > 0 { - return newError(gerror.CodeValidationFailed, checkRules, errorMaps) + return newError(gcode.CodeValidationFailed, checkRules, errorMaps) } return nil } diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 341afe542..78c4f9847 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -7,7 +7,7 @@ package gvalid import ( - "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/internal/structs" "github.com/gogf/gf/util/gconv" "github.com/gogf/gf/util/gutil" @@ -288,7 +288,7 @@ func (v *Validator) doCheckStruct(object interface{}) Error { } } if len(errorMaps) > 0 { - return newError(gerror.CodeValidationFailed, checkRules, errorMaps) + return newError(gcode.CodeValidationFailed, checkRules, errorMaps) } return nil } diff --git a/util/gvalid/gvalid_validator_check_value.go b/util/gvalid/gvalid_validator_check_value.go index 6d8d99684..f02fff8a2 100644 --- a/util/gvalid/gvalid_validator_check_value.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -7,6 +7,7 @@ package gvalid import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "strconv" "strings" @@ -168,7 +169,7 @@ func (v *Validator) doCheckValue(input doCheckValueInput) Error { index++ } if len(errorMsgArray) > 0 { - return newError(gerror.CodeValidationFailed, []fieldRule{{Name: input.Name, Rule: input.Rule}}, map[string]map[string]string{ + return newError(gcode.CodeValidationFailed, []fieldRule{{Name: input.Name, Rule: input.Rule}}, map[string]map[string]string{ input.Name: errorMsgArray, }) } @@ -209,7 +210,7 @@ func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match b if msg := v.checkLength(valueStr, input.RuleKey, input.RulePattern, input.CustomMsgMap); msg != "" { return match, gerror.NewOption(gerror.Option{ Text: msg, - Code: gerror.CodeValidationFailed, + Code: gcode.CodeValidationFailed, }) } else { match = true @@ -223,7 +224,7 @@ func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match b if msg := v.checkRange(valueStr, input.RuleKey, input.RulePattern, input.CustomMsgMap); msg != "" { return match, gerror.NewOption(gerror.Option{ Text: msg, - Code: gerror.CodeValidationFailed, + Code: gcode.CodeValidationFailed, }) } else { match = true @@ -262,7 +263,7 @@ func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match b msg = strings.Replace(msg, ":format", input.RulePattern, -1) return match, gerror.NewOption(gerror.Option{ Text: msg, - Code: gerror.CodeValidationFailed, + Code: gcode.CodeValidationFailed, }) } @@ -280,7 +281,7 @@ func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match b msg = strings.Replace(msg, ":field", input.RulePattern, -1) return match, gerror.NewOption(gerror.Option{ Text: msg, - Code: gerror.CodeValidationFailed, + Code: gcode.CodeValidationFailed, }) } @@ -299,7 +300,7 @@ func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match b msg = strings.Replace(msg, ":field", input.RulePattern, -1) return match, gerror.NewOption(gerror.Option{ Text: msg, - Code: gerror.CodeValidationFailed, + Code: gcode.CodeValidationFailed, }) } @@ -485,7 +486,7 @@ func (v *Validator) doCheckBuildInRules(input doCheckBuildInRulesInput) (match b default: return match, gerror.NewOption(gerror.Option{ Text: "Invalid rule name: " + input.RuleKey, - Code: gerror.CodeInvalidParameter, + Code: gcode.CodeInvalidParameter, }) } return match, nil diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index 2997913d2..23de12ab7 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -1019,12 +1019,12 @@ func Test_Code(t *testing.T) { gtest.C(t, func(t *gtest.T) { err := g.Validator().Rules("required").CheckValue("") t.AssertNE(err, nil) - t.Assert(gerror.Code(err), gerror.CodeValidationFailed) + t.Assert(gerror.Code(err), gcode.CodeValidationFailed) }) gtest.C(t, func(t *gtest.T) { err := g.Validator().Rules("none-exist-rule").CheckValue("") t.AssertNE(err, nil) - t.Assert(gerror.Code(err), gerror.CodeInternalError) + t.Assert(gerror.Code(err), gcode.CodeInternalError) }) } From 7b47fe96dd1b2b1c87d8bef955cd69c5d0812731 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 24 Aug 2021 21:22:58 +0800 Subject: [PATCH 474/492] update version of third-party redigo from v2.0.0+incompatible to v1.8.5 --- go.mod | 2 +- go.sum | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d07ddc00e..fc1af56fb 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/fatih/color v1.12.0 github.com/fsnotify/fsnotify v1.4.9 github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 - github.com/gomodule/redigo v2.0.0+incompatible + github.com/gomodule/redigo v1.8.5 github.com/gorilla/websocket v1.4.2 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf github.com/kr/text v0.2.0 // indirect diff --git a/go.sum b/go.sum index 506697682..181576aa5 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw= github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc= +github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= @@ -40,6 +40,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.opentelemetry.io/otel v1.0.0-RC2 h1:SHhxSjB+omnGZPgGlKe+QMp3MyazcOHdQ8qwo89oKbg= @@ -67,6 +68,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T 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/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 66b8b591dfd37b7c462d738af6199d9e82d5ae65 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 24 Aug 2021 21:31:15 +0800 Subject: [PATCH 475/492] fix issue in missing import for package gcode --- crypto/gdes/gdes.go | 1 + os/gcron/gcron_entry.go | 1 + os/gcron/gcron_schedule.go | 1 + util/gvalid/gvalid_z_unit_basic_all_test.go | 2 ++ 4 files changed, 5 insertions(+) diff --git a/crypto/gdes/gdes.go b/crypto/gdes/gdes.go index 00d030e75..8b00e1a7c 100644 --- a/crypto/gdes/gdes.go +++ b/crypto/gdes/gdes.go @@ -11,6 +11,7 @@ import ( "bytes" "crypto/cipher" "crypto/des" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" ) diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index 60aa64931..3f3b90d8f 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -7,6 +7,7 @@ package gcron import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "reflect" "runtime" diff --git a/os/gcron/gcron_schedule.go b/os/gcron/gcron_schedule.go index 0b92fec15..7ff33d03e 100644 --- a/os/gcron/gcron_schedule.go +++ b/os/gcron/gcron_schedule.go @@ -7,6 +7,7 @@ package gcron import ( + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" "strconv" diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index 23de12ab7..906d768fa 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -8,6 +8,7 @@ package gvalid_test import ( "context" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/os/gtime" "testing" @@ -1021,6 +1022,7 @@ func Test_Code(t *testing.T) { t.AssertNE(err, nil) t.Assert(gerror.Code(err), gcode.CodeValidationFailed) }) + gtest.C(t, func(t *gtest.T) { err := g.Validator().Rules("none-exist-rule").CheckValue("") t.AssertNE(err, nil) From a47699bf6e604e35562df72666bd609dac6bb118 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 24 Aug 2021 21:34:14 +0800 Subject: [PATCH 476/492] fix issue in unit testing case for package gjson --- encoding/gjson/gjson_z_example_conversion_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/encoding/gjson/gjson_z_example_conversion_test.go b/encoding/gjson/gjson_z_example_conversion_test.go index c55d9c409..e74211d48 100644 --- a/encoding/gjson/gjson_z_example_conversion_test.go +++ b/encoding/gjson/gjson_z_example_conversion_test.go @@ -48,8 +48,8 @@ func Example_conversionNormalFormats() { // YAML: // users: // array: - // - John - // - Ming + // - John + // - Ming // count: 1 // // ====================== From b129ee3f04e6b4de558c664dc5d4cd3418f43654 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 24 Aug 2021 21:35:49 +0800 Subject: [PATCH 477/492] fix issue in unit testing case for package ghttp --- net/ghttp/ghttp_unit_error_code_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ghttp/ghttp_unit_error_code_test.go b/net/ghttp/ghttp_unit_error_code_test.go index c5484afda..ca77f4254 100644 --- a/net/ghttp/ghttp_unit_error_code_test.go +++ b/net/ghttp/ghttp_unit_error_code_test.go @@ -10,6 +10,7 @@ package ghttp_test import ( "fmt" + "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/net/ghttp" "testing" @@ -30,7 +31,7 @@ func Test_Error_Code(t *testing.T) { r.Response.Write(gerror.Code(r.GetError())) }) group.ALL("/", func(r *ghttp.Request) { - panic(gerror.NewCode(10000, "test error")) + panic(gerror.NewCode(gcode.New(10000, "", nil), "test error")) }) }) s.SetPort(p) From 29d50994aec0cfbde8dfbb926c3ac5821e9a33aa Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Tue, 24 Aug 2021 22:59:25 +0800 Subject: [PATCH 478/492] add function Close for DB for package gdb --- database/gdb/gdb.go | 25 ++++++++++++++++--------- database/gdb/gdb_core.go | 22 ++++++++++++++++++++++ database/gdb/gdb_driver_oracle.go | 28 ---------------------------- frame/gins/gins_database.go | 11 ++++------- 4 files changed, 42 insertions(+), 44 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 49dd32c3a..35e392caa 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -73,6 +73,14 @@ type DB interface { // Also see Core.Ctx. Ctx(ctx context.Context) DB + // Close closes the database and prevents new queries from starting. + // Close then waits for all queries that have started processing on the server + // to finish. + // + // It is rare to Close a DB, as the DB handle is meant to be + // long-lived and shared between many goroutines. + Close(ctx context.Context) error + // =========================================================================== // Query APIs. // =========================================================================== @@ -94,7 +102,7 @@ type DB interface { Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete. // =========================================================================== - // Internal APIs for CURD, which can be overwrote for custom CURD implements. + // Internal APIs for CURD, which can be overwritten by custom CURD implements. // =========================================================================== DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll. @@ -179,6 +187,7 @@ type Core struct { group string // Configuration group name. debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime. cache *gcache.Cache // Cache manager, SQL result cache only. + links *gmap.StrAnyMap // links caches all created links by node. schema *gtype.String // Custom schema for this object. logger *glog.Logger // Logger for logging functionality. config *ConfigNode // Current config node. @@ -302,9 +311,6 @@ var ( // in the field name as it conflicts with "db.table.field" pattern in SOME situations. regularFieldNameWithoutDotRegPattern = `^[\w\-]+$` - // internalCache is the memory cache for internal usage. - internalCache = gcache.New() - // tableFieldsMap caches the table information retrived from database. tableFieldsMap = gmap.New(true) @@ -347,6 +353,7 @@ func New(group ...string) (db DB, err error) { group: groupName, debug: gtype.NewBool(), cache: gcache.New(), + links: gmap.NewStrAnyMap(true), schema: gtype.NewString(), logger: glog.New(), config: node, @@ -441,7 +448,7 @@ func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode { for i := 0; i < len(cg); i++ { total += cg[i].Weight * 100 } - // If total is 0 means all of the nodes have no weight attribute configured. + // If total is 0 means all the nodes have no weight attribute configured. // It then defaults each node's weight attribute to 1. if total == 0 { for i := 0; i < len(cg); i++ { @@ -490,7 +497,7 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error node = &n } // Cache the underlying connection pool object by node. - v, _ := internalCache.GetOrSetFuncLock(node.String(), func() (interface{}, error) { + v := c.links.GetOrSetFuncLock(node.String(), func() interface{} { intlog.Printf( c.db.GetCtx(), `open new connection, master:%#v, config:%#v, node:%#v`, @@ -510,7 +517,7 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error sqlDb, err = c.db.Open(node) if err != nil { - return nil, err + return nil } if c.config.MaxIdleConnCount > 0 { @@ -534,8 +541,8 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error } else { sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime) } - return sqlDb, nil - }, 0) + return sqlDb + }) if v != nil && sqlDb == nil { sqlDb = v.(*sql.DB) } diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 34954d65b..5a63e285c 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -12,6 +12,7 @@ import ( "database/sql" "fmt" "github.com/gogf/gf/errors/gcode" + "github.com/gogf/gf/internal/intlog" "reflect" "strings" @@ -95,6 +96,27 @@ func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Cont return ctx, func() {} } +// Close closes the database and prevents new queries from starting. +// Close then waits for all queries that have started processing on the server +// to finish. +// +// It is rare to Close a DB, as the DB handle is meant to be +// long-lived and shared between many goroutines. +func (c *Core) Close(ctx context.Context) (err error) { + c.links.LockFunc(func(m map[string]interface{}) { + for k, v := range m { + if db, ok := v.(*sql.DB); ok { + err = db.Close() + intlog.Printf(ctx, `close link: %s, err: %v`, k, err) + if err != nil { + return + } + } + } + }) + return +} + // Master creates and returns a connection from master node if master-slave configured. // It returns the default connection if master-slave not configured. func (c *Core) Master(schema ...string) (*sql.DB, error) { diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index d3e3d157e..cc308b687 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -236,34 +236,6 @@ FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, return } -func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[string]string, err error) { - table = strings.ToUpper(table) - v, _ := internalCache.GetOrSetFunc( - "table_unique_index_"+table, - func() (interface{}, error) { - res := (Result)(nil) - res, err = d.db.GetAll(fmt.Sprintf(` - SELECT INDEX_NAME,COLUMN_NAME,CHAR_LENGTH FROM USER_IND_COLUMNS - WHERE TABLE_NAME = '%s' - AND INDEX_NAME IN(SELECT INDEX_NAME FROM USER_INDEXES WHERE TABLE_NAME='%s' AND UNIQUENESS='UNIQUE') - ORDER BY INDEX_NAME,COLUMN_POSITION`, table, table)) - if err != nil { - return nil, err - } - fields := make(map[string]map[string]string) - for _, v := range res { - mm := make(map[string]string) - mm[v["COLUMN_NAME"].String()] = v["CHAR_LENGTH"].String() - fields[v["INDEX_NAME"].String()] = mm - } - return fields, nil - }, 0) - if err == nil { - fields = v.(map[string]map[string]string) - } - return -} - // DoInsert inserts or updates data for given table. // This function is usually used for custom interface definition, you do not need call it manually. // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index e27f7ce73..dfe83eae7 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -50,29 +50,26 @@ func Database(name ...string) gdb.DB { configMap = Config().GetMap(configNodeKey) } if len(configMap) == 0 && !gdb.IsConfigured() { - configFilePath, err := Config().GetFilePath() + configFilePath, _ := Config().GetFilePath() if configFilePath == "" { exampleFileName := "config.example.toml" if exampleConfigFilePath, _ := Config().GetFilePath(exampleFileName); exampleConfigFilePath != "" { - panic(gerror.WrapCodef( + panic(gerror.NewCodef( gcode.CodeMissingConfiguration, - err, `configuration file "%s" not found, but found "%s", did you miss renaming the example configuration file?`, Config().GetFileName(), exampleFileName, )) } else { - panic(gerror.WrapCodef( + panic(gerror.NewCodef( gcode.CodeMissingConfiguration, - err, `configuration file "%s" not found, did you miss the configuration file or the misspell the configuration file name?`, Config().GetFileName(), )) } } - panic(gerror.WrapCodef( + panic(gerror.NewCodef( gcode.CodeMissingConfiguration, - err, `database initialization failed: "%s" node not found, is configuration file or configuration node missing?`, configNodeNameDatabase, )) From 6be582355c682c087211697fe9990f856cfbfa38 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 25 Aug 2021 10:58:29 +0800 Subject: [PATCH 479/492] improve DB.Close for package gdb --- database/gdb/gdb_core.go | 1 + 1 file changed, 1 insertion(+) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 5a63e285c..7b76f5dca 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -111,6 +111,7 @@ func (c *Core) Close(ctx context.Context) (err error) { if err != nil { return } + delete(m, k) } } }) From 59397fd8a5c1e6b810c79bd61c6bff2171e4365c Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 25 Aug 2021 20:00:53 +0800 Subject: [PATCH 480/492] add buildin function add/minus/times/divide for package gview --- errors/gcode/gcode.go | 38 +------------------------ errors/gcode/gcode_local.go | 43 ++++++++++++++++++++++++++++ errors/gcode/gcode_test.go | 23 +++++++++++++++ os/gview/gview.go | 6 +++- os/gview/gview_buildin.go | 47 ++++++++++++++++++++++++++++++- os/gview/gview_config.go | 8 +++--- os/gview/gview_instance.go | 2 +- os/gview/gview_parse.go | 24 ++++++++-------- os/gview/gview_unit_basic_test.go | 37 ++++++++++++++++++++++++ 9 files changed, 172 insertions(+), 56 deletions(-) create mode 100644 errors/gcode/gcode_local.go create mode 100644 errors/gcode/gcode_test.go diff --git a/errors/gcode/gcode.go b/errors/gcode/gcode.go index 6248ed0c7..8665b8a27 100644 --- a/errors/gcode/gcode.go +++ b/errors/gcode/gcode.go @@ -7,8 +7,6 @@ // Package gcode provides universal error code definition and common error codes implements. package gcode -import "fmt" - // Code is universal error code interface definition. type Code interface { // Code returns the integer number of current error code. @@ -22,13 +20,6 @@ type Code interface { Detail() interface{} } -// localCode is an implementer for interface Code for internal usage only. -type localCode struct { - code int // Error code, usually an integer. - message string // Brief message for this error code. - detail interface{} // As type of interface, it is mainly designed as an extension field for error code. -} - // ================================================================================================================ // Common error code definition. // There are reserved internal error code by framework: code < 1000. @@ -52,7 +43,7 @@ var ( CodeSecurityReason = localCode{62, "Security Reason", nil} // Security Reason. CodeServerBusy = localCode{63, "Server Is Busy", nil} // Server is busy, please try again later. CodeUnknown = localCode{64, "Unknown Error", nil} // Unknown error. - CodeResourceNotExist = localCode{65, "Resource Not Exist", nil} // Resource does not exist. + CodeNotFound = localCode{65, "Not Found", nil} // Resource does not exist. CodeInvalidRequest = localCode{66, "Invalid Request", nil} // Invalid request. CodeBusinessValidationFailed = localCode{300, "Business Validation Failed", nil} // Business validation failed. ) @@ -66,30 +57,3 @@ func New(code int, message string, detail interface{}) Code { detail: detail, } } - -// Code returns the integer number of current error code. -func (c localCode) Code() int { - return c.code -} - -// Message returns the brief message for current error code. -func (c localCode) Message() string { - return c.message -} - -// Detail returns the detailed information of current error code, -// which is mainly designed as an extension field for error code. -func (c localCode) Detail() interface{} { - return c.detail -} - -// String returns current error code as a string. -func (c localCode) String() string { - if c.detail != nil { - return fmt.Sprintf(`%d:%s %v`, c.code, c.message, c.detail) - } - if c.message != "" { - return fmt.Sprintf(`%d:%s`, c.code, c.message) - } - return fmt.Sprintf(`%d`, c.code) -} diff --git a/errors/gcode/gcode_local.go b/errors/gcode/gcode_local.go new file mode 100644 index 000000000..1ec1d1ea9 --- /dev/null +++ b/errors/gcode/gcode_local.go @@ -0,0 +1,43 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcode + +import "fmt" + +// localCode is an implementer for interface Code for internal usage only. +type localCode struct { + code int // Error code, usually an integer. + message string // Brief message for this error code. + detail interface{} // As type of interface, it is mainly designed as an extension field for error code. +} + +// Code returns the integer number of current error code. +func (c localCode) Code() int { + return c.code +} + +// Message returns the brief message for current error code. +func (c localCode) Message() string { + return c.message +} + +// Detail returns the detailed information of current error code, +// which is mainly designed as an extension field for error code. +func (c localCode) Detail() interface{} { + return c.detail +} + +// String returns current error code as a string. +func (c localCode) String() string { + if c.detail != nil { + return fmt.Sprintf(`%d:%s %v`, c.code, c.message, c.detail) + } + if c.message != "" { + return fmt.Sprintf(`%d:%s`, c.code, c.message) + } + return fmt.Sprintf(`%d`, c.code) +} diff --git a/errors/gcode/gcode_test.go b/errors/gcode/gcode_test.go new file mode 100644 index 000000000..68a4e7da4 --- /dev/null +++ b/errors/gcode/gcode_test.go @@ -0,0 +1,23 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcode_test + +import ( + "github.com/gogf/gf/errors/gcode" + "testing" + + "github.com/gogf/gf/test/gtest" +) + +func Test_Nil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + c := gcode.New(1, "custom error", "detailed description") + t.Assert(c.Code(), 1) + t.Assert(c.Message(), "custom error") + t.Assert(c.Detail(), "detailed description") + }) +} diff --git a/os/gview/gview.go b/os/gview/gview.go index 1b072f6e3..e6823d072 100644 --- a/os/gview/gview.go +++ b/os/gview/gview.go @@ -61,7 +61,7 @@ func ParseContent(ctx context.Context, content string, params ...Params) (string } // New returns a new view object. -// The parameter <path> specifies the template directory path to load template files. +// The parameter `path` specifies the template directory path to load template files. func New(path ...string) *View { view := &View{ paths: garray.NewStrArray(), @@ -143,6 +143,10 @@ func New(path ...string) *View { "map": view.buildInFuncMap, "maps": view.buildInFuncMaps, "json": view.buildInFuncJson, + "plus": view.buildInFuncPlus, + "minus": view.buildInFuncMinus, + "times": view.buildInFuncTimes, + "divide": view.buildInFuncDivide, }) return view diff --git a/os/gview/gview_buildin.go b/os/gview/gview_buildin.go index c838750ee..da22f78fa 100644 --- a/os/gview/gview_buildin.go +++ b/os/gview/gview_buildin.go @@ -219,8 +219,53 @@ func (view *View) buildInFuncNl2Br(str interface{}) string { } // buildInFuncJson implements build-in template function: json , -// which encodes and returns <value> as JSON string. +// which encodes and returns `value` as JSON string. func (view *View) buildInFuncJson(value interface{}) (string, error) { b, err := json.Marshal(value) return gconv.UnsafeBytesToStr(b), err } + +// buildInFuncPlus implements build-in template function: plus , +// which returns the result that pluses all `deltas` to `value`. +func (view *View) buildInFuncPlus(value interface{}, deltas ...interface{}) string { + result := gconv.Float64(value) + for _, v := range deltas { + result += gconv.Float64(v) + } + return gconv.String(result) +} + +// buildInFuncMinus implements build-in template function: minus , +// which returns the result that subtracts all `deltas` from `value`. +func (view *View) buildInFuncMinus(value interface{}, deltas ...interface{}) string { + result := gconv.Float64(value) + for _, v := range deltas { + result -= gconv.Float64(v) + } + return gconv.String(result) +} + +// buildInFuncTimes implements build-in template function: times , +// which returns the result that multiplies `value` by all of `values`. +func (view *View) buildInFuncTimes(value interface{}, values ...interface{}) string { + result := gconv.Float64(value) + for _, v := range values { + result *= gconv.Float64(v) + } + return gconv.String(result) +} + +// buildInFuncDivide implements build-in template function: divide , +// which returns the result that divides `value` by all of `values`. +func (view *View) buildInFuncDivide(value interface{}, values ...interface{}) string { + result := gconv.Float64(value) + for _, v := range values { + value2Float64 := gconv.Float64(v) + if value2Float64 == 0 { + // Invalid `value2`. + return "0" + } + result /= value2Float64 + } + return gconv.String(result) +} diff --git a/os/gview/gview_config.go b/os/gview/gview_config.go index 79dd6eced..cc62d1e88 100644 --- a/os/gview/gview_config.go +++ b/os/gview/gview_config.go @@ -95,7 +95,7 @@ func (view *View) SetConfigWithMap(m map[string]interface{}) error { } // SetPath sets the template directory path for template file search. -// The parameter <path> can be absolute or relative path, but absolute path is suggested. +// The parameter `path` can be absolute or relative path, but absolute path is suggested. func (view *View) SetPath(path string) error { var ( isDir = false @@ -239,9 +239,9 @@ func (view *View) SetAutoEncode(enable bool) { view.config.AutoEncode = enable } -// BindFunc registers customized global template function named <name> -// with given function <function> to current view object. -// The <name> is the function name which can be called in template content. +// BindFunc registers customized global template function named `name` +// with given function `function` to current view object. +// The `name` is the function name which can be called in template content. func (view *View) BindFunc(name string, function interface{}) { view.funcMap[name] = function // Clear global template object cache. diff --git a/os/gview/gview_instance.go b/os/gview/gview_instance.go index 7781dcd0c..8845e5d21 100644 --- a/os/gview/gview_instance.go +++ b/os/gview/gview_instance.go @@ -19,7 +19,7 @@ var ( ) // Instance returns an instance of View with default settings. -// The parameter <name> is the name for the instance. +// The parameter `name` is the name for the instance. func Instance(name ...string) *View { key := DefaultName if len(name) > 0 && name[0] != "" { diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index cd50e7a8b..c8a4811ee 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -53,7 +53,7 @@ var ( resourceTryFolders = []string{"template/", "template", "/template", "/template/"} ) -// Parse parses given template file <file> with given template variables <params> +// Parse parses given template file `file` with given template variables `params` // and returns the parsed template content. func (view *View) Parse(ctx context.Context, file string, params ...Params) (result string, err error) { var tpl interface{} @@ -65,7 +65,7 @@ func (view *View) Parse(ctx context.Context, file string, params ...Params) (res content string resource *gres.File ) - // Searching the absolute file path for <file>. + // Searching the absolute file path for `file`. path, folder, resource, err = view.searchFile(file) if err != nil { return nil @@ -100,7 +100,7 @@ func (view *View) Parse(ctx context.Context, file string, params ...Params) (res if item.content == "" { return "", nil } - // Get the template object instance for <folder>. + // Get the template object instance for `folder`. tpl, err = view.getTemplate(item.path, item.folder, fmt.Sprintf(`*%s`, gfile.Ext(item.path))) if err != nil { return "", err @@ -120,7 +120,7 @@ func (view *View) Parse(ctx context.Context, file string, params ...Params) (res return "", err } // Note that the template variable assignment cannot change the value - // of the existing <params> or view.data because both variables are pointers. + // of the existing `params` or view.data because both variables are pointers. // It needs to merge the values of the two maps into a new map. variables := gutil.MapMergeCopy(params...) if len(view.data) > 0 { @@ -154,7 +154,7 @@ func (view *View) ParseDefault(ctx context.Context, params ...Params) (result st return view.Parse(ctx, view.config.DefaultFile, params...) } -// ParseContent parses given template content <content> with template variables <params> +// ParseContent parses given template content `content` with template variables `params` // and returns the parsed content in []byte. func (view *View) ParseContent(ctx context.Context, content string, params ...Params) (string, error) { // It's not necessary continuing parsing if template content is empty. @@ -188,7 +188,7 @@ func (view *View) ParseContent(ctx context.Context, content string, params ...Pa return "", err } // Note that the template variable assignment cannot change the value - // of the existing <params> or view.data because both variables are pointers. + // of the existing `params` or view.data because both variables are pointers. // It needs to merge the values of the two maps into a new map. variables := gutil.MapMergeCopy(params...) if len(view.data) > 0 { @@ -216,10 +216,10 @@ func (view *View) ParseContent(ctx context.Context, content string, params ...Pa return result, nil } -// getTemplate returns the template object associated with given template file <path>. +// getTemplate returns the template object associated with given template file `path`. // It uses template cache to enhance performance, that is, it will return the same template object -// with the same given <path>. It will also automatically refresh the template cache -// if the template files under <path> changes (recursively). +// with the same given `path`. It will also automatically refresh the template cache +// if the template files under `path` changes (recursively). func (view *View) getTemplate(filePath, folderPath, pattern string) (tpl interface{}, err error) { // Key for template cache. key := fmt.Sprintf("%s_%v", filePath, view.config.Delimiters) @@ -304,9 +304,9 @@ func (view *View) formatTemplateObjectCreatingError(filePath, tplName string, er return nil } -// searchFile returns the found absolute path for <file> and its template folder path. -// Note that, the returned <folder> is the template folder path, but not the folder of -// the returned template file <path>. +// searchFile returns the found absolute path for `file` and its template folder path. +// Note that, the returned `folder` is the template folder path, but not the folder of +// the returned template file `path`. func (view *View) searchFile(file string) (path string, folder string, resource *gres.File, err error) { // Firstly checking the resource manager. if !gres.IsEmpty() { diff --git a/os/gview/gview_unit_basic_test.go b/os/gview/gview_unit_basic_test.go index 4cad9247c..7bbdcd390 100644 --- a/os/gview/gview_unit_basic_test.go +++ b/os/gview/gview_unit_basic_test.go @@ -9,6 +9,7 @@ package gview_test import ( "context" "github.com/gogf/gf/encoding/ghtml" + "github.com/gogf/gf/os/gctx" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" "io/ioutil" @@ -426,3 +427,39 @@ func Test_BuildInFuncJson(t *testing.T) { t.Assert(r, `{"name":"john"}`) }) } + +func Test_BuildInFuncPlus(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + v := gview.New() + r, err := v.ParseContent(gctx.New(), "{{plus 1 2 3}}") + t.Assert(err, nil) + t.Assert(r, `6`) + }) +} + +func Test_BuildInFuncMinus(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + v := gview.New() + r, err := v.ParseContent(gctx.New(), "{{minus 1 2 3}}") + t.Assert(err, nil) + t.Assert(r, `-4`) + }) +} + +func Test_BuildInFuncTimes(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + v := gview.New() + r, err := v.ParseContent(gctx.New(), "{{times 1 2 3 4}}") + t.Assert(err, nil) + t.Assert(r, `24`) + }) +} + +func Test_BuildInFuncDivide(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + v := gview.New() + r, err := v.ParseContent(gctx.New(), "{{divide 8 2 2}}") + t.Assert(err, nil) + t.Assert(r, `2`) + }) +} From f9eaa8f930b17d0c57dbaa515a96bfd2b700af12 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 25 Aug 2021 22:31:59 +0800 Subject: [PATCH 481/492] fix issue of nested transaction missing in function operations for package gdb --- database/gdb/gdb_core.go | 4 --- database/gdb/gdb_core_underlying.go | 30 ++++++++++++++++-- database/gdb/gdb_z_mysql_transaction_test.go | 32 ++++++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 7b76f5dca..58519e927 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -38,10 +38,6 @@ func (c *Core) Ctx(ctx context.Context) DB { if ctx == nil { return c.db } - // It is already set context in previous chaining operation. - if c.ctx != nil { - return c.db - } ctx = context.WithValue(ctx, ctxStrictKeyName, 1) // It makes a shallow copy of current db and changes its context for next chaining operation. var ( diff --git a/database/gdb/gdb_core_underlying.go b/database/gdb/gdb_core_underlying.go index 8e88d0938..036ed3494 100644 --- a/database/gdb/gdb_core_underlying.go +++ b/database/gdb/gdb_core_underlying.go @@ -27,10 +27,15 @@ func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { // Transaction checks. if link == nil { - if link, err = c.SlaveLink(); err != nil { + if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { + // Firstly, check and retrieve transaction link from context. + link = &txLink{tx.tx} + } else if link, err = c.SlaveLink(); err != nil { + // Or else it creates one from master node. return nil, err } } else if !link.IsTransaction() { + // If current link is not transaction link, it checks and retrieves transaction from context. if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { link = &txLink{tx.tx} } @@ -84,10 +89,15 @@ func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err err func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) { // Transaction checks. if link == nil { - if link, err = c.MasterLink(); err != nil { + if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { + // Firstly, check and retrieve transaction link from context. + link = &txLink{tx.tx} + } else if link, err = c.MasterLink(); err != nil { + // Or else it creates one from master node. return nil, err } } else if !link.IsTransaction() { + // If current link is not transaction link, it checks and retrieves transaction from context. if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { link = &txLink{tx.tx} } @@ -170,11 +180,25 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) { // DoPrepare calls prepare function on given link object and returns the statement object. func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) { - if link != nil && !link.IsTransaction() { + // Transaction checks. + if link == nil { + if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { + // Firstly, check and retrieve transaction link from context. + link = &txLink{tx.tx} + } else { + // Or else it creates one from master node. + var err error + if link, err = c.MasterLink(); err != nil { + return nil, err + } + } + } else if !link.IsTransaction() { + // If current link is not transaction link, it checks and retrieves transaction from context. if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { link = &txLink{tx.tx} } } + if c.GetConfig().PrepareTimeout > 0 { // DO NOT USE cancel function in prepare statement. ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout) diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index 669055145..d12ff51e4 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -9,6 +9,7 @@ package gdb_test import ( "context" "fmt" + "github.com/gogf/gf/os/gctx" "testing" "github.com/gogf/gf/database/gdb" @@ -1116,3 +1117,34 @@ func Test_Transaction_Nested_SavePoint_RollbackTo(t *testing.T) { t.Assert(all[0]["id"], 1) }) } + +func Test_Transaction_Method(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + var err error + err = db.Transaction(gctx.New(), func(ctx context.Context, tx *gdb.TX) error { + _, err = db.Model(table).Ctx(ctx).Data(g.Map{ + "id": 1, + "passport": "t1", + "password": "25d55ad283aa400af464c76d713c07ad", + "nickname": "T1", + "create_time": gtime.Now().String(), + }).Insert() + t.AssertNil(err) + + _, err = db.Ctx(ctx).Exec(fmt.Sprintf( + "insert into %s(`passport`,`password`,`nickname`,`create_time`,`id`) "+ + "VALUES('t2','25d55ad283aa400af464c76d713c07ad','T2','2021-08-25 21:53:00',2) ", + table)) + t.AssertNil(err) + return gerror.New("rollback") + }) + t.AssertNE(err, nil) + + count, err := db.Model(table).Count() + t.AssertNil(err) + t.Assert(count, 0) + }) +} From e0a3233ea3cc05bdc5e92e5d3a16335a0afa6250 Mon Sep 17 00:00:00 2001 From: John <john@johng.cn> Date: Thu, 26 Aug 2021 00:11:27 +0800 Subject: [PATCH 482/492] improve package gcron --- go.sum | 24 +++---------- os/gcron/gcron_schedule.go | 73 +++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 52 deletions(-) diff --git a/go.sum b/go.sum index 181576aa5..45bd2270a 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,8 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4= github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= @@ -20,25 +18,16 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= -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/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= @@ -55,19 +44,16 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w 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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= diff --git a/os/gcron/gcron_schedule.go b/os/gcron/gcron_schedule.go index 7ff33d03e..8bfb68a35 100644 --- a/os/gcron/gcron_schedule.go +++ b/os/gcron/gcron_schedule.go @@ -33,6 +33,10 @@ type cronSchedule struct { const ( // regular expression for cron pattern, which contains 6 parts of time units. regexForCron = `^([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,A-Za-z]+)\s+([\-/\d\*\?,A-Za-z]+)$` + + patternItemTypeUnknown = iota + patternItemTypeWeek + patternItemTypeMonth ) var ( @@ -103,37 +107,37 @@ func newSchedule(pattern string) (*cronSchedule, error) { pattern: pattern, } // Second. - if m, err := parseItem(match[1], 0, 59, false); err != nil { + if m, err := parsePatternItem(match[1], 0, 59, false); err != nil { return nil, err } else { schedule.second = m } // Minute. - if m, err := parseItem(match[2], 0, 59, false); err != nil { + if m, err := parsePatternItem(match[2], 0, 59, false); err != nil { return nil, err } else { schedule.minute = m } // Hour. - if m, err := parseItem(match[3], 0, 23, false); err != nil { + if m, err := parsePatternItem(match[3], 0, 23, false); err != nil { return nil, err } else { schedule.hour = m } // Day. - if m, err := parseItem(match[4], 1, 31, true); err != nil { + if m, err := parsePatternItem(match[4], 1, 31, true); err != nil { return nil, err } else { schedule.day = m } // Month. - if m, err := parseItem(match[5], 1, 12, false); err != nil { + if m, err := parsePatternItem(match[5], 1, 12, false); err != nil { return nil, err } else { schedule.month = m } // Week. - if m, err := parseItem(match[6], 0, 6, true); err != nil { + if m, err := parsePatternItem(match[6], 0, 6, true); err != nil { return nil, err } else { schedule.week = m @@ -144,52 +148,55 @@ func newSchedule(pattern string) (*cronSchedule, error) { } } -// parseItem parses every item in the pattern and returns the result as map. -func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]struct{}, error) { +// parsePatternItem parses every item in the pattern and returns the result as map, which is used for indexing. +func parsePatternItem(item string, min int, max int, allowQuestionMark bool) (map[int]struct{}, error) { m := make(map[int]struct{}, max-min+1) if item == "*" || (allowQuestionMark && item == "?") { for i := min; i <= max; i++ { m[i] = struct{}{} } } else { + // Like: MON,FRI for _, item := range strings.Split(item, ",") { - interval := 1 - intervalArray := strings.Split(item, "/") + var ( + interval = 1 + intervalArray = strings.Split(item, "/") + ) if len(intervalArray) == 2 { - if i, err := strconv.Atoi(intervalArray[1]); err != nil { + if number, err := strconv.Atoi(intervalArray[1]); err != nil { return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { - interval = i + interval = number } } var ( rangeMin = min rangeMax = max - fieldType = byte(0) + itemType = patternItemTypeUnknown rangeArray = strings.Split(intervalArray[0], "-") // Like: 1-30, JAN-DEC ) switch max { case 6: // It's checking week field. - fieldType = 'w' + itemType = patternItemTypeWeek case 12: // It's checking month field. - fieldType = 'm' + itemType = patternItemTypeMonth } // Eg: */5 if rangeArray[0] != "*" { - if i, err := parseItemValue(rangeArray[0], fieldType); err != nil { + if number, err := parsePatternItemValue(rangeArray[0], itemType); err != nil { return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { - rangeMin = i - rangeMax = i + rangeMin = number + rangeMax = number } } if len(rangeArray) == 2 { - if i, err := parseItemValue(rangeArray[1], fieldType); err != nil { + if number, err := parsePatternItemValue(rangeArray[1], itemType); err != nil { return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { - rangeMax = i + rangeMax = number } } for i := rangeMin; i <= rangeMax; i += interval { @@ -200,24 +207,24 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s return m, nil } -// parseItemValue parses the field value to a number according to its field type. -func parseItemValue(value string, fieldType byte) (int, error) { +// parsePatternItemValue parses the field value to a number according to its field type. +func parsePatternItemValue(value string, itemType int) (int, error) { if gregex.IsMatchString(`^\d+$`, value) { - // Pure number. - if i, err := strconv.Atoi(value); err == nil { - return i, nil + // It is pure number. + if number, err := strconv.Atoi(value); err == nil { + return number, nil } } else { - // Check if contains letter, + // Check if it contains letter, // it converts the value to number according to predefined map. - switch fieldType { - case 'm': - if i, ok := monthMap[strings.ToLower(value)]; ok { - return i, nil + switch itemType { + case patternItemTypeWeek: + if number, ok := monthMap[strings.ToLower(value)]; ok { + return number, nil } - case 'w': - if i, ok := weekMap[strings.ToLower(value)]; ok { - return i, nil + case patternItemTypeMonth: + if number, ok := weekMap[strings.ToLower(value)]; ok { + return number, nil } } } From 5b57c35522804a8282e888abb69f059c765c25f8 Mon Sep 17 00:00:00 2001 From: John <john@johng.cn> Date: Fri, 27 Aug 2021 00:01:15 +0800 Subject: [PATCH 483/492] comment updates for package gcache --- os/gcache/gcache.go | 62 +++++++------- os/gcache/gcache_adapter.go | 90 ++++++++++---------- os/gcache/gcache_adapter_memory.go | 106 ++++++++++++------------ os/gcache/gcache_adapter_memory_data.go | 20 ++--- os/gcache/gcache_adapter_memory_item.go | 2 +- os/gcache/gcache_adapter_memory_lru.go | 10 +-- os/gcache/gcache_cache.go | 4 +- os/gcache/gcache_cache_adapter.go | 84 +++++++++---------- 8 files changed, 189 insertions(+), 189 deletions(-) diff --git a/os/gcache/gcache.go b/os/gcache/gcache.go index 6bfb36d48..8a0b1c07d 100644 --- a/os/gcache/gcache.go +++ b/os/gcache/gcache.go @@ -5,7 +5,7 @@ // You can obtain one at https://github.com/gogf/gf. // Package gcache provides kinds of cache management for process. -// It default provides a concurrent-safe in-memory cache adapter for process. +// It provides a concurrent-safe in-memory cache adapter for process in default. package gcache import ( @@ -23,62 +23,62 @@ func Ctx(ctx context.Context) *Cache { return defaultCache.Ctx(ctx) } -// Set sets cache with <key>-<value> pair, which is expired after <duration>. -// It does not expire if <duration> == 0. -func Set(key interface{}, value interface{}, duration time.Duration) { - defaultCache.Set(key, value, duration) +// Set sets cache with `key`-`value` pair, which is expired after `duration`. +// It does not expire if `duration` == 0. +func Set(key interface{}, value interface{}, duration time.Duration) error { + return defaultCache.Set(key, value, duration) } -// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache, -// which is expired after <duration>. It does not expire if <duration> == 0. +// SetIfNotExist sets cache with `key`-`value` pair if `key` does not exist in the cache, +// which is expired after `duration`. It does not expire if `duration` == 0. func SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) { return defaultCache.SetIfNotExist(key, value, duration) } -// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>. +// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`. // -// It does not expire if <duration> == 0. +// It does not expire if `duration` == 0. func Sets(data map[interface{}]interface{}, duration time.Duration) error { return defaultCache.Sets(data, duration) } -// Get returns the value of <key>. +// Get returns the value of `key`. // It returns nil if it does not exist or its value is nil. func Get(key interface{}) (interface{}, error) { return defaultCache.Get(key) } -// GetVar retrieves and returns the value of <key> as gvar.Var. +// GetVar retrieves and returns the value of `key` as gvar.Var. func GetVar(key interface{}) (*gvar.Var, error) { return defaultCache.GetVar(key) } -// GetOrSet returns the value of <key>, -// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache. -// The key-value pair expires after <duration>. +// GetOrSet returns the value of `key`, +// or sets `key`-`value` pair and returns `value` if `key` does not exist in the cache. +// The key-value pair expires after `duration`. // -// It does not expire if <duration> == 0. +// It does not expire if `duration` == 0. func GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error) { return defaultCache.GetOrSet(key, value, duration) } -// GetOrSetFunc returns the value of <key>, or sets <key> with result of function <f> -// and returns its result if <key> does not exist in the cache. The key-value pair expires -// after <duration>. It does not expire if <duration> == 0. +// GetOrSetFunc returns the value of `key`, or sets `key` with result of function `f` +// and returns its result if `key` does not exist in the cache. The key-value pair expires +// after `duration`. It does not expire if `duration` == 0. func GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { return defaultCache.GetOrSetFunc(key, f, duration) } -// GetOrSetFuncLock returns the value of <key>, or sets <key> with result of function <f> -// and returns its result if <key> does not exist in the cache. The key-value pair expires -// after <duration>. It does not expire if <duration> == 0. +// GetOrSetFuncLock returns the value of `key`, or sets `key` with result of function `f` +// and returns its result if `key` does not exist in the cache. The key-value pair expires +// after `duration`. It does not expire if `duration` == 0. // -// Note that the function <f> is executed within writing mutex lock. +// Note that the function `f` is executed within writing mutex lock. func GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { return defaultCache.GetOrSetFuncLock(key, f, duration) } -// Contains returns true if <key> exists in the cache, or else returns false. +// Contains returns true if `key` exists in the cache, or else returns false. func Contains(key interface{}) (bool, error) { return defaultCache.Contains(key) } @@ -89,10 +89,10 @@ func Remove(keys ...interface{}) (value interface{}, err error) { return defaultCache.Remove(keys...) } -// Removes deletes <keys> in the cache. +// Removes deletes `keys` in the cache. // Deprecated, use Remove instead. func Removes(keys []interface{}) { - defaultCache.Removes(keys) + defaultCache.Remove(keys...) } // Data returns a copy of all key-value pairs in the cache as map type. @@ -120,20 +120,20 @@ func Size() (int, error) { return defaultCache.Size() } -// GetExpire retrieves and returns the expiration of <key>. -// It returns -1 if the <key> does not exist in the cache. +// GetExpire retrieves and returns the expiration of `key`. +// It returns -1 if the `key` does not exist in the cache. func GetExpire(key interface{}) (time.Duration, error) { return defaultCache.GetExpire(key) } -// Update updates the value of <key> without changing its expiration and returns the old value. -// The returned <exist> value is false if the <key> does not exist in the cache. +// Update updates the value of `key` without changing its expiration and returns the old value. +// The returned `exist` value is false if the `key` does not exist in the cache. func Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { return defaultCache.Update(key, value) } -// UpdateExpire updates the expiration of <key> and returns the old expiration duration value. -// It returns -1 if the <key> does not exist in the cache. +// UpdateExpire updates the expiration of `key` and returns the old expiration duration value. +// It returns -1 if the `key` does not exist in the cache. func UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { return defaultCache.UpdateExpire(key, duration) } diff --git a/os/gcache/gcache_adapter.go b/os/gcache/gcache_adapter.go index f12c32570..33f179058 100644 --- a/os/gcache/gcache_adapter.go +++ b/os/gcache/gcache_adapter.go @@ -11,95 +11,95 @@ import ( "time" ) -// Adapter is the adapter for cache features implements. +// Adapter is the core adapter for cache features implements. type Adapter interface { - // Set sets cache with <key>-<value> pair, which is expired after <duration>. + // Set sets cache with `key`-`value` pair, which is expired after `duration`. // - // It does not expire if <duration> == 0. - // It deletes the <key> if <duration> < 0. + // It does not expire if `duration` == 0. + // It deletes the `key` if `duration` < 0. Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error - // Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>. + // Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`. // - // It does not expire if <duration> == 0. - // It deletes the keys of <data> if <duration> < 0 or given <value> is nil. + // It does not expire if `duration` == 0. + // It deletes the keys of `data` if `duration` < 0 or given `value` is nil. Sets(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error - // SetIfNotExist sets cache with <key>-<value> pair which is expired after <duration> - // if <key> does not exist in the cache. It returns true the <key> dose not exist in the - // cache and it sets <value> successfully to the cache, or else it returns false. + // SetIfNotExist sets cache with `key`-`value` pair which is expired after `duration` + // if `key` does not exist in the cache. It returns true the `key` does not exist in the + // cache, and it sets `value` successfully to the cache, or else it returns false. // - // The parameter <value> can be type of <func() interface{}>, but it dose nothing if its + // The parameter `value` can be type of `func() interface{}`, but it does nothing if its // result is nil. // - // It does not expire if <duration> == 0. - // It deletes the <key> if <duration> < 0 or given <value> is nil. + // It does not expire if `duration` == 0. + // It deletes the `key` if `duration` < 0 or given `value` is nil. SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (bool, error) - // Get retrieves and returns the associated value of given <key>. - // It returns nil if it does not exist, its value is nil or it's expired. + // Get retrieves and returns the associated value of given `key`. + // It returns nil if it does not exist, its value is nil, or it's expired. Get(ctx context.Context, key interface{}) (interface{}, error) - // GetOrSet retrieves and returns the value of <key>, or sets <key>-<value> pair and - // returns <value> if <key> does not exist in the cache. The key-value pair expires - // after <duration>. + // GetOrSet retrieves and returns the value of `key`, or sets `key`-`value` pair and + // returns `value` if `key` does not exist in the cache. The key-value pair expires + // after `duration`. // - // It does not expire if <duration> == 0. - // It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing - // if <value> is a function and the function result is nil. + // It does not expire if `duration` == 0. + // It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing + // if `value` is a function and the function result is nil. GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (interface{}, error) - // GetOrSetFunc retrieves and returns the value of <key>, or sets <key> with result of - // function <f> and returns its result if <key> does not exist in the cache. The key-value - // pair expires after <duration>. + // GetOrSetFunc retrieves and returns the value of `key`, or sets `key` with result of + // function `f` and returns its result if `key` does not exist in the cache. The key-value + // pair expires after `duration`. // - // It does not expire if <duration> == 0. - // It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing - // if <value> is a function and the function result is nil. + // It does not expire if `duration` == 0. + // It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing + // if `value` is a function and the function result is nil. GetOrSetFunc(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) - // GetOrSetFuncLock retrieves and returns the value of <key>, or sets <key> with result of - // function <f> and returns its result if <key> does not exist in the cache. The key-value - // pair expires after <duration>. + // GetOrSetFuncLock retrieves and returns the value of `key`, or sets `key` with result of + // function `f` and returns its result if `key` does not exist in the cache. The key-value + // pair expires after `duration`. // - // It does not expire if <duration> == 0. - // It does nothing if function <f> returns nil. + // It does not expire if `duration` == 0. + // It does nothing if function `f` returns nil. // - // Note that the function <f> should be executed within writing mutex lock for concurrent + // Note that the function `f` should be executed within writing mutex lock for concurrent // safety purpose. GetOrSetFuncLock(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) - // Contains returns true if <key> exists in the cache, or else returns false. + // Contains returns true if `key` exists in the cache, or else returns false. Contains(ctx context.Context, key interface{}) (bool, error) - // GetExpire retrieves and returns the expiration of <key> in the cache. + // GetExpire retrieves and returns the expiration of `key` in the cache. // - // It returns 0 if the <key> does not expire. - // It returns -1 if the <key> does not exist in the cache. + // It returns 0 if the `key` does not expire. + // It returns -1 if the `key` does not exist in the cache. GetExpire(ctx context.Context, key interface{}) (time.Duration, error) // Remove deletes one or more keys from cache, and returns its value. // If multiple keys are given, it returns the value of the last deleted item. Remove(ctx context.Context, keys ...interface{}) (value interface{}, err error) - // Update updates the value of <key> without changing its expiration and returns the old value. - // The returned value <exist> is false if the <key> does not exist in the cache. + // Update updates the value of `key` without changing its expiration and returns the old value. + // The returned value `exist` is false if the `key` does not exist in the cache. // - // It deletes the <key> if given <value> is nil. - // It does nothing if <key> does not exist in the cache. + // It deletes the `key` if given `value` is nil. + // It does nothing if `key` does not exist in the cache. Update(ctx context.Context, key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) - // UpdateExpire updates the expiration of <key> and returns the old expiration duration value. + // UpdateExpire updates the expiration of `key` and returns the old expiration duration value. // - // It returns -1 and does nothing if the <key> does not exist in the cache. - // It deletes the <key> if <duration> < 0. + // It returns -1 and does nothing if the `key` does not exist in the cache. + // It deletes the `key` if `duration` < 0. UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error) // Size returns the number of items in the cache. Size(ctx context.Context) (size int, err error) // Data returns a copy of all key-value pairs in the cache as map type. - // Note that this function may leads lots of memory usage, you can implement this function + // Note that this function may lead lots of memory usage, you can implement this function // if necessary. Data(ctx context.Context) (map[interface{}]interface{}, error) diff --git a/os/gcache/gcache_adapter_memory.go b/os/gcache/gcache_adapter_memory.go index cc8dba5b2..c70b2b3e3 100644 --- a/os/gcache/gcache_adapter_memory.go +++ b/os/gcache/gcache_adapter_memory.go @@ -29,7 +29,7 @@ type adapterMemory struct { expireTimes *adapterMemoryExpireTimes // expireTimes is the expiring key to its timestamp mapping, which is used for quick indexing and deleting. expireSets *adapterMemoryExpireSets // expireSets is the expiring timestamp to its key set mapping, which is used for quick indexing and deleting. lru *adapterMemoryLru // lru is the LRU manager, which is enabled when attribute cap > 0. - lruGetList *glist.List // lruGetList is the LRU history according with Get function. + lruGetList *glist.List // lruGetList is the LRU history according to Get function. eventList *glist.List // eventList is the asynchronous event list for internal data synchronization. closed *gtype.Bool // closed controls the cache closed or not. } @@ -69,10 +69,10 @@ func newAdapterMemory(lruCap ...int) *adapterMemory { return c } -// Set sets cache with <key>-<value> pair, which is expired after <duration>. +// Set sets cache with `key`-`value` pair, which is expired after `duration`. // -// It does not expire if <duration> == 0. -// It deletes the <key> if <duration> < 0. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0. func (c *adapterMemory) Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error { expireTime := c.getInternalExpire(duration) c.data.Set(key, adapterMemoryItem{ @@ -86,19 +86,19 @@ func (c *adapterMemory) Set(ctx context.Context, key interface{}, value interfac return nil } -// Update updates the value of <key> without changing its expiration and returns the old value. -// The returned value <exist> is false if the <key> does not exist in the cache. +// Update updates the value of `key` without changing its expiration and returns the old value. +// The returned value `exist` is false if the `key` does not exist in the cache. // -// It deletes the <key> if given <value> is nil. -// It does nothing if <key> does not exist in the cache. +// It deletes the `key` if given `value` is nil. +// It does nothing if `key` does not exist in the cache. func (c *adapterMemory) Update(ctx context.Context, key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { return c.data.Update(key, value) } -// UpdateExpire updates the expiration of <key> and returns the old expiration duration value. +// UpdateExpire updates the expiration of `key` and returns the old expiration duration value. // -// It returns -1 and does nothing if the <key> does not exist in the cache. -// It deletes the <key> if <duration> < 0. +// It returns -1 and does nothing if the `key` does not exist in the cache. +// It deletes the `key` if `duration` < 0. func (c *adapterMemory) UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { newExpireTime := c.getInternalExpire(duration) oldDuration, err = c.data.UpdateExpire(key, newExpireTime) @@ -114,10 +114,10 @@ func (c *adapterMemory) UpdateExpire(ctx context.Context, key interface{}, durat return } -// GetExpire retrieves and returns the expiration of <key> in the cache. +// GetExpire retrieves and returns the expiration of `key` in the cache. // -// It returns 0 if the <key> does not expire. -// It returns -1 if the <key> does not exist in the cache. +// It returns 0 if the `key` does not expire. +// It returns -1 if the `key` does not exist in the cache. func (c *adapterMemory) GetExpire(ctx context.Context, key interface{}) (time.Duration, error) { if item, ok := c.data.Get(key); ok { return time.Duration(item.e-gtime.TimestampMilli()) * time.Millisecond, nil @@ -125,14 +125,14 @@ func (c *adapterMemory) GetExpire(ctx context.Context, key interface{}) (time.Du return -1, nil } -// SetIfNotExist sets cache with <key>-<value> pair which is expired after <duration> -// if <key> does not exist in the cache. It returns true the <key> dose not exist in the -// cache and it sets <value> successfully to the cache, or else it returns false. -// The parameter <value> can be type of <func() interface{}>, but it dose nothing if its +// SetIfNotExist sets cache with `key`-`value` pair which is expired after `duration` +// if `key` does not exist in the cache. It returns true the `key` does not exist in the +// cache, and it sets `value` successfully to the cache, or else it returns false. +// The parameter `value` can be type of <func() interface{}>, but it dose nothing if its // result is nil. // -// It does not expire if <duration> == 0. -// It deletes the <key> if <duration> < 0 or given <value> is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil. func (c *adapterMemory) SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (bool, error) { isContained, err := c.Contains(ctx, key) if err != nil { @@ -148,10 +148,10 @@ func (c *adapterMemory) SetIfNotExist(ctx context.Context, key interface{}, valu return false, nil } -// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>. +// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`. // -// It does not expire if <duration> == 0. -// It deletes the keys of <data> if <duration> < 0 or given <value> is nil. +// It does not expire if `duration` == 0. +// It deletes the keys of `data` if `duration` < 0 or given `value` is nil. func (c *adapterMemory) Sets(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error { var ( expireTime = c.getInternalExpire(duration) @@ -169,7 +169,7 @@ func (c *adapterMemory) Sets(ctx context.Context, data map[interface{}]interface return nil } -// Get retrieves and returns the associated value of given <key>. +// Get retrieves and returns the associated value of given `key`. // It returns nil if it does not exist or its value is nil. func (c *adapterMemory) Get(ctx context.Context, key interface{}) (interface{}, error) { item, ok := c.data.Get(key) @@ -183,13 +183,13 @@ func (c *adapterMemory) Get(ctx context.Context, key interface{}) (interface{}, return nil, nil } -// GetOrSet retrieves and returns the value of <key>, or sets <key>-<value> pair and -// returns <value> if <key> does not exist in the cache. The key-value pair expires -// after <duration>. +// GetOrSet retrieves and returns the value of `key`, or sets `key`-`value` pair and +// returns `value` if `key` does not exist in the cache. The key-value pair expires +// after `duration`. // -// It does not expire if <duration> == 0. -// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing -// if <value> is a function and the function result is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing +// if `value` is a function and the function result is nil. func (c *adapterMemory) GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (interface{}, error) { v, err := c.Get(ctx, key) if err != nil { @@ -202,13 +202,13 @@ func (c *adapterMemory) GetOrSet(ctx context.Context, key interface{}, value int } } -// GetOrSetFunc retrieves and returns the value of <key>, or sets <key> with result of -// function <f> and returns its result if <key> does not exist in the cache. The key-value -// pair expires after <duration>. +// GetOrSetFunc retrieves and returns the value of `key`, or sets `key` with result of +// function `f` and returns its result if `key` does not exist in the cache. The key-value +// pair expires after `duration`. // -// It does not expire if <duration> == 0. -// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing -// if <value> is a function and the function result is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing +// if `value` is a function and the function result is nil. func (c *adapterMemory) GetOrSetFunc(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { v, err := c.Get(ctx, key) if err != nil { @@ -228,14 +228,14 @@ func (c *adapterMemory) GetOrSetFunc(ctx context.Context, key interface{}, f fun } } -// GetOrSetFuncLock retrieves and returns the value of <key>, or sets <key> with result of -// function <f> and returns its result if <key> does not exist in the cache. The key-value -// pair expires after <duration>. +// GetOrSetFuncLock retrieves and returns the value of `key`, or sets `key` with result of +// function `f` and returns its result if `key` does not exist in the cache. The key-value +// pair expires after `duration`. // -// It does not expire if <duration> == 0. -// It does nothing if function <f> returns nil. +// It does not expire if `duration` == 0. +// It does nothing if function `f` returns nil. // -// Note that the function <f> should be executed within writing mutex lock for concurrent +// Note that the function `f` should be executed within writing mutex lock for concurrent // safety purpose. func (c *adapterMemory) GetOrSetFuncLock(ctx context.Context, key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { v, err := c.Get(ctx, key) @@ -249,7 +249,7 @@ func (c *adapterMemory) GetOrSetFuncLock(ctx context.Context, key interface{}, f } } -// Contains returns true if <key> exists in the cache, or else returns false. +// Contains returns true if `key` exists in the cache, or else returns false. func (c *adapterMemory) Contains(ctx context.Context, key interface{}) (bool, error) { v, err := c.Get(ctx, key) if err != nil { @@ -310,14 +310,14 @@ func (c *adapterMemory) Close(ctx context.Context) error { return nil } -// doSetWithLockCheck sets cache with <key>-<value> pair if <key> does not exist in the -// cache, which is expired after <duration>. +// doSetWithLockCheck sets cache with `key`-`value` pair if `key` does not exist in the +// cache, which is expired after `duration`. // -// It does not expire if <duration> == 0. -// The parameter <value> can be type of <func() interface{}>, but it dose nothing if the +// It does not expire if `duration` == 0. +// The parameter `value` can be type of <func() interface{}>, but it dose nothing if the // function result is nil. // -// It doubly checks the <key> whether exists in the cache using mutex writing lock +// It doubly checks the `key` whether exists in the cache using mutex writing lock // before setting it to the cache. func (c *adapterMemory) doSetWithLockCheck(key interface{}, value interface{}, duration time.Duration) (result interface{}, err error) { expireTimestamp := c.getInternalExpire(duration) @@ -335,14 +335,14 @@ func (c *adapterMemory) getInternalExpire(duration time.Duration) int64 { } } -// makeExpireKey groups the <expire> in milliseconds to its according seconds. +// makeExpireKey groups the `expire` in milliseconds to its according seconds. func (c *adapterMemory) makeExpireKey(expire int64) int64 { return int64(math.Ceil(float64(expire/1000)+1) * 1000) } // syncEventAndClearExpired does the asynchronous task loop: // 1. Asynchronously process the data in the event list, -// and synchronize the results to the <expireTimes> and <expireSets> properties. +// and synchronize the results to the `expireTimes` and `expireSets` properties. // 2. Clean up the expired key-value pair data. func (c *adapterMemory) syncEventAndClearExpired() { if c.closed.Val() { @@ -411,13 +411,13 @@ func (c *adapterMemory) syncEventAndClearExpired() { } } -// clearByKey deletes the key-value pair with given <key>. -// The parameter <force> specifies whether doing this deleting forcibly. +// clearByKey deletes the key-value pair with given `key`. +// The parameter `force` specifies whether doing this deleting forcibly. func (c *adapterMemory) clearByKey(key interface{}, force ...bool) { // Doubly check before really deleting it from cache. c.data.DeleteWithDoubleCheck(key, force...) - // Deleting its expire time from <expireTimes>. + // Deleting its expire time from `expireTimes`. c.expireTimes.Delete(key) // Deleting it from LRU. diff --git a/os/gcache/gcache_adapter_memory_data.go b/os/gcache/gcache_adapter_memory_data.go index 839922e41..86b20e0fc 100644 --- a/os/gcache/gcache_adapter_memory_data.go +++ b/os/gcache/gcache_adapter_memory_data.go @@ -23,11 +23,11 @@ func newAdapterMemoryData() *adapterMemoryData { } } -// Update updates the value of <key> without changing its expiration and returns the old value. -// The returned value <exist> is false if the <key> does not exist in the cache. +// Update updates the value of `key` without changing its expiration and returns the old value. +// The returned value `exist` is false if the `key` does not exist in the cache. // -// It deletes the <key> if given <value> is nil. -// It does nothing if <key> does not exist in the cache. +// It deletes the `key` if given `value` is nil. +// It does nothing if `key` does not exist in the cache. func (d *adapterMemoryData) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { d.mu.Lock() defer d.mu.Unlock() @@ -41,10 +41,10 @@ func (d *adapterMemoryData) Update(key interface{}, value interface{}) (oldValue return nil, false, nil } -// UpdateExpire updates the expiration of <key> and returns the old expiration duration value. +// UpdateExpire updates the expiration of `key` and returns the old expiration duration value. // -// It returns -1 and does nothing if the <key> does not exist in the cache. -// It deletes the <key> if <duration> < 0. +// It returns -1 and does nothing if the `key` does not exist in the cache. +// It deletes the `key` if `duration` < 0. func (d *adapterMemoryData) UpdateExpire(key interface{}, expireTime int64) (oldDuration time.Duration, err error) { d.mu.Lock() defer d.mu.Unlock() @@ -152,10 +152,10 @@ func (d *adapterMemoryData) Set(key interface{}, value adapterMemoryItem) { d.mu.Unlock() } -// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>. +// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`. // -// It does not expire if <duration> == 0. -// It deletes the keys of <data> if <duration> < 0 or given <value> is nil. +// It does not expire if `duration` == 0. +// It deletes the keys of `data` if `duration` < 0 or given `value` is nil. func (d *adapterMemoryData) Sets(data map[interface{}]interface{}, expireTime int64) error { d.mu.Lock() for k, v := range data { diff --git a/os/gcache/gcache_adapter_memory_item.go b/os/gcache/gcache_adapter_memory_item.go index 44b72a5ef..ce411be8f 100644 --- a/os/gcache/gcache_adapter_memory_item.go +++ b/os/gcache/gcache_adapter_memory_item.go @@ -10,7 +10,7 @@ import ( "github.com/gogf/gf/os/gtime" ) -// IsExpired checks whether <item> is expired. +// IsExpired checks whether `item` is expired. func (item *adapterMemoryItem) IsExpired() bool { // Note that it should use greater than or equal judgement here // imagining that the cache time is only 1 millisecond. diff --git a/os/gcache/gcache_adapter_memory_lru.go b/os/gcache/gcache_adapter_memory_lru.go index 455ce6d7d..c2f2a64d5 100644 --- a/os/gcache/gcache_adapter_memory_lru.go +++ b/os/gcache/gcache_adapter_memory_lru.go @@ -43,7 +43,7 @@ func (lru *adapterMemoryLru) Close() { lru.closed.Set(true) } -// Remove deletes the <key> FROM <lru>. +// Remove deletes the `key` FROM `lru`. func (lru *adapterMemoryLru) Remove(key interface{}) { if v := lru.data.Get(key); v != nil { lru.data.Remove(key) @@ -51,17 +51,17 @@ func (lru *adapterMemoryLru) Remove(key interface{}) { } } -// Size returns the size of <lru>. +// Size returns the size of `lru`. func (lru *adapterMemoryLru) Size() int { return lru.data.Size() } -// Push pushes <key> to the tail of <lru>. +// Push pushes `key` to the tail of `lru`. func (lru *adapterMemoryLru) Push(key interface{}) { lru.rawList.PushBack(key) } -// Pop deletes and returns the key from tail of <lru>. +// Pop deletes and returns the key from tail of `lru`. func (lru *adapterMemoryLru) Pop() interface{} { if v := lru.list.PopBack(); v != nil { lru.data.Remove(v) @@ -78,7 +78,7 @@ func (lru *adapterMemoryLru) Pop() interface{} { // fmt.Println() //} -// SyncAndClear synchronizes the keys from <rawList> to <list> and <data> +// SyncAndClear synchronizes the keys from `rawList` to `list` and `data` // using Least Recently Used algorithm. func (lru *adapterMemoryLru) SyncAndClear() { if lru.closed.Val() { diff --git a/os/gcache/gcache_cache.go b/os/gcache/gcache_cache.go index 54f5ea626..216549bd9 100644 --- a/os/gcache/gcache_cache.go +++ b/os/gcache/gcache_cache.go @@ -56,13 +56,13 @@ func (c *Cache) SetAdapter(adapter Adapter) { c.adapter = adapter } -// GetVar retrieves and returns the value of <key> as gvar.Var. +// GetVar retrieves and returns the value of `key` as gvar.Var. func (c *Cache) GetVar(key interface{}) (*gvar.Var, error) { v, err := c.Get(key) return gvar.New(v), err } -// Removes deletes <keys> in the cache. +// Removes deletes `keys` in the cache. // Deprecated, use Remove instead. func (c *Cache) Removes(keys []interface{}) error { _, err := c.Remove(keys...) diff --git a/os/gcache/gcache_cache_adapter.go b/os/gcache/gcache_cache_adapter.go index 0f4f50852..7ad9547fe 100644 --- a/os/gcache/gcache_cache_adapter.go +++ b/os/gcache/gcache_cache_adapter.go @@ -10,85 +10,85 @@ import ( "time" ) -// Set sets cache with <key>-<value> pair, which is expired after <duration>. +// Set sets cache with `key`-`value` pair, which is expired after `duration`. // -// It does not expire if <duration> == 0. -// It deletes the <key> if <duration> < 0. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0. func (c *Cache) Set(key interface{}, value interface{}, duration time.Duration) error { return c.adapter.Set(c.getCtx(), key, value, duration) } -// Sets batch sets cache with key-value pairs by <data>, which is expired after <duration>. +// Sets batch sets cache with key-value pairs by `data`, which is expired after `duration`. // -// It does not expire if <duration> == 0. -// It deletes the keys of <data> if <duration> < 0 or given <value> is nil. +// It does not expire if `duration` == 0. +// It deletes the keys of `data` if `duration` < 0 or given `value` is nil. func (c *Cache) Sets(data map[interface{}]interface{}, duration time.Duration) error { return c.adapter.Sets(c.getCtx(), data, duration) } -// SetIfNotExist sets cache with <key>-<value> pair which is expired after <duration> -// if <key> does not exist in the cache. It returns true the <key> dose not exist in the -// cache and it sets <value> successfully to the cache, or else it returns false. +// SetIfNotExist sets cache with `key`-`value` pair which is expired after `duration` +// if `key` does not exist in the cache. It returns true the `key` dose not exist in the +// cache, and it sets `value` successfully to the cache, or else it returns false. // -// The parameter <value> can be type of <func() interface{}>, but it dose nothing if its +// The parameter `value` can be type of <func() interface{}>, but it dose nothing if its // result is nil. // -// It does not expire if <duration> == 0. -// It deletes the <key> if <duration> < 0 or given <value> is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil. func (c *Cache) SetIfNotExist(key interface{}, value interface{}, duration time.Duration) (bool, error) { return c.adapter.SetIfNotExist(c.getCtx(), key, value, duration) } -// Get retrieves and returns the associated value of given <key>. +// Get retrieves and returns the associated value of given `key`. // It returns nil if it does not exist, its value is nil or it's expired. func (c *Cache) Get(key interface{}) (interface{}, error) { return c.adapter.Get(c.getCtx(), key) } -// GetOrSet retrieves and returns the value of <key>, or sets <key>-<value> pair and -// returns <value> if <key> does not exist in the cache. The key-value pair expires -// after <duration>. +// GetOrSet retrieves and returns the value of `key`, or sets `key`-`value` pair and +// returns `value` if `key` does not exist in the cache. The key-value pair expires +// after `duration`. // -// It does not expire if <duration> == 0. -// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing -// if <value> is a function and the function result is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing +// if `value` is a function and the function result is nil. func (c *Cache) GetOrSet(key interface{}, value interface{}, duration time.Duration) (interface{}, error) { return c.adapter.GetOrSet(c.getCtx(), key, value, duration) } -// GetOrSetFunc retrieves and returns the value of <key>, or sets <key> with result of -// function <f> and returns its result if <key> does not exist in the cache. The key-value -// pair expires after <duration>. +// GetOrSetFunc retrieves and returns the value of `key`, or sets `key` with result of +// function `f` and returns its result if `key` does not exist in the cache. The key-value +// pair expires after `duration`. // -// It does not expire if <duration> == 0. -// It deletes the <key> if <duration> < 0 or given <value> is nil, but it does nothing -// if <value> is a function and the function result is nil. +// It does not expire if `duration` == 0. +// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing +// if `value` is a function and the function result is nil. func (c *Cache) GetOrSetFunc(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { return c.adapter.GetOrSetFunc(c.getCtx(), key, f, duration) } -// GetOrSetFuncLock retrieves and returns the value of <key>, or sets <key> with result of -// function <f> and returns its result if <key> does not exist in the cache. The key-value -// pair expires after <duration>. +// GetOrSetFuncLock retrieves and returns the value of `key`, or sets `key` with result of +// function `f` and returns its result if `key` does not exist in the cache. The key-value +// pair expires after `duration`. // -// It does not expire if <duration> == 0. -// It does nothing if function <f> returns nil. +// It does not expire if `duration` == 0. +// It does nothing if function `f` returns nil. // -// Note that the function <f> should be executed within writing mutex lock for concurrent +// Note that the function `f` should be executed within writing mutex lock for concurrent // safety purpose. func (c *Cache) GetOrSetFuncLock(key interface{}, f func() (interface{}, error), duration time.Duration) (interface{}, error) { return c.adapter.GetOrSetFuncLock(c.getCtx(), key, f, duration) } -// Contains returns true if <key> exists in the cache, or else returns false. +// Contains returns true if `key` exists in the cache, or else returns false. func (c *Cache) Contains(key interface{}) (bool, error) { return c.adapter.Contains(c.getCtx(), key) } -// GetExpire retrieves and returns the expiration of <key> in the cache. +// GetExpire retrieves and returns the expiration of `key` in the cache. // -// It returns 0 if the <key> does not expire. -// It returns -1 if the <key> does not exist in the cache. +// It returns 0 if the `key` does not expire. +// It returns -1 if the `key` does not exist in the cache. func (c *Cache) GetExpire(key interface{}) (time.Duration, error) { return c.adapter.GetExpire(c.getCtx(), key) } @@ -99,19 +99,19 @@ func (c *Cache) Remove(keys ...interface{}) (value interface{}, err error) { return c.adapter.Remove(c.getCtx(), keys...) } -// Update updates the value of <key> without changing its expiration and returns the old value. -// The returned value <exist> is false if the <key> does not exist in the cache. +// Update updates the value of `key` without changing its expiration and returns the old value. +// The returned value `exist` is false if the `key` does not exist in the cache. // -// It deletes the <key> if given <value> is nil. -// It does nothing if <key> does not exist in the cache. +// It deletes the `key` if given `value` is nil. +// It does nothing if `key` does not exist in the cache. func (c *Cache) Update(key interface{}, value interface{}) (oldValue interface{}, exist bool, err error) { return c.adapter.Update(c.getCtx(), key, value) } -// UpdateExpire updates the expiration of <key> and returns the old expiration duration value. +// UpdateExpire updates the expiration of `key` and returns the old expiration duration value. // -// It returns -1 and does nothing if the <key> does not exist in the cache. -// It deletes the <key> if <duration> < 0. +// It returns -1 and does nothing if the `key` does not exist in the cache. +// It deletes the `key` if `duration` < 0. func (c *Cache) UpdateExpire(key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { return c.adapter.UpdateExpire(c.getCtx(), key, duration) } From b316b9c0736ed6e08ec132d9d57ca8087e2120a2 Mon Sep 17 00:00:00 2001 From: John <john@johng.cn> Date: Fri, 27 Aug 2021 01:01:41 +0800 Subject: [PATCH 484/492] example updates for package gcache --- .example/os/gcache/getorset_func_lock.go | 28 +++++++++++++++++++++ .example/os/gcache/note_interface_key.go | 19 ++++++++++++++ .example/os/gcache/note_interface_value.go | 29 ++++++++++++++++++++++ os/gcache/gcache_cache_adapter.go | 6 ++--- 4 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 .example/os/gcache/getorset_func_lock.go create mode 100644 .example/os/gcache/note_interface_key.go create mode 100644 .example/os/gcache/note_interface_value.go diff --git a/.example/os/gcache/getorset_func_lock.go b/.example/os/gcache/getorset_func_lock.go new file mode 100644 index 000000000..b5e3760bc --- /dev/null +++ b/.example/os/gcache/getorset_func_lock.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/os/gcache" + "github.com/gogf/gf/os/gctx" + "time" +) + +func main() { + var ( + ch = make(chan struct{}, 0) + ctx = gctx.New() + key = `key` + value = `value` + ) + for i := 0; i < 10; i++ { + go func(index int) { + <-ch + _, _ = gcache.Ctx(ctx).GetOrSetFuncLock(key, func() (interface{}, error) { + fmt.Println(index, "entered") + return value, nil + }, 0) + }(i) + } + close(ch) + time.Sleep(time.Second) +} diff --git a/.example/os/gcache/note_interface_key.go b/.example/os/gcache/note_interface_key.go new file mode 100644 index 000000000..4f5e1ee44 --- /dev/null +++ b/.example/os/gcache/note_interface_key.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/os/gcache" + "github.com/gogf/gf/os/gctx" +) + +func main() { + var ( + ctx = gctx.New() + key1 int32 = 1 + key2 float64 = 1 + value = `value` + ) + _ = gcache.Ctx(ctx).Set(key1, value, 0) + fmt.Println(gcache.Ctx(ctx).Get(key1)) + fmt.Println(gcache.Ctx(ctx).Get(key2)) +} diff --git a/.example/os/gcache/note_interface_value.go b/.example/os/gcache/note_interface_value.go new file mode 100644 index 000000000..b102e86f0 --- /dev/null +++ b/.example/os/gcache/note_interface_value.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/os/gcache" + "github.com/gogf/gf/os/gctx" +) + +func main() { + type User struct { + Id int + Name string + Site string + } + var ( + ctx = gctx.New() + user *User + key = `UserKey` + value = &User{ + Id: 1, + Name: "GoFrame", + Site: "https://goframe.org", + } + ) + _ = gcache.Ctx(ctx).Set(key, value, 0) + v, _ := gcache.Ctx(ctx).GetVar(key) + _ = v.Scan(&user) + fmt.Printf(`%#v`, user) +} diff --git a/os/gcache/gcache_cache_adapter.go b/os/gcache/gcache_cache_adapter.go index 7ad9547fe..dce6b19ef 100644 --- a/os/gcache/gcache_cache_adapter.go +++ b/os/gcache/gcache_cache_adapter.go @@ -27,10 +27,10 @@ func (c *Cache) Sets(data map[interface{}]interface{}, duration time.Duration) e } // SetIfNotExist sets cache with `key`-`value` pair which is expired after `duration` -// if `key` does not exist in the cache. It returns true the `key` dose not exist in the +// if `key` does not exist in the cache. It returns true the `key` does not exist in the // cache, and it sets `value` successfully to the cache, or else it returns false. // -// The parameter `value` can be type of <func() interface{}>, but it dose nothing if its +// The parameter `value` can be type of <func() interface{}>, but it does nothing if its // result is nil. // // It does not expire if `duration` == 0. @@ -122,7 +122,7 @@ func (c *Cache) Size() (size int, err error) { } // Data returns a copy of all key-value pairs in the cache as map type. -// Note that this function may leads lots of memory usage, you can implement this function +// Note that this function may lead lots of memory usage, you can implement this function // if necessary. func (c *Cache) Data() (map[interface{}]interface{}, error) { return c.adapter.Data(c.getCtx()) From fa7a3e987d51aa3d9839672682b8f0b0714b56d9 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Fri, 27 Aug 2021 20:16:29 +0800 Subject: [PATCH 485/492] improve With feature for package gdb --- database/gdb/gdb_model_with.go | 73 +++++------ .../gdb/gdb_z_mysql_association_with_test.go | 124 ++++++++++++++++++ os/gcron/gcron.go | 10 +- os/gcron/gcron_cron.go | 2 + util/gmeta/gmeta.go | 32 ++--- util/guid/guid_string.go | 11 +- 6 files changed, 187 insertions(+), 65 deletions(-) diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 542f302a9..36782fffc 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -18,7 +18,7 @@ import ( "github.com/gogf/gf/text/gstr" ) -// With creates and returns an ORM model based on meta data of given object. +// With creates and returns an ORM model based on metadata of given object. // It also enables model association operations feature on given `object`. // It can be called multiple times to add one or more objects to model and enable // their mode association operations feature. @@ -64,7 +64,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { err error allowedTypeStrArray = make([]string, 0) ) - fieldMap, err := structs.FieldMap(structs.FieldMapInput{ + currentStructFieldMap, err := structs.FieldMap(structs.FieldMapInput{ Pointer: pointer, PriorityTagArray: nil, RecursiveOption: structs.RecursiveOptionEmbeddedNoTag, @@ -74,7 +74,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { } // It checks the with array and automatically calls the ScanList to complete association querying. if !m.withAll { - for _, field := range fieldMap { + for _, field := range currentStructFieldMap { for _, withItem := range m.withArray { withItemReflectValueType, err := structs.StructType(withItem) if err != nil { @@ -91,7 +91,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { } } } - for _, field := range fieldMap { + for _, field := range currentStructFieldMap { var ( fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") parsedTagOutput = m.parseWithTagInFieldStruct(field) @@ -99,43 +99,40 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { if parsedTagOutput.With == "" { continue } - // Just handler "with" type attribute struct. + // It just handlers "with" type attribute struct, so it ignores other struct types. if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) { continue } array := gstr.SplitAndTrim(parsedTagOutput.With, "=") if len(array) == 1 { - // It supports using only one column name + // It also supports using only one column name // if both tables associates using the same column name. array = append(array, parsedTagOutput.With) } var ( - model *Model - fieldKeys []string - relatedFieldName = array[0] - relatedAttrName = array[1] - relatedFieldValue interface{} + model *Model + fieldKeys []string + relatedSourceName = array[0] + relatedTargetName = array[1] + relatedTargetValue interface{} ) // Find the value of related attribute from `pointer`. - for attributeName, attributeValue := range fieldMap { - if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) { - relatedFieldValue = attributeValue.Value.Interface() + for attributeName, attributeValue := range currentStructFieldMap { + if utils.EqualFoldWithoutChars(attributeName, relatedTargetName) { + relatedTargetValue = attributeValue.Value.Interface() break } } - if relatedFieldValue == nil { + if relatedTargetValue == nil { return gerror.NewCodef( gcode.CodeInvalidParameter, - `cannot find the related value of attribute name "%s" in with tag "%s" for attribute "%s.%s"`, - relatedAttrName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(), + `cannot find the target related value of name "%s" in with tag "%s" for attribute "%s.%s"`, + relatedTargetName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(), ) } bindToReflectValue := field.Value - switch bindToReflectValue.Kind() { - case reflect.Array, reflect.Slice: - if bindToReflectValue.CanAddr() { - bindToReflectValue = bindToReflectValue.Addr() - } + if bindToReflectValue.Kind() != reflect.Ptr && bindToReflectValue.CanAddr() { + bindToReflectValue = bindToReflectValue.Addr() } // It automatically retrieves struct field names from current attribute struct/slice. @@ -159,7 +156,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error { model = model.Order(parsedTagOutput.Order) } - err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).Scan(bindToReflectValue) + err = model.Fields(fieldKeys).Where(relatedSourceName, relatedTargetValue).Scan(bindToReflectValue) if err != nil { return err } @@ -179,7 +176,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { err error allowedTypeStrArray = make([]string, 0) ) - fieldMap, err := structs.FieldMap(structs.FieldMapInput{ + currentStructFieldMap, err := structs.FieldMap(structs.FieldMapInput{ Pointer: pointer, PriorityTagArray: nil, RecursiveOption: structs.RecursiveOptionEmbeddedNoTag, @@ -189,7 +186,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { } // It checks the with array and automatically calls the ScanList to complete association querying. if !m.withAll { - for _, field := range fieldMap { + for _, field := range currentStructFieldMap { for _, withItem := range m.withArray { withItemReflectValueType, err := structs.StructType(withItem) if err != nil { @@ -207,7 +204,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { } } - for fieldName, field := range fieldMap { + for fieldName, field := range currentStructFieldMap { var ( fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]") parsedTagOutput = m.parseWithTagInFieldStruct(field) @@ -225,24 +222,24 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { array = append(array, parsedTagOutput.With) } var ( - model *Model - fieldKeys []string - relatedFieldName = array[0] - relatedAttrName = array[1] - relatedFieldValue interface{} + model *Model + fieldKeys []string + relatedSourceName = array[0] + relatedTargetName = array[1] + relatedTargetValue interface{} ) // Find the value slice of related attribute from `pointer`. - for attributeName, _ := range fieldMap { - if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) { - relatedFieldValue = ListItemValuesUnique(pointer, attributeName) + for attributeName, _ := range currentStructFieldMap { + if utils.EqualFoldWithoutChars(attributeName, relatedTargetName) { + relatedTargetValue = ListItemValuesUnique(pointer, attributeName) break } } - if relatedFieldValue == nil { + if relatedTargetValue == nil { return gerror.NewCodef( gcode.CodeInvalidParameter, `cannot find the related value for attribute name "%s" of with tag "%s"`, - relatedAttrName, parsedTagOutput.With, + relatedTargetName, parsedTagOutput.With, ) } @@ -267,7 +264,9 @@ func (m *Model) doWithScanStructs(pointer interface{}) error { model = model.Order(parsedTagOutput.Order) } - err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).ScanList(pointer, fieldName, parsedTagOutput.With) + err = model.Fields(fieldKeys). + Where(relatedSourceName, relatedTargetValue). + ScanList(pointer, fieldName, parsedTagOutput.With) if err != nil { return err } diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index d1b98727e..7345221d1 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -1867,3 +1867,127 @@ func Test_Table_Relation_With_MultipleDepends_Embedded(t *testing.T) { t.Assert(tableA[1].TableB.TableC.Id, 300) }) } + +func Test_Table_Relation_WithAll_Embedded_Meta_NameMatchingRule(t *testing.T) { + var ( + tableUser = "user1" + tableUserDetail = "user_detail1" + tableUserScores = "user_scores1" + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +name varchar(45) NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +user_id int(10) unsigned NOT NULL, +address varchar(45) NOT NULL, +PRIMARY KEY (user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE IF NOT EXISTS %s ( +id int(10) unsigned NOT NULL AUTO_INCREMENT, +user_id int(10) unsigned NOT NULL, +score int(10) unsigned NOT NULL, +PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:user_detail1"` + UserID int `json:"user_id"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:user_scores1"` + ID int `json:"id"` + UserID int `json:"user_id"` + Score int `json:"score"` + } + + // For Test Only + type UserEmbedded struct { + ID int `json:"id"` + Name string `json:"name"` + } + + type User struct { + gmeta.Meta `orm:"table:user1"` + UserEmbedded + UserDetail UserDetail `orm:"with:user_id=id"` + UserScores []*UserScores `orm:"with:user_id=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "user_id": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "user_id": i, + "score": j, + }) + gtest.AssertNil(err) + } + } + + db.SetDebug(true) + defer db.SetDebug(false) + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.ID, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.UserID, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].UserID, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].UserID, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.ID, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.UserID, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].UserID, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].UserID, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} diff --git a/os/gcron/gcron.go b/os/gcron/gcron.go index aeb63bc8b..66c5692aa 100644 --- a/os/gcron/gcron.go +++ b/os/gcron/gcron.go @@ -133,11 +133,13 @@ func Entries() []*Entry { } // Start starts running the specified timed task named `name`. -func Start(name string) { - defaultCron.Start(name) +// If no`name` specified, it starts the entire cron. +func Start(name ...string) { + defaultCron.Start(name...) } // Stop stops running the specified timed task named `name`. -func Stop(name string) { - defaultCron.Stop(name) +// If no`name` specified, it stops the entire cron. +func Stop(name ...string) { + defaultCron.Stop(name...) } diff --git a/os/gcron/gcron_cron.go b/os/gcron/gcron_cron.go index cace246d2..4b954810d 100644 --- a/os/gcron/gcron_cron.go +++ b/os/gcron/gcron_cron.go @@ -184,6 +184,7 @@ func (c *Cron) Search(name string) *Entry { } // Start starts running the specified timed task named `name`. +// If no`name` specified, it starts the entire cron. func (c *Cron) Start(name ...string) { if len(name) > 0 { for _, v := range name { @@ -197,6 +198,7 @@ func (c *Cron) Start(name ...string) { } // Stop stops running the specified timed task named `name`. +// If no`name` specified, it stops the entire cron. func (c *Cron) Stop(name ...string) { if len(name) > 0 { for _, v := range name { diff --git a/util/gmeta/gmeta.go b/util/gmeta/gmeta.go index 4969537c2..c522fcd7b 100644 --- a/util/gmeta/gmeta.go +++ b/util/gmeta/gmeta.go @@ -8,7 +8,6 @@ package gmeta import ( - "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/structs" ) @@ -21,34 +20,27 @@ const ( metaAttributeName = "Meta" ) -var ( - // metaDataCacheMap is a cache map for struct type to enhance the performance. - metaDataCacheMap = gmap.NewStrAnyMap(true) -) - -// Data retrieves and returns all meta data from `object`. +// Data retrieves and returns all metadata from `object`. // It automatically parses and caches the tag string from "Mata" attribute as its meta data. func Data(object interface{}) map[string]interface{} { reflectType, err := structs.StructType(object) if err != nil { panic(err) } - return metaDataCacheMap.GetOrSetFuncLock(reflectType.Signature(), func() interface{} { - if field, ok := reflectType.FieldByName(metaAttributeName); ok { - var ( - tags = structs.ParseTag(string(field.Tag)) - data = make(map[string]interface{}, len(tags)) - ) - for k, v := range tags { - data[k] = v - } - return data + if field, ok := reflectType.FieldByName(metaAttributeName); ok { + var ( + tags = structs.ParseTag(string(field.Tag)) + data = make(map[string]interface{}, len(tags)) + ) + for k, v := range tags { + data[k] = v } - return map[string]interface{}{} - }).(map[string]interface{}) + return data + } + return map[string]interface{}{} } -// Get retrieves and returns specified meta data by `key` from `object`. +// Get retrieves and returns specified metadata by `key` from `object`. func Get(object interface{}, key string) *gvar.Var { return gvar.New(Data(object)[key]) } diff --git a/util/guid/guid_string.go b/util/guid/guid_string.go index 2927b9e62..666d0ec8d 100644 --- a/util/guid/guid_string.go +++ b/util/guid/guid_string.go @@ -17,12 +17,15 @@ import ( "time" ) -var ( - sequence gtype.Uint32 // Sequence for unique purpose of current process. +const ( sequenceMax = uint32(46655) // Sequence max("zzz"). randomStrBase = "0123456789abcdefghijklmnopqrstuvwxyz" // Random chars string(36 bytes). - macAddrStr = "0000000" // MAC addresses hash result in 7 bytes. - processIdStr = "0000" // Process id in 4 bytes. +) + +var ( + sequence gtype.Uint32 // Sequence for unique purpose of current process. + macAddrStr = "0000000" // MAC addresses hash result in 7 bytes. + processIdStr = "0000" // Process id in 4 bytes. ) // init initializes several fixed local variable. From b5502c5580d961bdcad12a1ca65f7fb3a3f61acf Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Fri, 27 Aug 2021 20:26:07 +0800 Subject: [PATCH 486/492] revert and use --- database/gdb/gdb_driver_mysql.go | 4 ++-- database/gdb/gdb_z_mysql_internal_test.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index 251c37aed..46ab3a459 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -18,7 +18,7 @@ import ( "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" - _ "github.com/gogf/mysql" + _ "github.com/go-sql-driver/mysql" ) // DriverMysql is the driver for mysql database. @@ -34,7 +34,7 @@ func (d *DriverMysql) New(core *Core, node *ConfigNode) (DB, error) { }, nil } -// Open creates and returns a underlying sql.DB object for mysql. +// Open creates and returns an underlying sql.DB object for mysql. // Note that it converts time.Time argument to local timezone in default. func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) { var source string diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 016aef2cb..50958440a 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -12,7 +12,7 @@ import ( "github.com/gogf/gf/os/gcmd" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" - "github.com/gogf/mysql" + "github.com/go-sql-driver/mysql" "testing" ) diff --git a/go.mod b/go.mod index 264567cea..bbdb48b3a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 github.com/fatih/color v1.12.0 github.com/fsnotify/fsnotify v1.4.9 - github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 + github.com/go-sql-driver/mysql v1.6.0 github.com/gomodule/redigo v1.8.5 github.com/gorilla/websocket v1.4.2 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf diff --git a/go.sum b/go.sum index 45bd2270a..72133957f 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw= -github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8= +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/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc= github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= From 549adf64879ea865e5a5ad86dea4a7894e1a0f1a Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Fri, 27 Aug 2021 20:35:35 +0800 Subject: [PATCH 487/492] improve unit testing case for package gdb --- database/gdb/gdb_driver_mysql.go | 3 +-- database/gdb/gdb_z_mysql_basic_test.go | 17 +++++++++++++++-- database/gdb/gdb_z_mysql_internal_test.go | 14 -------------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index 46ab3a459..7caeefdbc 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -11,12 +11,11 @@ import ( "database/sql" "fmt" "github.com/gogf/gf/errors/gcode" - "net/url" - "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" + "net/url" _ "github.com/go-sql-driver/mysql" ) diff --git a/database/gdb/gdb_z_mysql_basic_test.go b/database/gdb/gdb_z_mysql_basic_test.go index b4a1a0083..610592ac3 100644 --- a/database/gdb/gdb_z_mysql_basic_test.go +++ b/database/gdb/gdb_z_mysql_basic_test.go @@ -7,10 +7,10 @@ package gdb_test import ( - "testing" - + "github.com/go-sql-driver/mysql" "github.com/gogf/gf/database/gdb" "github.com/gogf/gf/test/gtest" + "testing" ) func Test_Instance(t *testing.T) { @@ -27,3 +27,16 @@ func Test_Instance(t *testing.T) { t.Assert(err2, nil) }) } + +// Fix issue: https://github.com/gogf/gf/issues/819 +func Test_Func_ConvertDataForTableRecord(t *testing.T) { + type Test struct { + ResetPasswordTokenAt mysql.NullTime `orm:"reset_password_token_at"` + } + gtest.C(t, func(t *gtest.T) { + m := gdb.ConvertDataForTableRecord(new(Test)) + t.Assert(len(m), 1) + t.AssertNE(m["reset_password_token_at"], nil) + t.Assert(m["reset_password_token_at"], new(mysql.NullTime)) + }) +} diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 50958440a..d823b1253 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -12,7 +12,6 @@ import ( "github.com/gogf/gf/os/gcmd" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" - "github.com/go-sql-driver/mysql" "testing" ) @@ -292,19 +291,6 @@ CREATE TABLE %s ( }) } -// Fix issue: https://github.com/gogf/gf/issues/819 -func Test_Func_ConvertDataForTableRecord(t *testing.T) { - type Test struct { - ResetPasswordTokenAt mysql.NullTime `orm:"reset_password_token_at"` - } - gtest.C(t, func(t *gtest.T) { - m := ConvertDataForTableRecord(new(Test)) - t.Assert(len(m), 1) - t.AssertNE(m["reset_password_token_at"], nil) - t.Assert(m["reset_password_token_at"], new(mysql.NullTime)) - }) -} - func Test_isSubQuery(t *testing.T) { gtest.C(t, func(t *gtest.T) { t.Assert(isSubQuery("user"), false) From adee4ac8dd195d9e7fa78d0cc7586668f7e88f12 Mon Sep 17 00:00:00 2001 From: houseme <qzg40737@163.com> Date: Sun, 29 Aug 2021 12:23:22 +0800 Subject: [PATCH 488/492] fix #1389 --- encoding/gjson/gjson_api_encoding.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/encoding/gjson/gjson_api_encoding.go b/encoding/gjson/gjson_api_encoding.go index a0eabdb90..cea7a8a1b 100644 --- a/encoding/gjson/gjson_api_encoding.go +++ b/encoding/gjson/gjson_api_encoding.go @@ -169,14 +169,16 @@ func (j *Json) MustToTomlString() string { // INI // ======================================================================== +// ToIni json to ini func (j *Json) ToIni() ([]byte, error) { j.mu.RLock() defer j.mu.RUnlock() return gini.Encode((*(j.p)).(map[string]interface{})) } +// ToIniString ini to string func (j *Json) ToIniString() (string, error) { - b, e := j.ToToml() + b, e := j.ToIni() return string(b), e } @@ -188,6 +190,7 @@ func (j *Json) MustToIni() []byte { return result } +// MustToIniString . func (j *Json) MustToIniString() string { return gconv.UnsafeBytesToStr(j.MustToIni()) } From 0048e2c8c492e1d96d96f33fd63d605253526a54 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 30 Aug 2021 14:29:04 +0800 Subject: [PATCH 489/492] improve package gstr --- text/gstr/gstr.go | 261 +++++--------------------------------- text/gstr/gstr_pos.go | 4 +- text/gstr/gstr_replace.go | 89 +++++++++++++ text/gstr/gstr_str.go | 52 ++++++++ text/gstr/gstr_substr.go | 89 +++++++++++++ text/gstr/gstr_trim.go | 12 +- 6 files changed, 271 insertions(+), 236 deletions(-) create mode 100644 text/gstr/gstr_replace.go create mode 100644 text/gstr/gstr_str.go create mode 100644 text/gstr/gstr_substr.go diff --git a/text/gstr/gstr.go b/text/gstr/gstr.go index 903d609df..4a81426d4 100644 --- a/text/gstr/gstr.go +++ b/text/gstr/gstr.go @@ -28,95 +28,22 @@ const ( NotFoundIndex = -1 ) -// Replace returns a copy of the string <origin> -// in which string <search> replaced by <replace> case-sensitively. -func Replace(origin, search, replace string, count ...int) string { - n := -1 - if len(count) > 0 { - n = count[0] - } - return strings.Replace(origin, search, replace, n) -} +const ( + defaultSuffixForStrLimit = "..." +) -// Replace returns a copy of the string <origin> -// in which string <search> replaced by <replace> case-insensitively. -func ReplaceI(origin, search, replace string, count ...int) string { - n := -1 - if len(count) > 0 { - n = count[0] - } - if n == 0 { - return origin - } - var ( - length = len(search) - searchLower = strings.ToLower(search) - ) - for { - originLower := strings.ToLower(origin) - if pos := strings.Index(originLower, searchLower); pos != -1 { - origin = origin[:pos] + replace + origin[pos+length:] - if n--; n == 0 { - break - } - } else { - break - } - } - return origin -} - -// Count counts the number of <substr> appears in <s>. -// It returns 0 if no <substr> found in <s>. +// Count counts the number of `substr` appears in `s`. +// It returns 0 if no `substr` found in `s`. func Count(s, substr string) int { return strings.Count(s, substr) } -// CountI counts the number of <substr> appears in <s>, case-insensitively. -// It returns 0 if no <substr> found in <s>. +// CountI counts the number of `substr` appears in `s`, case-insensitively. +// It returns 0 if no `substr` found in `s`. func CountI(s, substr string) int { return strings.Count(ToLower(s), ToLower(substr)) } -// ReplaceByArray returns a copy of <origin>, -// which is replaced by a slice in order, case-sensitively. -func ReplaceByArray(origin string, array []string) string { - for i := 0; i < len(array); i += 2 { - if i+1 >= len(array) { - break - } - origin = Replace(origin, array[i], array[i+1]) - } - return origin -} - -// ReplaceIByArray returns a copy of <origin>, -// which is replaced by a slice in order, case-insensitively. -func ReplaceIByArray(origin string, array []string) string { - for i := 0; i < len(array); i += 2 { - if i+1 >= len(array) { - break - } - origin = ReplaceI(origin, array[i], array[i+1]) - } - return origin -} - -// ReplaceByMap returns a copy of <origin>, -// which is replaced by a map in unordered way, case-sensitively. -func ReplaceByMap(origin string, replaces map[string]string) string { - return utils.ReplaceByMap(origin, replaces) -} - -// ReplaceIByMap returns a copy of <origin>, -// which is replaced by a map in unordered way, case-insensitively. -func ReplaceIByMap(origin string, replaces map[string]string) string { - for k, v := range replaces { - origin = ReplaceI(origin, k, v) - } - return origin -} - // ToLower returns a copy of the string s with all Unicode letters mapped to their lower case. func ToLower(s string) string { return strings.ToLower(s) @@ -163,86 +90,7 @@ func IsNumeric(s string) bool { return utils.IsNumeric(s) } -// SubStr returns a portion of string <str> specified by the <start> and <length> parameters. -func SubStr(str string, start int, length ...int) (substr string) { - lth := len(str) - - // Simple border checks. - if start < 0 { - start = 0 - } - if start >= lth { - start = lth - } - end := lth - if len(length) > 0 { - end = start + length[0] - if end < start { - end = lth - } - } - if end > lth { - end = lth - } - return str[start:end] -} - -// SubStrRune returns a portion of string <str> specified by the <start> and <length> parameters. -// SubStrRune considers parameter <str> as unicode string. -func SubStrRune(str string, start int, length ...int) (substr string) { - // Converting to []rune to support unicode. - rs := []rune(str) - lth := len(rs) - - // Simple border checks. - if start < 0 { - start = 0 - } - if start >= lth { - start = lth - } - end := lth - if len(length) > 0 { - end = start + length[0] - if end < start { - end = lth - } - } - if end > lth { - end = lth - } - return string(rs[start:end]) -} - -// StrLimit returns a portion of string <str> specified by <length> parameters, if the length -// of <str> is greater than <length>, then the <suffix> will be appended to the result string. -func StrLimit(str string, length int, suffix ...string) string { - if len(str) < length { - return str - } - addStr := "..." - if len(suffix) > 0 { - addStr = suffix[0] - } - return str[0:length] + addStr -} - -// StrLimitRune returns a portion of string <str> specified by <length> parameters, if the length -// of <str> is greater than <length>, then the <suffix> will be appended to the result string. -// StrLimitRune considers parameter <str> as unicode string. -func StrLimitRune(str string, length int, suffix ...string) string { - rs := []rune(str) - if len(rs) < length { - return str - } - addStr := "..." - if len(suffix) > 0 { - addStr = suffix[0] - } - return string(rs[0:length]) + addStr -} - -// Reverse returns a string which is the reverse of <str>. +// Reverse returns a string which is the reverse of `str`. func Reverse(str string) string { runes := []rune(str) for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { @@ -252,9 +100,9 @@ func Reverse(str string) string { } // NumberFormat formats a number with grouped thousands. -// <decimals>: Sets the number of decimal points. -// <decPoint>: Sets the separator for the decimal point. -// <thousandsSep>: Sets the thousands separator. +// `decimals`: Sets the number of decimal points. +// `decPoint`: Sets the separator for the decimal point. +// `thousandsSep`: Sets the thousands' separator. // See http://php.net/manual/en/function.number-format.php. func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) string { neg := false @@ -301,7 +149,7 @@ func NumberFormat(number float64, decimals int, decPoint, thousandsSep string) s // Can be used to split a string into smaller chunks which is useful for // e.g. converting BASE64 string output to match RFC 2045 semantics. // It inserts end every chunkLen characters. -// It considers parameter <body> and <end> as unicode string. +// It considers parameter `body` and `end` as unicode string. func ChunkSplit(body string, chunkLen int, end string) string { if end == "" { end = "\r\n" @@ -329,7 +177,7 @@ func Compare(a, b string) int { return strings.Compare(a, b) } -// Equal reports whether <a> and <b>, interpreted as UTF-8 strings, +// Equal reports whether `a` and `b`, interpreted as UTF-8 strings, // are equal under Unicode case-folding, case-insensitively. func Equal(a, b string) bool { return strings.EqualFold(a, b) @@ -351,7 +199,7 @@ func HasSuffix(s, suffix string) bool { } // CountWords returns information about words' count used in a string. -// It considers parameter <str> as unicode string. +// It considers parameter `str` as unicode string. func CountWords(str string) map[string]int { m := make(map[string]int) buffer := bytes.NewBuffer(nil) @@ -372,7 +220,7 @@ func CountWords(str string) map[string]int { } // CountChars returns information about chars' count used in a string. -// It considers parameter <str> as unicode string. +// It considers parameter `str` as unicode string. func CountChars(str string, noSpace ...bool) map[string]int { m := make(map[string]int) countSpace := true @@ -465,51 +313,8 @@ func Repeat(input string, multiplier int) string { return strings.Repeat(input, multiplier) } -// Str returns part of <haystack> string starting from and including -// the first occurrence of <needle> to the end of <haystack>. -// See http://php.net/manual/en/function.strstr.php. -func Str(haystack string, needle string) string { - if needle == "" { - return "" - } - pos := strings.Index(haystack, needle) - if pos == NotFoundIndex { - return "" - } - return haystack[pos+len([]byte(needle))-1:] -} - -// StrEx returns part of <haystack> string starting from and excluding -// the first occurrence of <needle> to the end of <haystack>. -func StrEx(haystack string, needle string) string { - if s := Str(haystack, needle); s != "" { - return s[1:] - } - return "" -} - -// StrTill returns part of <haystack> string ending to and including -// the first occurrence of <needle> from the start of <haystack>. -func StrTill(haystack string, needle string) string { - pos := strings.Index(haystack, needle) - if pos == NotFoundIndex || pos == 0 { - return "" - } - return haystack[:pos+1] -} - -// StrTillEx returns part of <haystack> string ending to and excluding -// the first occurrence of <needle> from the start of <haystack>. -func StrTillEx(haystack string, needle string) string { - pos := strings.Index(haystack, needle) - if pos == NotFoundIndex || pos == 0 { - return "" - } - return haystack[:pos] -} - // Shuffle randomly shuffles a string. -// It considers parameter <str> as unicode string. +// It considers parameter `str` as unicode string. func Shuffle(str string) string { runes := []rune(str) s := make([]rune, len(runes)) @@ -519,12 +324,12 @@ func Shuffle(str string) string { return string(s) } -// Split splits string <str> by a string <delimiter>, to an array. +// Split splits string `str` by a string `delimiter`, to an array. func Split(str, delimiter string) []string { return strings.Split(str, delimiter) } -// SplitAndTrim splits string <str> by a string <delimiter> to an array, +// SplitAndTrim splits string `str` by a string `delimiter` to an array, // and calls Trim to every element of this array. It ignores the elements // which are empty after Trim. func SplitAndTrim(str, delimiter string, characterMask ...string) []string { @@ -538,7 +343,7 @@ func SplitAndTrim(str, delimiter string, characterMask ...string) []string { return array } -// SplitAndTrimSpace splits string <str> by a string <delimiter> to an array, +// SplitAndTrimSpace splits string `str` by a string `delimiter` to an array, // and calls TrimSpace to every element of this array. // Deprecated, use SplitAndTrim instead. func SplitAndTrimSpace(str, delimiter string) []string { @@ -552,27 +357,27 @@ func SplitAndTrimSpace(str, delimiter string) []string { return array } -// Join concatenates the elements of <array> to create a single string. The separator string -// <sep> is placed between elements in the resulting string. +// Join concatenates the elements of `array` to create a single string. The separator string +// `sep` is placed between elements in the resulting string. func Join(array []string, sep string) string { return strings.Join(array, sep) } -// JoinAny concatenates the elements of <array> to create a single string. The separator string -// <sep> is placed between elements in the resulting string. +// JoinAny concatenates the elements of `array` to create a single string. The separator string +// `sep` is placed between elements in the resulting string. // -// The parameter <array> can be any type of slice, which be converted to string array. +// The parameter `array` can be any type of slice, which be converted to string array. func JoinAny(array interface{}, sep string) string { return strings.Join(gconv.Strings(array), sep) } -// Explode splits string <str> by a string <delimiter>, to an array. +// Explode splits string `str` by a string `delimiter`, to an array. // See http://php.net/manual/en/function.explode.php. func Explode(delimiter, str string) []string { return Split(str, delimiter) } -// Implode joins array elements <pieces> with a string <glue>. +// Implode joins array elements `pieces` with a string `glue`. // http://php.net/manual/en/function.implode.php func Implode(glue string, pieces []string) string { return strings.Join(pieces, glue) @@ -588,8 +393,8 @@ func Ord(char string) int { return int(char[0]) } -// HideStr replaces part of the the string <str> to <hide> by <percentage> from the <middle>. -// It considers parameter <str> as unicode string. +// HideStr replaces part of the string `str` to `hide` by `percentage` from the `middle`. +// It considers parameter `str` as unicode string. func HideStr(str string, percent int, hide string) string { array := strings.Split(str, "@") if len(array) > 1 { @@ -619,7 +424,7 @@ func HideStr(str string, percent int, hide string) string { // Nl2Br inserts HTML line breaks(<br>|<br />) before all newlines in a string: // \n\r, \r\n, \r, \n. -// It considers parameter <str> as unicode string. +// It considers parameter `str` as unicode string. func Nl2Br(str string, isXhtml ...bool) string { r, n, runes := '\r', '\n', []rune(str) var br []byte @@ -705,9 +510,9 @@ func QuoteMeta(str string, chars ...string) string { return buf.String() } -// SearchArray searches string <s> in string slice <a> case-sensitively, -// returns its index in <a>. -// If <s> is not found in <a>, it returns -1. +// SearchArray searches string `s` in string slice `a` case-sensitively, +// returns its index in `a`. +// If `s` is not found in `a`, it returns -1. func SearchArray(a []string, s string) int { for i, v := range a { if s == v { @@ -717,7 +522,7 @@ func SearchArray(a []string, s string) int { return NotFoundIndex } -// InArray checks whether string <s> in slice <a>. +// InArray checks whether string `s` in slice `a`. func InArray(a []string, s string) bool { return SearchArray(a, s) != NotFoundIndex } diff --git a/text/gstr/gstr_pos.go b/text/gstr/gstr_pos.go index 6009cd7cb..e279793e6 100644 --- a/text/gstr/gstr_pos.go +++ b/text/gstr/gstr_pos.go @@ -8,8 +8,8 @@ package gstr import "strings" -// Pos returns the position of the first occurrence of <needle> -// in <haystack> from <startOffset>, case-sensitively. +// Pos returns the position of the first occurrence of `needle` +// in `haystack` from <startOffset>, case-sensitively. // It returns -1, if not found. func Pos(haystack, needle string, startOffset ...int) int { length := len(haystack) diff --git a/text/gstr/gstr_replace.go b/text/gstr/gstr_replace.go new file mode 100644 index 000000000..98ffa1957 --- /dev/null +++ b/text/gstr/gstr_replace.go @@ -0,0 +1,89 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gstr + +import ( + "github.com/gogf/gf/internal/utils" + "strings" +) + +// Replace returns a copy of the string `origin` +// in which string `search` replaced by `replace` case-sensitively. +func Replace(origin, search, replace string, count ...int) string { + n := -1 + if len(count) > 0 { + n = count[0] + } + return strings.Replace(origin, search, replace, n) +} + +// ReplaceI returns a copy of the string `origin` +// in which string `search` replaced by `replace` case-insensitively. +func ReplaceI(origin, search, replace string, count ...int) string { + n := -1 + if len(count) > 0 { + n = count[0] + } + if n == 0 { + return origin + } + var ( + length = len(search) + searchLower = strings.ToLower(search) + ) + for { + originLower := strings.ToLower(origin) + if pos := strings.Index(originLower, searchLower); pos != -1 { + origin = origin[:pos] + replace + origin[pos+length:] + if n--; n == 0 { + break + } + } else { + break + } + } + return origin +} + +// ReplaceByArray returns a copy of `origin`, +// which is replaced by a slice in order, case-sensitively. +func ReplaceByArray(origin string, array []string) string { + for i := 0; i < len(array); i += 2 { + if i+1 >= len(array) { + break + } + origin = Replace(origin, array[i], array[i+1]) + } + return origin +} + +// ReplaceIByArray returns a copy of `origin`, +// which is replaced by a slice in order, case-insensitively. +func ReplaceIByArray(origin string, array []string) string { + for i := 0; i < len(array); i += 2 { + if i+1 >= len(array) { + break + } + origin = ReplaceI(origin, array[i], array[i+1]) + } + return origin +} + +// ReplaceByMap returns a copy of `origin`, +// which is replaced by a map in unordered way, case-sensitively. +func ReplaceByMap(origin string, replaces map[string]string) string { + return utils.ReplaceByMap(origin, replaces) +} + +// ReplaceIByMap returns a copy of `origin`, +// which is replaced by a map in unordered way, case-insensitively. +func ReplaceIByMap(origin string, replaces map[string]string) string { + for k, v := range replaces { + origin = ReplaceI(origin, k, v) + } + return origin +} diff --git a/text/gstr/gstr_str.go b/text/gstr/gstr_str.go new file mode 100644 index 000000000..8b316efd1 --- /dev/null +++ b/text/gstr/gstr_str.go @@ -0,0 +1,52 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gstr + +import "strings" + +// Str returns part of `haystack` string starting from and including +// the first occurrence of `needle` to the end of `haystack`. +// See http://php.net/manual/en/function.strstr.php. +func Str(haystack string, needle string) string { + if needle == "" { + return "" + } + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex { + return "" + } + return haystack[pos+len([]byte(needle))-1:] +} + +// StrEx returns part of `haystack` string starting from and excluding +// the first occurrence of `needle` to the end of `haystack`. +func StrEx(haystack string, needle string) string { + if s := Str(haystack, needle); s != "" { + return s[1:] + } + return "" +} + +// StrTill returns part of `haystack` string ending to and including +// the first occurrence of `needle` from the start of `haystack`. +func StrTill(haystack string, needle string) string { + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex || pos == 0 { + return "" + } + return haystack[:pos+1] +} + +// StrTillEx returns part of `haystack` string ending to and excluding +// the first occurrence of `needle` from the start of `haystack`. +func StrTillEx(haystack string, needle string) string { + pos := strings.Index(haystack, needle) + if pos == NotFoundIndex || pos == 0 { + return "" + } + return haystack[:pos] +} diff --git a/text/gstr/gstr_substr.go b/text/gstr/gstr_substr.go new file mode 100644 index 000000000..d4d422c16 --- /dev/null +++ b/text/gstr/gstr_substr.go @@ -0,0 +1,89 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gstr + +// SubStr returns a portion of string `str` specified by the `start` and `length` parameters. +// The parameter `length` is optional, it uses the length of `str` in default. +func SubStr(str string, start int, length ...int) (substr string) { + strLength := len(str) + // Simple border checks. + if start < 0 { + start = 0 + } + if start >= strLength { + start = strLength + } + end := strLength + if len(length) > 0 { + end = start + length[0] + if end < start { + end = strLength + } + } + if end > strLength { + end = strLength + } + return str[start:end] +} + +// SubStrRune returns a portion of string `str` specified by the `start` and `length` parameters. +// SubStrRune considers parameter `str` as unicode string. +// The parameter `length` is optional, it uses the length of `str` in default. +func SubStrRune(str string, start int, length ...int) (substr string) { + // Converting to []rune to support unicode. + var ( + runes = []rune(str) + runesLength = len(runes) + ) + + // Simple border checks. + if start < 0 { + start = 0 + } + if start >= runesLength { + start = runesLength + } + end := runesLength + if len(length) > 0 { + end = start + length[0] + if end < start { + end = runesLength + } + } + if end > runesLength { + end = runesLength + } + return string(runes[start:end]) +} + +// StrLimit returns a portion of string `str` specified by `length` parameters, if the length +// of `str` is greater than `length`, then the `suffix` will be appended to the result string. +func StrLimit(str string, length int, suffix ...string) string { + if len(str) < length { + return str + } + suffixStr := defaultSuffixForStrLimit + if len(suffix) > 0 { + suffixStr = suffix[0] + } + return str[0:length] + suffixStr +} + +// StrLimitRune returns a portion of string `str` specified by `length` parameters, if the length +// of `str` is greater than `length`, then the `suffix` will be appended to the result string. +// StrLimitRune considers parameter `str` as unicode string. +func StrLimitRune(str string, length int, suffix ...string) string { + runes := []rune(str) + if len(runes) < length { + return str + } + suffixStr := defaultSuffixForStrLimit + if len(suffix) > 0 { + suffixStr = suffix[0] + } + return string(runes[0:length]) + suffixStr +} diff --git a/text/gstr/gstr_trim.go b/text/gstr/gstr_trim.go index e7883e986..a566bf935 100644 --- a/text/gstr/gstr_trim.go +++ b/text/gstr/gstr_trim.go @@ -17,8 +17,8 @@ func Trim(str string, characterMask ...string) string { return utils.Trim(str, characterMask...) } -// TrimStr strips all of the given <cut> string from the beginning and end of a string. -// Note that it does not strips the whitespaces of its beginning or end. +// TrimStr strips all the given <cut> string from the beginning and end of a string. +// Note that it does not strip the whitespaces of its beginning or end. func TrimStr(str string, cut string, count ...int) string { return TrimLeftStr(TrimRightStr(str, cut, count...), cut, count...) } @@ -32,8 +32,8 @@ func TrimLeft(str string, characterMask ...string) string { return strings.TrimLeft(str, trimChars) } -// TrimLeftStr strips all of the given <cut> string from the beginning of a string. -// Note that it does not strips the whitespaces of its beginning. +// TrimLeftStr strips all the given <cut> string from the beginning of a string. +// Note that it does not strip the whitespaces of its beginning. func TrimLeftStr(str string, cut string, count ...int) string { var ( lenCut = len(cut) @@ -58,8 +58,8 @@ func TrimRight(str string, characterMask ...string) string { return strings.TrimRight(str, trimChars) } -// TrimRightStr strips all of the given <cut> string from the end of a string. -// Note that it does not strips the whitespaces of its end. +// TrimRightStr strips all the given <cut> string from the end of a string. +// Note that it does not strip the whitespaces of its end. func TrimRightStr(str string, cut string, count ...int) string { var ( lenStr = len(str) From 73d2b7ed06fb59ebfa815544badbb9523b74d70a Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Mon, 30 Aug 2021 22:26:02 +0800 Subject: [PATCH 490/492] fix issue #1387 --- database/gdb/gdb_func.go | 7 +++- database/gdb/gdb_model_insert.go | 3 ++ database/gdb/gdb_z_mysql_model_test.go | 41 +++++++++++++++++++ os/gtime/gtime.go | 5 ++- os/gtime/gtime_time.go | 7 ++++ ...nv_z_unit_struct_marshal_unmarshal_test.go | 3 +- util/gconv/gconv_z_unit_struct_test.go | 16 ++++++++ 7 files changed, 79 insertions(+), 3 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 37ae7a8c3..87c6e8763 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -209,7 +209,12 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} { data[k] = nil } - case *time.Time, *gtime.Time: + case *gtime.Time: + if r.IsZero() { + data[k] = nil + } + + case *time.Time: continue case Counter, *Counter: diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 5c40a7eba..78099b4e6 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -83,8 +83,10 @@ func (m *Model) Data(data ...interface{}) *Model { list[i] = ConvertDataForTableRecord(rv.Index(i).Interface()) } model.data = list + case reflect.Map: model.data = ConvertDataForTableRecord(data[0]) + case reflect.Struct: if v, ok := data[0].(apiInterfaces); ok { var ( @@ -98,6 +100,7 @@ func (m *Model) Data(data ...interface{}) *Model { } else { model.data = ConvertDataForTableRecord(data[0]) } + default: model.data = data[0] } diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index ee079006a..f9f95fcef 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -3871,3 +3871,44 @@ func Test_Model_FieldAvg(t *testing.T) { t.Assert(all[0]["total"], 1) }) } + +// https://github.com/gogf/gf/issues/1387 +func Test_Model_GTime_DefaultValue(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + type User struct { + Id int + Passport string + Password string + Nickname string + CreateTime *gtime.Time + } + data := User{ + Id: 1, + Passport: "user_1", + Password: "pass_1", + Nickname: "name_1", + } + // Insert + _, err := db.Model(table).Data(data).Insert() + t.AssertNil(err) + + // Select + var ( + user *User + ) + err = db.Model(table).Scan(&user) + t.AssertNil(err) + t.Assert(user.Passport, data.Passport) + t.Assert(user.Password, data.Password) + t.Assert(user.CreateTime, data.CreateTime) + t.Assert(user.Nickname, data.Nickname) + + // Insert + user.Id = 2 + _, err = db.Model(table).Data(user).Insert() + t.AssertNil(err) + }) +} diff --git a/os/gtime/gtime.go b/os/gtime/gtime.go index cb164da44..318704c83 100644 --- a/os/gtime/gtime.go +++ b/os/gtime/gtime.go @@ -197,7 +197,7 @@ func ISO8601() string { return time.Now().Format("2006-01-02T15:04:05-07:00") } -// ISO8601 returns current datetime in RFC822 format like "Mon, 02 Jan 06 15:04 MST". +// RFC822 returns current datetime in RFC822 format like "Mon, 02 Jan 06 15:04 MST". func RFC822() string { return time.Now().Format("Mon, 02 Jan 06 15:04 MST") } @@ -238,6 +238,9 @@ func parseDateStr(s string) (year, month, day int) { // If <format> is not given, it converts string as a "standard" datetime string. // Note that, it fails and returns error if there's no date string in <str>. func StrToTime(str string, format ...string) (*Time, error) { + if str == "" { + return &Time{wrapper{time.Time{}}}, nil + } if len(format) > 0 { return StrToTimeFormat(str, format[0]) } diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 185c9dbd9..33e0de5e4 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -33,10 +33,13 @@ func New(param ...interface{}) *Time { return NewFromTime(r) case *time.Time: return NewFromTime(*r) + case Time: return &r + case *Time: return r + case string: if len(param) > 1 { switch t := param[1].(type) { @@ -47,6 +50,7 @@ func New(param ...interface{}) *Time { } } return NewFromStr(r) + case []byte: if len(param) > 1 { switch t := param[1].(type) { @@ -57,10 +61,13 @@ func New(param ...interface{}) *Time { } } return NewFromStr(string(r)) + case int: return NewFromTimeStamp(int64(r)) + case int64: return NewFromTimeStamp(r) + default: if v, ok := r.(apiUnixNano); ok { return NewFromTimeStamp(v.UnixNano()) diff --git a/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go b/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go index b7c0941f5..118022f82 100644 --- a/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go +++ b/util/gconv/gconv_z_unit_struct_marshal_unmarshal_test.go @@ -46,7 +46,8 @@ func Test_Struct_UnmarshalValue1(t *testing.T) { gtest.C(t, func(t *gtest.T) { st := &MyTimeSt{} err := gconv.Struct(g.Map{"ServiceDate": nil}, st) - t.AssertNE(err, nil) + t.AssertNil(err) + t.Assert(st.ServiceDate.Time.IsZero(), true) }) gtest.C(t, func(t *gtest.T) { st := &MyTimeSt{} diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index d1d12d46d..9c7ff0713 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -639,6 +639,22 @@ func Test_Struct_Time(t *testing.T) { }) } +func Test_Struct_GTime(t *testing.T) { + // https://github.com/gogf/gf/issues/1387 + gtest.C(t, func(t *gtest.T) { + type User struct { + Name string + CreateTime *gtime.Time + } + var user *User + err := gconv.Struct(`{"Name":"John","CreateTime":""}`, &user) + t.AssertNil(err) + t.AssertNE(user, nil) + t.Assert(user.Name, `John`) + t.Assert(user.CreateTime, nil) + }) +} + // Auto create struct when given pointer. func Test_Struct_Create(t *testing.T) { gtest.C(t, func(t *gtest.T) { From 425ca8aa3e35b940d3ef10e8513f60261fcc0b03 Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 1 Sep 2021 15:02:58 +0800 Subject: [PATCH 491/492] improve process comminucation folder creating for package gproc --- os/gfile/gfile_cache.go | 2 +- os/gfsnotify/gfsnotify_watcher_loop.go | 2 +- os/gproc/gproc_comm.go | 44 +++++++++++++++++++------- os/gproc/gproc_comm_receive.go | 8 +++-- os/gproc/gproc_comm_send.go | 2 +- version.go | 2 +- 6 files changed, 41 insertions(+), 19 deletions(-) diff --git a/os/gfile/gfile_cache.go b/os/gfile/gfile_cache.go index e1354f19f..c4c28d490 100644 --- a/os/gfile/gfile_cache.go +++ b/os/gfile/gfile_cache.go @@ -33,7 +33,7 @@ func GetContentsWithCache(path string, duration ...time.Duration) string { return string(GetBytesWithCache(path, duration...)) } -// GetBinContents returns []byte content of given file by <path> from cache. +// GetBytesWithCache returns []byte content of given file by <path> from cache. // If there's no content in the cache, it will read it from disk file specified by <path>. // The parameter <expire> specifies the caching time for this file content in seconds. func GetBytesWithCache(path string, duration ...time.Duration) []byte { diff --git a/os/gfsnotify/gfsnotify_watcher_loop.go b/os/gfsnotify/gfsnotify_watcher_loop.go index 6eb1e1a7d..3c47a7f0a 100644 --- a/os/gfsnotify/gfsnotify_watcher_loop.go +++ b/os/gfsnotify/gfsnotify_watcher_loop.go @@ -12,7 +12,7 @@ import ( "github.com/gogf/gf/internal/intlog" ) -// watchLoop starts the loop for event listening fro underlying inotify monitor. +// watchLoop starts the loop for event listening from underlying inotify monitor. func (w *Watcher) watchLoop() { go func() { for { diff --git a/os/gproc/gproc_comm.go b/os/gproc/gproc_comm.go index 51a40f2bd..4ab499ca6 100644 --- a/os/gproc/gproc_comm.go +++ b/os/gproc/gproc_comm.go @@ -7,10 +7,12 @@ package gproc import ( + "context" "fmt" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/errors/gcode" "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/net/gtcp" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/util/gconv" @@ -32,9 +34,10 @@ type MsgResponse struct { } const ( - defaultGroupNameFoProcComm = "" // Default group name. - defaultTcpPortForProcComm = 10000 // Starting port number for receiver listening. - maxLengthForProcMsgQueue = 10000 // Max size for each message queue of the group. + defaultFolderNameForProcComm = "gf_pid_port_mapping" // Default folder name for storing pid to port mapping files. + defaultGroupNameForProcComm = "" // Default group name. + defaultTcpPortForProcComm = 10000 // Starting port number for receiver listening. + maxLengthForProcMsgQueue = 10000 // Max size for each message queue of the group. ) var ( @@ -43,17 +46,36 @@ var ( commReceiveQueues = gmap.NewStrAnyMap(true) // commPidFolderPath specifies the folder path storing pid to port mapping files. - commPidFolderPath = gfile.TempDir("gproc") + commPidFolderPath string ) func init() { - // Automatically create the storage folder. - if !gfile.Exists(commPidFolderPath) { - err := gfile.Mkdir(commPidFolderPath) - if err != nil { - panic(fmt.Errorf(`create gproc folder failed: %v`, err)) + availablePaths := []string{ + "/var/tmp", + "/var/run", + } + if homePath, _ := gfile.Home(); homePath != "" { + availablePaths = append(availablePaths, gfile.Join(homePath, ".config")) + } + availablePaths = append(availablePaths, gfile.TempDir()) + for _, availablePath := range availablePaths { + checkPath := gfile.Join(availablePath, defaultFolderNameForProcComm) + if !gfile.Exists(checkPath) && gfile.Mkdir(checkPath) != nil { + continue + } + if gfile.IsWritable(checkPath) { + commPidFolderPath = checkPath + break } } + if commPidFolderPath == "" { + intlog.Errorf( + context.TODO(), + `cannot find available folder for storing pid to port mapping files in paths: %+v, process communication feature will fail`, + availablePaths, + ) + } + } // getConnByPid creates and returns a TCP connection for specified pid. @@ -72,9 +94,7 @@ func getConnByPid(pid int) (*gtcp.PoolConn, error) { // getPortByPid returns the listening port for specified pid. // It returns 0 if no port found for the specified pid. func getPortByPid(pid int) int { - path := getCommFilePath(pid) - content := gfile.GetContentsWithCache(path) - return gconv.Int(content) + return gconv.Int(gfile.GetContentsWithCache(getCommFilePath(pid))) } // getCommFilePath returns the pid to port mapping file path for given pid. diff --git a/os/gproc/gproc_comm_receive.go b/os/gproc/gproc_comm_receive.go index df4f52412..f358db822 100644 --- a/os/gproc/gproc_comm_receive.go +++ b/os/gproc/gproc_comm_receive.go @@ -35,7 +35,7 @@ func Receive(group ...string) *MsgRequest { if len(group) > 0 { groupName = group[0] } else { - groupName = defaultGroupNameFoProcComm + groupName = defaultGroupNameForProcComm } queue := commReceiveQueues.GetOrSetFuncLock(groupName, func() interface{} { return gqueue.New(maxLengthForProcMsgQueue) @@ -79,8 +79,10 @@ func receiveTcpListening() { // receiveTcpHandler is the connection handler for receiving data. func receiveTcpHandler(conn *gtcp.Conn) { - var result []byte - var response MsgResponse + var ( + result []byte + response MsgResponse + ) for { response.Code = 0 response.Message = "" diff --git a/os/gproc/gproc_comm_send.go b/os/gproc/gproc_comm_send.go index 5a06cef62..9e05d5437 100644 --- a/os/gproc/gproc_comm_send.go +++ b/os/gproc/gproc_comm_send.go @@ -18,7 +18,7 @@ func Send(pid int, data []byte, group ...string) error { msg := MsgRequest{ SendPid: Pid(), RecvPid: pid, - Group: defaultGroupNameFoProcComm, + Group: defaultGroupNameForProcComm, Data: data, } if len(group) > 0 { diff --git a/version.go b/version.go index f5adfbbf9..e2d7525e5 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.17.0" +const VERSION = "v1.16.6" const AUTHORS = "john<john@goframe.org>" From cc60bc9dabe82a9f4c02b5c5730f39adeb2c1f2d Mon Sep 17 00:00:00 2001 From: John Guo <john@johng.cn> Date: Wed, 1 Sep 2021 16:14:17 +0800 Subject: [PATCH 492/492] fix issue printing to stdout using fmt.Fprintf for package glog --- os/glog/glog_logger.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 28342804b..b52b860b1 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -277,8 +277,9 @@ func (l *Logger) printToStdout(ctx context.Context, input *HandlerInput) *bytes. ) // This will lose color in Windows os system. // if _, err := os.Stdout.Write(input.getRealBuffer(true).Bytes()); err != nil { + // This will print color in Windows os system. - if _, err := fmt.Fprintf(color.Output, buffer.String()); err != nil { + if _, err := fmt.Fprint(color.Output, buffer.String()); err != nil { intlog.Error(ctx, err) } return buffer