aboutsummaryrefslogtreecommitdiffstats
path: root/routers/api/v1/repo/issue.go
diff options
context:
space:
mode:
Diffstat (limited to 'routers/api/v1/repo/issue.go')
-rw-r--r--routers/api/v1/repo/issue.go324
1 files changed, 175 insertions, 149 deletions
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 8a61fc9834..861e63a9b8 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -132,74 +132,73 @@ func SearchIssues(ctx *context.APIContext) {
isClosed = util.OptionalBoolFalse
}
- // find repos user can access (for issue search)
- opts := &repo_model.SearchRepoOptions{
- Private: false,
- AllPublic: true,
- TopicOnly: false,
- Collaborate: util.OptionalBoolNone,
- // This needs to be a column that is not nil in fixtures or
- // MySQL will return different results when sorting by null in some cases
- OrderBy: db.SearchOrderByAlphabetically,
- Actor: ctx.Doer,
- }
- if ctx.IsSigned {
- opts.Private = true
- opts.AllLimited = true
- }
- if ctx.FormString("owner") != "" {
- owner, err := user_model.GetUserByName(ctx, ctx.FormString("owner"))
- if err != nil {
- if user_model.IsErrUserNotExist(err) {
- ctx.Error(http.StatusBadRequest, "Owner not found", err)
- } else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ var (
+ repoIDs []int64
+ allPublic bool
+ )
+ {
+ // find repos user can access (for issue search)
+ opts := &repo_model.SearchRepoOptions{
+ Private: false,
+ AllPublic: true,
+ TopicOnly: false,
+ Collaborate: util.OptionalBoolNone,
+ // This needs to be a column that is not nil in fixtures or
+ // MySQL will return different results when sorting by null in some cases
+ OrderBy: db.SearchOrderByAlphabetically,
+ Actor: ctx.Doer,
+ }
+ if ctx.IsSigned {
+ opts.Private = true
+ opts.AllLimited = true
+ }
+ if ctx.FormString("owner") != "" {
+ owner, err := user_model.GetUserByName(ctx, ctx.FormString("owner"))
+ if err != nil {
+ if user_model.IsErrUserNotExist(err) {
+ ctx.Error(http.StatusBadRequest, "Owner not found", err)
+ } else {
+ ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ }
+ return
}
- return
+ opts.OwnerID = owner.ID
+ opts.AllLimited = false
+ opts.AllPublic = false
+ opts.Collaborate = util.OptionalBoolFalse
+ }
+ if ctx.FormString("team") != "" {
+ if ctx.FormString("owner") == "" {
+ ctx.Error(http.StatusBadRequest, "", "Owner organisation is required for filtering on team")
+ return
+ }
+ team, err := organization.GetTeam(ctx, opts.OwnerID, ctx.FormString("team"))
+ if err != nil {
+ if organization.IsErrTeamNotExist(err) {
+ ctx.Error(http.StatusBadRequest, "Team not found", err)
+ } else {
+ ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ }
+ return
+ }
+ opts.TeamID = team.ID
}
- opts.OwnerID = owner.ID
- opts.AllLimited = false
- opts.AllPublic = false
- opts.Collaborate = util.OptionalBoolFalse
- }
- if ctx.FormString("team") != "" {
- if ctx.FormString("owner") == "" {
- ctx.Error(http.StatusBadRequest, "", "Owner organisation is required for filtering on team")
- return
+
+ if opts.AllPublic {
+ allPublic = true
+ opts.AllPublic = false // set it false to avoid returning too many repos, we could filter by indexer
}
- team, err := organization.GetTeam(ctx, opts.OwnerID, ctx.FormString("team"))
+ repoIDs, _, err = repo_model.SearchRepositoryIDs(opts)
if err != nil {
- if organization.IsErrTeamNotExist(err) {
- ctx.Error(http.StatusBadRequest, "Team not found", err)
- } else {
- ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
- }
+ ctx.Error(http.StatusInternalServerError, "SearchRepositoryIDs", err)
return
}
- opts.TeamID = team.ID
}
- repoCond := repo_model.SearchRepositoryCondition(opts)
- repoIDs, _, err := repo_model.SearchRepositoryIDs(opts)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchRepositoryIDs", err)
- return
- }
-
- var issues []*issues_model.Issue
- var filteredCount int64
-
keyword := ctx.FormTrim("q")
if strings.IndexByte(keyword, 0) >= 0 {
keyword = ""
}
- var issueIDs []int64
- if len(keyword) > 0 && len(repoIDs) > 0 {
- if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword, ctx.FormString("state")); err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err)
- return
- }
- }
var isPull util.OptionalBool
switch ctx.FormString("type") {
@@ -211,16 +210,33 @@ func SearchIssues(ctx *context.APIContext) {
isPull = util.OptionalBoolNone
}
- labels := ctx.FormTrim("labels")
- var includedLabelNames []string
- if len(labels) > 0 {
- includedLabelNames = strings.Split(labels, ",")
+ var includedAnyLabels []int64
+ {
+
+ labels := ctx.FormTrim("labels")
+ var includedLabelNames []string
+ if len(labels) > 0 {
+ includedLabelNames = strings.Split(labels, ",")
+ }
+ includedAnyLabels, err = issues_model.GetLabelIDsByNames(ctx, includedLabelNames)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "GetLabelIDsByNames", err)
+ return
+ }
}
- milestones := ctx.FormTrim("milestones")
- var includedMilestones []string
- if len(milestones) > 0 {
- includedMilestones = strings.Split(milestones, ",")
+ var includedMilestones []int64
+ {
+ milestones := ctx.FormTrim("milestones")
+ var includedMilestoneNames []string
+ if len(milestones) > 0 {
+ includedMilestoneNames = strings.Split(milestones, ",")
+ }
+ includedMilestones, err = issues_model.GetMilestoneIDsByNames(ctx, includedMilestoneNames)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "GetMilestoneIDsByNames", err)
+ return
+ }
}
// this api is also used in UI,
@@ -232,64 +248,64 @@ func SearchIssues(ctx *context.APIContext) {
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(includedLabelNames) > 0 || len(includedMilestones) > 0 {
- issuesOpt := &issues_model.IssuesOptions{
- ListOptions: db.ListOptions{
- Page: ctx.FormInt("page"),
- PageSize: limit,
- },
- RepoCond: repoCond,
- IsClosed: isClosed,
- IssueIDs: issueIDs,
- IncludedLabelNames: includedLabelNames,
- IncludeMilestones: includedMilestones,
- SortType: "priorityrepo",
- PriorityRepoID: ctx.FormInt64("priority_repo_id"),
- IsPull: isPull,
- UpdatedBeforeUnix: before,
- UpdatedAfterUnix: since,
- }
-
- ctxUserID := int64(0)
- if ctx.IsSigned {
- ctxUserID = ctx.Doer.ID
- }
+ searchOpt := &issue_indexer.SearchOptions{
+ Paginator: &db.ListOptions{
+ PageSize: limit,
+ Page: ctx.FormInt("page"),
+ },
+ Keyword: keyword,
+ RepoIDs: repoIDs,
+ AllPublic: allPublic,
+ IsPull: isPull,
+ IsClosed: isClosed,
+ IncludedAnyLabelIDs: includedAnyLabels,
+ MilestoneIDs: includedMilestones,
+ SortBy: issue_indexer.SortByCreatedDesc,
+ }
- // Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested
+ if since != 0 {
+ searchOpt.UpdatedAfterUnix = &since
+ }
+ if before != 0 {
+ searchOpt.UpdatedBeforeUnix = &before
+ }
+
+ if ctx.IsSigned {
+ ctxUserID := ctx.Doer.ID
if ctx.FormBool("created") {
- issuesOpt.PosterID = ctxUserID
+ searchOpt.PosterID = &ctxUserID
}
if ctx.FormBool("assigned") {
- issuesOpt.AssigneeID = ctxUserID
+ searchOpt.AssigneeID = &ctxUserID
}
if ctx.FormBool("mentioned") {
- issuesOpt.MentionedID = ctxUserID
+ searchOpt.MentionID = &ctxUserID
}
if ctx.FormBool("review_requested") {
- issuesOpt.ReviewRequestedID = ctxUserID
+ searchOpt.ReviewRequestedID = &ctxUserID
}
if ctx.FormBool("reviewed") {
- issuesOpt.ReviewedID = ctxUserID
+ searchOpt.ReviewedID = &ctxUserID
}
+ }
- if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil {
- ctx.Error(http.StatusInternalServerError, "Issues", err)
- return
- }
+ // FIXME: It's unsupported to sort by priority repo when searching by indexer,
+ // it's indeed an regression, but I think it is worth to support filtering by indexer first.
+ _ = ctx.FormInt64("priority_repo_id")
- issuesOpt.ListOptions = db.ListOptions{
- Page: -1,
- }
- if filteredCount, err = issues_model.CountIssues(ctx, issuesOpt); err != nil {
- ctx.Error(http.StatusInternalServerError, "CountIssues", err)
- return
- }
+ ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "SearchIssues", err)
+ return
+ }
+ issues, err := issues_model.GetIssuesByIDs(ctx, ids, true)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "FindIssuesByIDs", err)
+ return
}
- ctx.SetLinkHeader(int(filteredCount), limit)
- ctx.SetTotalCountHeader(filteredCount)
+ ctx.SetLinkHeader(int(total), limit)
+ ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
}
@@ -384,23 +400,12 @@ func ListIssues(ctx *context.APIContext) {
isClosed = util.OptionalBoolFalse
}
- var issues []*issues_model.Issue
- var filteredCount int64
-
keyword := ctx.FormTrim("q")
if strings.IndexByte(keyword, 0) >= 0 {
keyword = ""
}
- var issueIDs []int64
- var labelIDs []int64
- if len(keyword) > 0 {
- issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword, ctx.FormString("state"))
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err)
- return
- }
- }
+ var labelIDs []int64
if splitted := strings.Split(ctx.FormString("labels"), ","); len(splitted) > 0 {
labelIDs, err = issues_model.GetLabelIDsInRepoByNames(ctx.Repo.Repository.ID, splitted)
if err != nil {
@@ -465,40 +470,61 @@ func ListIssues(ctx *context.APIContext) {
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 {
- issuesOpt := &issues_model.IssuesOptions{
- ListOptions: listOptions,
- RepoIDs: []int64{ctx.Repo.Repository.ID},
- IsClosed: isClosed,
- IssueIDs: issueIDs,
- LabelIDs: labelIDs,
- MilestoneIDs: mileIDs,
- IsPull: isPull,
- UpdatedBeforeUnix: before,
- UpdatedAfterUnix: since,
- PosterID: createdByID,
- AssigneeID: assignedByID,
- MentionedID: mentionedByID,
- }
-
- if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil {
- ctx.Error(http.StatusInternalServerError, "Issues", err)
- return
+ searchOpt := &issue_indexer.SearchOptions{
+ Paginator: &listOptions,
+ Keyword: keyword,
+ RepoIDs: []int64{ctx.Repo.Repository.ID},
+ IsPull: isPull,
+ IsClosed: isClosed,
+ SortBy: issue_indexer.SortByCreatedDesc,
+ }
+ if since != 0 {
+ searchOpt.UpdatedAfterUnix = &since
+ }
+ if before != 0 {
+ searchOpt.UpdatedBeforeUnix = &before
+ }
+ if len(labelIDs) == 1 && labelIDs[0] == 0 {
+ searchOpt.NoLabelOnly = true
+ } else {
+ for _, labelID := range labelIDs {
+ if labelID > 0 {
+ searchOpt.IncludedLabelIDs = append(searchOpt.IncludedLabelIDs, labelID)
+ } else {
+ searchOpt.ExcludedLabelIDs = append(searchOpt.ExcludedLabelIDs, -labelID)
+ }
}
+ }
- issuesOpt.ListOptions = db.ListOptions{
- Page: -1,
- }
- if filteredCount, err = issues_model.CountIssues(ctx, issuesOpt); err != nil {
- ctx.Error(http.StatusInternalServerError, "CountIssues", err)
- return
- }
+ if len(mileIDs) == 1 && mileIDs[0] == db.NoConditionID {
+ searchOpt.MilestoneIDs = []int64{0}
+ } else {
+ searchOpt.MilestoneIDs = mileIDs
+ }
+
+ if createdByID > 0 {
+ searchOpt.PosterID = &createdByID
+ }
+ if assignedByID > 0 {
+ searchOpt.AssigneeID = &assignedByID
+ }
+ if mentionedByID > 0 {
+ searchOpt.MentionID = &mentionedByID
+ }
+
+ ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "SearchIssues", err)
+ return
+ }
+ issues, err := issues_model.GetIssuesByIDs(ctx, ids, true)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "FindIssuesByIDs", err)
+ return
}
- ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize)
- ctx.SetTotalCountHeader(filteredCount)
+ ctx.SetLinkHeader(int(total), listOptions.PageSize)
+ ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
}