Merge pull request #311 from FGYFFFF/feat/handler_by_method

feat(hz): generate a separate handler file for each method
This commit is contained in:
GuangyuFan 2022-11-17 17:36:57 +08:00 committed by GitHub
commit d6b85fec12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 195 additions and 69 deletions

View File

@ -123,6 +123,7 @@ func Init() *cli.App {
snakeNameFlag := cli.BoolFlag{Name: "snake_tag", Usage: "Use snake_case style naming for tags. (Only works for 'form', 'query', 'json')", Destination: &globalArgs.SnakeName} snakeNameFlag := cli.BoolFlag{Name: "snake_tag", Usage: "Use snake_case style naming for tags. (Only works for 'form', 'query', 'json')", Destination: &globalArgs.SnakeName}
customLayout := cli.StringFlag{Name: "customize_layout", Usage: "Specify the layout template. ({{Template Profile}}:{{Rendering Data}})", Destination: &globalArgs.CustomizeLayout} customLayout := cli.StringFlag{Name: "customize_layout", Usage: "Specify the layout template. ({{Template Profile}}:{{Rendering Data}})", Destination: &globalArgs.CustomizeLayout}
customPackage := cli.StringFlag{Name: "customize_package", Usage: "Specify the package template. ({{Template Profile}}:)", Destination: &globalArgs.CustomizePackage} customPackage := cli.StringFlag{Name: "customize_package", Usage: "Specify the package template. ({{Template Profile}}:)", Destination: &globalArgs.CustomizePackage}
handlerByMethod := cli.BoolFlag{Name: "handler_by_method", Usage: "Generate a separate handler file for each method.", Destination: &globalArgs.HandlerByMethod}
// app // app
app := cli.NewApp() app := cli.NewApp()
@ -164,6 +165,7 @@ func Init() *cli.App {
&excludeFilesFlag, &excludeFilesFlag,
&customLayout, &customLayout,
&customPackage, &customPackage,
&handlerByMethod,
&protoPluginsFlag, &protoPluginsFlag,
&thriftPluginsFlag, &thriftPluginsFlag,
}, },
@ -191,6 +193,7 @@ func Init() *cli.App {
&snakeNameFlag, &snakeNameFlag,
&excludeFilesFlag, &excludeFilesFlag,
&customPackage, &customPackage,
&handlerByMethod,
&protoPluginsFlag, &protoPluginsFlag,
&thriftPluginsFlag, &thriftPluginsFlag,
}, },

View File

