Migrate clientarggen to svcdef

This commit is contained in:
Leland Batey 2016-10-31 14:29:05 -07:00
Родитель 4cafb6b349
Коммит c57b063590
5 изменённых файлов: 86 добавлений и 102 удалений

Просмотреть файл

@ -9,10 +9,10 @@ import (
"fmt" "fmt"
"html/template" "html/template"
"strings" "strings"
"unicode"
log "github.com/Sirupsen/logrus" "github.com/TuneLab/go-truss/svcdef"
"github.com/TuneLab/go-truss/deftree" gogen "github.com/golang/protobuf/protoc-gen-go/generator"
generatego "github.com/golang/protobuf/protoc-gen-go/generator"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -58,6 +58,7 @@ type ClientArg struct {
// field corresponding to this arg, as provided by the protocol buffer // field corresponding to this arg, as provided by the protocol buffer
// compiler. For a list of these basic types and their corresponding Go // compiler. For a list of these basic types and their corresponding Go
// types, see the ProtoToGoTypeMap map in this file. // types, see the ProtoToGoTypeMap map in this file.
// Deprecated by Svcdef.
ProtbufType string ProtbufType string
// IsBaseType is true if this arg corresponds to a protobuf field which is // IsBaseType is true if this arg corresponds to a protobuf field which is
@ -161,54 +162,50 @@ var ProtoToGoTypeMap = map[string]string{
// New creates a ClientServiceArgs struct containing all the arguments for all // New creates a ClientServiceArgs struct containing all the arguments for all
// the methods of a given RPC. // the methods of a given RPC.
func New(svc *deftree.ProtoService) *ClientServiceArgs { func New(svc *svcdef.Service) *ClientServiceArgs {
svcArgs := ClientServiceArgs{ svcArgs := ClientServiceArgs{
MethArgs: make(map[string]*MethodArgs), MethArgs: make(map[string]*MethodArgs),
} }
for _, meth := range svc.Methods { for _, meth := range svc.Methods {
m := MethodArgs{} m := MethodArgs{}
for _, field := range meth.RequestType.Fields { // TODO implement correct map support in client argument generation
// Skip map fields, as they're currently incorrectly implemented by for _, field := range meth.RequestType.Message.Fields {
// deftree if field.Type.Map != nil {
// TODO implement correct map support in client argument generation
if field.IsMap {
continue continue
} }
newArg := newClientArg(meth.GetName(), field) newArg := newClientArg(meth.Name, field)
m.Args = append(m.Args, newArg) m.Args = append(m.Args, newArg)
} }
svcArgs.MethArgs[meth.GetName()] = &m svcArgs.MethArgs[meth.Name] = &m
} }
return &svcArgs return &svcArgs
} }
// newClientArg returns a ClientArg generated from the provided method name and MessageField // newClientArg returns a ClientArg generated from the provided method name and MessageField
func newClientArg(methName string, field *deftree.MessageField) *ClientArg { func newClientArg(methName string, field *svcdef.Field) *ClientArg {
newArg := ClientArg{} newArg := ClientArg{}
newArg.Name = field.GetName() newArg.Name = lowCamelName(field.Name)
if field.Label == "LABEL_REPEATED" { if field.Type.ArrayType {
newArg.Repeated = true newArg.Repeated = true
} }
newArg.ProtbufType = field.Type.GetName()
newArg.FlagName = fmt.Sprintf("%s.%s", strings.ToLower(methName), strings.ToLower(field.GetName())) newArg.FlagName = fmt.Sprintf("%s.%s", strings.ToLower(methName), strings.ToLower(field.Name))
newArg.FlagArg = fmt.Sprintf("flag%s%s", generatego.CamelCase(newArg.Name), generatego.CamelCase(methName)) newArg.FlagArg = fmt.Sprintf("flag%s%s", gogen.CamelCase(field.Name), gogen.CamelCase(methName))
if field.Type.Enum != nil { if field.Type.Enum != nil {
newArg.Enum = true newArg.Enum = true
log.WithField("Method", methName).WithField("Arg", newArg.Name).Debugf("type: %s", field.Type.GetName())
} }
// Determine the FlagType and flag invocation
var ft string var ft string
var ok bool if field.Type.Message == nil && field.Type.Enum == nil && field.Type.Map == nil {
log.WithField("Method", methName).WithField("Arg", newArg.Name).Debugf("type: %s", field.Type.GetName()) ft = field.Type.Name
// For types outside the base types, have flag treat them as strings newArg.IsBaseType = true
if ft, ok = ProtoToGoTypeMap[field.Type.GetName()]; !ok { } else {
// For types outside the base types, have flag treat them as strings
ft = "string" ft = "string"
newArg.IsBaseType = false newArg.IsBaseType = false
} else {
newArg.IsBaseType = true
} }
if newArg.Repeated { if newArg.Repeated {
ft = "string" ft = "string"
@ -216,19 +213,13 @@ func newClientArg(methName string, field *deftree.MessageField) *ClientArg {
newArg.FlagType = ft newArg.FlagType = ft
newArg.FlagConvertFunc = createFlagConvertFunc(newArg) newArg.FlagConvertFunc = createFlagConvertFunc(newArg)
newArg.GoArg = fmt.Sprintf("%s%s", generatego.CamelCase(newArg.Name), generatego.CamelCase(methName)) newArg.GoArg = fmt.Sprintf("%s%s", gogen.CamelCase(newArg.Name), gogen.CamelCase(methName))
// For types outside the base types, treat them as strings // For types outside the base types, treat them as strings
if newArg.IsBaseType { if newArg.IsBaseType {
newArg.GoType = ProtoToGoTypeMap[field.Type.GetName()] //newArg.GoType = ProtoToGoTypeMap[field.Type.GetName()]
newArg.GoType = field.Type.Name
} else { } else {
// TODO: Have GoType derivation respect nested definitions newArg.GoType = "pb." + field.Type.Name
tn := field.Type.GetName()
sections := strings.Split(tn, ".")
// Extract everything after the package name
remaining := sections[2:]
tn = generatego.CamelCaseSlice(remaining)
//log.WithField("Method", methName).WithField("Arg", newArg.Name).Warnf("type: %v, %v", sections[2:], generatego.CamelCaseSlice(sections[2:]))
newArg.GoType = "pb." + tn
} }
// The GoType is a slice of the GoType if it's a repeated field // The GoType is a slice of the GoType if it's a repeated field
if newArg.Repeated { if newArg.Repeated {
@ -257,7 +248,7 @@ if {{.FlagArg}} != nil && len(*{{.FlagArg}}) > 0 {
} }
` `
if a.Repeated || !a.IsBaseType { if a.Repeated || !a.IsBaseType {
code, err := ApplyTemplate("UnmarshalCliArgs", jsonConvTmpl, a, nil) code, err := applyTemplate("UnmarshalCliArgs", jsonConvTmpl, a, nil)
if err != nil { if err != nil {
panic(fmt.Sprintf("Couldn't apply template: %v", err)) panic(fmt.Sprintf("Couldn't apply template: %v", err))
} }
@ -312,10 +303,10 @@ func flagTypeConversion(a ClientArg) string {
return fmt.Sprintf(fType, a.FlagArg) return fmt.Sprintf(fType, a.FlagArg)
} }
// ApplyTemplate applies a template with a given name, executor context, and // applyTemplate applies a template with a given name, executor context, and
// function map. Returns the output of the template on success, returns an // function map. Returns the output of the template on success, returns an
// error if template failed to execute. // error if template failed to execute.
func ApplyTemplate(name string, tmpl string, executor interface{}, fncs template.FuncMap) (string, error) { func applyTemplate(name string, tmpl string, executor interface{}, fncs template.FuncMap) (string, error) {
codeTemplate := template.Must(template.New(name).Funcs(fncs).Parse(tmpl)) codeTemplate := template.Must(template.New(name).Funcs(fncs).Parse(tmpl))
code := bytes.NewBuffer(nil) code := bytes.NewBuffer(nil)
@ -325,3 +316,17 @@ func ApplyTemplate(name string, tmpl string, executor interface{}, fncs template
} }
return code.String(), nil return code.String(), nil
} }
// lowCamelName returns a CamelCased string, but with the first letter
// lowercased. "package_name" becomes "packageName".
func lowCamelName(s string) string {
s = gogen.CamelCase(s)
new := []rune(s)
if len(new) < 1 {
return s
}
rv := []rune{}
rv = append(rv, unicode.ToLower(new[0]))
rv = append(rv, new[1:]...)
return string(rv)
}

Просмотреть файл

@ -6,8 +6,8 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/TuneLab/go-truss/deftree"
"github.com/TuneLab/go-truss/gengokit/gentesthelper" "github.com/TuneLab/go-truss/gengokit/gentesthelper"
"github.com/TuneLab/go-truss/svcdef"
) )
var ( var (
@ -17,57 +17,37 @@ var (
) )
func TestNewClientServiceArgs(t *testing.T) { func TestNewClientServiceArgs(t *testing.T) {
svc := deftree.ProtoService{ defStr := `
Name: "AddSvc", syntax = "proto3";
Methods: []*deftree.ServiceMethod{
&deftree.ServiceMethod{ // General package
Name: "Sum", package general;
RequestType: &deftree.ProtoMessage{
Name: "SumRequest", import "google/api/annotations.proto";
Fields: []*deftree.MessageField{
&deftree.MessageField{ message SumRequest {
Name: "a", repeated int64 a = 1;
Number: 1, int64 b = 2;
Label: "LABEL_REPEATED", }
Type: deftree.FieldType{
Name: "TYPE_INT64", message SumReply {
}, int64 v = 1;
}, string err = 2;
&deftree.MessageField{ }
Name: "b",
Number: 2, service SumSvc {
Label: "LABEL_OPTIONAL", rpc Sum(SumRequest) returns (SumReply) {
Type: deftree.FieldType{ option (google.api.http) = {
Name: "TYPE_INT64", get: "/sum/{a}"
}, };
}, }
}, }
}, `
ResponseType: &deftree.ProtoMessage{ sd, err := svcdef.NewFromString(defStr)
Name: "SumReply", if err != nil {
Fields: []*deftree.MessageField{ t.Fatal(err, "Failed to create a service from the definition string")
&deftree.MessageField{
Name: "v",
Number: 1,
Label: "LABEL_OPTIONAL",
Type: deftree.FieldType{
Name: "TYPE_INT64",
},
},
&deftree.MessageField{
Name: "err",
Number: 2,
Label: "LABEL_OPTIONAL",
Type: deftree.FieldType{
Name: "TYPE_STRING",
},
},
},
},
},
},
} }
csa := New(&svc) csa := New(sd.Service)
expected := &ClientServiceArgs{ expected := &ClientServiceArgs{
MethArgs: map[string]*MethodArgs{ MethArgs: map[string]*MethodArgs{
@ -82,7 +62,7 @@ func TestNewClientServiceArgs(t *testing.T) {
GoArg: "ASum", GoArg: "ASum",
GoType: "[]int64", GoType: "[]int64",
GoConvertInvoc: "\nvar ASum []int64\nif flagASum != nil && len(*flagASum) > 0 {\n\terr = json.Unmarshal([]byte(*flagASum), &ASum)\n\tif err != nil {\n\t\tpanic(errors.Wrapf(err, \"unmarshalling ASum from %v:\", flagASum))\n\t}\n}\n", GoConvertInvoc: "\nvar ASum []int64\nif flagASum != nil && len(*flagASum) > 0 {\n\terr = json.Unmarshal([]byte(*flagASum), &ASum)\n\tif err != nil {\n\t\tpanic(errors.Wrapf(err, \"unmarshalling ASum from %v:\", flagASum))\n\t}\n}\n",
ProtbufType: "TYPE_INT64", ProtbufType: "",
IsBaseType: true, IsBaseType: true,
Repeated: true, Repeated: true,
}, },
@ -96,7 +76,7 @@ func TestNewClientServiceArgs(t *testing.T) {
GoArg: "BSum", GoArg: "BSum",
GoType: "int64", GoType: "int64",
GoConvertInvoc: "BSum := *flagBSum", GoConvertInvoc: "BSum := *flagBSum",
ProtbufType: "TYPE_INT64", ProtbufType: "",
IsBaseType: true, IsBaseType: true,
Repeated: false, Repeated: false,
}, },

Просмотреть файл

@ -67,7 +67,7 @@ func newTemplateExecutor(dt deftree.Deftree, sd *svcdef.Svcdef, conf config.Conf
PBImportPath: conf.PBPackage, PBImportPath: conf.PBPackage,
PackageName: sd.PkgName, PackageName: sd.PkgName,
Service: service, Service: service,
ClientArgs: clientarggen.New(service), ClientArgs: clientarggen.New(sd.Service),
HTTPHelper: httptransport.NewHelper(sd.Service), HTTPHelper: httptransport.NewHelper(sd.Service),
funcMap: funcMap, funcMap: funcMap,
}, nil }, nil

Просмотреть файл

@ -80,25 +80,25 @@ func NewBinding(i int, meth *svcdef.ServiceMethod) *Binding {
for _, param := range binding.Params { for _, param := range binding.Params {
// The 'Field' attr of each HTTPParameter always point to it's bound // The 'Field' attr of each HTTPParameter always point to it's bound
// Methods RequestType // Methods RequestType
rq := param.Field field := param.Field
newField := Field{ newField := Field{
Name: rq.Name, Name: field.Name,
CamelName: gogen.CamelCase(rq.Name), CamelName: gogen.CamelCase(field.Name),
LowCamelName: LowCamelName(rq.Name), LowCamelName: LowCamelName(field.Name),
Location: param.Location, Location: param.Location,
Repeated: rq.Type.ArrayType, Repeated: field.Type.ArrayType,
GoType: rq.Type.Name, GoType: field.Type.Name,
LocalName: fmt.Sprintf("%s%s", gogen.CamelCase(rq.Name), gogen.CamelCase(meth.Name)), LocalName: fmt.Sprintf("%s%s", gogen.CamelCase(field.Name), gogen.CamelCase(meth.Name)),
} }
if rq.Type.Message == nil && rq.Type.Enum == nil && rq.Type.Map == nil { if field.Type.Message == nil && field.Type.Enum == nil && field.Type.Map == nil {
newField.IsBaseType = true newField.IsBaseType = true
} }
// Modify GoType to reflect pointer or repeated status // Modify GoType to reflect pointer or repeated status
if rq.Type.StarExpr && rq.Type.ArrayType { if field.Type.StarExpr && field.Type.ArrayType {
newField.GoType = "[]*" + newField.GoType newField.GoType = "[]*" + newField.GoType
} else if rq.Type.ArrayType { } else if field.Type.ArrayType {
newField.GoType = "[]" + newField.GoType newField.GoType = "[]" + newField.GoType
} }

Просмотреть файл

@ -92,7 +92,6 @@ func TestNewMethod(t *testing.T) {
if got, want := newMeth, meth; !reflect.DeepEqual(got, want) { if got, want := newMeth, meth; !reflect.DeepEqual(got, want) {
diff := gentesthelper.DiffStrings(spew.Sdump(got), spew.Sdump(want)) diff := gentesthelper.DiffStrings(spew.Sdump(got), spew.Sdump(want))
t.Errorf("got != want; methods differ: %v\n", diff) t.Errorf("got != want; methods differ: %v\n", diff)
//t.Errorf("methods differ;\ngot = %+v\nwant = %+v\n", got, want)
} }
} }