From 0d0a7516f078e950fa2fb286daffdc95c6a5ead3 Mon Sep 17 00:00:00 2001 From: Keepers Date: Wed, 4 Jan 2023 12:39:25 -0700 Subject: [PATCH] use selector owners, not scope owners (#1890) ## Description Migrates code away from pulling the resource owner from each scope, and instead usees the selector as the canon identifier of the resource owner. ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :sunflower: Feature ## Issue(s) * #1617 ## Test Plan - [x] :zap: Unit test - [x] :green_heart: E2E --- src/cli/backup/exchange.go | 47 +++--- src/cli/backup/onedrive.go | 47 +++--- src/cli/backup/sharepoint.go | 47 +++--- src/internal/connector/data_collections.go | 35 +++-- .../connector/data_collections_test.go | 20 +-- .../connector/exchange/data_collections.go | 68 +++++---- .../exchange/data_collections_test.go | 8 +- .../connector/graph_connector_helper_test.go | 46 ++++-- .../connector/graph_connector_test.go | 136 ++---------------- .../connector/sharepoint/data_collections.go | 75 +++++----- src/internal/operations/backup.go | 24 ++-- .../operations/backup_integration_test.go | 11 +- src/internal/operations/backup_test.go | 2 +- src/internal/operations/restore_test.go | 4 +- src/pkg/repository/repository_test.go | 4 +- src/pkg/selectors/selectors.go | 6 + 16 files changed, 242 insertions(+), 338 deletions(-) diff --git a/src/cli/backup/exchange.go b/src/cli/backup/exchange.go index 711e6ca1d..cea7b684b 100644 --- a/src/cli/backup/exchange.go +++ b/src/cli/backup/exchange.go @@ -282,33 +282,30 @@ func createExchangeCmd(cmd *cobra.Command, args []string) error { bIDs []model.StableID ) - for _, sel := range sel.SplitByResourceOwner(users) { - // TODO: pass in entire selector, not individual scopes - for _, scope := range sel.Scopes() { - bo, err := r.NewBackup(ctx, sel.Selector) - if err != nil { - errs = multierror.Append(errs, errors.Wrapf( - err, - "Failed to initialize Exchange backup for user %s", - scope.Get(selectors.ExchangeUser), - )) + for _, discSel := range sel.SplitByResourceOwner(users) { + bo, err := r.NewBackup(ctx, discSel.Selector) + if err != nil { + errs = multierror.Append(errs, errors.Wrapf( + err, + "Failed to initialize Exchange backup for user %s", + discSel.DiscreteOwner, + )) - continue - } - - err = bo.Run(ctx) - if err != nil { - errs = multierror.Append(errs, errors.Wrapf( - err, - "Failed to run Exchange backup for user %s", - scope.Get(selectors.ExchangeUser), - )) - - continue - } - - bIDs = append(bIDs, bo.Results.BackupID) + continue } + + err = bo.Run(ctx) + if err != nil { + errs = multierror.Append(errs, errors.Wrapf( + err, + "Failed to run Exchange backup for user %s", + discSel.DiscreteOwner, + )) + + continue + } + + bIDs = append(bIDs, bo.Results.BackupID) } bups, err := r.Backups(ctx, bIDs) diff --git a/src/cli/backup/onedrive.go b/src/cli/backup/onedrive.go index 66e9d7c5b..a7b13f783 100644 --- a/src/cli/backup/onedrive.go +++ b/src/cli/backup/onedrive.go @@ -204,33 +204,30 @@ func createOneDriveCmd(cmd *cobra.Command, args []string) error { bIDs []model.StableID ) - for _, sel := range sel.SplitByResourceOwner(users) { - // TODO: pass in entire selector, not individual scopes - for _, scope := range sel.Scopes() { - bo, err := r.NewBackup(ctx, sel.Selector) - if err != nil { - errs = multierror.Append(errs, errors.Wrapf( - err, - "Failed to initialize OneDrive backup for user %s", - scope.Get(selectors.OneDriveUser), - )) + for _, discSel := range sel.SplitByResourceOwner(users) { + bo, err := r.NewBackup(ctx, discSel.Selector) + if err != nil { + errs = multierror.Append(errs, errors.Wrapf( + err, + "Failed to initialize OneDrive backup for user %s", + discSel.DiscreteOwner, + )) - continue - } - - err = bo.Run(ctx) - if err != nil { - errs = multierror.Append(errs, errors.Wrapf( - err, - "Failed to run OneDrive backup for user %s", - scope.Get(selectors.OneDriveUser), - )) - - continue - } - - bIDs = append(bIDs, bo.Results.BackupID) + continue } + + err = bo.Run(ctx) + if err != nil { + errs = multierror.Append(errs, errors.Wrapf( + err, + "Failed to run OneDrive backup for user %s", + discSel.DiscreteOwner, + )) + + continue + } + + bIDs = append(bIDs, bo.Results.BackupID) } bups, err := r.Backups(ctx, bIDs) diff --git a/src/cli/backup/sharepoint.go b/src/cli/backup/sharepoint.go index babf0ea09..a2b7889ac 100644 --- a/src/cli/backup/sharepoint.go +++ b/src/cli/backup/sharepoint.go @@ -210,33 +210,30 @@ func createSharePointCmd(cmd *cobra.Command, args []string) error { bIDs []model.StableID ) - for _, sel := range sel.SplitByResourceOwner(gc.GetSiteIDs()) { - // TODO: pass in entire selector, not individual scopes - for _, scope := range sel.Scopes() { - bo, err := r.NewBackup(ctx, sel.Selector) - if err != nil { - errs = multierror.Append(errs, errors.Wrapf( - err, - "Failed to initialize SharePoint backup for site %s", - scope.Get(selectors.SharePointSite), - )) + for _, discSel := range sel.SplitByResourceOwner(gc.GetSiteIDs()) { + bo, err := r.NewBackup(ctx, discSel.Selector) + if err != nil { + errs = multierror.Append(errs, errors.Wrapf( + err, + "Failed to initialize SharePoint backup for site %s", + discSel.DiscreteOwner, + )) - continue - } - - err = bo.Run(ctx) - if err != nil { - errs = multierror.Append(errs, errors.Wrapf( - err, - "Failed to run SharePoint backup for site %s", - scope.Get(selectors.SharePointSite), - )) - - continue - } - - bIDs = append(bIDs, bo.Results.BackupID) + continue } + + err = bo.Run(ctx) + if err != nil { + errs = multierror.Append(errs, errors.Wrapf( + err, + "Failed to run SharePoint backup for site %s", + discSel.DiscreteOwner, + )) + + continue + } + + bIDs = append(bIDs, bo.Results.BackupID) } bups, err := r.Backups(ctx, bIDs) diff --git a/src/internal/connector/data_collections.go b/src/internal/connector/data_collections.go index 4e0992522..4f4cba280 100644 --- a/src/internal/connector/data_collections.go +++ b/src/internal/connector/data_collections.go @@ -47,7 +47,6 @@ func (gc *GraphConnector) DataCollections( ctx, sels, metadata, - gc.GetUsers(), gc.credentials, // gc.Service, gc.UpdateStatus, @@ -154,31 +153,29 @@ func (gc *GraphConnector) OneDriveDataCollections( } var ( - scopes = odb.DiscreteScopes([]string{selector.DiscreteOwner}) + user = selector.DiscreteOwner collections = []data.Collection{} errs error ) // for each scope that includes oneDrive items, get all - for _, scope := range scopes { - for _, user := range scope.Get(selectors.OneDriveUser) { - logger.Ctx(ctx).With("user", user).Debug("Creating OneDrive collections") + for _, scope := range odb.Scopes() { + logger.Ctx(ctx).With("user", user).Debug("Creating OneDrive collections") - odcs, err := onedrive.NewCollections( - gc.credentials.AzureTenantID, - user, - onedrive.OneDriveSource, - odFolderMatcher{scope}, - gc.Service, - gc.UpdateStatus, - ctrlOpts, - ).Get(ctx) - if err != nil { - return nil, support.WrapAndAppend(user, err, errs) - } - - collections = append(collections, odcs...) + odcs, err := onedrive.NewCollections( + gc.credentials.AzureTenantID, + user, + onedrive.OneDriveSource, + odFolderMatcher{scope}, + gc.Service, + gc.UpdateStatus, + ctrlOpts, + ).Get(ctx) + if err != nil { + return nil, support.WrapAndAppend(user, err, errs) } + + collections = append(collections, odcs...) } for range collections { diff --git a/src/internal/connector/data_collections_test.go b/src/internal/connector/data_collections_test.go index 24629d5ad..c4f005e3c 100644 --- a/src/internal/connector/data_collections_test.go +++ b/src/internal/connector/data_collections_test.go @@ -71,7 +71,7 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestExchangeDataCollection getSelector: func(t *testing.T) selectors.Selector { sel := selectors.NewExchangeBackup(selUsers) sel.Include(sel.MailFolders(selUsers, []string{exchange.DefaultMailFolder}, selectors.PrefixMatch())) - + sel.DiscreteOwner = suite.user return sel.Selector }, }, @@ -79,11 +79,8 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestExchangeDataCollection name: suite.user + " Contacts", getSelector: func(t *testing.T) selectors.Selector { sel := selectors.NewExchangeBackup(selUsers) - sel.Include(sel.ContactFolders( - selUsers, - []string{exchange.DefaultContactFolder}, - selectors.PrefixMatch())) - + sel.Include(sel.ContactFolders(selUsers, []string{exchange.DefaultContactFolder}, selectors.PrefixMatch())) + sel.DiscreteOwner = suite.user return sel.Selector }, }, @@ -91,12 +88,8 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestExchangeDataCollection // name: suite.user + " Events", // getSelector: func(t *testing.T) selectors.Selector { // sel := selectors.NewExchangeBackup(selUsers) - // sel.Include(sel.EventCalendars( - // selUsers, - // []string{exchange.DefaultCalendar}, - // selectors.PrefixMatch(), - // )) - + // sel.Include(sel.EventCalendars(selUsers, []string{exchange.DefaultCalendar}, selectors.PrefixMatch())) + // sel.DiscreteOwner = suite.user // return sel.Selector // }, // }, @@ -108,7 +101,6 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestExchangeDataCollection ctx, test.getSelector(t), nil, - []string{suite.user}, connector.credentials, connector.UpdateStatus, control.Options{}) @@ -199,6 +191,7 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestSharePointDataCollecti sel := selectors.NewSharePointBackup(selSites) sel.Include(sel.Libraries(selSites, selectors.Any())) sel.DiscreteOwner = suite.site + return sel.Selector }, }, @@ -209,6 +202,7 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestSharePointDataCollecti sel := selectors.NewSharePointBackup(selSites) sel.Include(sel.Lists(selSites, selectors.Any())) sel.DiscreteOwner = suite.site + return sel.Selector }, }, diff --git a/src/internal/connector/exchange/data_collections.go b/src/internal/connector/exchange/data_collections.go index feccb1757..85e6c43da 100644 --- a/src/internal/connector/exchange/data_collections.go +++ b/src/internal/connector/exchange/data_collections.go @@ -163,7 +163,6 @@ func DataCollections( ctx context.Context, selector selectors.Selector, metadata []data.Collection, - userPNs []string, acct account.M365Config, su support.StatusUpdater, ctrlOpts control.Options, @@ -174,7 +173,8 @@ func DataCollections( } var ( - scopes = eb.DiscreteScopes(userPNs) + user = selector.DiscreteOwner + scopes = eb.DiscreteScopes([]string{user}) collections = []data.Collection{} errs error ) @@ -190,13 +190,13 @@ func DataCollections( dcs, err := createCollections( ctx, acct, + user, scope, dps, ctrlOpts, su) if err != nil { - user := scope.Get(selectors.ExchangeUser) - return nil, support.WrapAndAppend(user[0], err, errs) + return nil, support.WrapAndAppend(user, err, errs) } collections = append(collections, dcs...) @@ -211,6 +211,7 @@ func DataCollections( func createCollections( ctx context.Context, acct account.M365Config, + user string, scope selectors.ExchangeScope, dps DeltaPaths, ctrlOpts control.Options, @@ -218,48 +219,45 @@ func createCollections( ) ([]data.Collection, error) { var ( errs *multierror.Error - users = scope.Get(selectors.ExchangeUser) allCollections = make([]data.Collection, 0) ) // Create collection of ExchangeDataCollection - for _, user := range users { - collections := make(map[string]data.Collection) + collections := make(map[string]data.Collection) - qp := graph.QueryParams{ - Category: scope.Category().PathType(), - ResourceOwner: user, - Credentials: acct, - } + qp := graph.QueryParams{ + Category: scope.Category().PathType(), + ResourceOwner: user, + Credentials: acct, + } - foldersComplete, closer := observe.MessageWithCompletion(fmt.Sprintf("∙ %s - %s:", qp.Category, user)) - defer closer() - defer close(foldersComplete) + foldersComplete, closer := observe.MessageWithCompletion(fmt.Sprintf("∙ %s - %s:", qp.Category, user)) + defer closer() + defer close(foldersComplete) - resolver, err := PopulateExchangeContainerResolver(ctx, qp) - if err != nil { - return nil, errors.Wrap(err, "getting folder cache") - } + resolver, err := PopulateExchangeContainerResolver(ctx, qp) + if err != nil { + return nil, errors.Wrap(err, "getting folder cache") + } - err = filterContainersAndFillCollections( - ctx, - qp, - collections, - su, - resolver, - scope, - dps, - ctrlOpts) + err = filterContainersAndFillCollections( + ctx, + qp, + collections, + su, + resolver, + scope, + dps, + ctrlOpts) - if err != nil { - return nil, errors.Wrap(err, "filling collections") - } + if err != nil { + return nil, errors.Wrap(err, "filling collections") + } - foldersComplete <- struct{}{} + foldersComplete <- struct{}{} - for _, coll := range collections { - allCollections = append(allCollections, coll) - } + for _, coll := range collections { + allCollections = append(allCollections, coll) } return allCollections, errs.ErrorOrNil() diff --git a/src/internal/connector/exchange/data_collections_test.go b/src/internal/connector/exchange/data_collections_test.go index e8f09076d..46f5567b2 100644 --- a/src/internal/connector/exchange/data_collections_test.go +++ b/src/internal/connector/exchange/data_collections_test.go @@ -256,13 +256,12 @@ func (suite *DataCollectionsIntegrationSuite) TestMailFetch() { }, } - // gc := loadConnector(ctx, t, Users) - for _, test := range tests { suite.T().Run(test.name, func(t *testing.T) { collections, err := createCollections( ctx, acct, + userID, test.scope, DeltaPaths{}, control.Options{}, @@ -324,6 +323,7 @@ func (suite *DataCollectionsIntegrationSuite) TestDelta() { collections, err := createCollections( ctx, acct, + userID, test.scope, DeltaPaths{}, control.Options{}, @@ -351,6 +351,7 @@ func (suite *DataCollectionsIntegrationSuite) TestDelta() { collections, err = createCollections( ctx, acct, + userID, test.scope, dps, control.Options{}, @@ -395,6 +396,7 @@ func (suite *DataCollectionsIntegrationSuite) TestMailSerializationRegression() collections, err := createCollections( ctx, acct, + suite.user, sel.Scopes()[0], DeltaPaths{}, control.Options{}, @@ -462,6 +464,7 @@ func (suite *DataCollectionsIntegrationSuite) TestContactSerializationRegression edcs, err := createCollections( ctx, acct, + suite.user, test.scope, DeltaPaths{}, control.Options{}, @@ -546,6 +549,7 @@ func (suite *DataCollectionsIntegrationSuite) TestEventsSerializationRegression( collections, err := createCollections( ctx, acct, + suite.user, test.scope, DeltaPaths{}, control.Options{}, diff --git a/src/internal/connector/graph_connector_helper_test.go b/src/internal/connector/graph_connector_helper_test.go index d8b558d16..688d78ad2 100644 --- a/src/internal/connector/graph_connector_helper_test.go +++ b/src/internal/connector/graph_connector_helper_test.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" "github.com/alcionai/corso/src/internal/connector/mockconnector" "github.com/alcionai/corso/src/internal/connector/support" @@ -777,14 +778,14 @@ func makeExchangeBackupSel( dests []destAndCats, ) selectors.Selector { toInclude := [][]selectors.ExchangeScope{} - resourceOwners := []string{} + resourceOwners := map[string]struct{}{} for _, d := range dests { for c := range d.cats { sel := selectors.NewExchangeBackup(nil) builder := sel.MailFolders - resourceOwners = append(resourceOwners, d.resourceOwner) + resourceOwners[d.resourceOwner] = struct{}{} switch c { case path.ContactsCategory: @@ -802,7 +803,7 @@ func makeExchangeBackupSel( } } - sel := selectors.NewExchangeBackup(resourceOwners) + sel := selectors.NewExchangeBackup(maps.Keys(resourceOwners)) sel.Include(toInclude...) return sel.Selector @@ -938,20 +939,37 @@ func collectionsForInfo( } //nolint:deadcode -func getSelectorWith(service path.ServiceType) selectors.Selector { - s := selectors.ServiceUnknown - +func getSelectorWith( + t *testing.T, + service path.ServiceType, + resourceOwners []string, + forRestore bool, +) selectors.Selector { switch service { case path.ExchangeService: - s = selectors.ServiceExchange - case path.OneDriveService: - s = selectors.ServiceOneDrive - case path.SharePointService: - s = selectors.ServiceSharePoint - } + if forRestore { + return selectors.NewExchangeRestore(resourceOwners).Selector + } - return selectors.Selector{ - Service: s, + return selectors.NewExchangeBackup(resourceOwners).Selector + + case path.OneDriveService: + if forRestore { + return selectors.NewOneDriveRestore(resourceOwners).Selector + } + + return selectors.NewOneDriveBackup(resourceOwners).Selector + + case path.SharePointService: + if forRestore { + return selectors.NewSharePointRestore(resourceOwners).Selector + } + + return selectors.NewSharePointBackup(resourceOwners).Selector + + default: + require.FailNow(t, "unknown path service") + return selectors.Selector{} } } diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index 1b3f9e925..3b6de3586 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -319,7 +319,7 @@ func runRestoreBackupTest( acct account.Account, test restoreBackupInfo, tenant string, - users []string, + resourceOwners []string, ) { var ( collections []data.Collection @@ -332,32 +332,32 @@ func runRestoreBackupTest( ctx, flush := tester.NewContext() defer flush() - for _, user := range users { - numItems, userCollections, userExpectedData := collectionsForInfo( + for _, owner := range resourceOwners { + numItems, ownerCollections, userExpectedData := collectionsForInfo( t, test.service, tenant, - user, + owner, dest, test.collections, ) - collections = append(collections, userCollections...) + collections = append(collections, ownerCollections...) totalItems += numItems maps.Copy(expectedData, userExpectedData) } t.Logf( - "Restoring collections to %s for user(s) %v\n", + "Restoring collections to %s for resourceOwners(s) %v\n", dest.ContainerName, - users, + resourceOwners, ) start := time.Now() restoreGC := loadConnector(ctx, t, test.resource) - restoreSel := getSelectorWith(test.service) + restoreSel := getSelectorWith(t, test.service, resourceOwners, true) deets, err := restoreGC.RestoreDataCollections( ctx, acct, @@ -386,10 +386,10 @@ func runRestoreBackupTest( cats[c.category] = struct{}{} } - expectedDests := make([]destAndCats, 0, len(users)) - for _, u := range users { + expectedDests := make([]destAndCats, 0, len(resourceOwners)) + for _, ro := range resourceOwners { expectedDests = append(expectedDests, destAndCats{ - resourceOwner: u, + resourceOwner: ro, dest: dest.ContainerName, cats: cats, }) @@ -809,7 +809,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames ctx, flush := tester.NewContext() defer flush() - restoreSel := getSelectorWith(test.service) + restoreSel := getSelectorWith(t, test.service, []string{suite.user}, true) expectedDests := make([]destAndCats, 0, len(test.collections)) allItems := 0 allExpectedData := map[string]map[string][]byte{} @@ -883,115 +883,3 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames }) } } - -func (suite *GraphConnectorIntegrationSuite) TestMultiuserRestoreAndBackup() { - bodyText := "This email has some text. However, all the text is on the same line." - subjectText := "Test message for restore" - - users := []string{ - suite.user, - tester.SecondaryM365UserID(suite.T()), - } - table := []restoreBackupInfo{ - { - name: "Email", - service: path.ExchangeService, - resource: Users, - collections: []colInfo{ - { - pathElements: []string{"Inbox"}, - category: path.EmailCategory, - items: []itemInfo{ - { - name: "someencodeditemID", - data: mockconnector.GetMockMessageWithBodyBytes( - subjectText+"-1", - bodyText+" 1.", - bodyText+" 1.", - ), - lookupKey: subjectText + "-1", - }, - }, - }, - { - pathElements: []string{"Archive"}, - category: path.EmailCategory, - items: []itemInfo{ - { - name: "someencodeditemID2", - data: mockconnector.GetMockMessageWithBodyBytes( - subjectText+"-2", - bodyText+" 2.", - bodyText+" 2.", - ), - lookupKey: subjectText + "-2", - }, - }, - }, - }, - }, - { - name: "Contacts", - service: path.ExchangeService, - resource: Users, - collections: []colInfo{ - { - pathElements: []string{"Work"}, - category: path.ContactsCategory, - items: []itemInfo{ - { - name: "someencodeditemID", - data: mockconnector.GetMockContactBytes("Ghimley"), - lookupKey: "Ghimley", - }, - }, - }, - { - pathElements: []string{"Personal"}, - category: path.ContactsCategory, - items: []itemInfo{ - { - name: "someencodeditemID2", - data: mockconnector.GetMockContactBytes("Irgot"), - lookupKey: "Irgot", - }, - }, - }, - }, - }, - // { - // name: "Events", - // service: path.ExchangeService, - // collections: []colInfo{ - // { - // pathElements: []string{"Work"}, - // category: path.EventsCategory, - // items: []itemInfo{ - // { - // name: "someencodeditemID", - // data: mockconnector.GetMockEventWithSubjectBytes("Ghimley"), - // lookupKey: "Ghimley", - // }, - // }, - // }, - // { - // pathElements: []string{"Personal"}, - // category: path.EventsCategory, - // items: []itemInfo{ - // { - // name: "someencodeditemID2", - // data: mockconnector.GetMockEventWithSubjectBytes("Irgot"), - // lookupKey: "Irgot", - // }, - // }, - // }, - // }, - // }, - } - - for _, test := range table { - suite.T().Run(test.name, func(t *testing.T) { - runRestoreBackupTest(t, suite.acct, test, suite.connector.tenant, users) - }) - } -} diff --git a/src/internal/connector/sharepoint/data_collections.go b/src/internal/connector/sharepoint/data_collections.go index d746b467c..a197089b8 100644 --- a/src/internal/connector/sharepoint/data_collections.go +++ b/src/internal/connector/sharepoint/data_collections.go @@ -37,55 +37,50 @@ func DataCollections( } var ( - scopes = b.DiscreteScopes([]string{selector.DiscreteOwner}) + site = b.DiscreteOwner collections = []data.Collection{} errs error ) - for _, scope := range scopes { - // due to DiscreteScopes(siteIDs), each range should only contain one site. - for _, site := range scope.Get(selectors.SharePointSite) { - foldersComplete, closer := observe.MessageWithCompletion(fmt.Sprintf( - "∙ %s - %s:", - scope.Category().PathType(), site)) - defer closer() - defer close(foldersComplete) + for _, scope := range b.Scopes() { + foldersComplete, closer := observe.MessageWithCompletion(fmt.Sprintf( + "∙ %s - %s:", + scope.Category().PathType(), site)) + defer closer() + defer close(foldersComplete) - var spcs []data.Collection + var spcs []data.Collection - switch scope.Category().PathType() { - case path.ListsCategory: - spcs, err = collectLists( - ctx, - serv, - tenantID, - site, - scope, - su, - ctrlOpts, - ) - if err != nil { - return nil, support.WrapAndAppend(site, err, errs) - } - - case path.LibrariesCategory: - spcs, err = collectLibraries( - ctx, - serv, - tenantID, - site, - scope, - su, - ctrlOpts) - if err != nil { - return nil, support.WrapAndAppend(site, err, errs) - } + switch scope.Category().PathType() { + case path.ListsCategory: + spcs, err = collectLists( + ctx, + serv, + tenantID, + site, + scope, + su, + ctrlOpts) + if err != nil { + return nil, support.WrapAndAppend(site, err, errs) } - collections = append(collections, spcs...) - - foldersComplete <- struct{}{} + case path.LibrariesCategory: + spcs, err = collectLibraries( + ctx, + serv, + tenantID, + site, + scope, + su, + ctrlOpts) + if err != nil { + return nil, support.WrapAndAppend(site, err, errs) + } } + + collections = append(collections, spcs...) + foldersComplete <- struct{}{} } return collections, errs diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index d6a6581c8..e099d1b48 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -34,9 +34,10 @@ import ( type BackupOperation struct { operation - Results BackupResults `json:"results"` - Selectors selectors.Selector `json:"selectors"` - Version string `json:"version"` + ResourceOwner string `json:"resourceOwner"` + Results BackupResults `json:"results"` + Selectors selectors.Selector `json:"selectors"` + Version string `json:"version"` account account.Account } @@ -60,10 +61,11 @@ func NewBackupOperation( bus events.Eventer, ) (BackupOperation, error) { op := BackupOperation{ - operation: newOperation(opts, bus, kw, sw), - Selectors: selector, - Version: "v0", - account: acct, + operation: newOperation(opts, bus, kw, sw), + ResourceOwner: selector.DiscreteOwner, + Selectors: selector, + Version: "v0", + account: acct, } if err := op.validate(); err != nil { return BackupOperation{}, err @@ -73,6 +75,10 @@ func NewBackupOperation( } func (op BackupOperation) validate() error { + if len(op.ResourceOwner) == 0 { + return errors.New("backup requires a resource owner") + } + return op.operation.validate() } @@ -336,9 +342,7 @@ func selectorToOwnersCats(sel selectors.Selector) *kopia.OwnersCats { ServiceCats: map[string]kopia.ServiceCat{}, } - for _, ro := range sel.DiscreteResourceOwners() { - oc.ResourceOwners[ro] = struct{}{} - } + oc.ResourceOwners[sel.DiscreteOwner] = struct{}{} pcs, err := sel.PathCategories() if err != nil { diff --git a/src/internal/operations/backup_integration_test.go b/src/internal/operations/backup_integration_test.go index 05cfb1655..977f494bf 100644 --- a/src/internal/operations/backup_integration_test.go +++ b/src/internal/operations/backup_integration_test.go @@ -484,7 +484,7 @@ func (suite *BackupOpIntegrationSuite) TestNewBackupOperation() { test.kw, test.sw, test.acct, - selectors.Selector{}, + selectors.Selector{DiscreteOwner: "test"}, evmock.NewBus()) test.errCheck(t, err) }) @@ -516,6 +516,8 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchange() { selector: func() *selectors.ExchangeBackup { sel := selectors.NewExchangeBackup(users) sel.Include(sel.MailFolders(users, []string{exchange.DefaultMailFolder}, selectors.PrefixMatch())) + sel.DiscreteOwner = suite.user + return sel }, resourceOwner: suite.user, @@ -531,6 +533,7 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchange() { users, []string{exchange.DefaultContactFolder}, selectors.PrefixMatch())) + sel.DiscreteOwner = suite.user return sel }, @@ -544,6 +547,8 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchange() { selector: func() *selectors.ExchangeBackup { sel := selectors.NewExchangeBackup(users) sel.Include(sel.EventCalendars(users, []string{exchange.DefaultCalendar}, selectors.PrefixMatch())) + sel.DiscreteOwner = suite.user + return sel }, resourceOwner: suite.user, @@ -876,16 +881,20 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() { switch category { case path.EmailCategory: cmf := cli.MailFoldersById(containerID) + body, err := cmf.Get(ctx, nil) require.NoError(t, err, "getting mail folder") + body.SetDisplayName(&containerRename) _, err = cmf.Patch(ctx, body, nil) require.NoError(t, err, "updating mail folder name") case path.ContactsCategory: ccf := cli.ContactFoldersById(containerID) + body, err := ccf.Get(ctx, nil) require.NoError(t, err, "getting contact folder") + body.SetDisplayName(&containerRename) _, err = ccf.Patch(ctx, body, nil) require.NoError(t, err, "updating contact folder name") diff --git a/src/internal/operations/backup_test.go b/src/internal/operations/backup_test.go index 99d71fbd3..e046051c5 100644 --- a/src/internal/operations/backup_test.go +++ b/src/internal/operations/backup_test.go @@ -364,7 +364,7 @@ func (suite *BackupOpSuite) TestBackupOperation_PersistResults() { kw, sw, acct, - selectors.Selector{}, + selectors.Selector{DiscreteOwner: "test"}, evmock.NewBus()) require.NoError(t, err) test.expectErr(t, op.persistResults(now, &test.stats)) diff --git a/src/internal/operations/restore_test.go b/src/internal/operations/restore_test.go index 8def6999c..f0b58d3cd 100644 --- a/src/internal/operations/restore_test.go +++ b/src/internal/operations/restore_test.go @@ -98,7 +98,7 @@ func (suite *RestoreOpSuite) TestRestoreOperation_PersistResults() { sw, acct, "foo", - selectors.Selector{}, + selectors.Selector{DiscreteOwner: "test"}, dest, evmock.NewBus()) require.NoError(t, err) @@ -250,7 +250,7 @@ func (suite *RestoreOpIntegrationSuite) TestNewRestoreOperation() { test.sw, test.acct, "backup-id", - selectors.Selector{}, + selectors.Selector{DiscreteOwner: "test"}, dest, evmock.NewBus()) test.errCheck(t, err) diff --git a/src/pkg/repository/repository_test.go b/src/pkg/repository/repository_test.go index 132e9f76f..c84938615 100644 --- a/src/pkg/repository/repository_test.go +++ b/src/pkg/repository/repository_test.go @@ -193,7 +193,7 @@ func (suite *RepositoryIntegrationSuite) TestNewBackup() { r, err := repository.Initialize(ctx, acct, st, control.Options{}) require.NoError(t, err) - bo, err := r.NewBackup(ctx, selectors.Selector{}) + bo, err := r.NewBackup(ctx, selectors.Selector{DiscreteOwner: "test"}) require.NoError(t, err) require.NotNil(t, bo) } @@ -213,7 +213,7 @@ func (suite *RepositoryIntegrationSuite) TestNewRestore() { r, err := repository.Initialize(ctx, acct, st, control.Options{}) require.NoError(t, err) - ro, err := r.NewRestore(ctx, "backup-id", selectors.Selector{}, dest) + ro, err := r.NewRestore(ctx, "backup-id", selectors.Selector{DiscreteOwner: "test"}, dest) require.NoError(t, err) require.NotNil(t, ro) } diff --git a/src/pkg/selectors/selectors.go b/src/pkg/selectors/selectors.go index 1c111f1de..1a6a4503b 100644 --- a/src/pkg/selectors/selectors.go +++ b/src/pkg/selectors/selectors.go @@ -113,9 +113,15 @@ type Selector struct { // helper for specific selector instance constructors. func newSelector(s service, resourceOwners []string) Selector { + var owner string + if len(resourceOwners) == 1 { + owner = resourceOwners[0] + } + return Selector{ Service: s, ResourceOwners: filterize(scopeConfig{}, resourceOwners...), + DiscreteOwner: owner, Excludes: []scope{}, Includes: []scope{}, }