Skip to content

Commit 0e081ff

Browse files
6543adelowotechknowlogick
authored
[API] ListIssues add more filters (#16174)
* [API] ListIssues add more filters: optional filter repo issues by: - since - before - created_by - assigned_by - mentioned_by * Add Tests * Update routers/api/v1/repo/issue.go Co-authored-by: Lanre Adelowo <adelowomailbox@gmail.com> * Apply suggestions from code review Co-authored-by: Lanre Adelowo <adelowomailbox@gmail.com> Co-authored-by: techknowlogick <techknowlogick@gitea.io>
1 parent ffbf35b commit 0e081ff

File tree

4 files changed

+134
-15
lines changed

4 files changed

+134
-15
lines changed

Diff for: integrations/api_issue_test.go

+26-6
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ func TestAPIListIssues(t *testing.T) {
2525

2626
session := loginUser(t, owner.Name)
2727
token := getTokenForLoggedInUser(t, session)
28-
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues?state=all&token=%s",
29-
owner.Name, repo.Name, token)
30-
resp := session.MakeRequest(t, req, http.StatusOK)
28+
link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name))
29+
30+
link.RawQuery = url.Values{"token": {token}, "state": {"all"}}.Encode()
31+
resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
3132
var apiIssues []*api.Issue
3233
DecodeJSON(t, resp, &apiIssues)
3334
assert.Len(t, apiIssues, models.GetCount(t, &models.Issue{RepoID: repo.ID}))
@@ -36,15 +37,34 @@ func TestAPIListIssues(t *testing.T) {
3637
}
3738

3839
// test milestone filter
39-
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues?state=all&type=all&milestones=ignore,milestone1,3,4&token=%s",
40-
owner.Name, repo.Name, token)
41-
resp = session.MakeRequest(t, req, http.StatusOK)
40+
link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "type": {"all"}, "milestones": {"ignore,milestone1,3,4"}}.Encode()
41+
resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
4242
DecodeJSON(t, resp, &apiIssues)
4343
if assert.Len(t, apiIssues, 2) {
4444
assert.EqualValues(t, 3, apiIssues[0].Milestone.ID)
4545
assert.EqualValues(t, 1, apiIssues[1].Milestone.ID)
4646
}
4747

48+
link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "created_by": {"user2"}}.Encode()
49+
resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
50+
DecodeJSON(t, resp, &apiIssues)
51+
if assert.Len(t, apiIssues, 1) {
52+
assert.EqualValues(t, 5, apiIssues[0].ID)
53+
}
54+
55+
link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "assigned_by": {"user1"}}.Encode()
56+
resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
57+
DecodeJSON(t, resp, &apiIssues)
58+
if assert.Len(t, apiIssues, 1) {
59+
assert.EqualValues(t, 1, apiIssues[0].ID)
60+
}
61+
62+
link.RawQuery = url.Values{"token": {token}, "state": {"all"}, "mentioned_by": {"user4"}}.Encode()
63+
resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
64+
DecodeJSON(t, resp, &apiIssues)
65+
if assert.Len(t, apiIssues, 1) {
66+
assert.EqualValues(t, 1, apiIssues[0].ID)
67+
}
4868
}
4969

