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

This commit is contained in:
Joe Chen
2022-10-30 23:04:24 +08:00
committed by GitHub
parent 131be6e074
commit 1905b19ee7
18 changed files with 165 additions and 80 deletions

View File

@@ -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},

View File

@@ -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},

View File

@@ -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}

View File

@@ -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}

View File

@@ -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},

View File

@@ -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},

View File

@@ -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},

View File

@@ -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
internal/db/org_users.go Normal file
View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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},

View File

@@ -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},

View File

@@ -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},

View File

@@ -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,

View File

@@ -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)
}

View File

@@ -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},

View File

@@ -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},
} {

View File

@@ -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))