check for flag population instead of empty string (#1075)

Adds a processor that confirms whether user has
added a value for a flag in the cmd, or if it is the
default value.  This map of valued flags is added
to the service opts structs to for validation.

Also migrates many service flags to utils as
consts so that these values can be maintained
as consistent across packages.
This commit is contained in:
Keepers 2022-10-14 11:01:27 -06:00 committed by GitHub
parent 4a29d22216
commit 65d6780906
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 396 additions and 250 deletions

View File

@ -110,11 +110,11 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
// More generic (ex: --user) and more frequently used flags take precedence. // More generic (ex: --user) and more frequently used flags take precedence.
fs.StringSliceVar( fs.StringSliceVar(
&user, &user,
"user", nil, utils.UserFN, 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.StringSliceVar( fs.StringSliceVar(
&exchangeData, &exchangeData,
"data", nil, utils.DataFN, 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)
@ -134,83 +134,83 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
// Flags addition ordering should follow the order we want them to appear in help and docs: // Flags addition ordering should follow the order we want them to appear in help and docs:
// More generic (ex: --user) and more frequently used flags take precedence. // More generic (ex: --user) and more frequently used flags take precedence.
fs.StringVar(&backupID, fs.StringVar(&backupID,
"backup", "", utils.BackupFN, "",
"ID of the backup to explore. (required)") "ID of the backup to explore. (required)")
cobra.CheckErr(c.MarkFlagRequired("backup")) cobra.CheckErr(c.MarkFlagRequired(utils.BackupFN))
fs.StringSliceVar( fs.StringSliceVar(
&user, &user,
"user", nil, utils.UserFN, 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.")
// email flags // email flags
fs.StringSliceVar( fs.StringSliceVar(
&email, &email,
"email", nil, utils.EmailFN, nil,
"Select backup details for emails by email ID; accepts '"+utils.Wildcard+"' to select all emails.") "Select backup details for emails by email ID; accepts '"+utils.Wildcard+"' to select all emails.")
fs.StringSliceVar( fs.StringSliceVar(
&emailFolder, &emailFolder,
"email-folder", nil, utils.EmailFolderFN, nil,
"Select backup details for emails within a folder; accepts '"+utils.Wildcard+"' to select all email folders.") "Select backup details for emails within a folder; accepts '"+utils.Wildcard+"' to select all email folders.")
fs.StringVar( fs.StringVar(
&emailSubject, &emailSubject,
"email-subject", "", utils.EmailSubjectFN, "",
"Select backup details for emails with a subject containing this value.") "Select backup details for emails with a subject containing this value.")
fs.StringVar( fs.StringVar(
&emailSender, &emailSender,
"email-sender", "", utils.EmailSenderFN, "",
"Select backup details for emails from a specific sender.") "Select backup details for emails from a specific sender.")
fs.StringVar( fs.StringVar(
&emailReceivedAfter, &emailReceivedAfter,
"email-received-after", "", utils.EmailReceivedAfterFN, "",
"Select backup details for emails received after this datetime.") "Select backup details for emails received after this datetime.")
fs.StringVar( fs.StringVar(
&emailReceivedBefore, &emailReceivedBefore,
"email-received-before", "", utils.EmailReceivedBeforeFN, "",
"Select backup details for emails received before this datetime.") "Select backup details for emails received before this datetime.")
// event flags // event flags
fs.StringSliceVar( fs.StringSliceVar(
&event, &event,
"event", nil, utils.EventFN, nil,
"Select backup details for events by event ID; accepts '"+utils.Wildcard+"' to select all events.") "Select backup details for events by event ID; accepts '"+utils.Wildcard+"' to select all events.")
fs.StringSliceVar( fs.StringSliceVar(
&eventCalendar, &eventCalendar,
"event-calendar", nil, utils.EventCalendarFN, nil,
"Select backup details for events under a calendar; accepts '"+utils.Wildcard+"' to select all events.") "Select backup details for events under a calendar; accepts '"+utils.Wildcard+"' to select all events.")
fs.StringVar( fs.StringVar(
&eventSubject, &eventSubject,
"event-subject", "", utils.EventSubjectFN, "",
"Select backup details for events with a subject containing this value.") "Select backup details for events with a subject containing this value.")
fs.StringVar( fs.StringVar(
&eventOrganizer, &eventOrganizer,
"event-organizer", "", utils.EventOrganizerFN, "",
"Select backup details for events from a specific organizer.") "Select backup details for events from a specific organizer.")
fs.StringVar( fs.StringVar(
&eventRecurs, &eventRecurs,
"event-recurs", "", utils.EventRecursFN, "",
"Select backup details for recurring events. Use `--event-recurs false` to select non-recurring events.") "Select backup details for recurring events. Use `--event-recurs false` to select non-recurring events.")
fs.StringVar( fs.StringVar(
&eventStartsAfter, &eventStartsAfter,
"event-starts-after", "", utils.EventStartsAfterFN, "",
"Select backup details for events starting after this datetime.") "Select backup details for events starting after this datetime.")
fs.StringVar( fs.StringVar(
&eventStartsBefore, &eventStartsBefore,
"event-starts-before", "", utils.EventStartsBeforeFN, "",
"Select backup details for events starting before this datetime.") "Select backup details for events starting before this datetime.")
// contact flags // contact flags
fs.StringSliceVar( fs.StringSliceVar(
&contact, &contact,
"contact", nil, utils.ContactFN, nil,
"Select backup details for contacts by contact ID; accepts '"+utils.Wildcard+"' to select all contacts.") "Select backup details for contacts by contact ID; accepts '"+utils.Wildcard+"' to select all contacts.")
fs.StringSliceVar( fs.StringSliceVar(
&contactFolder, &contactFolder,
"contact-folder", nil, utils.ContactFolderFN, nil,
"Select backup details for contacts within a folder; accepts '"+utils.Wildcard+"' to select all contact folders.") "Select backup details for contacts within a folder; accepts '"+utils.Wildcard+"' to select all contact folders.")
fs.StringVar( fs.StringVar(
&contactName, &contactName,
"contact-name", "", utils.ContactNameFN, "",
"Select backup details for contacts whose contact name contains this value.") "Select backup details for contacts whose contact name contains this value.")
case deleteCommand: case deleteCommand:
@ -219,8 +219,10 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
c.Use = c.Use + " " + exchangeServiceCommandDeleteUseSuffix c.Use = c.Use + " " + exchangeServiceCommandDeleteUseSuffix
c.Example = exchangeServiceCommandDeleteExamples c.Example = exchangeServiceCommandDeleteExamples
fs.StringVar(&backupID, "backup", "", "ID of the backup to delete. (required)") fs.StringVar(&backupID,
cobra.CheckErr(c.MarkFlagRequired("backup")) utils.BackupFN, "",
"ID of the backup to delete. (required)")
cobra.CheckErr(c.MarkFlagRequired(utils.BackupFN))
} }
return c return c
@ -401,12 +403,12 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
ctx := cmd.Context() ctx := cmd.Context()
opts := utils.ExchangeOpts{ opts := utils.ExchangeOpts{
Contacts: contact, Contact: contact,
ContactFolders: contactFolder, ContactFolder: contactFolder,
Emails: email, Email: email,
EmailFolders: emailFolder, EmailFolder: emailFolder,
Events: event, Event: event,
EventCalendars: eventCalendar, EventCalendar: eventCalendar,
Users: user, Users: user,
ContactName: contactName, ContactName: contactName,
EmailReceivedAfter: emailReceivedAfter, EmailReceivedAfter: emailReceivedAfter,
@ -418,6 +420,8 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
EventStartsAfter: eventStartsAfter, EventStartsAfter: eventStartsAfter,
EventStartsBefore: eventStartsBefore, EventStartsBefore: eventStartsBefore,
EventSubject: eventSubject, EventSubject: eventSubject,
Populated: utils.GetPopulatedFlags(cmd),
} }
s, acct, err := config.GetStorageAndAccount(ctx, true, nil) s, acct, err := config.GetStorageAndAccount(ctx, true, nil)

View File

@ -14,6 +14,7 @@ import (
"github.com/alcionai/corso/src/cli" "github.com/alcionai/corso/src/cli"
"github.com/alcionai/corso/src/cli/config" "github.com/alcionai/corso/src/cli/config"
"github.com/alcionai/corso/src/cli/print" "github.com/alcionai/corso/src/cli/print"
"github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/connector/exchange" "github.com/alcionai/corso/src/internal/connector/exchange"
"github.com/alcionai/corso/src/internal/operations" "github.com/alcionai/corso/src/internal/operations"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
@ -108,8 +109,8 @@ func (suite *BackupExchangeIntegrationSuite) TestExchangeBackupCmd() {
cmd := tester.StubRootCmd( cmd := tester.StubRootCmd(
"backup", "create", "exchange", "backup", "create", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.cfgFP,
"--user", suite.m365UserID, "--"+utils.UserFN, suite.m365UserID,
"--data", set.String()) "--"+utils.DataFN, set.String())
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
cmd.SetOut(&recorder) cmd.SetOut(&recorder)
@ -327,7 +328,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeDetailsCmd() {
cmd := tester.StubRootCmd( cmd := tester.StubRootCmd(
"backup", "details", "exchange", "backup", "details", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.cfgFP,
"--backup", string(bID)) "--"+utils.BackupFN, string(bID))
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
cmd.SetOut(&suite.recorder) cmd.SetOut(&suite.recorder)
@ -358,7 +359,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeDetailsCmd() {
} }
// At least the prefix of the path should be encoded as folders. // At least the prefix of the path should be encoded as folders.
assert.Greater(suite.T(), foundFolders, 4) assert.Greater(t, foundFolders, 4)
}) })
} }
} }
@ -441,7 +442,7 @@ func (suite *BackupDeleteExchangeIntegrationSuite) TestExchangeBackupDeleteCmd()
cmd := tester.StubRootCmd( cmd := tester.StubRootCmd(
"backup", "delete", "exchange", "backup", "delete", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.cfgFP,
"--backup", string(suite.backupOp.Results.BackupID)) "--"+utils.BackupFN, string(suite.backupOp.Results.BackupID))
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
// run the command // run the command
@ -467,7 +468,7 @@ func (suite *BackupDeleteExchangeIntegrationSuite) TestExchangeBackupDeleteCmd_U
cmd := tester.StubRootCmd( cmd := tester.StubRootCmd(
"backup", "delete", "exchange", "backup", "delete", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.cfgFP,
"--backup", uuid.NewString()) "--"+utils.BackupFN, uuid.NewString())
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
// unknown backupIDs should error since the modelStore can't find the backup // unknown backupIDs should error since the modelStore can't find the backup

