Compare commits

...

40 Commits

Author SHA1 Message Date
1ede8c77ba version updates 2022-06-24 15:21:36 +08:00
48f95d0009 fix issue in Response.WriteJson/Xml functions 2022-06-24 15:21:10 +08:00
0bb57b8989 go.sum update 2022-06-24 15:16:46 +08:00
0503c17867 feat/gfcli: replace gofmt&goimports with tools/imports (#1935) 2022-06-24 14:15:20 +08:00
19779cd342 Fix multiple gdb-group overrides (#1890)
Co-authored-by: longl <longlei@dealmap.cloud>
2022-06-23 21:30:29 +08:00
141f3512a9 fix glog bug (#1844) 2022-06-23 21:05:12 +08:00
3b9e5c71bf version updates 2022-06-21 21:52:54 +08:00
2bcee014f7 add tracing feature for package gproc (#1923) 2022-06-21 21:46:12 +08:00
f0568b4e22 Merge branch 'develop' 2022-06-21 19:08:36 +08:00
8670f29c4e update project templates and improve command init for cli 2022-06-21 18:08:11 +08:00
33a528af76 fix issue #1913 2022-06-20 22:03:19 +08:00
52056644d4 add context parameter (#1919) 2022-06-20 20:34:59 +08:00
3ae23df4b3 Improve the code coverage of the gmap module (#1910) 2022-06-20 19:05:47 +08:00
1b327b8abd improve database configuration parsing for package gins/gdb 2022-06-20 12:07:51 +08:00
bb5cd3e224 fix database configuration parsing for package gins 2022-06-20 11:14:13 +08:00
1e8237446e add tracing logging content if trace id is available in context for package gcmd 2022-06-17 17:42:49 +08:00
b2b2044786 add tracing logging content if trace id is available in context for package gcmd 2022-06-17 17:41:10 +08:00
64c5222623 add environment support of opentelemetry propagation for package gctx 2022-06-17 16:47:01 +08:00
1597291ac3 improve configuration parsing for package gdb 2022-06-17 15:35:55 +08:00
c2e742335b add otel support for package gcmd 2022-06-17 15:16:35 +08:00
cf5884bc60 add SetBodyContent for gclient.Response 2022-06-17 11:31:32 +08:00
cbf5ee9649 go fmt -w 2022-06-16 18:02:51 +08:00
8ac177a6de improve UT for package gredis 2022-06-16 17:41:15 +08:00
cdd4473df5 improve command gen service; add error trace for error of some packages 2022-06-16 17:07:25 +08:00
aaebaa7250 improve DoRequestObj for gclient.Client 2022-06-16 10:51:12 +08:00
7443a8baa1 Merge branch 'develop' 2022-06-15 21:43:04 +08:00
c6ff95a3f4 README update for contrib/registry etcd/polaris 2022-06-15 21:42:12 +08:00
7957016ae2 Polaris Server Offical image support (#1911) 2022-06-15 19:38:22 +08:00
17ab0e2ced remove returning error of Write* functions for ghttp.Server; add UT cases for gclient.Client.DoRequestObj 2022-06-15 19:36:53 +08:00
b0650f3402 Improve the code coverage of the garray,glist module (#1908) 2022-06-15 19:35:21 +08:00
f4f73f2765 Merge branch 'master' of https://github.com/gogf/gf into develop 2022-06-15 16:28:20 +08:00
babc69e13d add DoRequestObj function for gclient.Client 2022-06-15 16:28:17 +08:00
481c50f233 add example for package gerror 2022-06-15 14:46:31 +08:00
b62b2f3598 Improve the code coverage of the gudp module (#1907) 2022-06-13 22:01:20 +08:00
4f37abac6a upgrade ClickHouse-Go dependencies to V2.0.15 (#1904) 2022-06-13 19:40:47 +08:00
31a23e724d Improve the code coverage of the gclient module (#1899) 2022-06-13 19:31:42 +08:00
7d5ab1f8db Merge branch 'master' of https://github.com/gogf/gf 2022-06-06 21:36:44 +08:00
0d8952dcde improve WithTraceID function for package gtrace 2022-06-06 21:36:30 +08:00
bd7ec5d0b0 Feature/sqlite ut (#1882) 2022-06-06 19:57:53 +08:00
MZ
9e6e8001ca fix gf gen service ignore watch file dir windows platform bug (#1889) 2022-06-06 19:56:44 +08:00
117 changed files with 8812 additions and 534 deletions

View File

@ -92,7 +92,7 @@ jobs:
- 9001:9001
polaris:
image: houseme/polaris-server-with-config:latest
image: polarismesh/polaris-server-standalone:latest
ports:
- 8090:8090
- 8091:8091

2
.gitignore vendored
View File

@ -15,3 +15,5 @@ cbuild
**/.DS_Store
.test/
cmd/gf/main
cmd/gf/gf
go.work

View File

@ -3,11 +3,12 @@ module github.com/gogf/gf/cmd/gf/v2
go 1.15
require (
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.0.6
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.0.6
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.0.6
github.com/gogf/gf/v2 v2.0.6
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.1.0
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.1.0
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.1.0
github.com/gogf/gf/v2 v2.1.0
github.com/olekukonko/tablewriter v0.0.5
golang.org/x/tools v0.1.11
)
replace (

View File

@ -91,6 +91,7 @@ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMT
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0=
@ -100,17 +101,22 @@ go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -132,8 +138,10 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -141,12 +149,15 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -206,11 +206,14 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
}
packCmd := fmt.Sprintf(`gf pack %s %s`, in.PackSrc, in.PackDst)
mlog.Print(packCmd)
gproc.MustShellRun(packCmd)
gproc.MustShellRun(ctx, packCmd)
}
// Injected information by building flags.
ldFlags := fmt.Sprintf(`-X 'github.com/gogf/gf/v2/os/gbuild.builtInVarStr=%v'`, c.getBuildInVarStr(in))
ldFlags := fmt.Sprintf(
`-X 'github.com/gogf/gf/v2/os/gbuild.builtInVarStr=%v'`,
c.getBuildInVarStr(ctx, in),
)
// start building
mlog.Print("start building...")
@ -261,7 +264,7 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
// It's not necessary printing the complete command string.
cmdShow, _ := gregex.ReplaceString(`\s+(-ldflags ".+?")\s+`, " ", cmd)
mlog.Print(cmdShow)
if result, err := gproc.ShellExec(cmd); err != nil {
if result, err := gproc.ShellExec(ctx, cmd); err != nil {
mlog.Printf(
"failed to build, os:%s, arch:%s, error:\n%s\n\n%s\n",
system, arch, gstr.Trim(result),
@ -287,12 +290,12 @@ buildDone:
// getBuildInVarMapJson retrieves and returns the custom build-in variables in configuration
// file as json.
func (c cBuild) getBuildInVarStr(in cBuildInput) string {
func (c cBuild) getBuildInVarStr(ctx context.Context, in cBuildInput) string {
buildInVarMap := in.VarMap
if buildInVarMap == nil {
buildInVarMap = make(g.Map)
}
buildInVarMap["builtGit"] = c.getGitCommit()
buildInVarMap["builtGit"] = c.getGitCommit(ctx)
buildInVarMap["builtTime"] = gtime.Now().String()
b, err := json.Marshal(buildInVarMap)
if err != nil {
@ -302,13 +305,13 @@ func (c cBuild) getBuildInVarStr(in cBuildInput) string {
}
// getGitCommit retrieves and returns the latest git commit hash string if present.
func (c cBuild) getGitCommit() string {
func (c cBuild) getGitCommit(ctx context.Context) string {
if gproc.SearchBinary("git") == "" {
return ""
}
var (
cmd = `git log -1 --format="%cd %H" --date=format:"%Y-%m-%d %H:%M:%S"`
s, _ = gproc.ShellExec(cmd)
s, _ = gproc.ShellExec(ctx, cmd)
)
mlog.Debug(cmd)
if s != "" {

View File

@ -88,14 +88,14 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
// Binary build.
in.Build += " --exit"
if in.Main != "" {
if err = gproc.ShellRun(fmt.Sprintf(`gf build %s %s`, in.Main, in.Build)); err != nil {
if err = gproc.ShellRun(ctx, fmt.Sprintf(`gf build %s %s`, in.Main, in.Build)); err != nil {
return
}
}
// Shell executing.
if in.Shell != "" && gfile.Exists(in.Shell) {
if err = c.exeDockerShell(in.Shell); err != nil {
if err = c.exeDockerShell(ctx, in.Shell); err != nil {
return
}
}
@ -116,7 +116,7 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
}
for i, dockerTag := range dockerTags {
if i > 0 {
err = gproc.ShellRun(fmt.Sprintf(`docker tag %s %s`, dockerTagBase, dockerTag))
err = gproc.ShellRun(ctx, fmt.Sprintf(`docker tag %s %s`, dockerTagBase, dockerTag))
if err != nil {
return
}
@ -130,7 +130,7 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
if in.Extra != "" {
dockerBuildOptions = fmt.Sprintf(`%s %s`, dockerBuildOptions, in.Extra)
}
err = gproc.ShellRun(fmt.Sprintf(`docker build -f %s . %s`, in.File, dockerBuildOptions))
err = gproc.ShellRun(ctx, fmt.Sprintf(`docker build -f %s . %s`, in.File, dockerBuildOptions))
if err != nil {
return
}
@ -144,7 +144,7 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
if dockerTag == "" {
continue
}
err = gproc.ShellRun(fmt.Sprintf(`docker push %s`, dockerTag))
err = gproc.ShellRun(ctx, fmt.Sprintf(`docker push %s`, dockerTag))
if err != nil {
return
}
@ -152,10 +152,10 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
return
}
func (c cDocker) exeDockerShell(shellFilePath string) error {
func (c cDocker) exeDockerShell(ctx context.Context, shellFilePath string) error {
if gfile.ExtName(shellFilePath) == "sh" && runtime.GOOS == "windows" {
mlog.Debugf(`ignore shell file "%s", as it cannot be run on windows system`, shellFilePath)
return nil
}
return gproc.ShellRun(gfile.GetContents(shellFilePath))
return gproc.ShellRun(ctx, gfile.GetContents(shellFilePath))
}

View File

@ -26,7 +26,7 @@ type cEnvInput struct {
type cEnvOutput struct{}
func (c cEnv) Index(ctx context.Context, in cEnvInput) (out *cEnvOutput, err error) {
result, err := gproc.ShellExec("go env")
result, err := gproc.ShellExec(ctx, "go env")
if err != nil {
mlog.Fatal(err)
}

View File

@ -11,6 +11,10 @@ var (
type cGen struct {
g.Meta `name:"gen" brief:"{cGenBrief}" dc:"{cGenDc}"`
cGenDao
cGenPb
cGenPbEntity
cGenService
}
const (

View File

@ -138,6 +138,7 @@ func init() {
}
type (
cGenDao struct{}
cGenDaoInput struct {
g.Meta `name:"dao" config:"{cGenDaoConfig}" usage:"{cGenDaoUsage}" brief:"{cGenDaoBrief}" eg:"{cGenDaoEg}" ad:"{cGenDaoAd}"`
Path string `name:"path" short:"p" brief:"{cGenDaoBriefPath}" d:"internal"`
@ -170,7 +171,7 @@ type (
}
)
func (c cGen) Dao(ctx context.Context, in cGenDaoInput) (out *cGenDaoOutput, err error) {
func (c cGenDao) Dao(ctx context.Context, in cGenDaoInput) (out *cGenDaoOutput, err error) {
if g.Cfg().Available(ctx) {
v := g.Cfg().MustGet(ctx, cGenDaoConfig)
if v.IsSlice() {

View File

@ -13,13 +13,14 @@ import (
)
type (
cGenPb struct{}
cGenPbInput struct {
g.Meta `name:"pb" brief:"parse proto files and generate protobuf go files"`
}
cGenPbOutput struct{}
)
func (c cGen) Pb(ctx context.Context, in cGenPbInput) (out *cGenPbOutput, err error) {
func (c cGenPb) Pb(ctx context.Context, in cGenPbInput) (out *cGenPbOutput, err error) {
// Necessary check.
if gproc.SearchBinary("protoc") == "" {
mlog.Fatalf(`command "protoc" not found in your environment, please install protoc first to proceed this command`)
@ -56,7 +57,7 @@ func (c cGen) Pb(ctx context.Context, in cGenPbInput) (out *cGenPbOutput, err er
parsingCommand += " -I" + goPathSrc
}
mlog.Print(parsingCommand)
if output, err := gproc.ShellExec(parsingCommand); err != nil {
if output, err := gproc.ShellExec(ctx, parsingCommand); err != nil {
mlog.Print(output)
mlog.Fatal(err)
}

View File

@ -19,6 +19,29 @@ import (
"github.com/olekukonko/tablewriter"
)
type (
cGenPbEntity struct{}
cGenPbEntityInput struct {
g.Meta `name:"pbentity" config:"{cGenPbEntityConfig}" brief:"{cGenPbEntityBrief}" eg:"{cGenPbEntityEg}" ad:"{cGenPbEntityAd}"`
Path string `name:"path" short:"p" brief:"{cGenPbEntityBriefPath}"`
Package string `name:"package" short:"k" brief:"{cGenPbEntityBriefPackage}"`
Link string `name:"link" short:"l" brief:"{cGenPbEntityBriefLink}"`
Tables string `name:"tables" short:"t" brief:"{cGenPbEntityBriefTables}"`
Prefix string `name:"prefix" short:"f" brief:"{cGenPbEntityBriefPrefix}"`
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenPbEntityBriefRemovePrefix}"`
NameCase string `name:"nameCase" short:"n" brief:"{cGenPbEntityBriefNameCase}" d:"Camel"`
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenPbEntityBriefJsonCase}" d:"CamelLower"`
Option string `name:"option" short:"o" brief:"{cGenPbEntityBriefOption}"`
}
cGenPbEntityOutput struct{}
cGenPbEntityInternalInput struct {
cGenPbEntityInput
TableName string // TableName specifies the table name of the table.
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
}
)
const (
cGenPbEntityConfig = `gfcli.gen.pbentity`
cGenPbEntityBrief = `generate entity message files in protobuf3 format`
@ -83,28 +106,6 @@ set it to "none" to ignore json tag generating.
`
)
type (
cGenPbEntityInput struct {
g.Meta `name:"pbentity" config:"{cGenPbEntityConfig}" brief:"{cGenPbEntityBrief}" eg:"{cGenPbEntityEg}" ad:"{cGenPbEntityAd}"`
Path string `name:"path" short:"p" brief:"{cGenPbEntityBriefPath}"`
Package string `name:"package" short:"k" brief:"{cGenPbEntityBriefPackage}"`
Link string `name:"link" short:"l" brief:"{cGenPbEntityBriefLink}"`
Tables string `name:"tables" short:"t" brief:"{cGenPbEntityBriefTables}"`
Prefix string `name:"prefix" short:"f" brief:"{cGenPbEntityBriefPrefix}"`
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenPbEntityBriefRemovePrefix}"`
NameCase string `name:"nameCase" short:"n" brief:"{cGenPbEntityBriefNameCase}" d:"Camel"`
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenPbEntityBriefJsonCase}" d:"CamelLower"`
Option string `name:"option" short:"o" brief:"{cGenPbEntityBriefOption}"`
}
cGenPbEntityOutput struct{}
cGenPbEntityInternalInput struct {
cGenPbEntityInput
TableName string // TableName specifies the table name of the table.
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
}
)
func init() {
gtag.Sets(g.MapStrStr{
`cGenPbEntityConfig`: cGenPbEntityConfig,
@ -124,7 +125,7 @@ func init() {
})
}
func (c cGen) PbEntity(ctx context.Context, in cGenPbEntityInput) (out *cGenPbEntityOutput, err error) {
func (c cGenPbEntity) PbEntity(ctx context.Context, in cGenPbEntityInput) (out *cGenPbEntityOutput, err error) {
var (
config = g.Cfg()
)

View File

@ -18,15 +18,16 @@ import (
)
type (
cGenService struct{}
cGenServiceInput struct {
g.Meta `name:"service" config:"gfcli.gen.service" brief:"parse struct and associated functions from packages to generate service go file"`
SrcFolder string `short:"s" name:"srcFolder" brief:"source folder path to be parsed. default: internal/logic" d:"internal/logic"`
DstFolder string `short:"d" name:"dstFolder" brief:"destination folder path storing automatically generated go files. default: internal/service" d:"internal/service"`
WatchFile string `short:"w" name:"watchFile" brief:"used in file watcher, it generates service go files only if given file is under srcFolder"`
StPattern string `short:"a" name:"stPattern" brief:"regular expression matching struct name for generating service. default: s([A-Z]\\w+)" d:"s([A-Z]\\w+)"`
Packages string `short:"p" name:"packages" brief:"produce go files only for given source packages, multiple packages joined with char ','"`
ImportPrefix string `short:"i" name:"importPrefix" brief:"custom import prefix to calculate import path for generated importing go file of logic"`
OverWrite bool `short:"o" name:"overwrite" brief:"overwrite service go files that already exist in generating folder. default: true" d:"true" orphan:"true"`
SrcFolder string `short:"s" name:"srcFolder" brief:"source folder path to be parsed. default: internal/logic" d:"internal/logic"`
DstFolder string `short:"d" name:"dstFolder" brief:"destination folder path storing automatically generated go files. default: internal/service" d:"internal/service"`
WatchFile string `short:"w" name:"watchFile" brief:"used in file watcher, it generates service go files only if given file is under srcFolder"`
StPattern string `short:"a" name:"stPattern" brief:"regular expression matching struct name for generating service. default: s([A-Z]\\\\w+)" d:"s([A-Z]\\w+)"`
Packages []string `short:"p" name:"packages" brief:"produce go files only for given source packages"`
ImportPrefix string `short:"i" name:"importPrefix" brief:"custom import prefix to calculate import path for generated importing go file of logic"`
OverWrite bool `short:"o" name:"overwrite" brief:"overwrite service go files that already exist in generating folder. default: true" d:"true" orphan:"true"`
}
cGenServiceOutput struct{}
)
@ -35,7 +36,7 @@ const (
genServiceFileLockSeconds = 10
)
func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServiceOutput, err error) {
func (c cGenService) Service(ctx context.Context, in cGenServiceInput) (out *cGenServiceOutput, err error) {
// File lock to avoid multiple processes.
var (
flockFilePath = gfile.Temp("gf.cli.gen.service.lock")
@ -65,7 +66,7 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
)
mlog.Debug("watchFileDir:", watchFileDir)
mlog.Debug("logicFolderDir:", srcFolderDir)
if !gstr.HasSuffix(srcFolderDir, in.SrcFolder) {
if !gstr.HasSuffix(gstr.Replace(srcFolderDir, `\`, `/`), in.SrcFolder) {
mlog.Printf(`ignore watch file "%s", not in source path "%s"`, in.WatchFile, in.SrcFolder)
return
}
@ -79,7 +80,7 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
`%s gen service -packages=%s`,
gfile.SelfName(), gfile.Basename(watchFileDir),
)
err = gproc.ShellRun(command)
err = gproc.ShellRun(ctx, command)
return
}
@ -101,13 +102,12 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
}
var (
isDirty bool
files []string
fileContent string
matches [][]string
importSrcPackages []string
inputPackages = gstr.SplitAndTrim(in.Packages, ",")
dstPackageName = gstr.ToLower(gfile.Basename(in.DstFolder))
isDirty bool
files []string
fileContent string
initImportSrcPackages []string
inputPackages = in.Packages
dstPackageName = gstr.ToLower(gfile.Basename(in.DstFolder))
)
srcFolders, err := gfile.ScanDir(in.SrcFolder, "*", false)
if err != nil {
@ -125,56 +125,25 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
}
var (
// StructName => FunctionDefinitions
srcPkgInterfaceMap = make(map[string]*garray.StrArray)
srcPkgInterfaceFuncArray *garray.StrArray
ok bool
srcPkgInterfaceMap = make(map[string]*garray.StrArray)
srcImportedPackages = garray.NewSortedStrArray().SetUnique(true)
ok bool
)
for _, file := range files {
fileContent = gfile.GetContents(file)
matches, err = gregex.MatchAllString(`func \((.+?)\) ([\s\S]+?) {`, fileContent)
// Calculate imported packages of source go files.
err = c.calculateImportedPackages(fileContent, srcImportedPackages)
if err != nil {
return nil, err
}
for _, match := range matches {
var (
structName string
structMatch []string
funcReceiver = gstr.Trim(match[1])
receiverArray = gstr.SplitAndTrim(funcReceiver, " ")
functionHead = gstr.Trim(gstr.Replace(match[2], "\n", ""))
)
if len(receiverArray) > 1 {
structName = receiverArray[1]
} else {
structName = receiverArray[0]
}
structName = gstr.Trim(structName, "*")
// Xxx(\n ctx context.Context, req *v1.XxxReq,\n) -> Xxx(ctx context.Context, req *v1.XxxReq)
functionHead = gstr.Replace(functionHead, `,)`, `)`)
functionHead, _ = gregex.ReplaceString(`\(\s+`, `(`, functionHead)
functionHead, _ = gregex.ReplaceString(`\s{2,}`, ` `, functionHead)
if !gstr.IsLetterUpper(functionHead[0]) {
continue
}
if structMatch, err = gregex.MatchString(in.StPattern, structName); err != nil {
return nil, err
}
if len(structMatch) < 1 {
continue
}
structName = gstr.CaseCamel(structMatch[1])
if srcPkgInterfaceFuncArray, ok = srcPkgInterfaceMap[structName]; !ok {
srcPkgInterfaceMap[structName] = garray.NewStrArray()
srcPkgInterfaceFuncArray = srcPkgInterfaceMap[structName]
}
// Remove package name calls of `dstPackageName` in produced codes.
functionHead, _ = gregex.ReplaceString(fmt.Sprintf(`\*{0,1}%s\.`, dstPackageName), ``, functionHead)
srcPkgInterfaceFuncArray.Append(functionHead)
// Calculate functions and interfaces for service generating.
err = c.calculateInterfaceFunctions(in, fileContent, srcPkgInterfaceMap, dstPackageName)
if err != nil {
return nil, err
}
}
importSrcPackages = append(
importSrcPackages,
initImportSrcPackages = append(
initImportSrcPackages,
fmt.Sprintf(`%s/%s`, in.ImportPrefix, gfile.Basename(srcFolder)),
)
// Ignore source packages if input packages given.
@ -186,7 +155,7 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
continue
}
// Generating go files for service.
if ok, err = c.generateServiceFiles(in, srcPkgInterfaceMap, dstPackageName); err != nil {
if ok, err = c.generateServiceFiles(in, srcPkgInterfaceMap, srcImportedPackages.Slice(), dstPackageName); err != nil {
return
}
if ok {
@ -196,8 +165,8 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
if isDirty {
// Generate initialization go file.
if len(importSrcPackages) > 0 {
if err = c.generateInitializationFile(in, importSrcPackages); err != nil {
if len(initImportSrcPackages) > 0 {
if err = c.generateInitializationFile(in, initImportSrcPackages); err != nil {
return
}
}
@ -218,13 +187,86 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
return
}
func (c cGen) generateServiceFiles(
in cGenServiceInput, srcPkgInterfaceMap map[string]*garray.StrArray, dstPackageName string,
func (c cGenService) calculateImportedPackages(fileContent string, srcImportedPackages *garray.SortedStrArray) (err error) {
var match []string
match, err = gregex.MatchString(`\s+import\s+\(([\s\S]+?)\)`, fileContent)
if err != nil {
return err
}
if len(match) < 2 {
return nil
}
importPart := gstr.Trim(match[1])
srcImportedPackages.Append(gstr.SplitAndTrim(importPart, "\n")...)
return nil
}
func (c cGenService) calculateInterfaceFunctions(
in cGenServiceInput, fileContent string, srcPkgInterfaceMap map[string]*garray.StrArray, dstPackageName string,
) (err error) {
var (
ok bool
matches [][]string
srcPkgInterfaceFuncArray *garray.StrArray
)
matches, err = gregex.MatchAllString(`func \((.+?)\) ([\s\S]+?) {`, fileContent)
if err != nil {
return err
}
for _, match := range matches {
var (
structName string
structMatch []string
funcReceiver = gstr.Trim(match[1])
receiverArray = gstr.SplitAndTrim(funcReceiver, " ")
functionHead = gstr.Trim(gstr.Replace(match[2], "\n", ""))
)
if len(receiverArray) > 1 {
structName = receiverArray[1]
} else {
structName = receiverArray[0]
}
structName = gstr.Trim(structName, "*")
// Xxx(\n ctx context.Context, req *v1.XxxReq,\n) -> Xxx(ctx context.Context, req *v1.XxxReq)
functionHead = gstr.Replace(functionHead, `,)`, `)`)
functionHead, _ = gregex.ReplaceString(`\(\s+`, `(`, functionHead)
functionHead, _ = gregex.ReplaceString(`\s{2,}`, ` `, functionHead)
if !gstr.IsLetterUpper(functionHead[0]) {
continue
}
if structMatch, err = gregex.MatchString(in.StPattern, structName); err != nil {
return err
}
if len(structMatch) < 1 {
continue
}
structName = gstr.CaseCamel(structMatch[1])
if srcPkgInterfaceFuncArray, ok = srcPkgInterfaceMap[structName]; !ok {
srcPkgInterfaceMap[structName] = garray.NewStrArray()
srcPkgInterfaceFuncArray = srcPkgInterfaceMap[structName]
}
// Remove package name calls of `dstPackageName` in produced codes.
functionHead, _ = gregex.ReplaceString(fmt.Sprintf(`\*{0,1}%s\.`, dstPackageName), ``, functionHead)
srcPkgInterfaceFuncArray.Append(functionHead)
}
return nil
}
func (c cGenService) generateServiceFiles(
in cGenServiceInput,
srcPkgInterfaceMap map[string]*garray.StrArray,
srcImportedPackages []string,
dstPackageName string,
) (ok bool, err error) {
srcImportedPackagesContent := fmt.Sprintf(
"import (\n%s\n)", gstr.Join(srcImportedPackages, "\n"),
)
for structName, funcArray := range srcPkgInterfaceMap {
var (
filePath = gfile.Join(in.DstFolder, gstr.ToLower(structName)+".go")
generatedContent = gstr.ReplaceByMap(consts.TemplateGenServiceContent, g.MapStrStr{
"{Imports}": srcImportedPackagesContent,
"{StructName}": structName,
"{PackageName}": dstPackageName,
"{FuncDefinition}": funcArray.Join("\n\t"),
@ -250,7 +292,7 @@ func (c cGen) generateServiceFiles(
}
// isToGenerateServiceGoFile checks and returns whether the service content dirty.
func (c cGen) isToGenerateServiceGoFile(filePath string, funcArray *garray.StrArray) bool {
func (c cGenService) isToGenerateServiceGoFile(filePath string, funcArray *garray.StrArray) bool {
if !utils.IsFileDoNotEdit(filePath) {
mlog.Debugf(`ignore file as it is manually maintained: %s`, filePath)
return false
@ -280,7 +322,7 @@ func (c cGen) isToGenerateServiceGoFile(filePath string, funcArray *garray.StrAr
return false
}
func (c cGen) generateInitializationFile(in cGenServiceInput, importSrcPackages []string) (err error) {
func (c cGenService) generateInitializationFile(in cGenServiceInput, importSrcPackages []string) (err error) {
var (
srcPackageName = gstr.ToLower(gfile.Basename(in.SrcFolder))
srcFilePath = gfile.Join(in.SrcFolder, srcPackageName+".go")
@ -306,7 +348,7 @@ func (c cGen) generateInitializationFile(in cGenServiceInput, importSrcPackages
return nil
}
func (c cGen) replaceGeneratedServiceContentGFV2(in cGenServiceInput) (err error) {
func (c cGenService) replaceGeneratedServiceContentGFV2(in cGenServiceInput) (err error) {
return gfile.ReplaceDirFunc(func(path, content string) string {
if gstr.Contains(content, `"github.com/gogf/gf`) && !gstr.Contains(content, `"github.com/gogf/gf/v2`) {
content = gstr.Replace(content, `"github.com/gogf/gf"`, `"github.com/gogf/gf/v2"`)

View File

@ -10,6 +10,7 @@ import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/os/gres"
"github.com/gogf/gf/v2/util/gtag"
)
@ -48,7 +49,8 @@ func init() {
type cInitInput struct {
g.Meta `name:"init"`
Name string `name:"NAME" arg:"true" v:"required" brief:"{cInitNameBrief}"`
Mono bool `name:"mono" short:"m" brief:"initialize a mono-repo instead a single-repo" orphan:"true"`
Mono bool `name:"mono" short:"m" brief:"initialize a mono-repo instead a single-repo" orphan:"true"`
Update bool `name:"update" short:"u" brief:"update to the latest goframe version" orphan:"true"`
}
type cInitOutput struct{}
@ -89,6 +91,18 @@ func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err
return
}
// Update the GoFrame version.
if in.Update {
mlog.Print("update goframe...")
updateCommand := `go get -u github.com/gogf/gf/v2@latest`
if in.Name != "." {
updateCommand = fmt.Sprintf(`cd %s && %s`, in.Name, updateCommand)
}
if err = gproc.ShellRun(ctx, updateCommand); err != nil {
mlog.Fatal(err)
}
}
mlog.Print("initialization done! ")
if !in.Mono {
enjoyCommand := `gf run main.go`

View File

@ -99,17 +99,17 @@ func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err err
gtimer.SetTimeout(ctx, 1500*gtime.MS, func(ctx context.Context) {
defer dirty.Set(false)
mlog.Printf(`go file changes: %s`, event.String())
app.Run()
app.Run(ctx)
})
})
if err != nil {
mlog.Fatal(err)
}
go app.Run()
go app.Run(ctx)
select {}
}
func (app *cRunApp) Run() {
func (app *cRunApp) Run(ctx context.Context) {
// Rebuild and run the codes.
renamePath := ""
mlog.Printf("build: %s", app.File)
@ -132,7 +132,7 @@ func (app *cRunApp) Run() {
app.File,
)
mlog.Print(buildCommand)
result, err := gproc.ShellExec(buildCommand)
result, err := gproc.ShellExec(ctx, buildCommand)
if err != nil {
mlog.Printf("build error: \n%s%s", result, err.Error())
return
@ -154,7 +154,7 @@ func (app *cRunApp) Run() {
} else {
process = gproc.NewProcessCmd(outputPath, gstr.SplitAndTrim(" ", app.Args))
}
if pid, err := process.Start(); err != nil {
if pid, err := process.Start(ctx); err != nil {
mlog.Printf("build running error: %s", err.Error())
} else {
mlog.Printf("build running pid: %d", pid)

View File

@ -7,6 +7,8 @@ const TemplateGenServiceContent = `
package {PackageName}
{Imports}
type I{StructName} interface {
{FuncDefinition}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,51 +1,24 @@
package utils
import (
"fmt"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/text/gstr"
"golang.org/x/tools/imports"
)
var (
gofmtPath = gproc.SearchBinaryPath("gofmt") // gofmtPath is the binary path of command `gofmt`.
goimportsPath = gproc.SearchBinaryPath("goimports") // gofmtPath is the binary path of command `goimports`.
)
func init() {
// Wraps the command binary path with char '"' if there's space char in the path.
if gstr.Contains(gofmtPath, " ") {
gofmtPath = fmt.Sprintf(`"%s"`, gofmtPath)
}
if gstr.Contains(goimportsPath, " ") {
goimportsPath = fmt.Sprintf(`"%s"`, goimportsPath)
}
}
// GoFmt formats the source file using command `gofmt -w -s PATH`.
// GoFmt formats the source file.
func GoFmt(path string) {
if gofmtPath == "" {
mlog.Fatal(`command "gofmt" not found`)
}
var command = fmt.Sprintf(`%s -w %s`, gofmtPath, path)
result, err := gproc.ShellExec(command)
if err != nil {
mlog.Fatalf(`error executing command "%s": %s`, command, result)
if err := pretty(path, true); err != nil {
mlog.Fatalf(`error format "%s" go files: %v`, path, err)
}
}
// GoImports formats the source file using command `goimports -w PATH`.
// GoImports adds or removes import statements as necessary for the source file.
func GoImports(path string) {
if goimportsPath == "" {
mlog.Fatal(`command "goimports" not found`)
}
var command = fmt.Sprintf(`%s -w %s`, goimportsPath, path)
result, err := gproc.ShellExec(command)
if err != nil {
mlog.Fatalf(`error executing command "%s": %s`, command, result)
if err := pretty(path); err != nil {
mlog.Fatalf(`error update "%s" go file imports: %v`, path, err)
}
}
@ -56,3 +29,31 @@ func IsFileDoNotEdit(filePath string) bool {
}
return gstr.Contains(gfile.GetContents(filePath), consts.DoNotEditKey)
}
// pretty format go file and adds or removes import statements as necessary.
func pretty(filePath string, formatOnly ...bool) error {
var genOpt *imports.Options
if len(formatOnly) > 0 {
genOpt = &imports.Options{
Comments: true,
TabIndent: true,
TabWidth: 8,
FormatOnly: true,
}
}
replaceFunc := func(path, content string) string {
res, err := imports.Process(path, []byte(content), genOpt)
if err != nil {
mlog.Printf(`pretty go file "%s" failed: %v`, path, err)
return content
}
return string(res)
}
if gfile.IsFile(filePath) {
if gfile.ExtName(filePath) != "go" {
return nil
}
return gfile.ReplaceFileFunc(replaceFunc, filePath)
}
return gfile.ReplaceDirFunc(replaceFunc, filePath, "*.go", true)
}

View File

@ -46,6 +46,8 @@ func ExampleNew() {
a.Set(0, 100)
fmt.Println(a.Slice())
fmt.Println(a.At(0))
// Search item and return its index.
fmt.Println(a.Search(5))
@ -66,6 +68,7 @@ func ExampleNew() {
// false
// [0 1 2 3 4 5 6 7 8 9 10 11]
// [100 1 2 3 4 5 6 7 8 9 10 11]
// 100
// 5
// [1 2 3 4 5 6 7 8 9 10 11]
// [1 2 3 4 5 6 7 8 9 10 11]

View File

@ -54,6 +54,14 @@ func ExampleNewIntArraySize() {
// [10 20 15] 3 5
}
func ExampleNewIntArrayRange() {
s := garray.NewIntArrayRange(1, 5, 1)
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
// Output:
// [1 2 3 4 5] 5 5
}
func ExampleNewIntArrayFrom() {
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30})
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
@ -83,9 +91,12 @@ func ExampleIntArray_Get() {
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30})
sGet, sBool := s.Get(3)
fmt.Println(sGet, sBool)
sGet, sBool = s.Get(99)
fmt.Println(sGet, sBool)
// Output:
// 30 true
// 0 false
}
func ExampleIntArray_Set() {

View File

@ -25,9 +25,24 @@ func Test_Array_Basic(t *testing.T) {
array := garray.NewArrayFrom(expect)
array2 := garray.NewArrayFrom(expect)
array3 := garray.NewArrayFrom([]interface{}{})
array4 := garray.NewArrayRange(1, 5, 1)
t.Assert(array.Slice(), expect)
t.Assert(array.Interfaces(), expect)
array.Set(0, 100)
err := array.Set(0, 100)
t.AssertNil(err)
err = array.Set(100, 100)
t.AssertNE(err, nil)
t.Assert(array.IsEmpty(), false)
copyArray := array.DeepCopy()
ca := copyArray.(*garray.Array)
ca.Set(0, 1)
cval, _ := ca.Get(0)
val, _ := array.Get(0)
t.AssertNE(cval, val)
v, ok := array.Get(0)
t.Assert(v, 100)
@ -37,6 +52,10 @@ func Test_Array_Basic(t *testing.T) {
t.Assert(v, 1)
t.Assert(ok, true)
v, ok = array.Get(4)
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.Search(100), 0)
t.Assert(array3.Search(100), -1)
t.Assert(array.Contains(100), true)
@ -71,6 +90,12 @@ func Test_Array_Basic(t *testing.T) {
array.InsertAfter(6, 400)
t.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 300, 4, 400})
t.Assert(array.Clear().Len(), 0)
err = array.InsertBefore(99, 9900)
t.AssertNE(err, nil)
err = array.InsertAfter(99, 9900)
t.AssertNE(err, nil)
t.Assert(array4.String(), "[1,2,3,4,5]")
})
}
@ -99,6 +124,11 @@ func TestArray_Unique(t *testing.T) {
array := garray.NewArrayFrom(expect)
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3, 4, 5})
})
gtest.C(t, func(t *gtest.T) {
expect := []interface{}{}
array := garray.NewArrayFrom(expect)
t.Assert(array.Unique().Slice(), []interface{}{})
})
}
func TestArray_PushAndPop(t *testing.T) {
@ -382,6 +412,21 @@ func TestArray_Rand(t *testing.T) {
t.Assert(a1.Contains(i1), true)
t.Assert(a1.Len(), 4)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{}
array1 := garray.NewArrayFrom(a1)
rand, found := array1.Rand()
t.AssertNil(rand)
t.Assert(found, false)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{}
array1 := garray.NewArrayFrom(a1)
rand := array1.Rands(1)
t.AssertNil(rand)
})
}
func TestArray_Shuffle(t *testing.T) {
@ -412,6 +457,12 @@ func TestArray_Join(t *testing.T) {
array1 := garray.NewArrayFrom(a1)
t.Assert(array1.Join("."), `0.1."a".\a`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{}
array1 := garray.NewArrayFrom(a1)
t.Assert(len(array1.Join(".")), 0)
})
}
func TestArray_String(t *testing.T) {
@ -419,6 +470,8 @@ func TestArray_String(t *testing.T) {
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewArrayFrom(a1)
t.Assert(array1.String(), `[0,1,2,3,4,5,6]`)
array1 = nil
t.Assert(array1.String(), "")
})
}

View File

@ -63,6 +63,19 @@ func Test_IntArray_Basic(t *testing.T) {
array.InsertAfter(6, 400)
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
t.Assert(array.Clear().Len(), 0)
err := array.InsertBefore(99, 300)
t.AssertNE(err, nil)
err = array.InsertAfter(99, 400)
t.AssertNE(err, nil)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom([]int{0, 1, 2, 3})
copyArray := array.DeepCopy().(*garray.IntArray)
copyArray.Set(0, 1)
cval, _ := copyArray.Get(0)
val, _ := array.Get(0)
t.AssertNE(cval, val)
})
}
@ -89,6 +102,8 @@ func TestIntArray_Unique(t *testing.T) {
expect := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array := garray.NewIntArrayFrom(expect)
t.Assert(array.Unique().Slice(), []int{1, 2, 3, 4, 5})
array2 := garray.NewIntArrayFrom([]int{})
t.Assert(array2.Unique().Slice(), []int{})
})
}
@ -374,6 +389,14 @@ func TestIntArray_Rand(t *testing.T) {
v, ok := array1.Rand()
t.AssertIN(v, a1)
t.Assert(ok, true)
array2 := garray.NewIntArrayFrom([]int{})
v, ok = array2.Rand()
t.Assert(v, 0)
t.Assert(ok, false)
intSlices := array2.Rands(1)
t.Assert(intSlices, nil)
})
}
@ -420,6 +443,8 @@ func TestIntArray_String(t *testing.T) {
a1 := []int{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewIntArrayFrom(a1)
t.Assert(array1.String(), "[0,1,2,3,4,5,6]")
array1 = nil
t.Assert(array1.String(), "")
})
}

View File

@ -34,6 +34,10 @@ func Test_StrArray_Basic(t *testing.T) {
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array3.Get(0)
t.Assert(v, "")
t.Assert(ok, false)
t.Assert(array.Search("100"), 0)
t.Assert(array.Contains("100"), true)
@ -61,12 +65,28 @@ func Test_StrArray_Basic(t *testing.T) {
t.Assert(array.Clear().Len(), 0)
t.Assert(array2.Slice(), expect)
t.Assert(array3.Search("100"), -1)
err := array.InsertBefore(99, "300")
t.AssertNE(err, nil)
array.InsertAfter(99, "400")
t.AssertNE(err, nil)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom([]string{"0", "1", "2", "3"})
copyArray := array.DeepCopy().(*garray.StrArray)
copyArray.Set(0, "1")
cval, _ := copyArray.Get(0)
val, _ := array.Get(0)
t.AssertNE(cval, val)
})
}
func TestStrArray_ContainsI(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := garray.NewStrArray()
t.Assert(s.Contains("A"), false)
s.Append("a", "b", "C")
t.Assert(s.Contains("A"), false)
t.Assert(s.Contains("a"), true)
@ -94,6 +114,8 @@ func TestStrArray_Unique(t *testing.T) {
expect := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
array := garray.NewStrArrayFrom(expect)
t.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
array1 := garray.NewStrArrayFrom([]string{})
t.Assert(array1.Unique().Slice(), []string{})
})
}
@ -364,6 +386,13 @@ func TestStrArray_Rand(t *testing.T) {
v, ok := array1.Rand()
t.Assert(ok, true)
t.AssertIN(v, a1)
array2 := garray.NewStrArrayFrom([]string{})
v, ok = array2.Rand()
t.Assert(ok, false)
t.Assert(v, "")
strArray := array2.Rands(1)
t.AssertNil(strArray)
})
}
@ -406,6 +435,11 @@ func TestStrArray_Join(t *testing.T) {
array1 := garray.NewStrArrayFrom(a1)
t.Assert(array1.Join("."), `0.1."a".\a`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []string{}
array1 := garray.NewStrArrayFrom(a1)
t.Assert(array1.Join("."), "")
})
}
func TestStrArray_String(t *testing.T) {
@ -413,6 +447,9 @@ func TestStrArray_String(t *testing.T) {
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
array1 := garray.NewStrArrayFrom(a1)
t.Assert(array1.String(), `["0","1","2","3","4","5","6"]`)
array1 = nil
t.Assert(array1.String(), "")
})
}

View File

@ -61,6 +61,18 @@ func TestNewSortedArrayFromCopy(t *testing.T) {
})
}
func TestNewSortedArrayRange(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 interface{}) int {
return gconv.Int(v1) - gconv.Int(v2)
}
array1 := garray.NewSortedArrayRange(1, 5, 1, func1)
t.Assert(array1.Len(), 5)
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
})
}
func TestSortedArray_SetArray(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
@ -106,10 +118,26 @@ func TestSortedArray_Get(t *testing.T) {
v, ok = array1.Get(1)
t.Assert(v, "c")
t.Assert(ok, true)
v, ok = array1.Get(99)
t.Assert(v, nil)
t.Assert(ok, false)
})
}
func TestSortedArray_At(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "f", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
v := array1.At(2)
t.Assert(v, "f")
})
}
func TestSortedArray_Remove(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b"}
@ -461,6 +489,11 @@ func TestSortedArray_Rand(t *testing.T) {
t.Assert(ok, true)
t.AssertIN(i1, []interface{}{"a", "d", "c"})
t.Assert(array1.Len(), 3)
array2 := garray.NewSortedArrayFrom([]interface{}{}, func1)
v, ok := array2.Rand()
t.Assert(ok, false)
t.Assert(v, nil)
})
}
@ -479,6 +512,10 @@ func TestSortedArray_Rands(t *testing.T) {
i1 = array1.Rands(4)
t.Assert(len(i1), 4)
array2 := garray.NewSortedArrayFrom([]interface{}{}, func1)
v := array2.Rands(1)
t.Assert(v, nil)
})
}
@ -498,6 +535,12 @@ func TestSortedArray_Join(t *testing.T) {
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
t.Assert(array1.Join("."), `"a".0.1.\a`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
t.Assert(array1.Join("."), "")
})
}
func TestSortedArray_String(t *testing.T) {
@ -505,6 +548,9 @@ func TestSortedArray_String(t *testing.T) {
a1 := []interface{}{0, 1, "a", "b"}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
t.Assert(array1.String(), `[0,1,"a","b"]`)
array1 = nil
t.Assert(array1.String(), "")
})
}
@ -541,6 +587,11 @@ func TestSortedArray_Unique(t *testing.T) {
array1.Unique()
t.Assert(array1.Len(), 5)
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
array2 := garray.NewSortedArrayFrom([]interface{}{}, gutil.ComparatorInt)
array2.Unique()
t.Assert(array2.Len(), 0)
t.Assert(array2, []interface{}{})
})
}
@ -878,3 +929,22 @@ func TestSortedArray_Walk(t *testing.T) {
}), g.Slice{"key-1", "key-2"})
})
}
func TestSortedArray_IsEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom([]interface{}{}, gutil.ComparatorString)
t.Assert(array.IsEmpty(), true)
})
}
func TestSortedArray_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom([]interface{}{1, 2, 3, 4, 5}, gutil.ComparatorString)
copyArray := array.DeepCopy().(*garray.SortedArray)
array.Add(6)
copyArray.Add(7)
cval, _ := copyArray.Get(5)
val, _ := array.Get(5)
t.AssertNE(cval, val)
})
}

