diff --git a/src/internal/connector/graph_connector.go b/src/internal/connector/graph_connector.go index d8830bce9..7f77d977e 100644 --- a/src/internal/connector/graph_connector.go +++ b/src/internal/connector/graph_connector.go @@ -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) +} diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index 9c59138f7..06a0f6549 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -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) + }) + } +} diff --git a/src/internal/connector/support/errors.go b/src/internal/connector/support/errors.go index 26db516a9..41aa69edd 100644 --- a/src/internal/connector/support/errors.go +++ b/src/internal/connector/support/errors.go @@ -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() diff --git a/src/internal/connector/support/errors_test.go b/src/internal/connector/support/errors_test.go index 45a5dbeca..4d9cfc03e 100644 --- a/src/internal/connector/support/errors_test.go +++ b/src/internal/connector/support/errors_test.go @@ -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() {