Skip to content

Commit b4abb6d

Browse files
lunnyZettat123
andauthored
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 <[email protected]>
1 parent 3f9c3e7 commit b4abb6d

File tree

6 files changed

+205
-172
lines changed

6 files changed

+205
-172
lines changed

models/organization/mini_org.go

Lines changed: 0 additions & 78 deletions
This file was deleted.

models/organization/org.go

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,6 @@ import (
2525
"xorm.io/xorm"
2626
)
2727

28-
// ________ .__ __ .__
29-
// \_____ \_______ _________ ____ |__|____________ _/ |_|__| ____ ____
30-
// / | \_ __ \/ ___\__ \ / \| \___ /\__ \\ __\ |/ _ \ / \
31-
// / | \ | \/ /_/ > __ \| | \ |/ / / __ \| | | ( <_> ) | \
32-
// \_______ /__| \___ (____ /___| /__/_____ \(____ /__| |__|\____/|___| /
33-
// \/ /_____/ \/ \/ \/ \/ \/
34-
3528
// ErrOrgNotExist represents a "OrgNotExist" kind of error.
3629
type ErrOrgNotExist struct {
3730
ID int64
@@ -465,42 +458,6 @@ func GetUsersWhoCanCreateOrgRepo(ctx context.Context, orgID int64) (map[int64]*u
465458
And("team_user.org_id = ?", orgID).Find(&users)
466459
}
467460

468-
// SearchOrganizationsOptions options to filter organizations
469-
type SearchOrganizationsOptions struct {
470-
db.ListOptions
471-
All bool
472-
}
473-
474-
// FindOrgOptions finds orgs options
475-
type FindOrgOptions struct {
476-
db.ListOptions
477-
UserID int64
478-
IncludePrivate bool
479-
}
480-
481-
func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
482-
cond := builder.Eq{"uid": userID}
483-
if !includePrivate {
484-
cond["is_public"] = true
485-
}
486-
return builder.Select("org_id").From("org_user").Where(cond)
487-
}
488-
489-
func (opts FindOrgOptions) ToConds() builder.Cond {
490-
var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization}
491-
if opts.UserID > 0 {
492-
cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
493-
}
494-
if !opts.IncludePrivate {
495-
cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
496-
}
497-
return cond
498-
}
499-
500-
func (opts FindOrgOptions) ToOrders() string {
501-
return "`user`.name ASC"
502-
}
503-
504461
// HasOrgOrUserVisible tells if the given user can see the given org or user
505462
func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
506463
// If user is nil, it's an anonymous user/request.
@@ -533,20 +490,6 @@ func HasOrgsVisible(ctx context.Context, orgs []*Organization, user *user_model.
533490
return false
534491
}
535492

536-
// GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
537-
// are allowed to create repos.
538-
func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organization, error) {
539-
orgs := make([]*Organization, 0, 10)
540-
541-
return orgs, db.GetEngine(ctx).Where(builder.In("id", builder.Select("`user`.id").From("`user`").
542-
Join("INNER", "`team_user`", "`team_user`.org_id = `user`.id").
543-
Join("INNER", "`team`", "`team`.id = `team_user`.team_id").
544-
Where(builder.Eq{"`team_user`.uid": userID}).
545-
And(builder.Eq{"`team`.authorize": perm.AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})))).
546-
Asc("`user`.name").
547-
Find(&orgs)
548-
}
549-
550493
// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
551494
func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUser, error) {
552495
sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)

