diff --git a/src/cmd/purge/purge.go b/src/cmd/purge/purge.go index b451fb398..7a48a5d5b 100644 --- a/src/cmd/purge/purge.go +++ b/src/cmd/purge/purge.go @@ -2,7 +2,6 @@ package main import ( "context" - "fmt" "os" "time" @@ -14,15 +13,11 @@ import ( "github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/internal/common" "github.com/alcionai/corso/src/internal/connector" - "github.com/alcionai/corso/src/internal/connector/exchange" "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/onedrive" "github.com/alcionai/corso/src/pkg/account" "github.com/alcionai/corso/src/pkg/credentials" - "github.com/alcionai/corso/src/pkg/filters" "github.com/alcionai/corso/src/pkg/logger" - "github.com/alcionai/corso/src/pkg/path" - "github.com/alcionai/corso/src/pkg/selectors" ) var purgeCmd = &cobra.Command{ @@ -31,24 +26,6 @@ var purgeCmd = &cobra.Command{ RunE: handleAllFolderPurge, } -var mailCmd = &cobra.Command{ - Use: "mail", - Short: "Purges mail folders", - RunE: handleMailFolderPurge, -} - -var eventsCmd = &cobra.Command{ - Use: "events", - Short: "Purges calendar event folders", - RunE: handleCalendarFolderPurge, -} - -var contactsCmd = &cobra.Command{ - Use: "contacts", - Short: "Purges contacts folders", - RunE: handleContactsFolderPurge, -} - var oneDriveCmd = &cobra.Command{ Use: "onedrive", Short: "Purges OneDrive folders", @@ -82,9 +59,6 @@ func main() { fs.StringVar(&prefix, "prefix", "", "filters mail folders by displayName prefix") cobra.CheckErr(purgeCmd.MarkPersistentFlagRequired("prefix")) - purgeCmd.AddCommand(mailCmd) - purgeCmd.AddCommand(eventsCmd) - purgeCmd.AddCommand(contactsCmd) purgeCmd.AddCommand(oneDriveCmd) if err := purgeCmd.ExecuteContext(ctx); err != nil { @@ -107,9 +81,6 @@ func handleAllFolderPurge(cmd *cobra.Command, args []string) error { err = runPurgeForEachUser( ctx, gc, t, - purgeMailFolders, - purgeCalendarFolders, - purgeContactFolders, purgeOneDriveFolders, ) if err != nil { @@ -119,66 +90,6 @@ func handleAllFolderPurge(cmd *cobra.Command, args []string) error { return nil } -func handleMailFolderPurge(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - - if utils.HasNoFlagsAndShownHelp(cmd) { - return nil - } - - gc, t, err := getGCAndBoundaryTime(ctx) - if err != nil { - return err - } - - if err := runPurgeForEachUser(ctx, gc, t, purgeMailFolders); err != nil { - logger.Ctx(ctx).Error(err) - return Only(ctx, errors.Wrap(ErrPurging, "mail folders")) - } - - return nil -} - -func handleCalendarFolderPurge(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - - if utils.HasNoFlagsAndShownHelp(cmd) { - return nil - } - - gc, t, err := getGCAndBoundaryTime(ctx) - if err != nil { - return err - } - - if err := runPurgeForEachUser(ctx, gc, t, purgeCalendarFolders); err != nil { - logger.Ctx(ctx).Error(err) - return Only(ctx, errors.Wrap(ErrPurging, "event calendars")) - } - - return nil -} - -func handleContactsFolderPurge(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - - if utils.HasNoFlagsAndShownHelp(cmd) { - return nil - } - - gc, t, err := getGCAndBoundaryTime(ctx) - if err != nil { - return err - } - - if err := runPurgeForEachUser(ctx, gc, t, purgeContactFolders); err != nil { - logger.Ctx(ctx).Error(err) - return Only(ctx, errors.Wrap(ErrPurging, "contact folders")) - } - - return nil -} - func handleOneDriveFolderPurge(cmd *cobra.Command, args []string) error { ctx := cmd.Context() @@ -231,114 +142,6 @@ func runPurgeForEachUser( return errs } -// ----- mail - -func purgeMailFolders( - ctx context.Context, - gc *connector.GraphConnector, - boundary time.Time, - uid string, -) error { - getter := func(gs graph.Service, uid, prefix string) ([]purgable, error) { - params, err := exchangeQueryParamFactory(uid, path.EmailCategory) - if err != nil { - return nil, err - } - - allFolders, err := exchange.GetAllMailFolders(ctx, *params, gs) - if err != nil { - return nil, err - } - - mfs := containerFilter(prefix, allFolders) - purgables := make([]purgable, len(mfs)) - - for i, v := range mfs { - purgables[i] = v - } - - return purgables, nil - } - - deleter := func(gs graph.Service, uid string, f purgable) error { - return exchange.DeleteMailFolder(ctx, gs, uid, *f.GetId()) - } - - return purgeFolders(ctx, gc, boundary, "Mail Folders", uid, getter, deleter) -} - -// ----- calendars - -func purgeCalendarFolders( - ctx context.Context, - gc *connector.GraphConnector, - boundary time.Time, - uid string, -) error { - getter := func(gs graph.Service, uid, prefix string) ([]purgable, error) { - params, err := exchangeQueryParamFactory(uid, path.EventsCategory) - if err != nil { - return nil, err - } - - allCalendars, err := exchange.GetAllCalendars(ctx, *params, gs) - if err != nil { - return nil, err - } - - cfs := containerFilter(prefix, allCalendars) - purgables := make([]purgable, len(cfs)) - - for i, v := range cfs { - purgables[i] = v - } - - return purgables, nil - } - - deleter := func(gs graph.Service, uid string, f purgable) error { - return exchange.DeleteCalendar(ctx, gs, uid, *f.GetId()) - } - - return purgeFolders(ctx, gc, boundary, "Event Calendars", uid, getter, deleter) -} - -// ----- contacts - -func purgeContactFolders( - ctx context.Context, - gc *connector.GraphConnector, - boundary time.Time, - uid string, -) error { - getter := func(gs graph.Service, uid, prefix string) ([]purgable, error) { - params, err := exchangeQueryParamFactory(uid, path.ContactsCategory) - if err != nil { - return nil, err - } - - allContainers, err := exchange.GetAllContactFolders(ctx, *params, gs) - if err != nil { - return nil, err - } - - cfs := containerFilter(prefix, allContainers) - purgables := make([]purgable, len(cfs)) - - for i, v := range cfs { - purgables[i] = v - } - - return purgables, nil - } - - deleter := func(gs graph.Service, uid string, f purgable) error { - return exchange.DeleteContactFolder(ctx, gs, uid, *f.GetId()) - } - - return purgeFolders(ctx, gc, boundary, "Contact Folders", uid, getter, deleter) -} - // ----- OneDrive func purgeOneDriveFolders( @@ -502,45 +305,3 @@ func userOrUsers(u string, us map[string]string) map[string]string { return map[string]string{u: u} } - -// containerFilter filters container list based on prefix -// @returns cachedContainers that meet the requirements for purging. -func containerFilter(nameContains string, containers []graph.CachedContainer) []graph.CachedContainer { - f := filters.In(nameContains) - result := make([]graph.CachedContainer, 0) - - for _, folder := range containers { - if f.Compare(*folder.GetDisplayName()) { - result = append(result, folder) - } - } - - return result -} - -func exchangeQueryParamFactory(user string, category path.CategoryType) (*graph.QueryParams, error) { - var scope selectors.ExchangeScope - - switch category { - case path.ContactsCategory: - scope = selectors.NewExchangeBackup().ContactFolders([]string{user}, selectors.Any())[0] - case path.EmailCategory: - scope = selectors.NewExchangeBackup().MailFolders([]string{user}, selectors.Any())[0] - case path.EventsCategory: - scope = selectors.NewExchangeBackup().EventCalendars([]string{user}, selectors.Any())[0] - default: - return nil, fmt.Errorf("category %s not supported", category) - } - - params := &graph.QueryParams{ - User: user, - Scope: scope, - FailFast: false, - Credentials: account.M365Config{ - M365: credentials.GetM365(), - AzureTenantID: common.First(tenant, os.Getenv(account.AzureTenantID)), - }, - } - - return params, nil -} diff --git a/src/internal/connector/exchange/service_functions.go b/src/internal/connector/exchange/service_functions.go index 136cb1e8d..8aa7ae505 100644 --- a/src/internal/connector/exchange/service_functions.go +++ b/src/internal/connector/exchange/service_functions.go @@ -130,115 +130,6 @@ func DeleteContactFolder(ctx context.Context, gs graph.Service, user, folderID s return gs.Client().UsersById(user).ContactFoldersById(folderID).Delete(ctx, nil) } -// GetAllMailFolders retrieves all mail folders for the specified user. -// If nameContains is populated, only returns mail matching that property. -// Returns a slice of {ID, DisplayName} tuples. -func GetAllMailFolders( - ctx context.Context, - qp graph.QueryParams, - gs graph.Service, -) ([]graph.CachedContainer, error) { - containers := make([]graph.CachedContainer, 0) - - resolver, err := PopulateExchangeContainerResolver(ctx, qp, path.EmailCategory) - if err != nil { - return nil, errors.Wrap(err, "building directory resolver in GetAllMailFolders") - } - - for _, c := range resolver.Items() { - directory := c.Path().String() - if len(directory) == 0 { - continue - } - - if qp.Scope.Matches(selectors.ExchangeMailFolder, directory) { - containers = append(containers, c) - } - } - - return containers, nil -} - -// GetAllCalendars retrieves all event calendars for the specified user. -// If nameContains is populated, only returns calendars matching that property. -// Returns a slice of {ID, DisplayName} tuples. -func GetAllCalendars( - ctx context.Context, - qp graph.QueryParams, - gs graph.Service, -) ([]graph.CachedContainer, error) { - containers := make([]graph.CachedContainer, 0) - - resolver, err := PopulateExchangeContainerResolver(ctx, qp, path.EventsCategory) - if err != nil { - return nil, errors.Wrap(err, "building calendar resolver in GetAllCalendars") - } - - for _, c := range resolver.Items() { - directory := c.Path().String() - - if qp.Scope.Matches(selectors.ExchangeEventCalendar, directory) { - containers = append(containers, c) - } - } - - return containers, err -} - -// GetAllContactFolders retrieves all contacts folders with a unique display -// name for the specified user. If multiple folders have the same display name -// the result is undefined. TODO: Replace with Cache Usage -// https://github.com/alcionai/corso/issues/1122 -func GetAllContactFolders( - ctx context.Context, - qp graph.QueryParams, - gs graph.Service, -) ([]graph.CachedContainer, error) { - var query string - - containers := make([]graph.CachedContainer, 0) - - resolver, err := PopulateExchangeContainerResolver(ctx, qp, path.ContactsCategory) - if err != nil { - return nil, errors.Wrap(err, "building directory resolver in GetAllContactFolders") - } - - for _, c := range resolver.Items() { - directory := c.Path().String() - - if len(directory) == 0 { - query = DefaultContactFolder - } else { - query = directory - } - - if qp.Scope.Matches(selectors.ExchangeContactFolder, query) { - containers = append(containers, c) - } - } - - return containers, err -} - -func GetContainers( - ctx context.Context, - qp graph.QueryParams, - gs graph.Service, -) ([]graph.CachedContainer, error) { - category := qp.Scope.Category().PathType() - - switch category { - case path.ContactsCategory: - return GetAllContactFolders(ctx, qp, gs) - case path.EmailCategory: - return GetAllMailFolders(ctx, qp, gs) - case path.EventsCategory: - return GetAllCalendars(ctx, qp, gs) - default: - return nil, fmt.Errorf("path.Category %s not supported", category) - } -} - // PopulateExchangeContainerResolver gets a folder resolver if one is available for // this category of data. If one is not available, returns nil so that other // logic in the caller can complete as long as they check if the resolver is not diff --git a/src/internal/connector/exchange/service_functions_test.go b/src/internal/connector/exchange/service_functions_test.go deleted file mode 100644 index 649f36163..000000000 --- a/src/internal/connector/exchange/service_functions_test.go +++ /dev/null @@ -1,366 +0,0 @@ -package exchange - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - - "github.com/alcionai/corso/src/internal/connector/graph" - "github.com/alcionai/corso/src/internal/tester" - "github.com/alcionai/corso/src/pkg/account" - "github.com/alcionai/corso/src/pkg/selectors" -) - -type ServiceFunctionsIntegrationSuite struct { - suite.Suite - m365UserID string - creds account.M365Config -} - -const ( - invalidUser = "fnords_mc_snarfens" - nonExistantLookup = "∂ç∂ç∂√≈∂ƒß∂ç√ßç√≈ç√ß∂ƒçß√ß≈∂ƒßç√" -) - -func TestServiceFunctionsIntegrationSuite(t *testing.T) { - if err := tester.RunOnAny( - tester.CorsoCITests, - tester.CorsoGraphConnectorTests, - tester.CorsoGraphConnectorExchangeTests, - ); err != nil { - t.Skip(err) - } - - suite.Run(t, new(ServiceFunctionsIntegrationSuite)) -} - -func (suite *ServiceFunctionsIntegrationSuite) SetupSuite() { - t := suite.T() - suite.m365UserID = tester.M365UserID(t) - a := tester.NewM365Account(t) - m365, err := a.M365Config() - require.NoError(t, err) - - suite.creds = m365 -} - -func (suite *ServiceFunctionsIntegrationSuite) TestGetAllCalendars() { - ctx, flush := tester.NewContext() - defer flush() - - gs := loadService(suite.T()) - userID := tester.M365UserID(suite.T()) - - table := []struct { - name, user string - expectCount assert.ComparisonAssertionFunc - getScope func(t *testing.T) selectors.ExchangeScope - expectErr assert.ErrorAssertionFunc - }{ - { - name: "plain lookup", - user: userID, - expectCount: assert.Greater, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors.NewExchangeBackup().EventCalendars([]string{userID}, selectors.Any())[0] - }, - }, - { - name: "Get Default Calendar", - user: userID, - expectCount: assert.Greater, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - EventCalendars([]string{userID}, []string{DefaultCalendar}, selectors.PrefixMatch())[0] - }, - }, - { - name: "non-root calendar", - user: userID, - expectCount: assert.Greater, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - EventCalendars([]string{userID}, []string{"Birthdays"}, selectors.PrefixMatch())[0] - }, - }, - { - name: "nonsense user", - user: invalidUser, - expectCount: assert.Equal, - expectErr: assert.Error, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - EventCalendars([]string{invalidUser}, []string{DefaultContactFolder}, selectors.PrefixMatch())[0] - }, - }, - { - name: "nonsense matcher", - user: userID, - expectCount: assert.Equal, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - EventCalendars([]string{userID}, []string{nonExistantLookup})[0] - }, - }, - } - for _, test := range table { - suite.T().Run(test.name, func(t *testing.T) { - params := graph.QueryParams{ - User: test.user, - Scope: test.getScope(t), - FailFast: false, - Credentials: suite.creds, - } - cals, err := GetAllCalendars(ctx, params, gs) - test.expectErr(t, err) - test.expectCount(t, len(cals), 0) - }) - } -} - -func (suite *ServiceFunctionsIntegrationSuite) TestGetAllContactFolders() { - ctx, flush := tester.NewContext() - defer flush() - - gs := loadService(suite.T()) - user := tester.M365UserID(suite.T()) - - table := []struct { - name, user string - expectCount assert.ComparisonAssertionFunc - getScope func(t *testing.T) selectors.ExchangeScope - expectErr assert.ErrorAssertionFunc - }{ - { - name: "plain lookup", - user: user, - expectCount: assert.Greater, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - ContactFolders([]string{user}, selectors.Any())[0] - }, - }, - { - name: "default contact folder", - user: user, - expectCount: assert.Greater, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - ContactFolders([]string{user}, []string{DefaultContactFolder}, selectors.PrefixMatch())[0] - }, - }, - { - name: "Trial folder lookup", - user: user, - expectCount: assert.Greater, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - ContactFolders([]string{user}, []string{"TrialFolder"}, selectors.PrefixMatch())[0] - }, - }, - { - name: "nonsense user", - user: invalidUser, - expectCount: assert.Equal, - expectErr: assert.Error, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - ContactFolders([]string{invalidUser}, []string{DefaultContactFolder}, selectors.PrefixMatch())[0] - }, - }, - { - name: "nonsense matcher", - user: user, - expectCount: assert.Equal, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - ContactFolders([]string{user}, []string{nonExistantLookup})[0] - }, - }, - } - for _, test := range table { - suite.T().Run(test.name, func(t *testing.T) { - params := graph.QueryParams{ - User: test.user, - Scope: test.getScope(t), - FailFast: false, - Credentials: suite.creds, - } - cals, err := GetAllContactFolders(ctx, params, gs) - test.expectErr(t, err) - test.expectCount(t, len(cals), 0) - }) - } -} - -func (suite *ServiceFunctionsIntegrationSuite) TestGetAllMailFolders() { - ctx, flush := tester.NewContext() - defer flush() - - gs := loadService(suite.T()) - userID := tester.M365UserID(suite.T()) - - table := []struct { - name, user string - expectCount assert.ComparisonAssertionFunc - getScope func(t *testing.T) selectors.ExchangeScope - expectErr assert.ErrorAssertionFunc - }{ - { - name: "plain lookup", - user: userID, - expectCount: assert.Greater, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - MailFolders([]string{userID}, selectors.Any())[0] - }, - }, - { - name: "root folder", - user: userID, - expectCount: assert.Greater, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - MailFolders([]string{userID}, []string{DefaultMailFolder}, selectors.PrefixMatch())[0] - }, - }, - { - name: "Trial folder lookup", - user: userID, - expectCount: assert.Greater, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - MailFolders([]string{userID}, []string{"Drafts"}, selectors.PrefixMatch())[0] - }, - }, - { - name: "nonsense user", - user: invalidUser, - expectCount: assert.Equal, - expectErr: assert.Error, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - MailFolders([]string{invalidUser}, []string{DefaultMailFolder}, selectors.PrefixMatch())[0] - }, - }, - { - name: "nonsense matcher", - user: userID, - expectCount: assert.Equal, - expectErr: assert.NoError, - getScope: func(t *testing.T) selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - MailFolders([]string{userID}, []string{nonExistantLookup}, selectors.PrefixMatch())[0] - }, - }, - } - for _, test := range table { - suite.T().Run(test.name, func(t *testing.T) { - params := graph.QueryParams{ - User: test.user, - Scope: test.getScope(t), - FailFast: false, - Credentials: suite.creds, - } - cals, err := GetAllMailFolders(ctx, params, gs) - test.expectErr(t, err) - test.expectCount(t, len(cals), 0) - }) - } -} - -func (suite *ServiceFunctionsIntegrationSuite) TestCollectContainers() { - ctx, flush := tester.NewContext() - defer flush() - - failFast := false - containerCount := 1 - t := suite.T() - user := tester.M365UserID(t) - a := tester.NewM365Account(t) - service := loadService(t) - credentials, err := a.M365Config() - require.NoError(t, err) - - tests := []struct { - name, contains string - getScope func() selectors.ExchangeScope - expectedCount assert.ComparisonAssertionFunc - }{ - { - name: "All Events", - contains: "Birthdays", - expectedCount: assert.Greater, - getScope: func() selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - EventCalendars([]string{user}, selectors.Any())[0] - }, - }, { - name: "Default Calendar", - contains: DefaultCalendar, - expectedCount: assert.Equal, - getScope: func() selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - EventCalendars([]string{user}, []string{DefaultCalendar}, selectors.PrefixMatch())[0] - }, - }, { - name: "Default Mail", - contains: DefaultMailFolder, - expectedCount: assert.Equal, - getScope: func() selectors.ExchangeScope { - return selectors. - NewExchangeBackup(). - MailFolders([]string{user}, []string{DefaultMailFolder}, selectors.PrefixMatch())[0] - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - qp := graph.QueryParams{ - User: user, - Scope: test.getScope(), - FailFast: failFast, - Credentials: credentials, - } - collections, err := GetContainers(ctx, qp, service) - assert.NoError(t, err) - test.expectedCount(t, len(collections), containerCount) - - keys := make([]string, 0, len(collections)) - for _, k := range collections { - keys = append(keys, *k.GetDisplayName()) - } - assert.Contains(t, keys, test.contains) - }) - } -}