Keepers e86592f51e
replace graph Wrap and Stack with clues (#5018)
Now that graph errors are always transformed as part of the graph client wrapper or http_wrapper, we don't need to call graph.Stack or graph.Wrap outside of those inner helpers any longer. This PR swaps all those graph calls with equivalent clues funcs as a cleanup.

Should not contain any logical changes.

---

#### Does this PR need a docs update or release note?

- [x]  No

#### Type of change

- [x] 🧹 Tech Debt/Cleanup

#### Issue(s)

* #4685 

#### Test Plan

- [x]  Unit test
- [x] 💚 E2E
2024-02-06 01:08:26 +00:00

380 lines
8.6 KiB
Go

package site
import (
"context"
"fmt"
stdpath "path"
"time"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/prefixmatcher"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/m365/collection/drive"
betaAPI "github.com/alcionai/corso/src/internal/m365/service/sharepoint/api"
"github.com/alcionai/corso/src/internal/m365/support"
"github.com/alcionai/corso/src/internal/observe"
"github.com/alcionai/corso/src/internal/operations/inject"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/backup/metadata"
"github.com/alcionai/corso/src/pkg/count"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/logger"
"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"
)
// CollectLibraries constructs a onedrive Collections struct and Get()s
// all the drives associated with the site.
func CollectLibraries(
ctx context.Context,
bpc inject.BackupProducerConfig,
bh drive.BackupHandler,
tenantID string,
ssmb *prefixmatcher.StringSetMatchBuilder,
su support.StatusUpdater,
counter *count.Bus,
errs *fault.Bus,
) ([]data.BackupCollection, bool, error) {
logger.Ctx(ctx).Debug("creating SharePoint Library collections")
var (
collections = []data.BackupCollection{}
colls = drive.NewCollections(
bh,
tenantID,
bpc.ProtectedResource,
su,
bpc.Options,
counter)
)
msg := fmt.Sprintf(
"%s (%s)",
path.LibrariesCategory.HumanString(),
stdpath.Base(bpc.ProtectedResource.Name()))
progressMessage := observe.MessageWithCompletion(
ctx,
observe.ProgressCfg{
Indent: 1,
CompletionMessage: func() string { return fmt.Sprintf("(found %d items)", colls.NumItems) },
},
msg)
close(progressMessage)
odcs, canUsePreviousBackup, err := colls.Get(ctx, bpc.MetadataCollections, ssmb, errs)
if err != nil {
return nil, false, clues.Wrap(err, "getting library")
}
return append(collections, odcs...), canUsePreviousBackup, nil
}
// CollectPages constructs a sharepoint Collections struct and Get()s the associated
// M365 IDs for the associated Pages.
func CollectPages(
ctx context.Context,
bpc inject.BackupProducerConfig,
creds account.M365Config,
ac api.Client,
scope selectors.SharePointScope,
su support.StatusUpdater,
counter *count.Bus,
errs *fault.Bus,
) ([]data.BackupCollection, error) {
logger.Ctx(ctx).Debug("creating SharePoint Pages collections")
var (
el = errs.Local()
spcs = make([]data.BackupCollection, 0)
)
// make the betaClient
// Need to receive From DataCollection Call
adpt, err := graph.CreateAdapter(
creds.AzureTenantID,
creds.AzureClientID,
creds.AzureClientSecret,
counter)
if err != nil {
return nil, clues.Wrap(err, "creating azure client adapter")
}
betaService := betaAPI.NewBetaService(adpt)
tuples, err := betaAPI.FetchPages(ctx, betaService, bpc.ProtectedResource.ID())
if err != nil {
return nil, err
}
for _, tuple := range tuples {
if el.Failure() != nil {
break
}
dir, err := path.Build(
creds.AzureTenantID,
bpc.ProtectedResource.ID(),
path.SharePointService,
path.PagesCategory,
false,
tuple.Name)
if err != nil {
el.AddRecoverable(ctx, clues.WrapWC(ctx, err, "creating page collection path"))
}
collection := NewPrefetchCollection(
nil,
dir,
nil,
nil,
ac,
scope,
su,
bpc.Options,
nil)
collection.SetBetaService(betaService)
collection.AddItem(tuple.ID, time.Now())
spcs = append(spcs, collection)
}
return spcs, el.Failure()
}
func CollectLists(
ctx context.Context,
bh backupHandler,
bpc inject.BackupProducerConfig,
ac api.Client,
tenantID string,
scope selectors.SharePointScope,
su support.StatusUpdater,
counter *count.Bus,
errs *fault.Bus,
) ([]data.BackupCollection, bool, error) {
logger.Ctx(ctx).Debug("Creating SharePoint List Collections")
var (
el = errs.Local()
spcs = make([]data.BackupCollection, 0)
cfg = api.CallConfig{Select: idAnd("list", "lastModifiedDateTime")}
)
dps, canUsePreviousBackup, err := parseListsMetadataCollections(ctx, path.ListsCategory, bpc.MetadataCollections)
if err != nil {
return nil, false, err
}
ctx = clues.Add(ctx, "can_use_previous_backup", canUsePreviousBackup)
lists, err := bh.GetItems(ctx, cfg)
if err != nil {
return nil, false, err
}
collections, err := populateListsCollections(
ctx,
bh,
bpc,
ac,
tenantID,
scope,
su,
lists,
dps,
counter,
el)
if err != nil {
return nil, false, err
}
for _, spc := range collections {
spcs = append(spcs, spc)
}
return spcs, canUsePreviousBackup, el.Failure()
}
func populateListsCollections(
ctx context.Context,
bh backupHandler,
bpc inject.BackupProducerConfig,
ac api.Client,
tenantID string,
scope selectors.SharePointScope,
su support.StatusUpdater,
lists []models.Listable,
dps metadata.DeltaPaths,
counter *count.Bus,
el *fault.Bus,
) (map[string]data.BackupCollection, error) {
var (
err error
collection data.BackupCollection
// collections: list-id -> backup-collection
collections = make(map[string]data.BackupCollection)
currPaths = make(map[string]string)
tombstones = makeTombstones(dps)
)
counter.Add(count.Lists, int64(len(lists)))
for _, list := range lists {
if el.Failure() != nil {
break
}
if api.SkipListTemplates.HasKey(ptr.Val(list.GetList().GetTemplate())) {
continue
}
var (
listID = ptr.Val(list.GetId())
storageDir = path.Elements{listID}
dp = dps[storageDir.String()]
prevPathStr = dp.Path
prevPath path.Path
)
delete(tombstones, listID)
if len(prevPathStr) > 0 {
if prevPath, err = pathFromPrevString(prevPathStr); err != nil {
err = clues.StackWC(ctx, err).Label(count.BadPrevPath)
logger.CtxErr(ctx, err).Error("parsing prev path")
return nil, err
}
}
currPath, err := bh.CanonicalPath(storageDir, tenantID)
if err != nil {
el.AddRecoverable(ctx, clues.WrapWC(ctx, err, "creating list collection path"))
return nil, err
}
modTime := ptr.Val(list.GetLastModifiedDateTime())
lazyFetchCol := NewLazyFetchCollection(
bh,
currPath,
prevPath,
storageDir.Builder(),
su,
counter.Local())
lazyFetchCol.AddItem(
ptr.Val(list.GetId()),
modTime)
collection = lazyFetchCol
// Always use lazyFetchCol.
// In case we receive zero mod time from graph fallback to prefetchCol.
if modTime.IsZero() {
prefetchCol := NewPrefetchCollection(
bh,
currPath,
prevPath,
storageDir.Builder(),
ac,
scope,
su,
bpc.Options,
counter.Local())
prefetchCol.AddItem(
ptr.Val(list.GetId()),
modTime)
collection = prefetchCol
}
collections[storageDir.String()] = collection
currPaths[storageDir.String()] = currPath.String()
}
handleTombstones(ctx, bpc, tombstones, collections, counter, el)
// Build metadata path
pathPrefix, err := path.BuildMetadata(
tenantID,
bpc.ProtectedResource.ID(),
path.SharePointService,
path.ListsCategory,
false)
if err != nil {
return nil, clues.WrapWC(ctx, err, "making metadata path prefix").
Label(count.BadPathPrefix)
}
mdCol, err := graph.MakeMetadataCollection(
pathPrefix,
[]graph.MetadataCollectionEntry{
graph.NewMetadataEntry(metadata.PreviousPathFileName, currPaths),
},
su,
counter.Local())
if err != nil {
return nil, clues.WrapWC(ctx, err, "making metadata collection")
}
collections["metadata"] = mdCol
return collections, nil
}
func idAnd(ss ...string) []string {
id := []string{"id"}
if len(ss) == 0 {
return id
}
return append(id, ss...)
}
func handleTombstones(
ctx context.Context,
bpc inject.BackupProducerConfig,
tombstones map[string]string,
collections map[string]data.BackupCollection,
counter *count.Bus,
el *fault.Bus,
) {
for id, p := range tombstones {
if el.Failure() != nil {
return
}
ictx := clues.Add(ctx, "tombstone_id", id)
if collections[id] != nil {
err := clues.NewWC(ictx, "conflict: tombstone exists for a live collection").Label(count.CollectionTombstoneConflict)
el.AddRecoverable(ictx, err)
continue
}
if len(p) == 0 {
continue
}
prevPath, err := pathFromPrevString(p)
if err != nil {
err := clues.StackWC(ictx, err).Label(count.BadPrevPath)
logger.CtxErr(ictx, err).Error("parsing tombstone prev path")
continue
}
collections[id] = data.NewTombstoneCollection(prevPath, bpc.Options, counter.Local())
}
}