Code Refactor of IssueWatch related things (#10401)

* refactor

* optimize

* remove Iretating function
LoadWatchUsers do not load Users into IW object and it is used only in api ... so move this logic

* remove unessesary

* Apply suggestions from code review

Thx

Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com>

* make Tests more robust

* fix rebase

* restart CI

* CI no dont hit sqlites deadlock

Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
This commit is contained in:
6543 2020-02-26 07:32:22 +01:00 committed by GitHub
parent e5944a9521
commit 084a2b0026
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 66 additions and 80 deletions

View file

@ -207,11 +207,14 @@ func TestCreateOrUpdateRepoFileForCreate(t *testing.T) {
commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch)
expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID) expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID)
assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) assert.NotNil(t, expectedFileResponse)
assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) if expectedFileResponse != nil {
assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content)
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA)
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL)
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email)
assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name)
}
}) })
} }

View file

@ -68,10 +68,14 @@ func getIssueWatch(e Engine, userID, issueID int64) (iw *IssueWatch, exists bool
// but avoids joining with `user` for performance reasons // but avoids joining with `user` for performance reasons
// User permissions must be verified elsewhere if required // User permissions must be verified elsewhere if required
func GetIssueWatchersIDs(issueID int64) ([]int64, error) { func GetIssueWatchersIDs(issueID int64) ([]int64, error) {
return getIssueWatchersIDs(x, issueID, true)
}
func getIssueWatchersIDs(e Engine, issueID int64, watching bool) ([]int64, error) {
ids := make([]int64, 0, 64) ids := make([]int64, 0, 64)
return ids, x.Table("issue_watch"). return ids, e.Table("issue_watch").
Where("issue_id=?", issueID). Where("issue_id=?", issueID).
And("is_watching = ?", true). And("is_watching = ?", watching).
Select("user_id"). Select("user_id").
Find(&ids) Find(&ids)
} }
@ -99,39 +103,9 @@ func getIssueWatchers(e Engine, issueID int64, listOptions ListOptions) (IssueWa
} }
func removeIssueWatchersByRepoID(e Engine, userID int64, repoID int64) error { func removeIssueWatchersByRepoID(e Engine, userID int64, repoID int64) error {
iw := &IssueWatch{
IsWatching: false,
}
_, err := e. _, err := e.
Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID). Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID).
Cols("is_watching", "updated_unix").
Where("`issue_watch`.user_id = ?", userID). Where("`issue_watch`.user_id = ?", userID).
Update(iw) Delete(new(IssueWatch))
return err return err
} }
// LoadWatchUsers return watching users
func (iwl IssueWatchList) LoadWatchUsers() (users UserList, err error) {
return iwl.loadWatchUsers(x)
}
func (iwl IssueWatchList) loadWatchUsers(e Engine) (users UserList, err error) {
if len(iwl) == 0 {
return []*User{}, nil
}
var userIDs = make([]int64, 0, len(iwl))
for _, iw := range iwl {
if iw.IsWatching {
userIDs = append(userIDs, iw.UserID)
}
}
if len(userIDs) == 0 {
return []*User{}, nil
}
err = e.In("id", userIDs).Find(&users)
return
}

View file

