|
|
|
package status
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
"github.com/bilibili/kratos/pkg/ecode"
|
|
|
|
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"google.golang.org/grpc/status"
|
|
|
|
)
|
|
|
|
|
|
|
|
// togRPCCode convert ecode.Codo to gRPC code
|
|
|
|
func togRPCCode(code ecode.Codes) codes.Code {
|
|
|
|
switch code.Code() {
|
|
|
|
case ecode.OK.Code():
|
|
|
|
return codes.OK
|
|
|
|
case ecode.RequestErr.Code():
|
|
|
|
return codes.InvalidArgument
|
|
|
|
case ecode.NothingFound.Code():
|
|
|
|
return codes.NotFound
|
|
|
|
case ecode.Unauthorized.Code():
|
|
|
|
return codes.Unauthenticated
|
|
|
|
case ecode.AccessDenied.Code():
|
|
|
|
return codes.PermissionDenied
|
|
|
|
case ecode.LimitExceed.Code():
|
|
|
|
return codes.ResourceExhausted
|
|
|
|
case ecode.MethodNotAllowed.Code():
|
|
|
|
return codes.Unimplemented
|
|
|
|
case ecode.Deadline.Code():
|
|
|
|
return codes.DeadlineExceeded
|
|
|
|
case ecode.ServiceUnavailable.Code():
|
|
|
|
return codes.Unavailable
|
|
|
|
}
|
|
|
|
return codes.Unknown
|
|
|
|
}
|
|
|
|
|
|
|
|
func toECode(gst *status.Status) ecode.Code {
|
|
|
|
gcode := gst.Code()
|
|
|
|
switch gcode {
|
|
|
|
case codes.OK:
|
|
|
|
return ecode.OK
|
|
|
|
case codes.InvalidArgument:
|
|
|
|
return ecode.RequestErr
|
|
|
|
case codes.NotFound:
|
|
|
|
return ecode.NothingFound
|
|
|
|
case codes.PermissionDenied:
|
|
|
|
return ecode.AccessDenied
|
|
|
|
case codes.Unauthenticated:
|
|
|
|
return ecode.Unauthorized
|
|
|
|
case codes.ResourceExhausted:
|
|
|
|
return ecode.LimitExceed
|
|
|
|
case codes.Unimplemented:
|
|
|
|
return ecode.MethodNotAllowed
|
|
|
|
case codes.DeadlineExceeded:
|
|
|
|
return ecode.Deadline
|
|
|
|
case codes.Unavailable:
|
|
|
|
return ecode.ServiceUnavailable
|
|
|
|
case codes.Unknown:
|
|
|
|
return ecode.String(gst.Message())
|
|
|
|
}
|
|
|
|
return ecode.ServerErr
|
|
|
|
}
|
|
|
|
|
|
|
|
// FromError convert error for service reply and try to convert it to grpc.Status.
|
|
|
|
func FromError(svrErr error) (gst *status.Status) {
|
|
|
|
var err error
|
|
|
|
svrErr = errors.Cause(svrErr)
|
|
|
|
if code, ok := svrErr.(ecode.Codes); ok {
|
|
|
|
// TODO: deal with err
|
|
|
|
if gst, err = gRPCStatusFromEcode(code); err == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// for some special error convert context.Canceled to ecode.Canceled,
|
|
|
|
// context.DeadlineExceeded to ecode.DeadlineExceeded only for raw error
|
|
|
|
// if err be wrapped will not effect.
|
|
|
|
switch svrErr {
|
|
|
|
case context.Canceled:
|
|
|
|
gst, _ = gRPCStatusFromEcode(ecode.Canceled)
|
|
|
|
case context.DeadlineExceeded:
|
|
|
|
gst, _ = gRPCStatusFromEcode(ecode.Deadline)
|
|
|
|
default:
|
|
|
|
gst, _ = status.FromError(svrErr)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func gRPCStatusFromEcode(code ecode.Codes) (*status.Status, error) {
|
|
|
|
var st *ecode.Status
|
|
|
|
switch v := code.(type) {
|
|
|
|
case *ecode.Status:
|
|
|
|
st = v
|
|
|
|
case ecode.Code:
|
|
|
|
st = ecode.FromCode(v)
|
|
|
|
default:
|
|
|
|
st = ecode.Error(ecode.Code(code.Code()), code.Message())
|
|
|
|
for _, detail := range code.Details() {
|
|
|
|
if msg, ok := detail.(proto.Message); ok {
|
|
|
|
st.WithDetails(msg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gst := status.New(codes.Unknown, strconv.Itoa(st.Code()))
|
|
|
|
return gst.WithDetails(st.Proto())
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToEcode convert grpc.status to ecode.Codes
|
|
|
|
func ToEcode(gst *status.Status) ecode.Codes {
|
|
|
|
details := gst.Details()
|
|
|
|
for _, detail := range details {
|
|
|
|
// convert detail to status only use first detail
|
|
|
|
if pb, ok := detail.(proto.Message); ok {
|
|
|
|
return ecode.FromProto(pb)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return toECode(gst)
|
|
|
|
}
|