Add functionality to export proto files
Added a new function, `WriteProtoFiles` in `desc_source.go` which is used to generate .proto files. The process involves resolving symbols from the descriptor source and writing their definitions to a designated output directory. The corresponding flag `--proto-out` has been included in `grpcurl.go` to allow users to specify the directory path.
This commit is contained in:
parent
a05d48d6dd
commit
876d9a9de3
|
|
@ -159,6 +159,11 @@ grpcurl -protoset my-protos.bin list
|
||||||
|
|
||||||
# Using proto sources
|
# Using proto sources
|
||||||
grpcurl -import-path ../protos -proto my-stuff.proto list
|
grpcurl -import-path ../protos -proto my-stuff.proto list
|
||||||
|
|
||||||
|
# Export proto files
|
||||||
|
grpcurl -plaintext -proto-out "out_protos" "192.168.100.1:9200" describe Api.Service
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The "list" verb also lets you see all methods in a particular service:
|
The "list" verb also lets you see all methods in a particular service:
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,14 @@ var (
|
||||||
file if this option is given. When invoking an RPC and this option is
|
file if this option is given. When invoking an RPC and this option is
|
||||||
given, the method being invoked and its transitive dependencies will be
|
given, the method being invoked and its transitive dependencies will be
|
||||||
included in the output file.`))
|
included in the output file.`))
|
||||||
|
protoOut = flags.String("proto-out", "", prettify(`
|
||||||
|
The name of a directory where the generated .proto files will be written.
|
||||||
|
With the list and describe verbs, the listed or described elements and
|
||||||
|
their transitive dependencies will be written as .proto files in the
|
||||||
|
specified directory if this option is given. When invoking an RPC and
|
||||||
|
this option is given, the method being invoked and its transitive
|
||||||
|
dependencies will be included in the generated .proto files in the
|
||||||
|
output directory.`))
|
||||||
msgTemplate = flags.Bool("msg-template", false, prettify(`
|
msgTemplate = flags.Bool("msg-template", false, prettify(`
|
||||||
When describing messages, show a template of input data.`))
|
When describing messages, show a template of input data.`))
|
||||||
verbose = flags.Bool("v", false, prettify(`
|
verbose = flags.Bool("v", false, prettify(`
|
||||||
|
|
@ -645,6 +653,9 @@ func main() {
|
||||||
if err := writeProtoset(descSource, svcs...); err != nil {
|
if err := writeProtoset(descSource, svcs...); err != nil {
|
||||||
fail(err, "Failed to write protoset to %s", *protosetOut)
|
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||||
}
|
}
|
||||||
|
if err := writeProtos(descSource, svcs...); err != nil {
|
||||||
|
fail(err, "Failed to write protos to %s", *protoOut)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
methods, err := grpcurl.ListMethods(descSource, symbol)
|
methods, err := grpcurl.ListMethods(descSource, symbol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -660,6 +671,9 @@ func main() {
|
||||||
if err := writeProtoset(descSource, symbol); err != nil {
|
if err := writeProtoset(descSource, symbol); err != nil {
|
||||||
fail(err, "Failed to write protoset to %s", *protosetOut)
|
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||||
}
|
}
|
||||||
|
if err := writeProtos(descSource, symbol); err != nil {
|
||||||
|
fail(err, "Failed to write protos to %s", *protoOut)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if describe {
|
} else if describe {
|
||||||
|
|
@ -764,6 +778,9 @@ func main() {
|
||||||
if err := writeProtoset(descSource, symbols...); err != nil {
|
if err := writeProtoset(descSource, symbols...); err != nil {
|
||||||
fail(err, "Failed to write protoset to %s", *protosetOut)
|
fail(err, "Failed to write protoset to %s", *protosetOut)
|
||||||
}
|
}
|
||||||
|
if err := writeProtos(descSource, symbol); err != nil {
|
||||||
|
fail(err, "Failed to write protos to %s", *protoOut)
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Invoke an RPC
|
// Invoke an RPC
|
||||||
|
|
@ -923,6 +940,13 @@ func writeProtoset(descSource grpcurl.DescriptorSource, symbols ...string) error
|
||||||
return grpcurl.WriteProtoset(f, descSource, symbols...)
|
return grpcurl.WriteProtoset(f, descSource, symbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeProtos(descSource grpcurl.DescriptorSource, symbols ...string) error {
|
||||||
|
if *protoOut == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return grpcurl.WriteProtoFiles(*protoOut, descSource, symbols...)
|
||||||
|
}
|
||||||
|
|
||||||
type optionalBoolFlag struct {
|
type optionalBoolFlag struct {
|
||||||
set, val bool
|
set, val bool
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/jhump/protoreflect/desc/protoprint"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
"github.com/golang/protobuf/proto" //lint:ignore SA1019 we have to import this because it appears in exported API
|
||||||
|
|
@ -258,19 +260,9 @@ func reflectionSupport(err error) error {
|
||||||
// given output. The output will include descriptors for all files in which the
|
// given output. The output will include descriptors for all files in which the
|
||||||
// symbols are defined as well as their transitive dependencies.
|
// symbols are defined as well as their transitive dependencies.
|
||||||
func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string) error {
|
func WriteProtoset(out io.Writer, descSource DescriptorSource, symbols ...string) error {
|
||||||
// compute set of file descriptors
|
filenames, fds, err := getFileDescriptors(symbols, descSource)
|
||||||
filenames := make([]string, 0, len(symbols))
|
if err != nil {
|
||||||
fds := make(map[string]*desc.FileDescriptor, len(symbols))
|
return err
|
||||||
for _, sym := range symbols {
|
|
||||||
d, err := descSource.FindSymbol(sym)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to find descriptor for %q: %v", sym, err)
|
|
||||||
}
|
|
||||||
fd := d.GetFile()
|
|
||||||
if _, ok := fds[fd.GetName()]; !ok {
|
|
||||||
fds[fd.GetName()] = fd
|
|
||||||
filenames = append(filenames, fd.GetName())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// now expand that to include transitive dependencies in topologically sorted
|
// now expand that to include transitive dependencies in topologically sorted
|
||||||
// order (such that file always appears after its dependencies)
|
// order (such that file always appears after its dependencies)
|
||||||
|
|
@ -302,3 +294,71 @@ func addFilesToSet(allFiles []*descriptorpb.FileDescriptorProto, expanded map[st
|
||||||
}
|
}
|
||||||
return append(allFiles, fd.AsFileDescriptorProto())
|
return append(allFiles, fd.AsFileDescriptorProto())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteProtoFiles will use the given descriptor source to resolve all the given
|
||||||
|
// symbols and write proto files with their definitions to the given output directory.
|
||||||
|
func WriteProtoFiles(outProtoDirPath string, descSource DescriptorSource, symbols ...string) error {
|
||||||
|
filenames, fds, err := getFileDescriptors(symbols, descSource)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// now expand that to include transitive dependencies in topologically sorted
|
||||||
|
// order (such that file always appears after its dependencies)
|
||||||
|
expandedFiles := make(map[string]struct{}, len(fds))
|
||||||
|
allFilesSlice := make([]*desc.FileDescriptor, 0, len(fds))
|
||||||
|
for _, filename := range filenames {
|
||||||
|
allFilesSlice = addFilesToFileDescriptorList(allFilesSlice, expandedFiles, fds[filename])
|
||||||
|
}
|
||||||
|
pr := protoprint.Printer{}
|
||||||
|
// now we can serialize to files
|
||||||
|
for _, fd := range allFilesSlice {
|
||||||
|
fdFQName := fd.GetFullyQualifiedName()
|
||||||
|
dirPath := filepath.Dir(fdFQName)
|
||||||
|
outFilepath := filepath.Join(outProtoDirPath, dirPath)
|
||||||
|
if err := os.MkdirAll(outFilepath, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create directory %q: %v", outFilepath, err)
|
||||||
|
}
|
||||||
|
fileName := filepath.Base(fdFQName)
|
||||||
|
filePath := filepath.Join(outFilepath, fileName)
|
||||||
|
f, err := os.Create(filePath)
|
||||||
|
defer f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create file %q: %v", filePath, err)
|
||||||
|
}
|
||||||
|
if err := pr.PrintProtoFile(fd, f); err != nil {
|
||||||
|
return fmt.Errorf("failed to write file %q: %v", filePath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFileDescriptors(symbols []string, descSource DescriptorSource) ([]string, map[string]*desc.FileDescriptor, error) {
|
||||||
|
// compute set of file descriptors
|
||||||
|
filenames := make([]string, 0, len(symbols))
|
||||||
|
fds := make(map[string]*desc.FileDescriptor, len(symbols))
|
||||||
|
for _, sym := range symbols {
|
||||||
|
d, err := descSource.FindSymbol(sym)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to find descriptor for %q: %v", sym, err)
|
||||||
|
}
|
||||||
|
fd := d.GetFile()
|
||||||
|
if _, ok := fds[fd.GetName()]; !ok {
|
||||||
|
fds[fd.GetName()] = fd
|
||||||
|
filenames = append(filenames, fd.GetName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filenames, fds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFilesToFileDescriptorList(allFiles []*desc.FileDescriptor, expanded map[string]struct{}, fd *desc.FileDescriptor) []*desc.FileDescriptor {
|
||||||
|
if _, ok := expanded[fd.GetName()]; ok {
|
||||||
|
// already seen this one
|
||||||
|
return allFiles
|
||||||
|
}
|
||||||
|
expanded[fd.GetName()] = struct{}{}
|
||||||
|
// add all dependencies first
|
||||||
|
for _, dep := range fd.GetDependencies() {
|
||||||
|
allFiles = addFilesToFileDescriptorList(allFiles, expanded, dep)
|
||||||
|
}
|
||||||
|
return append(allFiles, fd)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue