From e6e498d63ae0bd99b45c5f26de0a62ed218d1d93 Mon Sep 17 00:00:00 2001 From: Vicky Kurniawan Date: Sun, 1 Aug 2021 19:47:03 +0700 Subject: [PATCH] Add support for DM subscriptions based on "watching" an issue in Jira (#507) * add command setting watching * add new client api for GetWatchers * add function to add notification for the user who enable watching in setting --- server/client.go | 10 ++++++ server/command.go | 2 ++ server/settings.go | 39 +++++++++++++++++++++++ server/user.go | 1 + server/webhook.go | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+) diff --git a/server/client.go b/server/client.go index 4acb7f1b7..7c1dec504 100644 --- a/server/client.go +++ b/server/client.go @@ -65,6 +65,7 @@ type SearchService interface { type IssueService interface { GetIssue(key string, options *jira.GetQueryOptions) (*jira.Issue, error) CreateIssue(issue *jira.Issue) (*jira.Issue, error) + GetWatchers(issueKey string) (*[]jira.User, error) AddAttachment(api plugin.API, issueKey, fileID string, maxSize utils.ByteSize) (mattermostName, jiraName, mime string, err error) AddComment(issueKey string, comment *jira.Comment) (*jira.Comment, error) @@ -195,6 +196,15 @@ func (client JiraClient) GetIssue(key string, options *jira.GetQueryOptions) (*j return issue, nil } +// GetWatchers returns an Issue watchers by issueKey. +func (client JiraClient) GetWatchers(issueKey string) (*[]jira.User, error) { + issue, resp, err := client.Jira.Issue.GetWatchers(issueKey) + if err != nil { + return nil, userFriendlyJiraError(resp, err) + } + return issue, nil +} + // GetTransitions returns transitions for an issue with issueKey. func (client JiraClient) GetTransitions(issueKey string) ([]jira.Transition, error) { transitions, resp, err := client.Jira.Issue.GetTransitions(issueKey) diff --git a/server/command.go b/server/command.go index d27d0c63c..0a04a49e9 100644 --- a/server/command.go +++ b/server/command.go @@ -592,6 +592,8 @@ func executeSettings(p *Plugin, c *plugin.Context, header *model.CommandArgs, ar return p.responsef(header, "Current settings:\n%s", conn.Settings.String()) case "notifications": return p.settingsNotifications(header, instance.GetID(), user.MattermostUserID, conn, args) + case "watching": + return p.settingsWatching(header, instance.GetID(), user.MattermostUserID, conn, args) default: return p.responsef(header, "Unknown setting.") } diff --git a/server/settings.go b/server/settings.go index 8c0769d2e..a0acd8f0f 100644 --- a/server/settings.go +++ b/server/settings.go @@ -49,3 +49,42 @@ func (p *Plugin) settingsNotifications(header *model.CommandArgs, instanceID, ma return p.responsef(header, "Settings updated. Notifications %s.", notifications) } + +func (p *Plugin) settingsWatching(header *model.CommandArgs, instanceID, mattermostUserID types.ID, connection *Connection, args []string) *model.CommandResponse { + const helpText = "`/jira watching watching [value]`\n* Invalid value. Accepted values are: `on` or `off`." + + if len(args) != 2 { + return p.responsef(header, helpText) + } + + var value bool + switch args[1] { + case settingOn: + value = true + case settingOff: + value = false + default: + return p.responsef(header, helpText) + } + + if connection.Settings == nil { + connection.Settings = &ConnectionSettings{} + } + connection.Settings.Watching = value + if err := p.userStore.StoreConnection(instanceID, mattermostUserID, connection); err != nil { + p.errorf("settingsWatching, err: %v", err) + p.responsef(header, "Could not store new settings. Please contact your system administrator. error: %v", err) + } + + // send back the actual value + updatedConnection, err := p.userStore.LoadConnection(instanceID, mattermostUserID) + if err != nil { + return p.responsef(header, "Your username is not connected to Jira. Please type `jira connect`. %v", err) + } + watching := settingOff + if updatedConnection.Settings.Watching { + watching = settingOn + } + + return p.responsef(header, "Settings updated. Watching %s.", watching) +} diff --git a/server/user.go b/server/user.go index 87a1607b8..e6734e331 100644 --- a/server/user.go +++ b/server/user.go @@ -44,6 +44,7 @@ func (c *Connection) JiraAccountID() types.ID { type ConnectionSettings struct { Notifications bool `json:"notifications"` + Watching bool `json:"watching"` } func (s *ConnectionSettings) String() string { diff --git a/server/webhook.go b/server/webhook.go index cde363f3b..1377a466b 100644 --- a/server/webhook.go +++ b/server/webhook.go @@ -5,6 +5,7 @@ package main import ( "fmt" + "github.com/andygrunwald/go-jira" "net/http" "net/url" @@ -97,6 +98,81 @@ func (wh webhook) PostToChannel(p *Plugin, instanceID types.ID, channelID, fromU return post, http.StatusOK, nil } +func (wh *webhook) getConnection(p *Plugin, instance Instance, notification webhookUserNotification) (con *Connection, err error) { + var mattermostUserID types.ID + + // prefer accountId to username when looking up UserIds + if notification.jiraAccountID != "" { + mattermostUserID, err = p.userStore.LoadMattermostUserID(instance.GetID(), notification.jiraAccountID) + } else { + mattermostUserID, err = p.userStore.LoadMattermostUserID(instance.GetID(), notification.jiraUsername) + } + if err != nil { + return + } + + // Check if the user has permissions. + con, err = p.userStore.LoadConnection(instance.GetID(), mattermostUserID) + if err != nil { + // Not connected to Jira, so can't check permissions + return + } + + return +} + +func (wh *webhook) checkWatcherUser(p *Plugin, instance Instance) { + watcherUsers := &[]jira.User{} + for _, notification := range wh.notifications { + c, err := wh.getConnection(p, instance, notification) + if err != nil { + continue + } + client, err2 := instance.GetClient(c) + if err2 != nil { + continue + } + watcherUsers, err = client.GetWatchers(wh.Issue.ID) + if err != nil { + continue + } + } + + for _, watcherUser := range *watcherUsers { + whUserNotification := &webhookUserNotification{} + postType := "" + message := "" + for _, notification := range wh.notifications { + if notification.jiraAccountID == watcherUser.AccountID || notification.jiraUsername == watcherUser.Name { + whUserNotification = ¬ification + } + postType = notification.postType + message = notification.message + } + if whUserNotification == nil { + whUserNotification = &webhookUserNotification{ + jiraUsername: watcherUser.Name, + jiraAccountID: watcherUser.AccountID, + message: message, + postType: postType, + commentSelf: watcherUser.Self, + } + + c, err := wh.getConnection(p, instance, *whUserNotification) + if err != nil { + continue + } + + // if setting watching value is false don't put into webhookUserNotification + if c.Settings == nil || !c.Settings.Watching || err != nil { + continue + } + + wh.notifications = append(wh.notifications, *whUserNotification) + } + } +} + func (wh *webhook) PostNotifications(p *Plugin, instanceID types.ID) ([]*model.Post, int, error) { if len(wh.notifications) == 0 { return nil, http.StatusOK, nil @@ -109,6 +185,8 @@ func (wh *webhook) PostNotifications(p *Plugin, instanceID types.ID) ([]*model.P return nil, http.StatusOK, nil } + wh.checkWatcherUser(p, instance) + posts := []*model.Post{} for _, notification := range wh.notifications { var mattermostUserID types.ID