status.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package status
  2. import (
  3. "strconv"
  4. "github.com/golang/protobuf/proto"
  5. "github.com/golang/protobuf/ptypes"
  6. "github.com/pkg/errors"
  7. "google.golang.org/grpc/codes"
  8. "google.golang.org/grpc/status"
  9. "go-common/library/ecode"
  10. "go-common/library/ecode/pb"
  11. )
  12. // togRPCCode convert ecode.Codo to gRPC code
  13. func togRPCCode(code ecode.Codes) codes.Code {
  14. switch code.Code() {
  15. case ecode.OK.Code():
  16. return codes.OK
  17. case ecode.RequestErr.Code():
  18. return codes.InvalidArgument
  19. case ecode.NothingFound.Code():
  20. return codes.NotFound
  21. case ecode.Unauthorized.Code():
  22. return codes.Unauthenticated
  23. case ecode.AccessDenied.Code():
  24. return codes.PermissionDenied
  25. case ecode.LimitExceed.Code():
  26. return codes.ResourceExhausted
  27. case ecode.MethodNotAllowed.Code():
  28. return codes.Unimplemented
  29. case ecode.Deadline.Code():
  30. return codes.DeadlineExceeded
  31. case ecode.ServiceUnavailable.Code():
  32. return codes.Unavailable
  33. }
  34. return codes.Unknown
  35. }
  36. func toECode(gst *status.Status) ecode.Code {
  37. gcode := gst.Code()
  38. switch gcode {
  39. case codes.OK:
  40. return ecode.OK
  41. case codes.InvalidArgument:
  42. return ecode.RequestErr
  43. case codes.NotFound:
  44. return ecode.NothingFound
  45. case codes.PermissionDenied:
  46. return ecode.AccessDenied
  47. case codes.Unauthenticated:
  48. return ecode.Unauthorized
  49. case codes.ResourceExhausted:
  50. return ecode.LimitExceed
  51. case codes.Unimplemented:
  52. return ecode.MethodNotAllowed
  53. case codes.DeadlineExceeded:
  54. return ecode.Deadline
  55. case codes.Unavailable:
  56. return ecode.ServiceUnavailable
  57. case codes.Unknown:
  58. return ecode.String(gst.Message())
  59. }
  60. return ecode.ServerErr
  61. }
  62. // FromError convert error for service reply and try to convert it to grpc.Status.
  63. func FromError(err error) *status.Status {
  64. err = errors.Cause(err)
  65. if code, ok := err.(ecode.Codes); ok {
  66. // TODO: deal with err
  67. if gst, err := gRPCStatusFromEcode(code); err == nil {
  68. return gst
  69. }
  70. }
  71. gst, _ := status.FromError(err)
  72. return gst
  73. }
  74. func gRPCStatusFromEcode(code ecode.Codes) (*status.Status, error) {
  75. var st *ecode.Status
  76. switch v := code.(type) {
  77. // compatible old pb.Error remove it after nobody use pb.Error.
  78. case *pb.Error:
  79. return status.New(codes.Unknown, v.Error()).WithDetails(v)
  80. case *ecode.Status:
  81. st = v
  82. case ecode.Code:
  83. st = ecode.FromCode(v)
  84. default:
  85. st = ecode.Error(ecode.Code(code.Code()), code.Message())
  86. for _, detail := range code.Details() {
  87. if msg, ok := detail.(proto.Message); ok {
  88. st.WithDetails(msg)
  89. }
  90. }
  91. }
  92. // gst := status.New(togRPCCode(st), st.Message())
  93. // NOTE: compatible with PHP swoole gRPC put code in status message as string.
  94. // gst := status.New(togRPCCode(st), strconv.Itoa(st.Code()))
  95. gst := status.New(codes.Unknown, strconv.Itoa(st.Code()))
  96. pbe := &pb.Error{ErrCode: int32(st.Code()), ErrMessage: gst.Message()}
  97. // NOTE: server return ecode.Status will be covert to pb.Error details will be ignored
  98. // and put it at details[0] for compatible old client
  99. return gst.WithDetails(pbe, st.Proto())
  100. }
  101. // ToEcode convert grpc.status to ecode.Codes
  102. func ToEcode(gst *status.Status) ecode.Codes {
  103. details := gst.Details()
  104. // reverse range details, details may contain three case,
  105. // if details contain pb.Error and ecode.Status use eocde.Status first.
  106. //
  107. // Details layout:
  108. // pb.Error [0: pb.Error]
  109. // both pb.Error and ecode.Status [0: pb.Error, 1: ecode.Status]
  110. // ecode.Status [0: ecode.Status]
  111. for i := len(details) - 1; i >= 0; i-- {
  112. detail := details[i]
  113. // compatible with old pb.Error.
  114. if pe, ok := detail.(*pb.Error); ok {
  115. st := ecode.Error(ecode.Code(pe.ErrCode), pe.ErrMessage)
  116. if pe.ErrDetail != nil {
  117. dynMsg := new(ptypes.DynamicAny)
  118. // TODO deal with unmarshalAny error.
  119. if err := ptypes.UnmarshalAny(pe.ErrDetail, dynMsg); err == nil {
  120. st, _ = st.WithDetails(dynMsg.Message)
  121. }
  122. }
  123. return st
  124. }
  125. // convert detail to status only use first detail
  126. if pb, ok := detail.(proto.Message); ok {
  127. return ecode.FromProto(pb)
  128. }
  129. }
  130. return toECode(gst)
  131. }