pass micro errors from grpc server to grpc client (#1167)
* pass micro errors from grpc server to grpc client Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org> * wrap micro errors.Error to grpc status Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
		
							
								
								
									
										72
									
								
								grpc.go
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								grpc.go
									
									
									
									
									
								
							| @@ -14,6 +14,7 @@ import ( | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/golang/protobuf/proto" | ||||
| 	"github.com/micro/go-micro/v2/broker" | ||||
| 	"github.com/micro/go-micro/v2/codec" | ||||
| 	"github.com/micro/go-micro/v2/errors" | ||||
| @@ -375,20 +376,38 @@ func (g *grpcServer) processRequest(stream grpc.ServerStream, service *service, | ||||
|  | ||||
| 		statusCode := codes.OK | ||||
| 		statusDesc := "" | ||||
|  | ||||
| 		// execute the handler | ||||
| 		if appErr := fn(ctx, r, replyv.Interface()); appErr != nil { | ||||
| 			if err, ok := appErr.(*rpcError); ok { | ||||
| 				statusCode = err.code | ||||
| 				statusDesc = err.desc | ||||
| 			} else if err, ok := appErr.(*errors.Error); ok { | ||||
| 				statusCode = microError(err) | ||||
| 				statusDesc = appErr.Error() | ||||
| 			} else { | ||||
| 			var errStatus *status.Status | ||||
| 			switch verr := appErr.(type) { | ||||
| 			case *errors.Error: | ||||
| 				// micro.Error now proto based and we can attach it to grpc status | ||||
| 				statusCode = microError(verr) | ||||
| 				statusDesc = verr.Error() | ||||
| 				errStatus, err = status.New(statusCode, statusDesc).WithDetails(verr) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			case proto.Message: | ||||
| 				// user defined error that proto based we can attach it to grpc status | ||||
| 				statusCode = convertCode(appErr) | ||||
| 				statusDesc = appErr.Error() | ||||
| 				errStatus, err = status.New(statusCode, statusDesc).WithDetails(verr) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			case *rpcError: | ||||
| 				// rpcError handling may be we have ability to attach it to details? | ||||
| 				statusCode = verr.code | ||||
| 				statusDesc = verr.desc | ||||
| 				errStatus = status.New(statusCode, statusDesc) | ||||
| 			default: | ||||
| 				// default case user pass own error type that not proto based | ||||
| 				statusCode = convertCode(verr) | ||||
| 				statusDesc = verr.Error() | ||||
| 				errStatus = status.New(statusCode, statusDesc) | ||||
| 			} | ||||
| 			return status.New(statusCode, statusDesc).Err() | ||||
| 			return errStatus.Err() | ||||
| 		} | ||||
|  | ||||
| 		if err := stream.SendMsg(replyv.Interface()); err != nil { | ||||
| @@ -436,16 +455,37 @@ func (g *grpcServer) processStream(stream grpc.ServerStream, service *service, m | ||||
|  | ||||
| 	appErr := fn(ctx, r, ss) | ||||
| 	if appErr != nil { | ||||
| 		if err, ok := appErr.(*rpcError); ok { | ||||
| 			statusCode = err.code | ||||
| 			statusDesc = err.desc | ||||
| 		} else if err, ok := appErr.(*errors.Error); ok { | ||||
| 			statusCode = microError(err) | ||||
| 			statusDesc = appErr.Error() | ||||
| 		} else { | ||||
| 		var err error | ||||
| 		var errStatus *status.Status | ||||
| 		switch verr := appErr.(type) { | ||||
| 		case *errors.Error: | ||||
| 			// micro.Error now proto based and we can attach it to grpc status | ||||
| 			statusCode = microError(verr) | ||||
| 			statusDesc = verr.Error() | ||||
| 			errStatus, err = status.New(statusCode, statusDesc).WithDetails(verr) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		case proto.Message: | ||||
| 			// user defined error that proto based we can attach it to grpc status | ||||
| 			statusCode = convertCode(appErr) | ||||
| 			statusDesc = appErr.Error() | ||||
| 			errStatus, err = status.New(statusCode, statusDesc).WithDetails(verr) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		case *rpcError: | ||||
| 			// rpcError handling may be we have ability to attach it to details? | ||||
| 			statusCode = verr.code | ||||
| 			statusDesc = verr.desc | ||||
| 			errStatus = status.New(statusCode, statusDesc) | ||||
| 		default: | ||||
| 			// default case user pass own error type that not proto based | ||||
| 			statusCode = convertCode(verr) | ||||
| 			statusDesc = verr.Error() | ||||
| 			errStatus = status.New(statusCode, statusDesc) | ||||
| 		} | ||||
| 		return errStatus.Err() | ||||
| 	} | ||||
|  | ||||
| 	return status.New(statusCode, statusDesc).Err() | ||||
|   | ||||
							
								
								
									
										23
									
								
								grpc_test.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								grpc_test.go
									
									
									
									
									
								
							| @@ -4,9 +4,11 @@ import ( | ||||
| 	"context" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/micro/go-micro/v2/errors" | ||||
| 	"github.com/micro/go-micro/v2/registry/memory" | ||||
| 	"github.com/micro/go-micro/v2/server" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/grpc/status" | ||||
|  | ||||
| 	pb "github.com/micro/go-micro/v2/server/grpc/proto" | ||||
| ) | ||||
| @@ -16,6 +18,10 @@ type testServer struct{} | ||||
|  | ||||
| // TestHello implements helloworld.GreeterServer | ||||
| func (s *testServer) Call(ctx context.Context, req *pb.Request, rsp *pb.Response) error { | ||||
| 	if req.Name == "Error" { | ||||
| 		return &errors.Error{Id: "1", Code: 99, Detail: "detail"} | ||||
| 	} | ||||
|  | ||||
| 	rsp.Msg = "Hello " + req.Name | ||||
| 	return nil | ||||
| } | ||||
| @@ -63,4 +69,21 @@ func TestGRPCServer(t *testing.T) { | ||||
| 			t.Fatalf("Got unexpected response %v", rsp.Msg) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Test grpc error | ||||
| 	rsp := pb.Response{} | ||||
|  | ||||
| 	if err := cc.Invoke(context.Background(), "/test.Test/Call", &pb.Request{Name: "Error"}, &rsp); err != nil { | ||||
| 		st, ok := status.FromError(err) | ||||
| 		if !ok { | ||||
| 			t.Fatalf("invalid error received %#+v\n", err) | ||||
| 		} | ||||
| 		verr, ok := st.Details()[0].(*errors.Error) | ||||
| 		if !ok { | ||||
| 			t.Fatalf("invalid error received %#+v\n", st.Details()[0]) | ||||
| 		} | ||||
| 		if verr.Code != 99 && verr.Id != "1" && verr.Detail != "detail" { | ||||
| 			t.Fatalf("invalid error received %#+v\n", verr) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user