Skip to content

Commit

Permalink
Add AbsoluteListOptions (#17028)
Browse files Browse the repository at this point in the history
This PR adds a `ListOptions` type which is not paged but uses absolute values. It is implemented as discussed in Discord.
Extracted from #16510 to clean that PR.
  • Loading branch information
KN4CK3R authored Sep 14, 2021
1 parent 0a542dd commit 87505a9
Show file tree
Hide file tree
Showing 29 changed files with 159 additions and 56 deletions.
6 changes: 3 additions & 3 deletions models/commit_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func GetCommitStatuses(repo *Repository, sha string, opts *CommitStatusOptions)
}

countSession := listCommitStatusesStatement(repo, sha, opts)
countSession = opts.setSessionPagination(countSession)
countSession = setSessionPagination(countSession, opts)
maxResults, err := countSession.Count(new(CommitStatus))
if err != nil {
log.Error("Count PRs: %v", err)
Expand All @@ -106,7 +106,7 @@ func GetCommitStatuses(repo *Repository, sha string, opts *CommitStatusOptions)

statuses := make([]*CommitStatus, 0, opts.PageSize)
findSession := listCommitStatusesStatement(repo, sha, opts)
findSession = opts.setSessionPagination(findSession)
findSession = setSessionPagination(findSession, opts)
sortCommitStatusesSession(findSession, opts.SortType)
return statuses, maxResults, findSession.Find(&statuses)
}
Expand Down Expand Up @@ -149,7 +149,7 @@ func getLatestCommitStatus(e Engine, repoID int64, sha string, listOptions ListO
Select("max( id ) as id").
GroupBy("context_hash").OrderBy("max( id ) desc")

sess = listOptions.setSessionPagination(sess)
sess = setSessionPagination(sess, &listOptions)

err := sess.Find(&ids)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion models/gpg_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func ListGPGKeys(uid int64, listOptions ListOptions) ([]*GPGKey, error) {
func listGPGKeys(e Engine, uid int64, listOptions ListOptions) ([]*GPGKey, error) {
sess := e.Table(&GPGKey{}).Where("owner_id=? AND primary_key_id=''", uid)
if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
sess = setSessionPagination(sess, &listOptions)
}

keys := make([]*GPGKey, 0, 2)
Expand Down
2 changes: 1 addition & 1 deletion models/issue_comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,7 @@ func findComments(e Engine, opts *FindCommentsOptions) ([]*Comment, error) {
}

if opts.Page != 0 {
sess = opts.setSessionPagination(sess)
sess = setSessionPagination(sess, opts)
}

// WARNING: If you change this order you will need to fix createCodeComment
Expand Down
4 changes: 2 additions & 2 deletions models/issue_label.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ func getLabelsByRepoID(e Engine, repoID int64, sortType string, listOptions List
}

if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
sess = setSessionPagination(sess, &listOptions)
}

return labels, sess.Find(&labels)
Expand Down Expand Up @@ -576,7 +576,7 @@ func getLabelsByOrgID(e Engine, orgID int64, sortType string, listOptions ListOp
}

if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
sess = setSessionPagination(sess, &listOptions)
}

