diff --git a/src/internal/m365/backup.go b/src/internal/m365/backup.go index b6bab4120..19194ea79 100644 --- a/src/internal/m365/backup.go +++ b/src/internal/m365/backup.go @@ -13,7 +13,10 @@ import ( "github.com/alcionai/corso/src/internal/m365/service/groups" "github.com/alcionai/corso/src/internal/m365/service/onedrive" "github.com/alcionai/corso/src/internal/m365/service/sharepoint" + "github.com/alcionai/corso/src/internal/m365/service/teamschats" + "github.com/alcionai/corso/src/internal/m365/support" "github.com/alcionai/corso/src/internal/operations/inject" + "github.com/alcionai/corso/src/pkg/account" bupMD "github.com/alcionai/corso/src/pkg/backup/metadata" "github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/count" @@ -22,9 +25,31 @@ import ( "github.com/alcionai/corso/src/pkg/filters" "github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/selectors" + "github.com/alcionai/corso/src/pkg/services/m365/api" "github.com/alcionai/corso/src/pkg/services/m365/api/graph" ) +type backupHandler interface { + produceBackupCollectionser +} + +type produceBackupCollectionser interface { + ProduceBackupCollections( + ctx context.Context, + bpc inject.BackupProducerConfig, + ac api.Client, + creds account.M365Config, + su support.StatusUpdater, + counter *count.Bus, + errs *fault.Bus, + ) ( + collections []data.BackupCollection, + excludeItems *prefixmatcher.StringSetMatcher, + canUsePreviousBackup bool, + err error, + ) +} + // --------------------------------------------------------------------------- // Data Collections // --------------------------------------------------------------------------- @@ -63,67 +88,40 @@ func (ctrl *Controller) ProduceBackupCollections( canUsePreviousBackup bool ) + var handler backupHandler + switch service { case path.ExchangeService: - colls, excludeItems, canUsePreviousBackup, err = exchange.ProduceBackupCollections( - ctx, - bpc, - ctrl.AC, - ctrl.credentials, - ctrl.UpdateStatus, - counter, - errs) - if err != nil { - return nil, nil, false, err - } + handler = exchange.NewBackup() case path.OneDriveService: - colls, excludeItems, canUsePreviousBackup, err = onedrive.ProduceBackupCollections( - ctx, - bpc, - ctrl.AC, - ctrl.credentials, - ctrl.UpdateStatus, - counter, - errs) - if err != nil { - return nil, nil, false, err - } + handler = onedrive.NewBackup() case path.SharePointService: - colls, excludeItems, canUsePreviousBackup, err = sharepoint.ProduceBackupCollections( - ctx, - bpc, - ctrl.AC, - ctrl.credentials, - ctrl.UpdateStatus, - counter, - errs) - if err != nil { - return nil, nil, false, err - } + handler = sharepoint.NewBackup() case path.GroupsService: - colls, excludeItems, err = groups.ProduceBackupCollections( - ctx, - bpc, - ctrl.AC, - ctrl.credentials, - ctrl.UpdateStatus, - counter, - errs) - if err != nil { - return nil, nil, false, err - } + handler = groups.NewBackup() - // canUsePreviousBacukp can be always returned true for groups as we - // return a tombstone collection in case the metadata read fails - canUsePreviousBackup = true + case path.TeamsChatsService: + handler = teamschats.NewBackup() default: return nil, nil, false, clues.Wrap(clues.NewWC(ctx, service.String()), "service not supported") } + colls, excludeItems, canUsePreviousBackup, err = handler.ProduceBackupCollections( + ctx, + bpc, + ctrl.AC, + ctrl.credentials, + ctrl.UpdateStatus, + counter, + errs) + if err != nil { + return nil, nil, false, err + } + for _, c := range colls { // kopia doesn't stream Items() from deleted collections, // and so they never end up calling the UpdateStatus closer. @@ -153,25 +151,27 @@ func (ctrl *Controller) IsServiceEnabled( return sharepoint.IsServiceEnabled(ctx, ctrl.AC.Sites(), resourceOwner) case path.GroupsService: return groups.IsServiceEnabled(ctx, ctrl.AC.Groups(), resourceOwner) + case path.TeamsChatsService: + return teamschats.IsServiceEnabled(ctx, ctrl.AC.Users(), resourceOwner) } return false, clues.Wrap(clues.NewWC(ctx, service.String()), "service not supported") } -func verifyBackupInputs(sels selectors.Selector, cachedIDs []string) error { +func verifyBackupInputs(sel selectors.Selector, cachedIDs []string) error { var ids []string - switch sels.Service { + switch sel.Service { case selectors.ServiceExchange, selectors.ServiceOneDrive: // Exchange and OneDrive user existence now checked in checkServiceEnabled. return nil - case selectors.ServiceSharePoint, selectors.ServiceGroups: + case selectors.ServiceSharePoint, selectors.ServiceGroups, selectors.ServiceTeamsChats: ids = cachedIDs } - if !filters.Contains(ids).Compare(sels.ID()) { - return clues.Stack(core.ErrNotFound).With("selector_protected_resource", sels.DiscreteOwner) + if !filters.Contains(ids).Compare(sel.ID()) { + return clues.Stack(core.ErrNotFound).With("selector_protected_resource", sel.ID()) } return nil diff --git a/src/internal/m365/backup_test.go b/src/internal/m365/backup_test.go index 04ea6b66a..e3f841acc 100644 --- a/src/internal/m365/backup_test.go +++ b/src/internal/m365/backup_test.go @@ -139,7 +139,7 @@ func (suite *DataCollectionIntgSuite) TestExchangeDataCollection() { Selector: sel, } - collections, excludes, canUsePreviousBackup, err := exchange.ProduceBackupCollections( + collections, excludes, canUsePreviousBackup, err := exchange.NewBackup().ProduceBackupCollections( ctx, bpc, suite.ac, @@ -309,7 +309,7 @@ func (suite *DataCollectionIntgSuite) TestSharePointDataCollection() { Selector: sel, } - collections, excludes, canUsePreviousBackup, err := sharepoint.ProduceBackupCollections( + collections, excludes, canUsePreviousBackup, err := sharepoint.NewBackup().ProduceBackupCollections( ctx, bpc, suite.ac, diff --git a/src/internal/m365/service/exchange/backup.go b/src/internal/m365/service/exchange/backup.go index 879160375..999b2441d 100644 --- a/src/internal/m365/service/exchange/backup.go +++ b/src/internal/m365/service/exchange/backup.go @@ -19,9 +19,17 @@ import ( "github.com/alcionai/corso/src/pkg/services/m365/api/graph" ) +type exchangeBackup struct{} + +// NewBackup provides a struct that matches standard apis +// across m365/service handlers. +func NewBackup() *exchangeBackup { + return &exchangeBackup{} +} + // ProduceBackupCollections returns a DataCollection which the caller can // use to read mailbox data out for the specified user -func ProduceBackupCollections( +func (exchangeBackup) ProduceBackupCollections( ctx context.Context, bpc inject.BackupProducerConfig, ac api.Client, diff --git a/src/internal/m365/service/groups/backup.go b/src/internal/m365/service/groups/backup.go index 4dcae200a..f1cc11541 100644 --- a/src/internal/m365/service/groups/backup.go +++ b/src/internal/m365/service/groups/backup.go @@ -33,7 +33,15 @@ import ( "github.com/alcionai/corso/src/pkg/services/m365/api/graph" ) -func ProduceBackupCollections( +type groupsBackup struct{} + +// NewBackup provides a struct that matches standard apis +// across m365/service handlers. +func NewBackup() *groupsBackup { + return &groupsBackup{} +} + +func (groupsBackup) ProduceBackupCollections( ctx context.Context, bpc inject.BackupProducerConfig, ac api.Client, @@ -41,10 +49,10 @@ func ProduceBackupCollections( su support.StatusUpdater, counter *count.Bus, errs *fault.Bus, -) ([]data.BackupCollection, *prefixmatcher.StringSetMatcher, error) { +) ([]data.BackupCollection, *prefixmatcher.StringSetMatcher, bool, error) { b, err := bpc.Selector.ToGroupsBackup() if err != nil { - return nil, nil, clues.Wrap(err, "groupsDataCollection: parsing selector") + return nil, nil, true, clues.Wrap(err, "groupsDataCollection: parsing selector") } var ( @@ -65,7 +73,7 @@ func ProduceBackupCollections( bpc.ProtectedResource.ID(), api.CallConfig{}) if err != nil { - return nil, nil, clues.WrapWC(ctx, err, "getting group") + return nil, nil, true, clues.WrapWC(ctx, err, "getting group") } bc := backupCommon{ac, bpc, creds, group, sitesPreviousPaths, su} @@ -128,7 +136,7 @@ func ProduceBackupCollections( counter, errs) if err != nil { - return nil, nil, err + return nil, nil, true, err } collections = append(collections, baseCols...) @@ -142,7 +150,7 @@ func ProduceBackupCollections( su, counter) if err != nil { - return nil, nil, err + return nil, nil, true, err } collections = append(collections, md) @@ -151,7 +159,7 @@ func ProduceBackupCollections( logger.Ctx(ctx).Infow("produced collections", "stats", counter.Values()) - return collections, globalItemIDExclusions.ToReader(), el.Failure() + return collections, globalItemIDExclusions.ToReader(), true, el.Failure() } type backupCommon struct { diff --git a/src/internal/m365/service/onedrive/backup.go b/src/internal/m365/service/onedrive/backup.go index 0c19020ed..ece8c21a6 100644 --- a/src/internal/m365/service/onedrive/backup.go +++ b/src/internal/m365/service/onedrive/backup.go @@ -22,7 +22,15 @@ import ( "github.com/alcionai/corso/src/pkg/services/m365/api/graph" ) -func ProduceBackupCollections( +type oneDriveBackup struct{} + +// NewBackup provides a struct that matches standard apis +// across m365/service handlers. +func NewBackup() *oneDriveBackup { + return &oneDriveBackup{} +} + +func (oneDriveBackup) ProduceBackupCollections( ctx context.Context, bpc inject.BackupProducerConfig, ac api.Client, diff --git a/src/internal/m365/service/sharepoint/backup.go b/src/internal/m365/service/sharepoint/backup.go index 9cebb74a3..46706a180 100644 --- a/src/internal/m365/service/sharepoint/backup.go +++ b/src/internal/m365/service/sharepoint/backup.go @@ -20,7 +20,15 @@ import ( "github.com/alcionai/corso/src/pkg/services/m365/api/graph" ) -func ProduceBackupCollections( +type sharePointBackup struct{} + +// NewBackup provides a struct that matches standard apis +// across m365/service handlers. +func NewBackup() *sharePointBackup { + return &sharePointBackup{} +} + +func (sharePointBackup) ProduceBackupCollections( ctx context.Context, bpc inject.BackupProducerConfig, ac api.Client, diff --git a/src/internal/m365/service/teamschats/backup.go b/src/internal/m365/service/teamschats/backup.go index 88ce9ac30..da17cc8f4 100644 --- a/src/internal/m365/service/teamschats/backup.go +++ b/src/internal/m365/service/teamschats/backup.go @@ -22,7 +22,15 @@ import ( "github.com/alcionai/corso/src/pkg/services/m365/api/graph" ) -func ProduceBackupCollections( +type teamsChatsBackup struct{} + +// NewBackup provides a struct that matches standard apis +// across m365/service handlers. +func NewBackup() *teamsChatsBackup { + return &teamsChatsBackup{} +} + +func (teamsChatsBackup) ProduceBackupCollections( ctx context.Context, bpc inject.BackupProducerConfig, ac api.Client, @@ -30,10 +38,10 @@ func ProduceBackupCollections( su support.StatusUpdater, counter *count.Bus, errs *fault.Bus, -) ([]data.BackupCollection, *prefixmatcher.StringSetMatcher, error) { +) ([]data.BackupCollection, *prefixmatcher.StringSetMatcher, bool, error) { b, err := bpc.Selector.ToTeamsChatsBackup() if err != nil { - return nil, nil, clues.WrapWC(ctx, err, "parsing selector") + return nil, nil, true, clues.WrapWC(ctx, err, "parsing selector") } var ( @@ -98,7 +106,7 @@ func ProduceBackupCollections( counter, errs) if err != nil { - return nil, nil, err + return nil, nil, true, err } collections = append(collections, baseCols...) @@ -108,7 +116,7 @@ func ProduceBackupCollections( logger.Ctx(ctx).Infow("produced collections", "stats", counter.Values()) - return collections, nil, clues.Stack(el.Failure()).OrNil() + return collections, nil, true, clues.Stack(el.Failure()).OrNil() } type backupCommon struct { @@ -126,9 +134,7 @@ func backupChats( counter *count.Bus, errs *fault.Bus, ) ([]data.BackupCollection, error) { - var ( - colls []data.BackupCollection - ) + var colls []data.BackupCollection progressMessage := observe.MessageWithCompletion( ctx,