diff --git a/src/cli/backup/exchange.go b/src/cli/backup/exchange.go index 0ff79e9ea..d5eae8f5e 100644 --- a/src/cli/backup/exchange.go +++ b/src/cli/backup/exchange.go @@ -19,6 +19,11 @@ var ( backupDetailsID string exchangeAll bool exchangeData []string + contact []string + contactFolder []string + email []string + emailFolder []string + event []string user []string ) @@ -37,8 +42,8 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command { switch parent.Use { case createCommand: c, fs = utils.AddCommand(parent, exchangeCreateCmd) - fs.StringArrayVar(&user, "user", nil, "Back up Exchange data by user ID; accepts "+utils.Wildcard+" to select all users") - fs.BoolVar(&exchangeAll, "all", false, "Back up all Exchange data for all users") + fs.StringArrayVar(&user, "user", nil, "Backup Exchange data by user ID; accepts "+utils.Wildcard+" to select all users") + fs.BoolVar(&exchangeAll, "all", false, "Backup all Exchange data for all users") fs.StringArrayVar( &exchangeData, "data", @@ -51,6 +56,25 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command { c, fs = utils.AddCommand(parent, exchangeDetailsCmd) fs.StringVar(&backupDetailsID, "backup-details", "", "ID of the backup details to be shown") cobra.CheckErr(c.MarkFlagRequired("backup-details")) + fs.StringArrayVar(&contact, "contact", nil, "Select backup details by contact ID; accepts "+utils.Wildcard+" to select all contacts") + fs.StringArrayVar( + &contactFolder, + "contact-folder", + nil, + "Select backup details by contact folder ID; accepts "+utils.Wildcard+" to select all contact folders") + fs.StringArrayVar(&email, "email", nil, "Select backup details by emails ID; accepts "+utils.Wildcard+" to select all emails") + fs.StringArrayVar( + &emailFolder, + "email-folder", + nil, + "Select backup details by email folder ID; accepts "+utils.Wildcard+" to select all email folderss") + fs.StringArrayVar(&event, "event", nil, "Select backup details by event ID; accepts "+utils.Wildcard+" to select all events") + fs.StringArrayVar(&user, "user", nil, "Select backup details by user ID; accepts "+utils.Wildcard+" to select all users") + + // TODO: reveal these flags when their production is supported in GC + cobra.CheckErr(fs.MarkHidden("contact")) + cobra.CheckErr(fs.MarkHidden("contact-folder")) + cobra.CheckErr(fs.MarkHidden("event")) } return c } @@ -237,3 +261,89 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error { return nil } + +func exchangeBackupDetailSelectors( + contacts, contactFolders, emails, emailFolders, events, users []string, +) selectors.Selector { + sel := selectors.NewExchangeBackup() + // normalize the inputs + lc, lcf := len(contacts), len(contactFolders) + le, lef := len(emails), len(emailFolders) + lev := len(events) + lu := len(users) + + // if only the backupID is provided, treat that as an --all query + if lc+lcf+le+lef+lev+lu == 0 { + sel.Include(sel.Users(selectors.All())) + return sel.Selector + } + + // if only users are provided, we only get one selector + if lc+lcf+le+lef+lev == 0 { + sel.Include(sel.Users(users)) + return sel.Selector + } + + // otherwise, add selectors for each type of data + includeExchangeContacts(sel, users, contactFolders, contacts) + includeExchangeEmails(sel, users, emailFolders, email) + includeExchangeEvents(sel, users, events) + + return sel.Selector +} + +func includeExchangeContacts(sel *selectors.ExchangeBackup, users, contactFolders, contacts []string) { + if len(contactFolders) == 0 { + return + } + if len(contacts) > 0 { + sel.Include(sel.Contacts(users, contactFolders, contacts)) + } else { + sel.Include(sel.ContactFolders(users, contactFolders)) + } +} + +func includeExchangeEmails(sel *selectors.ExchangeBackup, users, emailFolders, emails []string) { + if len(emailFolders) == 0 { + return + } + if len(emails) > 0 { + sel.Include(sel.Mails(users, emailFolders, emails)) + } else { + sel.Include(sel.MailFolders(users, emailFolders)) + } +} + +func includeExchangeEvents(sel *selectors.ExchangeBackup, users, events []string) { + if len(events) == 0 { + return + } + sel.Include(sel.Events(users, events)) +} + +func validateExchangeBackupDetailFlags( + contacts, contactFolders, emails, emailFolders, events, users []string, + backupID string, +) error { + if len(backupID) == 0 { + return errors.New("a backup ID is requried") + } + lu := len(users) + lc, lcf := len(contacts), len(contactFolders) + le, lef := len(emails), len(emailFolders) + lev := len(events) + // if only the backupID is populated, that's the same as --all + if lu+lc+lcf+le+lef+lev == 0 { + return nil + } + if lu == 0 { + return errors.New("requries one or more --user ids, the wildcard --user *, or the --all flag.") + } + if lc > 0 && lcf == 0 { + return errors.New("one or more --contact-folder ids or the wildcard --contact-folder * must be included to specify a --contact") + } + if le > 0 && lef == 0 { + return errors.New("one or more --email-folder ids or the wildcard --email-folder * must be included to specify a --email") + } + return nil +} diff --git a/src/cli/backup/exchange_test.go b/src/cli/backup/exchange_test.go index cbbc9b5e6..644effda0 100644 --- a/src/cli/backup/exchange_test.go +++ b/src/cli/backup/exchange_test.go @@ -212,3 +212,267 @@ func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() { }) } } + +func (suite *ExchangeSuite) TestValidateBackupDetailFlags() { + stub := []string{"id-stub"} + table := []struct { + name string + contacts, contactFolders, emails, emailFolders, events, users []string + backupID string + expect assert.ErrorAssertionFunc + }{ + { + name: "only backupid", + backupID: "bid", + expect: assert.NoError, + }, + { + name: "all values populated", + backupID: "bid", + contacts: stub, + contactFolders: stub, + emails: stub, + emailFolders: stub, + events: stub, + users: stub, + expect: assert.NoError, + }, + { + name: "nothing populated", + expect: assert.Error, + }, + { + name: "no backup id", + contacts: stub, + contactFolders: stub, + emails: stub, + emailFolders: stub, + events: stub, + users: stub, + expect: assert.Error, + }, + { + name: "no users", + backupID: "bid", + contacts: stub, + contactFolders: stub, + emails: stub, + emailFolders: stub, + events: stub, + expect: assert.Error, + }, + { + name: "no contact folders", + backupID: "bid", + contacts: stub, + emails: stub, + emailFolders: stub, + events: stub, + users: stub, + expect: assert.Error, + }, + { + name: "no email folders", + backupID: "bid", + contacts: stub, + contactFolders: stub, + emails: stub, + events: stub, + users: stub, + expect: assert.Error, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + test.expect(t, validateExchangeBackupDetailFlags( + test.contacts, + test.contactFolders, + test.emails, + test.emailFolders, + test.events, + test.users, + test.backupID, + )) + }) + } +} + +func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() { + stub := []string{"id-stub"} + all := []string{utils.Wildcard} + table := []struct { + name string + contacts, contactFolders, emails, emailFolders, events, users []string + expectIncludeLen int + }{ + { + name: "no selectors", + expectIncludeLen: 1, + }, + { + name: "all users", + users: all, + expectIncludeLen: 1, + }, + { + name: "single user", + users: stub, + expectIncludeLen: 1, + }, + { + name: "multiple users", + users: []string{"fnord", "smarf"}, + expectIncludeLen: 1, + }, + { + name: "all users, all data", + contacts: all, + contactFolders: all, + emails: all, + emailFolders: all, + events: all, + users: all, + expectIncludeLen: 3, + }, + { + name: "all users, all folders", + contactFolders: all, + emailFolders: all, + users: all, + expectIncludeLen: 2, + }, + { + name: "single user, single of each data", + contacts: stub, + contactFolders: stub, + emails: stub, + emailFolders: stub, + events: stub, + users: stub, + expectIncludeLen: 3, + }, + { + name: "single user, single of each folder", + contactFolders: stub, + emailFolders: stub, + users: stub, + expectIncludeLen: 2, + }, + { + name: "all users, contacts", + contacts: all, + contactFolders: stub, + users: all, + expectIncludeLen: 1, + }, + { + name: "single user, contacts", + contacts: stub, + contactFolders: stub, + users: stub, + expectIncludeLen: 1, + }, + { + name: "all users, emails", + emails: all, + emailFolders: stub, + users: all, + expectIncludeLen: 1, + }, + { + name: "single user, emails", + emails: stub, + emailFolders: stub, + users: stub, + expectIncludeLen: 1, + }, + { + name: "all users, events", + events: all, + users: all, + expectIncludeLen: 1, + }, + { + name: "single user, events", + events: stub, + users: stub, + expectIncludeLen: 1, + }, + { + name: "all users, contacts + email", + contacts: all, + contactFolders: all, + emails: all, + emailFolders: all, + users: all, + expectIncludeLen: 2, + }, + { + name: "single users, contacts + email", + contacts: stub, + contactFolders: stub, + emails: stub, + emailFolders: stub, + users: stub, + expectIncludeLen: 2, + }, + { + name: "all users, email + event", + emails: all, + emailFolders: all, + events: all, + users: all, + expectIncludeLen: 2, + }, + { + name: "single users, email + event", + emails: stub, + emailFolders: stub, + events: stub, + users: stub, + expectIncludeLen: 2, + }, + { + name: "all users, event + contact", + contacts: all, + contactFolders: all, + events: all, + users: all, + expectIncludeLen: 2, + }, + { + name: "single users, event + contact", + contacts: stub, + contactFolders: stub, + events: stub, + users: stub, + expectIncludeLen: 2, + }, + { + name: "many users, events", + events: []string{"foo", "bar"}, + users: []string{"fnord", "smarf"}, + expectIncludeLen: 2, + }, + { + name: "many users, events + contacts", + contacts: []string{"foo", "bar"}, + contactFolders: []string{"foo", "bar"}, + events: []string{"foo", "bar"}, + users: []string{"fnord", "smarf"}, + expectIncludeLen: 6, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + sel := exchangeBackupDetailSelectors( + test.contacts, + test.contactFolders, + test.emails, + test.emailFolders, + test.events, + test.users) + assert.Equal(t, test.expectIncludeLen, len(sel.Includes)) + }) + } +} diff --git a/src/cli/utils/utils.go b/src/cli/utils/utils.go index 185ef42f2..02b5f17bf 100644 --- a/src/cli/utils/utils.go +++ b/src/cli/utils/utils.go @@ -5,9 +5,10 @@ import ( "errors" "fmt" - "github.com/alcionai/corso/pkg/repository" "github.com/spf13/cobra" "github.com/spf13/pflag" + + "github.com/alcionai/corso/pkg/repository" ) const (