|
| 1 | +// Copyright 2024 The Gitea Authors. All rights reserved. |
| 2 | +// SPDX-License-Identifier: MIT |
| 3 | + |
| 4 | +package pull |
| 5 | + |
| 6 | +import ( |
| 7 | + "context" |
| 8 | + |
| 9 | + "code.gitea.io/gitea/models/db" |
| 10 | + "code.gitea.io/gitea/models/organization" |
| 11 | + "code.gitea.io/gitea/models/perm" |
| 12 | + repo_model "code.gitea.io/gitea/models/repo" |
| 13 | + "code.gitea.io/gitea/models/unit" |
| 14 | + user_model "code.gitea.io/gitea/models/user" |
| 15 | + "code.gitea.io/gitea/modules/container" |
| 16 | + |
| 17 | + "xorm.io/builder" |
| 18 | +) |
| 19 | + |
| 20 | +// GetReviewers get all users can be requested to review: |
| 21 | +// - Poster should not be listed |
| 22 | +// - For collaborator, all users that have read access or higher to the repository. |
| 23 | +// - For repository under organization, users under the teams which have read permission or higher of pull request unit |
| 24 | +// - Owner will be listed if it's not an organization, not the poster and not in the list of reviewers |
| 25 | +func GetReviewers(ctx context.Context, repo *repo_model.Repository, doerID, posterID int64) ([]*user_model.User, error) { |
| 26 | + if err := repo.LoadOwner(ctx); err != nil { |
| 27 | + return nil, err |
| 28 | + } |
| 29 | + |
| 30 | + e := db.GetEngine(ctx) |
| 31 | + uniqueUserIDs := make(container.Set[int64]) |
| 32 | + |
| 33 | + collaboratorIDs := make([]int64, 0, 10) |
| 34 | + if err := e.Table("collaboration").Where("repo_id=?", repo.ID). |
| 35 | + And("mode >= ?", perm.AccessModeRead). |
| 36 | + Select("user_id"). |
| 37 | + Find(&collaboratorIDs); err != nil { |
| 38 | + return nil, err |
| 39 | + } |
| 40 | + uniqueUserIDs.AddMultiple(collaboratorIDs...) |
| 41 | + |
| 42 | + if repo.Owner.IsOrganization() { |
| 43 | + additionalUserIDs := make([]int64, 0, 10) |
| 44 | + if err := e.Table("team_user"). |
| 45 | + Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id"). |
| 46 | + Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id"). |
| 47 | + Where("`team_repo`.repo_id = ? AND (`team_unit`.access_mode >= ? AND `team_unit`.`type` = ?)", |
| 48 | + repo.ID, perm.AccessModeRead, unit.TypePullRequests). |
| 49 | + Distinct("`team_user`.uid"). |
| 50 | + Select("`team_user`.uid"). |
| 51 | + Find(&additionalUserIDs); err != nil { |
| 52 | + return nil, err |
| 53 | + } |
| 54 | + uniqueUserIDs.AddMultiple(additionalUserIDs...) |
| 55 | + } |
| 56 | + |
| 57 | + uniqueUserIDs.Remove(posterID) // posterID should not be in the list of reviewers |
| 58 | + |
| 59 | + // Leave a seat for owner itself to append later, but if owner is an organization |
| 60 | + // and just waste 1 unit is cheaper than re-allocate memory once. |
| 61 | + users := make([]*user_model.User, 0, len(uniqueUserIDs)+1) |
| 62 | + if len(uniqueUserIDs) > 0 { |
| 63 | + if err := e.In("id", uniqueUserIDs.Values()). |
| 64 | + Where(builder.Eq{"`user`.is_active": true}). |
| 65 | + OrderBy(user_model.GetOrderByName()). |
| 66 | + Find(&users); err != nil { |
| 67 | + return nil, err |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + // add owner after all users are loaded because we can avoid load owner twice |
| 72 | + if repo.OwnerID != posterID && !repo.Owner.IsOrganization() && !uniqueUserIDs.Contains(repo.OwnerID) { |
| 73 | + users = append(users, repo.Owner) |
| 74 | + } |
| 75 | + |
| 76 | + return users, nil |
| 77 | +} |
| 78 | + |
| 79 | +// GetReviewerTeams get all teams can be requested to review |
| 80 | +func GetReviewerTeams(ctx context.Context, repo *repo_model.Repository) ([]*organization.Team, error) { |
| 81 | + if err := repo.LoadOwner(ctx); err != nil { |
| 82 | + return nil, err |
| 83 | + } |
| 84 | + if !repo.Owner.IsOrganization() { |
| 85 | + return nil, nil |
| 86 | + } |
| 87 | + |
| 88 | + return organization.GetTeamsWithAccessToRepoUnit(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead, unit.TypePullRequests) |
| 89 | +} |
0 commit comments