return labels, sess.Find(&labels)
Expand Down
2 changes: 1 addition & 1 deletion models/issue_milestone.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ func GetMilestones(opts GetMilestonesOption) (MilestoneList, int64, error) {
sess := x.Where(opts.toCond())

if opts.Page != 0 {
sess = opts.setSessionPagination(sess)
sess = setSessionPagination(sess, &opts)
}

switch opts.SortType {
Expand Down
2 changes: 1 addition & 1 deletion models/issue_reaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func findReactions(e Engine, opts FindReactionsOptions) ([]*Reaction, error) {
In("reaction.`type`", setting.UI.Reactions).
Asc("reaction.issue_id", "reaction.comment_id", "reaction.created_unix", "reaction.id")
if opts.Page != 0 {
e = opts.setEnginePagination(e)
e = setEnginePagination(e, &opts)

reactions := make([]*Reaction, 0, opts.PageSize)
return reactions, e.Find(&reactions)
Expand Down
2 changes: 1 addition & 1 deletion models/issue_stopwatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func GetUserStopwatches(userID int64, listOptions ListOptions) ([]*Stopwatch, er
sws := make([]*Stopwatch, 0, 8)
sess := x.Where("stopwatch.user_id = ?", userID)
if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
sess = setSessionPagination(sess, &listOptions)
}

err := sess.Find(&sws)
Expand Down
2 changes: 1 addition & 1 deletion models/issue_tracked_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func (opts *FindTrackedTimesOptions) toSession(e Engine) Engine {
sess = sess.Where(opts.toCond())

if opts.Page != 0 {
sess = opts.setEnginePagination(sess)
sess = setEnginePagination(sess, opts)
}

return sess
Expand Down
2 changes: 1 addition & 1 deletion models/issue_watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func getIssueWatchers(e Engine, issueID int64, listOptions ListOptions) (IssueWa
Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id")

if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
sess = setSessionPagination(sess, &listOptions)
watches := make([]*IssueWatch, 0, listOptions.PageSize)
return watches, sess.Find(&watches)
}
Expand Down
79 changes: 60 additions & 19 deletions models/list_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,49 @@ import (
"xorm.io/xorm"
)

// ListOptions options to paginate results
type ListOptions struct {
PageSize int
Page int // start from 1
// Paginator is the base for different ListOptions types
type Paginator interface {
GetSkipTake() (skip, take int)
GetStartEnd() (start, end int)
}

func (opts *ListOptions) getPaginatedSession() *xorm.Session {
opts.setDefaultValues()
// getPaginatedSession creates a paginated database session
func getPaginatedSession(p Paginator) *xorm.Session {
skip, take := p.GetSkipTake()

return x.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
return x.Limit(take, skip)
}

func (opts *ListOptions) setSessionPagination(sess *xorm.Session) *xorm.Session {
opts.setDefaultValues()
// setSessionPagination sets pagination for a database session
func setSessionPagination(sess *xorm.Session, p Paginator) *xorm.Session {
skip, take := p.GetSkipTake()

if opts.PageSize <= 0 {
return sess
}
return sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
return sess.Limit(take, skip)
}

func (opts *ListOptions) setEnginePagination(e Engine) Engine {
opts.setDefaultValues()
// setSessionPagination sets pagination for a database engine
func setEnginePagination(e Engine, p Paginator) Engine {
skip, take := p.GetSkipTake()

return e.Limit(take, skip)
}

// ListOptions options to paginate results
type ListOptions struct {
PageSize int
Page int // start from 1
}

return e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
// GetSkipTake returns the skip and take values
func (opts *ListOptions) GetSkipTake() (skip, take int) {
opts.setDefaultValues()
return (opts.Page - 1) * opts.PageSize, opts.PageSize
}

// GetStartEnd returns the start and end of the ListOptions
func (opts *ListOptions) GetStartEnd() (start, end int) {
opts.setDefaultValues()
start = (opts.Page - 1) * opts.PageSize
end = start + opts.PageSize
start, take := opts.GetSkipTake()
end = start + take
return
}

Expand All @@ -56,3 +67,33 @@ func (opts *ListOptions) setDefaultValues() {
opts.Page = 1
}
}

// AbsoluteListOptions absolute options to paginate results
type AbsoluteListOptions struct {
skip int
take int
}

// NewAbsoluteListOptions creates a list option with applied limits
func NewAbsoluteListOptions(skip, take int) *AbsoluteListOptions {
if skip < 0 {
skip = 0
}
if take <= 0 {
take = setting.API.DefaultPagingNum
}
if take > setting.API.MaxResponseItems {
take = setting.API.MaxResponseItems
}
return &AbsoluteListOptions{skip, take}
}

// GetSkipTake returns the skip and take values
func (opts *AbsoluteListOptions) GetSkipTake() (skip, take int) {
return opts.skip, opts.take
}

// GetStartEnd returns the start and end values
func (opts *AbsoluteListOptions) GetStartEnd() (start, end int) {
return opts.skip, opts.skip + opts.take
}
62 changes: 62 additions & 0 deletions models/list_options_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2021 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 (
"testing"

"code.gitea.io/gitea/modules/setting"

"github.com/stretchr/testify/assert"
)

func TestPaginator(t *testing.T) {
cases := []struct {
Paginator
Skip int
Take int
Start int
End int
}{
{
Paginator: &ListOptions{Page: -1, PageSize: -1},
Skip: 0,
Take: setting.API.DefaultPagingNum,
Start: 0,
End: setting.API.DefaultPagingNum,
},
{
Paginator: &ListOptions{Page: 2, PageSize: 10},
Skip: 10,
Take: 10,
Start: 10,
End: 20,
},
{
Paginator: NewAbsoluteListOptions(-1, -1),
Skip: 0,
Take: setting.API.DefaultPagingNum,
Start: 0,
End: setting.API.DefaultPagingNum,
},
{
Paginator: NewAbsoluteListOptions(2, 10),
Skip: 2,
Take: 10,
Start: 2,
End: 12,
},
}

for _, c := range cases {
skip, take := c.Paginator.GetSkipTake()
start, end := c.Paginator.GetStartEnd()

assert.Equal(t, c.Skip, skip)
assert.Equal(t, c.Take, take)
assert.Equal(t, c.Start, start)
assert.Equal(t, c.End, end)
}
}
2 changes: 1 addition & 1 deletion models/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond {
func (opts *FindNotificationOptions) ToSession(e Engine) *xorm.Session {
sess := e.Where(opts.ToCond())
if opts.Page != 0 {
sess = opts.setSessionPagination(sess)
sess = setSessionPagination(sess, opts)
}
return sess
}
Expand Down
2 changes: 1 addition & 1 deletion models/oauth2_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ func ListOAuth2Applications(uid int64, listOptions ListOptions) ([]*OAuth2Applic
Desc("id")

if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
sess = setSessionPagination(sess, &listOptions)

apps := make([]*OAuth2Application, 0, listOptions.PageSize)
total, err := sess.FindAndCount(&apps)
Expand Down
4 changes: 2 additions & 2 deletions models/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ func GetOrgUsersByUserID(uid int64, opts *SearchOrganizationsOptions) ([]*OrgUse
}

if opts.PageSize != 0 {
sess = opts.setSessionPagination(sess)
sess = setSessionPagination(sess, opts)
}

err := sess.
Expand All @@ -589,7 +589,7 @@ func getOrgUsersByOrgID(e Engine, opts *FindOrgMembersOpts) ([]*OrgUser, error)
sess.And("is_public = ?", true)
}
if opts.ListOptions.PageSize > 0 {
sess = opts.setSessionPagination(sess)
sess = setSessionPagination(sess, opts)

ous := make([]*OrgUser, 0, opts.PageSize)
return ous, sess.Find(&ous)
Expand Down
4 changes: 2 additions & 2 deletions models/org_team.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ func (t *Team) GetRepositories(opts *SearchTeamOptions) error {
return t.getRepositories(x)
}

return t.getRepositories(opts.getPaginatedSession())
return t.getRepositories(getPaginatedSession(opts))
}

func (t *Team) getMembers(e Engine) (err error) {
Expand All @@ -182,7 +182,7 @@ func (t *Team) GetMembers(opts *SearchMembersOptions) (err error) {
return t.getMembers(x)
}

return t.getMembers(opts.getPaginatedSession())
return t.getMembers(getPaginatedSession(opts))
}

// AddMember adds new membership of the team to the organization,
Expand Down
2 changes: 1 addition & 1 deletion models/pull_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func PullRequests(baseRepoID int64, opts *PullRequestsOptions) ([]*PullRequest,
log.Error("listPullRequestStatement: %v", err)
return nil, maxResults, err
}
findSession = opts.setSessionPagination(findSession)
findSession = setSessionPagination(findSession, opts)
prs := make([]*PullRequest, 0, opts.PageSize)
return prs, maxResults, findSession.Find(&prs)
}
Expand Down
2 changes: 1 addition & 1 deletion models/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func GetReleasesByRepoID(repoID int64, opts FindReleasesOptions) ([]*Release, er
Where(opts.toConds(repoID))

if opts.PageSize != 0 {
sess = opts.setSessionPagination(sess)
sess = setSessionPagination(sess, &opts.ListOptions)
}

rels := make([]*Release, 0, opts.PageSize)
Expand Down
4 changes: 2 additions & 2 deletions models/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -1770,7 +1770,7 @@ func GetUserRepositories(opts *SearchRepoOptions) ([]*Repository, int64, error)

sess.Where(cond).OrderBy(opts.OrderBy.String())
repos := make([]*Repository, 0, opts.PageSize)
return repos, count, opts.setSessionPagination(sess).Find(&repos)
return repos, count, setSessionPagination(sess, opts).Find(&repos)
}

// GetUserMirrorRepositories returns a list of mirror repositories of given user.
Expand Down Expand Up @@ -2061,7 +2061,7 @@ func (repo *Repository) GetForks(listOptions ListOptions) ([]*Repository, error)
return forks, x.Find(&forks, &Repository{ForkID: repo.ID})
}

sess := listOptions.getPaginatedSession()
sess := getPaginatedSession(&listOptions)
forks := make([]*Repository, 0, listOptions.PageSize)
return forks, sess.Find(&forks, &Repository{ForkID: repo.ID})
}
Expand Down
2 changes: 1 addition & 1 deletion models/repo_collaboration.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (repo *Repository) getCollaborations(e Engine, listOptions ListOptions) ([]
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID})
}

e = listOptions.setEnginePagination(e)
e = setEnginePagination(e, &listOptions)

collaborations := make([]*Collaboration, 0, listOptions.PageSize)
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID})
Expand Down
2 changes: 1 addition & 1 deletion models/repo_watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func (repo *Repository) GetWatchers(opts ListOptions) ([]*User, error) {
Join("LEFT", "watch", "`user`.id=`watch`.user_id").
And("`watch`.mode<>?", RepoWatchModeDont)
if opts.Page > 0 {
sess = opts.setSessionPagination(sess)
sess = setSessionPagination(sess, &opts)
users := make([]*User, 0, opts.PageSize)

return users, sess.Find(&users)
Expand Down
2 changes: 1 addition & 1 deletion models/review.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func findReviews(e Engine, opts FindReviewOptions) ([]*Review, error) {
reviews := make([]*Review, 0, 10)
sess := e.Where(opts.toCond())
if opts.Page > 0 {
sess = opts.ListOptions.setSessionPagination(sess)
sess = setSessionPagination(sess, &opts)
}
return reviews, sess.
Asc("created_unix").
Expand Down
2 changes: 1 addition & 1 deletion models/ssh_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func SearchPublicKey(uid int64, fingerprint string) ([]*PublicKey, error) {
func ListPublicKeys(uid int64, listOptions ListOptions) ([]*PublicKey, error) {
sess := x.Where("owner_id = ? AND type != ?", uid, KeyTypePrincipal)
if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
sess = setSessionPagination(sess, &listOptions)

keys := make([]*PublicKey, 0, listOptions.PageSize)
return keys, sess.Find(&keys)
Expand Down
Loading

0 comments on commit 87505a9

Please sign in to comment.