Skip to content

Commit acb1ceb

Browse files
authored
Add review requested filter on pull request overview (#13701)
* Add review requested filter on pull request overview #13682 fix formatting * add review_requested filter to /repos/issues/search API endpoint * only Approve and Reject status should supersede Request status * add support for team reviews * refactor: remove duplication of issue filtering conditions
1 parent 872d308 commit acb1ceb

File tree

9 files changed

+156
-88
lines changed

9 files changed

+156
-88
lines changed

models/issue.go

Lines changed: 97 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,7 @@ type IssuesOptions struct {
10901090
AssigneeID int64
10911091
PosterID int64
10921092
MentionedID int64
1093+
ReviewRequestedID int64
10931094
MilestoneIDs []int64
10941095
ProjectID int64
10951096
ProjectBoardID int64
@@ -1151,8 +1152,7 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
11511152
}
11521153

11531154
if len(opts.RepoIDs) > 0 {
1154-
// In case repository IDs are provided but actually no repository has issue.
1155-
sess.In("issue.repo_id", opts.RepoIDs)
1155+
applyReposCondition(sess, opts.RepoIDs)
11561156
}
11571157

11581158
switch opts.IsClosed {
@@ -1163,18 +1163,19 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
11631163
}
11641164

11651165
if opts.AssigneeID > 0 {
1166-
sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
1167-
And("issue_assignees.assignee_id = ?", opts.AssigneeID)
1166+
applyAssigneeCondition(sess, opts.AssigneeID)
11681167
}
11691168

11701169
if opts.PosterID > 0 {
1171-
sess.And("issue.poster_id=?", opts.PosterID)
1170+
applyPosterCondition(sess, opts.PosterID)
11721171
}
11731172

11741173
if opts.MentionedID > 0 {
1175-
sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id").
1176-
And("issue_user.is_mentioned = ?", true).
1177-
And("issue_user.uid = ?", opts.MentionedID)
1174+
applyMentionedCondition(sess, opts.MentionedID)
1175+
}
1176+
1177+
if opts.ReviewRequestedID > 0 {
1178+
applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
11781179
}
11791180

11801181
if len(opts.MilestoneIDs) > 0 {
@@ -1232,6 +1233,33 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
12321233
}
12331234
}
12341235

1236+
func applyReposCondition(sess *xorm.Session, repoIDs []int64) *xorm.Session {
1237+
return sess.In("issue.repo_id", repoIDs)
1238+
}
1239+
1240+
func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) *xorm.Session {
1241+
return sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
1242+
And("issue_assignees.assignee_id = ?", assigneeID)
1243+
}
1244+
1245+
func applyPosterCondition(sess *xorm.Session, posterID int64) *xorm.Session {
1246+
return sess.And("issue.poster_id=?", posterID)
1247+
}
1248+
1249+
func applyMentionedCondition(sess *xorm.Session, mentionedID int64) *xorm.Session {
1250+
return sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id").
1251+
And("issue_user.is_mentioned = ?", true).
1252+
And("issue_user.uid = ?", mentionedID)
1253+
}
1254+
1255+
func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64) *xorm.Session {
1256+
return sess.Join("INNER", []string{"review", "r"}, "issue.id = r.issue_id").
1257+
And("r.type = ?", ReviewTypeRequest).
1258+
And("r.reviewer_id = ? and r.id in (select max(id) from review where issue_id = r.issue_id and reviewer_id = r.reviewer_id and type in (?, ?, ?))"+
1259+
" or r.reviewer_team_id in (select team_id from team_user where uid = ?)",
1260+
reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID)
1261+
}
1262+
12351263
// CountIssuesByRepo map from repoID to number of issues matching the options
12361264
func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
12371265
sess := x.NewSession()
@@ -1364,6 +1392,7 @@ type IssueStats struct {
13641392
AssignCount int64
13651393
CreateCount int64
13661394
MentionCount int64
1395+
ReviewRequestedCount int64
13671396
}
13681397

