Created
May 26, 2023 17:54
-
-
Save breadchris/9b7ec8afc5261283cffa6f30cca23051 to your computer and use it in GitHub Desktop.
Protobuf Protoflow Workflow
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package grpc | |
import ( | |
"fmt" | |
"github.com/jhump/protoreflect/desc" | |
"google.golang.org/protobuf/types/descriptorpb" | |
"strings" | |
"testing" | |
) | |
// TODO breadchris figure out if we can base a graph off of just protobuf files | |
func TestParseProto(t *testing.T) { | |
res, err := ParseProto("") | |
if err != nil { | |
t.Fatal(err) | |
} | |
m := map[string]interface{}{} | |
for _, r := range res { | |
buildUninterpretedMapForFile(r.GetFile(), m) | |
for _, s := range r.GetServices() { | |
t.Log(s.GetFullyQualifiedName()) | |
for _, m := range s.GetMethods() { | |
t.Log(m.GetFullyQualifiedName()) | |
mo := m.GetMethodOptions() | |
mo.GetUninterpretedOption() | |
t.Log(mo.String()) | |
for _, o := range mo.GetUninterpretedOption() { | |
for _, n := range o.GetName() { | |
t.Log(n) | |
} | |
} | |
} | |
} | |
} | |
} | |
func buildUninterpretedMapForFile(fd *desc.FileDescriptor, opts map[string]interface{}) { | |
buildUninterpretedMap(fd.GetName(), fd.GetFileOptions().GetUninterpretedOption(), opts) | |
for _, md := range fd.GetMessageTypes() { | |
buildUninterpretedMapForMessage(fd.GetPackage(), md.AsDescriptorProto(), opts) | |
} | |
//for _, extd := range fd.GetExtensions() { | |
// buildUninterpretedMap(qualify(fd.GetPackage(), extd.GetName()), extd.GetOp.GetUninterpretedOption(), opts) | |
//} | |
for _, ed := range fd.GetEnumTypes() { | |
buildUninterpretedMapForEnum(fd.GetPackage(), ed.AsEnumDescriptorProto(), opts) | |
} | |
for _, sd := range fd.GetServices() { | |
buildUninterpretedMap(sd.GetFullyQualifiedName(), sd.GetServiceOptions().GetUninterpretedOption(), opts) | |
for _, mtd := range sd.GetMethods() { | |
buildUninterpretedMap(mtd.GetFullyQualifiedName(), mtd.GetMethodOptions().GetUninterpretedOption(), opts) | |
} | |
} | |
} | |
func buildUninterpretedMapForMessage(qual string, md *descriptorpb.DescriptorProto, opts map[string]interface{}) { | |
fqn := qualify(qual, md.GetName()) | |
buildUninterpretedMap(fqn, md.GetOptions().GetUninterpretedOption(), opts) | |
for _, fld := range md.GetField() { | |
buildUninterpretedMap(qualify(fqn, fld.GetName()), fld.GetOptions().GetUninterpretedOption(), opts) | |
} | |
for _, ood := range md.GetOneofDecl() { | |
buildUninterpretedMap(qualify(fqn, ood.GetName()), ood.GetOptions().GetUninterpretedOption(), opts) | |
} | |
for _, extr := range md.GetExtensionRange() { | |
buildUninterpretedMap(qualify(fqn, fmt.Sprintf("%d-%d", extr.GetStart(), extr.GetEnd()-1)), extr.GetOptions().GetUninterpretedOption(), opts) | |
} | |
for _, nmd := range md.GetNestedType() { | |
buildUninterpretedMapForMessage(fqn, nmd, opts) | |
} | |
for _, extd := range md.GetExtension() { | |
buildUninterpretedMap(qualify(fqn, extd.GetName()), extd.GetOptions().GetUninterpretedOption(), opts) | |
} | |
for _, ed := range md.GetEnumType() { | |
buildUninterpretedMapForEnum(fqn, ed, opts) | |
} | |
} | |
func buildUninterpretedMapForEnum(qual string, ed *descriptorpb.EnumDescriptorProto, opts map[string]interface{}) { | |
fqn := qualify(qual, ed.GetName()) | |
buildUninterpretedMap(fqn, ed.GetOptions().GetUninterpretedOption(), opts) | |
for _, evd := range ed.GetValue() { | |
buildUninterpretedMap(qualify(fqn, evd.GetName()), evd.GetOptions().GetUninterpretedOption(), opts) | |
} | |
} | |
type ident string | |
type aggregate string | |
func buildUninterpretedMap(prefix string, uos []*descriptorpb.UninterpretedOption, opts map[string]interface{}) { | |
for _, uo := range uos { | |
parts := make([]string, len(uo.GetName())) | |
for i, np := range uo.GetName() { | |
if np.GetIsExtension() { | |
parts[i] = fmt.Sprintf("(%s)", np.GetNamePart()) | |
} else { | |
parts[i] = np.GetNamePart() | |
} | |
} | |
uoName := fmt.Sprintf("%s:%s", prefix, strings.Join(parts, ".")) | |
key := uoName | |
i := 0 | |
for { | |
if _, ok := opts[key]; !ok { | |
break | |
} | |
i++ | |
key = fmt.Sprintf("%s#%d", uoName, i) | |
} | |
var val interface{} | |
switch { | |
case uo.AggregateValue != nil: | |
val = aggregate(uo.GetAggregateValue()) | |
case uo.IdentifierValue != nil: | |
val = ident(uo.GetIdentifierValue()) | |
case uo.DoubleValue != nil: | |
val = uo.GetDoubleValue() | |
case uo.PositiveIntValue != nil: | |
val = int(uo.GetPositiveIntValue()) | |
case uo.NegativeIntValue != nil: | |
val = int(uo.GetNegativeIntValue()) | |
default: | |
val = string(uo.GetStringValue()) | |
} | |
opts[key] = val | |
} | |
} | |
func qualify(base, name string) string { | |
if base == "" { | |
return name | |
} | |
return base + "." + name | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
syntax = "proto3"; | |
import "google/protobuf/empty.proto"; | |
import "google/protobuf/descriptor.proto"; | |
import "block.proto"; | |
import "resource.proto"; | |
enum ServiceMethods { | |
ServiceOne_MethodOne = 0; | |
ServiceOne_MethodTwo = 1; | |
RemoteService_ExternalMethod = 2; | |
ServiceTwo_MethodOne = 3; | |
} | |
message MethodOneResponse { | |
string message = 1; | |
} | |
service RemoteService { | |
option (resource_info) = { | |
grpc_service: { | |
host: "localhost:50051" | |
} | |
}; | |
rpc ExternalMethod (google.protobuf.Empty) returns (MethodOneResponse) {}; | |
} | |
service ServiceOne { | |
rpc MethodOne (block.Input) returns (MethodOneResponse) { | |
option (protoflow) = { | |
calls: [ | |
ServiceOne_MethodTwo | |
] | |
}; | |
}; | |
rpc MethodTwo (MethodOneResponse) returns (google.protobuf.Empty) { | |
option (protoflow) = { | |
calls: [ | |
ServiceTwo_MethodOne | |
], | |
}; | |
}; | |
} | |
service ServiceTwo { | |
rpc MethodTwo (MethodOneResponse) returns (google.protobuf.Empty) {}; | |
} | |
// Definition of the `build.info` custom option | |
extend google.protobuf.MethodOptions { | |
Runtime protoflow = 1; | |
} | |
extend google.protobuf.ServiceOptions { | |
resource.Resource resource_info = 1; | |
} | |
// Definition of the `BuildMetadata` message used in `build.info` | |
message Runtime { | |
repeated ServiceMethods calls = 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment