add teamschats to m365 backup

tests will arrive in a later PR
This commit is contained in:
ryanfkeepers 2024-01-23 14:34:43 -07:00
parent 9831fb8f91
commit 7d9d0e04cc
7 changed files with 110 additions and 72 deletions

View File

@ -13,7 +13,10 @@ import (
"github.com/alcionai/corso/src/internal/m365/service/groups" "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/onedrive"
"github.com/alcionai/corso/src/internal/m365/service/sharepoint" "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/internal/operations/inject"
"github.com/alcionai/corso/src/pkg/account"
bupMD "github.com/alcionai/corso/src/pkg/backup/metadata" bupMD "github.com/alcionai/corso/src/pkg/backup/metadata"
"github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/count" "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/filters"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/selectors" "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" "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 // Data Collections
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -63,67 +88,40 @@ func (ctrl *Controller) ProduceBackupCollections(
canUsePreviousBackup bool canUsePreviousBackup bool
) )
var handler backupHandler
switch service { switch service {
case path.ExchangeService: case path.ExchangeService:
colls, excludeItems, canUsePreviousBackup, err = exchange.ProduceBackupCollections( handler = exchange.NewBackup()
ctx,
bpc,
ctrl.AC,
ctrl.credentials,
ctrl.UpdateStatus,
counter,
errs)
if err != nil {
return nil, nil, false, err
}
case path.OneDriveService: case path.OneDriveService:
colls, excludeItems, canUsePreviousBackup, err = onedrive.ProduceBackupCollections( handler = onedrive.NewBackup()
ctx,
bpc,
ctrl.AC,
ctrl.credentials,
ctrl.UpdateStatus,
counter,
errs)
if err != nil {
return nil, nil, false, err
}
case path.SharePointService: case path.SharePointService:
colls, excludeItems, canUsePreviousBackup, err = sharepoint.ProduceBackupCollections( handler = sharepoint.NewBackup()
ctx,
bpc,
ctrl.AC,
ctrl.credentials,
ctrl.UpdateStatus,
counter,
errs)
if err != nil {
return nil, nil, false, err
}
case path.GroupsService: case path.GroupsService:
colls, excludeItems, err = groups.ProduceBackupCollections( handler = groups.NewBackup()
ctx,
bpc,
ctrl.AC,
ctrl.credentials,
ctrl.UpdateStatus,
counter,
errs)
if err != nil {
return nil, nil, false, err
}
// canUsePreviousBacukp can be always returned true for groups as we case path.TeamsChatsService:
// return a tombstone collection in case the metadata read fails handler = teamschats.NewBackup()
canUsePreviousBackup = true
default: default:
return nil, nil, false, clues.Wrap(clues.NewWC(ctx, service.String()), "service not supported") 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 { for _, c := range colls {
// kopia doesn't stream Items() from deleted collections, // kopia doesn't stream Items() from deleted collections,
// and so they never end up calling the UpdateStatus closer. // 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) return sharepoint.IsServiceEnabled(ctx, ctrl.AC.Sites(), resourceOwner)
case path.GroupsService: case path.GroupsService:
return groups.IsServiceEnabled(ctx, ctrl.AC.Groups(), resourceOwner) 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") 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 var ids []string
switch sels.Service { switch sel.Service {
case selectors.ServiceExchange, selectors.ServiceOneDrive: case selectors.ServiceExchange, selectors.ServiceOneDrive:
// Exchange and OneDrive user existence now checked in checkServiceEnabled. // Exchange and OneDrive user existence now checked in checkServiceEnabled.
return nil return nil
case selectors.ServiceSharePoint, selectors.ServiceGroups: case selectors.ServiceSharePoint, selectors.ServiceGroups, selectors.ServiceTeamsChats:
ids = cachedIDs ids = cachedIDs
} }
if !filters.Contains(ids).Compare(sels.ID()) { if !filters.Contains(ids).Compare(sel.ID()) {
return clues.Stack(core.ErrNotFound).With("selector_protected_resource", sels.DiscreteOwner) return clues.Stack(core.ErrNotFound).With("selector_protected_resource", sel.ID())
} }
return nil return nil

View File

@ -139,7 +139,7 @@ func (suite *DataCollectionIntgSuite) TestExchangeDataCollection() {
Selector: sel, Selector: sel,
} }
collections, excludes, canUsePreviousBackup, err := exchange.ProduceBackupCollections( collections, excludes, canUsePreviousBackup, err := exchange.NewBackup().ProduceBackupCollections(
ctx, ctx,
bpc, bpc,
suite.ac, suite.ac,
@ -309,7 +309,7 @@ func (suite *DataCollectionIntgSuite) TestSharePointDataCollection() {
Selector: sel, Selector: sel,
} }
collections, excludes, canUsePreviousBackup, err := sharepoint.ProduceBackupCollections( collections, excludes, canUsePreviousBackup, err := sharepoint.NewBackup().ProduceBackupCollections(
ctx, ctx,
bpc, bpc,
suite.ac, suite.ac,

View File

@ -19,9 +19,17 @@ import (
"github.com/alcionai/corso/src/pkg/services/m365/api/graph" "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 // ProduceBackupCollections returns a DataCollection which the caller can
// use to read mailbox data out for the specified user // use to read mailbox data out for the specified user
func ProduceBackupCollections( func (exchangeBackup) ProduceBackupCollections(
ctx context.Context, ctx context.Context,
bpc inject.BackupProducerConfig, bpc inject.BackupProducerConfig,
ac api.Client, ac api.Client,

View File

@ -33,7 +33,15 @@ import (
"github.com/alcionai/corso/src/pkg/services/m365/api/graph" "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, ctx context.Context,
bpc inject.BackupProducerConfig, bpc inject.BackupProducerConfig,
ac api.Client, ac api.Client,
@ -41,10 +49,10 @@ func ProduceBackupCollections(
su support.StatusUpdater, su support.StatusUpdater,
counter *count.Bus, counter *count.Bus,
errs *fault.Bus, errs *fault.Bus,
) ([]data.BackupCollection, *prefixmatcher.StringSetMatcher, error) { ) ([]data.BackupCollection, *prefixmatcher.StringSetMatcher, bool, error) {
b, err := bpc.Selector.ToGroupsBackup() b, err := bpc.Selector.ToGroupsBackup()
if err != nil { if err != nil {
return nil, nil, clues.Wrap(err, "groupsDataCollection: parsing selector") return nil, nil, true, clues.Wrap(err, "groupsDataCollection: parsing selector")
} }
var ( var (
@ -65,7 +73,7 @@ func ProduceBackupCollections(
bpc.ProtectedResource.ID(), bpc.ProtectedResource.ID(),
api.CallConfig{}) api.CallConfig{})
if err != nil { 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} bc := backupCommon{ac, bpc, creds, group, sitesPreviousPaths, su}
@ -128,7 +136,7 @@ func ProduceBackupCollections(
counter, counter,
errs) errs)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, true, err
} }
collections = append(collections, baseCols...) collections = append(collections, baseCols...)
@ -142,7 +150,7 @@ func ProduceBackupCollections(
su, su,
counter) counter)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, true, err
} }
collections = append(collections, md) collections = append(collections, md)
@ -151,7 +159,7 @@ func ProduceBackupCollections(
logger.Ctx(ctx).Infow("produced collections", "stats", counter.Values()) 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 { type backupCommon struct {

View File

@ -22,7 +22,15 @@ import (
"github.com/alcionai/corso/src/pkg/services/m365/api/graph" "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, ctx context.Context,
bpc inject.BackupProducerConfig, bpc inject.BackupProducerConfig,
ac api.Client, ac api.Client,

View File

@ -20,7 +20,15 @@ import (
"github.com/alcionai/corso/src/pkg/services/m365/api/graph" "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, ctx context.Context,
bpc inject.BackupProducerConfig, bpc inject.BackupProducerConfig,
ac api.Client, ac api.Client,

View File

@ -22,7 +22,15 @@ import (
"github.com/alcionai/corso/src/pkg/services/m365/api/graph" "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, ctx context.Context,
bpc inject.BackupProducerConfig, bpc inject.BackupProducerConfig,
ac api.Client, ac api.Client,
@ -30,10 +38,10 @@ func ProduceBackupCollections(
su support.StatusUpdater, su support.StatusUpdater,
counter *count.Bus, counter *count.Bus,
errs *fault.Bus, errs *fault.Bus,
) ([]data.BackupCollection, *prefixmatcher.StringSetMatcher, error) { ) ([]data.BackupCollection, *prefixmatcher.StringSetMatcher, bool, error) {
b, err := bpc.Selector.ToTeamsChatsBackup() b, err := bpc.Selector.ToTeamsChatsBackup()
if err != nil { if err != nil {
return nil, nil, clues.WrapWC(ctx, err, "parsing selector") return nil, nil, true, clues.WrapWC(ctx, err, "parsing selector")
} }
var ( var (
@ -98,7 +106,7 @@ func ProduceBackupCollections(
counter, counter,
errs) errs)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, true, err
} }
collections = append(collections, baseCols...) collections = append(collections, baseCols...)
@ -108,7 +116,7 @@ func ProduceBackupCollections(
logger.Ctx(ctx).Infow("produced collections", "stats", counter.Values()) 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 { type backupCommon struct {
@ -126,9 +134,7 @@ func backupChats(
counter *count.Bus, counter *count.Bus,
errs *fault.Bus, errs *fault.Bus,
) ([]data.BackupCollection, error) { ) ([]data.BackupCollection, error) {
var ( var colls []data.BackupCollection
colls []data.BackupCollection
)
progressMessage := observe.MessageWithCompletion( progressMessage := observe.MessageWithCompletion(
ctx, ctx,