diff --git a/src/internal/connector/exchange/service_functions.go b/src/internal/connector/exchange/service_functions.go index 6e7a39dce..136cb1e8d 100644 --- a/src/internal/connector/exchange/service_functions.go +++ b/src/internal/connector/exchange/service_functions.go @@ -225,7 +225,7 @@ func GetContainers( qp graph.QueryParams, gs graph.Service, ) ([]graph.CachedContainer, error) { - category := graph.ScopeToPathCategory(qp.Scope) + category := qp.Scope.Category().PathType() switch category { case path.ContactsCategory: diff --git a/src/internal/connector/exchange/service_iterators.go b/src/internal/connector/exchange/service_iterators.go index 727e5ff03..5ce694ba3 100644 --- a/src/internal/connector/exchange/service_iterators.go +++ b/src/internal/connector/exchange/service_iterators.go @@ -28,7 +28,7 @@ func FilterContainersAndFillCollections( resolver graph.ContainerResolver, ) error { var ( - category = graph.ScopeToPathCategory(qp.Scope) + category = qp.Scope.Category().PathType() collectionType = CategoryToOptionIdentifier(category) errs error ) diff --git a/src/internal/connector/graph/service_helper.go b/src/internal/connector/graph/service_helper.go index f11ee1265..c4e86858d 100644 --- a/src/internal/connector/graph/service_helper.go +++ b/src/internal/connector/graph/service_helper.go @@ -17,7 +17,6 @@ import ( "github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/path" - "github.com/alcionai/corso/src/pkg/selectors" ) const ( @@ -70,23 +69,6 @@ func (handler *LoggingMiddleware) Intercept( return pipeline.Next(req, middlewareIndex) } -// ScopeToPathCategory helper function that maps selectors.ExchangeScope to path.CategoryType -func ScopeToPathCategory(scope selectors.ExchangeScope) path.CategoryType { - if scope.IncludesCategory(selectors.ExchangeMail) { - return path.EmailCategory - } - - if scope.IncludesCategory(selectors.ExchangeContact) { - return path.ContactsCategory - } - - if scope.IncludesCategory(selectors.ExchangeEvent) { - return path.EventsCategory - } - - return path.UnknownCategory -} - func StringToPathCategory(input string) path.CategoryType { param := strings.ToLower(input) diff --git a/src/internal/connector/graph_connector.go b/src/internal/connector/graph_connector.go index 05731549e..634aa6f46 100644 --- a/src/internal/connector/graph_connector.go +++ b/src/internal/connector/graph_connector.go @@ -298,7 +298,7 @@ func (gc *GraphConnector) createCollections( Credentials: gc.credentials, } - itemCategory := graph.ScopeToPathCategory(qp.Scope) + itemCategory := qp.Scope.Category().PathType() foldersComplete, closer := observe.MessageWithCompletion(fmt.Sprintf("∙ %s - %s:", itemCategory.String(), user)) defer closer() @@ -307,7 +307,7 @@ func (gc *GraphConnector) createCollections( resolver, err := exchange.PopulateExchangeContainerResolver( ctx, qp, - graph.ScopeToPathCategory(qp.Scope), + qp.Scope.Category().PathType(), ) if err != nil { return nil, errors.Wrap(err, "getting folder cache") diff --git a/src/pkg/selectors/exchange.go b/src/pkg/selectors/exchange.go index 027cc277b..e6e339807 100644 --- a/src/pkg/selectors/exchange.go +++ b/src/pkg/selectors/exchange.go @@ -466,14 +466,24 @@ const ( // append new filter cats here ) -// exchangePathSet describes the category type keys used in Exchange paths. -// The order of each slice is important, and should match the order in which -// these types appear in the canonical Path for each type. -var exchangePathSet = map[categorizer][]categorizer{ - ExchangeContact: {ExchangeUser, ExchangeContactFolder, ExchangeContact}, - ExchangeEvent: {ExchangeUser, ExchangeEventCalendar, ExchangeEvent}, - ExchangeMail: {ExchangeUser, ExchangeMailFolder, ExchangeMail}, - ExchangeUser: {ExchangeUser}, // the root category must be represented +// exchangeLeafProperties describes common metadata of the leaf categories +var exchangeLeafProperties = map[categorizer]leafProperty{ + ExchangeContact: { + pathKeys: []categorizer{ExchangeUser, ExchangeContactFolder, ExchangeContact}, + pathType: path.ContactsCategory, + }, + ExchangeEvent: { + pathKeys: []categorizer{ExchangeUser, ExchangeEventCalendar, ExchangeEvent}, + pathType: path.EventsCategory, + }, + ExchangeMail: { + pathKeys: []categorizer{ExchangeUser, ExchangeMailFolder, ExchangeMail}, + pathType: path.EmailCategory, + }, + ExchangeUser: { // the root category must be represented, even though it isn't a leaf + pathKeys: []categorizer{ExchangeUser}, + pathType: path.UnknownCategory, + }, } func (ec exchangeCategory) String() string { @@ -551,7 +561,12 @@ func (ec exchangeCategory) pathValues(p path.Path) map[categorizer]string { // pathKeys returns the path keys recognized by the receiver's leaf type. func (ec exchangeCategory) pathKeys() []categorizer { - return exchangePathSet[ec.leafCat()] + return exchangeLeafProperties[ec.leafCat()].pathKeys +} + +// PathType converts the category's leaf type into the matching path.CategoryType. +func (ec exchangeCategory) PathType() path.CategoryType { + return exchangeLeafProperties[ec.leafCat()].pathType } // --------------------------------------------------------------------------- diff --git a/src/pkg/selectors/exchange_test.go b/src/pkg/selectors/exchange_test.go index ac483f089..cafeec17c 100644 --- a/src/pkg/selectors/exchange_test.go +++ b/src/pkg/selectors/exchange_test.go @@ -1394,3 +1394,34 @@ func (suite *ExchangeSelectorSuite) TestCategoryFromItemType() { }) } } + +func (suite *ExchangeSelectorSuite) TestCategory_PathType() { + table := []struct { + cat exchangeCategory + pathType path.CategoryType + }{ + {ExchangeCategoryUnknown, path.UnknownCategory}, + {ExchangeContact, path.ContactsCategory}, + {ExchangeContactFolder, path.ContactsCategory}, + {ExchangeEvent, path.EventsCategory}, + {ExchangeEventCalendar, path.EventsCategory}, + {ExchangeMail, path.EmailCategory}, + {ExchangeMailFolder, path.EmailCategory}, + {ExchangeUser, path.UnknownCategory}, + {ExchangeFilterMailSender, path.EmailCategory}, + {ExchangeFilterMailSubject, path.EmailCategory}, + {ExchangeFilterMailReceivedAfter, path.EmailCategory}, + {ExchangeFilterMailReceivedBefore, path.EmailCategory}, + {ExchangeFilterContactName, path.ContactsCategory}, + {ExchangeFilterEventOrganizer, path.EventsCategory}, + {ExchangeFilterEventRecurs, path.EventsCategory}, + {ExchangeFilterEventStartsAfter, path.EventsCategory}, + {ExchangeFilterEventStartsBefore, path.EventsCategory}, + {ExchangeFilterEventSubject, path.EventsCategory}, + } + for _, test := range table { + suite.T().Run(test.cat.String(), func(t *testing.T) { + assert.Equal(t, test.pathType, test.cat.PathType()) + }) + } +} diff --git a/src/pkg/selectors/helpers_test.go b/src/pkg/selectors/helpers_test.go index fcdfab382..93732749e 100644 --- a/src/pkg/selectors/helpers_test.go +++ b/src/pkg/selectors/helpers_test.go @@ -62,6 +62,15 @@ func (mc mockCategorizer) pathKeys() []categorizer { return []categorizer{rootCatStub, leafCatStub} } +func (mc mockCategorizer) PathType() path.CategoryType { + switch mc { + case leafCatStub: + return path.EventsCategory + default: + return path.UnknownCategory + } +} + func stubPathValues() map[categorizer]string { return map[categorizer]string{ rootCatStub: rootCatStub.String(), diff --git a/src/pkg/selectors/onedrive.go b/src/pkg/selectors/onedrive.go index 136857696..72865baec 100644 --- a/src/pkg/selectors/onedrive.go +++ b/src/pkg/selectors/onedrive.go @@ -288,12 +288,16 @@ const ( FileFilterModifiedBefore oneDriveCategory = "FileFilterModifiedBefore" ) -// oneDrivePathSet describes the category type keys used in OneDrive paths. -// The order of each slice is important, and should match the order in which -// these types appear in the canonical Path for each type. -var oneDrivePathSet = map[categorizer][]categorizer{ - OneDriveItem: {OneDriveUser, OneDriveFolder, OneDriveItem}, - OneDriveUser: {OneDriveUser}, // the root category must be represented +// oneDriveLeafProperties describes common metadata of the leaf categories +var oneDriveLeafProperties = map[categorizer]leafProperty{ + OneDriveItem: { + pathKeys: []categorizer{OneDriveUser, OneDriveFolder, OneDriveItem}, + pathType: path.FilesCategory, + }, + OneDriveUser: { // the root category must be represented, even though it isn't a leaf + pathKeys: []categorizer{OneDriveUser}, + pathType: path.UnknownCategory, + }, } func (c oneDriveCategory) String() string { @@ -350,7 +354,12 @@ func (c oneDriveCategory) pathValues(p path.Path) map[categorizer]string { // pathKeys returns the path keys recognized by the receiver's leaf type. func (c oneDriveCategory) pathKeys() []categorizer { - return oneDrivePathSet[c.leafCat()] + return oneDriveLeafProperties[c.leafCat()].pathKeys +} + +// PathType converts the category's leaf type into the matching path.CategoryType. +func (c oneDriveCategory) PathType() path.CategoryType { + return oneDriveLeafProperties[c.leafCat()].pathType } // --------------------------------------------------------------------------- diff --git a/src/pkg/selectors/onedrive_test.go b/src/pkg/selectors/onedrive_test.go index e8a18f79b..784eea363 100644 --- a/src/pkg/selectors/onedrive_test.go +++ b/src/pkg/selectors/onedrive_test.go @@ -336,3 +336,24 @@ func (suite *OneDriveSelectorSuite) TestOneDriveScope_MatchesInfo() { }) } } + +func (suite *OneDriveSelectorSuite) TestCategory_PathType() { + table := []struct { + cat oneDriveCategory + pathType path.CategoryType + }{ + {OneDriveCategoryUnknown, path.UnknownCategory}, + {OneDriveUser, path.UnknownCategory}, + {OneDriveItem, path.FilesCategory}, + {OneDriveFolder, path.FilesCategory}, + {FileFilterCreatedAfter, path.FilesCategory}, + {FileFilterCreatedBefore, path.FilesCategory}, + {FileFilterModifiedAfter, path.FilesCategory}, + {FileFilterModifiedBefore, path.FilesCategory}, + } + for _, test := range table { + suite.T().Run(test.cat.String(), func(t *testing.T) { + assert.Equal(t, test.pathType, test.cat.PathType()) + }) + } +} diff --git a/src/pkg/selectors/scopes.go b/src/pkg/selectors/scopes.go index 5aaed5184..779355516 100644 --- a/src/pkg/selectors/scopes.go +++ b/src/pkg/selectors/scopes.go @@ -11,9 +11,29 @@ import ( ) // --------------------------------------------------------------------------- -// interfaces +// types & interfaces // --------------------------------------------------------------------------- +// leafProperty describes metadata associated with a leaf categorizer +type leafProperty struct { + // pathKeys describes the categorizer keys used to map scope type to a value + // extracted from a path.Path. + // The order of the slice is important, and should match the order in which + // these types appear in the path.Path for each type. + // Ex: given: exchangeMail + // categoryPath => [ExchangeUser, ExchangeMailFolder, ExchangeMail] + // suggests that scopes involving exchange mail will need to match a user, + // mailFolder, and mail; appearing in the path in that order. + pathKeys []categorizer + + // pathType produces the path.CategoryType representing this leafType. + // This allows the scope to type to be compared using the more commonly recognized + // path category consts. + // Ex: given: exchangeMail + // pathType => path.EmailCategory + pathType path.CategoryType +} + type ( // categorizer recognizes service specific item categories. categorizer interface { @@ -54,6 +74,10 @@ type ( // ids in a path with the same keys that it uses to retrieve those values from a scope, // so that the two can be compared. pathKeys() []categorizer + + // PathType converts the category's leaf type into the matching path.CategoryType. + // Exported due to common use by consuming packages. + PathType() path.CategoryType } // categoryT is the generic type interface of a categorizer categoryT interface { @@ -84,7 +108,7 @@ type ( scope map[string]filters.Filter // scoper describes the minimum necessary interface that a soundly built scope should - // comply with. + // comply with to be usable by selector generics. scoper interface { // Every scope is expected to contain a reference to its category. This allows users // to evaluate structs with a call to myscope.Category(). Category() is expected to