hand resource down to drive controller (#4436)

hands the backup resource into the drive collection for the handler to use to record as the siteID

---

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

- [x]  No

#### Type of change

- [x] 🐛 Bugfix

#### Issue(s)

* #3988

#### Test Plan

- [x] 💪 Manual
- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Keepers 2023-10-05 10:38:17 -06:00 committed by GitHub
parent 02db885c1e
commit c88b5764a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 305 additions and 267 deletions

View File

@ -120,7 +120,7 @@ func generateAndRestoreItems(
func getControllerAndVerifyResourceOwner(
ctx context.Context,
resourceOwner string,
protectedResource string,
pst path.ServiceType,
) (
*m365.Controller,
@ -150,12 +150,12 @@ func getControllerAndVerifyResourceOwner(
return nil, account.Account{}, nil, clues.Wrap(err, "connecting to graph api")
}
id, _, err := ctrl.PopulateProtectedResourceIDAndName(ctx, resourceOwner, nil)
pr, err := ctrl.PopulateProtectedResourceIDAndName(ctx, protectedResource, nil)
if err != nil {
return nil, account.Account{}, nil, clues.Wrap(err, "verifying user")
}
return ctrl, acct, ctrl.IDNameLookup.ProviderForID(id), nil
return ctrl, acct, pr, nil
}
type item struct {

View File

@ -1,8 +1,11 @@
package idname
import (
"context"
"fmt"
"strings"
"github.com/alcionai/clues"
"golang.org/x/exp/maps"
)
@ -21,7 +24,18 @@ type Provider interface {
Name() string
}
var _ Provider = &is{}
type GetResourceIDAndNamer interface {
GetResourceIDAndNameFrom(
ctx context.Context,
owner string,
cacher Cacher,
) (Provider, error)
}
var (
_ Provider = &is{}
_ clues.Concealer = &is{}
)
type is struct {
id string
@ -35,6 +49,24 @@ func NewProvider(id, name string) *is {
func (is is) ID() string { return is.id }
func (is is) Name() string { return is.name }
const isStringTmpl = "{id:%s, name:%s}"
func (is is) PlainString() string {
return fmt.Sprintf(isStringTmpl, clues.Hide(is.id), clues.Hide(is.name))
}
func (is is) Conceal() string {
return fmt.Sprintf(isStringTmpl, clues.Hide(is.id), clues.Hide(is.name))
}
func (is is) String() string {
return is.Conceal()
}
func (is is) Format(fs fmt.State, _ rune) {
fmt.Fprint(fs, is.Conceal())
}
type Cacher interface {
IDOf(name string) (string, bool)
NameOf(id string) (string, bool)

View File

@ -380,18 +380,18 @@ func (suite *SPCollectionIntgSuite) TestCreateSharePointCollection_Libraries() {
siteIDs = []string{siteID}
)
id, name, err := ctrl.PopulateProtectedResourceIDAndName(ctx, siteID, nil)
site, err := ctrl.PopulateProtectedResourceIDAndName(ctx, siteID, nil)
require.NoError(t, err, clues.ToCore(err))
sel := selectors.NewSharePointBackup(siteIDs)
sel.Include(sel.LibraryFolders([]string{"foo"}, selectors.PrefixMatch()))
sel.SetDiscreteOwnerIDName(id, name)
sel.SetDiscreteOwnerIDName(site.ID(), site.Name())
bpc := inject.BackupProducerConfig{
LastBackupVersion: version.NoBackup,
Options: control.DefaultOptions(),
ProtectedResource: inMock.NewProvider(id, name),
ProtectedResource: site,
Selector: sel.Selector,
}
@ -430,18 +430,18 @@ func (suite *SPCollectionIntgSuite) TestCreateSharePointCollection_Lists() {
siteIDs = []string{siteID}
)
id, name, err := ctrl.PopulateProtectedResourceIDAndName(ctx, siteID, nil)
site, err := ctrl.PopulateProtectedResourceIDAndName(ctx, siteID, nil)
require.NoError(t, err, clues.ToCore(err))
sel := selectors.NewSharePointBackup(siteIDs)
sel.Include(sel.Lists(selectors.Any()))
sel.SetDiscreteOwnerIDName(id, name)
sel.SetDiscreteOwnerIDName(site.ID(), site.Name())
bpc := inject.BackupProducerConfig{
LastBackupVersion: version.NoBackup,
Options: control.DefaultOptions(),
ProtectedResource: inMock.NewProvider(id, name),
ProtectedResource: site,
Selector: sel.Selector,
}
@ -516,18 +516,18 @@ func (suite *GroupsCollectionIntgSuite) TestCreateGroupsCollection_SharePoint()
groupIDs = []string{groupID}
)
id, name, err := ctrl.PopulateProtectedResourceIDAndName(ctx, groupID, nil)
group, err := ctrl.PopulateProtectedResourceIDAndName(ctx, groupID, nil)
require.NoError(t, err, clues.ToCore(err))
sel := selectors.NewGroupsBackup(groupIDs)
sel.Include(sel.LibraryFolders([]string{"test"}, selectors.PrefixMatch()))
sel.SetDiscreteOwnerIDName(id, name)
sel.SetDiscreteOwnerIDName(group.ID(), group.Name())
bpc := inject.BackupProducerConfig{
LastBackupVersion: version.NoBackup,
Options: control.DefaultOptions(),
ProtectedResource: inMock.NewProvider(id, name),
ProtectedResource: group,
Selector: sel.Selector,
}
@ -590,13 +590,13 @@ func (suite *GroupsCollectionIntgSuite) TestCreateGroupsCollection_SharePoint_In
groupIDs = []string{groupID}
)
id, name, err := ctrl.PopulateProtectedResourceIDAndName(ctx, groupID, nil)
group, err := ctrl.PopulateProtectedResourceIDAndName(ctx, groupID, nil)
require.NoError(t, err, clues.ToCore(err))
sel := selectors.NewGroupsBackup(groupIDs)
sel.Include(sel.LibraryFolders([]string{"test"}, selectors.PrefixMatch()))
sel.SetDiscreteOwnerIDName(id, name)
sel.SetDiscreteOwnerIDName(group.ID(), group.Name())
site, err := suite.connector.AC.Groups().GetRootSite(ctx, groupID)
require.NoError(t, err, clues.ToCore(err))
@ -626,7 +626,7 @@ func (suite *GroupsCollectionIntgSuite) TestCreateGroupsCollection_SharePoint_In
bpc := inject.BackupProducerConfig{
LastBackupVersion: version.NoBackup,
Options: control.DefaultOptions(),
ProtectedResource: inMock.NewProvider(id, name),
ProtectedResource: group,
Selector: sel.Selector,
MetadataCollections: mmc,
}

View File

@ -13,6 +13,7 @@ import (
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/spatialcurrent/go-lazy/pkg/lazy"
"github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/m365/collection/drive/metadata"
@ -39,6 +40,9 @@ var _ data.BackupCollection = &Collection{}
type Collection struct {
handler BackupHandler
// the protected resource represented in this collection.
protectedResource idname.Provider
// data is used to share data streams with the collection consumer
data chan data.Item
// folderPath indicates what level in the hierarchy this collection
@ -98,6 +102,7 @@ func pathToLocation(p path.Path) (*path.Builder, error) {
// NewCollection creates a Collection
func NewCollection(
handler BackupHandler,
resource idname.Provider,
currPath path.Path,
prevPath path.Path,
driveID string,
@ -123,6 +128,7 @@ func NewCollection(
c := newColl(
handler,
resource,
currPath,
prevPath,
driveID,
@ -140,6 +146,7 @@ func NewCollection(
func newColl(
handler BackupHandler,
resource idname.Provider,
currPath path.Path,
prevPath path.Path,
driveID string,
@ -151,6 +158,7 @@ func newColl(
) *Collection {
c := &Collection{
handler: handler,
protectedResource: resource,
folderPath: currPath,
prevPath: prevPath,
driveItems: map[string]models.DriveItemable{},
@ -551,7 +559,12 @@ func (oc *Collection) streamDriveItem(
return
}
itemInfo = oc.handler.AugmentItemInfo(itemInfo, item, itemSize, parentPath)
itemInfo = oc.handler.AugmentItemInfo(
itemInfo,
oc.protectedResource,
item,
itemSize,
parentPath)
ctx = clues.Add(ctx, "item_info", itemInfo)

View File

@ -207,6 +207,7 @@ func (suite *CollectionUnitSuite) TestCollection() {
coll, err := NewCollection(
mbh,
mbh.ProtectedResource,
folderPath,
nil,
"drive-id",
@ -328,6 +329,7 @@ func (suite *CollectionUnitSuite) TestCollectionReadError() {
coll, err := NewCollection(
mbh,
mbh.ProtectedResource,
folderPath,
nil,
"fakeDriveID",
@ -405,6 +407,7 @@ func (suite *CollectionUnitSuite) TestCollectionReadUnauthorizedErrorRetry() {
coll, err := NewCollection(
mbh,
mbh.ProtectedResource,
folderPath,
nil,
"fakeDriveID",
@ -460,6 +463,7 @@ func (suite *CollectionUnitSuite) TestCollectionPermissionBackupLatestModTime()
coll, err := NewCollection(
mbh,
mbh.ProtectedResource,
folderPath,
nil,
"drive-id",
@ -971,6 +975,7 @@ func (suite *CollectionUnitSuite) TestItemExtensions() {
coll, err := NewCollection(
mbh,
mbh.ProtectedResource,
folderPath,
nil,
driveID,

View File

@ -11,6 +11,7 @@ import (
"github.com/microsoftgraph/msgraph-sdk-go/models"
"golang.org/x/exp/maps"
"github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/common/prefixmatcher"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/data"
@ -49,7 +50,7 @@ type Collections struct {
handler BackupHandler
tenantID string
resourceOwner string
protectedResource idname.Provider
statusUpdater support.StatusUpdater
@ -69,14 +70,14 @@ type Collections struct {
func NewCollections(
bh BackupHandler,
tenantID string,
resourceOwner string,
protectedResource idname.Provider,
statusUpdater support.StatusUpdater,
ctrlOpts control.Options,
) *Collections {
return &Collections{
handler: bh,
tenantID: tenantID,
resourceOwner: resourceOwner,
protectedResource: protectedResource,
CollectionMap: map[string]map[string]*Collection{},
statusUpdater: statusUpdater,
ctrl: ctrlOpts,
@ -246,7 +247,7 @@ func (c *Collections) Get(
defer close(progressBar)
// Enumerate drives for the specified resourceOwner
pager := c.handler.NewDrivePager(c.resourceOwner, nil)
pager := c.handler.NewDrivePager(c.protectedResource.ID(), nil)
drives, err := api.GetAllDrives(ctx, pager)
if err != nil {
@ -384,6 +385,7 @@ func (c *Collections) Get(
col, err := NewCollection(
c.handler,
c.protectedResource,
nil, // delete the folder
prevPath,
driveID,
@ -420,6 +422,7 @@ func (c *Collections) Get(
coll, err := NewCollection(
c.handler,
c.protectedResource,
nil, // delete the drive
prevDrivePath,
driveID,
@ -605,6 +608,7 @@ func (c *Collections) handleDelete(
col, err := NewCollection(
c.handler,
c.protectedResource,
nil, // deletes the collection
prevPath,
driveID,
@ -789,6 +793,7 @@ func (c *Collections) UpdateCollections(
col, err := NewCollection(
c.handler,
c.protectedResource,
collectionPath,
prevPath,
driveID,

View File

@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/suite"
"golang.org/x/exp/maps"
"github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/common/prefixmatcher"
pmMock "github.com/alcionai/corso/src/internal/common/prefixmatcher/mock"
"github.com/alcionai/corso/src/internal/data"
@ -747,7 +748,7 @@ func (suite *OneDriveCollectionsUnitSuite) TestUpdateCollections() {
c := NewCollections(
&itemBackupHandler{api.Drives{}, user, tt.scope},
tenant,
user,
idname.NewProvider(user, user),
nil,
control.Options{ToggleFeatures: control.Toggles{}})
@ -2274,7 +2275,7 @@ func (suite *OneDriveCollectionsUnitSuite) TestGet() {
c := NewCollections(
mbh,
tenant,
user,
idname.NewProvider(user, user),
func(*support.ControllerOperationStatus) {},
control.Options{ToggleFeatures: control.Toggles{}})
@ -2648,7 +2649,7 @@ func (suite *OneDriveCollectionsUnitSuite) TestAddURLCacheToDriveCollections() {
c := NewCollections(
mbh,
"test-tenant",
"test-user",
idname.NewProvider("test-user", "test-user"),
nil,
control.Options{ToggleFeatures: control.Toggles{}})
@ -2660,6 +2661,7 @@ func (suite *OneDriveCollectionsUnitSuite) TestAddURLCacheToDriveCollections() {
for i := 0; i < collCount; i++ {
coll, err := NewCollection(
&itemBackupHandler{api.Drives{}, "test-user", anyFolder},
idname.NewProvider("", ""),
nil,
nil,
driveID,

View File

@ -5,6 +5,7 @@ import (
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/path"
@ -12,12 +13,13 @@ import (
func augmentItemInfo(
dii details.ItemInfo,
resource idname.Provider,
service path.ServiceType,
item models.DriveItemable,
size int64,
parentPath *path.Builder,
) details.ItemInfo {
var driveName, siteID, driveID, weburl, creatorEmail string
var driveName, driveID, creatorEmail string
// TODO: we rely on this info for details/restore lookups,
// so if it's nil we have an issue, and will need an alternative
@ -38,19 +40,6 @@ func augmentItemInfo(
}
}
if service == path.SharePointService ||
service == path.GroupsService {
gsi := item.GetSharepointIds()
if gsi != nil {
siteID = ptr.Val(gsi.GetSiteId())
weburl = ptr.Val(gsi.GetSiteUrl())
if len(weburl) == 0 {
weburl = constructWebURL(item.GetAdditionalData())
}
}
}
if item.GetParentReference() != nil {
driveID = ptr.Val(item.GetParentReference().GetDriveId())
driveName = strings.TrimSpace(ptr.Val(item.GetParentReference().GetName()))
@ -84,9 +73,9 @@ func augmentItemInfo(
Modified: ptr.Val(item.GetLastModifiedDateTime()),
Owner: creatorEmail,
ParentPath: pps,
SiteID: siteID,
SiteID: resource.ID(),
Size: size,
WebURL: weburl,
WebURL: resource.Name(),
}
case path.GroupsService:
@ -99,9 +88,9 @@ func augmentItemInfo(
Modified: ptr.Val(item.GetLastModifiedDateTime()),
Owner: creatorEmail,
ParentPath: pps,
SiteID: siteID,
SiteID: resource.ID(),
Size: size,
WebURL: weburl,
WebURL: resource.Name(),
}
}

View File

@ -6,6 +6,7 @@ import (
"github.com/microsoftgraph/msgraph-sdk-go/drives"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control"
"github.com/alcionai/corso/src/pkg/path"
@ -20,6 +21,7 @@ type ItemInfoAugmenter interface {
// and kiota drops any SetSize update.
AugmentItemInfo(
dii details.ItemInfo,
resource idname.Provider,
item models.DriveItemable,
size int64,
parentPath *path.Builder,

View File

@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/common/prefixmatcher"
"github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/internal/tester"
@ -267,7 +268,7 @@ func (suite *OneDriveIntgSuite) TestOneDriveNewCollections() {
colls := NewCollections(
&itemBackupHandler{suite.ac.Drives(), test.user, scope},
creds.AzureTenantID,
test.user,
idname.NewProvider(test.user, test.user),
service.updateStatus,
control.Options{
ToggleFeatures: control.Toggles{},

View File

@ -8,6 +8,7 @@ import (
"github.com/microsoftgraph/msgraph-sdk-go/drives"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/idname"
odConsts "github.com/alcionai/corso/src/internal/m365/service/onedrive/consts"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control"
@ -96,11 +97,12 @@ func (h itemBackupHandler) NewItemPager(
func (h itemBackupHandler) AugmentItemInfo(
dii details.ItemInfo,
resource idname.Provider,
item models.DriveItemable,
size int64,
parentPath *path.Builder,
) details.ItemInfo {
return augmentItemInfo(dii, path.OneDriveService, item, size, parentPath)
return augmentItemInfo(dii, resource, path.OneDriveService, item, size, parentPath)
}
func (h itemBackupHandler) FormatDisplayPath(
@ -173,11 +175,12 @@ func (h itemRestoreHandler) NewDrivePager(
// and kiota drops any SetSize update.
func (h itemRestoreHandler) AugmentItemInfo(
dii details.ItemInfo,
resource idname.Provider,
item models.DriveItemable,
size int64,
parentPath *path.Builder,
) details.ItemInfo {
return augmentItemInfo(dii, path.OneDriveService, item, size, parentPath)
return augmentItemInfo(dii, resource, path.OneDriveService, item, size, parentPath)
}
func (h itemRestoreHandler) DeleteItem(

View File

@ -3,13 +3,12 @@ package drive
import (
"context"
"net/http"
"strings"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/drives"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/common/idname"
odConsts "github.com/alcionai/corso/src/internal/m365/service/onedrive/consts"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control"
@ -101,44 +100,12 @@ func (h libraryBackupHandler) NewItemPager(
func (h libraryBackupHandler) AugmentItemInfo(
dii details.ItemInfo,
resource idname.Provider,
item models.DriveItemable,
size int64,
parentPath *path.Builder,
) details.ItemInfo {
return augmentItemInfo(dii, h.service, item, size, parentPath)
}
// constructWebURL is a helper function for recreating the webURL
// for the originating SharePoint site. Uses the additionalData map
// from a models.DriveItemable that possesses a downloadURL within the map.
// Returns "" if the map is nil or key is not present.
func constructWebURL(adtl map[string]any) string {
var (
desiredKey = "@microsoft.graph.downloadUrl"
sep = `/_layouts`
url string
)
if adtl == nil {
return url
}
r := adtl[desiredKey]
point, ok := r.(*string)
if !ok {
return url
}
value := ptr.Val(point)
if len(value) == 0 {
return url
}
temp := strings.Split(value, sep)
url = temp[0]
return url
return augmentItemInfo(dii, resource, h.service, item, size, parentPath)
}
func (h libraryBackupHandler) FormatDisplayPath(
@ -208,11 +175,12 @@ func (h libraryRestoreHandler) NewDrivePager(
func (h libraryRestoreHandler) AugmentItemInfo(
dii details.ItemInfo,
resource idname.Provider,
item models.DriveItemable,
size int64,
parentPath *path.Builder,
) details.ItemInfo {
return augmentItemInfo(dii, h.service, item, size, parentPath)
return augmentItemInfo(dii, resource, h.service, item, size, parentPath)
}
func (h libraryRestoreHandler) DeleteItem(

View File

@ -271,7 +271,7 @@ func restoreItem(
itemInfo, err := restoreV0File(
ctx,
rh,
rcc.RestoreConfig,
rcc,
drivePath,
fibn,
restoreFolderID,
@ -377,7 +377,7 @@ func restoreItem(
func restoreV0File(
ctx context.Context,
rh RestoreHandler,
restoreCfg control.RestoreConfig,
rcc inject.RestoreConsumerConfig,
drivePath *path.DrivePath,
fibn data.FetchItemByNamer,
restoreFolderID string,
@ -388,7 +388,7 @@ func restoreV0File(
) (details.ItemInfo, error) {
_, itemInfo, err := restoreFile(
ctx,
restoreCfg,
rcc,
rh,
fibn,
itemData.ID(),
@ -423,7 +423,7 @@ func restoreV1File(
itemID, itemInfo, err := restoreFile(
ctx,
rcc.RestoreConfig,
rcc,
rh,
fibn,
trimmedName,
@ -509,7 +509,7 @@ func restoreV6File(
itemID, itemInfo, err := restoreFile(
ctx,
rcc.RestoreConfig,
rcc,
rh,
fibn,
meta.FileName,
@ -711,7 +711,7 @@ type itemRestorer interface {
// restoreFile will create a new item in the specified `parentFolderID` and upload the data.Item
func restoreFile(
ctx context.Context,
restoreCfg control.RestoreConfig,
rcc inject.RestoreConsumerConfig,
ir itemRestorer,
fibn data.FetchItemByNamer,
name string,
@ -743,7 +743,7 @@ func restoreFile(
log := logger.Ctx(ctx).With("collision_key", clues.Hide(collisionKey))
log.Debug("item collision")
if restoreCfg.OnCollision == control.Skip {
if rcc.RestoreConfig.OnCollision == control.Skip {
ctr.Inc(count.CollisionSkip)
log.Debug("skipping item with collision")
@ -751,7 +751,7 @@ func restoreFile(
}
collision = dci
shouldDeleteOriginal = restoreCfg.OnCollision == control.Replace && !dci.IsFolder
shouldDeleteOriginal = rcc.RestoreConfig.OnCollision == control.Replace && !dci.IsFolder
}
// drive items do not support PUT requests on the drive item data, so
@ -850,7 +850,12 @@ func restoreFile(
defer closeProgressBar()
dii := ir.AugmentItemInfo(details.ItemInfo{}, newItem, written, nil)
dii := ir.AugmentItemInfo(
details.ItemInfo{},
rcc.ProtectedResource,
newItem,
written,
nil)
if shouldDeleteOriginal {
ctr.Inc(count.CollisionReplace)

View File

@ -38,7 +38,7 @@ func CollectLibraries(
colls = drive.NewCollections(
bh,
tenantID,
bpc.ProtectedResource.ID(),
bpc.ProtectedResource,
su,
bpc.Options)
)

View File

@ -36,7 +36,7 @@ type Controller struct {
tenant string
credentials account.M365Config
ownerLookup getOwnerIDAndNamer
ownerLookup idname.GetResourceIDAndNamer
// maps of resource owner ids to names, and names to ids.
// not guaranteed to be populated, only here as a post-population
// reference for processes that choose to populate the values.
@ -229,38 +229,24 @@ type getIDAndNamer interface {
)
}
var _ getOwnerIDAndNamer = &resourceClient{}
var _ idname.GetResourceIDAndNamer = &resourceClient{}
type getOwnerIDAndNamer interface {
getOwnerIDAndNameFrom(
ctx context.Context,
discovery api.Client,
owner string,
ins idname.Cacher,
) (
ownerID string,
ownerName string,
err error,
)
}
// getOwnerIDAndNameFrom looks up the owner's canonical id and display name.
// If the owner is present in the idNameSwapper, then that interface's id and
// GetResourceIDAndNameFrom looks up the resource's canonical id and display name.
// If the resource is present in the idNameSwapper, then that interface's id and
// name values are returned. As a fallback, the resource calls the discovery
// api to fetch the user or site using the owner value. This fallback assumes
// that the owner is a well formed ID or display name of appropriate design
// api to fetch the user or site using the resource value. This fallback assumes
// that the resource is a well formed ID or display name of appropriate design
// (PrincipalName for users, WebURL for sites).
func (r resourceClient) getOwnerIDAndNameFrom(
func (r resourceClient) GetResourceIDAndNameFrom(
ctx context.Context,
discovery api.Client,
owner string,
ins idname.Cacher,
) (string, string, error) {
) (idname.Provider, error) {
if ins != nil {
if n, ok := ins.NameOf(owner); ok {
return owner, n, nil
return idname.NewProvider(owner, n), nil
} else if i, ok := ins.IDOf(owner); ok {
return i, owner, nil
return idname.NewProvider(i, owner), nil
}
}
@ -274,17 +260,17 @@ func (r resourceClient) getOwnerIDAndNameFrom(
id, name, err = r.getter.GetIDAndName(ctx, owner, api.CallConfig{})
if err != nil {
if graph.IsErrUserNotFound(err) {
return "", "", clues.Stack(graph.ErrResourceOwnerNotFound, err)
return nil, clues.Stack(graph.ErrResourceOwnerNotFound, err)
}
return "", "", err
return nil, err
}
if len(id) == 0 || len(name) == 0 {
return "", "", clues.Stack(graph.ErrResourceOwnerNotFound)
return nil, clues.Stack(graph.ErrResourceOwnerNotFound)
}
return id, name, nil
return idname.NewProvider(id, name), nil
}
// PopulateProtectedResourceIDAndName takes the provided owner identifier and produces
@ -297,15 +283,15 @@ func (r resourceClient) getOwnerIDAndNameFrom(
// data gets stored inside the controller instance for later re-use.
func (ctrl *Controller) PopulateProtectedResourceIDAndName(
ctx context.Context,
owner string, // input value, can be either id or name
resourceID string, // input value, can be either id or name
ins idname.Cacher,
) (string, string, error) {
id, name, err := ctrl.ownerLookup.getOwnerIDAndNameFrom(ctx, ctrl.AC, owner, ins)
) (idname.Provider, error) {
pr, err := ctrl.ownerLookup.GetResourceIDAndNameFrom(ctx, resourceID, ins)
if err != nil {
return "", "", clues.Wrap(err, "identifying resource owner")
return nil, clues.Wrap(err, "identifying resource owner")
}
ctrl.IDNameLookup = idname.NewCache(map[string]string{id: name})
ctrl.IDNameLookup = idname.NewCache(map[string]string{pr.ID(): pr.Name()})
return id, name, nil
return pr, nil
}

View File

@ -66,113 +66,125 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom() {
table := []struct {
name string
owner string
protectedResource string
ins inMock.Cache
rc *resourceClient
expectID string
expectName string
expectErr require.ErrorAssertionFunc
expectNil require.ValueAssertionFunc
}{
{
name: "nil ins",
owner: id,
protectedResource: id,
rc: lookup,
expectID: id,
expectName: name,
expectErr: require.NoError,
expectNil: require.NotNil,
},
{
name: "nil ins no lookup",
owner: id,
protectedResource: id,
rc: noLookup,
expectID: "",
expectName: "",
expectErr: require.Error,
expectNil: require.Nil,
},
{
name: "only id map with owner id",
owner: id,
protectedResource: id,
ins: inMock.NewCache(itn, nil),
rc: noLookup,
expectID: id,
expectName: name,
expectErr: require.NoError,
expectNil: require.NotNil,
},
{
name: "only name map with owner id",
owner: id,
protectedResource: id,
ins: inMock.NewCache(nil, nti),
rc: noLookup,
expectID: "",
expectName: "",
expectErr: require.Error,
expectNil: require.Nil,
},
{
name: "only name map with owner id and lookup",
owner: id,
protectedResource: id,
ins: inMock.NewCache(nil, nti),
rc: lookup,
expectID: id,
expectName: name,
expectErr: require.NoError,
expectNil: require.NotNil,
},
{
name: "only id map with owner name",
owner: name,
protectedResource: name,
ins: inMock.NewCache(itn, nil),
rc: lookup,
expectID: id,
expectName: name,
expectErr: require.NoError,
expectNil: require.NotNil,
},
{
name: "only name map with owner name",
owner: name,
protectedResource: name,
ins: inMock.NewCache(nil, nti),
rc: noLookup,
expectID: id,
expectName: name,
expectErr: require.NoError,
expectNil: require.NotNil,
},
{
name: "only id map with owner name",
owner: name,
protectedResource: name,
ins: inMock.NewCache(itn, nil),
rc: noLookup,
expectID: "",
expectName: "",
expectErr: require.Error,
expectNil: require.Nil,
},
{
name: "only id map with owner name and lookup",
owner: name,
protectedResource: name,
ins: inMock.NewCache(itn, nil),
rc: lookup,
expectID: id,
expectName: name,
expectErr: require.NoError,
expectNil: require.NotNil,
},
{
name: "both maps with owner id",
owner: id,
protectedResource: id,
ins: inMock.NewCache(itn, nti),
rc: noLookup,
expectID: id,
expectName: name,
expectErr: require.NoError,
expectNil: require.NotNil,
},
{
name: "both maps with owner name",
owner: name,
protectedResource: name,
ins: inMock.NewCache(itn, nti),
rc: noLookup,
expectID: id,
expectName: name,
expectErr: require.NoError,
expectNil: require.NotNil,
},
{
name: "non-matching maps with owner id",
owner: id,
protectedResource: id,
ins: inMock.NewCache(
map[string]string{"foo": "bar"},
map[string]string{"fnords": "smarf"}),
@ -180,10 +192,11 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom() {
expectID: "",
expectName: "",
expectErr: require.Error,
expectNil: require.Nil,
},
{
name: "non-matching with owner name",
owner: name,
protectedResource: name,
ins: inMock.NewCache(
map[string]string{"foo": "bar"},
map[string]string{"fnords": "smarf"}),
@ -191,10 +204,11 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom() {
expectID: "",
expectName: "",
expectErr: require.Error,
expectNil: require.Nil,
},
{
name: "non-matching maps with owner id and lookup",
owner: id,
protectedResource: id,
ins: inMock.NewCache(
map[string]string{"foo": "bar"},
map[string]string{"fnords": "smarf"}),
@ -202,10 +216,11 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom() {
expectID: id,
expectName: name,
expectErr: require.NoError,
expectNil: require.NotNil,
},
{
name: "non-matching with owner name and lookup",
owner: name,
protectedResource: name,
ins: inMock.NewCache(
map[string]string{"foo": "bar"},
map[string]string{"fnords": "smarf"}),
@ -213,6 +228,7 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom() {
expectID: id,
expectName: name,
expectErr: require.NoError,
expectNil: require.NotNil,
},
}
for _, test := range table {
@ -224,10 +240,16 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom() {
ctrl := &Controller{ownerLookup: test.rc}
rID, rName, err := ctrl.PopulateProtectedResourceIDAndName(ctx, test.owner, test.ins)
resource, err := ctrl.PopulateProtectedResourceIDAndName(ctx, test.protectedResource, test.ins)
test.expectErr(t, err, clues.ToCore(err))
assert.Equal(t, test.expectID, rID, "id")
assert.Equal(t, test.expectName, rName, "name")
test.expectNil(t, resource)
if err != nil {
return
}
assert.Equal(t, test.expectID, resource.ID(), "id")
assert.Equal(t, test.expectName, resource.Name(), "name")
})
}
}
@ -1362,15 +1384,15 @@ func (suite *ControllerIntegrationSuite) TestBackup_CreatesPrefixCollections() {
start = time.Now()
)
id, name, err := backupCtrl.PopulateProtectedResourceIDAndName(ctx, backupSel.DiscreteOwner, nil)
resource, err := backupCtrl.PopulateProtectedResourceIDAndName(ctx, backupSel.DiscreteOwner, nil)
require.NoError(t, err, clues.ToCore(err))
backupSel.SetDiscreteOwnerIDName(id, name)
backupSel.SetDiscreteOwnerIDName(resource.ID(), resource.Name())
bpc := inject.BackupProducerConfig{
LastBackupVersion: version.NoBackup,
Options: control.DefaultOptions(),
ProtectedResource: inMock.NewProvider(id, name),
ProtectedResource: resource,
Selector: backupSel,
}

View File

@ -99,8 +99,7 @@ func (ctrl Controller) PopulateProtectedResourceIDAndName(
ctx context.Context,
protectedResource string, // input value, can be either id or name
ins idname.Cacher,
) (string, string, error) {
return ctrl.ProtectedResourceID,
ctrl.ProtectedResourceName,
) (idname.Provider, error) {
return idname.NewProvider(ctrl.ProtectedResourceID, ctrl.ProtectedResourceName),
ctrl.ProtectedResourceErr
}

View File

@ -93,7 +93,7 @@ func ProduceBackupCollections(
}
for _, s := range sites {
pr := idname.NewProvider(ptr.Val(s.GetId()), ptr.Val(s.GetName()))
pr := idname.NewProvider(ptr.Val(s.GetId()), ptr.Val(s.GetWebUrl()))
sbpc := inject.BackupProducerConfig{
LastBackupVersion: bpc.LastBackupVersion,
Options: bpc.Options,

View File

@ -51,7 +51,7 @@ func ProduceBackupCollections(
nc := drive.NewCollections(
drive.NewItemBackupHandler(ac.Drives(), bpc.ProtectedResource.ID(), scope),
tenant,
bpc.ProtectedResource.ID(),
bpc.ProtectedResource,
su,
bpc.Options)

View File

@ -8,6 +8,7 @@ import (
"github.com/microsoftgraph/msgraph-sdk-go/drives"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/idname"
odConsts "github.com/alcionai/corso/src/internal/m365/service/onedrive/consts"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control"
@ -34,7 +35,7 @@ type BackupHandler struct {
CanonPathFn canonPather
CanonPathErr error
ResourceOwner string
ProtectedResource idname.Provider
Service path.ServiceType
Category path.CategoryType
@ -60,7 +61,7 @@ func DefaultOneDriveBH(resourceOwner string) *BackupHandler {
PathPrefixFn: defaultOneDrivePathPrefixer,
MetadataPathPrefixFn: defaultOneDriveMetadataPathPrefixer,
CanonPathFn: defaultOneDriveCanonPather,
ResourceOwner: resourceOwner,
ProtectedResource: idname.NewProvider(resourceOwner, resourceOwner),
Service: path.OneDriveService,
Category: path.FilesCategory,
LocationIDFn: defaultOneDriveLocationIDer,
@ -80,7 +81,7 @@ func DefaultSharePointBH(resourceOwner string) *BackupHandler {
PathPrefixFn: defaultSharePointPathPrefixer,
MetadataPathPrefixFn: defaultSharePointMetadataPathPrefixer,
CanonPathFn: defaultSharePointCanonPather,
ResourceOwner: resourceOwner,
ProtectedResource: idname.NewProvider(resourceOwner, resourceOwner),
Service: path.SharePointService,
Category: path.LibrariesCategory,
LocationIDFn: defaultSharePointLocationIDer,
@ -90,7 +91,7 @@ func DefaultSharePointBH(resourceOwner string) *BackupHandler {
}
func (h BackupHandler) PathPrefix(tID, driveID string) (path.Path, error) {
pp, err := h.PathPrefixFn(tID, h.ResourceOwner, driveID)
pp, err := h.PathPrefixFn(tID, h.ProtectedResource.ID(), driveID)
if err != nil {
return nil, err
}
@ -99,7 +100,7 @@ func (h BackupHandler) PathPrefix(tID, driveID string) (path.Path, error) {
}
func (h BackupHandler) MetadataPathPrefix(tID string) (path.Path, error) {
pp, err := h.MetadataPathPrefixFn(tID, h.ResourceOwner)
pp, err := h.MetadataPathPrefixFn(tID, h.ProtectedResource.ID())
if err != nil {
return nil, err
}
@ -108,7 +109,7 @@ func (h BackupHandler) MetadataPathPrefix(tID string) (path.Path, error) {
}
func (h BackupHandler) CanonicalPath(pb *path.Builder, tID string) (path.Path, error) {
cp, err := h.CanonPathFn(pb, tID, h.ResourceOwner)
cp, err := h.CanonPathFn(pb, tID, h.ProtectedResource.ID())
if err != nil {
return nil, err
}
@ -136,7 +137,13 @@ func (h BackupHandler) NewLocationIDer(driveID string, elems ...string) details.
return h.LocationIDFn(driveID, elems...)
}
func (h BackupHandler) AugmentItemInfo(details.ItemInfo, models.DriveItemable, int64, *path.Builder) details.ItemInfo {
func (h BackupHandler) AugmentItemInfo(
details.ItemInfo,
idname.Provider,
models.DriveItemable,
int64,
*path.Builder,
) details.ItemInfo {
return h.ItemInfo
}
@ -308,6 +315,7 @@ func (h RestoreHandler) NewDrivePager(string, []string) api.Pager[models.Driveab
func (h *RestoreHandler) AugmentItemInfo(
details.ItemInfo,
idname.Provider,
models.DriveItemable,
int64,
*path.Builder,

View File

@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/m365/collection/drive"
odConsts "github.com/alcionai/corso/src/internal/m365/service/onedrive/consts"
"github.com/alcionai/corso/src/internal/tester"
@ -103,7 +104,7 @@ func (suite *LibrariesBackupUnitSuite) TestUpdateCollections() {
c := drive.NewCollections(
drive.NewLibraryBackupHandler(api.Drives{}, siteID, test.scope, path.SharePointService),
tenantID,
siteID,
idname.NewProvider(siteID, siteID),
nil,
control.DefaultOptions())

View File

@ -34,7 +34,7 @@ func ControllerWithSelector(
t.FailNow()
}
id, name, err := ctrl.PopulateProtectedResourceIDAndName(ctx, sel.DiscreteOwner, ins)
resource, err := ctrl.PopulateProtectedResourceIDAndName(ctx, sel.DiscreteOwner, ins)
if !assert.NoError(t, err, clues.ToCore(err)) {
if onFail != nil {
onFail()
@ -43,7 +43,7 @@ func ControllerWithSelector(
t.FailNow()
}
sel = sel.SetDiscreteOwnerIDName(id, name)
sel = sel.SetDiscreteOwnerIDName(resource.ID(), resource.Name())
return ctrl, sel
}

View File

@ -109,10 +109,7 @@ type (
ctx context.Context,
owner string, // input value, can be either id or name
ins idname.Cacher,
) (
id, name string,
err error,
)
) (idname.Provider, error)
}
RepoMaintenancer interface {

View File

@ -362,12 +362,12 @@ func chooseRestoreResource(
return orig, nil
}
id, name, err := pprian.PopulateProtectedResourceIDAndName(
resource, err := pprian.PopulateProtectedResourceIDAndName(
ctx,
restoreCfg.ProtectedResource,
nil)
return idname.NewProvider(id, name), clues.Stack(err).OrNil()
return resource, clues.Stack(err).OrNil()
}
// ---------------------------------------------------------------------------

View File

@ -550,7 +550,7 @@ func ControllerWithSelector(
t.FailNow()
}
id, name, err := ctrl.PopulateProtectedResourceIDAndName(ctx, sel.DiscreteOwner, ins)
resource, err := ctrl.PopulateProtectedResourceIDAndName(ctx, sel.DiscreteOwner, ins)
if !assert.NoError(t, err, clues.ToCore(err)) {
if onFail != nil {
onFail(t, ctx)
@ -559,7 +559,7 @@ func ControllerWithSelector(
t.FailNow()
}
sel = sel.SetDiscreteOwnerIDName(id, name)
sel = sel.SetDiscreteOwnerIDName(resource.ID(), resource.Name())
return ctrl, sel
}

View File

@ -76,13 +76,13 @@ func (r repository) NewBackupWithLookup(
return operations.BackupOperation{}, clues.Wrap(err, "connecting to m365")
}
ownerID, ownerName, err := r.Provider.PopulateProtectedResourceIDAndName(ctx, sel.DiscreteOwner, ins)
resource, err := r.Provider.PopulateProtectedResourceIDAndName(ctx, sel.DiscreteOwner, ins)
if err != nil {
return operations.BackupOperation{}, clues.Wrap(err, "resolving resource owner details")
}
// TODO: retrieve display name from gc
sel = sel.SetDiscreteOwnerIDName(ownerID, ownerName)
sel = sel.SetDiscreteOwnerIDName(resource.ID(), resource.Name())
return operations.NewBackupOperation(
ctx,