@ -61,6 +61,7 @@ type Argument struct {
SnakeName bool SnakeName bool
Excludes []string Excludes []string
NoRecurse bool NoRecurse bool
HandlerByMethod bool
CustomizeLayout string CustomizeLayout string
CustomizePackage string CustomizePackage string

View File

@ -36,6 +36,7 @@ type HttpMethod struct {
ReturnTypeName string ReturnTypeName string
Path string Path string
Serializer string Serializer string
OutputDir string
// Annotations map[string]string // Annotations map[string]string
Models map[string]*model.Model Models map[string]*model.Model
} }
@ -54,29 +55,37 @@ type Client struct {
func (pkgGen *HttpPackageGenerator) genHandler(pkg *HttpPackage, handlerDir, handlerPackage string, root *RouterNode) error { func (pkgGen *HttpPackageGenerator) genHandler(pkg *HttpPackage, handlerDir, handlerPackage string, root *RouterNode) error {
for _, s := range pkg.Services { for _, s := range pkg.Services {
handler := Handler{ var handler Handler
FilePath: filepath.Join(handlerDir, util.ToSnakeCase(s.Name)+".go"), if pkgGen.HandlerByMethod { // generate handler by method
PackageName: util.SplitPackage(handlerPackage, ""), for _, m := range s.Methods {
Methods: s.Methods, handler = Handler{
} FilePath: filepath.Join(handlerDir, m.OutputDir, util.ToSnakeCase(m.Name)+".go"),
PackageName: util.SplitPackage(handlerPackage, ""),
handler.Imports = make(map[string]*model.Model, len(s.Methods)) Methods: []*HttpMethod{m},
for _, m := range s.Methods { }
for key, mm := range m.Models {
if v, ok := handler.Imports[mm.PackageName]; ok && v.Package != mm.Package { if err := pkgGen.processHandler(&handler, root, handlerDir, m.OutputDir, true); err != nil {
handler.Imports[key] = mm return fmt.Errorf("generate handler %s failed, err: %v", handler.FilePath, err.Error())
continue }
if err := pkgGen.updateHandler(handler, handlerTplName, handler.FilePath, false); err != nil {
return fmt.Errorf("generate handler %s failed, err: %v", handler.FilePath, err.Error())
} }
handler.Imports[mm.PackageName] = mm
} }
err := root.Update(m, handler.PackageName) } else { // generate handler service
if err != nil { handler = Handler{
return err FilePath: filepath.Join(handlerDir, util.ToSnakeCase(s.Name)+".go"),
PackageName: util.SplitPackage(handlerPackage, ""),
Methods: s.Methods,
}
if err := pkgGen.processHandler(&handler, root, "", "", false); err != nil {
return fmt.Errorf("generate handler %s failed, err: %v", handler.FilePath, err.Error())
}
if err := pkgGen.updateHandler(handler, handlerTplName, handler.FilePath, false); err != nil {
return fmt.Errorf("generate handler %s failed, err: %v", handler.FilePath, err.Error())
} }
}
handler.Format()
if err := pkgGen.updateHandler(handler, handlerTplName, handler.FilePath, false); err != nil {
return fmt.Errorf("generate handler %s failed, err: %v", handler.FilePath, err.Error())
} }
if len(pkgGen.ClientDir) != 0 { if len(pkgGen.ClientDir) != 0 {
@ -96,6 +105,31 @@ func (pkgGen *HttpPackageGenerator) genHandler(pkg *HttpPackage, handlerDir, han
return nil return nil
} }
func (pkgGen *HttpPackageGenerator) processHandler(handler *Handler, root *RouterNode, handlerDir, projectOutDir string, handlerByMethod bool) error {
singleHandlerPackage := ""
if handlerByMethod {
singleHandlerPackage = util.SubPackage(pkgGen.ProjPackage, filepath.Join(handlerDir, projectOutDir))
}
handler.Imports = make(map[string]*model.Model, len(handler.Methods))
for _, m := range handler.Methods {
// Iterate over the request and return parameters of the method to get import path.
for key, mm := range m.Models {
if v, ok := handler.Imports[mm.PackageName]; ok && v.Package != mm.Package {
handler.Imports[key] = mm
continue
}
handler.Imports[mm.PackageName] = mm
}
err := root.Update(m, handler.PackageName, singleHandlerPackage)
if err != nil {
return err
}
}
handler.Format()
return nil
}
func (pkgGen *HttpPackageGenerator) updateHandler(handler interface{}, handlerTpl, filePath string, noRepeat bool) error { func (pkgGen *HttpPackageGenerator) updateHandler(handler interface{}, handlerTpl, filePath string, noRepeat bool) error {
isExist, err := util.PathExist(filePath) isExist, err := util.PathExist(filePath)
if err != nil { if err != nil {

View File

@ -44,15 +44,16 @@ type Service struct {
} }
type HttpPackageGenerator struct { type HttpPackageGenerator struct {
ConfigPath string ConfigPath string
Backend meta.Backend Backend meta.Backend
Options []Option Options []Option
ProjPackage string ProjPackage string
HandlerDir string HandlerDir string
RouterDir string RouterDir string
ModelDir string ModelDir string
ClientDir string ClientDir string
NeedModel bool NeedModel bool
HandlerByMethod bool
loadedBackend Backend loadedBackend Backend
curModel *model.Model curModel *model.Model
@ -135,7 +136,11 @@ func (pkgGen *HttpPackageGenerator) Generate(pkg *HttpPackage) error {
} }
} }
// this is for handler_by_service, the handler_dir is {$HANDLER_DIR}/{$PKG}
handlerDir := util.SubDir(pkgGen.HandlerDir, pkg.Package) handlerDir := util.SubDir(pkgGen.HandlerDir, pkg.Package)
if pkgGen.HandlerByMethod {
handlerDir = pkgGen.HandlerDir
}
handlerPackage := util.SubPackage(pkgGen.ProjPackage, handlerDir) handlerPackage := util.SubPackage(pkgGen.ProjPackage, handlerDir)
routerDir := util.SubDir(pkgGen.RouterDir, pkg.Package) routerDir := util.SubDir(pkgGen.RouterDir, pkg.Package)
routerPackage := util.SubPackage(pkgGen.ProjPackage, routerDir) routerPackage := util.SubPackage(pkgGen.ProjPackage, routerDir)

View File

@ -97,7 +97,9 @@ package {{$.PackageName}}
import ( import (
"github.com/cloudwego/hertz/pkg/app/server" "github.com/cloudwego/hertz/pkg/app/server"
{{range $k, $v := .HandlerPackages}}{{$k}} "{{$v}}"{{end}} {{- range $k, $v := .HandlerPackages}}
{{$k}} "{{$v}}"
{{- end}}
) )
/* /*

View File

@ -42,8 +42,10 @@ type RouterNode struct {
Path string Path string
Children childrenRouterInfo Children childrenRouterInfo
Handler string // {{HandlerPackage}}.{{HandlerName}} Handler string // {{HandlerPackage}}.{{HandlerName}}
HttpMethod string HandlerPackage string
HandlerPackageAlias string
HttpMethod string
} }
type RegisterDependency struct { type RegisterDependency struct {
@ -64,7 +66,7 @@ func (routerNode *RouterNode) Sort() {
sort.Sort(routerNode.Children) sort.Sort(routerNode.Children)
} }
func (routerNode *RouterNode) Update(method *HttpMethod, handlerType string) error { func (routerNode *RouterNode) Update(method *HttpMethod, handlerType, handlerPkg string) error {
if method.Path == "" { if method.Path == "" {
return fmt.Errorf("empty path for method '%s'", method.Name) return fmt.Errorf("empty path for method '%s'", method.Name)
} }
@ -77,7 +79,7 @@ func (routerNode *RouterNode) Update(method *HttpMethod, handlerType string) err
return fmt.Errorf("path '%s' has been registered", method.Path) return fmt.Errorf("path '%s' has been registered", method.Path)
} }
name := util.ToVarName(paths[:last]) name := util.ToVarName(paths[:last])
parent.Insert(name, method, handlerType, paths[last:]) parent.Insert(name, method, handlerType, paths[last:], handlerPkg)
parent.Sort() parent.Sort()
return nil return nil
} }
@ -136,14 +138,36 @@ func (routerNode *RouterNode) DFS(i int, hook func(layer int, node *RouterNode)
return nil return nil
} }
func (routerNode *RouterNode) Insert(name string, method *HttpMethod, handlerType string, paths []string) { var handlerPkgMap map[string]string
func (routerNode *RouterNode) Insert(name string, method *HttpMethod, handlerType string, paths []string, handlerPkg string) {
cur := routerNode cur := routerNode
for i, p := range paths { for i, p := range paths {
c := &RouterNode{ c := &RouterNode{
Path: "/" + p, Path: "/" + p,
} }
if i == len(paths)-1 { if i == len(paths)-1 {
c.Handler = handlerType + "." + method.Name // generate handler by method
if len(handlerPkg) != 0 {
// get a unique package alias for every handler
pkgAlias := filepath.Base(handlerPkg)
pkgAlias = util.ToVarName([]string{pkgAlias})
val, exist := handlerPkgMap[handlerPkg]
if !exist {
pkgAlias, _ = util.GetHandlerPackageUniqueName(pkgAlias)
if len(handlerPkgMap) == 0 {
handlerPkgMap = make(map[string]string, 10)
}
handlerPkgMap[handlerPkg] = pkgAlias
} else {
pkgAlias = val
}
c.HandlerPackageAlias = pkgAlias
c.Handler = pkgAlias + "." + method.Name
c.HandlerPackage = handlerPkg
} else { // generate handler by service
c.Handler = handlerType + "." + method.Name
}
c.HttpMethod = getHttpMethod(method.HTTPMethod) c.HttpMethod = getHttpMethod(method.HTTPMethod)
} }
if cur.Children == nil { if cur.Children == nil {
@ -272,6 +296,19 @@ func (pkgGen *HttpPackageGenerator) genRouter(pkg *HttpPackage, root *RouterNode
}, },
Router: root, Router: root,
} }
if pkgGen.HandlerByMethod {
handlerMap := make(map[string]string, 1)
hook := func(layer int, node *RouterNode) error {
if len(node.HandlerPackage) != 0 {
handlerMap[node.HandlerPackageAlias] = node.HandlerPackage
}
return nil
}
root.DFS(0, hook)
router.HandlerPackages = handlerMap
}
if err := pkgGen.TemplateGenerator.Generate(router, routerTplName, router.FilePath, false); err != nil { if err := pkgGen.TemplateGenerator.Generate(router, routerTplName, router.FilePath, false); err != nil {
return fmt.Errorf("generate router %s failed, err: %v", router.FilePath, err.Error()) return fmt.Errorf("generate router %s failed, err: %v", router.FilePath, err.Error())
} }

View File

@ -1,17 +1,3 @@
// Copyright 2022 CloudWeGo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.28.0 // protoc-gen-go v1.28.0
@ -243,6 +229,14 @@ var file_api_proto_extTypes = []protoimpl.ExtensionInfo{
Tag: "bytes,50308,opt,name=baseurl", Tag: "bytes,50308,opt,name=baseurl",
Filename: "api.proto", Filename: "api.proto",
}, },
{
ExtendedType: (*descriptorpb.MethodOptions)(nil),
ExtensionType: (*string)(nil),
Field: 50309,
Name: "api.handler_path",
Tag: "bytes,50309,opt,name=handler_path",
Filename: "api.proto",
},
{ {
ExtendedType: (*descriptorpb.EnumValueOptions)(nil), ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
ExtensionType: (*int32)(nil), ExtensionType: (*int32)(nil),
@ -311,12 +305,14 @@ var (
E_Param = &file_api_proto_extTypes[24] // Whether client requests take public parameters E_Param = &file_api_proto_extTypes[24] // Whether client requests take public parameters
// optional string baseurl = 50308; // optional string baseurl = 50308;
E_Baseurl = &file_api_proto_extTypes[25] // Baseurl used in ttnet routing E_Baseurl = &file_api_proto_extTypes[25] // Baseurl used in ttnet routing
// optional string handler_path = 50309;
E_HandlerPath = &file_api_proto_extTypes[26] // handler_path specifies the path to generate the method
) )
// Extension fields to descriptorpb.EnumValueOptions. // Extension fields to descriptorpb.EnumValueOptions.
var ( var (
// optional int32 http_code = 50401; // optional int32 http_code = 50401;
E_HttpCode = &file_api_proto_extTypes[26] E_HttpCode = &file_api_proto_extTypes[27]
) )
var File_api_proto protoreflect.FileDescriptor var File_api_proto protoreflect.FileDescriptor
@ -416,12 +412,16 @@ var file_api_proto_rawDesc = []byte{
0x61, 0x6d, 0x3a, 0x3a, 0x0a, 0x07, 0x62, 0x61, 0x73, 0x65, 0x75, 0x72, 0x6c, 0x12, 0x1e, 0x2e, 0x61, 0x6d, 0x3a, 0x3a, 0x0a, 0x07, 0x62, 0x61, 0x73, 0x65, 0x75, 0x72, 0x6c, 0x12, 0x1e, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x84, 0x89, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x84, 0x89,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x75, 0x72, 0x6c, 0x3a, 0x40, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x75, 0x72, 0x6c, 0x3a, 0x43,
0x0a, 0x09, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x0a, 0x0c, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1e,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe1, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x85,
0x89, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x68, 0x74, 0x74, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x89, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x50,
0x42, 0x06, 0x5a, 0x04, 0x2f, 0x61, 0x70, 0x69, 0x61, 0x74, 0x68, 0x3a, 0x40, 0x0a, 0x09, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65,
0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x18, 0xe1, 0x89, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x68, 0x74, 0x74,
0x70, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x06, 0x5a, 0x04, 0x2f, 0x61, 0x70, 0x69,
} }
var file_api_proto_goTypes = []interface{}{ var file_api_proto_goTypes = []interface{}{
@ -456,11 +456,12 @@ var file_api_proto_depIdxs = []int32{
1, // 23: api.serializer:extendee -> google.protobuf.MethodOptions 1, // 23: api.serializer:extendee -> google.protobuf.MethodOptions
1, // 24: api.param:extendee -> google.protobuf.MethodOptions 1, // 24: api.param:extendee -> google.protobuf.MethodOptions
1, // 25: api.baseurl:extendee -> google.protobuf.MethodOptions 1, // 25: api.baseurl:extendee -> google.protobuf.MethodOptions
2, // 26: api.http_code:extendee -> google.protobuf.EnumValueOptions 1, // 26: api.handler_path:extendee -> google.protobuf.MethodOptions
27, // [27:27] is the sub-list for method output_type 2, // 27: api.http_code:extendee -> google.protobuf.EnumValueOptions
27, // [27:27] is the sub-list for method input_type 28, // [28:28] is the sub-list for method output_type
27, // [27:27] is the sub-list for extension type_name 28, // [28:28] is the sub-list for method input_type
0, // [0:27] is the sub-list for extension extendee 28, // [28:28] is the sub-list for extension type_name
0, // [0:28] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name 0, // [0:0] is the sub-list for field type_name
} }
@ -476,7 +477,7 @@ func file_api_proto_init() {
RawDescriptor: file_api_proto_rawDesc, RawDescriptor: file_api_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 0, NumMessages: 0,
NumExtensions: 27, NumExtensions: 28,
NumServices: 0, NumServices: 0,
}, },
GoTypes: file_api_proto_goTypes, GoTypes: file_api_proto_goTypes,

View File

@ -36,6 +36,7 @@ extend google.protobuf.MethodOptions {
optional string serializer = 50306; // Serialization method optional string serializer = 50306; // Serialization method
optional string param = 50307; // Whether client requests take public parameters optional string param = 50307; // Whether client requests take public parameters
optional string baseurl = 50308; // Baseurl used in ttnet routing optional string baseurl = 50308; // Baseurl used in ttnet routing
optional string handler_path = 50309; // handler_path specifies the path to generate the method
} }
extend google.protobuf.EnumValueOptions { extend google.protobuf.EnumValueOptions {

View File

@ -23,6 +23,7 @@ import (
"github.com/cloudwego/hertz/cmd/hz/internal/generator" "github.com/cloudwego/hertz/cmd/hz/internal/generator"
"github.com/cloudwego/hertz/cmd/hz/internal/generator/model" "github.com/cloudwego/hertz/cmd/hz/internal/generator/model"
"github.com/cloudwego/hertz/cmd/hz/internal/protobuf/api"
"github.com/cloudwego/hertz/cmd/hz/internal/util" "github.com/cloudwego/hertz/cmd/hz/internal/util"
"github.com/cloudwego/hertz/cmd/hz/internal/util/logs" "github.com/cloudwego/hertz/cmd/hz/internal/util/logs"
"github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/desc"
@ -121,6 +122,13 @@ func astToService(ast *descriptorpb.FileDescriptorProto, resolver *Resolver) ([]
} }
path := vpath.(string) path := vpath.(string)
var handlerOutDir string
genPath := checkFirstOption(api.E_HandlerPath, m.GetOptions())
handlerOutDir, ok := genPath.(string)
if !ok || len(handlerOutDir) == 0 {
handlerOutDir = ""
}
reqName := m.GetInputType() reqName := m.GetInputType()
sb, err := resolver.ResolveIdentifier(reqName) sb, err := resolver.ResolveIdentifier(reqName)
reqName = util.BaseName(sb.Scope.GetOptions().GetGoPackage(), "") + "." + sb.Name reqName = util.BaseName(sb.Scope.GetOptions().GetGoPackage(), "") + "." + sb.Name
@ -145,6 +153,7 @@ func astToService(ast *descriptorpb.FileDescriptorProto, resolver *Resolver) ([]
HTTPMethod: hmethod, HTTPMethod: hmethod,
Path: path, Path: path,
Serializer: serializer, Serializer: serializer,
OutputDir: handlerOutDir,
} }
goOptMapAlias := make(map[string]string, 1) goOptMapAlias := make(map[string]string, 1)

View File

@ -582,8 +582,9 @@ func (plugin *Plugin) genHttpPackage(ast *descriptorpb.FileDescriptorProto, deps
TemplateGenerator: generator.TemplateGenerator{ TemplateGenerator: generator.TemplateGenerator{
OutputDir: args.OutDir, OutputDir: args.OutDir,
}, },
ProjPackage: pkg, ProjPackage: pkg,
Options: options, Options: options,
HandlerByMethod: args.HandlerByMethod,
} }
if args.ModelBackend != "" { if args.ModelBackend != "" {

View File

@ -70,6 +70,17 @@ func astToService(ast *parser.Thrift, resolver *Resolver) ([]*generator.Service,
if len(rs) > 1 { if len(rs) > 1 {
return nil, fmt.Errorf("too many 'api.XXX' annotations: %s", rs) return nil, fmt.Errorf("too many 'api.XXX' annotations: %s", rs)
} }
var handlerOutDir string
genPaths := getAnnotation(m.Annotations, ApiGenPath)
if len(genPaths) == 0 {
handlerOutDir = ""
} else if len(genPaths) > 1 {
return nil, fmt.Errorf("too many 'api.handler_path' for %s", m.Name)
} else {
handlerOutDir = genPaths[0]
}
hmethod, path := util.GetFirstKV(rs) hmethod, path := util.GetFirstKV(rs)
if len(path) != 1 || path[0] == "" { if len(path) != 1 || path[0] == "" {
return nil, fmt.Errorf("invalid api.%s for %s.%s: %s", hmethod, s.Name, m.Name, path) return nil, fmt.Errorf("invalid api.%s for %s.%s: %s", hmethod, s.Name, m.Name, path)
@ -103,6 +114,7 @@ func astToService(ast *parser.Thrift, resolver *Resolver) ([]*generator.Service,
ReturnTypeName: respName, ReturnTypeName: respName,
Path: path[0], Path: path[0],
Serializer: sr, Serializer: sr,
OutputDir: handlerOutDir,
// Annotations: m.Annotations, // Annotations: m.Annotations,
} }
refs := resolver.ExportReferred(false, true) refs := resolver.ExportReferred(false, true)

View File

@ -90,6 +90,10 @@ func (plugin *Plugin) Run() int {
options := CheckTagOption(plugin.args) options := CheckTagOption(plugin.args)
pkgInfo, err := plugin.getPackageInfo() pkgInfo, err := plugin.getPackageInfo()
if err != nil {
logs.Errorf("get http package info failed: %s", err.Error())
return meta.PluginError
}
cf, _ := util.GetColonPair(args.CustomizePackage) cf, _ := util.GetColonPair(args.CustomizePackage)
pkg, err := args.GetGoPackage() pkg, err := args.GetGoPackage()
@ -126,8 +130,9 @@ func (plugin *Plugin) Run() int {
TemplateGenerator: generator.TemplateGenerator{ TemplateGenerator: generator.TemplateGenerator{
OutputDir: args.OutDir, OutputDir: args.OutDir,
}, },
ProjPackage: pkg, ProjPackage: pkg,
Options: options, Options: options,
HandlerByMethod: args.HandlerByMethod,
} }
if args.ModelBackend != "" { if args.ModelBackend != "" {
sg.Backend = meta.Backend(args.ModelBackend) sg.Backend = meta.Backend(args.ModelBackend)

View File

@ -56,6 +56,7 @@ const (
ApiAny = "api.any" ApiAny = "api.any"
ApiPath = "api.path" ApiPath = "api.path"
ApiSerializer = "api.serializer" ApiSerializer = "api.serializer"
ApiGenPath = "api.handler_path"
) )
var ( var (
@ -70,6 +71,10 @@ var (
ApiAny: "ANY", ApiAny: "ANY",
} }
HttpMethodOptionAnnotations = map[string]string{
ApiGenPath: "handler_path",
}
BindingTags = map[string]string{ BindingTags = map[string]string{
AnnotationPath: "path", AnnotationPath: "path",
AnnotationQuery: "query", AnnotationQuery: "query",

View File

@ -366,8 +366,9 @@ func SubDir(root, subPkg string) string {
} }
var ( var (
uniquePackageName = map[string]bool{} uniquePackageName = map[string]bool{}
uniqueMiddlewareName = map[string]bool{} uniqueMiddlewareName = map[string]bool{}
uniqueHandlerPackageName = map[string]bool{}
) )
// GetPackageUniqueName can get a non-repeating variable name for package alias // GetPackageUniqueName can get a non-repeating variable name for package alias
@ -390,6 +391,15 @@ func GetMiddlewareUniqueName(name string) (string, error) {
return name, nil return name, nil
} }
func GetHandlerPackageUniqueName(name string) (string, error) {
name, err := getUniqueName(name, uniqueHandlerPackageName)
if err != nil {
return "", fmt.Errorf("can not generate unique handler package name: '%s', err: %v", name, err)
}
return name, nil
}
// getUniqueName can get a non-repeating variable name // getUniqueName can get a non-repeating variable name
func getUniqueName(name string, uniqueNameSet map[string]bool) (string, error) { func getUniqueName(name string, uniqueNameSet map[string]bool) (string, error) {
uniqueName := name uniqueName := name