View File

@ -19,6 +19,26 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
func TestNewSortedIntArrayComparator(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 3, 2, 1, 4, 5, 6}
array1 := garray.NewSortedIntArrayComparator(func(a, b int) int {
return a - b
}, true)
array1.Append(a1...)
t.Assert(array1.Len(), 7)
t.Assert(array1.Interfaces(), []int{0, 1, 2, 3, 4, 5, 6})
})
}
func TestNewSortedIntArrayRange(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array1 := garray.NewSortedIntArrayRange(1, 5, 1)
t.Assert(array1.Len(), 5)
t.Assert(array1.Interfaces(), []int{1, 2, 3, 4, 5})
})
}
func TestNewSortedIntArrayFrom(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 3, 2, 1, 4, 5, 6}
@ -37,6 +57,17 @@ func TestNewSortedIntArrayFromCopy(t *testing.T) {
})
}
func TestSortedIntArray_At(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 3, 2, 1}
array1 := garray.NewSortedIntArrayFrom(a1)
v := array1.At(1)
t.Assert(v, 1)
})
}
func TestSortedIntArray_SetArray(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{0, 1, 2, 3}
@ -78,6 +109,10 @@ func TestSortedIntArray_Get(t *testing.T) {
v, ok = array1.Get(3)
t.Assert(v, 5)
t.Assert(ok, true)
v, ok = array1.Get(99)
t.Assert(v, 0)
t.Assert(ok, false)
})
}
@ -294,6 +329,9 @@ func TestSortedIntArray_Join(t *testing.T) {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
t.Assert(array1.Join("."), `1.3.5`)
array2 := garray.NewSortedIntArrayFrom([]int{})
t.Assert(array2.Join("."), "")
})
}
@ -302,6 +340,9 @@ func TestSortedIntArray_String(t *testing.T) {
a1 := []int{1, 3, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
t.Assert(array1.String(), `[1,3,5]`)
array1 = nil
t.Assert(array1.String(), "")
})
}
@ -406,6 +447,11 @@ func TestSortedIntArray_Rand(t *testing.T) {
ns1, ok := array1.Rand()
t.AssertIN(ns1, a1)
t.Assert(ok, true)
array2 := garray.NewSortedIntArrayFrom([]int{})
ns2, ok := array2.Rand()
t.Assert(ns2, 0)
t.Assert(ok, false)
})
}
@ -419,6 +465,10 @@ func TestSortedIntArray_Rands(t *testing.T) {
ns2 := array1.Rands(6)
t.Assert(len(ns2), 6)
array2 := garray.NewSortedIntArrayFrom([]int{})
val := array2.Rands(1)
t.Assert(val, nil)
})
}
@ -450,6 +500,10 @@ func TestSortedIntArray_Unique(t *testing.T) {
array1.Unique()
t.Assert(array1.Len(), 5)
t.Assert(array1, []int{1, 2, 3, 4, 5})
array2 := garray.NewSortedIntArrayFrom([]int{})
array2.Unique()
t.Assert(array2.Len(), 0)
})
}
@ -731,3 +785,22 @@ func TestSortedIntArray_Walk(t *testing.T) {
}), g.Slice{11, 12})
})
}
func TestSortedIntArray_IsEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom([]int{})
t.Assert(array.IsEmpty(), true)
})
}
func TestSortedIntArray_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom([]int{1, 2, 3, 4, 5})
copyArray := array.DeepCopy().(*garray.SortedIntArray)
array.Add(6)
copyArray.Add(7)
cval, _ := copyArray.Get(5)
val, _ := array.Get(5)
t.AssertNE(cval, val)
})
}

