From 58cd93280e42868dd1f27f29c8a8c8494148cf66 Mon Sep 17 00:00:00 2001 From: Joshua Humphries Date: Thu, 18 Oct 2018 13:50:44 -0400 Subject: [PATCH] use proto syntax for describe (#57) --- cmd/grpcurl/formatting_test.go | 11 ++----- cmd/grpcurl/grpcurl.go | 57 ++++++++++++++++++++++++++-------- go.mod | 2 +- go.sum | 4 +-- grpcurl.go | 36 +++++++++++++++------ 5 files changed, 76 insertions(+), 34 deletions(-) diff --git a/cmd/grpcurl/formatting_test.go b/cmd/grpcurl/formatting_test.go index bc69079..ba7a58b 100644 --- a/cmd/grpcurl/formatting_test.go +++ b/cmd/grpcurl/formatting_test.go @@ -165,7 +165,7 @@ func TestHandler(t *testing.T) { out := buf.String() if !compare(out, expectedOutput) { - t.Errorf("%s: Incorrect output.", name) // Expected:\n%s\nGot:\n%s", name, expectedOutput, out) + t.Errorf("%s: Incorrect output. Expected:\n%s\nGot:\n%s", name, expectedOutput, out) } } } @@ -209,14 +209,7 @@ func makeProto() (proto.Message, error) { var ( verbosePrefix = ` Resolved method descriptor: -{ - "name": "GetFiles", - "inputType": ".TestRequest", - "outputType": ".TestResponse", - "options": { - - } -} +rpc GetFiles ( .TestRequest ) returns ( .TestResponse ); Request metadata to send: bar: 456 diff --git a/cmd/grpcurl/grpcurl.go b/cmd/grpcurl/grpcurl.go index 0d6093d..6aca975 100644 --- a/cmd/grpcurl/grpcurl.go +++ b/cmd/grpcurl/grpcurl.go @@ -406,30 +406,61 @@ func main() { fail(err, "Failed to resolve symbol %q", s) } - txt, err := grpcurl.GetDescriptorText(dsc, descSource) - if err != nil { - fail(err, "Failed to describe symbol %q", s) - } - - switch dsc.(type) { + fqn := dsc.GetFullyQualifiedName() + var elementType string + switch d := dsc.(type) { case *desc.MessageDescriptor: - fmt.Printf("%s is a message:\n", dsc.GetFullyQualifiedName()) + elementType = "a message" + parent, ok := d.GetParent().(*desc.MessageDescriptor) + if ok { + if d.IsMapEntry() { + for _, f := range parent.GetFields() { + if f.IsMap() && f.GetMessageType() == d { + // found it: describe the map field instead + elementType = "the entry type for a map field" + dsc = f + break + } + } + } else { + // see if it's a group + for _, f := range parent.GetFields() { + if f.GetType() == descpb.FieldDescriptorProto_TYPE_GROUP && f.GetMessageType() == d { + // found it: describe the map field instead + elementType = "the type of a group field" + dsc = f + break + } + } + } + } case *desc.FieldDescriptor: - fmt.Printf("%s is a field:\n", dsc.GetFullyQualifiedName()) + elementType = "a field" + if d.GetType() == descpb.FieldDescriptorProto_TYPE_GROUP { + elementType = "a group field" + } else if d.IsExtension() { + elementType = "an extension" + } case *desc.OneOfDescriptor: - fmt.Printf("%s is a one-of:\n", dsc.GetFullyQualifiedName()) + elementType = "a one-of" case *desc.EnumDescriptor: - fmt.Printf("%s is an enum:\n", dsc.GetFullyQualifiedName()) + elementType = "an enum" case *desc.EnumValueDescriptor: - fmt.Printf("%s is an enum value:\n", dsc.GetFullyQualifiedName()) + elementType = "an enum value" case *desc.ServiceDescriptor: - fmt.Printf("%s is a service:\n", dsc.GetFullyQualifiedName()) + elementType = "a service" case *desc.MethodDescriptor: - fmt.Printf("%s is a method:\n", dsc.GetFullyQualifiedName()) + elementType = "a method" default: err = fmt.Errorf("descriptor has unrecognized type %T", dsc) fail(err, "Failed to describe symbol %q", s) } + + txt, err := grpcurl.GetDescriptorText(dsc, descSource) + if err != nil { + fail(err, "Failed to describe symbol %q", s) + } + fmt.Printf("%s is %s:\n", fqn, elementType) fmt.Println(txt) if dsc, ok := dsc.(*desc.MessageDescriptor); ok && *msgTemplate { diff --git a/go.mod b/go.mod index 356d24a..fce16e0 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/fullstorydev/grpcurl require ( github.com/golang/protobuf v1.1.0 - github.com/jhump/protoreflect v1.0.0 + github.com/jhump/protoreflect v1.1.0 golang.org/x/net v0.0.0-20180530234432-1e491301e022 google.golang.org/grpc v1.12.0 ) diff --git a/go.sum b/go.sum index 792d813..b9ed0e7 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/golang/protobuf v1.1.0 h1:0iH4Ffd/meGoXqF2lSAhZHt8X+cPgkfn/cb6Cce5Vpc= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/jhump/protoreflect v1.0.0 h1:l94KtQ6gRI3ouKVcXNdofCQJWoHATzcI6tDizOgUaf0= -github.com/jhump/protoreflect v1.0.0/go.mod h1:kG/zRVeS2M91gYaCvvUbPkMjjtFQS4qqjcPFzFkh2zE= +github.com/jhump/protoreflect v1.1.0 h1:h+zsMrsiq0vIl7yWmeowmd8e8VtnWk75U04GgXA2s6Y= +github.com/jhump/protoreflect v1.1.0/go.mod h1:kG/zRVeS2M91gYaCvvUbPkMjjtFQS4qqjcPFzFkh2zE= golang.org/x/net v0.0.0-20180530234432-1e491301e022 h1:MVYFTUmVD3/+ERcvRRI+P/C2+WOUimXh+Pd8LVsklZ4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= diff --git a/grpcurl.go b/grpcurl.go index 9419c02..4360be4 100644 --- a/grpcurl.go +++ b/grpcurl.go @@ -24,9 +24,10 @@ import ( "github.com/golang/protobuf/jsonpb" "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" + descpb "github.com/golang/protobuf/protoc-gen-go/descriptor" "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/desc/protoparse" + "github.com/jhump/protoreflect/desc/protoprint" "github.com/jhump/protoreflect/dynamic" "github.com/jhump/protoreflect/dynamic/grpcdynamic" "github.com/jhump/protoreflect/grpcreflect" @@ -59,13 +60,13 @@ type DescriptorSource interface { // DescriptorSourceFromProtoSets creates a DescriptorSource that is backed by the named files, whose contents // are encoded FileDescriptorSet protos. func DescriptorSourceFromProtoSets(fileNames ...string) (DescriptorSource, error) { - files := &descriptor.FileDescriptorSet{} + files := &descpb.FileDescriptorSet{} for _, fileName := range fileNames { b, err := ioutil.ReadFile(fileName) if err != nil { return nil, fmt.Errorf("could not load protoset file %q: %v", fileName, err) } - var fs descriptor.FileDescriptorSet + var fs descpb.FileDescriptorSet err = proto.Unmarshal(b, &fs) if err != nil { return nil, fmt.Errorf("could not parse contents of protoset file %q: %v", fileName, err) @@ -91,8 +92,8 @@ func DescriptorSourceFromProtoFiles(importPaths []string, fileNames ...string) ( } // DescriptorSourceFromFileDescriptorSet creates a DescriptorSource that is backed by the FileDescriptorSet. -func DescriptorSourceFromFileDescriptorSet(files *descriptor.FileDescriptorSet) (DescriptorSource, error) { - unresolved := map[string]*descriptor.FileDescriptorProto{} +func DescriptorSourceFromFileDescriptorSet(files *descpb.FileDescriptorSet) (DescriptorSource, error) { + unresolved := map[string]*descpb.FileDescriptorProto{} for _, fd := range files.File { unresolved[fd.GetName()] = fd } @@ -106,7 +107,7 @@ func DescriptorSourceFromFileDescriptorSet(files *descriptor.FileDescriptorSet) return &fileSource{files: resolved}, nil } -func resolveFileDescriptor(unresolved map[string]*descriptor.FileDescriptorProto, resolved map[string]*desc.FileDescriptor, filename string) (*desc.FileDescriptor, error) { +func resolveFileDescriptor(unresolved map[string]*descpb.FileDescriptorProto, resolved map[string]*desc.FileDescriptor, filename string) (*desc.FileDescriptor, error) { if r, ok := resolved[filename]; ok { return r, nil } @@ -811,10 +812,27 @@ func MetadataToString(md metadata.MD) string { return b.String() } +var printer = &protoprint.Printer{ + Compact: true, + OmitComments: protoprint.CommentsNonDoc, + SortElements: true, + ForceFullyQualifiedNames: true, +} + // GetDescriptorText returns a string representation of the given descriptor. -func GetDescriptorText(dsc desc.Descriptor, descSource DescriptorSource) (string, error) { - dscProto := EnsureExtensions(descSource, dsc.AsProto()) - return (&jsonpb.Marshaler{Indent: " "}).MarshalToString(dscProto) +// This returns a snippet of proto source that describes the given element. +func GetDescriptorText(dsc desc.Descriptor, _ DescriptorSource) (string, error) { + // Note: DescriptorSource is not used, but remains an argument for backwards + // compatibility with previous implementation. + txt, err := printer.PrintProtoToString(dsc) + if err != nil { + return "", err + } + // callers don't expect trailing newlines + if txt[len(txt)-1] == '\n' { + txt = txt[:len(txt)-1] + } + return txt, nil } // EnsureExtensions uses the given descriptor source to download extensions for