deduplicate shared exchange code in cli (#732)

This commit is contained in:
Keepers 2022-09-09 18:30:15 -06:00 committed by GitHub
parent 57ddf26007
commit 25ce11b2c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 216 additions and 549 deletions

View File

@ -66,15 +66,14 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
c, fs = utils.AddCommand(parent, exchangeCreateCmd()) c, fs = utils.AddCommand(parent, exchangeCreateCmd())
fs.StringSliceVar( fs.StringSliceVar(
&user, &user,
"user", "user", nil,
nil, "Backup Exchange data by user ID; accepts "+utils.Wildcard+" to select all users")
"Backup Exchange data by user ID; accepts "+utils.Wildcard+" to select all users", fs.BoolVar(&exchangeAll,
) "all", false,
fs.BoolVar(&exchangeAll, "all", false, "Backup all Exchange data for all users") "Backup all Exchange data for all users")
fs.StringSliceVar( fs.StringSliceVar(
&exchangeData, &exchangeData,
"data", "data", nil,
nil,
"Select one or more types of data to backup: "+dataEmail+", "+dataContacts+", or "+dataEvents) "Select one or more types of data to backup: "+dataEmail+", "+dataContacts+", or "+dataEvents)
options.AddOperationFlags(c) options.AddOperationFlags(c)
@ -83,113 +82,82 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
case detailsCommand: case detailsCommand:
c, fs = utils.AddCommand(parent, exchangeDetailsCmd()) 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")) cobra.CheckErr(c.MarkFlagRequired("backup"))
// per-data-type flags // per-data-type flags
fs.StringSliceVar( fs.StringSliceVar(
&contact, &contact,
"contact", "contact", nil,
nil, "Select backup details by contact ID; accepts "+utils.Wildcard+" to select all contacts")
"Select backup details by contact ID; accepts "+utils.Wildcard+" to select all contacts",
)
fs.StringSliceVar( fs.StringSliceVar(
&contactFolder, &contactFolder,
"contact-folder", "contact-folder", nil,
nil, "Select backup details by contact folder ID; accepts "+utils.Wildcard+" to select all contact folders")
"Select backup details by contact folder ID; accepts "+utils.Wildcard+" to select all contact folders",
)
fs.StringSliceVar( fs.StringSliceVar(
&email, &email,
"email", "email", nil,
nil, "Select backup details by emails ID; accepts "+utils.Wildcard+" to select all emails")
"Select backup details by emails ID; accepts "+utils.Wildcard+" to select all emails",
)
fs.StringSliceVar( fs.StringSliceVar(
&emailFolder, &emailFolder,
"email-folder", "email-folder", nil,
nil,
"Select backup details by email folder ID; accepts "+utils.Wildcard+" to select all email folders") "Select backup details by email folder ID; accepts "+utils.Wildcard+" to select all email folders")
fs.StringSliceVar( fs.StringSliceVar(
&event, &event,
"event", "event", nil,
nil, "Select backup details by event ID; accepts "+utils.Wildcard+" to select all events")
"Select backup details by event ID; accepts "+utils.Wildcard+" to select all events",
)
fs.StringSliceVar( fs.StringSliceVar(
&eventCalendar, &eventCalendar,
"event-calendar", "event-calendar", nil,
nil, "Select backup details by event calendar ID; accepts "+utils.Wildcard+" to select all events")
"Select backup details by event calendar ID; accepts "+utils.Wildcard+" to select all events",
)
fs.StringSliceVar( fs.StringSliceVar(
&user, &user,
"user", "user", nil,
nil, "Select backup details by user ID; accepts "+utils.Wildcard+" to select all users")
"Select backup details by user ID; accepts "+utils.Wildcard+" to select all users",
)
// exchange-info flags // exchange-info flags
fs.StringVar( fs.StringVar(
&contactName, &contactName,
"contact-name", "contact-name", "",
"", "Select backup details where the contact name contains this value")
"Select backup details where the contact name contains this value",
)
fs.StringVar( fs.StringVar(
&emailReceivedAfter, &emailReceivedAfter,
"email-received-after", "email-received-after", "",
"", "Restore mail where the email was received after this datetime")
"Restore mail where the email was received after this datetime",
)
fs.StringVar( fs.StringVar(
&emailReceivedBefore, &emailReceivedBefore,
"email-received-before", "email-received-before", "",
"", "Restore mail where the email was received before this datetime")
"Restore mail where the email was received before this datetime",
)
fs.StringVar( fs.StringVar(
&emailSender, &emailSender,
"email-sender", "email-sender", "",
"", "Restore mail where the email sender matches this user id")
"Restore mail where the email sender matches this user id",
)
fs.StringVar( fs.StringVar(
&emailSubject, &emailSubject,
"email-subject", "email-subject", "",
"", "Restore mail where the email subject lines contain this value")
"Restore mail where the email subject lines contain this value",
)
fs.StringVar( fs.StringVar(
&eventOrganizer, &eventOrganizer,
"event-organizer", "event-organizer", "",
"", "Select backup details where the event organizer user id contains this value")
"Select backup details where the event organizer user id contains this value",
)
fs.StringVar( fs.StringVar(
&eventRecurs, &eventRecurs,
"event-recurs", "event-recurs", "",
"", "Select backup details if the event recurs. Use --event-recurs false to select where the event does not recur.")
"Select backup details if the event recurs. Use --event-recurs false to select where the event does not recur.",
)
fs.StringVar( fs.StringVar(
&eventStartsAfter, &eventStartsAfter,
"event-starts-after", "event-starts-after", "",
"", "Select backup details where the event starts after this datetime")
"Select backup details where the event starts after this datetime",
)
fs.StringVar( fs.StringVar(
&eventStartsBefore, &eventStartsBefore,
"event-starts-before", "event-starts-before", "",
"", "Select backup details where the event starts before this datetime")
"Select backup details where the event starts before this datetime",
)
fs.StringVar( fs.StringVar(
&eventSubject, &eventSubject,
"event-subject", "event-subject", "",
"", "Select backup details where the event subject contains this value")
"Select backup details where the event subject contains this value",
)
case deleteCommand: case deleteCommand:
c, fs = utils.AddCommand(parent, exchangeDeleteCmd()) c, fs = utils.AddCommand(parent, exchangeDeleteCmd())
@ -368,7 +336,7 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
return nil return nil
} }
if err := validateExchangeBackupDetailFlags( if err := utils.ValidateExchangeRestoreFlags(
contact, contact,
contactFolder, contactFolder,
email, email,
@ -399,7 +367,7 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
} }
sel := selectors.NewExchangeRestore() sel := selectors.NewExchangeRestore()
includeExchangeBackupDetailDataSelectors( utils.IncludeExchangeRestoreDataSelectors(
sel, sel,
contact, contact,
contactFolder, contactFolder,
@ -408,7 +376,7 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
event, event,
eventCalendar, eventCalendar,
user) user)
filterExchangeBackupDetailInfoSelectors( utils.FilterExchangeRestoreInfoSelectors(
sel, sel,
contactName, contactName,
emailReceivedAfter, emailReceivedAfter,
@ -436,207 +404,6 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
return nil 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 // backup delete
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -303,7 +303,7 @@ func (suite *ExchangeSuite) TestValidateBackupDetailFlags() {
} }
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
test.expect(t, validateExchangeBackupDetailFlags( test.expect(t, utils.ValidateExchangeRestoreFlags(
test.contacts, test.contacts,
test.contactFolders, test.contactFolders,
test.emails, test.emails,
@ -499,7 +499,7 @@ func (suite *ExchangeSuite) TestIncludeExchangeBackupDetailDataSelectors() {
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
sel := selectors.NewExchangeRestore() sel := selectors.NewExchangeRestore()
includeExchangeBackupDetailDataSelectors( utils.IncludeExchangeRestoreDataSelectors(
sel, sel,
test.contacts, test.contacts,
test.contactFolders, test.contactFolders,
@ -600,7 +600,7 @@ func (suite *ExchangeSuite) TestFilterExchangeBackupDetailInfoSelectors() {
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
sel := selectors.NewExchangeRestore() sel := selectors.NewExchangeRestore()
filterExchangeBackupDetailInfoSelectors( utils.FilterExchangeRestoreInfoSelectors(
sel, sel,
test.contactName, test.contactName,
test.after, test.after,

View File

@ -54,91 +54,71 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
// per-data-type flags // per-data-type flags
fs.StringSliceVar( fs.StringSliceVar(
&contact, &contact,
"contact", "contact", nil,
nil, "Restore contacts by ID; accepts "+utils.Wildcard+" to select all contacts")
"Restore contacts by ID; accepts "+utils.Wildcard+" to select all contacts",
)
fs.StringSliceVar( fs.StringSliceVar(
&contactFolder, &contactFolder,
"contact-folder", "contact-folder", nil,
nil,
"Restore all contacts within the folder ID; accepts "+utils.Wildcard+" to select all contact folders") "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( fs.StringSliceVar(
&emailFolder, &emailFolder,
"email-folder", "email-folder", nil,
nil, "Restore all emails by folder ID; accepts "+utils.Wildcard+" to select all email folders")
"Restore all emails by folder ID; accepts "+utils.Wildcard+" to select all email folders", fs.StringSliceVar(&event,
) "event", nil,
fs.StringSliceVar(&event, "event", nil, "Restore events by ID; accepts "+utils.Wildcard+" to select all events") "Restore events by ID; accepts "+utils.Wildcard+" to select all events")
fs.StringSliceVar( fs.StringSliceVar(
&eventCalendar, &eventCalendar,
"event-calendar", "event-calendar", nil,
nil,
"Restore events by calendar ID; accepts "+utils.Wildcard+" to select all event calendars") "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 // exchange-info flags
fs.StringVar( fs.StringVar(
&contactName, &contactName,
"contact-name", "contact-name", "",
"", "Restore contacts where the contact name contains this value")
"Restore contacts where the contact name contains this value",
)
fs.StringVar( fs.StringVar(
&emailReceivedAfter, &emailReceivedAfter,
"email-received-after", "email-received-after", "",
"", "Restore mail where the email was received after this datetime")
"Restore mail where the email was received after this datetime",
)
fs.StringVar( fs.StringVar(
&emailReceivedBefore, &emailReceivedBefore,
"email-received-before", "email-received-before", "",
"", "Restore mail where the email was received before this datetime")
"Restore mail where the email was received before this datetime",
)
fs.StringVar( fs.StringVar(
&emailSender, &emailSender,
"email-sender", "email-sender", "",
"", "Restore mail where the email sender matches this user id")
"Restore mail where the email sender matches this user id",
)
fs.StringVar( fs.StringVar(
&emailSubject, &emailSubject,
"email-subject", "email-subject", "",
"", "Restore mail where the email subject lines contain this value")
"Restore mail where the email subject lines contain this value",
)
fs.StringVar( fs.StringVar(
&eventOrganizer, &eventOrganizer,
"event-organizer", "event-organizer", "",
"", "Restore events where the event organizer user id contains this value")
"Restore events where the event organizer user id contains this value",
)
fs.StringVar( fs.StringVar(
&eventRecurs, &eventRecurs,
"event-recurs", "event-recurs", "",
"", "Restore events if the event recurs. Use --event-recurs false to select where the event does not recur.")
"Restore events if the event recurs. Use --event-recurs false to select where the event does not recur.",
)
fs.StringVar( fs.StringVar(
&eventStartsAfter, &eventStartsAfter,
"event-starts-after", "event-starts-after", "",
"", "Restore events where the event starts after this datetime")
"Restore events where the event starts after this datetime",
)
fs.StringVar( fs.StringVar(
&eventStartsBefore, &eventStartsBefore,
"event-starts-before", "event-starts-before", "",
"", "Restore events where the event starts before this datetime")
"Restore events where the event starts before this datetime",
)
fs.StringVar( fs.StringVar(
&eventSubject, &eventSubject,
"event-subject", "event-subject", "",
"", "Restore events where the event subject contains this value")
"Restore events where the event subject contains this value",
)
// others // others
options.AddOperationFlags(c) options.AddOperationFlags(c)
@ -167,7 +147,7 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error {
return nil return nil
} }
if err := validateExchangeRestoreFlags( if err := utils.ValidateExchangeRestoreFlags(
contact, contact,
contactFolder, contactFolder,
email, email,
@ -193,7 +173,7 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error {
defer utils.CloseRepo(ctx, r) defer utils.CloseRepo(ctx, r)
sel := selectors.NewExchangeRestore() sel := selectors.NewExchangeRestore()
includeExchangeRestoreDataSelectors( utils.IncludeExchangeRestoreDataSelectors(
sel, sel,
contact, contact,
contactFolder, contactFolder,
@ -202,7 +182,7 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error {
event, event,
eventCalendar, eventCalendar,
user) user)
filterExchangeRestoreInfoSelectors( utils.FilterExchangeRestoreInfoSelectors(
sel, sel,
contactName, contactName,
emailReceivedAfter, emailReceivedAfter,
@ -233,205 +213,3 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error {
return nil 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
}

View File

@ -138,7 +138,7 @@ func (suite *ExchangeSuite) TestValidateExchangeRestoreFlags() {
} }
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
test.expect(t, validateExchangeRestoreFlags( test.expect(t, utils.ValidateExchangeRestoreFlags(
test.contacts, test.contacts,
test.contactFolders, test.contactFolders,
test.emails, test.emails,
@ -334,7 +334,7 @@ func (suite *ExchangeSuite) TestIncludeExchangeRestoreDataSelectors() {
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
sel := selectors.NewExchangeRestore() sel := selectors.NewExchangeRestore()
includeExchangeRestoreDataSelectors( utils.IncludeExchangeRestoreDataSelectors(
sel, sel,
test.contacts, test.contacts,
test.contactFolders, test.contactFolders,
@ -435,7 +435,7 @@ func (suite *ExchangeSuite) TestFilterExchangeRestoreInfoSelectors() {
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
sel := selectors.NewExchangeRestore() sel := selectors.NewExchangeRestore()
filterExchangeRestoreInfoSelectors( utils.FilterExchangeRestoreInfoSelectors(
sel, sel,
test.contactName, test.contactName,
test.after, test.after,

126
src/cli/utils/exchange.go Normal file
View File

@ -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)
}

View File

@ -12,6 +12,7 @@ require (
github.com/kopia/kopia v0.11.1 github.com/kopia/kopia v0.11.1
github.com/microsoft/kiota-abstractions-go v0.8.2 github.com/microsoft/kiota-abstractions-go v0.8.2
github.com/microsoft/kiota-authentication-azure-go v0.3.0 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/microsoft/kiota-serialization-json-go v0.5.5
github.com/microsoftgraph/msgraph-sdk-go v0.34.0 github.com/microsoftgraph/msgraph-sdk-go v0.34.0
github.com/microsoftgraph/msgraph-sdk-go-core v0.27.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/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect
github.com/cjlapao/common-go v0.0.25 // 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/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect
github.com/edsrzf/mmap-go v1.1.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/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // 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/microsoft/kiota-serialization-text-go v0.4.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.34 // 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/prometheus/procfs v0.8.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
github.com/rs/xid v1.4.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/sirupsen/logrus v1.9.0 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect github.com/zeebo/blake3 v0.2.3 // indirect

View File

@ -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-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-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/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/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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/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 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 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/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.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=