Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["status.go"],
importpath = "go-common/library/net/rpc/warden/status",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/ecode:go_default_library",
"//library/ecode/pb:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"@com_github_golang_protobuf//proto:go_default_library",
"@com_github_golang_protobuf//ptypes:go_default_library_gen",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["status_test.go"],
embed = [":go_default_library"],
tags = ["manual"],
deps = [
"//library/ecode:go_default_library",
"//library/ecode/pb:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"@com_github_golang_protobuf//ptypes:go_default_library_gen",
"@io_bazel_rules_go//proto/wkt:timestamp_go_proto",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
],
)

View File

@@ -0,0 +1,139 @@
package status
import (
"strconv"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"go-common/library/ecode"
"go-common/library/ecode/pb"
)
// 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(err error) *status.Status {
err = errors.Cause(err)
if code, ok := err.(ecode.Codes); ok {
// TODO: deal with err
if gst, err := gRPCStatusFromEcode(code); err == nil {
return gst
}
}
gst, _ := status.FromError(err)
return gst
}
func gRPCStatusFromEcode(code ecode.Codes) (*status.Status, error) {
var st *ecode.Status
switch v := code.(type) {
// compatible old pb.Error remove it after nobody use pb.Error.
case *pb.Error:
return status.New(codes.Unknown, v.Error()).WithDetails(v)
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(togRPCCode(st), st.Message())
// NOTE: compatible with PHP swoole gRPC put code in status message as string.
// gst := status.New(togRPCCode(st), strconv.Itoa(st.Code()))
gst := status.New(codes.Unknown, strconv.Itoa(st.Code()))
pbe := &pb.Error{ErrCode: int32(st.Code()), ErrMessage: gst.Message()}
// NOTE: server return ecode.Status will be covert to pb.Error details will be ignored
// and put it at details[0] for compatible old client
return gst.WithDetails(pbe, st.Proto())
}
// ToEcode convert grpc.status to ecode.Codes
func ToEcode(gst *status.Status) ecode.Codes {
details := gst.Details()
// reverse range details, details may contain three case,
// if details contain pb.Error and ecode.Status use eocde.Status first.
//
// Details layout:
// pb.Error [0: pb.Error]
// both pb.Error and ecode.Status [0: pb.Error, 1: ecode.Status]
// ecode.Status [0: ecode.Status]
for i := len(details) - 1; i >= 0; i-- {
detail := details[i]
// compatible with old pb.Error.
if pe, ok := detail.(*pb.Error); ok {
st := ecode.Error(ecode.Code(pe.ErrCode), pe.ErrMessage)
if pe.ErrDetail != nil {
dynMsg := new(ptypes.DynamicAny)
// TODO deal with unmarshalAny error.
if err := ptypes.UnmarshalAny(pe.ErrDetail, dynMsg); err == nil {
st, _ = st.WithDetails(dynMsg.Message)
}
}
return st
}
// convert detail to status only use first detail
if pb, ok := detail.(proto.Message); ok {
return ecode.FromProto(pb)
}
}
return toECode(gst)
}

View File

@@ -0,0 +1,151 @@
package status
import (
"errors"
"fmt"
"testing"
"time"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/timestamp"
pkgerr "github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"go-common/library/ecode"
"go-common/library/ecode/pb"
)
func TestCodeConvert(t *testing.T) {
var table = map[codes.Code]ecode.Code{
codes.OK: ecode.OK,
// codes.Canceled
codes.Unknown: ecode.ServerErr,
codes.InvalidArgument: ecode.RequestErr,
codes.DeadlineExceeded: ecode.Deadline,
codes.NotFound: ecode.NothingFound,
// codes.AlreadyExists
codes.PermissionDenied: ecode.AccessDenied,
codes.ResourceExhausted: ecode.LimitExceed,
// codes.FailedPrecondition
// codes.Aborted
// codes.OutOfRange
codes.Unimplemented: ecode.MethodNotAllowed,
codes.Unavailable: ecode.ServiceUnavailable,
// codes.DataLoss
codes.Unauthenticated: ecode.Unauthorized,
}
for k, v := range table {
assert.Equal(t, toECode(status.New(k, "-500")), v)
}
for k, v := range table {
assert.Equal(t, togRPCCode(v), k, fmt.Sprintf("togRPC code error: %d -> %d", v, k))
}
}
func TestNoDetailsConvert(t *testing.T) {
gst := status.New(codes.Unknown, "-2233")
assert.Equal(t, toECode(gst).Code(), -2233)
gst = status.New(codes.Internal, "")
assert.Equal(t, toECode(gst).Code(), -500)
}
func TestFromError(t *testing.T) {
t.Run("input general error", func(t *testing.T) {
err := errors.New("general error")
gst := FromError(err)
assert.Equal(t, codes.Unknown, gst.Code())
assert.Contains(t, gst.Message(), "general")
})
t.Run("input wrap error", func(t *testing.T) {
err := pkgerr.Wrap(ecode.RequestErr, "hh")
gst := FromError(err)
assert.Equal(t, "-400", gst.Message())
})
t.Run("input ecode.Code", func(t *testing.T) {
err := ecode.RequestErr
gst := FromError(err)
//assert.Equal(t, codes.InvalidArgument, gst.Code())
// NOTE: set all grpc.status as Unkown when error is ecode.Codes for compatible
assert.Equal(t, codes.Unknown, gst.Code())
// NOTE: gst.Message == str(ecode.Code) for compatible php leagcy code
assert.Equal(t, err.Message(), gst.Message())
})
t.Run("input pb.Error", func(t *testing.T) {
m := &timestamp.Timestamp{Seconds: time.Now().Unix()}
detail, _ := ptypes.MarshalAny(m)
err := &pb.Error{ErrCode: 2233, ErrMessage: "message", ErrDetail: detail}
gst := FromError(err)
assert.Equal(t, codes.Unknown, gst.Code())
assert.Len(t, gst.Details(), 1)
assert.Equal(t, "2233", gst.Message())
})
t.Run("input ecode.Status", func(t *testing.T) {
m := &timestamp.Timestamp{Seconds: time.Now().Unix()}
err, _ := ecode.Error(ecode.Unauthorized, "unauthorized").WithDetails(m)
gst := FromError(err)
//assert.Equal(t, codes.Unauthenticated, gst.Code())
// NOTE: set all grpc.status as Unkown when error is ecode.Codes for compatible
assert.Equal(t, codes.Unknown, gst.Code())
assert.Len(t, gst.Details(), 2)
details := gst.Details()
assert.IsType(t, &pb.Error{}, details[0])
assert.IsType(t, err.Proto(), details[1])
})
}
func TestToEcode(t *testing.T) {
t.Run("input general grpc.Status", func(t *testing.T) {
gst := status.New(codes.Unknown, "unknown")
ec := ToEcode(gst)
assert.Equal(t, int(ecode.ServerErr), ec.Code())
assert.Equal(t, "-500", ec.Message())
assert.Len(t, ec.Details(), 0)
})
t.Run("input pb.Error", func(t *testing.T) {
m := &timestamp.Timestamp{Seconds: time.Now().Unix()}
detail, _ := ptypes.MarshalAny(m)
gst := status.New(codes.InvalidArgument, "requesterr")
gst, _ = gst.WithDetails(&pb.Error{ErrCode: 1122, ErrMessage: "message", ErrDetail: detail})
ec := ToEcode(gst)
assert.Equal(t, 1122, ec.Code())
assert.Equal(t, "message", ec.Message())
assert.Len(t, ec.Details(), 1)
assert.IsType(t, m, ec.Details()[0])
})
t.Run("input pb.Error and ecode.Status", func(t *testing.T) {
gst := status.New(codes.InvalidArgument, "requesterr")
gst, _ = gst.WithDetails(
&pb.Error{ErrCode: 1122, ErrMessage: "message"},
ecode.Errorf(ecode.AccessKeyErr, "AccessKeyErr").Proto(),
)
ec := ToEcode(gst)
assert.Equal(t, int(ecode.AccessKeyErr), ec.Code())
assert.Equal(t, "AccessKeyErr", ec.Message())
})
t.Run("input encode.Status", func(t *testing.T) {
m := &timestamp.Timestamp{Seconds: time.Now().Unix()}
st, _ := ecode.Errorf(ecode.AccessKeyErr, "AccessKeyErr").WithDetails(m)
gst := status.New(codes.InvalidArgument, "requesterr")
gst, _ = gst.WithDetails(st.Proto())
ec := ToEcode(gst)
assert.Equal(t, int(ecode.AccessKeyErr), ec.Code())
assert.Equal(t, "AccessKeyErr", ec.Message())
assert.Len(t, ec.Details(), 1)
assert.IsType(t, m, ec.Details()[0])
})
}