enable restore path creation for sharepoint lists (#4922)

Enable restore path creation for sharepoint lists
Changes previously approved in: https://github.com/alcionai/corso/pull/4855

#### Does this PR need a docs update or release note?
- [x]  No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [x] 🌻 Feature

#### Issue(s)
#4754 

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Hitesh Pattanayak 2023-12-22 22:12:53 +05:30 committed by GitHub
parent d6e9a2112d
commit 98e8cac374
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 101 additions and 30 deletions

View File

@ -142,7 +142,8 @@ func makeRestorePathsForEntry(
// * OneDrive/SharePoint (needs drive information) // * OneDrive/SharePoint (needs drive information)
switch true { switch true {
case ent.Exchange != nil || case ent.Exchange != nil ||
(ent.Groups != nil && ent.Groups.ItemType == details.GroupsChannelMessage): (ent.Groups != nil && ent.Groups.ItemType == details.GroupsChannelMessage) ||
(ent.SharePoint != nil && ent.SharePoint.ItemType == details.SharePointList):
// TODO(ashmrtn): Eventually make Events have it's own function to handle // TODO(ashmrtn): Eventually make Events have it's own function to handle
// setting the restore destination properly. // setting the restore destination properly.
res.RestorePath, err = basicLocationPath(repoRef, locRef) res.RestorePath, err = basicLocationPath(repoRef, locRef)

View File

@ -50,16 +50,19 @@ func (suite *RestorePathTransformerUnitSuite) TestGetPaths() {
driveID = "some-drive-id" driveID = "some-drive-id"
siteID = "some-site-id" siteID = "some-site-id"
extraItemName = "some-item" extraItemName = "some-item"
listName = "list1"
SharePointRootItemPath = testdata.SharePointRootPath.MustAppend(extraItemName, true) SharePointRootItemPath = testdata.SharePointRootPath.MustAppend(extraItemName, true)
SharePointListItemPath = testdata.SharePointListPath.MustAppend(listName, true)
GroupsRootItemPath = testdata.GroupsRootPath.MustAppend(extraItemName, true) GroupsRootItemPath = testdata.GroupsRootPath.MustAppend(extraItemName, true)
) )
table := []struct { table := []struct {
name string name string
backupVersion int backupVersion int
input []*details.Entry input []*details.Entry
expectErr assert.ErrorAssertionFunc expectErr assert.ErrorAssertionFunc
expected []expectPaths expected []expectPaths
isSharepointList bool
}{ }{
{ {
name: "Groups List Errors v9", name: "Groups List Errors v9",
@ -157,13 +160,13 @@ func (suite *RestorePathTransformerUnitSuite) TestGetPaths() {
}, },
}, },
{ {
name: "SharePoint List Errors", name: "SharePoint List, item in root",
// No version bump for the change so we always have to check for this. // No version bump for the change so we always have to check for this.
backupVersion: version.All8MigrateUserPNToID, backupVersion: version.All8MigrateUserPNToID,
input: []*details.Entry{ input: []*details.Entry{
{ {
RepoRef: SharePointRootItemPath.RR.String(), RepoRef: SharePointListItemPath.RR.String(),
LocationRef: SharePointRootItemPath.Loc.String(), LocationRef: SharePointListItemPath.Loc.String(),
ItemInfo: details.ItemInfo{ ItemInfo: details.ItemInfo{
SharePoint: &details.SharePointInfo{ SharePoint: &details.SharePointInfo{
ItemType: details.SharePointList, ItemType: details.SharePointList,
@ -171,7 +174,14 @@ func (suite *RestorePathTransformerUnitSuite) TestGetPaths() {
}, },
}, },
}, },
expectErr: assert.Error, expected: []expectPaths{
{
storage: SharePointListItemPath.RR.String(),
restore: toRestore(SharePointListItemPath.RR),
},
},
expectErr: assert.NoError,
isSharepointList: true,
}, },
{ {
name: "SharePoint Page Errors", name: "SharePoint Page Errors",
@ -413,12 +423,23 @@ func (suite *RestorePathTransformerUnitSuite) TestGetPaths() {
for _, e := range test.expected { for _, e := range test.expected {
tmp := path.RestorePaths{} tmp := path.RestorePaths{}
p, err := path.FromDataLayerPath(e.storage, true) var p path.Path
var err error
if test.isSharepointList {
p, err = path.PrefixOrPathFromDataLayerPath(e.storage, true)
} else {
p, err = path.FromDataLayerPath(e.storage, true)
}
require.NoError(t, err, "parsing expected storage path", clues.ToCore(err)) require.NoError(t, err, "parsing expected storage path", clues.ToCore(err))
tmp.StoragePath = p tmp.StoragePath = p
p, err = path.FromDataLayerPath(e.restore, false) if test.isSharepointList {
p, err = path.PrefixOrPathFromDataLayerPath(e.restore, false)
} else {
p, err = path.FromDataLayerPath(e.restore, false)
}
require.NoError(t, err, "parsing expected restore path", clues.ToCore(err)) require.NoError(t, err, "parsing expected restore path", clues.ToCore(err))
if e.isRestorePrefix { if e.isRestorePrefix {

View File

@ -16,8 +16,16 @@ import (
// mustParsePath takes a string representing a resource path and returns a path // mustParsePath takes a string representing a resource path and returns a path
// instance. Panics if the path cannot be parsed. Useful for simple variable // instance. Panics if the path cannot be parsed. Useful for simple variable
// assignments. // assignments.
func mustParsePath(ref string, isItem bool) path.Path { func mustParsePath(ref string, isItem, isSharepointList bool) path.Path {
p, err := path.FromDataLayerPath(ref, isItem) var p path.Path
var err error
if isSharepointList {
p, err = path.PrefixOrPathFromDataLayerPath(ref, isItem)
} else {
p, err = path.FromDataLayerPath(ref, isItem)
}
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -47,7 +55,7 @@ func locFromRepo(rr path.Path, isItem bool) *path.Builder {
if rr.Service() == path.GroupsService { if rr.Service() == path.GroupsService {
loc = loc.PopFront().PopFront().PopFront() loc = loc.PopFront().PopFront().PopFront()
} else if rr.Service() == path.OneDriveService || rr.Category() == path.LibrariesCategory { } else if rr.Service() == path.OneDriveService || rr.Category() == path.LibrariesCategory || rr.Category() == path.ListsCategory {
loc = loc.PopFront() loc = loc.PopFront()
} }
@ -117,9 +125,12 @@ func (p repoRefAndLocRef) locationAsRepoRef() path.Path {
return res return res
} }
func mustPathRep(ref string, isItem bool) repoRefAndLocRef { func mustPathRep(ref string, isItem, isSharepointList bool) repoRefAndLocRef {
var rr path.Path
var err error
res := repoRefAndLocRef{} res := repoRefAndLocRef{}
tmp := mustParsePath(ref, isItem) tmp := mustParsePath(ref, isItem, isSharepointList)
// Now append stuff to the RepoRef elements so we have distinct LocationRef // Now append stuff to the RepoRef elements so we have distinct LocationRef
// and RepoRef elements to simulate using IDs in the path instead of display // and RepoRef elements to simulate using IDs in the path instead of display
@ -133,12 +144,21 @@ func mustPathRep(ref string, isItem bool) repoRefAndLocRef {
rrPB = rrPB.Append(tmp.Item() + fileSuffix) rrPB = rrPB.Append(tmp.Item() + fileSuffix)
} }
rr, err := rrPB.ToDataLayerPath( if isSharepointList {
tmp.Tenant(), rr, err = rrPB.ToDataLayerSharePointListPath(
tmp.ProtectedResource(), tmp.Tenant(),
tmp.Service(), tmp.ProtectedResource(),
tmp.Category(), tmp.Category(),
isItem) isItem)
} else {
rr, err = rrPB.ToDataLayerPath(
tmp.Tenant(),
tmp.ProtectedResource(),
tmp.Service(),
tmp.Category(),
isItem)
}
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -166,7 +186,7 @@ var (
Time3 = time.Date(2023, 9, 21, 10, 0, 0, 0, time.UTC) Time3 = time.Date(2023, 9, 21, 10, 0, 0, 0, time.UTC)
Time4 = time.Date(2023, 10, 21, 10, 0, 0, 0, time.UTC) Time4 = time.Date(2023, 10, 21, 10, 0, 0, 0, time.UTC)
ExchangeEmailInboxPath = mustPathRep("tenant-id/exchange/user-id/email/Inbox", false) ExchangeEmailInboxPath = mustPathRep("tenant-id/exchange/user-id/email/Inbox", false, false)
ExchangeEmailBasePath = ExchangeEmailInboxPath.MustAppend("subfolder", false) ExchangeEmailBasePath = ExchangeEmailInboxPath.MustAppend("subfolder", false)
ExchangeEmailBasePath2 = ExchangeEmailInboxPath.MustAppend("othersubfolder/", false) ExchangeEmailBasePath2 = ExchangeEmailInboxPath.MustAppend("othersubfolder/", false)
ExchangeEmailBasePath3 = ExchangeEmailBasePath2.MustAppend("subsubfolder", false) ExchangeEmailBasePath3 = ExchangeEmailBasePath2.MustAppend("subsubfolder", false)
@ -314,7 +334,7 @@ var (
}, },
} }
ExchangeContactsRootPath = mustPathRep("tenant-id/exchange/user-id/contacts/contacts", false) ExchangeContactsRootPath = mustPathRep("tenant-id/exchange/user-id/contacts/contacts", false, false)
ExchangeContactsBasePath = ExchangeContactsRootPath.MustAppend("contacts", false) ExchangeContactsBasePath = ExchangeContactsRootPath.MustAppend("contacts", false)
ExchangeContactsBasePath2 = ExchangeContactsRootPath.MustAppend("morecontacts", false) ExchangeContactsBasePath2 = ExchangeContactsRootPath.MustAppend("morecontacts", false)
ExchangeContactsItemPath1 = ExchangeContactsBasePath.MustAppend(ItemName1, true) ExchangeContactsItemPath1 = ExchangeContactsBasePath.MustAppend(ItemName1, true)
@ -403,8 +423,8 @@ var (
}, },
} }
ExchangeEventsBasePath = mustPathRep("tenant-id/exchange/user-id/events/holidays", false) ExchangeEventsBasePath = mustPathRep("tenant-id/exchange/user-id/events/holidays", false, false)
ExchangeEventsBasePath2 = mustPathRep("tenant-id/exchange/user-id/events/moreholidays", false) ExchangeEventsBasePath2 = mustPathRep("tenant-id/exchange/user-id/events/moreholidays", false, false)
ExchangeEventsItemPath1 = ExchangeEventsBasePath.MustAppend(ItemName1, true) ExchangeEventsItemPath1 = ExchangeEventsBasePath.MustAppend(ItemName1, true)
ExchangeEventsItemPath2 = ExchangeEventsBasePath2.MustAppend(ItemName2, true) ExchangeEventsItemPath2 = ExchangeEventsBasePath2.MustAppend(ItemName2, true)
@ -507,7 +527,7 @@ var (
}, },
} }
OneDriveRootPath = mustPathRep("tenant-id/onedrive/user-id/files/drives/foo/root:", false) OneDriveRootPath = mustPathRep("tenant-id/onedrive/user-id/files/drives/foo/root:", false, false)
OneDriveFolderPath = OneDriveRootPath.MustAppend("folder", false) OneDriveFolderPath = OneDriveRootPath.MustAppend("folder", false)
OneDriveBasePath1 = OneDriveFolderPath.MustAppend("a", false) OneDriveBasePath1 = OneDriveFolderPath.MustAppend("a", false)
OneDriveBasePath2 = OneDriveFolderPath.MustAppend("b", false) OneDriveBasePath2 = OneDriveFolderPath.MustAppend("b", false)
@ -732,10 +752,11 @@ var (
}, },
} }
GroupsRootPath = mustPathRep("tenant-id/groups/group-id/libraries/sites/site-id/drives/foo/root:", false) GroupsRootPath = mustPathRep("tenant-id/groups/group-id/libraries/sites/site-id/drives/foo/root:", false, false)
SharePointRootPath = mustPathRep("tenant-id/sharepoint/site-id/libraries/drives/foo/root:", false) SharePointRootPath = mustPathRep("tenant-id/sharepoint/site-id/libraries/drives/foo/root:", false, false)
SharePointLibraryPath = SharePointRootPath.MustAppend("library", false) SharePointLibraryPath = SharePointRootPath.MustAppend("library", false)
SharePointListPath = mustPathRep("tenant-id/sharepoint/site-id/lists", false, true)
SharePointBasePath1 = SharePointLibraryPath.MustAppend("a", false) SharePointBasePath1 = SharePointLibraryPath.MustAppend("a", false)
SharePointBasePath2 = SharePointLibraryPath.MustAppend("b", false) SharePointBasePath2 = SharePointLibraryPath.MustAppend("b", false)

View File

@ -355,6 +355,34 @@ func (pb Builder) ToDataLayerSharePointPath(
return pb.ToDataLayerPath(tenant, site, SharePointService, category, isItem) return pb.ToDataLayerPath(tenant, site, SharePointService, category, isItem)
} }
func (pb Builder) ToDataLayerSharePointListPath(
tenant, site string,
category CategoryType,
isItem bool,
) (Path, error) {
if err := ValidateServiceAndCategory(SharePointService, category); err != nil {
return nil, err
}
if err := verifyInputValues(tenant, site); err != nil {
return nil, err
}
prefixItems := []string{
tenant,
SharePointService.String(),
site,
category.String(),
}
return &dataLayerResourcePath{
Builder: *pb.withPrefix(prefixItems...),
service: SharePointService,
category: category,
hasItem: isItem,
}, nil
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Stringers and PII Concealer Compliance // Stringers and PII Concealer Compliance
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------