selectors use mulit-value short-refs (#2736)
With onedrive storage file names being changed from the file display name to the file id, we need a more granular form of indentification when using selectors to choose which values count as matchable fields. This change modifies the selector PathValues to return slices of strings for each category instead of a single string, and the reducer matches on any. This will allow each service to decide what values are considered equivalent (id, shortRef, a value inside the info, etc) for each property. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🌻 Feature #### Issue(s) * #2708 #### Test Plan - [x] ⚡ Unit test
This commit is contained in:
parent
07b4900f58
commit
23b90f9bec
@ -580,7 +580,7 @@ func (ec exchangeCategory) isLeaf() bool {
|
|||||||
// Example:
|
// Example:
|
||||||
// [tenantID, service, userPN, category, mailFolder, mailID]
|
// [tenantID, service, userPN, category, mailFolder, mailID]
|
||||||
// => {exchMailFolder: mailFolder, exchMail: mailID}
|
// => {exchMailFolder: mailFolder, exchMail: mailID}
|
||||||
func (ec exchangeCategory) pathValues(repo, location path.Path) (map[categorizer]string, map[categorizer]string) {
|
func (ec exchangeCategory) pathValues(repo path.Path, ent details.DetailsEntry) map[categorizer][]string {
|
||||||
var folderCat, itemCat categorizer
|
var folderCat, itemCat categorizer
|
||||||
|
|
||||||
switch ec {
|
switch ec {
|
||||||
@ -594,24 +594,19 @@ func (ec exchangeCategory) pathValues(repo, location path.Path) (map[categorizer
|
|||||||
folderCat, itemCat = ExchangeMailFolder, ExchangeMail
|
folderCat, itemCat = ExchangeMailFolder, ExchangeMail
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return map[categorizer]string{}, map[categorizer]string{}
|
return map[categorizer][]string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
rv := map[categorizer]string{
|
result := map[categorizer][]string{
|
||||||
folderCat: repo.Folder(false),
|
folderCat: {repo.Folder(false)},
|
||||||
itemCat: repo.Item(),
|
itemCat: {repo.Item(), ent.ShortRef},
|
||||||
}
|
}
|
||||||
|
|
||||||
lv := map[categorizer]string{}
|
if len(ent.LocationRef) > 0 {
|
||||||
|
result[folderCat] = append(result[folderCat], ent.LocationRef)
|
||||||
if location != nil {
|
|
||||||
lv = map[categorizer]string{
|
|
||||||
folderCat: location.Folder(false),
|
|
||||||
itemCat: location.Item(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv, lv
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathKeys returns the path keys recognized by the receiver's leaf type.
|
// pathKeys returns the path keys recognized by the receiver's leaf type.
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package selectors
|
package selectors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -716,9 +717,14 @@ func (suite *ExchangeSelectorSuite) TestExchangeScope_MatchesPath() {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
repo = stubPath(suite.T(), usr, []string{fID1, fID2, mail}, path.EmailCategory)
|
repo = stubPath(suite.T(), usr, []string{fID1, fID2, mail}, path.EmailCategory)
|
||||||
loc = stubPath(suite.T(), usr, []string{fld1, fld2, mail}, path.EmailCategory)
|
loc = strings.Join([]string{fld1, fld2, mail}, "/")
|
||||||
short = "thisisahashofsomekind"
|
short = "thisisahashofsomekind"
|
||||||
es = NewExchangeRestore(Any())
|
es = NewExchangeRestore(Any())
|
||||||
|
ent = details.DetailsEntry{
|
||||||
|
RepoRef: repo.String(),
|
||||||
|
ShortRef: short,
|
||||||
|
LocationRef: loc,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
table := []struct {
|
table := []struct {
|
||||||
@ -758,12 +764,12 @@ func (suite *ExchangeSelectorSuite) TestExchangeScope_MatchesPath() {
|
|||||||
scopes := setScopesToDefault(test.scope)
|
scopes := setScopesToDefault(test.scope)
|
||||||
var aMatch bool
|
var aMatch bool
|
||||||
for _, scope := range scopes {
|
for _, scope := range scopes {
|
||||||
repoVals, locVals := ExchangeMail.pathValues(repo, loc)
|
pvs := ExchangeMail.pathValues(repo, ent)
|
||||||
if matchesPathValues(scope, ExchangeMail, repoVals, short) {
|
if matchesPathValues(scope, ExchangeMail, pvs) {
|
||||||
aMatch = true
|
aMatch = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if matchesPathValues(scope, ExchangeMail, locVals, short) {
|
if matchesPathValues(scope, ExchangeMail, pvs) {
|
||||||
aMatch = true
|
aMatch = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1313,7 +1319,10 @@ func (suite *ExchangeSelectorSuite) TestPasses() {
|
|||||||
mail = setScopesToDefault(es.Mails(Any(), []string{mid}))
|
mail = setScopesToDefault(es.Mails(Any(), []string{mid}))
|
||||||
noMail = setScopesToDefault(es.Mails(Any(), None()))
|
noMail = setScopesToDefault(es.Mails(Any(), None()))
|
||||||
allMail = setScopesToDefault(es.Mails(Any(), Any()))
|
allMail = setScopesToDefault(es.Mails(Any(), Any()))
|
||||||
pth = stubPath(suite.T(), "user", []string{"folder", mid}, path.EmailCategory)
|
repo = stubPath(suite.T(), "user", []string{"folder", mid}, path.EmailCategory)
|
||||||
|
ent = details.DetailsEntry{
|
||||||
|
RepoRef: repo.String(),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
table := []struct {
|
table := []struct {
|
||||||
@ -1336,12 +1345,11 @@ func (suite *ExchangeSelectorSuite) TestPasses() {
|
|||||||
suite.Run(test.name, func() {
|
suite.Run(test.name, func() {
|
||||||
t := suite.T()
|
t := suite.T()
|
||||||
|
|
||||||
repoVals, locVals := cat.pathValues(pth, pth)
|
pvs := cat.pathValues(repo, ent)
|
||||||
|
|
||||||
result := passes(
|
result := passes(
|
||||||
cat,
|
cat,
|
||||||
repoVals,
|
pvs,
|
||||||
locVals,
|
|
||||||
entry,
|
entry,
|
||||||
test.excludes,
|
test.excludes,
|
||||||
test.filters,
|
test.filters,
|
||||||
@ -1447,25 +1455,25 @@ func (suite *ExchangeSelectorSuite) TestExchangeCategory_PathValues() {
|
|||||||
t := suite.T()
|
t := suite.T()
|
||||||
|
|
||||||
contactPath := stubPath(t, "user", []string{"cfolder", "contactitem"}, path.ContactsCategory)
|
contactPath := stubPath(t, "user", []string{"cfolder", "contactitem"}, path.ContactsCategory)
|
||||||
contactMap := map[categorizer]string{
|
contactMap := map[categorizer][]string{
|
||||||
ExchangeContactFolder: contactPath.Folder(false),
|
ExchangeContactFolder: {contactPath.Folder(false)},
|
||||||
ExchangeContact: contactPath.Item(),
|
ExchangeContact: {contactPath.Item(), "short"},
|
||||||
}
|
}
|
||||||
eventPath := stubPath(t, "user", []string{"ecalendar", "eventitem"}, path.EventsCategory)
|
eventPath := stubPath(t, "user", []string{"ecalendar", "eventitem"}, path.EventsCategory)
|
||||||
eventMap := map[categorizer]string{
|
eventMap := map[categorizer][]string{
|
||||||
ExchangeEventCalendar: eventPath.Folder(false),
|
ExchangeEventCalendar: {eventPath.Folder(false)},
|
||||||
ExchangeEvent: eventPath.Item(),
|
ExchangeEvent: {eventPath.Item(), "short"},
|
||||||
}
|
}
|
||||||
mailPath := stubPath(t, "user", []string{"mfolder", "mailitem"}, path.EmailCategory)
|
mailPath := stubPath(t, "user", []string{"mfolder", "mailitem"}, path.EmailCategory)
|
||||||
mailMap := map[categorizer]string{
|
mailMap := map[categorizer][]string{
|
||||||
ExchangeMailFolder: mailPath.Folder(false),
|
ExchangeMailFolder: {mailPath.Folder(false)},
|
||||||
ExchangeMail: mailPath.Item(),
|
ExchangeMail: {mailPath.Item(), "short"},
|
||||||
}
|
}
|
||||||
|
|
||||||
table := []struct {
|
table := []struct {
|
||||||
cat exchangeCategory
|
cat exchangeCategory
|
||||||
path path.Path
|
path path.Path
|
||||||
expect map[categorizer]string
|
expect map[categorizer][]string
|
||||||
}{
|
}{
|
||||||
{ExchangeContact, contactPath, contactMap},
|
{ExchangeContact, contactPath, contactMap},
|
||||||
{ExchangeEvent, eventPath, eventMap},
|
{ExchangeEvent, eventPath, eventMap},
|
||||||
@ -1473,9 +1481,13 @@ func (suite *ExchangeSelectorSuite) TestExchangeCategory_PathValues() {
|
|||||||
}
|
}
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
suite.T().Run(string(test.cat), func(t *testing.T) {
|
suite.T().Run(string(test.cat), func(t *testing.T) {
|
||||||
r, l := test.cat.pathValues(test.path, test.path)
|
ent := details.DetailsEntry{
|
||||||
assert.Equal(t, test.expect, r)
|
RepoRef: test.path.String(),
|
||||||
assert.Equal(t, test.expect, l)
|
ShortRef: "short",
|
||||||
|
}
|
||||||
|
|
||||||
|
pvs := test.cat.pathValues(test.path, ent)
|
||||||
|
assert.Equal(t, test.expect, pvs)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,13 +55,11 @@ func (mc mockCategorizer) isLeaf() bool {
|
|||||||
return mc == leafCatStub
|
return mc == leafCatStub
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc mockCategorizer) pathValues(repo, location path.Path) (map[categorizer]string, map[categorizer]string) {
|
func (mc mockCategorizer) pathValues(repo path.Path, ent details.DetailsEntry) map[categorizer][]string {
|
||||||
pv := map[categorizer]string{
|
return map[categorizer][]string{
|
||||||
rootCatStub: "root",
|
rootCatStub: {"root"},
|
||||||
leafCatStub: "leaf",
|
leafCatStub: {"leaf"},
|
||||||
}
|
}
|
||||||
|
|
||||||
return pv, pv
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc mockCategorizer) pathKeys() []categorizer {
|
func (mc mockCategorizer) pathKeys() []categorizer {
|
||||||
@ -77,10 +75,10 @@ func (mc mockCategorizer) PathType() path.CategoryType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stubPathValues() map[categorizer]string {
|
func stubPathValues() map[categorizer][]string {
|
||||||
return map[categorizer]string{
|
return map[categorizer][]string{
|
||||||
rootCatStub: rootCatStub.String(),
|
rootCatStub: {rootCatStub.String()},
|
||||||
leafCatStub: leafCatStub.String(),
|
leafCatStub: {leafCatStub.String()},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -65,7 +65,7 @@ func (s Selector) ToOneDriveBackup() (*OneDriveBackup, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s OneDriveBackup) SplitByResourceOwner(users []string) []OneDriveBackup {
|
func (s OneDriveBackup) SplitByResourceOwner(users []string) []OneDriveBackup {
|
||||||
sels := splitByResourceOwner[ExchangeScope](s.Selector, users, OneDriveUser)
|
sels := splitByResourceOwner[OneDriveScope](s.Selector, users, OneDriveUser)
|
||||||
|
|
||||||
ss := make([]OneDriveBackup, 0, len(sels))
|
ss := make([]OneDriveBackup, 0, len(sels))
|
||||||
for _, sel := range sels {
|
for _, sel := range sels {
|
||||||
@ -99,7 +99,7 @@ func (s Selector) ToOneDriveRestore() (*OneDriveRestore, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s OneDriveRestore) SplitByResourceOwner(users []string) []OneDriveRestore {
|
func (s OneDriveRestore) SplitByResourceOwner(users []string) []OneDriveRestore {
|
||||||
sels := splitByResourceOwner[ExchangeScope](s.Selector, users, ExchangeUser)
|
sels := splitByResourceOwner[OneDriveScope](s.Selector, users, OneDriveUser)
|
||||||
|
|
||||||
ss := make([]OneDriveRestore, 0, len(sels))
|
ss := make([]OneDriveRestore, 0, len(sels))
|
||||||
for _, sel := range sels {
|
for _, sel := range sels {
|
||||||
@ -376,25 +376,20 @@ func (c oneDriveCategory) isLeaf() bool {
|
|||||||
// Example:
|
// Example:
|
||||||
// [tenantID, service, userPN, category, folder, fileID]
|
// [tenantID, service, userPN, category, folder, fileID]
|
||||||
// => {odFolder: folder, odFileID: fileID}
|
// => {odFolder: folder, odFileID: fileID}
|
||||||
func (c oneDriveCategory) pathValues(repo, location path.Path) (map[categorizer]string, map[categorizer]string) {
|
func (c oneDriveCategory) pathValues(repo path.Path, ent details.DetailsEntry) map[categorizer][]string {
|
||||||
// Ignore `drives/<driveID>/root:` for folder comparison
|
// Ignore `drives/<driveID>/root:` for folder comparison
|
||||||
rFld := path.Builder{}.Append(repo.Folders()...).PopFront().PopFront().PopFront().String()
|
rFld := path.Builder{}.Append(repo.Folders()...).PopFront().PopFront().PopFront().String()
|
||||||
rv := map[categorizer]string{
|
|
||||||
OneDriveFolder: rFld,
|
result := map[categorizer][]string{
|
||||||
OneDriveItem: repo.Item(),
|
OneDriveFolder: {rFld},
|
||||||
|
OneDriveItem: {repo.Item(), ent.ShortRef},
|
||||||
}
|
}
|
||||||
|
|
||||||
lv := map[categorizer]string{}
|
if len(ent.LocationRef) > 0 {
|
||||||
|
result[OneDriveFolder] = append(result[OneDriveFolder], ent.LocationRef)
|
||||||
if location != nil {
|
|
||||||
lFld := path.Builder{}.Append(location.Folders()...).PopFront().PopFront().PopFront().String()
|
|
||||||
lv = map[categorizer]string{
|
|
||||||
OneDriveFolder: lFld,
|
|
||||||
OneDriveItem: location.Item(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv, lv
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathKeys returns the path keys recognized by the receiver's leaf type.
|
// pathKeys returns the path keys recognized by the receiver's leaf type.
|
||||||
|
|||||||
@ -261,14 +261,18 @@ func (suite *OneDriveSelectorSuite) TestOneDriveCategory_PathValues() {
|
|||||||
filePath, err := pathBuilder.ToDataLayerOneDrivePath("tenant", "user", true)
|
filePath, err := pathBuilder.ToDataLayerOneDrivePath("tenant", "user", true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected := map[categorizer]string{
|
expected := map[categorizer][]string{
|
||||||
OneDriveFolder: "dir1/dir2",
|
OneDriveFolder: {"dir1/dir2"},
|
||||||
OneDriveItem: "file",
|
OneDriveItem: {"file", "short"},
|
||||||
}
|
}
|
||||||
|
|
||||||
r, l := OneDriveItem.pathValues(filePath, filePath)
|
ent := details.DetailsEntry{
|
||||||
|
RepoRef: filePath.String(),
|
||||||
|
ShortRef: "short",
|
||||||
|
}
|
||||||
|
|
||||||
|
r := OneDriveItem.pathValues(filePath, ent)
|
||||||
assert.Equal(t, expected, r)
|
assert.Equal(t, expected, r)
|
||||||
assert.Equal(t, expected, l)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *OneDriveSelectorSuite) TestOneDriveScope_MatchesInfo() {
|
func (suite *OneDriveSelectorSuite) TestOneDriveScope_MatchesInfo() {
|
||||||
|
|||||||
@ -88,7 +88,7 @@ type (
|
|||||||
// folderCat: folder,
|
// folderCat: folder,
|
||||||
// itemCat: itemID,
|
// itemCat: itemID,
|
||||||
// }
|
// }
|
||||||
pathValues(path.Path, path.Path) (map[categorizer]string, map[categorizer]string)
|
pathValues(path.Path, details.DetailsEntry) map[categorizer][]string
|
||||||
|
|
||||||
// pathKeys produces a list of categorizers that can be used as keys in the pathValues
|
// pathKeys produces a list of categorizers that can be used as keys in the pathValues
|
||||||
// map. The combination of the two funcs generically interprets the context of the
|
// map. The combination of the two funcs generically interprets the context of the
|
||||||
@ -212,6 +212,20 @@ func matches[T scopeT, C categoryT](s T, cat C, inpt string) bool {
|
|||||||
return s[cat.String()].Compare(inpt)
|
return s[cat.String()].Compare(inpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// matchesAny returns true if the category is included in the scope's
|
||||||
|
// data type, and any one of the input strings passes the scope's filter.
|
||||||
|
func matchesAny[T scopeT, C categoryT](s T, cat C, inpts []string) bool {
|
||||||
|
if !typeAndCategoryMatches(cat, s.categorizer()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(inpts) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return s[cat.String()].CompareAny(inpts...)
|
||||||
|
}
|
||||||
|
|
||||||
// getCategory returns the scope's category value.
|
// getCategory returns the scope's category value.
|
||||||
// if s is a filter-type scope, returns the filter category.
|
// if s is a filter-type scope, returns the filter category.
|
||||||
func getCategory[T scopeT](s T) string {
|
func getCategory[T scopeT](s T) string {
|
||||||
@ -297,6 +311,8 @@ func reduce[T scopeT, C categoryT](
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
el := errs.Local()
|
||||||
|
|
||||||
// if a DiscreteOwner is specified, only match details for that owner.
|
// if a DiscreteOwner is specified, only match details for that owner.
|
||||||
matchesResourceOwner := s.ResourceOwners
|
matchesResourceOwner := s.ResourceOwners
|
||||||
if len(s.DiscreteOwner) > 0 {
|
if len(s.DiscreteOwner) > 0 {
|
||||||
@ -314,35 +330,10 @@ func reduce[T scopeT, C categoryT](
|
|||||||
for _, ent := range deets.Items() {
|
for _, ent := range deets.Items() {
|
||||||
repoPath, err := path.FromDataLayerPath(ent.RepoRef, true)
|
repoPath, err := path.FromDataLayerPath(ent.RepoRef, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs.AddRecoverable(clues.Wrap(err, "transforming repoRef to path").WithClues(ctx))
|
el.AddRecoverable(clues.Wrap(err, "transforming repoRef to path").WithClues(ctx))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var locationPath path.Path
|
|
||||||
|
|
||||||
// if the details entry has a locationRef specified, use those folders in place
|
|
||||||
// of the repoRef folders, so that scopes can match against the display names
|
|
||||||
// instead of container IDs.
|
|
||||||
if len(ent.LocationRef) > 0 {
|
|
||||||
pb, err := path.Builder{}.SplitUnescapeAppend(ent.LocationRef)
|
|
||||||
if err != nil {
|
|
||||||
errs.AddRecoverable(clues.Wrap(err, "transforming locationRef to path").WithClues(ctx))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
locationPath, err = pb.Append(repoPath.Item()).
|
|
||||||
ToDataLayerPath(
|
|
||||||
repoPath.Tenant(),
|
|
||||||
repoPath.ResourceOwner(),
|
|
||||||
repoPath.Service(),
|
|
||||||
repoPath.Category(),
|
|
||||||
true)
|
|
||||||
if err != nil {
|
|
||||||
errs.AddRecoverable(clues.Wrap(err, "transforming locationRef to path").WithClues(ctx))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// first check, every entry needs to match the selector's resource owners.
|
// first check, every entry needs to match the selector's resource owners.
|
||||||
if !matchesResourceOwner.Compare(repoPath.ResourceOwner()) {
|
if !matchesResourceOwner.Compare(repoPath.ResourceOwner()) {
|
||||||
continue
|
continue
|
||||||
@ -360,9 +351,9 @@ func reduce[T scopeT, C categoryT](
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rv, lv := dc.pathValues(repoPath, locationPath)
|
pv := dc.pathValues(repoPath, *ent)
|
||||||
|
|
||||||
passed := passes(dc, rv, lv, *ent, e, f, i)
|
passed := passes(dc, pv, *ent, e, f, i)
|
||||||
if passed {
|
if passed {
|
||||||
ents = append(ents, *ent)
|
ents = append(ents, *ent)
|
||||||
}
|
}
|
||||||
@ -407,7 +398,7 @@ func scopesByCategory[T scopeT, C categoryT](
|
|||||||
// if the path is included, passes filters, and not excluded.
|
// if the path is included, passes filters, and not excluded.
|
||||||
func passes[T scopeT, C categoryT](
|
func passes[T scopeT, C categoryT](
|
||||||
cat C,
|
cat C,
|
||||||
repoValues, locationValues map[categorizer]string,
|
pathValues map[categorizer][]string,
|
||||||
entry details.DetailsEntry,
|
entry details.DetailsEntry,
|
||||||
excs, filts, incs []T,
|
excs, filts, incs []T,
|
||||||
) bool {
|
) bool {
|
||||||
@ -423,7 +414,7 @@ func passes[T scopeT, C categoryT](
|
|||||||
var included bool
|
var included bool
|
||||||
|
|
||||||
for _, inc := range incs {
|
for _, inc := range incs {
|
||||||
if matchesEntry(inc, cat, repoValues, locationValues, entry) {
|
if matchesEntry(inc, cat, pathValues, entry) {
|
||||||
included = true
|
included = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -436,14 +427,14 @@ func passes[T scopeT, C categoryT](
|
|||||||
|
|
||||||
// all filters must pass
|
// all filters must pass
|
||||||
for _, filt := range filts {
|
for _, filt := range filts {
|
||||||
if !matchesEntry(filt, cat, repoValues, locationValues, entry) {
|
if !matchesEntry(filt, cat, pathValues, entry) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// any matching exclusion means failure
|
// any matching exclusion means failure
|
||||||
for _, exc := range excs {
|
for _, exc := range excs {
|
||||||
if matchesEntry(exc, cat, repoValues, locationValues, entry) {
|
if matchesEntry(exc, cat, pathValues, entry) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,7 +447,7 @@ func passes[T scopeT, C categoryT](
|
|||||||
func matchesEntry[T scopeT, C categoryT](
|
func matchesEntry[T scopeT, C categoryT](
|
||||||
sc T,
|
sc T,
|
||||||
cat C,
|
cat C,
|
||||||
repoValues, locationValues map[categorizer]string,
|
pathValues map[categorizer][]string,
|
||||||
entry details.DetailsEntry,
|
entry details.DetailsEntry,
|
||||||
) bool {
|
) bool {
|
||||||
// filterCategory requires matching against service-specific info values
|
// filterCategory requires matching against service-specific info values
|
||||||
@ -464,11 +455,7 @@ func matchesEntry[T scopeT, C categoryT](
|
|||||||
return sc.matchesInfo(entry.ItemInfo)
|
return sc.matchesInfo(entry.ItemInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(locationValues) > 0 && matchesPathValues(sc, cat, locationValues, entry.ShortRef) {
|
return matchesPathValues(sc, cat, pathValues)
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchesPathValues(sc, cat, repoValues, entry.ShortRef)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchesPathValues will check whether the pathValues have matching entries
|
// matchesPathValues will check whether the pathValues have matching entries
|
||||||
@ -479,8 +466,7 @@ func matchesEntry[T scopeT, C categoryT](
|
|||||||
func matchesPathValues[T scopeT, C categoryT](
|
func matchesPathValues[T scopeT, C categoryT](
|
||||||
sc T,
|
sc T,
|
||||||
cat C,
|
cat C,
|
||||||
pathValues map[categorizer]string,
|
pathValues map[categorizer][]string,
|
||||||
shortRef string,
|
|
||||||
) bool {
|
) bool {
|
||||||
for _, c := range cat.pathKeys() {
|
for _, c := range cat.pathKeys() {
|
||||||
// resourceOwners are now checked at the beginning of the reduction.
|
// resourceOwners are now checked at the beginning of the reduction.
|
||||||
@ -488,12 +474,6 @@ func matchesPathValues[T scopeT, C categoryT](
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// the pathValues must have an entry for the given categorizer
|
|
||||||
pathVal, ok := pathValues[c]
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
cc := c.(C)
|
cc := c.(C)
|
||||||
|
|
||||||
if isNoneTarget(sc, cc) {
|
if isNoneTarget(sc, cc) {
|
||||||
@ -505,23 +485,13 @@ func matchesPathValues[T scopeT, C categoryT](
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// the pathValues must have an entry for the given categorizer
|
||||||
match bool
|
pathVals, ok := pathValues[c]
|
||||||
isLeaf = c.isLeaf()
|
if !ok || len(pathVals) == 0 {
|
||||||
)
|
return false
|
||||||
|
|
||||||
switch {
|
|
||||||
// Leaf category - the scope can match either the path value (the item ID itself),
|
|
||||||
// or the shortRef hash representing the item.
|
|
||||||
case isLeaf && len(shortRef) > 0:
|
|
||||||
match = matches(sc, cc, pathVal) || matches(sc, cc, shortRef)
|
|
||||||
|
|
||||||
// all other categories (root, folder, etc) just need to pass the filter
|
|
||||||
default:
|
|
||||||
match = matches(sc, cc, pathVal)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !match {
|
if !matchesAny(sc, cc, pathVals) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -530,7 +500,7 @@ func matchesPathValues[T scopeT, C categoryT](
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// categorizer funcs
|
// helper funcs
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// categoryMatches returns true if:
|
// categoryMatches returns true if:
|
||||||
|
|||||||
@ -354,10 +354,14 @@ func (suite *SelectorScopesSuite) TestScopesByCategory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *SelectorScopesSuite) TestPasses() {
|
func (suite *SelectorScopesSuite) TestPasses() {
|
||||||
cat := rootCatStub
|
var (
|
||||||
pth := stubPath(suite.T(), "uid", []string{"fld"}, path.EventsCategory)
|
cat = rootCatStub
|
||||||
repoVals, locVals := cat.pathValues(pth, pth)
|
pth = stubPath(suite.T(), "uid", []string{"fld"}, path.EventsCategory)
|
||||||
entry := details.DetailsEntry{}
|
entry = details.DetailsEntry{
|
||||||
|
RepoRef: pth.String(),
|
||||||
|
}
|
||||||
|
pvs = cat.pathValues(pth, entry)
|
||||||
|
)
|
||||||
|
|
||||||
for _, test := range reduceTestTable {
|
for _, test := range reduceTestTable {
|
||||||
suite.Run(test.name, func() {
|
suite.Run(test.name, func() {
|
||||||
@ -369,8 +373,7 @@ func (suite *SelectorScopesSuite) TestPasses() {
|
|||||||
incl := toMockScope(sel.Includes)
|
incl := toMockScope(sel.Includes)
|
||||||
result := passes(
|
result := passes(
|
||||||
cat,
|
cat,
|
||||||
repoVals,
|
pvs,
|
||||||
locVals,
|
|
||||||
entry,
|
entry,
|
||||||
excl, filt, incl)
|
excl, filt, incl)
|
||||||
test.expectPasses(t, result)
|
test.expectPasses(t, result)
|
||||||
@ -394,7 +397,6 @@ func toMockScope(sc []scope) []mockScope {
|
|||||||
|
|
||||||
func (suite *SelectorScopesSuite) TestMatchesPathValues() {
|
func (suite *SelectorScopesSuite) TestMatchesPathValues() {
|
||||||
cat := rootCatStub
|
cat := rootCatStub
|
||||||
pvs := stubPathValues()
|
|
||||||
short := "brunheelda"
|
short := "brunheelda"
|
||||||
|
|
||||||
table := []struct {
|
table := []struct {
|
||||||
@ -440,12 +442,14 @@ func (suite *SelectorScopesSuite) TestMatchesPathValues() {
|
|||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
suite.Run(test.name, func() {
|
suite.Run(test.name, func() {
|
||||||
t := suite.T()
|
t := suite.T()
|
||||||
|
pvs := stubPathValues()
|
||||||
|
pvs[leafCatStub] = append(pvs[leafCatStub], test.shortRef)
|
||||||
|
|
||||||
sc := stubScope("")
|
sc := stubScope("")
|
||||||
sc[rootCatStub.String()] = filterize(scopeConfig{}, test.rootVal)
|
sc[rootCatStub.String()] = filterize(scopeConfig{}, test.rootVal)
|
||||||
sc[leafCatStub.String()] = filterize(scopeConfig{}, test.leafVal)
|
sc[leafCatStub.String()] = filterize(scopeConfig{}, test.leafVal)
|
||||||
|
|
||||||
test.expect(t, matchesPathValues(sc, cat, pvs, test.shortRef))
|
test.expect(t, matchesPathValues(sc, cat, pvs))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,7 +65,7 @@ func (s Selector) ToSharePointBackup() (*SharePointBackup, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s SharePointBackup) SplitByResourceOwner(sites []string) []SharePointBackup {
|
func (s SharePointBackup) SplitByResourceOwner(sites []string) []SharePointBackup {
|
||||||
sels := splitByResourceOwner[ExchangeScope](s.Selector, sites, SharePointSite)
|
sels := splitByResourceOwner[SharePointScope](s.Selector, sites, SharePointSite)
|
||||||
|
|
||||||
ss := make([]SharePointBackup, 0, len(sels))
|
ss := make([]SharePointBackup, 0, len(sels))
|
||||||
for _, sel := range sels {
|
for _, sel := range sels {
|
||||||
@ -98,8 +98,8 @@ func (s Selector) ToSharePointRestore() (*SharePointRestore, error) {
|
|||||||
return &src, nil
|
return &src, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SharePointRestore) SplitByResourceOwner(users []string) []SharePointRestore {
|
func (s SharePointRestore) SplitByResourceOwner(sites []string) []SharePointRestore {
|
||||||
sels := splitByResourceOwner[ExchangeScope](s.Selector, users, ExchangeUser)
|
sels := splitByResourceOwner[SharePointScope](s.Selector, sites, SharePointSite)
|
||||||
|
|
||||||
ss := make([]SharePointRestore, 0, len(sels))
|
ss := make([]SharePointRestore, 0, len(sels))
|
||||||
for _, sel := range sels {
|
for _, sel := range sels {
|
||||||
@ -476,7 +476,7 @@ func (c sharePointCategory) isLeaf() bool {
|
|||||||
// Example:
|
// Example:
|
||||||
// [tenantID, service, siteID, category, folder, itemID]
|
// [tenantID, service, siteID, category, folder, itemID]
|
||||||
// => {spFolder: folder, spItemID: itemID}
|
// => {spFolder: folder, spItemID: itemID}
|
||||||
func (c sharePointCategory) pathValues(repo, location path.Path) (map[categorizer]string, map[categorizer]string) {
|
func (c sharePointCategory) pathValues(repo path.Path, ent details.DetailsEntry) map[categorizer][]string {
|
||||||
var folderCat, itemCat categorizer
|
var folderCat, itemCat categorizer
|
||||||
|
|
||||||
switch c {
|
switch c {
|
||||||
@ -487,24 +487,19 @@ func (c sharePointCategory) pathValues(repo, location path.Path) (map[categorize
|
|||||||
case SharePointPage, SharePointPageFolder:
|
case SharePointPage, SharePointPageFolder:
|
||||||
folderCat, itemCat = SharePointPageFolder, SharePointPage
|
folderCat, itemCat = SharePointPageFolder, SharePointPage
|
||||||
default:
|
default:
|
||||||
return map[categorizer]string{}, map[categorizer]string{}
|
return map[categorizer][]string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
rv := map[categorizer]string{
|
result := map[categorizer][]string{
|
||||||
folderCat: repo.Folder(false),
|
folderCat: {repo.Folder(false)},
|
||||||
itemCat: repo.Item(),
|
itemCat: {repo.Item(), ent.ShortRef},
|
||||||
}
|
}
|
||||||
|
|
||||||
lv := map[categorizer]string{}
|
if len(ent.LocationRef) > 0 {
|
||||||
|
result[folderCat] = append(result[folderCat], ent.LocationRef)
|
||||||
if location != nil {
|
|
||||||
lv = map[categorizer]string{
|
|
||||||
folderCat: location.Folder(false),
|
|
||||||
itemCat: location.Item(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv, lv
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathKeys returns the path keys recognized by the receiver's leaf type.
|
// pathKeys returns the path keys recognized by the receiver's leaf type.
|
||||||
|
|||||||
@ -326,22 +326,22 @@ func (suite *SharePointSelectorSuite) TestSharePointCategory_PathValues() {
|
|||||||
table := []struct {
|
table := []struct {
|
||||||
name string
|
name string
|
||||||
sc sharePointCategory
|
sc sharePointCategory
|
||||||
expected map[categorizer]string
|
expected map[categorizer][]string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "SharePoint Libraries",
|
name: "SharePoint Libraries",
|
||||||
sc: SharePointLibraryItem,
|
sc: SharePointLibraryItem,
|
||||||
expected: map[categorizer]string{
|
expected: map[categorizer][]string{
|
||||||
SharePointLibrary: "dir1/dir2",
|
SharePointLibrary: {"dir1/dir2"},
|
||||||
SharePointLibraryItem: "item",
|
SharePointLibraryItem: {"item", "short"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SharePoint Lists",
|
name: "SharePoint Lists",
|
||||||
sc: SharePointListItem,
|
sc: SharePointListItem,
|
||||||
expected: map[categorizer]string{
|
expected: map[categorizer][]string{
|
||||||
SharePointList: "dir1/dir2",
|
SharePointList: {"dir1/dir2"},
|
||||||
SharePointListItem: "item",
|
SharePointListItem: {"item", "short"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -356,9 +356,14 @@ func (suite *SharePointSelectorSuite) TestSharePointCategory_PathValues() {
|
|||||||
test.sc.PathType(),
|
test.sc.PathType(),
|
||||||
true)
|
true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
r, l := test.sc.pathValues(itemPath, itemPath)
|
|
||||||
assert.Equal(t, test.expected, r)
|
ent := details.DetailsEntry{
|
||||||
assert.Equal(t, test.expected, l)
|
RepoRef: itemPath.String(),
|
||||||
|
ShortRef: "short",
|
||||||
|
}
|
||||||
|
|
||||||
|
pv := test.sc.pathValues(itemPath, ent)
|
||||||
|
assert.Equal(t, test.expected, pv)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user