Skip to content

Commit

Permalink
Implement better error handling (kyma-project#2013)
Browse files Browse the repository at this point in the history
* Implement better error handling

* test

* Simplify struct name

* make test a bit mor readable

* fix formatting issues

* info

* remove pleonasm
  • Loading branch information
halamix2 authored and pPrecel committed May 21, 2024
1 parent 130acf0 commit 5c6a9c5
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 2 deletions.
50 changes: 50 additions & 0 deletions internal/clierror/cli_error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package clierror

import (
"fmt"
"os"
)

type Error struct {
Message string
Details string
Hints []string
}

// Wrap adds a new message and hints to the error
func (e *Error) Wrap(message string, hints []string) *Error {
newError := &Error{
Message: message,
Details: e.Message,
Hints: append(hints, e.Hints...),
}

if e.Details != "" {
newError.Details = fmt.Sprintf("%s: %s", e.Message, e.Details)
}

return newError
}

func (e *Error) Print() {
fmt.Printf("%s\n", e.Error())
}

func (e *Error) PrintStderr() {
fmt.Fprintf(os.Stderr, "%s\n", e.Error())
}

// Error returns the error string, compatible with the error interface
func (e Error) Error() string {
output := fmt.Sprintf("Error:\n %s\n\n", e.Message)
if e.Details != "" {
output += fmt.Sprintf("Error Details:\n %s\n\n", e.Details)
}
if len(e.Hints) > 0 {
output += "Hints:\n"
for _, hint := range e.Hints {
output += fmt.Sprintf(" - %s\n", hint)
}
}
return output
}
111 changes: 111 additions & 0 deletions internal/clierror/cli_error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package clierror

import (
"testing"

"github.com/stretchr/testify/assert"
)

func Test_CLIError_String(t *testing.T) {
tests := []struct {
name string
err Error
want string
}{
{
name: "empty",
err: Error{},
want: "Error:\n \n\n",
},
{
name: "error",
err: Error{
Message: "error",
},
want: "Error:\n error\n\n",
},
{
name: "error and details",
err: Error{
Message: "error",
Details: "details",
},
want: "Error:\n error\n\nError Details:\n details\n\n",
},
{
name: "error, details and hints",
err: Error{
Message: "error",
Details: "details",
Hints: []string{"hint1", "hint2"},
},
want: "Error:\n error\n\nError Details:\n details\n\nHints:\n - hint1\n - hint2\n",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, tt.err.Error())
})
}
}

// test Wrap function
func Test_CLIError_Wrap(t *testing.T) {
tests := []struct {
name string
err Error
message string
hints []string
want string
}{
{
name: "Add to empty error",
err: Error{},
message: "error",
want: "Error:\n error\n\n",
},
{
name: "Add with hints to empty error",
err: Error{},
message: "error",
hints: []string{"hint1", "hint2"},
want: "Error:\n error\n\nHints:\n - hint1\n - hint2\n",
},
{
name: "add to error",
err: Error{
Message: "error",
},
message: "error",
hints: []string{"hint1", "hint2"},
want: "Error:\n error\n\nError Details:\n error\n\nHints:\n - hint1\n - hint2\n",
},
{
name: "add to error with details",
err: Error{
Message: "previous",
Details: "details",
},
message: "error",
hints: []string{"hint1", "hint2"},
want: "Error:\n error\n\nError Details:\n previous: details\n\nHints:\n - hint1\n - hint2\n",
},
{
name: "add to error with details and hints",
err: Error{
Message: "previous",
Details: "details",
Hints: []string{"hint1", "hint2"},
},
message: "error",
hints: []string{"hint3", "hint4"},
want: "Error:\n error\n\nError Details:\n previous: details\n\nHints:\n - hint3\n - hint4\n - hint1\n - hint2\n",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.err.Wrap(tt.message, tt.hints)
assert.Equal(t, tt.want, err.Error())
})
}
}
3 changes: 2 additions & 1 deletion internal/cmd/kyma.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ func NewKymaCMD() *cobra.Command {
Use: "kyma",

// Affects children as well
SilenceErrors: false,
// by default Cobra adds `Error:` to the front of the error message, we want to supress it
SilenceErrors: true,
SilenceUsage: true,
Run: func(cmd *cobra.Command, _ []string) {
if err := cmd.Help(); err != nil {
Expand Down
7 changes: 6 additions & 1 deletion internal/cmd/provision/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/kyma-project/cli.v3/internal/btp/auth"
"github.com/kyma-project/cli.v3/internal/btp/cis"
"github.com/kyma-project/cli.v3/internal/clierror"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -46,7 +47,11 @@ func runProvision(config *provisionConfig) error {
// TODO: is the credentials a good name for this field? it contains much more than credentials only
credentials, err := auth.LoadCISCredentials(config.credentialsPath)
if err != nil {
return fmt.Errorf("failed to load credentials from '%s' file: %s", config.credentialsPath, err.Error())
return clierror.Error{
Message: "failed to load credentials",
Details: err.Error(),
Hints: []string{"Make sure the path to the credentials file is correct."},
}
}

token, err := auth.GetOAuthToken(
Expand Down
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"fmt"
"os"

"github.com/kyma-project/cli.v3/internal/cmd"
Expand All @@ -10,6 +11,7 @@ func main() {
cmd := cmd.NewKymaCMD()

if err := cmd.Execute(); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
}

0 comments on commit 5c6a9c5

Please sign in to comment.