]> source.dussan.org Git - gitea.git/commitdiff
Reimplement GetUserOrgsList to make it simple and clear (#32486)
authorLunny Xiao <xiaolunwen@gmail.com>
Thu, 14 Nov 2024 05:31:47 +0000 (21:31 -0800)
committerGitHub <noreply@github.com>
Thu, 14 Nov 2024 05:31:47 +0000 (05:31 +0000)
Reimplement GetUserOrgsList and also move some functions and test to
org_list file.

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
models/organization/mini_org.go [deleted file]
models/organization/org.go
models/organization/org_list.go [new file with mode: 0644]
models/organization/org_list_test.go [new file with mode: 0644]
models/organization/org_test.go
services/oauth2_provider/access_token.go

diff --git a/models/organization/mini_org.go b/models/organization/mini_org.go
deleted file mode 100644 (file)
index b1b2462..0000000
+++ /dev/null
@@ -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
-}
index 6231f1eeedf5807c4846ec89277d4ca5f7f5ed22..725a99356e974881ae1c6fe403aa0063bc312c0c 100644 (file)
@@ -25,13 +25,6 @@ import (
        "xorm.io/xorm"
 )
 
-// ________                            .__                __  .__
-// \_____  \_______  _________    ____ |__|____________ _/  |_|__| ____   ____
-//  /   |   \_  __ \/ ___\__  \  /    \|  \___   /\__  \\   __\  |/  _ \ /    \
-// /    |    \  | \/ /_/  > __ \|   |  \  |/    /  / __ \|  | |  (  <_> )   |  \
-// \_______  /__|  \___  (____  /___|  /__/_____ \(____  /__| |__|\____/|___|  /
-//         \/     /_____/     \/     \/         \/     \/                    \/
-
 // ErrOrgNotExist represents a "OrgNotExist" kind of error.
 type ErrOrgNotExist struct {
        ID   int64
@@ -465,42 +458,6 @@ func GetUsersWhoCanCreateOrgRepo(ctx context.Context, orgID int64) (map[int64]*u
                And("team_user.org_id = ?", orgID).Find(&users)
 }
 
-// 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`.name ASC"
-}
-
 // HasOrgOrUserVisible tells if the given user can see the given org or user
 func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
        // If user is nil, it's an anonymous user/request.
@@ -533,20 +490,6 @@ func HasOrgsVisible(ctx context.Context, orgs []*Organization, user *user_model.
        return false
 }
 
-// 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)
-}
-
 // GetOrgUsersByOrgID returns all organization-user relations by organization ID.
 func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUser, error) {
        sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)
diff --git a/models/organization/org_list.go b/models/organization/org_list.go
new file mode 100644 (file)
index 0000000..72ebf6f
--- /dev/null
@@ -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
+}
diff --git a/models/organization/org_list_test.go b/models/organization/org_list_test.go
new file mode 100644 (file)
index 0000000..fc8d148
--- /dev/null
@@ -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)
+       }
+}
index c614aaacf56e5b0ad7dbc49189186ef4fca054b7..7159f0fc46510dbe9272ed69c0daaaccf4281079 100644 (file)
@@ -129,15 +129,6 @@ func TestGetOrgByName(t *testing.T) {
        assert.True(t, organization.IsErrOrgNotExist(err))
 }
 
-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 TestIsOrganizationOwner(t *testing.T) {
        assert.NoError(t, unittest.PrepareTestDatabase())
        test := func(orgID, userID int64, expected bool) {
@@ -251,33 +242,6 @@ func TestRestrictedUserOrgMembers(t *testing.T) {
        }
 }
 
-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 TestGetOrgUsersByOrgID(t *testing.T) {
        assert.NoError(t, unittest.PrepareTestDatabase())
 
index 00c960caf2ee75307beb4c1a65ad20345fa50198..f79afa4b301baed1f4eca111897c86ee115ec2c3 100644 (file)
@@ -8,6 +8,7 @@ import (
        "fmt"
 
        auth "code.gitea.io/gitea/models/auth"
+       "code.gitea.io/gitea/models/db"
        org_model "code.gitea.io/gitea/models/organization"
        user_model "code.gitea.io/gitea/models/user"
        "code.gitea.io/gitea/modules/log"
@@ -192,7 +193,10 @@ func NewAccessTokenResponse(ctx context.Context, grant *auth.OAuth2Grant, server
 // returns a list of "org" and "org:team" strings,
 // that the given user is a part of.
 func GetOAuthGroupsForUser(ctx context.Context, user *user_model.User) ([]string, error) {
-       orgs, err := org_model.GetUserOrgsList(ctx, user)
+       orgs, err := db.Find[org_model.Organization](ctx, org_model.FindOrgOptions{
+               UserID:         user.ID,
+               IncludePrivate: true,
+       })
        if err != nil {
                return nil, fmt.Errorf("GetUserOrgList: %w", err)
        }