mirror of
https://github.com/go-gitea/gitea.git
synced 2025-06-23 07:48:53 +03:00
Do not mutate incoming options to RenderUserSearch and SearchUsers (#34544)
This PR changes the `opts` argument in `SearchUsers()` to be passed by value instead of by pointer, as its mutations do not escape the function scope and are not used elsewhere. This simplifies reasoning about the function and avoids unnecessary pointer usage. This insight emerged during an initial attempt to refactor `RenderUserSearch()`, which currently intermixes multiple concerns. Co-authored-by: Philip Peterson <philip-peterson@users.noreply.github.com>
This commit is contained in:
@ -137,7 +137,7 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
|
|||||||
|
|
||||||
// SearchUsers takes options i.e. keyword and part of user name to search,
|
// SearchUsers takes options i.e. keyword and part of user name to search,
|
||||||
// it returns results in given range and number of total results.
|
// it returns results in given range and number of total results.
|
||||||
func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _ int64, _ error) {
|
func SearchUsers(ctx context.Context, opts SearchUserOptions) (users []*User, _ int64, _ error) {
|
||||||
sessCount := opts.toSearchQueryBase(ctx)
|
sessCount := opts.toSearchQueryBase(ctx)
|
||||||
defer sessCount.Close()
|
defer sessCount.Close()
|
||||||
count, err := sessCount.Count(new(User))
|
count, err := sessCount.Count(new(User))
|
||||||
@ -152,7 +152,7 @@ func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _
|
|||||||
sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String())
|
sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String())
|
||||||
defer sessQuery.Close()
|
defer sessQuery.Close()
|
||||||
if opts.Page > 0 {
|
if opts.Page > 0 {
|
||||||
sessQuery = db.SetSessionPagination(sessQuery, opts)
|
sessQuery = db.SetSessionPagination(sessQuery, &opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// the sql may contain JOIN, so we must only select User related columns
|
// the sql may contain JOIN, so we must only select User related columns
|
||||||
|
@ -88,7 +88,7 @@ func TestCanCreateOrganization(t *testing.T) {
|
|||||||
|
|
||||||
func TestSearchUsers(t *testing.T) {
|
func TestSearchUsers(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
testSuccess := func(opts *user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {
|
testSuccess := func(opts user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {
|
||||||
users, _, err := user_model.SearchUsers(db.DefaultContext, opts)
|
users, _, err := user_model.SearchUsers(db.DefaultContext, opts)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
cassText := fmt.Sprintf("ids: %v, opts: %v", expectedUserOrOrgIDs, opts)
|
cassText := fmt.Sprintf("ids: %v, opts: %v", expectedUserOrOrgIDs, opts)
|
||||||
@ -100,61 +100,61 @@ func TestSearchUsers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// test orgs
|
// test orgs
|
||||||
testOrgSuccess := func(opts *user_model.SearchUserOptions, expectedOrgIDs []int64) {
|
testOrgSuccess := func(opts user_model.SearchUserOptions, expectedOrgIDs []int64) {
|
||||||
opts.Type = user_model.UserTypeOrganization
|
opts.Type = user_model.UserTypeOrganization
|
||||||
testSuccess(opts, expectedOrgIDs)
|
testSuccess(opts, expectedOrgIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}},
|
testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}},
|
||||||
[]int64{3, 6})
|
[]int64{3, 6})
|
||||||
|
|
||||||
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}},
|
testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}},
|
||||||
[]int64{7, 17})
|
[]int64{7, 17})
|
||||||
|
|
||||||
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}},
|
testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}},
|
||||||
[]int64{19, 25})
|
[]int64{19, 25})
|
||||||
|
|
||||||
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}},
|
testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}},
|
||||||
[]int64{26, 41})
|
[]int64{26, 41})
|
||||||
|
|
||||||
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 5, PageSize: 2}},
|
testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 5, PageSize: 2}},
|
||||||
[]int64{42})
|
[]int64{42})
|
||||||
|
|
||||||
testOrgSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 6, PageSize: 2}},
|
testOrgSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 6, PageSize: 2}},
|
||||||
[]int64{})
|
[]int64{})
|
||||||
|
|
||||||
// test users
|
// test users
|
||||||
testUserSuccess := func(opts *user_model.SearchUserOptions, expectedUserIDs []int64) {
|
testUserSuccess := func(opts user_model.SearchUserOptions, expectedUserIDs []int64) {
|
||||||
opts.Type = user_model.UserTypeIndividual
|
opts.Type = user_model.UserTypeIndividual
|
||||||
testSuccess(opts, expectedUserIDs)
|
testSuccess(opts, expectedUserIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
|
testUserSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
|
||||||
[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
|
[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
|
||||||
|
|
||||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
|
testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
|
||||||
[]int64{9})
|
[]int64{9})
|
||||||
|
|
||||||
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
testUserSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||||
[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
|
[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
|
||||||
|
|
||||||
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
testUserSuccess(user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||||
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
|
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
|
||||||
|
|
||||||
// order by name asc default
|
// order by name asc default
|
||||||
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
testUserSuccess(user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||||
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
|
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
|
||||||
|
|
||||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
|
testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
|
||||||
[]int64{1})
|
[]int64{1})
|
||||||
|
|
||||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
|
testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
|
||||||
[]int64{29})
|
[]int64{29})
|
||||||
|
|
||||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
|
testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
|
||||||
[]int64{37})
|
[]int64{37})
|
||||||
|
|
||||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
|
testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
|
||||||
[]int64{24})
|
[]int64{24})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ func GetAllOrgs(ctx *context.APIContext) {
|
|||||||
|
|
||||||
listOptions := utils.GetListOptions(ctx)
|
listOptions := utils.GetListOptions(ctx)
|
||||||
|
|
||||||
users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
users, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
Type: user_model.UserTypeOrganization,
|
Type: user_model.UserTypeOrganization,
|
||||||
OrderBy: db.SearchOrderByAlphabetically,
|
OrderBy: db.SearchOrderByAlphabetically,
|
||||||
|
@ -423,7 +423,7 @@ func SearchUsers(ctx *context.APIContext) {
|
|||||||
|
|
||||||
listOptions := utils.GetListOptions(ctx)
|
listOptions := utils.GetListOptions(ctx)
|
||||||
|
|
||||||
users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
users, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
Type: user_model.UserTypeIndividual,
|
Type: user_model.UserTypeIndividual,
|
||||||
LoginName: ctx.FormTrim("login_name"),
|
LoginName: ctx.FormTrim("login_name"),
|
||||||
|
@ -201,7 +201,7 @@ func GetAll(ctx *context.APIContext) {
|
|||||||
|
|
||||||
listOptions := utils.GetListOptions(ctx)
|
listOptions := utils.GetListOptions(ctx)
|
||||||
|
|
||||||
publicOrgs, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
publicOrgs, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
ListOptions: listOptions,
|
ListOptions: listOptions,
|
||||||
Type: user_model.UserTypeOrganization,
|
Type: user_model.UserTypeOrganization,
|
||||||
|
@ -73,7 +73,7 @@ func Search(ctx *context.APIContext) {
|
|||||||
if ctx.PublicOnly {
|
if ctx.PublicOnly {
|
||||||
visible = []structs.VisibleType{structs.VisibleTypePublic}
|
visible = []structs.VisibleType{structs.VisibleTypePublic}
|
||||||
}
|
}
|
||||||
users, maxResults, err = user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
users, maxResults, err = user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
Keyword: ctx.FormTrim("q"),
|
Keyword: ctx.FormTrim("q"),
|
||||||
UID: uid,
|
UID: uid,
|
||||||
|
@ -27,7 +27,7 @@ func Organizations(ctx *context.Context) {
|
|||||||
ctx.SetFormString("sort", UserSearchDefaultAdminSort)
|
ctx.SetFormString("sort", UserSearchDefaultAdminSort)
|
||||||
}
|
}
|
||||||
|
|
||||||
explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
|
explore.RenderUserSearch(ctx, user_model.SearchUserOptions{
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
Type: user_model.UserTypeOrganization,
|
Type: user_model.UserTypeOrganization,
|
||||||
IncludeReserved: true, // administrator needs to list all accounts include reserved
|
IncludeReserved: true, // administrator needs to list all accounts include reserved
|
||||||
|
@ -64,7 +64,7 @@ func Users(ctx *context.Context) {
|
|||||||
"SortType": sortType,
|
"SortType": sortType,
|
||||||
}
|
}
|
||||||
|
|
||||||
explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
|
explore.RenderUserSearch(ctx, user_model.SearchUserOptions{
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
Type: user_model.UserTypeIndividual,
|
Type: user_model.UserTypeIndividual,
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
|
@ -44,7 +44,7 @@ func Organizations(ctx *context.Context) {
|
|||||||
ctx.SetFormString("sort", sortOrder)
|
ctx.SetFormString("sort", sortOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderUserSearch(ctx, &user_model.SearchUserOptions{
|
RenderUserSearch(ctx, user_model.SearchUserOptions{
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
Type: user_model.UserTypeOrganization,
|
Type: user_model.UserTypeOrganization,
|
||||||
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
|
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
|
||||||
|
@ -32,7 +32,7 @@ func isKeywordValid(keyword string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RenderUserSearch render user search page
|
// RenderUserSearch render user search page
|
||||||
func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, tplName templates.TplName) {
|
func RenderUserSearch(ctx *context.Context, opts user_model.SearchUserOptions, tplName templates.TplName) {
|
||||||
// Sitemap index for sitemap paths
|
// Sitemap index for sitemap paths
|
||||||
opts.Page = int(ctx.PathParamInt64("idx"))
|
opts.Page = int(ctx.PathParamInt64("idx"))
|
||||||
isSitemap := ctx.PathParam("idx") != ""
|
isSitemap := ctx.PathParam("idx") != ""
|
||||||
@ -151,7 +151,7 @@ func Users(ctx *context.Context) {
|
|||||||
ctx.SetFormString("sort", sortOrder)
|
ctx.SetFormString("sort", sortOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderUserSearch(ctx, &user_model.SearchUserOptions{
|
RenderUserSearch(ctx, user_model.SearchUserOptions{
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
Type: user_model.UserTypeIndividual,
|
Type: user_model.UserTypeIndividual,
|
||||||
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
|
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
|
||||||
|
@ -68,7 +68,7 @@ func Home(ctx *context.Context) {
|
|||||||
func HomeSitemap(ctx *context.Context) {
|
func HomeSitemap(ctx *context.Context) {
|
||||||
m := sitemap.NewSitemapIndex()
|
m := sitemap.NewSitemapIndex()
|
||||||
if !setting.Service.Explore.DisableUsersPage {
|
if !setting.Service.Explore.DisableUsersPage {
|
||||||
_, cnt, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
_, cnt, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||||
Type: user_model.UserTypeIndividual,
|
Type: user_model.UserTypeIndividual,
|
||||||
ListOptions: db.ListOptions{PageSize: 1},
|
ListOptions: db.ListOptions{PageSize: 1},
|
||||||
IsActive: optional.Some(true),
|
IsActive: optional.Some(true),
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
|
|
||||||
// SearchCandidates searches candidate users for dropdown list
|
// SearchCandidates searches candidate users for dropdown list
|
||||||
func SearchCandidates(ctx *context.Context) {
|
func SearchCandidates(ctx *context.Context) {
|
||||||
users, _, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
users, _, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
Keyword: ctx.FormTrim("q"),
|
Keyword: ctx.FormTrim("q"),
|
||||||
Type: user_model.UserTypeIndividual,
|
Type: user_model.UserTypeIndividual,
|
||||||
|
Reference in New Issue
Block a user