genpacket.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. package tsgen
  2. import (
  3. _ "embed"
  4. "fmt"
  5. "path"
  6. "strings"
  7. "text/template"
  8. "github.com/zeromicro/go-zero/tools/goctl/api/spec"
  9. apiutil "github.com/zeromicro/go-zero/tools/goctl/api/util"
  10. "github.com/zeromicro/go-zero/tools/goctl/util"
  11. "github.com/zeromicro/go-zero/tools/goctl/util/pathx"
  12. )
  13. //go:embed handler.tpl
  14. var handlerTemplate string
  15. func genHandler(dir, webAPI, caller string, api *spec.ApiSpec, unwrapAPI bool) error {
  16. filename := strings.Replace(api.Service.Name, "-api", "", 1) + ".ts"
  17. if err := pathx.RemoveIfExist(path.Join(dir, filename)); err != nil {
  18. return err
  19. }
  20. fp, created, err := apiutil.MaybeCreateFile(dir, "", filename)
  21. if err != nil {
  22. return err
  23. }
  24. if !created {
  25. return nil
  26. }
  27. defer fp.Close()
  28. imports := ""
  29. if len(caller) == 0 {
  30. caller = "webapi"
  31. }
  32. importCaller := caller
  33. if unwrapAPI {
  34. importCaller = "{ " + importCaller + " }"
  35. }
  36. if len(webAPI) > 0 {
  37. imports += `import ` + importCaller + ` from ` + "\"" + webAPI + "\""
  38. }
  39. if len(api.Types) != 0 {
  40. if len(imports) > 0 {
  41. imports += pathx.NL
  42. }
  43. outputFile := apiutil.ComponentName(api)
  44. imports += fmt.Sprintf(`import * as components from "%s"`, "./"+outputFile)
  45. imports += fmt.Sprintf(`%sexport * from "%s"`, pathx.NL, "./"+outputFile)
  46. }
  47. apis, err := genAPI(api, caller)
  48. if err != nil {
  49. return err
  50. }
  51. t := template.Must(template.New("handlerTemplate").Parse(handlerTemplate))
  52. return t.Execute(fp, map[string]string{
  53. "imports": imports,
  54. "apis": strings.TrimSpace(apis),
  55. })
  56. }
  57. func genAPI(api *spec.ApiSpec, caller string) (string, error) {
  58. var builder strings.Builder
  59. for _, group := range api.Service.Groups {
  60. for _, route := range group.Routes {
  61. handler := route.Handler
  62. if len(handler) == 0 {
  63. return "", fmt.Errorf("missing handler annotation for route %q", route.Path)
  64. }
  65. handler = util.Untitle(handler)
  66. handler = strings.Replace(handler, "Handler", "", 1)
  67. comment := commentForRoute(route)
  68. if len(comment) > 0 {
  69. fmt.Fprintf(&builder, "%s\n", comment)
  70. }
  71. fmt.Fprintf(&builder, "export function %s(%s) {\n", handler, paramsForRoute(route))
  72. writeIndent(&builder, 1)
  73. responseGeneric := "<null>"
  74. if len(route.ResponseTypeName()) > 0 {
  75. val, err := goTypeToTs(route.ResponseType, true)
  76. if err != nil {
  77. return "", err
  78. }
  79. responseGeneric = fmt.Sprintf("<%s>", val)
  80. }
  81. fmt.Fprintf(&builder, `return %s.%s%s(%s)`, caller, strings.ToLower(route.Method),
  82. util.Title(responseGeneric), callParamsForRoute(route, group))
  83. builder.WriteString("\n}\n\n")
  84. }
  85. }
  86. apis := builder.String()
  87. return apis, nil
  88. }
  89. func paramsForRoute(route spec.Route) string {
  90. if route.RequestType == nil {
  91. return ""
  92. }
  93. hasParams := pathHasParams(route)
  94. hasBody := hasRequestBody(route)
  95. hasHeader := hasRequestHeader(route)
  96. hasPath := hasRequestPath(route)
  97. rt, err := goTypeToTs(route.RequestType, true)
  98. if err != nil {
  99. fmt.Println(err.Error())
  100. return ""
  101. }
  102. var params []string
  103. if hasParams {
  104. params = append(params, fmt.Sprintf("params: %s", rt+"Params"))
  105. }
  106. if hasBody {
  107. params = append(params, fmt.Sprintf("req: %s", rt))
  108. }
  109. if hasHeader {
  110. params = append(params, fmt.Sprintf("headers: %s", rt+"Headers"))
  111. }
  112. if hasPath {
  113. ds, ok := route.RequestType.(spec.DefineStruct)
  114. if !ok {
  115. fmt.Printf("invalid route.RequestType: {%v}\n", route.RequestType)
  116. }
  117. members := ds.GetTagMembers(pathTagKey)
  118. for _, member := range members {
  119. tags := member.Tags()
  120. if len(tags) > 0 && tags[0].Key == pathTagKey {
  121. valueType, err := goTypeToTs(member.Type, false)
  122. if err != nil {
  123. fmt.Println(err.Error())
  124. return ""
  125. }
  126. params = append(params, fmt.Sprintf("%s: %s", tags[0].Name, valueType))
  127. }
  128. }
  129. }
  130. return strings.Join(params, ", ")
  131. }
  132. func commentForRoute(route spec.Route) string {
  133. var builder strings.Builder
  134. comment := route.JoinedDoc()
  135. builder.WriteString("/**")
  136. builder.WriteString("\n * @description " + comment)
  137. hasParams := pathHasParams(route)
  138. hasBody := hasRequestBody(route)
  139. hasHeader := hasRequestHeader(route)
  140. if hasParams {
  141. builder.WriteString("\n * @param params")
  142. }
  143. if hasBody {
  144. builder.WriteString("\n * @param req")
  145. }
  146. if hasHeader {
  147. builder.WriteString("\n * @param headers")
  148. }
  149. builder.WriteString("\n */")
  150. return builder.String()
  151. }
  152. func callParamsForRoute(route spec.Route, group spec.Group) string {
  153. hasParams := pathHasParams(route)
  154. hasBody := hasRequestBody(route)
  155. hasHeader := hasRequestHeader(route)
  156. var params = []string{pathForRoute(route, group)}
  157. if hasParams {
  158. params = append(params, "params")
  159. }
  160. if hasBody {
  161. params = append(params, "req")
  162. }
  163. if hasHeader {
  164. params = append(params, "headers")
  165. }
  166. return strings.Join(params, ", ")
  167. }
  168. func pathForRoute(route spec.Route, group spec.Group) string {
  169. prefix := group.GetAnnotation(pathPrefix)
  170. routePath := route.Path
  171. if strings.Contains(routePath, ":") {
  172. pathSlice := strings.Split(routePath, "/")
  173. for i, part := range pathSlice {
  174. if strings.Contains(part, ":") {
  175. pathSlice[i] = fmt.Sprintf("${%s}", part[1:])
  176. }
  177. }
  178. routePath = strings.Join(pathSlice, "/")
  179. }
  180. if len(prefix) == 0 {
  181. return "`" + routePath + "`"
  182. }
  183. prefix = strings.TrimPrefix(prefix, `"`)
  184. prefix = strings.TrimSuffix(prefix, `"`)
  185. return fmt.Sprintf("`%s/%s`", prefix, strings.TrimPrefix(routePath, "/"))
  186. }
  187. func pathHasParams(route spec.Route) bool {
  188. ds, ok := route.RequestType.(spec.DefineStruct)
  189. if !ok {
  190. return false
  191. }
  192. return len(ds.Members) != len(ds.GetBodyMembers())
  193. }
  194. func hasRequestBody(route spec.Route) bool {
  195. ds, ok := route.RequestType.(spec.DefineStruct)
  196. if !ok {
  197. return false
  198. }
  199. return len(route.RequestTypeName()) > 0 && len(ds.GetBodyMembers()) > 0
  200. }
  201. func hasRequestPath(route spec.Route) bool {
  202. ds, ok := route.RequestType.(spec.DefineStruct)
  203. if !ok {
  204. return false
  205. }
  206. return len(route.RequestTypeName()) > 0 && len(ds.GetTagMembers(pathTagKey)) > 0
  207. }
  208. func hasRequestHeader(route spec.Route) bool {
  209. ds, ok := route.RequestType.(spec.DefineStruct)
  210. if !ok {
  211. return false
  212. }
  213. return len(route.RequestTypeName()) > 0 && len(ds.GetTagMembers(headerTagKey)) > 0
  214. }