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:
Keepers 2022-12-09 11:57:06 -07:00 committed by GitHub
parent 65284620ea
commit edd0708285
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 124 additions and 33 deletions

View File

@ -157,28 +157,7 @@ func FilterContainersAndFillCollections(
collections[metadataKey] = col
}
// TODO(ashmrtn): getFetchIDFunc functions should probably just return a
// 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()
return errs
}
func IterativeCollectContactContainers(

View File

@ -40,6 +40,7 @@ var (
_ Reducer = &ExchangeRestore{}
_ printabler = &ExchangeRestore{}
_ resourceOwnerer = &ExchangeRestore{}
_ pathCategorier = &ExchangeRestore{}
)
// 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
@ -210,7 +220,7 @@ func (s *exchange) Contacts(users, folders, contacts []string, opts ...option) [
func (s *exchange) ContactFolders(users, folders []string, opts ...option) []ExchangeScope {
var (
scopes = []ExchangeScope{}
os = append([]option{pathType()}, opts...)
os = append([]option{pathComparator()}, opts...)
)
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 {
var (
scopes = []ExchangeScope{}
os = append([]option{pathType()}, opts...)
os = append([]option{pathComparator()}, opts...)
)
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 {
var (
scopes = []ExchangeScope{}
os = append([]option{pathType()}, opts...)
os = append([]option{pathComparator()}, opts...)
)
scopes = append(
@ -663,7 +673,7 @@ func (s ExchangeScope) Get(cat exchangeCategory) []string {
func (s ExchangeScope) set(cat exchangeCategory, v []string, opts ...option) ExchangeScope {
os := []option{}
if cat == ExchangeContactFolder || cat == ExchangeEventCalendar || cat == ExchangeMailFolder {
os = append(os, pathType())
os = append(os, pathComparator())
}
return set(s, cat, v, append(os, opts...)...)

View File

@ -39,6 +39,7 @@ var (
_ Reducer = &OneDriveRestore{}
_ printabler = &OneDriveRestore{}
_ resourceOwnerer = &OneDriveRestore{}
_ pathCategorier = &OneDriveRestore{}
)
// 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
@ -197,7 +207,7 @@ func (s *oneDrive) Users(users []string) []OneDriveScope {
func (s *oneDrive) Folders(users, folders []string, opts ...option) []OneDriveScope {
var (
scopes = []OneDriveScope{}
os = append([]option{pathType()}, opts...)
os = append([]option{pathComparator()}, opts...)
)
scopes = append(
@ -447,7 +457,7 @@ func (s OneDriveScope) Get(cat oneDriveCategory) []string {
func (s OneDriveScope) set(cat oneDriveCategory, v []string, opts ...option) OneDriveScope {
os := []option{}
if cat == OneDriveFolder {
os = append(os, pathType())
os = append(os, pathComparator())
}
return set(s, cat, v, append(os, opts...)...)

View File

@ -84,6 +84,19 @@ type resourceOwnerer interface {
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
// ---------------------------------------------------------------------------
@ -205,6 +218,7 @@ func (s Selector) Reduce(ctx context.Context, deets *details.Details) (*details.
return r.Reduce(ctx, deets), nil
}
// returns the sets of resource owners identified in each scope set.
func (s Selector) ResourceOwners() (selectorResourceOwners, error) {
ro, err := selectorAsIface[resourceOwnerer](s)
if err != nil {
@ -214,6 +228,16 @@ func (s Selector) ResourceOwners() (selectorResourceOwners, error) {
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
func selectorAsIface[T any](s Selector) (T, error) {
var (
@ -388,6 +412,30 @@ func resourceOwnersIn(s []scope, rootCat string) []string {
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
// ---------------------------------------------------------------------------
@ -424,10 +472,10 @@ func SuffixMatch() option {
}
}
// pathType is an internal-facing option. It is assumed that scope
// constructors will provide the pathType option whenever a folder-
// pathComparator is an internal-facing option. It is assumed that scope
// constructors will provide the pathComparator option whenever a folder-
// level scope (ie, a scope that compares path hierarchies) is created.
func pathType() option {
func pathComparator() option {
return func(sc *scopeConfig) {
sc.usePathFilter = true
}

View File

@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/pkg/filters"
"github.com/alcionai/corso/src/pkg/path"
)
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() {
t := suite.T()
key := rootCatStub

View File

@ -37,6 +37,7 @@ var (
_ Reducer = &SharePointRestore{}
_ printabler = &SharePointRestore{}
_ resourceOwnerer = &SharePointRestore{}
_ pathCategorier = &SharePointRestore{}
)
// 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
@ -210,7 +220,7 @@ func (s *sharePoint) Sites(sites []string) []SharePointScope {
func (s *sharePoint) Libraries(sites, libraries []string, opts ...option) []SharePointScope {
var (
scopes = []SharePointScope{}
os = append([]option{pathType()}, opts...)
os = append([]option{pathComparator()}, opts...)
)
scopes = append(
@ -397,7 +407,7 @@ func (s SharePointScope) Get(cat sharePointCategory) []string {
func (s SharePointScope) set(cat sharePointCategory, v []string, opts ...option) SharePointScope {
os := []option{}
if cat == SharePointLibrary {
os = append(os, pathType())
os = append(os, pathComparator())
}
return set(s, cat, v, append(os, opts...)...)