diff --git a/src/cli/backup/exchange.go b/src/cli/backup/exchange.go index 93768dcb9..eeb3830cf 100644 --- a/src/cli/backup/exchange.go +++ b/src/cli/backup/exchange.go @@ -66,15 +66,14 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command { c, fs = utils.AddCommand(parent, exchangeCreateCmd()) fs.StringSliceVar( &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") + "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.StringSliceVar( &exchangeData, - "data", - nil, + "data", nil, "Select one or more types of data to backup: "+dataEmail+", "+dataContacts+", or "+dataEvents) options.AddOperationFlags(c) @@ -83,113 +82,82 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command { case detailsCommand: c, fs = utils.AddCommand(parent, exchangeDetailsCmd()) - fs.StringVar(&backupID, "backup", "", "ID of the backup containing the details to be shown") + fs.StringVar(&backupID, + "backup", "", + "ID of the backup containing the details to be shown") cobra.CheckErr(c.MarkFlagRequired("backup")) // per-data-type flags fs.StringSliceVar( &contact, - "contact", - nil, - "Select backup details by contact ID; accepts "+utils.Wildcard+" to select all contacts", - ) + "contact", nil, + "Select backup details by contact ID; accepts "+utils.Wildcard+" to select all contacts") fs.StringSliceVar( &contactFolder, - "contact-folder", - nil, - "Select backup details by contact folder ID; accepts "+utils.Wildcard+" to select all contact folders", - ) + "contact-folder", nil, + "Select backup details by contact folder ID; accepts "+utils.Wildcard+" to select all contact folders") fs.StringSliceVar( &email, - "email", - nil, - "Select backup details by emails ID; accepts "+utils.Wildcard+" to select all emails", - ) + "email", nil, + "Select backup details by emails ID; accepts "+utils.Wildcard+" to select all emails") fs.StringSliceVar( &emailFolder, - "email-folder", - nil, + "email-folder", nil, "Select backup details by email folder ID; accepts "+utils.Wildcard+" to select all email folders") fs.StringSliceVar( &event, - "event", - nil, - "Select backup details by event ID; accepts "+utils.Wildcard+" to select all events", - ) + "event", nil, + "Select backup details by event ID; accepts "+utils.Wildcard+" to select all events") fs.StringSliceVar( &eventCalendar, - "event-calendar", - nil, - "Select backup details by event calendar ID; accepts "+utils.Wildcard+" to select all events", - ) + "event-calendar", nil, + "Select backup details by event calendar ID; accepts "+utils.Wildcard+" to select all events") fs.StringSliceVar( &user, - "user", - nil, - "Select backup details by user ID; accepts "+utils.Wildcard+" to select all users", - ) + "user", nil, + "Select backup details by user ID; accepts "+utils.Wildcard+" to select all users") // exchange-info flags fs.StringVar( &contactName, - "contact-name", - "", - "Select backup details where the contact name contains this value", - ) + "contact-name", "", + "Select backup details where the contact name contains this value") fs.StringVar( &emailReceivedAfter, - "email-received-after", - "", - "Restore mail where the email was received after this datetime", - ) + "email-received-after", "", + "Restore mail where the email was received after this datetime") fs.StringVar( &emailReceivedBefore, - "email-received-before", - "", - "Restore mail where the email was received before this datetime", - ) + "email-received-before", "", + "Restore mail where the email was received before this datetime") fs.StringVar( &emailSender, - "email-sender", - "", - "Restore mail where the email sender matches this user id", - ) + "email-sender", "", + "Restore mail where the email sender matches this user id") fs.StringVar( &emailSubject, - "email-subject", - "", - "Restore mail where the email subject lines contain this value", - ) + "email-subject", "", + "Restore mail where the email subject lines contain this value") fs.StringVar( &eventOrganizer, - "event-organizer", - "", - "Select backup details where the event organizer user id contains this value", - ) + "event-organizer", "", + "Select backup details where the event organizer user id contains this value") fs.StringVar( &eventRecurs, - "event-recurs", - "", - "Select backup details if the event recurs. Use --event-recurs false to select where the event does not recur.", - ) + "event-recurs", "", + "Select backup details if the event recurs. Use --event-recurs false to select where the event does not recur.") fs.StringVar( &eventStartsAfter, - "event-starts-after", - "", - "Select backup details where the event starts after this datetime", - ) + "event-starts-after", "", + "Select backup details where the event starts after this datetime") fs.StringVar( &eventStartsBefore, - "event-starts-before", - "", - "Select backup details where the event starts before this datetime", - ) + "event-starts-before", "", + "Select backup details where the event starts before this datetime") fs.StringVar( &eventSubject, - "event-subject", - "", - "Select backup details where the event subject contains this value", - ) + "event-subject", "", + "Select backup details where the event subject contains this value") case deleteCommand: c, fs = utils.AddCommand(parent, exchangeDeleteCmd()) @@ -368,7 +336,7 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error { return nil } - if err := validateExchangeBackupDetailFlags( + if err := utils.ValidateExchangeRestoreFlags( contact, contactFolder, email, @@ -399,7 +367,7 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error { } sel := selectors.NewExchangeRestore() - includeExchangeBackupDetailDataSelectors( + utils.IncludeExchangeRestoreDataSelectors( sel, contact, contactFolder, @@ -408,7 +376,7 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error { event, eventCalendar, user) - filterExchangeBackupDetailInfoSelectors( + utils.FilterExchangeRestoreInfoSelectors( sel, contactName, emailReceivedAfter, @@ -436,207 +404,6 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error { return nil } -// builds the data-selector inclusions for `backup details exchange` -func includeExchangeBackupDetailDataSelectors( - sel *selectors.ExchangeRestore, - contacts, contactFolders, emails, emailFolders, events, eventCalendars, users []string, -) { - lc, lcf := len(contacts), len(contactFolders) - le, lef := len(emails), len(emailFolders) - lev, lec := len(events), len(eventCalendars) - - // either scope the request to a set of users - if lc+lcf+le+lef+lev+lec == 0 { - if len(users) == 0 { - users = selectors.Any() - } - - sel.Include(sel.Users(users)) - - return - } - - // or add selectors for each type of data - includeExchangeContacts(sel, users, contactFolders, contacts) - includeExchangeEmails(sel, users, emailFolders, email) - includeExchangeEvents(sel, users, eventCalendars, events) -} - -func includeExchangeContacts(sel *selectors.ExchangeRestore, users, contactFolders, contacts []string) { - if len(contactFolders) == 0 { - return - } - - if len(contacts) == 0 { - contacts = selectors.Any() - } - - sel.Include(sel.Contacts(users, contactFolders, contacts)) -} - -func includeExchangeEmails(sel *selectors.ExchangeRestore, users, emailFolders, emails []string) { - if len(emailFolders) == 0 { - return - } - - if len(emails) == 0 { - emails = selectors.Any() - } - - sel.Include(sel.Mails(users, emailFolders, emails)) -} - -func includeExchangeEvents(sel *selectors.ExchangeRestore, users, eventCalendars, events []string) { - if len(eventCalendars) == 0 { - return - } - - if len(events) == 0 { - events = selectors.Any() - } - - sel.Include(sel.Events(users, eventCalendars, events)) -} - -// builds the info-selector filters for `backup details exchange` -func filterExchangeBackupDetailInfoSelectors( - sel *selectors.ExchangeRestore, - contactName, - emailReceivedAfter, emailReceivedBefore, emailSender, emailSubject, - eventOrganizer, eventRecurs, eventStartsAfter, eventStartsBefore, eventSubject string, -) { - filterExchangeInfoContactName(sel, contactName) - filterExchangeInfoMailReceivedAfter(sel, emailReceivedAfter) - filterExchangeInfoMailReceivedBefore(sel, emailReceivedBefore) - filterExchangeInfoMailSender(sel, emailSender) - filterExchangeInfoMailSubject(sel, emailSubject) - filterExchangeInfoEventOrganizer(sel, eventOrganizer) - filterExchangeInfoEventRecurs(sel, eventRecurs) - filterExchangeInfoEventStartsAfter(sel, eventStartsAfter) - filterExchangeInfoEventStartsBefore(sel, eventStartsBefore) - filterExchangeInfoEventSubject(sel, eventSubject) -} - -func filterExchangeInfoContactName(sel *selectors.ExchangeRestore, name string) { - if len(name) == 0 { - return - } - - sel.Filter(sel.ContactName(name)) -} - -func filterExchangeInfoMailReceivedAfter(sel *selectors.ExchangeRestore, receivedAfter string) { - if len(receivedAfter) == 0 { - return - } - - sel.Filter(sel.MailReceivedAfter(receivedAfter)) -} - -func filterExchangeInfoMailReceivedBefore(sel *selectors.ExchangeRestore, receivedBefore string) { - if len(receivedBefore) == 0 { - return - } - - sel.Filter(sel.MailReceivedBefore(receivedBefore)) -} - -func filterExchangeInfoMailSender(sel *selectors.ExchangeRestore, sender string) { - if len(sender) == 0 { - return - } - - sel.Filter(sel.MailSender(sender)) -} - -func filterExchangeInfoMailSubject(sel *selectors.ExchangeRestore, subject string) { - if len(subject) == 0 { - return - } - - sel.Filter(sel.MailSubject(subject)) -} - -func filterExchangeInfoEventOrganizer(sel *selectors.ExchangeRestore, organizer string) { - if len(organizer) == 0 { - return - } - - sel.Filter(sel.EventOrganizer(organizer)) -} - -func filterExchangeInfoEventRecurs(sel *selectors.ExchangeRestore, recurs string) { - if len(recurs) == 0 { - return - } - - sel.Filter(sel.EventRecurs(recurs)) -} - -func filterExchangeInfoEventStartsAfter(sel *selectors.ExchangeRestore, startsAfter string) { - if len(startsAfter) == 0 { - return - } - - sel.Filter(sel.EventStartsAfter(startsAfter)) -} - -func filterExchangeInfoEventStartsBefore(sel *selectors.ExchangeRestore, startsBefore string) { - if len(startsBefore) == 0 { - return - } - - sel.Filter(sel.EventStartsBefore(startsBefore)) -} - -func filterExchangeInfoEventSubject(sel *selectors.ExchangeRestore, subject string) { - if len(subject) == 0 { - return - } - - sel.Filter(sel.EventSubject(subject)) -} - -// checks all flags for correctness and interdependencies -func validateExchangeBackupDetailFlags( - contacts, contactFolders, emails, emailFolders, events, eventCalendars, users []string, - backupID string, -) error { - if len(backupID) == 0 { - return errors.New("a backup ID is required") - } - - lu := len(users) - lc, lcf := len(contacts), len(contactFolders) - le, lef := len(emails), len(emailFolders) - lev, lec := len(events), len(eventCalendars) - - if lu+lc+lcf+le+lef+lev+lec == 0 { - return nil - } - - if lu == 0 { - return errors.New("requires 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 an --email") - } - - if lev > 0 && lec == 0 { - return errors.New( - "one or more --event-calendar ids or the wildcard --event-calendar * must be included to specify an --event") - } - - return nil -} - // ------------------------------------------------------------------------------------------------ // backup delete // ------------------------------------------------------------------------------------------------ diff --git a/src/cli/backup/exchange_test.go b/src/cli/backup/exchange_test.go index 4ce8403b1..587dbeb7a 100644 --- a/src/cli/backup/exchange_test.go +++ b/src/cli/backup/exchange_test.go @@ -303,7 +303,7 @@ func (suite *ExchangeSuite) TestValidateBackupDetailFlags() { } for _, test := range table { suite.T().Run(test.name, func(t *testing.T) { - test.expect(t, validateExchangeBackupDetailFlags( + test.expect(t, utils.ValidateExchangeRestoreFlags( test.contacts, test.contactFolders, test.emails, @@ -499,7 +499,7 @@ func (suite *ExchangeSuite) TestIncludeExchangeBackupDetailDataSelectors() { for _, test := range table { suite.T().Run(test.name, func(t *testing.T) { sel := selectors.NewExchangeRestore() - includeExchangeBackupDetailDataSelectors( + utils.IncludeExchangeRestoreDataSelectors( sel, test.contacts, test.contactFolders, @@ -600,7 +600,7 @@ func (suite *ExchangeSuite) TestFilterExchangeBackupDetailInfoSelectors() { for _, test := range table { suite.T().Run(test.name, func(t *testing.T) { sel := selectors.NewExchangeRestore() - filterExchangeBackupDetailInfoSelectors( + utils.FilterExchangeRestoreInfoSelectors( sel, test.contactName, test.after, diff --git a/src/cli/restore/exchange.go b/src/cli/restore/exchange.go index fe9a0b241..b73a60dd4 100644 --- a/src/cli/restore/exchange.go +++ b/src/cli/restore/exchange.go @@ -54,91 +54,71 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command { // per-data-type flags fs.StringSliceVar( &contact, - "contact", - nil, - "Restore contacts by ID; accepts "+utils.Wildcard+" to select all contacts", - ) + "contact", nil, + "Restore contacts by ID; accepts "+utils.Wildcard+" to select all contacts") fs.StringSliceVar( &contactFolder, - "contact-folder", - nil, + "contact-folder", nil, "Restore all contacts within the folder ID; accepts "+utils.Wildcard+" to select all contact folders") - fs.StringSliceVar(&email, "email", nil, "Restore emails by ID; accepts "+utils.Wildcard+" to select all emails") + fs.StringSliceVar(&email, + "email", nil, + "Restore emails by ID; accepts "+utils.Wildcard+" to select all emails") fs.StringSliceVar( &emailFolder, - "email-folder", - nil, - "Restore all emails by folder ID; accepts "+utils.Wildcard+" to select all email folders", - ) - fs.StringSliceVar(&event, "event", nil, "Restore events by ID; accepts "+utils.Wildcard+" to select all events") + "email-folder", nil, + "Restore all emails by folder ID; accepts "+utils.Wildcard+" to select all email folders") + fs.StringSliceVar(&event, + "event", nil, + "Restore events by ID; accepts "+utils.Wildcard+" to select all events") fs.StringSliceVar( &eventCalendar, - "event-calendar", - nil, + "event-calendar", nil, "Restore events by calendar ID; accepts "+utils.Wildcard+" to select all event calendars") - fs.StringSliceVar(&user, "user", nil, "Restore all data by user ID; accepts "+utils.Wildcard+" to select all users") + fs.StringSliceVar(&user, + "user", nil, + "Restore all data by user ID; accepts "+utils.Wildcard+" to select all users") // exchange-info flags fs.StringVar( &contactName, - "contact-name", - "", - "Restore contacts where the contact name contains this value", - ) + "contact-name", "", + "Restore contacts where the contact name contains this value") fs.StringVar( &emailReceivedAfter, - "email-received-after", - "", - "Restore mail where the email was received after this datetime", - ) + "email-received-after", "", + "Restore mail where the email was received after this datetime") fs.StringVar( &emailReceivedBefore, - "email-received-before", - "", - "Restore mail where the email was received before this datetime", - ) + "email-received-before", "", + "Restore mail where the email was received before this datetime") fs.StringVar( &emailSender, - "email-sender", - "", - "Restore mail where the email sender matches this user id", - ) + "email-sender", "", + "Restore mail where the email sender matches this user id") fs.StringVar( &emailSubject, - "email-subject", - "", - "Restore mail where the email subject lines contain this value", - ) + "email-subject", "", + "Restore mail where the email subject lines contain this value") fs.StringVar( &eventOrganizer, - "event-organizer", - "", - "Restore events where the event organizer user id contains this value", - ) + "event-organizer", "", + "Restore events where the event organizer user id contains this value") fs.StringVar( &eventRecurs, - "event-recurs", - "", - "Restore events if the event recurs. Use --event-recurs false to select where the event does not recur.", - ) + "event-recurs", "", + "Restore events if the event recurs. Use --event-recurs false to select where the event does not recur.") fs.StringVar( &eventStartsAfter, - "event-starts-after", - "", - "Restore events where the event starts after this datetime", - ) + "event-starts-after", "", + "Restore events where the event starts after this datetime") fs.StringVar( &eventStartsBefore, - "event-starts-before", - "", - "Restore events where the event starts before this datetime", - ) + "event-starts-before", "", + "Restore events where the event starts before this datetime") fs.StringVar( &eventSubject, - "event-subject", - "", - "Restore events where the event subject contains this value", - ) + "event-subject", "", + "Restore events where the event subject contains this value") // others options.AddOperationFlags(c) @@ -167,7 +147,7 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error { return nil } - if err := validateExchangeRestoreFlags( + if err := utils.ValidateExchangeRestoreFlags( contact, contactFolder, email, @@ -193,7 +173,7 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error { defer utils.CloseRepo(ctx, r) sel := selectors.NewExchangeRestore() - includeExchangeRestoreDataSelectors( + utils.IncludeExchangeRestoreDataSelectors( sel, contact, contactFolder, @@ -202,7 +182,7 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error { event, eventCalendar, user) - filterExchangeRestoreInfoSelectors( + utils.FilterExchangeRestoreInfoSelectors( sel, contactName, emailReceivedAfter, @@ -233,205 +213,3 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error { return nil } - -// builds the data-selector inclusions for `restore exchange` -func includeExchangeRestoreDataSelectors( - sel *selectors.ExchangeRestore, - contacts, contactFolders, emails, emailFolders, events, eventCalendars, users []string, -) { - lc, lcf := len(contacts), len(contactFolders) - le, lef := len(emails), len(emailFolders) - lev, lec := len(events), len(eventCalendars) - - // either scope the request to a set of users - if lc+lcf+le+lef+lev+lec == 0 { - if len(users) == 0 { - users = selectors.Any() - } - - sel.Include(sel.Users(users)) - - return - } - - // or add selectors for each type of data - includeExchangeContacts(sel, users, contactFolders, contacts) - includeExchangeEmails(sel, users, emailFolders, email) - includeExchangeEvents(sel, users, eventCalendars, events) -} - -func includeExchangeContacts(sel *selectors.ExchangeRestore, users, contactFolders, contacts []string) { - if len(contactFolders) == 0 { - return - } - - if len(contacts) == 0 { - contacts = selectors.Any() - } - - sel.Include(sel.Contacts(users, contactFolders, contacts)) -} - -func includeExchangeEmails(sel *selectors.ExchangeRestore, users, emailFolders, emails []string) { - if len(emailFolders) == 0 { - return - } - - if len(emails) == 0 { - emails = selectors.Any() - } - - sel.Include(sel.Mails(users, emailFolders, emails)) -} - -func includeExchangeEvents(sel *selectors.ExchangeRestore, users, eventCalendars, events []string) { - if len(eventCalendars) == 0 { - return - } - - if len(events) == 0 { - events = selectors.Any() - } - - sel.Include(sel.Events(users, eventCalendars, events)) -} - -// builds the info-selector filters for `restore exchange` -func filterExchangeRestoreInfoSelectors( - sel *selectors.ExchangeRestore, - contactName, - emailReceivedAfter, emailReceivedBefore, emailSender, emailSubject, - eventOrganizer, eventRecurs, eventStartsAfter, eventStartsBefore, eventSubject string, -) { - filterExchangeInfoContactName(sel, contactName) - filterExchangeInfoMailReceivedAfter(sel, emailReceivedAfter) - filterExchangeInfoMailReceivedBefore(sel, emailReceivedBefore) - filterExchangeInfoMailSender(sel, emailSender) - filterExchangeInfoMailSubject(sel, emailSubject) - filterExchangeInfoEventOrganizer(sel, eventOrganizer) - filterExchangeInfoEventRecurs(sel, eventRecurs) - filterExchangeInfoEventStartsAfter(sel, eventStartsAfter) - filterExchangeInfoEventStartsBefore(sel, eventStartsBefore) - filterExchangeInfoEventSubject(sel, eventSubject) -} - -func filterExchangeInfoContactName(sel *selectors.ExchangeRestore, name string) { - if len(name) == 0 { - return - } - - sel.Filter(sel.ContactName(name)) -} - -func filterExchangeInfoMailReceivedAfter(sel *selectors.ExchangeRestore, receivedAfter string) { - if len(receivedAfter) == 0 { - return - } - - sel.Filter(sel.MailReceivedAfter(receivedAfter)) -} - -func filterExchangeInfoMailReceivedBefore(sel *selectors.ExchangeRestore, receivedBefore string) { - if len(receivedBefore) == 0 { - return - } - - sel.Filter(sel.MailReceivedBefore(receivedBefore)) -} - -func filterExchangeInfoMailSender(sel *selectors.ExchangeRestore, sender string) { - if len(sender) == 0 { - return - } - - sel.Filter(sel.MailSender(sender)) -} - -func filterExchangeInfoMailSubject(sel *selectors.ExchangeRestore, subject string) { - if len(subject) == 0 { - return - } - - sel.Filter(sel.MailSubject(subject)) -} - -func filterExchangeInfoEventOrganizer(sel *selectors.ExchangeRestore, organizer string) { - if len(organizer) == 0 { - return - } - - sel.Filter(sel.EventOrganizer(organizer)) -} - -func filterExchangeInfoEventRecurs(sel *selectors.ExchangeRestore, recurs string) { - if len(recurs) == 0 { - return - } - - sel.Filter(sel.EventRecurs(recurs)) -} - -func filterExchangeInfoEventStartsAfter(sel *selectors.ExchangeRestore, startsAfter string) { - if len(startsAfter) == 0 { - return - } - - sel.Filter(sel.EventStartsAfter(startsAfter)) -} - -func filterExchangeInfoEventStartsBefore(sel *selectors.ExchangeRestore, startsBefore string) { - if len(startsBefore) == 0 { - return - } - - sel.Filter(sel.EventStartsBefore(startsBefore)) -} - -func filterExchangeInfoEventSubject(sel *selectors.ExchangeRestore, subject string) { - if len(subject) == 0 { - return - } - - sel.Filter(sel.EventSubject(subject)) -} - -// checks all flags for correctness and interdependencies -func validateExchangeRestoreFlags( - contacts, contactFolders, emails, emailFolders, events, eventCalendars, users []string, - backupID string, -) error { - if len(backupID) == 0 { - return errors.New("a backup ID is required") - } - - lu := len(users) - lc, lcf := len(contacts), len(contactFolders) - le, lef := len(emails), len(emailFolders) - lev, lec := len(events), len(eventCalendars) - - // if only the backupID is populated, that's the same as --all - if lu+lc+lcf+le+lef+lev+lec == 0 { - return nil - } - - if lu == 0 { - return errors.New("requires 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 an --email") - } - - if lev > 0 && lec == 0 { - return errors.New( - "one or more --event-calendar ids or the wildcard --event-calendar * must be included to specify an --event") - } - - return nil -} diff --git a/src/cli/restore/exchange_test.go b/src/cli/restore/exchange_test.go index f522fc314..eb0499338 100644 --- a/src/cli/restore/exchange_test.go +++ b/src/cli/restore/exchange_test.go @@ -138,7 +138,7 @@ func (suite *ExchangeSuite) TestValidateExchangeRestoreFlags() { } for _, test := range table { suite.T().Run(test.name, func(t *testing.T) { - test.expect(t, validateExchangeRestoreFlags( + test.expect(t, utils.ValidateExchangeRestoreFlags( test.contacts, test.contactFolders, test.emails, @@ -334,7 +334,7 @@ func (suite *ExchangeSuite) TestIncludeExchangeRestoreDataSelectors() { for _, test := range table { suite.T().Run(test.name, func(t *testing.T) { sel := selectors.NewExchangeRestore() - includeExchangeRestoreDataSelectors( + utils.IncludeExchangeRestoreDataSelectors( sel, test.contacts, test.contactFolders, @@ -435,7 +435,7 @@ func (suite *ExchangeSuite) TestFilterExchangeRestoreInfoSelectors() { for _, test := range table { suite.T().Run(test.name, func(t *testing.T) { sel := selectors.NewExchangeRestore() - filterExchangeRestoreInfoSelectors( + utils.FilterExchangeRestoreInfoSelectors( sel, test.contactName, test.after, diff --git a/src/cli/utils/exchange.go b/src/cli/utils/exchange.go new file mode 100644 index 000000000..cfcec4fcd --- /dev/null +++ b/src/cli/utils/exchange.go @@ -0,0 +1,126 @@ +package utils + +import ( + "errors" + + "github.com/alcionai/corso/src/pkg/selectors" +) + +// AddExchangeInclude adds the scope of the provided values to the selector's +// inclusion set. +func AddExchangeInclude( + sel *selectors.ExchangeRestore, + resource, folders, items []string, + incl func([]string, []string, []string) []selectors.ExchangeScope, +) { + if len(folders) == 0 { + return + } + + if len(items) == 0 { + items = selectors.Any() + } + + sel.Include(incl(resource, folders, items)) +} + +// AddExchangeFilter adds the scope of the provided values to the selector's +// filter set +func AddExchangeFilter( + sel *selectors.ExchangeRestore, + v string, + f func(string) []selectors.ExchangeScope, +) { + if len(v) == 0 { + return + } + + sel.Filter(f(v)) +} + +// ValidateExchangeRestoreFlags checks common flags for correctness and interdependencies +func ValidateExchangeRestoreFlags( + contacts, contactFolders, emails, emailFolders, events, eventCalendars, users []string, + backupID string, +) error { + if len(backupID) == 0 { + return errors.New("a backup ID is required") + } + + lu := len(users) + lc, lcf := len(contacts), len(contactFolders) + le, lef := len(emails), len(emailFolders) + lev, lec := len(events), len(eventCalendars) + + // if only the backupID is populated, that's the same as --all + if lu+lc+lcf+le+lef+lev+lec == 0 { + return nil + } + + if lu == 0 { + return errors.New("requires 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 an --email") + } + + if lev > 0 && lec == 0 { + return errors.New( + "one or more --event-calendar ids or the wildcard --event-calendar * must be included to specify an --event") + } + + return nil +} + +// IncludeExchangeRestoreDataSelectors builds the common data-selector +// inclusions for exchange commands. +func IncludeExchangeRestoreDataSelectors( + sel *selectors.ExchangeRestore, + contacts, contactFolders, emails, emailFolders, events, eventCalendars, users []string, +) { + lc, lcf := len(contacts), len(contactFolders) + le, lef := len(emails), len(emailFolders) + lev, lec := len(events), len(eventCalendars) + + // either scope the request to a set of users + if lc+lcf+le+lef+lev+lec == 0 { + if len(users) == 0 { + users = selectors.Any() + } + + sel.Include(sel.Users(users)) + + return + } + + // or add selectors for each type of data + AddExchangeInclude(sel, users, contactFolders, contacts, sel.Contacts) + AddExchangeInclude(sel, users, emailFolders, emails, sel.Mails) + AddExchangeInclude(sel, users, eventCalendars, events, sel.Events) +} + +// FilterExchangeRestoreInfoSelectors builds the common info-selector filters. +func FilterExchangeRestoreInfoSelectors( + sel *selectors.ExchangeRestore, + contactName, + emailReceivedAfter, emailReceivedBefore, emailSender, emailSubject, + eventOrganizer, eventRecurs, eventStartsAfter, eventStartsBefore, eventSubject string, +) { + AddExchangeFilter(sel, contactName, sel.ContactName) + AddExchangeFilter(sel, emailReceivedAfter, sel.MailReceivedAfter) + AddExchangeFilter(sel, emailReceivedBefore, sel.MailReceivedBefore) + AddExchangeFilter(sel, emailSender, sel.MailSender) + AddExchangeFilter(sel, emailSubject, sel.MailSubject) + AddExchangeFilter(sel, eventOrganizer, sel.EventOrganizer) + AddExchangeFilter(sel, eventRecurs, sel.EventRecurs) + AddExchangeFilter(sel, eventStartsAfter, sel.EventStartsAfter) + AddExchangeFilter(sel, eventStartsBefore, sel.EventStartsBefore) + AddExchangeFilter(sel, eventSubject, sel.EventSubject) +} diff --git a/src/go.mod b/src/go.mod index bfdb667d1..6e3849d42 100644 --- a/src/go.mod +++ b/src/go.mod @@ -12,6 +12,7 @@ require ( github.com/kopia/kopia v0.11.1 github.com/microsoft/kiota-abstractions-go v0.8.2 github.com/microsoft/kiota-authentication-azure-go v0.3.0 + github.com/microsoft/kiota-http-go v0.6.0 github.com/microsoft/kiota-serialization-json-go v0.5.5 github.com/microsoftgraph/msgraph-sdk-go v0.34.0 github.com/microsoftgraph/msgraph-sdk-go-core v0.27.0 @@ -48,7 +49,6 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect github.com/cjlapao/common-go v0.0.25 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect @@ -71,7 +71,6 @@ require ( github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect - github.com/microsoft/kiota-http-go v0.6.0 // indirect github.com/microsoft/kiota-serialization-text-go v0.4.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/minio-go/v7 v7.0.34 // indirect @@ -88,7 +87,6 @@ require ( github.com/prometheus/procfs v0.8.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rs/xid v1.4.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect github.com/zeebo/blake3 v0.2.3 // indirect diff --git a/src/go.sum b/src/go.sum index d1d4be955..49d2b2422 100644 --- a/src/go.sum +++ b/src/go.sum @@ -79,7 +79,6 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -335,7 +334,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=