aggregate categories from selector (#1742)
## Description Adds a PathCategories() func to selectors which returns all the path categories exhibited by the scopes within the selector. ## Type of change - [x] 🌻 Feature ## Issue(s) * #1725 ## Test Plan - [x] ⚡ Unit test
This commit is contained in:
parent
65284620ea
commit
edd0708285
@ -157,28 +157,7 @@ func FilterContainersAndFillCollections(
|
|||||||
collections[metadataKey] = col
|
collections[metadataKey] = col
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(ashmrtn): getFetchIDFunc functions should probably just return a
|
return errs
|
||||||
// multierror and all of the error handling should just use those so that it
|
|
||||||
// all ends up more consistent.
|
|
||||||
merrs := multierror.Append(nil, errs)
|
|
||||||
|
|
||||||
col, err = makeMetadataCollection(
|
|
||||||
qp.Credentials.AzureTenantID,
|
|
||||||
qp.ResourceOwner,
|
|
||||||
qp.Category,
|
|
||||||
deltaTokens,
|
|
||||||
statusUpdater,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
merrs = multierror.Append(
|
|
||||||
merrs,
|
|
||||||
errors.Wrap(err, "making metadata collection"),
|
|
||||||
)
|
|
||||||
} else if col != nil {
|
|
||||||
collections[metadataKey] = col
|
|
||||||
}
|
|
||||||
|
|
||||||
return merrs.ErrorOrNil()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func IterativeCollectContactContainers(
|
func IterativeCollectContactContainers(
|
||||||
|
|||||||
@ -40,6 +40,7 @@ var (
|
|||||||
_ Reducer = &ExchangeRestore{}
|
_ Reducer = &ExchangeRestore{}
|
||||||
_ printabler = &ExchangeRestore{}
|
_ printabler = &ExchangeRestore{}
|
||||||
_ resourceOwnerer = &ExchangeRestore{}
|
_ resourceOwnerer = &ExchangeRestore{}
|
||||||
|
_ pathCategorier = &ExchangeRestore{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewExchange produces a new Selector with the service set to ServiceExchange.
|
// NewExchange produces a new Selector with the service set to ServiceExchange.
|
||||||
@ -103,6 +104,15 @@ func (s exchange) ResourceOwners() selectorResourceOwners {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PathCategories produces the aggregation of discrete users described by each type of scope.
|
||||||
|
func (s exchange) PathCategories() selectorPathCategories {
|
||||||
|
return selectorPathCategories{
|
||||||
|
Excludes: pathCategoriesIn[ExchangeScope, exchangeCategory](s.Excludes),
|
||||||
|
Filters: pathCategoriesIn[ExchangeScope, exchangeCategory](s.Filters),
|
||||||
|
Includes: pathCategoriesIn[ExchangeScope, exchangeCategory](s.Includes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------
|
// -------------------
|
||||||
// Exclude/Includes
|
// Exclude/Includes
|
||||||
|
|
||||||
@ -210,7 +220,7 @@ func (s *exchange) Contacts(users, folders, contacts []string, opts ...option) [
|
|||||||
func (s *exchange) ContactFolders(users, folders []string, opts ...option) []ExchangeScope {
|
func (s *exchange) ContactFolders(users, folders []string, opts ...option) []ExchangeScope {
|
||||||
var (
|
var (
|
||||||
scopes = []ExchangeScope{}
|
scopes = []ExchangeScope{}
|
||||||
os = append([]option{pathType()}, opts...)
|
os = append([]option{pathComparator()}, opts...)
|
||||||
)
|
)
|
||||||
|
|
||||||
scopes = append(
|
scopes = append(
|
||||||
@ -247,7 +257,7 @@ func (s *exchange) Events(users, calendars, events []string, opts ...option) []E
|
|||||||
func (s *exchange) EventCalendars(users, events []string, opts ...option) []ExchangeScope {
|
func (s *exchange) EventCalendars(users, events []string, opts ...option) []ExchangeScope {
|
||||||
var (
|
var (
|
||||||
scopes = []ExchangeScope{}
|
scopes = []ExchangeScope{}
|
||||||
os = append([]option{pathType()}, opts...)
|
os = append([]option{pathComparator()}, opts...)
|
||||||
)
|
)
|
||||||
|
|
||||||
scopes = append(
|
scopes = append(
|
||||||
@ -283,7 +293,7 @@ func (s *exchange) Mails(users, folders, mails []string, opts ...option) []Excha
|
|||||||
func (s *exchange) MailFolders(users, folders []string, opts ...option) []ExchangeScope {
|
func (s *exchange) MailFolders(users, folders []string, opts ...option) []ExchangeScope {
|
||||||
var (
|
var (
|
||||||
scopes = []ExchangeScope{}
|
scopes = []ExchangeScope{}
|
||||||
os = append([]option{pathType()}, opts...)
|
os = append([]option{pathComparator()}, opts...)
|
||||||
)
|
)
|
||||||
|
|
||||||
scopes = append(
|
scopes = append(
|
||||||
@ -663,7 +673,7 @@ func (s ExchangeScope) Get(cat exchangeCategory) []string {
|
|||||||
func (s ExchangeScope) set(cat exchangeCategory, v []string, opts ...option) ExchangeScope {
|
func (s ExchangeScope) set(cat exchangeCategory, v []string, opts ...option) ExchangeScope {
|
||||||
os := []option{}
|
os := []option{}
|
||||||
if cat == ExchangeContactFolder || cat == ExchangeEventCalendar || cat == ExchangeMailFolder {
|
if cat == ExchangeContactFolder || cat == ExchangeEventCalendar || cat == ExchangeMailFolder {
|
||||||
os = append(os, pathType())
|
os = append(os, pathComparator())
|
||||||
}
|
}
|
||||||
|
|
||||||
return set(s, cat, v, append(os, opts...)...)
|
return set(s, cat, v, append(os, opts...)...)
|
||||||
|
|||||||
@ -39,6 +39,7 @@ var (
|
|||||||
_ Reducer = &OneDriveRestore{}
|
_ Reducer = &OneDriveRestore{}
|
||||||
_ printabler = &OneDriveRestore{}
|
_ printabler = &OneDriveRestore{}
|
||||||
_ resourceOwnerer = &OneDriveRestore{}
|
_ resourceOwnerer = &OneDriveRestore{}
|
||||||
|
_ pathCategorier = &OneDriveRestore{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewOneDriveBackup produces a new Selector with the service set to ServiceOneDrive.
|
// NewOneDriveBackup produces a new Selector with the service set to ServiceOneDrive.
|
||||||
@ -102,6 +103,15 @@ func (s oneDrive) ResourceOwners() selectorResourceOwners {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PathCategories produces the aggregation of discrete users described by each type of scope.
|
||||||
|
func (s oneDrive) PathCategories() selectorPathCategories {
|
||||||
|
return selectorPathCategories{
|
||||||
|
Excludes: pathCategoriesIn[OneDriveScope, oneDriveCategory](s.Excludes),
|
||||||
|
Filters: pathCategoriesIn[OneDriveScope, oneDriveCategory](s.Filters),
|
||||||
|
Includes: pathCategoriesIn[OneDriveScope, oneDriveCategory](s.Includes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------
|
// -------------------
|
||||||
// Scope Factories
|
// Scope Factories
|
||||||
|
|
||||||
@ -197,7 +207,7 @@ func (s *oneDrive) Users(users []string) []OneDriveScope {
|
|||||||
func (s *oneDrive) Folders(users, folders []string, opts ...option) []OneDriveScope {
|
func (s *oneDrive) Folders(users, folders []string, opts ...option) []OneDriveScope {
|
||||||
var (
|
var (
|
||||||
scopes = []OneDriveScope{}
|
scopes = []OneDriveScope{}
|
||||||
os = append([]option{pathType()}, opts...)
|
os = append([]option{pathComparator()}, opts...)
|
||||||
)
|
)
|
||||||
|
|
||||||
scopes = append(
|
scopes = append(
|
||||||
@ -447,7 +457,7 @@ func (s OneDriveScope) Get(cat oneDriveCategory) []string {
|
|||||||
func (s OneDriveScope) set(cat oneDriveCategory, v []string, opts ...option) OneDriveScope {
|
func (s OneDriveScope) set(cat oneDriveCategory, v []string, opts ...option) OneDriveScope {
|
||||||
os := []option{}
|
os := []option{}
|
||||||
if cat == OneDriveFolder {
|
if cat == OneDriveFolder {
|
||||||
os = append(os, pathType())
|
os = append(os, pathComparator())
|
||||||
}
|
}
|
||||||
|
|
||||||
return set(s, cat, v, append(os, opts...)...)
|
return set(s, cat, v, append(os, opts...)...)
|
||||||
|
|||||||
@ -84,6 +84,19 @@ type resourceOwnerer interface {
|
|||||||
ResourceOwners() selectorResourceOwners
|
ResourceOwners() selectorResourceOwners
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// selectorResourceOwners aggregates all discrete path category types described
|
||||||
|
// in the selector. Category sets are grouped by their scope type (includes,
|
||||||
|
// excludes, filters).
|
||||||
|
type selectorPathCategories struct {
|
||||||
|
Includes []path.CategoryType
|
||||||
|
Excludes []path.CategoryType
|
||||||
|
Filters []path.CategoryType
|
||||||
|
}
|
||||||
|
|
||||||
|
type pathCategorier interface {
|
||||||
|
PathCategories() selectorPathCategories
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Selector
|
// Selector
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -205,6 +218,7 @@ func (s Selector) Reduce(ctx context.Context, deets *details.Details) (*details.
|
|||||||
return r.Reduce(ctx, deets), nil
|
return r.Reduce(ctx, deets), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns the sets of resource owners identified in each scope set.
|
||||||
func (s Selector) ResourceOwners() (selectorResourceOwners, error) {
|
func (s Selector) ResourceOwners() (selectorResourceOwners, error) {
|
||||||
ro, err := selectorAsIface[resourceOwnerer](s)
|
ro, err := selectorAsIface[resourceOwnerer](s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -214,6 +228,16 @@ func (s Selector) ResourceOwners() (selectorResourceOwners, error) {
|
|||||||
return ro.ResourceOwners(), nil
|
return ro.ResourceOwners(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns the sets of path categories identified in each scope set.
|
||||||
|
func (s Selector) PathCategories() (selectorPathCategories, error) {
|
||||||
|
ro, err := selectorAsIface[pathCategorier](s)
|
||||||
|
if err != nil {
|
||||||
|
return selectorPathCategories{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ro.PathCategories(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// transformer for arbitrary selector interfaces
|
// transformer for arbitrary selector interfaces
|
||||||
func selectorAsIface[T any](s Selector) (T, error) {
|
func selectorAsIface[T any](s Selector) (T, error) {
|
||||||
var (
|
var (
|
||||||
@ -388,6 +412,30 @@ func resourceOwnersIn(s []scope, rootCat string) []string {
|
|||||||
return rs
|
return rs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// produces the discrete set of path categories in the slice of scopes.
|
||||||
|
func pathCategoriesIn[T scopeT, C categoryT](ss []scope) []path.CategoryType {
|
||||||
|
rm := map[path.CategoryType]struct{}{}
|
||||||
|
|
||||||
|
for _, s := range ss {
|
||||||
|
t := T(s)
|
||||||
|
|
||||||
|
lc := t.categorizer().leafCat()
|
||||||
|
if lc == lc.unknownCat() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rm[lc.PathType()] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
rs := []path.CategoryType{}
|
||||||
|
|
||||||
|
for k := range rm {
|
||||||
|
rs = append(rs, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// scope helpers
|
// scope helpers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -424,10 +472,10 @@ func SuffixMatch() option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathType is an internal-facing option. It is assumed that scope
|
// pathComparator is an internal-facing option. It is assumed that scope
|
||||||
// constructors will provide the pathType option whenever a folder-
|
// constructors will provide the pathComparator option whenever a folder-
|
||||||
// level scope (ie, a scope that compares path hierarchies) is created.
|
// level scope (ie, a scope that compares path hierarchies) is created.
|
||||||
func pathType() option {
|
func pathComparator() option {
|
||||||
return func(sc *scopeConfig) {
|
return func(sc *scopeConfig) {
|
||||||
sc.usePathFilter = true
|
sc.usePathFilter = true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/pkg/filters"
|
"github.com/alcionai/corso/src/pkg/filters"
|
||||||
|
"github.com/alcionai/corso/src/pkg/path"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SelectorSuite struct {
|
type SelectorSuite struct {
|
||||||
@ -208,6 +209,39 @@ func (suite *SelectorSuite) TestResourceOwnersIn() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *SelectorSuite) TestPathCategoriesIn() {
|
||||||
|
leafCat := leafCatStub.String()
|
||||||
|
f := filters.Identity(leafCat)
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
input []scope
|
||||||
|
expect []path.CategoryType
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil",
|
||||||
|
input: nil,
|
||||||
|
expect: []path.CategoryType{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
input: []scope{},
|
||||||
|
expect: []path.CategoryType{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single",
|
||||||
|
input: []scope{{leafCat: f, scopeKeyCategory: f}},
|
||||||
|
expect: []path.CategoryType{leafCatStub.PathType()},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.T().Run(test.name, func(t *testing.T) {
|
||||||
|
result := pathCategoriesIn[mockScope, mockCategorizer](test.input)
|
||||||
|
assert.ElementsMatch(t, test.expect, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *SelectorSuite) TestContains() {
|
func (suite *SelectorSuite) TestContains() {
|
||||||
t := suite.T()
|
t := suite.T()
|
||||||
key := rootCatStub
|
key := rootCatStub
|
||||||
|
|||||||
@ -37,6 +37,7 @@ var (
|
|||||||
_ Reducer = &SharePointRestore{}
|
_ Reducer = &SharePointRestore{}
|
||||||
_ printabler = &SharePointRestore{}
|
_ printabler = &SharePointRestore{}
|
||||||
_ resourceOwnerer = &SharePointRestore{}
|
_ resourceOwnerer = &SharePointRestore{}
|
||||||
|
_ pathCategorier = &SharePointRestore{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewSharePointBackup produces a new Selector with the service set to ServiceSharePoint.
|
// NewSharePointBackup produces a new Selector with the service set to ServiceSharePoint.
|
||||||
@ -100,6 +101,15 @@ func (s sharePoint) ResourceOwners() selectorResourceOwners {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PathCategories produces the aggregation of discrete users described by each type of scope.
|
||||||
|
func (s sharePoint) PathCategories() selectorPathCategories {
|
||||||
|
return selectorPathCategories{
|
||||||
|
Excludes: pathCategoriesIn[SharePointScope, sharePointCategory](s.Excludes),
|
||||||
|
Filters: pathCategoriesIn[SharePointScope, sharePointCategory](s.Filters),
|
||||||
|
Includes: pathCategoriesIn[SharePointScope, sharePointCategory](s.Includes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------
|
// -------------------
|
||||||
// Scope Factories
|
// Scope Factories
|
||||||
|
|
||||||
@ -210,7 +220,7 @@ func (s *sharePoint) Sites(sites []string) []SharePointScope {
|
|||||||
func (s *sharePoint) Libraries(sites, libraries []string, opts ...option) []SharePointScope {
|
func (s *sharePoint) Libraries(sites, libraries []string, opts ...option) []SharePointScope {
|
||||||
var (
|
var (
|
||||||
scopes = []SharePointScope{}
|
scopes = []SharePointScope{}
|
||||||
os = append([]option{pathType()}, opts...)
|
os = append([]option{pathComparator()}, opts...)
|
||||||
)
|
)
|
||||||
|
|
||||||
scopes = append(
|
scopes = append(
|
||||||
@ -397,7 +407,7 @@ func (s SharePointScope) Get(cat sharePointCategory) []string {
|
|||||||
func (s SharePointScope) set(cat sharePointCategory, v []string, opts ...option) SharePointScope {
|
func (s SharePointScope) set(cat sharePointCategory, v []string, opts ...option) SharePointScope {
|
||||||
os := []option{}
|
os := []option{}
|
||||||
if cat == SharePointLibrary {
|
if cat == SharePointLibrary {
|
||||||
os = append(os, pathType())
|
os = append(os, pathComparator())
|
||||||
}
|
}
|
||||||
|
|
||||||
return set(s, cat, v, append(os, opts...)...)
|
return set(s, cat, v, append(os, opts...)...)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user