Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ You can specify which events trigger a notification. They can see:
- pipeline - include pipeline
- tag - include tag creation
- pull_reviews - includes merge request reviews
- merge_request_assigns - includes merge request assignment and unassignment notifications
- label:"<labelname>" - must include "merges" or "issues" in feature list when using a label
- Defaults to "merges,issues,tag"

Expand Down
17 changes: 9 additions & 8 deletions server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const commandHelp = `* |/gitlab connect| - Connect your Mattermost account to yo
* pushes - includes pushes
* issue_comments - includes new issue comments
* merge_request_comments - include new merge-request comments
* merge_request_assigns - includes merge request assignment and unassignment notifications
* pipeline - includes pipeline runs
* tag - include tag creation
* pull_reviews - includes merge request reviews
Expand All @@ -52,14 +53,14 @@ const commandHelp = `* |/gitlab connect| - Connect your Mattermost account to yo
* |*| - or missing defaults to all with SSL verification enabled
* *noSSL - all triggers with SSL verification not enabled.
* PushEvents
* TagPushEvents
* Comments
* ConfidentialComments
* TagPushEvents
* Comments
* ConfidentialComments
* IssuesEvents
* ConfidentialIssuesEvents
* MergeRequestsEvents
* JobEvents
* PipelineEvents
* ConfidentialIssuesEvents
* MergeRequestsEvents
* JobEvents
* PipelineEvents
* WikiPageEvents
* DeploymentEvents
* ReleaseEvents
Expand Down Expand Up @@ -1103,7 +1104,7 @@ func (p *Plugin) getAutocompleteData(config *configuration) *model.AutocompleteD

subscriptionsAdd := model.NewAutocompleteData(commandAdd, "owner[/repo] [features]", "Subscribe the current channel to receive notifications from a project")
subscriptionsAdd.AddTextArgument("Project path: includes user or group name with optional slash project name", "owner[/repo]", "")
subscriptionsAdd.AddTextArgument("comma-delimited list of features to subscribe to: issues, confidential_issues, merges, pushes, issue_comments, merge_request_comments, pipeline, tag, pull_reviews, label:<labelName>, deployments, releases", "[features] (optional)", `/[^,-\s]+(,[^,-\s]+)*/`)
subscriptionsAdd.AddTextArgument("comma-delimited list of features to subscribe to: issues, confidential_issues, merges, pushes, issue_comments, merge_request_comments, merge_request_assigns, pipeline, tag, pull_reviews, label:<labelName>, deployments, releases", "[features] (optional)", `/[^,-\s]+(,[^,-\s]+)*/`)
subscriptions.AddCommand(subscriptionsAdd)

subscriptionsDelete := model.NewAutocompleteData(commandDelete, "owner[/repo]", "Unsubscribe the current channel from a repository")
Expand Down
5 changes: 5 additions & 0 deletions server/subscription/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var allFeatures = map[string]bool{
"confidential_issues": true,
"deployments": true,
"releases": true,
"merge_request_assigns": true,
// "label:": true,//particular case for label:XXX
}

Expand Down Expand Up @@ -146,3 +147,7 @@ func (s *Subscription) Releases() bool {
func (s *Subscription) Deployments() bool {
return strings.Contains(s.Features, "deployments")
}

func (s *Subscription) MergeRequestAssigns() bool {
return strings.Contains(s.Features, "merge_request_assigns")
}
83 changes: 60 additions & 23 deletions server/webhook/merge_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"fmt"

"github.com/xanzy/go-gitlab"

"github.com/mattermost/mattermost-plugin-gitlab/server/subscription"
)

