Skip to content

Commit 5d60d68

Browse files
committed
Merge branch 'main' into fix-repo-file-table
# Conflicts: # web_src/css/index.css
2 parents 562e63b + 18061af commit 5d60d68

File tree

97 files changed

+1197
-739
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+1197
-739
lines changed

models/db/search.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ const (
2626
SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
2727
)
2828

29-
const (
30-
// Which means a condition to filter the records which don't match any id.
31-
// It's different from zero which means the condition could be ignored.
32-
NoConditionID = -1
33-
)
29+
// NoConditionID means a condition to filter the records which don't match any id.
30+
// eg: "milestone_id=-1" means "find the items without any milestone.
31+
const NoConditionID int64 = -1
32+
33+
// NonExistingID means a condition to match no result (eg: a non-existing user)
34+
// It doesn't use -1 or -2 because they are used as builtin users.
35+
const NonExistingID int64 = -1000000

models/fixtures/review.yml

+18-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
reviewer_id: 1
55
issue_id: 2
66
content: "Demo Review"
7+
original_author_id: 0
78
updated_unix: 946684810
89
created_unix: 946684810
910
-
@@ -12,6 +13,7 @@
1213
reviewer_id: 534543
1314
issue_id: 534543
1415
content: "Invalid Review #1"
16+
original_author_id: 0
1517
updated_unix: 946684810
1618
created_unix: 946684810
1719
-
@@ -20,6 +22,7 @@
2022
reviewer_id: 1
2123
issue_id: 343545
2224
content: "Invalid Review #2"
25+
original_author_id: 0
2326
updated_unix: 946684810
2427
created_unix: 946684810
2528
-
@@ -28,6 +31,7 @@
2831
reviewer_id: 1
2932
issue_id: 2
3033
content: "Pending Review"
34+
original_author_id: 0
3135
updated_unix: 946684810
3236
created_unix: 946684810
3337
-
@@ -36,6 +40,7 @@
3640
reviewer_id: 1
3741
issue_id: 3
3842
content: "New review 1"
43+
original_author_id: 0
3944
updated_unix: 946684810
4045
created_unix: 946684810
4146
-
@@ -61,8 +66,8 @@
6166
type: 1
6267
reviewer_id: 4
6368
issue_id: 3
64-
original_author_id: 0
6569
content: "New review 5"
70+
original_author_id: 0
6671
commit_id: 8091a55037cd59e47293aca02981b5a67076b364
6772
stale: true
6873
updated_unix: 946684813
@@ -73,16 +78,17 @@
7378
reviewer_id: 2
7479
issue_id: 3
7580
content: "New review 3 rejected"
81+
original_author_id: 0
7682
updated_unix: 946684814
7783
created_unix: 946684814
78-
original_author_id: 0
7984

8085
-
8186
id: 10
8287
type: 3
8388
reviewer_id: 100
8489
issue_id: 3
8590
content: "a deleted user's review"
91+
original_author_id: 0
8692
official: true
8793
updated_unix: 946684815
8894
created_unix: 946684815
@@ -112,6 +118,7 @@
112118
reviewer_id: 5
113119
issue_id: 11
114120
content: "old review from user5"
121+
original_author_id: 0
115122
updated_unix: 946684820
116123
created_unix: 946684820
117124

@@ -121,6 +128,7 @@
121128
reviewer_id: 5
122129
issue_id: 11
123130
content: "duplicate review from user5 (latest)"
131+
original_author_id: 0
124132
updated_unix: 946684830
125133
created_unix: 946684830
126134

@@ -130,6 +138,7 @@
130138
reviewer_id: 6
131139
issue_id: 11
132140
content: "singular review from org6 and final review for this pr"
141+
original_author_id: 0
133142
updated_unix: 946684831
134143
created_unix: 946684831
135144

@@ -139,6 +148,7 @@
139148
reviewer_id: 20
140149
issue_id: 20
141150
content: "review request for user20"
151+
original_author_id: 0
142152
updated_unix: 946684832
143153
created_unix: 946684832
144154

@@ -148,6 +158,7 @@
148158
reviewer_id: 20
149159
issue_id: 20
150160
content: "review approved by user20"
161+
original_author_id: 0
151162
updated_unix: 946684833
152163
created_unix: 946684833
153164

@@ -158,6 +169,7 @@
158169
reviewer_team_id: 5
159170
issue_id: 20
160171
content: "review request for team5"
172+
original_author_id: 0
161173
updated_unix: 946684834
162174
created_unix: 946684834
163175

@@ -168,6 +180,7 @@
168180
reviewer_team_id: 0
169181
issue_id: 20
170182
content: "review request for user15"
183+
original_author_id: 0
171184
updated_unix: 946684835
172185
created_unix: 946684835
173186

@@ -177,6 +190,7 @@
177190
reviewer_id: 1
178191
issue_id: 2
179192
content: "Review Comment"
193+
original_author_id: 0
180194
updated_unix: 946684810
181195
created_unix: 946684810
182196

@@ -186,6 +200,7 @@
186200
reviewer_id: 5
187201
issue_id: 3
188202
content: "reviewed by user5"
203+
original_author_id: 0
189204
commit_id: 4a357436d925b5c974181ff12a994538ddc5a269
190205
updated_unix: 946684816
191206
created_unix: 946684816
@@ -196,5 +211,6 @@
196211
reviewer_id: 5
197212
issue_id: 3
198213
content: "review request for user5"
214+
original_author_id: 0
199215
updated_unix: 946684817
200216
created_unix: 946684817

models/issues/issue_search.go

+23-16
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ type IssuesOptions struct { //nolint
2727
RepoIDs []int64 // overwrites RepoCond if the length is not 0
2828
AllPublic bool // include also all public repositories
2929
RepoCond builder.Cond
30-
AssigneeID int64
31-
PosterID int64
30+
AssigneeID optional.Option[int64]
31+
PosterID optional.Option[int64]
3232
MentionedID int64
3333
ReviewRequestedID int64
3434
ReviewedID int64
@@ -231,15 +231,8 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) {
231231
sess.And("issue.is_closed=?", opts.IsClosed.Value())
232232
}
233233

234-
if opts.AssigneeID > 0 {
235-
applyAssigneeCondition(sess, opts.AssigneeID)
236-
} else if opts.AssigneeID == db.NoConditionID {
237-
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)")
238-
}
239-
240-
if opts.PosterID > 0 {
241-
applyPosterCondition(sess, opts.PosterID)
242-
}
234+
applyAssigneeCondition(sess, opts.AssigneeID)
235+
applyPosterCondition(sess, opts.PosterID)
243236

244237
if opts.MentionedID > 0 {
245238
applyMentionedCondition(sess, opts.MentionedID)
@@ -359,13 +352,27 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati
359352
return cond
360353
}
361354

362-
func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) {
363-
sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
364-
And("issue_assignees.assignee_id = ?", assigneeID)
355+
func applyAssigneeCondition(sess *xorm.Session, assigneeID optional.Option[int64]) {
356+
// old logic: 0 is also treated as "not filtering assignee", because the "assignee" was read as FormInt64
357+
if !assigneeID.Has() || assigneeID.Value() == 0 {
358+
return
359+
}
360+
if assigneeID.Value() == db.NoConditionID {
361+
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)")
362+
} else {
363+
sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
364+
And("issue_assignees.assignee_id = ?", assigneeID.Value())
365+
}
365366
}
366367

