From 1f01f53c53ea75634f981611635be49c69e1920b Mon Sep 17 00:00:00 2001 From: SpaWn2KiLl Date: Fri, 24 Jan 2020 19:00:29 +0000 Subject: [PATCH] API add/generalize pagination (#9452) * paginate results * fixed deadlock * prevented breaking change * updated swagger * go fmt * fixed find topic * go mod tidy * go mod vendor with go1.13.5 * fixed repo find topics * fixed unit test * added Limit method to Engine struct; use engine variable when provided; fixed gitignore * use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors * set Page value on Home route * improved memory allocations * fixed response headers * removed logfiles * fixed import order * import order * improved swagger * added function to get models.ListOptions from context * removed pagesize diff on unit test * fixed imports * removed unnecessary struct field * fixed go fmt * scoped PR * code improvements * code improvements * go mod tidy * fixed import order * fixed commit statuses session * fixed files headers * fixed headers; added pagination for notifications * go mod tidy * go fmt * removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list * Apply suggestions from code review Co-Authored-By: 6543 <6543@obermui.de> Co-Authored-By: zeripath * fixed build error * CI.restart() * fixed merge conflicts resolve * fixed conflicts resolve * improved FindTrackedTimesOptions.ToOptions() method * added backwards compatibility on ListReleases request; fixed issue tracked time ToSession * fixed build error; fixed swagger template * fixed swagger template * fixed ListReleases backwards compatibility * added page to user search route Co-authored-by: techknowlogick Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath --- cmd/admin.go | 8 +- models/access.go | 2 +- models/commit_status.go | 10 +- models/gpg_key.go | 13 +- models/issue.go | 3 +- models/issue_comment.go | 6 + models/issue_label.go | 11 +- models/issue_label_test.go | 2 +- models/issue_milestone.go | 8 +- models/issue_milestone_test.go | 4 +- models/issue_reaction.go | 25 +- models/issue_stopwatch.go | 11 +- models/issue_test.go | 18 +- models/issue_tracked_time.go | 16 +- models/issue_watch.go | 16 +- models/issue_watch_test.go | 8 +- models/list_options.go | 44 ++ models/models.go | 2 + models/notification.go | 5 +- models/org.go | 49 +- models/org_team.go | 47 +- models/org_team_test.go | 6 +- models/org_test.go | 20 +- models/pull_list.go | 6 +- models/pull_sign.go | 2 +- models/pull_test.go | 8 +- models/release.go | 19 +- models/repo.go | 43 +- models/repo_collaboration.go | 20 +- models/repo_collaboration_test.go | 2 +- models/repo_generate.go | 4 +- models/repo_list.go | 3 +- models/repo_list_test.go | 92 +-- models/repo_sign.go | 6 +- models/repo_watch.go | 12 +- models/repo_watch_test.go | 20 +- models/ssh_key.go | 32 +- models/star.go | 12 +- models/star_test.go | 4 +- models/token.go | 18 +- models/token_test.go | 6 +- models/topic.go | 7 +- models/topic_test.go | 2 +- models/user.go | 94 +-- models/user_test.go | 20 +- models/userlist.go | 2 +- models/webhook.go | 27 +- models/webhook_test.go | 4 +- modules/context/org.go | 3 +- modules/git/commit.go | 4 +- modules/git/repo_commit.go | 7 +- modules/indexer/issues/indexer.go | 3 +- modules/migrations/gitea_test.go | 14 +- modules/repository/create_test.go | 2 +- modules/repository/repo.go | 5 +- routers/admin/orgs.go | 9 +- routers/admin/users.go | 7 +- routers/api/v1/admin/org.go | 10 +- routers/api/v1/admin/user.go | 16 +- routers/api/v1/notify/repo.go | 9 + routers/api/v1/notify/user.go | 9 + routers/api/v1/org/hook.go | 10 +- routers/api/v1/org/member.go | 24 +- routers/api/v1/org/org.go | 32 +- routers/api/v1/org/team.go | 59 +- routers/api/v1/repo/collaborators.go | 11 +- routers/api/v1/repo/commits.go | 29 +- routers/api/v1/repo/fork.go | 12 +- routers/api/v1/repo/hook.go | 11 +- routers/api/v1/repo/issue.go | 51 +- routers/api/v1/repo/issue_comment.go | 18 +- routers/api/v1/repo/issue_reaction.go | 11 +- routers/api/v1/repo/issue_stopwatch.go | 12 +- routers/api/v1/repo/issue_subscription.go | 11 +- routers/api/v1/repo/issue_tracked_time.go | 32 +- routers/api/v1/repo/key.go | 12 +- routers/api/v1/repo/label.go | 11 +- routers/api/v1/repo/milestone.go | 12 +- routers/api/v1/repo/pull.go | 19 +- routers/api/v1/repo/release.go | 35 +- routers/api/v1/repo/repo.go | 23 +- routers/api/v1/repo/star.go | 11 +- routers/api/v1/repo/status.go | 33 +- routers/api/v1/repo/subscriber.go | 11 +- routers/api/v1/repo/topic.go | 34 +- routers/api/v1/user/app.go | 11 +- routers/api/v1/user/follower.go | 40 +- routers/api/v1/user/gpg_key.go | 26 +- routers/api/v1/user/key.go | 19 +- routers/api/v1/user/repo.go | 38 +- routers/api/v1/user/star.go | 27 +- routers/api/v1/user/user.go | 16 +- routers/api/v1/user/watch.go | 26 +- routers/api/v1/utils/utils.go | 10 + routers/home.go | 20 +- routers/org/home.go | 14 +- routers/org/members.go | 7 +- routers/org/setting.go | 2 +- routers/org/teams.go | 6 +- routers/repo/commit.go | 7 +- routers/repo/issue.go | 14 +- routers/repo/issue_label.go | 2 +- routers/repo/release.go | 25 +- routers/repo/setting.go | 6 +- routers/repo/view.go | 6 +- routers/repo/webhook.go | 2 +- routers/user/home.go | 6 +- routers/user/profile.go | 12 +- routers/user/setting/applications.go | 2 +- routers/user/setting/keys.go | 4 +- routers/user/setting/profile.go | 2 +- routers/user/setting/security.go | 2 +- templates/swagger/v1_json.tmpl | 686 ++++++++++++++++++++-- 113 files changed, 1890 insertions(+), 569 deletions(-) create mode 100644 models/list_options.go diff --git a/cmd/admin.go b/cmd/admin.go index f6f3e22b9..bc469cdae 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -351,9 +351,11 @@ func runRepoSyncReleases(c *cli.Context) error { log.Trace("Synchronizing repository releases (this may take a while)") for page := 1; ; page++ { repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{ - Page: page, - PageSize: models.RepositoryListDefaultPageSize, - Private: true, + ListOptions: models.ListOptions{ + PageSize: models.RepositoryListDefaultPageSize, + Page: page, + }, + Private: true, }) if err != nil { return fmt.Errorf("SearchRepositoryByName: %v", err) diff --git a/models/access.go b/models/access.go index 94defbb19..c50986706 100644 --- a/models/access.go +++ b/models/access.go @@ -215,7 +215,7 @@ func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]*userAcces // refreshCollaboratorAccesses retrieves repository collaborations with their access modes. func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]*userAccess) error { - collaborators, err := repo.getCollaborators(e) + collaborators, err := repo.getCollaborators(e, ListOptions{}) if err != nil { return fmt.Errorf("getCollaborations: %v", err) } diff --git a/models/commit_status.go b/models/commit_status.go index 4102e731e..ed6f8702c 100644 --- a/models/commit_status.go +++ b/models/commit_status.go @@ -104,7 +104,7 @@ func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus { // CommitStatusOptions holds the options for query commit statuses type CommitStatusOptions struct { - Page int + ListOptions State string SortType string } @@ -114,18 +114,22 @@ func GetCommitStatuses(repo *Repository, sha string, opts *CommitStatusOptions) if opts.Page <= 0 { opts.Page = 1 } + if opts.PageSize <= 0 { + opts.Page = ItemsPerPage + } countSession := listCommitStatusesStatement(repo, sha, opts) + countSession = opts.setSessionPagination(countSession) maxResults, err := countSession.Count(new(CommitStatus)) if err != nil { log.Error("Count PRs: %v", err) return nil, maxResults, err } - statuses := make([]*CommitStatus, 0, ItemsPerPage) + statuses := make([]*CommitStatus, 0, opts.PageSize) findSession := listCommitStatusesStatement(repo, sha, opts) + findSession = opts.setSessionPagination(findSession) sortCommitStatusesSession(findSession, opts.SortType) - findSession.Limit(ItemsPerPage, (opts.Page-1)*ItemsPerPage) return statuses, maxResults, findSession.Find(&statuses) } diff --git a/models/gpg_key.go b/models/gpg_key.go index 58eaa61e2..643aa6822 100644 --- a/models/gpg_key.go +++ b/models/gpg_key.go @@ -64,9 +64,14 @@ func (key *GPGKey) AfterLoad(session *xorm.Session) { } // ListGPGKeys returns a list of public keys belongs to given user. -func ListGPGKeys(uid int64) ([]*GPGKey, error) { - keys := make([]*GPGKey, 0, 5) - return keys, x.Where("owner_id=? AND primary_key_id=''", uid).Find(&keys) +func ListGPGKeys(uid int64, listOptions ListOptions) ([]*GPGKey, error) { + sess := x.Where("owner_id=? AND primary_key_id=''", uid) + if listOptions.Page != 0 { + sess = listOptions.setSessionPagination(sess) + } + + keys := make([]*GPGKey, 0, 2) + return keys, sess.Find(&keys) } // GetGPGKeyByID returns public key by given ID. @@ -628,7 +633,7 @@ func ParseCommitWithSignature(c *git.Commit) *CommitVerification { // Now try to associate the signature with the committer, if present if committer.ID != 0 { - keys, err := ListGPGKeys(committer.ID) + keys, err := ListGPGKeys(committer.ID, ListOptions{}) if err != nil { //Skipping failed to get gpg keys of user log.Error("ListGPGKeys: %v", err) return &CommitVerification{ diff --git a/models/issue.go b/models/issue.go index 797636605..c0be987ac 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1103,13 +1103,12 @@ func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) { // IssuesOptions represents options of an issue. type IssuesOptions struct { + ListOptions RepoIDs []int64 // include all repos if empty AssigneeID int64 PosterID int64 MentionedID int64 MilestoneID int64 - Page int - PageSize int IsClosed util.OptionalBool IsPull util.OptionalBool LabelIDs []int64 diff --git a/models/issue_comment.go b/models/issue_comment.go index a2e198774..4abc3f9c1 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -777,6 +777,7 @@ func GetCommentByID(id int64) (*Comment, error) { // FindCommentsOptions describes the conditions to Find comments type FindCommentsOptions struct { + ListOptions RepoID int64 IssueID int64 ReviewID int64 @@ -814,6 +815,11 @@ func findComments(e Engine, opts FindCommentsOptions) ([]*Comment, error) { if opts.RepoID > 0 { sess.Join("INNER", "issue", "issue.id = comment.issue_id") } + + if opts.Page != 0 { + sess = opts.setSessionPagination(sess) + } + return comments, sess. Asc("comment.created_unix"). Asc("comment.id"). diff --git a/models/issue_label.go b/models/issue_label.go index 66f93f4f4..abf0521ce 100644 --- a/models/issue_label.go +++ b/models/issue_label.go @@ -1,4 +1,5 @@ // Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -298,7 +299,7 @@ func GetLabelsInRepoByIDs(repoID int64, labelIDs []int64) ([]*Label, error) { Find(&labels) } -func getLabelsByRepoID(e Engine, repoID int64, sortType string) ([]*Label, error) { +func getLabelsByRepoID(e Engine, repoID int64, sortType string, listOptions ListOptions) ([]*Label, error) { labels := make([]*Label, 0, 10) sess := e.Where("repo_id = ?", repoID) @@ -313,12 +314,16 @@ func getLabelsByRepoID(e Engine, repoID int64, sortType string) ([]*Label, error sess.Asc("name") } + if listOptions.Page != 0 { + sess = listOptions.setSessionPagination(sess) + } + return labels, sess.Find(&labels) } // GetLabelsByRepoID returns all labels that belong to given repository by ID. -func GetLabelsByRepoID(repoID int64, sortType string) ([]*Label, error) { - return getLabelsByRepoID(x, repoID, sortType) +func GetLabelsByRepoID(repoID int64, sortType string, listOptions ListOptions) ([]*Label, error) { + return getLabelsByRepoID(x, repoID, sortType, listOptions) } func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) { diff --git a/models/issue_label_test.go b/models/issue_label_test.go index 3cf6cc0e5..e0aaf82f7 100644 --- a/models/issue_label_test.go +++ b/models/issue_label_test.go @@ -131,7 +131,7 @@ func TestGetLabelsInRepoByIDs(t *testing.T) { func TestGetLabelsByRepoID(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) testSuccess := func(repoID int64, sortType string, expectedIssueIDs []int64) { - labels, err := GetLabelsByRepoID(repoID, sortType) + labels, err := GetLabelsByRepoID(repoID, sortType, ListOptions{}) assert.NoError(t, err) assert.Len(t, labels, len(expectedIssueIDs)) for i, label := range labels { diff --git a/models/issue_milestone.go b/models/issue_milestone.go index f33ad19ad..68a568b51 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -221,7 +221,7 @@ func (milestones MilestoneList) getMilestoneIDs() []int64 { } // GetMilestonesByRepoID returns all opened milestones of a repository. -func GetMilestonesByRepoID(repoID int64, state api.StateType) (MilestoneList, error) { +func GetMilestonesByRepoID(repoID int64, state api.StateType, listOptions ListOptions) (MilestoneList, error) { sess := x.Where("repo_id = ?", repoID) switch state { @@ -238,7 +238,11 @@ func GetMilestonesByRepoID(repoID int64, state api.StateType) (MilestoneList, er sess = sess.And("is_closed = ?", false) } - miles := make([]*Milestone, 0, 10) + if listOptions.Page != 0 { + sess = listOptions.setSessionPagination(sess) + } + + miles := make([]*Milestone, 0, listOptions.PageSize) return miles, sess.Asc("deadline_unix").Asc("id").Find(&miles) } diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go index 5b7d19aa4..1b7999d40 100644 --- a/models/issue_milestone_test.go +++ b/models/issue_milestone_test.go @@ -71,7 +71,7 @@ func TestGetMilestonesByRepoID(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) test := func(repoID int64, state api.StateType) { repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository) - milestones, err := GetMilestonesByRepoID(repo.ID, state) + milestones, err := GetMilestonesByRepoID(repo.ID, state, ListOptions{}) assert.NoError(t, err) var n int @@ -105,7 +105,7 @@ func TestGetMilestonesByRepoID(t *testing.T) { test(3, api.StateClosed) test(3, api.StateAll) - milestones, err := GetMilestonesByRepoID(NonexistentID, api.StateOpen) + milestones, err := GetMilestonesByRepoID(NonexistentID, api.StateOpen, ListOptions{}) assert.NoError(t, err) assert.Len(t, milestones, 0) } diff --git a/models/issue_reaction.go b/models/issue_reaction.go index 5c3bf9d06..50b9d6848 100644 --- a/models/issue_reaction.go +++ b/models/issue_reaction.go @@ -30,6 +30,7 @@ type Reaction struct { // FindReactionsOptions describes the conditions to Find reactions type FindReactionsOptions struct { + ListOptions IssueID int64 CommentID int64 UserID int64 @@ -71,20 +72,28 @@ func FindCommentReactions(comment *Comment) (ReactionList, error) { } // FindIssueReactions returns a ReactionList of all reactions from an issue -func FindIssueReactions(issue *Issue) (ReactionList, error) { +func FindIssueReactions(issue *Issue, listOptions ListOptions) (ReactionList, error) { return findReactions(x, FindReactionsOptions{ - IssueID: issue.ID, - CommentID: -1, + ListOptions: listOptions, + IssueID: issue.ID, + CommentID: -1, }) } func findReactions(e Engine, opts FindReactionsOptions) ([]*Reaction, error) { - reactions := make([]*Reaction, 0, 10) - sess := e.Where(opts.toConds()) - return reactions, sess. + e = e. + Where(opts.toConds()). In("reaction.`type`", setting.UI.Reactions). - Asc("reaction.issue_id", "reaction.comment_id", "reaction.created_unix", "reaction.id"). - Find(&reactions) + Asc("reaction.issue_id", "reaction.comment_id", "reaction.created_unix", "reaction.id") + if opts.Page != 0 { + e = opts.setEnginePagination(e) + + reactions := make([]*Reaction, 0, opts.PageSize) + return reactions, e.Find(&reactions) + } + + reactions := make([]*Reaction, 0, 10) + return reactions, e.Find(&reactions) } func createReaction(e *xorm.Session, opts *ReactionOptions) (*Reaction, error) { diff --git a/models/issue_stopwatch.go b/models/issue_stopwatch.go index 8047f122b..ca9214115 100644 --- a/models/issue_stopwatch.go +++ b/models/issue_stopwatch.go @@ -33,9 +33,14 @@ func getStopwatch(e Engine, userID, issueID int64) (sw *Stopwatch, exists bool, } // GetUserStopwatches return list of all stopwatches of a user -func GetUserStopwatches(userID int64) (sws *Stopwatches, err error) { - sws = new(Stopwatches) - err = x.Where("stopwatch.user_id = ?", userID).Find(sws) +func GetUserStopwatches(userID int64, listOptions ListOptions) (*Stopwatches, error) { + sws := new(Stopwatches) + sess := x.Where("stopwatch.user_id = ?", userID) + if listOptions.Page != 0 { + sess = listOptions.setSessionPagination(sess) + } + + err := sess.Find(sws) if err != nil { return nil, err } diff --git a/models/issue_test.go b/models/issue_test.go index d65345a50..681ef8441 100644 --- a/models/issue_test.go +++ b/models/issue_test.go @@ -139,24 +139,30 @@ func TestIssues(t *testing.T) { IssuesOptions{ RepoIDs: []int64{1, 3}, SortType: "oldest", - Page: 1, - PageSize: 4, + ListOptions: ListOptions{ + Page: 1, + PageSize: 4, + }, }, []int64{1, 2, 3, 5}, }, { IssuesOptions{ LabelIDs: []int64{1}, - Page: 1, - PageSize: 4, + ListOptions: ListOptions{ + Page: 1, + PageSize: 4, + }, }, []int64{2, 1}, }, { IssuesOptions{ LabelIDs: []int64{1, 2}, - Page: 1, - PageSize: 4, + ListOptions: ListOptions{ + Page: 1, + PageSize: 4, + }, }, []int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests }, diff --git a/models/issue_tracked_time.go b/models/issue_tracked_time.go index 55e8baacd..49fefd327 100644 --- a/models/issue_tracked_time.go +++ b/models/issue_tracked_time.go @@ -10,7 +10,6 @@ import ( "code.gitea.io/gitea/modules/setting" "xorm.io/builder" - "xorm.io/xorm" ) // TrackedTime represents a time that was spent for a specific issue. @@ -71,6 +70,7 @@ func (tl TrackedTimeList) LoadAttributes() (err error) { // FindTrackedTimesOptions represent the filters for tracked times. If an ID is 0 it will be ignored. type FindTrackedTimesOptions struct { + ListOptions IssueID int64 UserID int64 RepositoryID int64 @@ -104,11 +104,19 @@ func (opts *FindTrackedTimesOptions) ToCond() builder.Cond { } // ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required -func (opts *FindTrackedTimesOptions) ToSession(e Engine) *xorm.Session { +func (opts *FindTrackedTimesOptions) ToSession(e Engine) Engine { + sess := e if opts.RepositoryID > 0 || opts.MilestoneID > 0 { - return e.Join("INNER", "issue", "issue.id = tracked_time.issue_id").Where(opts.ToCond()) + sess = e.Join("INNER", "issue", "issue.id = tracked_time.issue_id") } - return e.Where(opts.ToCond()) + + sess = sess.Where(opts.ToCond()) + + if opts.Page != 0 { + sess = opts.setEnginePagination(sess) + } + + return sess } func getTrackedTimes(e Engine, options FindTrackedTimesOptions) (trackedTimes TrackedTimeList, err error) { diff --git a/models/issue_watch.go b/models/issue_watch.go index 6144d6caf..343ad16cd 100644 --- a/models/issue_watch.go +++ b/models/issue_watch.go @@ -77,18 +77,22 @@ func GetIssueWatchersIDs(issueID int64) ([]int64, error) { } // GetIssueWatchers returns watchers/unwatchers of a given issue -func GetIssueWatchers(issueID int64) (IssueWatchList, error) { - return getIssueWatchers(x, issueID) +func GetIssueWatchers(issueID int64, listOptions ListOptions) (IssueWatchList, error) { + return getIssueWatchers(x, issueID, listOptions) } -func getIssueWatchers(e Engine, issueID int64) (watches IssueWatchList, err error) { - err = e. +func getIssueWatchers(e Engine, issueID int64, listOptions ListOptions) (watches IssueWatchList, err error) { + sess := e. Where("`issue_watch`.issue_id = ?", issueID). And("`issue_watch`.is_watching = ?", true). And("`user`.is_active = ?", true). And("`user`.prohibit_login = ?", false). - Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id"). - Find(&watches) + Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id") + + if listOptions.Page == 0 { + sess = listOptions.setSessionPagination(sess) + } + err = sess.Find(&watches) return } diff --git a/models/issue_watch_test.go b/models/issue_watch_test.go index 90140591b..762b1486c 100644 --- a/models/issue_watch_test.go +++ b/models/issue_watch_test.go @@ -42,22 +42,22 @@ func TestGetIssueWatch(t *testing.T) { func TestGetIssueWatchers(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - iws, err := GetIssueWatchers(1) + iws, err := GetIssueWatchers(1, ListOptions{}) assert.NoError(t, err) // Watcher is inactive, thus 0 assert.Len(t, iws, 0) - iws, err = GetIssueWatchers(2) + iws, err = GetIssueWatchers(2, ListOptions{}) assert.NoError(t, err) // Watcher is explicit not watching assert.Len(t, iws, 0) - iws, err = GetIssueWatchers(5) + iws, err = GetIssueWatchers(5, ListOptions{}) assert.NoError(t, err) // Issue has no Watchers assert.Len(t, iws, 0) - iws, err = GetIssueWatchers(7) + iws, err = GetIssueWatchers(7, ListOptions{}) assert.NoError(t, err) // Issue has one watcher assert.Len(t, iws, 1) diff --git a/models/list_options.go b/models/list_options.go new file mode 100644 index 000000000..91c4298c8 --- /dev/null +++ b/models/list_options.go @@ -0,0 +1,44 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +import ( + "code.gitea.io/gitea/modules/setting" + + "xorm.io/xorm" +) + +// ListOptions options to paginate results +type ListOptions struct { + PageSize int + Page int +} + +func (opts ListOptions) getPaginatedSession() *xorm.Session { + opts.setDefaultValues() + + return x.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) +} + +func (opts ListOptions) setSessionPagination(sess *xorm.Session) *xorm.Session { + opts.setDefaultValues() + + return sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) +} + +func (opts ListOptions) setEnginePagination(e Engine) Engine { + opts.setDefaultValues() + + return e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) +} + +func (opts ListOptions) setDefaultValues() { + if opts.PageSize <= 0 || opts.PageSize > setting.API.MaxResponseItems { + opts.PageSize = setting.API.MaxResponseItems + } + if opts.Page <= 0 { + opts.Page = 1 + } +} diff --git a/models/models.go b/models/models.go index 74680d847..239a9cf28 100644 --- a/models/models.go +++ b/models/models.go @@ -44,6 +44,8 @@ type Engine interface { SQL(interface{}, ...interface{}) *xorm.Session Where(interface{}, ...interface{}) *xorm.Session Asc(colNames ...string) *xorm.Session + Limit(limit int, start ...int) *xorm.Session + SumInt(bean interface{}, columnName string) (res int64, err error) } var ( diff --git a/models/notification.go b/models/notification.go index 403c53243..e7217a6e0 100644 --- a/models/notification.go +++ b/models/notification.go @@ -68,6 +68,7 @@ type Notification struct { // FindNotificationOptions represent the filters for notifications. If an ID is 0 it will be ignored. type FindNotificationOptions struct { + ListOptions UserID int64 RepoID int64 IssueID int64 @@ -102,7 +103,7 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond { // ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required func (opts *FindNotificationOptions) ToSession(e Engine) *xorm.Session { - return e.Where(opts.ToCond()) + return opts.setSessionPagination(e.Where(opts.ToCond())) } func getNotifications(e Engine, options FindNotificationOptions) (nl NotificationList, err error) { @@ -132,7 +133,7 @@ func CreateOrUpdateIssueNotifications(issueID, commentID int64, notificationAuth } func createOrUpdateIssueNotifications(e Engine, issueID, commentID int64, notificationAuthorID int64) error { - issueWatches, err := getIssueWatchers(e, issueID) + issueWatches, err := getIssueWatchers(e, issueID, ListOptions{}) if err != nil { return err } diff --git a/models/org.go b/models/org.go index d79c0db84..176a51ef9 100644 --- a/models/org.go +++ b/models/org.go @@ -62,14 +62,18 @@ func (org *User) getTeams(e Engine) error { Find(&org.Teams) } -// GetTeams returns all teams that belong to organization. -func (org *User) GetTeams() error { +// GetTeams returns paginated teams that belong to organization. +func (org *User) GetTeams(opts *SearchTeamOptions) error { + if opts.Page != 0 { + return org.getTeams(opts.getPaginatedSession()) + } + return org.getTeams(x) } // GetMembers returns all members of organization. func (org *User) GetMembers() (err error) { - org.Members, org.MembersIsPublic, err = FindOrgMembers(FindOrgMembersOpts{ + org.Members, org.MembersIsPublic, err = FindOrgMembers(&FindOrgMembersOpts{ OrgID: org.ID, }) return @@ -77,10 +81,9 @@ func (org *User) GetMembers() (err error) { // FindOrgMembersOpts represensts find org members condtions type FindOrgMembersOpts struct { + ListOptions OrgID int64 PublicOnly bool - Start int - Limit int } // CountOrgMembers counts the organization's members @@ -93,8 +96,8 @@ func CountOrgMembers(opts FindOrgMembersOpts) (int64, error) { } // FindOrgMembers loads organization members according conditions -func FindOrgMembers(opts FindOrgMembersOpts) (UserList, map[int64]bool, error) { - ous, err := GetOrgUsersByOrgID(opts.OrgID, opts.PublicOnly, opts.Start, opts.Limit) +func FindOrgMembers(opts *FindOrgMembersOpts) (UserList, map[int64]bool, error) { + ous, err := GetOrgUsersByOrgID(opts) if err != nil { return nil, nil, err } @@ -479,15 +482,20 @@ func GetOrgsCanCreateRepoByUserID(userID int64) ([]*User, error) { } // GetOrgUsersByUserID returns all organization-user relations by user ID. -func GetOrgUsersByUserID(uid int64, all bool) ([]*OrgUser, error) { +func GetOrgUsersByUserID(uid int64, opts *SearchOrganizationsOptions) ([]*OrgUser, error) { ous := make([]*OrgUser, 0, 10) sess := x. Join("LEFT", "`user`", "`org_user`.org_id=`user`.id"). Where("`org_user`.uid=?", uid) - if !all { + if !opts.All { // Only show public organizations sess.And("is_public=?", true) } + + if opts.PageSize != 0 { + sess = opts.setSessionPagination(sess) + } + err := sess. Asc("`user`.name"). Find(&ous) @@ -495,21 +503,24 @@ func GetOrgUsersByUserID(uid int64, all bool) ([]*OrgUser, error) { } // GetOrgUsersByOrgID returns all organization-user relations by organization ID. -func GetOrgUsersByOrgID(orgID int64, publicOnly bool, start, limit int) ([]*OrgUser, error) { - return getOrgUsersByOrgID(x, orgID, publicOnly, start, limit) +func GetOrgUsersByOrgID(opts *FindOrgMembersOpts) ([]*OrgUser, error) { + return getOrgUsersByOrgID(x, opts) } -func getOrgUsersByOrgID(e Engine, orgID int64, publicOnly bool, start, limit int) ([]*OrgUser, error) { - ous := make([]*OrgUser, 0, 10) - sess := e.Where("org_id=?", orgID) - if publicOnly { +func getOrgUsersByOrgID(e Engine, opts *FindOrgMembersOpts) ([]*OrgUser, error) { + sess := e.Where("org_id=?", opts.OrgID) + if opts.PublicOnly { sess.And("is_public = ?", true) } - if limit > 0 { - sess.Limit(limit, start) + if opts.ListOptions.PageSize > 0 { + sess = opts.setSessionPagination(sess) + + ous := make([]*OrgUser, 0, opts.PageSize) + return ous, sess.Find(&ous) } - err := sess.Find(&ous) - return ous, err + + var ous []*OrgUser + return ous, sess.Find(&ous) } // ChangeOrgUserStatus changes public or private membership status. diff --git a/models/org_team.go b/models/org_team.go index 0c0a1e7b7..214790703 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -39,12 +39,16 @@ type Team struct { // SearchTeamOptions holds the search options type SearchTeamOptions struct { + ListOptions UserID int64 Keyword string OrgID int64 IncludeDesc bool - PageSize int - Page int +} + +// SearchMembersOptions holds the search options +type SearchMembersOptions struct { + ListOptions } // SearchTeam search for teams. Caller is responsible to check permissions. @@ -160,9 +164,13 @@ func (t *Team) getRepositories(e Engine) error { Find(&t.Repos) } -// GetRepositories returns all repositories in team of organization. -func (t *Team) GetRepositories() error { - return t.getRepositories(x) +// GetRepositories returns paginated repositories in team of organization. +func (t *Team) GetRepositories(opts *SearchTeamOptions) error { + if opts.Page == 0 { + return t.getRepositories(x) + } + + return t.getRepositories(opts.getPaginatedSession()) } func (t *Team) getMembers(e Engine) (err error) { @@ -170,9 +178,13 @@ func (t *Team) getMembers(e Engine) (err error) { return err } -// GetMembers returns all members in team of organization. -func (t *Team) GetMembers() (err error) { - return t.getMembers(x) +// GetMembers returns paginated members in team of organization. +func (t *Team) GetMembers(opts *SearchMembersOptions) (err error) { + if opts.Page == 0 { + return t.getMembers(x) + } + + return t.getMembers(opts.getPaginatedSession()) } // AddMember adds new membership of the team to the organization, @@ -642,7 +654,7 @@ func UpdateTeam(t *Team, authChanged bool, includeAllChanged bool) (err error) { // DeleteTeam deletes given team. // It's caller's responsibility to assign organization ID. func DeleteTeam(t *Team) error { - if err := t.GetRepositories(); err != nil { + if err := t.GetRepositories(&SearchTeamOptions{}); err != nil { return err } @@ -747,11 +759,14 @@ func GetTeamMembers(teamID int64) ([]*User, error) { return getTeamMembers(x, teamID) } -func getUserTeams(e Engine, userID int64) (teams []*Team, err error) { - return teams, e. +func getUserTeams(e Engine, userID int64, listOptions ListOptions) (teams []*Team, err error) { + sess := e. Join("INNER", "team_user", "team_user.team_id = team.id"). - Where("team_user.uid=?", userID). - Find(&teams) + Where("team_user.uid=?", userID) + if listOptions.Page != 0 { + sess = listOptions.setSessionPagination(sess) + } + return teams, sess.Find(&teams) } func getUserOrgTeams(e Engine, orgID, userID int64) (teams []*Team, err error) { @@ -778,8 +793,8 @@ func GetUserOrgTeams(orgID, userID int64) ([]*Team, error) { } // GetUserTeams returns all teams that user belongs across all organizations. -func GetUserTeams(userID int64) ([]*Team, error) { - return getUserTeams(x, userID) +func GetUserTeams(userID int64, listOptions ListOptions) ([]*Team, error) { + return getUserTeams(x, userID, listOptions) } // AddTeamMember adds new membership of given team to given organization, @@ -795,7 +810,7 @@ func AddTeamMember(team *Team, userID int64) error { } // Get team and its repositories. - if err := team.GetRepositories(); err != nil { + if err := team.GetRepositories(&SearchTeamOptions{}); err != nil { return err } diff --git a/models/org_team_test.go b/models/org_team_test.go index 249e50b07..41040651b 100644 --- a/models/org_team_test.go +++ b/models/org_team_test.go @@ -40,7 +40,7 @@ func TestTeam_GetRepositories(t *testing.T) { test := func(teamID int64) { team := AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team) - assert.NoError(t, team.GetRepositories()) + assert.NoError(t, team.GetRepositories(&SearchTeamOptions{})) assert.Len(t, team.Repos, team.NumRepos) for _, repo := range team.Repos { AssertExistsAndLoadBean(t, &TeamRepo{TeamID: teamID, RepoID: repo.ID}) @@ -55,7 +55,7 @@ func TestTeam_GetMembers(t *testing.T) { test := func(teamID int64) { team := AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team) - assert.NoError(t, team.GetMembers()) + assert.NoError(t, team.GetMembers(&SearchMembersOptions{})) assert.Len(t, team.Members, team.NumMembers) for _, member := range team.Members { AssertExistsAndLoadBean(t, &TeamUser{UID: member.ID, TeamID: teamID}) @@ -286,7 +286,7 @@ func TestGetTeamMembers(t *testing.T) { func TestGetUserTeams(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) test := func(userID int64) { - teams, err := GetUserTeams(userID) + teams, err := GetUserTeams(userID, ListOptions{}) assert.NoError(t, err) for _, team := range teams { AssertExistsAndLoadBean(t, &TeamUser{TeamID: team.ID, UID: userID}) diff --git a/models/org_test.go b/models/org_test.go index ac1a23991..934d5bc4a 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -86,7 +86,7 @@ func TestUser_GetOwnerTeam(t *testing.T) { func TestUser_GetTeams(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) org := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) - assert.NoError(t, org.GetTeams()) + assert.NoError(t, org.GetTeams(&SearchTeamOptions{})) if assert.Len(t, org.Teams, 4) { assert.Equal(t, int64(1), org.Teams[0].ID) assert.Equal(t, int64(2), org.Teams[1].ID) @@ -367,7 +367,7 @@ func TestGetOwnedOrgsByUserIDDesc(t *testing.T) { func TestGetOrgUsersByUserID(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - orgUsers, err := GetOrgUsersByUserID(5, true) + orgUsers, err := GetOrgUsersByUserID(5, &SearchOrganizationsOptions{All: true}) assert.NoError(t, err) if assert.Len(t, orgUsers, 2) { assert.Equal(t, OrgUser{ @@ -382,12 +382,12 @@ func TestGetOrgUsersByUserID(t *testing.T) { IsPublic: false}, *orgUsers[1]) } - publicOrgUsers, err := GetOrgUsersByUserID(5, false) + publicOrgUsers, err := GetOrgUsersByUserID(5, &SearchOrganizationsOptions{All: false}) assert.NoError(t, err) assert.Len(t, publicOrgUsers, 1) assert.Equal(t, *orgUsers[0], *publicOrgUsers[0]) - orgUsers, err = GetOrgUsersByUserID(1, true) + orgUsers, err = GetOrgUsersByUserID(1, &SearchOrganizationsOptions{All: true}) assert.NoError(t, err) assert.Len(t, orgUsers, 0) } @@ -395,7 +395,11 @@ func TestGetOrgUsersByUserID(t *testing.T) { func TestGetOrgUsersByOrgID(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - orgUsers, err := GetOrgUsersByOrgID(3, false, 0, 0) + orgUsers, err := GetOrgUsersByOrgID(&FindOrgMembersOpts{ + ListOptions: ListOptions{}, + OrgID: 3, + PublicOnly: false, + }) assert.NoError(t, err) if assert.Len(t, orgUsers, 3) { assert.Equal(t, OrgUser{ @@ -410,7 +414,11 @@ func TestGetOrgUsersByOrgID(t *testing.T) { IsPublic: false}, *orgUsers[1]) } - orgUsers, err = GetOrgUsersByOrgID(NonexistentID, false, 0, 0) + orgUsers, err = GetOrgUsersByOrgID(&FindOrgMembersOpts{ + ListOptions: ListOptions{}, + OrgID: NonexistentID, + PublicOnly: false, + }) assert.NoError(t, err) assert.Len(t, orgUsers, 0) } diff --git a/models/pull_list.go b/models/pull_list.go index 137697835..989de4689 100644 --- a/models/pull_list.go +++ b/models/pull_list.go @@ -16,7 +16,7 @@ import ( // PullRequestsOptions holds the options for PRs type PullRequestsOptions struct { - Page int + ListOptions State string SortType string Labels []string @@ -94,14 +94,14 @@ func PullRequests(baseRepoID int64, opts *PullRequestsOptions) ([]*PullRequest, return nil, maxResults, err } - prs := make([]*PullRequest, 0, ItemsPerPage) findSession, err := listPullRequestStatement(baseRepoID, opts) sortIssuesSession(findSession, opts.SortType, 0) if err != nil { log.Error("listPullRequestStatement: %v", err) return nil, maxResults, err } - findSession.Limit(ItemsPerPage, (opts.Page-1)*ItemsPerPage) + findSession = opts.setSessionPagination(findSession) + prs := make([]*PullRequest, 0, opts.PageSize) return prs, maxResults, findSession.Find(&prs) } diff --git a/models/pull_sign.go b/models/pull_sign.go index 1d3474abe..6ac215272 100644 --- a/models/pull_sign.go +++ b/models/pull_sign.go @@ -34,7 +34,7 @@ func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit st case always: break case pubkey: - keys, err := ListGPGKeys(u.ID) + keys, err := ListGPGKeys(u.ID, ListOptions{}) if err != nil { return false, "", err } diff --git a/models/pull_test.go b/models/pull_test.go index 6ceeae665..0dc3bb86d 100644 --- a/models/pull_test.go +++ b/models/pull_test.go @@ -55,7 +55,9 @@ func TestPullRequest_GetHeadRepo(t *testing.T) { func TestPullRequestsNewest(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) prs, count, err := PullRequests(1, &PullRequestsOptions{ - Page: 1, + ListOptions: ListOptions{ + Page: 1, + }, State: "open", SortType: "newest", Labels: []string{}, @@ -72,7 +74,9 @@ func TestPullRequestsNewest(t *testing.T) { func TestPullRequestsOldest(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) prs, count, err := PullRequests(1, &PullRequestsOptions{ - Page: 1, + ListOptions: ListOptions{ + Page: 1, + }, State: "open", SortType: "oldest", Labels: []string{}, diff --git a/models/release.go b/models/release.go index 46f9e8875..ebd2b6d38 100644 --- a/models/release.go +++ b/models/release.go @@ -175,6 +175,7 @@ func GetReleaseByID(id int64) (*Release, error) { // FindReleasesOptions describes the conditions to Find releases type FindReleasesOptions struct { + ListOptions IncludeDrafts bool IncludeTags bool TagNames []string @@ -197,17 +198,17 @@ func (opts *FindReleasesOptions) toConds(repoID int64) builder.Cond { } // GetReleasesByRepoID returns a list of releases of repository. -func GetReleasesByRepoID(repoID int64, opts FindReleasesOptions, page, pageSize int) (rels []*Release, err error) { - if page <= 0 { - page = 1 +func GetReleasesByRepoID(repoID int64, opts FindReleasesOptions) ([]*Release, error) { + sess := x. + Desc("created_unix", "id"). + Where(opts.toConds(repoID)) + + if opts.PageSize != 0 { + sess = opts.setSessionPagination(sess) } - err = x. - Desc("created_unix", "id"). - Limit(pageSize, (page-1)*pageSize). - Where(opts.toConds(repoID)). - Find(&rels) - return rels, err + rels := make([]*Release, 0, opts.PageSize) + return rels, sess.Find(&rels) } // GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames. diff --git a/models/repo.go b/models/repo.go index ecff8482a..aa7639e4b 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1024,7 +1024,7 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error // Give access to all members in teams with access to all repositories. if u.IsOrganization() { - if err := u.GetTeams(); err != nil { + if err := u.GetTeams(&SearchTeamOptions{}); err != nil { return fmt.Errorf("GetTeams: %v", err) } for _, t := range u.Teams { @@ -1141,7 +1141,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error } // Remove redundant collaborators. - collaborators, err := repo.getCollaborators(sess) + collaborators, err := repo.getCollaborators(sess, ListOptions{}) if err != nil { return fmt.Errorf("getCollaborators: %v", err) } @@ -1171,7 +1171,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error } if newOwner.IsOrganization() { - if err := newOwner.GetTeams(); err != nil { + if err := newOwner.GetTeams(&SearchTeamOptions{}); err != nil { return fmt.Errorf("GetTeams: %v", err) } for _, t := range newOwner.Teams { @@ -1422,7 +1422,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error { return err } if org.IsOrganization() { - if err = org.GetTeams(); err != nil { + if err = org.GetTeams(&SearchTeamOptions{}); err != nil { return err } } @@ -1442,7 +1442,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error { } // Delete Deploy Keys - deployKeys, err := listDeployKeys(sess, repo.ID) + deployKeys, err := listDeployKeys(sess, repo.ID, ListOptions{}) if err != nil { return fmt.Errorf("listDeployKeys: %v", err) } @@ -1701,25 +1701,22 @@ func GetRepositoriesMapByIDs(ids []int64) (map[int64]*Repository, error) { } // GetUserRepositories returns a list of repositories of given user. -func GetUserRepositories(userID int64, private bool, page, pageSize int, orderBy string) ([]*Repository, error) { - if len(orderBy) == 0 { - orderBy = "updated_unix DESC" +func GetUserRepositories(opts *SearchRepoOptions) ([]*Repository, error) { + if len(opts.OrderBy) == 0 { + opts.OrderBy = "updated_unix DESC" } sess := x. - Where("owner_id = ?", userID). - OrderBy(orderBy) - if !private { + Where("owner_id = ?", opts.Actor.ID). + OrderBy(opts.OrderBy.String()) + if !opts.Private { sess.And("is_private=?", false) } - if page <= 0 { - page = 1 - } - sess.Limit(pageSize, (page-1)*pageSize) + sess = opts.setSessionPagination(sess) - repos := make([]*Repository, 0, pageSize) - return repos, sess.Find(&repos) + repos := make([]*Repository, 0, opts.PageSize) + return repos, opts.setSessionPagination(sess).Find(&repos) } // GetUserMirrorRepositories returns a list of mirror repositories of given user. @@ -2029,9 +2026,15 @@ func CopyLFS(ctx DBContext, newRepo, oldRepo *Repository) error { } // GetForks returns all the forks of the repository -func (repo *Repository) GetForks() ([]*Repository, error) { - forks := make([]*Repository, 0, repo.NumForks) - return forks, x.Find(&forks, &Repository{ForkID: repo.ID}) +func (repo *Repository) GetForks(listOptions ListOptions) ([]*Repository, error) { + if listOptions.Page == 0 { + forks := make([]*Repository, 0, repo.NumForks) + return forks, x.Find(&forks, &Repository{ForkID: repo.ID}) + } + + sess := listOptions.getPaginatedSession() + forks := make([]*Repository, 0, listOptions.PageSize) + return forks, sess.Find(&forks, &Repository{ForkID: repo.ID}) } // GetUserFork return user forked repository from this repository, if not forked return nil diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index f04507f3e..8c6ef3623 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -1,4 +1,5 @@ // Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -52,8 +53,15 @@ func (repo *Repository) AddCollaborator(u *User) error { return sess.Commit() } -func (repo *Repository) getCollaborations(e Engine) ([]*Collaboration, error) { - var collaborations []*Collaboration +func (repo *Repository) getCollaborations(e Engine, listOptions ListOptions) ([]*Collaboration, error) { + if listOptions.Page == 0 { + collaborations := make([]*Collaboration, 0, 8) + return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID}) + } + + e = listOptions.setEnginePagination(e) + + collaborations := make([]*Collaboration, 0, listOptions.PageSize) return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID}) } @@ -63,8 +71,8 @@ type Collaborator struct { Collaboration *Collaboration } -func (repo *Repository) getCollaborators(e Engine) ([]*Collaborator, error) { - collaborations, err := repo.getCollaborations(e) +func (repo *Repository) getCollaborators(e Engine, listOptions ListOptions) ([]*Collaborator, error) { + collaborations, err := repo.getCollaborations(e, listOptions) if err != nil { return nil, fmt.Errorf("getCollaborations: %v", err) } @@ -84,8 +92,8 @@ func (repo *Repository) getCollaborators(e Engine) ([]*Collaborator, error) { } // GetCollaborators returns the collaborators for a repository -func (repo *Repository) GetCollaborators() ([]*Collaborator, error) { - return repo.getCollaborators(x) +func (repo *Repository) GetCollaborators(listOptions ListOptions) ([]*Collaborator, error) { + return repo.getCollaborators(x, listOptions) } func (repo *Repository) getCollaboration(e Engine, uid int64) (*Collaboration, error) { diff --git a/models/repo_collaboration_test.go b/models/repo_collaboration_test.go index 084221246..7bae27bce 100644 --- a/models/repo_collaboration_test.go +++ b/models/repo_collaboration_test.go @@ -29,7 +29,7 @@ func TestRepository_GetCollaborators(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) test := func(repoID int64) { repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository) - collaborators, err := repo.GetCollaborators() + collaborators, err := repo.GetCollaborators(ListOptions{}) assert.NoError(t, err) expectedLen, err := x.Count(&Collaboration{RepoID: repoID}) assert.NoError(t, err) diff --git a/models/repo_generate.go b/models/repo_generate.go index b3230acd0..480683cd4 100644 --- a/models/repo_generate.go +++ b/models/repo_generate.go @@ -110,7 +110,7 @@ func GenerateGitHooks(ctx DBContext, templateRepo, generateRepo *Repository) err // GenerateWebhooks generates webhooks from a template repository func GenerateWebhooks(ctx DBContext, templateRepo, generateRepo *Repository) error { - templateWebhooks, err := GetWebhooksByRepoID(templateRepo.ID) + templateWebhooks, err := GetWebhooksByRepoID(templateRepo.ID, ListOptions{}) if err != nil { return err } @@ -148,7 +148,7 @@ func GenerateAvatar(ctx DBContext, templateRepo, generateRepo *Repository) error // GenerateIssueLabels generates issue labels from a template repository func GenerateIssueLabels(ctx DBContext, templateRepo, generateRepo *Repository) error { - templateLabels, err := getLabelsByRepoID(ctx.e, templateRepo.ID, "") + templateLabels, err := getLabelsByRepoID(ctx.e, templateRepo.ID, "", ListOptions{}) if err != nil { return err } diff --git a/models/repo_list.go b/models/repo_list.go index 45a506698..3644b01d8 100644 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -111,6 +111,7 @@ func (repos MirrorRepositoryList) LoadAttributes() error { // SearchRepoOptions holds the search options type SearchRepoOptions struct { + ListOptions Actor *User Keyword string OwnerID int64 @@ -118,11 +119,9 @@ type SearchRepoOptions struct { OrderBy SearchOrderBy Private bool // Include private repositories in results StarredByID int64 - Page int IsProfile bool AllPublic bool // Include also all public repositories of users and public organisations AllLimited bool // Include also all public repositories of limited organisations - PageSize int // Can be smaller than or equal to setting.ExplorePagingNum // None -> include collaborative AND non-collaborative // True -> include just collaborative // False -> incude just non-collaborative diff --git a/models/repo_list_test.go b/models/repo_list_test.go index 07f84207e..ff8b7eb59 100644 --- a/models/repo_list_test.go +++ b/models/repo_list_test.go @@ -17,9 +17,11 @@ func TestSearchRepository(t *testing.T) { // test search public repository on explore page repos, count, err := SearchRepositoryByName(&SearchRepoOptions{ + ListOptions: ListOptions{ + Page: 1, + PageSize: 10, + }, Keyword: "repo_12", - Page: 1, - PageSize: 10, Collaborate: util.OptionalBoolFalse, }) @@ -30,9 +32,11 @@ func TestSearchRepository(t *testing.T) { assert.Equal(t, int64(1), count) repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + ListOptions: ListOptions{ + Page: 1, + PageSize: 10, + }, Keyword: "test_repo", - Page: 1, - PageSize: 10, Collaborate: util.OptionalBoolFalse, }) @@ -42,9 +46,11 @@ func TestSearchRepository(t *testing.T) { // test search private repository on explore page repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + ListOptions: ListOptions{ + Page: 1, + PageSize: 10, + }, Keyword: "repo_13", - Page: 1, - PageSize: 10, Private: true, Collaborate: util.OptionalBoolFalse, }) @@ -56,9 +62,11 @@ func TestSearchRepository(t *testing.T) { assert.Equal(t, int64(1), count) repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + ListOptions: ListOptions{ + Page: 1, + PageSize: 10, + }, Keyword: "test_repo", - Page: 1, - PageSize: 10, Private: true, Collaborate: util.OptionalBoolFalse, }) @@ -76,9 +84,11 @@ func TestSearchRepository(t *testing.T) { // Test search within description repos, count, err = SearchRepository(&SearchRepoOptions{ + ListOptions: ListOptions{ + Page: 1, + PageSize: 10, + }, Keyword: "description_14", - Page: 1, - PageSize: 10, Collaborate: util.OptionalBoolFalse, IncludeDescription: true, }) @@ -91,9 +101,11 @@ func TestSearchRepository(t *testing.T) { // Test NOT search within description repos, count, err = SearchRepository(&SearchRepoOptions{ + ListOptions: ListOptions{ + Page: 1, + PageSize: 10, + }, Keyword: "description_14", - Page: 1, - PageSize: 10, Collaborate: util.OptionalBoolFalse, IncludeDescription: false, }) @@ -108,88 +120,88 @@ func TestSearchRepository(t *testing.T) { count int }{ {name: "PublicRepositoriesByName", - opts: &SearchRepoOptions{Keyword: "big_test_", PageSize: 10, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{PageSize: 10}, Collaborate: util.OptionalBoolFalse}, count: 7}, {name: "PublicAndPrivateRepositoriesByName", - opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 10, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: util.OptionalBoolFalse}, count: 14}, {name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage", - opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, count: 14}, {name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage", - opts: &SearchRepoOptions{Keyword: "big_test_", Page: 2, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, count: 14}, {name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage", - opts: &SearchRepoOptions{Keyword: "big_test_", Page: 3, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, count: 14}, {name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage", - opts: &SearchRepoOptions{Keyword: "big_test_", Page: 3, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, count: 14}, {name: "PublicRepositoriesOfUser", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: util.OptionalBoolFalse}, count: 2}, {name: "PublicRepositoriesOfUser2", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: util.OptionalBoolFalse}, count: 0}, {name: "PublicRepositoriesOfUser3", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: util.OptionalBoolFalse}, count: 2}, {name: "PublicAndPrivateRepositoriesOfUser", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: util.OptionalBoolFalse}, count: 4}, {name: "PublicAndPrivateRepositoriesOfUser2", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: util.OptionalBoolFalse}, count: 0}, {name: "PublicAndPrivateRepositoriesOfUser3", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: util.OptionalBoolFalse}, count: 4}, {name: "PublicRepositoriesOfUserIncludingCollaborative", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15}, count: 5}, {name: "PublicRepositoriesOfUser2IncludingCollaborative", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 18}, count: 1}, {name: "PublicRepositoriesOfUser3IncludingCollaborative", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 20}, count: 3}, {name: "PublicAndPrivateRepositoriesOfUserIncludingCollaborative", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true}, count: 9}, {name: "PublicAndPrivateRepositoriesOfUser2IncludingCollaborative", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Private: true}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true}, count: 4}, {name: "PublicAndPrivateRepositoriesOfUser3IncludingCollaborative", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20, Private: true}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true}, count: 7}, {name: "PublicRepositoriesOfOrganization", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: util.OptionalBoolFalse}, count: 1}, {name: "PublicAndPrivateRepositoriesOfOrganization", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, Private: true, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: util.OptionalBoolFalse}, count: 2}, {name: "AllPublic/PublicRepositoriesByName", - opts: &SearchRepoOptions{Keyword: "big_test_", PageSize: 10, AllPublic: true, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{PageSize: 10}, AllPublic: true, Collaborate: util.OptionalBoolFalse}, count: 7}, {name: "AllPublic/PublicAndPrivateRepositoriesByName", - opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 10, Private: true, AllPublic: true, Collaborate: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: util.OptionalBoolFalse}, count: 14}, {name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse}, count: 25}, {name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse}, count: 30}, {name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName", - opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, + opts: &SearchRepoOptions{Keyword: "test", ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true}, count: 15}, {name: "AllPublic/PublicAndPrivateRepositoriesOfUser2IncludingCollaborativeByName", - opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 18, Private: true, AllPublic: true}, + opts: &SearchRepoOptions{Keyword: "test", ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, AllPublic: true}, count: 13}, {name: "AllPublic/PublicRepositoriesOfOrganization", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse}, count: 25}, {name: "AllTemplates", - opts: &SearchRepoOptions{Page: 1, PageSize: 10, Template: util.OptionalBoolTrue}, + opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, Template: util.OptionalBoolTrue}, count: 2}, } diff --git a/models/repo_sign.go b/models/repo_sign.go index 64f70ac7b..8913620fa 100644 --- a/models/repo_sign.go +++ b/models/repo_sign.go @@ -110,7 +110,7 @@ func SignInitialCommit(repoPath string, u *User) (bool, string, error) { case always: break case pubkey: - keys, err := ListGPGKeys(u.ID) + keys, err := ListGPGKeys(u.ID, ListOptions{}) if err != nil { return false, "", err } @@ -145,7 +145,7 @@ func (repo *Repository) SignWikiCommit(u *User) (bool, string, error) { case always: break case pubkey: - keys, err := ListGPGKeys(u.ID) + keys, err := ListGPGKeys(u.ID, ListOptions{}) if err != nil { return false, "", err } @@ -197,7 +197,7 @@ func (repo *Repository) SignCRUDAction(u *User, tmpBasePath, parentCommit string case always: break case pubkey: - keys, err := ListGPGKeys(u.ID) + keys, err := ListGPGKeys(u.ID, ListOptions{}) if err != nil { return false, "", err } diff --git a/models/repo_watch.go b/models/repo_watch.go index 9b3659dbf..a9d56eff0 100644 --- a/models/repo_watch.go +++ b/models/repo_watch.go @@ -153,14 +153,18 @@ func GetRepoWatchersIDs(repoID int64) ([]int64, error) { } // GetWatchers returns range of users watching given repository. -func (repo *Repository) GetWatchers(page int) ([]*User, error) { - users := make([]*User, 0, ItemsPerPage) +func (repo *Repository) GetWatchers(opts ListOptions) ([]*User, error) { sess := x.Where("watch.repo_id=?", repo.ID). Join("LEFT", "watch", "`user`.id=`watch`.user_id"). And("`watch`.mode<>?", RepoWatchModeDont) - if page > 0 { - sess = sess.Limit(ItemsPerPage, (page-1)*ItemsPerPage) + if opts.Page > 0 { + sess = opts.setSessionPagination(sess) + users := make([]*User, 0, opts.PageSize) + + return users, sess.Find(&users) } + + users := make([]*User, 0, 8) return users, sess.Find(&users) } diff --git a/models/repo_watch_test.go b/models/repo_watch_test.go index c3d40ec91..e1bbc4023 100644 --- a/models/repo_watch_test.go +++ b/models/repo_watch_test.go @@ -59,7 +59,7 @@ func TestRepository_GetWatchers(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) - watchers, err := repo.GetWatchers(1) + watchers, err := repo.GetWatchers(ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, repo.NumWatches) for _, watcher := range watchers { @@ -67,7 +67,7 @@ func TestRepository_GetWatchers(t *testing.T) { } repo = AssertExistsAndLoadBean(t, &Repository{ID: 9}).(*Repository) - watchers, err = repo.GetWatchers(1) + watchers, err = repo.GetWatchers(ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, 0) } @@ -113,7 +113,7 @@ func TestWatchIfAuto(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) - watchers, err := repo.GetWatchers(1) + watchers, err := repo.GetWatchers(ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, repo.NumWatches) @@ -123,13 +123,13 @@ func TestWatchIfAuto(t *testing.T) { // Must not add watch assert.NoError(t, WatchIfAuto(8, 1, true)) - watchers, err = repo.GetWatchers(1) + watchers, err = repo.GetWatchers(ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, prevCount) // Should not add watch assert.NoError(t, WatchIfAuto(10, 1, true)) - watchers, err = repo.GetWatchers(1) + watchers, err = repo.GetWatchers(ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, prevCount) @@ -137,31 +137,31 @@ func TestWatchIfAuto(t *testing.T) { // Must not add watch assert.NoError(t, WatchIfAuto(8, 1, true)) - watchers, err = repo.GetWatchers(1) + watchers, err = repo.GetWatchers(ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, prevCount) // Should not add watch assert.NoError(t, WatchIfAuto(12, 1, false)) - watchers, err = repo.GetWatchers(1) + watchers, err = repo.GetWatchers(ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, prevCount) // Should add watch assert.NoError(t, WatchIfAuto(12, 1, true)) - watchers, err = repo.GetWatchers(1) + watchers, err = repo.GetWatchers(ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, prevCount+1) // Should remove watch, inhibit from adding auto assert.NoError(t, WatchRepo(12, 1, false)) - watchers, err = repo.GetWatchers(1) + watchers, err = repo.GetWatchers(ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, prevCount) // Must not add watch assert.NoError(t, WatchIfAuto(12, 1, true)) - watchers, err = repo.GetWatchers(1) + watchers, err = repo.GetWatchers(ListOptions{Page: 1}) assert.NoError(t, err) assert.Len(t, watchers, prevCount) } diff --git a/models/ssh_key.go b/models/ssh_key.go index f441c3e42..d3e9de577 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -567,11 +567,17 @@ func SearchPublicKey(uid int64, fingerprint string) ([]*PublicKey, error) { } // ListPublicKeys returns a list of public keys belongs to given user. -func ListPublicKeys(uid int64) ([]*PublicKey, error) { +func ListPublicKeys(uid int64, listOptions ListOptions) ([]*PublicKey, error) { + sess := x.Where("owner_id = ?", uid) + if listOptions.Page != 0 { + sess = listOptions.setSessionPagination(sess) + + keys := make([]*PublicKey, 0, listOptions.PageSize) + return keys, sess.Find(&keys) + } + keys := make([]*PublicKey, 0, 5) - return keys, x. - Where("owner_id = ?", uid). - Find(&keys) + return keys, sess.Find(&keys) } // ListPublicLdapSSHKeys returns a list of synchronized public ldap ssh keys belongs to given user and login source. @@ -970,15 +976,21 @@ func deleteDeployKey(sess Engine, doer *User, id int64) error { } // ListDeployKeys returns all deploy keys by given repository ID. -func ListDeployKeys(repoID int64) ([]*DeployKey, error) { - return listDeployKeys(x, repoID) +func ListDeployKeys(repoID int64, listOptions ListOptions) ([]*DeployKey, error) { + return listDeployKeys(x, repoID, listOptions) } -func listDeployKeys(e Engine, repoID int64) ([]*DeployKey, error) { +func listDeployKeys(e Engine, repoID int64, listOptions ListOptions) ([]*DeployKey, error) { + sess := e.Where("repo_id = ?", repoID) + if listOptions.Page != 0 { + sess = listOptions.setSessionPagination(sess) + + keys := make([]*DeployKey, 0, listOptions.PageSize) + return keys, sess.Find(&keys) + } + keys := make([]*DeployKey, 0, 5) - return keys, e. - Where("repo_id = ?", repoID). - Find(&keys) + return keys, sess.Find(&keys) } // SearchDeployKeys returns a list of deploy keys matching the provided arguments. diff --git a/models/star.go b/models/star.go index 18d28b558..4e84a6e4d 100644 --- a/models/star.go +++ b/models/star.go @@ -64,13 +64,17 @@ func isStaring(e Engine, userID, repoID int64) bool { } // GetStargazers returns the users that starred the repo. -func (repo *Repository) GetStargazers(page int) ([]*User, error) { - users := make([]*User, 0, ItemsPerPage) +func (repo *Repository) GetStargazers(opts ListOptions) ([]*User, error) { sess := x.Where("star.repo_id = ?", repo.ID). Join("LEFT", "star", "`user`.id = star.uid") - if page > 0 { - sess = sess.Limit(ItemsPerPage, (page-1)*ItemsPerPage) + if opts.Page > 0 { + sess = opts.setSessionPagination(sess) + + users := make([]*User, 0, opts.PageSize) + return users, sess.Find(&users) } + + users := make([]*User, 0, 8) return users, sess.Find(&users) } diff --git a/models/star_test.go b/models/star_test.go index 55b4a1cfa..962923800 100644 --- a/models/star_test.go +++ b/models/star_test.go @@ -33,7 +33,7 @@ func TestRepository_GetStargazers(t *testing.T) { // repo with stargazers assert.NoError(t, PrepareTestDatabase()) repo := AssertExistsAndLoadBean(t, &Repository{ID: 4}).(*Repository) - gazers, err := repo.GetStargazers(0) + gazers, err := repo.GetStargazers(ListOptions{Page: 0}) assert.NoError(t, err) if assert.Len(t, gazers, 1) { assert.Equal(t, int64(2), gazers[0].ID) @@ -44,7 +44,7 @@ func TestRepository_GetStargazers2(t *testing.T) { // repo with stargazers assert.NoError(t, PrepareTestDatabase()) repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) - gazers, err := repo.GetStargazers(0) + gazers, err := repo.GetStargazers(ListOptions{Page: 0}) assert.NoError(t, err) assert.Len(t, gazers, 0) } diff --git a/models/token.go b/models/token.go index 8bd20a691..ce3218d8d 100644 --- a/models/token.go +++ b/models/token.go @@ -78,12 +78,20 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) { } // ListAccessTokens returns a list of access tokens belongs to given user. -func ListAccessTokens(uid int64) ([]*AccessToken, error) { - tokens := make([]*AccessToken, 0, 5) - return tokens, x. +func ListAccessTokens(uid int64, listOptions ListOptions) ([]*AccessToken, error) { + sess := x. Where("uid=?", uid). - Desc("id"). - Find(&tokens) + Desc("id") + + if listOptions.Page == 0 { + sess = listOptions.setSessionPagination(sess) + + tokens := make([]*AccessToken, 0, listOptions.PageSize) + return tokens, sess.Find(&tokens) + } + + tokens := make([]*AccessToken, 0, 5) + return tokens, sess.Find(&tokens) } // UpdateAccessToken updates information of access token. diff --git a/models/token_test.go b/models/token_test.go index a74de8f81..45f50a1b8 100644 --- a/models/token_test.go +++ b/models/token_test.go @@ -47,7 +47,7 @@ func TestGetAccessTokenBySHA(t *testing.T) { func TestListAccessTokens(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - tokens, err := ListAccessTokens(1) + tokens, err := ListAccessTokens(1, ListOptions{}) assert.NoError(t, err) if assert.Len(t, tokens, 2) { assert.Equal(t, int64(1), tokens[0].UID) @@ -56,14 +56,14 @@ func TestListAccessTokens(t *testing.T) { assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token B") } - tokens, err = ListAccessTokens(2) + tokens, err = ListAccessTokens(2, ListOptions{}) assert.NoError(t, err) if assert.Len(t, tokens, 1) { assert.Equal(t, int64(2), tokens[0].UID) assert.Equal(t, "Token A", tokens[0].Name) } - tokens, err = ListAccessTokens(100) + tokens, err = ListAccessTokens(100, ListOptions{}) assert.NoError(t, err) assert.Empty(t, tokens) } diff --git a/models/topic.go b/models/topic.go index e4fda03fc..1a76c4915 100644 --- a/models/topic.go +++ b/models/topic.go @@ -147,10 +147,9 @@ func removeTopicFromRepo(repoID int64, topic *Topic, e Engine) error { // FindTopicOptions represents the options when fdin topics type FindTopicOptions struct { + ListOptions RepoID int64 Keyword string - Limit int - Page int } func (opts *FindTopicOptions) toConds() builder.Cond { @@ -172,8 +171,8 @@ func FindTopics(opts *FindTopicOptions) (topics []*Topic, err error) { if opts.RepoID > 0 { sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") } - if opts.Limit > 0 { - sess.Limit(opts.Limit, opts.Page*opts.Limit) + if opts.PageSize != 0 && opts.Page != 0 { + sess = opts.setSessionPagination(sess) } return topics, sess.Desc("topic.repo_count").Find(&topics) } diff --git a/models/topic_test.go b/models/topic_test.go index c173c7bf2..b6ef8f565 100644 --- a/models/topic_test.go +++ b/models/topic_test.go @@ -22,7 +22,7 @@ func TestAddTopic(t *testing.T) { assert.EqualValues(t, totalNrOfTopics, len(topics)) topics, err = FindTopics(&FindTopicOptions{ - Limit: 2, + ListOptions: ListOptions{Page: 1, PageSize: 2}, }) assert.NoError(t, err) assert.EqualValues(t, 2, len(topics)) diff --git a/models/user.go b/models/user.go index c2631d5dd..d10d2cbff 100644 --- a/models/user.go +++ b/models/user.go @@ -163,6 +163,12 @@ type User struct { Theme string `xorm:"NOT NULL DEFAULT ''"` } +// SearchOrganizationsOptions options to filter organizations +type SearchOrganizationsOptions struct { + ListOptions + All bool +} + // ColorFormat writes a colored string to identify this struct func (u *User) ColorFormat(s fmt.State) { log.ColorFprintf(s, "%d:%s", @@ -430,12 +436,19 @@ func (u *User) AvatarLink() string { } // GetFollowers returns range of user's followers. -func (u *User) GetFollowers(page int) ([]*User, error) { - users := make([]*User, 0, ItemsPerPage) +func (u *User) GetFollowers(listOptions ListOptions) ([]*User, error) { sess := x. - Limit(ItemsPerPage, (page-1)*ItemsPerPage). Where("follow.follow_id=?", u.ID). Join("LEFT", "follow", "`user`.id=follow.user_id") + + if listOptions.Page != 0 { + sess = listOptions.setSessionPagination(sess) + + users := make([]*User, 0, listOptions.PageSize) + return users, sess.Find(&users) + } + + users := make([]*User, 0, 8) return users, sess.Find(&users) } @@ -445,12 +458,19 @@ func (u *User) IsFollowing(followID int64) bool { } // GetFollowing returns range of user's following. -func (u *User) GetFollowing(page int) ([]*User, error) { - users := make([]*User, 0, ItemsPerPage) +func (u *User) GetFollowing(listOptions ListOptions) ([]*User, error) { sess := x. - Limit(ItemsPerPage, (page-1)*ItemsPerPage). Where("follow.user_id=?", u.ID). Join("LEFT", "follow", "`user`.id=follow.follow_id") + + if listOptions.Page != 0 { + sess = listOptions.setSessionPagination(sess) + + users := make([]*User, 0, listOptions.PageSize) + return users, sess.Find(&users) + } + + users := make([]*User, 0, 8) return users, sess.Find(&users) } @@ -616,8 +636,8 @@ func (u *User) GetOrganizationCount() (int64, error) { } // GetRepositories returns repositories that user owns, including private repositories. -func (u *User) GetRepositories(page, pageSize int) (err error) { - u.Repos, err = GetUserRepositories(u.ID, true, page, pageSize, "") +func (u *User) GetRepositories(listOpts ListOptions) (err error) { + u.Repos, err = GetUserRepositories(&SearchRepoOptions{Actor: u, Private: true, ListOptions: listOpts}) return err } @@ -682,9 +702,9 @@ func (u *User) GetOwnedOrganizations() (err error) { return err } -// GetOrganizations returns all organizations that user belongs to. -func (u *User) GetOrganizations(all bool) error { - ous, err := GetOrgUsersByUserID(u.ID, all) +// GetOrganizations returns paginated organizations that user belongs to. +func (u *User) GetOrganizations(opts *SearchOrganizationsOptions) error { + ous, err := GetOrgUsersByUserID(u.ID, opts) if err != nil { return err } @@ -1477,14 +1497,13 @@ func GetUser(user *User) (bool, error) { // SearchUserOptions contains the options for searching type SearchUserOptions struct { + ListOptions Keyword string Type UserType UID int64 OrderBy SearchOrderBy - Page int Visible []structs.VisibleType Actor *User // The user doing the search - PageSize int // Can be smaller than or equal to setting.UI.ExplorePagingNum IsActive util.OptionalBool SearchByEmail bool // Search by email as well as username/full name } @@ -1552,57 +1571,56 @@ func SearchUsers(opts *SearchUserOptions) (users []*User, _ int64, _ error) { return nil, 0, fmt.Errorf("Count: %v", err) } - if opts.PageSize == 0 || opts.PageSize > setting.UI.ExplorePagingNum { - opts.PageSize = setting.UI.ExplorePagingNum - } - if opts.Page <= 0 { - opts.Page = 1 - } if len(opts.OrderBy) == 0 { opts.OrderBy = SearchOrderByAlphabetically } - sess := x.Where(cond) - if opts.PageSize > 0 { - sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - if opts.PageSize == -1 { - opts.PageSize = int(count) + sess := x.Where(cond).OrderBy(opts.OrderBy.String()) + if opts.Page != 0 { + sess = opts.setSessionPagination(sess) } users = make([]*User, 0, opts.PageSize) - return users, count, sess.OrderBy(opts.OrderBy.String()).Find(&users) + return users, count, sess.Find(&users) } // GetStarredRepos returns the repos starred by a particular user -func GetStarredRepos(userID int64, private bool) ([]*Repository, error) { +func GetStarredRepos(userID int64, private bool, listOptions ListOptions) ([]*Repository, error) { sess := x.Where("star.uid=?", userID). Join("LEFT", "star", "`repository`.id=`star`.repo_id") if !private { sess = sess.And("is_private=?", false) } - repos := make([]*Repository, 0, 10) - err := sess.Find(&repos) - if err != nil { - return nil, err + + if listOptions.Page != 0 { + sess = listOptions.setSessionPagination(sess) + + repos := make([]*Repository, 0, listOptions.PageSize) + return repos, sess.Find(&repos) } - return repos, nil + + repos := make([]*Repository, 0, 10) + return repos, sess.Find(&repos) } // GetWatchedRepos returns the repos watched by a particular user -func GetWatchedRepos(userID int64, private bool) ([]*Repository, error) { +func GetWatchedRepos(userID int64, private bool, listOptions ListOptions) ([]*Repository, error) { sess := x.Where("watch.user_id=?", userID). And("`watch`.mode<>?", RepoWatchModeDont). Join("LEFT", "watch", "`repository`.id=`watch`.repo_id") if !private { sess = sess.And("is_private=?", false) } - repos := make([]*Repository, 0, 10) - err := sess.Find(&repos) - if err != nil { - return nil, err + + if listOptions.Page != 0 { + sess = listOptions.setSessionPagination(sess) + + repos := make([]*Repository, 0, listOptions.PageSize) + return repos, sess.Find(&repos) } - return repos, nil + + repos := make([]*Repository, 0, 10) + return repos, sess.Find(&repos) } // deleteKeysMarkedForDeletion returns true if ssh keys needs update diff --git a/models/user_test.go b/models/user_test.go index 2232d5996..fa1b0048e 100644 --- a/models/user_test.go +++ b/models/user_test.go @@ -131,19 +131,19 @@ func TestSearchUsers(t *testing.T) { testSuccess(opts, expectedOrgIDs) } - testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1, PageSize: 2}, + testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 1, PageSize: 2}}, []int64{3, 6}) - testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 2, PageSize: 2}, + testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 2, PageSize: 2}}, []int64{7, 17}) - testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 3, PageSize: 2}, + testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 3, PageSize: 2}}, []int64{19, 25}) - testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 4, PageSize: 2}, + testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 4, PageSize: 2}}, []int64{26}) - testOrgSuccess(&SearchUserOptions{Page: 5, PageSize: 2}, + testOrgSuccess(&SearchUserOptions{ListOptions: ListOptions{Page: 5, PageSize: 2}}, []int64{}) // test users @@ -152,20 +152,20 @@ func TestSearchUsers(t *testing.T) { testSuccess(opts, expectedUserIDs) } - testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1}, + testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 1}}, []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29}) - testUserSuccess(&SearchUserOptions{Page: 1, IsActive: util.OptionalBoolFalse}, + testUserSuccess(&SearchUserOptions{ListOptions: ListOptions{Page: 1}, IsActive: util.OptionalBoolFalse}, []int64{9}) - testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, + testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 28, 29}) - testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, + testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) // order by name asc default - testUserSuccess(&SearchUserOptions{Keyword: "user1", Page: 1, IsActive: util.OptionalBoolTrue}, + testUserSuccess(&SearchUserOptions{Keyword: "user1", ListOptions: ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) } diff --git a/models/userlist.go b/models/userlist.go index 16ec6fee5..7e6cab50b 100644 --- a/models/userlist.go +++ b/models/userlist.go @@ -97,7 +97,7 @@ func (users UserList) loadTwoFactorStatus(e Engine) (map[int64]*TwoFactor, error //APIFormat return list of users in api format func (users UserList) APIFormat() []*api.User { - var result []*api.User + result := make([]*api.User, 0, len(users)) for _, u := range users { result = append(result, u.APIFormat()) } diff --git a/models/webhook.go b/models/webhook.go index 7eb17caaf..55f58c64a 100644 --- a/models/webhook.go +++ b/models/webhook.go @@ -274,9 +274,16 @@ func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) { } // GetWebhooksByRepoID returns all webhooks of a repository. -func GetWebhooksByRepoID(repoID int64) ([]*Webhook, error) { - webhooks := make([]*Webhook, 0, 5) - return webhooks, x.Find(&webhooks, &Webhook{RepoID: repoID}) +func GetWebhooksByRepoID(repoID int64, listOptions ListOptions) ([]*Webhook, error) { + if listOptions.Page == 0 { + webhooks := make([]*Webhook, 0, 5) + return webhooks, x.Find(&webhooks, &Webhook{RepoID: repoID}) + } + + sess := listOptions.getPaginatedSession() + webhooks := make([]*Webhook, 0, listOptions.PageSize) + + return webhooks, sess.Find(&webhooks, &Webhook{RepoID: repoID}) } // GetActiveWebhooksByOrgID returns all active webhooks for an organization. @@ -292,10 +299,16 @@ func getActiveWebhooksByOrgID(e Engine, orgID int64) (ws []*Webhook, err error) return ws, err } -// GetWebhooksByOrgID returns all webhooks for an organization. -func GetWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) { - err = x.Find(&ws, &Webhook{OrgID: orgID}) - return ws, err +// GetWebhooksByOrgID returns paginated webhooks for an organization. +func GetWebhooksByOrgID(orgID int64, listOptions ListOptions) ([]*Webhook, error) { + if listOptions.Page == 0 { + ws := make([]*Webhook, 0, 5) + return ws, x.Find(&ws, &Webhook{OrgID: orgID}) + } + + sess := listOptions.getPaginatedSession() + ws := make([]*Webhook, 0, listOptions.PageSize) + return ws, sess.Find(&ws, &Webhook{OrgID: orgID}) } // GetDefaultWebhook returns admin-default webhook by given ID. diff --git a/models/webhook_test.go b/models/webhook_test.go index 0fd9b245c..f8e423429 100644 --- a/models/webhook_test.go +++ b/models/webhook_test.go @@ -120,7 +120,7 @@ func TestGetActiveWebhooksByRepoID(t *testing.T) { func TestGetWebhooksByRepoID(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - hooks, err := GetWebhooksByRepoID(1) + hooks, err := GetWebhooksByRepoID(1, ListOptions{}) assert.NoError(t, err) if assert.Len(t, hooks, 2) { assert.Equal(t, int64(1), hooks[0].ID) @@ -140,7 +140,7 @@ func TestGetActiveWebhooksByOrgID(t *testing.T) { func TestGetWebhooksByOrgID(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - hooks, err := GetWebhooksByOrgID(3) + hooks, err := GetWebhooksByOrgID(3, ListOptions{}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(3), hooks[0].ID) diff --git a/modules/context/org.go b/modules/context/org.go index ae19aebfc..9b87fba9f 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -118,7 +119,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { // Team. if ctx.Org.IsMember { if ctx.Org.IsOwner { - if err := org.GetTeams(); err != nil { + if err := org.GetTeams(&models.SearchTeamOptions{}); err != nil { ctx.ServerError("GetTeams", err) return } diff --git a/modules/git/commit.go b/modules/git/commit.go index 9646d5606..f530d7942 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -297,8 +297,8 @@ func (c *Commit) CommitsCount() (int64, error) { } // CommitsByRange returns the specific page commits before current revision, every page's number default by CommitsRangeSize -func (c *Commit) CommitsByRange(page int) (*list.List, error) { - return c.repo.commitsByRange(c.ID, page) +func (c *Commit) CommitsByRange(page, pageSize int) (*list.List, error) { + return c.repo.commitsByRange(c.ID, page, pageSize) } // CommitsBefore returns all the commits before current revision diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index 8762b63e2..1f211aaca 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -198,9 +198,10 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) { // CommitsRangeSize the default commits range size var CommitsRangeSize = 50 -func (repo *Repository) commitsByRange(id SHA1, page int) (*list.List, error) { - stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*CommitsRangeSize), - "--max-count="+strconv.Itoa(CommitsRangeSize), prettyLogFormat).RunInDirBytes(repo.Path) +func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) (*list.List, error) { + stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize), + "--max-count="+strconv.Itoa(pageSize), prettyLogFormat).RunInDirBytes(repo.Path) + if err != nil { return nil, err } diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index 894f37a96..95bd5b8a6 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -221,8 +221,7 @@ func populateIssueIndexer(ctx context.Context) { default: } repos, _, err := models.SearchRepositoryByName(&models.SearchRepoOptions{ - Page: page, - PageSize: models.RepositoryListDefaultPageSize, + ListOptions: models.ListOptions{Page: page, PageSize: models.RepositoryListDefaultPageSize}, OrderBy: models.SearchOrderByID, Private: true, Collaborate: util.OptionalBoolFalse, diff --git a/modules/migrations/gitea_test.go b/modules/migrations/gitea_test.go index 438902f32..3f03cbab3 100644 --- a/modules/migrations/gitea_test.go +++ b/modules/migrations/gitea_test.go @@ -59,19 +59,27 @@ func TestGiteaUploadRepo(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 0, len(milestones)) - labels, err := models.GetLabelsByRepoID(repo.ID, "") + labels, err := models.GetLabelsByRepoID(repo.ID, "", models.ListOptions{}) assert.NoError(t, err) assert.EqualValues(t, 11, len(labels)) releases, err := models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{ + ListOptions: models.ListOptions{ + PageSize: 10, + Page: 0, + }, IncludeTags: true, - }, 0, 10) + }) assert.NoError(t, err) assert.EqualValues(t, 8, len(releases)) releases, err = models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{ + ListOptions: models.ListOptions{ + PageSize: 10, + Page: 0, + }, IncludeTags: false, - }, 0, 10) + }) assert.NoError(t, err) assert.EqualValues(t, 1, len(releases)) diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go index 53c0b0f30..ee76d2181 100644 --- a/modules/repository/create_test.go +++ b/modules/repository/create_test.go @@ -19,7 +19,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { testTeamRepositories := func(teamID int64, repoIds []int64) { team := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team) - assert.NoError(t, team.GetRepositories(), "%s: GetRepositories", team.Name) + assert.NoError(t, team.GetRepositories(&models.SearchTeamOptions{}), "%s: GetRepositories", team.Name) assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name) assert.Equal(t, len(repoIds), len(team.Repos), "%s: repo count", team.Name) for i, rid := range repoIds { diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 4ecb9f660..d57b16c91 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -183,9 +183,10 @@ func CleanUpMigrateInfo(repo *models.Repository) (*models.Repository, error) { // SyncReleasesWithTags synchronizes release table with repository tags func SyncReleasesWithTags(repo *models.Repository, gitRepo *git.Repository) error { existingRelTags := make(map[string]struct{}) - opts := models.FindReleasesOptions{IncludeDrafts: true, IncludeTags: true} + opts := models.FindReleasesOptions{IncludeDrafts: true, IncludeTags: true, ListOptions: models.ListOptions{PageSize: 50}} for page := 1; ; page++ { - rels, err := models.GetReleasesByRepoID(repo.ID, opts, page, 100) + opts.Page = page + rels, err := models.GetReleasesByRepoID(repo.ID, opts) if err != nil { return fmt.Errorf("GetReleasesByRepoID: %v", err) } diff --git a/routers/admin/orgs.go b/routers/admin/orgs.go index 02068d618..627f56eae 100644 --- a/routers/admin/orgs.go +++ b/routers/admin/orgs.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -24,8 +25,10 @@ func Organizations(ctx *context.Context) { ctx.Data["PageIsAdminOrganizations"] = true routers.RenderUserSearch(ctx, &models.SearchUserOptions{ - Type: models.UserTypeOrganization, - PageSize: setting.UI.Admin.OrgPagingNum, - Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, + Type: models.UserTypeOrganization, + ListOptions: models.ListOptions{ + PageSize: setting.UI.Admin.OrgPagingNum, + }, + Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, }, tplOrgs) } diff --git a/routers/admin/users.go b/routers/admin/users.go index 71cda86cc..675369969 100644 --- a/routers/admin/users.go +++ b/routers/admin/users.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -33,8 +34,10 @@ func Users(ctx *context.Context) { ctx.Data["PageIsAdminUsers"] = true routers.RenderUserSearch(ctx, &models.SearchUserOptions{ - Type: models.UserTypeIndividual, - PageSize: setting.UI.Admin.UserPagingNum, + Type: models.UserTypeIndividual, + ListOptions: models.ListOptions{ + PageSize: setting.UI.Admin.UserPagingNum, + }, SearchByEmail: true, }, tplUsers) } diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index ca2ef574f..01c2c4973 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/user" + "code.gitea.io/gitea/routers/api/v1/utils" ) // CreateOrg api for create organization @@ -100,11 +101,10 @@ func GetAllOrgs(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" users, _, err := models.SearchUsers(&models.SearchUserOptions{ - Type: models.UserTypeOrganization, - OrderBy: models.SearchOrderByAlphabetically, - Page: ctx.QueryInt("page"), - PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")), - Visible: []api.VisibleType{api.VisibleTypePublic, api.VisibleTypeLimited, api.VisibleTypePrivate}, + Type: models.UserTypeOrganization, + OrderBy: models.SearchOrderByAlphabetically, + ListOptions: utils.GetListOptions(ctx), + Visible: []api.VisibleType{api.VisibleTypePublic, api.VisibleTypeLimited, api.VisibleTypePrivate}, }) if err != nil { ctx.Error(http.StatusInternalServerError, "SearchOrganizations", err) diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index ebc651516..28acc062c 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/password" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/user" + "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/mailer" ) @@ -328,6 +329,15 @@ func GetAllUsers(ctx *context.APIContext) { // summary: List all users // produces: // - application/json + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/UserList" @@ -335,9 +345,9 @@ func GetAllUsers(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" users, _, err := models.SearchUsers(&models.SearchUserOptions{ - Type: models.UserTypeIndividual, - OrderBy: models.SearchOrderByAlphabetically, - PageSize: -1, + Type: models.UserTypeIndividual, + OrderBy: models.SearchOrderByAlphabetically, + ListOptions: utils.GetListOptions(ctx), }) if err != nil { ctx.Error(http.StatusInternalServerError, "GetAllUsers", err) diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go index b939d90f0..10c00c546 100644 --- a/routers/api/v1/notify/repo.go +++ b/routers/api/v1/notify/repo.go @@ -51,6 +51,14 @@ func ListRepoNotifications(ctx *context.APIContext) { // type: string // format: date-time // required: false + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/NotificationThreadList" @@ -61,6 +69,7 @@ func ListRepoNotifications(ctx *context.APIContext) { return } opts := models.FindNotificationOptions{ + ListOptions: utils.GetListOptions(ctx), UserID: ctx.User.ID, RepoID: ctx.Repo.Repository.ID, UpdatedBeforeUnix: before, diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go index d16e4da0e..7f731e25d 100644 --- a/routers/api/v1/notify/user.go +++ b/routers/api/v1/notify/user.go @@ -41,6 +41,14 @@ func ListNotifications(ctx *context.APIContext) { // type: string // format: date-time // required: false + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/NotificationThreadList" @@ -51,6 +59,7 @@ func ListNotifications(ctx *context.APIContext) { return } opts := models.FindNotificationOptions{ + ListOptions: utils.GetListOptions(ctx), UserID: ctx.User.ID, UpdatedBeforeUnix: before, UpdatedAfterUnix: since, diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go index b3faac7b5..2fd084a13 100644 --- a/routers/api/v1/org/hook.go +++ b/routers/api/v1/org/hook.go @@ -27,12 +27,20 @@ func ListHooks(ctx *context.APIContext) { // description: name of the organization // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/HookList" org := ctx.Org.Organization - orgHooks, err := models.GetWebhooksByOrgID(org.ID) + orgHooks, err := models.GetWebhooksByOrgID(org.ID, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetWebhooksByOrgID", err) return diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 1575600a6..6450577f4 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -14,14 +14,16 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/user" + "code.gitea.io/gitea/routers/api/v1/utils" ) // 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, + members, _, err := models.FindOrgMembers(&models.FindOrgMembersOpts{ + OrgID: ctx.Org.Organization.ID, + PublicOnly: publicOnly, + ListOptions: utils.GetListOptions(ctx), }) if err != nil { ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err) @@ -48,6 +50,14 @@ func ListMembers(ctx *context.APIContext) { // description: name of the organization // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/UserList" @@ -75,6 +85,14 @@ func ListPublicMembers(ctx *context.APIContext) { // description: name of the organization // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // produces: // - application/json // responses: diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 4bcd60a67..fde58bd5c 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -13,10 +13,14 @@ import ( "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/user" + "code.gitea.io/gitea/routers/api/v1/utils" ) func listUserOrgs(ctx *context.APIContext, u *models.User, all bool) { - if err := u.GetOrganizations(all); err != nil { + if err := u.GetOrganizations(&models.SearchOrganizationsOptions{ + ListOptions: utils.GetListOptions(ctx), + All: all, + }); err != nil { ctx.Error(http.StatusInternalServerError, "GetOrganizations", err) return } @@ -35,6 +39,15 @@ func ListMyOrgs(ctx *context.APIContext) { // summary: List the current user's organizations // produces: // - application/json + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/OrganizationList" @@ -55,6 +68,14 @@ func ListUserOrgs(ctx *context.APIContext) { // description: username of user // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/OrganizationList" @@ -95,11 +116,10 @@ func GetAll(ctx *context.APIContext) { } publicOrgs, _, err := models.SearchUsers(&models.SearchUserOptions{ - Type: models.UserTypeOrganization, - OrderBy: models.SearchOrderByAlphabetically, - Page: ctx.QueryInt("page"), - PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")), - Visible: vMode, + ListOptions: utils.GetListOptions(ctx), + Type: models.UserTypeOrganization, + OrderBy: models.SearchOrderByAlphabetically, + Visible: vMode, }) if err != nil { ctx.Error(http.StatusInternalServerError, "SearchOrganizations", err) diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 446287a34..1bf984b17 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/user" + "code.gitea.io/gitea/routers/api/v1/utils" ) // ListTeams list all the teams of an organization @@ -30,12 +31,22 @@ func ListTeams(ctx *context.APIContext) { // description: name of the organization // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/TeamList" org := ctx.Org.Organization - if err := org.GetTeams(); err != nil { + if err := org.GetTeams(&models.SearchTeamOptions{ + ListOptions: utils.GetListOptions(ctx), + }); err != nil { ctx.Error(http.StatusInternalServerError, "GetTeams", err) return } @@ -59,11 +70,20 @@ func ListUserTeams(ctx *context.APIContext) { // summary: List all the teams a user belongs to // produces: // - application/json + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/TeamList" - teams, err := models.GetUserTeams(ctx.User.ID) + teams, err := models.GetUserTeams(ctx.User.ID, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetUserTeams", err) return @@ -284,6 +304,14 @@ func GetTeamMembers(ctx *context.APIContext) { // type: integer // format: int64 // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/UserList" @@ -297,7 +325,9 @@ func GetTeamMembers(ctx *context.APIContext) { return } team := ctx.Org.Team - if err := team.GetMembers(); err != nil { + if err := team.GetMembers(&models.SearchMembersOptions{ + ListOptions: utils.GetListOptions(ctx), + }); err != nil { ctx.Error(http.StatusInternalServerError, "GetTeamMembers", err) return } @@ -436,12 +466,22 @@ func GetTeamRepos(ctx *context.APIContext) { // type: integer // format: int64 // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/RepositoryList" team := ctx.Org.Team - if err := team.GetRepositories(); err != nil { + if err := team.GetRepositories(&models.SearchTeamOptions{ + ListOptions: utils.GetListOptions(ctx), + }); err != nil { ctx.Error(http.StatusInternalServerError, "GetTeamRepos", err) } repos := make([]*api.Repository, len(team.Repos)) @@ -589,14 +629,14 @@ func SearchTeam(ctx *context.APIContext) { // in: query // description: include search within team description (defaults to true) // type: boolean - // - name: limit - // in: query - // description: limit size of results - // type: integer // - name: page // in: query // description: page number of results to return (1-based) // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // description: "SearchResults of a successful search" @@ -615,8 +655,7 @@ func SearchTeam(ctx *context.APIContext) { Keyword: strings.TrimSpace(ctx.Query("q")), OrgID: ctx.Org.Organization.ID, IncludeDesc: (ctx.Query("include_desc") == "" || ctx.QueryBool("include_desc")), - PageSize: ctx.QueryInt("limit"), - Page: ctx.QueryInt("page"), + ListOptions: utils.GetListOptions(ctx), } teams, _, err := models.SearchTeam(opts) diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index aec389ab3..e99bc7b62 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // ListCollaborators list a repository's collaborators @@ -33,11 +34,19 @@ func ListCollaborators(ctx *context.APIContext) { // description: name of the repo // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/UserList" - collaborators, err := ctx.Repo.Repository.GetCollaborators() + collaborators, err := ctx.Repo.Repository.GetCollaborators(utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) return diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index d8777eaf3..f7da1698d 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // GetSingleCommit get a commit via @@ -92,7 +93,11 @@ func GetAllCommits(ctx *context.APIContext) { // type: string // - name: page // in: query - // description: page number of requested commits + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 // type: integer // responses: // "200": @@ -117,9 +122,13 @@ func GetAllCommits(ctx *context.APIContext) { } defer gitRepo.Close() - page := ctx.QueryInt("page") - if page <= 0 { - page = 1 + listOptions := utils.GetListOptions(ctx) + if listOptions.Page <= 0 { + listOptions.Page = 1 + } + + if listOptions.PageSize > git.CommitsRangeSize { + listOptions.PageSize = git.CommitsRangeSize } sha := ctx.Query("sha") @@ -154,10 +163,10 @@ func GetAllCommits(ctx *context.APIContext) { return } - pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(git.CommitsRangeSize))) + pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize))) // Query commits - commits, err := baseCommit.CommitsByRange(page) + commits, err := baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize) if err != nil { ctx.ServerError("CommitsByRange", err) return @@ -181,13 +190,13 @@ func GetAllCommits(ctx *context.APIContext) { i++ } - ctx.SetLinkHeader(int(commitsCountTotal), git.CommitsRangeSize) + ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize) - ctx.Header().Set("X-Page", strconv.Itoa(page)) - ctx.Header().Set("X-PerPage", strconv.Itoa(git.CommitsRangeSize)) + 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(page < pageCount)) + ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount)) ctx.JSON(http.StatusOK, &apiCommits) } diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index 0bf7fc6ce..3536b7f43 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -1,4 +1,5 @@ // Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -11,6 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" repo_service "code.gitea.io/gitea/services/repository" ) @@ -32,11 +34,19 @@ func ListForks(ctx *context.APIContext) { // description: name of the repo // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/RepositoryList" - forks, err := ctx.Repo.Repository.GetForks() + forks, err := ctx.Repo.Repository.GetForks(utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetForks", err) return diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index 7fd7cd1be..7fc5680c4 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -34,11 +35,19 @@ func ListHooks(ctx *context.APIContext) { // description: name of the repo // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/HookList" - hooks, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID) + hooks, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetWebhooksByRepoID", err) return diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index c480f2a46..c400ec6f3 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -19,6 +19,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/routers/api/v1/utils" issue_service "code.gitea.io/gitea/services/issue" ) @@ -38,10 +39,6 @@ func SearchIssues(ctx *context.APIContext) { // 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 @@ -55,6 +52,10 @@ func SearchIssues(ctx *context.APIContext) { // in: query // description: filter by type (issues / pulls) if set // type: string + // - name: page + // in: query + // description: page number of requested issues + // type: integer // responses: // "200": // "$ref": "#/responses/IssueList" @@ -72,7 +73,9 @@ func SearchIssues(ctx *context.APIContext) { // find repos user can access (for issue search) repoIDs := make([]int64, 0) opts := &models.SearchRepoOptions{ - PageSize: 15, + ListOptions: models.ListOptions{ + PageSize: 15, + }, Private: false, AllPublic: true, TopicOnly: false, @@ -146,9 +149,11 @@ func SearchIssues(ctx *context.APIContext) { // 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{ + ListOptions: models.ListOptions{ + Page: ctx.QueryInt("page"), + PageSize: setting.UI.IssuePagingNum, + }, RepoIDs: repoIDs, - Page: ctx.QueryInt("page"), - PageSize: setting.UI.IssuePagingNum, IsClosed: isClosed, IssueIDs: issueIDs, LabelIDs: labelIDs, @@ -198,10 +203,6 @@ func ListIssues(ctx *context.APIContext) { // 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 @@ -210,6 +211,14 @@ func ListIssues(ctx *context.APIContext) { // in: query // description: filter by type (issues / pulls) if set // type: string + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/IssueList" @@ -245,6 +254,11 @@ func ListIssues(ctx *context.APIContext) { } } + listOptions := utils.GetListOptions(ctx) + if ctx.QueryInt("limit") == 0 { + listOptions.PageSize = setting.UI.IssuePagingNum + } + var isPull util.OptionalBool switch ctx.Query("type") { case "pulls": @@ -259,13 +273,12 @@ func ListIssues(ctx *context.APIContext) { // 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: []int64{ctx.Repo.Repository.ID}, - Page: ctx.QueryInt("page"), - PageSize: setting.UI.IssuePagingNum, - IsClosed: isClosed, - IssueIDs: issueIDs, - LabelIDs: labelIDs, - IsPull: isPull, + ListOptions: listOptions, + RepoIDs: []int64{ctx.Repo.Repository.ID}, + IsClosed: isClosed, + IssueIDs: issueIDs, + LabelIDs: labelIDs, + IsPull: isPull, }) } @@ -279,7 +292,7 @@ func ListIssues(ctx *context.APIContext) { apiIssues[i] = issues[i].APIFormat() } - ctx.SetLinkHeader(ctx.Repo.Repository.NumIssues, setting.UI.IssuePagingNum) + ctx.SetLinkHeader(ctx.Repo.Repository.NumIssues, listOptions.PageSize) ctx.JSON(http.StatusOK, &apiIssues) } diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 6b0e38819..6b7c2beac 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -1,4 +1,5 @@ // Copyright 2015 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -117,6 +118,14 @@ func ListRepoIssueComments(ctx *context.APIContext) { // description: if provided, only comments updated before the provided time are returned. // type: string // format: date-time + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/CommentList" @@ -128,10 +137,11 @@ func ListRepoIssueComments(ctx *context.APIContext) { } comments, err := models.FindComments(models.FindCommentsOptions{ - RepoID: ctx.Repo.Repository.ID, - Since: since, - Before: before, - Type: models.CommentTypeComment, + ListOptions: utils.GetListOptions(ctx), + RepoID: ctx.Repo.Repository.ID, + Type: models.CommentTypeComment, + Since: since, + Before: before, }) if err != nil { ctx.Error(http.StatusInternalServerError, "FindComments", err) diff --git a/routers/api/v1/repo/issue_reaction.go b/routers/api/v1/repo/issue_reaction.go index 9c1322b3f..5e49ea4aa 100644 --- a/routers/api/v1/repo/issue_reaction.go +++ b/routers/api/v1/repo/issue_reaction.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // GetIssueCommentReactions list reactions of a comment from an issue @@ -245,6 +246,14 @@ func GetIssueReactions(ctx *context.APIContext) { // type: integer // format: int64 // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/ReactionList" @@ -266,7 +275,7 @@ func GetIssueReactions(ctx *context.APIContext) { return } - reactions, err := models.FindIssueReactions(issue) + reactions, err := models.FindIssueReactions(issue, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "FindIssueReactions", err) return diff --git a/routers/api/v1/repo/issue_stopwatch.go b/routers/api/v1/repo/issue_stopwatch.go index 3b7c20d4d..01faa0d6b 100644 --- a/routers/api/v1/repo/issue_stopwatch.go +++ b/routers/api/v1/repo/issue_stopwatch.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/routers/api/v1/utils" ) // StartIssueStopwatch creates a stopwatch for the given issue. @@ -197,6 +198,15 @@ func GetStopwatches(ctx *context.APIContext) { // swagger:operation GET /user/stopwatches user userGetStopWatches // --- // summary: Get list of all existing stopwatches + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // consumes: // - application/json // produces: @@ -205,7 +215,7 @@ func GetStopwatches(ctx *context.APIContext) { // "200": // "$ref": "#/responses/StopWatchList" - sws, err := models.GetUserStopwatches(ctx.User.ID) + sws, err := models.GetUserStopwatches(ctx.User.ID, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetUserStopwatches", err) return diff --git a/routers/api/v1/repo/issue_subscription.go b/routers/api/v1/repo/issue_subscription.go index 153b01de6..274da966f 100644 --- a/routers/api/v1/repo/issue_subscription.go +++ b/routers/api/v1/repo/issue_subscription.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/routers/api/v1/utils" ) // AddIssueSubscription Subscribe user to issue @@ -158,6 +159,14 @@ func GetIssueSubscribers(ctx *context.APIContext) { // type: integer // format: int64 // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/UserList" @@ -175,7 +184,7 @@ func GetIssueSubscribers(ctx *context.APIContext) { return } - iwl, err := models.GetIssueWatchers(issue.ID) + iwl, err := models.GetIssueWatchers(issue.ID, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetIssueWatchers", err) return diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index 323bf67d8..9bad19a2e 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -51,6 +51,14 @@ func ListTrackedTimes(ctx *context.APIContext) { // description: Only show times updated before the given time. This is a timestamp in RFC 3339 format // type: string // format: date-time + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/TrackedTimeList" @@ -72,6 +80,7 @@ func ListTrackedTimes(ctx *context.APIContext) { } opts := models.FindTrackedTimesOptions{ + ListOptions: utils.GetListOptions(ctx), RepositoryID: ctx.Repo.Repository.ID, IssueID: issue.ID, } @@ -435,6 +444,14 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) { // description: Only show times updated before the given time. This is a timestamp in RFC 3339 format // type: string // format: date-time + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/TrackedTimeList" @@ -449,6 +466,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) { } opts := models.FindTrackedTimesOptions{ + ListOptions: utils.GetListOptions(ctx), RepositoryID: ctx.Repo.Repository.ID, } @@ -495,6 +513,15 @@ func ListMyTrackedTimes(ctx *context.APIContext) { // swagger:operation GET /user/times user userCurrentTrackedTimes // --- // summary: List the current user's tracked times + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // produces: // - application/json // parameters: @@ -512,7 +539,10 @@ func ListMyTrackedTimes(ctx *context.APIContext) { // "200": // "$ref": "#/responses/TrackedTimeList" - opts := models.FindTrackedTimesOptions{UserID: ctx.User.ID} + opts := models.FindTrackedTimesOptions{ + ListOptions: utils.GetListOptions(ctx), + UserID: ctx.User.ID, + } var err error if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil { diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index 4ced8ec42..4039100e1 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -1,4 +1,5 @@ // Copyright 2015 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -13,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // appendPrivateInformation appends the owner and key type information to api.PublicKey @@ -60,6 +62,14 @@ func ListDeployKeys(ctx *context.APIContext) { // in: query // description: fingerprint of the key // type: string + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/DeployKeyList" @@ -72,7 +82,7 @@ func ListDeployKeys(ctx *context.APIContext) { if fingerprint != "" || keyID != 0 { keys, err = models.SearchDeployKeys(ctx.Repo.Repository.ID, keyID, fingerprint) } else { - keys, err = models.ListDeployKeys(ctx.Repo.Repository.ID) + keys, err = models.ListDeployKeys(ctx.Repo.Repository.ID, utils.GetListOptions(ctx)) } if err != nil { diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go index 16c905878..6d438610b 100644 --- a/routers/api/v1/repo/label.go +++ b/routers/api/v1/repo/label.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // ListLabels list all the labels of a repository @@ -32,11 +33,19 @@ func ListLabels(ctx *context.APIContext) { // description: name of the repo // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/LabelList" - labels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID, ctx.Query("sort")) + labels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID, ctx.Query("sort"), utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetLabelsByRepoID", err) return diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go index a979e2b69..f3bb4386c 100644 --- a/routers/api/v1/repo/milestone.go +++ b/routers/api/v1/repo/milestone.go @@ -1,4 +1,5 @@ // Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -12,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/routers/api/v1/utils" ) // ListMilestones list milestones for a repository @@ -36,11 +38,19 @@ func ListMilestones(ctx *context.APIContext) { // in: query // description: Milestone state, Recognised values are open, closed and all. Defaults to "open" // type: string + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/MilestoneList" - milestones, err := models.GetMilestonesByRepoID(ctx.Repo.Repository.ID, api.StateType(ctx.Query("state"))) + milestones, err := models.GetMilestonesByRepoID(ctx.Repo.Repository.ID, api.StateType(ctx.Query("state")), utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetMilestonesByRepoID", err) return diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index e2e6c2799..1abc2806f 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/notification" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/routers/api/v1/utils" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" ) @@ -41,10 +42,6 @@ func ListPullRequests(ctx *context.APIContext, form api.ListPullRequestsOptions) // description: name of the repo // type: string // required: true - // - name: page - // in: query - // description: Page number - // type: integer // - name: state // in: query // description: "State of pull request: open or closed (optional)" @@ -68,12 +65,22 @@ func ListPullRequests(ctx *context.APIContext, form api.ListPullRequestsOptions) // items: // type: integer // format: int64 + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/PullRequestList" + listOptions := utils.GetListOptions(ctx) + prs, maxResults, err := models.PullRequests(ctx.Repo.Repository.ID, &models.PullRequestsOptions{ - Page: ctx.QueryInt("page"), + ListOptions: listOptions, State: ctx.QueryTrim("state"), SortType: ctx.QueryTrim("sort"), Labels: ctx.QueryStrings("labels"), @@ -106,7 +113,7 @@ func ListPullRequests(ctx *context.APIContext, form api.ListPullRequestsOptions) apiPrs[i] = convert.ToAPIPullRequest(prs[i]) } - ctx.SetLinkHeader(int(maxResults), models.ItemsPerPage) + ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.JSON(http.StatusOK, &apiPrs) } diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index f94d6ba63..5a04fbeec 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -9,8 +9,8 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" releaseservice "code.gitea.io/gitea/services/release" ) @@ -59,20 +59,6 @@ func GetRelease(ctx *context.APIContext) { ctx.JSON(http.StatusOK, release.APIFormat()) } -func getPagesInfo(ctx *context.APIContext) (int, int) { - page := ctx.QueryInt("page") - if page == 0 { - page = 1 - } - perPage := ctx.QueryInt("per_page") - if perPage == 0 { - perPage = setting.API.DefaultPagingNum - } else if perPage > setting.API.MaxResponseItems { - perPage = setting.API.MaxResponseItems - } - return page, perPage -} - // ListReleases list a repository's releases func ListReleases(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/releases repository repoListReleases @@ -91,23 +77,32 @@ func ListReleases(ctx *context.APIContext) { // description: name of the repo // type: string // required: true + // - name: per_page + // in: query + // description: items count every page wants to load + // type: integer + // deprecated: true // - name: page // in: query - // description: page wants to load + // description: page number of results to return (1-based) // type: integer - // - name: per_page + // - name: limit // in: query - // description: items count every page wants to load + // description: page size of results, maximum page size is 50 // type: integer // responses: // "200": // "$ref": "#/responses/ReleaseList" + listOptions := utils.GetListOptions(ctx) + if ctx.QueryInt("per_page") != 0 { + listOptions.PageSize = ctx.QueryInt("per_page") + } - page, limit := getPagesInfo(ctx) releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{ + ListOptions: listOptions, IncludeDrafts: ctx.Repo.AccessMode >= models.AccessModeWrite, IncludeTags: false, - }, page, limit) + }) if err != nil { ctx.Error(http.StatusInternalServerError, "GetReleasesByRepoID", err) return diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index a13f6ebe0..61c522bc0 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -16,7 +16,6 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" @@ -27,6 +26,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" + "code.gitea.io/gitea/routers/api/v1/utils" mirror_service "code.gitea.io/gitea/services/mirror" repo_service "code.gitea.io/gitea/services/repository" ) @@ -91,14 +91,6 @@ func Search(ctx *context.APIContext) { // in: query // description: include template repositories this user has access to (defaults to true) // type: boolean - // - name: page - // in: query - // description: page number of results to return (1-based) - // type: integer - // - name: limit - // in: query - // description: page size of results, maximum page size is 50 - // type: integer // - name: mode // in: query // description: type of repository to search for. Supported values are @@ -119,6 +111,14 @@ func Search(ctx *context.APIContext) { // description: sort order, either "asc" (ascending) or "desc" (descending). // Default is "asc", ignored if "sort" is not specified. // type: string + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/SearchResults" @@ -126,12 +126,11 @@ func Search(ctx *context.APIContext) { // "$ref": "#/responses/validationError" opts := &models.SearchRepoOptions{ + ListOptions: utils.GetListOptions(ctx), Actor: ctx.User, Keyword: strings.Trim(ctx.Query("q"), " "), OwnerID: ctx.QueryInt64("uid"), PriorityOwnerID: ctx.QueryInt64("priority_owner_id"), - Page: ctx.QueryInt("page"), - PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")), TopicOnly: ctx.QueryBool("topic"), Collaborate: util.OptionalBoolNone, Private: ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")), @@ -214,7 +213,7 @@ func Search(ctx *context.APIContext) { results[i] = repo.APIFormat(accessMode) } - ctx.SetLinkHeader(int(count), setting.API.MaxResponseItems) + ctx.SetLinkHeader(int(count), opts.PageSize) ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, api.SearchResults{ OK: true, diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go index 65a99d442..c68f8f274 100644 --- a/routers/api/v1/repo/star.go +++ b/routers/api/v1/repo/star.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // ListStargazers list a repository's stargazers @@ -30,11 +31,19 @@ func ListStargazers(ctx *context.APIContext) { // description: name of the repo // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/UserList" - stargazers, err := ctx.Repo.Repository.GetStargazers(-1) + stargazers, err := ctx.Repo.Repository.GetStargazers(utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetStargazers", err) return diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index b6b3d495c..6ce71cc91 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/repofiles" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // NewCommitStatus creates a new CommitStatus @@ -89,11 +90,6 @@ func GetCommitStatuses(ctx *context.APIContext) { // description: sha of the commit // type: string // required: true - // - name: page - // in: query - // description: page number of results - // type: integer - // required: false // - name: sort // in: query // description: type of sort @@ -106,6 +102,14 @@ func GetCommitStatuses(ctx *context.APIContext) { // type: string // enum: [pending, success, error, failure, warning] // required: false + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/StatusList" @@ -138,11 +142,6 @@ func GetCommitStatusesByRef(ctx *context.APIContext) { // description: name of branch/tag/commit // type: string // required: true - // - name: page - // in: query - // description: page number of results - // type: integer - // required: false // - name: sort // in: query // description: type of sort @@ -155,6 +154,14 @@ func GetCommitStatusesByRef(ctx *context.APIContext) { // type: string // enum: [pending, success, error, failure, warning] // required: false + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/StatusList" @@ -202,9 +209,9 @@ func getCommitStatuses(ctx *context.APIContext, sha string) { repo := ctx.Repo.Repository statuses, _, err := models.GetCommitStatuses(repo, sha, &models.CommitStatusOptions{ - Page: ctx.QueryInt("page"), - SortType: ctx.QueryTrim("sort"), - State: ctx.QueryTrim("state"), + ListOptions: utils.GetListOptions(ctx), + SortType: ctx.QueryTrim("sort"), + State: ctx.QueryTrim("state"), }) if err != nil { ctx.Error(http.StatusInternalServerError, "GetCommitStatuses", fmt.Errorf("GetCommitStatuses[%s, %s, %d]: %v", repo.FullName(), sha, ctx.QueryInt("page"), err)) diff --git a/routers/api/v1/repo/subscriber.go b/routers/api/v1/repo/subscriber.go index 352f84288..d3cd8ccc3 100644 --- a/routers/api/v1/repo/subscriber.go +++ b/routers/api/v1/repo/subscriber.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // ListSubscribers list a repo's subscribers (i.e. watchers) @@ -30,11 +31,19 @@ func ListSubscribers(ctx *context.APIContext) { // description: name of the repo // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/UserList" - subscribers, err := ctx.Repo.Repository.GetWatchers(0) + subscribers, err := ctx.Repo.Repository.GetWatchers(utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetWatchers", err) return diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index 0c56f2a76..530b92a10 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // ListTopics returns list of current topics for repo @@ -33,12 +34,21 @@ func ListTopics(ctx *context.APIContext) { // description: name of the repo // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/TopicNames" topics, err := models.FindTopics(&models.FindTopicOptions{ - RepoID: ctx.Repo.Repository.ID, + ListOptions: utils.GetListOptions(ctx), + RepoID: ctx.Repo.Repository.ID, }) if err != nil { log.Error("ListTopics failed: %v", err) @@ -231,7 +241,7 @@ func DeleteTopic(ctx *context.APIContext) { } // TopicSearch search for creating topic -func TopicSearch(ctx *context.Context) { +func TopicSearch(ctx *context.APIContext) { // swagger:operation GET /topics/search repository topicSearch // --- // summary: search topics via keyword @@ -243,6 +253,14 @@ func TopicSearch(ctx *context.Context) { // description: keywords to search // required: true // type: string + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/TopicListResponse" @@ -256,9 +274,17 @@ func TopicSearch(ctx *context.Context) { kw := ctx.Query("q") + listOptions := utils.GetListOptions(ctx) + if listOptions.Page < 1 { + listOptions.Page = 1 + } + if listOptions.PageSize < 1 { + listOptions.PageSize = 10 + } + topics, err := models.FindTopics(&models.FindTopicOptions{ - Keyword: kw, - Limit: 10, + Keyword: kw, + ListOptions: listOptions, }) if err != nil { log.Error("SearchTopics failed: %v", err) diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index ec52f02d3..a65bdc51c 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // ListAccessTokens list all the access tokens @@ -26,11 +27,19 @@ func ListAccessTokens(ctx *context.APIContext) { // description: username of user // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/AccessTokenList" - tokens, err := models.ListAccessTokens(ctx.User.ID) + tokens, err := models.ListAccessTokens(ctx.User.ID, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err) return diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index bd6807026..6e180b37f 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -1,4 +1,5 @@ // Copyright 2015 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -11,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) func responseAPIUsers(ctx *context.APIContext, users []*models.User) { @@ -22,7 +24,7 @@ func responseAPIUsers(ctx *context.APIContext, users []*models.User) { } func listUserFollowers(ctx *context.APIContext, u *models.User) { - users, err := u.GetFollowers(ctx.QueryInt("page")) + users, err := u.GetFollowers(utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err) return @@ -35,6 +37,15 @@ func ListMyFollowers(ctx *context.APIContext) { // swagger:operation GET /user/followers user userCurrentListFollowers // --- // summary: List the authenticated user's followers + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // produces: // - application/json // responses: @@ -57,6 +68,14 @@ func ListFollowers(ctx *context.APIContext) { // description: username of user // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/UserList" @@ -69,7 +88,7 @@ func ListFollowers(ctx *context.APIContext) { } func listUserFollowing(ctx *context.APIContext, u *models.User) { - users, err := u.GetFollowing(ctx.QueryInt("page")) + users, err := u.GetFollowing(utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetFollowing", err) return @@ -82,6 +101,15 @@ func ListMyFollowing(ctx *context.APIContext) { // swagger:operation GET /user/following user userCurrentListFollowing // --- // summary: List the users that the authenticated user is following + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // produces: // - application/json // responses: @@ -104,6 +132,14 @@ func ListFollowing(ctx *context.APIContext) { // description: username of user // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/UserList" diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index b6133ca7b..a4f6d0e92 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -11,10 +11,11 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) -func listGPGKeys(ctx *context.APIContext, uid int64) { - keys, err := models.ListGPGKeys(uid) +func listGPGKeys(ctx *context.APIContext, uid int64, listOptions models.ListOptions) { + keys, err := models.ListGPGKeys(uid, listOptions) if err != nil { ctx.Error(http.StatusInternalServerError, "ListGPGKeys", err) return @@ -41,6 +42,14 @@ func ListGPGKeys(ctx *context.APIContext) { // description: username of user // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/GPGKeyList" @@ -49,7 +58,7 @@ func ListGPGKeys(ctx *context.APIContext) { if ctx.Written() { return } - listGPGKeys(ctx, user.ID) + listGPGKeys(ctx, user.ID, utils.GetListOptions(ctx)) } //ListMyGPGKeys get the GPG key list of the authenticated user @@ -57,13 +66,22 @@ func ListMyGPGKeys(ctx *context.APIContext) { // swagger:operation GET /user/gpg_keys user userCurrentListGPGKeys // --- // summary: List the authenticated user's GPG keys + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // produces: // - application/json // responses: // "200": // "$ref": "#/responses/GPGKeyList" - listGPGKeys(ctx, ctx.User.ID) + listGPGKeys(ctx, ctx.User.ID, utils.GetListOptions(ctx)) } //GetGPGKey get the GPG key based on a id diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index 7cf6fa383..58705dd2f 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/repo" + "code.gitea.io/gitea/routers/api/v1/utils" ) // appendPrivateInformation appends the owner and key type information to api.PublicKey @@ -79,7 +80,7 @@ func listPublicKeys(ctx *context.APIContext, user *models.User) { } } else { // Use ListPublicKeys - keys, err = models.ListPublicKeys(user.ID) + keys, err = models.ListPublicKeys(user.ID, utils.GetListOptions(ctx)) } if err != nil { @@ -109,6 +110,14 @@ func ListMyPublicKeys(ctx *context.APIContext) { // in: query // description: fingerprint of the key // type: string + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // produces: // - application/json // responses: @@ -135,6 +144,14 @@ func ListPublicKeys(ctx *context.APIContext) { // in: query // description: fingerprint of the key // type: string + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/PublicKeyList" diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go index 90518f95e..4024bf96c 100644 --- a/routers/api/v1/user/repo.go +++ b/routers/api/v1/user/repo.go @@ -10,11 +10,16 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // listUserRepos - List the repositories owned by the given user. func listUserRepos(ctx *context.APIContext, u *models.User, private bool) { - repos, err := models.GetUserRepositories(u.ID, private, 1, u.NumRepos, "") + repos, err := models.GetUserRepositories(&models.SearchRepoOptions{ + Actor: u, + Private: private, + ListOptions: utils.GetListOptions(ctx), + }) if err != nil { ctx.Error(http.StatusInternalServerError, "GetUserRepositories", err) return @@ -47,6 +52,14 @@ func ListUserRepos(ctx *context.APIContext) { // description: username of user // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/RepositoryList" @@ -66,11 +79,24 @@ func ListMyRepos(ctx *context.APIContext) { // summary: List the repos that the authenticated user owns or has access to // produces: // - application/json + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/RepositoryList" - ownRepos, err := models.GetUserRepositories(ctx.User.ID, true, 1, ctx.User.NumRepos, "") + ownRepos, err := models.GetUserRepositories(&models.SearchRepoOptions{ + Actor: ctx.User, + Private: true, + ListOptions: utils.GetListOptions(ctx), + }) if err != nil { ctx.Error(http.StatusInternalServerError, "GetUserRepositories", err) return @@ -106,6 +132,14 @@ func ListOrgRepos(ctx *context.APIContext) { // description: name of the organization // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/RepositoryList" diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index e5d3a8f0a..0e0875f04 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -1,4 +1,5 @@ // Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -10,12 +11,13 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // getStarredRepos returns the repos that the user with the specified userID has // starred -func getStarredRepos(user *models.User, private bool) ([]*api.Repository, error) { - starredRepos, err := models.GetStarredRepos(user.ID, private) +func getStarredRepos(user *models.User, private bool, listOptions models.ListOptions) ([]*api.Repository, error) { + starredRepos, err := models.GetStarredRepos(user.ID, private, listOptions) if err != nil { return nil, err } @@ -44,13 +46,21 @@ func GetStarredRepos(ctx *context.APIContext) { // description: username of user // type: string // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/RepositoryList" user := GetUserByParams(ctx) private := user.ID == ctx.User.ID - repos, err := getStarredRepos(user, private) + repos, err := getStarredRepos(user, private, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "getStarredRepos", err) } @@ -62,13 +72,22 @@ func GetMyStarredRepos(ctx *context.APIContext) { // swagger:operation GET /user/starred user userCurrentListStarred // --- // summary: The repos that the authenticated user has starred + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // produces: // - application/json // responses: // "200": // "$ref": "#/responses/RepositoryList" - repos, err := getStarredRepos(ctx.User, true) + repos, err := getStarredRepos(ctx.User, true, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "getStarredRepos", err) } diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 3f17a6f91..c0b0f1170 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -12,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" "github.com/unknwon/com" ) @@ -33,9 +35,13 @@ func Search(ctx *context.APIContext) { // description: ID of the user to search for // type: integer // format: int64 + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer // - name: limit // in: query - // description: maximum number of users to return + // description: page size of results, maximum page size is 50 // type: integer // responses: // "200": @@ -51,10 +57,10 @@ func Search(ctx *context.APIContext) { // "$ref": "#/definitions/User" opts := &models.SearchUserOptions{ - Keyword: strings.Trim(ctx.Query("q"), " "), - UID: com.StrTo(ctx.Query("uid")).MustInt64(), - Type: models.UserTypeIndividual, - PageSize: com.StrTo(ctx.Query("limit")).MustInt(), + Keyword: strings.Trim(ctx.Query("q"), " "), + UID: com.StrTo(ctx.Query("uid")).MustInt64(), + Type: models.UserTypeIndividual, + ListOptions: utils.GetListOptions(ctx), } users, _, err := models.SearchUsers(opts) diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go index ec8543dcf..e54709b59 100644 --- a/routers/api/v1/user/watch.go +++ b/routers/api/v1/user/watch.go @@ -11,12 +11,13 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers/api/v1/utils" ) // getWatchedRepos returns the repos that the user with the specified userID is // watching -func getWatchedRepos(user *models.User, private bool) ([]*api.Repository, error) { - watchedRepos, err := models.GetWatchedRepos(user.ID, private) +func getWatchedRepos(user *models.User, private bool, listOptions models.ListOptions) ([]*api.Repository, error) { + watchedRepos, err := models.GetWatchedRepos(user.ID, private, listOptions) if err != nil { return nil, err } @@ -45,13 +46,21 @@ func GetWatchedRepos(ctx *context.APIContext) { // in: path // description: username of the user // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/RepositoryList" user := GetUserByParams(ctx) private := user.ID == ctx.User.ID - repos, err := getWatchedRepos(user, private) + repos, err := getWatchedRepos(user, private, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) } @@ -65,11 +74,20 @@ func GetMyWatchedRepos(ctx *context.APIContext) { // summary: List repositories watched by the authenticated user // produces: // - application/json + // parameters: + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, maximum page size is 50 + // type: integer // responses: // "200": // "$ref": "#/responses/RepositoryList" - repos, err := getWatchedRepos(ctx.User, true) + repos, err := getWatchedRepos(ctx.User, true, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) } diff --git a/routers/api/v1/utils/utils.go b/routers/api/v1/utils/utils.go index 35f487396..e02406d13 100644 --- a/routers/api/v1/utils/utils.go +++ b/routers/api/v1/utils/utils.go @@ -8,7 +8,9 @@ import ( "strings" "time" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/convert" ) // UserID user ID of authenticated user, or 0 if not authenticated @@ -44,3 +46,11 @@ func GetQueryBeforeSince(ctx *context.APIContext) (before, since int64, err erro } return before, since, nil } + +// GetListOptions returns list options using the page and limit parameters +func GetListOptions(ctx *context.APIContext) models.ListOptions { + return models.ListOptions{ + Page: ctx.QueryInt("page"), + PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")), + } +} diff --git a/routers/home.go b/routers/home.go index 96e13cc68..77a7e153e 100644 --- a/routers/home.go +++ b/routers/home.go @@ -137,9 +137,11 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { ctx.Data["TopicOnly"] = topicOnly repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ + ListOptions: models.ListOptions{ + Page: page, + PageSize: opts.PageSize, + }, Actor: ctx.User, - Page: page, - PageSize: opts.PageSize, OrderBy: orderBy, Private: opts.Private, Keyword: keyword, @@ -250,10 +252,10 @@ func ExploreUsers(ctx *context.Context) { ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled RenderUserSearch(ctx, &models.SearchUserOptions{ - Type: models.UserTypeIndividual, - PageSize: setting.UI.ExplorePagingNum, - IsActive: util.OptionalBoolTrue, - Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, + Type: models.UserTypeIndividual, + ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum}, + IsActive: util.OptionalBoolTrue, + Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, }, tplExploreUsers) } @@ -270,9 +272,9 @@ func ExploreOrganizations(ctx *context.Context) { } RenderUserSearch(ctx, &models.SearchUserOptions{ - Type: models.UserTypeOrganization, - PageSize: setting.UI.ExplorePagingNum, - Visible: visibleTypes, + Type: models.UserTypeOrganization, + ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum}, + Visible: visibleTypes, }, tplExploreOrganizations) } diff --git a/routers/org/home.go b/routers/org/home.go index 2f461d861..e1bea5b7a 100644 --- a/routers/org/home.go +++ b/routers/org/home.go @@ -76,14 +76,16 @@ func Home(ctx *context.Context) { err error ) repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ + ListOptions: models.ListOptions{ + PageSize: setting.UI.User.RepoPagingNum, + Page: page, + }, Keyword: keyword, OwnerID: org.ID, OrderBy: orderBy, Private: ctx.IsSigned, Actor: ctx.User, - Page: page, IsProfile: true, - PageSize: setting.UI.User.RepoPagingNum, IncludeDescription: setting.UI.SearchRepoDescription, }) if err != nil { @@ -92,9 +94,9 @@ func Home(ctx *context.Context) { } var opts = models.FindOrgMembersOpts{ - OrgID: org.ID, - PublicOnly: true, - Limit: 25, + OrgID: org.ID, + PublicOnly: true, + ListOptions: models.ListOptions{Page: 1, PageSize: 25}, } if ctx.User != nil { @@ -106,7 +108,7 @@ func Home(ctx *context.Context) { opts.PublicOnly = !isMember && !ctx.User.IsAdmin } - members, _, err := models.FindOrgMembers(opts) + members, _, err := models.FindOrgMembers(&opts) if err != nil { ctx.ServerError("FindOrgMembers", err) return diff --git a/routers/org/members.go b/routers/org/members.go index 6bccb3d97..053bd21f5 100644 --- a/routers/org/members.go +++ b/routers/org/members.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2020 The Gitea Authors. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -51,9 +52,9 @@ func Members(ctx *context.Context) { } pager := context.NewPagination(int(total), setting.UI.MembersPagingNum, page, 5) - opts.Start = (page - 1) * setting.UI.MembersPagingNum - opts.Limit = setting.UI.MembersPagingNum - members, membersIsPublic, err := models.FindOrgMembers(opts) + opts.ListOptions.Page = (page - 1) * setting.UI.MembersPagingNum + opts.ListOptions.PageSize = setting.UI.MembersPagingNum + members, membersIsPublic, err := models.FindOrgMembers(&opts) if err != nil { ctx.ServerError("GetMembers", err) return diff --git a/routers/org/setting.go b/routers/org/setting.go index 0aeefb50a..df5ff24c0 100644 --- a/routers/org/setting.go +++ b/routers/org/setting.go @@ -155,7 +155,7 @@ func Webhooks(ctx *context.Context) { ctx.Data["BaseLink"] = ctx.Org.OrgLink + "/settings/hooks" ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc") - ws, err := models.GetWebhooksByOrgID(ctx.Org.Organization.ID) + ws, err := models.GetWebhooksByOrgID(ctx.Org.Organization.ID, models.ListOptions{}) if err != nil { ctx.ServerError("GetWebhooksByOrgId", err) return diff --git a/routers/org/teams.go b/routers/org/teams.go index 2aa69f5e9..03fbf068d 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -38,7 +38,7 @@ func Teams(ctx *context.Context) { ctx.Data["PageIsOrgTeams"] = true for _, t := range org.Teams { - if err := t.GetMembers(); err != nil { + if err := t.GetMembers(&models.SearchMembersOptions{}); err != nil { ctx.ServerError("GetMembers", err) return } @@ -246,7 +246,7 @@ func TeamMembers(ctx *context.Context) { ctx.Data["Title"] = ctx.Org.Team.Name ctx.Data["PageIsOrgTeams"] = true ctx.Data["PageIsOrgTeamMembers"] = true - if err := ctx.Org.Team.GetMembers(); err != nil { + if err := ctx.Org.Team.GetMembers(&models.SearchMembersOptions{}); err != nil { ctx.ServerError("GetMembers", err) return } @@ -258,7 +258,7 @@ func TeamRepositories(ctx *context.Context) { ctx.Data["Title"] = ctx.Org.Team.Name ctx.Data["PageIsOrgTeams"] = true ctx.Data["PageIsOrgTeamRepos"] = true - if err := ctx.Org.Team.GetRepositories(); err != nil { + if err := ctx.Org.Team.GetRepositories(&models.SearchTeamOptions{}); err != nil { ctx.ServerError("GetRepositories", err) return } diff --git a/routers/repo/commit.go b/routers/repo/commit.go index e581d3952..cb9727fcc 100644 --- a/routers/repo/commit.go +++ b/routers/repo/commit.go @@ -58,8 +58,13 @@ func Commits(ctx *context.Context) { page = 1 } + pageSize := ctx.QueryInt("limit") + if pageSize <= 0 { + pageSize = git.CommitsRangeSize + } + // Both `git log branchName` and `git log commitId` work. - commits, err := ctx.Repo.Commit.CommitsByRange(page) + commits, err := ctx.Repo.Commit.CommitsByRange(page, pageSize) if err != nil { ctx.ServerError("CommitsByRange", err) return diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 49481e7e2..fdade2795 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -195,13 +195,15 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB issues = []*models.Issue{} } else { issues, err = models.Issues(&models.IssuesOptions{ + ListOptions: models.ListOptions{ + Page: pager.Paginater.Current(), + PageSize: setting.UI.IssuePagingNum, + }, RepoIDs: []int64{repo.ID}, AssigneeID: assigneeID, PosterID: posterID, MentionedID: mentionedID, MilestoneID: milestoneID, - Page: pager.Paginater.Current(), - PageSize: setting.UI.IssuePagingNum, IsClosed: util.OptionalBoolOf(isShowClosed), IsPull: isPullOption, LabelIDs: labelIDs, @@ -246,7 +248,7 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB return } - labels, err := models.GetLabelsByRepoID(repo.ID, "") + labels, err := models.GetLabelsByRepoID(repo.ID, "", models.ListOptions{}) if err != nil { ctx.ServerError("GetLabelsByRepoID", err) return @@ -309,7 +311,7 @@ func Issues(ctx *context.Context) { var err error // Get milestones. - ctx.Data["Milestones"], err = models.GetMilestonesByRepoID(ctx.Repo.Repository.ID, api.StateType(ctx.Query("state"))) + ctx.Data["Milestones"], err = models.GetMilestonesByRepoID(ctx.Repo.Repository.ID, api.StateType(ctx.Query("state")), models.ListOptions{}) if err != nil { ctx.ServerError("GetAllRepoMilestones", err) return @@ -347,7 +349,7 @@ func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository, isPull boo return nil } - labels, err := models.GetLabelsByRepoID(repo.ID, "") + labels, err := models.GetLabelsByRepoID(repo.ID, "", models.ListOptions{}) if err != nil { ctx.ServerError("GetLabelsByRepoID", err) return nil @@ -733,7 +735,7 @@ func ViewIssue(ctx *context.Context) { for i := range issue.Labels { labelIDMark[issue.Labels[i].ID] = true } - labels, err := models.GetLabelsByRepoID(repo.ID, "") + labels, err := models.GetLabelsByRepoID(repo.ID, "", models.ListOptions{}) if err != nil { ctx.ServerError("GetLabelsByRepoID", err) return diff --git a/routers/repo/issue_label.go b/routers/repo/issue_label.go index 98f2dded3..e61fcbe5c 100644 --- a/routers/repo/issue_label.go +++ b/routers/repo/issue_label.go @@ -50,7 +50,7 @@ func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) { // RetrieveLabels find all the labels of a repository func RetrieveLabels(ctx *context.Context) { - labels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID, ctx.Query("sort")) + labels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID, ctx.Query("sort"), models.ListOptions{}) if err != nil { ctx.ServerError("RetrieveLabels.GetLabels", err) return diff --git a/routers/repo/release.go b/routers/repo/release.go index 0ab8450bd..545419518 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" @@ -56,24 +57,26 @@ func Releases(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.release.releases") ctx.Data["PageIsReleaseList"] = true - page := ctx.QueryInt("page") - if page <= 1 { - page = 1 - } - limit := ctx.QueryInt("limit") - if limit <= 0 { - limit = 10 - } - writeAccess := ctx.Repo.CanWrite(models.UnitTypeReleases) ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived opts := models.FindReleasesOptions{ + ListOptions: models.ListOptions{ + Page: ctx.QueryInt("page"), + PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")), + }, IncludeDrafts: writeAccess, IncludeTags: true, } - releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts, page, limit) + if opts.ListOptions.Page <= 1 { + opts.ListOptions.Page = 1 + } + if opts.ListOptions.PageSize <= 0 { + opts.ListOptions.Page = 10 + } + + releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts) if err != nil { ctx.ServerError("GetReleasesByRepoID", err) return @@ -121,7 +124,7 @@ func Releases(ctx *context.Context) { ctx.Data["Releases"] = releases - pager := context.NewPagination(int(count), limit, page, 5) + pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) pager.SetDefaultParams(ctx) ctx.Data["Page"] = pager diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 6ad0b4a96..22699f9e8 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -486,7 +486,7 @@ func Collaboration(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsCollaboration"] = true - users, err := ctx.Repo.Repository.GetCollaborators() + users, err := ctx.Repo.Repository.GetCollaborators(models.ListOptions{}) if err != nil { ctx.ServerError("GetCollaborators", err) return @@ -738,7 +738,7 @@ func DeployKeys(ctx *context.Context) { ctx.Data["PageIsSettingsKeys"] = true ctx.Data["DisableSSH"] = setting.SSH.Disabled - keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID) + keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID, models.ListOptions{}) if err != nil { ctx.ServerError("ListDeployKeys", err) return @@ -753,7 +753,7 @@ func DeployKeysPost(ctx *context.Context, form auth.AddKeyForm) { ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") ctx.Data["PageIsSettingsKeys"] = true - keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID) + keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID, models.ListOptions{}) if err != nil { ctx.ServerError("ListDeployKeys", err) return diff --git a/routers/repo/view.go b/routers/repo/view.go index 8730523d8..3fbff007e 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -522,7 +522,7 @@ func renderCode(ctx *context.Context) { } // RenderUserCards render a page show users according the input templaet -func RenderUserCards(ctx *context.Context, total int, getter func(page int) ([]*models.User, error), tpl base.TplName) { +func RenderUserCards(ctx *context.Context, total int, getter func(opts models.ListOptions) ([]*models.User, error), tpl base.TplName) { page := ctx.QueryInt("page") if page <= 0 { page = 1 @@ -530,7 +530,7 @@ func RenderUserCards(ctx *context.Context, total int, getter func(page int) ([]* pager := context.NewPagination(total, models.ItemsPerPage, page, 5) ctx.Data["Page"] = pager - items, err := getter(pager.Paginater.Current()) + items, err := getter(models.ListOptions{Page: pager.Paginater.Current()}) if err != nil { ctx.ServerError("getter", err) return @@ -561,7 +561,7 @@ func Stars(ctx *context.Context) { func Forks(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repos.forks") - forks, err := ctx.Repo.Repository.GetForks() + forks, err := ctx.Repo.Repository.GetForks(models.ListOptions{}) if err != nil { ctx.ServerError("GetForks", err) return diff --git a/routers/repo/webhook.go b/routers/repo/webhook.go index 9ae15882c..049ce0aed 100644 --- a/routers/repo/webhook.go +++ b/routers/repo/webhook.go @@ -38,7 +38,7 @@ func Webhooks(ctx *context.Context) { ctx.Data["BaseLink"] = ctx.Repo.RepoLink + "/settings/hooks" ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.io/en-us/webhooks/") - ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID) + ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID, models.ListOptions{}) if err != nil { ctx.ServerError("GetWebhooksByRepoID", err) return diff --git a/routers/user/home.go b/routers/user/home.go index 0d78b17da..8b3155ea0 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -53,7 +53,7 @@ func getDashboardContextUser(ctx *context.Context) *models.User { } ctx.Data["ContextUser"] = ctxUser - if err := ctx.User.GetOrganizations(true); err != nil { + if err := ctx.User.GetOrganizations(&models.SearchOrganizationsOptions{All: true}); err != nil { ctx.ServerError("GetOrganizations", err) return nil } @@ -609,7 +609,7 @@ func Issues(ctx *context.Context) { // ShowSSHKeys output all the ssh keys of user by uid func ShowSSHKeys(ctx *context.Context, uid int64) { - keys, err := models.ListPublicKeys(uid) + keys, err := models.ListPublicKeys(uid, models.ListOptions{}) if err != nil { ctx.ServerError("ListPublicKeys", err) return @@ -625,7 +625,7 @@ func ShowSSHKeys(ctx *context.Context, uid int64) { // ShowGPGKeys output all the public GPG keys of user by uid func ShowGPGKeys(ctx *context.Context, uid int64) { - keys, err := models.ListGPGKeys(uid) + keys, err := models.ListGPGKeys(uid, models.ListOptions{}) if err != nil { ctx.ServerError("ListGPGKeys", err) return diff --git a/routers/user/profile.go b/routers/user/profile.go index b5933788d..808ce382c 100644 --- a/routers/user/profile.go +++ b/routers/user/profile.go @@ -172,12 +172,14 @@ func Profile(ctx *context.Context) { case "stars": ctx.Data["PageIsProfileStarList"] = true repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ + ListOptions: models.ListOptions{ + PageSize: setting.UI.User.RepoPagingNum, + Page: page, + }, Actor: ctx.User, Keyword: keyword, OrderBy: orderBy, Private: ctx.IsSigned, - Page: page, - PageSize: setting.UI.User.RepoPagingNum, StarredByID: ctxUser.ID, Collaborate: util.OptionalBoolFalse, TopicOnly: topicOnly, @@ -191,14 +193,16 @@ func Profile(ctx *context.Context) { total = int(count) default: repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ + ListOptions: models.ListOptions{ + PageSize: setting.UI.User.RepoPagingNum, + Page: page, + }, Actor: ctx.User, Keyword: keyword, OwnerID: ctxUser.ID, OrderBy: orderBy, Private: ctx.IsSigned, - Page: page, IsProfile: true, - PageSize: setting.UI.User.RepoPagingNum, Collaborate: util.OptionalBoolFalse, TopicOnly: topicOnly, IncludeDescription: setting.UI.SearchRepoDescription, diff --git a/routers/user/setting/applications.go b/routers/user/setting/applications.go index d93684bcc..e7bf61226 100644 --- a/routers/user/setting/applications.go +++ b/routers/user/setting/applications.go @@ -68,7 +68,7 @@ func DeleteApplication(ctx *context.Context) { } func loadApplicationsData(ctx *context.Context) { - tokens, err := models.ListAccessTokens(ctx.User.ID) + tokens, err := models.ListAccessTokens(ctx.User.ID, models.ListOptions{}) if err != nil { ctx.ServerError("ListAccessTokens", err) return diff --git a/routers/user/setting/keys.go b/routers/user/setting/keys.go index c62b117a7..347529943 100644 --- a/routers/user/setting/keys.go +++ b/routers/user/setting/keys.go @@ -133,14 +133,14 @@ func DeleteKey(ctx *context.Context) { } func loadKeysData(ctx *context.Context) { - keys, err := models.ListPublicKeys(ctx.User.ID) + keys, err := models.ListPublicKeys(ctx.User.ID, models.ListOptions{}) if err != nil { ctx.ServerError("ListPublicKeys", err) return } ctx.Data["Keys"] = keys - gpgkeys, err := models.ListGPGKeys(ctx.User.ID) + gpgkeys, err := models.ListGPGKeys(ctx.User.ID, models.ListOptions{}) if err != nil { ctx.ServerError("ListGPGKeys", err) return diff --git a/routers/user/setting/profile.go b/routers/user/setting/profile.go index 6db9fc7c6..a5cc433a9 100644 --- a/routers/user/setting/profile.go +++ b/routers/user/setting/profile.go @@ -196,7 +196,7 @@ func Repos(ctx *context.Context) { ctxUser := ctx.User var err error - if err = ctxUser.GetRepositories(1, setting.UI.User.RepoPagingNum); err != nil { + if err = ctxUser.GetRepositories(models.ListOptions{Page: 1, PageSize: setting.UI.User.RepoPagingNum}); err != nil { ctx.ServerError("GetRepositories", err) return } diff --git a/routers/user/setting/security.go b/routers/user/setting/security.go index f8f5cc0cd..c7c3226c9 100644 --- a/routers/user/setting/security.go +++ b/routers/user/setting/security.go @@ -71,7 +71,7 @@ func loadSecurityData(ctx *context.Context) { ctx.Data["RequireU2F"] = true } - tokens, err := models.ListAccessTokens(ctx.User.ID) + tokens, err := models.ListAccessTokens(ctx.User.ID, models.ListOptions{}) if err != nil { ctx.ServerError("ListAccessTokens", err) return diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 7aa29f0b0..11af8e035 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -67,6 +67,20 @@ ], "summary": "List all users", "operationId": "adminGetAllUsers", + "parameters": [ + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/UserList" @@ -458,6 +472,18 @@ "description": "Only show notifications updated before the given time. This is a timestamp in RFC 3339 format", "name": "before", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -789,6 +815,18 @@ "name": "org", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -960,6 +998,18 @@ "name": "org", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -1053,6 +1103,18 @@ "name": "org", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -1180,6 +1242,18 @@ "name": "org", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -1246,6 +1320,18 @@ "name": "org", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -1324,14 +1410,14 @@ }, { "type": "integer", - "description": "limit size of results", - "name": "limit", + "description": "page number of results to return (1-based)", + "name": "page", "in": "query" }, { "type": "integer", - "description": "page number of results to return (1-based)", - "name": "page", + "description": "page size of results, maximum page size is 50", + "name": "limit", "in": "query" } ], @@ -1379,12 +1465,6 @@ "name": "labels", "in": "query" }, - { - "type": "integer", - "description": "page number of requested issues", - "name": "page", - "in": "query" - }, { "type": "string", "description": "search string", @@ -1403,6 +1483,12 @@ "description": "filter by type (issues / pulls) if set", "name": "type", "in": "query" + }, + { + "type": "integer", + "description": "page number of requested issues", + "name": "page", + "in": "query" } ], "responses": { @@ -1509,18 +1595,6 @@ "name": "template", "in": "query" }, - { - "type": "integer", - "description": "page number of results to return (1-based)", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "page size of results, maximum page size is 50", - "name": "limit", - "in": "query" - }, { "type": "string", "description": "type of repository to search for. Supported values are \"fork\", \"source\", \"mirror\" and \"collaborative\"", @@ -1544,6 +1618,18 @@ "description": "sort order, either \"asc\" (ascending) or \"desc\" (descending). Default is \"asc\", ignored if \"sort\" is not specified.", "name": "order", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -1808,6 +1894,18 @@ "name": "repo", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -1985,9 +2083,15 @@ }, { "type": "integer", - "description": "page number of requested commits", + "description": "page number of results to return (1-based)", "name": "page", "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -2351,6 +2455,18 @@ "name": "repo", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -2697,6 +2813,18 @@ "name": "repo", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -3122,12 +3250,6 @@ "name": "labels", "in": "query" }, - { - "type": "integer", - "description": "page number of requested issues", - "name": "page", - "in": "query" - }, { "type": "string", "description": "search string", @@ -3139,6 +3261,18 @@ "description": "filter by type (issues / pulls) if set", "name": "type", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -3236,6 +3370,18 @@ "description": "if provided, only comments updated before the provided time are returned.", "name": "before", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -4216,6 +4362,18 @@ "name": "index", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -4529,6 +4687,18 @@ "name": "index", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -4699,6 +4869,18 @@ "description": "Only show times updated before the given time. This is a timestamp in RFC 3339 format", "name": "before", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -4908,6 +5090,18 @@ "description": "fingerprint of the key", "name": "fingerprint", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -5065,12 +5259,24 @@ "name": "repo", "in": "path", "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/LabelList" - } + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/LabelList" + } } }, "post": { @@ -5271,6 +5477,18 @@ "description": "Milestone state, Recognised values are open, closed and all. Defaults to \"open\"", "name": "state", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -5530,6 +5748,18 @@ "description": "Only show notifications updated before the given time. This is a timestamp in RFC 3339 format", "name": "before", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -5605,12 +5835,6 @@ "in": "path", "required": true }, - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, { "enum": [ "closed", @@ -5653,6 +5877,18 @@ "description": "Label IDs", "name": "labels", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -5977,14 +6213,20 @@ }, { "type": "integer", - "description": "page wants to load", + "description": "items count every page wants to load", + "name": "per_page", + "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", "name": "page", "in": "query" }, { "type": "integer", - "description": "items count every page wants to load", - "name": "per_page", + "description": "page size of results, maximum page size is 50", + "name": "limit", "in": "query" } ], @@ -6477,6 +6719,18 @@ "name": "repo", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -6518,12 +6772,6 @@ "in": "path", "required": true }, - { - "type": "integer", - "description": "page number of results", - "name": "page", - "in": "query" - }, { "enum": [ "oldest", @@ -6549,6 +6797,18 @@ "description": "type of state", "name": "state", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -6633,6 +6893,18 @@ "name": "repo", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -6805,6 +7077,18 @@ "description": "Only show times updated before the given time. This is a timestamp in RFC 3339 format", "name": "before", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -6891,6 +7175,18 @@ "name": "repo", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -7173,6 +7469,18 @@ "name": "id", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -7307,6 +7615,18 @@ "name": "id", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -7420,6 +7740,18 @@ "name": "q", "in": "query", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -7527,6 +7859,20 @@ ], "summary": "List the authenticated user's followers", "operationId": "userCurrentListFollowers", + "parameters": [ + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/UserList" @@ -7544,6 +7890,20 @@ ], "summary": "List the users that the authenticated user is following", "operationId": "userCurrentListFollowing", + "parameters": [ + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/UserList" @@ -7629,6 +7989,20 @@ ], "summary": "List the authenticated user's GPG keys", "operationId": "userCurrentListGPGKeys", + "parameters": [ + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/GPGKeyList" @@ -7740,6 +8114,18 @@ "description": "fingerprint of the key", "name": "fingerprint", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -7850,6 +8236,20 @@ ], "summary": "List the current user's organizations", "operationId": "orgListCurrentUserOrgs", + "parameters": [ + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/OrganizationList" @@ -7867,6 +8267,20 @@ ], "summary": "List the repos that the authenticated user owns or has access to", "operationId": "userCurrentListRepos", + "parameters": [ + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/RepositoryList" @@ -7918,6 +8332,20 @@ ], "summary": "The repos that the authenticated user has starred", "operationId": "userCurrentListStarred", + "parameters": [ + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/RepositoryList" @@ -8027,6 +8455,20 @@ ], "summary": "Get list of all existing stopwatches", "operationId": "userGetStopWatches", + "parameters": [ + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/StopWatchList" @@ -8044,6 +8486,20 @@ ], "summary": "List repositories watched by the authenticated user", "operationId": "userCurrentListSubscriptions", + "parameters": [ + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/RepositoryList" @@ -8061,6 +8517,20 @@ ], "summary": "List all the teams a user belongs to", "operationId": "userListTeams", + "parameters": [ + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/TeamList" @@ -8127,7 +8597,13 @@ }, { "type": "integer", - "description": "maximum number of users to return", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", "name": "limit", "in": "query" } @@ -8232,6 +8708,18 @@ "name": "username", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -8258,6 +8746,18 @@ "name": "username", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -8284,6 +8784,18 @@ "name": "username", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -8345,6 +8857,18 @@ "description": "fingerprint of the key", "name": "fingerprint", "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -8371,6 +8895,18 @@ "name": "username", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -8397,6 +8933,18 @@ "name": "username", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -8423,6 +8971,18 @@ "name": "username", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -8449,6 +9009,18 @@ "name": "username", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": { @@ -8475,6 +9047,18 @@ "name": "username", "in": "path", "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, maximum page size is 50", + "name": "limit", + "in": "query" } ], "responses": {