diff --git a/src/cli/backup/backup.go b/src/cli/backup/backup.go index cfa4a0b61..30819eed1 100644 --- a/src/cli/backup/backup.go +++ b/src/cli/backup/backup.go @@ -39,6 +39,7 @@ var serviceCommands = []func(cmd *cobra.Command) *cobra.Command{ addExchangeCommands, addOneDriveCommands, addSharePointCommands, + addTeamsCommands, } // AddCommands attaches all `corso backup * *` commands to the parent. diff --git a/src/cli/backup/teams.go b/src/cli/backup/teams.go new file mode 100644 index 000000000..3a2289fe1 --- /dev/null +++ b/src/cli/backup/teams.go @@ -0,0 +1,190 @@ +package backup + +import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/alcionai/corso/src/cli/flags" + "github.com/alcionai/corso/src/cli/utils" + "github.com/alcionai/corso/src/pkg/path" +) + +// ------------------------------------------------------------------------------------------------ +// setup and globals +// ------------------------------------------------------------------------------------------------ + +const ( + teamsServiceCommand = "team" + teamsServiceCommandCreateUseSuffix = "--team | '" + flags.Wildcard + "'" + teamsServiceCommandDeleteUseSuffix = "--backup " + teamsServiceCommandDetailsUseSuffix = "--backup " +) + +const ( + teamsServiceCommandCreateExamples = `# Backup all Teams data for Alice +corso backup create team --team alice@example.com + +# Backup only Teams contacts for Alice and Bob +corso backup create team --team testTeams1,testTeams2 --data contacts + +# Backup all Teams data for all M365 users +corso backup create team --team '*'` + + teamsServiceCommandDeleteExamples = `# Delete Teams backup with ID 1234abcd-12ab-cd34-56de-1234abcd +corso backup delete team --backup 1234abcd-12ab-cd34-56de-1234abcd` + + teamsServiceCommandDetailsExamples = `# Explore items in Alice's latest backup (1234abcd...) +corso backup details team --backup 1234abcd-12ab-cd34-56de-1234abcd + +# Explore calendar events occurring after start of 2022 +corso backup details teams --backup 1234abcd-12ab-cd34-56de-1234abcd \ + --event-starts-after 2022-01-01T00:00:00 + +# Explore contacts named Andy +corso backup details teams --backup 1234abcd-12ab-cd34-56de-1234abcd \ + --contact-name Andy` +) + +// called by backup.go to map subcommands to provider-specific handling. +func addTeamsCommands(cmd *cobra.Command) *cobra.Command { + var ( + c *cobra.Command + fs *pflag.FlagSet + ) + + switch cmd.Use { + case createCommand: + c, fs = utils.AddCommand(cmd, teamsCreateCmd()) + fs.SortFlags = false + + c.Use = c.Use + " " + teamsServiceCommandCreateUseSuffix + c.Example = teamsServiceCommandCreateExamples + + // Flags addition ordering should follow the order we want them to appear in help and docs: + // TODO Neha: add teams flag + flags.AddDataFlag(c, []string{dataEmail, dataContacts, dataEvents}, false) + flags.AddCorsoPassphaseFlags(c) + flags.AddAWSCredsFlags(c) + flags.AddAzureCredsFlags(c) + flags.AddFetchParallelismFlag(c) + flags.AddFailFastFlag(c) + + case listCommand: + c, fs = utils.AddCommand(cmd, teamsListCmd()) + fs.SortFlags = false + + flags.AddBackupIDFlag(c, false) + flags.AddCorsoPassphaseFlags(c) + flags.AddAWSCredsFlags(c) + flags.AddAzureCredsFlags(c) + addFailedItemsFN(c) + addSkippedItemsFN(c) + addRecoveredErrorsFN(c) + + case detailsCommand: + c, fs = utils.AddCommand(cmd, teamsDetailsCmd()) + fs.SortFlags = false + + c.Use = c.Use + " " + teamsServiceCommandDetailsUseSuffix + c.Example = teamsServiceCommandDetailsExamples + + flags.AddSkipReduceFlag(c) + + // 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. + flags.AddBackupIDFlag(c, true) + flags.AddCorsoPassphaseFlags(c) + flags.AddAWSCredsFlags(c) + flags.AddAzureCredsFlags(c) + + case deleteCommand: + c, fs = utils.AddCommand(cmd, teamsDeleteCmd()) + fs.SortFlags = false + + c.Use = c.Use + " " + teamsServiceCommandDeleteUseSuffix + c.Example = teamsServiceCommandDeleteExamples + + flags.AddBackupIDFlag(c, true) + flags.AddCorsoPassphaseFlags(c) + flags.AddAWSCredsFlags(c) + flags.AddAzureCredsFlags(c) + } + + return c +} + +// ------------------------------------------------------------------------------------------------ +// backup create +// ------------------------------------------------------------------------------------------------ + +// `corso backup create team [...]` +func teamsCreateCmd() *cobra.Command { + return &cobra.Command{ + Use: teamsServiceCommand, + Short: "Backup M365 Team service data", + RunE: createTeamsCmd, + Args: cobra.NoArgs, + } +} + +// processes an team service backup. +func createTeamsCmd(cmd *cobra.Command, args []string) error { + return cmd.Help() +} + +// ------------------------------------------------------------------------------------------------ +// backup list +// ------------------------------------------------------------------------------------------------ + +// `corso backup list team [...]` +func teamsListCmd() *cobra.Command { + return &cobra.Command{ + Use: teamsServiceCommand, + Short: "List the history of M365 Teams service backups", + RunE: listTeamsCmd, + Args: cobra.NoArgs, + } +} + +// lists the history of backup operations +func listTeamsCmd(cmd *cobra.Command, args []string) error { + return genericListCommand(cmd, flags.BackupIDFV, path.TeamsService, args) +} + +// ------------------------------------------------------------------------------------------------ +// backup details +// ------------------------------------------------------------------------------------------------ + +// `corso backup details team [...]` +func teamsDetailsCmd() *cobra.Command { + return &cobra.Command{ + Use: teamsServiceCommand, + Short: "Shows the details of a M365 Teams service backup", + RunE: detailsTeamsCmd, + Args: cobra.NoArgs, + } +} + +// processes an team service backup. +func detailsTeamsCmd(cmd *cobra.Command, args []string) error { + return cmd.Help() +} + +// ------------------------------------------------------------------------------------------------ +// backup delete +// ------------------------------------------------------------------------------------------------ + +// `corso backup delete team [...]` +func teamsDeleteCmd() *cobra.Command { + return &cobra.Command{ + Use: teamsServiceCommand, + Short: "Delete backed-up M365 Teams service data", + RunE: deleteTeamsCmd, + Args: cobra.NoArgs, + } +} + +// deletes an teams service backup. +func deleteTeamsCmd(cmd *cobra.Command, args []string) error { + return genericDeleteCommand(cmd, path.TeamsService, flags.BackupIDFV, "Teams", args) +} diff --git a/src/cli/backup/teams_test.go b/src/cli/backup/teams_test.go new file mode 100644 index 000000000..966830f82 --- /dev/null +++ b/src/cli/backup/teams_test.go @@ -0,0 +1,98 @@ +package backup + +import ( + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/cli/flags" + "github.com/alcionai/corso/src/internal/tester" +) + +type TeamsUnitSuite struct { + tester.Suite +} + +func TestTeamsUnitSuite(t *testing.T) { + suite.Run(t, &TeamsUnitSuite{Suite: tester.NewUnitSuite(t)}) +} + +func (suite *TeamsUnitSuite) TestAddTeamsCommands() { + expectUse := teamsServiceCommand + + table := []struct { + name string + use string + expectUse string + expectShort string + flags []string + expectRunE func(*cobra.Command, []string) error + }{ + { + "create teams", + createCommand, + expectUse + " " + teamsServiceCommandCreateUseSuffix, + teamsCreateCmd().Short, + []string{ + flags.CategoryDataFN, + flags.FailFastFN, + flags.FetchParallelismFN, + flags.SkipReduceFN, + flags.NoStatsFN, + }, + createTeamsCmd, + }, + { + "list teams", + listCommand, + expectUse, + teamsListCmd().Short, + []string{ + flags.BackupFN, + flags.FailedItemsFN, + flags.SkippedItemsFN, + flags.RecoveredErrorsFN, + }, + listTeamsCmd, + }, + { + "details teams", + detailsCommand, + expectUse + " " + teamsServiceCommandDetailsUseSuffix, + teamsDetailsCmd().Short, + []string{ + flags.BackupFN, + }, + detailsTeamsCmd, + }, + { + "delete teams", + deleteCommand, + expectUse + " " + teamsServiceCommandDeleteUseSuffix, + teamsDeleteCmd().Short, + []string{flags.BackupFN}, + deleteTeamsCmd, + }, + } + for _, test := range table { + suite.Run(test.name, func() { + t := suite.T() + + cmd := &cobra.Command{Use: test.use} + + c := addTeamsCommands(cmd) + require.NotNil(t, c) + + cmds := cmd.Commands() + require.Len(t, cmds, 1) + + child := cmds[0] + assert.Equal(t, test.expectUse, child.Use) + assert.Equal(t, test.expectShort, child.Short) + tester.AreSameFunc(t, test.expectRunE, child.RunE) + }) + } +} diff --git a/src/internal/operations/opstatus_string.go b/src/internal/operations/opstatus_string.go index 0468287b4..776120fb2 100644 --- a/src/internal/operations/opstatus_string.go +++ b/src/internal/operations/opstatus_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=opStatus -linecomment"; DO NOT EDIT. +// Code generated by "stringer -type=OpStatus -linecomment"; DO NOT EDIT. package operations @@ -15,13 +15,13 @@ func _() { _ = x[NoData-4] } -const _opStatus_name = "Status UnknownIn ProgressCompletedFailedNo Data" +const _OpStatus_name = "Status UnknownIn ProgressCompletedFailedNo Data" -var _opStatus_index = [...]uint8{0, 14, 25, 34, 40, 47} +var _OpStatus_index = [...]uint8{0, 14, 25, 34, 40, 47} func (i OpStatus) String() string { - if i < 0 || i >= OpStatus(len(_opStatus_index)-1) { - return "opStatus(" + strconv.FormatInt(int64(i), 10) + ")" + if i < 0 || i >= OpStatus(len(_OpStatus_index)-1) { + return "OpStatus(" + strconv.FormatInt(int64(i), 10) + ")" } - return _opStatus_name[_opStatus_index[i]:_opStatus_index[i+1]] + return _OpStatus_name[_OpStatus_index[i]:_OpStatus_index[i+1]] } diff --git a/src/pkg/path/resource_path.go b/src/pkg/path/resource_path.go index 923d66453..f2e0f9f4b 100644 --- a/src/pkg/path/resource_path.go +++ b/src/pkg/path/resource_path.go @@ -27,9 +27,11 @@ const ( ExchangeService // exchange OneDriveService // onedrive SharePointService // sharepoint + TeamsService // teams ExchangeMetadataService // exchangeMetadata OneDriveMetadataService // onedriveMetadata SharePointMetadataService // sharepointMetadata + TeamsMetadataService // teamsMetadata ) func toServiceType(service string) ServiceType { diff --git a/src/pkg/path/servicetype_string.go b/src/pkg/path/servicetype_string.go index 6d6b960d8..6267dd011 100644 --- a/src/pkg/path/servicetype_string.go +++ b/src/pkg/path/servicetype_string.go @@ -12,14 +12,16 @@ func _() { _ = x[ExchangeService-1] _ = x[OneDriveService-2] _ = x[SharePointService-3] - _ = x[ExchangeMetadataService-4] - _ = x[OneDriveMetadataService-5] - _ = x[SharePointMetadataService-6] + _ = x[TeamsService-4] + _ = x[ExchangeMetadataService-5] + _ = x[OneDriveMetadataService-6] + _ = x[SharePointMetadataService-7] + _ = x[TeamsMetadataService-8] } -const _ServiceType_name = "UnknownServiceexchangeonedrivesharepointexchangeMetadataonedriveMetadatasharepointMetadata" +const _ServiceType_name = "UnknownServiceexchangeonedrivesharepointteamsexchangeMetadataonedriveMetadatasharepointMetadatateamsMetadata" -var _ServiceType_index = [...]uint8{0, 14, 22, 30, 40, 56, 72, 90} +var _ServiceType_index = [...]uint8{0, 14, 22, 30, 40, 45, 61, 77, 95, 108} func (i ServiceType) String() string { if i < 0 || i >= ServiceType(len(_ServiceType_index)-1) {