diff --git a/server/cmd/mmctl/commands/compliance_export.go b/server/cmd/mmctl/commands/compliance_export.go index 301714ad400..01b53496597 100644 --- a/server/cmd/mmctl/commands/compliance_export.go +++ b/server/cmd/mmctl/commands/compliance_export.go @@ -12,7 +12,7 @@ import ( ) var ComplianceExportCmd = &cobra.Command{ - Use: "compliance_export", + Use: "compliance-export", Short: "Management of compliance exports", } @@ -26,12 +26,20 @@ var ComplianceExportListCmd = &cobra.Command{ var ComplianceExportShowCmd = &cobra.Command{ Use: "show [complianceExportJobID]", - Example: " compliance_export show o98rj3ur83dp5dppfyk5yk6osy", + Example: "compliance-export show o98rj3ur83dp5dppfyk5yk6osy", Short: "Show compliance export job", Args: cobra.ExactArgs(1), RunE: withClient(complianceExportShowCmdF), } +var ComplianceExportCancelCmd = &cobra.Command{ + Use: "cancel [complianceExportJobID]", + Example: "compliance-export cancel o98rj3ur83dp5dppfyk5yk6osy", + Short: "Cancel compliance export job", + Args: cobra.ExactArgs(1), + RunE: withClient(complianceExportCancelCmdF), +} + func init() { ComplianceExportListCmd.Flags().Int("page", 0, "Page number to fetch for the list of compliance export jobs") ComplianceExportListCmd.Flags().Int("per-page", DefaultPageSize, "Number of compliance export jobs to be fetched") @@ -40,6 +48,7 @@ func init() { ComplianceExportCmd.AddCommand( ComplianceExportListCmd, ComplianceExportShowCmd, + ComplianceExportCancelCmd, ) RootCmd.AddCommand(ComplianceExportCmd) } @@ -58,3 +67,11 @@ func complianceExportShowCmdF(c client.Client, command *cobra.Command, args []st return nil } + +func complianceExportCancelCmdF(c client.Client, command *cobra.Command, args []string) error { + if _, err := c.CancelJob(context.TODO(), args[0]); err != nil { + return fmt.Errorf("failed to cancel compliance export job: %w", err) + } + + return nil +} diff --git a/server/cmd/mmctl/commands/compliance_export_e2e_test.go b/server/cmd/mmctl/commands/compliance_export_e2e_test.go index 7e9b15cd91e..fa971a782f3 100644 --- a/server/cmd/mmctl/commands/compliance_export_e2e_test.go +++ b/server/cmd/mmctl/commands/compliance_export_e2e_test.go @@ -198,3 +198,106 @@ func (s *MmctlE2ETestSuite) TestComplianceExportShowCmdE2E() { s.Require().Equal(job.Id, printer.GetLines()[0].(*model.Job).Id) }) } + +func (s *MmctlE2ETestSuite) TestComplianceExportCancelCmdE2E() { + s.SetupMessageExportTestHelper() + + s.Run("no permissions", func() { + printer.Clean() + + now := model.GetMillis() + // Create a job + job, _, err := s.th.SystemAdminClient.CreateJob(context.Background(), &model.Job{ + Id: st.NewTestID(), + CreateAt: now - 1000, + Status: model.JobStatusInProgress, + Type: model.JobTypeMessageExport, + StartAt: now - 1000, + LastActivityAt: now - 1000, + }) + s.Require().NoError(err) + defer func() { + // Ensure job is deleted from the database + var result string + result, err = s.th.App.Srv().Store().Job().Delete(job.Id) + s.Require().NoError(err, "Failed to delete job (result: %v)", result) + }() + + cmd := makeCmd() + err = complianceExportCancelCmdF(s.th.Client, cmd, []string{job.Id}) + s.Require().EqualError(err, "failed to get compliance export job: You do not have the appropriate permissions.") + s.Require().Empty(printer.GetLines()) + s.Require().Empty(printer.GetErrorLines()) + }) + + s.RunForSystemAdminAndLocal("Cancel non-existent job", func(c client.Client) { + printer.Clean() + + cmd := makeCmd() + err := complianceExportCancelCmdF(c, cmd, []string{"non-existent-job-id"}) + s.Require().EqualError(err, "failed to get compliance export job: Sorry, we could not find the page., There doesn't appear to be an api call for the url='/api/v4/jobs/non-existent-job-id'. Typo? are you missing a team_id or user_id as part of the url?") + s.Require().Empty(printer.GetLines()) + s.Require().Empty(printer.GetErrorLines()) + }) + + s.RunForSystemAdminAndLocal("Cancel existing job", func(c client.Client) { + now := model.GetMillis() + // Create a job + job, _, err := s.th.SystemAdminClient.CreateJob(context.Background(), &model.Job{ + Id: st.NewTestID(), + CreateAt: now - 1000, + Status: model.JobStatusInProgress, + Type: model.JobTypeMessageExport, + StartAt: now - 1000, + LastActivityAt: now - 1000, + }) + s.Require().NoError(err) + defer func() { + // Ensure job is deleted from the database + var result string + result, err = s.th.App.Srv().Store().Job().Delete(job.Id) + s.Require().NoError(err, "Failed to delete job (result: %v)", result) + }() + + printer.Clean() + cmd := makeCmd() + err = complianceExportCancelCmdF(c, cmd, []string{job.Id}) + s.Require().NoError(err) + s.Require().Empty(printer.GetLines()) + s.Require().Empty(printer.GetErrorLines()) + + // Verify job was cancelled + job, _, err = s.th.SystemAdminClient.GetJob(context.Background(), job.Id) + s.Require().NoError(err) + s.Require().Equal(model.JobStatusCanceled, job.Status) + }) + + s.RunForSystemAdminAndLocal("Error cancelling job in non-cancellable state", func(c client.Client) { + now := model.GetMillis() + // Create a job + job, _, err := s.th.SystemAdminClient.CreateJob(context.Background(), &model.Job{ + Id: st.NewTestID(), + CreateAt: now - 1000, + Status: model.JobStatusInProgress, + Type: model.JobTypeMessageExport, + StartAt: now - 1000, + LastActivityAt: now - 1000, + }) + s.Require().NoError(err) + _, err = s.th.SystemAdminClient.UpdateJobStatus(context.Background(), job.Id, model.JobStatusCanceled, true) + s.Require().NoError(err) + defer func() { + // Ensure job is deleted from the database + var result string + result, err = s.th.App.Srv().Store().Job().Delete(job.Id) + s.Require().NoError(err, "Failed to delete job (result: %v)", result) + }() + + printer.Clean() + cmd := makeCmd() + err = complianceExportCancelCmdF(c, cmd, []string{job.Id}) + s.Require().EqualError(err, "failed to cancel compliance export job: Could not request cancellation for job that is not in a cancelable state.") + s.Require().Empty(printer.GetLines()) + s.Require().Empty(printer.GetErrorLines()) + }) +} diff --git a/server/cmd/mmctl/commands/compliance_export_test.go b/server/cmd/mmctl/commands/compliance_export_test.go index 7a0e744a523..acf0540cd0e 100644 --- a/server/cmd/mmctl/commands/compliance_export_test.go +++ b/server/cmd/mmctl/commands/compliance_export_test.go @@ -13,6 +13,7 @@ import ( func (s *MmctlUnitTestSuite) TestComplianceExportListCmdF() { s.Run("list default pagination", func() { + s.SetupTest() // Reset mocks before test printer.Clean() var mockJobs []*model.Job @@ -115,6 +116,7 @@ func (s *MmctlUnitTestSuite) TestComplianceExportListCmdF() { func (s *MmctlUnitTestSuite) TestComplianceExportShowCmdF() { s.Run("show job successfully", func() { + s.SetupTest() // Reset mocks before test printer.Clean() mockJob := &model.Job{ Id: model.NewId(), @@ -137,6 +139,7 @@ func (s *MmctlUnitTestSuite) TestComplianceExportShowCmdF() { }) s.Run("show job with error", func() { + s.SetupTest() // Reset mocks before test printer.Clean() mockError := &model.AppError{ Message: "failed to get job", @@ -157,6 +160,70 @@ func (s *MmctlUnitTestSuite) TestComplianceExportShowCmdF() { }) } +func (s *MmctlUnitTestSuite) TestComplianceExportCancelCmdF() { + s.Run("cancel job successfully", func() { + s.SetupTest() // Reset mocks before test + printer.Clean() + id := model.NewId() + + s.client. + EXPECT(). + CancelJob(context.TODO(), id). + Return(&model.Response{}, nil). + Times(1) + + cmd := makeCmd() + err := complianceExportCancelCmdF(s.client, cmd, []string{id}) + s.Require().Nil(err) + s.Len(printer.GetLines(), 0) + s.Len(printer.GetErrorLines(), 0) + }) + + s.Run("cancel job with get error", func() { + s.SetupTest() // Reset mocks before test + printer.Clean() + mockError := &model.AppError{ + Message: "failed to get job", + } + + s.client. + EXPECT(). + CancelJob(context.TODO(), "invalid-job-id"). + Return(&model.Response{}, mockError). + Times(1) + + cmd := makeCmd() + err := complianceExportCancelCmdF(s.client, cmd, []string{"invalid-job-id"}) + s.Require().NotNil(err) + s.EqualError(err, "failed to cancel compliance export job: failed to get job") + s.Len(printer.GetLines(), 0) + s.Len(printer.GetErrorLines(), 0) + }) + + s.Run("cancel job with cancel error", func() { + s.SetupTest() // Reset mocks before test + printer.Clean() + id := model.NewId() + + mockError := &model.AppError{ + Message: "failed to cancel job", + } + + s.client. + EXPECT(). + CancelJob(context.TODO(), id). + Return(&model.Response{}, mockError). + Times(1) + + cmd := makeCmd() + err := complianceExportCancelCmdF(s.client, cmd, []string{id}) + s.Require().NotNil(err) + s.EqualError(err, "failed to cancel compliance export job: failed to cancel job") + s.Len(printer.GetLines(), 0) + s.Len(printer.GetErrorLines(), 0) + }) +} + func makeCmd() *cobra.Command { cmd := &cobra.Command{} cmd.Flags().Int("page", 0, "") diff --git a/server/cmd/mmctl/docs/mmctl.rst b/server/cmd/mmctl/docs/mmctl.rst index afb9eb645c9..898d75337b5 100644 --- a/server/cmd/mmctl/docs/mmctl.rst +++ b/server/cmd/mmctl/docs/mmctl.rst @@ -35,7 +35,7 @@ SEE ALSO * `mmctl channel `_ - Management of channels * `mmctl command `_ - Management of slash commands * `mmctl completion `_ - Generates autocompletion scripts for bash and zsh -* `mmctl compliance_export `_ - Management of compliance exports +* `mmctl compliance-export `_ - Management of compliance exports * `mmctl config `_ - Configuration * `mmctl docs `_ - Generates mmctl documentation * `mmctl export `_ - Management of exports diff --git a/server/cmd/mmctl/docs/mmctl_compliance_export.rst b/server/cmd/mmctl/docs/mmctl_compliance-export.rst similarity index 78% rename from server/cmd/mmctl/docs/mmctl_compliance_export.rst rename to server/cmd/mmctl/docs/mmctl_compliance-export.rst index abe351204b7..7d6c91b1561 100644 --- a/server/cmd/mmctl/docs/mmctl_compliance_export.rst +++ b/server/cmd/mmctl/docs/mmctl_compliance-export.rst @@ -1,6 +1,6 @@ -.. _mmctl_compliance_export: +.. _mmctl_compliance-export: -mmctl compliance_export +mmctl compliance-export ----------------------- Management of compliance exports @@ -16,7 +16,7 @@ Options :: - -h, --help help for compliance_export + -h, --help help for compliance-export Options inherited from parent commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -37,6 +37,7 @@ SEE ALSO ~~~~~~~~ * `mmctl `_ - Remote client for the Open Source, self-hosted Slack-alternative -* `mmctl compliance_export list `_ - List compliance export jobs, sorted by creation date descending (newest first) -* `mmctl compliance_export show `_ - Show compliance export job +* `mmctl compliance-export cancel `_ - Cancel compliance export job +* `mmctl compliance-export list `_ - List compliance export jobs, sorted by creation date descending (newest first) +* `mmctl compliance-export show `_ - Show compliance export job diff --git a/server/cmd/mmctl/docs/mmctl_compliance-export_cancel.rst b/server/cmd/mmctl/docs/mmctl_compliance-export_cancel.rst new file mode 100644 index 00000000000..8ef397397a3 --- /dev/null +++ b/server/cmd/mmctl/docs/mmctl_compliance-export_cancel.rst @@ -0,0 +1,51 @@ +.. _mmctl_compliance-export_cancel: + +mmctl compliance-export cancel +------------------------------ + +Cancel compliance export job + +Synopsis +~~~~~~~~ + + +Cancel compliance export job + +:: + + mmctl compliance-export cancel [complianceExportJobID] [flags] + +Examples +~~~~~~~~ + +:: + + compliance-export cancel o98rj3ur83dp5dppfyk5yk6osy + +Options +~~~~~~~ + +:: + + -h, --help help for cancel + +Options inherited from parent commands +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + --config string path to the configuration file (default "$XDG_CONFIG_HOME/mmctl/config") + --disable-pager disables paged output + --insecure-sha1-intermediate allows to use insecure TLS protocols, such as SHA-1 + --insecure-tls-version allows to use TLS versions 1.0 and 1.1 + --json the output format will be in json format + --local allows communicating with the server through a unix socket + --quiet prevent mmctl to generate output for the commands + --strict will only run commands if the mmctl version matches the server one + --suppress-warnings disables printing warning messages + +SEE ALSO +~~~~~~~~ + +* `mmctl compliance-export `_ - Management of compliance exports + diff --git a/server/cmd/mmctl/docs/mmctl_compliance_export_list.rst b/server/cmd/mmctl/docs/mmctl_compliance-export_list.rst similarity index 89% rename from server/cmd/mmctl/docs/mmctl_compliance_export_list.rst rename to server/cmd/mmctl/docs/mmctl_compliance-export_list.rst index d81ca8e8f32..a9c6559a0f6 100644 --- a/server/cmd/mmctl/docs/mmctl_compliance_export_list.rst +++ b/server/cmd/mmctl/docs/mmctl_compliance-export_list.rst @@ -1,6 +1,6 @@ -.. _mmctl_compliance_export_list: +.. _mmctl_compliance-export_list: -mmctl compliance_export list +mmctl compliance-export list ---------------------------- List compliance export jobs, sorted by creation date descending (newest first) @@ -13,7 +13,7 @@ List compliance export jobs, sorted by creation date descending (newest first) :: - mmctl compliance_export list [flags] + mmctl compliance-export list [flags] Options ~~~~~~~ @@ -43,5 +43,5 @@ Options inherited from parent commands SEE ALSO ~~~~~~~~ -* `mmctl compliance_export `_ - Management of compliance exports +* `mmctl compliance-export `_ - Management of compliance exports diff --git a/server/cmd/mmctl/docs/mmctl_compliance_export_show.rst b/server/cmd/mmctl/docs/mmctl_compliance-export_show.rst similarity index 81% rename from server/cmd/mmctl/docs/mmctl_compliance_export_show.rst rename to server/cmd/mmctl/docs/mmctl_compliance-export_show.rst index 2c09d3e4698..b616a6525af 100644 --- a/server/cmd/mmctl/docs/mmctl_compliance_export_show.rst +++ b/server/cmd/mmctl/docs/mmctl_compliance-export_show.rst @@ -1,6 +1,6 @@ -.. _mmctl_compliance_export_show: +.. _mmctl_compliance-export_show: -mmctl compliance_export show +mmctl compliance-export show ---------------------------- Show compliance export job @@ -13,14 +13,14 @@ Show compliance export job :: - mmctl compliance_export show [complianceExportJobID] [flags] + mmctl compliance-export show [complianceExportJobID] [flags] Examples ~~~~~~~~ :: - compliance_export show o98rj3ur83dp5dppfyk5yk6osy + compliance-export show o98rj3ur83dp5dppfyk5yk6osy Options ~~~~~~~ @@ -47,5 +47,5 @@ Options inherited from parent commands SEE ALSO ~~~~~~~~ -* `mmctl compliance_export `_ - Management of compliance exports +* `mmctl compliance-export `_ - Management of compliance exports