Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new feature: use grpcurl as an offline tool to convert JSON into proto #309

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 45 additions & 20 deletions cmd/grpcurl/grpcurl.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ var (
permitted if they are both set to the same value, to increase backwards
compatibility with earlier releases that allowed both to be set).`))
reflection = optionalBoolFlag{val: true}

convertMessage = flags.String("convert-message", "", prettify(`
Turns the tool into an offline converter; the proto representation of
the JSON input will be printed to the standard output.
This parameter should specify the fully qualified name of the protobuf
message definition (e.g. some.service/ReadRequest).
`))
)

func init() {
Expand Down Expand Up @@ -301,28 +308,35 @@ func main() {
}

args := flags.Args()

convertJson := *convertMessage != ""

if len(args) == 0 {
fail(nil, "Too few arguments.")
}
var target string
if args[0] != "list" && args[0] != "describe" {
target = args[0]
args = args[1:]
}

if len(args) == 0 {
fail(nil, "Too few arguments.")
}
var list, describe, invoke bool
if args[0] == "list" {
list = true
args = args[1:]
} else if args[0] == "describe" {
describe = true
args = args[1:]
if len(args) != 0 {
if convertJson {
fail(nil, "Target service should be omitted for offline conversions.")
}

if args[0] != "list" && args[0] != "describe" {
target = args[0]
args = args[1:]
}

if args[0] == "list" {
list = true
args = args[1:]
} else if args[0] == "describe" {
describe = true
args = args[1:]
} else {
invoke = true
}

} else {
invoke = true
if !convertJson {
fail(nil, "Too few arguments.")
}
}

verbosityLevel := 0
Expand All @@ -341,7 +355,7 @@ func main() {
symbol = args[0]
args = args[1:]
} else {
if *data != "" {
if *data != "" && !convertJson {
warn("The -d argument is not used with 'list' or 'describe' verb.")
}
if len(rpcHeaders) > 0 {
Expand Down Expand Up @@ -675,9 +689,10 @@ func main() {

} else {
// Invoke an RPC
if cc == nil {
if cc == nil && !convertJson {
cc = dial()
}

var in io.Reader
if *data == "@" {
in = os.Stdin
Expand All @@ -698,6 +713,16 @@ func main() {
if err != nil {
fail(err, "Failed to construct request parser and formatter for %q", *format)
}

if convertJson {
proto, err:= grpcurl.ConvertMessage(descSource, *convertMessage, rf.Next)
if err != nil {
fail(err, "Error converting message %q", *convertMessage)
}
os.Stdout.Write(proto)
os.Exit(0)
}

h := &grpcurl.DefaultEventHandler{
Out: os.Stdout,
Formatter: formatter,
Expand Down
58 changes: 46 additions & 12 deletions invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,18 +119,7 @@ func InvokeRPC(ctx context.Context, source DescriptorSource, ch grpcdynamic.Chan

handler.OnResolveMethod(mtd)

// we also download any applicable extensions so we can provide full support for parsing user-provided data
var ext dynamic.ExtensionRegistry
alreadyFetched := map[string]bool{}
if err = fetchAllExtensions(source, &ext, mtd.GetInputType(), alreadyFetched); err != nil {
return fmt.Errorf("error resolving server extensions for message %s: %v", mtd.GetInputType().GetFullyQualifiedName(), err)
}
if err = fetchAllExtensions(source, &ext, mtd.GetOutputType(), alreadyFetched); err != nil {
return fmt.Errorf("error resolving server extensions for message %s: %v", mtd.GetOutputType().GetFullyQualifiedName(), err)
}

msgFactory := dynamic.NewMessageFactoryWithExtensionRegistry(&ext)
req := msgFactory.NewMessage(mtd.GetInputType())
req, msgFactory, err:= getExtensions(source, mtd.GetInputType())

handler.OnSendHeaders(md)
ctx = metadata.NewOutgoingContext(ctx, md)
Expand Down Expand Up @@ -403,3 +392,48 @@ func parseSymbol(svcAndMethod string) (string, string) {
}
return svcAndMethod[:pos], svcAndMethod[pos+1:]
}

func ConvertMessage(source DescriptorSource, messageFullName string, requestData RequestSupplier) ([]byte, error) {
dsc, err := source.FindSymbol(messageFullName)
if err != nil {
return nil, err
}
md, ok := dsc.(*desc.MessageDescriptor)
if ! ok {
return nil, fmt.Errorf("%q should point to a message descriptor instead of %d:", messageFullName, md)
}

req, _, err:= getExtensions(source, md)
if err != nil {
return nil, err
}

err = requestData(req)
if err != nil {
return nil, err
}

re, err:= proto.Marshal(req)
if err != nil {
return nil, err
}

return re, nil
}

func getExtensions(source DescriptorSource, md *desc.MessageDescriptor) (req proto.Message, msgFactory *dynamic.MessageFactory, err error) {
// we also download any applicable extensions so we can provide full support for parsing user-provided data
var ext dynamic.ExtensionRegistry
alreadyFetched := map[string]bool{}
if err = fetchAllExtensions(source, &ext, md, alreadyFetched); err != nil {
return nil, nil, fmt.Errorf("error resolving server extensions for message %s: %v", md.GetFullyQualifiedName(), err)
}
if err = fetchAllExtensions(source, &ext, md, alreadyFetched); err != nil {
return nil, nil, fmt.Errorf("error resolving server extensions for message %s: %v", md.GetFullyQualifiedName(), err)
}

msgFactory = dynamic.NewMessageFactoryWithExtensionRegistry(&ext)
req = msgFactory.NewMessage(md)

return req, msgFactory, nil
}