View File

@ -9,6 +9,7 @@
package garray_test
import (
"github.com/gogf/gf/v2/text/gstr"
"testing"
"time"
@ -19,6 +20,18 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
func TestNewSortedStrArrayComparator(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
s1 := garray.NewSortedStrArrayComparator(func(a, b string) int {
return gstr.Compare(a, b)
})
s1.Add(a1...)
t.Assert(s1.Len(), 4)
t.Assert(s1, []string{"a", "b", "c", "d"})
})
}
func TestNewSortedStrArrayFrom(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
@ -58,6 +71,9 @@ func TestSortedStrArray_ContainsI(t *testing.T) {
t.Assert(s.Contains("A"), false)
t.Assert(s.Contains("a"), true)
t.Assert(s.ContainsI("A"), true)
s = garray.NewSortedStrArray()
t.Assert(s.Contains("A"), false)
})
}
@ -85,6 +101,10 @@ func TestSortedStrArray_Get(t *testing.T) {
v, ok = array1.Get(0)
t.Assert(v, "a")
t.Assert(ok, true)
v, ok = array1.Get(99)
t.Assert(v, "")
t.Assert(ok, false)
})
}
@ -361,6 +381,11 @@ func TestSortedStrArray_Rand(t *testing.T) {
v, ok := array1.Rand()
t.AssertIN(v, []string{"e", "a", "d"})
t.Assert(ok, true)
array2 := garray.NewSortedStrArrayFrom([]string{})
v, ok = array2.Rand()
t.Assert(v, "")
t.Assert(ok, false)
})
}
@ -375,6 +400,10 @@ func TestSortedStrArray_Rands(t *testing.T) {
s1 = array1.Rands(4)
t.Assert(len(s1), 4)
array2 := garray.NewSortedStrArrayFrom([]string{})
val := array2.Rands(1)
t.Assert(val, nil)
})
}
@ -391,6 +420,11 @@ func TestSortedStrArray_Join(t *testing.T) {
array1 := garray.NewSortedStrArrayFrom(a1)
t.Assert(array1.Join("."), `"b".\c.a`)
})
gtest.C(t, func(t *gtest.T) {
array1 := garray.NewSortedStrArrayFrom([]string{})
t.Assert(array1.Join("."), "")
})
}
func TestSortedStrArray_String(t *testing.T) {
@ -398,6 +432,9 @@ func TestSortedStrArray_String(t *testing.T) {
a1 := []string{"e", "a", "d"}
array1 := garray.NewSortedStrArrayFrom(a1)
t.Assert(array1.String(), `["a","d","e"]`)
array1 = nil
t.Assert(array1.String(), "")
})
}
@ -469,6 +506,11 @@ func TestSortedStrArray_Unique(t *testing.T) {
array1.Unique()
t.Assert(array1.Len(), 3)
t.Assert(array1, []string{"1", "2", "3"})
array2 := garray.NewSortedStrArrayFrom([]string{})
array2.Unique()
t.Assert(array2.Len(), 0)
t.Assert(array2, []string{})
})
}
@ -755,3 +797,15 @@ func TestSortedStrArray_Walk(t *testing.T) {
}), g.Slice{"key-1", "key-2"})
})
}
func TestSortedStrArray_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom([]string{"a", "b", "c", "d"})
copyArray := array.DeepCopy().(*garray.SortedStrArray)
array.Add("e")
copyArray.Add("f")
cval, _ := copyArray.Get(4)
val, _ := array.Get(4)
t.AssertNE(cval, val)
})
}

View File

@ -755,3 +755,13 @@ func TestList_UnmarshalValue(t *testing.T) {
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
})
}
func TestList_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
copyList := l.DeepCopy()
cl := copyList.(*List)
cl.PopBack()
t.AssertNE(l.Size(), cl.Size())
})
}

View File

@ -602,8 +602,12 @@ func ExampleAnyAnyMap_String() {
fmt.Println(m.String())
var m1 *gmap.Map = nil
fmt.Println(len(m1.String()))
// Output:
// {"k1":"v1"}
// 0
}
func ExampleAnyAnyMap_MarshalJSON() {

View File

@ -601,8 +601,12 @@ func ExampleIntAnyMap_String() {
fmt.Println(m.String())
var m1 *gmap.IntAnyMap = nil
fmt.Println(len(m1.String()))
// Output:
// {"1":"v1"}
// 0
}
func ExampleIntAnyMap_MarshalJSON() {

View File

@ -529,8 +529,12 @@ func ExampleIntIntMap_String() {
fmt.Println(m.String())
var m1 *gmap.IntIntMap = nil
fmt.Println(len(m1.String()))
// Output:
// {"1":1}
// 0
}
func ExampleIntIntMap_MarshalJSON() {

View File

@ -599,8 +599,12 @@ func ExampleStrAnyMap_String() {
fmt.Println(m.String())
var m1 *gmap.StrAnyMap = nil
fmt.Println(len(m1.String()))
// Output:
// {"k1":"v1"}
// 0
}
func ExampleStrAnyMap_MarshalJSON() {

View File

@ -8,11 +8,10 @@ package gmap_test
import (
"fmt"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/container/gmap"
)
func ExampleStrIntMap_Iterator() {
@ -535,8 +534,12 @@ func ExampleStrIntMap_String() {
fmt.Println(m.String())
var m1 *gmap.StrIntMap = nil
fmt.Println(len(m1.String()))
// Output:
// {"k1":1}
// 0
}
func ExampleStrIntMap_MarshalJSON() {

View File

@ -531,8 +531,12 @@ func ExampleStrStrMap_String() {
fmt.Println(m.String())
var m1 *gmap.StrStrMap = nil
fmt.Println(len(m1.String()))
// Output:
// {"k1":"v1"}
// 0
}
func ExampleStrStrMap_MarshalJSON() {

View File

@ -90,7 +90,7 @@ func ExampleNewFrom() {
}
func ExampleNewHashMap() {
m := gmap.New()
m := gmap.NewHashMap()
m.Set("key1", "val1")
fmt.Println(m)
@ -105,7 +105,7 @@ func ExampleNewHashMapFrom() {
m.Set("key1", "val1")
fmt.Println(m)
n := gmap.NewFrom(m.MapCopy(), true)
n := gmap.NewHashMapFrom(m.MapCopy(), true)
fmt.Println(n)
// Output:

View File

@ -171,6 +171,9 @@ func Test_AnyAnyMap_Merge(t *testing.T) {
m2.Set(2, "2")
m1.Merge(m2)
t.Assert(m1.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
m3 := gmap.NewAnyAnyMapFrom(nil)
m3.Merge(m2)
t.Assert(m3.Map(), m2.Map())
})
}
@ -296,6 +299,10 @@ func Test_AnyAnyMap_Pop(t *testing.T) {
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
k3, v3 := m.Pop()
t.AssertNil(k3)
t.AssertNil(v3)
})
}
@ -327,6 +334,11 @@ func Test_AnyAnyMap_Pops(t *testing.T) {
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
v := m.Pops(1)
t.AssertNil(v)
v = m.Pops(-1)
t.AssertNil(v)
})
}
@ -365,3 +377,17 @@ func TestAnyAnyMap_UnmarshalValue(t *testing.T) {
t.Assert(v.Map.Get("k2"), "v2")
})
}
func Test_AnyAnyMap_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
})
t.Assert(m.Size(), 2)
n := m.DeepCopy().(*gmap.AnyAnyMap)
n.Set("k1", "val1")
t.AssertNE(m.Get("k1"), n.Get("k1"))
})
}

