1
0
mirror of https://github.com/go-gitea/gitea.git synced 2024-11-04 08:17:24 -05:00

Fix get reviewers' bug

This commit is contained in:
Lunny Xiao 2024-11-03 22:50:10 -08:00
parent f2a6df03d9
commit ab7cb9509d
No known key found for this signature in database
GPG Key ID: C3B7C91B632F738A

View File

@ -11,7 +11,6 @@ import (
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
api "code.gitea.io/gitea/modules/structs"
"xorm.io/builder" "xorm.io/builder"
) )
@ -145,54 +144,59 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us
} }
// GetReviewers get all users can be requested to review: // GetReviewers get all users can be requested to review:
// * for private repositories this returns all users that have read access or higher to the repository. // - Poster should not be listed
// * for public repositories this returns all users that have read access or higher to the repository, // - For collabrators, all users that have read access or higher to the repository.
// all repo watchers and all organization members. // - For repository under orgnization, users under the teams which have read permission or higher of pull request unit
// TODO: may be we should have a busy choice for users to block review request to them. // - Owner will be listed if it's not an organization, not the poster and not in the list of reviewers
func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64) ([]*user_model.User, error) { func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64) ([]*user_model.User, error) {
// Get the owner of the repository - this often already pre-cached and if so saves complexity for the following queries
if err := repo.LoadOwner(ctx); err != nil { if err := repo.LoadOwner(ctx); err != nil {
return nil, err return nil, err
} }
cond := builder.And(builder.Neq{"`user`.id": posterID}). e := db.GetEngine(ctx)
And(builder.Eq{"`user`.is_active": true}) uniqueUserIDs := make(container.Set[int64])
if repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate { if repo.Owner.IsOrganization() {
// This a private repository: additionalUserIDs := make([]int64, 0, 10)
// Anyone who can read the repository is a requestable reviewer if err := e.Table("team_user").
Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id").
cond = cond.And(builder.In("`user`.id", Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id").
builder.Select("user_id").From("access").Where( Where("`team_repo`.repo_id = ? AND (`team_unit`.access_mode >= ? AND `team_unit`.`type` = ?)",
builder.Eq{"repo_id": repo.ID}. repo.ID, perm.AccessModeRead, unit.TypePullRequests).
And(builder.Gte{"mode": perm.AccessModeRead}), Distinct("`team_user`.uid").
), Select("`team_user`.uid").
)) Find(&additionalUserIDs); err != nil {
return nil, err
if repo.Owner.Type == user_model.UserTypeIndividual && repo.Owner.ID != posterID {
// as private *user* repos don't generate an entry in the `access` table,
// the owner of a private repo needs to be explicitly added.
cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID})
} }
uniqueUserIDs.AddMultiple(additionalUserIDs...)
} else { } else {
// This is a "public" repository: userIDs := make([]int64, 0, 10)
// Any user that has read access, is a watcher or organization member can be requested to review if err := e.Table("access").
cond = cond.And(builder.And(builder.In("`user`.id", Where("repo_id = ? AND mode >= ?", repo.ID, perm.AccessModeRead).
builder.Select("user_id").From("access"). Select("user_id").
Where(builder.Eq{"repo_id": repo.ID}. Find(&userIDs); err != nil {
And(builder.Gte{"mode": perm.AccessModeRead})), return nil, err
).Or(builder.In("`user`.id", }
builder.Select("user_id").From("watch"). uniqueUserIDs.AddMultiple(userIDs...)
Where(builder.Eq{"repo_id": repo.ID}. }
And(builder.In("mode", WatchModeNormal, WatchModeAuto))), uniqueUserIDs.Remove(posterID) // posterID should not be in the list of reviewers
).Or(builder.In("`user`.id",
builder.Select("uid").From("org_user"). // Leave a seat for owner itself to append later, but if owner is an organization
Where(builder.Eq{"org_id": repo.OwnerID}), // and just waste 1 unit is cheaper than re-allocate memory once.
))))) users := make([]*user_model.User, 0, len(uniqueUserIDs)+1)
if len(uniqueUserIDs) > 0 {
if err := e.In("id", uniqueUserIDs.Values()).
Where(builder.Eq{"`user`.is_active": true}).
OrderBy(user_model.GetOrderByName()).
Find(&users); err != nil {
return nil, err
}
}
if repo.OwnerID != posterID && !repo.Owner.IsOrganization() && !uniqueUserIDs.Contains(repo.OwnerID) {
users = append(users, repo.Owner)
} }
users := make([]*user_model.User, 0, 8) return users, nil
return users, db.GetEngine(ctx).Where(cond).OrderBy(user_model.GetOrderByName()).Find(&users)
} }
// GetIssuePostersWithSearch returns users with limit of 30 whose username started with prefix that have authored an issue/pull request for the given repository // GetIssuePostersWithSearch returns users with limit of 30 whose username started with prefix that have authored an issue/pull request for the given repository