Skip to content

Commit

Permalink
Marshal error with full context chain
Browse files Browse the repository at this point in the history
When using the new `Augment` function to add context and wrap errors,
the error message in Go is chained and displayed with the full chain
of context:

```
err := InternalService("code", "message", nil)
augmentedErr := Augment(err, "context", nil)

// This is "internal_service.code: context: message"
augmentedErr.Error()
```

But the marshaled error only displays the first level of context,
i.e.

```
// This is just "context"
Marshal(augmentedErr.(*Error)).Message
```

This commit changes the Marshal behaviour to include the full context
chain of error, so it's actually useful outside of Go when marshaled.

```
// This is now "context: message"
Marshal(augmentedErr.(*Error)).Message
```
  • Loading branch information
nickng committed Nov 29, 2022
1 parent c706d80 commit 2472e06
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 3 deletions.
24 changes: 21 additions & 3 deletions marshaling.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package terrors

import (
"strings"

pe "github.com/monzo/terrors/proto"
"github.com/monzo/terrors/stack"
)
Expand All @@ -15,22 +17,38 @@ func Marshal(e *Error) *pe.Error {
}
}

// Build message with all the context
errMessage := strings.Builder{}
errMessage.WriteString(e.Message)
next := e.cause
for next != nil {
errMessage.WriteString(": ")
switch typed := next.(type) {
case *Error:
errMessage.WriteString(typed.Message)
next = typed.cause
case error:
errMessage.WriteString(typed.Error())
next = nil
}
}

retryable := &pe.BoolValue{}
if e.IsRetryable != nil {
retryable.Value = *e.IsRetryable
}

err := &pe.Error{
err := pe.Error{
Code: e.Code,
Message: e.Message,
Message: errMessage.String(),
Stack: stackToProto(e.StackFrames),
Params: e.Params,
Retryable: retryable,
}
if err.Code == "" {
err.Code = ErrUnknown
}
return err
return &err
}

// Unmarshal a protobuf error into a local error
Expand Down
92 changes: 92 additions & 0 deletions marshaling_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package terrors

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -122,6 +123,61 @@ var marshalTestCases = []struct {
Retryable: nil,
},
},
// Wrapped errors
{
Augment(&Error{
Code: ErrInternalService,
Message: "bar",
}, "foo", nil).(*Error),
&pe.Error{
Code: ErrInternalService,
Message: "foo: bar",
Retryable: nil,
Params: map[string]string{},
},
},
{
Augment(&Error{
Code: ErrInternalService,
Message: "bar",
}, "foo", map[string]string{"key": "value"}).(*Error),
&pe.Error{
Code: ErrInternalService,
Message: "foo: bar",
Retryable: nil,
Params: map[string]string{"key": "value"},
},
},
{
// Nested Augment
Augment(
Augment(&Error{
Code: ErrInternalService,
Message: "baz",
},
"bar",
map[string]string{"key": "value"},
),
"foo",
map[string]string{"key2": "value2"},
).(*Error),
&pe.Error{
Code: ErrInternalService,
Message: "foo: bar: baz",
Retryable: nil,
Params: map[string]string{"key": "value", "key2": "value2"},
},
},
{
// Wrapping a Go error
Augment(fmt.Errorf("a go error"), "boom", map[string]string{"key": "value"}).(*Error),
&pe.Error{
Code: ErrInternalService,
Message: "boom: a go error",
Retryable: nil,
Params: map[string]string{"key": "value"},
},
},
}

func TestMarshal(t *testing.T) {
Expand Down Expand Up @@ -235,6 +291,42 @@ var unmarshalTestCases = []struct {
Retryable: nil,
},
},
// Wrapped errors only gets unmarshaled as a single error
{
&Error{
Code: ErrInternalService,
Message: "foo: bar: baz", // Augment(Augment(bazErr, "bar", nil), "foo", nil)
Params: map[string]string{},
},
&pe.Error{
Code: ErrInternalService,
Message: "foo: bar: baz",
Retryable: &pe.BoolValue{
Value: false,
},
},
},
{
&Error{
Code: ErrInternalService,
Message: "foo: bar",
Params: map[string]string{
"key": "value",
"key2": "value2",
},
},
&pe.Error{
Code: ErrInternalService,
Message: "foo: bar",
Retryable: &pe.BoolValue{
Value: false,
},
Params: map[string]string{
"key": "value",
"key2": "value2",
},
},
},
}

func TestUnmarshal(t *testing.T) {
Expand Down

0 comments on commit 2472e06

Please sign in to comment.