OneDrive Folder and Item selectors (#985)
## Description Adds support for folder and item selection ## Type of change <!--- Please check the type of change your PR introduces: ---> - [x] 🌻 Feature - [ ] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Test - [ ] 💻 CI/Deployment - [ ] 🐹 Trivial/Minor ## Issue(s) * #627 ## Test Plan <!-- How will this be tested prior to merging.--> - [ ] 💪 Manual - [x] ⚡ Unit test - [ ] 💚 E2E
This commit is contained in:
parent
011e24583f
commit
b7f5ed73c3
@ -1,6 +1,8 @@
|
||||
package selectors
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
@ -141,19 +143,6 @@ func (s *oneDrive) Filter(scopes ...[]OneDriveScope) {
|
||||
s.Filters = appendScopes(s.Filters, scopes...)
|
||||
}
|
||||
|
||||
// Produces one or more oneDrive user scopes.
|
||||
// One scope is created per user entry.
|
||||
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
||||
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
|
||||
// If any slice is empty, it defaults to [selectors.None]
|
||||
func (s *oneDrive) Users(users []string) []OneDriveScope {
|
||||
scopes := []OneDriveScope{}
|
||||
|
||||
scopes = append(scopes, makeScope[OneDriveScope](OneDriveUser, users, users))
|
||||
|
||||
return scopes
|
||||
}
|
||||
|
||||
// Scopes retrieves the list of oneDriveScopes in the selector.
|
||||
func (s *oneDrive) Scopes() []OneDriveScope {
|
||||
return scopes[OneDriveScope](s.Selector)
|
||||
@ -166,6 +155,53 @@ func (s *oneDrive) DiscreteScopes(userPNs []string) []OneDriveScope {
|
||||
return discreteScopes[OneDriveScope](s.Selector, OneDriveUser, userPNs)
|
||||
}
|
||||
|
||||
// -------------------
|
||||
// Scope Factories
|
||||
|
||||
// Produces one or more OneDrive user scopes.
|
||||
// One scope is created per user entry.
|
||||
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
||||
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
|
||||
// If any slice is empty, it defaults to [selectors.None]
|
||||
func (s *oneDrive) Users(users []string) []OneDriveScope {
|
||||
scopes := []OneDriveScope{}
|
||||
|
||||
scopes = append(scopes, makeScope[OneDriveScope](OneDriveFolder, users, Any()))
|
||||
|
||||
return scopes
|
||||
}
|
||||
|
||||
// Folders produces one or more OneDrive folder scopes.
|
||||
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
||||
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
|
||||
// If any slice is empty, it defaults to [selectors.None]
|
||||
func (s *oneDrive) Folders(users, folders []string) []OneDriveScope {
|
||||
scopes := []OneDriveScope{}
|
||||
|
||||
scopes = append(
|
||||
scopes,
|
||||
makeScope[OneDriveScope](OneDriveFolder, users, folders),
|
||||
)
|
||||
|
||||
return scopes
|
||||
}
|
||||
|
||||
// Items produces one or more OneDrive item scopes.
|
||||
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
||||
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
|
||||
// If any slice is empty, it defaults to [selectors.None]
|
||||
func (s *oneDrive) Items(users, folders, items []string) []OneDriveScope {
|
||||
scopes := []OneDriveScope{}
|
||||
|
||||
scopes = append(
|
||||
scopes,
|
||||
makeScope[OneDriveScope](OneDriveItem, users, items).
|
||||
set(OneDriveFolder, folders),
|
||||
)
|
||||
|
||||
return scopes
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Categories
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -180,13 +216,16 @@ var _ categorizer = OneDriveCategoryUnknown
|
||||
const (
|
||||
OneDriveCategoryUnknown oneDriveCategory = ""
|
||||
// types of data identified by OneDrive
|
||||
OneDriveUser oneDriveCategory = "OneDriveUser"
|
||||
OneDriveUser oneDriveCategory = "OneDriveUser"
|
||||
OneDriveItem oneDriveCategory = "OneDriveItem"
|
||||
OneDriveFolder oneDriveCategory = "OneDriveFolder"
|
||||
)
|
||||
|
||||
// oneDrivePathSet describes the category type keys used in OneDrive paths.
|
||||
// The order of each slice is important, and should match the order in which
|
||||
// these types appear in the canonical Path for each type.
|
||||
var oneDrivePathSet = map[categorizer][]categorizer{
|
||||
OneDriveItem: {OneDriveUser, OneDriveFolder, OneDriveItem},
|
||||
OneDriveUser: {OneDriveUser}, // the root category must be represented
|
||||
}
|
||||
|
||||
@ -200,6 +239,11 @@ func (c oneDriveCategory) String() string {
|
||||
// Ex: ServiceTypeFolder.leafCat() => ServiceTypeItem
|
||||
// Ex: ServiceUser.leafCat() => ServiceUser
|
||||
func (c oneDriveCategory) leafCat() categorizer {
|
||||
switch c {
|
||||
case OneDriveFolder, OneDriveItem:
|
||||
return OneDriveItem
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
@ -213,9 +257,10 @@ func (c oneDriveCategory) unknownCat() categorizer {
|
||||
return OneDriveCategoryUnknown
|
||||
}
|
||||
|
||||
// isLeaf is true if the category is a mail, event, or contact category.
|
||||
// isLeaf is true if the category is a OneDriveItem category.
|
||||
func (c oneDriveCategory) isLeaf() bool {
|
||||
return c == c.leafCat()
|
||||
// return c == c.leafCat()??
|
||||
return c == OneDriveItem
|
||||
}
|
||||
|
||||
// pathValues transforms a path to a map of identified properties.
|
||||
@ -225,7 +270,9 @@ func (c oneDriveCategory) isLeaf() bool {
|
||||
// => {odUser: userPN, odFolder: folder, odFileID: fileID}
|
||||
func (c oneDriveCategory) pathValues(p path.Path) map[categorizer]string {
|
||||
return map[categorizer]string{
|
||||
OneDriveUser: p.ResourceOwner(),
|
||||
OneDriveUser: p.ResourceOwner(),
|
||||
OneDriveFolder: p.Folder(), // TODO: Should we filter out the DriveID here?
|
||||
OneDriveItem: p.Item(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,13 +337,19 @@ func (s OneDriveScope) Get(cat oneDriveCategory) []string {
|
||||
}
|
||||
|
||||
// sets a value by category to the scope. Only intended for internal use.
|
||||
// func (s OneDriveScope) set(cat oneDriveCategory, v string) OneDriveScope {
|
||||
// return set(s, cat, v)
|
||||
// }
|
||||
func (s OneDriveScope) set(cat oneDriveCategory, v []string) OneDriveScope {
|
||||
return set(s, cat, v)
|
||||
}
|
||||
|
||||
// setDefaults ensures that user scopes express `AnyTgt` for their child category types.
|
||||
func (s OneDriveScope) setDefaults() {
|
||||
// no-op while no child scope types below user are identified
|
||||
switch s.Category() {
|
||||
case OneDriveUser:
|
||||
s[OneDriveFolder.String()] = passAny
|
||||
s[OneDriveItem.String()] = passAny
|
||||
case OneDriveFolder:
|
||||
s[OneDriveItem.String()] = passAny
|
||||
}
|
||||
}
|
||||
|
||||
// matchesInfo handles the standard behavior when comparing a scope and an oneDriveInfo
|
||||
@ -333,3 +386,20 @@ func (s OneDriveScope) matchesInfo(dii details.ItemInfo) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Backup Details Filtering
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Reduce filters the entries in a details struct to only those that match the
|
||||
// inclusions, filters, and exclusions in the selector.
|
||||
func (s oneDrive) Reduce(ctx context.Context, deets *details.Details) *details.Details {
|
||||
return reduce[OneDriveScope](
|
||||
ctx,
|
||||
deets,
|
||||
s.Selector,
|
||||
map[path.CategoryType]oneDriveCategory{
|
||||
path.FilesCategory: OneDriveItem,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
package selectors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
)
|
||||
|
||||
type OneDriveSelectorSuite struct {
|
||||
@ -174,3 +178,92 @@ func (suite *OneDriveSelectorSuite) TestToOneDriveRestore() {
|
||||
assert.Equal(t, or.Service, ServiceOneDrive)
|
||||
assert.NotZero(t, or.Scopes())
|
||||
}
|
||||
|
||||
func (suite *OneDriveSelectorSuite) TestOneDriveRestore_Reduce() {
|
||||
var (
|
||||
file = stubRepoRef(path.OneDriveService, path.FilesCategory, "uid", "folderA/folderB", "file")
|
||||
file2 = stubRepoRef(path.OneDriveService, path.FilesCategory, "uid", "folderA/folderC", "file2")
|
||||
file3 = stubRepoRef(path.OneDriveService, path.FilesCategory, "uid", "folderD/folderE", "file3")
|
||||
)
|
||||
|
||||
deets := &details.Details{
|
||||
DetailsModel: details.DetailsModel{
|
||||
Entries: []details.DetailsEntry{
|
||||
{
|
||||
RepoRef: file,
|
||||
ItemInfo: details.ItemInfo{
|
||||
OneDrive: &details.OneDriveInfo{
|
||||
ItemType: details.OneDriveItem,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
RepoRef: file2,
|
||||
ItemInfo: details.ItemInfo{
|
||||
OneDrive: &details.OneDriveInfo{
|
||||
ItemType: details.OneDriveItem,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
RepoRef: file3,
|
||||
ItemInfo: details.ItemInfo{
|
||||
OneDrive: &details.OneDriveInfo{
|
||||
ItemType: details.OneDriveItem,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
arr := func(s ...string) []string {
|
||||
return s
|
||||
}
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
deets *details.Details
|
||||
makeSelector func() *OneDriveRestore
|
||||
expect []string
|
||||
}{
|
||||
{
|
||||
"all",
|
||||
deets,
|
||||
func() *OneDriveRestore {
|
||||
odr := NewOneDriveRestore()
|
||||
odr.Include(odr.Users(Any()))
|
||||
return odr
|
||||
},
|
||||
arr(file, file2, file3),
|
||||
},
|
||||
{
|
||||
"only match file",
|
||||
deets,
|
||||
func() *OneDriveRestore {
|
||||
odr := NewOneDriveRestore()
|
||||
odr.Include(odr.Items(Any(), Any(), []string{"file2"}))
|
||||
return odr
|
||||
},
|
||||
arr(file2),
|
||||
},
|
||||
{
|
||||
"only match folder",
|
||||
deets,
|
||||
func() *OneDriveRestore {
|
||||
odr := NewOneDriveRestore()
|
||||
odr.Include(odr.Folders([]string{"uid"}, []string{"folderA/folderB", "folderA/folderC"}))
|
||||
return odr
|
||||
},
|
||||
arr(file, file2),
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.T().Run(test.name, func(t *testing.T) {
|
||||
sel := test.makeSelector()
|
||||
results := sel.Reduce(context.Background(), test.deets)
|
||||
paths := results.Paths()
|
||||
assert.Equal(t, test.expect, paths)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user