From 81324cf37c87d834137a3b5ebf287adeed513f18 Mon Sep 17 00:00:00 2001 From: Cirno the Strongest <1447794+CirnoT@users.noreply.github.com> Date: Sun, 21 Jun 2020 10:22:06 +0200 Subject: [PATCH] Add pagination headers on endpoints that support total count from database (#11145) * begin work * import fmt * more work * empty commit Co-authored-by: Lauris BH --- routers/api/v1/admin/org.go | 10 ++++++++-- routers/api/v1/admin/user.go | 8 ++++++-- routers/api/v1/org/member.go | 2 ++ routers/api/v1/org/org.go | 9 +++++++-- routers/api/v1/org/team.go | 9 +++++++-- routers/api/v1/repo/commits.go | 6 ++++-- routers/api/v1/repo/issue.go | 2 ++ routers/api/v1/repo/status.go | 9 +++++++-- routers/api/v1/user/user.go | 10 ++++++++-- 9 files changed, 51 insertions(+), 14 deletions(-) diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index fc2f81eb6..62d485d82 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -6,6 +6,7 @@ package admin import ( + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -101,10 +102,12 @@ func GetAllOrgs(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - users, _, err := models.SearchUsers(&models.SearchUserOptions{ + listOptions := utils.GetListOptions(ctx) + + users, maxResults, err := models.SearchUsers(&models.SearchUserOptions{ Type: models.UserTypeOrganization, OrderBy: models.SearchOrderByAlphabetically, - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, Visible: []api.VisibleType{api.VisibleTypePublic, api.VisibleTypeLimited, api.VisibleTypePrivate}, }) if err != nil { @@ -115,5 +118,8 @@ func GetAllOrgs(ctx *context.APIContext) { for i := range users { orgs[i] = convert.ToOrganization(users[i]) } + + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.JSON(http.StatusOK, &orgs) } diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index f416ea956..153ce66d7 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -351,10 +351,12 @@ func GetAllUsers(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - users, _, err := models.SearchUsers(&models.SearchUserOptions{ + listOptions := utils.GetListOptions(ctx) + + users, maxResults, err := models.SearchUsers(&models.SearchUserOptions{ Type: models.UserTypeIndividual, OrderBy: models.SearchOrderByAlphabetically, - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, }) if err != nil { ctx.Error(http.StatusInternalServerError, "GetAllUsers", err) @@ -366,5 +368,7 @@ func GetAllUsers(ctx *context.APIContext) { results[i] = convert.ToUser(users[i], ctx.IsSigned, ctx.User.IsAdmin) } + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.JSON(http.StatusOK, &results) } diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 285a401d2..75ae1191a 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -20,6 +20,7 @@ import ( // listMembers list an organization's members func listMembers(ctx *context.APIContext, publicOnly bool) { var members []*models.User + members, _, err := models.FindOrgMembers(&models.FindOrgMembersOpts{ OrgID: ctx.Org.Organization.ID, PublicOnly: publicOnly, @@ -34,6 +35,7 @@ func listMembers(ctx *context.APIContext, publicOnly bool) { for i, member := range members { apiMembers[i] = convert.ToUser(member, ctx.IsSigned, ctx.User != nil && ctx.User.IsAdmin) } + ctx.JSON(http.StatusOK, apiMembers) } diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 220a7dc26..1d277521c 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -6,6 +6,7 @@ package org import ( + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -115,8 +116,10 @@ func GetAll(ctx *context.APIContext) { } } - publicOrgs, _, err := models.SearchUsers(&models.SearchUserOptions{ - ListOptions: utils.GetListOptions(ctx), + listOptions := utils.GetListOptions(ctx) + + publicOrgs, maxResults, err := models.SearchUsers(&models.SearchUserOptions{ + ListOptions: listOptions, Type: models.UserTypeOrganization, OrderBy: models.SearchOrderByAlphabetically, Visible: vMode, @@ -130,6 +133,8 @@ func GetAll(ctx *context.APIContext) { orgs[i] = convert.ToOrganization(publicOrgs[i]) } + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.JSON(http.StatusOK, &orgs) } diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 9ca40a779..0e92f2edb 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -6,6 +6,7 @@ package org import ( + "fmt" "net/http" "strings" @@ -650,15 +651,17 @@ func SearchTeam(ctx *context.APIContext) { // items: // "$ref": "#/definitions/Team" + listOptions := utils.GetListOptions(ctx) + opts := &models.SearchTeamOptions{ UserID: ctx.User.ID, Keyword: strings.TrimSpace(ctx.Query("q")), OrgID: ctx.Org.Organization.ID, IncludeDesc: (ctx.Query("include_desc") == "" || ctx.QueryBool("include_desc")), - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, } - teams, _, err := models.SearchTeam(opts) + teams, maxResults, err := models.SearchTeam(opts) if err != nil { log.Error("SearchTeam failed: %v", err) ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ @@ -681,6 +684,8 @@ func SearchTeam(ctx *context.APIContext) { apiTeams[i] = convert.ToTeam(teams[i]) } + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.JSON(http.StatusOK, map[string]interface{}{ "ok": true, "data": apiTeams, diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index faae214a4..bdfd2bbad 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -202,14 +202,16 @@ func GetAllCommits(ctx *context.APIContext) { i++ } - ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize) - + // kept for backwards compatibility ctx.Header().Set("X-Page", strconv.Itoa(listOptions.Page)) ctx.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize)) ctx.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10)) ctx.Header().Set("X-PageCount", strconv.Itoa(pageCount)) ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount)) + ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", commitsCountTotal)) + ctx.JSON(http.StatusOK, &apiCommits) } diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 51f6efe8a..937b581a6 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -322,6 +322,8 @@ func ListIssues(ctx *context.APIContext) { } ctx.SetLinkHeader(ctx.Repo.Repository.NumIssues, listOptions.PageSize) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.Repo.Repository.NumIssues)) + ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues)) } diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index e4eeecf37..11af4a0d1 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -208,8 +208,10 @@ func getCommitStatuses(ctx *context.APIContext, sha string) { } repo := ctx.Repo.Repository - statuses, _, err := models.GetCommitStatuses(repo, sha, &models.CommitStatusOptions{ - ListOptions: utils.GetListOptions(ctx), + listOptions := utils.GetListOptions(ctx) + + statuses, maxResults, err := models.GetCommitStatuses(repo, sha, &models.CommitStatusOptions{ + ListOptions: listOptions, SortType: ctx.QueryTrim("sort"), State: ctx.QueryTrim("state"), }) @@ -223,6 +225,9 @@ func getCommitStatuses(ctx *context.APIContext, sha string) { apiStatuses = append(apiStatuses, status.APIFormat()) } + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) + ctx.JSON(http.StatusOK, apiStatuses) } diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index aaca53093..426501f33 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -6,6 +6,7 @@ package user import ( + "fmt" "net/http" "strings" @@ -56,14 +57,16 @@ func Search(ctx *context.APIContext) { // items: // "$ref": "#/definitions/User" + listOptions := utils.GetListOptions(ctx) + opts := &models.SearchUserOptions{ Keyword: strings.Trim(ctx.Query("q"), " "), UID: com.StrTo(ctx.Query("uid")).MustInt64(), Type: models.UserTypeIndividual, - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, } - users, _, err := models.SearchUsers(opts) + users, maxResults, err := models.SearchUsers(opts) if err != nil { ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ "ok": false, @@ -77,6 +80,9 @@ func Search(ctx *context.APIContext) { results[i] = convert.ToUser(users[i], ctx.IsSigned, ctx.User != nil && ctx.User.IsAdmin) } + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) + ctx.JSON(http.StatusOK, map[string]interface{}{ "ok": true, "data": results,