View File

@ -79,7 +79,8 @@ func addOneDriveCommands(parent *cobra.Command) *cobra.Command {
c.Use = c.Use + " " + oneDriveServiceCommandCreateUseSuffix c.Use = c.Use + " " + oneDriveServiceCommandCreateUseSuffix
c.Example = oneDriveServiceCommandCreateExamples c.Example = oneDriveServiceCommandCreateExamples
fs.StringArrayVar(&user, "user", nil, fs.StringArrayVar(&user,
utils.UserFN, nil,
"Backup OneDrive data by user ID; accepts '"+utils.Wildcard+"' to select all users. (required)") "Backup OneDrive data by user ID; accepts '"+utils.Wildcard+"' to select all users. (required)")
options.AddOperationFlags(c) options.AddOperationFlags(c)
@ -96,39 +97,41 @@ func addOneDriveCommands(parent *cobra.Command) *cobra.Command {
c.Use = c.Use + " " + oneDriveServiceCommandDetailsUseSuffix c.Use = c.Use + " " + oneDriveServiceCommandDetailsUseSuffix
c.Example = oneDriveServiceCommandDetailsExamples c.Example = oneDriveServiceCommandDetailsExamples
fs.StringVar(&backupID, "backup", "", "ID of the backup to explore. (required)") fs.StringVar(&backupID,
cobra.CheckErr(c.MarkFlagRequired("backup")) utils.BackupFN, "",
"ID of the backup to explore. (required)")
cobra.CheckErr(c.MarkFlagRequired(utils.BackupFN))
// onedrive hierarchy flags // onedrive hierarchy flags
fs.StringSliceVar( fs.StringSliceVar(
&folderPaths, &folderPaths,
"folder", nil, utils.FolderFN, nil,
"Select backup details by OneDrive folder; defaults to root.") "Select backup details by OneDrive folder; defaults to root.")
fs.StringSliceVar( fs.StringSliceVar(
&fileNames, &fileNames,
"file", nil, utils.FileFN, nil,
"Select backup details by file name or ID.") "Select backup details by file name or ID.")
// onedrive info flags // onedrive info flags
fs.StringVar( fs.StringVar(
&fileCreatedAfter, &fileCreatedAfter,
"file-created-after", "", utils.FileCreatedAfterFN, "",
"Select backup details for files created after this datetime.") "Select backup details for files created after this datetime.")
fs.StringVar( fs.StringVar(
&fileCreatedBefore, &fileCreatedBefore,
"file-created-before", "", utils.FileCreatedBeforeFN, "",
"Select backup details for files created before this datetime.") "Select backup details for files created before this datetime.")
fs.StringVar( fs.StringVar(
&fileModifiedAfter, &fileModifiedAfter,
"file-modified-after", "", utils.FileModifiedAfterFN, "",
"Select backup details for files modified after this datetime.") "Select backup details for files modified after this datetime.")
fs.StringVar( fs.StringVar(
&fileModifiedBefore, &fileModifiedBefore,
"file-modified-before", "", utils.FileModifiedBeforeFN, "",
"Select backup details for files modified before this datetime.") "Select backup details for files modified before this datetime.")
case deleteCommand: case deleteCommand:
@ -137,8 +140,10 @@ func addOneDriveCommands(parent *cobra.Command) *cobra.Command {
c.Use = c.Use + " " + oneDriveServiceCommandDeleteUseSuffix c.Use = c.Use + " " + oneDriveServiceCommandDeleteUseSuffix
c.Example = oneDriveServiceCommandDeleteExamples c.Example = oneDriveServiceCommandDeleteExamples
fs.StringVar(&backupID, "backup", "", "ID of the backup to delete. (required)") fs.StringVar(&backupID,
cobra.CheckErr(c.MarkFlagRequired("backup")) utils.BackupFN, "",
"ID of the backup to delete. (required)")
cobra.CheckErr(c.MarkFlagRequired(utils.BackupFN))
} }
return c return c
@ -311,13 +316,15 @@ func detailsOneDriveCmd(cmd *cobra.Command, args []string) error {
defer utils.CloseRepo(ctx, r) defer utils.CloseRepo(ctx, r)
opts := utils.OneDriveOpts{ opts := utils.OneDriveOpts{
Users: user, Users: user,
Paths: folderPaths, Paths: folderPaths,
Names: fileNames, Names: fileNames,
CreatedAfter: fileCreatedAfter, FileCreatedAfter: fileCreatedAfter,
CreatedBefore: fileCreatedBefore, FileCreatedBefore: fileCreatedBefore,
ModifiedAfter: fileModifiedAfter, FileModifiedAfter: fileModifiedAfter,
ModifiedBefore: fileModifiedBefore, FileModifiedBefore: fileModifiedBefore,
Populated: utils.GetPopulatedFlags(cmd),
} }
ds, err := runDetailsOneDriveCmd(ctx, r, backupID, opts) ds, err := runDetailsOneDriveCmd(ctx, r, backupID, opts)

View File

@ -56,79 +56,81 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
// Flags addition ordering should follow the order we want them to appear in help and docs: // Flags addition ordering should follow the order we want them to appear in help and docs:
// More generic (ex: --user) and more frequently used flags take precedence. // More generic (ex: --user) and more frequently used flags take precedence.
// general flags // general flags
fs.StringVar(&backupID, "backup", "", "ID of the backup to restore. (required)") fs.StringVar(&backupID,
cobra.CheckErr(c.MarkFlagRequired("backup")) utils.BackupFN, "",
"ID of the backup to restore. (required)")
cobra.CheckErr(c.MarkFlagRequired(utils.BackupFN))
fs.StringSliceVar(&user, fs.StringSliceVar(&user,
"user", nil, utils.UserFN, nil,
"Restore data by user ID; accepts '"+utils.Wildcard+"' to select all users.") "Restore data by user ID; accepts '"+utils.Wildcard+"' to select all users.")
// email flags // email flags
fs.StringSliceVar(&email, fs.StringSliceVar(&email,
"email", nil, utils.EmailFN, nil,
"Restore emails by ID; accepts '"+utils.Wildcard+"' to select all emails.") "Restore emails by ID; accepts '"+utils.Wildcard+"' to select all emails.")
fs.StringSliceVar( fs.StringSliceVar(
&emailFolder, &emailFolder,
"email-folder", nil, utils.EmailFolderFN, nil,
"Restore emails within a folder; accepts '"+utils.Wildcard+"' to select all email folders.") "Restore emails within a folder; accepts '"+utils.Wildcard+"' to select all email folders.")
fs.StringVar( fs.StringVar(
&emailSubject, &emailSubject,
"email-subject", "", utils.EmailSubjectFN, "",
"Restore emails with a subject containing this value.") "Restore emails with a subject containing this value.")
fs.StringVar( fs.StringVar(
&emailSender, &emailSender,
"email-sender", "", utils.EmailSenderFN, "",
"Restore emails from a specific sender.") "Restore emails from a specific sender.")
fs.StringVar( fs.StringVar(
&emailReceivedAfter, &emailReceivedAfter,
"email-received-after", "", utils.EmailReceivedAfterFN, "",
"Restore emails received after this datetime.") "Restore emails received after this datetime.")
fs.StringVar( fs.StringVar(
&emailReceivedBefore, &emailReceivedBefore,
"email-received-before", "", utils.EmailReceivedBeforeFN, "",
"Restore emails received before this datetime.") "Restore emails received before this datetime.")
// event flags // event flags
fs.StringSliceVar(&event, fs.StringSliceVar(&event,
"event", nil, utils.EventFN, nil,
"Restore events by event ID; accepts '"+utils.Wildcard+"' to select all events.") "Restore events by event ID; accepts '"+utils.Wildcard+"' to select all events.")
fs.StringSliceVar( fs.StringSliceVar(
&eventCalendar, &eventCalendar,
"event-calendar", nil, utils.EventCalendarFN, nil,
"Restore events under a calendar; accepts '"+utils.Wildcard+"' to select all event calendars.") "Restore events under a calendar; accepts '"+utils.Wildcard+"' to select all event calendars.")
fs.StringVar( fs.StringVar(
&eventSubject, &eventSubject,
"event-subject", "", utils.EventSubjectFN, "",
"Restore events with a subject containing this value.") "Restore events with a subject containing this value.")
fs.StringVar( fs.StringVar(
&eventOrganizer, &eventOrganizer,
"event-organizer", "", utils.EventOrganizerFN, "",
"Restore events from a specific organizer.") "Restore events from a specific organizer.")
fs.StringVar( fs.StringVar(
&eventRecurs, &eventRecurs,
"event-recurs", "", utils.EventRecursFN, "",
"Restore recurring events. Use `--event-recurs false` to restore non-recurring events.") "Restore recurring events. Use `--event-recurs false` to restore non-recurring events.")
fs.StringVar( fs.StringVar(
&eventStartsAfter, &eventStartsAfter,
"event-starts-after", "", utils.EventStartsAfterFN, "",
"Restore events starting after this datetime.") "Restore events starting after this datetime.")
fs.StringVar( fs.StringVar(
&eventStartsBefore, &eventStartsBefore,
"event-starts-before", "", utils.EventStartsBeforeFN, "",
"Restore events starting before this datetime.") "Restore events starting before this datetime.")
// contacts flags // contacts flags
fs.StringSliceVar( fs.StringSliceVar(
&contact, &contact,
"contact", nil, utils.ContactFN, nil,
"Restore contacts by contact ID; accepts '"+utils.Wildcard+"' to select all contacts.") "Restore contacts by contact ID; accepts '"+utils.Wildcard+"' to select all contacts.")
fs.StringSliceVar( fs.StringSliceVar(
&contactFolder, &contactFolder,
"contact-folder", nil, utils.ContactFolderFN, nil,
"Restore contacts within a folder; accepts '"+utils.Wildcard+"' to select all contact folders.") "Restore contacts within a folder; accepts '"+utils.Wildcard+"' to select all contact folders.")
fs.StringVar( fs.StringVar(
&contactName, &contactName,
"contact-name", "", utils.ContactNameFN, "",
"Restore contacts whose contact name contains this value.") "Restore contacts whose contact name contains this value.")
// others // others
@ -177,12 +179,12 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error {
} }
opts := utils.ExchangeOpts{ opts := utils.ExchangeOpts{
Contacts: contact, Contact: contact,
ContactFolders: contactFolder, ContactFolder: contactFolder,
Emails: email, Email: email,
EmailFolders: emailFolder, EmailFolder: emailFolder,
Events: event, Event: event,
EventCalendars: eventCalendar, EventCalendar: eventCalendar,
Users: user, Users: user,
ContactName: contactName, ContactName: contactName,
EmailReceivedAfter: emailReceivedAfter, EmailReceivedAfter: emailReceivedAfter,
@ -194,6 +196,8 @@ func restoreExchangeCmd(cmd *cobra.Command, args []string) error {
EventStartsAfter: eventStartsAfter, EventStartsAfter: eventStartsAfter,
EventStartsBefore: eventStartsBefore, EventStartsBefore: eventStartsBefore,
EventSubject: eventSubject, EventSubject: eventSubject,
Populated: utils.GetPopulatedFlags(cmd),
} }
if err := utils.ValidateExchangeRestoreFlags(backupID, opts); err != nil { if err := utils.ValidateExchangeRestoreFlags(backupID, opts); err != nil {

View File

@ -10,6 +10,7 @@ import (
"github.com/alcionai/corso/src/cli" "github.com/alcionai/corso/src/cli"
"github.com/alcionai/corso/src/cli/config" "github.com/alcionai/corso/src/cli/config"
"github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/connector/exchange" "github.com/alcionai/corso/src/internal/connector/exchange"
"github.com/alcionai/corso/src/internal/operations" "github.com/alcionai/corso/src/internal/operations"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
@ -136,7 +137,7 @@ func (suite *RestoreExchangeIntegrationSuite) TestExchangeRestoreCmd() {
cmd := tester.StubRootCmd( cmd := tester.StubRootCmd(
"restore", "exchange", "restore", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.cfgFP,
"--backup", string(suite.backupOps[set].Results.BackupID)) "--"+utils.BackupFN, string(suite.backupOps[set].Results.BackupID))
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
// run the command // run the command
@ -160,15 +161,15 @@ func (suite *RestoreExchangeIntegrationSuite) TestExchangeRestoreCmd_badTimeFlag
var timeFilter string var timeFilter string
switch set { switch set {
case email: case email:
timeFilter = "--email-received-after" timeFilter = "--" + utils.EmailReceivedAfterFN
case events: case events:
timeFilter = "--event-starts-after" timeFilter = "--" + utils.EventStartsAfterFN
} }
cmd := tester.StubRootCmd( cmd := tester.StubRootCmd(
"restore", "exchange", "restore", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.cfgFP,
"--backup", string(suite.backupOps[set].Results.BackupID), "--"+utils.BackupFN, string(suite.backupOps[set].Results.BackupID),
timeFilter, "smarf") timeFilter, "smarf")
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)
@ -192,13 +193,13 @@ func (suite *RestoreExchangeIntegrationSuite) TestExchangeRestoreCmd_badBoolFlag
var timeFilter string var timeFilter string
switch set { switch set {
case events: case events:
timeFilter = "--event-recurs" timeFilter = "--" + utils.EventRecursFN
} }
cmd := tester.StubRootCmd( cmd := tester.StubRootCmd(
"restore", "exchange", "restore", "exchange",
"--config-file", suite.cfgFP, "--config-file", suite.cfgFP,
"--backup", string(suite.backupOps[set].Results.BackupID), "--"+utils.BackupFN, string(suite.backupOps[set].Results.BackupID),
timeFilter, "wingbat") timeFilter, "wingbat")
cli.BuildCommandTree(cmd) cli.BuildCommandTree(cmd)

View File

@ -42,43 +42,45 @@ func addOneDriveCommands(parent *cobra.Command) *cobra.Command {
// More generic (ex: --user) and more frequently used flags take precedence. // More generic (ex: --user) and more frequently used flags take precedence.
fs.SortFlags = false fs.SortFlags = false
fs.StringVar(&backupID, "backup", "", "ID of the backup to restore. (required)") fs.StringVar(&backupID,
cobra.CheckErr(c.MarkFlagRequired("backup")) utils.BackupFN, "",
"ID of the backup to restore. (required)")
cobra.CheckErr(c.MarkFlagRequired(utils.BackupFN))
fs.StringSliceVar(&user, fs.StringSliceVar(&user,
"user", nil, utils.UserFN, nil,
"Restore data by user ID; accepts '"+utils.Wildcard+"' to select all users.") "Restore data by user ID; accepts '"+utils.Wildcard+"' to select all users.")
// onedrive hierarchy (path/name) flags // onedrive hierarchy (path/name) flags
fs.StringSliceVar( fs.StringSliceVar(
&folderPaths, &folderPaths,
"folder", nil, utils.FolderFN, nil,
"Restore items by OneDrive folder; defaults to root") "Restore items by OneDrive folder; defaults to root")
fs.StringSliceVar( fs.StringSliceVar(
&fileNames, &fileNames,
"file", nil, utils.FileFN, nil,
"Restore items by file name or ID") "Restore items by file name or ID")
// onedrive info flags // onedrive info flags
fs.StringVar( fs.StringVar(
&fileCreatedAfter, &fileCreatedAfter,
"file-created-after", "", utils.FileCreatedAfterFN, "",
"Restore files created after this datetime") "Restore files created after this datetime")
fs.StringVar( fs.StringVar(
&fileCreatedBefore, &fileCreatedBefore,
"file-created-before", "", utils.FileCreatedBeforeFN, "",
"Restore files created before this datetime") "Restore files created before this datetime")
fs.StringVar( fs.StringVar(
&fileModifiedAfter, &fileModifiedAfter,
"file-modified-after", "", utils.FileModifiedAfterFN, "",
"Restore files modified after this datetime") "Restore files modified after this datetime")
fs.StringVar( fs.StringVar(
&fileModifiedBefore, &fileModifiedBefore,
"file-modified-before", "", utils.FileModifiedBeforeFN, "",
"Restore files modified before this datetime") "Restore files modified before this datetime")
// others // others
@ -124,13 +126,15 @@ func restoreOneDriveCmd(cmd *cobra.Command, args []string) error {
} }
opts := utils.OneDriveOpts{ opts := utils.OneDriveOpts{
Users: user, Users: user,
Paths: folderPaths, Paths: folderPaths,
Names: fileNames, Names: fileNames,
CreatedAfter: fileCreatedAfter, FileCreatedAfter: fileCreatedAfter,
CreatedBefore: fileCreatedBefore, FileCreatedBefore: fileCreatedBefore,
ModifiedAfter: fileModifiedAfter, FileModifiedAfter: fileModifiedAfter,
ModifiedBefore: fileModifiedBefore, FileModifiedBefore: fileModifiedBefore,
Populated: utils.GetPopulatedFlags(cmd),
} }
if err := utils.ValidateOneDriveRestoreFlags(backupID, opts); err != nil { if err := utils.ValidateOneDriveRestoreFlags(backupID, opts); err != nil {

View File

@ -6,13 +6,33 @@ import (
"github.com/alcionai/corso/src/pkg/selectors" "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 { type ExchangeOpts struct {
Contacts []string Contact []string
ContactFolders []string ContactFolder []string
Emails []string Email []string
EmailFolders []string EmailFolder []string
Events []string Event []string
EventCalendars []string EventCalendar []string
Users []string Users []string
ContactName string ContactName string
EmailReceivedAfter string EmailReceivedAfter string
@ -24,6 +44,8 @@ type ExchangeOpts struct {
EventStartsAfter string EventStartsAfter string
EventStartsBefore string EventStartsBefore string
EventSubject string EventSubject string
Populated PopulatedFlags
} }
// AddExchangeInclude adds the scope of the provided values to the selector's // AddExchangeInclude adds the scope of the provided values to the selector's
@ -77,23 +99,23 @@ func ValidateExchangeRestoreFlags(backupID string, opts ExchangeOpts) error {
return errors.New("a backup ID is required") return errors.New("a backup ID is required")
} }
if !IsValidTimeFormat(opts.EmailReceivedAfter) { if _, ok := opts.Populated[EmailReceivedAfterFN]; ok && !IsValidTimeFormat(opts.EmailReceivedAfter) {
return errors.New("invalid time format for email-received-after") return errors.New("invalid time format for email-received-after")
} }
if !IsValidTimeFormat(opts.EmailReceivedBefore) { if _, ok := opts.Populated[EmailReceivedBeforeFN]; ok && !IsValidTimeFormat(opts.EmailReceivedBefore) {
return errors.New("invalid time format for email-received-before") return errors.New("invalid time format for email-received-before")
} }
if !IsValidTimeFormat(opts.EventStartsAfter) { if _, ok := opts.Populated[EventStartsAfterFN]; ok && !IsValidTimeFormat(opts.EventStartsAfter) {
return errors.New("invalid time format for event-starts-after") return errors.New("invalid time format for event-starts-after")
} }
if !IsValidTimeFormat(opts.EventStartsBefore) { if _, ok := opts.Populated[EventStartsBeforeFN]; ok && !IsValidTimeFormat(opts.EventStartsBefore) {
return errors.New("invalid time format for event-starts-before") return errors.New("invalid time format for event-starts-before")
} }
if !IsValidBool(opts.EventRecurs) { if _, ok := opts.Populated[EventRecursFN]; ok && !IsValidBool(opts.EventRecurs) {
return errors.New("invalid format for event-recurs") return errors.New("invalid format for event-recurs")
} }
@ -106,9 +128,9 @@ func IncludeExchangeRestoreDataSelectors(
sel *selectors.ExchangeRestore, sel *selectors.ExchangeRestore,
opts ExchangeOpts, opts ExchangeOpts,
) { ) {
lc, lcf := len(opts.Contacts), len(opts.ContactFolders) lc, lcf := len(opts.Contact), len(opts.ContactFolder)
le, lef := len(opts.Emails), len(opts.EmailFolders) le, lef := len(opts.Email), len(opts.EmailFolder)
lev, lec := len(opts.Events), len(opts.EventCalendars) lev, lec := len(opts.Event), len(opts.EventCalendar)
// either scope the request to a set of users // either scope the request to a set of users
if lc+lcf+le+lef+lev+lec == 0 { if lc+lcf+le+lef+lev+lec == 0 {
if len(opts.Users) == 0 { if len(opts.Users) == 0 {
@ -121,9 +143,9 @@ func IncludeExchangeRestoreDataSelectors(
} }
// or add selectors for each type of data // or add selectors for each type of data
AddExchangeInclude(sel, opts.Users, opts.ContactFolders, opts.Contacts, sel.Contacts) AddExchangeInclude(sel, opts.Users, opts.ContactFolder, opts.Contact, sel.Contacts)
AddExchangeInclude(sel, opts.Users, opts.EmailFolders, opts.Emails, sel.Mails) AddExchangeInclude(sel, opts.Users, opts.EmailFolder, opts.Email, sel.Mails)
AddExchangeInclude(sel, opts.Users, opts.EventCalendars, opts.Events, sel.Events) AddExchangeInclude(sel, opts.Users, opts.EventCalendar, opts.Event, sel.Events)
} }
// FilterExchangeRestoreInfoSelectors builds the common info-selector filters. // FilterExchangeRestoreInfoSelectors builds the common info-selector filters.

View File

@ -94,207 +94,207 @@ func (suite *ExchangeUtilsSuite) TestIncludeExchangeBackupDetailDataSelectors()
{ {
name: "any users, any data", name: "any users, any data",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Contacts: a, Contact: a,
ContactFolders: a, ContactFolder: a,
Emails: a, Email: a,
EmailFolders: a, EmailFolder: a,
Events: a, Event: a,
EventCalendars: a, EventCalendar: a,
Users: a, Users: a,
}, },
expectIncludeLen: 3, expectIncludeLen: 3,
}, },
{ {
name: "any users, any folders", name: "any users, any folders",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
ContactFolders: a, ContactFolder: a,
EmailFolders: a, EmailFolder: a,
EventCalendars: a, EventCalendar: a,
Users: a, Users: a,
}, },
expectIncludeLen: 3, expectIncludeLen: 3,
}, },
{ {
name: "single user, single of each data", name: "single user, single of each data",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Contacts: stub, Contact: stub,
ContactFolders: stub, ContactFolder: stub,
Emails: stub, Email: stub,
EmailFolders: stub, EmailFolder: stub,
Events: stub, Event: stub,
EventCalendars: stub, EventCalendar: stub,
Users: stub, Users: stub,
}, },
expectIncludeLen: 3, expectIncludeLen: 3,
}, },
{ {
name: "single user, single of each folder", name: "single user, single of each folder",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
ContactFolders: stub, ContactFolder: stub,
EmailFolders: stub, EmailFolder: stub,
EventCalendars: stub, EventCalendar: stub,
Users: stub, Users: stub,
}, },
expectIncludeLen: 3, expectIncludeLen: 3,
}, },
{ {
name: "any users, contacts", name: "any users, contacts",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Contacts: a, Contact: a,
ContactFolders: stub, ContactFolder: stub,
Users: a, Users: a,
}, },
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "single user, contacts", name: "single user, contacts",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Contacts: stub, Contact: stub,
ContactFolders: stub, ContactFolder: stub,
Users: stub, Users: stub,
}, },
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "any users, emails", name: "any users, emails",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Emails: a, Email: a,
EmailFolders: stub, EmailFolder: stub,
Users: a, Users: a,
}, },
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "single user, emails", name: "single user, emails",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Emails: stub, Email: stub,
EmailFolders: stub, EmailFolder: stub,
Users: stub, Users: stub,
}, },
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "any users, events", name: "any users, events",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Events: a, Event: a,
EventCalendars: a, EventCalendar: a,
Users: a, Users: a,
}, },
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "single user, events", name: "single user, events",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Events: stub, Event: stub,
EventCalendars: stub, EventCalendar: stub,
Users: stub, Users: stub,
}, },
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "any users, contacts + email", name: "any users, contacts + email",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Contacts: a, Contact: a,
ContactFolders: a, ContactFolder: a,
Emails: a, Email: a,
EmailFolders: a, EmailFolder: a,
Users: a, Users: a,
}, },
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "single users, contacts + email", name: "single users, contacts + email",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Contacts: stub, Contact: stub,
ContactFolders: stub, ContactFolder: stub,
Emails: stub, Email: stub,
EmailFolders: stub, EmailFolder: stub,
Users: stub, Users: stub,
}, },
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "any users, email + event", name: "any users, email + event",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Emails: a, Email: a,
EmailFolders: a, EmailFolder: a,
Events: a, Event: a,
EventCalendars: a, EventCalendar: a,
Users: a, Users: a,
}, },
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "single users, email + event", name: "single users, email + event",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Emails: stub, Email: stub,
EmailFolders: stub, EmailFolder: stub,
Events: stub, Event: stub,
EventCalendars: stub, EventCalendar: stub,
Users: stub, Users: stub,
}, },
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "any users, event + contact", name: "any users, event + contact",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Contacts: a, Contact: a,
ContactFolders: a, ContactFolder: a,
Events: a, Event: a,
EventCalendars: a, EventCalendar: a,
Users: a, Users: a,
}, },
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "single users, event + contact", name: "single users, event + contact",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Contacts: stub, Contact: stub,
ContactFolders: stub, ContactFolder: stub,
Events: stub, Event: stub,
EventCalendars: stub, EventCalendar: stub,
Users: stub, Users: stub,
}, },
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "many users, events", name: "many users, events",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Events: many, Event: many,
EventCalendars: many, EventCalendar: many,
Users: many, Users: many,
}, },
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "many users, events + contacts", name: "many users, events + contacts",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Contacts: many, Contact: many,
ContactFolders: many, ContactFolder: many,
Events: many, Event: many,
EventCalendars: many, EventCalendar: many,
Users: many, Users: many,
}, },
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "mail, no folder or user", name: "mail, no folder or user",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Emails: stub, Email: stub,
}, },
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "contacts, no folder or user", name: "contacts, no folder or user",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Contacts: stub, Contact: stub,
}, },
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "event, no folder or user", name: "event, no folder or user",
opts: utils.ExchangeOpts{ opts: utils.ExchangeOpts{
Events: stub, Event: stub,
}, },
expectIncludeLen: 1, expectIncludeLen: 1,
}, },