View File

@ -82,6 +82,12 @@ func Test_IntAnyMap_Basic(t *testing.T) {
m2 := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
t.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMap(true)
m.Set(1, 1)
t.Assert(m.Map(), map[int]interface{}{1: 1})
})
}
func Test_IntAnyMap_Set_Fun(t *testing.T) {
@ -171,6 +177,9 @@ func Test_IntAnyMap_Merge(t *testing.T) {
m2.Set(2, "2")
m1.Merge(m2)
t.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"})
m3 := gmap.NewIntAnyMapFrom(nil)
m3.Merge(m2)
t.Assert(m3.Map(), m2.Map())
})
}
@ -271,6 +280,10 @@ func Test_IntAnyMap_Pop(t *testing.T) {
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
k3, v3 := m.Pop()
t.Assert(k3, 0)
t.AssertNil(v3)
})
}
@ -302,6 +315,11 @@ func Test_IntAnyMap_Pops(t *testing.T) {
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
v := m.Pops(1)
t.AssertNil(v)
v = m.Pops(-1)
t.AssertNil(v)
})
}
@ -340,3 +358,17 @@ func TestIntAnyMap_UnmarshalValue(t *testing.T) {
t.Assert(v.Map.Get(2), "v2")
})
}
func Test_IntAnyMap_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
1: "v1",
2: "v2",
})
t.Assert(m.Size(), 2)
n := m.DeepCopy().(*gmap.IntAnyMap)
n.Set(1, "val1")
t.AssertNE(m.Get(1), n.Get(1))
})
}

View File

@ -86,6 +86,12 @@ func Test_IntIntMap_Basic(t *testing.T) {
m2 := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
t.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMap(true)
m.Set(1, 1)
t.Assert(m.Map(), map[int]int{1: 1})
})
}
func Test_IntIntMap_Set_Fun(t *testing.T) {
@ -102,6 +108,11 @@ func Test_IntIntMap_Set_Fun(t *testing.T) {
t.Assert(m.SetIfNotExistFuncLock(2, getInt), false)
t.Assert(m.SetIfNotExistFuncLock(4, getInt), true)
})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMapFrom(nil)
t.Assert(m.GetOrSetFuncLock(1, getInt), getInt())
})
}
func Test_IntIntMap_Batch(t *testing.T) {
@ -177,6 +188,9 @@ func Test_IntIntMap_Merge(t *testing.T) {
m2.Set(2, 2)
m1.Merge(m2)
t.Assert(m1.Map(), map[int]int{1: 1, 2: 2})
m3 := gmap.NewIntIntMapFrom(nil)
m3.Merge(m2)
t.Assert(m3.Map(), m2.Map())
})
}
@ -277,6 +291,10 @@ func Test_IntIntMap_Pop(t *testing.T) {
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
k3, v3 := m.Pop()
t.Assert(k3, 0)
t.Assert(v3, 0)
})
}
@ -308,6 +326,11 @@ func Test_IntIntMap_Pops(t *testing.T) {
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
v := m.Pops(1)
t.AssertNil(v)
v = m.Pops(-1)
t.AssertNil(v)
})
}
@ -346,3 +369,17 @@ func TestIntIntMap_UnmarshalValue(t *testing.T) {
t.Assert(v.Map.Get(2), "2")
})
}
func Test_IntIntMap_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMapFrom(g.MapIntInt{
1: 1,
2: 2,
})
t.Assert(m.Size(), 2)
n := m.DeepCopy().(*gmap.IntIntMap)
n.Set(1, 2)
t.AssertNE(m.Get(1), n.Get(1))
})
}

View File

@ -63,6 +63,7 @@ func Test_IntStrMap_Basic(t *testing.T) {
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet(1, "a"), "a")
t.Assert(m.GetOrSet(2, "b"), "b")
t.Assert(m.SetIfNotExist(2, "b"), false)
@ -90,6 +91,29 @@ func Test_IntStrMap_Basic(t *testing.T) {
m2 := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b"})
t.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMap(true)
m.Set(1, "val1")
t.Assert(m.Map(), map[int]string{1: "val1"})
})
}
func TestIntStrMap_MapStrAny(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMap()
m.GetOrSetFunc(1, getStr)
m.GetOrSetFuncLock(2, getStr)
t.Assert(m.MapStrAny(), g.MapStrAny{"1": "z", "2": "z"})
})
}
func TestIntStrMap_Sets(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMapFrom(nil)
m.Sets(g.MapIntStr{1: "z", 2: "z"})
t.Assert(len(m.Map()), 2)
})
}
func Test_IntStrMap_Set_Fun(t *testing.T) {
@ -97,6 +121,8 @@ func Test_IntStrMap_Set_Fun(t *testing.T) {
m := gmap.NewIntStrMap()
m.GetOrSetFunc(1, getStr)
m.GetOrSetFuncLock(2, getStr)
t.Assert(m.GetOrSetFunc(1, getStr), "z")
t.Assert(m.GetOrSetFuncLock(2, getStr), "z")
t.Assert(m.Get(1), "z")
t.Assert(m.Get(2), "z")
t.Assert(m.SetIfNotExistFunc(1, getStr), false)
@ -105,6 +131,16 @@ func Test_IntStrMap_Set_Fun(t *testing.T) {
t.Assert(m.SetIfNotExistFuncLock(2, getStr), false)
t.Assert(m.SetIfNotExistFuncLock(4, getStr), true)
})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMapFrom(nil)
t.Assert(m.GetOrSetFuncLock(1, getStr), "z")
})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMapFrom(nil)
t.Assert(m.SetIfNotExistFuncLock(1, getStr), true)
})
}
func Test_IntStrMap_Batch(t *testing.T) {
@ -176,6 +212,10 @@ func Test_IntStrMap_Merge(t *testing.T) {
m2.Set(2, "b")
m1.Merge(m2)
t.Assert(m1.Map(), map[int]string{1: "a", 2: "b"})
m3 := gmap.NewIntStrMapFrom(nil)
m3.Merge(m2)
t.Assert(m3.Map(), m2.Map())
})
}
@ -275,6 +315,10 @@ func Test_IntStrMap_Pop(t *testing.T) {
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
k3, v3 := m.Pop()
t.Assert(k3, 0)
t.Assert(v3, "")
})
}
@ -306,6 +350,11 @@ func Test_IntStrMap_Pops(t *testing.T) {
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
v := m.Pops(1)
t.AssertNil(v)
v = m.Pops(-1)
t.AssertNil(v)
})
}
@ -344,3 +393,55 @@ func TestIntStrMap_UnmarshalValue(t *testing.T) {
t.Assert(v.Map.Get(2), "v2")
})
}
func TestIntStrMap_Replace(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMapFrom(g.MapIntStr{
1: "v1",
2: "v2",
3: "v3",
})
t.Assert(m.Get(1), "v1")
t.Assert(m.Get(2), "v2")
t.Assert(m.Get(3), "v3")
m.Replace(g.MapIntStr{
1: "v2",
2: "v3",
3: "v1",
})
t.Assert(m.Get(1), "v2")
t.Assert(m.Get(2), "v3")
t.Assert(m.Get(3), "v1")
})
}
func TestIntStrMap_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMapFrom(g.MapIntStr{
1: "v1",
2: "v2",
3: "v3",
})
t.Assert(m.String(), "{\"1\":\"v1\",\"2\":\"v2\",\"3\":\"v3\"}")
m = nil
t.Assert(len(m.String()), 0)
})
}
func Test_IntStrMap_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMapFrom(g.MapIntStr{
1: "val1",
2: "val2",
})
t.Assert(m.Size(), 2)
n := m.DeepCopy().(*gmap.IntStrMap)
n.Set(1, "v1")
t.AssertNE(m.Get(1), n.Get(1))
})
}

View File

@ -169,6 +169,10 @@ func Test_StrAnyMap_Merge(t *testing.T) {
m2.Set("b", "2")
m1.Merge(m2)
t.Assert(m1.Map(), map[string]interface{}{"a": 1, "b": "2"})
m3 := gmap.NewStrAnyMapFrom(nil)
m3.Merge(m2)
t.Assert(m3.Map(), m2.Map())
})
}
@ -283,6 +287,10 @@ func Test_StrAnyMap_Pop(t *testing.T) {
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
k3, v3 := m.Pop()
t.Assert(k3, "")
t.Assert(v3, "")
})
}
@ -314,6 +322,11 @@ func Test_StrAnyMap_Pops(t *testing.T) {
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
v := m.Pops(1)
t.AssertNil(v)
v = m.Pops(-1)
t.AssertNil(v)
})
}
@ -352,3 +365,17 @@ func TestStrAnyMap_UnmarshalValue(t *testing.T) {
t.Assert(v.Map.Get("k2"), "v2")
})
}
func Test_StrAnyMap_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrAnyMapFrom(g.MapStrAny{
"key1": "val1",
"key2": "val2",
})
t.Assert(m.Size(), 2)
n := m.DeepCopy().(*gmap.StrAnyMap)
n.Set("key1", "v1")
t.AssertNE(m.Get("key1"), n.Get("key1"))
})
}

View File

@ -100,6 +100,11 @@ func Test_StrIntMap_Set_Fun(t *testing.T) {
t.Assert(m.SetIfNotExistFuncLock("b", getInt), false)
t.Assert(m.SetIfNotExistFuncLock("d", getInt), true)
})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrIntMapFrom(nil)
t.Assert(m.GetOrSetFuncLock("a", getInt), 123)
})
}
func Test_StrIntMap_Batch(t *testing.T) {
@ -173,6 +178,9 @@ func Test_StrIntMap_Merge(t *testing.T) {
m2.Set("b", 2)
m1.Merge(m2)
t.Assert(m1.Map(), map[string]int{"a": 1, "b": 2})
m3 := gmap.NewStrIntMapFrom(nil)
m3.Merge(m2)
t.Assert(m3.Map(), m2.Map())
})
}
@ -287,6 +295,10 @@ func Test_StrIntMap_Pop(t *testing.T) {
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
k3, v3 := m.Pop()
t.Assert(k3, "")
t.Assert(v3, 0)
})
}
@ -318,6 +330,11 @@ func Test_StrIntMap_Pops(t *testing.T) {
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
v := m.Pops(1)
t.AssertNil(v)
v = m.Pops(-1)
t.AssertNil(v)
})
}
@ -356,3 +373,17 @@ func TestStrIntMap_UnmarshalValue(t *testing.T) {
t.Assert(v.Map.Get("k2"), 2)
})
}
func Test_StrIntMap_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrIntMapFrom(g.MapStrInt{
"key1": 1,
"key2": 2,
})
t.Assert(m.Size(), 2)
n := m.DeepCopy().(*gmap.StrIntMap)
n.Set("key1", 2)
t.AssertNE(m.Get("key1"), n.Get("key1"))
})
}

View File

@ -98,6 +98,12 @@ func Test_StrStrMap_Set_Fun(t *testing.T) {
t.Assert(m.SetIfNotExistFuncLock("b", getStr), false)
t.Assert(m.SetIfNotExistFuncLock("d", getStr), true)
})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrStrMapFrom(nil)
t.Assert(m.GetOrSetFuncLock("b", getStr), "z")
})
}
func Test_StrStrMap_Batch(t *testing.T) {
@ -170,6 +176,9 @@ func Test_StrStrMap_Merge(t *testing.T) {
m2.Set("b", "b")
m1.Merge(m2)
t.Assert(m1.Map(), map[string]string{"a": "a", "b": "b"})
m3 := gmap.NewStrStrMapFrom(nil)
m3.Merge(m2)
t.Assert(m3.Map(), m2.Map())
})
}
@ -284,6 +293,10 @@ func Test_StrStrMap_Pop(t *testing.T) {
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
k3, v3 := m.Pop()
t.Assert(k3, "")
t.Assert(v3, "")
})
}
@ -315,6 +328,11 @@ func Test_StrStrMap_Pops(t *testing.T) {
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
v := m.Pops(1)
t.AssertNil(v)
v = m.Pops(-1)
t.AssertNil(v)
})
}
@ -353,3 +371,17 @@ func TestStrStrMap_UnmarshalValue(t *testing.T) {
t.Assert(v.Map.Get("k2"), "v2")
})
}
func Test_StrStrMap_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrStrMapFrom(g.MapStrStr{
"key1": "val1",
"key2": "val2",
})
t.Assert(m.Size(), 2)
n := m.DeepCopy().(*gmap.StrStrMap)
n.Set("key1", "v1")
t.AssertNE(m.Get("key1"), n.Get("key1"))
})
}

View File

@ -154,6 +154,9 @@ func Test_ListMap_Basic_Merge(t *testing.T) {
m2.Set("key2", "val2")
m1.Merge(m2)
t.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
m3 := gmap.NewListMapFrom(nil)
m3.Merge(m2)
t.Assert(m3.Map(), m2.Map())
})
}
@ -266,6 +269,10 @@ func Test_ListMap_Pop(t *testing.T) {
t.AssertNE(k1, k2)
t.AssertNE(v1, v2)
k3, v3 := m.Pop()
t.AssertNil(k3)
t.AssertNil(v3)
})
}
@ -297,6 +304,11 @@ func Test_ListMap_Pops(t *testing.T) {
t.Assert(kArray.Unique().Len(), 3)
t.Assert(vArray.Unique().Len(), 3)
v := m.Pops(1)
t.AssertNil(v)
v = m.Pops(-1)
t.AssertNil(v)
})
}
@ -335,3 +347,44 @@ func TestListMap_UnmarshalValue(t *testing.T) {
t.Assert(v.Map.Get("2"), "v2")
})
}
func TestListMap_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMap()
m.Set(1, "")
m.Set(2, "2")
t.Assert(m.String(), "{\"1\":\"\",\"2\":\"2\"}")
m1 := gmap.NewListMapFrom(nil)
t.Assert(m1.String(), "{}")
})
}
func TestListMap_MarshalJSON(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMap()
m.Set(1, "")
m.Set(2, "2")
res, err := m.MarshalJSON()
t.Assert(res, []byte("{\"1\":\"\",\"2\":\"2\"}"))
t.AssertNil(err)
m1 := gmap.NewListMapFrom(nil)
res, err = m1.MarshalJSON()
t.Assert(res, []byte("{}"))
t.AssertNil(err)
})
}
func TestListMap_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMap()
m.Set(1, "1")
m.Set(2, "2")
t.Assert(m.Size(), 2)
n := m.DeepCopy().(*gmap.ListMap)
n.Set(1, "val1")
t.AssertNE(m.Get(1), n.Get(1))
})
}

View File

@ -40,7 +40,7 @@ import _ "github.com/gogf/gf/contrib/drivers/mysql/v2"
import _ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
```
Note:
- It does not support `Save/Replace` features.
- It does not support `Save` features.
## PostgreSQL
```

View File

@ -3,13 +3,12 @@ module github.com/gogf/gf/contrib/drivers/clickhouse/v2
go 1.15
require (
github.com/ClickHouse/clickhouse-go/v2 v2.0.14
github.com/ClickHouse/clickhouse-go/v2 v2.0.15
github.com/gogf/gf/v2 v2.0.0
github.com/google/uuid v1.3.0
github.com/mattn/go-isatty v0.0.14 // indirect
)
replace (
github.com/ClickHouse/clickhouse-go/v2 => github.com/gogf/clickhouse-go/v2 v2.0.14-compatible
github.com/ClickHouse/clickhouse-go/v2 => github.com/gogf/clickhouse-go/v2 v2.0.15-compatible
github.com/gogf/gf/v2 => ../../../
)

View File

