From 6b59ab4ee8561d96b0099e59da20b172febf9df5 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Tue, 8 Jan 2019 11:10:19 -0800 Subject: [PATCH] [MM 12373] Create CLI command "webhook modify-outgoing" (#10028) * Add test for modifying outgoing webhook * Add command to modify an outgoing webhook * Use updated test helper * Use errors.Wrap * Lower case error strings and use fmt.Errorf * Clone webhook into updatedHook * Rename error variables --- cmd/mattermost/commands/webhook.go | 103 ++++++++++++++++++++++++ cmd/mattermost/commands/webhook_test.go | 99 +++++++++++++++++++++++ 2 files changed, 202 insertions(+) diff --git a/cmd/mattermost/commands/webhook.go b/cmd/mattermost/commands/webhook.go index 4f24eb9cc1..62b245aa09 100644 --- a/cmd/mattermost/commands/webhook.go +++ b/cmd/mattermost/commands/webhook.go @@ -5,6 +5,7 @@ package commands import ( "fmt" + "strings" "github.com/mattermost/mattermost-server/model" "github.com/pkg/errors" @@ -49,6 +50,14 @@ var WebhookCreateOutgoingCmd = &cobra.Command{ RunE: createOutgoingWebhookCmdF, } +var WebhookModifyOutgoingCmd = &cobra.Command{ + Use: "modify-outgoing", + Short: "Modify outgoing webhook", + Long: "Modify existing outgoing webhook by changing its title, description, channel, icon, url, content-type, and triggers", + Example: ` webhook modify-outgoing [webhookId] --channel [channelId] --display-name [displayName] --description "New webhook description" --icon http://localhost:8000/my-slash-handler-bot-icon.png --url http://localhost:8000/my-webhook-handler --content-type "application/json" --trigger-word test --trigger-when start`, + RunE: modifyOutgoingWebhookCmdF, +} + var WebhookDeleteCmd = &cobra.Command{ Use: "delete", Short: "Delete webhooks", @@ -285,6 +294,90 @@ func createOutgoingWebhookCmdF(command *cobra.Command, args []string) error { return nil } +func modifyOutgoingWebhookCmdF(command *cobra.Command, args []string) error { + app, err := InitDBCommandContextCobra(command) + if err != nil { + return err + } + defer app.Shutdown() + + if len(args) < 1 { + return errors.New("WebhookID is not specified") + } + + webhookArg := args[0] + oldHook, appErr := app.GetOutgoingWebhook(webhookArg) + if appErr != nil { + return fmt.Errorf("unable to find webhook '%s'", webhookArg) + } + + updatedHook := model.OutgoingWebhookFromJson(strings.NewReader(oldHook.ToJson())) + + channelArg, _ := command.Flags().GetString("channel") + if channelArg != "" { + channel := getChannelFromChannelArg(app, channelArg) + if channel == nil { + return fmt.Errorf("unable to find channel '%s'", channelArg) + } + updatedHook.ChannelId = channel.Id + } + + displayName, _ := command.Flags().GetString("display-name") + if displayName != "" { + updatedHook.DisplayName = displayName + } + + description, _ := command.Flags().GetString("description") + if description != "" { + updatedHook.Description = description + } + + triggerWords, err := command.Flags().GetStringArray("trigger-word") + if err != nil { + return errors.Wrap(err, "invalid trigger-word parameter") + } + if len(triggerWords) > 0 { + updatedHook.TriggerWords = triggerWords + } + + triggerWhenString, _ := command.Flags().GetString("trigger-when") + if triggerWhenString != "" { + var triggerWhen int + if triggerWhenString == "exact" { + triggerWhen = 0 + } else if triggerWhenString == "start" { + triggerWhen = 1 + } else { + return errors.New("invalid trigger-when parameter") + } + updatedHook.TriggerWhen = triggerWhen + } + + iconURL, _ := command.Flags().GetString("icon") + if iconURL != "" { + updatedHook.IconURL = iconURL + } + + contentType, _ := command.Flags().GetString("content-type") + if contentType != "" { + updatedHook.ContentType = contentType + } + + callbackURLs, err := command.Flags().GetStringArray("url") + if err != nil { + return errors.Wrap(err, "invalid URL parameter") + } + if len(callbackURLs) > 0 { + updatedHook.CallbackURLs = callbackURLs + } + + if _, appErr := app.UpdateOutgoingWebhook(oldHook, updatedHook); appErr != nil { + return appErr + } + + return nil +} + func deleteWebhookCmdF(command *cobra.Command, args []string) error { app, err := InitDBCommandContextCobra(command) if err != nil { @@ -332,11 +425,21 @@ func init() { WebhookCreateOutgoingCmd.Flags().StringArray("url", []string{}, "Callback URL (required)") WebhookCreateOutgoingCmd.Flags().String("content-type", "", "Content-type") + WebhookModifyOutgoingCmd.Flags().String("channel", "", "Channel name or ID") + WebhookModifyOutgoingCmd.Flags().String("display-name", "", "Outgoing webhook display name") + WebhookModifyOutgoingCmd.Flags().String("description", "", "Outgoing webhook description") + WebhookModifyOutgoingCmd.Flags().StringArray("trigger-word", []string{}, "Word to trigger webhook") + WebhookModifyOutgoingCmd.Flags().String("trigger-when", "", "When to trigger webhook (exact: for first word matches a trigger word exactly, start: for first word starts with a trigger word)") + WebhookModifyOutgoingCmd.Flags().String("icon", "", "Icon URL") + WebhookModifyOutgoingCmd.Flags().StringArray("url", []string{}, "Callback URL") + WebhookModifyOutgoingCmd.Flags().String("content-type", "", "Content-type") + WebhookCmd.AddCommand( WebhookListCmd, WebhookCreateIncomingCmd, WebhookModifyIncomingCmd, WebhookCreateOutgoingCmd, + WebhookModifyOutgoingCmd, WebhookDeleteCmd, ) diff --git a/cmd/mattermost/commands/webhook_test.go b/cmd/mattermost/commands/webhook_test.go index 44610ee821..af8434f08c 100644 --- a/cmd/mattermost/commands/webhook_test.go +++ b/cmd/mattermost/commands/webhook_test.go @@ -245,6 +245,105 @@ func TestCreateOutgoingWebhook(t *testing.T) { } } +func TestModifyOutgoingWebhook(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + config := th.Config() + config.ServiceSettings.EnableOutgoingWebhooks = true + th.SetConfig(config) + + defaultRolePermissions := th.SaveDefaultRolePermissions() + defer func() { + th.RestoreDefaultRolePermissions(defaultRolePermissions) + }() + th.AddPermissionToRole(model.PERMISSION_MANAGE_WEBHOOKS.Id, model.TEAM_ADMIN_ROLE_ID) + th.RemovePermissionFromRole(model.PERMISSION_MANAGE_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" + + outgoingWebhook := &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(outgoingWebhook) + if err != nil { + t.Fatal("unable to create outgoing webhooks: " + err.Error()) + } + defer func() { + th.App.DeleteOutgoingWebhook(oldHook.Id) + }() + + // should fail because you need to specify valid outgoing webhook + require.Error(t, th.RunCommand(t, "webhook", "modify-outgoing", "doesnotexist")) + // should fail because you need to specify valid channel + require.Error(t, th.RunCommand(t, "webhook", "modify-outgoing", oldHook.Id, "--channel", th.BasicTeam.Name+":doesnotexist")) + // should fail because you need to specify valid trigger when + require.Error(t, th.RunCommand(t, "webhook", "modify-outgoing", oldHook.Id, "--channel", th.BasicTeam.Name+th.BasicChannel.Id, "--trigger-when", "invalid")) + // should fail because you need to specify a valid callback URL + require.Error(t, th.RunCommand(t, "webhook", "modify-outgoing", oldHook.Id, "--channel", th.BasicTeam.Name+th.BasicChannel.Id, "--callback-url", "invalid")) + + modifiedChannelID := th.BasicChannel2.Id + modifiedDisplayName := "myhookoutname2" + modifiedDescription := "myhookoutdesc2" + modifiedTriggerWords := model.StringArray{"myhookoutword2A", "myhookoutword2B"} + modifiedTriggerWhen := "start" + modifiedIconURL := "myhookouticon2" + modifiedContentType := "myhookcontent2" + modifiedCallbackURLs := model.StringArray{"http://myhookouturl2A", "http://myhookouturl2B"} + + th.CheckCommand(t, "webhook", "modify-outgoing", oldHook.Id, + "--channel", modifiedChannelID, + "--display-name", modifiedDisplayName, + "--description", modifiedDescription, + "--trigger-word", modifiedTriggerWords[0], + "--trigger-word", modifiedTriggerWords[1], + "--trigger-when", modifiedTriggerWhen, + "--icon", modifiedIconURL, + "--content-type", modifiedContentType, + "--url", modifiedCallbackURLs[0], + "--url", modifiedCallbackURLs[1], + ) + + modifiedHook, err := th.App.GetOutgoingWebhook(oldHook.Id) + if err != nil { + t.Fatal("unable to retrieve modified outgoing webhook") + } + + updateFailed := modifiedHook.ChannelId != modifiedChannelID || + modifiedHook.DisplayName != modifiedDisplayName || + modifiedHook.Description != modifiedDescription || + len(modifiedHook.TriggerWords) != len(modifiedTriggerWords) || + modifiedHook.TriggerWords[0] != modifiedTriggerWords[0] || + modifiedHook.TriggerWords[1] != modifiedTriggerWords[1] || + modifiedHook.TriggerWhen != 1 || + modifiedHook.IconURL != modifiedIconURL || + modifiedHook.ContentType != modifiedContentType || + len(modifiedHook.CallbackURLs) != len(modifiedCallbackURLs) || + modifiedHook.CallbackURLs[0] != modifiedCallbackURLs[0] || + modifiedHook.CallbackURLs[1] != modifiedCallbackURLs[1] + + if updateFailed { + t.Fatal("Failed to update outgoing webhook") + } +} + func TestDeleteWebhooks(t *testing.T) { th := Setup().InitBasic() defer th.TearDown()