models/organization/org_list.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package organization
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"strings"
10+
11+
"code.gitea.io/gitea/models/db"
12+
"code.gitea.io/gitea/models/perm"
13+
user_model "code.gitea.io/gitea/models/user"
14+
"code.gitea.io/gitea/modules/structs"
15+
16+
"xorm.io/builder"
17+
)
18+
19+
// SearchOrganizationsOptions options to filter organizations
20+
type SearchOrganizationsOptions struct {
21+
db.ListOptions
22+
All bool
23+
}
24+
25+
// FindOrgOptions finds orgs options
26+
type FindOrgOptions struct {
27+
db.ListOptions
28+
UserID int64
29+
IncludePrivate bool
30+
}
31+
32+
func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
33+
cond := builder.Eq{"uid": userID}
34+
if !includePrivate {
35+
cond["is_public"] = true
36+
}
37+
return builder.Select("org_id").From("org_user").Where(cond)
38+
}
39+
40+
func (opts FindOrgOptions) ToConds() builder.Cond {
41+
var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization}
42+
if opts.UserID > 0 {
43+
cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
44+
}
45+
if !opts.IncludePrivate {
46+
cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
47+
}
48+
return cond
49+
}
50+
51+
func (opts FindOrgOptions) ToOrders() string {
52+
return "`user`.lower_name ASC"
53+
}
54+
55+
// GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
56+
// are allowed to create repos.
57+
func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organization, error) {
58+
orgs := make([]*Organization, 0, 10)
59+
60+
return orgs, db.GetEngine(ctx).Where(builder.In("id", builder.Select("`user`.id").From("`user`").
61+
Join("INNER", "`team_user`", "`team_user`.org_id = `user`.id").
62+
Join("INNER", "`team`", "`team`.id = `team_user`.team_id").
63+
Where(builder.Eq{"`team_user`.uid": userID}).
64+
And(builder.Eq{"`team`.authorize": perm.AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})))).
65+
Asc("`user`.name").
66+
Find(&orgs)
67+
}
68+
69+
// MinimalOrg represents a simple organization with only the needed columns
70+
type MinimalOrg = Organization
71+
72+
// GetUserOrgsList returns all organizations the given user has access to
73+
func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, error) {
74+
schema, err := db.TableInfo(new(user_model.User))
75+
if err != nil {
76+
return nil, err
77+
}
78+
79+
outputCols := []string{
80+
"id",
81+
"name",
82+
"full_name",
83+
"visibility",
84+
"avatar",
85+
"avatar_email",
86+
"use_custom_avatar",
87+
}
88+
89+
selectColumns := &strings.Builder{}
90+
for i, col := range outputCols {
91+
fmt.Fprintf(selectColumns, "`%s`.%s", schema.Name, col)
92+
if i < len(outputCols)-1 {
93+
selectColumns.WriteString(", ")
94+
}
95+
}
96+
columnsStr := selectColumns.String()
97+
98+
var orgs []*MinimalOrg
99+
if err := db.GetEngine(ctx).Select(columnsStr).
100+
Table("user").
101+
Where(builder.In("`user`.`id`", queryUserOrgIDs(user.ID, true))).
102+
Find(&orgs); err != nil {
103+
return nil, err
104+
}
105+
106+
type orgCount struct {
107+
OrgID int64
108+
RepoCount int
109+
}
110+
var orgCounts []orgCount
111+
if err := db.GetEngine(ctx).
112+
Select("owner_id AS org_id, COUNT(DISTINCT(repository.id)) as repo_count").
113+
Table("repository").
114+
Join("INNER", "org_user", "owner_id = org_user.org_id").
115+
Where("org_user.uid = ?", user.ID).
116+
And(builder.Or(
117+
builder.Eq{"repository.is_private": false},
118+
builder.In("repository.id", builder.Select("repo_id").From("team_repo").
119+
InnerJoin("team_user", "team_user.team_id = team_repo.team_id").
120+
Where(builder.Eq{"team_user.uid": user.ID})),
121+
builder.In("repository.id", builder.Select("repo_id").From("collaboration").
122+
Where(builder.Eq{"user_id": user.ID})),
123+
)).
124+
GroupBy("owner_id").Find(&orgCounts); err != nil {
125+
return nil, err
126+
}
127+
128+
orgCountMap := make(map[int64]int, len(orgCounts))
129+
for _, orgCount := range orgCounts {
130+
orgCountMap[orgCount.OrgID] = orgCount.RepoCount
131+
}
132+
133+
for _, org := range orgs {
134+
org.NumRepos = orgCountMap[org.ID]
135+
}
136+
137+
return orgs, nil
138+
}

models/organization/org_list_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package organization_test
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/db"
10+
"code.gitea.io/gitea/models/organization"
11+
"code.gitea.io/gitea/models/unittest"
12+
user_model "code.gitea.io/gitea/models/user"
13+
14+
"github.com/stretchr/testify/assert"
15+
)
16+
17+
func TestCountOrganizations(t *testing.T) {
18+
assert.NoError(t, unittest.PrepareTestDatabase())
19+
expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&organization.Organization{})
20+
assert.NoError(t, err)
21+
cnt, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{IncludePrivate: true})
22+
assert.NoError(t, err)
23+
assert.Equal(t, expected, cnt)
24+
}
25+
26+
func TestFindOrgs(t *testing.T) {
27+
assert.NoError(t, unittest.PrepareTestDatabase())
28+
29+
orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
30+
UserID: 4,
31+
IncludePrivate: true,
32+
})
33+
assert.NoError(t, err)
34+
if assert.Len(t, orgs, 1) {
35+
assert.EqualValues(t, 3, orgs[0].ID)
36+
}
37+
38+
orgs, err = db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
39+
UserID: 4,
40+
IncludePrivate: false,
41+
})
42+
assert.NoError(t, err)
43+
assert.Len(t, orgs, 0)
44+
45+
total, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
46+
UserID: 4,
47+
IncludePrivate: true,
48+
})
49+
assert.NoError(t, err)
50+
assert.EqualValues(t, 1, total)
51+
}
52+
53+
func TestGetUserOrgsList(t *testing.T) {
54+
assert.NoError(t, unittest.PrepareTestDatabase())
55+
orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4})
56+
assert.NoError(t, err)
57+
if assert.Len(t, orgs, 1) {
58+
assert.EqualValues(t, 3, orgs[0].ID)
59+
// repo_id: 3 is in the team, 32 is public, 5 is private with no team
60+
assert.EqualValues(t, 2, orgs[0].NumRepos)
61+
}
62+
}

0 commit comments

Comments
 (0)