Skip to content

Commit 4c9341f

Browse files
ethantkoeniglafriks
authored andcommitted
Fix bugs in issue dashboard stats (#3073)
1 parent fabf3f2 commit 4c9341f

File tree

7 files changed

+224
-55
lines changed

7 files changed

+224
-55
lines changed

models/fixtures/issue.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
content: content for the fourth issue
4848
is_closed: true
4949
is_pull: false
50+
created_unix: 946684830
51+
updated_unix: 978307200
5052

5153
-
5254
id: 5
@@ -57,6 +59,9 @@
5759
content: content for the fifth issue
5860
is_closed: true
5961
is_pull: false
62+
created_unix: 946684840
63+
updated_unix: 978307200
64+
6065
-
6166
id: 6
6267
repo_id: 3
@@ -68,5 +73,5 @@
6873
is_closed: false
6974
is_pull: false
7075
num_comments: 0
71-
created_unix: 946684800
76+
created_unix: 946684850
7277
updated_unix: 978307200

models/issue.go

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import (
1010
"sort"
1111
"strings"
1212

13-
api "code.gitea.io/sdk/gitea"
14-
"github.com/Unknwon/com"
15-
"github.com/go-xorm/xorm"
16-
1713
"code.gitea.io/gitea/modules/base"
1814
"code.gitea.io/gitea/modules/log"
1915
"code.gitea.io/gitea/modules/setting"
2016
"code.gitea.io/gitea/modules/util"
17+
api "code.gitea.io/sdk/gitea"
18+
19+
"github.com/Unknwon/com"
20+
"github.com/go-xorm/builder"
21+
"github.com/go-xorm/xorm"
2122
)
2223

2324
// Issue represents an issue or pull request of repository.
@@ -1022,12 +1023,11 @@ func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) {
10221023

10231024
// IssuesOptions represents options of an issue.
10241025
type IssuesOptions struct {
1025-
RepoID int64
1026+
RepoIDs []int64 // include all repos if empty
10261027
AssigneeID int64
10271028
PosterID int64
10281029
MentionedID int64
10291030
MilestoneID int64
1030-
RepoIDs []int64
10311031
Page int
10321032
PageSize int
10331033
IsClosed util.OptionalBool
@@ -1073,9 +1073,7 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) error {
10731073
sess.In("issue.id", opts.IssueIDs)
10741074
}
10751075

1076-
if opts.RepoID > 0 {
1077-
sess.And("issue.repo_id=?", opts.RepoID)
1078-
} else if len(opts.RepoIDs) > 0 {
1076+
if len(opts.RepoIDs) > 0 {
10791077
// In case repository IDs are provided but actually no repository has issue.
10801078
sess.In("issue.repo_id", opts.RepoIDs)
10811079
}
@@ -1339,58 +1337,92 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
13391337
return stats, err
13401338
}
13411339

1340+
// UserIssueStatsOptions contains parameters accepted by GetUserIssueStats.
1341+
type UserIssueStatsOptions struct {
1342+
UserID int64
1343+
RepoID int64
1344+
UserRepoIDs []int64
1345+
FilterMode int
1346+
IsPull bool
1347+
IsClosed bool
1348+
}
1349+
13421350
// GetUserIssueStats returns issue statistic information for dashboard by given conditions.
1343-
func GetUserIssueStats(repoID, uid int64, repoIDs []int64, filterMode int, isPull bool) *IssueStats {
1351+
func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
1352+
var err error
13441353
stats := &IssueStats{}
13451354

1346-
countSession := func(isClosed, isPull bool, repoID int64, repoIDs []int64) *xorm.Session {
1347-
sess := x.
1348-
Where("issue.is_closed = ?", isClosed).
1349-
And("issue.is_pull = ?", isPull)
1350-
1351-
if repoID > 0 {
1352-
sess.And("repo_id = ?", repoID)
1353-
} else if len(repoIDs) > 0 {
1354-
sess.In("repo_id", repoIDs)
1355-
}
1356-
1357-
return sess
1355+
cond := builder.NewCond()
1356+
cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull})
1357+
if opts.RepoID > 0 {
1358+
cond = cond.And(builder.Eq{"issue.repo_id": opts.RepoID})
13581359
}
13591360

1360-
stats.AssignCount, _ = countSession(false, isPull, repoID, nil).
1361-
And("assignee_id = ?", uid).
1362-
Count(new(Issue))
1363-
1364-
stats.CreateCount, _ = countSession(false, isPull, repoID, nil).
1365-
And("poster_id = ?", uid).
1366-
Count(new(Issue))
1367-
1368-
stats.YourRepositoriesCount, _ = countSession(false, isPull, repoID, repoIDs).
1369-
Count(new(Issue))
1370-
1371-
switch filterMode {
1361+
switch opts.FilterMode {
13721362
case FilterModeAll:
1373-
stats.OpenCount, _ = countSession(false, isPull, repoID, repoIDs).
1363+
stats.OpenCount, err = x.Where(cond).And("is_closed = ?", false).
1364+
And(builder.In("issue.repo_id", opts.UserRepoIDs)).
13741365
Count(new(Issue))
1375-
stats.ClosedCount, _ = countSession(true, isPull, repoID, repoIDs).
1366+
if err != nil {
1367+
return nil, err
1368+
}
1369+
stats.ClosedCount, err = x.Where(cond).And("is_closed = ?", true).
1370+
And(builder.In("issue.repo_id", opts.UserRepoIDs)).
13761371
Count(new(Issue))
1372+
if err != nil {
1373+
return nil, err
1374+
}
13771375
case FilterModeAssign:
1378-
stats.OpenCount, _ = countSession(false, isPull, repoID, nil).
1379-
And("assignee_id = ?", uid).
1376+
stats.OpenCount, err = x.Where(cond).And("is_closed = ?", false).
1377+
And("assignee_id = ?", opts.UserID).
13801378
Count(new(Issue))
1381-
stats.ClosedCount, _ = countSession(true, isPull, repoID, nil).
1382-
And("assignee_id = ?", uid).
1379+
if err != nil {
1380+
return nil, err
1381+
}
1382+
stats.ClosedCount, err = x.Where(cond).And("is_closed = ?", true).
1383+
And("assignee_id = ?", opts.UserID).
13831384
Count(new(Issue))
1385+
if err != nil {
1386+
return nil, err
1387+
}
13841388
case FilterModeCreate:
1385-
stats.OpenCount, _ = countSession(false, isPull, repoID, nil).
1386-
And("poster_id = ?", uid).
1389+
stats.OpenCount, err = x.Where(cond).And("is_closed = ?", false).
1390+
And("poster_id = ?", opts.UserID).
13871391
Count(new(Issue))
1388-
stats.ClosedCount, _ = countSession(true, isPull, repoID, nil).
1389-
And("poster_id = ?", uid).
1392+
if err != nil {
1393+
return nil, err
1394+
}
1395+
stats.ClosedCount, err = x.Where(cond).And("is_closed = ?", true).
1396+
And("poster_id = ?", opts.UserID).
13901397
Count(new(Issue))
1398+
if err != nil {
1399+
return nil, err
1400+
}
1401+
}
1402+
1403+
cond = cond.And(builder.Eq{"issue.is_closed": opts.IsClosed})
1404+
stats.AssignCount, err = x.Where(cond).
1405+
And("assignee_id = ?", opts.UserID).
1406+
Count(new(Issue))
1407+
if err != nil {
1408+
return nil, err
1409+
}
1410+
1411+
stats.CreateCount, err = x.Where(cond).
1412+
And("poster_id = ?", opts.UserID).
1413+
Count(new(Issue))
1414+
if err != nil {
1415+
return nil, err
1416+
}
1417+
1418+
stats.YourRepositoriesCount, err = x.Where(cond).
1419+
And(builder.In("issue.repo_id", opts.UserRepoIDs)).
1420+
Count(new(Issue))
1421+
if err != nil {
1422+
return nil, err
13911423
}
13921424

1393-
return stats
1425+
return stats, nil
13941426
}
13951427

13961428
// GetRepoIssueStats returns number of open and closed repository issues by given filter mode.

models/issue_indexer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func populateIssueIndexer() error {
4242
}
4343
for _, repo := range repos {
4444
issues, err := Issues(&IssuesOptions{
45-
RepoID: repo.ID,
45+
RepoIDs: []int64{repo.ID},
4646
IsClosed: util.OptionalBoolNone,
4747
IsPull: util.OptionalBoolNone,
4848
})

models/issue_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,114 @@ func TestUpdateIssueCols(t *testing.T) {
168168
assert.EqualValues(t, prevContent, updatedIssue.Content)
169169
AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
170170
}
171+
172+
func TestIssues(t *testing.T) {
173+
assert.NoError(t, PrepareTestDatabase())
174+
for _, test := range []struct {
175+
Opts IssuesOptions
176+
ExpectedIssueIDs []int64
177+
}{
178+
{
179+
IssuesOptions{
180+
AssigneeID: 1,
181+
SortType: "oldest",
182+
},
183+
[]int64{1, 6},
184+
},
185+
{
186+
IssuesOptions{
187+
RepoIDs: []int64{1, 3},
188+
SortType: "oldest",
189+
Page: 1,
190+
PageSize: 4,
191+
},
192+
[]int64{1, 2, 3, 5},
193+
},
194+
{
195+
IssuesOptions{
196+
Labels: "1,2",
197+
Page: 1,
198+
PageSize: 4,
199+
},
200+
[]int64{5, 2, 1},
201+
},
202+
} {
203+
issues, err := Issues(&test.Opts)
204+
assert.NoError(t, err)
205+
if assert.Len(t, issues, len(test.ExpectedIssueIDs)) {
206+
for i, issue := range issues {
207+
assert.EqualValues(t, test.ExpectedIssueIDs[i], issue.ID)
208+
}
209+
}
210+
}
211+
}
212+
213+
func TestGetUserIssueStats(t *testing.T) {
214+
assert.NoError(t, PrepareTestDatabase())
215+
for _, test := range []struct {
216+
Opts UserIssueStatsOptions
217+
ExpectedIssueStats IssueStats
218+
}{
219+
{
220+
UserIssueStatsOptions{
221+
UserID: 1,
222+
RepoID: 1,
223+
FilterMode: FilterModeAll,
224+
},
225+
IssueStats{
226+
YourRepositoriesCount: 0,
227+
AssignCount: 1,
228+
CreateCount: 1,
229+
OpenCount: 0,
230+
ClosedCount: 0,
231+
},
232+
},
233+
{
234+
UserIssueStatsOptions{
235+
UserID: 1,
236+
FilterMode: FilterModeAssign,
237+
},
238+
IssueStats{
239+
YourRepositoriesCount: 0,
240+
AssignCount: 2,
241+
CreateCount: 2,
242+
OpenCount: 2,
243+
ClosedCount: 0,
244+
},
245+
},
246+
{
247+
UserIssueStatsOptions{
248+
UserID: 1,
249+
FilterMode: FilterModeCreate,
250+
},
251+
IssueStats{
252+
YourRepositoriesCount: 0,
253+
AssignCount: 2,
254+
CreateCount: 2,
255+
OpenCount: 2,
256+
ClosedCount: 0,
257+
},
258+
},
259+
{
260+
UserIssueStatsOptions{
261+
UserID: 2,
262+
UserRepoIDs: []int64{1, 2},
263+
FilterMode: FilterModeAll,
264+
IsClosed: true,
265+
},
266+
IssueStats{
267+
YourRepositoriesCount: 2,
268+
AssignCount: 0,
269+
CreateCount: 2,
270+
OpenCount: 1,
271+
ClosedCount: 2,
272+
},
273+
},
274+
} {
275+
stats, err := GetUserIssueStats(test.Opts)
276+
if !assert.NoError(t, err) {
277+
continue
278+
}
279+
assert.Equal(t, test.ExpectedIssueStats, *stats)
280+
}
281+
}

routers/api/v1/repo/issue.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func ListIssues(ctx *context.APIContext) {
5656
}
5757

5858
issues, err := models.Issues(&models.IssuesOptions{
59-
RepoID: ctx.Repo.Repository.ID,
59+
RepoIDs: []int64{ctx.Repo.Repository.ID},
6060
Page: ctx.QueryInt("page"),
6161
PageSize: setting.UI.IssuePagingNum,
6262
IsClosed: isClosed,

routers/repo/issue.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ func Issues(ctx *context.Context) {
190190
issues = []*models.Issue{}
191191
} else {
192192
issues, err = models.Issues(&models.IssuesOptions{
193+
RepoIDs: []int64{repo.ID},
193194
AssigneeID: assigneeID,
194-
RepoID: repo.ID,
195195
PosterID: posterID,
196196
MentionedID: mentionedID,
197197
MilestoneID: milestoneID,

0 commit comments

Comments
 (0)