Prechádzať zdrojové kódy

refactor(db): migrate methods off `user.go` (#7219)

Joe Chen 2 rokov pred
rodič
commit
1905b19ee7

+ 1 - 1
internal/db/access_tokens_test.go

@@ -105,7 +105,7 @@ func TestAccessTokens(t *testing.T) {
 
 	for _, tc := range []struct {
 		name string
-		test func(*testing.T, *accessTokens)
+		test func(t *testing.T, db *accessTokens)
 	}{
 		{"Create", accessTokensCreate},
 		{"DeleteByID", accessTokensDeleteByID},

+ 1 - 1
internal/db/actions_test.go

@@ -106,7 +106,7 @@ func TestActions(t *testing.T) {
 
 	for _, tc := range []struct {
 		name string
-		test func(*testing.T, *actions)
+		test func(t *testing.T, db *actions)
 	}{
 		{"CommitRepo", actionsCommitRepo},
 		{"ListByOrganization", actionsListByOrganization},

+ 1 - 0
internal/db/db.go

@@ -124,6 +124,7 @@ func Init(w logger.Writer) (*gorm.DB, error) {
 	Follows = NewFollowsStore(db)
 	LoginSources = &loginSources{DB: db, files: sourceFiles}
 	LFS = &lfs{DB: db}
+	OrgUsers = NewOrgUsersStore(db)
 	Perms = &perms{DB: db}
 	Repos = NewReposStore(db)
 	TwoFactors = &twoFactors{DB: db}

+ 2 - 2
internal/db/follows.go

@@ -11,7 +11,7 @@ import (
 	"gorm.io/gorm"
 )
 
-// FollowsStore is the persistent interface for follows.
+// FollowsStore is the persistent interface for user follows.
 //
 // NOTE: All methods are sorted in alphabetical order.
 type FollowsStore interface {
@@ -31,7 +31,7 @@ type follows struct {
 	*gorm.DB
 }
 
-// NewFollowsStore returns a persistent interface for follows with given
+// NewFollowsStore returns a persistent interface for user follows with given
 // database connection.
 func NewFollowsStore(db *gorm.DB) FollowsStore {
 	return &follows{DB: db}

+ 1 - 1
internal/db/follows_test.go

@@ -27,7 +27,7 @@ func TestFollows(t *testing.T) {
 
 	for _, tc := range []struct {
 		name string
-		test func(*testing.T, *follows)
+		test func(t *testing.T, db *follows)
 	}{
 		{"Follow", followsFollow},
 		{"IsFollowing", followsIsFollowing},

+ 1 - 1
internal/db/lfs_test.go

@@ -30,7 +30,7 @@ func TestLFS(t *testing.T) {
 
 	for _, tc := range []struct {
 		name string
-		test func(*testing.T, *lfs)
+		test func(t *testing.T, db *lfs)
 	}{
 		{"CreateObject", lfsCreateObject},
 		{"GetObjectByOID", lfsGetObjectByOID},

+ 2 - 2
internal/db/login_sources_test.go

@@ -157,7 +157,7 @@ func TestLoginSource_AfterFind(t *testing.T) {
 	}
 }
 
-func Test_loginSources(t *testing.T) {
+func TestLoginSources(t *testing.T) {
 	if testing.Short() {
 		t.Skip()
 	}
@@ -170,7 +170,7 @@ func Test_loginSources(t *testing.T) {
 
 	for _, tc := range []struct {
 		name string
-		test func(*testing.T, *loginSources)
+		test func(t *testing.T, db *loginSources)
 	}{
 		{"Create", loginSourcesCreate},
 		{"Count", loginSourcesCount},

+ 7 - 13
internal/db/org.go

@@ -235,14 +235,14 @@ func DeleteOrganization(org *User) (err error) {
 // \_______  /__|  \___  /|______//____  >\___  >__|
 //         \/     /_____/              \/     \/
 
-// OrgUser represents an organization-user relation.
+// OrgUser represents relations of organizations and their members.
 type OrgUser struct {
-	ID       int64
-	Uid      int64 `xorm:"INDEX UNIQUE(s)"`
-	OrgID    int64 `xorm:"INDEX UNIQUE(s)"`
-	IsPublic bool
-	IsOwner  bool
-	NumTeams int
+	ID       int64 `gorm:"primaryKey"`
+	Uid      int64 `xorm:"INDEX UNIQUE(s)" gorm:"uniqueIndex:org_user_user_org_unique;index;not null"`
+	OrgID    int64 `xorm:"INDEX UNIQUE(s)" gorm:"uniqueIndex:org_user_user_org_unique;index;not null"`
+	IsPublic bool  `gorm:"not null;default:FALSE"`
+	IsOwner  bool  `gorm:"not null;default:FALSE"`
+	NumTeams int   `gorm:"not null;default:0"`
 }
 
 // IsOrganizationOwner returns true if given user is in the owner team.
@@ -278,12 +278,6 @@ func GetOrgsByUserID(userID int64, showAll bool) ([]*User, error) {
 	return getOrgsByUserID(x.NewSession(), userID, showAll)
 }
 
-// GetOrgsByUserIDDesc returns a list of organizations that the given user ID
-// has joined, ordered descending by the given condition.
-func GetOrgsByUserIDDesc(userID int64, desc string, showAll bool) ([]*User, error) {
-	return getOrgsByUserID(x.NewSession().Desc(desc), userID, showAll)
-}
-
 func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
 	orgs := make([]*User, 0, 10)
 	return orgs, sess.Where("`org_user`.uid=?", userID).And("`org_user`.is_owner=?", true).

+ 38 - 0
internal/db/org_users.go

@@ -0,0 +1,38 @@
+// Copyright 2022 The Gogs 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 db
+
+import (
+	"context"
+
+	"gorm.io/gorm"
+)
+
+// OrgUsersStore is the persistent interface for organization-user relations.
+//
+// NOTE: All methods are sorted in alphabetical order.
+type OrgUsersStore interface {
+	// CountByUser returns the number of organizations the user is a member of.
+	CountByUser(ctx context.Context, userID int64) (int64, error)
+}
+
+var OrgUsers OrgUsersStore
+
+var _ OrgUsersStore = (*orgUsers)(nil)
+
+type orgUsers struct {
+	*gorm.DB
+}
+
+// NewOrgUsersStore returns a persistent interface for organization-user
+// relations with given database connection.
+func NewOrgUsersStore(db *gorm.DB) OrgUsersStore {
+	return &orgUsers{DB: db}
+}
+
+func (db *orgUsers) CountByUser(ctx context.Context, userID int64) (int64, error) {
+	var count int64
+	return count, db.WithContext(ctx).Model(&OrgUser{}).Where("uid = ?", userID).Count(&count).Error
+}

+ 51 - 0
internal/db/org_users_test.go

@@ -0,0 +1,51 @@
+// Copyright 2022 The Gogs 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 db
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"gogs.io/gogs/internal/dbtest"
+)
+
+func TestOrgUsers(t *testing.T) {
+	if testing.Short() {
+		t.Skip()
+	}
+	t.Parallel()
+
+	tables := []interface{}{new(OrgUser)}
+	db := &orgUsers{
+		DB: dbtest.NewDB(t, "orgUsers", tables...),
+	}
+
+	for _, tc := range []struct {
+		name string
+		test func(t *testing.T, db *orgUsers)
+	}{
+		{"CountByUser", orgUsersCountByUser},
+	} {
+		t.Run(tc.name, func(t *testing.T) {
+			t.Cleanup(func() {
+				err := clearTables(t, db.DB, tables...)
+				require.NoError(t, err)
+			})
+			tc.test(t, db)
+		})
+		if t.Failed() {
+			break
+		}
+	}
+}
+
+func orgUsersCountByUser(t *testing.T, db *orgUsers) {
+	// TODO: Use OrgUsers.Join to replace SQL hack when the method is available.
+	err := db.Exec(`INSERT INTO org_user (uid, org_id) VALUES (?, ?)`, 1, 1).Error
+	require.NoError(t, err)
+	err = db.Exec(`INSERT INTO org_user (uid, org_id) VALUES (?, ?)`, 2, 1).Error
+	require.NoError(t, err)
+}

+ 1 - 1
internal/db/perms_test.go

@@ -27,7 +27,7 @@ func TestPerms(t *testing.T) {
 
 	for _, tc := range []struct {
 		name string
-		test func(*testing.T, *perms)
+		test func(t *testing.T, db *perms)
 	}{
 		{"AccessMode", permsAccessMode},
 		{"Authorize", permsAuthorize},

+ 1 - 1
internal/db/repos_test.go

@@ -92,7 +92,7 @@ func TestRepos(t *testing.T) {
 
 	for _, tc := range []struct {
 		name string
-		test func(*testing.T, *repos)
+		test func(t *testing.T, db *repos)
 	}{
 		{"Create", reposCreate},
 		{"GetByName", reposGetByName},

+ 1 - 1
internal/db/two_factors_test.go

@@ -74,7 +74,7 @@ func TestTwoFactors(t *testing.T) {
 
 	for _, tc := range []struct {
 		name string
-		test func(*testing.T, *twoFactors)
+		test func(t *testing.T, db *twoFactors)
 	}{
 		{"Create", twoFactorsCreate},
 		{"GetByUserID", twoFactorsGetByUserID},

+ 3 - 46
internal/db/user.go

@@ -53,37 +53,13 @@ func (u *User) AfterSet(colName string, _ xorm.Cell) {
 	}
 }
 
+// Deprecated: Use OrgsUsers.CountByUser instead.
+//
+// TODO(unknwon): Delete me once no more call sites.
 func (u *User) getOrganizationCount(e Engine) (int64, error) {
 	return e.Where("uid=?", u.ID).Count(new(OrgUser))
 }
 
-// GetOrganizationCount returns count of membership of organization of user.
-func (u *User) GetOrganizationCount() (int64, error) {
-	return u.getOrganizationCount(x)
-}
-
-// GetRepositories returns repositories that user owns, including private repositories.
-func (u *User) GetRepositories(page, pageSize int) (err error) {
-	u.Repos, err = GetUserRepositories(&UserRepoOptions{
-		UserID:   u.ID,
-		Private:  true,
-		Page:     page,
-		PageSize: pageSize,
-	})
-	return err
-}
-
-// GetRepositories returns mirror repositories that user owns, including private repositories.
-func (u *User) GetMirrorRepositories() ([]*Repository, error) {
-	return GetUserMirrorRepositories(u.ID)
-}
-
-// GetOwnedOrganizations returns all organizations that user owns.
-func (u *User) GetOwnedOrganizations() (err error) {
-	u.OwnedOrgs, err = GetOwnedOrgsByUserID(u.ID)
-	return err
-}
-
 // GetOrganizations returns all organizations that user belongs to.
 func (u *User) GetOrganizations(showPrivate bool) error {
 	orgIDs, err := GetOrgIDsByUserID(u.ID, showPrivate)
@@ -101,25 +77,6 @@ func (u *User) GetOrganizations(showPrivate bool) error {
 	return nil
 }
 
-// DisplayName returns full name if it's not empty,
-// returns username otherwise.
-func (u *User) DisplayName() string {
-	if len(u.FullName) > 0 {
-		return u.FullName
-	}
-	return u.Name
-}
-
-func (u *User) ShortName(length int) string {
-	return strutil.Ellipsis(u.Name, length)
-}
-
-// IsMailable checks if a user is eligible
-// to receive emails.
-func (u *User) IsMailable() bool {
-	return u.IsActive
-}
-
 // IsUserExist checks if given user name exist,
 // the user name should be noncased unique.
 // If uid is presented, then check will rule out that one,

+ 33 - 3
internal/db/users.go

@@ -22,6 +22,7 @@ import (
 	"gogs.io/gogs/internal/cryptoutil"
 	"gogs.io/gogs/internal/errutil"
 	"gogs.io/gogs/internal/osutil"
+	"gogs.io/gogs/internal/strutil"
 	"gogs.io/gogs/internal/tool"
 	"gogs.io/gogs/internal/userutil"
 )
@@ -451,9 +452,7 @@ type User struct {
 	LoginSource int64  `xorm:"NOT NULL DEFAULT 0" gorm:"not null;default:0"`
 	LoginName   string
 	Type        UserType
-	OwnedOrgs   []*User       `xorm:"-" gorm:"-" json:"-"`
-	Orgs        []*User       `xorm:"-" gorm:"-" json:"-"`
-	Repos       []*Repository `xorm:"-" gorm:"-" json:"-"`
+	Orgs        []*User `xorm:"-" gorm:"-" json:"-"`
 	Location    string
 	Website     string
 	Rands       string `xorm:"VARCHAR(10)" gorm:"type:VARCHAR(10)"`
@@ -521,6 +520,11 @@ func (u *User) IsOrganization() bool {
 	return u.Type == UserTypeOrganization
 }
 
+// IsMailable returns true if the user is eligible to receive emails.
+func (u *User) IsMailable() bool {
+	return u.IsActive
+}
+
 // APIFormat returns the API format of a user.
 func (u *User) APIFormat() *api.User {
 	return &api.User{
@@ -562,6 +566,15 @@ func (u *User) CanImportLocal() bool {
 	return conf.Repository.EnableLocalPathMigration && (u.IsAdmin || u.AllowImportLocal)
 }
 
+// DisplayName returns the full name of the user if it's not empty, returns the
+// username otherwise.
+func (u *User) DisplayName() string {
+	if len(u.FullName) > 0 {
+		return u.FullName
+	}
+	return u.Name
+}
+
 // HomeURLPath returns the URL path to the user or organization home page.
 //
 // TODO(unknwon): This is also used in templates, which should be fixed by
@@ -649,3 +662,20 @@ func (u *User) IsUserOrgOwner(orgId int64) bool {
 func (u *User) IsPublicMember(orgId int64) bool {
 	return IsPublicMembership(orgId, u.ID)
 }
+
+// GetOrganizationCount returns the count of organization membership that the
+// user has.
+//
+// TODO(unknwon): This is also used in templates, which should be fixed by
+// having a dedicated type `template.User`.
+func (u *User) GetOrganizationCount() (int64, error) {
+	return OrgUsers.CountByUser(context.TODO(), u.ID)
+}
+
+// ShortName truncates and returns the username at most in given length.
+//
+// TODO(unknwon): This is also used in templates, which should be fixed by
+// having a dedicated type `template.User`.
+func (u *User) ShortName(length int) string {
+	return strutil.Ellipsis(u.Name, length)
+}

+ 1 - 1
internal/db/users_test.go

@@ -84,7 +84,7 @@ func TestUsers(t *testing.T) {
 
 	for _, tc := range []struct {
 		name string
-		test func(*testing.T, *users)
+		test func(t *testing.T, db *users)
 	}{
 		{"Authenticate", usersAuthenticate},
 		{"Create", usersCreate},

+ 1 - 1
internal/db/watches_test.go

@@ -25,7 +25,7 @@ func TestWatches(t *testing.T) {
 
 	for _, tc := range []struct {
 		name string
-		test func(*testing.T, *watches)
+		test func(t *testing.T, db *watches)
 	}{
 		{"ListByRepo", watchesListByRepo},
 	} {

+ 19 - 5
internal/route/user/home.go

@@ -145,14 +145,21 @@ func Dashboard(c *context.Context) {
 			return
 		}
 	} else {
-		if err = ctxUser.GetRepositories(1, conf.UI.User.RepoPagingNum); err != nil {
+		repos, err = db.GetUserRepositories(
+			&db.UserRepoOptions{
+				UserID:   ctxUser.ID,
+				Private:  true,
+				Page:     1,
+				PageSize: conf.UI.User.RepoPagingNum,
+			},
+		)
+		if err != nil {
 			c.Error(err, "get repositories")
 			return
 		}
-		repos = ctxUser.Repos
 		repoCount = int64(ctxUser.NumRepos)
 
-		mirrors, err = ctxUser.GetMirrorRepositories()
+		mirrors, err = db.GetUserMirrorRepositories(ctxUser.ID)
 		if err != nil {
 			c.Error(err, "get mirror repositories")
 			return
@@ -228,11 +235,18 @@ func Issues(c *context.Context) {
 			return
 		}
 	} else {
-		if err := ctxUser.GetRepositories(1, c.User.NumRepos); err != nil {
+		repos, err = db.GetUserRepositories(
+			&db.UserRepoOptions{
+				UserID:   ctxUser.ID,
+				Private:  true,
+				Page:     1,
+				PageSize: ctxUser.NumRepos,
+			},
+		)
+		if err != nil {
 			c.Error(err, "get repositories")
 			return
 		}
-		repos = ctxUser.Repos
 	}
 
 	userRepoIDs = make([]int64, 0, len(repos))