Skip to content

Commit 789b73b

Browse files
update per feedback
1 parent f601501 commit 789b73b

File tree

4 files changed

+124
-98
lines changed

4 files changed

+124
-98
lines changed

models/user/badge.go

Lines changed: 62 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import (
99
"strings"
1010

1111
"code.gitea.io/gitea/models/db"
12+
"code.gitea.io/gitea/modules/util"
1213

1314
"xorm.io/builder"
14-
"xorm.io/xorm"
1515
)
1616

1717
// Badge represents a user badge
@@ -29,6 +29,50 @@ type UserBadge struct { //nolint:revive
2929
UserID int64 `xorm:"INDEX"`
3030
}
3131

32+
// ErrBadgeAlreadyExist represents a "badge already exists" error.
33+
type ErrBadgeAlreadyExist struct {
34+
Slug string
35+
}
36+
37+
// IsErrBadgeAlreadyExist checks if an error is a ErrBadgeAlreadyExist.
38+
func IsErrBadgeAlreadyExist(err error) bool {
39+
_, ok := err.(ErrBadgeAlreadyExist)
40+
return ok
41+
}
42+
43+
func (err ErrBadgeAlreadyExist) Error() string {
44+
return fmt.Sprintf("badge already exists [slug: %s]", err.Slug)
45+
}
46+
47+
// Unwrap unwraps this error as a ErrExist error
48+
func (err ErrBadgeAlreadyExist) Unwrap() error {
49+
return util.ErrAlreadyExist
50+
}
51+
52+
// ErrBadgeNotExist represents a "BadgeNotExist" kind of error.
53+
type ErrBadgeNotExist struct {
54+
Slug string
55+
ID int64
56+
}
57+
58+
func (err ErrBadgeNotExist) Error() string {
59+
if err.ID > 0 {
60+
return fmt.Sprintf("badge does not exist [id: %d]", err.ID)
61+
}
62+
return fmt.Sprintf("badge does not exist [slug: %s]", err.Slug)
63+
}
64+
65+
// IsErrBadgeNotExist checks if an error is a ErrBadgeNotExist.
66+
func IsErrBadgeNotExist(err error) bool {
67+
_, ok := err.(ErrBadgeNotExist)
68+
return ok
69+
}
70+
71+
// Unwrap unwraps this error as a ErrNotExist error
72+
func (err ErrBadgeNotExist) Unwrap() error {
73+
return util.ErrNotExist
74+
}
75+
3276
func init() {
3377
db.RegisterModel(new(Badge))
3478
db.RegisterModel(new(UserBadge))
@@ -73,7 +117,6 @@ func GetBadgeUsers(ctx context.Context, opts *GetBadgeUsersOptions) ([]*User, in
73117
func CreateBadge(ctx context.Context, badge *Badge) error {
74118
// this will fail if the badge already exists due to the UNIQUE constraint
75119
_, err := db.GetEngine(ctx).Insert(badge)
76-
77120
return err
78121
}
79122

@@ -151,11 +194,14 @@ func RemoveUserBadges(ctx context.Context, u *User, badges []*Badge) error {
151194
slugs[i] = badge.Slug
152195
}
153196

197+
var badgeIDs []int64
198+
if err := db.GetEngine(ctx).Table("badge").In("slug", slugs).Cols("id").Find(&badgeIDs); err != nil {
199+
return err
200+
}
201+
154202
if _, err := db.GetEngine(ctx).
155-
Table("user_badge").
156-
Join("INNER", "badge", "`user_badge`.badge_id = badge.id").
157-
Where("`user_badge`.user_id = ?", u.ID).
158-
And(builder.In("badge.slug", slugs)).
203+
Where("user_id = ?", u.ID).
204+
In("badge_id", badgeIDs).
159205
Delete(&UserBadge{}); err != nil {
160206
return err
161207
}
@@ -184,66 +230,29 @@ func (opts *SearchBadgeOptions) ToConds() builder.Cond {
184230
cond := builder.NewCond()
185231

186232
if opts.Keyword != "" {
187-
cond = cond.And(builder.Like{"badge.slug", opts.Keyword})
188-
}
189-
190-
return cond
191-
}
192-
193-
func (opts *SearchBadgeOptions) ToOrders() string {
194-
orderBy := "badge.slug"
195-
return orderBy
196-
}
197-
198-
func SearchBadges(ctx context.Context, opts *SearchBadgeOptions) (badges []*Badge, _ int64, _ error) {
199-
sessCount := opts.toSearchQueryBase(ctx)
200-
count, err := sessCount.Count(new(Badge))
201-
if err != nil {
202-
return nil, 0, fmt.Errorf("count: %w", err)
203-
}
204-
sessCount.Close()
205-
206-
if len(opts.OrderBy) == 0 {
207-
opts.OrderBy = db.SearchOrderByID
208-
}
209-
210-
sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String())
211-
defer sessQuery.Close()
212-
if opts.Page != 0 {
213-
sessQuery = db.SetSessionPagination(sessQuery, opts)
214-
}
215-
216-
// the sql may contain JOIN, so we must only select Badge related columns
217-
sessQuery = sessQuery.Select("`badge`.*")
218-
badges = make([]*Badge, 0, opts.PageSize)
219-
return badges, count, sessQuery.Find(&badges)
220-
}
221-
222-
func (opts *SearchBadgeOptions) toSearchQueryBase(ctx context.Context) *xorm.Session {
223-
var cond builder.Cond
224-
cond = builder.Neq{"id": -1}
225-
226-
if len(opts.Keyword) > 0 {
227233
lowerKeyword := strings.ToLower(opts.Keyword)
228234
keywordCond := builder.Or(
229-
builder.Like{"slug", lowerKeyword},
230-
builder.Like{"description", lowerKeyword},
231-
builder.Like{"id", lowerKeyword},
235+
builder.Like{"badge.slug", lowerKeyword},
236+
builder.Like{"badge.description", lowerKeyword},
237+
builder.Like{"badge.id", lowerKeyword},
232238
)
233239
cond = cond.And(keywordCond)
234240
}
235241

236242
if opts.ID > 0 {
237-
cond = cond.And(builder.Eq{"id": opts.ID})
243+
cond = cond.And(builder.Eq{"badge.id": opts.ID})
238244
}
239245

240246
if len(opts.Slug) > 0 {
241-
cond = cond.And(builder.Eq{"slug": opts.Slug})
247+
cond = cond.And(builder.Eq{"badge.slug": opts.Slug})
242248
}
243249

244-
e := db.GetEngine(ctx)
250+
return cond
251+
}
245252

246-
return e.Where(cond)
253+
// SearchBadges returns badges based on the provided SearchBadgeOptions options
254+
func SearchBadges(ctx context.Context, opts *SearchBadgeOptions) ([]*Badge, int64, error) {
255+
return db.FindAndCount[Badge](ctx, opts)
247256
}
248257

249258
// GetBadgeByID returns a specific badge by ID

models/user/badge_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package user_test
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/db"
10+
"code.gitea.io/gitea/models/unittest"
11+
user_model "code.gitea.io/gitea/models/user"
12+
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestGetBadgeUsers(t *testing.T) {
17+
assert.NoError(t, unittest.PrepareTestDatabase())
18+
19+
// Create a test badge
20+
badge := &user_model.Badge{
21+
Slug: "test-badge",
22+
Description: "Test Badge",
23+
ImageURL: "test.png",
24+
}
25+
assert.NoError(t, user_model.CreateBadge(db.DefaultContext, badge))
26+
27+
// Create test users and assign badges
28+
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
29+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
30+
31+
assert.NoError(t, user_model.AddUserBadge(db.DefaultContext, user1, badge))
32+
assert.NoError(t, user_model.AddUserBadge(db.DefaultContext, user2, badge))
33+
34+
// Test getting users with pagination
35+
opts := &user_model.GetBadgeUsersOptions{
36+
Badge: badge,
37+
ListOptions: db.ListOptions{
38+
Page: 1,
39+
PageSize: 1,
40+
},
41+
}
42+
43+
users, count, err := user_model.GetBadgeUsers(db.DefaultContext, opts)
44+
assert.NoError(t, err)
45+
assert.EqualValues(t, 2, count)
46+
assert.Len(t, users, 1)
47+
48+
// Test second page
49+
opts.Page = 2
50+
users, count, err = user_model.GetBadgeUsers(db.DefaultContext, opts)
51+
assert.NoError(t, err)
52+
assert.EqualValues(t, 2, count)
53+
assert.Len(t, users, 1)
54+
55+
// Test with non-existent badge
56+
opts.Badge = &user_model.Badge{Slug: "non-existent"}
57+
users, count, err = user_model.GetBadgeUsers(db.DefaultContext, opts)
58+
assert.NoError(t, err)
59+
assert.EqualValues(t, 0, count)
60+
assert.Len(t, users, 0)
61+
}

models/user/error.go

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -107,47 +107,3 @@ func IsErrUserIsNotLocal(err error) bool {
107107
_, ok := err.(ErrUserIsNotLocal)
108108
return ok
109109
}
110-
111-
// ErrBadgeAlreadyExist represents a "badge already exists" error.
112-
type ErrBadgeAlreadyExist struct {
113-
Slug string
114-
}
115-
116-
// IsErrBadgeAlreadyExist checks if an error is a ErrBadgeAlreadyExist.
117-
func IsErrBadgeAlreadyExist(err error) bool {
118-
_, ok := err.(ErrBadgeAlreadyExist)
119-
return ok
120-
}
121-
122-
func (err ErrBadgeAlreadyExist) Error() string {
123-
return fmt.Sprintf("badge already exists [slug: %s]", err.Slug)
124-
}
125-
126-
// Unwrap unwraps this error as a ErrExist error
127-
func (err ErrBadgeAlreadyExist) Unwrap() error {
128-
return util.ErrAlreadyExist
129-
}
130-
131-
// ErrBadgeNotExist represents a "BadgeNotExist" kind of error.
132-
type ErrBadgeNotExist struct {
133-
Slug string
134-
ID int64
135-
}
136-
137-
func (err ErrBadgeNotExist) Error() string {
138-
if err.ID > 0 {
139-
return fmt.Sprintf("badge does not exist [id: %d]", err.ID)
140-
}
141-
return fmt.Sprintf("badge does not exist [slug: %s]", err.Slug)
142-
}
143-
144-
// IsErrBadgeNotExist checks if an error is a ErrBadgeNotExist.
145-
func IsErrBadgeNotExist(err error) bool {
146-
_, ok := err.(ErrBadgeNotExist)
147-
return ok
148-
}
149-
150-
// Unwrap unwraps this error as a ErrNotExist error
151-
func (err ErrBadgeNotExist) Unwrap() error {
152-
return util.ErrNotExist
153-
}

routers/web/admin/badges.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ func DeleteBadgeUser(ctx *context.Context) {
286286
if err := user_model.RemoveUserBadge(ctx, user, &user_model.Badge{Slug: ctx.PathParam(":badge_slug")}); err == nil {
287287
ctx.Flash.Success(ctx.Tr("admin.badges.user_remove_success"))
288288
} else {
289-
ctx.Flash.Error("DeleteUser: " + err.Error())
289+
ctx.Flash.Error("DeleteBadgeUser: " + err.Error())
290290
}
291291

292292
ctx.JSONRedirect(fmt.Sprintf("%s/-/admin/badges/%s/users", setting.AppSubURL, ctx.PathParam(":badge_slug")))

0 commit comments

Comments
 (0)