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