13691398
// Filter modes.
@@ -1372,6 +1401,7 @@ const (
13721401
FilterModeAssign
13731402
FilterModeCreate
13741403
FilterModeMention
1404+
FilterModeReviewRequested
13751405
)
13761406

13771407
func parseCountResult(results []map[string][]byte) int64 {
@@ -1387,14 +1417,15 @@ func parseCountResult(results []map[string][]byte) int64 {
13871417

13881418
// IssueStatsOptions contains parameters accepted by GetIssueStats.
13891419
type IssueStatsOptions struct {
1390-
RepoID int64
1391-
Labels string
1392-
MilestoneID int64
1393-
AssigneeID int64
1394-
MentionedID int64
1395-
PosterID int64
1396-
IsPull util.OptionalBool
1397-
IssueIDs []int64
1420+
RepoID int64
1421+
Labels string
1422+
MilestoneID int64
1423+
AssigneeID int64
1424+
MentionedID int64
1425+
PosterID int64
1426+
ReviewRequestedID int64
1427+
IsPull util.OptionalBool
1428+
IssueIDs []int64
13981429
}
13991430

14001431
// GetIssueStats returns issue statistic information by given conditions.
@@ -1423,6 +1454,7 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
14231454
accum.AssignCount += stats.AssignCount
14241455
accum.CreateCount += stats.CreateCount
14251456
accum.OpenCount += stats.MentionCount
1457+
accum.ReviewRequestedCount += stats.ReviewRequestedCount
14261458
i = chunk
14271459
}
14281460
return accum, nil
@@ -1460,18 +1492,19 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
14601492
}
14611493

14621494
if opts.AssigneeID > 0 {
1463-
sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
1464-
And("issue_assignees.assignee_id = ?", opts.AssigneeID)
1495+
applyAssigneeCondition(sess, opts.AssigneeID)
14651496
}
14661497

14671498
if opts.PosterID > 0 {
1468-
sess.And("issue.poster_id = ?", opts.PosterID)
1499+
applyPosterCondition(sess, opts.PosterID)
14691500
}
14701501

14711502
if opts.MentionedID > 0 {
1472-
sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id").
1473-
And("issue_user.uid = ?", opts.MentionedID).
1474-
And("issue_user.is_mentioned = ?", true)
1503+
applyMentionedCondition(sess, opts.MentionedID)
1504+
}
1505+
1506+
if opts.ReviewRequestedID > 0 {
1507+
applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
14751508
}
14761509

