add delta tree (#4692)
introduces the delta tree in drive collections --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Issue(s) * #4689 #### Test Plan - [x] ⚡ Unit test
This commit is contained in:
parent
6fe6c9586d
commit
dbdd3f236c
80
src/internal/m365/collection/drive/delta_tree.go
Normal file
80
src/internal/m365/collection/drive/delta_tree.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package drive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/pkg/path"
|
||||||
|
)
|
||||||
|
|
||||||
|
// folderyMcFolderFace owns our delta processing tree.
|
||||||
|
type folderyMcFolderFace struct {
|
||||||
|
// tenant/service/resource/category/driveID
|
||||||
|
// (or whatever variant the service defines)
|
||||||
|
// allows the tree to focus only on folder structure,
|
||||||
|
// and minimizes the possibility of multi-prefix path bugs.
|
||||||
|
prefix path.Path
|
||||||
|
|
||||||
|
// the root of the tree;
|
||||||
|
// new, moved, and notMoved collections
|
||||||
|
collections *nodeyMcNodeFace
|
||||||
|
|
||||||
|
// the majority of operations we perform can be handled with
|
||||||
|
// a folder ID lookup instead of re-walking the entire tree.
|
||||||
|
// Ex: adding a new file to its parent folder.
|
||||||
|
folderIDToNode map[string]*nodeyMcNodeFace
|
||||||
|
|
||||||
|
// tombstones don't need to form a tree.
|
||||||
|
// we only need the folder ID and their previous path.
|
||||||
|
tombstones map[string]path.Path
|
||||||
|
|
||||||
|
// it's just a sensible place to store the data, since we're
|
||||||
|
// already pushing file additions through the api.
|
||||||
|
excludeFileIDs map[string]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFolderyMcFolderFace(
|
||||||
|
prefix path.Path,
|
||||||
|
) *folderyMcFolderFace {
|
||||||
|
return &folderyMcFolderFace{
|
||||||
|
prefix: prefix,
|
||||||
|
folderIDToNode: map[string]*nodeyMcNodeFace{},
|
||||||
|
tombstones: map[string]path.Path{},
|
||||||
|
excludeFileIDs: map[string]struct{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type nodeyMcNodeFace struct {
|
||||||
|
// required for mid-enumeration folder moves, else we have to walk
|
||||||
|
// the tree completely to remove the node from its old parent.
|
||||||
|
parent *nodeyMcNodeFace
|
||||||
|
// the microsoft item ID. Mostly because we might as well
|
||||||
|
// attach that to the node if we're also attaching the dir.
|
||||||
|
id string
|
||||||
|
// single directory name, not a path
|
||||||
|
name string
|
||||||
|
// only contains the folders starting at and including '/root:'
|
||||||
|
prev path.Path
|
||||||
|
// map folderID -> node
|
||||||
|
childDirs map[string]*nodeyMcNodeFace
|
||||||
|
// items are keyed by item ID
|
||||||
|
items map[string]time.Time
|
||||||
|
// for special handling protocols around packages
|
||||||
|
isPackage bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNodeyMcNodeFace(
|
||||||
|
parent *nodeyMcNodeFace,
|
||||||
|
id, name string,
|
||||||
|
prev path.Path,
|
||||||
|
isPackage bool,
|
||||||
|
) *nodeyMcNodeFace {
|
||||||
|
return &nodeyMcNodeFace{
|
||||||
|
parent: parent,
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
prev: prev,
|
||||||
|
childDirs: map[string]*nodeyMcNodeFace{},
|
||||||
|
items: map[string]time.Time{},
|
||||||
|
isPackage: isPackage,
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/internal/m365/collection/drive/delta_tree_test.go
Normal file
56
src/internal/m365/collection/drive/delta_tree_test.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package drive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/alcionai/clues"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/internal/tester"
|
||||||
|
"github.com/alcionai/corso/src/pkg/path"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeltaTreeUnitSuite struct {
|
||||||
|
tester.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeltaTreeUnitSuite(t *testing.T) {
|
||||||
|
suite.Run(t, &DeltaTreeUnitSuite{Suite: tester.NewUnitSuite(t)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *DeltaTreeUnitSuite) TestNewFolderyMcFolderFace() {
|
||||||
|
var (
|
||||||
|
t = suite.T()
|
||||||
|
p, err = path.BuildPrefix("t", "r", path.OneDriveService, path.FilesCategory)
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
folderFace := newFolderyMcFolderFace(p)
|
||||||
|
assert.Equal(t, p, folderFace.prefix)
|
||||||
|
assert.Nil(t, folderFace.collections)
|
||||||
|
assert.NotNil(t, folderFace.folderIDToNode)
|
||||||
|
assert.NotNil(t, folderFace.tombstones)
|
||||||
|
assert.NotNil(t, folderFace.excludeFileIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *DeltaTreeUnitSuite) TestNewNodeyMcNodeFace() {
|
||||||
|
var (
|
||||||
|
t = suite.T()
|
||||||
|
parent = &nodeyMcNodeFace{}
|
||||||
|
p, err = path.Build("t", "r", path.SharePointService, path.LibrariesCategory, false, "drive-id", "root:")
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
nodeFace := newNodeyMcNodeFace(parent, "id", "name", p, true)
|
||||||
|
assert.Equal(t, parent, nodeFace.parent)
|
||||||
|
assert.Equal(t, "id", nodeFace.id)
|
||||||
|
assert.Equal(t, "name", nodeFace.name)
|
||||||
|
assert.Equal(t, p, nodeFace.prev)
|
||||||
|
assert.True(t, nodeFace.isPackage)
|
||||||
|
assert.NotNil(t, nodeFace.childDirs)
|
||||||
|
assert.NotNil(t, nodeFace.items)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user