diff --git a/models/fixtures/follow.yml b/models/fixtures/follow.yml new file mode 100644 index 000000000..53db1e88b --- /dev/null +++ b/models/fixtures/follow.yml @@ -0,0 +1,4 @@ +- + id: 1 + user_id: 4 + follow_id: 2 diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 5a2dd2f08..409747aa1 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -26,6 +26,7 @@ avatar_email: user2@example.com num_repos: 2 num_stars: 2 + num_followers: 1 - id: 3 @@ -56,6 +57,7 @@ avatar: avatar4 avatar_email: user4@example.com num_repos: 0 + num_following: 1 - id: 5 @@ -72,6 +74,7 @@ num_repos: 1 allow_create_organization: false is_active: true + num_following: 0 - id: 6 diff --git a/models/user.go b/models/user.go index 54a60acd8..a7e22659e 100644 --- a/models/user.go +++ b/models/user.go @@ -1292,78 +1292,6 @@ func SearchUserByName(opts *SearchUserOptions) (users []*User, _ int64, _ error) return users, count, sess.Find(&users) } -// ___________ .__ .__ -// \_ _____/___ | | | | ______ _ __ -// | __)/ _ \| | | | / _ \ \/ \/ / -// | \( <_> ) |_| |_( <_> ) / -// \___ / \____/|____/____/\____/ \/\_/ -// \/ - -// Follow represents relations of user and his/her followers. -type Follow struct { - ID int64 `xorm:"pk autoincr"` - UserID int64 `xorm:"UNIQUE(follow)"` - FollowID int64 `xorm:"UNIQUE(follow)"` -} - -// IsFollowing returns true if user is following followID. -func IsFollowing(userID, followID int64) bool { - has, _ := x.Get(&Follow{UserID: userID, FollowID: followID}) - return has -} - -// FollowUser marks someone be another's follower. -func FollowUser(userID, followID int64) (err error) { - if userID == followID || IsFollowing(userID, followID) { - return nil - } - - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { - return err - } - - if _, err = sess.Insert(&Follow{UserID: userID, FollowID: followID}); err != nil { - return err - } - - if _, err = sess.Exec("UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { - return err - } - - if _, err = sess.Exec("UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil { - return err - } - return sess.Commit() -} - -// UnfollowUser unmarks someone as another's follower. -func UnfollowUser(userID, followID int64) (err error) { - if userID == followID || !IsFollowing(userID, followID) { - return nil - } - - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { - return err - } - - if _, err = sess.Delete(&Follow{UserID: userID, FollowID: followID}); err != nil { - return err - } - - if _, err = sess.Exec("UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { - return err - } - - if _, err = sess.Exec("UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { - return err - } - return sess.Commit() -} - // GetStarredRepos returns the repos starred by a particular user func GetStarredRepos(userID int64, private bool) ([]*Repository, error) { sess := x.Where("star.uid=?", userID). diff --git a/models/user_follow.go b/models/user_follow.go new file mode 100644 index 000000000..ac0cf0a8f --- /dev/null +++ b/models/user_follow.go @@ -0,0 +1,71 @@ +// 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 + +// Follow represents relations of user and his/her followers. +type Follow struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"UNIQUE(follow)"` + FollowID int64 `xorm:"UNIQUE(follow)"` +} + +// IsFollowing returns true if user is following followID. +func IsFollowing(userID, followID int64) bool { + has, _ := x.Get(&Follow{UserID: userID, FollowID: followID}) + return has +} + +// FollowUser marks someone be another's follower. +func FollowUser(userID, followID int64) (err error) { + if userID == followID || IsFollowing(userID, followID) { + return nil + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if _, err = sess.Insert(&Follow{UserID: userID, FollowID: followID}); err != nil { + return err + } + + if _, err = sess.Exec("UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { + return err + } + + if _, err = sess.Exec("UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil { + return err + } + return sess.Commit() +} + +// UnfollowUser unmarks someone as another's follower. +func UnfollowUser(userID, followID int64) (err error) { + if userID == followID || !IsFollowing(userID, followID) { + return nil + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if _, err = sess.Delete(&Follow{UserID: userID, FollowID: followID}); err != nil { + return err + } + + if _, err = sess.Exec("UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { + return err + } + + if _, err = sess.Exec("UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { + return err + } + return sess.Commit() +} + diff --git a/models/user_follow_test.go b/models/user_follow_test.go new file mode 100644 index 000000000..59392dcb1 --- /dev/null +++ b/models/user_follow_test.go @@ -0,0 +1,45 @@ +package models + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsFollowing(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + assert.True(t, IsFollowing(4, 2)) + assert.False(t, IsFollowing(2, 4)) + assert.False(t, IsFollowing(5, NonexistentID)) + assert.False(t, IsFollowing(NonexistentID, 5)) + assert.False(t, IsFollowing(NonexistentID, NonexistentID)) +} + +func TestFollowUser(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + testSuccess := func(followerID, followedID int64) { + assert.NoError(t, FollowUser(followerID, followedID)) + AssertExistsAndLoadBean(t, &Follow{UserID: followerID, FollowID: followedID}) + } + testSuccess(4, 2) + testSuccess(5, 2) + + assert.NoError(t, FollowUser(2, 2)) + + CheckConsistencyFor(t, &User{}) +} + +func TestUnfollowUser(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + testSuccess := func(followerID, followedID int64) { + assert.NoError(t, UnfollowUser(followerID, followedID)) + AssertNotExistsBean(t, &Follow{UserID: followerID, FollowID: followedID}) + } + testSuccess(4, 2) + testSuccess(5, 2) + testSuccess(2, 2) + + CheckConsistencyFor(t, &User{}) +}