14771510
switch opts.IsPull {
@@ -1539,90 +1572,94 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
15391572

15401573
switch opts.FilterMode {
15411574
case FilterModeAll:
1542-
stats.OpenCount, err = sess(cond).And("issue.is_closed = ?", false).
1543-
And(builder.In("issue.repo_id", opts.UserRepoIDs)).
1575+
stats.OpenCount, err = applyReposCondition(sess(cond), opts.UserRepoIDs).
1576+
And("issue.is_closed = ?", false).
15441577
Count(new(Issue))
15451578
if err != nil {
15461579
return nil, err
15471580
}
1548-
stats.ClosedCount, err = sess(cond).And("issue.is_closed = ?", true).
1549-
And(builder.In("issue.repo_id", opts.UserRepoIDs)).
1581+
stats.ClosedCount, err = applyReposCondition(sess(cond), opts.UserRepoIDs).
1582+
And("issue.is_closed = ?", true).
15501583
Count(new(Issue))
15511584
if err != nil {
15521585
return nil, err
15531586
}
15541587
case FilterModeAssign:
1555-
stats.OpenCount, err = sess(cond).And("issue.is_closed = ?", false).
1556-
Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
1557-
And("issue_assignees.assignee_id = ?", opts.UserID).
1588+
stats.OpenCount, err = applyAssigneeCondition(sess(cond), opts.UserID).
1589+
And("issue.is_closed = ?", false).
15581590
Count(new(Issue))
15591591
if err != nil {
15601592
return nil, err
15611593
}
1562-
stats.ClosedCount, err = sess(cond).And("issue.is_closed = ?", true).
1563-
Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
1564-
And("issue_assignees.assignee_id = ?", opts.UserID).
1594+
stats.ClosedCount, err = applyAssigneeCondition(sess(cond), opts.UserID).
1595+
And("issue.is_closed = ?", true).
15651596
Count(new(Issue))
15661597
if err != nil {
15671598
return nil, err
15681599
}
15691600
case FilterModeCreate:
1570-
stats.OpenCount, err = sess(cond).And("issue.is_closed = ?", false).
1571-
And("issue.poster_id = ?", opts.UserID).
1601+
stats.OpenCount, err = applyPosterCondition(sess(cond), opts.UserID).
1602+
And("issue.is_closed = ?", false).
15721603
Count(new(Issue))
15731604
if err != nil {
15741605
return nil, err
15751606
}
1576-
stats.ClosedCount, err = sess(cond).And("issue.is_closed = ?", true).
1577-
And("issue.poster_id = ?", opts.UserID).
1607+
stats.ClosedCount, err = applyPosterCondition(sess(cond), opts.UserID).
1608+
And("issue.is_closed = ?", true).
15781609
Count(new(Issue))
15791610
if err != nil {
15801611
return nil, err
15811612
}
15821613
case FilterModeMention:
1583-
stats.OpenCount, err = sess(cond).And("issue.is_closed = ?", false).
1584-
Join("INNER", "issue_user", "issue.id = issue_user.issue_id and issue_user.is_mentioned = ?", true).
1585-
And("issue_user.uid = ?", opts.UserID).
1614+
stats.OpenCount, err = applyMentionedCondition(sess(cond), opts.UserID).
1615+
And("issue.is_closed = ?", false).
15861616
Count(new(Issue))
15871617
if err != nil {
15881618
return nil, err
15891619
}
1590-
stats.ClosedCount, err = sess(cond).And("issue.is_closed = ?", true).
1591-
Join("INNER", "issue_user", "issue.id = issue_user.issue_id and issue_user.is_mentioned = ?", true).
1592-
And("issue_user.uid = ?", opts.UserID).
1620+
stats.ClosedCount, err = applyMentionedCondition(sess(cond), opts.UserID).
1621+
And("issue.is_closed = ?", true).
1622+
Count(new(Issue))
1623+
if err != nil {
1624+
return nil, err
1625+
}
1626+
case FilterModeReviewRequested:
1627+
stats.OpenCount, err = applyReviewRequestedCondition(sess(cond), opts.UserID).
1628+
And("issue.is_closed = ?", false).
1629+
Count(new(Issue))
1630+
if err != nil {
1631+
return nil, err
1632+
}
1633+
stats.ClosedCount, err = applyReviewRequestedCondition(sess(cond), opts.UserID).
1634+
And("issue.is_closed = ?", true).
15931635
Count(new(Issue))
15941636
if err != nil {
15951637
return nil, err
15961638
}
15971639
}
15981640

15991641
cond = cond.And(builder.Eq{"issue.is_closed": opts.IsClosed})
1600-
stats.AssignCount, err = sess(cond).
1601-
Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
1602-
And("issue_assignees.assignee_id = ?", opts.UserID).
1603-
Count(new(Issue))
1642+
stats.AssignCount, err = applyAssigneeCondition(sess(cond), opts.UserID).Count(new(Issue))
16041643
if err != nil {
16051644
return nil, err
16061645
}
16071646

1608-
stats.CreateCount, err = sess(cond).
1609-
And("poster_id = ?", opts.UserID).
1610-
Count(new(Issue))
1647+
stats.CreateCount, err = applyPosterCondition(sess(cond), opts.UserID).Count(new(Issue))
16111648
if err != nil {
16121649
return nil, err
16131650
}
16141651

1615-
stats.MentionCount, err = sess(cond).
1616-
Join("INNER", "issue_user", "issue.id = issue_user.issue_id and issue_user.is_mentioned = ?", true).
1617-
And("issue_user.uid = ?", opts.UserID).
1618-
Count(new(Issue))
1652+
stats.MentionCount, err = applyMentionedCondition(sess(cond), opts.UserID).Count(new(Issue))
16191653
if err != nil {
16201654
return nil, err
16211655
}
16221656

1623-
stats.YourRepositoriesCount, err = sess(cond).
1624-
And(builder.In("issue.repo_id", opts.UserRepoIDs)).
1625-
Count(new(Issue))
1657+
stats.YourRepositoriesCount, err = applyReposCondition(sess(cond), opts.UserRepoIDs).Count(new(Issue))
1658+
if err != nil {
1659+
return nil, err
1660+
}
1661+
1662+
stats.ReviewRequestedCount, err = applyReviewRequestedCondition(sess(cond), opts.UserID).Count(new(Issue))
16261663
if err != nil {
16271664
return nil, err
16281665
}
@@ -1646,13 +1683,11 @@ func GetRepoIssueStats(repoID, uid int64, filterMode int, isPull bool) (numOpen
16461683

16471684
switch filterMode {
16481685
case FilterModeAssign:
1649-
openCountSession.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
1650-
And("issue_assignees.assignee_id = ?", uid)
1651-
closedCountSession.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
1652-
And("issue_assignees.assignee_id = ?", uid)
1686+
applyAssigneeCondition(openCountSession, uid)
1687+
applyAssigneeCondition(closedCountSession, uid)
16531688
case FilterModeCreate:
1654-
openCountSession.And("poster_id = ?", uid)
1655-
closedCountSession.And("poster_id = ?", uid)
1689+
applyPosterCondition(openCountSession, uid)
1690+
applyPosterCondition(closedCountSession, uid)
16561691
}
16571692

16581693
openResult, _ := openCountSession.Count(new(Issue))

options/locale/locale_en-US.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,7 @@ issues.filter_type.all_issues = All issues
10301030
issues.filter_type.assigned_to_you = Assigned to you
10311031
issues.filter_type.created_by_you = Created by you
10321032
issues.filter_type.mentioning_you = Mentioning you
1033+
issues.filter_type.review_requested = Review requested
10331034
issues.filter_sort = Sort
10341035
issues.filter_sort.latest = Newest
10351036
issues.filter_sort.oldest = Oldest

routers/api/v1/repo/issue.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ func SearchIssues(ctx *context.APIContext) {
7979
// in: query
8080
// description: filter (issues / pulls) mentioning you, default is false
8181
// type: boolean
82+
// - name: review_requested
83+
// in: query
84+
// description: filter pulls requesting your review, default is false
85+
// type: boolean
8286
// - name: page
8387
// in: query
8488
// description: page number of results to return (1-based)
@@ -204,7 +208,7 @@ func SearchIssues(ctx *context.APIContext) {
204208
UpdatedAfterUnix: since,
205209
}
206210

207-
// Filter for: Created by User, Assigned to User, Mentioning User
211+
// Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested
208212
if ctx.QueryBool("created") {
209213
issuesOpt.PosterID = ctx.User.ID
210214
}
@@ -214,6 +218,9 @@ func SearchIssues(ctx *context.APIContext) {
214218
if ctx.QueryBool("mentioned") {
215219
issuesOpt.MentionedID = ctx.User.ID
216220
}
221+
if ctx.QueryBool("review_requested") {
222+
issuesOpt.ReviewRequestedID = ctx.User.ID
223+
}
217224

218225
if issues, err = models.Issues(issuesOpt); err != nil {
219226
ctx.Error(http.StatusInternalServerError, "Issues", err)

0 commit comments

Comments
 (0)