diff --git a/cmd/mattermost/commands/webhook.go b/cmd/mattermost/commands/webhook.go index 5c314953a2..20c0a61890 100644 --- a/cmd/mattermost/commands/webhook.go +++ b/cmd/mattermost/commands/webhook.go @@ -5,6 +5,7 @@ package commands import ( "fmt" + "net/http" "strings" "github.com/mattermost/mattermost-server/v5/model" @@ -76,6 +77,15 @@ var WebhookDeleteCmd = &cobra.Command{ RunE: deleteWebhookCmdF, } +var WebhookMoveOutgoingCmd = &cobra.Command{ + Use: "move-outgoing", + Short: "Move outgoing webhook", + Long: "Move outgoing webhook with an id", + Example: " webhook move-outgoing newteam oldteam:webhook-id --channel new-default-channel", + Args: cobra.ExactArgs(2), + RunE: moveOutgoingWebhookCmd, +} + func listWebhookCmdF(command *cobra.Command, args []string) error { app, err := InitDBCommandContextCobra(command) if err != nil { @@ -448,6 +458,62 @@ func showWebhookCmdF(command *cobra.Command, args []string) error { return errors.New("Webhook with id " + webhookId + " not found") } +func moveOutgoingWebhookCmd(command *cobra.Command, args []string) error { + app, err := InitDBCommandContextCobra(command) + if err != nil { + return err + } + defer app.Shutdown() + + newTeamId := args[0] + _, teamError := app.GetTeam(newTeamId) + if teamError != nil { + return teamError + } + + webhookInformation := strings.Split(args[1], ":") + sourceTeam := webhookInformation[0] + _, teamErr := app.GetTeam(sourceTeam) + if teamErr != nil { + return teamErr + } + + webhookId := webhookInformation[1] + webhook, appError := app.GetOutgoingWebhook(webhookId) + if appError != nil { + return appError + } + + channelName, channelErr := command.Flags().GetString("channel") + if channelErr != nil { + return channelErr + } + channel, getChannelErr := app.GetChannelByName(channelName, newTeamId, false) + + if webhook.ChannelId != "" { + if getChannelErr != nil { + return getChannelErr + } + webhook.ChannelId = channel.Id + } else if channelName != "" { + webhook.ChannelId = channel.Id + } + + deleteErr := app.DeleteOutgoingWebhook(webhook.Id) + if deleteErr != nil { + return deleteErr + } + + webhook.Id = "" + webhook.TeamId = newTeamId + + _, createErr := app.CreateOutgoingWebhook(webhook) + if createErr != nil { + return model.NewAppError("moveOutgoingWebhookCmd", "cli.outgoing_webhook.inconsistent_state.app_error", nil, "", http.StatusInternalServerError) + } + return nil +} + func init() { WebhookCreateIncomingCmd.Flags().String("channel", "", "Channel ID (required)") WebhookCreateIncomingCmd.Flags().String("user", "", "User ID (required)") @@ -482,6 +548,8 @@ func init() { WebhookModifyOutgoingCmd.Flags().StringArray("url", []string{}, "Callback URL") WebhookModifyOutgoingCmd.Flags().String("content-type", "", "Content-type") + WebhookMoveOutgoingCmd.Flags().String("channel", "", "Channel name or ID") + WebhookCmd.AddCommand( WebhookListCmd, WebhookCreateIncomingCmd, @@ -490,6 +558,7 @@ func init() { WebhookModifyOutgoingCmd, WebhookDeleteCmd, WebhookShowCmd, + WebhookMoveOutgoingCmd, ) RootCmd.AddCommand(WebhookCmd) diff --git a/cmd/mattermost/commands/webhook_test.go b/cmd/mattermost/commands/webhook_test.go index 80c43c4e36..f2ac1e851c 100644 --- a/cmd/mattermost/commands/webhook_test.go +++ b/cmd/mattermost/commands/webhook_test.go @@ -4,10 +4,11 @@ package commands import ( + "github.com/stretchr/testify/assert" "strconv" + "strings" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/mattermost/mattermost-server/v5/api4" @@ -444,3 +445,89 @@ func TestDeleteWebhooks(t *testing.T) { assert.NotContains(t, hooksAfterDeletion, dispName, "Should not have incoming webhooks") assert.NotContains(t, hooksAfterDeletion, dispName2, "Should not have outgoing webhooks") } + +func TestMoveOutgoingWebhook(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + config := th.Config() + *config.ServiceSettings.EnableOutgoingWebhooks = true + th.SetConfig(config) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer th.RestoreDefaultRolePermissions(defaultRolePermissions) + + th.AddPermissionToRole(model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_OUTGOING_WEBHOOKS.Id, model.TEAM_USER_ROLE_ID) + + description := "myhookoutdesc" + displayName := "myhookoutname" + triggerWords := model.StringArray{"myhookoutword1"} + triggerWhen := 0 + callbackURLs := model.StringArray{"http://myhookouturl1"} + iconURL := "myhookicon1" + contentType := "myhookcontent1" + + outgoingWebhookWithChannel := &model.OutgoingWebhook{ + CreatorId: th.BasicUser.Id, + Username: th.BasicUser.Username, + TeamId: th.BasicTeam.Id, + ChannelId: th.BasicChannel.Id, + DisplayName: displayName, + Description: description, + TriggerWords: triggerWords, + TriggerWhen: triggerWhen, + CallbackURLs: callbackURLs, + IconURL: iconURL, + ContentType: contentType, + } + + oldHook, err := th.App.CreateOutgoingWebhook(outgoingWebhookWithChannel) + require.Nil(t, err) + defer th.App.DeleteOutgoingWebhook(oldHook.Id) + + require.Error(t, th.RunCommand(t, "webhook", "move-outgoing")) + require.Error(t, th.RunCommand(t, "webhook", "move-outgoing", th.BasicTeam.Id)) + require.Error(t, th.RunCommand(t, "webhook", "move-outgoing", "invalid-team", "webhook")) + require.Error(t, th.RunCommand(t, "webhook", "move-outgoing", "invalid-team", "webhook", "--channel")) + + newTeam := th.CreateTeam() + + webhookInformation := "oldTeam" + ":" + "webhookId" + require.Error(t, th.RunCommand(t, "webhook", "move-outgoing", newTeam.Id, webhookInformation)) + + webhookInformation = th.BasicTeam.Id + ":" + "webhookId" + require.Error(t, th.RunCommand(t, "webhook", "move-outgoing", newTeam.Id, webhookInformation)) + + require.Error(t, th.RunCommand(t, "webhook", "move-outgoing", newTeam.Id, th.BasicTeam.Id+":"+oldHook.Id, "--channel", "invalid")) + + channel := th.CreateChannelWithClientAndTeam(th.SystemAdminClient, model.CHANNEL_OPEN, newTeam.Id) + th.CheckCommand(t, "webhook", "move-outgoing", newTeam.Id, th.BasicTeam.Id+":"+oldHook.Id, "--channel", channel.Name) + + _, webhookErr := th.App.GetOutgoingWebhook(oldHook.Id) + assert.Error(t, webhookErr) + + output := th.CheckCommand(t, "webhook", "list", newTeam.Name) + assert.True(t, strings.Contains(output, displayName)) + + outgoingWebhookWithoutChannel := &model.OutgoingWebhook{ + CreatorId: th.BasicUser.Id, + Username: th.BasicUser.Username, + TeamId: th.BasicTeam.Id, + DisplayName: displayName + "2", + Description: description, + TriggerWords: triggerWords, + TriggerWhen: triggerWhen, + CallbackURLs: callbackURLs, + IconURL: iconURL, + ContentType: contentType, + } + + oldHook2, err := th.App.CreateOutgoingWebhook(outgoingWebhookWithoutChannel) + require.Nil(t, err) + defer th.App.DeleteOutgoingWebhook(oldHook2.Id) + + th.CheckCommand(t, "webhook", "move-outgoing", newTeam.Id, th.BasicTeam.Id+":"+oldHook2.Id) + output = th.CheckCommand(t, "webhook", "list", newTeam.Name) + assert.True(t, strings.Contains(output, displayName+"2")) +} diff --git a/i18n/en.json b/i18n/en.json index 45659494f9..3a8542dfa1 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3738,6 +3738,10 @@ "id": "cli.license.critical", "translation": "Feature requires an upgrade to Enterprise Edition and the inclusion of a license key. Please contact your System Administrator." }, + { + "id": "cli.outgoing_webhook.inconsistent_state.app_error", + "translation": "The outgoing webhook is deleted but unable to create a new one due to some error." + }, { "id": "ent.account_migration.get_all_failed", "translation": "Unable to get users."