-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
847527f
commit be48b32
Showing
4 changed files
with
202 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,7 @@ | |
num_closed_issues: 0 | ||
num_pulls: 0 | ||
num_closed_pulls: 0 | ||
num_watches: 0 | ||
|
||
- | ||
id: 4 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// Copyright 2017 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package models | ||
|
||
import "fmt" | ||
|
||
// Watch is connection request for receiving repository notification. | ||
type Watch struct { | ||
ID int64 `xorm:"pk autoincr"` | ||
UserID int64 `xorm:"UNIQUE(watch)"` | ||
RepoID int64 `xorm:"UNIQUE(watch)"` | ||
} | ||
|
||
func isWatching(e Engine, userID, repoID int64) bool { | ||
has, _ := e.Get(&Watch{UserID: userID, RepoID: repoID}) | ||
return has | ||
} | ||
|
||
// IsWatching checks if user has watched given repository. | ||
func IsWatching(userID, repoID int64) bool { | ||
return isWatching(x, userID, repoID) | ||
} | ||
|
||
func watchRepo(e Engine, userID, repoID int64, watch bool) (err error) { | ||
if watch { | ||
if isWatching(e, userID, repoID) { | ||
return nil | ||
} | ||
if _, err = e.Insert(&Watch{RepoID: repoID, UserID: userID}); err != nil { | ||
return err | ||
} | ||
_, err = e.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", repoID) | ||
} else { | ||
if !isWatching(e, userID, repoID) { | ||
return nil | ||
} | ||
if _, err = e.Delete(&Watch{0, userID, repoID}); err != nil { | ||
return err | ||
} | ||
_, err = e.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repoID) | ||
} | ||
return err | ||
} | ||
|
||
// WatchRepo watch or unwatch repository. | ||
func WatchRepo(userID, repoID int64, watch bool) (err error) { | ||
return watchRepo(x, userID, repoID, watch) | ||
} | ||
|
||
func getWatchers(e Engine, repoID int64) ([]*Watch, error) { | ||
watches := make([]*Watch, 0, 10) | ||
return watches, e.Find(&watches, &Watch{RepoID: repoID}) | ||
} | ||
|
||
// GetWatchers returns all watchers of given repository. | ||
func GetWatchers(repoID int64) ([]*Watch, error) { | ||
return getWatchers(x, repoID) | ||
} | ||
|
||
// GetWatchers returns range of users watching given repository. | ||
func (repo *Repository) GetWatchers(page int) ([]*User, error) { | ||
users := make([]*User, 0, ItemsPerPage) | ||
sess := x.Where("watch.repo_id=?", repo.ID). | ||
Join("LEFT", "watch", "`user`.id=`watch`.user_id") | ||
if page > 0 { | ||
sess = sess.Limit(ItemsPerPage, (page-1)*ItemsPerPage) | ||
} | ||
return users, sess.Find(&users) | ||
} | ||
|
||
func notifyWatchers(e Engine, act *Action) error { | ||
// Add feeds for user self and all watchers. | ||
watches, err := getWatchers(e, act.RepoID) | ||
if err != nil { | ||
return fmt.Errorf("get watchers: %v", err) | ||
} | ||
|
||
// Add feed for actioner. | ||
act.UserID = act.ActUserID | ||
if _, err = e.InsertOne(act); err != nil { | ||
return fmt.Errorf("insert new actioner: %v", err) | ||
} | ||
|
||
for i := range watches { | ||
if act.ActUserID == watches[i].UserID { | ||
continue | ||
} | ||
|
||
act.ID = 0 | ||
act.UserID = watches[i].UserID | ||
if _, err = e.InsertOne(act); err != nil { | ||
return fmt.Errorf("insert new action: %v", err) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// NotifyWatchers creates batch of actions for every watcher. | ||
func NotifyWatchers(act *Action) error { | ||
return notifyWatchers(x, act) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// Copyright 2017 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package models | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestIsWatching(t *testing.T) { | ||
assert.NoError(t, PrepareTestDatabase()) | ||
|
||
assert.True(t, IsWatching(1, 1)) | ||
assert.True(t, IsWatching(4, 1)) | ||
|
||
assert.False(t, IsWatching(1, 5)) | ||
assert.False(t, IsWatching(NonexistentID, NonexistentID)) | ||
} | ||
|
||
func TestWatchRepo(t *testing.T) { | ||
assert.NoError(t, PrepareTestDatabase()) | ||
const repoID = 3 | ||
const userID = 2 | ||
|
||
assert.NoError(t, WatchRepo(userID, repoID, true)) | ||
AssertExistsAndLoadBean(t, &Watch{RepoID: repoID, UserID: userID}) | ||
CheckConsistencyFor(t, &Repository{ID: repoID}) | ||
|
||
assert.NoError(t, WatchRepo(userID, repoID, false)) | ||
AssertNotExistsBean(t, &Watch{RepoID: repoID, UserID: userID}) | ||
CheckConsistencyFor(t, &Repository{ID: repoID}) | ||
} | ||
|
||
func TestGetWatchers(t *testing.T) { | ||
assert.NoError(t, PrepareTestDatabase()) | ||
|
||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) | ||
watches, err := GetWatchers(repo.ID) | ||
assert.NoError(t, err) | ||
assert.Len(t, watches, repo.NumWatches) | ||
for _, watch := range watches { | ||
assert.EqualValues(t, repo.ID, watch.RepoID) | ||
} | ||
|
||
watches, err = GetWatchers(NonexistentID) | ||
assert.NoError(t, err) | ||
assert.Len(t, watches, 0) | ||
} | ||
|
||
func TestRepository_GetWatchers(t *testing.T) { | ||
assert.NoError(t, PrepareTestDatabase()) | ||
|
||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) | ||
watchers, err := repo.GetWatchers(1) | ||
assert.NoError(t, err) | ||
assert.Len(t, watchers, repo.NumWatches) | ||
for _, watcher := range watchers { | ||
AssertExistsAndLoadBean(t, &Watch{UserID: watcher.ID, RepoID: repo.ID}) | ||
} | ||
|
||
repo = AssertExistsAndLoadBean(t, &Repository{ID: 10}).(*Repository) | ||
watchers, err = repo.GetWatchers(1) | ||
assert.NoError(t, err) | ||
assert.Len(t, watchers, 0) | ||
} | ||
|
||
func TestNotifyWatchers(t *testing.T) { | ||
assert.NoError(t, PrepareTestDatabase()) | ||
|
||
action := &Action{ | ||
ActUserID: 8, | ||
RepoID: 1, | ||
OpType: ActionStarRepo, | ||
} | ||
assert.NoError(t, NotifyWatchers(action)) | ||
|
||
AssertExistsAndLoadBean(t, &Action{ | ||
ActUserID: action.ActUserID, | ||
UserID: 1, | ||
RepoID: action.RepoID, | ||
OpType: action.OpType, | ||
}) | ||
AssertExistsAndLoadBean(t, &Action{ | ||
ActUserID: action.ActUserID, | ||
UserID: 4, | ||
RepoID: action.RepoID, | ||
OpType: action.OpType, | ||
}) | ||
AssertExistsAndLoadBean(t, &Action{ | ||
ActUserID: action.ActUserID, | ||
UserID: 8, | ||
RepoID: action.RepoID, | ||
OpType: action.OpType, | ||
}) | ||
} |