Update OneDrive metadata with folder deletions (#2192)
## Description Track folder and package deletions in OneDrive metadata for incremental backups. Add test for deletions ## Does this PR need a docs update or release note? - [ ] ✅ Yes, it's included - [ ] 🕐 Yes, but in a later PR - [x] ⛔ No ## Type of change - [x] 🌻 Feature - [ ] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Test - [ ] 💻 CI/Deployment - [ ] 🧹 Tech Debt/Cleanup ## Issue(s) * #2120 ## Test Plan - [x] 💪 Manual - [x] ⚡ Unit test - [ ] 💚 E2E
This commit is contained in:
parent
5923c4a546
commit
6651305d4f
@ -197,16 +197,26 @@ func (c *Collections) UpdateCollections(
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
case item.GetFolder() != nil, item.GetPackage() != nil:
|
case item.GetFolder() != nil, item.GetPackage() != nil:
|
||||||
// Eventually, deletions of folders will be handled here so we may as well
|
if item.GetDeleted() != nil {
|
||||||
// start off by saving the path.Path of the item instead of just the
|
// Nested folders also return deleted delta results so we don't have to
|
||||||
// OneDrive parentRef or such.
|
// worry about doing a prefix search in the map to remove the subtree of
|
||||||
|
// the deleted folder/package.
|
||||||
|
delete(paths, *item.GetId())
|
||||||
|
|
||||||
|
// TODO(ashmrtn): Create a collection with state Deleted.
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletions of folders are handled in this case so we may as well start
|
||||||
|
// off by saving the path.Path of the item instead of just the OneDrive
|
||||||
|
// parentRef or such.
|
||||||
folderPath, err := collectionPath.Append(*item.GetName(), false)
|
folderPath, err := collectionPath.Append(*item.GetName(), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Ctx(ctx).Errorw("failed building collection path", "error", err)
|
logger.Ctx(ctx).Errorw("failed building collection path", "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(ashmrtn): Handle deletions by removing this entry from the map.
|
|
||||||
// TODO(ashmrtn): Handle moves by setting the collection state if the
|
// TODO(ashmrtn): Handle moves by setting the collection state if the
|
||||||
// collection doesn't already exist/have that state.
|
// collection doesn't already exist/have that state.
|
||||||
paths[*item.GetId()] = folderPath.String()
|
paths[*item.GetId()] = folderPath.String()
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/internal/tester"
|
"github.com/alcionai/corso/src/internal/tester"
|
||||||
"github.com/alcionai/corso/src/pkg/control"
|
"github.com/alcionai/corso/src/pkg/control"
|
||||||
@ -96,6 +97,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
testCase string
|
testCase string
|
||||||
items []models.DriveItemable
|
items []models.DriveItemable
|
||||||
|
inputFolderMap map[string]string
|
||||||
scope selectors.OneDriveScope
|
scope selectors.OneDriveScope
|
||||||
expect assert.ErrorAssertionFunc
|
expect assert.ErrorAssertionFunc
|
||||||
expectedCollectionPaths []string
|
expectedCollectionPaths []string
|
||||||
@ -109,6 +111,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
items: []models.DriveItemable{
|
items: []models.DriveItemable{
|
||||||
driveItem("item", "item", testBaseDrivePath, false, false, false),
|
driveItem("item", "item", testBaseDrivePath, false, false, false),
|
||||||
},
|
},
|
||||||
|
inputFolderMap: map[string]string{},
|
||||||
scope: anyFolder,
|
scope: anyFolder,
|
||||||
expect: assert.Error,
|
expect: assert.Error,
|
||||||
expectedMetadataPaths: map[string]string{},
|
expectedMetadataPaths: map[string]string{},
|
||||||
@ -118,8 +121,9 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
items: []models.DriveItemable{
|
items: []models.DriveItemable{
|
||||||
driveItem("file", "file", testBaseDrivePath, true, false, false),
|
driveItem("file", "file", testBaseDrivePath, true, false, false),
|
||||||
},
|
},
|
||||||
scope: anyFolder,
|
inputFolderMap: map[string]string{},
|
||||||
expect: assert.NoError,
|
scope: anyFolder,
|
||||||
|
expect: assert.NoError,
|
||||||
expectedCollectionPaths: expectedPathAsSlice(
|
expectedCollectionPaths: expectedPathAsSlice(
|
||||||
suite.T(),
|
suite.T(),
|
||||||
tenant,
|
tenant,
|
||||||
@ -137,6 +141,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
items: []models.DriveItemable{
|
items: []models.DriveItemable{
|
||||||
driveItem("folder", "folder", testBaseDrivePath, false, true, false),
|
driveItem("folder", "folder", testBaseDrivePath, false, true, false),
|
||||||
},
|
},
|
||||||
|
inputFolderMap: map[string]string{},
|
||||||
scope: anyFolder,
|
scope: anyFolder,
|
||||||
expect: assert.NoError,
|
expect: assert.NoError,
|
||||||
expectedCollectionPaths: []string{},
|
expectedCollectionPaths: []string{},
|
||||||
@ -154,6 +159,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
items: []models.DriveItemable{
|
items: []models.DriveItemable{
|
||||||
driveItem("package", "package", testBaseDrivePath, false, false, true),
|
driveItem("package", "package", testBaseDrivePath, false, false, true),
|
||||||
},
|
},
|
||||||
|
inputFolderMap: map[string]string{},
|
||||||
scope: anyFolder,
|
scope: anyFolder,
|
||||||
expect: assert.NoError,
|
expect: assert.NoError,
|
||||||
expectedCollectionPaths: []string{},
|
expectedCollectionPaths: []string{},
|
||||||
@ -175,8 +181,9 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
driveItem("fileInFolder", "fileInFolder", testBaseDrivePath+folder, true, false, false),
|
driveItem("fileInFolder", "fileInFolder", testBaseDrivePath+folder, true, false, false),
|
||||||
driveItem("fileInPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
|
driveItem("fileInPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
|
||||||
},
|
},
|
||||||
scope: anyFolder,
|
inputFolderMap: map[string]string{},
|
||||||
expect: assert.NoError,
|
scope: anyFolder,
|
||||||
|
expect: assert.NoError,
|
||||||
expectedCollectionPaths: expectedPathAsSlice(
|
expectedCollectionPaths: expectedPathAsSlice(
|
||||||
suite.T(),
|
suite.T(),
|
||||||
tenant,
|
tenant,
|
||||||
@ -215,8 +222,9 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
driveItem("fileInFolder2", "fileInFolder2", testBaseDrivePath+folderSub+folder, true, false, false),
|
driveItem("fileInFolder2", "fileInFolder2", testBaseDrivePath+folderSub+folder, true, false, false),
|
||||||
driveItem("fileInFolderPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
|
driveItem("fileInFolderPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
|
||||||
},
|
},
|
||||||
scope: (&selectors.OneDriveBackup{}).Folders([]string{"folder"})[0],
|
inputFolderMap: map[string]string{},
|
||||||
expect: assert.NoError,
|
scope: (&selectors.OneDriveBackup{}).Folders([]string{"folder"})[0],
|
||||||
|
expect: assert.NoError,
|
||||||
expectedCollectionPaths: append(
|
expectedCollectionPaths: append(
|
||||||
expectedPathAsSlice(
|
expectedPathAsSlice(
|
||||||
suite.T(),
|
suite.T(),
|
||||||
@ -257,12 +265,13 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
driveItem("fileInRoot", "fileInRoot", testBaseDrivePath, true, false, false),
|
driveItem("fileInRoot", "fileInRoot", testBaseDrivePath, true, false, false),
|
||||||
driveItem("folder", "folder", testBaseDrivePath, false, true, false),
|
driveItem("folder", "folder", testBaseDrivePath, false, true, false),
|
||||||
driveItem("subfolder", "subfolder", testBaseDrivePath+folder, false, true, false),
|
driveItem("subfolder", "subfolder", testBaseDrivePath+folder, false, true, false),
|
||||||
driveItem("folder", "folder", testBaseDrivePath+folderSub, false, true, false),
|
driveItem("folder2", "folder", testBaseDrivePath+folderSub, false, true, false),
|
||||||
driveItem("package", "package", testBaseDrivePath, false, false, true),
|
driveItem("package", "package", testBaseDrivePath, false, false, true),
|
||||||
driveItem("fileInFolder", "fileInFolder", testBaseDrivePath+folder, true, false, false),
|
driveItem("fileInFolder", "fileInFolder", testBaseDrivePath+folder, true, false, false),
|
||||||
driveItem("fileInFolder2", "fileInFolder2", testBaseDrivePath+folderSub+folder, true, false, false),
|
driveItem("fileInFolder2", "fileInFolder2", testBaseDrivePath+folderSub+folder, true, false, false),
|
||||||
driveItem("fileInPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
|
driveItem("fileInPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
|
||||||
},
|
},
|
||||||
|
inputFolderMap: map[string]string{},
|
||||||
scope: (&selectors.OneDriveBackup{}).
|
scope: (&selectors.OneDriveBackup{}).
|
||||||
Folders([]string{"/folder/subfolder"}, selectors.PrefixMatch())[0],
|
Folders([]string{"/folder/subfolder"}, selectors.PrefixMatch())[0],
|
||||||
expect: assert.NoError,
|
expect: assert.NoError,
|
||||||
@ -276,7 +285,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
expectedFileCount: 1,
|
expectedFileCount: 1,
|
||||||
expectedContainerCount: 1,
|
expectedContainerCount: 1,
|
||||||
expectedMetadataPaths: map[string]string{
|
expectedMetadataPaths: map[string]string{
|
||||||
"folder": expectedPathAsSlice(
|
"folder2": expectedPathAsSlice(
|
||||||
suite.T(),
|
suite.T(),
|
||||||
tenant,
|
tenant,
|
||||||
user,
|
user,
|
||||||
@ -295,8 +304,9 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
driveItem("fileInSubfolder", "fileInSubfolder", testBaseDrivePath+folderSub, true, false, false),
|
driveItem("fileInSubfolder", "fileInSubfolder", testBaseDrivePath+folderSub, true, false, false),
|
||||||
driveItem("fileInPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
|
driveItem("fileInPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
|
||||||
},
|
},
|
||||||
scope: (&selectors.OneDriveBackup{}).Folders([]string{"folder/subfolder"})[0],
|
inputFolderMap: map[string]string{},
|
||||||
expect: assert.NoError,
|
scope: (&selectors.OneDriveBackup{}).Folders([]string{"folder/subfolder"})[0],
|
||||||
|
expect: assert.NoError,
|
||||||
expectedCollectionPaths: expectedPathAsSlice(
|
expectedCollectionPaths: expectedPathAsSlice(
|
||||||
suite.T(),
|
suite.T(),
|
||||||
tenant,
|
tenant,
|
||||||
@ -309,6 +319,34 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
// No child folders for subfolder so nothing here.
|
// No child folders for subfolder so nothing here.
|
||||||
expectedMetadataPaths: map[string]string{},
|
expectedMetadataPaths: map[string]string{},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
testCase: "deleted folder and package",
|
||||||
|
items: []models.DriveItemable{
|
||||||
|
delItem("folder", testBaseDrivePath, false, true, false),
|
||||||
|
delItem("package", testBaseDrivePath, false, false, true),
|
||||||
|
},
|
||||||
|
inputFolderMap: map[string]string{
|
||||||
|
"folder": expectedPathAsSlice(
|
||||||
|
suite.T(),
|
||||||
|
tenant,
|
||||||
|
user,
|
||||||
|
testBaseDrivePath+"/folder",
|
||||||
|
)[0],
|
||||||
|
"package": expectedPathAsSlice(
|
||||||
|
suite.T(),
|
||||||
|
tenant,
|
||||||
|
user,
|
||||||
|
testBaseDrivePath+"/package",
|
||||||
|
)[0],
|
||||||
|
},
|
||||||
|
scope: anyFolder,
|
||||||
|
expect: assert.NoError,
|
||||||
|
expectedCollectionPaths: []string{},
|
||||||
|
expectedItemCount: 0,
|
||||||
|
expectedFileCount: 0,
|
||||||
|
expectedContainerCount: 0,
|
||||||
|
expectedMetadataPaths: map[string]string{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -316,7 +354,8 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
ctx, flush := tester.NewContext()
|
ctx, flush := tester.NewContext()
|
||||||
defer flush()
|
defer flush()
|
||||||
|
|
||||||
paths := map[string]string{}
|
outputFolderMap := map[string]string{}
|
||||||
|
maps.Copy(outputFolderMap, tt.inputFolderMap)
|
||||||
c := NewCollections(
|
c := NewCollections(
|
||||||
tenant,
|
tenant,
|
||||||
user,
|
user,
|
||||||
@ -326,7 +365,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
nil,
|
nil,
|
||||||
control.Options{})
|
control.Options{})
|
||||||
|
|
||||||
err := c.UpdateCollections(ctx, "driveID", "General", tt.items, paths)
|
err := c.UpdateCollections(ctx, "driveID", "General", tt.items, outputFolderMap)
|
||||||
tt.expect(t, err)
|
tt.expect(t, err)
|
||||||
assert.Equal(t, len(tt.expectedCollectionPaths), len(c.CollectionMap), "collection paths")
|
assert.Equal(t, len(tt.expectedCollectionPaths), len(c.CollectionMap), "collection paths")
|
||||||
assert.Equal(t, tt.expectedItemCount, c.NumItems, "item count")
|
assert.Equal(t, tt.expectedItemCount, c.NumItems, "item count")
|
||||||
@ -336,7 +375,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
|
|||||||
assert.Contains(t, c.CollectionMap, collPath)
|
assert.Contains(t, c.CollectionMap, collPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, tt.expectedMetadataPaths, paths)
|
assert.Equal(t, tt.expectedMetadataPaths, outputFolderMap)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -361,3 +400,26 @@ func driveItem(id string, name string, path string, isFile, isFolder, isPackage
|
|||||||
|
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delItem creates a DriveItemable that is marked as deleted. path must be set
|
||||||
|
// to the base drive path.
|
||||||
|
func delItem(id string, path string, isFile, isFolder, isPackage bool) models.DriveItemable {
|
||||||
|
item := models.NewDriveItem()
|
||||||
|
item.SetId(&id)
|
||||||
|
item.SetDeleted(models.NewDeleted())
|
||||||
|
|
||||||
|
parentReference := models.NewItemReference()
|
||||||
|
parentReference.SetPath(&path)
|
||||||
|
item.SetParentReference(parentReference)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case isFile:
|
||||||
|
item.SetFile(models.NewFile())
|
||||||
|
case isFolder:
|
||||||
|
item.SetFolder(models.NewFolder())
|
||||||
|
case isPackage:
|
||||||
|
item.SetPackage(models.NewPackage_escaped())
|
||||||
|
}
|
||||||
|
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user