52
src/cli/utils/flags.go Normal file
View File

@ -0,0 +1,52 @@
package utils
import (
"strconv"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/alcionai/corso/src/internal/common"
)
type PopulatedFlags map[string]struct{}
func (fs PopulatedFlags) populate(pf *pflag.Flag) {
if pf == nil {
return
}
if pf.Changed {
fs[pf.Name] = struct{}{}
}
}
// GetPopulatedFlags returns a map of flags that have been
// populated by the user. Entry keys match the flag's long
// name. Values are empty.
func GetPopulatedFlags(cmd *cobra.Command) PopulatedFlags {
pop := PopulatedFlags{}
fs := cmd.Flags()
if fs == nil {
return pop
}
fs.VisitAll(pop.populate)
return pop
}
// IsValidTimeFormat returns true if the input is recognized as a
// supported format by the common time parser.
func IsValidTimeFormat(in string) bool {
_, err := common.ParseTime(in)
return err == nil
}
// IsValidTimeFormat returns true if the input is recognized as a
// boolean.
func IsValidBool(in string) bool {
_, err := strconv.ParseBool(in)
return err == nil
}

View File

@ -6,14 +6,27 @@ import (
"github.com/alcionai/corso/src/pkg/selectors" "github.com/alcionai/corso/src/pkg/selectors"
) )
const (
FileFN = "file"
FolderFN = "folders"
NamesFN = "names"
PathsFN = "paths"
FileCreatedAfterFN = "file-created-after"
FileCreatedBeforeFN = "file-created-before"
FileModifiedAfterFN = "file-modified-after"
FileModifiedBeforeFN = "file-modified-before"
)
type OneDriveOpts struct { type OneDriveOpts struct {
Users []string Users []string
Names []string Names []string
Paths []string Paths []string
CreatedAfter string FileCreatedAfter string
CreatedBefore string FileCreatedBefore string
ModifiedAfter string FileModifiedAfter string
ModifiedBefore string FileModifiedBefore string
Populated PopulatedFlags
} }
// ValidateOneDriveRestoreFlags checks common flags for correctness and interdependencies // ValidateOneDriveRestoreFlags checks common flags for correctness and interdependencies
@ -22,19 +35,19 @@ func ValidateOneDriveRestoreFlags(backupID string, opts OneDriveOpts) error {
return errors.New("a backup ID is required") return errors.New("a backup ID is required")
} }
if !IsValidTimeFormat(opts.CreatedAfter) { if _, ok := opts.Populated[FileCreatedAfterFN]; ok && !IsValidTimeFormat(opts.FileCreatedAfter) {
return errors.New("invalid time format for created-after") return errors.New("invalid time format for created-after")
} }
if !IsValidTimeFormat(opts.CreatedBefore) { if _, ok := opts.Populated[FileCreatedBeforeFN]; ok && !IsValidTimeFormat(opts.FileCreatedBefore) {
return errors.New("invalid time format for created-before") return errors.New("invalid time format for created-before")
} }
if !IsValidTimeFormat(opts.ModifiedAfter) { if _, ok := opts.Populated[FileModifiedAfterFN]; ok && !IsValidTimeFormat(opts.FileModifiedAfter) {
return errors.New("invalid time format for modified-after") return errors.New("invalid time format for modified-after")
} }
if !IsValidTimeFormat(opts.ModifiedBefore) { if _, ok := opts.Populated[FileModifiedAfterFN]; ok && !IsValidTimeFormat(opts.FileModifiedBefore) {
return errors.New("invalid time format for modified-before") return errors.New("invalid time format for modified-before")
} }
@ -90,8 +103,8 @@ func FilterOneDriveRestoreInfoSelectors(
sel *selectors.OneDriveRestore, sel *selectors.OneDriveRestore,
opts OneDriveOpts, opts OneDriveOpts,
) { ) {
AddOneDriveFilter(sel, opts.CreatedAfter, sel.CreatedAfter) AddOneDriveFilter(sel, opts.FileCreatedAfter, sel.CreatedAfter)
AddOneDriveFilter(sel, opts.CreatedBefore, sel.CreatedBefore) AddOneDriveFilter(sel, opts.FileCreatedBefore, sel.CreatedBefore)
AddOneDriveFilter(sel, opts.ModifiedAfter, sel.ModifiedAfter) AddOneDriveFilter(sel, opts.FileModifiedAfter, sel.ModifiedAfter)
AddOneDriveFilter(sel, opts.ModifiedBefore, sel.ModifiedBefore) AddOneDriveFilter(sel, opts.FileModifiedBefore, sel.ModifiedBefore)
} }

