diff --git a/internal/db/actions_test.go b/internal/db/actions_test.go index aa6bbf4ee..15e11e9fa 100644 --- a/internal/db/actions_test.go +++ b/internal/db/actions_test.go @@ -720,6 +720,10 @@ func actionsNewRepo(t *testing.T, db *actions) { func actionsPushTag(t *testing.T, db *actions) { ctx := context.Background() + // NOTE: We set a noop mock here to avoid data race with other tests that writes + // to the mock server because this function holds a lock. + conf.SetMockServer(t, conf.ServerOpts{}) + alice, err := NewUsersStore(db.DB).Create(ctx, "alice", "alice@example.com", CreateUserOptions{}) require.NoError(t, err) repo, err := NewReposStore(db.DB).Create(ctx, diff --git a/internal/db/db.go b/internal/db/db.go index 7b063decc..2e883fc05 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -127,7 +127,7 @@ func Init(w logger.Writer) (*gorm.DB, error) { LFS = &lfs{DB: db} Orgs = NewOrgsStore(db) OrgUsers = NewOrgUsersStore(db) - Perms = &perms{DB: db} + Perms = NewPermsStore(db) Repos = NewReposStore(db) TwoFactors = &twoFactors{DB: db} Users = NewUsersStore(db) diff --git a/internal/db/perms.go b/internal/db/perms.go index a72a013a9..b0a1a85a2 100644 --- a/internal/db/perms.go +++ b/internal/db/perms.go @@ -82,6 +82,12 @@ type perms struct { *gorm.DB } +// NewPermsStore returns a persistent interface for permissions with given +// database connection. +func NewPermsStore(db *gorm.DB) PermsStore { + return &perms{DB: db} +} + type AccessModeOptions struct { OwnerID int64 // The ID of the repository owner. Private bool // Whether the repository is private. diff --git a/internal/db/repos.go b/internal/db/repos.go index 7ca78ad82..10f292b96 100644 --- a/internal/db/repos.go +++ b/internal/db/repos.go @@ -26,6 +26,16 @@ type ReposStore interface { // ErrRepoAlreadyExist when a repository with same name already exists for the // owner. Create(ctx context.Context, ownerID int64, opts CreateRepoOptions) (*Repository, error) + // GetByCollaboratorID returns a list of repositories that the given + // collaborator has access to. Results are limited to the given limit and sorted + // by the given order (e.g. "updated_unix DESC"). Repositories that are owned + // directly by the given collaborator are not included. + GetByCollaboratorID(ctx context.Context, collaboratorID int64, limit int, orderBy string) ([]*Repository, error) + // GetByCollaboratorIDWithAccessMode returns a list of repositories and + // corresponding access mode that the given collaborator has access to. + // Repositories that are owned directly by the given collaborator are not + // included. + GetByCollaboratorIDWithAccessMode(ctx context.Context, collaboratorID int64) (map[*Repository]AccessMode, error) // GetByName returns the repository with given owner and name. It returns // ErrRepoNotExist when not found. GetByName(ctx context.Context, ownerID int64, name string) (*Repository, error) @@ -170,6 +180,59 @@ func (db *repos) Create(ctx context.Context, ownerID int64, opts CreateRepoOptio return repo, db.WithContext(ctx).Create(repo).Error } +func (db *repos) GetByCollaboratorID(ctx context.Context, collaboratorID int64, limit int, orderBy string) ([]*Repository, error) { + /* + Equivalent SQL for PostgreSQL: + + SELECT * FROM repository + JOIN access ON access.repo_id = repository.id AND access.user_id = @collaboratorID + WHERE access.mode >= @accessModeRead + ORDER BY @orderBy + LIMIT @limit + */ + var repos []*Repository + return repos, db.WithContext(ctx). + Joins("JOIN access ON access.repo_id = repository.id AND access.user_id = ?", collaboratorID). + Where("access.mode >= ?", AccessModeRead). + Order(orderBy). + Limit(limit). + Find(&repos). + Error +} + +func (db *repos) GetByCollaboratorIDWithAccessMode(ctx context.Context, collaboratorID int64) (map[*Repository]AccessMode, error) { + /* + Equivalent SQL for PostgreSQL: + + SELECT + repository.*, + access.mode + FROM repository + JOIN access ON access.repo_id = repository.id AND access.user_id = @collaboratorID + WHERE access.mode >= @accessModeRead + */ + var reposWithAccessMode []*struct { + *Repository + Mode AccessMode + } + err := db.WithContext(ctx). + Select("repository.*", "access.mode"). + Table("repository"). + Joins("JOIN access ON access.repo_id = repository.id AND access.user_id = ?", collaboratorID). + Where("access.mode >= ?", AccessModeRead). + Find(&reposWithAccessMode). + Error + if err != nil { + return nil, err + } + + repos := make(map[*Repository]AccessMode, len(reposWithAccessMode)) + for _, repoWithAccessMode := range reposWithAccessMode { + repos[repoWithAccessMode.Repository] = repoWithAccessMode.Mode + } + return repos, nil +} + var _ errutil.NotFound = (*ErrRepoNotExist)(nil) type ErrRepoNotExist struct { diff --git a/internal/db/repos_test.go b/internal/db/repos_test.go index 3c01105dd..092897295 100644 --- a/internal/db/repos_test.go +++ b/internal/db/repos_test.go @@ -85,7 +85,7 @@ func TestRepos(t *testing.T) { } t.Parallel() - tables := []any{new(Repository)} + tables := []any{new(Repository), new(Access)} db := &repos{ DB: dbtest.NewDB(t, "repos", tables...), } @@ -95,6 +95,8 @@ func TestRepos(t *testing.T) { test func(t *testing.T, db *repos) }{ {"Create", reposCreate}, + {"GetByCollaboratorID", reposGetByCollaboratorID}, + {"GetByCollaboratorIDWithAccessMode", reposGetByCollaboratorIDWithAccessMode}, {"GetByName", reposGetByName}, {"Touch", reposTouch}, } { @@ -154,6 +156,64 @@ func reposCreate(t *testing.T, db *repos) { assert.Equal(t, db.NowFunc().Format(time.RFC3339), repo.Created.UTC().Format(time.RFC3339)) } +func reposGetByCollaboratorID(t *testing.T, db *repos) { + ctx := context.Background() + + repo1, err := db.Create(ctx, 1, CreateRepoOptions{Name: "repo1"}) + require.NoError(t, err) + repo2, err := db.Create(ctx, 2, CreateRepoOptions{Name: "repo2"}) + require.NoError(t, err) + + permsStore := NewPermsStore(db.DB) + err = permsStore.SetRepoPerms(ctx, repo1.ID, map[int64]AccessMode{3: AccessModeRead}) + require.NoError(t, err) + err = permsStore.SetRepoPerms(ctx, repo2.ID, map[int64]AccessMode{4: AccessModeAdmin}) + require.NoError(t, err) + + t.Run("user 3 is a collaborator of repo1", func(t *testing.T) { + got, err := db.GetByCollaboratorID(ctx, 3, 10, "") + require.NoError(t, err) + require.Len(t, got, 1) + assert.Equal(t, repo1.ID, got[0].ID) + }) + + t.Run("do not return directly owned repository", func(t *testing.T) { + got, err := db.GetByCollaboratorID(ctx, 1, 10, "") + require.NoError(t, err) + require.Len(t, got, 0) + }) +} + +func reposGetByCollaboratorIDWithAccessMode(t *testing.T, db *repos) { + ctx := context.Background() + + repo1, err := db.Create(ctx, 1, CreateRepoOptions{Name: "repo1"}) + require.NoError(t, err) + repo2, err := db.Create(ctx, 2, CreateRepoOptions{Name: "repo2"}) + require.NoError(t, err) + repo3, err := db.Create(ctx, 2, CreateRepoOptions{Name: "repo3"}) + require.NoError(t, err) + + permsStore := NewPermsStore(db.DB) + err = permsStore.SetRepoPerms(ctx, repo1.ID, map[int64]AccessMode{3: AccessModeRead}) + require.NoError(t, err) + err = permsStore.SetRepoPerms(ctx, repo2.ID, map[int64]AccessMode{3: AccessModeAdmin, 4: AccessModeWrite}) + require.NoError(t, err) + err = permsStore.SetRepoPerms(ctx, repo3.ID, map[int64]AccessMode{4: AccessModeWrite}) + require.NoError(t, err) + + got, err := db.GetByCollaboratorIDWithAccessMode(ctx, 3) + require.NoError(t, err) + require.Len(t, got, 2) + + accessModes := make(map[int64]AccessMode) + for repo, mode := range got { + accessModes[repo.ID] = mode + } + assert.Equal(t, AccessModeRead, accessModes[repo1.ID]) + assert.Equal(t, AccessModeAdmin, accessModes[repo2.ID]) +} + func reposGetByName(t *testing.T, db *repos) { ctx := context.Background() diff --git a/internal/db/user.go b/internal/db/user.go index 5be646680..9cbc2fa3a 100644 --- a/internal/db/user.go +++ b/internal/db/user.go @@ -10,7 +10,6 @@ import ( "os" "time" - log "unknwon.dev/clog/v2" "xorm.io/xorm" "gogs.io/gogs/internal/repoutil" @@ -196,41 +195,3 @@ func DeleteInactivateUsers() (err error) { _, err = x.Where("is_activated = ?", false).Delete(new(EmailAddress)) return err } - -// GetRepositoryAccesses finds all repositories with their access mode where a user has access but does not own. -func (u *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) { - accesses := make([]*Access, 0, 10) - if err := x.Find(&accesses, &Access{UserID: u.ID}); err != nil { - return nil, err - } - - repos := make(map[*Repository]AccessMode, len(accesses)) - for _, access := range accesses { - repo, err := GetRepositoryByID(access.RepoID) - if err != nil { - if IsErrRepoNotExist(err) { - log.Error("Failed to get repository by ID: %v", err) - continue - } - return nil, err - } - if repo.OwnerID == u.ID { - continue - } - repos[repo] = access.Mode - } - return repos, nil -} - -// GetAccessibleRepositories finds repositories which the user has access but does not own. -// If limit is smaller than 1 means returns all found results. -func (user *User) GetAccessibleRepositories(limit int) (repos []*Repository, _ error) { - sess := x.Where("owner_id !=? ", user.ID).Desc("updated_unix") - if limit > 0 { - sess.Limit(limit) - repos = make([]*Repository, 0, limit) - } else { - repos = make([]*Repository, 0, 10) - } - return repos, sess.Join("INNER", "access", "access.user_id = ? AND access.repo_id = repository.id", user.ID).Find(&repos) -} diff --git a/internal/route/api/v1/repo/repo.go b/internal/route/api/v1/repo/repo.go index 089269ac4..a4cc36b30 100644 --- a/internal/route/api/v1/repo/repo.go +++ b/internal/route/api/v1/repo/repo.go @@ -116,26 +116,26 @@ func listUserRepositories(c *context.APIContext, username string) { return } - accessibleRepos, err := user.GetRepositoryAccesses() + accessibleRepos, err := db.Repos.GetByCollaboratorIDWithAccessMode(c.Req.Context(), user.ID) if err != nil { - c.Error(err, "get repositories accesses") + c.Error(err, "get repositories accesses by collaborator") return } numOwnRepos := len(ownRepos) - repos := make([]*api.Repository, numOwnRepos+len(accessibleRepos)) - for i := range ownRepos { - repos[i] = ownRepos[i].APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true}) + repos := make([]*api.Repository, 0, numOwnRepos+len(accessibleRepos)) + for _, r := range ownRepos { + repos = append(repos, r.APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true})) } - i := numOwnRepos for repo, access := range accessibleRepos { - repos[i] = repo.APIFormatLegacy(&api.Permission{ - Admin: access >= db.AccessModeAdmin, - Push: access >= db.AccessModeWrite, - Pull: true, - }) - i++ + repos = append(repos, + repo.APIFormatLegacy(&api.Permission{ + Admin: access >= db.AccessModeAdmin, + Push: access >= db.AccessModeWrite, + Pull: true, + }), + ) } c.JSONSuccess(&repos) diff --git a/internal/route/lfs/mocks_test.go b/internal/route/lfs/mocks_test.go index 9a51b3951..089004fce 100644 --- a/internal/route/lfs/mocks_test.go +++ b/internal/route/lfs/mocks_test.go @@ -1495,6 +1495,13 @@ type MockReposStore struct { // CreateFunc is an instance of a mock function object controlling the // behavior of the method Create. CreateFunc *ReposStoreCreateFunc + // GetByCollaboratorIDFunc is an instance of a mock function object + // controlling the behavior of the method GetByCollaboratorID. + GetByCollaboratorIDFunc *ReposStoreGetByCollaboratorIDFunc + // GetByCollaboratorIDWithAccessModeFunc is an instance of a mock + // function object controlling the behavior of the method + // GetByCollaboratorIDWithAccessMode. + GetByCollaboratorIDWithAccessModeFunc *ReposStoreGetByCollaboratorIDWithAccessModeFunc // GetByNameFunc is an instance of a mock function object controlling // the behavior of the method GetByName. GetByNameFunc *ReposStoreGetByNameFunc @@ -1512,6 +1519,16 @@ func NewMockReposStore() *MockReposStore { return }, }, + GetByCollaboratorIDFunc: &ReposStoreGetByCollaboratorIDFunc{ + defaultHook: func(context.Context, int64, int, string) (r0 []*db.Repository, r1 error) { + return + }, + }, + GetByCollaboratorIDWithAccessModeFunc: &ReposStoreGetByCollaboratorIDWithAccessModeFunc{ + defaultHook: func(context.Context, int64) (r0 map[*db.Repository]db.AccessMode, r1 error) { + return + }, + }, GetByNameFunc: &ReposStoreGetByNameFunc{ defaultHook: func(context.Context, int64, string) (r0 *db.Repository, r1 error) { return @@ -1534,6 +1551,16 @@ func NewStrictMockReposStore() *MockReposStore { panic("unexpected invocation of MockReposStore.Create") }, }, + GetByCollaboratorIDFunc: &ReposStoreGetByCollaboratorIDFunc{ + defaultHook: func(context.Context, int64, int, string) ([]*db.Repository, error) { + panic("unexpected invocation of MockReposStore.GetByCollaboratorID") + }, + }, + GetByCollaboratorIDWithAccessModeFunc: &ReposStoreGetByCollaboratorIDWithAccessModeFunc{ + defaultHook: func(context.Context, int64) (map[*db.Repository]db.AccessMode, error) { + panic("unexpected invocation of MockReposStore.GetByCollaboratorIDWithAccessMode") + }, + }, GetByNameFunc: &ReposStoreGetByNameFunc{ defaultHook: func(context.Context, int64, string) (*db.Repository, error) { panic("unexpected invocation of MockReposStore.GetByName") @@ -1554,6 +1581,12 @@ func NewMockReposStoreFrom(i db.ReposStore) *MockReposStore { CreateFunc: &ReposStoreCreateFunc{ defaultHook: i.Create, }, + GetByCollaboratorIDFunc: &ReposStoreGetByCollaboratorIDFunc{ + defaultHook: i.GetByCollaboratorID, + }, + GetByCollaboratorIDWithAccessModeFunc: &ReposStoreGetByCollaboratorIDWithAccessModeFunc{ + defaultHook: i.GetByCollaboratorIDWithAccessMode, + }, GetByNameFunc: &ReposStoreGetByNameFunc{ defaultHook: i.GetByName, }, @@ -1673,6 +1706,234 @@ func (c ReposStoreCreateFuncCall) Results() []interface{} { return []interface{}{c.Result0, c.Result1} } +// ReposStoreGetByCollaboratorIDFunc describes the behavior when the +// GetByCollaboratorID method of the parent MockReposStore instance is +// invoked. +type ReposStoreGetByCollaboratorIDFunc struct { + defaultHook func(context.Context, int64, int, string) ([]*db.Repository, error) + hooks []func(context.Context, int64, int, string) ([]*db.Repository, error) + history []ReposStoreGetByCollaboratorIDFuncCall + mutex sync.Mutex +} + +// GetByCollaboratorID delegates to the next hook function in the queue and +// stores the parameter and result values of this invocation. +func (m *MockReposStore) GetByCollaboratorID(v0 context.Context, v1 int64, v2 int, v3 string) ([]*db.Repository, error) { + r0, r1 := m.GetByCollaboratorIDFunc.nextHook()(v0, v1, v2, v3) + m.GetByCollaboratorIDFunc.appendCall(ReposStoreGetByCollaboratorIDFuncCall{v0, v1, v2, v3, r0, r1}) + return r0, r1 +} + +// SetDefaultHook sets function that is called when the GetByCollaboratorID +// method of the parent MockReposStore instance is invoked and the hook +// queue is empty. +func (f *ReposStoreGetByCollaboratorIDFunc) SetDefaultHook(hook func(context.Context, int64, int, string) ([]*db.Repository, error)) { + f.defaultHook = hook +} + +// PushHook adds a function to the end of hook queue. Each invocation of the +// GetByCollaboratorID method of the parent MockReposStore instance invokes +// the hook at the front of the queue and discards it. After the queue is +// empty, the default hook function is invoked for any future action. +func (f *ReposStoreGetByCollaboratorIDFunc) PushHook(hook func(context.Context, int64, int, string) ([]*db.Repository, error)) { + f.mutex.Lock() + f.hooks = append(f.hooks, hook) + f.mutex.Unlock() +} + +// SetDefaultReturn calls SetDefaultHook with a function that returns the +// given values. +func (f *ReposStoreGetByCollaboratorIDFunc) SetDefaultReturn(r0 []*db.Repository, r1 error) { + f.SetDefaultHook(func(context.Context, int64, int, string) ([]*db.Repository, error) { + return r0, r1 + }) +} + +// PushReturn calls PushHook with a function that returns the given values. +func (f *ReposStoreGetByCollaboratorIDFunc) PushReturn(r0 []*db.Repository, r1 error) { + f.PushHook(func(context.Context, int64, int, string) ([]*db.Repository, error) { + return r0, r1 + }) +} + +func (f *ReposStoreGetByCollaboratorIDFunc) nextHook() func(context.Context, int64, int, string) ([]*db.Repository, error) { + f.mutex.Lock() + defer f.mutex.Unlock() + + if len(f.hooks) == 0 { + return f.defaultHook + } + + hook := f.hooks[0] + f.hooks = f.hooks[1:] + return hook +} + +func (f *ReposStoreGetByCollaboratorIDFunc) appendCall(r0 ReposStoreGetByCollaboratorIDFuncCall) { + f.mutex.Lock() + f.history = append(f.history, r0) + f.mutex.Unlock() +} + +// History returns a sequence of ReposStoreGetByCollaboratorIDFuncCall +// objects describing the invocations of this function. +func (f *ReposStoreGetByCollaboratorIDFunc) History() []ReposStoreGetByCollaboratorIDFuncCall { + f.mutex.Lock() + history := make([]ReposStoreGetByCollaboratorIDFuncCall, len(f.history)) + copy(history, f.history) + f.mutex.Unlock() + + return history +} + +// ReposStoreGetByCollaboratorIDFuncCall is an object that describes an +// invocation of method GetByCollaboratorID on an instance of +// MockReposStore. +type ReposStoreGetByCollaboratorIDFuncCall struct { + // Arg0 is the value of the 1st argument passed to this method + // invocation. + Arg0 context.Context + // Arg1 is the value of the 2nd argument passed to this method + // invocation. + Arg1 int64 + // Arg2 is the value of the 3rd argument passed to this method + // invocation. + Arg2 int + // Arg3 is the value of the 4th argument passed to this method + // invocation. + Arg3 string + // Result0 is the value of the 1st result returned from this method + // invocation. + Result0 []*db.Repository + // Result1 is the value of the 2nd result returned from this method + // invocation. + Result1 error +} + +// Args returns an interface slice containing the arguments of this +// invocation. +func (c ReposStoreGetByCollaboratorIDFuncCall) Args() []interface{} { + return []interface{}{c.Arg0, c.Arg1, c.Arg2, c.Arg3} +} + +// Results returns an interface slice containing the results of this +// invocation. +func (c ReposStoreGetByCollaboratorIDFuncCall) Results() []interface{} { + return []interface{}{c.Result0, c.Result1} +} + +// ReposStoreGetByCollaboratorIDWithAccessModeFunc describes the behavior +// when the GetByCollaboratorIDWithAccessMode method of the parent +// MockReposStore instance is invoked. +type ReposStoreGetByCollaboratorIDWithAccessModeFunc struct { + defaultHook func(context.Context, int64) (map[*db.Repository]db.AccessMode, error) + hooks []func(context.Context, int64) (map[*db.Repository]db.AccessMode, error) + history []ReposStoreGetByCollaboratorIDWithAccessModeFuncCall + mutex sync.Mutex +} + +// GetByCollaboratorIDWithAccessMode delegates to the next hook function in +// the queue and stores the parameter and result values of this invocation. +func (m *MockReposStore) GetByCollaboratorIDWithAccessMode(v0 context.Context, v1 int64) (map[*db.Repository]db.AccessMode, error) { + r0, r1 := m.GetByCollaboratorIDWithAccessModeFunc.nextHook()(v0, v1) + m.GetByCollaboratorIDWithAccessModeFunc.appendCall(ReposStoreGetByCollaboratorIDWithAccessModeFuncCall{v0, v1, r0, r1}) + return r0, r1 +} + +// SetDefaultHook sets function that is called when the +// GetByCollaboratorIDWithAccessMode method of the parent MockReposStore +// instance is invoked and the hook queue is empty. +func (f *ReposStoreGetByCollaboratorIDWithAccessModeFunc) SetDefaultHook(hook func(context.Context, int64) (map[*db.Repository]db.AccessMode, error)) { + f.defaultHook = hook +} + +// PushHook adds a function to the end of hook queue. Each invocation of the +// GetByCollaboratorIDWithAccessMode method of the parent MockReposStore +// instance invokes the hook at the front of the queue and discards it. +// After the queue is empty, the default hook function is invoked for any +// future action. +func (f *ReposStoreGetByCollaboratorIDWithAccessModeFunc) PushHook(hook func(context.Context, int64) (map[*db.Repository]db.AccessMode, error)) { + f.mutex.Lock() + f.hooks = append(f.hooks, hook) + f.mutex.Unlock() +} + +// SetDefaultReturn calls SetDefaultHook with a function that returns the +// given values. +func (f *ReposStoreGetByCollaboratorIDWithAccessModeFunc) SetDefaultReturn(r0 map[*db.Repository]db.AccessMode, r1 error) { + f.SetDefaultHook(func(context.Context, int64) (map[*db.Repository]db.AccessMode, error) { + return r0, r1 + }) +} + +// PushReturn calls PushHook with a function that returns the given values. +func (f *ReposStoreGetByCollaboratorIDWithAccessModeFunc) PushReturn(r0 map[*db.Repository]db.AccessMode, r1 error) { + f.PushHook(func(context.Context, int64) (map[*db.Repository]db.AccessMode, error) { + return r0, r1 + }) +} + +func (f *ReposStoreGetByCollaboratorIDWithAccessModeFunc) nextHook() func(context.Context, int64) (map[*db.Repository]db.AccessMode, error) { + f.mutex.Lock() + defer f.mutex.Unlock() + + if len(f.hooks) == 0 { + return f.defaultHook + } + + hook := f.hooks[0] + f.hooks = f.hooks[1:] + return hook +} + +func (f *ReposStoreGetByCollaboratorIDWithAccessModeFunc) appendCall(r0 ReposStoreGetByCollaboratorIDWithAccessModeFuncCall) { + f.mutex.Lock() + f.history = append(f.history, r0) + f.mutex.Unlock() +} + +// History returns a sequence of +// ReposStoreGetByCollaboratorIDWithAccessModeFuncCall objects describing +// the invocations of this function. +func (f *ReposStoreGetByCollaboratorIDWithAccessModeFunc) History() []ReposStoreGetByCollaboratorIDWithAccessModeFuncCall { + f.mutex.Lock() + history := make([]ReposStoreGetByCollaboratorIDWithAccessModeFuncCall, len(f.history)) + copy(history, f.history) + f.mutex.Unlock() + + return history +} + +// ReposStoreGetByCollaboratorIDWithAccessModeFuncCall is an object that +// describes an invocation of method GetByCollaboratorIDWithAccessMode on an +// instance of MockReposStore. +type ReposStoreGetByCollaboratorIDWithAccessModeFuncCall struct { + // Arg0 is the value of the 1st argument passed to this method + // invocation. + Arg0 context.Context + // Arg1 is the value of the 2nd argument passed to this method + // invocation. + Arg1 int64 + // Result0 is the value of the 1st result returned from this method + // invocation. + Result0 map[*db.Repository]db.AccessMode + // Result1 is the value of the 2nd result returned from this method + // invocation. + Result1 error +} + +// Args returns an interface slice containing the arguments of this +// invocation. +func (c ReposStoreGetByCollaboratorIDWithAccessModeFuncCall) Args() []interface{} { + return []interface{}{c.Arg0, c.Arg1} +} + +// Results returns an interface slice containing the results of this +// invocation. +func (c ReposStoreGetByCollaboratorIDWithAccessModeFuncCall) Results() []interface{} { + return []interface{}{c.Result0, c.Result1} +} + // ReposStoreGetByNameFunc describes the behavior when the GetByName method // of the parent MockReposStore instance is invoked. type ReposStoreGetByNameFunc struct { diff --git a/internal/route/user/home.go b/internal/route/user/home.go index f9f1b7b13..4521f511b 100644 --- a/internal/route/user/home.go +++ b/internal/route/user/home.go @@ -125,9 +125,9 @@ func Dashboard(c *context.Context) { // Only user can have collaborative repositories. if !ctxUser.IsOrganization() { - collaborateRepos, err := c.User.GetAccessibleRepositories(conf.UI.User.RepoPagingNum) + collaborateRepos, err := db.Repos.GetByCollaboratorID(c.Req.Context(), c.User.ID, conf.UI.User.RepoPagingNum, "updated_unix DESC") if err != nil { - c.Error(err, "get accessible repositories") + c.Error(err, "get accessible repositories by collaborator") return } else if err = db.RepositoryList(collaborateRepos).LoadAttributes(); err != nil { c.Error(err, "load attributes")