From 98e8cac37464447876082ba7f44afbfb25ec930a Mon Sep 17 00:00:00 2001 From: Hitesh Pattanayak <48874082+HiteshRepo@users.noreply.github.com> Date: Fri, 22 Dec 2023 22:12:53 +0530 Subject: [PATCH] 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_entry: No #### Type of change - [x] :sunflower: Feature #### Issue(s) #4754 #### Test Plan - [x] :muscle: Manual - [x] :zap: Unit test - [x] :green_heart: E2E --- .../restore_path_transformer.go | 3 +- .../restore_path_transformer_test.go | 43 ++++++++++---- src/pkg/backup/details/testdata/testdata.go | 57 +++++++++++++------ src/pkg/path/builder.go | 28 +++++++++ 4 files changed, 101 insertions(+), 30 deletions(-) diff --git a/src/internal/operations/pathtransformer/restore_path_transformer.go b/src/internal/operations/pathtransformer/restore_path_transformer.go index 5ee431b53..5cc794479 100644 --- a/src/internal/operations/pathtransformer/restore_path_transformer.go +++ b/src/internal/operations/pathtransformer/restore_path_transformer.go @@ -142,7 +142,8 @@ func makeRestorePathsForEntry( // * OneDrive/SharePoint (needs drive information) switch true { 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 // setting the restore destination properly. res.RestorePath, err = basicLocationPath(repoRef, locRef) diff --git a/src/internal/operations/pathtransformer/restore_path_transformer_test.go b/src/internal/operations/pathtransformer/restore_path_transformer_test.go index 5dc637e15..390354ac0 100644 --- a/src/internal/operations/pathtransformer/restore_path_transformer_test.go +++ b/src/internal/operations/pathtransformer/restore_path_transformer_test.go @@ -50,16 +50,19 @@ func (suite *RestorePathTransformerUnitSuite) TestGetPaths() { driveID = "some-drive-id" siteID = "some-site-id" extraItemName = "some-item" + listName = "list1" SharePointRootItemPath = testdata.SharePointRootPath.MustAppend(extraItemName, true) + SharePointListItemPath = testdata.SharePointListPath.MustAppend(listName, true) GroupsRootItemPath = testdata.GroupsRootPath.MustAppend(extraItemName, true) ) table := []struct { - name string - backupVersion int - input []*details.Entry - expectErr assert.ErrorAssertionFunc - expected []expectPaths + name string + backupVersion int + input []*details.Entry + expectErr assert.ErrorAssertionFunc + expected []expectPaths + isSharepointList bool }{ { 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. backupVersion: version.All8MigrateUserPNToID, input: []*details.Entry{ { - RepoRef: SharePointRootItemPath.RR.String(), - LocationRef: SharePointRootItemPath.Loc.String(), + RepoRef: SharePointListItemPath.RR.String(), + LocationRef: SharePointListItemPath.Loc.String(), ItemInfo: details.ItemInfo{ SharePoint: &details.SharePointInfo{ 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", @@ -413,12 +423,23 @@ func (suite *RestorePathTransformerUnitSuite) TestGetPaths() { for _, e := range test.expected { 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)) 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)) if e.isRestorePrefix { diff --git a/src/pkg/backup/details/testdata/testdata.go b/src/pkg/backup/details/testdata/testdata.go index ee56d36c3..1b47620d2 100644 --- a/src/pkg/backup/details/testdata/testdata.go +++ b/src/pkg/backup/details/testdata/testdata.go @@ -16,8 +16,16 @@ import ( // mustParsePath takes a string representing a resource path and returns a path // instance. Panics if the path cannot be parsed. Useful for simple variable // assignments. -func mustParsePath(ref string, isItem bool) path.Path { - p, err := path.FromDataLayerPath(ref, isItem) +func mustParsePath(ref string, isItem, isSharepointList bool) path.Path { + 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 { panic(err) } @@ -47,7 +55,7 @@ func locFromRepo(rr path.Path, isItem bool) *path.Builder { if rr.Service() == path.GroupsService { 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() } @@ -117,9 +125,12 @@ func (p repoRefAndLocRef) locationAsRepoRef() path.Path { 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{} - tmp := mustParsePath(ref, isItem) + tmp := mustParsePath(ref, isItem, isSharepointList) // 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 @@ -133,12 +144,21 @@ func mustPathRep(ref string, isItem bool) repoRefAndLocRef { rrPB = rrPB.Append(tmp.Item() + fileSuffix) } - rr, err := rrPB.ToDataLayerPath( - tmp.Tenant(), - tmp.ProtectedResource(), - tmp.Service(), - tmp.Category(), - isItem) + if isSharepointList { + rr, err = rrPB.ToDataLayerSharePointListPath( + tmp.Tenant(), + tmp.ProtectedResource(), + tmp.Category(), + isItem) + } else { + rr, err = rrPB.ToDataLayerPath( + tmp.Tenant(), + tmp.ProtectedResource(), + tmp.Service(), + tmp.Category(), + isItem) + } + if err != nil { panic(err) } @@ -166,7 +186,7 @@ var ( Time3 = time.Date(2023, 9, 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) ExchangeEmailBasePath2 = ExchangeEmailInboxPath.MustAppend("othersubfolder/", 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) ExchangeContactsBasePath2 = ExchangeContactsRootPath.MustAppend("morecontacts", false) ExchangeContactsItemPath1 = ExchangeContactsBasePath.MustAppend(ItemName1, true) @@ -403,8 +423,8 @@ var ( }, } - ExchangeEventsBasePath = mustPathRep("tenant-id/exchange/user-id/events/holidays", false) - ExchangeEventsBasePath2 = mustPathRep("tenant-id/exchange/user-id/events/moreholidays", false) + ExchangeEventsBasePath = mustPathRep("tenant-id/exchange/user-id/events/holidays", false, false) + ExchangeEventsBasePath2 = mustPathRep("tenant-id/exchange/user-id/events/moreholidays", false, false) ExchangeEventsItemPath1 = ExchangeEventsBasePath.MustAppend(ItemName1, 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) OneDriveBasePath1 = OneDriveFolderPath.MustAppend("a", 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) + SharePointListPath = mustPathRep("tenant-id/sharepoint/site-id/lists", false, true) SharePointBasePath1 = SharePointLibraryPath.MustAppend("a", false) SharePointBasePath2 = SharePointLibraryPath.MustAppend("b", false) diff --git a/src/pkg/path/builder.go b/src/pkg/path/builder.go index 5fe8169c1..241591279 100644 --- a/src/pkg/path/builder.go +++ b/src/pkg/path/builder.go @@ -355,6 +355,34 @@ func (pb Builder) ToDataLayerSharePointPath( 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 // ---------------------------------------------------------------------------