]> source.dussan.org Git - gitea.git/commitdiff
[API] Add more filters to issues search (#13514)
author6543 <6543@obermui.de>
Mon, 23 Nov 2020 20:49:36 +0000 (21:49 +0100)
committerGitHub <noreply@github.com>
Mon, 23 Nov 2020 20:49:36 +0000 (20:49 +0000)
* Add time filter for issue search

* Add limit option for paggination

* Add Filter for: Created by User, Assigned to User, Mentioning User

* update swagger

* Add Tests for limit, before & since

integrations/api_issue_test.go
models/issue.go
routers/api/v1/repo/issue.go
templates/swagger/v1_json.tmpl

index 9311d50c5cbf7810d99869464a32a77bc0ba4572..81e5c44873b1b156b465b654ddeca9c0555cd1f1 100644 (file)
@@ -9,6 +9,7 @@ import (
        "net/http"
        "net/url"
        "testing"
+       "time"
 
        "code.gitea.io/gitea/models"
        api "code.gitea.io/gitea/modules/structs"
@@ -152,17 +153,27 @@ func TestAPISearchIssues(t *testing.T) {
        resp := session.MakeRequest(t, req, http.StatusOK)
        var apiIssues []*api.Issue
        DecodeJSON(t, resp, &apiIssues)
-
        assert.Len(t, apiIssues, 10)
 
-       query := url.Values{}
-       query.Add("token", token)
+       query := url.Values{"token": {token}}
        link.RawQuery = query.Encode()
        req = NewRequest(t, "GET", link.String())
        resp = session.MakeRequest(t, req, http.StatusOK)
        DecodeJSON(t, resp, &apiIssues)
        assert.Len(t, apiIssues, 10)
 
+       since := "2000-01-01T00%3A50%3A01%2B00%3A00" // 946687801
+       before := time.Unix(999307200, 0).Format(time.RFC3339)
+       query.Add("since", since)
+       query.Add("before", before)
+       link.RawQuery = query.Encode()
+       req = NewRequest(t, "GET", link.String())
+       resp = session.MakeRequest(t, req, http.StatusOK)
+       DecodeJSON(t, resp, &apiIssues)
+       assert.Len(t, apiIssues, 8)
+       query.Del("since")
+       query.Del("before")
+
        query.Add("state", "closed")
        link.RawQuery = query.Encode()
        req = NewRequest(t, "GET", link.String())
@@ -175,14 +186,22 @@ func TestAPISearchIssues(t *testing.T) {
        req = NewRequest(t, "GET", link.String())
        resp = session.MakeRequest(t, req, http.StatusOK)
        DecodeJSON(t, resp, &apiIssues)
+       assert.EqualValues(t, "12", resp.Header().Get("X-Total-Count"))
        assert.Len(t, apiIssues, 10) //there are more but 10 is page item limit
 
-       query.Add("page", "2")
+       query.Add("limit", "20")
        link.RawQuery = query.Encode()
        req = NewRequest(t, "GET", link.String())
        resp = session.MakeRequest(t, req, http.StatusOK)
        DecodeJSON(t, resp, &apiIssues)
-       assert.Len(t, apiIssues, 2)
+       assert.Len(t, apiIssues, 12)
+
+       query = url.Values{"assigned": {"true"}, "state": {"all"}}
+       link.RawQuery = query.Encode()
+       req = NewRequest(t, "GET", link.String())
+       resp = session.MakeRequest(t, req, http.StatusOK)
+       DecodeJSON(t, resp, &apiIssues)
+       assert.Len(t, apiIssues, 1)
 }
 
 func TestAPISearchIssuesWithLabels(t *testing.T) {
index ee75623f530258158cb352283913a052f75d4698..8c135faa8df3d2e5b0636676c219e82c8f6acea3 100644 (file)
@@ -1100,6 +1100,8 @@ type IssuesOptions struct {
        ExcludedLabelNames []string
        SortType           string
        IssueIDs           []int64
+       UpdatedAfterUnix   int64
+       UpdatedBeforeUnix  int64
        // prioritize issues from this repo
        PriorityRepoID int64
 }
@@ -1178,6 +1180,13 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
                sess.In("issue.milestone_id", opts.MilestoneIDs)
        }
 
+       if opts.UpdatedAfterUnix != 0 {
+               sess.And(builder.Gte{"issue.updated_unix": opts.UpdatedAfterUnix})
+       }
+       if opts.UpdatedBeforeUnix != 0 {
+               sess.And(builder.Lte{"issue.updated_unix": opts.UpdatedBeforeUnix})
+       }
+
        if opts.ProjectID > 0 {
                sess.Join("INNER", "project_issue", "issue.id = project_issue.issue_id").
                        And("project_issue.project_id=?", opts.ProjectID)
index 0dbf2741ad4bd4d0223a006c476e4e4db1e07a02..c58e0bb6ce51f10cc53216fa7d28eff7757a6e26 100644 (file)
@@ -55,14 +55,48 @@ func SearchIssues(ctx *context.APIContext) {
        //   in: query
        //   description: filter by type (issues / pulls) if set
        //   type: string
+       // - name: since
+       //   in: query
+       //   description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format
+       //   type: string
+       //   format: date-time
+       //   required: false
+       // - name: before
+       //   in: query
+       //   description: Only show notifications updated before the given time. This is a timestamp in RFC 3339 format
+       //   type: string
+       //   format: date-time
+       //   required: false
+       // - name: assigned
+       //   in: query
+       //   description: filter (issues / pulls) assigned to you, default is false
+       //   type: boolean
+       // - name: created
+       //   in: query
+       //   description: filter (issues / pulls) created by you, default is false
+       //   type: boolean
+       // - name: mentioned
+       //   in: query
+       //   description: filter (issues / pulls) mentioning you, default is false
+       //   type: boolean
        // - name: page
        //   in: query
-       //   description: page number of requested issues
+       //   description: page number of results to return (1-based)
+       //   type: integer
+       // - name: limit
+       //   in: query
+       //   description: page size of results
        //   type: integer
        // responses:
        //   "200":
        //     "$ref": "#/responses/IssueList"
 
+       before, since, err := utils.GetQueryBeforeSince(ctx)
+       if err != nil {
+               ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
+               return
+       }
+
        var isClosed util.OptionalBool
        switch ctx.Query("state") {
        case "closed":
@@ -119,7 +153,6 @@ func SearchIssues(ctx *context.APIContext) {
        }
        var issueIDs []int64
        var labelIDs []int64
-       var err error
        if len(keyword) > 0 && len(repoIDs) > 0 {
                if issueIDs, err = issue_indexer.SearchIssuesByKeyword(repoIDs, keyword); err != nil {
                        ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err)
@@ -143,13 +176,22 @@ func SearchIssues(ctx *context.APIContext) {
                includedLabelNames = strings.Split(labels, ",")
        }
 
+       // this api is also used in UI,
+       // so the default limit is set to fit UI needs
+       limit := ctx.QueryInt("limit")
+       if limit == 0 {
+               limit = setting.UI.IssuePagingNum
+       } else if limit > setting.API.MaxResponseItems {
+               limit = setting.API.MaxResponseItems
+       }
+
        // Only fetch the issues if we either don't have a keyword or the search returned issues
        // This would otherwise return all issues if no issues were found by the search.
        if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
                issuesOpt := &models.IssuesOptions{
                        ListOptions: models.ListOptions{
                                Page:     ctx.QueryInt("page"),
-                               PageSize: setting.UI.IssuePagingNum,
+                               PageSize: limit,
                        },
                        RepoIDs:            repoIDs,
                        IsClosed:           isClosed,
@@ -158,6 +200,19 @@ func SearchIssues(ctx *context.APIContext) {
                        SortType:           "priorityrepo",
                        PriorityRepoID:     ctx.QueryInt64("priority_repo_id"),
                        IsPull:             isPull,
+                       UpdatedBeforeUnix:  before,
+                       UpdatedAfterUnix:   since,
+               }
+
+               // Filter for: Created by User, Assigned to User, Mentioning User
+               if ctx.QueryBool("created") {
+                       issuesOpt.PosterID = ctx.User.ID
+               }
+               if ctx.QueryBool("assigned") {
+                       issuesOpt.AssigneeID = ctx.User.ID
+               }
+               if ctx.QueryBool("mentioned") {
+                       issuesOpt.MentionedID = ctx.User.ID
                }
 
                if issues, err = models.Issues(issuesOpt); err != nil {
index 9d775da7d6b8c5f7b883fb8b0f731abad132969d..8bcfc43d73f75d6f9f4f7d6c221dd63b74fe2a26 100644 (file)
             "name": "type",
             "in": "query"
           },
+          {
+            "type": "string",
+            "format": "date-time",
+            "description": "Only show notifications updated after the given time. This is a timestamp in RFC 3339 format",
+            "name": "since",
+            "in": "query"
+          },
+          {
+            "type": "string",
+            "format": "date-time",
+            "description": "Only show notifications updated before the given time. This is a timestamp in RFC 3339 format",
+            "name": "before",
+            "in": "query"
+          },
+          {
+            "type": "boolean",
+            "description": "filter (issues / pulls) assigned to you, default is false",
+            "name": "assigned",
+            "in": "query"
+          },
+          {
+            "type": "boolean",
+            "description": "filter (issues / pulls) created by you, default is false",
+            "name": "created",
+            "in": "query"
+          },
+          {
+            "type": "boolean",
+            "description": "filter (issues / pulls) mentioning you, default is false",
+            "name": "mentioned",
+            "in": "query"
+          },
           {
             "type": "integer",
-            "description": "page number of requested issues",
+            "description": "page number of results to return (1-based)",
             "name": "page",
             "in": "query"
+          },
+          {
+            "type": "integer",
+            "description": "page size of results",
+            "name": "limit",
+            "in": "query"
           }
         ],
         "responses": {