mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-11 00:40:00 +08:00
(goctl)feature: supported sse generation (#5082)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -15,8 +15,12 @@ import (
|
|||||||
|
|
||||||
const defaultLogicPackage = "logic"
|
const defaultLogicPackage = "logic"
|
||||||
|
|
||||||
//go:embed handler.tpl
|
var (
|
||||||
var handlerTemplate string
|
//go:embed handler.tpl
|
||||||
|
handlerTemplate string
|
||||||
|
//go:embed sse_handler.tpl
|
||||||
|
sseHandlerTemplate string
|
||||||
|
)
|
||||||
|
|
||||||
func genHandler(dir, rootPkg string, cfg *config.Config, group spec.Group, route spec.Route) error {
|
func genHandler(dir, rootPkg string, cfg *config.Config, group spec.Group, route spec.Route) error {
|
||||||
handler := getHandlerName(route)
|
handler := getHandlerName(route)
|
||||||
@@ -32,6 +36,12 @@ func genHandler(dir, rootPkg string, cfg *config.Config, group spec.Group, route
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var builtinTemplate = handlerTemplate
|
||||||
|
sse := group.GetAnnotation("sse")
|
||||||
|
if sse == "true" {
|
||||||
|
builtinTemplate = sseHandlerTemplate
|
||||||
|
}
|
||||||
|
|
||||||
return genFile(fileGenConfig{
|
return genFile(fileGenConfig{
|
||||||
dir: dir,
|
dir: dir,
|
||||||
subdir: getHandlerFolderPath(group, route),
|
subdir: getHandlerFolderPath(group, route),
|
||||||
@@ -39,12 +49,13 @@ func genHandler(dir, rootPkg string, cfg *config.Config, group spec.Group, route
|
|||||||
templateName: "handlerTemplate",
|
templateName: "handlerTemplate",
|
||||||
category: category,
|
category: category,
|
||||||
templateFile: handlerTemplateFile,
|
templateFile: handlerTemplateFile,
|
||||||
builtinTemplate: handlerTemplate,
|
builtinTemplate: builtinTemplate,
|
||||||
data: map[string]any{
|
data: map[string]any{
|
||||||
"PkgName": pkgName,
|
"PkgName": pkgName,
|
||||||
"ImportPackages": genHandlerImports(group, route, rootPkg),
|
"ImportPackages": genHandlerImports(group, route, rootPkg),
|
||||||
"HandlerName": handler,
|
"HandlerName": handler,
|
||||||
"RequestType": util.Title(route.RequestTypeName()),
|
"RequestType": util.Title(route.RequestTypeName()),
|
||||||
|
"ResponseType": responseGoTypeName(route, typesPacket),
|
||||||
"LogicName": logicName,
|
"LogicName": logicName,
|
||||||
"LogicType": strings.Title(getLogicName(route)),
|
"LogicType": strings.Title(getLogicName(route)),
|
||||||
"Call": strings.Title(strings.TrimSuffix(handler, "Handler")),
|
"Call": strings.Title(strings.TrimSuffix(handler, "Handler")),
|
||||||
@@ -73,7 +84,8 @@ func genHandlerImports(group spec.Group, route spec.Route, parentPkg string) str
|
|||||||
fmt.Sprintf("\"%s\"", pathx.JoinPackages(parentPkg, getLogicFolderPath(group, route))),
|
fmt.Sprintf("\"%s\"", pathx.JoinPackages(parentPkg, getLogicFolderPath(group, route))),
|
||||||
fmt.Sprintf("\"%s\"", pathx.JoinPackages(parentPkg, contextDir)),
|
fmt.Sprintf("\"%s\"", pathx.JoinPackages(parentPkg, contextDir)),
|
||||||
}
|
}
|
||||||
if len(route.RequestTypeName()) > 0 {
|
sse := group.GetAnnotation("sse")
|
||||||
|
if len(route.RequestTypeName()) > 0 || sse == "true" {
|
||||||
imports = append(imports, fmt.Sprintf("\"%s\"\n", pathx.JoinPackages(parentPkg, typesDir)))
|
imports = append(imports, fmt.Sprintf("\"%s\"\n", pathx.JoinPackages(parentPkg, typesDir)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,13 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/tools/goctl/vars"
|
"github.com/zeromicro/go-zero/tools/goctl/vars"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed logic.tpl
|
var (
|
||||||
var logicTemplate string
|
//go:embed logic.tpl
|
||||||
|
logicTemplate string
|
||||||
|
|
||||||
|
//go:embed sse_logic.tpl
|
||||||
|
sseLogicTemplate string
|
||||||
|
)
|
||||||
|
|
||||||
func genLogic(dir, rootPkg string, cfg *config.Config, api *spec.ApiSpec) error {
|
func genLogic(dir, rootPkg string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||||
for _, g := range api.Service.Groups {
|
for _, g := range api.Service.Groups {
|
||||||
@@ -54,6 +59,20 @@ func genLogicByRoute(dir, rootPkg string, cfg *config.Config, group spec.Group,
|
|||||||
}
|
}
|
||||||
|
|
||||||
subDir := getLogicFolderPath(group, route)
|
subDir := getLogicFolderPath(group, route)
|
||||||
|
builtinTemplate := logicTemplate
|
||||||
|
sse := group.GetAnnotation("sse")
|
||||||
|
if sse == "true" {
|
||||||
|
builtinTemplate = sseLogicTemplate
|
||||||
|
responseString = "error"
|
||||||
|
returnString = "return nil"
|
||||||
|
resp := responseGoTypeName(route, typesPacket)
|
||||||
|
if len(requestString) == 0 {
|
||||||
|
requestString = "client chan<- " + resp
|
||||||
|
} else {
|
||||||
|
requestString += ", client chan<- " + resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return genFile(fileGenConfig{
|
return genFile(fileGenConfig{
|
||||||
dir: dir,
|
dir: dir,
|
||||||
subdir: subDir,
|
subdir: subDir,
|
||||||
@@ -61,7 +80,7 @@ func genLogicByRoute(dir, rootPkg string, cfg *config.Config, group spec.Group,
|
|||||||
templateName: "logicTemplate",
|
templateName: "logicTemplate",
|
||||||
category: category,
|
category: category,
|
||||||
templateFile: logicTemplateFile,
|
templateFile: logicTemplateFile,
|
||||||
builtinTemplate: logicTemplate,
|
builtinTemplate: builtinTemplate,
|
||||||
data: map[string]any{
|
data: map[string]any{
|
||||||
"pkgName": subDir[strings.LastIndex(subDir, "/")+1:],
|
"pkgName": subDir[strings.LastIndex(subDir, "/")+1:],
|
||||||
"imports": imports,
|
"imports": imports,
|
||||||
|
|||||||
63
tools/goctl/api/gogen/sse_handler.tpl
Normal file
63
tools/goctl/api/gogen/sse_handler.tpl
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package {{.PkgName}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logc"
|
||||||
|
"github.com/zeromicro/go-zero/core/threading"
|
||||||
|
{{if .HasRequest}}"github.com/zeromicro/go-zero/rest/httpx"{{end}}
|
||||||
|
{{.ImportPackages}}
|
||||||
|
)
|
||||||
|
|
||||||
|
{{if .HasDoc}}{{.Doc}}{{end}}
|
||||||
|
func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
{{if .HasRequest}}var req types.{{.RequestType}}
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
httpx.ErrorCtx(r.Context(), w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
{{end}}// Buffer size of 16 is chosen as a reasonable default to balance throughput and memory usage.
|
||||||
|
// You can change this based on your application's needs.
|
||||||
|
// if your go-zero version less than 1.8.1, you need to add 3 lines below.
|
||||||
|
// w.Header().Set("Content-Type", "text/event-stream")
|
||||||
|
// w.Header().Set("Cache-Control", "no-cache")
|
||||||
|
// w.Header().Set("Connection", "keep-alive")
|
||||||
|
client := make(chan {{.ResponseType}}, 16)
|
||||||
|
defer func() {
|
||||||
|
close(client)
|
||||||
|
}()
|
||||||
|
l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx)
|
||||||
|
threading.GoSafeCtx(r.Context(), func() {
|
||||||
|
err := l.{{.Call}}({{if .HasRequest}}&req, {{end}}client)
|
||||||
|
if err != nil {
|
||||||
|
logc.Errorw(r.Context(), "{{.HandlerName}}", logc.Field("error", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case data := <-client:
|
||||||
|
output, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
logc.Errorw(r.Context(), "{{.HandlerName}}", logc.Field("error", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := fmt.Fprintf(w, "data: %s\n\n", string(output)); err != nil {
|
||||||
|
logc.Errorw(r.Context(), "{{.HandlerName}}", logc.Field("error", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if flusher, ok := w.(http.Flusher); ok {
|
||||||
|
flusher.Flush()
|
||||||
|
}
|
||||||
|
case <-r.Context().Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
tools/goctl/api/gogen/sse_logic.tpl
Normal file
26
tools/goctl/api/gogen/sse_logic.tpl
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package {{.pkgName}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
{{.imports}}
|
||||||
|
)
|
||||||
|
|
||||||
|
type {{.logic}} struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
{{if .hasDoc}}{{.doc}}{{end}}
|
||||||
|
func New{{.logic}}(ctx context.Context, svcCtx *svc.ServiceContext) *{{.logic}} {
|
||||||
|
return &{{.logic}}{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
{{.returnString}}
|
||||||
|
}
|
||||||
@@ -12,8 +12,10 @@ const (
|
|||||||
contextTemplateFile = "context.tpl"
|
contextTemplateFile = "context.tpl"
|
||||||
etcTemplateFile = "etc.tpl"
|
etcTemplateFile = "etc.tpl"
|
||||||
handlerTemplateFile = "handler.tpl"
|
handlerTemplateFile = "handler.tpl"
|
||||||
|
sseHandlerTemplateFile = "sse_handler.tpl"
|
||||||
handlerTestTemplateFile = "handler_test.tpl"
|
handlerTestTemplateFile = "handler_test.tpl"
|
||||||
logicTemplateFile = "logic.tpl"
|
logicTemplateFile = "logic.tpl"
|
||||||
|
sseLogicTemplateFile = "sse_logic.tpl"
|
||||||
logicTestTemplateFile = "logic_test.tpl"
|
logicTestTemplateFile = "logic_test.tpl"
|
||||||
mainTemplateFile = "main.tpl"
|
mainTemplateFile = "main.tpl"
|
||||||
middlewareImplementCodeFile = "middleware.tpl"
|
middlewareImplementCodeFile = "middleware.tpl"
|
||||||
@@ -27,8 +29,10 @@ var templates = map[string]string{
|
|||||||
contextTemplateFile: contextTemplate,
|
contextTemplateFile: contextTemplate,
|
||||||
etcTemplateFile: etcTemplate,
|
etcTemplateFile: etcTemplate,
|
||||||
handlerTemplateFile: handlerTemplate,
|
handlerTemplateFile: handlerTemplate,
|
||||||
|
sseHandlerTemplateFile: sseHandlerTemplate,
|
||||||
handlerTestTemplateFile: handlerTestTemplate,
|
handlerTestTemplateFile: handlerTestTemplate,
|
||||||
logicTemplateFile: logicTemplate,
|
logicTemplateFile: logicTemplate,
|
||||||
|
sseLogicTemplateFile: sseLogicTemplate,
|
||||||
logicTestTemplateFile: logicTestTemplate,
|
logicTestTemplateFile: logicTestTemplate,
|
||||||
mainTemplateFile: mainTemplate,
|
mainTemplateFile: mainTemplate,
|
||||||
middlewareImplementCodeFile: middlewareImplementCode,
|
middlewareImplementCodeFile: middlewareImplementCode,
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// BuildVersion is the version of goctl.
|
// BuildVersion is the version of goctl.
|
||||||
const BuildVersion = "1.8.5"
|
const BuildVersion = "1.8.6-alpha"
|
||||||
|
|
||||||
var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5}
|
var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-beta": 2, "beta": 3, "released": 4, "": 5}
|
||||||
|
|
||||||
// GetGoctlVersion returns BuildVersion
|
// GetGoctlVersion returns BuildVersion
|
||||||
func GetGoctlVersion() string {
|
func GetGoctlVersion() string {
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ func (a *Analyzer) fillService() error {
|
|||||||
group.Annotation.Properties = a.convertKV(item.AtServerStmt.Values)
|
group.Annotation.Properties = a.convertKV(item.AtServerStmt.Values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sse := group.GetAnnotation("sse") == "true"
|
||||||
for _, astRoute := range item.Routes {
|
for _, astRoute := range item.Routes {
|
||||||
head, leading := astRoute.CommentGroup()
|
head, leading := astRoute.CommentGroup()
|
||||||
route := spec.Route{
|
route := spec.Route{
|
||||||
@@ -277,6 +278,13 @@ func (a *Analyzer) fillService() error {
|
|||||||
}
|
}
|
||||||
route.ResponseType = responseType
|
route.ResponseType = responseType
|
||||||
}
|
}
|
||||||
|
if route.ResponseType == nil && sse {
|
||||||
|
if route.RequestType != nil {
|
||||||
|
return ast.SyntaxError(astRoute.Route.Request.Pos(), "missing response type")
|
||||||
|
} else {
|
||||||
|
return ast.SyntaxError(astRoute.Route.Path.Pos(), "missing response type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := a.fillRouteType(&route); err != nil {
|
if err := a.fillRouteType(&route); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
Reference in New Issue
Block a user