diff --git a/e2e/cli_test.go b/e2e/cli_test.go index e2988418..2d18d79b 100644 --- a/e2e/cli_test.go +++ b/e2e/cli_test.go @@ -109,67 +109,67 @@ func TestE2E_CLI(t *testing.T) { args: "-h", assertWithGolden: true, }, - "cannot launch CLI mode because proto files didn't be passed": { + "cannot launch because proto files didn't be passed": { cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", expectedCode: 1, }, - "cannot launch CLI mode because package name is invalid value": { + "cannot launch because package name is invalid value": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in foo.Example.Unary", expectedCode: 1, }, - "cannot launch CLI mode because service name is invalid value": { + "cannot launch because service name is invalid value": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Foo.Unary", expectedCode: 1, }, - "cannot launch CLI mode because method name is missing": { + "cannot launch because method name is missing": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example", expectedCode: 1, }, - "cannot launch CLI mode because method name is invalid value": { + "cannot launch because method name is invalid value": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Foo", expectedCode: 1, }, - "cannot launch CLI mode because the path of --file is invalid path": { + "cannot launch because the path of --file is invalid path": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file foo api.Example.Unary", expectedCode: 1, }, - "cannot launch CLI mode because the path of --file is invalid input": { + "cannot launch because the path of --file is invalid input": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/invalid.in api.Example.Unary", expectedCode: 1, }, - "cannot launch CLI mode because --header didn't have value": { + "cannot launch because --header didn't have value": { commonFlags: "--header foo --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", expectedCode: 1, }, - "call unary RPC with an input file by CLI mode (backward-compatibility)": { + "call unary RPC with an input file (backward-compatibility)": { commonFlags: "--package api --service Example --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in Unary", deprecatedUsage: true, expectedOut: `{ "message": "hello, oumae" }`, }, - "call unary RPC with an input file by CLI mode": { + "call unary RPC with an input file": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", expectedOut: `{ "message": "hello, oumae" }`, }, - "call fully-qualified unary RPC with an input file by CLI mode": { + "call fully-qualified unary RPC with an input file": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", @@ -195,7 +195,7 @@ func TestE2E_CLI(t *testing.T) { args: "--file testdata/unary_call.in Unary", expectedOut: `{ "message": "hello, oumae" }`, }, - "call unary RPC with an input reader by CLI mode": { + "call unary RPC with an input reader": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "api.Example.Unary", @@ -208,19 +208,19 @@ func TestE2E_CLI(t *testing.T) { }, expectedOut: `{ "message": "hello, oumae" }`, }, - "call client streaming RPC by CLI mode": { + "call client streaming RPC": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/client_streaming.in api.Example.ClientStreaming", expectedOut: `{ "message": "you sent requests 4 times (oumae, kousaka, kawashima, kato)." }`, }, - "call server streaming RPC by CLI mode": { + "call server streaming RPC": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/server_streaming.in api.Example.ServerStreaming", expectedOut: `{ "message": "hello oumae, I greet 1 times." } { "message": "hello oumae, I greet 2 times." } { "message": "hello oumae, I greet 3 times." }`, }, - "call bidi streaming RPC by CLI mode": { + "call bidi streaming RPC": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/bidi_streaming.in api.Example.BidiStreaming", @@ -238,7 +238,7 @@ func TestE2E_CLI(t *testing.T) { } }, }, - "call unary RPC with an input file and custom headers by CLI mode": { + "call unary RPC with an input file and custom headers": { commonFlags: "--header ogiso=setsuna --header touma=kazusa,youko --header sound=of=destiny --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_header.in api.Example.UnaryHeader", @@ -259,23 +259,86 @@ func TestE2E_CLI(t *testing.T) { }, }, + // call command with timeout header. + + "unary call timed out": { + commonFlags: "--header grpc-timeout=0s --proto testdata/test.proto", + cmd: "call", + args: "--file testdata/unary_call.in api.example.unary", + expectedCode: 1, + }, + "unary call with timeout header": { + commonFlags: "--header grpc-timeout=1s --proto testdata/test.proto", + cmd: "call", + args: "--file testdata/unary_call.in api.Example.Unary", + expectedOut: `{ "message": "hello, oumae" }`, + }, + "client streaming call timed out": { + commonFlags: "--header grpc-timeout=0s --proto testdata/test.proto", + cmd: "call", + args: "--file testdata/client_streaming.in api.Example.ClientStreaming", + expectedCode: 1, + }, + "client streaming call with timeout header": { + commonFlags: "--header grpc-timeout=1s --proto testdata/test.proto", + cmd: "call", + args: "--file testdata/client_streaming.in api.Example.ClientStreaming", + expectedOut: `{ "message": "you sent requests 4 times (oumae, kousaka, kawashima, kato)." }`, + }, + "server streaming call timed out": { + commonFlags: "--header grpc-timeout=0s --proto testdata/test.proto", + cmd: "call", + args: "--file testdata/server_streaming.in api.Example.ServerStreaming", + expectedCode: 1, + }, + "server streaming call with timeout header": { + commonFlags: "--header grpc-timeout=1s --proto testdata/test.proto", + cmd: "call", + args: "--file testdata/server_streaming.in api.Example.ServerStreaming", + expectedOut: `{ "message": "hello oumae, I greet 1 times." } { "message": "hello oumae, I greet 2 times." } { "message": "hello oumae, I greet 3 times." }`, + }, + "bidi streaming call timed out": { + commonFlags: "--header grpc-timeout=0s --proto testdata/test.proto", + cmd: "call", + args: "--file testdata/bidi_streaming.in api.Example.BidiStreaming", + expectedCode: 1, + }, + "bidi streaming call with timeout header": { + commonFlags: "--header grpc-timeout=1s --proto testdata/test.proto", + cmd: "call", + args: "--file testdata/bidi_streaming.in api.Example.BidiStreaming", + assertTest: func(t *testing.T, output string) { + dec := json.NewDecoder(strings.NewReader(output)) + for { + var iface interface{} + err := dec.Decode(&iface) + if errors.Is(err, io.EOF) { + return + } + if err != nil { + t.Errorf("expected no errors, but got '%s'", err) + } + } + }, + }, + // call command with reflection - "cannot launch CLI mode with reflection because method name is missing": { + "cannot launch with reflection because method name is missing": { commonFlags: "--reflection --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example", reflection: true, expectedCode: 1, }, - "cannot launch CLI mode with reflection because server didn't enable reflection": { + "cannot launch with reflection because server didn't enable reflection": { commonFlags: "--reflection", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", reflection: false, expectedCode: 1, }, - "call unary RPC by CLI mode with reflection with an input file (backward-compatibility)": { + "call unary RPC with reflection with an input file (backward-compatibility)": { commonFlags: "--reflection --package api --service Example", cmd: "call", args: "--file testdata/unary_call.in Unary", @@ -283,7 +346,7 @@ func TestE2E_CLI(t *testing.T) { deprecatedUsage: true, expectedOut: `{ "message": "hello, oumae" }`, }, - "call unary RPC by CLI mode with reflection with an input file": { + "call unary RPC with reflection with an input file": { commonFlags: "--reflection", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", @@ -293,42 +356,42 @@ func TestE2E_CLI(t *testing.T) { // call command with TLS - "cannot launch CLI mode with TLS because the server didn't enable TLS": { + "cannot launch with TLS because the server didn't enable TLS": { commonFlags: "--tls --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", tls: false, expectedCode: 1, }, - "cannot launch CLI mode with TLS because the client didn't enable TLS": { + "cannot launch with TLS because the client didn't enable TLS": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", tls: true, expectedCode: 1, }, - "cannot launch CLI mode with TLS because cannot validate certs for 127.0.0.1 (default value)": { + "cannot launch with TLS because cannot validate certs for 127.0.0.1 (default value)": { commonFlags: "--tls --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", tls: true, expectedCode: 1, }, - "cannot launch CLI mode with TLS because signed authority is unknown": { + "cannot launch with TLS because signed authority is unknown": { commonFlags: "--tls --host localhost --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", tls: true, expectedCode: 1, }, - "call unary RPC with TLS by CLI mode": { + "call unary RPC with TLS": { commonFlags: "--tls --host localhost --cacert testdata/rootCA.pem --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", tls: true, expectedOut: `{ "message": "hello, oumae" }`, }, - "cannot launch CLI mode with TLS and reflection by CLI mode because server didn't enable TLS": { + "cannot launch with TLS and reflection because server didn't enable TLS": { commonFlags: "--tls -r --host localhost --cacert testdata/rootCA.pem", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", @@ -336,7 +399,7 @@ func TestE2E_CLI(t *testing.T) { reflection: true, expectedCode: 1, }, - "call unary RPC with TLS and reflection by CLI mode": { + "call unary RPC with TLS and reflection": { commonFlags: "--tls -r --host localhost --cacert testdata/rootCA.pem", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", @@ -344,28 +407,28 @@ func TestE2E_CLI(t *testing.T) { reflection: true, expectedOut: `{ "message": "hello, oumae" }`, }, - "call unary RPC with TLS and --servername by CLI mode": { + "call unary RPC with TLS and --servername": { commonFlags: "--tls --servername localhost --cacert testdata/rootCA.pem --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", tls: true, expectedOut: `{ "message": "hello, oumae" }`, }, - "cannot launch CLI mode with mutual TLS auth because --certkey is missing": { + "cannot launch with mutual TLS auth because --certkey is missing": { commonFlags: "--tls --host localhost --cacert testdata/rootCA.pem --cert testdata/localhost.pem --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", tls: true, expectedCode: 1, }, - "cannot launch CLI mode with mutual TLS auth because --cert is missing": { + "cannot launch with mutual TLS auth because --cert is missing": { commonFlags: "--tls --host localhost --cacert testdata/rootCA.pem --certkey testdata/localhost-key.pem --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", tls: true, expectedCode: 1, }, - "call unary RPC with mutual TLS auth by CLI mode": { + "call unary RPC with mutual TLS auth": { commonFlags: "--tls --host localhost --cacert testdata/rootCA.pem --cert testdata/localhost.pem --certkey testdata/localhost-key.pem --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", @@ -382,14 +445,14 @@ func TestE2E_CLI(t *testing.T) { web: false, expectedCode: 1, }, - "call unary RPC with an input file by CLI mode against to gRPC-Web server": { + "call unary RPC with an input file against to gRPC-Web server": { commonFlags: "--web --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", web: true, expectedOut: `{ "message": "hello, oumae" }`, }, - "call unary RPC with an input file by CLI mode and reflection against to gRPC-Web server": { + "call unary RPC with an input file and reflection against to gRPC-Web server": { commonFlags: "--web -r", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", @@ -397,21 +460,21 @@ func TestE2E_CLI(t *testing.T) { reflection: true, expectedOut: `{ "message": "hello, oumae" }`, }, - "call client streaming RPC by CLI mode against to gRPC-Web server": { + "call client streaming RPC against to gRPC-Web server": { commonFlags: "--web --proto testdata/test.proto", cmd: "call", args: "--file testdata/client_streaming.in api.Example.ClientStreaming", web: true, expectedOut: `{ "message": "you sent requests 4 times (oumae, kousaka, kawashima, kato)." }`, }, - "call server streaming RPC by CLI mode against to gRPC-Web server": { + "call server streaming RPC against to gRPC-Web server": { commonFlags: "--web --proto testdata/test.proto", cmd: "call", args: "--file testdata/server_streaming.in api.Example.ServerStreaming", web: true, expectedOut: `{ "message": "hello oumae, I greet 1 times." } { "message": "hello oumae, I greet 2 times." } { "message": "hello oumae, I greet 3 times." }`, }, - "call bidi streaming RPC by CLI mode against to gRPC-Web server": { + "call bidi streaming RPC against to gRPC-Web server": { commonFlags: "--web --proto testdata/test.proto", cmd: "call", args: "--file testdata/bidi_streaming.in api.Example.BidiStreaming", diff --git a/go.sum b/go.sum index 48a645a7..33b3da14 100644 --- a/go.sum +++ b/go.sum @@ -325,6 +325,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= @@ -532,8 +533,7 @@ github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUC github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/matoous/godox v0.0.0-20200801072554-4fb83dc2941e h1:2U5rOmpaB96l35w+NDjMtmmrp2e6a6AJKoc4B5+7UwA= github.com/matoous/godox v0.0.0-20200801072554-4fb83dc2941e/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= -github.com/matryer/moq v0.1.3 h1:+fW3u2jmlPw59a3V6spZKOLCcvrDKzPjMsRvUhnZ/c0= -github.com/matryer/moq v0.1.3/go.mod h1:9RtPYjTnH1bSBIkpvtHkFN7nbWAnO7oRpdJkEIn6UtE= +github.com/matryer/moq v0.1.4 h1:dkN4G4jtl0zpssB9OPCb8D+m4zTHdnZpi62HhlGnyTU= github.com/matryer/moq v0.1.4/go.mod h1:9RtPYjTnH1bSBIkpvtHkFN7nbWAnO7oRpdJkEIn6UtE= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -1135,8 +1135,7 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1148,6 +1147,7 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/grpc/grpc.go b/grpc/grpc.go index c2a286ab..e12291b8 100644 --- a/grpc/grpc.go +++ b/grpc/grpc.go @@ -43,7 +43,7 @@ type Type struct { FullyQualifiedName string // New instantiates a new instance of Type. It is used for decode requests and responses. - New func() (interface{}, error) + New func() interface{} } // Client represents the gRPC client. diff --git a/idl/proto/proto.go b/idl/proto/proto.go index 1923a062..4e5e98f2 100644 --- a/idl/proto/proto.go +++ b/idl/proto/proto.go @@ -74,17 +74,15 @@ func (s *spec) RPC(svcName, rpcName string) (*grpc.RPC, error) { RequestType: &grpc.Type{ Name: d.GetInputType().GetName(), FullyQualifiedName: d.GetInputType().GetFullyQualifiedName(), - New: func() (interface{}, error) { - m := dynamic.NewMessage(d.GetInputType()) - return m, nil + New: func() interface{} { + return dynamic.NewMessage(d.GetInputType()) }, }, ResponseType: &grpc.Type{ Name: d.GetOutputType().GetName(), FullyQualifiedName: d.GetOutputType().GetFullyQualifiedName(), - New: func() (interface{}, error) { - m := dynamic.NewMessage(d.GetOutputType()) - return m, nil + New: func() interface{} { + return dynamic.NewMessage(d.GetOutputType()) }, }, IsServerStreaming: d.IsServerStreaming(), diff --git a/usecase/call_rpc.go b/usecase/call_rpc.go index 3e7fc6d6..efa8341c 100644 --- a/usecase/call_rpc.go +++ b/usecase/call_rpc.go @@ -55,10 +55,7 @@ func (m *dependencyManager) CallRPC(ctx context.Context, w io.Writer, rpcName st return errors.Wrap(err, "failed to get the RPC descriptor") } newRequest := func() (interface{}, error) { - req, err := rpc.RequestType.New() - if err != nil { - return nil, errors.Wrapf(err, "failed to instantiate an instance of the request type '%s'", rpc.RequestType.FullyQualifiedName) - } + req := rpc.RequestType.New() err = filler.Fill(req) if errors.Is(err, io.EOF) { return nil, io.EOF @@ -68,12 +65,8 @@ func (m *dependencyManager) CallRPC(ctx context.Context, w io.Writer, rpcName st } return req, nil } - newResponse := func() (interface{}, error) { - res, err := rpc.ResponseType.New() - if err != nil { - return nil, errors.Wrapf(err, "failed to instantiate an instance of the response type '%s'", rpc.RequestType.FullyQualifiedName) - } - return res, nil + newResponse := func() interface{} { + return rpc.ResponseType.New() } flushHeader := func(header metadata.MD) { m.responseFormatter.FormatHeader(header) @@ -156,10 +149,7 @@ func (m *dependencyManager) CallRPC(ctx context.Context, w io.Writer, rpcName st ) eg.Go(func() error { for { - res, err := newResponse() - if err != nil { - return err - } + res := newResponse() stat, err := handleGRPCResponseError(stream.Receive(res)) if err != nil { if errors.Is(err, context.Canceled) { @@ -268,10 +258,7 @@ func (m *dependencyManager) CallRPC(ctx context.Context, w io.Writer, rpcName st req, err := newRequest() if errors.Is(err, io.EOF) { - res, err := newResponse() - if err != nil { - return err - } + res := newResponse() stat, err := handleGRPCResponseError(stream.CloseAndReceive(res)) if err != nil { return errors.Wrapf(err, "failed to close the stream of RPC '%s'", streamDesc.StreamName) @@ -340,10 +327,7 @@ func (m *dependencyManager) CallRPC(ctx context.Context, w io.Writer, rpcName st var writeHeaderOnce, writeTrailerOnce sync.Once for { - res, err := newResponse() - if err != nil { - return err - } + res := newResponse() stat, err := handleGRPCResponseError(stream.Receive(res)) if err != nil { if errors.Is(err, context.Canceled) { @@ -408,10 +392,7 @@ func (m *dependencyManager) CallRPC(ctx context.Context, w io.Writer, rpcName st return errors.Wrap(err, "failed to enhance context with metadata") } - res, err := newResponse() - if err != nil { - return err - } + res := newResponse() header, trailer, err := m.gRPCClient.Invoke(ctx, rpc.FullyQualifiedName, req, res) stat, err := handleGRPCResponseError(err) if err != nil {