diff --git a/src/internal/m365/collection/exchange/backup.go b/src/internal/m365/collection/exchange/backup.go index 62e5ac9da..5999002ad 100644 --- a/src/internal/m365/collection/exchange/backup.go +++ b/src/internal/m365/collection/exchange/backup.go @@ -21,9 +21,6 @@ import ( "github.com/alcionai/corso/src/pkg/services/m365/api" ) -// CreateCollections - utility function that retrieves M365 -// IDs through Microsoft Graph API. The selectors.ExchangeScope -// determines the type of collections that are retrieved. func CreateCollections( ctx context.Context, bpc inject.BackupProducerConfig, diff --git a/src/internal/m365/collection/exchange/backup_test.go b/src/internal/m365/collection/exchange/backup_test.go index bb6aad27c..6e661983a 100644 --- a/src/internal/m365/collection/exchange/backup_test.go +++ b/src/internal/m365/collection/exchange/backup_test.go @@ -796,53 +796,25 @@ func (suite *BackupIntgSuite) TestContactSerializationRegression() { // TestEventsSerializationRegression ensures functionality of createCollections // to be able to successfully query, download and restore event objects func (suite *BackupIntgSuite) TestEventsSerializationRegression() { - t := suite.T() - - ctx, flush := tester.NewContext(t) - defer flush() - var ( users = []string{suite.user} handlers = BackupHandlers(suite.ac) - calID string - bdayID string ) - fn := func(gcc graph.CachedContainer) error { - if ptr.Val(gcc.GetDisplayName()) == api.DefaultCalendar { - calID = ptr.Val(gcc.GetId()) - } - - if ptr.Val(gcc.GetDisplayName()) == "Birthdays" { - bdayID = ptr.Val(gcc.GetId()) - } - - return nil - } - - err := suite.ac.Events().EnumerateContainers( - ctx, - suite.user, - "", - false, - fn, - fault.New(true)) - require.NoError(t, err, clues.ToCore(err)) - tests := []struct { - name, expected string - scope selectors.ExchangeScope + name, expectedContainerName string + scope selectors.ExchangeScope }{ { - name: "Default Event Calendar", - expected: calID, + name: "Default Event Calendar", + expectedContainerName: api.DefaultCalendar, scope: selectors.NewExchangeBackup(users).EventCalendars( []string{api.DefaultCalendar}, selectors.PrefixMatch())[0], }, { - name: "Birthday Calendar", - expected: bdayID, + name: "Birthday Calendar", + expectedContainerName: "Birthdays", scope: selectors.NewExchangeBackup(users).EventCalendars( []string{"Birthdays"}, selectors.PrefixMatch())[0], @@ -879,13 +851,16 @@ func (suite *BackupIntgSuite) TestEventsSerializationRegression() { wg.Add(len(collections)) for _, edc := range collections { + dlp, isDLP := edc.(data.LocationPather) + var isMetadata bool - if edc.FullPath().Service() != path.ExchangeMetadataService { - isMetadata = true - assert.Equal(t, test.expected, edc.FullPath().Folder(false)) + if edc.FullPath().Service() == path.ExchangeService { + require.True(t, isDLP, "must be a location pather") + assert.Contains(t, dlp.LocationPath().Elements(), test.expectedContainerName) } else { - assert.Equal(t, "", edc.FullPath().Folder(false)) + isMetadata = true + assert.Empty(t, edc.FullPath().Folder(false)) } for item := range edc.Items(ctx, fault.New(true)) { diff --git a/src/internal/m365/collection/exchange/contacts_container_cache.go b/src/internal/m365/collection/exchange/contacts_container_cache.go index 758271a71..39fbcf04e 100644 --- a/src/internal/m365/collection/exchange/contacts_container_cache.go +++ b/src/internal/m365/collection/exchange/contacts_container_cache.go @@ -4,6 +4,7 @@ import ( "context" "github.com/alcionai/clues" + "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/m365/graph" @@ -37,11 +38,30 @@ func (r *contactRefresher) refreshContainer( type contactContainerCache struct { *containerResolver - enumer containersEnumerator + enumer containersEnumerator[models.ContactFolderable] getter containerGetter userID string } +func (cfc *contactContainerCache) init( + ctx context.Context, + baseNode string, + baseContainerPath []string, +) error { + if len(baseNode) == 0 { + return clues.New("m365 folderID required for base contact folder").WithClues(ctx) + } + + if cfc.containerResolver == nil { + cfc.containerResolver = newContainerResolver(&contactRefresher{ + userID: cfc.userID, + getter: cfc.getter, + }) + } + + return cfc.populateContactRoot(ctx, baseNode, baseContainerPath) +} + func (cfc *contactContainerCache) populateContactRoot( ctx context.Context, directoryID string, @@ -77,39 +97,35 @@ func (cfc *contactContainerCache) Populate( return clues.Wrap(err, "initializing") } - err := cfc.enumer.EnumerateContainers( + el := errs.Local() + + containers, err := cfc.enumer.EnumerateContainers( ctx, cfc.userID, baseID, - false, - cfc.addFolder, - errs) + false) if err != nil { return clues.Wrap(err, "enumerating containers") } + for _, c := range containers { + if el.Failure() != nil { + return el.Failure() + } + + cacheFolder := graph.NewCacheFolder(c, nil, nil) + + err := cfc.addFolder(&cacheFolder) + if err != nil { + errs.AddRecoverable( + ctx, + graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation)) + } + } + if err := cfc.populatePaths(ctx, errs); err != nil { return clues.Wrap(err, "populating paths") } - return nil -} - -func (cfc *contactContainerCache) init( - ctx context.Context, - baseNode string, - baseContainerPath []string, -) error { - if len(baseNode) == 0 { - return clues.New("m365 folderID required for base contact folder").WithClues(ctx) - } - - if cfc.containerResolver == nil { - cfc.containerResolver = newContainerResolver(&contactRefresher{ - userID: cfc.userID, - getter: cfc.getter, - }) - } - - return cfc.populateContactRoot(ctx, baseNode, baseContainerPath) + return el.Failure() } diff --git a/src/internal/m365/collection/exchange/container_resolver.go b/src/internal/m365/collection/exchange/container_resolver.go index 6f068f91b..64ae691bf 100644 --- a/src/internal/m365/collection/exchange/container_resolver.go +++ b/src/internal/m365/collection/exchange/container_resolver.go @@ -23,14 +23,12 @@ type containerGetter interface { ) (graph.Container, error) } -type containersEnumerator interface { +type containersEnumerator[T any] interface { EnumerateContainers( ctx context.Context, userID, baseDirID string, immutableIDs bool, - fn func(graph.CachedContainer) error, - errs *fault.Bus, - ) error + ) ([]T, error) } type containerRefresher interface { diff --git a/src/internal/m365/collection/exchange/events_container_cache.go b/src/internal/m365/collection/exchange/events_container_cache.go index 0bcb65170..926c9c3e8 100644 --- a/src/internal/m365/collection/exchange/events_container_cache.go +++ b/src/internal/m365/collection/exchange/events_container_cache.go @@ -4,6 +4,7 @@ import ( "context" "github.com/alcionai/clues" + "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/m365/graph" @@ -16,7 +17,7 @@ var _ graph.ContainerResolver = &eventContainerCache{} type eventContainerCache struct { *containerResolver - enumer containersEnumerator + enumer containersEnumerator[models.Calendarable] getter containerGetter userID string } @@ -70,22 +71,40 @@ func (ecc *eventContainerCache) Populate( return clues.Wrap(err, "initializing") } - err := ecc.enumer.EnumerateContainers( + el := errs.Local() + + containers, err := ecc.enumer.EnumerateContainers( ctx, ecc.userID, "", - false, - ecc.addFolder, - errs) + false) if err != nil { return clues.Wrap(err, "enumerating containers") } - if err := ecc.populatePaths(ctx, errs); err != nil { - return clues.Wrap(err, "establishing calendar paths") + for _, c := range containers { + if el.Failure() != nil { + return el.Failure() + } + + cacheFolder := graph.NewCacheFolder( + api.CalendarDisplayable{Calendarable: c}, + path.Builder{}.Append(ptr.Val(c.GetId())), + path.Builder{}.Append(ptr.Val(c.GetName()))) + + err := ecc.addFolder(&cacheFolder) + if err != nil { + errs.AddRecoverable( + ctx, + graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation)) + } } - return nil + if err := ecc.populatePaths(ctx, errs); err != nil { + return clues.Wrap(err, "populating paths") + } + + return el.Failure() } // AddToCache adds container to map in field 'cache' diff --git a/src/internal/m365/collection/exchange/mail_container_cache.go b/src/internal/m365/collection/exchange/mail_container_cache.go index f06068be4..81d977c74 100644 --- a/src/internal/m365/collection/exchange/mail_container_cache.go +++ b/src/internal/m365/collection/exchange/mail_container_cache.go @@ -4,6 +4,7 @@ import ( "context" "github.com/alcionai/clues" + "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/alcionai/corso/src/internal/m365/graph" "github.com/alcionai/corso/src/pkg/fault" @@ -40,7 +41,7 @@ func (r *mailRefresher) refreshContainer( // nameLookup map: Key: DisplayName Value: ID type mailContainerCache struct { *containerResolver - enumer containersEnumerator + enumer containersEnumerator[models.MailFolderable] getter containerGetter userID string } @@ -100,20 +101,35 @@ func (mc *mailContainerCache) Populate( return clues.Wrap(err, "initializing") } - err := mc.enumer.EnumerateContainers( + el := errs.Local() + + containers, err := mc.enumer.EnumerateContainers( ctx, mc.userID, "", - false, - mc.addFolder, - errs) + false) if err != nil { return clues.Wrap(err, "enumerating containers") } + for _, c := range containers { + if el.Failure() != nil { + return el.Failure() + } + + cacheFolder := graph.NewCacheFolder(c, nil, nil) + + err := mc.addFolder(&cacheFolder) + if err != nil { + errs.AddRecoverable( + ctx, + graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation)) + } + } + if err := mc.populatePaths(ctx, errs); err != nil { return clues.Wrap(err, "populating paths") } - return nil + return el.Failure() } diff --git a/src/pkg/services/m365/api/contacts_pager.go b/src/pkg/services/m365/api/contacts_pager.go index 068e7406c..688650f99 100644 --- a/src/pkg/services/m365/api/contacts_pager.go +++ b/src/pkg/services/m365/api/contacts_pager.go @@ -10,7 +10,6 @@ import ( "github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/m365/graph" - "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/path" ) @@ -67,42 +66,14 @@ func (p *contactsFoldersPageCtrl) ValidModTimes() bool { return true } -// EnumerateContainers iterates through all of the users current -// contacts folders, transforming each to a graph.CacheFolder, and calling -// fn(cf). -// Contact folders are represented in their current state, and do -// not contain historical data. +// EnumerateContainers retrieves all of the user's current contact folders. func (c Contacts) EnumerateContainers( ctx context.Context, userID, baseContainerID string, immutableIDs bool, - fn func(graph.CachedContainer) error, - errs *fault.Bus, -) error { - var ( - el = errs.Local() - pgr = c.NewContactFoldersPager(userID, baseContainerID, immutableIDs) - ) - - containers, err := batchEnumerateItems(ctx, pgr) - if err != nil { - return graph.Stack(ctx, err) - } - - for _, c := range containers { - if el.Failure() != nil { - break - } - - gncf := graph.NewCacheFolder(c, nil, nil) - - if err := fn(&gncf); err != nil { - errs.AddRecoverable(ctx, graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation)) - continue - } - } - - return el.Failure() +) ([]models.ContactFolderable, error) { + containers, err := batchEnumerateItems(ctx, c.NewContactFoldersPager(userID, baseContainerID, immutableIDs)) + return containers, graph.Stack(ctx, err).OrNil() } // --------------------------------------------------------------------------- diff --git a/src/pkg/services/m365/api/events_pager.go b/src/pkg/services/m365/api/events_pager.go index 05474367f..624e5fad6 100644 --- a/src/pkg/services/m365/api/events_pager.go +++ b/src/pkg/services/m365/api/events_pager.go @@ -11,7 +11,6 @@ import ( "github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/m365/graph" - "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/path" ) @@ -68,45 +67,14 @@ func (p *eventsCalendarsPageCtrl) ValidModTimes() bool { return true } -// EnumerateContainers iterates through all of the users current -// events calendars, transforming each to a graph.CacheFolder, and calling -// fn(cf). -// Calendars are represented in their current state, and do -// not contain historical data. +// EnumerateContainers retrieves all of the user's current mail folders. func (c Events) EnumerateContainers( ctx context.Context, - userID, _ string, // baseContainerID not needed + userID, _ string, // baseContainerID not needed here immutableIDs bool, - fn func(graph.CachedContainer) error, - errs *fault.Bus, -) error { - var ( - el = errs.Local() - pgr = c.NewEventCalendarsPager(userID, immutableIDs) - ) - - containers, err := batchEnumerateItems(ctx, pgr) - if err != nil { - return graph.Stack(ctx, err) - } - - for _, c := range containers { - if el.Failure() != nil { - break - } - - gncf := graph.NewCacheFolder( - CalendarDisplayable{Calendarable: c}, - path.Builder{}.Append(ptr.Val(c.GetId())), - path.Builder{}.Append(ptr.Val(c.GetName()))) - - if err := fn(&gncf); err != nil { - errs.AddRecoverable(ctx, graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation)) - continue - } - } - - return el.Failure() +) ([]models.Calendarable, error) { + containers, err := batchEnumerateItems(ctx, c.NewEventCalendarsPager(userID, immutableIDs)) + return containers, graph.Stack(ctx, err).OrNil() } // --------------------------------------------------------------------------- diff --git a/src/pkg/services/m365/api/events_test.go b/src/pkg/services/m365/api/events_test.go index f52f999e5..d55d9538f 100644 --- a/src/pkg/services/m365/api/events_test.go +++ b/src/pkg/services/m365/api/events_test.go @@ -13,13 +13,11 @@ import ( "github.com/alcionai/corso/src/internal/common/dttm" "github.com/alcionai/corso/src/internal/common/ptr" - "github.com/alcionai/corso/src/internal/m365/graph" exchMock "github.com/alcionai/corso/src/internal/m365/service/exchange/mock" "github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester/tconfig" "github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/control/testdata" - "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/services/m365/api" ) @@ -294,8 +292,8 @@ func (suite *EventsAPIIntgSuite) TestEvents_canFindNonStandardFolder() { var ( found bool calID = ptr.Val(cal.GetId()) - findContainer = func(gcc graph.CachedContainer) error { - if ptr.Val(gcc.GetId()) == calID { + findContainer = func(mc models.Calendarable) error { + if ptr.Val(mc.GetId()) == calID { found = true } @@ -303,14 +301,18 @@ func (suite *EventsAPIIntgSuite) TestEvents_canFindNonStandardFolder() { } ) - err = ac.EnumerateContainers( + containers, err := ac.EnumerateContainers( ctx, suite.its.user.id, api.DefaultCalendar, - false, - findContainer, - fault.New(true)) + false) require.NoError(t, err, clues.ToCore(err)) + + for _, c := range containers { + err := findContainer(c) + require.NoError(t, err, clues.ToCore(err)) + } + require.True( t, found, diff --git a/src/pkg/services/m365/api/mail_pager.go b/src/pkg/services/m365/api/mail_pager.go index 7882de7cd..7bfa2b516 100644 --- a/src/pkg/services/m365/api/mail_pager.go +++ b/src/pkg/services/m365/api/mail_pager.go @@ -11,7 +11,6 @@ import ( "github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/m365/graph" - "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/path" ) @@ -64,42 +63,14 @@ func (p *mailFoldersPageCtrl) ValidModTimes() bool { return true } -// EnumerateContainers iterates through all of the users current -// mail folders, transforming each to a graph.CacheFolder, and calling -// fn(cf). -// Folder hierarchy is represented in its current state, and does -// not contain historical data. +// EnumerateContainers retrieves all of the user's current mail folders. func (c Mail) EnumerateContainers( ctx context.Context, userID, _ string, // baseContainerID not needed here immutableIDs bool, - fn func(graph.CachedContainer) error, - errs *fault.Bus, -) error { - var ( - el = errs.Local() - pgr = c.NewMailFoldersPager(userID, immutableIDs) - ) - - containers, err := batchEnumerateItems(ctx, pgr) - if err != nil { - return graph.Stack(ctx, err) - } - - for _, c := range containers { - if el.Failure() != nil { - break - } - - gncf := graph.NewCacheFolder(c, nil, nil) - - if err := fn(&gncf); err != nil { - errs.AddRecoverable(ctx, graph.Stack(ctx, err).Label(fault.LabelForceNoBackupCreation)) - continue - } - } - - return el.Failure() +) ([]models.MailFolderable, error) { + containers, err := batchEnumerateItems(ctx, c.NewMailFoldersPager(userID, immutableIDs)) + return containers, graph.Stack(ctx, err).OrNil() } // ---------------------------------------------------------------------------