View File

@ -31,30 +31,90 @@ var (
Name: "BadEmailReceiveAfter", Name: "BadEmailReceiveAfter",
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
EmailReceivedAfter: "foo", EmailReceivedAfter: "foo",
Populated: utils.PopulatedFlags{
utils.EmailReceivedAfterFN: struct{}{},
},
},
},
{
Name: "EmptyEmailReceiveAfter",
Opts: utils.ExchangeOpts{
EmailReceivedAfter: "",
Populated: utils.PopulatedFlags{
utils.EmailReceivedAfterFN: struct{}{},
},
}, },
}, },
{ {
Name: "BadEmailReceiveBefore", Name: "BadEmailReceiveBefore",
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
EmailReceivedBefore: "foo", EmailReceivedBefore: "foo",
Populated: utils.PopulatedFlags{
utils.EmailReceivedBeforeFN: struct{}{},
},
},
},
{
Name: "EmptyEmailReceiveBefore",
Opts: utils.ExchangeOpts{
EmailReceivedBefore: "",
Populated: utils.PopulatedFlags{
utils.EmailReceivedBeforeFN: struct{}{},
},
}, },
}, },
{ {
Name: "BadEventRecurs", Name: "BadEventRecurs",
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
EventRecurs: "foo", EventRecurs: "foo",
Populated: utils.PopulatedFlags{
utils.EventRecursFN: struct{}{},
},
},
},
{
Name: "EmptyEventRecurs",
Opts: utils.ExchangeOpts{
EventRecurs: "",
Populated: utils.PopulatedFlags{
utils.EventRecursFN: struct{}{},
},
}, },
}, },
{ {
Name: "BadEventStartsAfter", Name: "BadEventStartsAfter",
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
EventStartsAfter: "foo", EventStartsAfter: "foo",
Populated: utils.PopulatedFlags{
utils.EventStartsAfterFN: struct{}{},
},
},
},
{
Name: "EmptyEventStartsAfter",
Opts: utils.ExchangeOpts{
EventStartsAfter: "",
Populated: utils.PopulatedFlags{
utils.EventStartsAfterFN: struct{}{},
},
}, },
}, },
{ {
Name: "BadEventStartsBefore", Name: "BadEventStartsBefore",
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
EventStartsBefore: "foo", EventStartsBefore: "foo",
Populated: utils.PopulatedFlags{
utils.EventStartsBeforeFN: struct{}{},
},
},
},
{
Name: "EmptyEventStartsBefore",
Opts: utils.ExchangeOpts{
EventStartsBefore: "",
Populated: utils.PopulatedFlags{
utils.EventStartsBeforeFN: struct{}{},
},
}, },
}, },
} }
@ -68,14 +128,14 @@ var (
Name: "Emails", Name: "Emails",
Expected: testdata.ExchangeEmailItems, Expected: testdata.ExchangeEmailItems,
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
Emails: selectors.Any(), Email: selectors.Any(),
}, },
}, },
{ {
Name: "EmailsFolderPrefixMatch", Name: "EmailsFolderPrefixMatch",
Expected: testdata.ExchangeEmailItems, Expected: testdata.ExchangeEmailItems,
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
EmailFolders: []string{testdata.ExchangeEmailInboxPath.Folder()}, EmailFolder: []string{testdata.ExchangeEmailInboxPath.Folder()},
}, },
}, },
{ {
@ -109,21 +169,21 @@ var (
Name: "MailID", Name: "MailID",
Expected: []details.DetailsEntry{testdata.ExchangeEmailItems[0]}, Expected: []details.DetailsEntry{testdata.ExchangeEmailItems[0]},
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
Emails: []string{testdata.ExchangeEmailItemPath1.Item()}, Email: []string{testdata.ExchangeEmailItemPath1.Item()},
}, },
}, },
{ {
Name: "MailShortRef", Name: "MailShortRef",
Expected: []details.DetailsEntry{testdata.ExchangeEmailItems[0]}, Expected: []details.DetailsEntry{testdata.ExchangeEmailItems[0]},
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
Emails: []string{testdata.ExchangeEmailItemPath1.ShortRef()}, Email: []string{testdata.ExchangeEmailItemPath1.ShortRef()},
}, },
}, },
{ {
Name: "MultipleMailShortRef", Name: "MultipleMailShortRef",
Expected: testdata.ExchangeEmailItems, Expected: testdata.ExchangeEmailItems,
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
Emails: []string{ Email: []string{
testdata.ExchangeEmailItemPath1.ShortRef(), testdata.ExchangeEmailItemPath1.ShortRef(),
testdata.ExchangeEmailItemPath2.ShortRef(), testdata.ExchangeEmailItemPath2.ShortRef(),
}, },
@ -134,7 +194,7 @@ var (
Expected: []details.DetailsEntry{testdata.ExchangeEmailItems[0]}, Expected: []details.DetailsEntry{testdata.ExchangeEmailItems[0]},
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
EmailSubject: "foo", EmailSubject: "foo",
Events: selectors.Any(), Event: selectors.Any(),
}, },
}, },
{ {
@ -152,8 +212,8 @@ var (
testdata.ExchangeEventsItems[0], testdata.ExchangeEventsItems[0],
}, },
Opts: utils.ExchangeOpts{ Opts: utils.ExchangeOpts{
Emails: []string{testdata.ExchangeEmailItemPath1.ShortRef()}, Email: []string{testdata.ExchangeEmailItemPath1.ShortRef()},
Events: []string{testdata.ExchangeEventsItemPath1.ShortRef()}, Event: []string{testdata.ExchangeEventsItemPath1.ShortRef()},
}, },
}, },
} }

View File

@ -4,15 +4,20 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"strconv"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/alcionai/corso/src/internal/common"
"github.com/alcionai/corso/src/pkg/repository" "github.com/alcionai/corso/src/pkg/repository"
) )
// common flag names
const (
BackupFN = "backup"
DataFN = "data"
UserFN = "user"
)
const ( const (
Wildcard = "*" Wildcard = "*"
) )
@ -59,30 +64,3 @@ func AddCommand(parent, c *cobra.Command) (*cobra.Command, *pflag.FlagSet) {
return c, c.Flags() return c, c.Flags()
} }
// IsValidTimeFormat returns true if the input is regonized as a
// supported format by the common time parser. Returns true if
// the input is zero valued, which indicates that the flag was not
// called.
func IsValidTimeFormat(in string) bool {
if len(in) == 0 {
return true
}
_, err := common.ParseTime(in)
return err == nil
}
// IsValidTimeFormat returns true if the input is regonized as a
// boolean. Returns true if the input is zero valued, which
// indicates that the flag was not called.
func IsValidBool(in string) bool {
if len(in) == 0 {
return true
}
_, err := strconv.ParseBool(in)
return err == nil
}