From 6fa19a8458e993972be2355e0575a76823a75735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=B2=E3=81=AE=E3=81=97=E3=81=B0=20/=20hinoshiba?= Date: Fri, 25 Sep 2020 08:30:40 +0900 Subject: [PATCH] Fixed count of filtered issues when api request. (#12275) * Improved total count of issue when filtered. * Fixed size of slice when selected 1 repository. * Improved function of error check. * improved comment * Added parameter of return header. Co-authored-by: 6543 <6543@obermui.de> * Updated corresponded to the current vendored of "xorm.io/xorm". * Dedublicated it by store the Options Struct into a variable. * format code * Update routers/api/v1/repo/issue.go Co-authored-by: 6543 <6543@obermui.de> * Update routers/api/v1/repo/issue.go Co-authored-by: 6543 <6543@obermui.de> * Updated number of range. Co-authored-by: 6543 <6543@obermui.de> * Updated number of range. Co-authored-by: 6543 <6543@obermui.de> * Removed total value. * make fmt * Improved value of sql. Co-authored-by: zeripath * Improved value of sql. * improved message * improved message * improved message * fixed message Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath Co-authored-by: techknowlogick --- models/issue.go | 23 ++++++++++- routers/api/v1/repo/issue.go | 74 +++++++++++++++++++++--------------- 2 files changed, 66 insertions(+), 31 deletions(-) diff --git a/models/issue.go b/models/issue.go index ede318095..82bd21455 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1266,7 +1266,7 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) { opts.setupSession(sess) sortIssuesSession(sess, opts.SortType, opts.PriorityRepoID) - issues := make([]*Issue, 0, setting.UI.IssuePagingNum) + issues := make([]*Issue, 0, opts.ListOptions.PageSize) if err := sess.Find(&issues); err != nil { return nil, fmt.Errorf("Find: %v", err) } @@ -1279,6 +1279,27 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) { return issues, nil } +// CountIssues number return of issues by given conditions. +func CountIssues(opts *IssuesOptions) (int64, error) { + sess := x.NewSession() + defer sess.Close() + + countsSlice := make([]*struct { + RepoID int64 + Count int64 + }, 0, 1) + + sess.Select("COUNT(issue.id) AS count").Table("issue") + opts.setupSession(sess) + if err := sess.Find(&countsSlice); err != nil { + return 0, fmt.Errorf("Find: %v", err) + } + if len(countsSlice) < 1 { + return 0, fmt.Errorf("there is less than one result sql record") + } + return countsSlice[0].Count, nil +} + // GetParticipantsIDsByIssueID returns the IDs of all users who participated in comments of an issue, // but skips joining with `user` for performance reasons. // User permissions must be verified elsewhere if required. diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 1653b8594..0dbf2741a 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -93,7 +93,6 @@ func SearchIssues(ctx *context.APIContext) { opts.AllLimited = true } - issueCount := 0 for page := 1; ; page++ { opts.Page = page repos, count, err := models.SearchRepositoryByName(opts) @@ -107,19 +106,12 @@ func SearchIssues(ctx *context.APIContext) { } 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 + var filteredCount int64 keyword := strings.Trim(ctx.Query("q"), " ") if strings.IndexByte(keyword, 0) >= 0 { @@ -129,7 +121,10 @@ func SearchIssues(ctx *context.APIContext) { var labelIDs []int64 var err error if len(keyword) > 0 && len(repoIDs) > 0 { - issueIDs, err = issue_indexer.SearchIssuesByKeyword(repoIDs, keyword) + if issueIDs, err = issue_indexer.SearchIssuesByKeyword(repoIDs, keyword); err != nil { + ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err) + return + } } var isPull util.OptionalBool @@ -151,12 +146,11 @@ func SearchIssues(ctx *context.APIContext) { // 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{ + issuesOpt := &models.IssuesOptions{ ListOptions: models.ListOptions{ Page: ctx.QueryInt("page"), PageSize: setting.UI.IssuePagingNum, }, - RepoIDs: repoIDs, IsClosed: isClosed, IssueIDs: issueIDs, @@ -164,16 +158,24 @@ func SearchIssues(ctx *context.APIContext) { SortType: "priorityrepo", PriorityRepoID: ctx.QueryInt64("priority_repo_id"), IsPull: isPull, - }) + } + + if issues, err = models.Issues(issuesOpt); err != nil { + ctx.Error(http.StatusInternalServerError, "Issues", err) + return + } + + issuesOpt.ListOptions = models.ListOptions{ + Page: -1, + } + if filteredCount, err = models.CountIssues(issuesOpt); err != nil { + ctx.Error(http.StatusInternalServerError, "CountIssues", err) + return + } } - if err != nil { - ctx.Error(http.StatusInternalServerError, "Issues", err) - return - } - - ctx.SetLinkHeader(issueCount, setting.UI.IssuePagingNum) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", issueCount)) + ctx.SetLinkHeader(int(filteredCount), setting.UI.IssuePagingNum) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", filteredCount)) ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues)) } @@ -241,6 +243,7 @@ func ListIssues(ctx *context.APIContext) { } var issues []*models.Issue + var filteredCount int64 keyword := strings.Trim(ctx.Query("q"), " ") if strings.IndexByte(keyword, 0) >= 0 { @@ -251,6 +254,10 @@ func ListIssues(ctx *context.APIContext) { var err error if len(keyword) > 0 { issueIDs, err = issue_indexer.SearchIssuesByKeyword([]int64{ctx.Repo.Repository.ID}, keyword) + if err != nil { + ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err) + return + } } if splitted := strings.Split(ctx.Query("labels"), ","); len(splitted) > 0 { @@ -306,7 +313,7 @@ func ListIssues(ctx *context.APIContext) { // 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{ + issuesOpt := &models.IssuesOptions{ ListOptions: listOptions, RepoIDs: []int64{ctx.Repo.Repository.ID}, IsClosed: isClosed, @@ -314,18 +321,25 @@ func ListIssues(ctx *context.APIContext) { LabelIDs: labelIDs, MilestoneIDs: mileIDs, IsPull: isPull, - }) + } + + if issues, err = models.Issues(issuesOpt); err != nil { + ctx.Error(http.StatusInternalServerError, "Issues", err) + return + } + + issuesOpt.ListOptions = models.ListOptions{ + Page: -1, + } + if filteredCount, err = models.CountIssues(issuesOpt); err != nil { + ctx.Error(http.StatusInternalServerError, "CountIssues", err) + return + } } - if err != nil { - ctx.Error(http.StatusInternalServerError, "Issues", err) - return - } - - ctx.SetLinkHeader(ctx.Repo.Repository.NumIssues, listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.Repo.Repository.NumIssues)) + ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", filteredCount)) ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues)) }