Skip to content

Commit

Permalink
gRPC interface reflection. (#6722)
Browse files Browse the repository at this point in the history
* WIP on gRPC interface reflection.

* Update docs in proto

* Add tests

* Add test

* Add route inside router

* Address nits

* ListInterfaces -> ListAllInterfaces

* Fix proto lint

* Remove stray println

* Update proto/cosmos/base/reflection/v1beta1/reflection.proto

Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>

* Update codec/types/interface_registry.go

Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>

* Update codec/types/interface_registry.go

Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>

* Add godoc

Co-authored-by: Amaury Martiny <amaury.martiny@protonmail.com>
Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 17, 2020
1 parent c25b3b9 commit 3f81c0a
Show file tree
Hide file tree
Showing 10 changed files with 1,139 additions and 19 deletions.
29 changes: 16 additions & 13 deletions baseapp/grpcrouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"google.golang.org/grpc/encoding"
"google.golang.org/grpc/encoding/proto"

"github.com/cosmos/cosmos-sdk/client/grpc/reflection"
"github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
Expand All @@ -17,9 +18,9 @@ var protoCodec = encoding.GetCodec(proto.Name)

// GRPCQueryRouter routes ABCI Query requests to GRPC handlers
type GRPCQueryRouter struct {
routes map[string]GRPCQueryHandler
anyUnpacker types.AnyUnpacker
serviceData []serviceData
routes map[string]GRPCQueryHandler
interfaceRegistry types.InterfaceRegistry
serviceData []serviceData
}

// serviceData represents a gRPC service, along with its handler.
Expand All @@ -28,7 +29,7 @@ type serviceData struct {
handler interface{}
}

var _ gogogrpc.Server
var _ gogogrpc.Server = &GRPCQueryRouter{}

// NewGRPCQueryRouter creates a new GRPCQueryRouter
func NewGRPCQueryRouter() *GRPCQueryRouter {
Expand Down Expand Up @@ -67,8 +68,8 @@ func (qrt *GRPCQueryRouter) RegisterService(sd *grpc.ServiceDesc, handler interf
if err != nil {
return err
}
if qrt.anyUnpacker != nil {
return types.UnpackInterfaces(i, qrt.anyUnpacker)
if qrt.interfaceRegistry != nil {
return types.UnpackInterfaces(i, qrt.interfaceRegistry)
}
return nil
}, nil)
Expand Down Expand Up @@ -96,12 +97,14 @@ func (qrt *GRPCQueryRouter) RegisterService(sd *grpc.ServiceDesc, handler interf
})
}

// AnyUnpacker returns the AnyUnpacker for the router
func (qrt *GRPCQueryRouter) AnyUnpacker() types.AnyUnpacker {
return qrt.anyUnpacker
}
// SetInterfaceRegistry sets the interface registry for the router.
func (qrt *GRPCQueryRouter) SetInterfaceRegistry(interfaceRegistry types.InterfaceRegistry) {
qrt.interfaceRegistry = interfaceRegistry

// SetAnyUnpacker sets the AnyUnpacker for the router
func (qrt *GRPCQueryRouter) SetAnyUnpacker(anyUnpacker types.AnyUnpacker) {
qrt.anyUnpacker = anyUnpacker
// Once we have an interface registry, we can register the interface
// registry reflection gRPC service.
reflection.RegisterReflectionServiceServer(
qrt,
reflection.NewReflectionServiceServer(qrt.interfaceRegistry),
)
}
8 changes: 4 additions & 4 deletions baseapp/grpcrouter_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ type QueryServiceTestHelper struct {

// NewQueryServerTestHelper creates a new QueryServiceTestHelper that wraps
// the provided sdk.Context
func NewQueryServerTestHelper(ctx sdk.Context, anyUnpacker types.AnyUnpacker) *QueryServiceTestHelper {
func NewQueryServerTestHelper(ctx sdk.Context, interfaceRegistry types.InterfaceRegistry) *QueryServiceTestHelper {
qrt := NewGRPCQueryRouter()
qrt.SetAnyUnpacker(anyUnpacker)
qrt.SetInterfaceRegistry(interfaceRegistry)
return &QueryServiceTestHelper{GRPCQueryRouter: qrt, ctx: ctx}
}

Expand All @@ -51,8 +51,8 @@ func (q *QueryServiceTestHelper) Invoke(_ gocontext.Context, method string, args
return err
}

if q.anyUnpacker != nil {
return types.UnpackInterfaces(reply, q.anyUnpacker)
if q.interfaceRegistry != nil {
return types.UnpackInterfaces(reply, q.interfaceRegistry)
}

return nil
Expand Down
2 changes: 1 addition & 1 deletion baseapp/grpcrouter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
func TestGRPCRouter(t *testing.T) {
qr := NewGRPCQueryRouter()
interfaceRegistry := testdata.NewTestInterfaceRegistry()
qr.SetAnyUnpacker(interfaceRegistry)
qr.SetInterfaceRegistry(interfaceRegistry)
testdata.RegisterTestServiceServer(qr, testdata.TestServiceImpl{})
helper := &QueryServiceTestHelper{
GRPCQueryRouter: qr,
Expand Down
45 changes: 45 additions & 0 deletions client/grpc/reflection/reflection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package reflection

import (
"context"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/cosmos/cosmos-sdk/codec/types"
)

type reflectionServiceServer struct {
interfaceRegistry types.InterfaceRegistry
}

// NewReflectionServiceServer creates a new reflectionServiceServer.
func NewReflectionServiceServer(interfaceRegistry types.InterfaceRegistry) ReflectionServiceServer {
return &reflectionServiceServer{interfaceRegistry: interfaceRegistry}
}

var _ ReflectionServiceServer = (*reflectionServiceServer)(nil)

// ListAllInterfaces implements the ListAllInterfaces method of the
// ReflectionServiceServer interface.
func (r reflectionServiceServer) ListAllInterfaces(_ context.Context, _ *ListAllInterfacesRequest) (*ListAllInterfacesResponse, error) {
ifaces := r.interfaceRegistry.ListAllInterfaces()

return &ListAllInterfacesResponse{InterfaceNames: ifaces}, nil
}

// ListImplementations implements the ListImplementations method of the
// ReflectionServiceServer interface.
func (r reflectionServiceServer) ListImplementations(_ context.Context, req *ListImplementationsRequest) (*ListImplementationsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

if req.InterfaceName == "" {
return nil, status.Error(codes.InvalidArgument, "invalid interface name")
}

impls := r.interfaceRegistry.ListImplementations(req.InterfaceName)

return &ListImplementationsResponse{ImplementationMessageNames: impls}, nil
}
Loading

0 comments on commit 3f81c0a

Please sign in to comment.