utilize shorthash in selector sels (#854)

## Description

The details entry shorthash can be treated as
equal to the leaf item ID.  This adds support to
the selector reduce step to compare the leaf val
to either the path item ID or the shortHash.

## Type of change

- [x] 🌻 Feature

## Issue(s)

* closes #572

## Test Plan

- [ ] 💪 Manual
- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Keepers 2022-09-15 10:53:03 -06:00 committed by GitHub
parent a368570e20
commit ed52a07e2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 77 additions and 32 deletions

View File

@ -551,6 +551,11 @@ func (ec exchangeCategory) unknownCat() categorizer {
return ExchangeCategoryUnknown return ExchangeCategoryUnknown
} }
// isLeaf is true if the category is a mail, event, or contact category.
func (ec exchangeCategory) isLeaf() bool {
return ec == ec.leafCat()
}
// pathValues transforms a path to a map of identified properties. // pathValues transforms a path to a map of identified properties.
// //
// Example: // Example:
@ -692,7 +697,7 @@ func (s ExchangeScope) matchesEntry(
entry details.DetailsEntry, entry details.DetailsEntry,
) bool { ) bool {
// matchesPathValues can be handled generically, thanks to SCIENCE. // matchesPathValues can be handled generically, thanks to SCIENCE.
return matchesPathValues(s, cat.(exchangeCategory), pathValues) || s.matchesInfo(entry.Exchange) return matchesPathValues(s, cat.(exchangeCategory), pathValues, entry.ShortRef) || s.matchesInfo(entry.Exchange)
} }
// matchesInfo handles the standard behavior when comparing a scope and an ExchangeFilter // matchesInfo handles the standard behavior when comparing a scope and an ExchangeFilter

View File

