-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
1,201 additions
and
151 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package baseapp | ||
|
||
import ( | ||
"fmt" | ||
|
||
gogogrpc "github.com/gogo/protobuf/grpc" | ||
abci "github.com/tendermint/tendermint/abci/types" | ||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/encoding" | ||
"google.golang.org/grpc/encoding/proto" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
var protoCodec = encoding.GetCodec(proto.Name) | ||
|
||
// GRPCRouter routes ABCI Query requests to GRPC handlers | ||
type GRPCRouter struct { | ||
routes map[string]GRPCQueryHandler | ||
} | ||
|
||
var _ gogogrpc.Server | ||
|
||
// NewGRPCRouter creates a new GRPCRouter | ||
func NewGRPCRouter() *GRPCRouter { | ||
return &GRPCRouter{ | ||
routes: map[string]GRPCQueryHandler{}, | ||
} | ||
} | ||
|
||
// GRPCQueryHandler defines a function type which handles ABCI Query requests | ||
// using gRPC | ||
type GRPCQueryHandler = func(ctx sdk.Context, req abci.RequestQuery) (abci.ResponseQuery, error) | ||
|
||
// Route returns the GRPCQueryHandler for a given query route path or nil | ||
// if not found | ||
func (qrt *GRPCRouter) Route(path string) GRPCQueryHandler { | ||
handler, found := qrt.routes[path] | ||
if !found { | ||
return nil | ||
} | ||
return handler | ||
} | ||
|
||
// RegisterService implements the gRPC Server.RegisterService method. sd is a gRPC | ||
// service description, handler is an object which implements that gRPC service | ||
func (qrt *GRPCRouter) RegisterService(sd *grpc.ServiceDesc, handler interface{}) { | ||
// adds a top-level query handler based on the gRPC service name | ||
for _, method := range sd.Methods { | ||
fqName := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName) | ||
methodHandler := method.Handler | ||
|
||
qrt.routes[fqName] = func(ctx sdk.Context, req abci.RequestQuery) (abci.ResponseQuery, error) { | ||
// call the method handler from the service description with the handler object, | ||
// a wrapped sdk.Context with proto-unmarshaled data from the ABCI request data | ||
res, err := methodHandler(handler, sdk.WrapSDKContext(ctx), func(i interface{}) error { | ||
return protoCodec.Unmarshal(req.Data, i) | ||
}, nil) | ||
if err != nil { | ||
return abci.ResponseQuery{}, err | ||
} | ||
|
||
// proto marshal the result bytes | ||
resBytes, err := protoCodec.Marshal(res) | ||
if err != nil { | ||
return abci.ResponseQuery{}, err | ||
} | ||
|
||
// return the result bytes as the response value | ||
return abci.ResponseQuery{ | ||
Height: req.Height, | ||
Value: resBytes, | ||
}, nil | ||
} | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package baseapp | ||
|
||
import ( | ||
gocontext "context" | ||
"fmt" | ||
|
||
gogogrpc "github.com/gogo/protobuf/grpc" | ||
abci "github.com/tendermint/tendermint/abci/types" | ||
"google.golang.org/grpc" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
// QueryServiceTestHelper provides a helper for making grpc query service | ||
// rpc calls in unit tests. It implements both the grpc Server and ClientConn | ||
// interfaces needed to register a query service server and create a query | ||
// service client. | ||
type QueryServiceTestHelper struct { | ||
*GRPCRouter | ||
ctx sdk.Context | ||
} | ||
|
||
// NewQueryServerTestHelper creates a new QueryServiceTestHelper that wraps | ||
// the provided sdk.Context | ||
func NewQueryServerTestHelper(ctx sdk.Context) *QueryServiceTestHelper { | ||
return &QueryServiceTestHelper{GRPCRouter: NewGRPCRouter(), ctx: ctx} | ||
} | ||
|
||
// Invoke implements the grpc ClientConn.Invoke method | ||
func (q *QueryServiceTestHelper) Invoke(_ gocontext.Context, method string, args, reply interface{}, _ ...grpc.CallOption) error { | ||
querier := q.Route(method) | ||
if querier == nil { | ||
return fmt.Errorf("handler not found for %s", method) | ||
} | ||
reqBz, err := protoCodec.Marshal(args) | ||
if err != nil { | ||
return err | ||
} | ||
res, err := querier(q.ctx, abci.RequestQuery{Data: reqBz}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
return protoCodec.Unmarshal(res.Value, reply) | ||
} | ||
|
||
// NewStream implements the grpc ClientConn.NewStream method | ||
func (q *QueryServiceTestHelper) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { | ||
return nil, fmt.Errorf("not supported") | ||
} | ||
|
||
var _ gogogrpc.Server = &QueryServiceTestHelper{} | ||
var _ gogogrpc.ClientConn = &QueryServiceTestHelper{} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package baseapp | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/cosmos/cosmos-sdk/codec/testdata" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
type testServer struct{} | ||
|
||
func (e testServer) Echo(_ context.Context, req *testdata.EchoRequest) (*testdata.EchoResponse, error) { | ||
return &testdata.EchoResponse{Message: req.Message}, nil | ||
} | ||
|
||
func (e testServer) SayHello(_ context.Context, request *testdata.SayHelloRequest) (*testdata.SayHelloResponse, error) { | ||
greeting := fmt.Sprintf("Hello %s!", request.Name) | ||
return &testdata.SayHelloResponse{Greeting: greeting}, nil | ||
} | ||
|
||
var _ testdata.TestServiceServer = testServer{} | ||
|
||
func TestGRPCRouter(t *testing.T) { | ||
qr := NewGRPCRouter() | ||
testdata.RegisterTestServiceServer(qr, testServer{}) | ||
helper := &QueryServiceTestHelper{ | ||
GRPCRouter: qr, | ||
ctx: sdk.Context{}, | ||
} | ||
client := testdata.NewTestServiceClient(helper) | ||
|
||
res, err := client.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}) | ||
require.Nil(t, err) | ||
require.NotNil(t, res) | ||
require.Equal(t, "hello", res.Message) | ||
|
||
require.Panics(t, func() { | ||
_, _ = client.Echo(context.Background(), nil) | ||
}) | ||
|
||
res2, err := client.SayHello(context.Background(), &testdata.SayHelloRequest{Name: "Foo"}) | ||
require.Nil(t, err) | ||
require.NotNil(t, res) | ||
require.Equal(t, "Hello Foo!", res2.Greeting) | ||
} |
Oops, something went wrong.