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:
ashmrtn 2023-01-20 09:02:23 -08:00 committed by GitHub
parent 5923c4a546
commit 6651305d4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 17 deletions

View File

@ -197,16 +197,26 @@ func (c *Collections) UpdateCollections(
switch {
case item.GetFolder() != nil, item.GetPackage() != nil:
// Eventually, deletions of folders will be handled here so we may as well
// start off by saving the path.Path of the item instead of just the
// OneDrive parentRef or such.
if item.GetDeleted() != nil {
// Nested folders also return deleted delta results so we don't have to
// 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)
if err != nil {
logger.Ctx(ctx).Errorw("failed building collection path", "error", 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
// collection doesn't already exist/have that state.
paths[*item.GetId()] = folderPath.String()

View File

@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"golang.org/x/exp/maps"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/control"
@ -96,6 +97,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
tests := []struct {
testCase string
items []models.DriveItemable
inputFolderMap map[string]string
scope selectors.OneDriveScope
expect assert.ErrorAssertionFunc
expectedCollectionPaths []string
@ -109,6 +111,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
items: []models.DriveItemable{
driveItem("item", "item", testBaseDrivePath, false, false, false),
},
inputFolderMap: map[string]string{},
scope: anyFolder,
expect: assert.Error,
expectedMetadataPaths: map[string]string{},
@ -118,8 +121,9 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
items: []models.DriveItemable{
driveItem("file", "file", testBaseDrivePath, true, false, false),
},
scope: anyFolder,
expect: assert.NoError,
inputFolderMap: map[string]string{},
scope: anyFolder,
expect: assert.NoError,
expectedCollectionPaths: expectedPathAsSlice(
suite.T(),
tenant,
@ -137,6 +141,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
items: []models.DriveItemable{
driveItem("folder", "folder", testBaseDrivePath, false, true, false),
},
inputFolderMap: map[string]string{},
scope: anyFolder,
expect: assert.NoError,
expectedCollectionPaths: []string{},
@ -154,6 +159,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
items: []models.DriveItemable{
driveItem("package", "package", testBaseDrivePath, false, false, true),
},
inputFolderMap: map[string]string{},
scope: anyFolder,
expect: assert.NoError,
expectedCollectionPaths: []string{},
@ -175,8 +181,9 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
driveItem("fileInFolder", "fileInFolder", testBaseDrivePath+folder, true, false, false),
driveItem("fileInPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
},
scope: anyFolder,
expect: assert.NoError,
inputFolderMap: map[string]string{},
scope: anyFolder,
expect: assert.NoError,
expectedCollectionPaths: expectedPathAsSlice(
suite.T(),
tenant,
@ -215,8 +222,9 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
driveItem("fileInFolder2", "fileInFolder2", testBaseDrivePath+folderSub+folder, true, false, false),
driveItem("fileInFolderPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
},
scope: (&selectors.OneDriveBackup{}).Folders([]string{"folder"})[0],
expect: assert.NoError,
inputFolderMap: map[string]string{},
scope: (&selectors.OneDriveBackup{}).Folders([]string{"folder"})[0],
expect: assert.NoError,
expectedCollectionPaths: append(
expectedPathAsSlice(
suite.T(),
@ -257,12 +265,13 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
driveItem("fileInRoot", "fileInRoot", testBaseDrivePath, true, false, false),
driveItem("folder", "folder", testBaseDrivePath, 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("fileInFolder", "fileInFolder", testBaseDrivePath+folder, true, false, false),
driveItem("fileInFolder2", "fileInFolder2", testBaseDrivePath+folderSub+folder, true, false, false),
driveItem("fileInPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
},
inputFolderMap: map[string]string{},
scope: (&selectors.OneDriveBackup{}).
Folders([]string{"/folder/subfolder"}, selectors.PrefixMatch())[0],
expect: assert.NoError,
@ -276,7 +285,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
expectedFileCount: 1,
expectedContainerCount: 1,
expectedMetadataPaths: map[string]string{
"folder": expectedPathAsSlice(
"folder2": expectedPathAsSlice(
suite.T(),
tenant,
user,
@ -295,8 +304,9 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
driveItem("fileInSubfolder", "fileInSubfolder", testBaseDrivePath+folderSub, true, false, false),
driveItem("fileInPackage", "fileInPackage", testBaseDrivePath+pkg, true, false, false),
},
scope: (&selectors.OneDriveBackup{}).Folders([]string{"folder/subfolder"})[0],
expect: assert.NoError,
inputFolderMap: map[string]string{},
scope: (&selectors.OneDriveBackup{}).Folders([]string{"folder/subfolder"})[0],
expect: assert.NoError,
expectedCollectionPaths: expectedPathAsSlice(
suite.T(),
tenant,
@ -309,6 +319,34 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
// No child folders for subfolder so nothing here.
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 {
@ -316,7 +354,8 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
ctx, flush := tester.NewContext()
defer flush()
paths := map[string]string{}
outputFolderMap := map[string]string{}
maps.Copy(outputFolderMap, tt.inputFolderMap)
c := NewCollections(
tenant,
user,
@ -326,7 +365,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
nil,
control.Options{})
err := c.UpdateCollections(ctx, "driveID", "General", tt.items, paths)
err := c.UpdateCollections(ctx, "driveID", "General", tt.items, outputFolderMap)
tt.expect(t, err)
assert.Equal(t, len(tt.expectedCollectionPaths), len(c.CollectionMap), "collection paths")
assert.Equal(t, tt.expectedItemCount, c.NumItems, "item count")
@ -336,7 +375,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() {
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
}
// 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
}