corso/src/cli/utils/exchange.go
ashmrtn 72a3c7ab3b
Trim trailing '/' from CLI input for restore/details (#1175)
## Description

Use paths package to trim unescaped trailing '/' characters from input for Exchange mail and OneDrive folder names. Add tests for Exchange showing that the trimming also works properly if the folder name ends with '/'.

## Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [x] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Test
- [ ] 💻 CI/Deployment
- [ ] 🐹 Trivial/Minor

## Issue(s)

* closes #1147 

## Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
2022-10-17 17:54:36 +00:00

169 lines
5.0 KiB
Go

package utils
import (
"errors"
"github.com/alcionai/corso/src/pkg/selectors"
)
// flag names
const (
ContactFN = "contacts"
ContactFolderFN = "contact-folder"
EmailFN = "email"
EmailFolderFN = "email-folder"
EventFN = "events"
EventCalendarFN = "event-calendar"
ContactNameFN = "contact-name"
EmailReceivedAfterFN = "email-received-after"
EmailReceivedBeforeFN = "email-received-before"
EmailSenderFN = "email-sender"
EmailSubjectFN = "email-subject"
EventOrganizerFN = "event-organizer"
EventRecursFN = "event-recurs"
EventStartsAfterFN = "event-starts-after"
EventStartsBeforeFN = "event-starts-before"
EventSubjectFN = "event-subject"
)
type ExchangeOpts struct {
Contact []string
ContactFolder []string
Email []string
EmailFolder []string
Event []string
EventCalendar []string
Users []string
ContactName string
EmailReceivedAfter string
EmailReceivedBefore string
EmailSender string
EmailSubject string
EventOrganizer string
EventRecurs string
EventStartsAfter string
EventStartsBefore string
EventSubject string
Populated PopulatedFlags
}
// AddExchangeInclude adds the scope of the provided values to the selector's
// inclusion set. Any unpopulated slice will be replaced with selectors.Any()
// to act as a wildcard.
func AddExchangeInclude(
sel *selectors.ExchangeRestore,
resource, folders, items []string,
incl func([]string, []string, []string) []selectors.ExchangeScope,
) {
lf, li := len(folders), len(items)
// only use the inclusion if either a folder or item of
// this type is specified.
if lf+li == 0 {
return
}
if len(resource) == 0 {
resource = selectors.Any()
}
if lf == 0 {
folders = selectors.Any()
}
if li == 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(backupID string, opts ExchangeOpts) error {
if len(backupID) == 0 {
return errors.New("a backup ID is required")
}
if _, ok := opts.Populated[EmailReceivedAfterFN]; ok && !IsValidTimeFormat(opts.EmailReceivedAfter) {
return errors.New("invalid time format for email-received-after")
}
if _, ok := opts.Populated[EmailReceivedBeforeFN]; ok && !IsValidTimeFormat(opts.EmailReceivedBefore) {
return errors.New("invalid time format for email-received-before")
}
if _, ok := opts.Populated[EventStartsAfterFN]; ok && !IsValidTimeFormat(opts.EventStartsAfter) {
return errors.New("invalid time format for event-starts-after")
}
if _, ok := opts.Populated[EventStartsBeforeFN]; ok && !IsValidTimeFormat(opts.EventStartsBefore) {
return errors.New("invalid time format for event-starts-before")
}
if _, ok := opts.Populated[EventRecursFN]; ok && !IsValidBool(opts.EventRecurs) {
return errors.New("invalid format for event-recurs")
}
return nil
}
// IncludeExchangeRestoreDataSelectors builds the common data-selector
// inclusions for exchange commands.
func IncludeExchangeRestoreDataSelectors(
sel *selectors.ExchangeRestore,
opts ExchangeOpts,
) {
lc, lcf := len(opts.Contact), len(opts.ContactFolder)
le, lef := len(opts.Email), len(opts.EmailFolder)
lev, lec := len(opts.Event), len(opts.EventCalendar)
// either scope the request to a set of users
if lc+lcf+le+lef+lev+lec == 0 {
if len(opts.Users) == 0 {
opts.Users = selectors.Any()
}
sel.Include(sel.Users(opts.Users))
return
}
opts.EmailFolder = trimFolderSlash(opts.EmailFolder)
// or add selectors for each type of data
AddExchangeInclude(sel, opts.Users, opts.ContactFolder, opts.Contact, sel.Contacts)
AddExchangeInclude(sel, opts.Users, opts.EmailFolder, opts.Email, sel.Mails)
AddExchangeInclude(sel, opts.Users, opts.EventCalendar, opts.Event, sel.Events)
}
// FilterExchangeRestoreInfoSelectors builds the common info-selector filters.
func FilterExchangeRestoreInfoSelectors(
sel *selectors.ExchangeRestore,
opts ExchangeOpts,
) {
AddExchangeFilter(sel, opts.ContactName, sel.ContactName)
AddExchangeFilter(sel, opts.EmailReceivedAfter, sel.MailReceivedAfter)
AddExchangeFilter(sel, opts.EmailReceivedBefore, sel.MailReceivedBefore)
AddExchangeFilter(sel, opts.EmailSender, sel.MailSender)
AddExchangeFilter(sel, opts.EmailSubject, sel.MailSubject)
AddExchangeFilter(sel, opts.EventOrganizer, sel.EventOrganizer)
AddExchangeFilter(sel, opts.EventRecurs, sel.EventRecurs)
AddExchangeFilter(sel, opts.EventStartsAfter, sel.EventStartsAfter)
AddExchangeFilter(sel, opts.EventStartsBefore, sel.EventStartsBefore)
AddExchangeFilter(sel, opts.EventSubject, sel.EventSubject)
}