Skip to content

Commit

Permalink
Moved project from the Nordcloud private repository
Browse files Browse the repository at this point in the history
  • Loading branch information
kevwargo committed Dec 23, 2019
1 parent ee950f0 commit 3be5a91
Show file tree
Hide file tree
Showing 10 changed files with 1,013 additions and 0 deletions.
39 changes: 39 additions & 0 deletions errors/aws_error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package errors

import (
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/redshift"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/pkg/errors"
)

const (
AWSAccessDenied = "AccessDenied"
AWSAccessDeniedException = "AccessDeniedException"

AWSDynamoTableNotFound = dynamodb.ErrCodeTableNotFoundException
AWSS3BucketNotFound = s3.ErrCodeNoSuchBucket

AWSRedshiftClusterSnapsotQuotaExceeded = redshift.ErrCodeClusterSnapshotQuotaExceededFault
)

//GetAWSErrorCode returns the underlying AWS error code from the error.
func GetAWSErrorCode(err error) string {
nativeError := errors.Cause(err)
if nativeError == nil {
return ""
}
if ncError, ok := nativeError.(NCError); ok {
nativeError = errors.Cause(ncError.RootError)
}
if nativeError == nil {
return ""
}

if awsError, ok := nativeError.(awserr.Error); ok {
return awsError.Code()
}

return ""
}
40 changes: 40 additions & 0 deletions errors/aws_error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package errors

import (
"testing"

"github.com/pkg/errors"

"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/stretchr/testify/assert"
)

func TestGetAWSErrorCode_Standard(t *testing.T) {
cause := errors.New("std except")
errCode := GetAWSErrorCode(cause)
assert.Equal(t, "", errCode)
}

func TestGetAWSErrorCode_AWSErr(t *testing.T) {
cause := awserr.New("code", "aws error", nil)
errCode := GetAWSErrorCode(cause)
assert.Equal(t, "code", errCode)
}

func TestGetAWSErrorCode_Context(t *testing.T) {
cause := WithContext(awserr.New("code1", "aws error", nil), "context1", nil)
errCode := GetAWSErrorCode(cause)
assert.Equal(t, "code1", errCode)
}

func TestGetAWSErrorCode_Wrap(t *testing.T) {
cause := WithContext(errors.Wrap(awserr.New("code1", "aws error", nil), "wrap"), "context1", nil)
errCode := GetAWSErrorCode(cause)
assert.Equal(t, "code1", errCode)
}

func TestGetAWSErrorCode_MultilevelContext(t *testing.T) {
cause := WithContext(WithContext(errors.Wrap(awserr.New("code1", "aws error", nil), "wrap"), "context0", nil), "context1", nil)
errCode := GetAWSErrorCode(cause)
assert.Equal(t, "code1", errCode)
}
196 changes: 196 additions & 0 deletions errors/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package errors

import (
"fmt"
"strings"
)

type LogSeverity string

const (
//ERROR Severity - logged with error level
ERROR LogSeverity = "error"
//WARN Severity warn - logged with warning level
WARN LogSeverity = "warning"
//INFO Severity - logged with info level
INFO LogSeverity = "info"
//DEBUG Severity - logged with Debug level
DEBUG LogSeverity = "debug"
)

// ListToError converts errors list to single error
func ListToError(errs []error) error {
if len(errs) == 0 {
return nil
}
var errorMessages []string
for _, err := range errs {
errorMessages = append(errorMessages, err.Error())
}
return fmt.Errorf("[%s]", strings.Join(errorMessages[:], ", "))
}

// Fields keeps context.
type Fields map[string]interface{}

// Cause keeps the context information about the error
type Cause struct {
Message string
Fields Fields
FuncName string
FileName string
Line int
Severity LogSeverity
}

// NCError basic error structure
type NCError struct {
Causes []Cause
// Contains stack trace from the initial place when the error
// was raised.
Stack []string
//The root error at the base level.
RootError error
}

