genroutes.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. package gogen
  2. import (
  3. "fmt"
  4. "os"
  5. "path"
  6. "sort"
  7. "strconv"
  8. "strings"
  9. "text/template"
  10. "time"
  11. "github.com/wuntsong-org/go-zero-plus/core/collection"
  12. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/api/spec"
  13. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/config"
  14. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/util/format"
  15. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/util/pathx"
  16. "github.com/wuntsong-org/go-zero-plus/tools/goctlwt/vars"
  17. )
  18. const (
  19. jwtTransKey = "jwtTransition"
  20. routesFilename = "routes"
  21. routesTemplate = `// Code generated by goctl. DO NOT EDIT.
  22. package handler
  23. import (
  24. "net/http"{{if .hasTimeout}}
  25. "time"{{end}}
  26. {{.importPackages}}
  27. )
  28. func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
  29. {{.routesAdditions}}
  30. }
  31. `
  32. routesAdditionTemplate = `
  33. server.AddRoutes(
  34. {{.routes}} {{.jwt}}{{.signature}} {{.prefix}} {{.timeout}} {{.maxBytes}}
  35. )
  36. `
  37. timeoutThreshold = time.Millisecond
  38. )
  39. var mapping = map[string]string{
  40. "delete": "http.MethodDelete",
  41. "get": "http.MethodGet",
  42. "head": "http.MethodHead",
  43. "post": "http.MethodPost",
  44. "put": "http.MethodPut",
  45. "patch": "http.MethodPatch",
  46. "connect": "http.MethodConnect",
  47. "options": "http.MethodOptions",
  48. "trace": "http.MethodTrace",
  49. }
  50. type (
  51. group struct {
  52. routes []route
  53. jwtEnabled bool
  54. signatureEnabled bool
  55. authName string
  56. timeout string
  57. middlewares []string
  58. prefix string
  59. jwtTrans string
  60. maxBytes string
  61. }
  62. route struct {
  63. method string
  64. path string
  65. handler string
  66. }
  67. )
  68. func genRoutes(dir, rootPkg string, cfg *config.Config, api *spec.ApiSpec) error {
  69. var builder strings.Builder
  70. groups, err := getRoutes(api)
  71. if err != nil {
  72. return err
  73. }
  74. templateText, err := pathx.LoadTemplate(category, routesAdditionTemplateFile, routesAdditionTemplate)
  75. if err != nil {
  76. return err
  77. }
  78. var hasTimeout bool
  79. gt := template.Must(template.New("groupTemplate").Parse(templateText))
  80. for _, g := range groups {
  81. var gbuilder strings.Builder
  82. gbuilder.WriteString("[]rest.Route{")
  83. for _, r := range g.routes {
  84. fmt.Fprintf(&gbuilder, `
  85. {
  86. Method: %s,
  87. Path: "%s",
  88. Handler: %s,
  89. },`,
  90. r.method, r.path, r.handler)
  91. }
  92. var jwt string
  93. if g.jwtEnabled {
  94. jwt = fmt.Sprintf("\n rest.WithJwt(serverCtx.Config.%s.AccessSecret),", g.authName)
  95. }
  96. if len(g.jwtTrans) > 0 {
  97. jwt = jwt + fmt.Sprintf("\n rest.WithJwtTransition(serverCtx.Config.%s.PrevSecret,serverCtx.Config.%s.Secret),", g.jwtTrans, g.jwtTrans)
  98. }
  99. var signature, prefix string
  100. if g.signatureEnabled {
  101. signature = "\n rest.WithSignature(serverCtx.Config.Signature),"
  102. }
  103. if len(g.prefix) > 0 {
  104. prefix = fmt.Sprintf(`
  105. rest.WithPrefix("%s"),`, g.prefix)
  106. }
  107. var timeout string
  108. if len(g.timeout) > 0 {
  109. duration, err := time.ParseDuration(g.timeout)
  110. if err != nil {
  111. return err
  112. }
  113. // why we check this, maybe some users set value 1, it's 1ns, not 1s.
  114. if duration < timeoutThreshold {
  115. return fmt.Errorf("timeout should not less than 1ms, now %v", duration)
  116. }
  117. timeout = fmt.Sprintf("\n rest.WithTimeout(%d * time.Millisecond),", duration.Milliseconds())
  118. hasTimeout = true
  119. }
  120. var maxBytes string
  121. if len(g.maxBytes) > 0 {
  122. _, err := strconv.ParseInt(g.maxBytes, 10, 64)
  123. if err != nil {
  124. return fmt.Errorf("maxBytes %s parse error,it is an invalid number", g.maxBytes)
  125. }
  126. maxBytes = fmt.Sprintf("\n rest.WithMaxBytes(%s),", g.maxBytes)
  127. }
  128. var routes string
  129. if len(g.middlewares) > 0 {
  130. gbuilder.WriteString("\n}...,")
  131. params := g.middlewares
  132. for i := range params {
  133. params[i] = "serverCtx." + params[i]
  134. }
  135. middlewareStr := strings.Join(params, ", ")
  136. routes = fmt.Sprintf("rest.WithMiddlewares(\n[]rest.Middleware{ %s }, \n %s \n),",
  137. middlewareStr, strings.TrimSpace(gbuilder.String()))
  138. } else {
  139. gbuilder.WriteString("\n},")
  140. routes = strings.TrimSpace(gbuilder.String())
  141. }
  142. if err := gt.Execute(&builder, map[string]string{
  143. "routes": routes,
  144. "jwt": jwt,
  145. "signature": signature,
  146. "prefix": prefix,
  147. "timeout": timeout,
  148. "maxBytes": maxBytes,
  149. }); err != nil {
  150. return err
  151. }
  152. }
  153. routeFilename, err := format.FileNamingFormat(cfg.NamingFormat, routesFilename)
  154. if err != nil {
  155. return err
  156. }
  157. routeFilename = routeFilename + ".go"
  158. filename := path.Join(dir, handlerDir, routeFilename)
  159. os.Remove(filename)
  160. return genFile(fileGenConfig{
  161. dir: dir,
  162. subdir: handlerDir,
  163. filename: routeFilename,
  164. templateName: "routesTemplate",
  165. category: category,
  166. templateFile: routesTemplateFile,
  167. builtinTemplate: routesTemplate,
  168. data: map[string]any{
  169. "hasTimeout": hasTimeout,
  170. "importPackages": genRouteImports(rootPkg, api),
  171. "routesAdditions": strings.TrimSpace(builder.String()),
  172. },
  173. })
  174. }
  175. func genRouteImports(parentPkg string, api *spec.ApiSpec) string {
  176. importSet := collection.NewSet()
  177. importSet.AddStr(fmt.Sprintf("\"%s\"", pathx.JoinPackages(parentPkg, contextDir)))
  178. for _, group := range api.Service.Groups {
  179. for _, route := range group.Routes {
  180. folder := route.GetAnnotation(groupProperty)
  181. if len(folder) == 0 {
  182. folder = group.GetAnnotation(groupProperty)
  183. if len(folder) == 0 {
  184. continue
  185. }
  186. }
  187. importSet.AddStr(fmt.Sprintf("%s \"%s\"", toPrefix(folder),
  188. pathx.JoinPackages(parentPkg, handlerDir, folder)))
  189. }
  190. }
  191. imports := importSet.KeysStr()
  192. sort.Strings(imports)
  193. projectSection := strings.Join(imports, "\n\t")
  194. depSection := fmt.Sprintf("\"%s/rest\"", vars.ProjectOpenSourceURL)
  195. return fmt.Sprintf("%s\n\n\t%s", projectSection, depSection)
  196. }
  197. func getRoutes(api *spec.ApiSpec) ([]group, error) {
  198. var routes []group
  199. for _, g := range api.Service.Groups {
  200. var groupedRoutes group
  201. for _, r := range g.Routes {
  202. handler := getHandlerName(r)
  203. handler = handler + "(serverCtx)"
  204. folder := r.GetAnnotation(groupProperty)
  205. if len(folder) > 0 {
  206. handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:]
  207. } else {
  208. folder = g.GetAnnotation(groupProperty)
  209. if len(folder) > 0 {
  210. handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:]
  211. }
  212. }
  213. groupedRoutes.routes = append(groupedRoutes.routes, route{
  214. method: mapping[r.Method],
  215. path: r.Path,
  216. handler: handler,
  217. })
  218. }
  219. groupedRoutes.timeout = g.GetAnnotation("timeout")
  220. groupedRoutes.maxBytes = g.GetAnnotation("maxBytes")
  221. jwt := g.GetAnnotation("jwt")
  222. if len(jwt) > 0 {
  223. groupedRoutes.authName = jwt
  224. groupedRoutes.jwtEnabled = true
  225. }
  226. jwtTrans := g.GetAnnotation(jwtTransKey)
  227. if len(jwtTrans) > 0 {
  228. groupedRoutes.jwtTrans = jwtTrans
  229. }
  230. signature := g.GetAnnotation("signature")
  231. if signature == "true" {
  232. groupedRoutes.signatureEnabled = true
  233. }
  234. middleware := g.GetAnnotation("middleware")
  235. if len(middleware) > 0 {
  236. groupedRoutes.middlewares = append(groupedRoutes.middlewares,
  237. strings.Split(middleware, ",")...)
  238. }
  239. prefix := g.GetAnnotation(spec.RoutePrefixKey)
  240. prefix = strings.ReplaceAll(prefix, `"`, "")
  241. prefix = strings.TrimSpace(prefix)
  242. if len(prefix) > 0 {
  243. prefix = path.Join("/", prefix)
  244. groupedRoutes.prefix = prefix
  245. }
  246. routes = append(routes, groupedRoutes)
  247. }
  248. return routes, nil
  249. }
  250. func toPrefix(folder string) string {
  251. return strings.ReplaceAll(folder, "/", "")
  252. }