From ed52a07e2f01444662d2ef91c44ab2644e784ae6 Mon Sep 17 00:00:00 2001 From: Keepers Date: Thu, 15 Sep 2022 10:53:03 -0600 Subject: [PATCH] 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] :sunflower: Feature ## Issue(s) * closes #572 ## Test Plan - [ ] :muscle: Manual - [x] :zap: Unit test - [x] :green_heart: E2E --- src/pkg/selectors/exchange.go | 7 +++- src/pkg/selectors/exchange_test.go | 51 ++++++++++++++++-------------- src/pkg/selectors/helpers_test.go | 4 +++ src/pkg/selectors/onedrive.go | 7 +++- src/pkg/selectors/scopes.go | 13 ++++++-- src/pkg/selectors/scopes_test.go | 27 +++++++++++++--- 6 files changed, 77 insertions(+), 32 deletions(-) diff --git a/src/pkg/selectors/exchange.go b/src/pkg/selectors/exchange.go index 67bf2badf..75accde1e 100644 --- a/src/pkg/selectors/exchange.go +++ b/src/pkg/selectors/exchange.go @@ -551,6 +551,11 @@ func (ec exchangeCategory) unknownCat() categorizer { 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. // // Example: @@ -692,7 +697,7 @@ func (s ExchangeScope) matchesEntry( entry details.DetailsEntry, ) bool { // 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 diff --git a/src/pkg/selectors/exchange_test.go b/src/pkg/selectors/exchange_test.go index 43c4f5c9f..42bc852f2 100644 --- a/src/pkg/selectors/exchange_test.go +++ b/src/pkg/selectors/exchange_test.go @@ -779,30 +779,34 @@ func (suite *ExchangeSelectorSuite) TestExchangeScope_MatchesPath() { ) var ( - pth = stubPath(suite.T(), usr, []string{fld, mail}, path.EmailCategory) - es = NewExchangeRestore() + pth = stubPath(suite.T(), usr, []string{fld, mail}, path.EmailCategory) + short = "thisisahashofsomekind" + es = NewExchangeRestore() ) table := []struct { - name string - scope []ExchangeScope - expect assert.BoolAssertionFunc + name string + scope []ExchangeScope + shortRef string + expect assert.BoolAssertionFunc }{ - {"all user's items", es.Users(Any()), assert.True}, - {"no user's items", es.Users(None()), assert.False}, - {"matching user", es.Users([]string{usr}), assert.True}, - {"non-matching user", es.Users([]string{"smarf"}), assert.False}, - {"one of multiple users", es.Users([]string{"smarf", usr}), assert.True}, - {"all folders", es.MailFolders(Any(), Any()), assert.True}, - {"no folders", es.MailFolders(Any(), None()), assert.False}, - {"matching folder", es.MailFolders(Any(), []string{fld}), assert.True}, - {"non-matching folder", es.MailFolders(Any(), []string{"smarf"}), assert.False}, - {"one of multiple folders", es.MailFolders(Any(), []string{"smarf", fld}), assert.True}, - {"all mail", es.Mails(Any(), Any(), Any()), assert.True}, - {"no mail", es.Mails(Any(), Any(), None()), assert.False}, - {"matching mail", es.Mails(Any(), Any(), []string{mail}), assert.True}, - {"non-matching mail", es.Mails(Any(), Any(), []string{"smarf"}), assert.False}, - {"one of multiple mails", es.Mails(Any(), Any(), []string{"smarf", mail}), assert.True}, + {"all user's items", es.Users(Any()), "", assert.True}, + {"no user's items", es.Users(None()), "", assert.False}, + {"matching user", es.Users([]string{usr}), "", assert.True}, + {"non-matching user", es.Users([]string{"smarf"}), "", assert.False}, + {"one of multiple users", es.Users([]string{"smarf", usr}), "", assert.True}, + {"all folders", es.MailFolders(Any(), Any()), "", assert.True}, + {"no folders", es.MailFolders(Any(), None()), "", assert.False}, + {"matching folder", es.MailFolders(Any(), []string{fld}), "", assert.True}, + {"non-matching folder", es.MailFolders(Any(), []string{"smarf"}), "", assert.False}, + {"one of multiple folders", es.MailFolders(Any(), []string{"smarf", fld}), "", assert.True}, + {"all mail", es.Mails(Any(), Any(), Any()), "", assert.True}, + {"no mail", es.Mails(Any(), Any(), None()), "", assert.False}, + {"matching mail", es.Mails(Any(), Any(), []string{mail}), "", assert.True}, + {"non-matching mail", es.Mails(Any(), Any(), []string{"smarf"}), "", assert.False}, + {"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 { suite.T().Run(test.name, func(t *testing.T) { @@ -810,7 +814,7 @@ func (suite *ExchangeSelectorSuite) TestExchangeScope_MatchesPath() { var aMatch bool for _, scope := range scopes { pv := ExchangeMail.pathValues(pth) - if matchesPathValues(scope, ExchangeMail, pv) { + if matchesPathValues(scope, ExchangeMail, pv, short) { aMatch = true break } @@ -1072,7 +1076,8 @@ func (suite *ExchangeSelectorSuite) TestScopesByCategory() { } func (suite *ExchangeSelectorSuite) TestPasses() { - deets := details.DetailsEntry{} + short := "thisisahashofsomekind" + entry := details.DetailsEntry{ShortRef: short} const ( mid = "mailID" @@ -1118,7 +1123,7 @@ func (suite *ExchangeSelectorSuite) TestPasses() { result := passes( cat, cat.pathValues(pth), - deets, + entry, test.excludes, test.filters, test.includes) diff --git a/src/pkg/selectors/helpers_test.go b/src/pkg/selectors/helpers_test.go index ceb3923ee..1fa216c66 100644 --- a/src/pkg/selectors/helpers_test.go +++ b/src/pkg/selectors/helpers_test.go @@ -47,6 +47,10 @@ func (mc mockCategorizer) unknownCat() categorizer { return unknownCatStub } +func (mc mockCategorizer) isLeaf() bool { + return mc == leafCatStub +} + func (mc mockCategorizer) pathValues(pth path.Path) map[categorizer]string { return map[categorizer]string{rootCatStub: "stub"} } diff --git a/src/pkg/selectors/onedrive.go b/src/pkg/selectors/onedrive.go index 6f2942f71..84f417f6e 100644 --- a/src/pkg/selectors/onedrive.go +++ b/src/pkg/selectors/onedrive.go @@ -183,6 +183,11 @@ func (c oneDriveCategory) unknownCat() categorizer { 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. // // Example: @@ -271,7 +276,7 @@ func (s OneDriveScope) matchesEntry( entry details.DetailsEntry, ) bool { // 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 diff --git a/src/pkg/selectors/scopes.go b/src/pkg/selectors/scopes.go index a2c9f1060..ed16628f2 100644 --- a/src/pkg/selectors/scopes.go +++ b/src/pkg/selectors/scopes.go @@ -32,6 +32,10 @@ type ( // unknownType returns the unknown category value 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 // 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 // inclusions, filters, and exclusions in the selector. -// func reduce[T scopeT, C categoryT]( ctx context.Context, deets *details.Details, @@ -337,6 +340,7 @@ func matchesPathValues[T scopeT, C categoryT]( sc T, cat C, pathValues map[categorizer]string, + shortRef string, ) bool { // if scope specifies a filter category, // path checking is automatically skipped. @@ -362,7 +366,12 @@ func matchesPathValues[T scopeT, C categoryT]( // all parts of the scope must match cc := c.(C) 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 } } diff --git a/src/pkg/selectors/scopes_test.go b/src/pkg/selectors/scopes_test.go index f4aca08ec..944515751 100644 --- a/src/pkg/selectors/scopes_test.go +++ b/src/pkg/selectors/scopes_test.go @@ -301,12 +301,15 @@ func toMockScope(sc []scope) []mockScope { func (suite *SelectorScopesSuite) TestMatchesPathValues() { cat := rootCatStub pvs := stubPathValues() + short := "brunheelda" table := []struct { - name string - rootVal string - leafVal string - expect assert.BoolAssertionFunc + name string + cat mockCategorizer + rootVal string + leafVal string + shortRef string + expect assert.BoolAssertionFunc }{ { name: "matching values", @@ -332,6 +335,20 @@ func (suite *SelectorScopesSuite) TestMatchesPathValues() { leafVal: "smarf", 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 { suite.T().Run(test.name, func(t *testing.T) { @@ -339,7 +356,7 @@ func (suite *SelectorScopesSuite) TestMatchesPathValues() { sc[rootCatStub.String()] = filterize(test.rootVal) sc[leafCatStub.String()] = filterize(test.leafVal) - test.expect(t, matchesPathValues(sc, cat, pvs)) + test.expect(t, matchesPathValues(sc, cat, pvs, test.shortRef)) }) } }