@ -779,30 +779,34 @@ func (suite *ExchangeSelectorSuite) TestExchangeScope_MatchesPath() {
) )
var ( var (
pth = stubPath(suite.T(), usr, []string{fld, mail}, path.EmailCategory) pth = stubPath(suite.T(), usr, []string{fld, mail}, path.EmailCategory)
es = NewExchangeRestore() short = "thisisahashofsomekind"
es = NewExchangeRestore()
) )
table := []struct { table := []struct {
name string name string
scope []ExchangeScope scope []ExchangeScope
expect assert.BoolAssertionFunc shortRef string
expect assert.BoolAssertionFunc
}{ }{
{"all user's items", es.Users(Any()), assert.True}, {"all user's items", es.Users(Any()), "", assert.True},
{"no user's items", es.Users(None()), assert.False}, {"no user's items", es.Users(None()), "", assert.False},
{"matching user", es.Users([]string{usr}), assert.True}, {"matching user", es.Users([]string{usr}), "", assert.True},
{"non-matching user", es.Users([]string{"smarf"}), assert.False}, {"non-matching user", es.Users([]string{"smarf"}), "", assert.False},
{"one of multiple users", es.Users([]string{"smarf", usr}), assert.True}, {"one of multiple users", es.Users([]string{"smarf", usr}), "", assert.True},
{"all folders", es.MailFolders(Any(), Any()), assert.True}, {"all folders", es.MailFolders(Any(), Any()), "", assert.True},
{"no folders", es.MailFolders(Any(), None()), assert.False}, {"no folders", es.MailFolders(Any(), None()), "", assert.False},
{"matching folder", es.MailFolders(Any(), []string{fld}), assert.True}, {"matching folder", es.MailFolders(Any(), []string{fld}), "", assert.True},
{"non-matching folder", es.MailFolders(Any(), []string{"smarf"}), assert.False}, {"non-matching folder", es.MailFolders(Any(), []string{"smarf"}), "", assert.False},
{"one of multiple folders", es.MailFolders(Any(), []string{"smarf", fld}), assert.True}, {"one of multiple folders", es.MailFolders(Any(), []string{"smarf", fld}), "", assert.True},
{"all mail", es.Mails(Any(), Any(), Any()), assert.True}, {"all mail", es.Mails(Any(), Any(), Any()), "", assert.True},
{"no mail", es.Mails(Any(), Any(), None()), assert.False}, {"no mail", es.Mails(Any(), Any(), None()), "", assert.False},
{"matching mail", es.Mails(Any(), Any(), []string{mail}), assert.True}, {"matching mail", es.Mails(Any(), Any(), []string{mail}), "", assert.True},
{"non-matching mail", es.Mails(Any(), Any(), []string{"smarf"}), assert.False}, {"non-matching mail", es.Mails(Any(), Any(), []string{"smarf"}), "", assert.False},
{"one of multiple mails", es.Mails(Any(), Any(), []string{"smarf", mail}), assert.True}, {"one of multiple mails", es.Mails(Any(), Any(), []string{"smarf", mail}), "", assert.True},
{"mail short ref", es.Mails(Any(), Any(), []string{short}), short, assert.True},
{"non-leaf short ref", es.Mails([]string{short}, []string{short}, []string{"foo"}), short, assert.False},
} }
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
@ -810,7 +814,7 @@ func (suite *ExchangeSelectorSuite) TestExchangeScope_MatchesPath() {
var aMatch bool var aMatch bool
for _, scope := range scopes { for _, scope := range scopes {
pv := ExchangeMail.pathValues(pth) pv := ExchangeMail.pathValues(pth)
if matchesPathValues(scope, ExchangeMail, pv) { if matchesPathValues(scope, ExchangeMail, pv, short) {
aMatch = true aMatch = true
break break
} }
@ -1072,7 +1076,8 @@ func (suite *ExchangeSelectorSuite) TestScopesByCategory() {
} }
func (suite *ExchangeSelectorSuite) TestPasses() { func (suite *ExchangeSelectorSuite) TestPasses() {
deets := details.DetailsEntry{} short := "thisisahashofsomekind"
entry := details.DetailsEntry{ShortRef: short}
const ( const (
mid = "mailID" mid = "mailID"
@ -1118,7 +1123,7 @@ func (suite *ExchangeSelectorSuite) TestPasses() {
result := passes( result := passes(
cat, cat,
cat.pathValues(pth), cat.pathValues(pth),
deets, entry,
test.excludes, test.excludes,
test.filters, test.filters,
test.includes) test.includes)

View File

@ -47,6 +47,10 @@ func (mc mockCategorizer) unknownCat() categorizer {
return unknownCatStub return unknownCatStub
} }
func (mc mockCategorizer) isLeaf() bool {
return mc == leafCatStub
}
func (mc mockCategorizer) pathValues(pth path.Path) map[categorizer]string { func (mc mockCategorizer) pathValues(pth path.Path) map[categorizer]string {
return map[categorizer]string{rootCatStub: "stub"} return map[categorizer]string{rootCatStub: "stub"}
} }

View File

@ -183,6 +183,11 @@ func (c oneDriveCategory) unknownCat() categorizer {
return OneDriveCategoryUnknown return OneDriveCategoryUnknown
} }
// isLeaf is true if the category is a mail, event, or contact category.
func (c oneDriveCategory) isLeaf() bool {
return c == c.leafCat()
}
// pathValues transforms a path to a map of identified properties. // pathValues transforms a path to a map of identified properties.
// //
// Example: // Example:
@ -271,7 +276,7 @@ func (s OneDriveScope) matchesEntry(
entry details.DetailsEntry, entry details.DetailsEntry,
) bool { ) bool {
// matchesPathValues can be handled generically, thanks to SCIENCE. // matchesPathValues can be handled generically, thanks to SCIENCE.
return matchesPathValues(s, cat.(oneDriveCategory), pathValues) || s.matchesInfo(entry.OneDrive) return matchesPathValues(s, cat.(oneDriveCategory), pathValues, entry.ShortRef) || s.matchesInfo(entry.OneDrive)
} }
// matchesInfo handles the standard behavior when comparing a scope and an oneDriveInfo // matchesInfo handles the standard behavior when comparing a scope and an oneDriveInfo

View File

@ -32,6 +32,10 @@ type (
// unknownType returns the unknown category value // unknownType returns the unknown category value
unknownCat() categorizer unknownCat() categorizer
// isLeaf returns true if the category is one of the leaf categories.
// eg: in a resourceOwner/folder/item structure, the item is the leaf.
isLeaf() bool
// pathValues should produce a map of category:string pairs populated by extracting // pathValues should produce a map of category:string pairs populated by extracting
// values out of the path.Path struct. // values out of the path.Path struct.
// //
@ -205,7 +209,6 @@ func isAnyTarget[T scopeT, C categoryT](s T, cat C) bool {
// reduce filters the entries in the details to only those that match the // reduce filters the entries in the details to only those that match the
// inclusions, filters, and exclusions in the selector. // inclusions, filters, and exclusions in the selector.
//
func reduce[T scopeT, C categoryT]( func reduce[T scopeT, C categoryT](
ctx context.Context, ctx context.Context,
deets *details.Details, deets *details.Details,
@ -337,6 +340,7 @@ 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 {
// if scope specifies a filter category, // if scope specifies a filter category,
// path checking is automatically skipped. // path checking is automatically skipped.
@ -362,7 +366,12 @@ func matchesPathValues[T scopeT, C categoryT](
// all parts of the scope must match // all parts of the scope must match
cc := c.(C) cc := c.(C)
if !isAnyTarget(sc, cc) { if !isAnyTarget(sc, cc) {
if filters.NotContains(join(scopeVals...)).Compare(pathVal) { notMatch := filters.NotContains(join(scopeVals...))
if c.isLeaf() && len(shortRef) > 0 {
if notMatch.Compare(pathVal) && notMatch.Compare(shortRef) {
return false
}
} else if notMatch.Compare(pathVal) {
return false return false
} }
} }

View File

@ -301,12 +301,15 @@ func toMockScope(sc []scope) []mockScope {
func (suite *SelectorScopesSuite) TestMatchesPathValues() { func (suite *SelectorScopesSuite) TestMatchesPathValues() {
cat := rootCatStub cat := rootCatStub
pvs := stubPathValues() pvs := stubPathValues()
short := "brunheelda"
table := []struct { table := []struct {
name string name string
rootVal string cat mockCategorizer
leafVal string rootVal string
expect assert.BoolAssertionFunc leafVal string
shortRef string
expect assert.BoolAssertionFunc
}{ }{
{ {
name: "matching values", name: "matching values",
@ -332,6 +335,20 @@ func (suite *SelectorScopesSuite) TestMatchesPathValues() {
leafVal: "smarf", leafVal: "smarf",
expect: assert.False, expect: assert.False,
}, },
{
name: "leaf matches shortRef",
rootVal: rootCatStub.String(),
leafVal: short,
shortRef: short,
expect: assert.True,
},
{
name: "root matches shortRef",
rootVal: short,
leafVal: leafCatStub.String(),
shortRef: short,
expect: assert.False,
},
} }
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
@ -339,7 +356,7 @@ func (suite *SelectorScopesSuite) TestMatchesPathValues() {
sc[rootCatStub.String()] = filterize(test.rootVal) sc[rootCatStub.String()] = filterize(test.rootVal)
sc[leafCatStub.String()] = filterize(test.leafVal) sc[leafCatStub.String()] = filterize(test.leafVal)
test.expect(t, matchesPathValues(sc, cat, pvs)) test.expect(t, matchesPathValues(sc, cat, pvs, test.shortRef))
}) })
} }
} }