@ -34,8 +34,8 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gogf/clickhouse-go/v2 v2.0.14-compatible h1:eTT+USRgOZyTXyHyIFzJU+utFUsNVPmrzmQaKS9zdoM=
github.com/gogf/clickhouse-go/v2 v2.0.14-compatible/go.mod h1:NdPxn4Kfffa6Tv14otrFtbV+zH6v2xHVfDf4KICt/0s=
github.com/gogf/clickhouse-go/v2 v2.0.15-compatible h1:VYgibtmI/u+hUQmtJpC7zzg1YJsDCXS052R7vCqGpDc=
github.com/gogf/clickhouse-go/v2 v2.0.15-compatible/go.mod h1:Z21o82zD8FFqefOQDg93c0XITlxGbTsWQuRm588Azkk=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@ -96,8 +96,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/paulmach/orb v0.7.0 h1:l6uxkg+vRU9QJkBHtzvYpkVb09tCIRwnEHbY5MNMNqo=
github.com/paulmach/orb v0.7.0/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
github.com/paulmach/orb v0.7.1 h1:Zha++Z5OX/l168sqHK3k4z18LDvr+YAO/VjK0ReQ9rU=
github.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@ -114,8 +114,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -173,8 +174,6 @@ golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32 h1:Js08h5hqB5xyWR789+QqueR6sDE8mk+YvpETZ+F6X9Y=
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba h1:AyHWHCBVlIYI5rgEM3o+1PLd0sLPcIAoaUckGQMaWtw=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -213,5 +212,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -25,6 +25,7 @@ import (
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)
// Driver is the driver for sqlite database.
@ -35,6 +36,8 @@ type Driver struct {
var (
// tableFieldsMap caches the table information retrieved from database.
tableFieldsMap = gmap.New(true)
// Error
ErrorSave = gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`)
)
func init() {
@ -147,10 +150,17 @@ func (d *Driver) TableFields(ctx context.Context, table string, schema ...string
}
fields = make(map[string]*gdb.TableField)
for i, m := range result {
mKey := ""
if m["pk"].Bool() {
mKey = "pri"
}
fields[strings.ToLower(m["name"].String())] = &gdb.TableField{
Index: i,
Name: strings.ToLower(m["name"].String()),
Type: strings.ToLower(m["type"].String()),
Index: i,
Name: strings.ToLower(m["name"].String()),
Type: strings.ToLower(m["type"].String()),
Key: mKey,
Default: m["dflt_value"].Val(),
Null: !m["notnull"].Bool(),
}
}
return fields
@ -166,11 +176,73 @@ func (d *Driver) TableFields(ctx context.Context, table string, schema ...string
func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption) (result sql.Result, err error) {
switch option.InsertOption {
case gdb.InsertOptionSave:
return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`)
case gdb.InsertOptionReplace:
return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by sqlite driver`)
return nil, ErrorSave
case gdb.InsertOptionIgnore, gdb.InsertOptionReplace:
var (
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.
)
// Handle the field names and placeholders.
for k := range list[0] {
keys = append(keys, k)
}
// Prepare the batch result pointer.
var (
charL, charR = d.GetChars()
batchResult = new(gdb.SqlResult)
keysStr = charL + strings.Join(keys, charR+","+charL) + charR
operation = "INSERT OR IGNORE"
)
if option.InsertOption == gdb.InsertOptionReplace {
operation = "INSERT OR REPLACE"
}
var (
listLength = len(list)
valueHolder = make([]string, 0)
)
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 := list[i][k].(gdb.Raw); ok {
values = append(values, gconv.String(s))
} else {
values = append(values, "?")
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) == option.BatchCount || (i == listLength-1 && len(valueHolder) > 0) {
var (
stdSqlResult sql.Result
affectedRows int64
)
stdSqlResult, err = d.DoExec(ctx, link, fmt.Sprintf(
"%s INTO %s(%s) VALUES%s %s",
operation, d.QuotePrefixTableName(table), keysStr,
gstr.Join(valueHolder, ","),
onDuplicateStr,
), params...)
if err != nil {
return stdSqlResult, err
}
if affectedRows, err = stdSqlResult.RowsAffected(); err != nil {
err = gerror.WrapCode(gcode.CodeDbOperationError, err, `sql.Result.RowsAffected failed`)
return stdSqlResult, err
} else {
batchResult.Result = stdSqlResult
batchResult.Affected += affectedRows
}
params = params[:0]
valueHolder = valueHolder[:0]
}
}
return batchResult, nil
default:
return d.Core.DoInsert(ctx, link, table, list, option)
}

View File

@ -0,0 +1,161 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This 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 sqlite_test
import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
)
var (
db gdb.DB
dbPrefix gdb.DB
dbInvalid gdb.DB
configNode gdb.ConfigNode
dbDir = gfile.Temp("sqlite")
ctx = gctx.New()
// Error
ErrorSave = gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`)
)
const (
TableSize = 10
TableName = "user"
TestSchema1 = "test1"
TestSchema2 = "test2"
TableNamePrefix = "gf_"
CreateTime = "2018-10-24 10:00:00"
DBGroupTest = "test"
DBGroupPrefix = "prefix"
DBGroupInvalid = "invalid"
)
func init() {
fmt.Println("init sqlite db start")
if err := gfile.Mkdir(dbDir); err != nil {
gtest.Error(err)
}
fmt.Println("init sqlite db dir: ", dbDir)
configNode = gdb.ConfigNode{
Type: "sqlite",
Link: gfile.Join(dbDir, "test.db"),
Charset: "utf8",
}
nodePrefix := configNode
nodePrefix.Prefix = TableNamePrefix
nodeInvalid := configNode
gdb.AddConfigNode(DBGroupTest, configNode)
gdb.AddConfigNode(DBGroupPrefix, nodePrefix)
gdb.AddConfigNode(DBGroupInvalid, nodeInvalid)
gdb.AddConfigNode(gdb.DefaultGroupName, configNode)
// Default db.
if r, err := gdb.NewByGroup(); err != nil {
gtest.Error(err)
} else {
db = r
}
// Prefix db.
if r, err := gdb.NewByGroup(DBGroupPrefix); err != nil {
gtest.Error(err)
} else {
dbPrefix = r
}
// Invalid db.
if r, err := gdb.NewByGroup(DBGroupInvalid); err != nil {
gtest.Error(err)
} else {
dbInvalid = r
}
fmt.Println("init sqlite db finish")
}
func createTable(table ...string) string {
return createTableWithDb(db, table...)
}
func createInitTable(table ...string) string {
return createInitTableWithDb(db, table...)
}
func dropTable(table string) {
dropTableWithDb(db, table)
}
func createTableWithDb(db gdb.DB, table ...string) (name string) {
if len(table) > 0 {
name = table[0]
} else {
name = fmt.Sprintf(`%s_%d`, TableName, gtime.TimestampNano())
}
dropTableWithDb(db, name)
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id INTEGER PRIMARY KEY AUTOINCREMENT
UNIQUE
NOT NULL,
passport VARCHAR(45) NOT NULL
DEFAULT passport,
password VARCHAR(128) NOT NULL
DEFAULT password,
nickname VARCHAR(45),
create_time DATETIME
);
`, name,
)); err != nil {
gtest.Fatal(err)
}
return
}
func createInitTableWithDb(db gdb.DB, table ...string) (name string) {
name = createTableWithDb(db, table...)
array := garray.New(true)
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(CreateTime).String(),
})
}
result, err := db.Insert(ctx, name, array.Slice())
gtest.AssertNil(err)
n, e := result.RowsAffected()
gtest.Assert(e, nil)
gtest.Assert(n, TableSize)
return
}
func dropTableWithDb(db gdb.DB, table string) {
if _, err := db.Exec(ctx, fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
gtest.Error(err)
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
# GoFrame Etcd Registry
Use `etcd` as service registration and discovery management.
## Installation
```
go get -u -v github.com/gogf/gf/contrib/registry/etcd/v2
```
suggested using `go.mod`:
```
require github.com/gogf/gf/contrib/registry/etcd/v2 latest
```
## Example
### Reference example
[server](example/registry/etcd/server/main.go)
```go
package main
import (
"github.com/gogf/gf/contrib/registry/etcd/v2"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/net/gsvc"
)
func main() {
gsvc.SetRegistry(etcd.New(`127.0.0.1:2379`))
s := g.Server(`hello.svc`)
s.BindHandler("/", func(r *ghttp.Request) {
g.Log().Info(r.Context(), `request received`)
r.Response.Write(`Hello world`)
})
s.Run()
}
```
[client](example/registry/etcd/client/main.go)
```go
package main
import (
"fmt"
"time"
"github.com/gogf/gf/contrib/registry/etcd/v2"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gsel"
"github.com/gogf/gf/v2/net/gsvc"
"github.com/gogf/gf/v2/os/gctx"
)
func main() {
gsvc.SetRegistry(etcd.New(`127.0.0.1:2379`))
gsel.SetBuilder(gsel.NewBuilderRoundRobin())
client := g.Client()
for i := 0; i < 100; i++ {
res, err := client.Get(gctx.New(), `http://hello.svc/`)
if err != nil {
panic(err)
}
fmt.Println(res.ReadAllString())
res.Close()
time.Sleep(time.Second)
}
}
```
## License
`GoFrame etcd` is licensed under the [MIT License](../../../LICENSE), 100% free and open-source, forever.

View File

@ -2,7 +2,7 @@
English | [简体中文](README_ZH.MD)
Use Polaris mesh as service registration, discovery management and heartbeat reporting
Use `PolarisMesh` as service registration, discovery management and heartbeat reporting.
## Installation
@ -14,11 +14,6 @@ suggested using `go.mod`:
require github.com/gogf/gf/contrib/registry/polaris/v2 latest
```
## Limitation
```
golang version >= 1.15
```
## Example
@ -50,7 +45,6 @@ func main() {
})
s.Run()
}
```
[client](example/registry/polaris/client/main.go)
@ -85,7 +79,6 @@ func main() {
time.Sleep(time.Second)
}
}
```
## License

View File

@ -2,7 +2,7 @@
[English](README.MD) | 简体中文
使用Polarismesh作为服务注册、发现管理和心跳上报
使用`PolarisMesh`作为服务注册、发现管理和心跳上报
## Installation

View File

@ -331,9 +331,6 @@ var (
// in the field name as it conflicts with "db.table.field" pattern in SOME situations.
regularFieldNameWithoutDotRegPattern = `^[\w\-]+$`
// tableFieldsMap caches the table information retrieved 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
@ -399,8 +396,7 @@ func doNewByNode(node ConfigNode, group string) (db DB, err error) {
config: &node,
}
if v, ok := driverMap[node.Type]; ok {
c.db, err = v.New(c, &node)
if err != nil {
if c.db, err = v.New(c, &node); err != nil {
return nil, err
}
return c.db, nil

View File

@ -13,6 +13,8 @@ import (
"github.com/gogf/gf/v2/os/gcache"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
)
// Config is the configuration management object.
@ -72,6 +74,12 @@ func SetConfig(config Config) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
for k, nodes := range config {
for i, node := range nodes {
nodes[i] = parseConfigNode(node)
}
config[k] = nodes
}
configs.config = config
}
@ -80,6 +88,9 @@ func SetConfigGroup(group string, nodes ConfigGroup) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
for i, node := range nodes {
nodes[i] = parseConfigNode(node)
}
configs.config[group] = nodes
}
@ -88,7 +99,19 @@ func AddConfigNode(group string, node ConfigNode) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config[group] = append(configs.config[group], node)
configs.config[group] = append(configs.config[group], parseConfigNode(node))
}
// parseConfigNode parses `Link` configuration syntax.
func parseConfigNode(node ConfigNode) ConfigNode {
if node.Link != "" && node.Type == "" {
match, _ := gregex.MatchString(`([a-z]+):(.+)`, node.Link)
if len(match) == 3 {
node.Type = gstr.Trim(match[1])
node.Link = gstr.Trim(match[2])
}
}
return node
}
// AddDefaultConfigNode adds one node configuration to configuration of default group.

View File

@ -72,6 +72,6 @@ func (m *Model) Group(groupBy ...string) *Model {
if model.groupBy != "" {
model.groupBy += ","
}
model.groupBy = model.db.GetCore().QuoteString(strings.Join(groupBy, ","))
model.groupBy += model.db.GetCore().QuoteString(strings.Join(groupBy, ","))
return model
}

View File

@ -168,9 +168,13 @@ func Test_Error(t *testing.T) {
_, err = conn.Do(ctx, "Subscribe", "gf")
t.AssertNil(err)
time.Sleep(time.Second)
_, err = redis.Do(ctx, "PUBLISH", "gf", "test")
t.AssertNil(err)
time.Sleep(time.Second)
v, err = conn.Receive(ctx)
t.AssertNil(err)
t.Assert(v.Val().(*gredis.Subscription).Channel, "gf")

View File

@ -222,6 +222,9 @@ func Code(err error) gcode.Code {
if e, ok := err.(iNext); ok {
return Code(e.Next())
}
if e, ok := err.(iUnwrap); ok {
return Code(e.Unwrap())
}
return gcode.CodeNil
}
@ -236,6 +239,9 @@ func Cause(err error) error {
if e, ok := err.(iNext); ok {
return Cause(e.Next())
}
if e, ok := err.(iUnwrap); ok {
return Cause(e.Unwrap())
}
return err
}

View File

@ -49,3 +49,9 @@ type iNext interface {
Error() string
Next() error
}
// iUnwrap is the interface for Unwrap feature.
type iUnwrap interface {
Error() string
Unwrap() error
}

View File

@ -55,3 +55,30 @@ func ExampleWrapCodef() {
// It's Custom Error: permission denied
// 10000
}
func ExampleEqual() {
err1 := errors.New("permission denied")
err2 := gerror.New("permission denied")
err3 := gerror.NewCode(gcode.CodeNotAuthorized, "permission denied")
fmt.Println(gerror.Equal(err1, err2))
fmt.Println(gerror.Equal(err2, err3))
// Output:
// true
// false
}
func ExampleIs() {
err1 := errors.New("permission denied")
err2 := gerror.Wrap(err1, "operation failed")
fmt.Println(gerror.Is(err1, err1))
fmt.Println(gerror.Is(err2, err2))
fmt.Println(gerror.Is(err2, err1))
fmt.Println(gerror.Is(err1, err2))
// Output:
// false
// true
// true
// false
}

View File

@ -7,8 +7,8 @@ require (
github.com/gogf/gf/contrib/registry/etcd/v2 v2.1.0-rc3.0.20220523034830-510fa3faf03f
github.com/gogf/gf/contrib/registry/polaris/v2 v2.0.0-rc2
github.com/gogf/gf/contrib/trace/jaeger/v2 v2.0.0-rc2
github.com/gogf/gf/v2 v2.1.0-rc3.0.20220523034830-510fa3faf03f
github.com/gogf/katyusha v0.4.0
github.com/gogf/gf/v2 v2.1.0-rc4.0.20220620123459-52056644d499
github.com/gogf/katyusha v0.4.1-0.20220620125113-f55d6f739773
github.com/gogo/protobuf v1.3.2
github.com/golang/protobuf v1.5.2
github.com/polarismesh/polaris-go v1.2.0-beta.0.0.20220517041223-596a6a63b00f

View File

@ -111,8 +111,8 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogf/katyusha v0.4.0 h1:mQVfXHhzC+UQf11Q8HAk9IOhQZ1VMXqGUqezyywZUOs=
github.com/gogf/katyusha v0.4.0/go.mod h1:nqsIWBsImnq9+OLlfB6iNef6ZLRyR2L1Bnk9h2aZvKs=
github.com/gogf/katyusha v0.4.1-0.20220620125113-f55d6f739773 h1:YQBLawktoymYtPGs9idE9JS5Wqd3SjIzUEZOPKCdSw0=
github.com/gogf/katyusha v0.4.1-0.20220620125113-f55d6f739773/go.mod h1:Z0GCeHXz1UI0HtA0K45c6TzEGM4DL/PLatS747/WarI=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=

View File

@ -0,0 +1,25 @@
package main
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gproc"
)
var (
Main = &gcmd.Command{
Name: "main",
Brief: "main process",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
g.Log().Debug(ctx, `this is main process`)
return gproc.ShellRun(ctx, `go run sub/sub.go`)
},
}
)
func main() {
Main.Run(gctx.New())
}

View File

@ -0,0 +1,24 @@
package main
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gctx"
)
var (
Sub = &gcmd.Command{
Name: "sub",
Brief: "sub process",
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
g.Log().Debug(ctx, `this is sub process`)
return nil
},
}
)
func main() {
Sub.Run(gctx.New())
}

View File

@ -0,0 +1,15 @@
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gproc"
)
func main() {
ctx := gctx.New()
g.Log().Debug(ctx, `this is main process`)
if err := gproc.ShellRun(ctx, `go run sub/sub.go`); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,11 @@
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
)
func main() {
ctx := gctx.New()
g.Log().Debug(ctx, `this is sub process`)
}

View File

