Recoverable and Non-Recoverable errors (#232)
GraphConnector exports 2 error types. Recoverable and NonRecoverable. The package also implements error checks to confirm if errors are one of the exported types.
This commit is contained in:
parent
60eb8eec08
commit
3ee7ff0c0b
@ -199,7 +199,7 @@ func (gc *GraphConnector) RestoreMessages(ctx context.Context, dc DataCollection
|
||||
sentMessage, err := gc.client.UsersById(user).MailFoldersById(address).Messages().Post(clone)
|
||||
if err != nil {
|
||||
errs = support.WrapAndAppend(data.UUID()+": "+
|
||||
support.ConnectorStackErrorTrace(ctx, err), err, errs)
|
||||
support.ConnectorStackErrorTrace(err), err, errs)
|
||||
continue
|
||||
// TODO: Add to retry Handler for the for failure
|
||||
}
|
||||
@ -275,7 +275,8 @@ func (gc *GraphConnector) serializeMessages(ctx context.Context, user string) ([
|
||||
}
|
||||
err = objectWriter.WriteObjectValue("", message)
|
||||
if err != nil {
|
||||
errs = support.WrapAndAppend(*message.GetId(), err, errs)
|
||||
errs = support.WrapAndAppend(*message.GetId(), support.SetNonRecoverableError(err),
|
||||
errs)
|
||||
return true
|
||||
}
|
||||
byteArray, err = objectWriter.GetSerializedContent()
|
||||
@ -321,3 +322,15 @@ func (gc *GraphConnector) Status() string {
|
||||
}
|
||||
return gc.status.String()
|
||||
}
|
||||
|
||||
// IsRecoverableError returns true iff error is a RecoverableGCEerror
|
||||
func IsRecoverableError(e error) bool {
|
||||
var recoverable *support.RecoverableGCError
|
||||
return errors.As(e, &recoverable)
|
||||
}
|
||||
|
||||
// IsNonRecoverableError returns true iff error is a NonRecoverableGCEerror
|
||||
func IsNonRecoverableError(e error) bool {
|
||||
var nonRecoverable *support.NonRecoverableGCError
|
||||
return errors.As(e, &nonRecoverable)
|
||||
}
|
||||
|
||||
@ -46,12 +46,12 @@ func (suite *GraphConnectorIntegrationSuite) TestGraphConnector() {
|
||||
suite.NotNil(suite.connector)
|
||||
}
|
||||
|
||||
type DiconnectedGraphConnectorSuite struct {
|
||||
type DisconnectedGraphConnectorSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func TestDisconnectedGraphSuite(t *testing.T) {
|
||||
suite.Run(t, new(DiconnectedGraphConnectorSuite))
|
||||
suite.Run(t, new(DisconnectedGraphConnectorSuite))
|
||||
}
|
||||
|
||||
func (suite *GraphConnectorIntegrationSuite) TestGraphConnector_setTenantUsers() {
|
||||
@ -92,7 +92,7 @@ func (suite *GraphConnectorIntegrationSuite) TestGraphConnector_restoreMessages(
|
||||
assert.NoError(suite.T(), err)
|
||||
}
|
||||
|
||||
func (suite *DiconnectedGraphConnectorSuite) TestBadConnection() {
|
||||
func (suite *DisconnectedGraphConnectorSuite) TestBadConnection() {
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
@ -144,7 +144,7 @@ func Contains(elems []string, value string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (suite *DiconnectedGraphConnectorSuite) TestBuild() {
|
||||
func (suite *DisconnectedGraphConnectorSuite) TestBuild() {
|
||||
names := make(map[string]string)
|
||||
names["Al"] = "Bundy"
|
||||
names["Ellen"] = "Ripley"
|
||||
@ -160,7 +160,7 @@ func (suite *DiconnectedGraphConnectorSuite) TestBuild() {
|
||||
|
||||
}
|
||||
|
||||
func (suite *DiconnectedGraphConnectorSuite) TestInterfaceAlignment() {
|
||||
func (suite *DisconnectedGraphConnectorSuite) TestInterfaceAlignment() {
|
||||
var dc DataCollection
|
||||
concrete := NewExchangeDataCollection("Check", []string{"interface", "works"})
|
||||
dc = &concrete
|
||||
@ -168,7 +168,7 @@ func (suite *DiconnectedGraphConnectorSuite) TestInterfaceAlignment() {
|
||||
|
||||
}
|
||||
|
||||
func (suite *DiconnectedGraphConnectorSuite) TestGraphConnector_Status() {
|
||||
func (suite *DisconnectedGraphConnectorSuite) TestGraphConnector_Status() {
|
||||
gc := GraphConnector{}
|
||||
suite.Equal(len(gc.Status()), 0)
|
||||
status, err := support.CreateStatus(support.Restore, 12, 9, 8,
|
||||
@ -177,3 +177,50 @@ func (suite *DiconnectedGraphConnectorSuite) TestGraphConnector_Status() {
|
||||
gc.SetStatus(*status)
|
||||
suite.Greater(len(gc.Status()), 0)
|
||||
}
|
||||
func (suite *DisconnectedGraphConnectorSuite) TestGraphConnector_ErrorChecking() {
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
returnRecoverable bool
|
||||
returnNonRecoverable bool
|
||||
}{
|
||||
{
|
||||
name: "Neither Option",
|
||||
err: errors.New("regular error"),
|
||||
returnRecoverable: false,
|
||||
returnNonRecoverable: false,
|
||||
},
|
||||
{
|
||||
name: "Validate Recoverable",
|
||||
err: support.SetRecoverableError(errors.New("Recoverable")),
|
||||
returnRecoverable: true,
|
||||
returnNonRecoverable: false,
|
||||
},
|
||||
{name: "Validate NonRecoverable",
|
||||
err: support.SetNonRecoverableError(errors.New("Non-recoverable")),
|
||||
returnRecoverable: false,
|
||||
returnNonRecoverable: true,
|
||||
},
|
||||
{
|
||||
name: "Wrapped Recoverable",
|
||||
err: support.SetRecoverableError(support.WrapAndAppend(
|
||||
"Wrapped Recoverable", errors.New("Recoverable"), nil)),
|
||||
returnRecoverable: true,
|
||||
returnNonRecoverable: false,
|
||||
},
|
||||
{
|
||||
name: "On Nil",
|
||||
err: nil,
|
||||
returnRecoverable: false,
|
||||
returnNonRecoverable: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
suite.T().Run(test.name, func(t *testing.T) {
|
||||
recoverable := IsRecoverableError(test.err)
|
||||
nonRecoverable := IsNonRecoverableError(test.err)
|
||||
suite.Equal(recoverable, test.returnRecoverable, "Expected: %v received %v", test.returnRecoverable, recoverable)
|
||||
suite.Equal(nonRecoverable, test.returnNonRecoverable)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package support
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -9,10 +8,35 @@ import (
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
msgraph_errors "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/alcionai/corso/pkg/logger"
|
||||
)
|
||||
|
||||
// GraphConnector has two types of errors that are exported
|
||||
// RecoverableGCError is a query error that can be overcome with time
|
||||
type RecoverableGCError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (rgc *RecoverableGCError) Error() string {
|
||||
return rgc.err.Error()
|
||||
}
|
||||
|
||||
func SetRecoverableError(e error) error {
|
||||
return &RecoverableGCError{err: e}
|
||||
}
|
||||
|
||||
// NonRecoverableGCError is a permanent query error
|
||||
type NonRecoverableGCError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (nrgc *NonRecoverableGCError) Error() string {
|
||||
return nrgc.err.Error()
|
||||
}
|
||||
|
||||
func SetNonRecoverableError(e error) error {
|
||||
return &NonRecoverableGCError{err: e}
|
||||
}
|
||||
|
||||
// WrapErrorAndAppend helper function used to attach identifying information to an error
|
||||
// and return it as a mulitierror
|
||||
func WrapAndAppend(identifier string, e error, previous error) error {
|
||||
@ -42,10 +66,10 @@ func GetNumberOfErrors(err error) int {
|
||||
// ListErrors is a helper method used to return the string of errors when
|
||||
// the multiError library is used.
|
||||
// depends on ConnectorStackErrorTrace
|
||||
func ListErrors(ctx context.Context, multi multierror.Error) string {
|
||||
func ListErrors(multi multierror.Error) string {
|
||||
aString := ""
|
||||
for idx, err := range multi.Errors {
|
||||
detail := ConnectorStackErrorTrace(ctx, err)
|
||||
detail := ConnectorStackErrorTrace(err)
|
||||
if detail == "" {
|
||||
detail = fmt.Sprintf("%v", err)
|
||||
}
|
||||
@ -67,7 +91,7 @@ func concatenateStringFromPointers(orig string, pointers []*string) string {
|
||||
|
||||
// ConnectorStackErrorTrace is a helper function that wraps the
|
||||
// stack trace for oDataError types from querying the M365 back store.
|
||||
func ConnectorStackErrorTrace(ctx context.Context, e error) string {
|
||||
func ConnectorStackErrorTrace(e error) string {
|
||||
eMessage := ""
|
||||
if oDataError, ok := e.(msgraph_errors.ODataErrorable); ok {
|
||||
// Get MainError
|
||||
@ -99,7 +123,6 @@ func ConnectorStackErrorTrace(ctx context.Context, e error) string {
|
||||
}
|
||||
}
|
||||
if inners != nil {
|
||||
logger.Ctx(ctx).Debug("error contains inner errors")
|
||||
eMessage = eMessage + "\nConnector Section:"
|
||||
client := inners.GetClientRequestId()
|
||||
rId := inners.GetRequestId()
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package support
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@ -27,8 +26,8 @@ func (suite *GraphConnectorErrorSuite) TestWrapAndAppend() {
|
||||
suite.True(strings.Contains(returnErr.Error(), "arc376"))
|
||||
suite.Error(returnErr)
|
||||
multi := &multierror.Error{Errors: []error{err1, err2}}
|
||||
suite.True(strings.Contains(ListErrors(context.Background(), *multi), "two")) // Does not contain the wrapped information
|
||||
suite.T().Log(ListErrors(context.Background(), *multi))
|
||||
suite.True(strings.Contains(ListErrors(*multi), "two")) // Does not contain the wrapped information
|
||||
suite.T().Log(ListErrors(*multi))
|
||||
}
|
||||
|
||||
func (suite *GraphConnectorErrorSuite) TestWrapAndAppend_OnVar() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user