mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
refactor : Refactor error handling to use dry/usefulerror (#283)
* update the go.sum Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * migrate most of the files to dry errors Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * update the rest of the files Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * fixs the review comments Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> * chores Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> --------- Signed-off-by: DivyanshuVortex <divyanshuchandra9027@gmail.com> Co-authored-by: Abhisek Datta <abhisek.datta@gmail.com>
This commit is contained in:
co-authored by
Abhisek Datta
parent
e5fe0df82e
commit
20e01d5cae
@@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/safedep/dry/log"
|
||||
"github.com/safedep/pmg/usefulerror"
|
||||
"github.com/safedep/pmg/errcodes"
|
||||
)
|
||||
|
||||
// ErrorExit prints a minimal, clean error message and exits with a non-zero status code.
|
||||
@@ -23,7 +23,7 @@ func ErrorExitWithCode(err error, code int) {
|
||||
|
||||
// Use help as hint, but for unknown errors show bug report link
|
||||
hint := usefulErr.Help()
|
||||
if usefulErr.Code() == usefulerror.ErrCodeUnknown {
|
||||
if usefulErr.Code() == errcodes.Unknown {
|
||||
hint = "Report this issue: https://github.com/safedep/pmg/issues/new?labels=bug"
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/safedep/pmg/usefulerror"
|
||||
"github.com/safedep/dry/usefulerror"
|
||||
"github.com/safedep/pmg/errcodes"
|
||||
)
|
||||
|
||||
// errorMatcher defines how to detect and convert a specific error type
|
||||
@@ -35,8 +36,8 @@ var errorMatchers = []errorMatcher{
|
||||
humanError = fmt.Sprintf("File or directory not found: %s", path)
|
||||
}
|
||||
|
||||
return usefulerror.Useful().
|
||||
WithCode(usefulerror.ErrCodeNotFound).
|
||||
return usefulerror.NewUsefulError().
|
||||
WithCode(errcodes.NotFound).
|
||||
WithHumanError(humanError).
|
||||
WithHelp("Check if the path exists").
|
||||
WithAdditionalHelp("Use 'ls' to check directory contents").
|
||||
@@ -54,8 +55,8 @@ var errorMatchers = []errorMatcher{
|
||||
if path != "" {
|
||||
humanError = fmt.Sprintf("Permission denied: %s", path)
|
||||
}
|
||||
return usefulerror.Useful().
|
||||
WithCode(usefulerror.ErrCodePermissionDenied).
|
||||
return usefulerror.NewUsefulError().
|
||||
WithCode(errcodes.PermissionDenied).
|
||||
WithHumanError(humanError).
|
||||
WithHelp("Check permissions or use sudo").
|
||||
WithAdditionalHelp("Use 'ls -la' to check permissions").
|
||||
@@ -72,8 +73,8 @@ var errorMatchers = []errorMatcher{
|
||||
var exitErr *exec.ExitError
|
||||
errors.As(err, &exitErr)
|
||||
exitCode := exitErr.ExitCode()
|
||||
return usefulerror.Useful().
|
||||
WithCode(usefulerror.ErrCodeLifecycle).
|
||||
return usefulerror.NewUsefulError().
|
||||
WithCode(errcodes.Lifecycle).
|
||||
WithHumanError(fmt.Sprintf("Command failed with exit code %d", exitCode)).
|
||||
WithHelp("Check command output above").
|
||||
Wrap(err)
|
||||
@@ -85,8 +86,8 @@ var errorMatchers = []errorMatcher{
|
||||
return errors.Is(err, context.DeadlineExceeded)
|
||||
},
|
||||
convert: func(err error) usefulerror.UsefulError {
|
||||
return usefulerror.Useful().
|
||||
WithCode(usefulerror.ErrCodeTimeout).
|
||||
return usefulerror.NewUsefulError().
|
||||
WithCode(errcodes.Timeout).
|
||||
WithHumanError("Operation timed out").
|
||||
WithHelp("Try again or check your network").
|
||||
WithAdditionalHelp("Consider increasing timeout or retry later").
|
||||
@@ -99,8 +100,8 @@ var errorMatchers = []errorMatcher{
|
||||
return errors.Is(err, context.Canceled)
|
||||
},
|
||||
convert: func(err error) usefulerror.UsefulError {
|
||||
return usefulerror.Useful().
|
||||
WithCode(usefulerror.ErrCodeCanceled).
|
||||
return usefulerror.NewUsefulError().
|
||||
WithCode(errcodes.Canceled).
|
||||
WithHumanError("Operation was canceled").
|
||||
Wrap(err)
|
||||
},
|
||||
@@ -121,15 +122,15 @@ var errorMatchers = []errorMatcher{
|
||||
convert: func(err error) usefulerror.UsefulError {
|
||||
var netErr net.Error
|
||||
if errors.As(err, &netErr) && netErr.Timeout() {
|
||||
return usefulerror.Useful().
|
||||
WithCode(usefulerror.ErrCodeTimeout).
|
||||
return usefulerror.NewUsefulError().
|
||||
WithCode(errcodes.Timeout).
|
||||
WithHumanError("Network request timed out").
|
||||
WithHelp("Check your internet connection").
|
||||
WithAdditionalHelp("Consider increasing timeout or retry later").
|
||||
Wrap(err)
|
||||
}
|
||||
return usefulerror.Useful().
|
||||
WithCode(usefulerror.ErrCodeNetwork).
|
||||
return usefulerror.NewUsefulError().
|
||||
WithCode(errcodes.Network).
|
||||
WithHumanError("Network error occurred").
|
||||
WithHelp("Check your internet connection").
|
||||
WithAdditionalHelp("The package registry may be temporarily unavailable").
|
||||
@@ -142,8 +143,8 @@ var errorMatchers = []errorMatcher{
|
||||
return errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)
|
||||
},
|
||||
convert: func(err error) usefulerror.UsefulError {
|
||||
return usefulerror.Useful().
|
||||
WithCode(usefulerror.ErrCodeUnexpectedEOF).
|
||||
return usefulerror.NewUsefulError().
|
||||
WithCode(errcodes.UnexpectedEOF).
|
||||
WithHumanError("Unexpected end of data").
|
||||
WithHelp("Retry the download").
|
||||
WithAdditionalHelp("This may indicate network instability").
|
||||
@@ -152,6 +153,17 @@ var errorMatchers = []errorMatcher{
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
usefulerror.RegisterErrorConverter("pmg-ui-converter", func(err error) (usefulerror.UsefulError, bool) {
|
||||
for _, matcher := range errorMatchers {
|
||||
if matcher.match(err) {
|
||||
return matcher.convert(err), true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
})
|
||||
}
|
||||
|
||||
// convertToUsefulError attempts to convert a regular error to a UsefulError
|
||||
// by analyzing the error chain for known error types.
|
||||
// Returns the original error wrapped in a generic UsefulError if no specific match is found.
|
||||
@@ -164,14 +176,8 @@ func convertToUsefulError(err error) usefulerror.UsefulError {
|
||||
return ue
|
||||
}
|
||||
|
||||
for _, matcher := range errorMatchers {
|
||||
if matcher.match(err) {
|
||||
return matcher.convert(err)
|
||||
}
|
||||
}
|
||||
|
||||
return usefulerror.Useful().
|
||||
WithCode(usefulerror.ErrCodeUnknown).
|
||||
return usefulerror.NewUsefulError().
|
||||
WithCode(errcodes.Unknown).
|
||||
WithHumanError(extractRootCause(err)).
|
||||
WithHelp("An unexpected error occurred.").
|
||||
Wrap(err)
|
||||
|
||||
@@ -9,11 +9,12 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/safedep/pmg/usefulerror"
|
||||
"github.com/safedep/dry/usefulerror"
|
||||
"github.com/safedep/pmg/errcodes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_convertToUsefulError(t *testing.T) {
|
||||
func Test_ErrorConverters(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputError error
|
||||
@@ -24,60 +25,58 @@ func Test_convertToUsefulError(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "AlreadyUseful",
|
||||
inputError: usefulerror.Useful().
|
||||
inputError: usefulerror.NewUsefulError().
|
||||
WithCode("CUSTOM").
|
||||
WithHumanError("Already useful").
|
||||
Msg("test"),
|
||||
WithMsg("test"),
|
||||
wantCode: "CUSTOM",
|
||||
wantHumanError: "Already useful",
|
||||
},
|
||||
{
|
||||
name: "FileNotExist",
|
||||
inputError: &fs.PathError{Op: "open", Path: "/nonexistent/file.txt", Err: os.ErrNotExist},
|
||||
wantCode: usefulerror.ErrCodeNotFound,
|
||||
wantCode: errcodes.NotFound,
|
||||
wantContains: "/nonexistent/file.txt",
|
||||
},
|
||||
{
|
||||
name: "PermissionDenied",
|
||||
inputError: &fs.PathError{Op: "open", Path: "/root/secret", Err: os.ErrPermission},
|
||||
wantCode: usefulerror.ErrCodePermissionDenied,
|
||||
wantCode: errcodes.PermissionDenied,
|
||||
wantContains: "/root/secret",
|
||||
},
|
||||
{
|
||||
name: "ContextTimeout",
|
||||
inputError: context.DeadlineExceeded,
|
||||
wantCode: usefulerror.ErrCodeTimeout,
|
||||
wantCode: errcodes.Timeout,
|
||||
wantContains: "timed out",
|
||||
},
|
||||
{
|
||||
name: "ContextCanceled",
|
||||
inputError: context.Canceled,
|
||||
wantCode: usefulerror.ErrCodeCanceled,
|
||||
wantCode: errcodes.Canceled,
|
||||
wantContains: "canceled",
|
||||
},
|
||||
{
|
||||
name: "UnexpectedEOF",
|
||||
inputError: io.ErrUnexpectedEOF,
|
||||
wantCode: usefulerror.ErrCodeUnexpectedEOF,
|
||||
wantCode: errcodes.UnexpectedEOF,
|
||||
},
|
||||
{
|
||||
name: "WrappedError",
|
||||
inputError: fmt.Errorf("failed to read config: %w", os.ErrNotExist),
|
||||
wantCode: usefulerror.ErrCodeNotFound,
|
||||
wantCode: errcodes.NotFound,
|
||||
},
|
||||
{
|
||||
name: "UnknownError",
|
||||
inputError: errors.New("some unknown error"),
|
||||
wantCode: usefulerror.ErrCodeUnknown,
|
||||
wantHumanError: "some unknown error",
|
||||
name: "UnknownError",
|
||||
inputError: errors.New("some unknown error"),
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "UnknownWrappedError",
|
||||
inputError: fmt.Errorf("more context: %w",
|
||||
fmt.Errorf("outer context: %w",
|
||||
errors.New("root cause error"))),
|
||||
wantCode: usefulerror.ErrCodeUnknown,
|
||||
wantHumanError: "root cause error",
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "Nil",
|
||||
@@ -87,19 +86,20 @@ func Test_convertToUsefulError(t *testing.T) {
|
||||
{
|
||||
name: "NetworkErrorMessage",
|
||||
inputError: errors.New("connection refused"),
|
||||
wantCode: usefulerror.ErrCodeNetwork,
|
||||
wantCode: errcodes.Network,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := convertToUsefulError(tt.inputError)
|
||||
result, ok := usefulerror.AsUsefulError(tt.inputError)
|
||||
|
||||
if tt.wantNil {
|
||||
assert.Nil(t, result)
|
||||
assert.False(t, ok)
|
||||
return
|
||||
}
|
||||
|
||||
assert.True(t, ok)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, tt.wantCode, result.Code())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user