func (w *webhook) HandleMergeRequest(ctx context.Context, event *gitlab.MergeEvent) ([]*HandleWebhook, []string, error) {
Expand Down Expand Up @@ -59,13 +61,23 @@ func (w *webhook) handleDMMergeRequest(event *gitlab.MergeEvent) ([]*HandleWebho

// Handle change in assignees
if event.Changes.Assignees.Current != nil || event.Changes.Assignees.Previous != nil {
updatedCurrentUsers := w.calculateUserDiffs(event.Changes.Assignees.Previous, event.Changes.Assignees.Current)
newlyAssigned := w.calculateUserDiffs(event.Changes.Assignees.Previous, event.Changes.Assignees.Current)
newlyUnassigned := w.calculateUserDiffs(event.Changes.Assignees.Current, event.Changes.Assignees.Previous)

if len(updatedCurrentUsers) != 0 {
if len(newlyAssigned) != 0 {
message = fmt.Sprintf("[%s](%s) assigned you to merge request [#%d](%s) in [%s](%s)", senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername), event.ObjectAttributes.IID, event.ObjectAttributes.URL, event.ObjectAttributes.Target.PathWithNamespace, event.Repository.Homepage)
handlers = append(handlers, &HandleWebhook{
Message: message,
ToUsers: updatedCurrentUsers,
ToUsers: newlyAssigned,
From: senderGitlabUsername,
})
}

if len(newlyUnassigned) != 0 {
message = fmt.Sprintf("[%s](%s) unassigned you from merge request [#%d](%s) in [%s](%s)", senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername), event.ObjectAttributes.IID, event.ObjectAttributes.URL, event.ObjectAttributes.Target.PathWithNamespace, event.Repository.Homepage)
handlers = append(handlers, &HandleWebhook{
Message: message,
ToUsers: newlyUnassigned,
From: senderGitlabUsername,
})
}
Expand Down Expand Up @@ -139,6 +151,7 @@ func (w *webhook) handleChannelMergeRequest(ctx context.Context, event *gitlab.M
res := []*HandleWebhook{}
var warnings []string
message := ""
var assignMessages []string

switch pr.Action {
case actionOpen:
Expand All @@ -153,30 +166,37 @@ func (w *webhook) handleChannelMergeRequest(ctx context.Context, event *gitlab.M
message = fmt.Sprintf("[%s](%s) Merge request [!%v %s](%s) was approved by [%s](%s)", repo.PathWithNamespace, repo.WebURL, pr.IID, pr.Title, pr.URL, senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername))
case actionUnapproved:
message = fmt.Sprintf("[%s](%s) Merge request [!%v %s](%s) changes were requested by [%s](%s)", repo.PathWithNamespace, repo.WebURL, pr.IID, pr.Title, pr.URL, senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername))
}

if len(message) > 0 {
toChannels := make([]string, 0)
namespace, project := normalizeNamespacedProject(repo.PathWithNamespace)
subs := w.gitlabRetreiver.GetSubscribedChannelsForProject(
ctx, namespace, project,
repo.Visibility == gitlab.PublicVisibility,
)
for _, sub := range subs {
if !sub.Merges() {
continue
case actionUpdate:
if event.Changes.Assignees.Current != nil || event.Changes.Assignees.Previous != nil {
newlyAssigned := w.calculateUserDiffs(event.Changes.Assignees.Previous, event.Changes.Assignees.Current)
newlyUnassigned := w.calculateUserDiffs(event.Changes.Assignees.Current, event.Changes.Assignees.Previous)

for _, username := range newlyAssigned {
msg := fmt.Sprintf("[%s](%s) Merge request [!%v %s](%s) was assigned to [%s](%s) by [%s](%s)",
repo.PathWithNamespace, repo.WebURL, pr.IID, pr.Title, pr.URL,
username, w.gitlabRetreiver.GetUserURL(username),
senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername))
assignMessages = append(assignMessages, msg)
}

labels, err := sub.Labels()
if err != nil {
warnings = append(warnings, err.Error())
} else if len(labels) > 0 && !containsAnyLabel(event.Labels, labels) {
continue
for _, username := range newlyUnassigned {
msg := fmt.Sprintf("[%s](%s) Merge request [!%v %s](%s) was unassigned from [%s](%s) by [%s](%s)",
repo.PathWithNamespace, repo.WebURL, pr.IID, pr.Title, pr.URL,
username, w.gitlabRetreiver.GetUserURL(username),
senderGitlabUsername, w.gitlabRetreiver.GetUserURL(senderGitlabUsername))
assignMessages = append(assignMessages, msg)
}

toChannels = append(toChannels, sub.ChannelID)
}
}

namespace, project := normalizeNamespacedProject(repo.PathWithNamespace)
subs := w.gitlabRetreiver.GetSubscribedChannelsForProject(
ctx, namespace, project,
repo.Visibility == gitlab.PublicVisibility,
)

if len(message) > 0 {
toChannels, ws := filterChannelsByFeature(subs, event.Labels, (*subscription.Subscription).Merges)
warnings = append(warnings, ws...)
if len(toChannels) > 0 {
res = append(res, &HandleWebhook{
From: senderGitlabUsername,
Expand All @@ -187,6 +207,23 @@ func (w *webhook) handleChannelMergeRequest(ctx context.Context, event *gitlab.M
}
}

if len(assignMessages) > 0 {
toChannels, ws := filterChannelsByFeature(subs, event.Labels, func(sub *subscription.Subscription) bool {
Comment on lines +210 to +211
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will existing channel subscriptions start to get the notifications for assigned/unassiged?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, from what I could understand the merges subscription pertains to anything related to merge requests, so these events will automatically kick-in for subscriptions made at that level, and the new merge_request_assigns is a more granular event added in case users would like to individually select these events to be included in a subscription

return sub.Merges() || sub.MergeRequestAssigns()
})
warnings = append(warnings, ws...)
if len(toChannels) > 0 {
for _, msg := range assignMessages {
res = append(res, &HandleWebhook{
From: senderGitlabUsername,
Message: msg,
ToUsers: []string{},
ToChannels: toChannels,
})
}
}
}

return res, warnings, nil
}

Expand Down
172 changes: 172 additions & 0 deletions server/webhook/merge_request_fixture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,178 @@ const RootUpdateReviewerMergeRequest = `{
}
}`

const RootUnassignUserMergeRequest = `{
"object_kind":"merge_request",
"event_type":"merge_request",
"user":{
"name":"Administrator",
"username":"root",
"avatar_url":"https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon"
},
"project":{
"id":24,
"name":"webhook",
"namespace":"manland",
"visibility_level":20,
"path_with_namespace":"manland/webhook",
"web_url":"http://localhost:3000/manland/webhook",
"http_url":"http://localhost:3000/manland/webhook.git"
},
"object_attributes":{
"assignee_id":null,
"author_id":1,
"created_at":"2019-04-03 21:07:32 UTC",
"description":"test open merge request",
"id":35,
"iid":4,
"merge_status":"can_be_merged",
"state":"opened",
"title":"Master-2",
"url":"http://localhost:3000/manland/webhook/merge_requests/4",
"source":{
"id":25,
"name":"webhook",
"namespace":"root",
"visibility_level":20,
"path_with_namespace":"root/webhook",
"http_url":"http://localhost:3000/root/webhook.git"
},
"target":{
"id":24,
"name":"webhook",
"namespace":"manland",
"visibility_level":20,
"path_with_namespace":"manland/webhook",
"http_url":"http://localhost:3000/manland/webhook.git"
},
"last_commit":{
"id":"1fd967c14f8265a6056525c343d984ce56472d5c",
"message":"Update README.md",
"timestamp":"2019-04-03T21:04:58Z",
"url":"http://localhost:3000/manland/webhook/commit/1fd967c14f8265a6056525c343d984ce56472d5c",
"author":{
"name":"Administrator",
"email":"admin@example.com"
}
},
"assignee_ids": [],
"action":"update"
},
"changes":{
"assignees":{
"previous":[
{
"id": 50,
"name": "manland",
"username": "manland",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon"
}
],
"current":[]
}
},
"assignees": [],
"repository":{
"name":"webhook",
"url":"ssh://rmaneschi@localhost:2222/manland/webhook.git",
"description":"",
"homepage":"http://localhost:3000/manland/webhook"
}
}`

const RootAssignMergeRequestWithChannel = `{
"object_kind":"merge_request",
"event_type":"merge_request",
"user":{
"name":"Administrator",
"username":"root",
"avatar_url":"https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon"
},
"project":{
"id":24,
"name":"webhook",
"namespace":"manland",
"visibility_level":20,
"path_with_namespace":"manland/webhook",
"web_url":"http://localhost:3000/manland/webhook",
"http_url":"http://localhost:3000/manland/webhook.git"
},
"object_attributes":{
"assignee_id":50,
"author_id":1,
"created_at":"2019-04-03 21:07:32 UTC",
"description":"test open merge request",
"id":35,
"iid":4,
"merge_status":"can_be_merged",
"state":"opened",
"title":"Master-2",
"url":"http://localhost:3000/manland/webhook/merge_requests/4",
"source":{
"id":25,
"name":"webhook",
"namespace":"root",
"visibility_level":20,
"path_with_namespace":"root/webhook",
"http_url":"http://localhost:3000/root/webhook.git"
},
"target":{
"id":24,
"name":"webhook",
"namespace":"manland",
"visibility_level":20,
"path_with_namespace":"manland/webhook",
"http_url":"http://localhost:3000/manland/webhook.git"
},
"last_commit":{
"id":"1fd967c14f8265a6056525c343d984ce56472d5c",
"message":"Update README.md",
"timestamp":"2019-04-03T21:04:58Z",
"url":"http://localhost:3000/manland/webhook/commit/1fd967c14f8265a6056525c343d984ce56472d5c",
"author":{
"name":"Administrator",
"email":"admin@example.com"
}
},
"assignee_ids": [50],
"action":"update"
},
"changes":{
"assignees":{
"previous":[
{
"id": 100,
"name": "user",
"username": "user",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon"
}
],
"current":[
{
"id": 50,
"name": "manland",
"username": "manland",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon"
}
]
}
},
"assignees": [
{
"id": 50,
"name": "manland",
"username": "manland",
"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon"
}
],
"repository":{
"name":"webhook",
"url":"ssh://rmaneschi@localhost:2222/manland/webhook.git",
"description":"",
"homepage":"http://localhost:3000/manland/webhook"
}
}`

const MultipleEventsMergeRequest = `{
"object_kind":"merge_request",
"event_type":"merge_request",
Expand Down
Loading
Loading