func (n NCError) Error() string {
var messages []string
for _, cause := range n.Causes {
messages = append(messages, cause.Message)
}
return strings.Join(messages, ": ")
}

//New error with context.
func New(message string, fields Fields) error {
fileName, funcName, lineNumber := GetRuntimeContext()
newCause := Cause{
Message: message,
Fields: fields,
FuncName: funcName,
FileName: fileName,
Line: lineNumber,
Severity: ERROR}
return NCError{
Causes: []Cause{newCause},
Stack: GetTrace()}
}

func NewWithSeverity(message string, fields Fields, severity LogSeverity) error {
fileName, funcName, lineNumber := GetRuntimeContext()
newCause := Cause{
Message: message,
Fields: fields,
FuncName: funcName,
FileName: fileName,
Line: lineNumber,
Severity: severity,
}
return NCError{
Causes: []Cause{newCause},
Stack: GetTrace(),
}
}

//WithContext set new error wrapped with message and error context.
func WithContext(err error, message string, fields Fields) error {
// Attach message to the list of causes.
fileName, funcName, lineNumber := GetRuntimeContext()
newCause := Cause{
Message: message,
Fields: fields,
FuncName: funcName,
FileName: fileName,
Line: lineNumber,
Severity: ERROR,
}
//If we wrap existing NCError at the higher layer. Here we only append causes.
//and do not touch stack trace and root error.
if ncError, ok := err.(NCError); ok {
ncError.Causes = append([]Cause{newCause}, ncError.Causes...)
return ncError
}

return NCError{
Causes: []Cause{newCause, Cause{Message: err.Error()}},
Stack: GetTrace(),
RootError: err}
}

//WithContextAndSeverity set new error wrapped with message, severity and error context.
func WithContextAndSeverity(err error, message string, severity LogSeverity, fields Fields) error {
// Attach message to the list of causes.
fileName, funcName, lineNumber := GetRuntimeContext()
newCause := Cause{
Message: message,
Fields: fields,
FuncName: funcName,
FileName: fileName,
Line: lineNumber,
Severity: severity,
}
//If we wrap existing NCError at the higher layer. Here we only append causes.
//and do not touch stack trace and root error.
if ncError, ok := err.(NCError); ok {
ncError.Causes = append([]Cause{newCause}, ncError.Causes...)
return ncError
}

return NCError{
Causes: []Cause{newCause, Cause{Message: err.Error()}},
Stack: GetTrace(),
RootError: err,
}
}

// GetContext returns fields from the error (with attached stack and causes fields)
// This will be used for logrus.WithFields method.
func (n *NCError) GetContext() Fields {
return Fields{
"stack": n.Stack,
"causes": n.Causes}
}

// GetMergedFields returns fields from the error.
// The custom fields are merged from every error's cause's fields (as defined by WithContext invocations).
// If the field with the same name is present in multiple causes the value from the outermost cause is taken.
// This will be used for logrus.WithFields method.
func (n *NCError) GetMergedFields() Fields {
errFields := Fields{}
for _, cause := range n.Causes {
for k, v := range cause.Fields {
if _, exists := errFields[k]; !exists {
errFields[k] = v
}
}
}

return errFields
}

// GetMergedFieldsContext returns error stack and merged fields.
func (n *NCError) GetMergedFieldsContext() Fields {
return Fields{
"stack": n.Stack,
"fields": n.GetMergedFields(),
}
}

//GetErrorSeverity returns outermost NCError severity or ERROR level.
func GetErrorSeverity(err error) LogSeverity {
if ncError, ok := err.(NCError); ok {
if len(ncError.Causes) > 0 {
return ncError.Causes[0].Severity
}
return ERROR
}
return ERROR
}

// GetRootError returns root error.
func GetRootError(err error) error {
if ncError, ok := err.(NCError); ok && ncError.RootError != nil {
return ncError.RootError
}
return err
}
Loading

0 comments on commit 3be5a91

Please sign in to comment.