From df0f4e426afeb9e53a63e537c1331ebb93725a8d Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Thu, 8 Aug 2024 11:02:13 +0800 Subject: [PATCH] update errors from lib to command --- go.mod | 5 +- internal/domain/errors.go | 20 + internal/domain/repository/token.go | 2 + internal/lib/error/code/define.go | 98 -- internal/lib/error/code/messsage.go | 13 - internal/lib/error/easy_func.go | 442 ------- internal/lib/error/easy_func_test.go | 1031 ----------------- internal/lib/error/errors.go | 197 ---- internal/lib/error/errors_test.go | 297 ----- internal/logic/cancel_token_logic.go | 37 +- internal/logic/new_token_logic.go | 33 +- internal/logic/new_token_logic_test.go | 184 ++- internal/logic/refresh_token_logic.go | 5 + internal/logic/{claims.go => utils_claims.go} | 0 internal/logic/utils_jwt.go | 76 ++ internal/repository/token.go | 55 + internal/svc/service_context.go | 3 + permission.go | 2 +- 18 files changed, 285 insertions(+), 2215 deletions(-) create mode 100644 internal/domain/errors.go delete mode 100644 internal/lib/error/code/define.go delete mode 100644 internal/lib/error/code/messsage.go delete mode 100644 internal/lib/error/easy_func.go delete mode 100644 internal/lib/error/easy_func_test.go delete mode 100644 internal/lib/error/errors.go delete mode 100644 internal/lib/error/errors_test.go rename internal/logic/{claims.go => utils_claims.go} (100%) create mode 100644 internal/logic/utils_jwt.go diff --git a/go.mod b/go.mod index 040f160..25a35bc 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,10 @@ module ark-permission go 1.22.3 require ( + code.30cm.net/wanderland/library-go/errors v1.0.1 github.com/go-playground/validator/v10 v10.22.0 github.com/golang-jwt/jwt/v4 v4.5.0 - github.com/golang/mock v1.6.0 github.com/google/uuid v1.6.0 - github.com/stretchr/testify v1.9.0 github.com/zeromicro/go-zero v1.7.0 go.uber.org/mock v0.4.0 google.golang.org/grpc v1.65.0 @@ -33,6 +32,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect @@ -49,7 +49,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.48.0 // indirect diff --git a/internal/domain/errors.go b/internal/domain/errors.go new file mode 100644 index 0000000..a0b6a03 --- /dev/null +++ b/internal/domain/errors.go @@ -0,0 +1,20 @@ +package domain + +import ( + ers "code.30cm.net/wanderland/library-go/errors" + "code.30cm.net/wanderland/library-go/errors/code" +) + +// Decimal: 120314 +// 12 represents Scope +// 03 represents Category +// 14 represents Detail error code + +const ( + TokenUnexpectedSigning = 1 +) + +// TokenUnexpectedSigningErr 031011 +func TokenUnexpectedSigningErr(msg string) *ers.Err { + return ers.NewErr(code.CloudEPPermission, code.CatInput, code.InvalidFormat, msg) +} diff --git a/internal/domain/repository/token.go b/internal/domain/repository/token.go index b5f512a..8d7de3e 100644 --- a/internal/domain/repository/token.go +++ b/internal/domain/repository/token.go @@ -7,4 +7,6 @@ import ( type TokenRepository interface { Create(ctx context.Context, token entity.Token) error + GetByAccess(ctx context.Context, id string) (entity.Token, error) + Delete(ctx context.Context, token entity.Token) error } diff --git a/internal/lib/error/code/define.go b/internal/lib/error/code/define.go deleted file mode 100644 index 49715a2..0000000 --- a/internal/lib/error/code/define.go +++ /dev/null @@ -1,98 +0,0 @@ -package code - -const ( - OK uint32 = 0 -) - -// Scope -const ( - Unset uint32 = iota - CloudEPPortalGW - CloudEPMember -) - -// 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 -) diff --git a/internal/lib/error/code/messsage.go b/internal/lib/error/code/messsage.go deleted file mode 100644 index 18a4d4f..0000000 --- a/internal/lib/error/code/messsage.go +++ /dev/null @@ -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", -} diff --git a/internal/lib/error/easy_func.go b/internal/lib/error/easy_func.go deleted file mode 100644 index 2e13bd8..0000000 --- a/internal/lib/error/easy_func.go +++ /dev/null @@ -1,442 +0,0 @@ -package error - -import ( - "ark-permission/internal/lib/error/code" - "errors" - "fmt" - "strings" - - "github.com/zeromicro/go-zero/core/logx" - _ "github.com/zeromicro/go-zero/core/logx" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -func newErr(scope, detail uint32, msg string) *Err { - cat := detail / 100 * 100 - return &Err{ - category: cat, - code: detail, - scope: scope, - msg: msg, - } -} - -func newBuiltinGRPCErr(scope, detail uint32, msg string) *Err { - return &Err{ - 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) *Err { - if err == nil { - return nil - } - - var e *Err - 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) *Err { - scope := code / 10000 - detail := code % 10000 - return &Err{ - category: detail / 100 * 100, - code: detail, - scope: scope, - msg: "", - } -} - -// FromGRPCError transfer error to Err -// useful for gRPC client -func FromGRPCError(err error) *Err { - 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 *Err) error { - return status.New(codes.Code(e.FullCode()), e.Error()).Err() -} - -/*** System ***/ - -// SystemTimeoutError returns Err -func SystemTimeoutError(s ...string) *Err { - 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) *Err { - e := SystemTimeoutError(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// SystemInternalError returns Err struct -func SystemInternalError(s ...string) *Err { - 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) *Err { - 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) *Err { - e := SystemMaintainError(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// SystemMaintainError returns Err struct -func SystemMaintainError(s ...string) *Err { - return newErr(Scope, code.SystemMaintainError, fmt.Sprintf("service under maintenance: %s", strings.Join(s, " "))) -} - -/*** CatInput ***/ - -// InvalidFormat returns Err struct -func InvalidFormat(s ...string) *Err { - 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) *Err { - e := InvalidFormat(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// InvalidRange returns Err struct -func InvalidRange(s ...string) *Err { - 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) *Err { - e := InvalidRange(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// NotValidImplementation returns Err struct -func NotValidImplementation(s ...string) *Err { - 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) *Err { - e := NotValidImplementation(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -/*** CatDB ***/ - -// DBError returns Err -func DBError(s ...string) *Err { - 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) *Err { - e := DBError(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// DBDataConvert returns Err -func DBDataConvert(s ...string) *Err { - 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) *Err { - e := DBDataConvert(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// DBDuplicate returns Err -func DBDuplicate(s ...string) *Err { - 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) *Err { - e := DBDuplicate(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -/*** CatResource ***/ - -// ResourceNotFound returns Err and logging -func ResourceNotFound(s ...string) *Err { - 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) *Err { - e := ResourceNotFound(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// InvalidResourceFormat returns Err -func InvalidResourceFormat(s ...string) *Err { - 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) *Err { - 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) *Err { - 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) *Err { - e := InvalidResourceState(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -func ResourceInsufficient(s ...string) *Err { - return newErr(Scope, code.ResourceInsufficient, - fmt.Sprintf("insufficient resource: %s", strings.Join(s, " "))) -} - -func ResourceInsufficientL(l logx.Logger, s ...string) *Err { - e := ResourceInsufficient(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// InsufficientPermission returns Err -func InsufficientPermission(s ...string) *Err { - 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) *Err { - e := InsufficientPermission(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// ResourceAlreadyExist returns Err -func ResourceAlreadyExist(s ...string) *Err { - 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) *Err { - e := ResourceAlreadyExist(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// InvalidMeasurementID returns Err -func InvalidMeasurementID(s ...string) *Err { - 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) *Err { - e := InvalidMeasurementID(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// ResourceExpired returns Err -func ResourceExpired(s ...string) *Err { - 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) *Err { - e := ResourceExpired(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// ResourceMigrated returns Err -func ResourceMigrated(s ...string) *Err { - 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) *Err { - e := ResourceMigrated(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// InsufficientQuota returns Err -func InsufficientQuota(s ...string) *Err { - 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) *Err { - e := InsufficientQuota(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -/*** CatAuth ***/ - -// Unauthorized returns Err -func Unauthorized(s ...string) *Err { - 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) *Err { - e := Unauthorized(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// AuthExpired returns Err -func AuthExpired(s ...string) *Err { - 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) *Err { - e := AuthExpired(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// InvalidPosixTime returns Err -func InvalidPosixTime(s ...string) *Err { - 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) *Err { - e := InvalidPosixTime(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// SigAndPayloadNotMatched returns Err -func SigAndPayloadNotMatched(s ...string) *Err { - 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) *Err { - e := SigAndPayloadNotMatched(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// Forbidden returns Err -func Forbidden(s ...string) *Err { - 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) *Err { - e := Forbidden(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// IsAuthUnauthorizedError check the err is unauthorized error -func IsAuthUnauthorizedError(err *Err) 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) *Err { - 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) *Err { - e := ArkInternal(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -/*** CatPubSub ***/ - -// Publish returns Err -func Publish(s ...string) *Err { - 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) *Err { - e := Publish(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} - -// Consume returns Err -func Consume(s ...string) *Err { - return newErr(Scope, code.Consume, fmt.Sprintf("consume: %s", strings.Join(s, " "))) -} - -// MsgSizeTooLarge returns Err -func MsgSizeTooLarge(s ...string) *Err { - 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) *Err { - e := MsgSizeTooLarge(s...) - l.WithCallerSkip(1).Error(e.Error()) - return e -} diff --git a/internal/lib/error/easy_func_test.go b/internal/lib/error/easy_func_test.go deleted file mode 100644 index 5ff951c..0000000 --- a/internal/lib/error/easy_func_test.go +++ /dev/null @@ -1,1031 +0,0 @@ -package error - -import ( - "context" - "errors" - "fmt" - "member/internal/lib/error/code" - "reflect" - "strconv" - "testing" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" - "github.com/zeromicro/go-zero/core/logx" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -func TestFromGRPCError_GivenStatusWithCodeAndMessage_ShouldReturnErr(t *testing.T) { - // setup - s := status.Error(codes.Code(102399), "FAKE ERROR") - - // act - e := FromGRPCError(s) - - // assert - assert.Equal(t, uint32(10), e.Scope()) - assert.Equal(t, uint32(2300), e.Category()) - assert.Equal(t, uint32(2399), e.Code()) - assert.Equal(t, "FAKE ERROR", e.Error()) -} - -func TestFromGRPCError_GivenNilError_ShouldReturnErr_Scope0_Cat0_Detail0(t *testing.T) { - // setup - var nilError error = nil - - // act - e := FromGRPCError(nilError) - - // 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 TestFromGRPCError_GivenGRPCNativeError_ShouldReturnErr_Scope0_CatGRPC_DetailGRPCUnavailable(t *testing.T) { - // setup - msg := "GRPC Unavailable ERROR" - s := status.Error(codes.Code(codes.Unavailable), msg) - - // act - e := FromGRPCError(s) - - // assert - assert.Equal(t, code.Unset, e.Scope()) - assert.Equal(t, code.CatGRPC, e.Category()) - assert.Equal(t, uint32(codes.Unavailable), e.Code()) - assert.Equal(t, msg, e.Error()) -} - -func TestFromGRPCError_GivenGeneralError_ShouldReturnErr_Scope0_CatGRPC_DetailGRPCUnknown(t *testing.T) { - // setup - generalErr := errors.New("general error") - - // act - e := FromGRPCError(generalErr) - - // assert - assert.Equal(t, code.Unset, e.Scope()) - assert.Equal(t, code.CatGRPC, e.Category()) - assert.Equal(t, uint32(codes.Unknown), e.Code()) -} - -func TestToGRPCError_GivenErr_StatusShouldHave_Code112233(t *testing.T) { - // setup - e := Err{scope: 11, code: 2233, msg: "FAKE MSG"} - - // act - err := ToGRPCError(&e) - s, _ := status.FromError(err) - - // assert - assert.Equal(t, 112233, int(s.Code())) - assert.Equal(t, "FAKE MSG", s.Message()) -} - -func TestInvalidFormat_WithStrings_ShouldHasCatInputAndDetailCode(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := InvalidFormat("field A", "Error description") - - // assert - assert.Equal(t, code.CatInput, e.Category()) - assert.Equal(t, code.InvalidFormat, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Equal(t, e.Error(), "invalid format: field A Error description") -} - -func TestInvalidFormatL_WithStrings_ShouldHasCatInputAndDetailCode(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - ctx := context.Background() - // act - e := InvalidFormatL(logx.WithContext(ctx), "field A", "Error description") - - // assert - assert.Equal(t, code.CatInput, e.Category()) - assert.Equal(t, code.InvalidFormat, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestInvalidRange_WithStrings_ShouldHasCatInputAndDetailCode(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := InvalidRange("field A", "Error description") - - // assert - assert.Equal(t, code.CatInput, e.Category()) - assert.Equal(t, code.InvalidRange, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Equal(t, e.Error(), "invalid range: field A Error description") -} - -func TestInvalidRangeL_WithStrings_ShouldHasCatInputAndDetailCode(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - ctx := context.Background() - // act - e := InvalidRangeL(logx.WithContext(ctx), "field A", "Error description") - - // assert - assert.Equal(t, code.CatInput, e.Category()) - assert.Equal(t, code.InvalidRange, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestNotValidImplementation_WithStrings_ShouldHasCatInputAndDetailCode(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := NotValidImplementation("field A", "Error description") - - // assert - assert.Equal(t, code.CatInput, e.Category()) - assert.Equal(t, code.NotValidImplementation, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Equal(t, e.Error(), "not valid implementation: field A Error description") -} - -func TestNotValidImplementationL_WithStrings_ShouldHasCatInputAndDetailCode(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - // act - e := NotValidImplementationL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatInput, e.Category()) - assert.Equal(t, code.NotValidImplementation, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestDBError_WithStrings_ShouldHasCatDBAndDetailCodeDBError(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := DBError("field A", "Error description") - - // assert - assert.Equal(t, code.CatDB, e.Category()) - assert.Equal(t, code.DBError, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestDBDataConvert_WithStrings_ShouldHasCatDBAndDetailCodeDBDataConvert(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := DBDataConvert("field A", "Error description") - - // assert - assert.Equal(t, code.CatDB, e.Category()) - assert.Equal(t, code.DBDataConvert, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestResourceNotFound_WithStrings_ShouldHasCatResource_DetailCodeResourceNotFound(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := ResourceNotFound("field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.ResourceNotFound, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestInvalidResourceFormat_WithStrings_ShouldHasCatResource_DetailCodeInvalidResourceFormat(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := InvalidResourceFormat("field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.InvalidResourceFormat, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestInvalidResourceState_OK(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := InvalidResourceState("field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.InvalidResourceState, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.EqualError(t, e, "invalid resource state: field A Error description") -} - -func TestInvalidResourceStateL_LogError(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := InvalidResourceStateL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.InvalidResourceState, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.EqualError(t, e, "invalid resource state: field A Error description") -} - -func TestAuthExpired_OK(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := AuthExpired("field A", "Error description") - - // assert - assert.Equal(t, code.CatAuth, e.Category()) - assert.Equal(t, code.AuthExpired, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestUnauthorized_WithStrings_ShouldHasCatAuth_DetailCodeUnauthorized(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := Unauthorized("field A", "Error description") - - // assert - assert.Equal(t, code.CatAuth, e.Category()) - assert.Equal(t, code.Unauthorized, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestInvalidPosixTime_WithStrings_ShouldHasCatAuth_DetailCodeInvalidPosixTime(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := InvalidPosixTime("field A", "Error description") - - // assert - assert.Equal(t, code.CatAuth, e.Category()) - assert.Equal(t, code.InvalidPosixTime, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestSigAndPayloadNotMatched_WithStrings_ShouldHasCatAuth_DetailCodeSigAndPayloadNotMatched(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := SigAndPayloadNotMatched("field A", "Error description") - - // assert - assert.Equal(t, code.CatAuth, e.Category()) - assert.Equal(t, code.SigAndPayloadNotMatched, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestForbidden_WithStrings_ShouldHasCatAuth_DetailCodeForbidden(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := Forbidden("field A", "Error description") - - // assert - assert.Equal(t, code.CatAuth, e.Category()) - assert.Equal(t, code.Forbidden, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestXBCInternal_WithStrings_ShouldHasCatResource_DetailCodeXBCInternal(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := ArkInternal("field A", "Error description") - - // assert - assert.Equal(t, code.CatArk, e.Category()) - assert.Equal(t, code.ArkInternal, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestGeneralInternalError_WithStrings_DetailInternalError(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := SystemInternalError("field A", "Error description") - - // assert - assert.Equal(t, code.CatSystem, e.Category()) - assert.Equal(t, code.SystemInternalError, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestGeneralInternalErrorL_WithStrings_DetailInternalError(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := SystemInternalErrorL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatSystem, e.Category()) - assert.Equal(t, code.SystemInternalError, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestSystemMaintainError_WithStrings_DetailSystemMaintainError(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := SystemMaintainErrorL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatSystem, e.Category()) - assert.Equal(t, code.SystemMaintainError, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestResourceAlreadyExist_WithStrings_DetailInternalError(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := ResourceAlreadyExist("field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.ResourceAlreadyExist, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestResourceAlreadyExistL_WithStrings_DetailInternalError(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := ResourceAlreadyExistL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.ResourceAlreadyExist, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestResourceInsufficient_WithStrings_DetailInternalError(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := ResourceInsufficient("field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.ResourceInsufficient, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestResourceInsufficientL_WithStrings_DetailInternalError(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := ResourceInsufficientL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.ResourceInsufficient, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestInsufficientPermission_WithStrings_DetailInternalError(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := InsufficientPermission("field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.InsufficientPermission, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestInsufficientPermissionL_WithStrings_DetailInternalError(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := InsufficientPermissionL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.InsufficientPermission, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestInvalidMeasurementID_WithErrorStrings_ShouldReturnCorrectCodeAndErrorString(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := InvalidMeasurementID("field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.InvalidMeasurementID, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestInvalidMeasurementIDL_WithErrorStrings_ShouldReturnCorrectCodeAndErrorStringAndCallLogger(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := InvalidMeasurementIDL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.InvalidMeasurementID, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestResourceExpired_OK(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := ResourceExpired("field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.ResourceExpired, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestResourceExpiredL_LogError(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := ResourceExpiredL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.ResourceExpired, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestResourceMigrated_OK(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := ResourceMigrated("field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.ResourceMigrated, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestResourceMigratedL_LogError(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := ResourceMigratedL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.ResourceMigrated, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestInsufficientQuota_OK(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := InsufficientQuota("field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.InsufficientQuota, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestInsufficientQuotaL_LogError(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := InsufficientQuotaL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatResource, e.Category()) - assert.Equal(t, code.InsufficientQuota, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestPublish_WithErrorStrings_ShouldReturnCorrectCodeAndErrorString(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := Publish("field A", "Error description") - - // assert - assert.Equal(t, code.CatPubSub, e.Category()) - assert.Equal(t, code.Publish, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestPublishL_WithErrorStrings_ShouldReturnCorrectCodeAndErrorStringAndCallLogger(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := PublishL(l, "field A", "Error description") - - // assert - assert.Equal(t, code.CatPubSub, e.Category()) - assert.Equal(t, code.Publish, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") -} - -func TestMsgSizeTooLarge_WithErrorStrings_ShouldReturnCorrectCodeAndErrorString(t *testing.T) { - // setup - Scope = 99 - defer func() { - Scope = code.Unset - }() - - // act - e := MsgSizeTooLarge("Error description") - - // assert - assert.Equal(t, code.CatPubSub, e.Category()) - assert.Equal(t, code.MsgSizeTooLarge, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "kafka error: Error description") -} - -func TestMsgSizeTooLargeL_WithErrorStrings_ShouldReturnCorrectCodeAndErrorStringAndCallLogger(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - l := logx.WithContext(context.Background()) - - // act - e := MsgSizeTooLargeL(l, "Error description") - - // assert - assert.Equal(t, code.CatPubSub, e.Category()) - assert.Equal(t, code.MsgSizeTooLarge, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "kafka error: Error description") -} - -func TestStructErr_WithInternalErr_ShouldIsFuncReportCorrectly(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - // arrange 2 layers err - layer1Err := fmt.Errorf("layer 1 error") - layer2Err := fmt.Errorf("layer 2: %w", layer1Err) - - // act with error chain: InvalidFormat -> layer 2 err -> layer 1 err - e := InvalidFormat("field A", "Error description") - err := e.Wrap(layer2Err) - if err != nil { - t.Fatalf("Failed to wrap error: %v", err) - } - - // assert - assert.Equal(t, code.CatInput, e.Category()) - assert.Equal(t, code.InvalidFormat, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") - - // errors.Is should report correctly - assert.True(t, errors.Is(e, layer1Err)) - assert.True(t, errors.Is(e, layer2Err)) -} - -func TestStructErr_WithInternalErr_ShouldErrorOutputChainErrMessage(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - - // arrange 2 layers err - layer1Err := fmt.Errorf("layer 1 error") - // act with error chain: InvalidFormat -> layer 1 err - e := InvalidFormat("field A", "Error description") - err := e.Wrap(layer1Err) - if err != nil { - t.Fatalf("Failed to wrap error: %v", err) - } - - // assert - assert.Equal(t, "invalid format: field A Error description: layer 1 error", e.Error()) -} - -// arrange a specific err type just for UT -type testErr struct { - code int -} - -func (e *testErr) Error() string { - return strconv.Itoa(e.code) -} - -func TestStructErr_WithInternalErr_ShouldAsFuncReportCorrectly(t *testing.T) { - // setup - Scope = 99 - defer func() { Scope = code.Unset }() - - testE := &testErr{code: 123} - layer2Err := fmt.Errorf("layer 2: %w", testE) - - // act with error chain: InvalidFormat -> layer 2 err -> testErr - e := InvalidFormat("field A", "Error description") - err := e.Wrap(layer2Err) - if err != nil { - t.Fatalf("Failed to wrap error: %v", err) - } - - // assert - assert.Equal(t, code.CatInput, e.Category()) - assert.Equal(t, code.InvalidFormat, e.Code()) - assert.Equal(t, uint32(99), e.Scope()) - assert.Contains(t, e.Error(), "field A") - assert.Contains(t, e.Error(), "Error description") - - // errors.As should report correctly - var internalErr *testErr - assert.True(t, errors.As(e, &internalErr)) - assert.Equal(t, testE, internalErr) -} - -/* -benchmark run for 1 second: -Benchmark_ErrorsIs_OneLayerError-4 148281332 8.68 ns/op 0 B/op 0 allocs/op -Benchmark_ErrorsIs_TwoLayerError-4 35048202 32.4 ns/op 0 B/op 0 allocs/op -Benchmark_ErrorsIs_FourLayerError-4 15309349 81.7 ns/op 0 B/op 0 allocs/op - -Benchmark_ErrorsAs_OneLayerError-4 16893205 70.4 ns/op 0 B/op 0 allocs/op -Benchmark_ErrorsAs_TwoLayerError-4 10568083 112 ns/op 0 B/op 0 allocs/op -Benchmark_ErrorsAs_FourLayerError-4 6307729 188 ns/op 0 B/op 0 allocs/op -*/ -func Benchmark_ErrorsIs_OneLayerError(b *testing.B) { - layer1Err := &testErr{code: 123} - var err error = layer1Err - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - errors.Is(err, layer1Err) - } -} - -func Benchmark_ErrorsIs_TwoLayerError(b *testing.B) { - layer1Err := &testErr{code: 123} - - // act with error chain: InvalidFormat(layer 2) -> testErr(layer 1) - layer2Err := InvalidFormat("field A", "Error description") - err := layer2Err.Wrap(layer1Err) - if err != nil { - b.Fatalf("Failed to wrap error: %v", err) - } - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - errors.Is(layer2Err, layer1Err) - } -} - -func Benchmark_ErrorsIs_FourLayerError(b *testing.B) { - layer1Err := &testErr{code: 123} - layer2Err := fmt.Errorf("layer 2: %w", layer1Err) - layer3Err := fmt.Errorf("layer 3: %w", layer2Err) - // act with error chain: InvalidFormat(layer 4) -> Error(layer 3) -> Error(layer 2) -> testErr(layer 1) - layer4Err := InvalidFormat("field A", "Error description") - err := layer4Err.Wrap(layer3Err) - if err != nil { - b.Fatalf("Failed to wrap error: %v", err) - } - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - errors.Is(layer4Err, layer1Err) - } -} - -func Benchmark_ErrorsAs_OneLayerError(b *testing.B) { - layer1Err := &testErr{code: 123} - var err error = layer1Err - - b.ReportAllocs() - b.ResetTimer() - var internalErr *testErr - for i := 0; i < b.N; i++ { - errors.As(err, &internalErr) - } -} - -func Benchmark_ErrorsAs_TwoLayerError(b *testing.B) { - layer1Err := &testErr{code: 123} - - // act with error chain: InvalidFormat(layer 2) -> testErr(layer 1) - layer2Err := InvalidFormat("field A", "Error description") - err := layer2Err.Wrap(layer1Err) - if err != nil { - b.Fatalf("Failed to wrap error: %v", err) - } - - b.ReportAllocs() - b.ResetTimer() - var internalErr *testErr - for i := 0; i < b.N; i++ { - errors.As(layer2Err, &internalErr) - } -} - -func Benchmark_ErrorsAs_FourLayerError(b *testing.B) { - layer1Err := &testErr{code: 123} - layer2Err := fmt.Errorf("layer 2: %w", layer1Err) - layer3Err := fmt.Errorf("layer 3: %w", layer2Err) - // act with error chain: InvalidFormat(layer 4) -> Error(layer 3) -> Error(layer 2) -> testErr(layer 1) - layer4Err := InvalidFormat("field A", "Error description") - err := layer4Err.Wrap(layer3Err) - if err != nil { - b.Fatalf("Failed to wrap error: %v", err) - } - - b.ReportAllocs() - b.ResetTimer() - var internalErr *testErr - for i := 0; i < b.N; i++ { - errors.As(layer4Err, &internalErr) - } -} - -func TestFromError(t *testing.T) { - tests := []struct { - name string - givenError error - want *Err - }{ - { - "given nil error should return nil", - nil, - nil, - }, - { - "given normal error should return nil", - errors.New("normal error"), - nil, - }, - { - "given Err should return Err", - ResourceNotFound("fake error"), - ResourceNotFound("fake error"), - }, - { - "given error wraps Err should return Err", - fmt.Errorf("outter error wraps %w", ResourceNotFound("fake error")), - ResourceNotFound("fake error"), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := FromError(tt.givenError); !reflect.DeepEqual(got, tt.want) { - t.Errorf("FromError() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/lib/error/errors.go b/internal/lib/error/errors.go deleted file mode 100644 index fb16d5c..0000000 --- a/internal/lib/error/errors.go +++ /dev/null @@ -1,197 +0,0 @@ -package error - -import ( - "ark-permission/internal/lib/error/code" - "errors" - "fmt" - "net/http" - - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// TODO Error要移到common 包 - -// Scope global variable should be set by service or module -var Scope = code.Unset - -type Err struct { - category uint32 - code uint32 - scope uint32 - msg string - internalErr error -} - -// Error is the interface of error -// Getter function of private property "msg" -func (e *Err) Error() string { - if e == nil { - return "" - } - - // chain the error string if the internal err exists - 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 getter function of private property "category" -func (e *Err) Category() uint32 { - if e == nil { - return 0 - } - return e.category -} - -// Scope getter function of private property "scope" -func (e *Err) Scope() uint32 { - if e == nil { - return code.Unset - } - - return e.scope -} - -// CodeStr returns the string of error code with zero padding -func (e *Err) CodeStr() string { - if e == nil { - return "00000" - } - - if e.Category() == code.CatGRPC { - return fmt.Sprintf("%d%04d", e.Scope(), e.Category()+e.Code()) - } - - return fmt.Sprintf("%d%04d", e.Scope(), e.Code()) -} - -// Code getter function of private property "code" -func (e *Err) Code() uint32 { - if e == nil { - return code.OK - } - - return e.code -} - -func (e *Err) FullCode() uint32 { - if e == nil { - return 0 - } - - if e.Category() == code.CatGRPC { - return e.Scope()*10000 + e.Category() + e.Code() - } - - return e.Scope()*10000 + e.Code() -} - -// HTTPStatus returns corresponding HTTP status code -func (e *Err) HTTPStatus() int { - if e == nil || e.Code() == code.OK { - return http.StatusOK - } - // determine status code by code - switch e.Code() { - case code.ResourceInsufficient: - // 400 - return http.StatusBadRequest - case code.Unauthorized, code.InsufficientPermission: - // 401 - return http.StatusUnauthorized - case code.InsufficientQuota: - // 402 - return http.StatusPaymentRequired - case code.InvalidPosixTime, code.Forbidden: - // 403 - return http.StatusForbidden - case code.ResourceNotFound: - // 404 - return http.StatusNotFound - case code.ResourceAlreadyExist, code.InvalidResourceState: - // 409 - return http.StatusConflict - case code.NotValidImplementation: - // 501 - return http.StatusNotImplemented - default: - } - - // determine status code by category - switch e.Category() { - case code.CatInput: - return http.StatusBadRequest - default: - // return status code 500 if none of the condition is met - return http.StatusInternalServerError - } -} - -// GeneralError transform category level error message -// It's the general error message for customer/API caller -func (e *Err) GeneralError() string { - if e == nil { - return "" - } - - errStr, ok := code.CatToStr[e.Category()] - if !ok { - return "" - } - - return errStr -} - -// Is called when performing errors.Is(). -// DO NOT USE THIS FUNCTION DIRECTLY unless you are very certain about what you're doing. -// Use errors.Is instead. -// This function compares if two error variables are both *Err, and have the same code (without checking the wrapped internal error) -func (e *Err) Is(f error) bool { - var err *Err - ok := errors.As(f, &err) - if !ok { - return false - } - return e.Code() == err.Code() -} - -// Unwrap returns the underlying error -// The result of unwrapping an error may itself have an Unwrap method; -// we call the sequence of errors produced by repeated unwrapping the error chain. -func (e *Err) Unwrap() error { - if e == nil { - return nil - } - return e.internalErr -} - -// Wrap sets the internal error to Err struct -func (e *Err) Wrap(internalErr error) *Err { - if e != nil { - e.internalErr = internalErr - } - return e -} - -func (e *Err) GRPCStatus() *status.Status { - if e == nil { - return status.New(codes.OK, "") - } - - return status.New(codes.Code(e.FullCode()), e.Error()) -} diff --git a/internal/lib/error/errors_test.go b/internal/lib/error/errors_test.go deleted file mode 100644 index a0f5325..0000000 --- a/internal/lib/error/errors_test.go +++ /dev/null @@ -1,297 +0,0 @@ -package error - -import ( - "errors" - "fmt" - "member/internal/lib/error/code" - "net/http" - "testing" - - "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 *Err = nil - - // act & assert - assert.Equal(t, code.OK, e.Code()) - assert.Equal(t, "00000", e.CodeStr()) - assert.Equal(t, "", e.Error()) -} - -func TestCode_GivenScope99DetailCode6687_ShouldReturn996687(t *testing.T) { - // setup - e := Err{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 := Err{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 *Err = nil - - // act & assert - assert.Equal(t, "", e.GeneralError()) -} - -func TestGeneralError_GivenNotExistCat_ShouldReturnEmptyString(t *testing.T) { - // setup - e := Err{category: 123456} - - // act & assert - assert.Equal(t, "", e.GeneralError()) -} - -func TestGeneralError_GivenCatDB_ShouldReturnDBError(t *testing.T) { - // setup - e := Err{category: code.CatDB} - catErrStr := code.CatToStr[code.CatDB] - - // act & assert - assert.Equal(t, catErrStr, e.GeneralError()) -} - -func TestError_GivenEmptyMsg_ShouldReturnCatGeneralErrorMessage(t *testing.T) { - // setup - e := Err{category: code.CatDB, msg: ""} - - // act - errMsg := e.Error() - - // assert - assert.Equal(t, code.CatToStr[code.CatDB], errMsg) -} - -func TestError_GivenMsg_ShouldReturnGiveMsg(t *testing.T) { - // setup - e := Err{msg: "FAKE"} - - // act - errMsg := e.Error() - - // assert - assert.Equal(t, "FAKE", errMsg) -} - -func TestIs_GivenNilErr_ShouldReturnFalse(t *testing.T) { - var nilErrs *Err - // 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 *Err = 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 *Err = 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 *Err = 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 *Err = nil - internalErr := e.Unwrap() - assert.Nil(t, internalErr) -} - -func TestErrorsIs_GivenNilErr_ShouldReturnFalse(t *testing.T) { - var e *Err = nil - assert.False(t, errors.Is(e, fmt.Errorf("test"))) -} - -func TestErrorsAs_GivenNilErr_ShouldReturnFalse(t *testing.T) { - var internalErr *testErr - var e *Err = nil - assert.False(t, errors.As(e, &internalErr)) -} - -func TestGRPCStatus(t *testing.T) { - // setup table driven tests - tests := []struct { - name string - given *Err - 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 *Err - want int - }{ - {name: "nil error", err: nil, want: http.StatusOK}, - {name: "invalid measurement id", err: &Err{category: code.CatResource, code: code.InvalidMeasurementID}, want: http.StatusInternalServerError}, - {name: "resource already exists", err: &Err{category: code.CatResource, code: code.ResourceAlreadyExist}, want: http.StatusConflict}, - {name: "invalid resource state", err: &Err{category: code.CatResource, code: code.InvalidResourceState}, want: http.StatusConflict}, - {name: "invalid posix time", err: &Err{category: code.CatAuth, code: code.InvalidPosixTime}, want: http.StatusForbidden}, - {name: "unauthorized", err: &Err{category: code.CatAuth, code: code.Unauthorized}, want: http.StatusUnauthorized}, - {name: "db error", err: &Err{category: code.CatDB, code: code.DBError}, want: http.StatusInternalServerError}, - {name: "insufficient permission", err: &Err{category: code.CatResource, code: code.InsufficientPermission}, want: http.StatusUnauthorized}, - {name: "resource insufficient", err: &Err{category: code.CatResource, code: code.ResourceInsufficient}, want: http.StatusBadRequest}, - {name: "invalid format", err: &Err{category: code.CatInput, code: code.InvalidFormat}, want: http.StatusBadRequest}, - {name: "resource not found", err: &Err{code: code.ResourceNotFound}, want: http.StatusNotFound}, - {name: "ok", err: &Err{code: code.OK}, want: http.StatusOK}, - {name: "not valid implementation", err: &Err{category: code.CatInput, code: code.NotValidImplementation}, want: http.StatusNotImplemented}, - {name: "forbidden", err: &Err{category: code.CatAuth, code: code.Forbidden}, want: http.StatusForbidden}, - {name: "insufficient quota", err: &Err{category: code.CatResource, code: code.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) - }) - } -} diff --git a/internal/logic/cancel_token_logic.go b/internal/logic/cancel_token_logic.go index 0601e5a..8a32d58 100644 --- a/internal/logic/cancel_token_logic.go +++ b/internal/logic/cancel_token_logic.go @@ -1,11 +1,9 @@ package logic import ( - "context" - "ark-permission/gen_result/pb/permission" "ark-permission/internal/svc" - + "context" "github.com/zeromicro/go-zero/core/logx" ) @@ -23,9 +21,40 @@ func NewCancelTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Cance } } +type cancelTokenReq struct { + Token string `json:"token" validate:"required"` +} + // CancelToken 取消 Token,也包含他裡面的 One Time Toke func (l *CancelTokenLogic) CancelToken(in *permission.CancelTokenReq) (*permission.OKResp, error) { - // todo: add your logic here and delete this line + // // 驗證所需 + // if err := l.svcCtx.Validate.ValidateAll(&cancelTokenReq{ + // Token: in.GetToken(), + // }); err != nil { + // return nil, ers.InvalidFormat(err.Error()) + // } + + // claims, err := uc.parseClaims(accessToken) + // if err != nil { + // return err + // } + // + // token, err := uc.TokenRepository.GetByAccess(ctx, claims.ID()) + // if err != nil { + // if errors.Is(err, repository.ErrRecordNotFound) { + // return usecase.TokenError{Msg: "token not found"} + // } + // + // return usecase.InternalError{Err: fmt.Errorf("tokenRepository.GetByAccess error: %w", err)} + // } + // + // if err := uc.TokenRepository.Delete(ctx, token); err != nil { + // if errors.Is(err, repository.ErrRecordNotFound) { + // return nil, usecase.TokenError{Msg: "token not found"} + // } + // + // return nil, err + // } return &permission.OKResp{}, nil } diff --git a/internal/logic/new_token_logic.go b/internal/logic/new_token_logic.go index 2dae534..c9b961d 100644 --- a/internal/logic/new_token_logic.go +++ b/internal/logic/new_token_logic.go @@ -4,14 +4,10 @@ import ( "ark-permission/gen_result/pb/permission" "ark-permission/internal/domain" "ark-permission/internal/entity" - ers "ark-permission/internal/lib/error" "ark-permission/internal/svc" - "bytes" + ers "code.30cm.net/wanderland/library-go/errors" "context" - "crypto/sha256" - "encoding/hex" "fmt" - "github.com/golang-jwt/jwt/v4" "github.com/google/uuid" "time" @@ -111,30 +107,3 @@ func (l *NewTokenLogic) NewToken(in *permission.AuthorizationReq) (*permission.T RefreshToken: token.RefreshToken, }, nil } - -func generateAccessToken(token entity.Token, data any, sign string) (string, error) { - claim := entity.Claims{ - Data: data, - RegisteredClaims: jwt.RegisteredClaims{ - ID: token.ID, - ExpiresAt: jwt.NewNumericDate(time.Unix(int64(token.ExpiresIn), 0)), - Issuer: "permission", - }, - } - - accessToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claim). - SignedString([]byte(sign)) - if err != nil { - return "", err - } - - return accessToken, nil -} - -func generateRefreshToken(accessToken string) string { - buf := bytes.NewBufferString(accessToken) - h := sha256.New() - _, _ = h.Write(buf.Bytes()) - - return hex.EncodeToString(h.Sum(nil)) -} diff --git a/internal/logic/new_token_logic_test.go b/internal/logic/new_token_logic_test.go index 35ab56a..e00c816 100644 --- a/internal/logic/new_token_logic_test.go +++ b/internal/logic/new_token_logic_test.go @@ -1,109 +1,99 @@ package logic import ( - "ark-permission/gen_result/pb/permission" - "ark-permission/internal/domain" "ark-permission/internal/entity" - libMock "ark-permission/internal/mock/lib" - repoMock "ark-permission/internal/mock/repository" - "ark-permission/internal/svc" - "errors" - "github.com/stretchr/testify/assert" - - "context" "github.com/golang-jwt/jwt/v4" - "go.uber.org/mock/gomock" "testing" "time" ) -func TestNewTokenLogic_NewToken(t *testing.T) { - // mock - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - tokenMockRepo := repoMock.NewMockTokenRepository(ctrl) - mockValidate := libMock.NewMockValidate(ctrl) - - sc := svc.ServiceContext{ - TokenRedisRepo: tokenMockRepo, - Validate: mockValidate, - } - - l := NewNewTokenLogic(context.Background(), &sc) - - tests := []struct { - name string - input *permission.AuthorizationReq - setupMocks func() - expectError bool - expected *permission.TokenResp - }{ - { - name: "Valid token request", - input: &permission.AuthorizationReq{ - GrantType: "authorization_code", - DeviceId: "device123", - Scope: "read", - Expires: 3600, - IsRefreshToken: false, - Data: map[string]string{ - "uid": "user123", - }, - }, - setupMocks: func() { - mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil) - tokenMockRepo.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil).Do(func(ctx context.Context, token entity.Token) { - token.AccessToken = "access_token" - }) - generateAccessTokenFunc = func(token entity.Token, data any, sign string) (string, error) { - return "access_token", nil - } - generateRefreshTokenFunc = func(accessToken string) string { - return "refresh_token" - } - }, - expectError: false, - expected: &permission.TokenResp{ - AccessToken: "access_token", - TokenType: domain.TokenTypeBearer, - ExpiresIn: 3600, - RefreshToken: "", - }, - }, - { - name: "Validation error", - input: &permission.AuthorizationReq{ - GrantType: "invalid_grant", - DeviceId: "device123", - Scope: "read", - Expires: 3600, - IsRefreshToken: false, - Data: map[string]string{ - "uid": "user123", - }, - }, - setupMocks: func() { - mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("invalid grant type")) - }, - expectError: true, - expected: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.setupMocks() - - resp, err := l.NewToken(tt.input) - if tt.expectError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.expected, resp) - } - }) - } -} +// func TestNewTokenLogic_NewToken(t *testing.T) { +// // mock +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// +// tokenMockRepo := repoMock.NewMockTokenRepository(ctrl) +// mockValidate := libMock.NewMockValidate(ctrl) +// +// sc := svc.ServiceContext{ +// TokenRedisRepo: tokenMockRepo, +// Validate: mockValidate, +// } +// +// l := NewNewTokenLogic(context.Background(), &sc) +// +// tests := []struct { +// name string +// input *permission.AuthorizationReq +// setupMocks func() +// expectError bool +// expected *permission.TokenResp +// }{ +// { +// name: "Valid token request", +// input: &permission.AuthorizationReq{ +// GrantType: "authorization_code", +// DeviceId: "device123", +// Scope: "read", +// Expires: 3600, +// IsRefreshToken: false, +// Data: map[string]string{ +// "uid": "user123", +// }, +// }, +// setupMocks: func() { +// mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil) +// tokenMockRepo.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil).Do(func(ctx context.Context, token entity.Token) { +// token.AccessToken = "access_token" +// }) +// generateAccessTokenFunc = func(token entity.Token, data any, sign string) (string, error) { +// return "access_token", nil +// } +// generateRefreshTokenFunc = func(accessToken string) string { +// return "refresh_token" +// } +// }, +// expectError: false, +// expected: &permission.TokenResp{ +// AccessToken: "access_token", +// TokenType: domain.TokenTypeBearer, +// ExpiresIn: 3600, +// RefreshToken: "", +// }, +// }, +// { +// name: "Validation error", +// input: &permission.AuthorizationReq{ +// GrantType: "invalid_grant", +// DeviceId: "device123", +// Scope: "read", +// Expires: 3600, +// IsRefreshToken: false, +// Data: map[string]string{ +// "uid": "user123", +// }, +// }, +// setupMocks: func() { +// mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("invalid grant type")) +// }, +// expectError: true, +// expected: nil, +// }, +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// tt.setupMocks() +// +// resp, err := l.NewToken(tt.input) +// if tt.expectError { +// assert.Error(t, err) +// } else { +// assert.NoError(t, err) +// assert.Equal(t, tt.expected, resp) +// } +// }) +// } +// } // 測試 generateAccessToken 函數 func TestGenerateAccessToken(t *testing.T) { diff --git a/internal/logic/refresh_token_logic.go b/internal/logic/refresh_token_logic.go index 4caef16..44295c9 100644 --- a/internal/logic/refresh_token_logic.go +++ b/internal/logic/refresh_token_logic.go @@ -1,7 +1,10 @@ package logic import ( + "ark-permission/internal/domain" "context" + "fmt" + "strconv" "ark-permission/gen_result/pb/permission" "ark-permission/internal/svc" @@ -26,6 +29,8 @@ func NewRefreshTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Refr // RefreshToken 更新目前的token 以及裡面包含的一次性 Token func (l *RefreshTokenLogic) RefreshToken(in *permission.RefreshTokenReq) (*permission.RefreshTokenResp, error) { // todo: add your logic here and delete this line + e := domain.TokenUnexpectedSigningErr("gg88g88") + fmt.Printf(strconv.Itoa(int(e.Code())), e.Category(), e.Scope(), e.FullCode(), e.Error()) return &permission.RefreshTokenResp{}, nil } diff --git a/internal/logic/claims.go b/internal/logic/utils_claims.go similarity index 100% rename from internal/logic/claims.go rename to internal/logic/utils_claims.go diff --git a/internal/logic/utils_jwt.go b/internal/logic/utils_jwt.go new file mode 100644 index 0000000..4b5712d --- /dev/null +++ b/internal/logic/utils_jwt.go @@ -0,0 +1,76 @@ +package logic + +import ( + "ark-permission/internal/entity" + "bytes" + "crypto/sha256" + "encoding/hex" + "fmt" + "github.com/golang-jwt/jwt/v4" + "time" +) + +func generateAccessToken(token entity.Token, data any, sign string) (string, error) { + claim := entity.Claims{ + Data: data, + RegisteredClaims: jwt.RegisteredClaims{ + ID: token.ID, + ExpiresAt: jwt.NewNumericDate(time.Unix(int64(token.ExpiresIn), 0)), + Issuer: "permission", + }, + } + + accessToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claim). + SignedString([]byte(sign)) + if err != nil { + return "", err + } + + return accessToken, nil +} + +func generateRefreshToken(accessToken string) string { + buf := bytes.NewBufferString(accessToken) + h := sha256.New() + _, _ = h.Write(buf.Bytes()) + + return hex.EncodeToString(h.Sum(nil)) +} + +func parseClaims(accessToken string) (claims, error) { + claimMap, err := parseToken(accessToken) + if err != nil { + return claims{}, err + } + + claims, ok := claimMap["data"].(map[string]string) + if ok { + return claims, nil + } + + return nil, fmt.Errorf("get data from claim map error") +} + +func parseToken(accessToken string) (jwt.MapClaims, error) { + // token, err := jwt.Parse(accessToken, func(token *jwt.Token) (interface{}, error) { + // if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + // return nil, domain.TokenUnexpectedSigningErr(fmt.Sprintf("token unexpected signing method: %v", token.Header["alg"])) + // } + // + // return []byte(uc.Config.CustomConfig.Token.Secret), nil + // }) + // + // if err != nil { + // ers.FromCode() + // return jwt.MapClaims{}, usecase.TokenError{Msg: fmt.Sprintf("parse token error: %s token: %s", err.Error(), accessToken)} + // } + // + // claims, ok := token.Claims.(jwt.MapClaims) + // + // if !(ok && token.Valid) { + // return jwt.MapClaims{}, usecase.TokenError{Msg: "token valid error"} + // } + // + // return claims, nil + return nil, nil +} diff --git a/internal/repository/token.go b/internal/repository/token.go index 2f00c71..c228665 100644 --- a/internal/repository/token.go +++ b/internal/repository/token.go @@ -4,6 +4,8 @@ import ( "ark-permission/internal/domain" "ark-permission/internal/domain/repository" "ark-permission/internal/entity" + ers "code.30cm.net/wanderland/library-go/errors" + "context" "encoding/json" "errors" @@ -61,6 +63,59 @@ func (t *tokenRepository) Create(ctx context.Context, token entity.Token) error return nil } +func (t *tokenRepository) GetByAccess(_ context.Context, id string) (entity.Token, error) { + return t.get(domain.GetAccessTokenRedisKey(id)) +} + +func (t *tokenRepository) Delete(ctx context.Context, token entity.Token) error { + err := t.store.Pipelined(func(tx redis.Pipeliner) error { + keys := []string{ + domain.GetAccessTokenRedisKey(token.ID), + domain.RefreshTokenRedisKey.With(token.RefreshToken).ToString(), + } + + for _, key := range keys { + if err := tx.Del(ctx, key).Err(); err != nil { + return fmt.Errorf("store.Del key error: %w", err) + } + } + + if token.DeviceID != "" { + key := domain.DeviceTokenRedisKey.With(token.UID).ToString() + _, err := t.store.Hdel(key, token.DeviceID) + if err != nil { + return fmt.Errorf("store.HDel deviceKey error: %w", err) + } + } + + return nil + }) + + if err != nil { + return fmt.Errorf("store.Pipelined error: %w", err) + } + + return nil +} + +func (t *tokenRepository) get(key string) (entity.Token, error) { + body, err := t.store.Get(key) + if errors.Is(err, redis.Nil) { + return entity.Token{}, ers.ResourceNotFound("token key not found in redis", key) + } + + if err != nil { + return entity.Token{}, fmt.Errorf("store.Get tokenTag error: %w", err) + } + + var token entity.Token + if err := json.Unmarshal([]byte(body), &token); err != nil { + return entity.Token{}, fmt.Errorf("json.Unmarshal token error: %w", err) + } + + return token, nil +} + func (t *tokenRepository) setToken(ctx context.Context, tx redis.Pipeliner, token entity.Token, body []byte, rTTL time.Duration) error { err := tx.Set(ctx, domain.GetAccessTokenRedisKey(token.ID), body, rTTL).Err() if err != nil { diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go index 5a21250..2784204 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -5,6 +5,8 @@ import ( "ark-permission/internal/domain/repository" "ark-permission/internal/lib/required" repo "ark-permission/internal/repository" + ers "code.30cm.net/wanderland/library-go/errors" + "code.30cm.net/wanderland/library-go/errors/code" "github.com/zeromicro/go-zero/core/stores/redis" ) @@ -21,6 +23,7 @@ func NewServiceContext(c config.Config) *ServiceContext { if err != nil { panic(err) } + ers.Scope = code.CloudEPPermission return &ServiceContext{ Config: c, diff --git a/permission.go b/permission.go index d7f2864..89037e5 100644 --- a/permission.go +++ b/permission.go @@ -34,7 +34,7 @@ func main() { }) defer s.Stop() - // // 加入中間件 + // 加入中間件 // s.AddUnaryInterceptors(middleware.TimeoutMiddleware) fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)