@ -133,55 +133,42 @@ func CreateOrUpdateIssueNotifications(issueID, commentID int64, notificationAuth
} }
func createOrUpdateIssueNotifications(e Engine, issueID, commentID int64, notificationAuthorID int64) error { func createOrUpdateIssueNotifications(e Engine, issueID, commentID int64, notificationAuthorID int64) error {
issueWatches, err := getIssueWatchers(e, issueID, ListOptions{}) // init
toNotify := make(map[int64]struct{}, 32)
notifications, err := getNotificationsByIssueID(e, issueID)
if err != nil { if err != nil {
return err return err
} }
issue, err := getIssueByID(e, issueID) issue, err := getIssueByID(e, issueID)
if err != nil { if err != nil {
return err return err
} }
watches, err := getWatchers(e, issue.RepoID) issueWatches, err := getIssueWatchersIDs(e, issueID, true)
if err != nil { if err != nil {
return err return err
} }
for _, id := range issueWatches {
toNotify[id] = struct{}{}
}
notifications, err := getNotificationsByIssueID(e, issueID) repoWatches, err := getRepoWatchersIDs(e, issue.RepoID)
if err != nil { if err != nil {
return err return err
} }
for _, id := range repoWatches {
alreadyNotified := make(map[int64]struct{}, len(issueWatches)+len(watches)) toNotify[id] = struct{}{}
notifyUser := func(userID int64) error {
// do not send notification for the own issuer/commenter
if userID == notificationAuthorID {
return nil
}
if _, ok := alreadyNotified[userID]; ok {
return nil
}
alreadyNotified[userID] = struct{}{}
if notificationExists(notifications, issue.ID, userID) {
return updateIssueNotification(e, userID, issue.ID, commentID, notificationAuthorID)
}
return createIssueNotification(e, userID, issue, commentID, notificationAuthorID)
} }
for _, issueWatch := range issueWatches { // dont notify user who cause notification
// ignore if user unwatched the issue delete(toNotify, notificationAuthorID)
if !issueWatch.IsWatching { // explicit unwatch on issue
alreadyNotified[issueWatch.UserID] = struct{}{} issueUnWatches, err := getIssueWatchersIDs(e, issueID, false)
continue if err != nil {
} return err
}
if err := notifyUser(issueWatch.UserID); err != nil { for _, id := range issueUnWatches {
return err delete(toNotify, id)
}
} }
err = issue.loadRepo(e) err = issue.loadRepo(e)
@ -189,16 +176,23 @@ func createOrUpdateIssueNotifications(e Engine, issueID, commentID int64, notifi
return err return err
} }
for _, watch := range watches { // notify
for userID := range toNotify {
issue.Repo.Units = nil issue.Repo.Units = nil
if issue.IsPull && !issue.Repo.checkUnitUser(e, watch.UserID, false, UnitTypePullRequests) { if issue.IsPull && !issue.Repo.checkUnitUser(e, userID, false, UnitTypePullRequests) {
continue continue
} }
if !issue.IsPull && !issue.Repo.checkUnitUser(e, watch.UserID, false, UnitTypeIssues) { if !issue.IsPull && !issue.Repo.checkUnitUser(e, userID, false, UnitTypeIssues) {
continue continue
} }
if err := notifyUser(watch.UserID); err != nil { if notificationExists(notifications, issue.ID, userID) {
if err = updateIssueNotification(e, userID, issue.ID, commentID, notificationAuthorID); err != nil {
return err
}
continue
}
if err = createIssueNotification(e, userID, issue, commentID, notificationAuthorID); err != nil {
return err return err
} }
} }

View file

@ -144,8 +144,12 @@ func GetWatchers(repoID int64) ([]*Watch, error) {
// but avoids joining with `user` for performance reasons // but avoids joining with `user` for performance reasons
// User permissions must be verified elsewhere if required // User permissions must be verified elsewhere if required
func GetRepoWatchersIDs(repoID int64) ([]int64, error) { func GetRepoWatchersIDs(repoID int64) ([]int64, error) {
return getRepoWatchersIDs(x, repoID)
}
func getRepoWatchersIDs(e Engine, repoID int64) ([]int64, error) {
ids := make([]int64, 0, 64) ids := make([]int64, 0, 64)
return ids, x.Table("watch"). return ids, e.Table("watch").
Where("watch.repo_id=?", repoID). Where("watch.repo_id=?", repoID).
And("watch.mode<>?", RepoWatchModeDont). And("watch.mode<>?", RepoWatchModeDont).
Select("user_id"). Select("user_id").

View file

@ -1409,7 +1409,7 @@ func GetUserNamesByIDs(ids []int64) ([]string, error) {
} }
// GetUsersByIDs returns all resolved users from a list of Ids. // GetUsersByIDs returns all resolved users from a list of Ids.
func GetUsersByIDs(ids []int64) ([]*User, error) { func GetUsersByIDs(ids []int64) (UserList, error) {
ous := make([]*User, 0, len(ids)) ous := make([]*User, 0, len(ids))
if len(ids) == 0 { if len(ids) == 0 {
return ous, nil return ous, nil

View file

@ -48,6 +48,9 @@ type Branch struct {
// GetHEADBranch returns corresponding branch of HEAD. // GetHEADBranch returns corresponding branch of HEAD.
func (repo *Repository) GetHEADBranch() (*Branch, error) { func (repo *Repository) GetHEADBranch() (*Branch, error) {
if repo == nil {
return nil, fmt.Errorf("nil repo")
}
stdout, err := NewCommand("symbolic-ref", "HEAD").RunInDir(repo.Path) stdout, err := NewCommand("symbolic-ref", "HEAD").RunInDir(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -58,8 +58,11 @@ func LoadRepoCommit(t *testing.T, ctx *context.Context) {
defer gitRepo.Close() defer gitRepo.Close()
branch, err := gitRepo.GetHEADBranch() branch, err := gitRepo.GetHEADBranch()
assert.NoError(t, err) assert.NoError(t, err)
ctx.Repo.Commit, err = gitRepo.GetBranchCommit(branch.Name) assert.NotNil(t, branch)
assert.NoError(t, err) if branch != nil {
ctx.Repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
assert.NoError(t, err)
}
} }
// LoadUser load a user into a test context. // LoadUser load a user into a test context.

View file

@ -190,9 +190,14 @@ func GetIssueSubscribers(ctx *context.APIContext) {
return return
} }
users, err := iwl.LoadWatchUsers() var userIDs = make([]int64, 0, len(iwl))
for _, iw := range iwl {
userIDs = append(userIDs, iw.UserID)
}
users, err := models.GetUsersByIDs(userIDs)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "LoadWatchUsers", err) ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err)
return return
} }