Compare commits

...

7 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
03fd74b955 docs: regenerate swagger example files with fixed array definitions
The swagger generation code already contains the fix for array definitions
with useDefinitions=true (from PR #5216). The fix ensures that when arrays
contain structs, the $ref is placed inside items rather than at the schema level.

However, the example swagger files were not regenerated after the fix,
so they still showed the old incorrect structure. This commit regenerates
the example files to reflect the corrected behavior.

Co-authored-by: kevwan <1918356+kevwan@users.noreply.github.com>
2025-10-07 02:36:07 +00:00
copilot-swe-agent[bot]
f8716fe6fa Initial plan 2025-10-07 02:28:40 +00:00
Kevin Wan
cf21cb2b0b chore: refactor to remove duplicated code (#5216) 2025-10-06 22:24:44 +08:00
Copilot
61e8894c31 Fix swagger generation: info block and server tags not included (#5215)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kevwan <1918356+kevwan@users.noreply.github.com>
2025-10-06 22:02:42 +08:00
Copilot
7a6c3c8129 Fix swagger path generation: remove trailing slash for root routes with prefix (#5212) 2025-10-05 12:12:03 +08:00
Rizky Ikwan
875fec3e1a chore: fix typos (#5210) 2025-10-04 02:56:07 +00:00
Kevin Wan
60128c2100 chore: update goctl version (#5205) 2025-10-02 22:48:57 +08:00
12 changed files with 271 additions and 5691 deletions

View File

@@ -92,7 +92,7 @@ Port: 0
Path: "/",
Handler: nil,
}, WithJwt("thesecret"), WithSignature(SignatureConf{}),
WithJwtTransition("preivous", "thenewone"))
WithJwtTransition("previous", "thenewone"))
func() {
defer func() {

View File

@@ -8,70 +8,66 @@ import (
)
func getBoolFromKVOrDefault(properties map[string]string, key string, def bool) bool {
if len(properties) == 0 {
return def
}
md := metadata.New(properties)
val := md.Get(key)
if len(val) == 0 {
return def
}
//I think this function and those below should handle error, but they didn't.
//Since a default value (def) is provided, any parsing errors will result in the default being returned.
str, err := strconv.Unquote(val[0])
if err != nil || len(str) == 0 {
return def
}
res, _ := strconv.ParseBool(str)
return res
}
return getOrDefault(properties, key, def, func(str string, def bool) bool {
res, err := strconv.ParseBool(str)
if err != nil {
return def
}
func getStringFromKVOrDefault(properties map[string]string, key string, def string) string {
if len(properties) == 0 {
return def
}
md := metadata.New(properties)
val := md.Get(key)
if len(val) == 0 {
return def
}
str, err := strconv.Unquote(val[0])
if err != nil || len(str) == 0 {
return def
}
return str
}
func getListFromInfoOrDefault(properties map[string]string, key string, def []string) []string {
if len(properties) == 0 {
return def
}
md := metadata.New(properties)
val := md.Get(key)
if len(val) == 0 {
return def
}
str, err := strconv.Unquote(val[0])
if err != nil || len(str) == 0 {
return def
}
resp := util.FieldsAndTrimSpace(str, commaRune)
if len(resp) == 0 {
return def
}
return resp
return res
})
}
func getFirstUsableString(def ...string) string {
if len(def) == 0 {
return ""
}
for _, val := range def {
str, err := strconv.Unquote(val)
if err == nil && len(str) != 0 {
return str
}
}
return ""
}
func getListFromInfoOrDefault(properties map[string]string, key string, def []string) []string {
return getOrDefault(properties, key, def, func(str string, def []string) []string {
resp := util.FieldsAndTrimSpace(str, commaRune)
if len(resp) == 0 {
return def
}
return resp
})
}
// getOrDefault abstracts the common logic for fetching, unquoting, and defaulting.
func getOrDefault[T any](properties map[string]string, key string, def T, convert func(string, T) T) T {
if len(properties) == 0 {
return def
}
md := metadata.New(properties)
val := md.Get(key)
if len(val) == 0 {
return def
}
str := val[0]
if unquoted, err := strconv.Unquote(str); err == nil {
str = unquoted
}
if len(str) == 0 {
return def
}
return convert(str, def)
}
func getStringFromKVOrDefault(properties map[string]string, key string, def string) string {
return getOrDefault(properties, key, def, func(str string, def string) string {
return str
})
}

View File

@@ -21,6 +21,19 @@ func Test_getBoolFromKVOrDefault(t *testing.T) {
assert.False(t, getBoolFromKVOrDefault(properties, "empty_value", false))
assert.False(t, getBoolFromKVOrDefault(nil, "nil", false))
assert.False(t, getBoolFromKVOrDefault(map[string]string{}, "empty", false))
// Test with unquoted values (as stored by RawText())
unquotedProperties := map[string]string{
"enabled": "true",
"disabled": "false",
"invalid": "notabool",
"empty_value": "",
}
assert.True(t, getBoolFromKVOrDefault(unquotedProperties, "enabled", false))
assert.False(t, getBoolFromKVOrDefault(unquotedProperties, "disabled", true))
assert.False(t, getBoolFromKVOrDefault(unquotedProperties, "invalid", false))
assert.False(t, getBoolFromKVOrDefault(unquotedProperties, "empty_value", false))
}
func Test_getStringFromKVOrDefault(t *testing.T) {
@@ -34,6 +47,17 @@ func Test_getStringFromKVOrDefault(t *testing.T) {
assert.Equal(t, "default", getStringFromKVOrDefault(properties, "missing", "default"))
assert.Equal(t, "default", getStringFromKVOrDefault(nil, "nil", "default"))
assert.Equal(t, "default", getStringFromKVOrDefault(map[string]string{}, "empty", "default"))
// Test with unquoted values (as stored by RawText())
unquotedProperties := map[string]string{
"name": "example",
"title": "Demo API",
"empty": "",
}
assert.Equal(t, "example", getStringFromKVOrDefault(unquotedProperties, "name", "default"))
assert.Equal(t, "Demo API", getStringFromKVOrDefault(unquotedProperties, "title", "default"))
assert.Equal(t, "default", getStringFromKVOrDefault(unquotedProperties, "empty", "default"))
}
func Test_getListFromInfoOrDefault(t *testing.T) {
@@ -50,4 +74,18 @@ func Test_getListFromInfoOrDefault(t *testing.T) {
assert.Equal(t, []string{"default"}, getListFromInfoOrDefault(map[string]string{
"foo": ",,",
}, "foo", []string{"default"}))
// Test with unquoted values (as stored by RawText())
unquotedProperties := map[string]string{
"list": "a, b, c",
"schemes": "http,https",
"tags": "query",
"empty": "",
}
// Note: FieldsAndTrimSpace doesn't actually trim the spaces from returned values
assert.Equal(t, []string{"a", " b", " c"}, getListFromInfoOrDefault(unquotedProperties, "list", []string{"default"}))
assert.Equal(t, []string{"http", "https"}, getListFromInfoOrDefault(unquotedProperties, "schemes", []string{"default"}))
assert.Equal(t, []string{"query"}, getListFromInfoOrDefault(unquotedProperties, "tags", []string{"default"}))
assert.Equal(t, []string{"default"}, getListFromInfoOrDefault(unquotedProperties, "empty", []string{"default"}))
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,11 @@ func spec2Paths(ctx Context, srv apiSpec.Service) *spec.Paths {
for _, route := range group.Routes {
routPath := pathVariable2SwaggerVariable(ctx, route.Path)
if len(prefix) > 0 && prefix != "." {
routPath = "/" + path.Clean(prefix) + routPath
if routPath == "/" {
routPath = "/" + path.Clean(prefix)
} else {
routPath = "/" + path.Clean(prefix) + routPath
}
}
pathItem := spec2Path(ctx, group, route)
existPathItem, ok := paths.Paths[routPath]

View File

@@ -0,0 +1,90 @@
package swagger
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/tools/goctl/api/spec"
)
func TestSpec2PathsWithRootRoute(t *testing.T) {
tests := []struct {
name string
prefix string
routePath string
expectedPath string
}{
{
name: "prefix with root route",
prefix: "/api/v1/shoppings",
routePath: "/",
expectedPath: "/api/v1/shoppings",
},
{
name: "prefix with sub route",
prefix: "/api/v1/shoppings",
routePath: "/list",
expectedPath: "/api/v1/shoppings/list",
},
{
name: "empty prefix with root route",
prefix: "",
routePath: "/",
expectedPath: "/",
},
{
name: "empty prefix with sub route",
prefix: "",
routePath: "/list",
expectedPath: "/list",
},
{
name: "prefix with trailing slash and root route",
prefix: "/api/v1/shoppings/",
routePath: "/",
expectedPath: "/api/v1/shoppings",
},
{
name: "prefix without leading slash and root route",
prefix: "api/v1/shoppings",
routePath: "/",
expectedPath: "/api/v1/shoppings",
},
{
name: "single level prefix with root route",
prefix: "/api",
routePath: "/",
expectedPath: "/api",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
srv := spec.Service{
Groups: []spec.Group{
{
Annotation: spec.Annotation{
Properties: map[string]string{
propertyKeyPrefix: tt.prefix,
},
},
Routes: []spec.Route{
{
Method: "get",
Path: tt.routePath,
Handler: "TestHandler",
},
},
},
},
}
ctx := testingContext(t)
paths := spec2Paths(ctx, srv)
assert.Contains(t, paths.Paths, tt.expectedPath,
"Expected path %s not found in generated paths. Got: %v",
tt.expectedPath, paths.Paths)
})
}
}

View File

@@ -16,7 +16,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.9.0
github.com/zeromicro/go-zero v1.9.1
golang.org/x/text v0.22.0
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.36.5
@@ -47,8 +47,8 @@ require (
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grafana/pyroscope-go v1.2.4 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/grafana/pyroscope-go v1.2.7 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
@@ -72,7 +72,7 @@ require (
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/redis/go-redis/v9 v9.12.1 // indirect
github.com/redis/go-redis/v9 v9.15.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect

View File

@@ -75,10 +75,10 @@ github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
github.com/grafana/pyroscope-go v1.2.4 h1:B22GMXz+O0nWLatxLuaP7o7L9dvP0clLvIpmeEQQM0Q=
github.com/grafana/pyroscope-go v1.2.4/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grafana/pyroscope-go v1.2.7 h1:VWBBlqxjyR0Cwk2W6UrE8CdcdD80GOFNutj0Kb1T8ac=
github.com/grafana/pyroscope-go v1.2.7/go.mod h1:o/bpSLiJYYP6HQtvcoVKiE9s5RiNgjYTj1DhiddP2Pc=
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og=
github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
@@ -148,8 +148,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg=
github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/redis/go-redis/v9 v9.15.0 h1:2jdes0xJxer4h3NUZrZ4OGSntGlXp4WbXju2nOTRXto=
github.com/redis/go-redis/v9 v9.15.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -185,8 +185,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.9.0 h1:hlVtQCSHPszQdcwZTawzGwTej1G2mhHybYzMRLuwCt4=
github.com/zeromicro/go-zero v1.9.0/go.mod h1:TMyCxiaOjLQ3YxyYlJrejaQZF40RlzQ3FVvFu5EbcV4=
github.com/zeromicro/go-zero v1.9.1 h1:GZCl4jun/ZgZHnSvX3SSNDHf+tEGmEQ8x2Z23xjHa9g=
github.com/zeromicro/go-zero v1.9.1/go.mod h1:bHOl7Xr7EV/iHZWEqsUNJwFc/9WgAMrPpPagYvOaMtY=
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=

View File

@@ -6,7 +6,7 @@ import (
)
// BuildVersion is the version of goctl.
const BuildVersion = "1.9.1-alpha"
const BuildVersion = "1.9.1"
var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-beta": 2, "beta": 3, "released": 4, "": 5}

View File

@@ -5,7 +5,7 @@ import (
"github.com/zeromicro/go-zero/tools/goctl/util/format"
)
// BeforeCommands run before comamnd run to show some migration notes
// BeforeCommands run before command run to show some migration notes
func BeforeCommands(dir, style string) error {
if err := migrateBefore1_3_4(dir, style); err != nil {
return err

View File

@@ -43,7 +43,7 @@ func Install(cacheDir string) (string, error) {
case vars.OsLinux:
downloadUrl = url[fmt.Sprintf("%s_%d", vars.OsLinux, bit)]
default:
return "", fmt.Errorf("unsupport OS: %q", goos)
return "", fmt.Errorf("unsupported OS: %q", goos)
}
err := downloader.Download(downloadUrl, tempFile)