From 9d6c8f67f50dc43513b6c912fdb692c52f98303a Mon Sep 17 00:00:00 2001 From: Zlx <362211854@qq.com> Date: Sun, 28 Aug 2022 21:51:27 +0800 Subject: [PATCH] generates nested types in doc (#2201) Co-authored-by: Link_Zhao --- tools/goctl/api/docgen/doc.go | 17 +++-- tools/goctl/api/gogen/gentypes.go | 18 ++--- tools/goctl/api/gogen/testdata/api_common.api | 3 + .../gogen/testdata/api_has_nested_type.api | 48 ++++++++++++ tools/goctl/api/gogen/util.go | 74 +++++++++++++++++-- 5 files changed, 134 insertions(+), 26 deletions(-) create mode 100644 tools/goctl/api/gogen/testdata/api_common.api create mode 100644 tools/goctl/api/gogen/testdata/api_has_nested_type.api diff --git a/tools/goctl/api/docgen/doc.go b/tools/goctl/api/docgen/doc.go index 2ef66959..ffa0e183 100644 --- a/tools/goctl/api/docgen/doc.go +++ b/tools/goctl/api/docgen/doc.go @@ -4,6 +4,7 @@ import ( "bytes" _ "embed" "fmt" + "go/format" "html/template" "strconv" "strings" @@ -35,12 +36,12 @@ func genDoc(api *spec.ApiSpec, dir, filename string) error { routeComment = "N/A" } - requestContent, err := buildDoc(route.RequestType) + requestContent, err := buildDoc(route.RequestType, api) if err != nil { return err } - responseContent, err := buildDoc(route.ResponseType) + responseContent, err := buildDoc(route.ResponseType, api) if err != nil { return err } @@ -60,7 +61,6 @@ func genDoc(api *spec.ApiSpec, dir, filename string) error { if err != nil { return err } - builder.Write(tmplBytes.Bytes()) } @@ -68,7 +68,7 @@ func genDoc(api *spec.ApiSpec, dir, filename string) error { return err } -func buildDoc(route spec.Type) (string, error) { +func buildDoc(route spec.Type, api *spec.ApiSpec) (string, error) { if route == nil || len(route.Name()) == 0 { return "", nil } @@ -78,12 +78,15 @@ func buildDoc(route spec.Type) (string, error) { if definedType, ok := route.(spec.DefineStruct); ok { associatedTypes(definedType, &tps) } - value, err := gogen.BuildTypes(tps) + value, err := gogen.BuildTypes(tps, api) if err != nil { return "", err } - - return fmt.Sprintf("\n\n```golang\n%s\n```\n", value), nil + formatted, err := format.Source([]byte(value)) + if err != nil { + return "", err + } + return fmt.Sprintf("\n\n```golang\n%s\n```\n", string(formatted)), nil } func associatedTypes(tp spec.DefineStruct, tps *[]spec.Type) { diff --git a/tools/goctl/api/gogen/gentypes.go b/tools/goctl/api/gogen/gentypes.go index 9e91735f..3b2e53ad 100644 --- a/tools/goctl/api/gogen/gentypes.go +++ b/tools/goctl/api/gogen/gentypes.go @@ -21,7 +21,7 @@ const typesFile = "types" var typesTemplate string // BuildTypes gen types to string -func BuildTypes(types []spec.Type) (string, error) { +func BuildTypes(types []spec.Type, api *spec.ApiSpec) (string, error) { var builder strings.Builder first := true for _, tp := range types { @@ -30,7 +30,7 @@ func BuildTypes(types []spec.Type) (string, error) { } else { builder.WriteString("\n\n") } - if err := writeType(&builder, tp); err != nil { + if err := writeType(&builder, tp, api); err != nil { return "", apiutil.WrapErr(err, "Type "+tp.Name()+" generate error") } } @@ -39,7 +39,7 @@ func BuildTypes(types []spec.Type) (string, error) { } func genTypes(dir string, cfg *config.Config, api *spec.ApiSpec) error { - val, err := BuildTypes(api.Types) + val, err := BuildTypes(api.Types, api) if err != nil { return err } @@ -68,7 +68,7 @@ func genTypes(dir string, cfg *config.Config, api *spec.ApiSpec) error { }) } -func writeType(writer io.Writer, tp spec.Type) error { +func writeType(writer io.Writer, tp spec.Type, api *spec.ApiSpec) error { structType, ok := tp.(spec.DefineStruct) if !ok { return fmt.Errorf("unspport struct type: %s", tp.Name()) @@ -76,15 +76,7 @@ func writeType(writer io.Writer, tp spec.Type) error { fmt.Fprintf(writer, "type %s struct {\n", util.Title(tp.Name())) for _, member := range structType.Members { - if member.IsInline { - if _, err := fmt.Fprintf(writer, "%s\n", strings.Title(member.Type.Name())); err != nil { - return err - } - - continue - } - - if err := writeProperty(writer, member.Name, member.Tag, member.GetComment(), member.Type, 1); err != nil { + if err := writeProperty(writer, member.Name, member.Tag, member.GetComment(), member.Type, 1, api); err != nil { return err } } diff --git a/tools/goctl/api/gogen/testdata/api_common.api b/tools/goctl/api/gogen/testdata/api_common.api new file mode 100644 index 00000000..3cdfe869 --- /dev/null +++ b/tools/goctl/api/gogen/testdata/api_common.api @@ -0,0 +1,3 @@ +type Common { + Some string `json:"some"` // some imported type +} \ No newline at end of file diff --git a/tools/goctl/api/gogen/testdata/api_has_nested_type.api b/tools/goctl/api/gogen/testdata/api_has_nested_type.api new file mode 100644 index 00000000..e75ae13d --- /dev/null +++ b/tools/goctl/api/gogen/testdata/api_has_nested_type.api @@ -0,0 +1,48 @@ +import "./api_common.api" +type Resp { + R1 string `json:"r1"` + R2 bool `json:"r2"` + R3 Common `json:"r3"` +} +type CommonResponse { + Code int `json:"code"` // 100 | 200 + Message string `json:"message"` + Common + Response Resp `json:"response"` +} + +type LoginResponseDto { + Id string `bson:"_id" json:"id"` + Name string `json:"name"` + AccessToken string `json:"accessToken"` +} + +type UserCreateDto { + Id string `json:"id"` +} +type LoginRequest { + Username string `json:"username,optional"` // user name is optional + Password string `json:"password,optional"` +} +type LoginResponse { + *CommonResponse + Result *LoginResponseDto `json:"result"` +} + +type UserCreateRequest { + Username string `json:"username,optional"` // some dsada + Password string `json:"password,optional"` +} + +type UserCreateResponse { + *CommonResponse + Result *UserCreateDto `json:"result"` // result +} + +service user-api { + @handler UserHandler + post /user/signin(UserCreateRequest) returns (UserCreateResponse) + + @handler LoginHandler + post /user/login(LoginRequest) returns (LoginResponse) +} \ No newline at end of file diff --git a/tools/goctl/api/gogen/util.go b/tools/goctl/api/gogen/util.go index a8798ebd..82e259e3 100644 --- a/tools/goctl/api/gogen/util.go +++ b/tools/goctl/api/gogen/util.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io" + "os" "strings" "text/template" @@ -57,15 +58,21 @@ func genFile(c fileGenConfig) error { return err } -func writeProperty(writer io.Writer, name, tag, comment string, tp spec.Type, indent int) error { +func writeProperty(writer io.Writer, name, tag, comment string, tp spec.Type, indent int, api *spec.ApiSpec) error { util.WriteIndent(writer, indent) var err error - if len(comment) > 0 { - comment = strings.TrimPrefix(comment, "//") - comment = "//" + comment - _, err = fmt.Fprintf(writer, "%s %s %s %s\n", strings.Title(name), tp.Name(), tag, comment) + var refPropertyName = tp.Name() + if isCustomType(refPropertyName) { + strs := getRefProperty(api, refPropertyName, name) + _, err = fmt.Fprintf(writer, "%s\n", strs) } else { - _, err = fmt.Fprintf(writer, "%s %s %s\n", strings.Title(name), tp.Name(), tag) + if len(comment) > 0 { + comment = strings.TrimPrefix(comment, "//") + comment = "//" + comment + _, err = fmt.Fprintf(writer, "%s %s %s %s\n", strings.Title(name), tp.Name(), tag, comment) + } else { + _, err = fmt.Fprintf(writer, "%s %s %s\n", strings.Title(name), tp.Name(), tag) + } } return err @@ -181,3 +188,58 @@ func golangExpr(ty spec.Type, pkg ...string) string { return "" } + +func isCustomType(t string) bool { + var builtinType = []string{"string", "bool", "int", "uint", "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "float32", "float64", "uintptr", "complex64", "complex128"} + var is bool = true + for _, v := range builtinType { + if t == v { + is = false + break + } + } + return is +} + +// Generate nested types recursively +func getRefProperty(api *spec.ApiSpec, refPropertyName string, name string) string { + var str string = "" + for _, t := range api.Types { + if strings.TrimLeft(refPropertyName, "*") == t.Name() { + switch tm := t.(type) { + case spec.DefineStruct: + for _, m := range tm.Members { + if isCustomType(m.Type.Name()) { + // recursive + str += getRefProperty(api, m.Type.Name(), m.Name) + } else { + if len(m.Comment) > 0 { + comment := strings.TrimPrefix(m.Comment, "//") + comment = "//" + comment + str += fmt.Sprintf("%s %s %s %s\n\t", m.Name, m.Type.Name(), m.Tag, comment) + } else { + str += fmt.Sprintf("%s %s %s\n\t", m.Name, m.Type.Name(), m.Tag) + } + + } + + } + } + } + } + if name == "" { + temp := `${str}` + return os.Expand(temp, func(k string) string { + return str + }) + } else { + temp := `${name} struct { + ${str}}` + return os.Expand(temp, func(k string) string { + return map[string]string{ + "name": name, + "str": str, + }[k] + }) + } +}