From aef1d49c32e99a56946a0a9c4b355b860eecdaff Mon Sep 17 00:00:00 2001 From: Christopher Homberger Date: Wed, 28 May 2025 20:49:46 +0200 Subject: [PATCH 1/2] Reduce paging inconsistency * disallow bypassing configured limits in api * allow per_page without documenting it * allow limit for the only endpoint only allowing per_page This is an paging inconsistency api review, please comment your opinion about the added code comments * ctx.SetLinkHeader(int(totalNumOfBranches), listOptions.PageSize) missing for several api endpoints should I add them everywhere? * do we want to accept per_page in api? * better compatibility with existing keda github_runner --- models/issues/issue.go | 1 + models/issues/issue_watch.go | 9 +++---- models/issues/label.go | 2 ++ models/issues/review.go | 1 + models/issues/review_list.go | 1 + models/issues/stopwatch.go | 1 + models/packages/package_version.go | 1 + models/repo/star.go | 1 + models/repo/watch.go | 9 ++----- models/user/search.go | 1 + models/user/user.go | 11 +++----- modules/git/repo_tag.go | 1 + routers/api/v1/admin/adopt.go | 4 +-- routers/api/v1/admin/cron.go | 1 + routers/api/v1/admin/org.go | 2 ++ routers/api/v1/admin/user.go | 2 ++ routers/api/v1/org/org.go | 2 ++ routers/api/v1/repo/commits.go | 1 + routers/api/v1/repo/issue.go | 19 ++++---------- routers/api/v1/repo/issue_dependency.go | 35 +++++++------------------ routers/api/v1/repo/label.go | 6 ++++- routers/api/v1/repo/mirror.go | 7 +++-- routers/api/v1/repo/pull_review.go | 2 ++ routers/api/v1/repo/star.go | 6 ++++- routers/api/v1/repo/tag.go | 2 ++ routers/api/v1/repo/tree.go | 5 +++- routers/api/v1/repo/wiki.go | 15 +++-------- routers/api/v1/user/follower.go | 5 +++- routers/api/v1/user/user.go | 2 ++ routers/api/v1/utils/page.go | 8 +++++- services/pull/review.go | 1 + 31 files changed, 82 insertions(+), 82 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index a86d50ca9d..bbaae09a79 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -690,6 +690,7 @@ func (issue *Issue) BlockedByDependencies(ctx context.Context, opts db.ListOptio Where("issue_id = ?", issue.ID). // sort by repo id then created date, with the issues of the same repo at the beginning of the list OrderBy("CASE WHEN issue.repo_id = ? THEN 0 ELSE issue.repo_id END, issue.created_unix DESC", issue.RepoID) + // Pagination bypass needed by ViewIssue => prepareIssueViewSidebarDependency if opts.Page > 0 { sess = db.SetSessionPagination(sess, &opts) } diff --git a/models/issues/issue_watch.go b/models/issues/issue_watch.go index 560be17eb6..67f3db4cbe 100644 --- a/models/issues/issue_watch.go +++ b/models/issues/issue_watch.go @@ -105,12 +105,9 @@ func GetIssueWatchers(ctx context.Context, issueID int64, listOptions db.ListOpt And("`user`.prohibit_login = ?", false). Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id") - if listOptions.Page > 0 { - sess = db.SetSessionPagination(sess, &listOptions) - watches := make([]*IssueWatch, 0, listOptions.PageSize) - return watches, sess.Find(&watches) - } - watches := make([]*IssueWatch, 0, 8) + listOptions.SetDefaultValues() + sess = db.SetSessionPagination(sess, &listOptions) + watches := make([]*IssueWatch, 0, listOptions.PageSize) return watches, sess.Find(&watches) } diff --git a/models/issues/label.go b/models/issues/label.go index cfbe100926..559a09d95f 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -408,6 +408,7 @@ func GetLabelsByRepoID(ctx context.Context, repoID int64, sortType string, listO sess.Asc("name") } + // Pagination bypass used by some callers if listOptions.Page > 0 { sess = db.SetSessionPagination(sess, &listOptions) } @@ -483,6 +484,7 @@ func GetLabelsByOrgID(ctx context.Context, orgID int64, sortType string, listOpt sess.Asc("name") } + // Why can we bypass limits here? if listOptions.Page > 0 { sess = db.SetSessionPagination(sess, &listOptions) } diff --git a/models/issues/review.go b/models/issues/review.go index 71fdb7456f..1480e4768c 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -389,6 +389,7 @@ func GetCurrentReview(ctx context.Context, reviewer *user_model.User, issue *Iss if reviewer == nil { return nil, nil } + // Missing db.ListOptionsAll reviews, err := FindReviews(ctx, FindReviewOptions{ Types: []ReviewType{ReviewTypePending}, IssueID: issue.ID, diff --git a/models/issues/review_list.go b/models/issues/review_list.go index 928f24fb2d..5476208f21 100644 --- a/models/issues/review_list.go +++ b/models/issues/review_list.go @@ -120,6 +120,7 @@ func (opts *FindReviewOptions) toCond() builder.Cond { func FindReviews(ctx context.Context, opts FindReviewOptions) (ReviewList, error) { reviews := make([]*Review, 0, 10) sess := db.GetEngine(ctx).Where(opts.toCond()) + // Pagination bypass used by some callers if opts.Page > 0 && !opts.IsListAll() { sess = db.SetSessionPagination(sess, &opts) } diff --git a/models/issues/stopwatch.go b/models/issues/stopwatch.go index 7c05a3a883..2897e91aeb 100644 --- a/models/issues/stopwatch.go +++ b/models/issues/stopwatch.go @@ -91,6 +91,7 @@ func GetUIDsAndStopwatch(ctx context.Context) ([]*UserStopwatch, error) { func GetUserStopwatches(ctx context.Context, userID int64, listOptions db.ListOptions) ([]*Stopwatch, error) { sws := make([]*Stopwatch, 0, 8) sess := db.GetEngine(ctx).Where("stopwatch.user_id = ?", userID) + // Pagination bypass used by CancelStopwatch if listOptions.Page > 0 { sess = db.SetSessionPagination(sess, &listOptions) } diff --git a/models/packages/package_version.go b/models/packages/package_version.go index bb7fd895f8..e97dffbf5b 100644 --- a/models/packages/package_version.go +++ b/models/packages/package_version.go @@ -187,6 +187,7 @@ type PackageSearchOptions struct { HasFileWithName string // only results are found which are associated with a file with the specific name HasFiles optional.Option[bool] // only results are found which have associated files Sort VersionSort + // Only one with interface db.Paginator } diff --git a/models/repo/star.go b/models/repo/star.go index 4c66855525..fa83d7eb8d 100644 --- a/models/repo/star.go +++ b/models/repo/star.go @@ -79,6 +79,7 @@ func IsStaring(ctx context.Context, userID, repoID int64) bool { func GetStargazers(ctx context.Context, repo *Repository, opts db.ListOptions) ([]*user_model.User, error) { sess := db.GetEngine(ctx).Where("star.repo_id = ?", repo.ID). Join("LEFT", "star", "`user`.id = star.uid") + // Paging bypass used by UI if opts.Page > 0 { sess = db.SetSessionPagination(sess, &opts) diff --git a/models/repo/watch.go b/models/repo/watch.go index a616544cae..acb23c3410 100644 --- a/models/repo/watch.go +++ b/models/repo/watch.go @@ -151,14 +151,9 @@ func GetRepoWatchers(ctx context.Context, repoID int64, opts db.ListOptions) ([] sess := db.GetEngine(ctx).Where("watch.repo_id=?", repoID). Join("LEFT", "watch", "`user`.id=`watch`.user_id"). And("`watch`.mode<>?", WatchModeDont) - if opts.Page > 0 { - sess = db.SetSessionPagination(sess, &opts) - users := make([]*user_model.User, 0, opts.PageSize) + sess = db.SetSessionPagination(sess, &opts) + users := make([]*user_model.User, 0, opts.PageSize) - return users, sess.Find(&users) - } - - users := make([]*user_model.User, 0, 8) return users, sess.Find(&users) } diff --git a/models/user/search.go b/models/user/search.go index cfd0d011bc..fe1e277c14 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -151,6 +151,7 @@ func SearchUsers(ctx context.Context, opts SearchUserOptions) (users []*User, _ sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String()) defer sessQuery.Close() + // Pagination bypass used by UI if opts.Page > 0 { sessQuery = db.SetSessionPagination(sessQuery, &opts) } diff --git a/models/user/user.go b/models/user/user.go index d7331d79f0..b8ebe6f6ba 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -321,15 +321,9 @@ func GetUserFollowers(ctx context.Context, u, viewer *User, listOptions db.ListO And("`user`.type=?", UserTypeIndividual). And(isUserVisibleToViewerCond(viewer)) - if listOptions.Page > 0 { - sess = db.SetSessionPagination(sess, &listOptions) + sess = db.SetSessionPagination(sess, &listOptions) - users := make([]*User, 0, listOptions.PageSize) - count, err := sess.FindAndCount(&users) - return users, count, err - } - - users := make([]*User, 0, 8) + users := make([]*User, 0, listOptions.PageSize) count, err := sess.FindAndCount(&users) return users, count, err } @@ -343,6 +337,7 @@ func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListO And("`user`.type IN (?, ?)", UserTypeIndividual, UserTypeOrganization). And(isUserVisibleToViewerCond(viewer)) + // pagination bypass, otherwise 8 if listOptions.Page > 0 { sess = db.SetSessionPagination(sess, &listOptions) diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index c74618471a..11e323309e 100644 --- a/modules/git/repo_tag.go +++ b/modules/git/repo_tag.go @@ -147,6 +147,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) { sortTagsByTime(tags) tagsTotal := len(tags) + // pagination bypass if page != 0 { tags = util.PaginateSlice(tags, page, pageSize).([]*Tag) } diff --git a/routers/api/v1/admin/adopt.go b/routers/api/v1/admin/adopt.go index c2efed7490..9cf3103e68 100644 --- a/routers/api/v1/admin/adopt.go +++ b/routers/api/v1/admin/adopt.go @@ -41,9 +41,7 @@ func ListUnadoptedRepositories(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" listOptions := utils.GetListOptions(ctx) - if listOptions.Page == 0 { - listOptions.Page = 1 - } + listOptions.SetDefaultValues() repoNames, count, err := repo_service.ListUnadoptedRepositories(ctx, ctx.FormString("query"), &listOptions) if err != nil { ctx.APIErrorInternal(err) diff --git a/routers/api/v1/admin/cron.go b/routers/api/v1/admin/cron.go index b4dae11095..792e19c14c 100644 --- a/routers/api/v1/admin/cron.go +++ b/routers/api/v1/admin/cron.go @@ -39,6 +39,7 @@ func ListCronTasks(ctx *context.APIContext) { count := len(tasks) listOpts := utils.GetListOptions(ctx) + listOpts.SetDefaultValues() tasks = util.PaginateSlice(tasks, listOpts.Page, listOpts.PageSize).(cron.TaskTable) res := make([]structs.Cron, len(tasks)) diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index c7a4ae8419..c941f3cc2c 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -100,6 +100,8 @@ func GetAllOrgs(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" listOptions := utils.GetListOptions(ctx) + // SearchUsers allows pagination bypass + listOptions.SetDefaultValues() users, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{ Actor: ctx.Doer, diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 85dd547ac1..7baa55c393 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -422,6 +422,8 @@ func SearchUsers(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" listOptions := utils.GetListOptions(ctx) + // SearchUsers allows pagination bypass + listOptions.SetDefaultValues() users, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{ Actor: ctx.Doer, diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 27c646896a..185b555972 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -200,6 +200,8 @@ func GetAll(ctx *context.APIContext) { } listOptions := utils.GetListOptions(ctx) + // SearchUsers allows pagination bypass + listOptions.SetDefaultValues() publicOrgs, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{ Actor: ctx.Doer, diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index 6a93be624f..594ff59bee 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -184,6 +184,7 @@ func GetAllCommits(ctx *context.APIContext) { return } + // Unusual use of ListOptions, but we need to support pagination listOptions := utils.GetListOptions(ctx) if listOptions.Page <= 0 { listOptions.Page = 1 diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index b9a71982d0..13ad70538b 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -21,7 +21,6 @@ import ( user_model "code.gitea.io/gitea/models/user" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web" @@ -256,20 +255,12 @@ func SearchIssues(ctx *context.APIContext) { } } - // this api is also used in UI, - // so the default limit is set to fit UI needs - limit := ctx.FormInt("limit") - if limit == 0 { - limit = setting.UI.IssuePagingNum - } else if limit > setting.API.MaxResponseItems { - limit = setting.API.MaxResponseItems - } + opts := utils.GetListOptions(ctx) + // field opts.PageSize is used below + opts.SetDefaultValues() searchOpt := &issue_indexer.SearchOptions{ - Paginator: &db.ListOptions{ - PageSize: limit, - Page: ctx.FormInt("page"), - }, + Paginator: &opts, Keyword: keyword, RepoIDs: repoIDs, AllPublic: allPublic, @@ -321,7 +312,7 @@ func SearchIssues(ctx *context.APIContext) { return } - ctx.SetLinkHeader(int(total), limit) + ctx.SetLinkHeader(int(total), opts.PageSize) ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues)) } diff --git a/routers/api/v1/repo/issue_dependency.go b/routers/api/v1/repo/issue_dependency.go index 2048c76ea0..04b7e232f0 100644 --- a/routers/api/v1/repo/issue_dependency.go +++ b/routers/api/v1/repo/issue_dependency.go @@ -7,13 +7,13 @@ package repo import ( "net/http" - "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) @@ -77,26 +77,16 @@ func GetIssueDependencies(ctx *context.APIContext) { return } - page := ctx.FormInt("page") - if page <= 1 { - page = 1 - } - limit := ctx.FormInt("limit") - if limit == 0 { - limit = setting.API.DefaultPagingNum - } else if limit > setting.API.MaxResponseItems { - limit = setting.API.MaxResponseItems - } + opts := utils.GetListOptions(ctx) + // field opts.PageSize is used below + opts.SetDefaultValues() canWrite := ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull) - blockerIssues := make([]*issues_model.Issue, 0, limit) + blockerIssues := make([]*issues_model.Issue, 0, opts.PageSize) // 2. Get the issues this issue depends on, i.e. the `<#b>`: ` <- <#b>` - blockersInfo, err := issue.BlockedByDependencies(ctx, db.ListOptions{ - Page: page, - PageSize: limit, - }) + blockersInfo, err := issue.BlockedByDependencies(ctx, opts) if err != nil { ctx.APIErrorInternal(err) return @@ -328,17 +318,10 @@ func GetIssueBlocks(ctx *context.APIContext) { return } - page := ctx.FormInt("page") - if page <= 1 { - page = 1 - } - limit := ctx.FormInt("limit") - if limit <= 1 { - limit = setting.API.DefaultPagingNum - } + opts := utils.GetListOptions(ctx) - skip := (page - 1) * limit - maxNum := page * limit + skip, limit := opts.GetSkipTake() + maxNum := skip + limit deps, err := issue.BlockingDependencies(ctx) if err != nil { diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go index 4f79d42595..8a3b066d2a 100644 --- a/routers/api/v1/repo/label.go +++ b/routers/api/v1/repo/label.go @@ -49,7 +49,11 @@ func ListLabels(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - labels, err := issues_model.GetLabelsByRepoID(ctx, ctx.Repo.Repository.ID, ctx.FormString("sort"), utils.GetListOptions(ctx)) + opts := utils.GetListOptions(ctx) + // GetLabelsByRepoID allows pagination bypass + opts.SetDefaultValues() + + labels, err := issues_model.GetLabelsByRepoID(ctx, ctx.Repo.Repository.ID, ctx.FormString("sort"), opts) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go index f11a1603c4..61df308706 100644 --- a/routers/api/v1/repo/mirror.go +++ b/routers/api/v1/repo/mirror.go @@ -165,8 +165,11 @@ func ListPushMirrors(ctx *context.APIContext) { } repo := ctx.Repo.Repository + opts := utils.GetListOptions(ctx) + // field opts.PageSize is used below + opts.SetDefaultValues() // Get all push mirrors for the specified repository. - pushMirrors, count, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, utils.GetListOptions(ctx)) + pushMirrors, count, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, opts) if err != nil { ctx.APIError(http.StatusNotFound, err) return @@ -179,7 +182,7 @@ func ListPushMirrors(ctx *context.APIContext) { responsePushMirrors = append(responsePushMirrors, m) } } - ctx.SetLinkHeader(len(responsePushMirrors), utils.GetListOptions(ctx).PageSize) + ctx.SetLinkHeader(len(responsePushMirrors), opts.PageSize) ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, responsePushMirrors) } diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 9421a052db..ff9f44c8f2 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -85,6 +85,8 @@ func ListPullReviews(ctx *context.APIContext) { ListOptions: utils.GetListOptions(ctx), IssueID: pr.IssueID, } + // FindReviews allows pagination bypass + opts.SetDefaultValues() allReviews, err := issues_model.FindReviews(ctx, opts) if err != nil { diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go index 46218e0e28..13eb3ace49 100644 --- a/routers/api/v1/repo/star.go +++ b/routers/api/v1/repo/star.go @@ -47,7 +47,11 @@ func ListStargazers(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - stargazers, err := repo_model.GetStargazers(ctx, ctx.Repo.Repository, utils.GetListOptions(ctx)) + opts := utils.GetListOptions(ctx) + // GetStargazers allows pagination bypass + opts.SetDefaultValues() + + stargazers, err := repo_model.GetStargazers(ctx, ctx.Repo.Repository, opts) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index 9e77637282..111aafcf6b 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -54,6 +54,8 @@ func ListTags(ctx *context.APIContext) { // "$ref": "#/responses/notFound" listOpts := utils.GetListOptions(ctx) + // GetTagInfos allows pagination bypass + listOpts.SetDefaultValues() tags, total, err := ctx.Repo.GitRepo.GetTagInfos(listOpts.Page, listOpts.PageSize) if err != nil { diff --git a/routers/api/v1/repo/tree.go b/routers/api/v1/repo/tree.go index dfd69600fb..2494878576 100644 --- a/routers/api/v1/repo/tree.go +++ b/routers/api/v1/repo/tree.go @@ -6,6 +6,7 @@ package repo import ( "net/http" + "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" files_service "code.gitea.io/gitea/services/repository/files" ) @@ -61,7 +62,9 @@ func GetTree(ctx *context.APIContext) { ctx.APIError(http.StatusBadRequest, "sha not provided") return } - if tree, err := files_service.GetTreeBySHA(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, sha, ctx.FormInt("page"), ctx.FormInt("per_page"), ctx.FormBool("recursive")); err != nil { + opts := utils.GetListOptions(ctx) + opts.SetDefaultValues() + if tree, err := files_service.GetTreeBySHA(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, sha, opts.Page, opts.PageSize, ctx.FormBool("recursive")); err != nil { ctx.APIError(http.StatusBadRequest, err.Error()) } else { ctx.SetTotalCountHeader(int64(tree.TotalCount)) diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go index d5840b4149..497fa220b0 100644 --- a/routers/api/v1/repo/wiki.go +++ b/routers/api/v1/repo/wiki.go @@ -16,6 +16,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" notify_service "code.gitea.io/gitea/services/notify" @@ -298,17 +299,9 @@ func ListWikiPages(ctx *context.APIContext) { return } - page := ctx.FormInt("page") - if page <= 1 { - page = 1 - } - limit := ctx.FormInt("limit") - if limit <= 1 { - limit = setting.API.DefaultPagingNum - } - - skip := (page - 1) * limit - maxNum := page * limit + opts := utils.GetListOptions(ctx) + skip, limit := opts.GetSkipTake() + maxNum := skip + limit entries, err := commit.ListEntries() if err != nil { diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index 0d0c0be7e0..72f422060e 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -88,7 +88,10 @@ func ListFollowers(ctx *context.APIContext) { } func listUserFollowing(ctx *context.APIContext, u *user_model.User) { - users, count, err := user_model.GetUserFollowing(ctx, u, ctx.Doer, utils.GetListOptions(ctx)) + opts := utils.GetListOptions(ctx) + // GetUserFollowing allows pagination bypass + opts.SetDefaultValues() + users, count, err := user_model.GetUserFollowing(ctx, u, ctx.Doer, opts) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 2b98fb5ac7..a3339ddb6c 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -55,6 +55,8 @@ func Search(ctx *context.APIContext) { // "$ref": "#/definitions/User" listOptions := utils.GetListOptions(ctx) + // SearchUsers allows pagination bypass + listOptions.SetDefaultValues() uid := ctx.FormInt64("uid") var users []*user_model.User diff --git a/routers/api/v1/utils/page.go b/routers/api/v1/utils/page.go index 024ba7b8d9..41f83a2ff1 100644 --- a/routers/api/v1/utils/page.go +++ b/routers/api/v1/utils/page.go @@ -11,8 +11,14 @@ import ( // GetListOptions returns list options using the page and limit parameters func GetListOptions(ctx *context.APIContext) db.ListOptions { + limit := ctx.FormInt("limit") + if limit == 0 { + // Compatibility with foreign API clients that used "per_page" instead of "limit" + limit = ctx.FormInt("per_page") + } + return db.ListOptions{ Page: ctx.FormInt("page"), - PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), + PageSize: convert.ToCorrectPageSize(limit), } } diff --git a/services/pull/review.go b/services/pull/review.go index 5c80e7b338..83c1fc3d18 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -428,6 +428,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string, } if dismissPriors { + // missing db.ListOptionsAll reviews, err := issues_model.FindReviews(ctx, issues_model.FindReviewOptions{ IssueID: review.IssueID, ReviewerID: review.ReviewerID, From 02a4515bc9c44cc08e6d5c9d9836a5994fafc191 Mon Sep 17 00:00:00 2001 From: Christopher Homberger Date: Wed, 28 May 2025 22:16:09 +0200 Subject: [PATCH 2/2] update test to as this API is no longer used in the frontend, it uses API page size --- tests/integration/api_issue_test.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go index e035f7200b..26ec1515d1 100644 --- a/tests/integration/api_issue_test.go +++ b/tests/integration/api_issue_test.go @@ -266,10 +266,10 @@ func TestAPIEditIssue(t *testing.T) { func TestAPISearchIssues(t *testing.T) { defer tests.PrepareTestEnv(t)() - // as this API was used in the frontend, it uses UI page size + // as this API is no longer used in the frontend, it uses API page size expectedIssueCount := 20 // from the fixtures - if expectedIssueCount > setting.UI.IssuePagingNum { - expectedIssueCount = setting.UI.IssuePagingNum + if expectedIssueCount > setting.API.MaxResponseItems { + expectedIssueCount = setting.API.MaxResponseItems } link, _ := url.Parse("/api/v1/repos/issues/search") @@ -314,7 +314,8 @@ func TestAPISearchIssues(t *testing.T) { resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Equal(t, "22", resp.Header().Get("X-Total-Count")) - assert.Len(t, apiIssues, 20) + // 30 is default page size now + assert.Len(t, apiIssues, 22) query.Add("limit", "10") link.RawQuery = query.Encode() @@ -370,10 +371,10 @@ func TestAPISearchIssues(t *testing.T) { func TestAPISearchIssuesWithLabels(t *testing.T) { defer tests.PrepareTestEnv(t)() - // as this API was used in the frontend, it uses UI page size + // as this API is no longer used in the frontend, it uses API page size expectedIssueCount := 20 // from the fixtures - if expectedIssueCount > setting.UI.IssuePagingNum { - expectedIssueCount = setting.UI.IssuePagingNum + if expectedIssueCount > setting.API.MaxResponseItems { + expectedIssueCount = setting.API.MaxResponseItems } link, _ := url.Parse("/api/v1/repos/issues/search")