-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* introduce custom Error * use k6build.Error for wrapping errors * return HTTP status ok In APIs when requests are processed but return an error Signed-off-by: Pablo Chacin <[email protected]>
- Loading branch information
1 parent
9efb171
commit 604fca1
Showing
15 changed files
with
317 additions
and
110 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package k6build | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
// ErrReasonUnknown signals the reason for an APIError in unknown | ||
var ErrReasonUnknown = errors.New("reason unknown") | ||
|
||
// Error represents an error returned by the build service | ||
// This custom error type facilitates extracting the reason of an error | ||
// by using errors.Unwrap method. | ||
// It also facilitates checking an error (or its reason) using errors.Is by | ||
// comparing the error and its reason. | ||
// This custom type has the following known limitations: | ||
// - A nil Error 'e' will not satisfy errors.Is(e, nil) | ||
// - Is method will not | ||
type Error struct { | ||
Err error `json:"error,omitempty"` | ||
Reason error `json:"reason,omitempty"` | ||
} | ||
|
||
// Error returns the Error as a string | ||
func (e *Error) Error() string { | ||
return fmt.Sprintf("%s: %s", e.Err, e.Reason) | ||
} | ||
|
||
// Is returns true if the target error is the same as the Error or its reason | ||
// It attempts several strategies: | ||
// - compare error and reason to target's Error() | ||
// - unwrap the error and reason and compare to target's Error | ||
// - unwrap target and compares to the error recursively | ||
func (e *Error) Is(target error) bool { | ||
if target == nil { | ||
return false | ||
} | ||
|
||
if e.Err.Error() == target.Error() { | ||
return true | ||
} | ||
|
||
if e.Reason != nil && e.Reason.Error() == target.Error() { | ||
return true | ||
} | ||
|
||
if u := errors.Unwrap(e.Err); u != nil && u.Error() == target.Error() { | ||
return true | ||
} | ||
|
||
if u := errors.Unwrap(e.Reason); u != nil && u.Error() == target.Error() { | ||
return true | ||
} | ||
|
||
return e.Is(errors.Unwrap(target)) | ||
} | ||
|
||
// Unwrap returns the underlying reason for the Error | ||
func (e *Error) Unwrap() error { | ||
return e.Reason | ||
} | ||
|
||
// MarshalJSON implements the json.Marshaler interface for the Error type | ||
func (e *Error) MarshalJSON() ([]byte, error) { | ||
return json.Marshal(&struct { | ||
Err string `json:"error,omitempty"` | ||
Reason string `json:"reason,omitempty"` | ||
}{ | ||
Err: e.Err.Error(), | ||
Reason: e.Reason.Error(), | ||
}) | ||
} | ||
|
||
// UnmarshalJSON implements the json.Unmarshaler interface for the Error type | ||
func (e *Error) UnmarshalJSON(data []byte) error { | ||
val := struct { | ||
Err string `json:"error,omitempty"` | ||
Reason string `json:"reason,omitempty"` | ||
}{} | ||
|
||
if err := json.Unmarshal(data, &val); err != nil { | ||
return err | ||
} | ||
|
||
e.Err = errors.New(val.Err) | ||
e.Reason = errors.New(val.Reason) | ||
return nil | ||
} | ||
|
||
// NewError creates an Error from an error and a reason | ||
// If the reason is nil, ErrReasonUnknown is used | ||
func NewError(err error, reason error) *Error { | ||
if reason == nil { | ||
reason = ErrReasonUnknown | ||
} | ||
return &Error{ | ||
Err: err, | ||
Reason: reason, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package k6build | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
func Test_Error(t *testing.T) { | ||
t.Parallel() | ||
|
||
var ( | ||
err = errors.New("error") | ||
reason = errors.New("reason") | ||
) | ||
|
||
testCases := []struct { | ||
title string | ||
err error | ||
reason error | ||
expect []error | ||
}{ | ||
{ | ||
title: "error and reason", | ||
err: err, | ||
reason: reason, | ||
expect: []error{err, reason}, | ||
}, | ||
{ | ||
title: "error not reason", | ||
err: err, | ||
reason: nil, | ||
expect: []error{err}, | ||
}, | ||
{ | ||
title: "multiple and reasons", | ||
err: err, | ||
reason: reason, | ||
expect: []error{err, reason}, | ||
}, | ||
{ | ||
title: "wrapped err", | ||
err: fmt.Errorf("wrapped %w", err), | ||
reason: reason, | ||
expect: []error{err, reason}, | ||
}, | ||
{ | ||
title: "wrapped reason", | ||
err: err, | ||
reason: fmt.Errorf("wrapped %w", reason), | ||
expect: []error{err, reason}, | ||
}, | ||
{ | ||
title: "wrapped err in target", | ||
err: err, | ||
reason: reason, | ||
expect: []error{fmt.Errorf("wrapped %w", err)}, | ||
}, | ||
{ | ||
title: "wrapped reason in target", | ||
err: err, | ||
reason: reason, | ||
expect: []error{fmt.Errorf("wrapped %w", reason)}, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.title, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
err := NewError(tc.err, tc.reason) | ||
for _, expected := range tc.expect { | ||
if !errors.Is(err, expected) { | ||
t.Fatalf("expected %v got %v", expected, err) | ||
} | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.