5070
func TestAPICreateIssue(t *testing.T) {

Diff for: models/fixtures/issue_user.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
uid: 4
1818
issue_id: 1
1919
is_read: false
20-
is_mentioned: false
20+
is_mentioned: true

Diff for: routers/api/v1/repo/issue.go

+75-8
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,30 @@ func ListIssues(ctx *context.APIContext) {
266266
// in: query
267267
// description: comma separated list of milestone names or ids. It uses names and fall back to ids. Fetch only issues that have any of this milestones. Non existent milestones are discarded
268268
// type: string
269+
// - name: since
270+
// in: query
271+
// description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format
272+
// type: string
273+
// format: date-time
274+
// required: false
275+
// - name: before
276+
// in: query
277+
// description: Only show notifications updated before the given time. This is a timestamp in RFC 3339 format
278+
// type: string
279+
// format: date-time
280+
// required: false
281+
// - name: created_by
282+
// in: query
283+
// description: filter (issues / pulls) created to
284+
// type: string
285+
// - name: assigned_by
286+
// in: query
287+
// description: filter (issues / pulls) assigned to
288+
// type: string
289+
// - name: mentioned_by
290+
// in: query
291+
// description: filter (issues / pulls) mentioning to
292+
// type: string
269293
// - name: page
270294
// in: query
271295
// description: page number of results to return (1-based)
@@ -277,6 +301,11 @@ func ListIssues(ctx *context.APIContext) {
277301
// responses:
278302
// "200":
279303
// "$ref": "#/responses/IssueList"
304+
before, since, err := utils.GetQueryBeforeSince(ctx)
305+
if err != nil {
306+
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
307+
return
308+
}
280309

281310
var isClosed util.OptionalBool
282311
switch ctx.Query("state") {
@@ -297,7 +326,6 @@ func ListIssues(ctx *context.APIContext) {
297326
}
298327
var issueIDs []int64
299328
var labelIDs []int64
300-
var err error
301329
if len(keyword) > 0 {
302330
issueIDs, err = issue_indexer.SearchIssuesByKeyword([]int64{ctx.Repo.Repository.ID}, keyword)
303331
if err != nil {
@@ -356,17 +384,36 @@ func ListIssues(ctx *context.APIContext) {
356384
isPull = util.OptionalBoolNone
357385
}
358386

387+
// FIXME: we should be more efficient here
388+
createdByID := getUserIDForFilter(ctx, "created_by")
389+
if ctx.Written() {
390+
return
391+
}
392+
assignedByID := getUserIDForFilter(ctx, "assigned_by")
393+
if ctx.Written() {
394+
return
395+
}
396+
mentionedByID := getUserIDForFilter(ctx, "mentioned_by")
397+
if ctx.Written() {
398+
return
399+
}
400+
359401
// Only fetch the issues if we either don't have a keyword or the search returned issues
360402
// This would otherwise return all issues if no issues were found by the search.
361403
if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
362404
issuesOpt := &models.IssuesOptions{
363-
ListOptions: listOptions,
364-
RepoIDs: []int64{ctx.Repo.Repository.ID},
365-
IsClosed: isClosed,
366-
IssueIDs: issueIDs,
367-
LabelIDs: labelIDs,
368-
MilestoneIDs: mileIDs,
369-
IsPull: isPull,
405+
ListOptions: listOptions,
406+
RepoIDs: []int64{ctx.Repo.Repository.ID},
407+
IsClosed: isClosed,
408+
IssueIDs: issueIDs,
409+
LabelIDs: labelIDs,
410+
MilestoneIDs: mileIDs,
411+
IsPull: isPull,
412+
UpdatedBeforeUnix: before,
413+
UpdatedAfterUnix: since,
414+
PosterID: createdByID,
415+
AssigneeID: assignedByID,
416+
MentionedID: mentionedByID,
370417
}
371418

372419
if issues, err = models.Issues(issuesOpt); err != nil {
@@ -389,6 +436,26 @@ func ListIssues(ctx *context.APIContext) {
389436
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues))
390437
}
391438

439+
func getUserIDForFilter(ctx *context.APIContext, queryName string) int64 {
440+
userName := ctx.Query(queryName)
441+
if len(userName) == 0 {
442+
return 0
443+
}
444+
445+
user, err := models.GetUserByName(userName)
446+
if models.IsErrUserNotExist(err) {
447+
ctx.NotFound(err)
448+
return 0
449+
}
450+
451+
if err != nil {
452+
ctx.InternalServerError(err)
453+
return 0
454+
}
455+
456+
return user.ID
457+
}
458+
392459
// GetIssue get an issue of a repository
393460
func GetIssue(ctx *context.APIContext) {
394461
// swagger:operation GET /repos/{owner}/{repo}/issues/{index} issue issueGetIssue

Diff for: templates/swagger/v1_json.tmpl

+32
Original file line numberDiff line numberDiff line change
@@ -4234,6 +4234,38 @@
42344234
"name": "milestones",
42354235
"in": "query"
42364236
},
4237+
{
4238+
"type": "string",
4239+
"format": "date-time",
4240+
"description": "Only show notifications updated after the given time. This is a timestamp in RFC 3339 format",
4241+
"name": "since",
4242+
"in": "query"
4243+
},
4244+
{
4245+
"type": "string",
4246+
"format": "date-time",
4247+
"description": "Only show notifications updated before the given time. This is a timestamp in RFC 3339 format",
4248+
"name": "before",
4249+
"in": "query"
4250+
},
4251+
{
4252+
"type": "string",
4253+
"description": "filter (issues / pulls) created to",
4254+
"name": "created_by",
4255+
"in": "query"
4256+
},
4257+
{
4258+
"type": "string",
4259+
"description": "filter (issues / pulls) assigned to",
4260+
"name": "assigned_by",
4261+
"in": "query"
4262+
},
4263+
{
4264+
"type": "string",
4265+
"description": "filter (issues / pulls) mentioning to",
4266+
"name": "mentioned_by",
4267+
"in": "query"
4268+
},
42374269
{
42384270
"type": "integer",
42394271
"description": "page number of results to return (1-based)",

0 commit comments

Comments
 (0)