@ -63,14 +63,14 @@ func Throw(exception interface{}) {
// Try implements try... logistics using internal panic...recover.
// It returns error if any exception occurs, or else it returns nil.
func Try(try func()) (err error) {
return gutil.Try(try)
func Try(ctx context.Context, try func(ctx context.Context)) (err error) {
return gutil.Try(ctx, try)
}
// TryCatch implements try...catch... logistics using internal panic...recover.
// It automatically calls function `catch` if any exception occurs ans passes the exception as an error.
func TryCatch(try func(), catch ...func(exception error)) {
gutil.TryCatch(try, catch...)
func TryCatch(ctx context.Context, try func(ctx context.Context), catch ...func(ctx context.Context, exception error)) {
gutil.TryCatch(ctx, try, catch...)
}
// IsNil checks whether given `value` is nil.

View File

@ -16,8 +16,6 @@ import (
"github.com/gogf/gf/v2/internal/consts"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gutil"
)
@ -173,13 +171,5 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode {
if _, v := gutil.MapPossibleItemByKey(nodeMap, "Link"); v != nil {
node.Link = gconv.String(v)
}
// Parse `Link` configuration syntax.
if node.Link != "" && node.Type == "" {
match, _ := gregex.MatchString(`([a-z]+):(.+)`, node.Link)
if len(match) == 3 {
node.Type = gstr.Trim(match[1])
node.Link = gstr.Trim(match[2])
}
}
return node
}

View File

@ -83,6 +83,9 @@ func Server(name ...interface{}) *ghttp.Server {
ctx,
fmt.Sprintf(`%s.%s.%s`, configNodeName, instanceName, consts.ConfigNodeNameLogger),
).Map()
if len(serverLoggerConfigMap) == 0 && len(serverConfigMap) > 0 {
serverLoggerConfigMap = gconv.Map(serverConfigMap[consts.ConfigNodeNameLogger])
}
if len(serverLoggerConfigMap) > 0 {
if err = server.Logger().SetConfigWithMap(serverLoggerConfigMap); err != nil {
panic(err)

View File

@ -79,13 +79,17 @@ func New() *Client {
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
if len(c.header) > 0 {
newClient.header = make(map[string]string)
for k, v := range c.header {
newClient.header[k] = v
}
}
for k, v := range c.cookies {
newClient.cookies[k] = v
if len(c.cookies) > 0 {
newClient.cookies = make(map[string]string)
for k, v := range c.cookies {
newClient.cookies[k] = v
}
}
return newClient
}

View File

@ -12,6 +12,9 @@ import (
// Prefix is a chaining function,
// which sets the URL prefix for next request of this client.
// Eg:
// Prefix("http://127.0.0.1:8199/api/v1")
// Prefix("http://127.0.0.1:8199/api/v2")
func (c *Client) Prefix(prefix string) *Client {
newClient := c.Clone()
newClient.SetPrefix(prefix)

View File

@ -37,10 +37,7 @@ func getResponseBody(res *http.Response) string {
// RawRequest returns the raw content of the request.
func (r *Response) RawRequest() string {
// Response can be nil.
if r == nil {
return ""
}
if r.request == nil {
if r == nil || r.request == nil {
return ""
}
// DumpRequestOut writes more request headers than DumpRequest, such as User-Agent.

View File

@ -0,0 +1,97 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This 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 gclient
import (
"context"
"net/http"
"reflect"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/net/goai"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gmeta"
"github.com/gogf/gf/v2/util/gutil"
)
// DoRequestObj does HTTP request using standard request/response object.
// The request object `req` is defined like:
// type UseCreateReq struct {
// g.Meta `path:"/user" method:"put"`
// // other fields....
// }
// The response object `res` should be a pointer type. It automatically converts result
// to given object `res` is success.
// Eg:
// var (
// req = UseCreateReq{}
// res *UseCreateRes
// )
// DoRequestObj(ctx, req, &res)
func (c *Client) DoRequestObj(ctx context.Context, req, res interface{}) error {
var (
method = gmeta.Get(req, goai.TagNameMethod).String()
path = gmeta.Get(req, goai.TagNamePath).String()
)
if method == "" {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
`no "%s" tag found in request object: %s`,
goai.TagNameMethod, reflect.TypeOf(req).String(),
)
}
if path == "" {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
`no "%s" tag found in request object: %s`,
goai.TagNamePath, reflect.TypeOf(req).String(),
)
}
path = c.handlePathForObjRequest(path, req)
switch gstr.ToUpper(method) {
case
http.MethodGet,
http.MethodPut,
http.MethodPost,
http.MethodDelete,
http.MethodHead,
http.MethodPatch,
http.MethodConnect,
http.MethodOptions,
http.MethodTrace:
if result := c.RequestVar(ctx, method, path, req); res != nil && !result.IsEmpty() {
return result.Scan(res)
}
return nil
default:
return gerror.Newf(`invalid HTTP method "%s"`, method)
}
}
// handlePathForObjRequest replaces parameters in `path` with parameters from request object.
// Eg:
// /order/{id} -> /order/1
// /user/{name} -> /order/john
func (c *Client) handlePathForObjRequest(path string, req interface{}) string {
if gstr.Contains(path, "{") {
requestParamsMap := gconv.Map(req)
if len(requestParamsMap) > 0 {
path, _ = gregex.ReplaceStringFuncMatch(`\{(\w+)\}`, path, func(match []string) string {
foundKey, foundValue := gutil.MapPossibleItemByKey(requestParamsMap, match[1])
if foundKey != "" {
return gconv.String(foundValue)
}
return match[0]
})
}
}
return path
}

View File

@ -7,6 +7,7 @@
package gclient
import (
"bytes"
"io/ioutil"
"net/http"
@ -69,6 +70,13 @@ func (r *Response) ReadAllString() string {
return string(r.ReadAll())
}
// SetBodyContent overwrites response content with custom one.
func (r *Response) SetBodyContent(content []byte) {
buffer := bytes.NewBuffer(content)
r.Body = ioutil.NopCloser(buffer)
r.ContentLength = int64(buffer.Len())
}
// Close closes the response when it will never be used.
func (r *Response) Close() error {
if r == nil || r.Response == nil {

View File

@ -8,16 +8,27 @@ package gclient_test
import (
"context"
"crypto/tls"
"encoding/hex"
"fmt"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/os/gctx"
"net/http"
"time"
"github.com/gogf/gf/v2/debug/gdebug"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/net/gtcp"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
var (
crtFile = gfile.Dir(gdebug.CallerFilePath()) + gfile.Separator + "testdata/server.crt"
keyFile = gfile.Dir(gdebug.CallerFilePath()) + gfile.Separator + "testdata/server.key"
)
func init() {
// Default server for client.
p := 8999
@ -53,10 +64,39 @@ func init() {
)
})
group.HEAD("/", func(r *ghttp.Request) {
r.Response.Write("head")
r.Response.Writef(
"HEAD: form: %d, %s",
r.GetForm("id").Int(),
r.GetForm("name").String(),
)
})
group.PATCH("/", func(r *ghttp.Request) {
r.Response.Writef(
"PATCH: form: %d, %s",
r.GetForm("id").Int(),
r.GetForm("name").String(),
)
})
group.CONNECT("/", func(r *ghttp.Request) {
r.Response.Writef(
"CONNECT: form: %d, %s",
r.GetForm("id").Int(),
r.GetForm("name").String(),
)
})
group.OPTIONS("/", func(r *ghttp.Request) {
r.Response.Write("options")
r.Response.Writef(
"OPTIONS: form: %d, %s",
r.GetForm("id").Int(),
r.GetForm("name").String(),
)
})
group.TRACE("/", func(r *ghttp.Request) {
r.Response.Writef(
"TRACE: form: %d, %s",
r.GetForm("id").Int(),
r.GetForm("name").String(),
)
})
})
// Client chaining operations handlers.
@ -138,6 +178,11 @@ func ExampleClient_Clone() {
// {"id":1,"name":"john"}
}
func fromHex(s string) []byte {
b, _ := hex.DecodeString(s)
return b
}
func ExampleNew_MultiConn_Recommend() {
var (
ctx = gctx.New()
@ -274,6 +319,71 @@ func ExampleClient_PostBytes() {
// POST: form: 10000, john
}
func ExampleClient_DeleteBytes() {
url := "http://127.0.0.1:8999"
fmt.Println(string(g.Client().DeleteBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// DELETE: form: 10000, john
}
func ExampleClient_HeadBytes() {
url := "http://127.0.0.1:8999"
fmt.Println(string(g.Client().HeadBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
}
func ExampleClient_PatchBytes() {
url := "http://127.0.0.1:8999"
fmt.Println(string(g.Client().PatchBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// PATCH: form: 10000, john
}
func ExampleClient_ConnectBytes() {
url := "http://127.0.0.1:8999"
fmt.Println(string(g.Client().ConnectBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// CONNECT: form: 10000, john
}
func ExampleClient_OptionsBytes() {
url := "http://127.0.0.1:8999"
fmt.Println(string(g.Client().OptionsBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// OPTIONS: form: 10000, john
}
func ExampleClient_TraceBytes() {
url := "http://127.0.0.1:8999"
fmt.Println(string(g.Client().TraceBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// TRACE: form: 10000, john
}
func ExampleClient_PostContent() {
url := "http://127.0.0.1:8999"
fmt.Println(g.Client().PostContent(ctx, url, g.Map{
@ -343,6 +453,19 @@ func ExampleClient_Get() {
// GET: query: 10000, john
}
func ExampleClient_Put() {
url := "http://127.0.0.1:8999"
r, _ := g.Client().Put(ctx, url, g.Map{
"id": 10000,
"name": "john",
})
defer r.Close()
fmt.Println(r.ReadAllString())
// Output:
// PUT: form: 10000, john
}
func ExampleClient_GetBytes() {
var (
ctx = context.Background()
@ -357,6 +480,20 @@ func ExampleClient_GetBytes() {
// GET: query: 10000, john
}
func ExampleClient_PutBytes() {
var (
ctx = context.Background()
url = "http://127.0.0.1:8999"
)
fmt.Println(string(g.Client().PutBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// PUT: form: 10000, john
}
func ExampleClient_GetContent() {
url := "http://127.0.0.1:8999"
fmt.Println(g.Client().GetContent(ctx, url, g.Map{
@ -397,45 +534,52 @@ func ExampleClient_SetProxy() {
client := g.Client()
client.SetProxy("http://127.0.0.1:1081")
client.SetTimeout(5 * time.Second) // it's suggested to set http client timeout
response, err := client.Get(ctx, "https://api.ip.sb/ip")
resp, err := client.Get(ctx, "http://127.0.0.1:8999")
if err != nil {
// err is not nil when your proxy server is down.
// eg. Get "https://api.ip.sb/ip": proxyconnect tcp: dial tcp 127.0.0.1:1087: connect: connection refused
fmt.Println(err)
// eg. Get "http://127.0.0.1:8999": proxyconnect tcp: dial tcp 127.0.0.1:1087: connect: connection refused
}
response.RawDump()
fmt.Println(err != nil)
resp.Close()
// connect to a http proxy server which needs auth
client.SetProxy("http://user:password:127.0.0.1:1081")
client.SetTimeout(5 * time.Second) // it's suggested to set http client timeout
response, err = client.Get(ctx, "https://api.ip.sb/ip")
resp, err = client.Get(ctx, "http://127.0.0.1:8999")
if err != nil {
// err is not nil when your proxy server is down.
// eg. Get "https://api.ip.sb/ip": proxyconnect tcp: dial tcp 127.0.0.1:1087: connect: connection refused
fmt.Println(err)
// eg. Get "http://127.0.0.1:8999": proxyconnect tcp: dial tcp 127.0.0.1:1087: connect: connection refused
}
response.RawDump()
fmt.Println(err != nil)
resp.Close()
// connect to a socks5 proxy server
client.SetProxy("socks5://127.0.0.1:1080")
client.SetTimeout(5 * time.Second) // it's suggested to set http client timeout
response, err = client.Get(ctx, "https://api.ip.sb/ip")
resp, err = client.Get(ctx, "http://127.0.0.1:8999")
if err != nil {
// err is not nil when your proxy server is down.
// eg. Get "https://api.ip.sb/ip": socks connect tcp 127.0.0.1:1087->api.ip.sb:443: dial tcp 127.0.0.1:1087: connect: connection refused
fmt.Println(err)
// eg. Get "http://127.0.0.1:8999": socks connect tcp 127.0.0.1:1087->api.ip.sb:443: dial tcp 127.0.0.1:1087: connect: connection refused
}
fmt.Println(response.RawResponse())
fmt.Println(err != nil)
resp.Close()
// connect to a socks5 proxy server which needs auth
client.SetProxy("socks5://user:password@127.0.0.1:1080")
client.SetTimeout(5 * time.Second) // it's suggested to set http client timeout
response, err = client.Get(ctx, "https://api.ip.sb/ip")
resp, err = client.Get(ctx, "http://127.0.0.1:8999")
if err != nil {
// err is not nil when your proxy server is down.
// eg. Get "https://api.ip.sb/ip": socks connect tcp 127.0.0.1:1087->api.ip.sb:443: dial tcp 127.0.0.1:1087: connect: connection refused
fmt.Println(err)
// eg. Get "http://127.0.0.1:8999": socks connect tcp 127.0.0.1:1087->api.ip.sb:443: dial tcp 127.0.0.1:1087: connect: connection refused
}
fmt.Println(response.RawResponse())
fmt.Println(err != nil)
resp.Close()
// Output:
// true
// true
// true
// true
}
// ExampleClientChain_Proxy a chain version of example for `gclient.Client.Proxy` method.
@ -448,20 +592,521 @@ func ExampleClient_Proxy() {
ctx = context.Background()
)
client := g.Client()
response, err := client.Proxy("http://127.0.0.1:1081").Get(ctx, "https://api.ip.sb/ip")
if err != nil {
// err is not nil when your proxy server is down.
// eg. Get "https://api.ip.sb/ip": proxyconnect tcp: dial tcp 127.0.0.1:1087: connect: connection refused
fmt.Println(err)
}
fmt.Println(response.RawResponse())
_, err := client.Proxy("http://127.0.0.1:1081").Get(ctx, "http://127.0.0.1:8999")
fmt.Println(err != nil)
client2 := g.Client()
response, err = client2.Proxy("socks5://127.0.0.1:1080").Get(ctx, "https://api.ip.sb/ip")
if err != nil {
// err is not nil when your proxy server is down.
// eg. Get "https://api.ip.sb/ip": socks connect tcp 127.0.0.1:1087->api.ip.sb:443: dial tcp 127.0.0.1:1087: connect: connection refused
fmt.Println(err)
}
fmt.Println(response.RawResponse())
_, err = client2.Proxy("socks5://127.0.0.1:1080").Get(ctx, "http://127.0.0.1:8999")
fmt.Println(err != nil)
client3 := g.Client()
_, err = client3.Proxy("").Get(ctx, "http://127.0.0.1:8999")
fmt.Println(err != nil)
client4 := g.Client()
url := "http://127.0.0.1:1081" + string([]byte{0x7f})
_, err = client4.Proxy(url).Get(ctx, "http://127.0.0.1:8999")
fmt.Println(err != nil)
// Output:
// true
// true
// false
// false
}
func ExampleClient_Prefix() {
p := gtcp.MustGetFreePort()
var (
ctx = gctx.New()
prefix = fmt.Sprintf("http://127.0.0.1:%d/api/v1/", p)
)
s := g.Server(p)
// HTTP method handlers.
s.Group("/api", func(group *ghttp.RouterGroup) {
group.GET("/v1/prefix", func(r *ghttp.Request) {
r.Response.Write("this is v1 prefix")
})
group.GET("/v1/hello", func(r *ghttp.Request) {
r.Response.Write("this is v1 hello")
})
})
s.SetAccessLogEnabled(false)
s.SetDumpRouterMap(false)
s.SetPort(p)
s.Start()
time.Sleep(time.Millisecond * 100)
// Add Client URI Prefix
client := g.Client().Prefix(prefix)
fmt.Println(string(client.GetBytes(ctx, "prefix")))
fmt.Println(string(client.GetBytes(ctx, "hello")))
// Output:
// this is v1 prefix
// this is v1 hello
}
func ExampleClient_Retry() {
var (
ctx = gctx.New()
url = "http://127.0.0.1:8999"
)
client := g.Client().Retry(2, time.Second)
fmt.Println(string(client.GetBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// GET: query: 10000, john
}
func ExampleClient_RedirectLimit() {
var (
ctx = gctx.New()
url = "http://127.0.0.1:8999"
)
client := g.Client().RedirectLimit(1)
fmt.Println(string(client.GetBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// GET: query: 10000, john
}
func ExampleClient_SetBrowserMode() {
var (
ctx = gctx.New()
url = "http://127.0.0.1:8999"
)
client := g.Client().SetBrowserMode(true)
fmt.Println(string(client.GetBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// GET: query: 10000, john
}
func ExampleClient_SetHeader() {
var (
ctx = gctx.New()
url = "http://127.0.0.1:8999"
)
client := g.Client()
client.SetHeader("Server", "GoFrameServer")
client.SetHeader("Client", "g.Client()")
fmt.Println(string(client.GetBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// GET: query: 10000, john
}
func ExampleClient_SetRedirectLimit() {
go func() {
s := g.Server()
s.BindHandler("/hello", func(r *ghttp.Request) {
r.Response.Writeln("hello world")
})
s.BindHandler("/back", func(r *ghttp.Request) {
r.Response.RedirectBack()
})
s.SetDumpRouterMap(false)
s.SetPort(8199)
s.Run()
}()
time.Sleep(time.Second)
var (
ctx = gctx.New()
urlHello = "http://127.0.0.1:8199/hello"
urlBack = "http://127.0.0.1:8199/back"
)
client := g.Client().SetRedirectLimit(1)
client.SetHeader("Referer", urlHello)
resp, err := client.DoRequest(ctx, http.MethodGet, urlBack, g.Map{
"id": 10000,
"name": "john",
})
if err == nil {
fmt.Println(resp.ReadAllString())
resp.Close()
}
client.SetRedirectLimit(2)
resp, err = client.DoRequest(ctx, http.MethodGet, urlBack, g.Map{
"id": 10000,
"name": "john",
})
if err == nil {
fmt.Println(resp.ReadAllString())
resp.Close()
}
// Output:
// Found
// hello world
}
func ExampleClient_SetTLSKeyCrt() {
var (
ctx = gctx.New()
url = "http://127.0.0.1:8999"
testCrtFile = gfile.Dir(gdebug.CallerFilePath()) + gfile.Separator + "testdata/upload/file1.txt"
testKeyFile = gfile.Dir(gdebug.CallerFilePath()) + gfile.Separator + "testdata/upload/file2.txt"
)
client := g.Client()
client.SetTLSKeyCrt(testCrtFile, testKeyFile)
client.SetTLSKeyCrt(crtFile, keyFile)
fmt.Println(string(client.GetBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// GET: query: 10000, john
}
func ExampleClient_SetTLSConfig() {
var (
ctx = gctx.New()
url = "http://127.0.0.1:8999"
tlsConfig = &tls.Config{}
)
client := g.Client()
client.SetTLSConfig(tlsConfig)
fmt.Println(string(client.GetBytes(ctx, url, g.Map{
"id": 10000,
"name": "john",
})))
// Output:
// GET: query: 10000, john
}
func ExampleClient_PutContent() {
url := "http://127.0.0.1:8999"
fmt.Println(g.Client().PutContent(ctx, url, g.Map{
"id": 10000,
"name": "john",
}))
// Output:
// PUT: form: 10000, john
}
func ExampleClient_DeleteContent() {
url := "http://127.0.0.1:8999"
fmt.Println(g.Client().DeleteContent(ctx, url, g.Map{
"id": 10000,
"name": "john",
}))
// Output:
// DELETE: form: 10000, john
}
func ExampleClient_HeadContent() {
url := "http://127.0.0.1:8999"
fmt.Println(g.Client().HeadContent(ctx, url, g.Map{
"id": 10000,
"name": "john",
}))
// Output:
}
func ExampleClient_PatchContent() {
url := "http://127.0.0.1:8999"
fmt.Println(g.Client().PatchContent(ctx, url, g.Map{
"id": 10000,
"name": "john",
}))
// Output:
// PATCH: form: 10000, john
}
func ExampleClient_ConnectContent() {
url := "http://127.0.0.1:8999"
fmt.Println(g.Client().ConnectContent(ctx, url, g.Map{
"id": 10000,
"name": "john",
}))
// Output:
// CONNECT: form: 10000, john
}
func ExampleClient_OptionsContent() {
url := "http://127.0.0.1:8999"
fmt.Println(g.Client().OptionsContent(ctx, url, g.Map{
"id": 10000,
"name": "john",
}))
// Output:
// OPTIONS: form: 10000, john
}
func ExampleClient_TraceContent() {
url := "http://127.0.0.1:8999"
fmt.Println(g.Client().TraceContent(ctx, url, g.Map{
"id": 10000,
"name": "john",
}))
// Output:
// TRACE: form: 10000, john
}
func ExampleClient_RequestContent() {
url := "http://127.0.0.1:8999"
fmt.Println(g.Client().RequestContent(ctx, http.MethodGet, url, g.Map{
"id": 10000,
"name": "john",
}))
// Output:
// GET: query: 10000, john
}
func ExampleClient_RawRequest() {
url := "http://127.0.0.1:8999"
response, _ := g.Client().Get(ctx, url, g.Map{
"id": 10000,
"name": "john",
})
fmt.Println(len(response.RawResponse()) > 100)
// Output:
// true
}
func ExampleClient_Delete() {
url := "http://127.0.0.1:8999"
r, _ := g.Client().Delete(ctx, url, g.Map{
"id": 10000,
"name": "john",
})
defer r.Close()
fmt.Println(r.ReadAllString())
// Output:
// DELETE: form: 10000, john
}
func ExampleClient_Head() {
url := "http://127.0.0.1:8999"
r, _ := g.Client().Head(ctx, url, g.Map{
"id": 10000,
"name": "john",
})
defer r.Close()
fmt.Println(r.ReadAllString())
// Output:
//
}
func ExampleClient_Patch() {
url := "http://127.0.0.1:8999"
r, _ := g.Client().Patch(ctx, url, g.Map{
"id": 10000,
"name": "john",
})
defer r.Close()
fmt.Println(r.ReadAllString())
// Output:
// PATCH: form: 10000, john
}
func ExampleClient_Connect() {
url := "http://127.0.0.1:8999"
r, _ := g.Client().Connect(ctx, url, g.Map{
"id": 10000,
"name": "john",
})
defer r.Close()
fmt.Println(r.ReadAllString())
// Output:
// CONNECT: form: 10000, john
}
func ExampleClient_Options() {
url := "http://127.0.0.1:8999"
r, _ := g.Client().Options(ctx, url, g.Map{
"id": 10000,
"name": "john",
})
defer r.Close()
fmt.Println(r.ReadAllString())
// Output:
// OPTIONS: form: 10000, john
}
func ExampleClient_Trace() {
url := "http://127.0.0.1:8999"
r, _ := g.Client().Trace(ctx, url, g.Map{
"id": 10000,
"name": "john",
})
defer r.Close()
fmt.Println(r.ReadAllString())
// Output:
// TRACE: form: 10000, john
}
func ExampleClient_PutVar() {
type User struct {
Id int
Name string
}
var (
user *User
ctx = context.Background()
url = "http://127.0.0.1:8999/var/json"
)
err := g.Client().PutVar(ctx, url).Scan(&user)
if err != nil {
panic(err)
}
fmt.Println(user)
// Output:
// &{1 john}
}
func ExampleClient_DeleteVar() {
type User struct {
Id int
Name string
}
var (
users []User
url = "http://127.0.0.1:8999/var/jsons"
)
err := g.Client().DeleteVar(ctx, url).Scan(&users)
if err != nil {
panic(err)
}
fmt.Println(users)
// Output:
// [{1 john} {2 smith}]
}
func ExampleClient_HeadVar() {
type User struct {
Id int
Name string
}
var (
users []User
url = "http://127.0.0.1:8999/var/jsons"
)
err := g.Client().HeadVar(ctx, url).Scan(&users)
if err != nil {
panic(err)
}
fmt.Println(users)
// Output:
// []
}
func ExampleClient_PatchVar() {
type User struct {
Id int
Name string
}
var (
users []User
url = "http://127.0.0.1:8999/var/jsons"
)
err := g.Client().PatchVar(ctx, url).Scan(&users)
if err != nil {
panic(err)
}
fmt.Println(users)
// Output:
// [{1 john} {2 smith}]
}
func ExampleClient_ConnectVar() {
type User struct {
Id int
Name string
}
var (
users []User
url = "http://127.0.0.1:8999/var/jsons"
)
err := g.Client().ConnectVar(ctx, url).Scan(&users)
if err != nil {
panic(err)
}
fmt.Println(users)
// Output:
// [{1 john} {2 smith}]
}
func ExampleClient_OptionsVar() {
type User struct {
Id int
Name string
}
var (
users []User
url = "http://127.0.0.1:8999/var/jsons"
)
err := g.Client().OptionsVar(ctx, url).Scan(&users)
if err != nil {
panic(err)
}
fmt.Println(users)
// Output:
// [{1 john} {2 smith}]
}
func ExampleClient_TraceVar() {
type User struct {
Id int
Name string
}
var (
users []User
url = "http://127.0.0.1:8999/var/jsons"
)
err := g.Client().TraceVar(ctx, url).Scan(&users)
if err != nil {
panic(err)
}
fmt.Println(users)
// Output:
// [{1 john} {2 smith}]
}

View File

@ -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 gclient_test
import (
"context"
"fmt"
"net/http"
"testing"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/internal/tracing"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/net/gtcp"
"github.com/gogf/gf/v2/test/gtest"
"go.opentelemetry.io/otel"
sdkTrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
)
type CustomProvider struct {
*sdkTrace.TracerProvider
}
func NewCustomProvider() *CustomProvider {
return &CustomProvider{
TracerProvider: sdkTrace.NewTracerProvider(
sdkTrace.WithIDGenerator(NewCustomIDGenerator()),
),
}
}
type CustomIDGenerator struct{}
func NewCustomIDGenerator() *CustomIDGenerator {
return &CustomIDGenerator{}
}
func (id *CustomIDGenerator) NewIDs(ctx context.Context) (traceID trace.TraceID, spanID trace.SpanID) {
return tracing.NewIDs()
}
func (id *CustomIDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) (spanID trace.SpanID) {
return tracing.NewSpanID()
}
func TestClient_CustomProvider(t *testing.T) {
provider := otel.GetTracerProvider()
defer otel.SetTracerProvider(provider)
otel.SetTracerProvider(NewCustomProvider())
p, _ := gtcp.GetFreePort()
s := g.Server(p)
s.BindHandler("/hello", func(r *ghttp.Request) {
r.Response.WriteHeader(200)
r.Response.WriteJson(g.Map{"field": "test_for_response_body"})
})
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()
url := fmt.Sprintf("127.0.0.1:%d/hello", p)
resp, err := c.DoRequest(ctx, http.MethodGet, url)
t.AssertNil(err)
t.AssertNE(resp, nil)
t.Assert(resp.ReadAllString(), "{\"field\":\"test_for_response_body\"}")
})
}

View File

@ -0,0 +1,81 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This 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 gclient_test
import (
"fmt"
"testing"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/net/gtcp"
"github.com/gogf/gf/v2/test/gtest"
)
func Test_Client_DoRequestObj(t *testing.T) {
type UserCreateReq struct {
g.Meta `path:"/user" method:"post"`
Id int
Name string
}
type UserCreateRes struct {
Id int
}
type UserQueryReq struct {
g.Meta `path:"/user/{id}" method:"get"`
Id int
}
type UserQueryRes struct {
Id int
Name string
}
p, _ := gtcp.GetFreePort()
s := g.Server(p)
s.Group("/user", func(group *ghttp.RouterGroup) {
group.GET("/{id}", func(r *ghttp.Request) {
r.Response.WriteJson(g.Map{"id": r.Get("id").Int(), "name": "john"})
})
group.POST("/", func(r *ghttp.Request) {
r.Response.WriteJson(g.Map{"id": r.Get("Id")})
})
})
s.SetPort(p)
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
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()
var (
createRes *UserCreateRes
createReq = UserCreateReq{
Id: 1,
Name: "john",
}
)
err := client.DoRequestObj(ctx, createReq, &createRes)
t.AssertNil(err)
t.Assert(createRes.Id, 1)
})
gtest.C(t, func(t *gtest.T) {
url := fmt.Sprintf("http://127.0.0.1:%d", p)
client := g.Client().SetPrefix(url).ContentJson()
var (
queryRes *UserQueryRes
queryReq = UserQueryReq{
Id: 1,
}
)
err := client.DoRequestObj(ctx, queryReq, &queryRes)
t.AssertNil(err)
t.Assert(queryRes.Id, 1)
t.Assert(queryRes.Name, "john")
})
}

View File

@ -11,12 +11,13 @@ import (
"context"
"crypto/tls"
"fmt"
"github.com/gogf/gf/v2/debug/gdebug"
"io/ioutil"
"net/http"
"testing"
"time"
"github.com/gogf/gf/v2/debug/gdebug"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gclient"
@ -488,6 +489,28 @@ 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)
r2 = nil
t.Assert(r2.RawRequest(), "")
})
gtest.C(t, func(t *gtest.T) {
url := fmt.Sprintf("http://127.0.0.1:%d", p)
response, _ := g.Client().Get(ctx, url, g.Map{
"id": 10000,
"name": "john",
})
response = nil
t.Assert(response.RawRequest(), "")
})
gtest.C(t, func(t *gtest.T) {
url := fmt.Sprintf("http://127.0.0.1:%d", p)
response, _ := g.Client().Get(ctx, url, g.Map{
"id": 10000,
"name": "john",
})
response.RawDump()
t.AssertGT(len(response.Raw()), 0)
})
}
@ -558,3 +581,86 @@ func TestLoadKeyCrt(t *testing.T) {
t.AssertNE(tlsConfig, nil)
})
}
func TestClient_DoRequest(t *testing.T) {
p, _ := gtcp.GetFreePort()
s := g.Server(p)
s.BindHandler("/hello", func(r *ghttp.Request) {
r.Response.WriteHeader(200)
r.Response.WriteJson(g.Map{"field": "test_for_response_body"})
})
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()
url := fmt.Sprintf("127.0.0.1:%d/hello", p)
resp, err := c.DoRequest(ctx, http.MethodGet, url)
t.AssertNil(err)
t.AssertNE(resp, nil)
t.Assert(resp.ReadAllString(), "{\"field\":\"test_for_response_body\"}")
resp.Response = nil
bytes := resp.ReadAll()
t.Assert(bytes, []byte{})
resp.Close()
})
gtest.C(t, func(t *gtest.T) {
c := g.Client()
url := "127.0.0.1:99999/hello"
resp, err := c.DoRequest(ctx, http.MethodGet, url)
t.AssertNil(resp.Response)
t.AssertNE(err, nil)
})
}
func TestClient_RequestVar(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
url = "http://127.0.0.1:99999/var/jsons"
)
varValue := g.Client().RequestVar(ctx, http.MethodGet, url)
t.AssertNil(varValue)
})
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Name string
}
var (
users []User
url = "http://127.0.0.1:8999/var/jsons"
)
err := g.Client().RequestVar(ctx, http.MethodGet, url).Scan(&users)
t.AssertNil(err)
t.AssertNE(users, nil)
})
}
func TestClient_SetBodyContent(t *testing.T) {
p, _ := gtcp.GetFreePort()
s := g.Server(p)
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("hello")
})
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))
res, err := c.Get(ctx, "/")
t.AssertNil(err)
defer res.Close()
t.Assert(res.ReadAllString(), "hello")
res.SetBodyContent([]byte("world"))
t.Assert(res.ReadAllString(), "world")
})
}

View File

@ -117,21 +117,22 @@ const (
)
const (
supportedHttpMethods = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
defaultMethod = "ALL"
exceptionExit = "exit"
exceptionExitAll = "exit_all"
exceptionExitHook = "exit_hook"
routeCacheDuration = time.Hour
ctxKeyForRequest = "gHttpRequestObject"
contentTypeXml = "text/xml"
contentTypeHtml = "text/html"
contentTypeJson = "application/json"
swaggerUIPackedPath = "/goframe/swaggerui"
responseTraceIDHeader = "Trace-ID"
specialMethodNameInit = "Init"
specialMethodNameShut = "Shut"
specialMethodNameIndex = "Index"
supportedHttpMethods = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
defaultMethod = "ALL"
exceptionExit = "exit"
exceptionExitAll = "exit_all"
exceptionExitHook = "exit_hook"
routeCacheDuration = time.Hour
ctxKeyForRequest = "gHttpRequestObject"
contentTypeXml = "text/xml"
contentTypeHtml = "text/html"
contentTypeJson = "application/json"
swaggerUIPackedPath = "/goframe/swaggerui"
responseTraceIDHeader = "Trace-ID"
specialMethodNameInit = "Init"
specialMethodNameShut = "Shut"
specialMethodNameIndex = "Index"
gracefulShutdownTimeout = 5 * time.Second
)
var (

View File

@ -11,7 +11,6 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/intlog"
)
// DefaultHandlerResponse is the default implementation of HandlerResponse.
@ -32,7 +31,6 @@ func MiddlewareHandlerResponse(r *Request) {
var (
msg string
ctx = r.Context()
err = r.GetError()
res = r.GetHandlerResponse()
code = gerror.Code(err)
@ -55,12 +53,9 @@ func MiddlewareHandlerResponse(r *Request) {
} else {
code = gcode.CodeOK
}
internalErr := r.Response.WriteJson(DefaultHandlerResponse{
r.Response.WriteJson(DefaultHandlerResponse{
Code: code.Code(),
Message: msg,
Data: res,
})
if internalErr != nil {
intlog.Errorf(ctx, `%+v`, internalErr)
}
}

View File

@ -7,6 +7,7 @@
package ghttp
import (
"context"
"net/http"
"reflect"
@ -44,8 +45,8 @@ func (m *middleware) Next() {
// Router values switching.
m.request.routerMap = item.Values
gutil.TryCatch(func() {
ctx := m.request.context
gutil.TryCatch(ctx, func(ctx context.Context) {
// 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]
@ -98,7 +99,7 @@ func (m *middleware) Next() {
// There should be a "Next" function to be called in the middleware in order to manage the workflow.
loop = false
}
}, func(exception error) {
}, func(ctx context.Context, exception error) {
if v, ok := exception.(error); ok && gerror.HasStack(v) {
// It's already an error that has stack info.
m.request.error = v

View File

@ -12,6 +12,7 @@ import (
"net/http"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/util/gconv"
)
@ -102,48 +103,44 @@ func (r *Response) WriteflnExit(format string, params ...interface{}) {
}
// WriteJson writes `content` to the response with JSON format.
func (r *Response) WriteJson(content interface{}) error {
func (r *Response) WriteJson(content interface{}) {
r.Header().Set("Content-Type", contentTypeJson)
// If given string/[]byte, response it directly to the client.
switch content.(type) {
case string, []byte:
r.Write(gconv.String(content))
return nil
return
}
// Else use json.Marshal function to encode the parameter.
if b, err := json.Marshal(content); err != nil {
return err
panic(gerror.Wrap(err, `WriteJson failed`))
} else {
r.Write(b)
}
return nil
}
// WriteJsonExit writes `content` to the response with JSON format and exits executing
// of current handler if success. The "Exit" feature is commonly used to replace usage of
// return statements in the handler, for convenience.
func (r *Response) WriteJsonExit(content interface{}) error {
if err := r.WriteJson(content); err != nil {
return err
}
func (r *Response) WriteJsonExit(content interface{}) {
r.WriteJson(content)
r.Request.Exit()
return nil
}
// WriteJsonP writes `content` to the response with JSONP format.
//
// Note that there should be a "callback" parameter in the request for JSONP format.
func (r *Response) WriteJsonP(content interface{}) error {
func (r *Response) WriteJsonP(content interface{}) {
r.Header().Set("Content-Type", contentTypeJson)
// If given string/[]byte, response it directly to client.
switch content.(type) {
case string, []byte:
r.Write(gconv.String(content))
return nil
return
}
// Else use json.Marshal function to encode the parameter.
if b, err := json.Marshal(content); err != nil {
return err
panic(gerror.Wrap(err, `WriteJsonP failed`))
} else {
// r.Header().Set("Content-Type", "application/json")
if callback := r.Request.Get("callback").String(); callback != "" {
@ -156,7 +153,6 @@ func (r *Response) WriteJsonP(content interface{}) error {
r.Write(b)
}
}
return nil
}
// WriteJsonPExit writes `content` to the response with JSONP format and exits executing
@ -164,40 +160,33 @@ func (r *Response) WriteJsonP(content interface{}) error {
// return statements in the handler, for convenience.
//
// Note that there should be a "callback" parameter in the request for JSONP format.
func (r *Response) WriteJsonPExit(content interface{}) error {
if err := r.WriteJsonP(content); err != nil {
return err
}
func (r *Response) WriteJsonPExit(content interface{}) {
r.WriteJsonP(content)
r.Request.Exit()
return nil
}
// WriteXml writes `content` to the response with XML format.
func (r *Response) WriteXml(content interface{}, rootTag ...string) error {
func (r *Response) WriteXml(content interface{}, rootTag ...string) {
r.Header().Set("Content-Type", contentTypeXml)
// If given string/[]byte, response it directly to clients.
switch content.(type) {
case string, []byte:
r.Write(gconv.String(content))
return nil
return
}
if b, err := gjson.New(content).ToXml(rootTag...); err != nil {
return err
panic(gerror.Wrap(err, `WriteXml failed`))
} else {
r.Write(b)
}
return nil
}
// WriteXmlExit writes `content` to the response with XML format and exits executing
// of current handler if success. The "Exit" feature is commonly used to replace usage
// of return statements in the handler, for convenience.
func (r *Response) WriteXmlExit(content interface{}, rootTag ...string) error {
if err := r.WriteXml(content, rootTag...); err != nil {
return err
}
func (r *Response) WriteXmlExit(content interface{}, rootTag ...string) {
r.WriteXml(content, rootTag...)
r.Request.Exit()
return nil
}
// WriteStatus writes HTTP `status` and `content` to the response.

View File

@ -144,7 +144,7 @@ func forkReloadProcess(ctx context.Context, newExeFilePath ...string) error {
}
buffer, _ := gjson.Encode(sfm)
p.Env = append(p.Env, adminActionReloadEnvKey+"="+string(buffer))
if _, err := p.Start(); err != nil {
if _, err := p.Start(ctx); err != nil {
glog.Errorf(
ctx,
"%d: fork process failed, error:%s, %s",
@ -169,7 +169,7 @@ func forkRestartProcess(ctx context.Context, newExeFilePath ...string) error {
env := os.Environ()
env = append(env, adminActionRestartEnvKey+"=1")
p := gproc.NewProcess(path, os.Args, env)
if _, err := p.Start(); err != nil {
if _, err := p.Start(ctx); err != nil {
glog.Errorf(
ctx,
`%d: fork process failed, error:%s, are you running using "go run"?`,

View File

@ -215,7 +215,9 @@ func (s *gracefulServer) shutdown(ctx context.Context) {
if s.status == ServerStatusStopped {
return
}
if err := s.httpServer.Shutdown(context.Background()); err != nil {
timeoutCtx, cancelFunc := context.WithTimeout(ctx, gracefulShutdownTimeout)
defer cancelFunc()
if err := s.httpServer.Shutdown(timeoutCtx); err != nil {
s.server.Logger().Errorf(
ctx,
"%d: %s server [%s] shutdown error: %v",

View File

@ -9,7 +9,6 @@ package ghttp
import (
"context"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/net/goai"
"github.com/gogf/gf/v2/text/gstr"
)
@ -48,16 +47,9 @@ func (s *Server) initOpenApi() {
// openapiSpec is a build-in handler automatic producing for openapi specification json file.
func (s *Server) openapiSpec(r *Request) {
var (
err error
)
if s.config.OpenApiPath == "" {
r.Response.Write(`OpenApi specification file producing is disabled`)
} else {
err = r.Response.WriteJson(s.openapi)
}
if err != nil {
intlog.Errorf(r.Context(), `%+v`, err)
r.Response.WriteJson(s.openapi)
}
}

View File

@ -81,9 +81,9 @@ func watchAndUpdateService(watcher Watcher, service Service, watchFunc ServiceWa
if len(services) > 0 {
watchedServiceMap.Set(service.GetName(), services[0])
if watchFunc != nil {
gutil.TryCatch(func() {
gutil.TryCatch(ctx, func(ctx context.Context) {
watchFunc(services[0])
}, func(exception error) {
}, func(ctx context.Context, exception error) {
intlog.Errorf(ctx, `%+v`, exception)
})
}

View File

@ -196,7 +196,13 @@ func GetFreePort() (port int, err error) {
)
}
port = l.Addr().(*net.TCPAddr).Port
err = l.Close()
if err = l.Close(); err != nil {
err = gerror.Wrapf(
err,
`close listening failed for network "%s", address "%s", port "%d"`,
network, resolvedAddr.String(), port,
)
}
return
}

View File

@ -12,6 +12,8 @@ import (
"os"
"strings"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/propagation"
@ -145,7 +147,11 @@ func GetBaggageVar(ctx context.Context, key string) *gvar.Var {
}
// WithTraceID injects custom trace id into context to propagate.
func WithTraceID(ctx context.Context, traceID trace.TraceID) context.Context {
func WithTraceID(ctx context.Context, traceID string) (context.Context, error) {
generatedTraceID, err := trace.TraceIDFromHex(traceID)
if err != nil {
return nil, gerror.WrapCodef(gcode.CodeInvalidParameter, err, `invalid traceID: %s`, traceID)
}
sc := trace.SpanContextFromContext(ctx)
if !sc.HasTraceID() {
var (
@ -156,11 +162,11 @@ func WithTraceID(ctx context.Context, traceID trace.TraceID) context.Context {
sc = trace.SpanContextFromContext(ctx)
}
ctx = trace.ContextWithRemoteSpanContext(ctx, trace.NewSpanContext(trace.SpanContextConfig{
TraceID: traceID,
TraceID: generatedTraceID,
SpanID: sc.SpanID(),
TraceFlags: sc.TraceFlags(),
TraceState: sc.TraceState(),
Remote: sc.IsRemote(),
}))
return ctx
return ctx, nil
}

View File

@ -61,7 +61,8 @@ func Test_WithTraceID(t *testing.T) {
time.Sleep(100 * time.Millisecond)
ctx := gtrace.WithTraceID(context.TODO(), traceID)
ctx, err := gtrace.WithTraceID(context.TODO(), traceID.String())
t.AssertNil(err)
prefix := fmt.Sprintf("http://127.0.0.1:%d", p)
client := g.Client()

View File

@ -18,6 +18,27 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
var (
simpleTimeout = time.Millisecond * 100
sendData = []byte("hello")
invalidAddr = "127.0.0.1:99999"
)
func startUDPServer(addr string) {
s := gudp.NewServer(addr, func(conn *gudp.Conn) {
defer conn.Close()
for {
data, err := conn.Recv(-1)
if err != nil {
break
}
conn.Send(data)
}
})
go s.Run()
time.Sleep(simpleTimeout)
}
func Test_Basic(t *testing.T) {
var (
ctx = context.TODO()
@ -46,6 +67,11 @@ func Test_Basic(t *testing.T) {
conn, err := gudp.NewConn(fmt.Sprintf("127.0.0.1:%d", p))
t.AssertNil(err)
t.Assert(conn.Send([]byte(gconv.String(i))), nil)
t.AssertNil(conn.RemoteAddr())
result, err := conn.Recv(-1)
t.AssertNil(err)
t.AssertNE(conn.RemoteAddr(), nil)
t.Assert(string(result), fmt.Sprintf(`> %d`, i))
conn.Close()
}
})
@ -60,6 +86,41 @@ func Test_Basic(t *testing.T) {
conn.Close()
}
})
// gudp.Conn.SendWithTimeout
gtest.C(t, func(t *gtest.T) {
for i := 0; i < 100; i++ {
conn, err := gudp.NewConn(fmt.Sprintf("127.0.0.1:%d", p))
t.AssertNil(err)
err = conn.SendWithTimeout([]byte(gconv.String(i)), time.Second)
t.AssertNil(err)
conn.Close()
}
})
// gudp.Conn.RecvWithTimeout
gtest.C(t, func(t *gtest.T) {
for i := 0; i < 100; i++ {
conn, err := gudp.NewConn(fmt.Sprintf("127.0.0.1:%d", p))
t.AssertNil(err)
err = conn.Send([]byte(gconv.String(i)))
t.AssertNil(err)
conn.SetRecvBufferWait(time.Millisecond * 100)
result, err := conn.RecvWithTimeout(-1, time.Second)
t.AssertNil(err)
t.Assert(string(result), fmt.Sprintf(`> %d`, i))
conn.Close()
}
})
// gudp.Conn.SendRecvWithTimeout
gtest.C(t, func(t *gtest.T) {
for i := 0; i < 100; i++ {
conn, err := gudp.NewConn(fmt.Sprintf("127.0.0.1:%d", p))
t.AssertNil(err)
result, err := conn.SendRecvWithTimeout([]byte(gconv.String(i)), -1, time.Second)
t.AssertNil(err)
t.Assert(string(result), fmt.Sprintf(`> %d`, i))
conn.Close()
}
})
// gudp.Send
gtest.C(t, func(t *gtest.T) {
for i := 0; i < 100; i++ {
@ -112,3 +173,69 @@ func Test_Buffer(t *testing.T) {
t.Assert(string(result), "4")
})
}
func Test_NewConn(t *testing.T) {
var (
port, _ = gudp.GetFreePort()
)
startUDPServer(fmt.Sprintf("127.0.0.1:%d", port))
gtest.C(t, func(t *gtest.T) {
conn, err := gudp.NewConn(fmt.Sprintf("127.0.0.1:%d", port), fmt.Sprintf("127.0.0.1:%d", port+1))
t.AssertNil(err)
conn.SetDeadline(time.Now().Add(time.Second))
t.Assert(conn.Send(sendData), nil)
conn.Close()
})
gtest.C(t, func(t *gtest.T) {
conn, err := gudp.NewConn(fmt.Sprintf("127.0.0.1:%d", port), fmt.Sprintf("127.0.0.1:%d", 99999))
t.AssertNil(conn)
t.AssertNE(err, nil)
})
gtest.C(t, func(t *gtest.T) {
conn, err := gudp.NewConn(fmt.Sprintf("127.0.0.1:%d", 99999))
t.AssertNil(conn)
t.AssertNE(err, nil)
})
}
func Test_GetFreePorts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
ports, err := gudp.GetFreePorts(2)
t.AssertNil(err)
t.AssertEQ(len(ports), 2)
})
}
func Test_Server(t *testing.T) {
p, _ := gudp.GetFreePort()
gudp.NewServer(fmt.Sprintf("127.0.0.1:%d", p), func(conn *gudp.Conn) {
defer conn.Close()
for {
data, err := conn.Recv(1)
if len(data) > 0 {
conn.Send(data)
}
if err != nil {
break
}
}
}, "GoFrameUDPServer")
gtest.C(t, func(t *gtest.T) {
server := gudp.GetServer("GoFrameUDPServer")
t.AssertNE(server, nil)
server = gudp.GetServer("TestUDPServer")
t.AssertNE(server, nil)
server.SetAddress("127.0.0.1:8888")
server.SetHandler(func(conn *gudp.Conn) {
defer conn.Close()
for {
conn.Send([]byte("OtherHandle"))
}
})
})
}

View File

@ -24,9 +24,10 @@ const (
)
const (
helpOptionName = "help"
helpOptionNameShort = "h"
maxLineChars = 120
helpOptionName = "help"
helpOptionNameShort = "h"
maxLineChars = 120
tracingInstrumentName = "github.com/gogf/gf/v2/os/gcmd.Command"
)
// Init does custom initialization.

View File

@ -11,12 +11,19 @@ import (
"bytes"
"context"
"fmt"
"io"
"os"
"github.com/gogf/gf/v2/text/gstr"
)
// Print prints help info to stdout for current command.
func (c *Command) Print() {
c.PrintTo(os.Stdout)
}
// PrintTo prints help info to custom io.Writer.
func (c *Command) PrintTo(writer io.Writer) {
var (
prefix = gstr.Repeat(" ", 4)
buffer = bytes.NewBuffer(nil)
@ -191,7 +198,7 @@ func (c *Command) Print() {
}
content := buffer.String()
content = gstr.Replace(content, "\t", " ")
fmt.Println(content)
_, _ = writer.Write([]byte(content))
}
type printLineBriefInput struct {

View File

@ -8,16 +8,24 @@
package gcmd
import (
"bytes"
"context"
"fmt"
"os"
"github.com/gogf/gf/v2"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/net/gtrace"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/gogf/gf/v2/os/genv"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gutil"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
)
// Run calls custom function that bound to this command.
@ -34,18 +42,23 @@ func (c *Command) RunWithValue(ctx context.Context) (value interface{}) {
var (
code = gerror.Code(err)
detail = code.Detail()
buffer = bytes.NewBuffer(nil)
)
if code.Code() == gcode.CodeNotFound.Code() {
fmt.Printf("ERROR: %s\n", gstr.Trim(err.Error()))
buffer.WriteString(fmt.Sprintf("ERROR: %s\n", gstr.Trim(err.Error())))
if lastCmd, ok := detail.(*Command); ok {
lastCmd.Print()
lastCmd.PrintTo(buffer)
} else {
c.Print()
c.PrintTo(buffer)
}
} else {
fmt.Printf("%+v\n", err)
buffer.WriteString(fmt.Sprintf("%+v\n", err))
}
os.Exit(1)
if gtrace.GetTraceID(ctx) == "" {
fmt.Println(buffer.String())
os.Exit(1)
}
glog.Stack(false).Fatal(ctx, buffer.String())
}
return value
}
@ -107,6 +120,24 @@ func (c *Command) doRun(ctx context.Context, parser *Parser) (value interface{},
}
return nil, c.defaultHelpFunc(ctx, parser)
}
// OpenTelemetry for command.
var (
span trace.Span
tr = otel.GetTracerProvider().Tracer(
tracingInstrumentName,
trace.WithInstrumentationVersion(gf.VERSION),
)
)
ctx, span = tr.Start(
otel.GetTextMapPropagator().Extract(
ctx,
propagation.MapCarrier(genv.Map()),
),
gstr.Join(os.Args, " "),
trace.WithSpanKind(trace.SpanKindServer),
)
defer span.End()
span.SetAttributes(gtrace.CommonLabels()...)
// Reparse the arguments for current command configuration.
parser, err = c.reParse(ctx, parser)
if err != nil {
@ -126,7 +157,7 @@ func (c *Command) doRun(ctx context.Context, parser *Parser) (value interface{},
return nil, c.defaultHelpFunc(ctx, parser)
}
// reParse re-parses the arguments using option configuration of current command.
// reParse parses the arguments using option configuration of current command.
func (c *Command) reParse(ctx context.Context, parser *Parser) (*Parser, error) {
if len(c.Arguments) == 0 {
return parser, nil

View File

@ -9,8 +9,12 @@ package gctx
import (
"context"
"os"
"strings"
"github.com/gogf/gf/v2/net/gtrace"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
)
type (
@ -18,13 +22,36 @@ type (
StrKey string // StrKey is a type for warps basic type string as context key.
)
var (
// processCtx is the context initialized from process environment.
processCtx context.Context
)
func init() {
// All environment key-value pairs.
m := make(map[string]string)
i := 0
for _, s := range os.Environ() {
i = strings.IndexByte(s, '=')
m[s[0:i]] = s[i+1:]
}
// OpenTelemetry from environments.
processCtx = otel.GetTextMapPropagator().Extract(
context.Background(),
propagation.MapCarrier(m),
)
}
// New creates and returns a context which contains context id.
func New() context.Context {
return WithCtx(context.Background())
return WithCtx(processCtx)
}
// WithCtx creates and returns a context containing context id upon given parent context `ctx`.
func WithCtx(ctx context.Context) context.Context {
if CtxId(ctx) != "" {
return ctx
}
if gtrace.IsUsingDefaultProvider() {
var span *gtrace.Span
ctx, span = gtrace.NewSpan(ctx, "gctx.WithCtx")

View File

@ -8,6 +8,7 @@
package genv
import (
"fmt"
"os"
"strings"
@ -25,13 +26,7 @@ func All() []string {
// Map returns a copy of strings representing the environment as a map.
func Map() map[string]string {
m := make(map[string]string)
i := 0
for _, s := range os.Environ() {
i = strings.IndexByte(s, '=')
m[s[0:i]] = s[i+1:]
}
return m
return MapFromEnv(os.Environ())
}
// Get creates and returns a Var with the value of the environment variable
@ -117,3 +112,28 @@ func Build(m map[string]string) []string {
}
return array
}
// MapFromEnv converts environment variables from slice to map.
func MapFromEnv(envs []string) map[string]string {
m := make(map[string]string)
i := 0
for _, s := range envs {
i = strings.IndexByte(s, '=')
m[s[0:i]] = s[i+1:]
}
return m
}
// MapToEnv converts environment variables from map to slice.
func MapToEnv(m map[string]string) []string {
envs := make([]string, 0)
for k, v := range m {
envs = append(envs, fmt.Sprintf(`%s=%s`, k, v))
}
return envs
}
// Filter filters repeated items from given environment variables.
func Filter(envs []string) []string {
return MapToEnv(MapFromEnv(envs))
}

View File

@ -121,3 +121,24 @@ func Test_GetWithCmd(t *testing.T) {
t.Assert(genv.GetWithCmd("test"), 1)
})
}
func Test_MapFromEnv(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := genv.MapFromEnv([]string{"a=1", "b=2"})
t.Assert(m, g.Map{"a": 1, "b": 2})
})
}
func Test_MapToEnv(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := genv.MapToEnv(g.MapStrStr{"a": "1"})
t.Assert(s, []string{"a=1"})
})
}
func Test_Filter(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := genv.Filter([]string{"a=1", "a=3"})
t.Assert(s, []string{"a=3"})
})
}

View File

@ -10,6 +10,8 @@ import (
"context"
"time"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/command"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/os/gcache"
@ -17,22 +19,28 @@ import (
)
const (
defaultCacheExpire = "1m" // defaultCacheExpire is the expire time for file content caching in seconds.
defaultCacheDuration = "1m" // 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 = getCacheExpire()
cacheDuration = getCacheDuration()
// internalCache is the memory cache for internal usage.
internalCache = gcache.New()
)
func getCacheExpire() time.Duration {
d, err := time.ParseDuration(command.GetOptWithEnv(commandEnvKeyForCache, defaultCacheExpire))
func getCacheDuration() time.Duration {
cacheDurationConfigured := command.GetOptWithEnv(commandEnvKeyForCache, defaultCacheDuration)
d, err := time.ParseDuration(cacheDurationConfigured)
if err != nil {
panic(err)
panic(gerror.WrapCodef(
gcode.CodeInvalidConfiguration,
err,
`error parsing string "%s" to time duration`,
cacheDurationConfigured,
))
}
return d
}
@ -50,7 +58,7 @@ func GetContentsWithCache(path string, duration ...time.Duration) string {
func GetBytesWithCache(path string, duration ...time.Duration) []byte {
var (
ctx = context.Background()
expire = cacheExpire
expire = cacheDuration
cacheKey = commandEnvKeyForCache + path
)

View File

@ -39,6 +39,23 @@ func Open(path string, flag int, perm os.FileMode, ttl ...time.Duration) (file *
return pool.File()
}
// Get returns a file item with given file path, flag and opening permission.
// It retrieves a file item from the file pointer pool after then.
func Get(path string, flag int, perm os.FileMode, ttl ...time.Duration) (file *File) {
var fpTTL time.Duration
if len(ttl) > 0 {
fpTTL = ttl[0]
}
f, found := pools.Search(fmt.Sprintf("%s&%d&%d&%d", path, flag, fpTTL, perm))
if !found {
return nil
}
fp, _ := f.(*Pool).pool.Get()
return fp.(*File)
}
// Stat returns the FileInfo structure describing file.
func (f *File) Stat() (os.FileInfo, error) {
if f.stat == nil {
@ -48,7 +65,11 @@ func (f *File) Stat() (os.FileInfo, error) {
}
// Close puts the file pointer back to the file pointer pool.
func (f *File) Close() error {
func (f *File) Close(close ...bool) error {
if len(close) > 0 && close[0] {
f.File.Close()
}
if f.pid == f.pool.id.Val() {
return f.pool.pool.Put(f)
}

View File

@ -120,7 +120,7 @@ func (w *Watcher) eventLoop() {
}
// Calling the callbacks in order.
for _, v := range callbacks {
for _, callback := range callbacks {
go func(callback *Callback) {
defer func() {
if err := recover(); err != nil {
@ -133,7 +133,7 @@ func (w *Watcher) eventLoop() {
}
}()
callback.Func(event)
}(v)
}(callback)
}
} else {
break

View File

@ -8,8 +8,9 @@
package glog
import (
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/internal/command"
"github.com/gogf/gf/v2/os/grpool"
"github.com/gogf/gf/v2/util/gconv"
)
const (
@ -30,7 +31,7 @@ var (
)
func init() {
defaultDebug = gcmd.GetOptWithEnv(commandEnvKeyForDebug, true).Bool()
defaultDebug = gconv.Bool(command.GetOptWithEnv(commandEnvKeyForDebug, "true"))
SetDebug(defaultDebug)
}

View File

@ -12,6 +12,7 @@ import (
"fmt"
"io"
"os"
"runtime"
"strings"
"time"
@ -309,10 +310,27 @@ func (l *Logger) printToFile(ctx context.Context, t time.Time, in *HandlerInput)
defer gmlock.Unlock(memoryLockKey)
// Rotation file size checks.
if l.config.RotateSize > 0 {
if gfile.Size(logFilePath) > l.config.RotateSize {
if l.config.RotateSize > 0 && gfile.Size(logFilePath) > l.config.RotateSize {
if runtime.GOOS == "windows" {
file := l.getFilePointer(ctx, logFilePath)
if file == nil {
intlog.Errorf(ctx, `got nil file pointer for: %s`, logFilePath)
return buffer
}
if _, err := file.Write(buffer.Bytes()); err != nil {
intlog.Errorf(ctx, `%+v`, err)
}
if err := file.Close(true); err != nil {
intlog.Errorf(ctx, `%+v`, err)
}
l.rotateFileBySize(ctx, t)
return buffer
}
l.rotateFileBySize(ctx, t)
}
// Logging content outputting to disk file.
if file := l.getFilePointer(ctx, logFilePath); file == nil {
@ -343,6 +361,21 @@ func (l *Logger) getFilePointer(ctx context.Context, path string) *gfpool.File {
return file
}
// getFilePointer retrieves and returns a file pointer from file pool.
func (l *Logger) getOpenedFilePointer(ctx context.Context, path string) *gfpool.File {
file := gfpool.Get(
path,
defaultFileFlags,
defaultFilePerm,
defaultFileExpire,
)
if file == nil {
intlog.Errorf(ctx, `can not find the file, path:%s`, path)
}
return file
}
// printStd prints content `s` without stack.
func (l *Logger) printStd(ctx context.Context, level int, value ...interface{}) {
l.print(ctx, level, "", value...)

Some files were not shown because too many files have changed in this diff Show More