add error scope Tweeting
This commit is contained in:
parent
84d8afa2ba
commit
c1e7a00363
|
@ -1,99 +0,0 @@
|
|||
package code
|
||||
|
||||
const (
|
||||
OK uint32 = 0
|
||||
)
|
||||
|
||||
// Scope
|
||||
const (
|
||||
Unset uint32 = iota
|
||||
CloudEPPortalGW
|
||||
CloudEPMember
|
||||
CloudEPPermission
|
||||
)
|
||||
|
||||
// Category for general operations: 100 - 4900
|
||||
const (
|
||||
_ = iota
|
||||
CatInput uint32 = iota * 100
|
||||
CatDB
|
||||
CatResource
|
||||
CatGRPC
|
||||
CatAuth
|
||||
CatSystem
|
||||
CatPubSub
|
||||
)
|
||||
|
||||
// CatArk Category for specific app/service: 5000 - 9900
|
||||
const (
|
||||
CatArk uint32 = (iota + 50) * 100
|
||||
)
|
||||
|
||||
// Detail - Input 1xx
|
||||
const (
|
||||
_ = iota + CatInput
|
||||
InvalidFormat
|
||||
NotValidImplementation
|
||||
InvalidRange
|
||||
)
|
||||
|
||||
// Detail - Database 2xx
|
||||
const (
|
||||
_ = iota + CatDB
|
||||
DBError // general error
|
||||
DBDataConvert
|
||||
DBDuplicate
|
||||
)
|
||||
|
||||
// Detail - Resource 3xx
|
||||
const (
|
||||
_ = iota + CatResource
|
||||
ResourceNotFound
|
||||
InvalidResourceFormat
|
||||
ResourceAlreadyExist
|
||||
ResourceInsufficient
|
||||
InsufficientPermission
|
||||
InvalidMeasurementID
|
||||
ResourceExpired
|
||||
ResourceMigrated
|
||||
InvalidResourceState
|
||||
InsufficientQuota
|
||||
ResourceHasMultiOwner
|
||||
)
|
||||
|
||||
/* Detail - GRPC */
|
||||
// The GRPC detail code uses Go GRPC's built-in codes.
|
||||
// Refer to "google.golang.org/grpc/codes" for more detail.
|
||||
|
||||
// Detail - Auth 5xx
|
||||
const (
|
||||
_ = iota + CatAuth
|
||||
Unauthorized
|
||||
AuthExpired
|
||||
InvalidPosixTime
|
||||
SigAndPayloadNotMatched
|
||||
Forbidden
|
||||
)
|
||||
|
||||
// Detail - System 6xx
|
||||
const (
|
||||
_ = iota + CatSystem
|
||||
SystemInternalError
|
||||
SystemMaintainError
|
||||
SystemTimeoutError
|
||||
)
|
||||
|
||||
// Detail - PubSub 7xx
|
||||
const (
|
||||
_ = iota + CatPubSub
|
||||
Publish
|
||||
Consume
|
||||
MsgSizeTooLarge
|
||||
)
|
||||
|
||||
// Detail - Ark 5xxx
|
||||
const (
|
||||
_ = iota + CatArk
|
||||
ArkInternal
|
||||
ArkHTTP400
|
||||
)
|
|
@ -1,13 +0,0 @@
|
|||
package code
|
||||
|
||||
// CatToStr collects general error messages for each Category
|
||||
// It is used to send back to API caller
|
||||
var CatToStr = map[uint32]string{
|
||||
CatInput: "Invalid Input Data",
|
||||
CatDB: "Database Error",
|
||||
CatResource: "Resource Error",
|
||||
CatGRPC: "Internal Service Communication Error",
|
||||
CatAuth: "Authentication Error",
|
||||
CatArk: "Internal Service Communication Error",
|
||||
CatSystem: "System Error",
|
||||
}
|
|
@ -1,471 +0,0 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.30cm.net/digimon/library-go/errors/code"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func newErr(scope, detail uint32, msg string) *LibError {
|
||||
cat := detail / 100 * 100
|
||||
|
||||
return &LibError{
|
||||
category: cat,
|
||||
code: detail,
|
||||
scope: scope,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
func newBuiltinGRPCErr(scope, detail uint32, msg string) *LibError {
|
||||
return &LibError{
|
||||
category: code.CatGRPC,
|
||||
code: detail,
|
||||
scope: scope,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// FromError tries to let error as Err
|
||||
// it supports to unwrap error that has Err
|
||||
// return nil if failed to transfer
|
||||
func FromError(err error) *LibError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var e *LibError
|
||||
if errors.As(err, &e) {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FromCode parses code as following
|
||||
// Decimal: 120314
|
||||
// 12 represents Scope
|
||||
// 03 represents Category
|
||||
// 14 represents Detail error code
|
||||
func FromCode(code uint32) *LibError {
|
||||
scope := code / 10000
|
||||
detail := code % 10000
|
||||
|
||||
return &LibError{
|
||||
category: detail / 100 * 100,
|
||||
code: detail,
|
||||
scope: scope,
|
||||
msg: "",
|
||||
}
|
||||
}
|
||||
|
||||
// FromGRPCError transfer error to Err
|
||||
// useful for gRPC client
|
||||
func FromGRPCError(err error) *LibError {
|
||||
s, _ := status.FromError(err)
|
||||
e := FromCode(uint32(s.Code()))
|
||||
e.msg = s.Message()
|
||||
|
||||
// For GRPC built-in code
|
||||
if e.Scope() == code.Unset && e.Category() == 0 && e.Code() != code.OK {
|
||||
e = newBuiltinGRPCErr(Scope, e.Code(), s.Message())
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// Deprecated: check GRPCStatus() in Errs struct
|
||||
// ToGRPCError returns the status.Status
|
||||
// Useful to return error in gRPC server
|
||||
func ToGRPCError(e *LibError) error {
|
||||
return status.New(codes.Code(e.FullCode()), e.Error()).Err()
|
||||
}
|
||||
|
||||
/*** System ***/
|
||||
|
||||
// SystemTimeoutError returns Err
|
||||
func SystemTimeoutError(s ...string) *LibError {
|
||||
return newErr(Scope, code.SystemTimeoutError, fmt.Sprintf("system timeout: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// SystemTimeoutErrorL logs error message and returns Err
|
||||
func SystemTimeoutErrorL(l logx.Logger, s ...string) *LibError {
|
||||
e := SystemTimeoutError(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SystemInternalError returns Err struct
|
||||
func SystemInternalError(s ...string) *LibError {
|
||||
return newErr(Scope, code.SystemInternalError, fmt.Sprintf("internal error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// SystemInternalErrorL logs error message and returns Err
|
||||
func SystemInternalErrorL(l logx.Logger, s ...string) *LibError {
|
||||
e := SystemInternalError(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SystemMaintainErrorL logs error message and returns Err
|
||||
func SystemMaintainErrorL(l logx.Logger, s ...string) *LibError {
|
||||
e := SystemMaintainError(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SystemMaintainError returns Err struct
|
||||
func SystemMaintainError(s ...string) *LibError {
|
||||
return newErr(Scope, code.SystemMaintainError, fmt.Sprintf("service under maintenance: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
/*** CatInput ***/
|
||||
|
||||
// InvalidFormat returns Err struct
|
||||
func InvalidFormat(s ...string) *LibError {
|
||||
return newErr(Scope, code.InvalidFormat, fmt.Sprintf("invalid format: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidFormatL logs error message and returns Err
|
||||
func InvalidFormatL(l logx.Logger, s ...string) *LibError {
|
||||
e := InvalidFormat(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// InvalidRange returns Err struct
|
||||
func InvalidRange(s ...string) *LibError {
|
||||
return newErr(Scope, code.InvalidRange, fmt.Sprintf("invalid range: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidRangeL logs error message and returns Err
|
||||
func InvalidRangeL(l logx.Logger, s ...string) *LibError {
|
||||
e := InvalidRange(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NotValidImplementation returns Err struct
|
||||
func NotValidImplementation(s ...string) *LibError {
|
||||
return newErr(Scope, code.NotValidImplementation, fmt.Sprintf("not valid implementation: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// NotValidImplementationL logs error message and returns Err
|
||||
func NotValidImplementationL(l logx.Logger, s ...string) *LibError {
|
||||
e := NotValidImplementation(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
/*** CatDB ***/
|
||||
|
||||
// DBError returns Err
|
||||
func DBError(s ...string) *LibError {
|
||||
return newErr(Scope, code.DBError, fmt.Sprintf("db error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// DBErrorL logs error message and returns Err
|
||||
func DBErrorL(l logx.Logger, s ...string) *LibError {
|
||||
e := DBError(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// DBDataConvert returns Err
|
||||
func DBDataConvert(s ...string) *LibError {
|
||||
return newErr(Scope, code.DBDataConvert, fmt.Sprintf("data from db convert error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// DBDataConvertL logs error message and returns Err
|
||||
func DBDataConvertL(l logx.Logger, s ...string) *LibError {
|
||||
e := DBDataConvert(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// DBDuplicate returns Err
|
||||
func DBDuplicate(s ...string) *LibError {
|
||||
return newErr(Scope, code.DBDuplicate, fmt.Sprintf("data Duplicate key error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// DBDuplicateL logs error message and returns Err
|
||||
func DBDuplicateL(l logx.Logger, s ...string) *LibError {
|
||||
e := DBDuplicate(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
/*** CatResource ***/
|
||||
|
||||
// ResourceNotFound returns Err and logging
|
||||
func ResourceNotFound(s ...string) *LibError {
|
||||
return newErr(Scope, code.ResourceNotFound, fmt.Sprintf("resource not found: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ResourceNotFoundL logs error message and returns Err
|
||||
func ResourceNotFoundL(l logx.Logger, s ...string) *LibError {
|
||||
e := ResourceNotFound(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// InvalidResourceFormat returns Err
|
||||
func InvalidResourceFormat(s ...string) *LibError {
|
||||
return newErr(Scope, code.InvalidResourceFormat, fmt.Sprintf("invalid resource format: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidResourceFormatL logs error message and returns Err
|
||||
func InvalidResourceFormatL(l logx.Logger, s ...string) *LibError {
|
||||
e := InvalidResourceFormat(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// InvalidResourceState returns status not correct.
|
||||
// for example: company should be destroy, agent should be no-sensor/fail-install ...
|
||||
func InvalidResourceState(s ...string) *LibError {
|
||||
return newErr(Scope, code.InvalidResourceState, fmt.Sprintf("invalid resource state: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidResourceStateL logs error message and returns status not correct.
|
||||
func InvalidResourceStateL(l logx.Logger, s ...string) *LibError {
|
||||
e := InvalidResourceState(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func ResourceInsufficient(s ...string) *LibError {
|
||||
return newErr(Scope, code.ResourceInsufficient,
|
||||
fmt.Sprintf("insufficient resource: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
func ResourceInsufficientL(l logx.Logger, s ...string) *LibError {
|
||||
e := ResourceInsufficient(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// InsufficientPermission returns Err
|
||||
func InsufficientPermission(s ...string) *LibError {
|
||||
return newErr(Scope, code.InsufficientPermission,
|
||||
fmt.Sprintf("insufficient permission: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InsufficientPermissionL returns Err and log
|
||||
func InsufficientPermissionL(l logx.Logger, s ...string) *LibError {
|
||||
e := InsufficientPermission(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ResourceAlreadyExist returns Err
|
||||
func ResourceAlreadyExist(s ...string) *LibError {
|
||||
return newErr(Scope, code.ResourceAlreadyExist, fmt.Sprintf("resource already exist: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ResourceAlreadyExistL logs error message and returns Err
|
||||
func ResourceAlreadyExistL(l logx.Logger, s ...string) *LibError {
|
||||
e := ResourceAlreadyExist(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// InvalidMeasurementID returns Err
|
||||
func InvalidMeasurementID(s ...string) *LibError {
|
||||
return newErr(Scope, code.InvalidMeasurementID, fmt.Sprintf("missing measurement id: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidMeasurementIDL logs error message and returns Err
|
||||
func InvalidMeasurementIDL(l logx.Logger, s ...string) *LibError {
|
||||
e := InvalidMeasurementID(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ResourceExpired returns Err
|
||||
func ResourceExpired(s ...string) *LibError {
|
||||
return newErr(Scope, code.ResourceExpired, fmt.Sprintf("resource expired: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ResourceExpiredL logs error message and returns Err
|
||||
func ResourceExpiredL(l logx.Logger, s ...string) *LibError {
|
||||
e := ResourceExpired(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// ResourceMigrated returns Err
|
||||
func ResourceMigrated(s ...string) *LibError {
|
||||
return newErr(Scope, code.ResourceMigrated, fmt.Sprintf("resource migrated: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ResourceMigratedL logs error message and returns Err
|
||||
func ResourceMigratedL(l logx.Logger, s ...string) *LibError {
|
||||
e := ResourceMigrated(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// InsufficientQuota returns Err
|
||||
func InsufficientQuota(s ...string) *LibError {
|
||||
return newErr(Scope, code.InsufficientQuota, fmt.Sprintf("insufficient quota: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InsufficientQuotaL logs error message and returns Err
|
||||
func InsufficientQuotaL(l logx.Logger, s ...string) *LibError {
|
||||
e := InsufficientQuota(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
/*** CatAuth ***/
|
||||
|
||||
// Unauthorized returns Err
|
||||
func Unauthorized(s ...string) *LibError {
|
||||
return newErr(Scope, code.Unauthorized, fmt.Sprintf("unauthorized: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// UnauthorizedL logs error message and returns Err
|
||||
func UnauthorizedL(l logx.Logger, s ...string) *LibError {
|
||||
e := Unauthorized(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// AuthExpired returns Err
|
||||
func AuthExpired(s ...string) *LibError {
|
||||
return newErr(Scope, code.AuthExpired, fmt.Sprintf("expired: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// AuthExpiredL logs error message and returns Err
|
||||
func AuthExpiredL(l logx.Logger, s ...string) *LibError {
|
||||
e := AuthExpired(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// InvalidPosixTime returns Err
|
||||
func InvalidPosixTime(s ...string) *LibError {
|
||||
return newErr(Scope, code.InvalidPosixTime, fmt.Sprintf("invalid posix time: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// InvalidPosixTimeL logs error message and returns Err
|
||||
func InvalidPosixTimeL(l logx.Logger, s ...string) *LibError {
|
||||
e := InvalidPosixTime(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// SigAndPayloadNotMatched returns Err
|
||||
func SigAndPayloadNotMatched(s ...string) *LibError {
|
||||
return newErr(Scope, code.SigAndPayloadNotMatched, fmt.Sprintf("signature and the payload are not match: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// SigAndPayloadNotMatchedL logs error message and returns Err
|
||||
func SigAndPayloadNotMatchedL(l logx.Logger, s ...string) *LibError {
|
||||
e := SigAndPayloadNotMatched(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// Forbidden returns Err
|
||||
func Forbidden(s ...string) *LibError {
|
||||
return newErr(Scope, code.Forbidden, fmt.Sprintf("forbidden: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ForbiddenL logs error message and returns Err
|
||||
func ForbiddenL(l logx.Logger, s ...string) *LibError {
|
||||
e := Forbidden(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// IsAuthUnauthorizedError check the err is unauthorized error
|
||||
func IsAuthUnauthorizedError(err *LibError) bool {
|
||||
switch err.Code() {
|
||||
case code.Unauthorized, code.AuthExpired, code.InvalidPosixTime,
|
||||
code.SigAndPayloadNotMatched, code.Forbidden,
|
||||
code.InvalidFormat, code.ResourceNotFound:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/*** CatXBC ***/
|
||||
|
||||
// ArkInternal returns Err
|
||||
func ArkInternal(s ...string) *LibError {
|
||||
return newErr(Scope, code.ArkInternal, fmt.Sprintf("ark internal error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// ArkInternalL logs error message and returns Err
|
||||
func ArkInternalL(l logx.Logger, s ...string) *LibError {
|
||||
e := ArkInternal(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
/*** CatPubSub ***/
|
||||
|
||||
// Publish returns Err
|
||||
func Publish(s ...string) *LibError {
|
||||
return newErr(Scope, code.Publish, fmt.Sprintf("publish: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// PublishL logs error message and returns Err
|
||||
func PublishL(l logx.Logger, s ...string) *LibError {
|
||||
e := Publish(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// Consume returns Err
|
||||
func Consume(s ...string) *LibError {
|
||||
return newErr(Scope, code.Consume, fmt.Sprintf("consume: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// MsgSizeTooLarge returns Err
|
||||
func MsgSizeTooLarge(s ...string) *LibError {
|
||||
return newErr(Scope, code.MsgSizeTooLarge, fmt.Sprintf("kafka error: %s", strings.Join(s, " ")))
|
||||
}
|
||||
|
||||
// MsgSizeTooLargeL logs error message and returns Err
|
||||
func MsgSizeTooLargeL(l logx.Logger, s ...string) *LibError {
|
||||
e := MsgSizeTooLarge(s...)
|
||||
l.WithCallerSkip(1).Error(e.Error())
|
||||
|
||||
return e
|
||||
}
|
File diff suppressed because it is too large
Load Diff
225
errors/errors.go
225
errors/errors.go
|
@ -1,225 +0,0 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
code2 "code.30cm.net/digimon/library-go/errors/code"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// Scope 全域變數應由服務或模組設置
|
||||
var Scope = code2.Unset
|
||||
|
||||
// LibError 6 碼,服務 2, 大類 2, 詳細錯誤 2
|
||||
type LibError struct {
|
||||
category uint32
|
||||
code uint32
|
||||
scope uint32
|
||||
msg string
|
||||
internalErr error
|
||||
}
|
||||
|
||||
// Error 是錯誤的介面
|
||||
// 私有屬性 "msg" 的 getter 函數
|
||||
func (e *LibError) Error() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 如果 internal err 存在,連接錯誤字串
|
||||
var internalErrStr string
|
||||
if e.internalErr != nil {
|
||||
internalErrStr = e.internalErr.Error()
|
||||
}
|
||||
|
||||
if e.msg != "" {
|
||||
if internalErrStr != "" {
|
||||
return fmt.Sprintf("%s: %s", e.msg, internalErrStr)
|
||||
}
|
||||
|
||||
return e.msg
|
||||
}
|
||||
|
||||
generalErrStr := e.GeneralError()
|
||||
if internalErrStr != "" {
|
||||
return fmt.Sprintf("%s: %s", generalErrStr, internalErrStr)
|
||||
}
|
||||
|
||||
return generalErrStr
|
||||
}
|
||||
|
||||
// Category 私有屬性 "category" 的 getter 函數
|
||||
func (e *LibError) Category() uint32 {
|
||||
if e == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return e.category
|
||||
}
|
||||
|
||||
// Scope 私有屬性 "scope" 的 getter 函數
|
||||
func (e *LibError) Scope() uint32 {
|
||||
if e == nil {
|
||||
return code2.Unset
|
||||
}
|
||||
|
||||
return e.scope
|
||||
}
|
||||
|
||||
// CodeStr 返回帶有零填充的錯誤代碼字串
|
||||
func (e *LibError) CodeStr() string {
|
||||
if e == nil {
|
||||
return "00000"
|
||||
}
|
||||
|
||||
if e.Category() == code2.CatGRPC {
|
||||
return fmt.Sprintf("%02d%04d", e.Scope(), e.Category()+e.Code())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%02d%04d", e.Scope(), e.Code())
|
||||
}
|
||||
|
||||
// Code 私有屬性 "code" 的 getter 函數
|
||||
func (e *LibError) Code() uint32 {
|
||||
if e == nil {
|
||||
return code2.OK
|
||||
}
|
||||
|
||||
return e.code
|
||||
}
|
||||
|
||||
func (e *LibError) FullCode() uint32 {
|
||||
if e == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if e.Category() == code2.CatGRPC {
|
||||
return e.Scope()*10000 + e.Category() + e.Code()
|
||||
}
|
||||
|
||||
return e.Scope()*10000 + e.Code()
|
||||
}
|
||||
|
||||
// HTTPStatus 返回對應的 HTTP 狀態碼
|
||||
func (e *LibError) HTTPStatus() int {
|
||||
if e == nil || e.Code() == code2.OK {
|
||||
return http.StatusOK
|
||||
}
|
||||
// 根據 code 判斷狀態碼
|
||||
switch e.Code() {
|
||||
case code2.ResourceInsufficient:
|
||||
// 400
|
||||
return http.StatusBadRequest
|
||||
case code2.Unauthorized, code2.InsufficientPermission:
|
||||
// 401
|
||||
return http.StatusUnauthorized
|
||||
case code2.InsufficientQuota:
|
||||
// 402
|
||||
return http.StatusPaymentRequired
|
||||
case code2.InvalidPosixTime, code2.Forbidden:
|
||||
// 403
|
||||
return http.StatusForbidden
|
||||
case code2.ResourceNotFound:
|
||||
// 404
|
||||
return http.StatusNotFound
|
||||
case code2.ResourceAlreadyExist, code2.InvalidResourceState:
|
||||
// 409
|
||||
return http.StatusConflict
|
||||
case code2.NotValidImplementation:
|
||||
// 501
|
||||
return http.StatusNotImplemented
|
||||
default:
|
||||
}
|
||||
|
||||
// 根據 category 判斷狀態碼
|
||||
switch e.Category() {
|
||||
case code2.CatInput:
|
||||
return http.StatusBadRequest
|
||||
default:
|
||||
// 如果沒有符合的條件,返回狀態碼 500
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
|
||||
// GeneralError 轉換 category 級別錯誤訊息
|
||||
// 這是給客戶或 API 調用者的一般錯誤訊息
|
||||
func (e *LibError) GeneralError() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
errStr, ok := code2.CatToStr[e.Category()]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
return errStr
|
||||
}
|
||||
|
||||
// Is 在執行 errors.Is() 時調用。
|
||||
// 除非你非常確定你在做什麼,否則不要直接使用這個函數。
|
||||
// 請使用 errors.Is 代替。
|
||||
// 此函數比較兩個錯誤變量是否都是 *Err,並且具有相同的 code(不檢查包裹的內部錯誤)
|
||||
func (e *LibError) Is(f error) bool {
|
||||
var err *LibError
|
||||
ok := errors.As(f, &err)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return e.Code() == err.Code()
|
||||
}
|
||||
|
||||
// Unwrap 返回底層錯誤
|
||||
// 解除包裹錯誤的結果本身可能具有 Unwrap 方法;
|
||||
// 我們稱通過反覆解除包裹產生的錯誤序列為錯誤鏈。
|
||||
func (e *LibError) Unwrap() error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return e.internalErr
|
||||
}
|
||||
|
||||
// Wrap 將內部錯誤設置到 Err 結構
|
||||
func (e *LibError) Wrap(internalErr error) *LibError {
|
||||
if e != nil {
|
||||
e.internalErr = internalErr
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *LibError) GRPCStatus() *status.Status {
|
||||
if e == nil {
|
||||
return status.New(codes.OK, "")
|
||||
}
|
||||
|
||||
return status.New(codes.Code(e.FullCode()), e.Error())
|
||||
}
|
||||
|
||||
// 工廠函數
|
||||
|
||||
// NewErr 創建新的 Err
|
||||
func NewErr(scope, category, detail uint32, msg string) *LibError {
|
||||
return &LibError{
|
||||
category: category,
|
||||
code: detail,
|
||||
scope: scope,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// NewGRPCErr 創建新的 gRPC Err
|
||||
func NewGRPCErr(scope, detail uint32, msg string) *LibError {
|
||||
return &LibError{
|
||||
category: code2.CatGRPC,
|
||||
code: detail,
|
||||
scope: scope,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
|
@ -1,298 +0,0 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
code2 "code.30cm.net/digimon/library-go/errors/code"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func TestCode_GivenNilReceiver_CodeReturnOK_CodeStrReturns00000(t *testing.T) {
|
||||
// setup
|
||||
var e *LibError = nil
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, code2.OK, e.Code())
|
||||
assert.Equal(t, "00000", e.CodeStr())
|
||||
assert.Equal(t, "", e.Error())
|
||||
}
|
||||
|
||||
func TestCode_GivenScope99DetailCode6687_ShouldReturn996687(t *testing.T) {
|
||||
// setup
|
||||
e := LibError{scope: 99, code: 6687}
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, uint32(6687), e.Code())
|
||||
assert.Equal(t, "996687", e.CodeStr())
|
||||
}
|
||||
|
||||
func TestCode_GivenScope0DetailCode87_ShouldReturn87(t *testing.T) {
|
||||
// setup
|
||||
e := LibError{scope: 0, code: 87}
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, uint32(87), e.Code())
|
||||
assert.Equal(t, "00087", e.CodeStr())
|
||||
}
|
||||
|
||||
func TestFromCode_Given870005_ShouldHasScope87_Cat0_Detail5(t *testing.T) {
|
||||
// setup
|
||||
e := FromCode(870005)
|
||||
|
||||
// assert
|
||||
assert.Equal(t, uint32(87), e.Scope())
|
||||
assert.Equal(t, uint32(0), e.Category())
|
||||
assert.Equal(t, uint32(5), e.Code())
|
||||
assert.Equal(t, "", e.Error())
|
||||
}
|
||||
|
||||
func TestFromCode_Given0_ShouldHasScope0_Cat0_Detail0(t *testing.T) {
|
||||
// setup
|
||||
e := FromCode(0)
|
||||
|
||||
// assert
|
||||
assert.Equal(t, uint32(0), e.Scope())
|
||||
assert.Equal(t, uint32(0), e.Category())
|
||||
assert.Equal(t, uint32(0), e.Code())
|
||||
assert.Equal(t, "", e.Error())
|
||||
}
|
||||
|
||||
func TestFromCode_Given9105_ShouldHasScope0_Cat9100_Detail9105(t *testing.T) {
|
||||
// setup
|
||||
e := FromCode(9105)
|
||||
|
||||
// assert
|
||||
assert.Equal(t, uint32(0), e.Scope())
|
||||
assert.Equal(t, uint32(9100), e.Category())
|
||||
assert.Equal(t, uint32(9105), e.Code())
|
||||
assert.Equal(t, "", e.Error())
|
||||
}
|
||||
|
||||
func TestErr_ShouldImplementErrorFunction(t *testing.T) {
|
||||
// setup a func return error
|
||||
f := func() error { return InvalidFormat("fake field") }
|
||||
|
||||
// act
|
||||
err := f()
|
||||
|
||||
// assert
|
||||
assert.NotNil(t, err)
|
||||
assert.Contains(t, fmt.Sprint(err), "fake field") // can be printed
|
||||
}
|
||||
|
||||
func TestGeneralError_GivenNilErr_ShouldReturnEmptyString(t *testing.T) {
|
||||
// setup
|
||||
var e *LibError = nil
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, "", e.GeneralError())
|
||||
}
|
||||
|
||||
func TestGeneralError_GivenNotExistCat_ShouldReturnEmptyString(t *testing.T) {
|
||||
// setup
|
||||
e := LibError{category: 123456}
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, "", e.GeneralError())
|
||||
}
|
||||
|
||||
func TestGeneralError_GivenCatDB_ShouldReturnDBError(t *testing.T) {
|
||||
// setup
|
||||
e := LibError{category: code2.CatDB}
|
||||
catErrStr := code2.CatToStr[code2.CatDB]
|
||||
|
||||
// act & assert
|
||||
assert.Equal(t, catErrStr, e.GeneralError())
|
||||
}
|
||||
|
||||
func TestError_GivenEmptyMsg_ShouldReturnCatGeneralErrorMessage(t *testing.T) {
|
||||
// setup
|
||||
e := LibError{category: code2.CatDB, msg: ""}
|
||||
|
||||
// act
|
||||
errMsg := e.Error()
|
||||
|
||||
// assert
|
||||
assert.Equal(t, code2.CatToStr[code2.CatDB], errMsg)
|
||||
}
|
||||
|
||||
func TestError_GivenMsg_ShouldReturnGiveMsg(t *testing.T) {
|
||||
// setup
|
||||
e := LibError{msg: "FAKE"}
|
||||
|
||||
// act
|
||||
errMsg := e.Error()
|
||||
|
||||
// assert
|
||||
assert.Equal(t, "FAKE", errMsg)
|
||||
}
|
||||
|
||||
func TestIs_GivenNilErr_ShouldReturnFalse(t *testing.T) {
|
||||
var nilErrs *LibError
|
||||
// act
|
||||
result := errors.Is(nilErrs, DBError())
|
||||
result2 := errors.Is(DBError(), nilErrs)
|
||||
|
||||
// assert
|
||||
assert.False(t, result)
|
||||
assert.False(t, result2)
|
||||
}
|
||||
|
||||
func TestIs_GivenNil_ShouldReturnFalse(t *testing.T) {
|
||||
// act
|
||||
result := errors.Is(nil, DBError())
|
||||
result2 := errors.Is(DBError(), nil)
|
||||
|
||||
// assert
|
||||
assert.False(t, result)
|
||||
assert.False(t, result2)
|
||||
}
|
||||
|
||||
func TestIs_GivenNilReceiver_ShouldReturnCorrectResult(t *testing.T) {
|
||||
var nilErr *LibError = nil
|
||||
|
||||
// test 1: nilErr != DBError
|
||||
var dbErr error = DBError("fake db error")
|
||||
assert.False(t, nilErr.Is(dbErr))
|
||||
|
||||
// test 2: nilErr != nil error
|
||||
var nilError error
|
||||
assert.False(t, nilErr.Is(nilError))
|
||||
|
||||
// test 3: nilErr == another nilErr
|
||||
var nilErr2 *LibError = nil
|
||||
assert.True(t, nilErr.Is(nilErr2))
|
||||
}
|
||||
|
||||
func TestIs_GivenDBError_ShouldReturnTrue(t *testing.T) {
|
||||
// setup
|
||||
dbErr := DBError("fake db error")
|
||||
|
||||
// act
|
||||
result := errors.Is(dbErr, DBError("not care"))
|
||||
result2 := errors.Is(DBError(), dbErr)
|
||||
|
||||
// assert
|
||||
assert.True(t, result)
|
||||
assert.True(t, result2)
|
||||
}
|
||||
|
||||
func TestIs_GivenDBErrorAssignToErrorType_ShouldReturnTrue(t *testing.T) {
|
||||
// setup
|
||||
var dbErr error = DBError("fake db error")
|
||||
|
||||
// act
|
||||
result := errors.Is(dbErr, DBError("not care"))
|
||||
result2 := errors.Is(DBError(), dbErr)
|
||||
|
||||
// assert
|
||||
assert.True(t, result)
|
||||
assert.True(t, result2)
|
||||
}
|
||||
|
||||
func TestWrap_GivenNilErr_ShouldNoPanic(t *testing.T) {
|
||||
// act & assert
|
||||
assert.NotPanics(t, func() {
|
||||
var e *LibError = nil
|
||||
_ = e.Wrap(fmt.Errorf("test"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestWrap_GivenErrorToWrap_ShouldReturnErrorWithWrappedError(t *testing.T) {
|
||||
// act & assert
|
||||
wrappedErr := fmt.Errorf("test")
|
||||
wrappingErr := SystemInternalError("WrappingError").Wrap(wrappedErr)
|
||||
unWrappedErr := wrappingErr.Unwrap()
|
||||
|
||||
assert.Equal(t, wrappedErr, unWrappedErr)
|
||||
}
|
||||
|
||||
func TestUnwrap_GivenNilErr_ShouldReturnNil(t *testing.T) {
|
||||
var e *LibError = nil
|
||||
internalErr := e.Unwrap()
|
||||
assert.Nil(t, internalErr)
|
||||
}
|
||||
|
||||
func TestErrorsIs_GivenNilErr_ShouldReturnFalse(t *testing.T) {
|
||||
var e *LibError = nil
|
||||
assert.False(t, errors.Is(e, fmt.Errorf("test")))
|
||||
}
|
||||
|
||||
func TestErrorsAs_GivenNilErr_ShouldReturnFalse(t *testing.T) {
|
||||
var internalErr *testErr
|
||||
var e *LibError = nil
|
||||
assert.False(t, errors.As(e, &internalErr))
|
||||
}
|
||||
|
||||
func TestGRPCStatus(t *testing.T) {
|
||||
// setup table driven tests
|
||||
tests := []struct {
|
||||
name string
|
||||
given *LibError
|
||||
expect *status.Status
|
||||
expectConvert error
|
||||
}{
|
||||
{
|
||||
"nil errs.Err",
|
||||
nil,
|
||||
status.New(codes.OK, ""),
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"InvalidFormat Err",
|
||||
InvalidFormat("fake"),
|
||||
status.New(codes.Code(101), "invalid format: fake"),
|
||||
status.New(codes.Code(101), "invalid format: fake").Err(),
|
||||
},
|
||||
}
|
||||
|
||||
// act & assert
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
s := test.given.GRPCStatus()
|
||||
assert.Equal(t, test.expect.Code(), s.Code())
|
||||
assert.Equal(t, test.expect.Message(), s.Message())
|
||||
assert.Equal(t, test.expectConvert, status.Convert(test.given).Err())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestErr_HTTPStatus(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err *LibError
|
||||
want int
|
||||
}{
|
||||
{name: "nil error", err: nil, want: http.StatusOK},
|
||||
{name: "invalid measurement id", err: &LibError{category: code2.CatResource, code: code2.InvalidMeasurementID}, want: http.StatusInternalServerError},
|
||||
{name: "resource already exists", err: &LibError{category: code2.CatResource, code: code2.ResourceAlreadyExist}, want: http.StatusConflict},
|
||||
{name: "invalid resource state", err: &LibError{category: code2.CatResource, code: code2.InvalidResourceState}, want: http.StatusConflict},
|
||||
{name: "invalid posix time", err: &LibError{category: code2.CatAuth, code: code2.InvalidPosixTime}, want: http.StatusForbidden},
|
||||
{name: "unauthorized", err: &LibError{category: code2.CatAuth, code: code2.Unauthorized}, want: http.StatusUnauthorized},
|
||||
{name: "db error", err: &LibError{category: code2.CatDB, code: code2.DBError}, want: http.StatusInternalServerError},
|
||||
{name: "insufficient permission", err: &LibError{category: code2.CatResource, code: code2.InsufficientPermission}, want: http.StatusUnauthorized},
|
||||
{name: "resource insufficient", err: &LibError{category: code2.CatResource, code: code2.ResourceInsufficient}, want: http.StatusBadRequest},
|
||||
{name: "invalid format", err: &LibError{category: code2.CatInput, code: code2.InvalidFormat}, want: http.StatusBadRequest},
|
||||
{name: "resource not found", err: &LibError{code: code2.ResourceNotFound}, want: http.StatusNotFound},
|
||||
{name: "ok", err: &LibError{code: code2.OK}, want: http.StatusOK},
|
||||
{name: "not valid implementation", err: &LibError{category: code2.CatInput, code: code2.NotValidImplementation}, want: http.StatusNotImplemented},
|
||||
{name: "forbidden", err: &LibError{category: code2.CatAuth, code: code2.Forbidden}, want: http.StatusForbidden},
|
||||
{name: "insufficient quota", err: &LibError{category: code2.CatResource, code: code2.InsufficientQuota}, want: http.StatusPaymentRequired},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
// act
|
||||
got := tt.err.HTTPStatus()
|
||||
|
||||
// assert
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
module code.30cm.net/digimon/library-go/errors
|
||||
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/zeromicro/go-zero v1.7.0
|
||||
google.golang.org/grpc v1.65.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
)
|
125
errors/readme.md
125
errors/readme.md
|
@ -1,125 +0,0 @@
|
|||
## Purpose of errs package
|
||||
1. compatible with `error` interface
|
||||
2. encapsulate error message with functions in `easy_function.go`
|
||||
3. easy for gRPC client/server
|
||||
4. support err's chain by [Working with Errors in Go 1.13](https://blog.golang.org/go1.13-errors)
|
||||
|
||||
|
||||
## Example - Normal function
|
||||
Using builtin functions `InvalidInput` to generate `Err struct`
|
||||
|
||||
**please add your own functions if not exist**
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "adc.github.trendmicro.com/commercial-mgcp/library-go/pkg/errs"
|
||||
|
||||
func handleParam(s string) error {
|
||||
// check user_id format
|
||||
if ok := userIDFormat(s); !ok {
|
||||
return errs.InvalidFormat("param user_id")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Example - gRPC Server
|
||||
`GetAgent` is a method of gRPC server, it wraps `Err` struct to `status.Status` struct
|
||||
```go
|
||||
func (as *agentService) GetAgent(ctx context.Context, req *cloudep.GetAgentRequest) (*cloudep.GetAgentResponse, error) {
|
||||
l := log.WithFields(logger.Fields{"tenant_id": req.TenantId, "agent_id": req.AgentId, "method": "GetAgent"})
|
||||
|
||||
tenantID, err := primitive.ObjectIDFromHex(req.TenantId)
|
||||
if err != nil {
|
||||
// err maybe errs.Err or general error
|
||||
// it's safe to use Convert() here
|
||||
return nil, status.Convert(err).Err()
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Example - gRPC Client
|
||||
Calling `GetAgent` and retry when Category is "DB"
|
||||
```go
|
||||
client := cloudep.NewAgentServiceClient(conn)
|
||||
req := cloudep.GetAgentRequest{
|
||||
TenantId: "not-a-valid-object-id",
|
||||
AgentId: "5eb4fa99006d53c0cb6f9cfe",
|
||||
}
|
||||
|
||||
// Retry if DB error
|
||||
for retry := 3; retry > 0 ; retry-- {
|
||||
resp, err := client.GetAgent(context.Background(), &req)
|
||||
if err != nil {
|
||||
e := errs.FromGRPCError(err)
|
||||
if e.Category() == code.CatGRPC {
|
||||
if e.Code() == uint32(codes.Unavailable) {
|
||||
log.warn("GRPC service unavailable. Retrying...")
|
||||
continue
|
||||
}
|
||||
log.errorf("GRPC built-in error: %v", e)
|
||||
}
|
||||
if e.Category() == code.CatDB {
|
||||
log.warn("retry...")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
```
|
||||
|
||||
## Example - REST server
|
||||
1. handling gRPC client error
|
||||
2. transfer to HTTP code
|
||||
3. transfer to Error body
|
||||
|
||||
```go
|
||||
func Handler(c *gin.Context) {
|
||||
|
||||
// handle error from gRPC client
|
||||
resp, err := client.GetAgent(context.Background(), &req)
|
||||
if err != nil {
|
||||
// to Err
|
||||
e := errs.FromGRPCError(err)
|
||||
|
||||
// get HTTP code & response struct
|
||||
// 2nd parameter true means return general error message to user
|
||||
c.JSON(e.HTTPStatus(), general.NewError(e, true))
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Example - Error Chain
|
||||
1. set internal error by func `Wrap`
|
||||
2. check Err has any error in err's chain matches the target by `errors.Is`
|
||||
3. finds the first error in err's chain that matches target by `errors.As`
|
||||
|
||||
```go
|
||||
// define a specific err type
|
||||
type testErr struct {
|
||||
code int
|
||||
}
|
||||
|
||||
func (e *testErr) Error() string {
|
||||
return strconv.Itoa(e.code)
|
||||
}
|
||||
|
||||
func main() {
|
||||
layer1Err := &testErr{code: 123}
|
||||
// error chain: InvalidFormat -> layer 1 err
|
||||
layer2Err := InvalidFormat("field A", "")
|
||||
layer2Err.Wrap(layer1Err) //set internal error
|
||||
|
||||
// errors.Is should report true
|
||||
hasLayer1Err := errors.Is(layer2Err, layer1Err)
|
||||
|
||||
// errors.As should return internal error
|
||||
var internalErr *testErr
|
||||
ok := errors.As(layer2Err, &internalErr)
|
||||
}
|
||||
```
|
|
@ -7,4 +7,5 @@ const (
|
|||
CloudEPMember
|
||||
CloudEPPermission
|
||||
CloudEPNotification
|
||||
CloudEPTweeting
|
||||
)
|
||||
|
|
|
@ -46,8 +46,6 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv
|
|||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=
|
||||
github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
|
@ -170,6 +168,12 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV
|
|||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
|
||||
|
|
Loading…
Reference in New Issue