From 6700910f642ff0d459eac530926e547f3d1f887b Mon Sep 17 00:00:00 2001 From: kesonan Date: Thu, 19 Dec 2024 16:19:10 +0800 Subject: [PATCH 01/22] (goctl)fix: api timeout limited during api generation (#4513) --- tools/goctl/api/gogen/genroutes.go | 17 +++++++----- tools/goctl/api/gogen/genroutes_test.go | 27 +++++++++++++++++++ tools/goctl/internal/version/version.go | 2 +- tools/goctl/pkg/parser/api/scanner/scanner.go | 4 +-- .../pkg/parser/api/scanner/scanner_test.go | 4 +-- 5 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 tools/goctl/api/gogen/genroutes_test.go diff --git a/tools/goctl/api/gogen/genroutes.go b/tools/goctl/api/gogen/genroutes.go index 6d9295f7..9770a57e 100644 --- a/tools/goctl/api/gogen/genroutes.go +++ b/tools/goctl/api/gogen/genroutes.go @@ -139,12 +139,7 @@ rest.WithPrefix("%s"),`, g.prefix) return err } - // why we check this, maybe some users set value 1, it's 1ns, not 1s. - if duration < timeoutThreshold { - return fmt.Errorf("timeout should not less than 1ms, now %v", duration) - } - - timeout = fmt.Sprintf("\n rest.WithTimeout(%d * time.Millisecond),", duration.Milliseconds()) + timeout = fmt.Sprintf("\n rest.WithTimeout(%s),", formatDuration(duration)) hasTimeout = true } @@ -211,6 +206,16 @@ rest.WithPrefix("%s"),`, g.prefix) }) } +func formatDuration(duration time.Duration) string { + if duration < time.Microsecond { + return fmt.Sprintf("%d * time.Nanosecond", duration.Nanoseconds()) + } + if duration < time.Millisecond { + return fmt.Sprintf("%d * time.Microsecond", duration.Microseconds()) + } + return fmt.Sprintf("%d * time.Millisecond", duration.Milliseconds()) +} + func genRouteImports(parentPkg string, api *spec.ApiSpec) string { importSet := collection.NewSet() importSet.AddStr(fmt.Sprintf("\"%s\"", pathx.JoinPackages(parentPkg, contextDir))) diff --git a/tools/goctl/api/gogen/genroutes_test.go b/tools/goctl/api/gogen/genroutes_test.go new file mode 100644 index 00000000..9aac4182 --- /dev/null +++ b/tools/goctl/api/gogen/genroutes_test.go @@ -0,0 +1,27 @@ +package gogen + +import ( + "testing" + "time" +) + +func Test_formatDuration(t *testing.T) { + tests := []struct { + duration time.Duration + expected string + }{ + {0, "0 * time.Nanosecond"}, + {time.Nanosecond, "1 * time.Nanosecond"}, + {100 * time.Nanosecond, "100 * time.Nanosecond"}, + {500 * time.Microsecond, "500 * time.Microsecond"}, + {2 * time.Millisecond, "2 * time.Millisecond"}, + {time.Second, "1000 * time.Millisecond"}, + } + + for _, test := range tests { + result := formatDuration(test.duration) + if result != test.expected { + t.Errorf("formatDuration(%v) = %v; want %v", test.duration, result, test.expected) + } + } +} diff --git a/tools/goctl/internal/version/version.go b/tools/goctl/internal/version/version.go index ce2225ec..bdbbc628 100644 --- a/tools/goctl/internal/version/version.go +++ b/tools/goctl/internal/version/version.go @@ -6,7 +6,7 @@ import ( ) // BuildVersion is the version of goctl. -const BuildVersion = "1.7.3" +const BuildVersion = "1.7.4" var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5} diff --git a/tools/goctl/pkg/parser/api/scanner/scanner.go b/tools/goctl/pkg/parser/api/scanner/scanner.go index 7aac0056..e1989104 100644 --- a/tools/goctl/pkg/parser/api/scanner/scanner.go +++ b/tools/goctl/pkg/parser/api/scanner/scanner.go @@ -29,8 +29,6 @@ const ( // string mode end ) -var missingInput = errors.New("missing input") - type mode int // Scanner is a lexical scanner. @@ -629,7 +627,7 @@ func NewScanner(filename string, src interface{}) (*Scanner, error) { } if len(data) == 0 { - return nil, missingInput + return nil, fmt.Errorf("filename: %s,missing input", filename) } var runeList []rune diff --git a/tools/goctl/pkg/parser/api/scanner/scanner_test.go b/tools/goctl/pkg/parser/api/scanner/scanner_test.go index 5e2f1956..42d20ff4 100644 --- a/tools/goctl/pkg/parser/api/scanner/scanner_test.go +++ b/tools/goctl/pkg/parser/api/scanner/scanner_test.go @@ -62,13 +62,13 @@ func TestNewScanner(t *testing.T) { { filename: "foo", src: "", - expected: missingInput, + expected: "missing input", }, } for _, v := range testData { s, err := NewScanner(v.filename, v.src) if err != nil { - assert.Equal(t, v.expected.(error).Error(), err.Error()) + assert.Contains(t, err.Error(), v.expected) } else { assert.Equal(t, v.expected, s.filename) } From b9d7f1cc776dc28c8a1a6d69f2123275b63ca7d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:54:44 +0800 Subject: [PATCH 02/22] chore(deps): bump github.com/emicklei/proto from 1.13.4 to 1.14.0 in /tools/goctl (#4510) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/goctl/go.mod | 2 +- tools/goctl/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/goctl/go.mod b/tools/goctl/go.mod index 4368ff31..35c5efcf 100644 --- a/tools/goctl/go.mod +++ b/tools/goctl/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/DATA-DOG/go-sqlmock v1.5.2 - github.com/emicklei/proto v1.13.4 + github.com/emicklei/proto v1.14.0 github.com/fatih/structtag v1.2.0 github.com/go-sql-driver/mysql v1.8.1 github.com/gookit/color v1.5.4 diff --git a/tools/goctl/go.sum b/tools/goctl/go.sum index 6f73f7f5..81b9535f 100644 --- a/tools/goctl/go.sum +++ b/tools/goctl/go.sum @@ -30,8 +30,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/emicklei/proto v1.13.4 h1:myn1fyf8t7tAqIzV91Tj9qXpvyXXGXk8OS2H6IBSc9g= -github.com/emicklei/proto v1.13.4/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= +github.com/emicklei/proto v1.14.0 h1:WYxC0OrBuuC+FUCTZvb8+fzEHdZMwLEF+OnVfZA3LXU= +github.com/emicklei/proto v1.14.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= From a1d9bc08f03e65574428e56385eb2e84cf6334da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 18:13:12 +0800 Subject: [PATCH 03/22] chore(deps): bump github.com/alicebob/miniredis/v2 from 2.33.0 to 2.34.0 (#4509) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index baff8cf6..ec465359 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/DATA-DOG/go-sqlmock v1.5.2 - github.com/alicebob/miniredis/v2 v2.33.0 + github.com/alicebob/miniredis/v2 v2.34.0 github.com/fatih/color v1.18.0 github.com/fullstorydev/grpcurl v1.9.2 github.com/go-sql-driver/mysql v1.8.1 diff --git a/go.sum b/go.sum index 320e8b45..ee76a829 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7Oputl github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA= -github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0= +github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0= +github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= From 8f9ba3ec11df000b49b997f282469d4a300f6509 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 12:04:16 +0800 Subject: [PATCH 04/22] chore(deps): bump golang.org/x/net from 0.32.0 to 0.33.0 (#4516) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ec465359..f6a141fb 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( go.opentelemetry.io/otel/trace v1.24.0 go.uber.org/automaxprocs v1.6.0 go.uber.org/goleak v1.3.0 - golang.org/x/net v0.32.0 + golang.org/x/net v0.33.0 golang.org/x/sys v0.28.0 golang.org/x/time v0.8.0 google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d diff --git a/go.sum b/go.sum index ee76a829..a2caef11 100644 --- a/go.sum +++ b/go.sum @@ -242,8 +242,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From 8625864d43d52339ad81f9d6b0a3057c8915a916 Mon Sep 17 00:00:00 2001 From: Xingchen Wang Date: Sat, 21 Dec 2024 11:22:54 +0800 Subject: [PATCH 05/22] bugfix:`SetSlowThreshold` not effective in function `logDetails` (#4511) Co-authored-by: Star Wang --- rest/handler/loghandler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest/handler/loghandler.go b/rest/handler/loghandler.go index eaf59a16..30348e3b 100644 --- a/rest/handler/loghandler.go +++ b/rest/handler/loghandler.go @@ -158,7 +158,7 @@ func logDetails(r *http.Request, response *detailLoggedResponseWriter, timer *ut logger := logx.WithContext(r.Context()) buf.WriteString(fmt.Sprintf("[HTTP] %s - %d - %s - %s\n=> %s\n", r.Method, code, r.RemoteAddr, timex.ReprOfDuration(duration), dumpRequest(r))) - if duration > defaultSlowThreshold { + if duration > slowThreshold.Load() { logger.Slowf("[HTTP] %s - %d - %s - slowcall(%s)\n=> %s\n", r.Method, code, r.RemoteAddr, fmt.Sprintf("slowcall(%s)", timex.ReprOfDuration(duration)), dumpRequest(r)) } From f57874a51fe88b80525f26554b31d852e2db39b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E6=8F=92=E7=94=B5?= <69096367+r27153733@users.noreply.github.com> Date: Sat, 21 Dec 2024 11:48:27 +0800 Subject: [PATCH 06/22] fix: DetailedLog format. (#4467) --- rest/handler/loghandler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest/handler/loghandler.go b/rest/handler/loghandler.go index 30348e3b..62f48c56 100644 --- a/rest/handler/loghandler.go +++ b/rest/handler/loghandler.go @@ -160,7 +160,7 @@ func logDetails(r *http.Request, response *detailLoggedResponseWriter, timer *ut r.Method, code, r.RemoteAddr, timex.ReprOfDuration(duration), dumpRequest(r))) if duration > slowThreshold.Load() { logger.Slowf("[HTTP] %s - %d - %s - slowcall(%s)\n=> %s\n", r.Method, code, r.RemoteAddr, - fmt.Sprintf("slowcall(%s)", timex.ReprOfDuration(duration)), dumpRequest(r)) + timex.ReprOfDuration(duration), dumpRequest(r)) } body := logs.Flush() From 2159d112c3cef6578fe246e27faf4dfdf596b56a Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Sun, 22 Dec 2024 15:10:44 +0800 Subject: [PATCH 07/22] chore: format the code (#4518) --- tools/goctl/pkg/parser/api/parser/parser.go | 14 +++++++------- tools/goctl/pkg/parser/api/parser/parser_test.go | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/goctl/pkg/parser/api/parser/parser.go b/tools/goctl/pkg/parser/api/parser/parser.go index 9faa0002..d4c267d6 100644 --- a/tools/goctl/pkg/parser/api/parser/parser.go +++ b/tools/goctl/pkg/parser/api/parser/parser.go @@ -13,13 +13,13 @@ import ( ) const ( - idAPI = "api" - groupKeyText = "group" - infoTitleKey = "Title" - infoDescKey = "Desc" - infoVersionKey = "Version" - infoAuthorKey = "Author" - infoEmailKey = "Email" + idAPI = "api" + groupKeyText = "group" + infoTitleKey = "Title" + infoDescKey = "Desc" + infoVersionKey = "Version" + infoAuthorKey = "Author" + infoEmailKey = "Email" ) // Parser is the parser for api file. diff --git a/tools/goctl/pkg/parser/api/parser/parser_test.go b/tools/goctl/pkg/parser/api/parser/parser_test.go index 361903ee..adc7b3ac 100644 --- a/tools/goctl/pkg/parser/api/parser/parser_test.go +++ b/tools/goctl/pkg/parser/api/parser/parser_test.go @@ -305,7 +305,7 @@ func TestParser_Parse_atServerStmt(t *testing.T) { "prefix3:": "v1/v2_", "prefix4:": "a-b-c", "summary:": `"test"`, - "key:": `"bar"`, + "key:": `"bar"`, } p := New("foo.api", atServerTestAPI) From 1d9159ea396bc944c285102afcc4e11d3bf4dfb0 Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Mon, 23 Dec 2024 00:56:20 +0800 Subject: [PATCH 08/22] feat: support form array in three notations (#4498) Signed-off-by: kevin --- core/mapping/unmarshaler.go | 145 ++++++++++++++++++--------- core/mapping/unmarshaler_test.go | 167 ++++++++++++++++++++++++++++--- rest/httpx/requests_test.go | 112 ++++++++++++++++++++- rest/httpx/util.go | 24 ++++- rest/httpx/util_test.go | 22 ++++ rest/router/patrouter_test.go | 63 ++++++++---- 6 files changed, 452 insertions(+), 81 deletions(-) diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go index b4fb356e..f68748c6 100644 --- a/core/mapping/unmarshaler.go +++ b/core/mapping/unmarshaler.go @@ -18,6 +18,7 @@ import ( ) const ( + comma = "," defaultKeyName = "key" delimiter = '.' ignoreKey = "-" @@ -36,6 +37,7 @@ var ( defaultCacheLock sync.Mutex emptyMap = map[string]any{} emptyValue = reflect.ValueOf(lang.Placeholder) + stringSliceType = reflect.TypeOf([]string{}) ) type ( @@ -80,40 +82,11 @@ func (u *Unmarshaler) Unmarshal(i, v any) error { return u.unmarshal(i, v, "") } -func (u *Unmarshaler) unmarshal(i, v any, fullName string) error { - valueType := reflect.TypeOf(v) - if valueType.Kind() != reflect.Ptr { - return errValueNotSettable - } - - elemType := Deref(valueType) - switch iv := i.(type) { - case map[string]any: - if elemType.Kind() != reflect.Struct { - return errTypeMismatch - } - - return u.unmarshalValuer(mapValuer(iv), v, fullName) - case []any: - if elemType.Kind() != reflect.Slice { - return errTypeMismatch - } - - return u.fillSlice(elemType, reflect.ValueOf(v).Elem(), iv, fullName) - default: - return errUnsupportedType - } -} - // UnmarshalValuer unmarshals m into v. func (u *Unmarshaler) UnmarshalValuer(m Valuer, v any) error { return u.unmarshalValuer(simpleValuer{current: m}, v, "") } -func (u *Unmarshaler) unmarshalValuer(m Valuer, v any, fullName string) error { - return u.unmarshalWithFullName(simpleValuer{current: m}, v, fullName) -} - func (u *Unmarshaler) fillMap(fieldType reflect.Type, value reflect.Value, mapValue any, fullName string) error { if !value.CanSet() { @@ -173,13 +146,18 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, baseType := fieldType.Elem() dereffedBaseType := Deref(baseType) dereffedBaseKind := dereffedBaseType.Kind() - conv := reflect.MakeSlice(reflect.SliceOf(baseType), refValue.Len(), refValue.Cap()) if refValue.Len() == 0 { - value.Set(conv) + value.Set(reflect.MakeSlice(reflect.SliceOf(baseType), 0, 0)) return nil } + if u.opts.fromArray { + refValue = makeStringSlice(refValue) + } + var valid bool + conv := reflect.MakeSlice(reflect.SliceOf(baseType), refValue.Len(), refValue.Cap()) + for i := 0; i < refValue.Len(); i++ { ithValue := refValue.Index(i).Interface() if ithValue == nil { @@ -191,17 +169,9 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, switch dereffedBaseKind { case reflect.Struct: - target := reflect.New(dereffedBaseType) - val, ok := ithValue.(map[string]any) - if !ok { - return errTypeMismatch - } - - if err := u.unmarshal(val, target.Interface(), sliceFullName); err != nil { + if err := u.fillStructElement(baseType, conv.Index(i), ithValue, sliceFullName); err != nil { return err } - - SetValue(fieldType.Elem(), conv.Index(i), target.Elem()) case reflect.Slice: if err := u.fillSlice(dereffedBaseType, conv.Index(i), ithValue, sliceFullName); err != nil { return err @@ -236,7 +206,7 @@ func (u *Unmarshaler) fillSliceFromString(fieldType reflect.Type, value reflect. return errUnsupportedType } - baseFieldType := Deref(fieldType.Elem()) + baseFieldType := fieldType.Elem() baseFieldKind := baseFieldType.Kind() conv := reflect.MakeSlice(reflect.SliceOf(baseFieldType), len(slice), cap(slice)) @@ -257,29 +227,39 @@ func (u *Unmarshaler) fillSliceValue(slice reflect.Value, index int, } ithVal := slice.Index(index) + ithValType := ithVal.Type() + switch v := value.(type) { case fmt.Stringer: return setValueFromString(baseKind, ithVal, v.String()) case string: return setValueFromString(baseKind, ithVal, v) case map[string]any: - return u.fillMap(ithVal.Type(), ithVal, value, fullName) + // deref to handle both pointer and non-pointer types. + switch Deref(ithValType).Kind() { + case reflect.Struct: + return u.fillStructElement(ithValType, ithVal, v, fullName) + case reflect.Map: + return u.fillMap(ithValType, ithVal, value, fullName) + default: + return errTypeMismatch + } default: // don't need to consider the difference between int, int8, int16, int32, int64, // uint, uint8, uint16, uint32, uint64, because they're handled as json.Number. if ithVal.Kind() == reflect.Ptr { - baseType := Deref(ithVal.Type()) + baseType := Deref(ithValType) if !reflect.TypeOf(value).AssignableTo(baseType) { return errTypeMismatch } target := reflect.New(baseType).Elem() target.Set(reflect.ValueOf(value)) - SetValue(ithVal.Type(), ithVal, target) + SetValue(ithValType, ithVal, target) return nil } - if !reflect.TypeOf(value).AssignableTo(ithVal.Type()) { + if !reflect.TypeOf(value).AssignableTo(ithValType) { return errTypeMismatch } @@ -310,6 +290,23 @@ func (u *Unmarshaler) fillSliceWithDefault(derefedType reflect.Type, value refle return u.fillSlice(derefedType, value, slice, fullName) } +func (u *Unmarshaler) fillStructElement(baseType reflect.Type, target reflect.Value, + value any, fullName string) error { + val, ok := value.(map[string]any) + if !ok { + return errTypeMismatch + } + + // use Deref(baseType) to get the base type in case the type is a pointer type. + ptr := reflect.New(Deref(baseType)) + if err := u.unmarshal(val, ptr.Interface(), fullName); err != nil { + return err + } + + SetValue(baseType, target, ptr.Elem()) + return nil +} + func (u *Unmarshaler) fillUnmarshalerStruct(fieldType reflect.Type, value reflect.Value, targetValue string) error { if !value.CanSet() { @@ -952,6 +949,35 @@ func (u *Unmarshaler) processNamedFieldWithoutValue(fieldType reflect.Type, valu return nil } +func (u *Unmarshaler) unmarshal(i, v any, fullName string) error { + valueType := reflect.TypeOf(v) + if valueType.Kind() != reflect.Ptr { + return errValueNotSettable + } + + elemType := Deref(valueType) + switch iv := i.(type) { + case map[string]any: + if elemType.Kind() != reflect.Struct { + return errTypeMismatch + } + + return u.unmarshalValuer(mapValuer(iv), v, fullName) + case []any: + if elemType.Kind() != reflect.Slice { + return errTypeMismatch + } + + return u.fillSlice(elemType, reflect.ValueOf(v).Elem(), iv, fullName) + default: + return errUnsupportedType + } +} + +func (u *Unmarshaler) unmarshalValuer(m Valuer, v any, fullName string) error { + return u.unmarshalWithFullName(simpleValuer{current: m}, v, fullName) +} + func (u *Unmarshaler) unmarshalWithFullName(m valuerWithParent, v any, fullName string) error { rv := reflect.ValueOf(v) if err := ValidatePtr(rv); err != nil { @@ -1146,6 +1172,35 @@ func join(elem ...string) string { return builder.String() } +func makeStringSlice(refValue reflect.Value) reflect.Value { + if refValue.Len() != 1 { + return refValue + } + + element := refValue.Index(0) + if element.Kind() != reflect.String { + return refValue + } + + val, ok := element.Interface().(string) + if !ok { + return refValue + } + + splits := strings.Split(val, comma) + if len(splits) <= 1 { + return refValue + } + + slice := reflect.MakeSlice(stringSliceType, len(splits), len(splits)) + for i, split := range splits { + // allow empty strings + slice.Index(i).Set(reflect.ValueOf(split)) + } + + return slice +} + func newInitError(name string) error { return fmt.Errorf("field %q is not set", name) } diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go index 3270632d..ae2aba0e 100644 --- a/core/mapping/unmarshaler_test.go +++ b/core/mapping/unmarshaler_test.go @@ -351,7 +351,7 @@ func TestUnmarshalIntSliceOfPtr(t *testing.T) { assert.Error(t, UnmarshalKey(m, &in)) }) - t.Run("int slice with nil", func(t *testing.T) { + t.Run("int slice with nil element", func(t *testing.T) { type inner struct { Ints []int `key:"ints"` } @@ -365,6 +365,21 @@ func TestUnmarshalIntSliceOfPtr(t *testing.T) { assert.Empty(t, in.Ints) } }) + + t.Run("int slice with nil", func(t *testing.T) { + type inner struct { + Ints []int `key:"ints"` + } + + m := map[string]any{ + "ints": []any(nil), + } + + var in inner + if assert.NoError(t, UnmarshalKey(m, &in)) { + assert.Empty(t, in.Ints) + } + }) } func TestUnmarshalIntWithDefault(t *testing.T) { @@ -1374,20 +1389,82 @@ func TestUnmarshalWithFloatPtr(t *testing.T) { } func TestUnmarshalIntSlice(t *testing.T) { - var v struct { - Ages []int `key:"ages"` - Slice []int `key:"slice"` - } - m := map[string]any{ - "ages": []int{1, 2}, - "slice": []any{}, - } + t.Run("int slice from int", func(t *testing.T) { + var v struct { + Ages []int `key:"ages"` + Slice []int `key:"slice"` + } + m := map[string]any{ + "ages": []int{1, 2}, + "slice": []any{}, + } - ast := assert.New(t) - if ast.NoError(UnmarshalKey(m, &v)) { - ast.ElementsMatch([]int{1, 2}, v.Ages) - ast.Equal([]int{}, v.Slice) - } + ast := assert.New(t) + if ast.NoError(UnmarshalKey(m, &v)) { + ast.ElementsMatch([]int{1, 2}, v.Ages) + ast.Equal([]int{}, v.Slice) + } + }) + + t.Run("int slice from one int", func(t *testing.T) { + var v struct { + Ages []int `key:"ages"` + } + m := map[string]any{ + "ages": []int{2}, + } + + ast := assert.New(t) + unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray()) + if ast.NoError(unmarshaler.Unmarshal(m, &v)) { + ast.ElementsMatch([]int{2}, v.Ages) + } + }) + + t.Run("int slice from one int string", func(t *testing.T) { + var v struct { + Ages []int `key:"ages"` + } + m := map[string]any{ + "ages": []string{"2"}, + } + + ast := assert.New(t) + unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray()) + if ast.NoError(unmarshaler.Unmarshal(m, &v)) { + ast.ElementsMatch([]int{2}, v.Ages) + } + }) + + t.Run("int slice from one json.Number", func(t *testing.T) { + var v struct { + Ages []int `key:"ages"` + } + m := map[string]any{ + "ages": []json.Number{"2"}, + } + + ast := assert.New(t) + unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray()) + if ast.NoError(unmarshaler.Unmarshal(m, &v)) { + ast.ElementsMatch([]int{2}, v.Ages) + } + }) + + t.Run("int slice from one int strings", func(t *testing.T) { + var v struct { + Ages []int `key:"ages"` + } + m := map[string]any{ + "ages": []string{"1,2"}, + } + + ast := assert.New(t) + unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray()) + if ast.NoError(unmarshaler.Unmarshal(m, &v)) { + ast.ElementsMatch([]int{1, 2}, v.Ages) + } + }) } func TestUnmarshalString(t *testing.T) { @@ -1442,6 +1519,36 @@ func TestUnmarshalStringSliceFromString(t *testing.T) { } }) + t.Run("slice from empty string", func(t *testing.T) { + var v struct { + Names []string `key:"names"` + } + m := map[string]any{ + "names": []string{""}, + } + + ast := assert.New(t) + unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray()) + if ast.NoError(unmarshaler.Unmarshal(m, &v)) { + ast.ElementsMatch([]string{""}, v.Names) + } + }) + + t.Run("slice from empty and valid string", func(t *testing.T) { + var v struct { + Names []string `key:"names"` + } + m := map[string]any{ + "names": []string{","}, + } + + ast := assert.New(t) + unmarshaler := NewUnmarshaler(defaultKeyName, WithFromArray()) + if ast.NoError(unmarshaler.Unmarshal(m, &v)) { + ast.ElementsMatch([]string{"", ""}, v.Names) + } + }) + t.Run("slice from string with slice error", func(t *testing.T) { var v struct { Names []int `key:"names"` @@ -5862,6 +5969,38 @@ func TestUnmarshal_Unmarshaler(t *testing.T) { }) } +func TestParseJsonStringValue(t *testing.T) { + t.Run("string", func(t *testing.T) { + type GoodsInfo struct { + Sku int64 `json:"sku,optional"` + } + + type GetReq struct { + GoodsList []*GoodsInfo `json:"goods_list"` + } + + input := map[string]any{"goods_list": "[{\"sku\":11},{\"sku\":22}]"} + var v GetReq + assert.NotPanics(t, func() { + assert.NoError(t, UnmarshalJsonMap(input, &v)) + assert.Equal(t, 2, len(v.GoodsList)) + assert.ElementsMatch(t, []int64{11, 22}, []int64{v.GoodsList[0].Sku, v.GoodsList[1].Sku}) + }) + }) + + t.Run("string with invalid type", func(t *testing.T) { + type GetReq struct { + GoodsList []*int `json:"goods_list"` + } + + input := map[string]any{"goods_list": "[{\"sku\":11},{\"sku\":22}]"} + var v GetReq + assert.NotPanics(t, func() { + assert.Error(t, UnmarshalJsonMap(input, &v)) + }) + }) +} + func BenchmarkDefaultValue(b *testing.B) { for i := 0; i < b.N; i++ { var a struct { diff --git a/rest/httpx/requests_test.go b/rest/httpx/requests_test.go index 437b9a13..fd7fb3a5 100644 --- a/rest/httpx/requests_test.go +++ b/rest/httpx/requests_test.go @@ -88,6 +88,36 @@ func TestParseFormArray(t *testing.T) { } }) + t.Run("slice with empty", func(t *testing.T) { + var v struct { + Name []string `form:"name,optional"` + } + + r, err := http.NewRequest( + http.MethodGet, + "/a", + http.NoBody) + assert.NoError(t, err) + if assert.NoError(t, Parse(r, &v)) { + assert.ElementsMatch(t, []string{}, v.Name) + } + }) + + t.Run("slice with empty", func(t *testing.T) { + var v struct { + Name []string `form:"name,optional"` + } + + r, err := http.NewRequest( + http.MethodGet, + "/a?name=", + http.NoBody) + assert.NoError(t, err) + if assert.NoError(t, Parse(r, &v)) { + assert.ElementsMatch(t, []string{""}, v.Name) + } + }) + t.Run("slice with empty and non-empty", func(t *testing.T) { var v struct { Name []string `form:"name"` @@ -99,7 +129,67 @@ func TestParseFormArray(t *testing.T) { http.NoBody) assert.NoError(t, err) if assert.NoError(t, Parse(r, &v)) { - assert.ElementsMatch(t, []string{"1"}, v.Name) + assert.ElementsMatch(t, []string{"", "1"}, v.Name) + } + }) + + t.Run("slice with one value on array format", func(t *testing.T) { + var v struct { + Names []string `form:"names"` + } + + r, err := http.NewRequest( + http.MethodGet, + "/a?names=1,2,3", + http.NoBody) + assert.NoError(t, err) + if assert.NoError(t, Parse(r, &v)) { + assert.ElementsMatch(t, []string{"1", "2", "3"}, v.Names) + } + }) + + t.Run("slice with one value on combined array format", func(t *testing.T) { + var v struct { + Names []string `form:"names"` + } + + r, err := http.NewRequest( + http.MethodGet, + "/a?names=[1,2,3]&names=4", + http.NoBody) + assert.NoError(t, err) + if assert.NoError(t, Parse(r, &v)) { + assert.ElementsMatch(t, []string{"[1,2,3]", "4"}, v.Names) + } + }) + + t.Run("slice with one value on integer array format", func(t *testing.T) { + var v struct { + Numbers []int `form:"numbers"` + } + + r, err := http.NewRequest( + http.MethodGet, + "/a?numbers=1,2,3", + http.NoBody) + assert.NoError(t, err) + if assert.NoError(t, Parse(r, &v)) { + assert.ElementsMatch(t, []int{1, 2, 3}, v.Numbers) + } + }) + + t.Run("slice with one value on array format brackets", func(t *testing.T) { + var v struct { + Names []string `form:"names"` + } + + r, err := http.NewRequest( + http.MethodGet, + "/a?names[]=1&names[]=2&names[]=3", + http.NoBody) + assert.NoError(t, err) + if assert.NoError(t, Parse(r, &v)) { + assert.ElementsMatch(t, []string{"1", "2", "3"}, v.Names) } }) } @@ -528,6 +618,26 @@ func TestCustomUnmarshalerStructRequest(t *testing.T) { assert.Equal(t, "hello", v.Foo.Name) } +func TestParseJsonStringRequest(t *testing.T) { + type GoodsInfo struct { + Sku int64 `json:"sku,optional"` + } + + type GetReq struct { + GoodsList []*GoodsInfo `json:"goods_list"` + } + + input := `{"goods_list":"[{\"sku\":11},{\"sku\":22}]"}` + r := httptest.NewRequest(http.MethodPost, "/a", strings.NewReader(input)) + r.Header.Set(ContentType, JsonContentType) + var v GetReq + assert.NotPanics(t, func() { + assert.NoError(t, Parse(r, &v)) + assert.Equal(t, 2, len(v.GoodsList)) + assert.ElementsMatch(t, []int64{11, 22}, []int64{v.GoodsList[0].Sku, v.GoodsList[1].Sku}) + }) +} + func BenchmarkParseRaw(b *testing.B) { r, err := http.NewRequest(http.MethodGet, "http://hello.com/a?name=hello&age=18&percent=3.4", http.NoBody) if err != nil { diff --git a/rest/httpx/util.go b/rest/httpx/util.go index 19248ae7..c22ad8e0 100644 --- a/rest/httpx/util.go +++ b/rest/httpx/util.go @@ -2,12 +2,23 @@ package httpx import ( "errors" + "fmt" "net/http" + "strings" ) -const xForwardedFor = "X-Forwarded-For" +const ( + xForwardedFor = "X-Forwarded-For" + arraySuffix = "[]" + // most servers and clients have a limit of 8192 bytes (8 KB) + // one parameter at least take 4 chars, for example `?a=b&c=d` + maxFormParamCount = 2048 +) -// GetFormValues returns the form values. +// GetFormValues returns the form values supporting three array notation formats: +// 1. Standard notation: /api?names=alice&names=bob +// 2. Comma notation: /api?names=alice,bob +// 3. Bracket notation: /api?names[]=alice&names[]=bob func GetFormValues(r *http.Request) (map[string]any, error) { if err := r.ParseForm(); err != nil { return nil, err @@ -19,16 +30,23 @@ func GetFormValues(r *http.Request) (map[string]any, error) { } } + var n int params := make(map[string]any, len(r.Form)) for name, values := range r.Form { filtered := make([]string, 0, len(values)) for _, v := range values { - if len(v) > 0 { + if n < maxFormParamCount { filtered = append(filtered, v) + n++ + } else { + return nil, fmt.Errorf("too many form values, error: %s", r.Form.Encode()) } } if len(filtered) > 0 { + if strings.HasSuffix(name, arraySuffix) { + name = name[:len(name)-2] + } params[name] = filtered } } diff --git a/rest/httpx/util_test.go b/rest/httpx/util_test.go index 8e804cbf..19725d47 100644 --- a/rest/httpx/util_test.go +++ b/rest/httpx/util_test.go @@ -1,7 +1,9 @@ package httpx import ( + "fmt" "net/http" + "net/url" "strings" "testing" @@ -23,3 +25,23 @@ func TestGetRemoteAddrNoHeader(t *testing.T) { assert.True(t, len(GetRemoteAddr(r)) == 0) } + +func TestGetFormValues_TooManyValues(t *testing.T) { + form := url.Values{} + + // Add more values than the limit + for i := 0; i < maxFormParamCount+10; i++ { + form.Add("param", fmt.Sprintf("value%d", i)) + } + + // Create a new request with the form data + req, err := http.NewRequest("POST", "/test", strings.NewReader(form.Encode())) + assert.NoError(t, err) + + // Set the content type for form data + req.Header.Set(ContentType, "application/x-www-form-urlencoded") + + _, err = GetFormValues(req) + assert.Error(t, err) + assert.Contains(t, err.Error(), "too many form values") +} diff --git a/rest/router/patrouter_test.go b/rest/router/patrouter_test.go index dca589e9..02f21ece 100644 --- a/rest/router/patrouter_test.go +++ b/rest/router/patrouter_test.go @@ -516,28 +516,55 @@ func TestParsePtrInRequestEmpty(t *testing.T) { } func TestParseQueryOptional(t *testing.T) { - r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=", nil) - assert.Nil(t, err) + t.Run("optional with string", func(t *testing.T) { + r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever&zipcode=", nil) + assert.Nil(t, err) - router := NewRouter() - err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - v := struct { - Nickname string `form:"nickname"` - Zipcode int64 `form:"zipcode,optional"` - }{} + router := NewRouter() + err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + v := struct { + Nickname string `form:"nickname"` + Zipcode string `form:"zipcode,optional"` + }{} - err = httpx.Parse(r, &v) - assert.Nil(t, err) - _, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Nickname, v.Zipcode)) - assert.Nil(t, err) - })) - assert.Nil(t, err) + err = httpx.Parse(r, &v) + assert.Nil(t, err) + _, err = io.WriteString(w, fmt.Sprintf("%s:%s", v.Nickname, v.Zipcode)) + assert.Nil(t, err) + })) + assert.Nil(t, err) - rr := httptest.NewRecorder() - router.ServeHTTP(rr, r) + rr := httptest.NewRecorder() + router.ServeHTTP(rr, r) - assert.Equal(t, "whatever:0", rr.Body.String()) + assert.Equal(t, "whatever:", rr.Body.String()) + }) + + t.Run("optional with int", func(t *testing.T) { + r, err := http.NewRequest(http.MethodGet, "http://hello.com/kevin/2017?nickname=whatever", nil) + assert.Nil(t, err) + + router := NewRouter() + err = router.Handle(http.MethodGet, "/:name/:year", http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + v := struct { + Nickname string `form:"nickname"` + Zipcode int `form:"zipcode,optional"` + }{} + + err = httpx.Parse(r, &v) + assert.Nil(t, err) + _, err = io.WriteString(w, fmt.Sprintf("%s:%d", v.Nickname, v.Zipcode)) + assert.Nil(t, err) + })) + assert.Nil(t, err) + + rr := httptest.NewRecorder() + router.ServeHTTP(rr, r) + + assert.Equal(t, "whatever:0", rr.Body.String()) + }) } func TestParse(t *testing.T) { From 1c3876810eb8b2825fb8cd69ca10c35958182101 Mon Sep 17 00:00:00 2001 From: kesonan Date: Tue, 24 Dec 2024 00:00:50 +0800 Subject: [PATCH 09/22] fix command `goctl bug` invalid (#4520) --- tools/goctl/bug/cmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/goctl/bug/cmd.go b/tools/goctl/bug/cmd.go index b78de540..92649b58 100644 --- a/tools/goctl/bug/cmd.go +++ b/tools/goctl/bug/cmd.go @@ -6,4 +6,4 @@ import ( ) // Cmd describes a bug command. -var Cmd = cobrax.NewCommand("bug", cobrax.WithRunE(cobra.NoArgs), cobrax.WithArgs(cobra.NoArgs)) +var Cmd = cobrax.NewCommand("bug", cobrax.WithRunE(runE), cobrax.WithArgs(cobra.NoArgs)) From b8206fb46a34366cc6c29ee35c1f48d339b4f2af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2024 09:58:03 +0800 Subject: [PATCH 10/22] chore(deps): bump google.golang.org/protobuf from 1.36.0 to 1.36.1 (#4523) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f6a141fb..641b3c84 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( golang.org/x/time v0.8.0 google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d google.golang.org/grpc v1.65.0 - google.golang.org/protobuf v1.36.0 + google.golang.org/protobuf v1.36.1 gopkg.in/cheggaaa/pb.v1 v1.0.28 gopkg.in/h2non/gock.v1 v1.1.2 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index a2caef11..dcefca8b 100644 --- a/go.sum +++ b/go.sum @@ -295,8 +295,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= -google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From eaa01ccb9fa519483793ecf4a76ba741d87f8b69 Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Sat, 28 Dec 2024 09:59:00 +0800 Subject: [PATCH 11/22] Update readme-cn.md (#4525) --- readme-cn.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme-cn.md b/readme-cn.md index 37c632c4..99add87b 100644 --- a/readme-cn.md +++ b/readme-cn.md @@ -300,6 +300,7 @@ go-zero 已被许多公司用于生产部署,接入场景如在线教育、电 >101. 上海巨瓴科技有限公司 >102. 深圳市兴海物联科技有限公司 >103. 爱芯元智半导体股份有限公司 +>104. 杭州升恒科技有限公司 如果贵公司也已使用 go-zero,欢迎在 [登记地址](https://github.com/zeromicro/go-zero/issues/602) 登记,仅仅为了推广,不做其它用途。 From 5c3679ffe723426f1d634e34d6e30db40cc0b39a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Dec 2024 12:05:03 +0800 Subject: [PATCH 12/22] chore(deps): bump google.golang.org/protobuf from 1.36.0 to 1.36.1 in /tools/goctl (#4521) --- tools/goctl/go.mod | 2 +- tools/goctl/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/goctl/go.mod b/tools/goctl/go.mod index 35c5efcf..e3e7237c 100644 --- a/tools/goctl/go.mod +++ b/tools/goctl/go.mod @@ -18,7 +18,7 @@ require ( github.com/zeromicro/go-zero v1.7.4 golang.org/x/text v0.21.0 google.golang.org/grpc v1.65.0 - google.golang.org/protobuf v1.36.0 + google.golang.org/protobuf v1.36.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/tools/goctl/go.sum b/tools/goctl/go.sum index 81b9535f..0a9986e7 100644 --- a/tools/goctl/go.sum +++ b/tools/goctl/go.sum @@ -271,8 +271,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= -google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From fcc246933cff1bd51d9c257c4335a8217f8222e8 Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Wed, 1 Jan 2025 15:06:50 +0800 Subject: [PATCH 13/22] fix: service group not working well when callback takes long time (#4531) Signed-off-by: kevin --- core/proc/shutdown.go | 3 +++ core/proc/shutdown_test.go | 37 ++++++++++++++++++++++++++++++++++++ core/service/servicegroup.go | 7 ++++++- gateway/server.go | 19 ++++++++++++++++++ gateway/server_test.go | 2 +- 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/core/proc/shutdown.go b/core/proc/shutdown.go index 763742a2..71ce429a 100644 --- a/core/proc/shutdown.go +++ b/core/proc/shutdown.go @@ -82,6 +82,9 @@ func (lm *listenerManager) addListener(fn func()) (waitForCalled func()) { }) lm.lock.Unlock() + // we can return lm.waitGroup.Wait directly, + // but we want to make the returned func more readable. + // creating an extra closure would be negligible in practice. return func() { lm.waitGroup.Wait() } diff --git a/core/proc/shutdown_test.go b/core/proc/shutdown_test.go index 79c7deff..64517f0f 100644 --- a/core/proc/shutdown_test.go +++ b/core/proc/shutdown_test.go @@ -3,6 +3,7 @@ package proc import ( + "sync/atomic" "testing" "time" @@ -29,6 +30,42 @@ func TestShutdown(t *testing.T) { assert.Equal(t, 3, val) } +func TestShutdownWithMultipleServices(t *testing.T) { + SetTimeToForceQuit(time.Hour) + assert.Equal(t, time.Hour, delayTimeBeforeForceQuit) + + var val int32 + called1 := AddShutdownListener(func() { + atomic.AddInt32(&val, 1) + }) + called2 := AddShutdownListener(func() { + atomic.AddInt32(&val, 2) + }) + Shutdown() + called1() + called2() + + assert.Equal(t, int32(3), atomic.LoadInt32(&val)) +} + +func TestWrapUpWithMultipleServices(t *testing.T) { + SetTimeToForceQuit(time.Hour) + assert.Equal(t, time.Hour, delayTimeBeforeForceQuit) + + var val int32 + called1 := AddWrapUpListener(func() { + atomic.AddInt32(&val, 1) + }) + called2 := AddWrapUpListener(func() { + atomic.AddInt32(&val, 2) + }) + WrapUp() + called1() + called2() + + assert.Equal(t, int32(3), atomic.LoadInt32(&val)) +} + func TestNotifyMoreThanOnce(t *testing.T) { ch := make(chan struct{}, 1) diff --git a/core/service/servicegroup.go b/core/service/servicegroup.go index 031abb5d..9281fb29 100644 --- a/core/service/servicegroup.go +++ b/core/service/servicegroup.go @@ -76,9 +76,14 @@ func (sg *ServiceGroup) doStart() { } func (sg *ServiceGroup) doStop() { + group := threading.NewRoutineGroup() for _, service := range sg.services { - service.Stop() + // new variable to avoid closure problems, can be removed after go 1.22 + // see https://golang.org/doc/faq#closures_and_goroutines + service := service + group.Run(service.Stop) } + group.Wait() } // WithStart wraps a start func as a Service. diff --git a/gateway/server.go b/gateway/server.go index 71d1e554..51c11a35 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -11,6 +11,7 @@ import ( "github.com/jhump/protoreflect/grpcreflect" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/mr" + "github.com/zeromicro/go-zero/core/threading" "github.com/zeromicro/go-zero/gateway/internal" "github.com/zeromicro/go-zero/rest" "github.com/zeromicro/go-zero/rest/httpx" @@ -23,6 +24,7 @@ type ( Server struct { *rest.Server upstreams []Upstream + conns []zrpc.Client processHeader func(http.Header) []string dialer func(conf zrpc.RpcClientConf) zrpc.Client } @@ -51,8 +53,24 @@ func (s *Server) Start() { } // Stop stops the gateway server. +// To get a graceful shutdown, it stops the HTTP server first, then closes gRPC connections. func (s *Server) Stop() { + // stop the HTTP server first, then close gRPC connections. + // in case the gRPC server is stopped first, + // the HTTP server may still be running to accept requests. s.Server.Stop() + + group := threading.NewRoutineGroup() + for _, conn := range s.conns { + // new variable to avoid closure problems, can be removed after go 1.22 + // see https://golang.org/doc/faq#closures_and_goroutines + conn := conn + group.Run(func() { + // ignore the error when closing the connection + _ = conn.Conn().Close() + }) + } + group.Wait() } func (s *Server) build() error { @@ -71,6 +89,7 @@ func (s *Server) build() error { } else { cli = zrpc.MustNewClient(up.Grpc) } + s.conns = append(s.conns, cli) source, err := s.createDescriptorSource(cli, up) if err != nil { diff --git a/gateway/server_test.go b/gateway/server_test.go index 74168559..68b56ade 100644 --- a/gateway/server_test.go +++ b/gateway/server_test.go @@ -46,7 +46,7 @@ func dialer() func(context.Context, string) (net.Conn, error) { func TestMustNewServer(t *testing.T) { var c GatewayConf assert.NoError(t, conf.FillDefault(&c)) - // avoid popup alert on macos for asking permissions + // avoid popup alert on MacOS for asking permissions c.DevServer.Host = "localhost" c.Host = "localhost" c.Port = 18881 From 22a41cacc772de869adc7020481ef7c60be8b6b1 Mon Sep 17 00:00:00 2001 From: Qiying Wang <781345688@qq.com> Date: Wed, 1 Jan 2025 19:48:53 +0800 Subject: [PATCH 14/22] feat: add ProcConf to make SetTimeToForceQuit configurable (#4446) --- core/proc/shutdown.go | 22 ++++++++++++++-------- core/proc/shutdown_test.go | 9 +++++++++ core/service/serviceconf.go | 2 ++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/core/proc/shutdown.go b/core/proc/shutdown.go index 71ce429a..d5072cb1 100644 --- a/core/proc/shutdown.go +++ b/core/proc/shutdown.go @@ -13,16 +13,17 @@ import ( "github.com/zeromicro/go-zero/core/threading" ) -const ( - wrapUpTime = time.Second - // why we use 5500 milliseconds is because most of our queue are blocking mode with 5 seconds - waitTime = 5500 * time.Millisecond -) +type ProcConf struct { + WrapUpTime time.Duration `json:",default=1s"` + WaitTime time.Duration `json:",default=5.5s"` +} var ( - wrapUpListeners = new(listenerManager) - shutdownListeners = new(listenerManager) - delayTimeBeforeForceQuit = waitTime + wrapUpListeners = new(listenerManager) + shutdownListeners = new(listenerManager) + wrapUpTime = time.Second + // why we use 5500 milliseconds is because most of our queue are blocking mode with 5 seconds + delayTimeBeforeForceQuit = 5500 * time.Millisecond ) // AddShutdownListener adds fn as a shutdown listener. @@ -42,6 +43,11 @@ func SetTimeToForceQuit(duration time.Duration) { delayTimeBeforeForceQuit = duration } +func Setup(conf ProcConf) { + wrapUpTime = conf.WrapUpTime + delayTimeBeforeForceQuit = conf.WaitTime +} + // Shutdown calls the registered shutdown listeners, only for test purpose. func Shutdown() { shutdownListeners.notifyListeners() diff --git a/core/proc/shutdown_test.go b/core/proc/shutdown_test.go index 64517f0f..d5f5869b 100644 --- a/core/proc/shutdown_test.go +++ b/core/proc/shutdown_test.go @@ -95,3 +95,12 @@ func TestNotifyMoreThanOnce(t *testing.T) { t.Fatal("timeout, check error logs") } } + +func TestSetup(t *testing.T) { + Setup(ProcConf{ + WrapUpTime: time.Second * 2, + WaitTime: time.Second * 30, + }) + assert.Equal(t, time.Second*2, wrapUpTime) + assert.Equal(t, time.Second*30, delayTimeBeforeForceQuit) +} diff --git a/core/service/serviceconf.go b/core/service/serviceconf.go index a4999ab8..fc66e91e 100644 --- a/core/service/serviceconf.go +++ b/core/service/serviceconf.go @@ -37,6 +37,7 @@ type ( Prometheus prometheus.Config `json:",optional"` Telemetry trace.Config `json:",optional"` DevServer DevServerConfig `json:",optional"` + Proc proc.ProcConf `json:",optional"` } ) @@ -61,6 +62,7 @@ func (sc ServiceConf) SetUp() error { sc.Telemetry.Name = sc.Name } trace.StartAgent(sc.Telemetry) + proc.Setup(sc.Proc) proc.AddShutdownListener(func() { trace.StopAgent() }) From 28a001c5f943f9c988500ed1890c7030a7ddb1a0 Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Wed, 1 Jan 2025 20:46:51 +0800 Subject: [PATCH 15/22] chore: refactor shutdown config, to prevent setting zero values (#4533) Signed-off-by: kevin --- core/proc/shutdown.go | 46 +++++++++++++++++++++--------- core/proc/shutdown_test.go | 56 +++++++++++++++++++++++++++++++------ core/service/serviceconf.go | 4 +-- 3 files changed, 83 insertions(+), 23 deletions(-) diff --git a/core/proc/shutdown.go b/core/proc/shutdown.go index d5072cb1..e3fa7174 100644 --- a/core/proc/shutdown.go +++ b/core/proc/shutdown.go @@ -13,19 +13,26 @@ import ( "github.com/zeromicro/go-zero/core/threading" ) -type ProcConf struct { - WrapUpTime time.Duration `json:",default=1s"` - WaitTime time.Duration `json:",default=5.5s"` -} +const ( + defaultWrapUpTime = time.Second + // why we use 5500 milliseconds is because most of our queue are blocking mode with 5 seconds + defaultWaitTime = 5500 * time.Millisecond +) var ( wrapUpListeners = new(listenerManager) shutdownListeners = new(listenerManager) - wrapUpTime = time.Second - // why we use 5500 milliseconds is because most of our queue are blocking mode with 5 seconds - delayTimeBeforeForceQuit = 5500 * time.Millisecond + wrapUpTime = defaultWrapUpTime + waitTime = defaultWaitTime + shutdownLock sync.Mutex ) +// ShutdownConf defines the shutdown configuration for the process. +type ShutdownConf struct { + WrapUpTime time.Duration `json:",default=1s"` + WaitTime time.Duration `json:",default=5.5s"` +} + // AddShutdownListener adds fn as a shutdown listener. // The returned func can be used to wait for fn getting called. func AddShutdownListener(fn func()) (waitForCalled func()) { @@ -40,12 +47,21 @@ func AddWrapUpListener(fn func()) (waitForCalled func()) { // SetTimeToForceQuit sets the waiting time before force quitting. func SetTimeToForceQuit(duration time.Duration) { - delayTimeBeforeForceQuit = duration + shutdownLock.Lock() + defer shutdownLock.Unlock() + waitTime = duration } -func Setup(conf ProcConf) { - wrapUpTime = conf.WrapUpTime - delayTimeBeforeForceQuit = conf.WaitTime +func Setup(conf ShutdownConf) { + shutdownLock.Lock() + defer shutdownLock.Unlock() + + if conf.WrapUpTime > 0 { + wrapUpTime = conf.WrapUpTime + } + if conf.WaitTime > 0 { + waitTime = conf.WaitTime + } } // Shutdown calls the registered shutdown listeners, only for test purpose. @@ -67,8 +83,12 @@ func gracefulStop(signals chan os.Signal, sig syscall.Signal) { time.Sleep(wrapUpTime) go shutdownListeners.notifyListeners() - time.Sleep(delayTimeBeforeForceQuit - wrapUpTime) - logx.Infof("Still alive after %v, going to force kill the process...", delayTimeBeforeForceQuit) + shutdownLock.Lock() + remainingTime := waitTime - wrapUpTime + shutdownLock.Unlock() + + time.Sleep(remainingTime) + logx.Infof("Still alive after %v, going to force kill the process...", waitTime) _ = syscall.Kill(syscall.Getpid(), sig) } diff --git a/core/proc/shutdown_test.go b/core/proc/shutdown_test.go index d5f5869b..4f3aed59 100644 --- a/core/proc/shutdown_test.go +++ b/core/proc/shutdown_test.go @@ -11,8 +11,12 @@ import ( ) func TestShutdown(t *testing.T) { + t.Cleanup(restoreSettings) + SetTimeToForceQuit(time.Hour) - assert.Equal(t, time.Hour, delayTimeBeforeForceQuit) + shutdownLock.Lock() + assert.Equal(t, time.Hour, waitTime) + shutdownLock.Unlock() var val int called := AddWrapUpListener(func() { @@ -31,8 +35,12 @@ func TestShutdown(t *testing.T) { } func TestShutdownWithMultipleServices(t *testing.T) { + t.Cleanup(restoreSettings) + SetTimeToForceQuit(time.Hour) - assert.Equal(t, time.Hour, delayTimeBeforeForceQuit) + shutdownLock.Lock() + assert.Equal(t, time.Hour, waitTime) + shutdownLock.Unlock() var val int32 called1 := AddShutdownListener(func() { @@ -49,8 +57,12 @@ func TestShutdownWithMultipleServices(t *testing.T) { } func TestWrapUpWithMultipleServices(t *testing.T) { + t.Cleanup(restoreSettings) + SetTimeToForceQuit(time.Hour) - assert.Equal(t, time.Hour, delayTimeBeforeForceQuit) + shutdownLock.Lock() + assert.Equal(t, time.Hour, waitTime) + shutdownLock.Unlock() var val int32 called1 := AddWrapUpListener(func() { @@ -67,6 +79,8 @@ func TestWrapUpWithMultipleServices(t *testing.T) { } func TestNotifyMoreThanOnce(t *testing.T) { + t.Cleanup(restoreSettings) + ch := make(chan struct{}, 1) go func() { @@ -97,10 +111,36 @@ func TestNotifyMoreThanOnce(t *testing.T) { } func TestSetup(t *testing.T) { - Setup(ProcConf{ - WrapUpTime: time.Second * 2, - WaitTime: time.Second * 30, + t.Run("valid time", func(t *testing.T) { + defer restoreSettings() + + Setup(ShutdownConf{ + WrapUpTime: time.Second * 2, + WaitTime: time.Second * 30, + }) + + shutdownLock.Lock() + assert.Equal(t, time.Second*2, wrapUpTime) + assert.Equal(t, time.Second*30, waitTime) + shutdownLock.Unlock() + }) + + t.Run("valid time", func(t *testing.T) { + defer restoreSettings() + + Setup(ShutdownConf{}) + + shutdownLock.Lock() + assert.Equal(t, defaultWrapUpTime, wrapUpTime) + assert.Equal(t, defaultWaitTime, waitTime) + shutdownLock.Unlock() }) - assert.Equal(t, time.Second*2, wrapUpTime) - assert.Equal(t, time.Second*30, delayTimeBeforeForceQuit) +} + +func restoreSettings() { + shutdownLock.Lock() + defer shutdownLock.Unlock() + + wrapUpTime = defaultWrapUpTime + waitTime = defaultWaitTime } diff --git a/core/service/serviceconf.go b/core/service/serviceconf.go index fc66e91e..bc3ab967 100644 --- a/core/service/serviceconf.go +++ b/core/service/serviceconf.go @@ -37,7 +37,7 @@ type ( Prometheus prometheus.Config `json:",optional"` Telemetry trace.Config `json:",optional"` DevServer DevServerConfig `json:",optional"` - Proc proc.ProcConf `json:",optional"` + Shutdown proc.ShutdownConf `json:",optional"` } ) @@ -62,7 +62,7 @@ func (sc ServiceConf) SetUp() error { sc.Telemetry.Name = sc.Name } trace.StartAgent(sc.Telemetry) - proc.Setup(sc.Proc) + proc.Setup(sc.Shutdown) proc.AddShutdownListener(func() { trace.StopAgent() }) From 48d21ef8adf6f910142473ee1f02767f6b4cbfc3 Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Wed, 1 Jan 2025 20:49:45 +0800 Subject: [PATCH 16/22] chore: add comments (#4534) --- core/proc/shutdown.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/proc/shutdown.go b/core/proc/shutdown.go index e3fa7174..402b9ce1 100644 --- a/core/proc/shutdown.go +++ b/core/proc/shutdown.go @@ -14,8 +14,10 @@ import ( ) const ( + // defaultWrapUpTime is the default time to wait before calling wrap up listeners. defaultWrapUpTime = time.Second - // why we use 5500 milliseconds is because most of our queue are blocking mode with 5 seconds + // defaultWaitTime is the default time to wait before force quitting. + // why we use 5500 milliseconds is because most of our queues are blocking mode with 5 seconds defaultWaitTime = 5500 * time.Millisecond ) @@ -29,8 +31,10 @@ var ( // ShutdownConf defines the shutdown configuration for the process. type ShutdownConf struct { + // WrapUpTime is the time to wait before calling shutdown listeners. WrapUpTime time.Duration `json:",default=1s"` - WaitTime time.Duration `json:",default=5.5s"` + // WaitTime is the time to wait before force quitting. + WaitTime time.Duration `json:",default=5.5s"` } // AddShutdownListener adds fn as a shutdown listener. From cfe03ea9e1c2beb388a3ab991baec6694ea65adf Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Thu, 2 Jan 2025 00:26:08 +0800 Subject: [PATCH 17/22] chore: update goctl deps (#4535) --- tools/goctl/go.mod | 6 +++--- tools/goctl/go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/goctl/go.mod b/tools/goctl/go.mod index e3e7237c..d9743443 100644 --- a/tools/goctl/go.mod +++ b/tools/goctl/go.mod @@ -15,7 +15,7 @@ require ( github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 github.com/zeromicro/antlr v0.0.1 github.com/zeromicro/ddl-parser v1.0.5 - github.com/zeromicro/go-zero v1.7.4 + github.com/zeromicro/go-zero v1.7.5 golang.org/x/text v0.21.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.36.1 @@ -25,7 +25,7 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect - github.com/alicebob/miniredis/v2 v2.33.0 // indirect + github.com/alicebob/miniredis/v2 v2.34.0 // indirect github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect @@ -94,7 +94,7 @@ require ( go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.31.0 // indirect - golang.org/x/net v0.31.0 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect diff --git a/tools/goctl/go.sum b/tools/goctl/go.sum index 0a9986e7..01bdfed5 100644 --- a/tools/goctl/go.sum +++ b/tools/goctl/go.sum @@ -4,8 +4,8 @@ github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7Oputl github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE= github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis/v2 v2.33.0 h1:uvTF0EDeu9RLnUEG27Db5I68ESoIxTiXbNUiji6lZrA= -github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQevyi/DJpoj6mi0= +github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0= +github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec h1:EEyRvzmpEUZ+I8WmD5cw/vY8EqhambkOqy5iFr0908A= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -174,8 +174,8 @@ github.com/zeromicro/antlr v0.0.1 h1:CQpIn/dc0pUjgGQ81y98s/NGOm2Hfru2NNio2I9mQgk github.com/zeromicro/antlr v0.0.1/go.mod h1:nfpjEwFR6Q4xGDJMcZnCL9tEfQRgszMwu3rDz2Z+p5M= github.com/zeromicro/ddl-parser v1.0.5 h1:LaVqHdzMTjasua1yYpIYaksxKqRzFrEukj2Wi2EbWaQ= github.com/zeromicro/ddl-parser v1.0.5/go.mod h1:ISU/8NuPyEpl9pa17Py9TBPetMjtsiHrb9f5XGiYbo8= -github.com/zeromicro/go-zero v1.7.4 h1:lyIUsqbpVRzM4NmXu5pRM3XrdRdUuWOkQmHiNmJF0VU= -github.com/zeromicro/go-zero v1.7.4/go.mod h1:jmv4hTdUBkDn6kxgI+WrKQw0q6LKxDElGPMfCLOeeEY= +github.com/zeromicro/go-zero v1.7.5 h1:B7Z2WszPQXHRhZTFbNQEt5Did2i/1jKTk8qNVRVQyY8= +github.com/zeromicro/go-zero v1.7.5/go.mod h1:SmGykRm5e0Z4CGNj+GaSKDffaHzQV56fel0FkymTLlE= go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk= go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM= go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA= @@ -226,8 +226,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From 6a0c8047f4167f6627504a69acd2b52be5fc534d Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Thu, 2 Jan 2025 06:22:26 +0000 Subject: [PATCH 18/22] fix compiling error for goctl on windows --- core/proc/shutdown+polyfill.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/proc/shutdown+polyfill.go b/core/proc/shutdown+polyfill.go index 58752a83..c8ef0000 100644 --- a/core/proc/shutdown+polyfill.go +++ b/core/proc/shutdown+polyfill.go @@ -4,6 +4,9 @@ package proc import "time" + +type ShutdownConf {} + // AddShutdownListener returns fn itself on windows, lets callers call fn on their own. func AddShutdownListener(fn func()) func() { return fn @@ -18,6 +21,10 @@ func AddWrapUpListener(fn func()) func() { func SetTimeToForceQuit(duration time.Duration) { } +// Setup does nothing on windows. +func Setup() { +} + // Shutdown does nothing on windows. func Shutdown() { } From acd2b94bd91c99a72a416a3498b560bfb8573c6b Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Thu, 2 Jan 2025 06:22:26 +0000 Subject: [PATCH 19/22] fix compiling error for goctl on windows --- core/proc/shutdown+polyfill.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/proc/shutdown+polyfill.go b/core/proc/shutdown+polyfill.go index c8ef0000..8fe75681 100644 --- a/core/proc/shutdown+polyfill.go +++ b/core/proc/shutdown+polyfill.go @@ -4,8 +4,8 @@ package proc import "time" - -type ShutdownConf {} +// ShutdownConf is empty on windows. +type ShutdownConf struct {} // AddShutdownListener returns fn itself on windows, lets callers call fn on their own. func AddShutdownListener(fn func()) func() { @@ -25,6 +25,10 @@ func SetTimeToForceQuit(duration time.Duration) { func Setup() { } +// Setup does nothing on windows. +func Setup() { +} + // Shutdown does nothing on windows. func Shutdown() { } From e89e2d8a7529fb17b691876d254597b0b7cc0790 Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Thu, 2 Jan 2025 07:00:42 +0000 Subject: [PATCH 20/22] fix compiling error on windows. --- core/proc/shutdown+polyfill.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/proc/shutdown+polyfill.go b/core/proc/shutdown+polyfill.go index 8fe75681..23128740 100644 --- a/core/proc/shutdown+polyfill.go +++ b/core/proc/shutdown+polyfill.go @@ -25,10 +25,6 @@ func SetTimeToForceQuit(duration time.Duration) { func Setup() { } -// Setup does nothing on windows. -func Setup() { -} - // Shutdown does nothing on windows. func Shutdown() { } From cdd068575c19a82f7d0a62dc79a56e34a7d8dc77 Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Thu, 2 Jan 2025 22:12:10 +0800 Subject: [PATCH 21/22] fix: goctl compile error on windows (#4538) --- core/proc/shutdown+polyfill.go | 4 ++-- tools/goctl/internal/version/version.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/proc/shutdown+polyfill.go b/core/proc/shutdown+polyfill.go index 23128740..f371f579 100644 --- a/core/proc/shutdown+polyfill.go +++ b/core/proc/shutdown+polyfill.go @@ -5,7 +5,7 @@ package proc import "time" // ShutdownConf is empty on windows. -type ShutdownConf struct {} +type ShutdownConf struct{} // AddShutdownListener returns fn itself on windows, lets callers call fn on their own. func AddShutdownListener(fn func()) func() { @@ -22,7 +22,7 @@ func SetTimeToForceQuit(duration time.Duration) { } // Setup does nothing on windows. -func Setup() { +func Setup(conf ShutdownConf) { } // Shutdown does nothing on windows. diff --git a/tools/goctl/internal/version/version.go b/tools/goctl/internal/version/version.go index bdbbc628..a9df8ca4 100644 --- a/tools/goctl/internal/version/version.go +++ b/tools/goctl/internal/version/version.go @@ -6,7 +6,7 @@ import ( ) // BuildVersion is the version of goctl. -const BuildVersion = "1.7.4" +const BuildVersion = "1.7.5" var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5} From 4ac8b492efbefb89120eef85e9fdd10b22aa3333 Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Thu, 2 Jan 2025 22:33:19 +0800 Subject: [PATCH 22/22] chore: update go-zero version (#4539) --- tools/goctl/go.mod | 2 +- tools/goctl/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/goctl/go.mod b/tools/goctl/go.mod index d9743443..67028036 100644 --- a/tools/goctl/go.mod +++ b/tools/goctl/go.mod @@ -15,7 +15,7 @@ require ( github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 github.com/zeromicro/antlr v0.0.1 github.com/zeromicro/ddl-parser v1.0.5 - github.com/zeromicro/go-zero v1.7.5 + github.com/zeromicro/go-zero v1.7.6 golang.org/x/text v0.21.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.36.1 diff --git a/tools/goctl/go.sum b/tools/goctl/go.sum index 01bdfed5..902122d3 100644 --- a/tools/goctl/go.sum +++ b/tools/goctl/go.sum @@ -174,8 +174,8 @@ github.com/zeromicro/antlr v0.0.1 h1:CQpIn/dc0pUjgGQ81y98s/NGOm2Hfru2NNio2I9mQgk github.com/zeromicro/antlr v0.0.1/go.mod h1:nfpjEwFR6Q4xGDJMcZnCL9tEfQRgszMwu3rDz2Z+p5M= github.com/zeromicro/ddl-parser v1.0.5 h1:LaVqHdzMTjasua1yYpIYaksxKqRzFrEukj2Wi2EbWaQ= github.com/zeromicro/ddl-parser v1.0.5/go.mod h1:ISU/8NuPyEpl9pa17Py9TBPetMjtsiHrb9f5XGiYbo8= -github.com/zeromicro/go-zero v1.7.5 h1:B7Z2WszPQXHRhZTFbNQEt5Did2i/1jKTk8qNVRVQyY8= -github.com/zeromicro/go-zero v1.7.5/go.mod h1:SmGykRm5e0Z4CGNj+GaSKDffaHzQV56fel0FkymTLlE= +github.com/zeromicro/go-zero v1.7.6 h1:SArK4xecdrpVY3ZFJcbc0IZCx+NuWyHNjCv9f1+Gwrc= +github.com/zeromicro/go-zero v1.7.6/go.mod h1:SmGykRm5e0Z4CGNj+GaSKDffaHzQV56fel0FkymTLlE= go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk= go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM= go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA=