Switch from glide to govendor

This commit is contained in:
Manfred Touron
2017-12-19 13:55:52 +01:00
parent ccffd8bfe2
commit 230480afd1
1871 changed files with 302 additions and 801202 deletions

View File

@@ -1,533 +0,0 @@
package descriptor
import (
"testing"
"github.com/golang/protobuf/proto"
descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
)
func loadFile(t *testing.T, reg *Registry, src string) *descriptor.FileDescriptorProto {
var file descriptor.FileDescriptorProto
if err := proto.UnmarshalText(src, &file); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &file) failed with %v; want success", src, err)
}
reg.loadFile(&file)
return &file
}
func load(t *testing.T, reg *Registry, src string) error {
var req plugin.CodeGeneratorRequest
if err := proto.UnmarshalText(src, &req); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &file) failed with %v; want success", src, err)
}
return reg.Load(&req)
}
func TestLoadFile(t *testing.T) {
reg := NewRegistry()
fd := loadFile(t, reg, `
name: 'example.proto'
package: 'example'
message_type <
name: 'ExampleMessage'
field <
name: 'str'
label: LABEL_OPTIONAL
type: TYPE_STRING
number: 1
>
>
`)
file := reg.files["example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: ".", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
msg, err := reg.LookupMsg("", ".example.ExampleMessage")
if err != nil {
t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", "", ".example.ExampleMessage", err)
return
}
if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", "", ".example.ExampleMessage", got, want)
}
if got, want := msg.File, file; got != want {
t.Errorf("msg.File = %v; want %v", got, want)
}
if got := msg.Outers; got != nil {
t.Errorf("msg.Outers = %v; want %v", got, nil)
}
if got, want := len(msg.Fields), 1; got != want {
t.Errorf("len(msg.Fields) = %d; want %d", got, want)
} else if got, want := msg.Fields[0].FieldDescriptorProto, fd.MessageType[0].Field[0]; got != want {
t.Errorf("msg.Fields[0].FieldDescriptorProto = %v; want %v", got, want)
} else if got, want := msg.Fields[0].Message, msg; got != want {
t.Errorf("msg.Fields[0].Message = %v; want %v", got, want)
}
if got, want := len(file.Messages), 1; got != want {
t.Errorf("file.Meeesages = %#v; want %#v", file.Messages, []*Message{msg})
}
if got, want := file.Messages[0], msg; got != want {
t.Errorf("file.Meeesages[0] = %v; want %v", got, want)
}
}
func TestLoadFileNestedPackage(t *testing.T) {
reg := NewRegistry()
loadFile(t, reg, `
name: 'example.proto'
package: 'example.nested.nested2'
`)
file := reg.files["example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: ".", Name: "example_nested_nested2"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithDir(t *testing.T) {
reg := NewRegistry()
loadFile(t, reg, `
name: 'path/to/example.proto'
package: 'example'
`)
file := reg.files["path/to/example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: "path/to", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithoutPackage(t *testing.T) {
reg := NewRegistry()
loadFile(t, reg, `
name: 'path/to/example_file.proto'
`)
file := reg.files["path/to/example_file.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: "path/to", Name: "example_file"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithMapping(t *testing.T) {
reg := NewRegistry()
reg.AddPkgMap("path/to/example.proto", "example.com/proj/example/proto")
loadFile(t, reg, `
name: 'path/to/example.proto'
package: 'example'
`)
file := reg.files["path/to/example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: "example.com/proj/example/proto", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithPackageNameCollision(t *testing.T) {
reg := NewRegistry()
loadFile(t, reg, `
name: 'path/to/another.proto'
package: 'example'
`)
loadFile(t, reg, `
name: 'path/to/example.proto'
package: 'example'
`)
if err := reg.ReserveGoPackageAlias("ioutil", "io/ioutil"); err != nil {
t.Fatalf("reg.ReserveGoPackageAlias(%q) failed with %v; want success", "ioutil", err)
}
loadFile(t, reg, `
name: 'path/to/ioutil.proto'
package: 'ioutil'
`)
file := reg.files["path/to/another.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/another.proto")
return
}
wantPkg := GoPackage{Path: "path/to", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
file = reg.files["path/to/example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/example.proto")
return
}
wantPkg = GoPackage{Path: "path/to", Name: "example", Alias: ""}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
file = reg.files["path/to/ioutil.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/ioutil.proto")
return
}
wantPkg = GoPackage{Path: "path/to", Name: "ioutil", Alias: "ioutil_0"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithIdenticalGoPkg(t *testing.T) {
reg := NewRegistry()
reg.AddPkgMap("path/to/another.proto", "example.com/example")
reg.AddPkgMap("path/to/example.proto", "example.com/example")
loadFile(t, reg, `
name: 'path/to/another.proto'
package: 'example'
`)
loadFile(t, reg, `
name: 'path/to/example.proto'
package: 'example'
`)
file := reg.files["path/to/example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: "example.com/example", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
file = reg.files["path/to/another.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg = GoPackage{Path: "example.com/example", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithPrefix(t *testing.T) {
reg := NewRegistry()
reg.SetPrefix("third_party")
loadFile(t, reg, `
name: 'path/to/example.proto'
package: 'example'
`)
file := reg.files["path/to/example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: "third_party/path/to", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLookupMsgWithoutPackage(t *testing.T) {
reg := NewRegistry()
fd := loadFile(t, reg, `
name: 'example.proto'
message_type <
name: 'ExampleMessage'
field <
name: 'str'
label: LABEL_OPTIONAL
type: TYPE_STRING
number: 1
>
>
`)
msg, err := reg.LookupMsg("", ".ExampleMessage")
if err != nil {
t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", "", ".ExampleMessage", err)
return
}
if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", "", ".ExampleMessage", got, want)
}
}
func TestLookupMsgWithNestedPackage(t *testing.T) {
reg := NewRegistry()
fd := loadFile(t, reg, `
name: 'example.proto'
package: 'nested.nested2.mypackage'
message_type <
name: 'ExampleMessage'
field <
name: 'str'
label: LABEL_OPTIONAL
type: TYPE_STRING
number: 1
>
>
`)
for _, name := range []string{
"nested.nested2.mypackage.ExampleMessage",
"nested2.mypackage.ExampleMessage",
"mypackage.ExampleMessage",
"ExampleMessage",
} {
msg, err := reg.LookupMsg("nested.nested2.mypackage", name)
if err != nil {
t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", ".nested.nested2.mypackage", name, err)
return
}
if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", ".nested.nested2.mypackage", name, got, want)
}
}
for _, loc := range []string{
".nested.nested2.mypackage",
"nested.nested2.mypackage",
".nested.nested2",
"nested.nested2",
".nested",
"nested",
".",
"",
"somewhere.else",
} {
name := "nested.nested2.mypackage.ExampleMessage"
msg, err := reg.LookupMsg(loc, name)
if err != nil {
t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", loc, name, err)
return
}
if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", loc, name, got, want)
}
}
for _, loc := range []string{
".nested.nested2.mypackage",
"nested.nested2.mypackage",
".nested.nested2",
"nested.nested2",
".nested",
"nested",
} {
name := "nested2.mypackage.ExampleMessage"
msg, err := reg.LookupMsg(loc, name)
if err != nil {
t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", loc, name, err)
return
}
if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", loc, name, got, want)
}
}
}
func TestLoadWithInconsistentTargetPackage(t *testing.T) {
for _, spec := range []struct {
req string
consistent bool
}{
// root package, no explicit go package
{
req: `
file_to_generate: 'a.proto'
file_to_generate: 'b.proto'
proto_file <
name: 'a.proto'
message_type < name: 'A' >
service <
name: "AService"
method <
name: "Meth"
input_type: "A"
output_type: "A"
options <
[google.api.http] < post: "/v1/a" body: "*" >
>
>
>
>
proto_file <
name: 'b.proto'
message_type < name: 'B' >
service <
name: "BService"
method <
name: "Meth"
input_type: "B"
output_type: "B"
options <
[google.api.http] < post: "/v1/b" body: "*" >
>
>
>
>
`,
consistent: false,
},
// named package, no explicit go package
{
req: `
file_to_generate: 'a.proto'
file_to_generate: 'b.proto'
proto_file <
name: 'a.proto'
package: 'example.foo'
message_type < name: 'A' >
service <
name: "AService"
method <
name: "Meth"
input_type: "A"
output_type: "A"
options <
[google.api.http] < post: "/v1/a" body: "*" >
>
>
>
>
proto_file <
name: 'b.proto'
package: 'example.foo'
message_type < name: 'B' >
service <
name: "BService"
method <
name: "Meth"
input_type: "B"
output_type: "B"
options <
[google.api.http] < post: "/v1/b" body: "*" >
>
>
>
>
`,
consistent: true,
},
// root package, explicit go package
{
req: `
file_to_generate: 'a.proto'
file_to_generate: 'b.proto'
proto_file <
name: 'a.proto'
options < go_package: 'foo' >
message_type < name: 'A' >
service <
name: "AService"
method <
name: "Meth"
input_type: "A"
output_type: "A"
options <
[google.api.http] < post: "/v1/a" body: "*" >
>
>
>
>
proto_file <
name: 'b.proto'
options < go_package: 'foo' >
message_type < name: 'B' >
service <
name: "BService"
method <
name: "Meth"
input_type: "B"
output_type: "B"
options <
[google.api.http] < post: "/v1/b" body: "*" >
>
>
>
>
`,
consistent: true,
},
// named package, explicit go package
{
req: `
file_to_generate: 'a.proto'
file_to_generate: 'b.proto'
proto_file <
name: 'a.proto'
package: 'example.foo'
options < go_package: 'foo' >
message_type < name: 'A' >
service <
name: "AService"
method <
name: "Meth"
input_type: "A"
output_type: "A"
options <
[google.api.http] < post: "/v1/a" body: "*" >
>
>
>
>
proto_file <
name: 'b.proto'
package: 'example.foo'
options < go_package: 'foo' >
message_type < name: 'B' >
service <
name: "BService"
method <
name: "Meth"
input_type: "B"
output_type: "B"
options <
[google.api.http] < post: "/v1/b" body: "*" >
>
>
>
>
`,
consistent: true,
},
} {
reg := NewRegistry()
err := load(t, reg, spec.req)
if got, want := err == nil, spec.consistent; got != want {
if want {
t.Errorf("reg.Load(%s) failed with %v; want success", spec.req, err)
continue
}
t.Errorf("reg.Load(%s) succeeded; want an package inconsistency error", spec.req)
}
}
}

View File

@@ -1,206 +0,0 @@
package descriptor
import (
"testing"
"github.com/golang/protobuf/proto"
descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
)
func TestGoPackageStandard(t *testing.T) {
for _, spec := range []struct {
pkg GoPackage
want bool
}{
{
pkg: GoPackage{Path: "fmt", Name: "fmt"},
want: true,
},
{
pkg: GoPackage{Path: "encoding/json", Name: "json"},
want: true,
},
{
pkg: GoPackage{Path: "github.com/golang/protobuf/jsonpb", Name: "jsonpb"},
want: false,
},
{
pkg: GoPackage{Path: "golang.org/x/net/context", Name: "context"},
want: false,
},
{
pkg: GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway", Name: "main"},
want: false,
},
{
pkg: GoPackage{Path: "github.com/google/googleapis/google/api/http.pb", Name: "http_pb", Alias: "htpb"},
want: false,
},
} {
if got, want := spec.pkg.Standard(), spec.want; got != want {
t.Errorf("%#v.Standard() = %v; want %v", spec.pkg, got, want)
}
}
}
func TestGoPackageString(t *testing.T) {
for _, spec := range []struct {
pkg GoPackage
want string
}{
{
pkg: GoPackage{Path: "fmt", Name: "fmt"},
want: `"fmt"`,
},
{
pkg: GoPackage{Path: "encoding/json", Name: "json"},
want: `"encoding/json"`,
},
{
pkg: GoPackage{Path: "github.com/golang/protobuf/jsonpb", Name: "jsonpb"},
want: `"github.com/golang/protobuf/jsonpb"`,
},
{
pkg: GoPackage{Path: "golang.org/x/net/context", Name: "context"},
want: `"golang.org/x/net/context"`,
},
{
pkg: GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway", Name: "main"},
want: `"github.com/grpc-ecosystem/grpc-gateway"`,
},
{
pkg: GoPackage{Path: "github.com/google/googleapis/google/api/http.pb", Name: "http_pb", Alias: "htpb"},
want: `htpb "github.com/google/googleapis/google/api/http.pb"`,
},
} {
if got, want := spec.pkg.String(), spec.want; got != want {
t.Errorf("%#v.String() = %q; want %q", spec.pkg, got, want)
}
}
}
func TestFieldPath(t *testing.T) {
var fds []*descriptor.FileDescriptorProto
for _, src := range []string{
`
name: 'example.proto'
package: 'example'
message_type <
name: 'Nest'
field <
name: 'nest2_field'
label: LABEL_OPTIONAL
type: TYPE_MESSAGE
type_name: 'Nest2'
number: 1
>
field <
name: 'terminal_field'
label: LABEL_OPTIONAL
type: TYPE_STRING
number: 2
>
>
syntax: "proto3"
`, `
name: 'another.proto'
package: 'example'
message_type <
name: 'Nest2'
field <
name: 'nest_field'
label: LABEL_OPTIONAL
type: TYPE_MESSAGE
type_name: 'Nest'
number: 1
>
field <
name: 'terminal_field'
label: LABEL_OPTIONAL
type: TYPE_STRING
number: 2
>
>
syntax: "proto2"
`,
} {
var fd descriptor.FileDescriptorProto
if err := proto.UnmarshalText(src, &fd); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
}
fds = append(fds, &fd)
}
nest := &Message{
DescriptorProto: fds[0].MessageType[0],
Fields: []*Field{
{FieldDescriptorProto: fds[0].MessageType[0].Field[0]},
{FieldDescriptorProto: fds[0].MessageType[0].Field[1]},
},
}
nest2 := &Message{
DescriptorProto: fds[1].MessageType[0],
Fields: []*Field{
{FieldDescriptorProto: fds[1].MessageType[0].Field[0]},
{FieldDescriptorProto: fds[1].MessageType[0].Field[1]},
},
}
file1 := &File{
FileDescriptorProto: fds[0],
GoPkg: GoPackage{Path: "example", Name: "example"},
Messages: []*Message{nest},
}
file2 := &File{
FileDescriptorProto: fds[1],
GoPkg: GoPackage{Path: "example", Name: "example"},
Messages: []*Message{nest2},
}
crossLinkFixture(file1)
crossLinkFixture(file2)
c1 := FieldPathComponent{
Name: "nest_field",
Target: nest2.Fields[0],
}
if got, want := c1.LHS(), "GetNestField()"; got != want {
t.Errorf("c1.LHS() = %q; want %q", got, want)
}
if got, want := c1.RHS(), "NestField"; got != want {
t.Errorf("c1.RHS() = %q; want %q", got, want)
}
c2 := FieldPathComponent{
Name: "nest2_field",
Target: nest.Fields[0],
}
if got, want := c2.LHS(), "Nest2Field"; got != want {
t.Errorf("c2.LHS() = %q; want %q", got, want)
}
if got, want := c2.LHS(), "Nest2Field"; got != want {
t.Errorf("c2.LHS() = %q; want %q", got, want)
}
fp := FieldPath{
c1, c2, c1, FieldPathComponent{
Name: "terminal_field",
Target: nest.Fields[1],
},
}
if got, want := fp.RHS("resp"), "resp.GetNestField().Nest2Field.GetNestField().TerminalField"; got != want {
t.Errorf("fp.RHS(%q) = %q; want %q", "resp", got, want)
}
fp2 := FieldPath{
c2, c1, c2, FieldPathComponent{
Name: "terminal_field",
Target: nest2.Fields[1],
},
}
if got, want := fp2.RHS("resp"), "resp.Nest2Field.GetNestField().Nest2Field.TerminalField"; got != want {
t.Errorf("fp2.RHS(%q) = %q; want %q", "resp", got, want)
}
var fpEmpty FieldPath
if got, want := fpEmpty.RHS("resp"), "resp"; got != want {
t.Errorf("fpEmpty.RHS(%q) = %q; want %q", "resp", got, want)
}
}

View File

@@ -1,13 +0,0 @@
// Package generator provides an abstract interface to code generators.
package generator
import (
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
)
// Generator is an abstraction of code generators.
type Generator interface {
// Generate generates output files from input .proto files.
Generate(targets []*descriptor.File) ([]*plugin.CodeGeneratorResponse_File, error)
}

View File

@@ -1,2 +0,0 @@
// Package gengateway provides a code generator for grpc gateway files.
package gengateway

View File

@@ -1,111 +0,0 @@
package gengateway
import (
"errors"
"fmt"
"go/format"
"path"
"path/filepath"
"strings"
"github.com/golang/glog"
"github.com/golang/protobuf/proto"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
gen "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/generator"
options "google.golang.org/genproto/googleapis/api/annotations"
)
var (
errNoTargetService = errors.New("no target service defined in the file")
)
type generator struct {
reg *descriptor.Registry
baseImports []descriptor.GoPackage
useRequestContext bool
}
// New returns a new generator which generates grpc gateway files.
func New(reg *descriptor.Registry, useRequestContext bool) gen.Generator {
var imports []descriptor.GoPackage
for _, pkgpath := range []string{
"io",
"net/http",
"github.com/grpc-ecosystem/grpc-gateway/runtime",
"github.com/grpc-ecosystem/grpc-gateway/utilities",
"github.com/golang/protobuf/proto",
"golang.org/x/net/context",
"google.golang.org/grpc",
"google.golang.org/grpc/codes",
"google.golang.org/grpc/grpclog",
} {
pkg := descriptor.GoPackage{
Path: pkgpath,
Name: path.Base(pkgpath),
}
if err := reg.ReserveGoPackageAlias(pkg.Name, pkg.Path); err != nil {
for i := 0; ; i++ {
alias := fmt.Sprintf("%s_%d", pkg.Name, i)
if err := reg.ReserveGoPackageAlias(alias, pkg.Path); err != nil {
continue
}
pkg.Alias = alias
break
}
}
imports = append(imports, pkg)
}
return &generator{reg: reg, baseImports: imports, useRequestContext: useRequestContext}
}
func (g *generator) Generate(targets []*descriptor.File) ([]*plugin.CodeGeneratorResponse_File, error) {
var files []*plugin.CodeGeneratorResponse_File
for _, file := range targets {
glog.V(1).Infof("Processing %s", file.GetName())
code, err := g.generate(file)
if err == errNoTargetService {
glog.V(1).Infof("%s: %v", file.GetName(), err)
continue
}
if err != nil {
return nil, err
}
formatted, err := format.Source([]byte(code))
if err != nil {
glog.Errorf("%v: %s", err, code)
return nil, err
}
name := file.GetName()
ext := filepath.Ext(name)
base := strings.TrimSuffix(name, ext)
output := fmt.Sprintf("%s.pb.gw.go", base)
files = append(files, &plugin.CodeGeneratorResponse_File{
Name: proto.String(output),
Content: proto.String(string(formatted)),
})
glog.V(1).Infof("Will emit %s", output)
}
return files, nil
}
func (g *generator) generate(file *descriptor.File) (string, error) {
pkgSeen := make(map[string]bool)
var imports []descriptor.GoPackage
for _, pkg := range g.baseImports {
pkgSeen[pkg.Path] = true
imports = append(imports, pkg)
}
for _, svc := range file.Services {
for _, m := range svc.Methods {
pkg := m.RequestType.File.GoPkg
if m.Options == nil || !proto.HasExtension(m.Options, options.E_Http) ||
pkg == file.GoPkg || pkgSeen[pkg.Path] {
continue
}
pkgSeen[pkg.Path] = true
imports = append(imports, pkg)
}
}
return applyTemplate(param{File: file, Imports: imports, UseRequestContext: g.useRequestContext})
}

View File

@@ -1,88 +0,0 @@
package gengateway
import (
"strings"
"testing"
"github.com/golang/protobuf/proto"
protodescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
)
func TestGenerateServiceWithoutBindings(t *testing.T) {
msgdesc := &protodescriptor.DescriptorProto{
Name: proto.String("ExampleMessage"),
}
msg := &descriptor.Message{
DescriptorProto: msgdesc,
}
msg1 := &descriptor.Message{
DescriptorProto: msgdesc,
File: &descriptor.File{
GoPkg: descriptor.GoPackage{
Path: "github.com/golang/protobuf/ptypes/empty",
Name: "empty",
},
},
}
meth := &protodescriptor.MethodDescriptorProto{
Name: proto.String("Example"),
InputType: proto.String("ExampleMessage"),
OutputType: proto.String("ExampleMessage"),
}
meth1 := &protodescriptor.MethodDescriptorProto{
Name: proto.String("ExampleWithoutBindings"),
InputType: proto.String("empty.Empty"),
OutputType: proto.String("empty.Empty"),
}
svc := &protodescriptor.ServiceDescriptorProto{
Name: proto.String("ExampleService"),
Method: []*protodescriptor.MethodDescriptorProto{meth, meth1},
}
file := descriptor.File{
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
Name: proto.String("example.proto"),
Package: proto.String("example"),
Dependency: []string{"a.example/b/c.proto", "a.example/d/e.proto"},
MessageType: []*protodescriptor.DescriptorProto{msgdesc},
Service: []*protodescriptor.ServiceDescriptorProto{svc},
},
GoPkg: descriptor.GoPackage{
Path: "example.com/path/to/example/example.pb",
Name: "example_pb",
},
Messages: []*descriptor.Message{msg},
Services: []*descriptor.Service{
{
ServiceDescriptorProto: svc,
Methods: []*descriptor.Method{
{
MethodDescriptorProto: meth,
RequestType: msg,
ResponseType: msg,
Bindings: []*descriptor.Binding{
{
HTTPMethod: "GET",
Body: &descriptor.Body{FieldPath: nil},
},
},
},
{
MethodDescriptorProto: meth1,
RequestType: msg1,
ResponseType: msg1,
},
},
},
},
}
g := &generator{}
got, err := g.generate(crossLinkFixture(&file))
if err != nil {
t.Errorf("generate(%#v) failed with %v; want success", file, err)
return
}
if notwanted := `"github.com/golang/protobuf/ptypes/empty"`; strings.Contains(got, notwanted) {
t.Errorf("generate(%#v) = %s; does not want to contain %s", file, got, notwanted)
}
}

View File

@@ -1,398 +0,0 @@
package gengateway
import (
"bytes"
"fmt"
"strings"
"text/template"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
)
type param struct {
*descriptor.File
Imports []descriptor.GoPackage
UseRequestContext bool
}
type binding struct {
*descriptor.Binding
}
// HasQueryParam determines if the binding needs parameters in query string.
//
// It sometimes returns true even though actually the binding does not need.
// But it is not serious because it just results in a small amount of extra codes generated.
func (b binding) HasQueryParam() bool {
if b.Body != nil && len(b.Body.FieldPath) == 0 {
return false
}
fields := make(map[string]bool)
for _, f := range b.Method.RequestType.Fields {
fields[f.GetName()] = true
}
if b.Body != nil {
delete(fields, b.Body.FieldPath.String())
}
for _, p := range b.PathParams {
delete(fields, p.FieldPath.String())
}
return len(fields) > 0
}
func (b binding) QueryParamFilter() queryParamFilter {
var seqs [][]string
if b.Body != nil {
seqs = append(seqs, strings.Split(b.Body.FieldPath.String(), "."))
}
for _, p := range b.PathParams {
seqs = append(seqs, strings.Split(p.FieldPath.String(), "."))
}
return queryParamFilter{utilities.NewDoubleArray(seqs)}
}
// queryParamFilter is a wrapper of utilities.DoubleArray which provides String() to output DoubleArray.Encoding in a stable and predictable format.
type queryParamFilter struct {
*utilities.DoubleArray
}
func (f queryParamFilter) String() string {
encodings := make([]string, len(f.Encoding))
for str, enc := range f.Encoding {
encodings[enc] = fmt.Sprintf("%q: %d", str, enc)
}
e := strings.Join(encodings, ", ")
return fmt.Sprintf("&utilities.DoubleArray{Encoding: map[string]int{%s}, Base: %#v, Check: %#v}", e, f.Base, f.Check)
}
type trailerParams struct {
Services []*descriptor.Service
UseRequestContext bool
}
func applyTemplate(p param) (string, error) {
w := bytes.NewBuffer(nil)
if err := headerTemplate.Execute(w, p); err != nil {
return "", err
}
var targetServices []*descriptor.Service
for _, svc := range p.Services {
var methodWithBindingsSeen bool
for _, meth := range svc.Methods {
glog.V(2).Infof("Processing %s.%s", svc.GetName(), meth.GetName())
methName := strings.Title(*meth.Name)
meth.Name = &methName
for _, b := range meth.Bindings {
methodWithBindingsSeen = true
if err := handlerTemplate.Execute(w, binding{Binding: b}); err != nil {
return "", err
}
}
}
if methodWithBindingsSeen {
targetServices = append(targetServices, svc)
}
}
if len(targetServices) == 0 {
return "", errNoTargetService
}
tp := trailerParams{
Services: targetServices,
UseRequestContext: p.UseRequestContext,
}
if err := trailerTemplate.Execute(w, tp); err != nil {
return "", err
}
return w.String(), nil
}
var (
headerTemplate = template.Must(template.New("header").Parse(`
// Code generated by protoc-gen-grpc-gateway
// source: {{.GetName}}
// DO NOT EDIT!
/*
Package {{.GoPkg.Name}} is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package {{.GoPkg.Name}}
import (
{{range $i := .Imports}}{{if $i.Standard}}{{$i | printf "%s\n"}}{{end}}{{end}}
{{range $i := .Imports}}{{if not $i.Standard}}{{$i | printf "%s\n"}}{{end}}{{end}}
)
var _ codes.Code
var _ io.Reader
var _ = runtime.String
var _ = utilities.NewDoubleArray
`))
handlerTemplate = template.Must(template.New("handler").Parse(`
{{if and .Method.GetClientStreaming .Method.GetServerStreaming}}
{{template "bidi-streaming-request-func" .}}
{{else if .Method.GetClientStreaming}}
{{template "client-streaming-request-func" .}}
{{else}}
{{template "client-rpc-request-func" .}}
{{end}}
`))
_ = template.Must(handlerTemplate.New("request-func-signature").Parse(strings.Replace(`
{{if .Method.GetServerStreaming}}
func request_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}}(ctx context.Context, marshaler runtime.Marshaler, client {{.Method.Service.GetName}}Client, req *http.Request, pathParams map[string]string) ({{.Method.Service.GetName}}_{{.Method.GetName}}Client, runtime.ServerMetadata, error)
{{else}}
func request_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}}(ctx context.Context, marshaler runtime.Marshaler, client {{.Method.Service.GetName}}Client, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error)
{{end}}`, "\n", "", -1)))
_ = template.Must(handlerTemplate.New("client-streaming-request-func").Parse(`
{{template "request-func-signature" .}} {
var metadata runtime.ServerMetadata
stream, err := client.{{.Method.GetName}}(ctx)
if err != nil {
grpclog.Printf("Failed to start streaming: %v", err)
return nil, metadata, err
}
dec := marshaler.NewDecoder(req.Body)
for {
var protoReq {{.Method.RequestType.GoType .Method.Service.File.GoPkg.Path}}
err = dec.Decode(&protoReq)
if err == io.EOF {
break
}
if err != nil {
grpclog.Printf("Failed to decode request: %v", err)
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
if err = stream.Send(&protoReq); err != nil {
grpclog.Printf("Failed to send request: %v", err)
return nil, metadata, err
}
}
if err := stream.CloseSend(); err != nil {
grpclog.Printf("Failed to terminate client stream: %v", err)
return nil, metadata, err
}
header, err := stream.Header()
if err != nil {
grpclog.Printf("Failed to get header from client: %v", err)
return nil, metadata, err
}
metadata.HeaderMD = header
{{if .Method.GetServerStreaming}}
return stream, metadata, nil
{{else}}
msg, err := stream.CloseAndRecv()
metadata.TrailerMD = stream.Trailer()
return msg, metadata, err
{{end}}
}
`))
_ = template.Must(handlerTemplate.New("client-rpc-request-func").Parse(`
{{if .HasQueryParam}}
var (
filter_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}} = {{.QueryParamFilter}}
)
{{end}}
{{template "request-func-signature" .}} {
var protoReq {{.Method.RequestType.GoType .Method.Service.File.GoPkg.Path}}
var metadata runtime.ServerMetadata
{{if .Body}}
if err := marshaler.NewDecoder(req.Body).Decode(&{{.Body.RHS "protoReq"}}); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
{{end}}
{{if .PathParams}}
var (
val string
ok bool
err error
_ = err
)
{{range $param := .PathParams}}
val, ok = pathParams[{{$param | printf "%q"}}]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", {{$param | printf "%q"}})
}
{{if $param.IsNestedProto3 }}
err = runtime.PopulateFieldFromPath(&protoReq, {{$param | printf "%q"}}, val)
{{else}}
{{$param.RHS "protoReq"}}, err = {{$param.ConvertFuncExpr}}(val)
{{end}}
if err != nil {
return nil, metadata, err
}
{{end}}
{{end}}
{{if .HasQueryParam}}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}}); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
{{end}}
{{if .Method.GetServerStreaming}}
stream, err := client.{{.Method.GetName}}(ctx, &protoReq)
if err != nil {
return nil, metadata, err
}
header, err := stream.Header()
if err != nil {
return nil, metadata, err
}
metadata.HeaderMD = header
return stream, metadata, nil
{{else}}
msg, err := client.{{.Method.GetName}}(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
{{end}}
}`))
_ = template.Must(handlerTemplate.New("bidi-streaming-request-func").Parse(`
{{template "request-func-signature" .}} {
var metadata runtime.ServerMetadata
stream, err := client.{{.Method.GetName}}(ctx)
if err != nil {
grpclog.Printf("Failed to start streaming: %v", err)
return nil, metadata, err
}
dec := marshaler.NewDecoder(req.Body)
handleSend := func() error {
var protoReq {{.Method.RequestType.GoType .Method.Service.File.GoPkg.Path}}
err = dec.Decode(&protoReq)
if err == io.EOF {
return err
}
if err != nil {
grpclog.Printf("Failed to decode request: %v", err)
return err
}
if err = stream.Send(&protoReq); err != nil {
grpclog.Printf("Failed to send request: %v", err)
return err
}
return nil
}
if err := handleSend(); err != nil {
if cerr := stream.CloseSend(); cerr != nil {
grpclog.Printf("Failed to terminate client stream: %v", cerr)
}
if err == io.EOF {
return stream, metadata, nil
}
return nil, metadata, err
}
go func() {
for {
if err := handleSend(); err != nil {
break
}
}
if err := stream.CloseSend(); err != nil {
grpclog.Printf("Failed to terminate client stream: %v", err)
}
}()
header, err := stream.Header()
if err != nil {
grpclog.Printf("Failed to get header from client: %v", err)
return nil, metadata, err
}
metadata.HeaderMD = header
return stream, metadata, nil
}
`))
trailerTemplate = template.Must(template.New("trailer").Parse(`
{{$UseRequestContext := .UseRequestContext}}
{{range $svc := .Services}}
// Register{{$svc.GetName}}HandlerFromEndpoint is same as Register{{$svc.GetName}}Handler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func Register{{$svc.GetName}}HandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return Register{{$svc.GetName}}Handler(ctx, mux, conn)
}
// Register{{$svc.GetName}}Handler registers the http handlers for service {{$svc.GetName}} to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func Register{{$svc.GetName}}Handler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
client := New{{$svc.GetName}}Client(conn)
{{range $m := $svc.Methods}}
{{range $b := $m.Bindings}}
mux.Handle({{$b.HTTPMethod | printf "%q"}}, pattern_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
{{- if $UseRequestContext }}
ctx, cancel := context.WithCancel(req.Context())
{{- else -}}
ctx, cancel := context.WithCancel(ctx)
{{- end }}
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
{{if $m.GetServerStreaming}}
forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(ctx, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
{{else}}
forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
{{end}}
})
{{end}}
{{end}}
return nil
}
var (
{{range $m := $svc.Methods}}
{{range $b := $m.Bindings}}
pattern_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}} = runtime.MustPattern(runtime.NewPattern({{$b.PathTmpl.Version}}, {{$b.PathTmpl.OpCodes | printf "%#v"}}, {{$b.PathTmpl.Pool | printf "%#v"}}, {{$b.PathTmpl.Verb | printf "%q"}}))
{{end}}
{{end}}
)
var (
{{range $m := $svc.Methods}}
{{range $b := $m.Bindings}}
forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}} = {{if $m.GetServerStreaming}}runtime.ForwardResponseStream{{else}}runtime.ForwardResponseMessage{{end}}
{{end}}
{{end}}
)
{{end}}`))
)

View File

@@ -1,404 +0,0 @@
package gengateway
import (
"strings"
"testing"
"github.com/golang/protobuf/proto"
protodescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
)
func crossLinkFixture(f *descriptor.File) *descriptor.File {
for _, m := range f.Messages {
m.File = f
}
for _, svc := range f.Services {
svc.File = f
for _, m := range svc.Methods {
m.Service = svc
for _, b := range m.Bindings {
b.Method = m
for _, param := range b.PathParams {
param.Method = m
}
}
}
}
return f
}
func TestApplyTemplateHeader(t *testing.T) {
msgdesc := &protodescriptor.DescriptorProto{
Name: proto.String("ExampleMessage"),
}
meth := &protodescriptor.MethodDescriptorProto{
Name: proto.String("Example"),
InputType: proto.String("ExampleMessage"),
OutputType: proto.String("ExampleMessage"),
}
svc := &protodescriptor.ServiceDescriptorProto{
Name: proto.String("ExampleService"),
Method: []*protodescriptor.MethodDescriptorProto{meth},
}
msg := &descriptor.Message{
DescriptorProto: msgdesc,
}
file := descriptor.File{
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
Name: proto.String("example.proto"),
Package: proto.String("example"),
Dependency: []string{"a.example/b/c.proto", "a.example/d/e.proto"},
MessageType: []*protodescriptor.DescriptorProto{msgdesc},
Service: []*protodescriptor.ServiceDescriptorProto{svc},
},
GoPkg: descriptor.GoPackage{
Path: "example.com/path/to/example/example.pb",
Name: "example_pb",
},
Messages: []*descriptor.Message{msg},
Services: []*descriptor.Service{
{
ServiceDescriptorProto: svc,
Methods: []*descriptor.Method{
{
MethodDescriptorProto: meth,
RequestType: msg,
ResponseType: msg,
Bindings: []*descriptor.Binding{
{
HTTPMethod: "GET",
Body: &descriptor.Body{FieldPath: nil},
},
},
},
},
},
},
}
got, err := applyTemplate(param{File: crossLinkFixture(&file)})
if err != nil {
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
return
}
if want := "package example_pb\n"; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
}
func TestApplyTemplateRequestWithoutClientStreaming(t *testing.T) {
msgdesc := &protodescriptor.DescriptorProto{
Name: proto.String("ExampleMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("nested"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
TypeName: proto.String("NestedMessage"),
Number: proto.Int32(1),
},
},
}
nesteddesc := &protodescriptor.DescriptorProto{
Name: proto.String("NestedMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("int32"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_INT32.Enum(),
Number: proto.Int32(1),
},
{
Name: proto.String("bool"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_BOOL.Enum(),
Number: proto.Int32(2),
},
},
}
meth := &protodescriptor.MethodDescriptorProto{
Name: proto.String("Echo"),
InputType: proto.String("ExampleMessage"),
OutputType: proto.String("ExampleMessage"),
ClientStreaming: proto.Bool(false),
}
svc := &protodescriptor.ServiceDescriptorProto{
Name: proto.String("ExampleService"),
Method: []*protodescriptor.MethodDescriptorProto{meth},
}
for _, spec := range []struct {
serverStreaming bool
sigWant string
}{
{
serverStreaming: false,
sigWant: `func request_ExampleService_Echo_0(ctx context.Context, marshaler runtime.Marshaler, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {`,
},
{
serverStreaming: true,
sigWant: `func request_ExampleService_Echo_0(ctx context.Context, marshaler runtime.Marshaler, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (ExampleService_EchoClient, runtime.ServerMetadata, error) {`,
},
} {
meth.ServerStreaming = proto.Bool(spec.serverStreaming)
msg := &descriptor.Message{
DescriptorProto: msgdesc,
}
nested := &descriptor.Message{
DescriptorProto: nesteddesc,
}
nestedField := &descriptor.Field{
Message: msg,
FieldDescriptorProto: msg.GetField()[0],
}
intField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[0],
}
boolField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[1],
}
file := descriptor.File{
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
Name: proto.String("example.proto"),
Package: proto.String("example"),
MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc},
Service: []*protodescriptor.ServiceDescriptorProto{svc},
},
GoPkg: descriptor.GoPackage{
Path: "example.com/path/to/example/example.pb",
Name: "example_pb",
},
Messages: []*descriptor.Message{msg, nested},
Services: []*descriptor.Service{
{
ServiceDescriptorProto: svc,
Methods: []*descriptor.Method{
{
MethodDescriptorProto: meth,
RequestType: msg,
ResponseType: msg,
Bindings: []*descriptor.Binding{
{
HTTPMethod: "POST",
PathTmpl: httprule.Template{
Version: 1,
OpCodes: []int{0, 0},
},
PathParams: []descriptor.Parameter{
{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "int32",
Target: intField,
},
}),
Target: intField,
},
},
Body: &descriptor.Body{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "bool",
Target: boolField,
},
}),
},
},
},
},
},
},
},
}
got, err := applyTemplate(param{File: crossLinkFixture(&file)})
if err != nil {
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
return
}
if want := spec.sigWant; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `marshaler.NewDecoder(req.Body).Decode(&protoReq.GetNested().Bool)`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `val, ok = pathParams["nested.int32"]`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `protoReq.GetNested().Int32, err = runtime.Int32P(val)`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `func RegisterExampleServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `pattern_ExampleService_Echo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{0, 0}, []string(nil), ""))`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
}
}
func TestApplyTemplateRequestWithClientStreaming(t *testing.T) {
msgdesc := &protodescriptor.DescriptorProto{
Name: proto.String("ExampleMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("nested"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
TypeName: proto.String("NestedMessage"),
Number: proto.Int32(1),
},
},
}
nesteddesc := &protodescriptor.DescriptorProto{
Name: proto.String("NestedMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("int32"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_INT32.Enum(),
Number: proto.Int32(1),
},
{
Name: proto.String("bool"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_BOOL.Enum(),
Number: proto.Int32(2),
},
},
}
meth := &protodescriptor.MethodDescriptorProto{
Name: proto.String("Echo"),
InputType: proto.String("ExampleMessage"),
OutputType: proto.String("ExampleMessage"),
ClientStreaming: proto.Bool(true),
}
svc := &protodescriptor.ServiceDescriptorProto{
Name: proto.String("ExampleService"),
Method: []*protodescriptor.MethodDescriptorProto{meth},
}
for _, spec := range []struct {
serverStreaming bool
sigWant string
}{
{
serverStreaming: false,
sigWant: `func request_ExampleService_Echo_0(ctx context.Context, marshaler runtime.Marshaler, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {`,
},
{
serverStreaming: true,
sigWant: `func request_ExampleService_Echo_0(ctx context.Context, marshaler runtime.Marshaler, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (ExampleService_EchoClient, runtime.ServerMetadata, error) {`,
},
} {
meth.ServerStreaming = proto.Bool(spec.serverStreaming)
msg := &descriptor.Message{
DescriptorProto: msgdesc,
}
nested := &descriptor.Message{
DescriptorProto: nesteddesc,
}
nestedField := &descriptor.Field{
Message: msg,
FieldDescriptorProto: msg.GetField()[0],
}
intField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[0],
}
boolField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[1],
}
file := descriptor.File{
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
Name: proto.String("example.proto"),
Package: proto.String("example"),
MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc},
Service: []*protodescriptor.ServiceDescriptorProto{svc},
},
GoPkg: descriptor.GoPackage{
Path: "example.com/path/to/example/example.pb",
Name: "example_pb",
},
Messages: []*descriptor.Message{msg, nested},
Services: []*descriptor.Service{
{
ServiceDescriptorProto: svc,
Methods: []*descriptor.Method{
{
MethodDescriptorProto: meth,
RequestType: msg,
ResponseType: msg,
Bindings: []*descriptor.Binding{
{
HTTPMethod: "POST",
PathTmpl: httprule.Template{
Version: 1,
OpCodes: []int{0, 0},
},
PathParams: []descriptor.Parameter{
{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "int32",
Target: intField,
},
}),
Target: intField,
},
},
Body: &descriptor.Body{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "bool",
Target: boolField,
},
}),
},
},
},
},
},
},
},
}
got, err := applyTemplate(param{File: crossLinkFixture(&file)})
if err != nil {
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
return
}
if want := spec.sigWant; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `marshaler.NewDecoder(req.Body)`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `func RegisterExampleServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `pattern_ExampleService_Echo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{0, 0}, []string(nil), ""))`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
}
}

View File

@@ -1,122 +0,0 @@
package httprule
import (
"reflect"
"testing"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
)
const (
operandFiller = 0
)
func TestCompile(t *testing.T) {
for _, spec := range []struct {
segs []segment
verb string
ops []int
pool []string
fields []string
}{
{},
{
segs: []segment{
wildcard{},
},
ops: []int{int(utilities.OpPush), operandFiller},
},
{
segs: []segment{
deepWildcard{},
},
ops: []int{int(utilities.OpPushM), operandFiller},
},
{
segs: []segment{
literal("v1"),
},
ops: []int{int(utilities.OpLitPush), 0},
pool: []string{"v1"},
},
{
segs: []segment{
literal("v1"),
},
verb: "LOCK",
ops: []int{int(utilities.OpLitPush), 0},
pool: []string{"v1"},
},
{
segs: []segment{
variable{
path: "name.nested",
segments: []segment{
wildcard{},
},
},
},
ops: []int{
int(utilities.OpPush), operandFiller,
int(utilities.OpConcatN), 1,
int(utilities.OpCapture), 0,
},
pool: []string{"name.nested"},
fields: []string{"name.nested"},
},
{
segs: []segment{
literal("obj"),
variable{
path: "name.nested",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
variable{
path: "obj",
segments: []segment{
deepWildcard{},
},
},
},
ops: []int{
int(utilities.OpLitPush), 0,
int(utilities.OpLitPush), 1,
int(utilities.OpPush), operandFiller,
int(utilities.OpLitPush), 2,
int(utilities.OpConcatN), 3,
int(utilities.OpCapture), 3,
int(utilities.OpPushM), operandFiller,
int(utilities.OpConcatN), 1,
int(utilities.OpCapture), 0,
},
pool: []string{"obj", "a", "b", "name.nested"},
fields: []string{"name.nested", "obj"},
},
} {
tmpl := template{
segments: spec.segs,
verb: spec.verb,
}
compiled := tmpl.Compile()
if got, want := compiled.Version, opcodeVersion; got != want {
t.Errorf("tmpl.Compile().Version = %d; want %d; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.OpCodes, spec.ops; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().OpCodes = %v; want %v; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Pool, spec.pool; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().Pool = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Verb, spec.verb; got != want {
t.Errorf("tmpl.Compile().Verb = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Fields, spec.fields; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().Fields = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
}
}

View File

@@ -1,313 +0,0 @@
package httprule
import (
"flag"
"fmt"
"reflect"
"testing"
"github.com/golang/glog"
)
func TestTokenize(t *testing.T) {
for _, spec := range []struct {
src string
tokens []string
}{
{
src: "",
tokens: []string{eof},
},
{
src: "v1",
tokens: []string{"v1", eof},
},
{
src: "v1/b",
tokens: []string{"v1", "/", "b", eof},
},
{
src: "v1/endpoint/*",
tokens: []string{"v1", "/", "endpoint", "/", "*", eof},
},
{
src: "v1/endpoint/**",
tokens: []string{"v1", "/", "endpoint", "/", "**", eof},
},
{
src: "v1/b/{bucket_name=*}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "*", "}",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}/o",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
"o",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}/o/{name}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
"o", "/", "{", "name", "}",
eof,
},
},
{
src: "v1/a=b&c=d;e=f:g/endpoint.rdf",
tokens: []string{
"v1", "/",
"a=b&c=d;e=f:g", "/",
"endpoint.rdf",
eof,
},
},
} {
tokens, verb := tokenize(spec.src)
if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
t.Errorf("tokenize(%q) = %q, _; want %q, _", spec.src, got, want)
}
if got, want := verb, ""; got != want {
t.Errorf("tokenize(%q) = _, %q; want _, %q", spec.src, got, want)
}
src := fmt.Sprintf("%s:%s", spec.src, "LOCK")
tokens, verb = tokenize(src)
if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
t.Errorf("tokenize(%q) = %q, _; want %q, _", src, got, want)
}
if got, want := verb, "LOCK"; got != want {
t.Errorf("tokenize(%q) = _, %q; want _, %q", src, got, want)
}
}
}
func TestParseSegments(t *testing.T) {
flag.Set("v", "3")
for _, spec := range []struct {
tokens []string
want []segment
}{
{
tokens: []string{"v1", eof},
want: []segment{
literal("v1"),
},
},
{
tokens: []string{"-._~!$&'()*+,;=:@", eof},
want: []segment{
literal("-._~!$&'()*+,;=:@"),
},
},
{
tokens: []string{"%e7%ac%ac%e4%b8%80%e7%89%88", eof},
want: []segment{
literal("%e7%ac%ac%e4%b8%80%e7%89%88"),
},
},
{
tokens: []string{"v1", "/", "*", eof},
want: []segment{
literal("v1"),
wildcard{},
},
},
{
tokens: []string{"v1", "/", "**", eof},
want: []segment{
literal("v1"),
deepWildcard{},
},
},
{
tokens: []string{"{", "name", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "name", "=", "*", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "field", ".", "nested", ".", "nested2", "=", "*", "}", eof},
want: []segment{
variable{
path: "field.nested.nested2",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "name", "=", "a", "/", "b", "/", "*", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
},
},
},
},
{
tokens: []string{
"v1", "/",
"{",
"name", ".", "nested", ".", "nested2",
"=",
"a", "/", "b", "/", "*",
"}", "/",
"o", "/",
"{",
"another_name",
"=",
"a", "/", "b", "/", "*", "/", "c",
"}", "/",
"**",
eof},
want: []segment{
literal("v1"),
variable{
path: "name.nested.nested2",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
},
},
literal("o"),
variable{
path: "another_name",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
literal("c"),
},
},
deepWildcard{},
},
},
} {
p := parser{tokens: spec.tokens}
segs, err := p.topLevelSegments()
if err != nil {
t.Errorf("parser{%q}.segments() failed with %v; want success", spec.tokens, err)
continue
}
if got, want := segs, spec.want; !reflect.DeepEqual(got, want) {
t.Errorf("parser{%q}.segments() = %#v; want %#v", spec.tokens, got, want)
}
if got := p.tokens; len(got) > 0 {
t.Errorf("p.tokens = %q; want []; spec.tokens=%q", got, spec.tokens)
}
}
}
func TestParseSegmentsWithErrors(t *testing.T) {
flag.Set("v", "3")
for _, spec := range []struct {
tokens []string
}{
{
// double slash
tokens: []string{"/", eof},
},
{
// invalid literal
tokens: []string{"a?b", eof},
},
{
// invalid percent-encoding
tokens: []string{"%", eof},
},
{
// invalid percent-encoding
tokens: []string{"%2", eof},
},
{
// invalid percent-encoding
tokens: []string{"a%2z", eof},
},
{
// empty segments
tokens: []string{eof},
},
{
// unterminated variable
tokens: []string{"{", "name", eof},
},
{
// unterminated variable
tokens: []string{"{", "name", "=", eof},
},
{
// unterminated variable
tokens: []string{"{", "name", "=", "*", eof},
},
{
// empty component in field path
tokens: []string{"{", "name", ".", "}", eof},
},
{
// empty component in field path
tokens: []string{"{", "name", ".", ".", "nested", "}", eof},
},
{
// invalid character in identifier
tokens: []string{"{", "field-name", "}", eof},
},
{
// no slash between segments
tokens: []string{"v1", "endpoint", eof},
},
{
// no slash between segments
tokens: []string{"v1", "{", "name", "}", eof},
},
} {
p := parser{tokens: spec.tokens}
segs, err := p.topLevelSegments()
if err == nil {
t.Errorf("parser{%q}.segments() succeeded; want InvalidTemplateError; accepted %#v", spec.tokens, segs)
continue
}
glog.V(1).Info(err)
}
}

View File

@@ -1,91 +0,0 @@
package httprule
import (
"fmt"
"testing"
)
func TestTemplateStringer(t *testing.T) {
for _, spec := range []struct {
segs []segment
want string
}{
{
segs: []segment{
literal("v1"),
},
want: "/v1",
},
{
segs: []segment{
wildcard{},
},
want: "/*",
},
{
segs: []segment{
deepWildcard{},
},
want: "/**",
},
{
segs: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
},
},
},
want: "/{name=a}",
},
{
segs: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
},
want: "/{name=a/*/b}",
},
{
segs: []segment{
literal("v1"),
variable{
path: "name",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
literal("c"),
variable{
path: "field.nested",
segments: []segment{
wildcard{},
literal("d"),
},
},
wildcard{},
literal("e"),
deepWildcard{},
},
want: "/v1/{name=a/*/b}/c/{field.nested=*/d}/*/e/**",
},
} {
tmpl := template{segments: spec.segs}
if got, want := tmpl.String(), spec.want; got != want {
t.Errorf("%#v.String() = %q; want %q", tmpl, got, want)
}
tmpl.verb = "LOCK"
if got, want := tmpl.String(), fmt.Sprintf("%s:LOCK", spec.want); got != want {
t.Errorf("%#v.String() = %q; want %q", tmpl, got, want)
}
}
}

View File

@@ -1,119 +0,0 @@
// Command protoc-gen-grpc-gateway is a plugin for Google protocol buffer
// compiler to generate a reverse-proxy, which converts incoming RESTful
// HTTP/1 requests gRPC invocation.
// You rarely need to run this program directly. Instead, put this program
// into your $PATH with a name "protoc-gen-grpc-gateway" and run
// protoc --grpc-gateway_out=output_directory path/to/input.proto
//
// See README.md for more details.
package main
import (
"flag"
"io"
"io/ioutil"
"os"
"strings"
"github.com/golang/glog"
"github.com/golang/protobuf/proto"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/gengateway"
)
var (
importPrefix = flag.String("import_prefix", "", "prefix to be added to go package paths for imported proto files")
useRequestContext = flag.Bool("request_context", false, "determine whether to use http.Request's context or not")
)
func parseReq(r io.Reader) (*plugin.CodeGeneratorRequest, error) {
glog.V(1).Info("Parsing code generator request")
input, err := ioutil.ReadAll(r)
if err != nil {
glog.Errorf("Failed to read code generator request: %v", err)
return nil, err
}
req := new(plugin.CodeGeneratorRequest)
if err = proto.Unmarshal(input, req); err != nil {
glog.Errorf("Failed to unmarshal code generator request: %v", err)
return nil, err
}
glog.V(1).Info("Parsed code generator request")
return req, nil
}
func main() {
flag.Parse()
defer glog.Flush()
reg := descriptor.NewRegistry()
glog.V(1).Info("Processing code generator request")
req, err := parseReq(os.Stdin)
if err != nil {
glog.Fatal(err)
}
if req.Parameter != nil {
for _, p := range strings.Split(req.GetParameter(), ",") {
spec := strings.SplitN(p, "=", 2)
if len(spec) == 1 {
if err := flag.CommandLine.Set(spec[0], ""); err != nil {
glog.Fatalf("Cannot set flag %s", p)
}
continue
}
name, value := spec[0], spec[1]
if strings.HasPrefix(name, "M") {
reg.AddPkgMap(name[1:], value)
continue
}
if err := flag.CommandLine.Set(name, value); err != nil {
glog.Fatalf("Cannot set flag %s", p)
}
}
}
g := gengateway.New(reg, *useRequestContext)
reg.SetPrefix(*importPrefix)
if err := reg.Load(req); err != nil {
emitError(err)
return
}
var targets []*descriptor.File
for _, target := range req.FileToGenerate {
f, err := reg.LookupFile(target)
if err != nil {
glog.Fatal(err)
}
targets = append(targets, f)
}
out, err := g.Generate(targets)
glog.V(1).Info("Processed code generator request")
if err != nil {
emitError(err)
return
}
emitFiles(out)
}
func emitFiles(out []*plugin.CodeGeneratorResponse_File) {
emitResp(&plugin.CodeGeneratorResponse{File: out})
}
func emitError(err error) {
emitResp(&plugin.CodeGeneratorResponse{Error: proto.String(err.Error())})
}
func emitResp(resp *plugin.CodeGeneratorResponse) {
buf, err := proto.Marshal(resp)
if err != nil {
glog.Fatal(err)
}
if _, err := os.Stdout.Write(buf); err != nil {
glog.Fatal(err)
}
}