diff options
Diffstat (limited to 'routers/api')
-rw-r--r-- | routers/api/v1/api.go | 2 | ||||
-rw-r--r-- | routers/api/v1/repo/issue.go | 134 |
2 files changed, 135 insertions, 1 deletions
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index f8ab9025b7..1acd849b8d 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -596,6 +596,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/search", repo.Search) }) + m.Get("/repos/issues/search", repo.SearchIssues) + m.Combo("/repositories/:id", reqToken()).Get(repo.GetByID) m.Group("/repos", func() { diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 426826653c..fe5862ea5e 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -22,6 +23,137 @@ import ( milestone_service "code.gitea.io/gitea/services/milestone" ) +// SearchIssues searches for issues across the repositories that the user has access to +func SearchIssues(ctx *context.APIContext) { + // swagger:operation GET /repos/issues/search issue issueSearchIssues + // --- + // summary: Search for issues across the repositories that the user has access to + // produces: + // - application/json + // parameters: + // - name: state + // in: query + // description: whether issue is open or closed + // type: string + // - name: labels + // in: query + // description: comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded + // type: string + // - name: page + // in: query + // description: page number of requested issues + // type: integer + // - name: q + // in: query + // description: search string + // type: string + // - name: priority_repo_id + // in: query + // description: repository to prioritize in the results + // type: integer + // format: int64 + // responses: + // "200": + // "$ref": "#/responses/IssueList" + var isClosed util.OptionalBool + switch ctx.Query("state") { + case "closed": + isClosed = util.OptionalBoolTrue + case "all": + isClosed = util.OptionalBoolNone + default: + isClosed = util.OptionalBoolFalse + } + + // find repos user can access (for issue search) + repoIDs := make([]int64, 0) + issueCount := 0 + for page := 1; ; page++ { + repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{ + Page: page, + PageSize: 15, + Private: true, + Keyword: "", + OwnerID: ctx.User.ID, + TopicOnly: false, + Collaborate: util.OptionalBoolNone, + UserIsAdmin: ctx.IsUserSiteAdmin(), + UserID: ctx.User.ID, + OrderBy: models.SearchOrderByRecentUpdated, + }) + if err != nil { + ctx.Error(500, "SearchRepositoryByName", err) + return + } + + if len(repos) == 0 { + break + } + log.Trace("Processing next %d repos of %d", len(repos), count) + for _, repo := range repos { + switch isClosed { + case util.OptionalBoolTrue: + issueCount += repo.NumClosedIssues + case util.OptionalBoolFalse: + issueCount += repo.NumOpenIssues + case util.OptionalBoolNone: + issueCount += repo.NumIssues + } + repoIDs = append(repoIDs, repo.ID) + } + } + + var issues []*models.Issue + + keyword := strings.Trim(ctx.Query("q"), " ") + if strings.IndexByte(keyword, 0) >= 0 { + keyword = "" + } + var issueIDs []int64 + var labelIDs []int64 + var err error + if len(keyword) > 0 && len(repoIDs) > 0 { + issueIDs, err = issue_indexer.SearchIssuesByKeyword(repoIDs, keyword) + } + + labels := ctx.Query("labels") + if splitted := strings.Split(labels, ","); labels != "" && len(splitted) > 0 { + labelIDs, err = models.GetLabelIDsInReposByNames(repoIDs, splitted) + if err != nil { + ctx.Error(500, "GetLabelIDsInRepoByNames", err) + return + } + } + + // 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 { + issues, err = models.Issues(&models.IssuesOptions{ + RepoIDs: repoIDs, + Page: ctx.QueryInt("page"), + PageSize: setting.UI.IssuePagingNum, + IsClosed: isClosed, + IssueIDs: issueIDs, + LabelIDs: labelIDs, + SortType: "priorityrepo", + PriorityRepoID: ctx.QueryInt64("priority_repo_id"), + }) + } + + if err != nil { + ctx.Error(500, "Issues", err) + return + } + + apiIssues := make([]*api.Issue, len(issues)) + for i := range issues { + apiIssues[i] = issues[i].APIFormat() + } + + ctx.SetLinkHeader(issueCount, setting.UI.IssuePagingNum) + ctx.JSON(200, &apiIssues) +} + // ListIssues list the issues of a repository func ListIssues(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/issues issue issueListIssues @@ -79,7 +211,7 @@ func ListIssues(ctx *context.APIContext) { var labelIDs []int64 var err error if len(keyword) > 0 { - issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx.Repo.Repository.ID, keyword) + issueIDs, err = issue_indexer.SearchIssuesByKeyword([]int64{ctx.Repo.Repository.ID}, keyword) } if splitted := strings.Split(ctx.Query("labels"), ","); len(splitted) > 0 { |