how to get httprule using this compiler
jaredzhou opened this issue · 4 comments
for example i got and demo.proto
syntax = "proto3";
package demo;
import "google/api/annotations.proto";
service DemoService {
rpc GetDemo(GetDemoReq) returns(GetDemoReply){
option (google.api.http) = {
get: "/api/demo/{id}"
};
}
}
and i use code from grpc-gateway
like below
package test
import (
"fmt"
"testing"
"github.com/bufbuild/protocompile/protoutil"
options "google.golang.org/genproto/googleapis/api/annotations"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
)
func TestCompile(t *testing.T) {
importPaths := compile([]string{"./proto"})
comp := pcompile.Compiler{
Resolver: pcompile.WithStandardImports(
&pcompile.SourceResolver{
ImportPaths: importPaths,
},
),
}
fs, err := comp.Compile(context.Background(), []string{"demo.proto"})
if err != nil {
t.Fatal(err)
}
for _, f := range fs {
fmt.Println(f.FullName())
svcs := f.Services()
for i := 0; i < svcs.Len(); i++ {
svc := svcs.Get(i)
fmt.Println(svc.FullName())
methods := svc.Methods()
for j := 0; j < methods.Len(); j++ {
m := methods.Get(j)
fmt.Println(m.FullName())
_, err := extractAPIOptions(m)
if err != nil {
fmt.Println(err)
return
}
}
}
}
}
func extractAPIOptions(d protoreflect.MethodDescriptor) (*options.HttpRule, error) {
dp := protoutil.ProtoFromMethodDescriptor(d)
if dp.Options == nil {
return nil, nil
}
if !proto.HasExtension(dp.Options, options.E_Http) {
return nil, nil
}
ext := proto.GetExtension(dp.Options, options.E_Http)
opts, ok := ext.(*options.HttpRule)
if !ok {
return nil, fmt.Errorf("extension is %T; want an HttpRule", ext)
}
return opts, nil
}
the code is exactly same with code from grpc-gateway
but i got and error
invalid type: got *dynamicpb.Message, want *annotations.HttpRule
,. i am not very familiar with the protobuf-go
package, so it's very confusing for me, anyone know what's the problem
Sorry this isn't better documented. But when you compile the file, it creates dynamic representations of all extensions to match the version defined in source. So it's not using options.E_Http
to populate MethodOptions
but instead has a dynamic version of that custom option, create from the definition in the sources provided.
So in order to use the result with generated extensions, you need to convert the dynamic extensions to non-dynamic extensions. The easiest way to do this is to marshal the MethodOptions
message to bytes and then unmarshal. When you unmarshal, you should use the proto.Unmarshal
function or use a proto.UnmarshalOptions
struct where the Resolver
field is either left nil or set to protoregistry.GlobalTypes
. That way, the serialized bytes corresponding to the custom options will be interpreted using the generated extension and its corresponding Go types -- in this case the options.E_Http
extension definition, which indicates *annotations.HttpRule
as the Go type of its value.
@jhump Thank you very much, it seems working. also i wonder is there any example using this compiler results to send dynamic rpc calls?
@jaredzhou, the package that tools like grpcurl
and grpcui
use for dynamic RPC is github.com/jhump/protoreflect/dynamic/grpcdynamic. This API wants a *desc.MethodDescriptor
, which can be created via desc.WrapMethod
, passing in a protoreflect.MethodDescriptor
that is inside a file descriptor returned from protocompile
.
An alternate example using the connect-go library can be found in the buf CLI's "curl" sub-command. This example uses a protoreflect.MethodDescriptor
(so no wrapping needed). This example is a bit more involved, and there's no import-able library to make it easier (though you can view its implementation techniques and copy/fork the relevant parts of the code).