367-
func applyPosterCondition(sess *xorm.Session, posterID int64) {
368-
sess.And("issue.poster_id=?", posterID)
368+
func applyPosterCondition(sess *xorm.Session, posterID optional.Option[int64]) {
369+
if !posterID.Has() {
370+
return
371+
}
372+
// poster doesn't need to support db.NoConditionID(-1), so just use the value as-is
373+
if posterID.Has() {
374+
sess.And("issue.poster_id=?", posterID.Value())
375+
}
369376
}
370377

371378
func applyMentionedCondition(sess *xorm.Session, mentionedID int64) {

models/issues/issue_stats.go

+2-8
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,9 @@ func applyIssuesOptions(sess *xorm.Session, opts *IssuesOptions, issueIDs []int6
151151

152152
applyProjectCondition(sess, opts)
153153

154-
if opts.AssigneeID > 0 {
155-
applyAssigneeCondition(sess, opts.AssigneeID)
156-
} else if opts.AssigneeID == db.NoConditionID {
157-
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)")
158-
}
154+
applyAssigneeCondition(sess, opts.AssigneeID)
159155

160-
if opts.PosterID > 0 {
161-
applyPosterCondition(sess, opts.PosterID)
162-
}
156+
applyPosterCondition(sess, opts.PosterID)
163157

164158
if opts.MentionedID > 0 {
165159
applyMentionedCondition(sess, opts.MentionedID)

models/issues/issue_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
repo_model "code.gitea.io/gitea/models/repo"
1717
"code.gitea.io/gitea/models/unittest"
1818
user_model "code.gitea.io/gitea/models/user"
19+
"code.gitea.io/gitea/modules/optional"
1920
"code.gitea.io/gitea/modules/setting"
2021

2122
"github.com/stretchr/testify/assert"
@@ -155,7 +156,7 @@ func TestIssues(t *testing.T) {
155156
}{
156157
{
157158
issues_model.IssuesOptions{
158-
AssigneeID: 1,
159+
AssigneeID: optional.Some(int64(1)),
159160
SortType: "oldest",
160161
},
161162
[]int64{1, 6},

models/issues/pull_list.go

+59
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"code.gitea.io/gitea/modules/log"
1717
"code.gitea.io/gitea/modules/util"
1818

19+
"xorm.io/builder"
1920
"xorm.io/xorm"
2021
)
2122

@@ -240,6 +241,64 @@ func (prs PullRequestList) GetIssueIDs() []int64 {
240241
})
241242
}
242243

244+
func (prs PullRequestList) LoadReviewCommentsCounts(ctx context.Context) (map[int64]int, error) {
245+
issueIDs := prs.GetIssueIDs()
246+
countsMap := make(map[int64]int, len(issueIDs))
247+
counts := make([]struct {
248+
IssueID int64
249+
Count int
250+
}, 0, len(issueIDs))
251+
if err := db.GetEngine(ctx).Select("issue_id, count(*) as count").
252+
Table("comment").In("issue_id", issueIDs).And("type = ?", CommentTypeReview).
253+
GroupBy("issue_id").Find(&counts); err != nil {
254+
return nil, err
255+
}
256+
for _, c := range counts {
257+
countsMap[c.IssueID] = c.Count
258+
}
259+
return countsMap, nil
260+
}
261+
262+
func (prs PullRequestList) LoadReviews(ctx context.Context) (ReviewList, error) {
263+
issueIDs := prs.GetIssueIDs()
264+
reviews := make([]*Review, 0, len(issueIDs))
265+
266+
subQuery := builder.Select("max(id) as id").
267+
From("review").
268+
Where(builder.In("issue_id", issueIDs)).
269+
And(builder.In("`type`", ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest)).
270+
And(builder.Eq{
271+
"dismissed": false,
272+
"original_author_id": 0,
273+
"reviewer_team_id": 0,
274+
}).
275+
GroupBy("issue_id, reviewer_id")
276+
// Get latest review of each reviewer, sorted in order they were made
277+
if err := db.GetEngine(ctx).In("id", subQuery).OrderBy("review.updated_unix ASC").Find(&reviews); err != nil {
278+
return nil, err
279+
}
280+
281+
teamReviewRequests := make([]*Review, 0, 5)
282+
subQueryTeam := builder.Select("max(id) as id").
283+
From("review").
284+
Where(builder.In("issue_id", issueIDs)).
285+
And(builder.Eq{
286+
"original_author_id": 0,
287+
}).And(builder.Neq{
288+
"reviewer_team_id": 0,
289+
}).
290+
GroupBy("issue_id, reviewer_team_id")
291+
if err := db.GetEngine(ctx).In("id", subQueryTeam).OrderBy("review.updated_unix ASC").Find(&teamReviewRequests); err != nil {
292+
return nil, err
293+
}
294+
295+
if len(teamReviewRequests) > 0 {
296+
reviews = append(reviews, teamReviewRequests...)
297+
}
298+
299+
return reviews, nil
300+
}
301+
243302
// HasMergedPullRequestInRepo returns whether the user(poster) has merged pull-request in the repo
244303
func HasMergedPullRequestInRepo(ctx context.Context, repoID, posterID int64) (bool, error) {
245304
return db.GetEngine(ctx).

models/issues/pull_list_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package issues_test
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/db"
10+
issues_model "code.gitea.io/gitea/models/issues"
11+
"code.gitea.io/gitea/models/unittest"
12+
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestPullRequestList_LoadAttributes(t *testing.T) {
17+
assert.NoError(t, unittest.PrepareTestDatabase())
18+
19+
prs := []*issues_model.PullRequest{
20+
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
21+
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
22+
}
23+
assert.NoError(t, issues_model.PullRequestList(prs).LoadAttributes(db.DefaultContext))
24+
for _, pr := range prs {
25+
assert.NotNil(t, pr.Issue)
26+
assert.Equal(t, pr.IssueID, pr.Issue.ID)
27+
}
28+
29+
assert.NoError(t, issues_model.PullRequestList([]*issues_model.PullRequest{}).LoadAttributes(db.DefaultContext))
30+
}
31+
32+
func TestPullRequestList_LoadReviewCommentsCounts(t *testing.T) {
33+
assert.NoError(t, unittest.PrepareTestDatabase())
34+
35+
prs := []*issues_model.PullRequest{
36+
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
37+
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
38+
}
39+
reviewComments, err := issues_model.PullRequestList(prs).LoadReviewCommentsCounts(db.DefaultContext)
40+
assert.NoError(t, err)
41+
assert.Len(t, reviewComments, 2)
42+
for _, pr := range prs {
43+
assert.EqualValues(t, reviewComments[pr.IssueID], 1)
44+
}
45+
}
46+
47+
func TestPullRequestList_LoadReviews(t *testing.T) {
48+
assert.NoError(t, unittest.PrepareTestDatabase())
49+
50+
prs := []*issues_model.PullRequest{
51+
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
52+
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
53+
}
54+
reviewList, err := issues_model.PullRequestList(prs).LoadReviews(db.DefaultContext)
55+
assert.NoError(t, err)
56+
// 1, 7, 8, 9, 10, 22
57+
assert.Len(t, reviewList, 6)
58+
assert.EqualValues(t, 1, reviewList[0].ID)
59+
assert.EqualValues(t, 7, reviewList[1].ID)
60+
assert.EqualValues(t, 8, reviewList[2].ID)
61+
assert.EqualValues(t, 9, reviewList[3].ID)
62+
assert.EqualValues(t, 10, reviewList[4].ID)
63+
assert.EqualValues(t, 22, reviewList[5].ID)
64+
}

0 commit comments

Comments
 (0)