diff --git a/server/command.go b/server/command.go index ff3fcfd42..0715ff87f 100644 --- a/server/command.go +++ b/server/command.go @@ -418,60 +418,17 @@ If you see an option to create a Jira issue, you're all set! If not, refer to ou return p.responsef(header, addResponseFormat, jiraURL, p.GetSiteURL(), ji.GetMattermostKey(), pkey) } -// executeUninstallCloud will uninstall the jira cloud instance if the url matches, and then update all connected -// clients so that their Jira-related menu options are removed. func executeUninstallCloud(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse { - authorized, err := authorizedSysAdmin(p, header.UserId) - if err != nil { - return p.responsef(header, "%v", err) - } - if !authorized { - return p.responsef(header, "`/jira uninstall` can only be run by a System Administrator.") - } - if len(args) != 1 { - return p.help(header) - } - - jiraURL, err := utils.NormalizeInstallURL(p.GetSiteURL(), args[0]) - if err != nil { - return p.responsef(header, err.Error()) - } - - ji, err := p.currentInstanceStore.LoadCurrentJIRAInstance() - if err != nil { - return p.responsef(header, "No current Jira instance to uninstall") - } - - jci, ok := ji.(*jiraCloudInstance) - if !ok { - return p.responsef(header, "The current Jira instance is not a cloud instance") - } - - if jiraURL != jci.GetURL() { - return p.responsef(header, "You have entered an incorrect URL. The current Jira instance URL is: `"+jci.GetURL()+"`. Please enter the URL correctly to confirm the uninstall command.") - } - - err = p.instanceStore.DeleteJiraInstance(jci.GetURL()) - if err != nil { - return p.responsef(header, "Failed to delete Jira instance "+ji.GetURL()) - } - - // Notify users we have uninstalled an instance - p.API.PublishWebSocketEvent( - wSEventInstanceStatus, - map[string]interface{}{ - "instance_installed": false, - "instance_type": "", - }, - &model.WebsocketBroadcast{}, - ) - - const uninstallInstructions = `Jira instance successfully disconnected. Go to **Settings > Apps > Manage Apps** to remove the application in your Jira Cloud instance.` - - return p.responsef(header, uninstallInstructions) + return executeUninstall(p, c, header, "cloud", args...) } func executeUninstallServer(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse { + return executeUninstall(p, c, header, "server", args...) +} + +// executeUninstall will uninstall the jira instance if the url matches, and then update all connected clients +// so that their Jira-related menu options are removed. +func executeUninstall(p *Plugin, c *plugin.Context, header *model.CommandArgs, instanceType string, args ...string) *model.CommandResponse { authorized, err := authorizedSysAdmin(p, header.UserId) if err != nil { return p.responsef(header, "%v", err) @@ -493,16 +450,22 @@ func executeUninstallServer(p *Plugin, c *plugin.Context, header *model.CommandA return p.responsef(header, "No current Jira instance to uninstall") } - jsi, ok := ji.(*jiraServerInstance) + var ok bool + if instanceType == "cloud" { + _, ok = ji.(*jiraCloudInstance) + } else { + _, ok = ji.(*jiraServerInstance) + } + if !ok { - return p.responsef(header, "The current Jira instance is not a server instance") + return p.responsef(header, fmt.Sprintf("The current Jira instance is not a %s instance", instanceType)) } - if jiraURL != jsi.GetURL() { - return p.responsef(header, "You have entered an incorrect URL. The current Jira instance URL is: `"+jsi.GetURL()+"`. Please enter the URL correctly to confirm the uninstall command.") + if jiraURL != ji.GetURL() { + return p.responsef(header, `You have entered an incorrect URL. The current Jira instance URL is %s. Please enter the URL correctly to confirm the uninstall command.`, ji.GetURL()) } - err = p.instanceStore.DeleteJiraInstance(jsi.GetURL()) + err = p.instanceStore.DeleteJiraInstance(ji.GetURL()) if err != nil { return p.responsef(header, "Failed to delete Jira instance "+ji.GetURL()) } @@ -517,9 +480,14 @@ func executeUninstallServer(p *Plugin, c *plugin.Context, header *model.CommandA &model.WebsocketBroadcast{}, ) - const uninstallInstructions = `Jira instance successfully disconnected. Go to **Settings > Applications > Application Links** to remove the application in your Jira Server or Data Center instance.` + var uninstallInstructions string + if instanceType == "cloud" { + uninstallInstructions = `Jira instance successfully disconnected. Go to [**Settings > Apps > Manage Apps**](%s/plugins/servlet/upm) to remove the application in your Jira Cloud instance.` + } else { + uninstallInstructions = `Jira instance successfully disconnected. Go to [**Settings > Applications > Application Links**](%s/plugins/servlet/applinks/listApplicationLinks) to remove the application in your Jira Server or Data Center instance.` + } - return p.responsef(header, uninstallInstructions) + return p.responsef(header, uninstallInstructions, jiraURL) } func executeUnassign(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse { diff --git a/server/command_test.go b/server/command_test.go index 0dbf7678f..0ba87a25d 100644 --- a/server/command_test.go +++ b/server/command_test.go @@ -2,14 +2,15 @@ package main import ( "errors" + "strings" + "testing" + "github.com/mattermost/mattermost-server/v5/model" "github.com/mattermost/mattermost-server/v5/plugin" "github.com/mattermost/mattermost-server/v5/plugin/plugintest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "strings" - "testing" ) const ( @@ -235,3 +236,71 @@ func TestPlugin_ExecuteCommand_Installation(t *testing.T) { }) } } + +func TestPlugin_ExecuteCommand_Uninstall(t *testing.T) { + p := Plugin{} + tc := TestConfiguration{} + p.updateConfig(func(conf *config) { + conf.Secret = tc.Secret + }) + api := &plugintest.API{} + siteURL := "https://somelink.com" + api.On("GetConfig").Return(&model.Config{ServiceSettings: model.ServiceSettings{SiteURL: &siteURL}}) + + sysAdminUser := &model.User{ + Id: mockUserIDSysAdmin, + Roles: "system_admin", + } + api.On("GetUser", mockUserIDSysAdmin).Return(sysAdminUser, nil) + nonSysAdminUser := &model.User{ + Id: mockUserIDNonSysAdmin, + Roles: "", + } + api.On("GetUser", mockUserIDNonSysAdmin).Return(nonSysAdminUser, nil) + + tests := map[string]struct { + commandArgs *model.CommandArgs + expectedMsgPrefix string + }{ + "no params - user is sys admin": { + commandArgs: &model.CommandArgs{Command: "/jira uninstall", UserId: mockUserIDSysAdmin}, + expectedMsgPrefix: strings.TrimSpace(helpTextHeader + commonHelpText + sysAdminHelpText), + }, + "no params - user is not sys admin": { + commandArgs: &model.CommandArgs{Command: "/jira uninstall", UserId: mockUserIDNonSysAdmin}, + expectedMsgPrefix: strings.TrimSpace(helpTextHeader + commonHelpText), + }, + "uninstall with invalid option": { + commandArgs: &model.CommandArgs{Command: "/jira uninstall foo", UserId: mockUserIDSysAdmin}, + expectedMsgPrefix: strings.TrimSpace(helpTextHeader + commonHelpText + sysAdminHelpText), + }, + "uninstall server instance without URL": { + commandArgs: &model.CommandArgs{Command: "/jira uninstall server", UserId: mockUserIDSysAdmin}, + expectedMsgPrefix: strings.TrimSpace(helpTextHeader + commonHelpText + sysAdminHelpText), + }, + "uninstall cloud instance without URL": { + commandArgs: &model.CommandArgs{Command: "/jira uninstall cloud", UserId: mockUserIDSysAdmin}, + expectedMsgPrefix: strings.TrimSpace(helpTextHeader + commonHelpText + sysAdminHelpText), + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + isSendEphemeralPostCalled := false + currentTestAPI := api + currentTestAPI.On("SendEphemeralPost", mock.AnythingOfType("string"), mock.AnythingOfType("*model.Post")).Run(func(args mock.Arguments) { + isSendEphemeralPostCalled = true + + post := args.Get(1).(*model.Post) + actual := strings.TrimSpace(post.Message) + assert.True(t, strings.HasPrefix(actual, tt.expectedMsgPrefix), "Expected returned message to start with: \n%s\nActual:\n%s", tt.expectedMsgPrefix, actual) + }).Once().Return(&model.Post{}) + + p.SetAPI(currentTestAPI) + + cmdResponse, appError := p.ExecuteCommand(&plugin.Context{}, tt.commandArgs) + require.Nil(t, appError) + require.NotNil(t, cmdResponse) + assert.True(t, isSendEphemeralPostCalled) + }) + } +}