Send notifications to partecipants in issue comments (#1217)
* Send notifications to partecipants in issue comments Closes #1216 Includes test (still failing) * Do not include "labelers" to participants Fix test to expect what GetParticipants returnrelease/v1.15
parent
ca1c3f1926
commit
447c9b428f
|
@ -2,6 +2,18 @@
|
||||||
id: 1
|
id: 1
|
||||||
type: 7 # label
|
type: 7 # label
|
||||||
poster_id: 2
|
poster_id: 2
|
||||||
issue_id: 1
|
issue_id: 1 # in repo_id 1
|
||||||
label_id: 1
|
label_id: 1
|
||||||
content: "1"
|
content: "1"
|
||||||
|
-
|
||||||
|
id: 2
|
||||||
|
type: 0 # comment
|
||||||
|
poster_id: 3 # user not watching (see watch.yml)
|
||||||
|
issue_id: 1 # in repo_id 1
|
||||||
|
content: "good work!"
|
||||||
|
-
|
||||||
|
id: 3
|
||||||
|
type: 0 # comment
|
||||||
|
poster_id: 5 # user not watching (see watch.yml)
|
||||||
|
issue_id: 1 # in repo_id 1
|
||||||
|
content: "meh..."
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
content: content1
|
content: content1
|
||||||
is_closed: false
|
is_closed: false
|
||||||
is_pull: false
|
is_pull: false
|
||||||
num_comments: 0
|
num_comments: 2
|
||||||
created_unix: 946684800
|
created_unix: 946684800
|
||||||
updated_unix: 978307200
|
updated_unix: 978307200
|
||||||
|
|
||||||
|
|
|
@ -1134,6 +1134,24 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
|
||||||
return issues, nil
|
return issues, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetParticipantsByIssueID returns all users who are participated in comments of an issue.
|
||||||
|
func GetParticipantsByIssueID(issueID int64) ([]*User, error) {
|
||||||
|
userIDs := make([]int64, 0, 5)
|
||||||
|
if err := x.Table("comment").Cols("poster_id").
|
||||||
|
Where("issue_id = ?", issueID).
|
||||||
|
And("type = ?", CommentTypeComment).
|
||||||
|
Distinct("poster_id").
|
||||||
|
Find(&userIDs); err != nil {
|
||||||
|
return nil, fmt.Errorf("get poster IDs: %v", err)
|
||||||
|
}
|
||||||
|
if len(userIDs) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
users := make([]*User, 0, len(userIDs))
|
||||||
|
return users, x.In("id", userIDs).Find(&users)
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateIssueMentions extracts mentioned people from content and
|
// UpdateIssueMentions extracts mentioned people from content and
|
||||||
// updates issue-user relations for them.
|
// updates issue-user relations for them.
|
||||||
func UpdateIssueMentions(e Engine, issueID int64, mentions []string) error {
|
func UpdateIssueMentions(e Engine, issueID int64, mentions []string) error {
|
||||||
|
|
|
@ -19,15 +19,27 @@ func (issue *Issue) mailSubject() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// mailIssueCommentToParticipants can be used for both new issue creation and comment.
|
// mailIssueCommentToParticipants can be used for both new issue creation and comment.
|
||||||
|
// This function sends two list of emails:
|
||||||
|
// 1. Repository watchers and users who are participated in comments.
|
||||||
|
// 2. Users who are not in 1. but get mentioned in current issue/comment.
|
||||||
func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string) error {
|
func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string) error {
|
||||||
if !setting.Service.EnableNotifyMail {
|
if !setting.Service.EnableNotifyMail {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mail watchers.
|
|
||||||
watchers, err := GetWatchers(issue.RepoID)
|
watchers, err := GetWatchers(issue.RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetWatchers [%d]: %v", issue.RepoID, err)
|
return fmt.Errorf("GetWatchers [repo_id: %d]: %v", issue.RepoID, err)
|
||||||
|
}
|
||||||
|
participants, err := GetParticipantsByIssueID(issue.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("GetParticipantsByIssueID [issue_id: %d]: %v", issue.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case the issue poster is not watching the repository,
|
||||||
|
// even if we have duplicated in watchers, can be safely filtered out.
|
||||||
|
if issue.PosterID != doer.ID {
|
||||||
|
participants = append(participants, issue.Poster)
|
||||||
}
|
}
|
||||||
|
|
||||||
tos := make([]string, 0, len(watchers)) // List of email addresses.
|
tos := make([]string, 0, len(watchers)) // List of email addresses.
|
||||||
|
@ -48,6 +60,16 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
|
||||||
tos = append(tos, to.Email)
|
tos = append(tos, to.Email)
|
||||||
names = append(names, to.Name)
|
names = append(names, to.Name)
|
||||||
}
|
}
|
||||||
|
for i := range participants {
|
||||||
|
if participants[i].ID == doer.ID {
|
||||||
|
continue
|
||||||
|
} else if com.IsSliceContainsStr(names, participants[i].Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tos = append(tos, participants[i].Email)
|
||||||
|
names = append(names, participants[i].Name)
|
||||||
|
}
|
||||||
SendIssueCommentMail(issue, doer, tos)
|
SendIssueCommentMail(issue, doer, tos)
|
||||||
|
|
||||||
// Mail mentioned people and exclude watchers.
|
// Mail mentioned people and exclude watchers.
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -58,3 +59,26 @@ func TestGetIssuesByIDs(t *testing.T) {
|
||||||
testSuccess([]int64{1, 2, 3}, []int64{})
|
testSuccess([]int64{1, 2, 3}, []int64{})
|
||||||
testSuccess([]int64{1, 2, 3}, []int64{NonexistentID})
|
testSuccess([]int64{1, 2, 3}, []int64{NonexistentID})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetParticipantsByIssueID(t *testing.T) {
|
||||||
|
|
||||||
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
|
||||||
|
checkPartecipants := func(issueID int64, userIDs []int) {
|
||||||
|
partecipants, err := GetParticipantsByIssueID(issueID)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
partecipantsIDs := make([]int,len(partecipants))
|
||||||
|
for i,u := range partecipants { partecipantsIDs[i] = int(u.ID) }
|
||||||
|
sort.Ints(partecipantsIDs)
|
||||||
|
sort.Ints(userIDs)
|
||||||
|
assert.Equal(t, userIDs, partecipantsIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// User 1 is issue1 poster (see fixtures/issue.yml)
|
||||||
|
// User 2 only labeled issue1 (see fixtures/comment.yml)
|
||||||
|
// Users 3 and 5 made actual comments (see fixtures/comment.yml)
|
||||||
|
checkPartecipants(1, []int{3,5})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue