mirror of https://github.com/go-gitea/gitea
Reimplement GetUserOrgsList to make it simple and clear (#32486)
Reimplement GetUserOrgsList and also move some functions and test to org_list file. --------- Co-authored-by: Zettat123 <zettat123@gmail.com>pull/32498/head^2
parent
3f9c3e7bc3
commit
b4abb6deff
@ -1,78 +0,0 @@ |
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package organization |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"strings" |
||||
|
||||
"code.gitea.io/gitea/models/db" |
||||
repo_model "code.gitea.io/gitea/models/repo" |
||||
"code.gitea.io/gitea/models/unit" |
||||
user_model "code.gitea.io/gitea/models/user" |
||||
|
||||
"xorm.io/builder" |
||||
) |
||||
|
||||
// MinimalOrg represents a simple organization with only the needed columns
|
||||
type MinimalOrg = Organization |
||||
|
||||
// GetUserOrgsList returns all organizations the given user has access to
|
||||
func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, error) { |
||||
schema, err := db.TableInfo(new(user_model.User)) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
outputCols := []string{ |
||||
"id", |
||||
"name", |
||||
"full_name", |
||||
"visibility", |
||||
"avatar", |
||||
"avatar_email", |
||||
"use_custom_avatar", |
||||
} |
||||
|
||||
groupByCols := &strings.Builder{} |
||||
for _, col := range outputCols { |
||||
fmt.Fprintf(groupByCols, "`%s`.%s,", schema.Name, col) |
||||
} |
||||
groupByStr := groupByCols.String() |
||||
groupByStr = groupByStr[0 : len(groupByStr)-1] |
||||
|
||||
sess := db.GetEngine(ctx) |
||||
sess = sess.Select(groupByStr+", count(distinct repo_id) as org_count"). |
||||
Table("user"). |
||||
Join("INNER", "team", "`team`.org_id = `user`.id"). |
||||
Join("INNER", "team_user", "`team`.id = `team_user`.team_id"). |
||||
Join("LEFT", builder. |
||||
Select("id as repo_id, owner_id as repo_owner_id"). |
||||
From("repository"). |
||||
Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)), "`repository`.repo_owner_id = `team`.org_id"). |
||||
Where("`team_user`.uid = ?", user.ID). |
||||
GroupBy(groupByStr) |
||||
|
||||
type OrgCount struct { |
||||
Organization `xorm:"extends"` |
||||
OrgCount int |
||||
} |
||||
|
||||
orgCounts := make([]*OrgCount, 0, 10) |
||||
|
||||
if err := sess. |
||||
Asc("`user`.name"). |
||||
Find(&orgCounts); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
orgs := make([]*MinimalOrg, len(orgCounts)) |
||||
for i, orgCount := range orgCounts { |
||||
orgCount.Organization.NumRepos = orgCount.OrgCount |
||||
orgs[i] = &orgCount.Organization |
||||
} |
||||
|
||||
return orgs, nil |
||||
} |
@ -0,0 +1,138 @@ |
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package organization |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"strings" |
||||
|
||||
"code.gitea.io/gitea/models/db" |
||||
"code.gitea.io/gitea/models/perm" |
||||
user_model "code.gitea.io/gitea/models/user" |
||||
"code.gitea.io/gitea/modules/structs" |
||||
|
||||
"xorm.io/builder" |
||||
) |
||||
|
||||
// SearchOrganizationsOptions options to filter organizations
|
||||
type SearchOrganizationsOptions struct { |
||||
db.ListOptions |
||||
All bool |
||||
} |
||||
|
||||
// FindOrgOptions finds orgs options
|
||||
type FindOrgOptions struct { |
||||
db.ListOptions |
||||
UserID int64 |
||||
IncludePrivate bool |
||||
} |
||||
|
||||
func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder { |
||||
cond := builder.Eq{"uid": userID} |
||||
if !includePrivate { |
||||
cond["is_public"] = true |
||||
} |
||||
return builder.Select("org_id").From("org_user").Where(cond) |
||||
} |
||||
|
||||
func (opts FindOrgOptions) ToConds() builder.Cond { |
||||
var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization} |
||||
if opts.UserID > 0 { |
||||
cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate))) |
||||
} |
||||
if !opts.IncludePrivate { |
||||
cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}) |
||||
} |
||||
return cond |
||||
} |
||||
|
||||
func (opts FindOrgOptions) ToOrders() string { |
||||
return "`user`.lower_name ASC" |
||||
} |
||||
|
||||
// GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
|
||||
// are allowed to create repos.
|
||||
func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organization, error) { |
||||
orgs := make([]*Organization, 0, 10) |
||||
|
||||
return orgs, db.GetEngine(ctx).Where(builder.In("id", builder.Select("`user`.id").From("`user`"). |
||||
Join("INNER", "`team_user`", "`team_user`.org_id = `user`.id"). |
||||
Join("INNER", "`team`", "`team`.id = `team_user`.team_id"). |
||||
Where(builder.Eq{"`team_user`.uid": userID}). |
||||
And(builder.Eq{"`team`.authorize": perm.AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})))). |
||||
Asc("`user`.name"). |
||||
Find(&orgs) |
||||
} |
||||
|
||||
// MinimalOrg represents a simple organization with only the needed columns
|
||||
type MinimalOrg = Organization |
||||
|
||||
// GetUserOrgsList returns all organizations the given user has access to
|
||||
func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, error) { |
||||
schema, err := db.TableInfo(new(user_model.User)) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
outputCols := []string{ |
||||
"id", |
||||
"name", |
||||
"full_name", |
||||
"visibility", |
||||
"avatar", |
||||
"avatar_email", |
||||
"use_custom_avatar", |
||||
} |
||||
|
||||
selectColumns := &strings.Builder{} |
||||
for i, col := range outputCols { |
||||
fmt.Fprintf(selectColumns, "`%s`.%s", schema.Name, col) |
||||
if i < len(outputCols)-1 { |
||||
selectColumns.WriteString(", ") |
||||
} |
||||
} |
||||
columnsStr := selectColumns.String() |
||||
|
||||
var orgs []*MinimalOrg |
||||
if err := db.GetEngine(ctx).Select(columnsStr). |
||||
Table("user"). |
||||
Where(builder.In("`user`.`id`", queryUserOrgIDs(user.ID, true))). |
||||
Find(&orgs); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
type orgCount struct { |
||||
OrgID int64 |
||||
RepoCount int |
||||
} |
||||
var orgCounts []orgCount |
||||
if err := db.GetEngine(ctx). |
||||
Select("owner_id AS org_id, COUNT(DISTINCT(repository.id)) as repo_count"). |
||||
Table("repository"). |
||||
Join("INNER", "org_user", "owner_id = org_user.org_id"). |
||||
Where("org_user.uid = ?", user.ID). |
||||
And(builder.Or( |
||||
builder.Eq{"repository.is_private": false}, |
||||
builder.In("repository.id", builder.Select("repo_id").From("team_repo"). |
||||
InnerJoin("team_user", "team_user.team_id = team_repo.team_id"). |
||||
Where(builder.Eq{"team_user.uid": user.ID})), |
||||
builder.In("repository.id", builder.Select("repo_id").From("collaboration"). |
||||
Where(builder.Eq{"user_id": user.ID})), |
||||
)). |
||||
GroupBy("owner_id").Find(&orgCounts); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
orgCountMap := make(map[int64]int, len(orgCounts)) |
||||
for _, orgCount := range orgCounts { |
||||
orgCountMap[orgCount.OrgID] = orgCount.RepoCount |
||||
} |
||||
|
||||
for _, org := range orgs { |
||||
org.NumRepos = orgCountMap[org.ID] |
||||
} |
||||
|
||||
return orgs, nil |
||||
} |
@ -0,0 +1,62 @@ |
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package organization_test |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"code.gitea.io/gitea/models/db" |
||||
"code.gitea.io/gitea/models/organization" |
||||
"code.gitea.io/gitea/models/unittest" |
||||
user_model "code.gitea.io/gitea/models/user" |
||||
|
||||
"github.com/stretchr/testify/assert" |
||||
) |
||||
|
||||
func TestCountOrganizations(t *testing.T) { |
||||
assert.NoError(t, unittest.PrepareTestDatabase()) |
||||
expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&organization.Organization{}) |
||||
assert.NoError(t, err) |
||||
cnt, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{IncludePrivate: true}) |
||||
assert.NoError(t, err) |
||||
assert.Equal(t, expected, cnt) |
||||
} |
||||
|
||||
func TestFindOrgs(t *testing.T) { |
||||
assert.NoError(t, unittest.PrepareTestDatabase()) |
||||
|
||||
orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{ |
||||
UserID: 4, |
||||
IncludePrivate: true, |
||||
}) |
||||
assert.NoError(t, err) |
||||
if assert.Len(t, orgs, 1) { |
||||
assert.EqualValues(t, 3, orgs[0].ID) |
||||
} |
||||
|
||||
orgs, err = db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{ |
||||
UserID: 4, |
||||
IncludePrivate: false, |
||||
}) |
||||
assert.NoError(t, err) |
||||
assert.Len(t, orgs, 0) |
||||
|
||||
total, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{ |
||||
UserID: 4, |
||||
IncludePrivate: true, |
||||
}) |
||||
assert.NoError(t, err) |
||||
assert.EqualValues(t, 1, total) |
||||
} |
||||
|
||||
func TestGetUserOrgsList(t *testing.T) { |
||||
assert.NoError(t, unittest.PrepareTestDatabase()) |
||||
orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4}) |
||||
assert.NoError(t, err) |
||||
if assert.Len(t, orgs, 1) { |
||||
assert.EqualValues(t, 3, orgs[0].ID) |
||||
// repo_id: 3 is in the team, 32 is public, 5 is private with no team
|
||||
assert.EqualValues(t, 2, orgs[0].NumRepos) |
||||
} |
||||
} |
Loading…
Reference in new issue