diff --git a/src/internal/m365/collection/exchange/backup_test.go b/src/internal/m365/collection/exchange/backup_test.go index 9cc953c24..6b08a2758 100644 --- a/src/internal/m365/collection/exchange/backup_test.go +++ b/src/internal/m365/collection/exchange/backup_test.go @@ -88,6 +88,15 @@ func (bh mockBackupHandler) folderGetter() containerGetter { return func (bh mockBackupHandler) previewIncludeContainers() []string { return bh.previewIncludes } func (bh mockBackupHandler) previewExcludeContainers() []string { return bh.previewExcludes } +func (bh mockBackupHandler) CanSkipItemFailure( + error, + string, + string, + control.Options, +) (fault.SkipCause, bool) { + return "", false +} + func (bh mockBackupHandler) NewContainerCache( userID string, ) (string, graph.ContainerResolver) { diff --git a/src/internal/m365/collection/exchange/contacts_backup.go b/src/internal/m365/collection/exchange/contacts_backup.go index 64a26f367..b568ba2b8 100644 --- a/src/internal/m365/collection/exchange/contacts_backup.go +++ b/src/internal/m365/collection/exchange/contacts_backup.go @@ -1,6 +1,8 @@ package exchange import ( + "github.com/alcionai/corso/src/pkg/control" + "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api/graph" ) @@ -52,3 +54,11 @@ func (h contactBackupHandler) NewContainerCache( getter: h.ac, } } + +func (h contactBackupHandler) CanSkipItemFailure( + error, + string, string, + control.Options, +) (fault.SkipCause, bool) { + return "", false +} diff --git a/src/internal/m365/collection/exchange/contacts_backup_test.go b/src/internal/m365/collection/exchange/contacts_backup_test.go new file mode 100644 index 000000000..851177f19 --- /dev/null +++ b/src/internal/m365/collection/exchange/contacts_backup_test.go @@ -0,0 +1,31 @@ +package exchange + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/pkg/control" + "github.com/alcionai/corso/src/pkg/fault" + "github.com/alcionai/corso/src/pkg/services/m365/api" +) + +type ContactsBackupHandlerUnitSuite struct { + tester.Suite +} + +func TestContactsBackupHandlerUnitSuite(t *testing.T) { + suite.Run(t, &ContactsBackupHandlerUnitSuite{Suite: tester.NewUnitSuite(t)}) +} + +func (suite *ContactsBackupHandlerUnitSuite) TestHandler_CanSkipItemFailure() { + t := suite.T() + + h := newContactBackupHandler(api.Client{}) + cause, result := h.CanSkipItemFailure(nil, "", "", control.Options{}) + + assert.False(t, result) + assert.Equal(t, fault.SkipCause(""), cause) +} diff --git a/src/internal/m365/collection/exchange/events_backup.go b/src/internal/m365/collection/exchange/events_backup.go index 86773f7b7..8dafd2826 100644 --- a/src/internal/m365/collection/exchange/events_backup.go +++ b/src/internal/m365/collection/exchange/events_backup.go @@ -1,6 +1,12 @@ package exchange import ( + "errors" + "slices" + + "github.com/alcionai/clues" + "github.com/alcionai/corso/src/pkg/control" + "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api/graph" ) @@ -52,3 +58,22 @@ func (h eventBackupHandler) NewContainerCache( getter: h.ac, } } + +func (h eventBackupHandler) CanSkipItemFailure( + err error, + resourceID, itemID string, + opts control.Options, +) (fault.SkipCause, bool) { + // yes, this is intentionally a todo. I'll get back to it. + if !errors.Is(err, clues.New("todo fix me")) { + return "", false + } + + itemIDs, ok := opts.SkipTheseEventsOnInstance503[resourceID] + if !ok { + return "", false + } + + // strict equals required here. ids are case sensitive. + return fault.SkipKnownEventInstance503s, slices.Contains(itemIDs, itemID) +} diff --git a/src/internal/m365/collection/exchange/events_backup_test.go b/src/internal/m365/collection/exchange/events_backup_test.go new file mode 100644 index 000000000..bc8fb7377 --- /dev/null +++ b/src/internal/m365/collection/exchange/events_backup_test.go @@ -0,0 +1,109 @@ +package exchange + +import ( + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/clues" + "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/pkg/control" + "github.com/alcionai/corso/src/pkg/fault" + "github.com/alcionai/corso/src/pkg/services/m365/api" +) + +type EventsBackupHandlerUnitSuite struct { + tester.Suite +} + +func TestEventsBackupHandlerUnitSuite(t *testing.T) { + suite.Run(t, &EventsBackupHandlerUnitSuite{Suite: tester.NewUnitSuite(t)}) +} + +func (suite *EventsBackupHandlerUnitSuite) TestHandler_CanSkipItemFailure() { + var ( + resourceID = uuid.NewString() + itemID = uuid.NewString() + ) + + table := []struct { + name string + err error + opts control.Options + expect assert.BoolAssertionFunc + expectCause fault.SkipCause + }{ + { + name: "no config", + err: nil, + opts: control.Options{}, + expect: assert.False, + }, + { + name: "empty skip on 503", + err: nil, + opts: control.Options{ + SkipTheseEventsOnInstance503: map[string][]string{}, + }, + expect: assert.False, + }, + { + name: "nil error", + err: nil, + opts: control.Options{ + SkipTheseEventsOnInstance503: map[string][]string{ + "foo": []string{"bar", "baz"}, + }, + }, + expect: assert.False, + }, + { + name: "non-matching resource", + err: clues.New("fix me I'm wrong"), + opts: control.Options{ + SkipTheseEventsOnInstance503: map[string][]string{ + "foo": []string{"bar", "baz"}, + }, + }, + expect: assert.False, + }, + { + name: "non-matching item", + err: clues.New("fix me I'm wrong"), + opts: control.Options{ + SkipTheseEventsOnInstance503: map[string][]string{ + resourceID: []string{"bar", "baz"}, + }, + }, + expect: assert.False, + }, + { + name: "match on instance 503", + err: clues.New("fix me I'm wrong"), + opts: control.Options{ + SkipTheseEventsOnInstance503: map[string][]string{ + resourceID: []string{"bar", itemID}, + }, + }, + expect: assert.True, + expectCause: fault.SkipKnownEventInstance503s, + }, + } + for _, test := range table { + suite.Run(test.name, func() { + t := suite.T() + + h := newEventBackupHandler(api.Client{}) + cause, result := h.CanSkipItemFailure( + test.err, + resourceID, + itemID, + test.opts) + + test.expect(t, result) + assert.Equal(t, test.expectCause, cause) + }) + } +} diff --git a/src/internal/m365/collection/exchange/handlers.go b/src/internal/m365/collection/exchange/handlers.go index 2da773a3d..e8bed86fc 100644 --- a/src/internal/m365/collection/exchange/handlers.go +++ b/src/internal/m365/collection/exchange/handlers.go @@ -26,6 +26,8 @@ type backupHandler interface { previewIncludeContainers() []string previewExcludeContainers() []string NewContainerCache(userID string) (string, graph.ContainerResolver) + + canSkipItemFailurer } type addedAndRemovedItemGetter interface { @@ -57,6 +59,14 @@ func BackupHandlers(ac api.Client) map[path.CategoryType]backupHandler { } } +type canSkipItemFailurer interface { + CanSkipItemFailure( + err error, + resourceID, itemID string, + opts control.Options, + ) (fault.SkipCause, bool) +} + // --------------------------------------------------------------------------- // restore // --------------------------------------------------------------------------- diff --git a/src/internal/m365/collection/exchange/mail_backup.go b/src/internal/m365/collection/exchange/mail_backup.go index 1bcc70ce5..662615253 100644 --- a/src/internal/m365/collection/exchange/mail_backup.go +++ b/src/internal/m365/collection/exchange/mail_backup.go @@ -1,6 +1,8 @@ package exchange import ( + "github.com/alcionai/corso/src/pkg/control" + "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api/graph" ) @@ -57,3 +59,11 @@ func (h mailBackupHandler) NewContainerCache( getter: h.ac, } } + +func (h mailBackupHandler) CanSkipItemFailure( + error, + string, string, + control.Options, +) (fault.SkipCause, bool) { + return "", false +} diff --git a/src/internal/m365/collection/exchange/mail_backup_test.go b/src/internal/m365/collection/exchange/mail_backup_test.go new file mode 100644 index 000000000..552c3aba9 --- /dev/null +++ b/src/internal/m365/collection/exchange/mail_backup_test.go @@ -0,0 +1,31 @@ +package exchange + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/pkg/control" + "github.com/alcionai/corso/src/pkg/fault" + "github.com/alcionai/corso/src/pkg/services/m365/api" +) + +type MailBackupHandlerUnitSuite struct { + tester.Suite +} + +func TestMailBackupHandlerUnitSuite(t *testing.T) { + suite.Run(t, &MailBackupHandlerUnitSuite{Suite: tester.NewUnitSuite(t)}) +} + +func (suite *MailBackupHandlerUnitSuite) TestHandler_CanSkipItemFailure() { + t := suite.T() + + h := newMailBackupHandler(api.Client{}) + cause, result := h.CanSkipItemFailure(nil, "", "", control.Options{}) + + assert.False(t, result) + assert.Equal(t, fault.SkipCause(""), cause) +}