[API] generalize list header (#16551)

* Add info about list endpoints to CONTRIBUTING.md

* Let all list endpoints return X-Total-Count header 

* Add TODOs for GetCombinedCommitStatusByRef

* Fix models/issue_stopwatch.go

* Rrefactor models.ListDeployKeys

* Introduce helper func and use them for SetLinkHeader related func
tokarchuk/v1.17
6543 3 years ago committed by GitHub
parent ca13e1d56c
commit 2289580bb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      CONTRIBUTING.md
  2. 2
      integrations/api_issue_tracked_time_test.go
  3. 33
      integrations/api_repo_topic_test.go
  4. 2
      models/access.go
  5. 2
      models/commit_status.go
  6. 5
      models/gpg_key.go
  7. 4
      models/issue.go
  8. 13
      models/issue_comment.go
  9. 10
      models/issue_label.go
  10. 24
      models/issue_milestone.go
  11. 8
      models/issue_milestone_test.go
  12. 5
      models/issue_stopwatch.go
  13. 31
      models/issue_tracked_time.go
  14. 22
      models/issue_tracked_time_test.go
  15. 5
      models/notification.go
  16. 8
      models/oauth2_application.go
  17. 14
      models/org.go
  18. 15
      models/org_team.go
  19. 2
      models/org_team_test.go
  20. 2
      models/org_test.go
  21. 8
      models/repo.go
  22. 5
      models/repo_collaboration.go
  23. 2
      models/repo_generate.go
  24. 4
      models/repo_transfer.go
  25. 5
      models/review.go
  26. 6
      models/ssh_key.go
  27. 56
      models/ssh_key_deploy.go
  28. 9
      models/token.go
  29. 17
      models/topic.go
  30. 15
      models/topic_test.go
  31. 8
      models/user.go
  32. 74
      models/webhook.go
  33. 9
      models/webhook_test.go
  34. 17
      modules/context/api.go
  35. 4
      modules/context/org.go
  36. 19
      modules/git/repo_tag.go
  37. 3
      modules/git/repo_tag_test.go
  38. 4
      modules/migrations/gitea_uploader_test.go
  39. 4
      routers/api/v1/admin/adopt.go
  40. 11
      routers/api/v1/admin/cron.go
  41. 4
      routers/api/v1/admin/org.go
  42. 3
      routers/api/v1/admin/user.go
  43. 8
      routers/api/v1/notify/repo.go
  44. 7
      routers/api/v1/notify/user.go
  45. 21
      routers/api/v1/org/hook.go
  46. 7
      routers/api/v1/org/label.go
  47. 17
      routers/api/v1/org/member.go
  48. 9
      routers/api/v1/org/org.go
  49. 41
      routers/api/v1/org/team.go
  50. 5
      routers/api/v1/repo/branch.go
  51. 9
      routers/api/v1/repo/collaborators.go
  52. 8
      routers/api/v1/repo/commits.go
  53. 2
      routers/api/v1/repo/fork.go
  54. 17
      routers/api/v1/repo/hook.go
  55. 6
      routers/api/v1/repo/issue.go
  56. 28
      routers/api/v1/repo/issue_comment.go
  57. 7
      routers/api/v1/repo/issue_stopwatch.go
  58. 31
      routers/api/v1/repo/issue_tracked_time.go
  59. 24
      routers/api/v1/repo/key.go
  60. 7
      routers/api/v1/repo/label.go
  61. 4
      routers/api/v1/repo/milestone.go
  62. 10
      routers/api/v1/repo/pull.go
  63. 14
      routers/api/v1/repo/pull_review.go
  64. 4
      routers/api/v1/repo/release.go
  65. 3
      routers/api/v1/repo/repo.go
  66. 2
      routers/api/v1/repo/star.go
  67. 4
      routers/api/v1/repo/status.go
  68. 2
      routers/api/v1/repo/subscriber.go
  69. 3
      routers/api/v1/repo/tag.go
  70. 33
      routers/api/v1/repo/topic.go
  71. 17
      routers/api/v1/user/app.go
  72. 4
      routers/api/v1/user/follower.go
  73. 7
      routers/api/v1/user/gpg_key.go
  74. 10
      routers/api/v1/user/key.go
  75. 7
      routers/api/v1/user/repo.go
  76. 2
      routers/api/v1/user/star.go
  77. 4
      routers/api/v1/user/user.go
  78. 21
      routers/api/v1/user/watch.go
  79. 4
      routers/web/org/home.go
  80. 4
      routers/web/org/members.go
  81. 2
      routers/web/org/setting.go
  82. 12
      routers/web/repo/issue.go
  83. 13
      routers/web/repo/milestone.go
  84. 4
      routers/web/repo/setting.go
  85. 2
      routers/web/repo/view.go
  86. 2
      routers/web/repo/webhook.go
  87. 2
      services/pull/review.go
  88. 12
      services/webhook/webhook.go

@ -207,6 +207,10 @@ In general, HTTP methods are chosen as follows:
An endpoint which changes/edits an object expects all fields to be optional (except ones to identify the object, which are required). An endpoint which changes/edits an object expects all fields to be optional (except ones to identify the object, which are required).
### Endpoints returning lists should
* support pagination (`page` & `limit` options in query)
* set `X-Total-Count` header via **SetTotalCountHeader** ([example](https://github.com/go-gitea/gitea/blob/7aae98cc5d4113f1e9918b7ee7dd09f67c189e3e/routers/api/v1/repo/issue.go#L444))
## Developer Certificate of Origin (DCO) ## Developer Certificate of Origin (DCO)
@ -231,8 +235,8 @@ on, finishing, and issuing releases. The overall goal is to make a
minor release every three or four months, which breaks down into two or three months of minor release every three or four months, which breaks down into two or three months of
general development followed by one month of testing and polishing general development followed by one month of testing and polishing
known as the release freeze. All the feature pull requests should be known as the release freeze. All the feature pull requests should be
merged before feature freeze. And, during the frozen period, a corresponding merged before feature freeze. And, during the frozen period, a corresponding
release branch is open for fixes backported from main branch. Release candidates release branch is open for fixes backported from main branch. Release candidates
are made during this period for user testing to are made during this period for user testing to
obtain a final version that is maintained in this branch. A release is obtain a final version that is maintained in this branch. A release is
maintained by issuing patch releases to only correct critical problems maintained by issuing patch releases to only correct critical problems

@ -30,7 +30,7 @@ func TestAPIGetTrackedTimes(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)
var apiTimes api.TrackedTimeList var apiTimes api.TrackedTimeList
DecodeJSON(t, resp, &apiTimes) DecodeJSON(t, resp, &apiTimes)
expect, err := models.GetTrackedTimes(models.FindTrackedTimesOptions{IssueID: issue2.ID}) expect, err := models.GetTrackedTimes(&models.FindTrackedTimesOptions{IssueID: issue2.ID})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, apiTimes, 3) assert.Len(t, apiTimes, 3)

@ -7,6 +7,7 @@ package integrations
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"testing" "testing"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@ -15,6 +16,38 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestAPITopicSearch(t *testing.T) {
defer prepareTestEnv(t)()
searchURL, _ := url.Parse("/api/v1/topics/search")
var topics struct {
TopicNames []*api.TopicResponse `json:"topics"`
}
query := url.Values{"page": []string{"1"}, "limit": []string{"4"}}
searchURL.RawQuery = query.Encode()
res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 4)
assert.EqualValues(t, "6", res.Header().Get("x-total-count"))
query.Add("q", "topic")
searchURL.RawQuery = query.Encode()
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 2)
query.Set("q", "database")
searchURL.RawQuery = query.Encode()
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
if assert.Len(t, topics.TopicNames, 1) {
assert.EqualValues(t, 2, topics.TopicNames[0].ID)
assert.EqualValues(t, "database", topics.TopicNames[0].Name)
assert.EqualValues(t, 1, topics.TopicNames[0].RepoCount)
}
}
func TestAPIRepoTopic(t *testing.T) { func TestAPIRepoTopic(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of repo2 user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of repo2

@ -246,7 +246,7 @@ func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err
return fmt.Errorf("refreshCollaboratorAccesses: %v", err) return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
} }
if err = repo.Owner.getTeams(e); err != nil { if err = repo.Owner.loadTeams(e); err != nil {
return err return err
} }

@ -159,7 +159,7 @@ func getLatestCommitStatus(e Engine, repoID int64, sha string, listOptions ListO
if len(ids) == 0 { if len(ids) == 0 {
return statuses, nil return statuses, nil
} }
return statuses, x.In("id", ids).Find(&statuses) return statuses, e.In("id", ids).Find(&statuses)
} }
// FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts // FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts

@ -71,6 +71,11 @@ func listGPGKeys(e Engine, uid int64, listOptions ListOptions) ([]*GPGKey, error
return keys, sess.Find(&keys) return keys, sess.Find(&keys)
} }
// CountUserGPGKeys return number of gpg keys a user own
func CountUserGPGKeys(userID int64) (int64, error) {
return x.Where("owner_id=? AND primary_key_id=''", userID).Count(&GPGKey{})
}
// GetGPGKeyByID returns public key by given ID. // GetGPGKeyByID returns public key by given ID.
func GetGPGKeyByID(keyID int64) (*GPGKey, error) { func GetGPGKeyByID(keyID int64) (*GPGKey, error) {
key := new(GPGKey) key := new(GPGKey)

@ -89,7 +89,7 @@ func init() {
func (issue *Issue) loadTotalTimes(e Engine) (err error) { func (issue *Issue) loadTotalTimes(e Engine) (err error) {
opts := FindTrackedTimesOptions{IssueID: issue.ID} opts := FindTrackedTimesOptions{IssueID: issue.ID}
issue.TotalTrackedTime, err = opts.ToSession(e).SumInt(&TrackedTime{}, "time") issue.TotalTrackedTime, err = opts.toSession(e).SumInt(&TrackedTime{}, "time")
if err != nil { if err != nil {
return err return err
} }
@ -214,7 +214,7 @@ func (issue *Issue) loadCommentsByType(e Engine, tp CommentType) (err error) {
if issue.Comments != nil { if issue.Comments != nil {
return nil return nil
} }
issue.Comments, err = findComments(e, FindCommentsOptions{ issue.Comments, err = findComments(e, &FindCommentsOptions{
IssueID: issue.ID, IssueID: issue.ID,
Type: tp, Type: tp,
}) })

@ -999,7 +999,7 @@ func (opts *FindCommentsOptions) toConds() builder.Cond {
return cond return cond
} }
func findComments(e Engine, opts FindCommentsOptions) ([]*Comment, error) { func findComments(e Engine, opts *FindCommentsOptions) ([]*Comment, error) {
comments := make([]*Comment, 0, 10) comments := make([]*Comment, 0, 10)
sess := e.Where(opts.toConds()) sess := e.Where(opts.toConds())
if opts.RepoID > 0 { if opts.RepoID > 0 {
@ -1019,10 +1019,19 @@ func findComments(e Engine, opts FindCommentsOptions) ([]*Comment, error) {
} }
// FindComments returns all comments according options // FindComments returns all comments according options
func FindComments(opts FindCommentsOptions) ([]*Comment, error) { func FindComments(opts *FindCommentsOptions) ([]*Comment, error) {
return findComments(x, opts) return findComments(x, opts)
} }
// CountComments count all comments according options by ignoring pagination
func CountComments(opts *FindCommentsOptions) (int64, error) {
sess := x.Where(opts.toConds())
if opts.RepoID > 0 {
sess.Join("INNER", "issue", "issue.id = comment.issue_id")
}
return sess.Count(&Comment{})
}
// UpdateComment updates information of comment. // UpdateComment updates information of comment.
func UpdateComment(c *Comment, doer *User) error { func UpdateComment(c *Comment, doer *User) error {
sess := x.NewSession() sess := x.NewSession()

@ -444,6 +444,11 @@ func GetLabelsByRepoID(repoID int64, sortType string, listOptions ListOptions) (
return getLabelsByRepoID(x, repoID, sortType, listOptions) return getLabelsByRepoID(x, repoID, sortType, listOptions)
} }
// CountLabelsByRepoID count number of all labels that belong to given repository by ID.
func CountLabelsByRepoID(repoID int64) (int64, error) {
return x.Where("repo_id = ?", repoID).Count(&Label{})
}
// ________ // ________
// \_____ \_______ ____ // \_____ \_______ ____
// / | \_ __ \/ ___\ // / | \_ __ \/ ___\
@ -556,6 +561,11 @@ func GetLabelsByOrgID(orgID int64, sortType string, listOptions ListOptions) ([]
return getLabelsByOrgID(x, orgID, sortType, listOptions) return getLabelsByOrgID(x, orgID, sortType, listOptions)
} }
// CountLabelsByOrgID count all labels that belong to given organization by ID.
func CountLabelsByOrgID(orgID int64) (int64, error) {
return x.Where("org_id = ?", orgID).Count(&Label{})
}
// .___ // .___
// | | ______ ________ __ ____ // | | ______ ________ __ ____
// | |/ ___// ___/ | \_/ __ \ // | |/ ___// ___/ | \_/ __ \

@ -380,24 +380,33 @@ type GetMilestonesOption struct {
SortType string SortType string
} }
// GetMilestones returns milestones filtered by GetMilestonesOption's func (opts GetMilestonesOption) toCond() builder.Cond {
func GetMilestones(opts GetMilestonesOption) (MilestoneList, error) { cond := builder.NewCond()
sess := x.Where("repo_id = ?", opts.RepoID) if opts.RepoID != 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
}
switch opts.State { switch opts.State {
case api.StateClosed: case api.StateClosed:
sess = sess.And("is_closed = ?", true) cond = cond.And(builder.Eq{"is_closed": true})
case api.StateAll: case api.StateAll:
break break
// api.StateOpen: // api.StateOpen:
default: default:
sess = sess.And("is_closed = ?", false) cond = cond.And(builder.Eq{"is_closed": false})
} }
if len(opts.Name) != 0 { if len(opts.Name) != 0 {
sess = sess.And(builder.Like{"name", opts.Name}) cond = cond.And(builder.Like{"name", opts.Name})
} }
return cond
}
// GetMilestones returns milestones filtered by GetMilestonesOption's
func GetMilestones(opts GetMilestonesOption) (MilestoneList, int64, error) {
sess := x.Where(opts.toCond())
if opts.Page != 0 { if opts.Page != 0 {
sess = opts.setSessionPagination(sess) sess = opts.setSessionPagination(sess)
} }
@ -420,7 +429,8 @@ func GetMilestones(opts GetMilestonesOption) (MilestoneList, error) {
} }
miles := make([]*Milestone, 0, opts.PageSize) miles := make([]*Milestone, 0, opts.PageSize)
return miles, sess.Find(&miles) total, err := sess.FindAndCount(&miles)
return miles, total, err
} }
// SearchMilestones search milestones // SearchMilestones search milestones

@ -50,7 +50,7 @@ func TestGetMilestonesByRepoID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
test := func(repoID int64, state api.StateType) { test := func(repoID int64, state api.StateType) {
repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository) repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository)
milestones, err := GetMilestones(GetMilestonesOption{ milestones, _, err := GetMilestones(GetMilestonesOption{
RepoID: repo.ID, RepoID: repo.ID,
State: state, State: state,
}) })
@ -87,7 +87,7 @@ func TestGetMilestonesByRepoID(t *testing.T) {
test(3, api.StateClosed) test(3, api.StateClosed)
test(3, api.StateAll) test(3, api.StateAll)
milestones, err := GetMilestones(GetMilestonesOption{ milestones, _, err := GetMilestones(GetMilestonesOption{
RepoID: NonexistentID, RepoID: NonexistentID,
State: api.StateOpen, State: api.StateOpen,
}) })
@ -100,7 +100,7 @@ func TestGetMilestones(t *testing.T) {
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
test := func(sortType string, sortCond func(*Milestone) int) { test := func(sortType string, sortCond func(*Milestone) int) {
for _, page := range []int{0, 1} { for _, page := range []int{0, 1} {
milestones, err := GetMilestones(GetMilestonesOption{ milestones, _, err := GetMilestones(GetMilestonesOption{
ListOptions: ListOptions{ ListOptions: ListOptions{
Page: page, Page: page,
PageSize: setting.UI.IssuePagingNum, PageSize: setting.UI.IssuePagingNum,
@ -117,7 +117,7 @@ func TestGetMilestones(t *testing.T) {
} }
assert.True(t, sort.IntsAreSorted(values)) assert.True(t, sort.IntsAreSorted(values))
milestones, err = GetMilestones(GetMilestonesOption{ milestones, _, err = GetMilestones(GetMilestonesOption{
ListOptions: ListOptions{ ListOptions: ListOptions{
Page: page, Page: page,
PageSize: setting.UI.IssuePagingNum, PageSize: setting.UI.IssuePagingNum,

@ -55,6 +55,11 @@ func GetUserStopwatches(userID int64, listOptions ListOptions) ([]*Stopwatch, er
return sws, nil return sws, nil
} }
// CountUserStopwatches return count of all stopwatches of a user
func CountUserStopwatches(userID int64) (int64, error) {
return x.Where("user_id = ?", userID).Count(&Stopwatch{})
}
// StopwatchExists returns true if the stopwatch exists // StopwatchExists returns true if the stopwatch exists
func StopwatchExists(userID, issueID int64) bool { func StopwatchExists(userID, issueID int64) bool {
_, exists, _ := getStopwatch(x, userID, issueID) _, exists, _ := getStopwatch(x, userID, issueID)

@ -79,8 +79,8 @@ type FindTrackedTimesOptions struct {
CreatedBeforeUnix int64 CreatedBeforeUnix int64
} }
// ToCond will convert each condition into a xorm-Cond // toCond will convert each condition into a xorm-Cond
func (opts *FindTrackedTimesOptions) ToCond() builder.Cond { func (opts *FindTrackedTimesOptions) toCond() builder.Cond {
cond := builder.NewCond().And(builder.Eq{"tracked_time.deleted": false}) cond := builder.NewCond().And(builder.Eq{"tracked_time.deleted": false})
if opts.IssueID != 0 { if opts.IssueID != 0 {
cond = cond.And(builder.Eq{"issue_id": opts.IssueID}) cond = cond.And(builder.Eq{"issue_id": opts.IssueID})
@ -103,14 +103,14 @@ func (opts *FindTrackedTimesOptions) ToCond() builder.Cond {
return cond return cond
} }
// ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required // 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) Engine { func (opts *FindTrackedTimesOptions) toSession(e Engine) Engine {
sess := e sess := e
if opts.RepositoryID > 0 || opts.MilestoneID > 0 { if opts.RepositoryID > 0 || opts.MilestoneID > 0 {
sess = e.Join("INNER", "issue", "issue.id = tracked_time.issue_id") sess = e.Join("INNER", "issue", "issue.id = tracked_time.issue_id")
} }
sess = sess.Where(opts.ToCond()) sess = sess.Where(opts.toCond())
if opts.Page != 0 { if opts.Page != 0 {
sess = opts.setEnginePagination(sess) sess = opts.setEnginePagination(sess)
@ -119,18 +119,27 @@ func (opts *FindTrackedTimesOptions) ToSession(e Engine) Engine {
return sess return sess
} }
func getTrackedTimes(e Engine, options FindTrackedTimesOptions) (trackedTimes TrackedTimeList, err error) { func getTrackedTimes(e Engine, options *FindTrackedTimesOptions) (trackedTimes TrackedTimeList, err error) {
err = options.ToSession(e).Find(&trackedTimes) err = options.toSession(e).Find(&trackedTimes)
return return
} }
// GetTrackedTimes returns all tracked times that fit to the given options. // GetTrackedTimes returns all tracked times that fit to the given options.
func GetTrackedTimes(opts FindTrackedTimesOptions) (TrackedTimeList, error) { func GetTrackedTimes(opts *FindTrackedTimesOptions) (TrackedTimeList, error) {
return getTrackedTimes(x, opts) return getTrackedTimes(x, opts)
} }
// CountTrackedTimes returns count of tracked times that fit to the given options.
func CountTrackedTimes(opts *FindTrackedTimesOptions) (int64, error) {
sess := x.Where(opts.toCond())
if opts.RepositoryID > 0 || opts.MilestoneID > 0 {
sess = sess.Join("INNER", "issue", "issue.id = tracked_time.issue_id")
}
return sess.Count(&TrackedTime{})
}
func getTrackedSeconds(e Engine, opts FindTrackedTimesOptions) (trackedSeconds int64, err error) { func getTrackedSeconds(e Engine, opts FindTrackedTimesOptions) (trackedSeconds int64, err error) {
return opts.ToSession(e).SumInt(&TrackedTime{}, "time") return opts.toSession(e).SumInt(&TrackedTime{}, "time")
} }
// GetTrackedSeconds return sum of seconds // GetTrackedSeconds return sum of seconds
@ -188,7 +197,7 @@ func addTime(e Engine, user *User, issue *Issue, amount int64, created time.Time
} }
// TotalTimes returns the spent time for each user by an issue // TotalTimes returns the spent time for each user by an issue
func TotalTimes(options FindTrackedTimesOptions) (map[*User]string, error) { func TotalTimes(options *FindTrackedTimesOptions) (map[*User]string, error) {
trackedTimes, err := GetTrackedTimes(options) trackedTimes, err := GetTrackedTimes(options)
if err != nil { if err != nil {
return nil, err return nil, err
@ -288,7 +297,7 @@ func deleteTimes(e Engine, opts FindTrackedTimesOptions) (removedTime int64, err
return return
} }
_, err = opts.ToSession(e).Table("tracked_time").Cols("deleted").Update(&TrackedTime{Deleted: true}) _, err = opts.toSession(e).Table("tracked_time").Cols("deleted").Update(&TrackedTime{Deleted: true})
return return
} }

@ -38,27 +38,27 @@ func TestGetTrackedTimes(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
// by Issue // by Issue
times, err := GetTrackedTimes(FindTrackedTimesOptions{IssueID: 1}) times, err := GetTrackedTimes(&FindTrackedTimesOptions{IssueID: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, times, 1) assert.Len(t, times, 1)
assert.Equal(t, int64(400), times[0].Time) assert.Equal(t, int64(400), times[0].Time)
times, err = GetTrackedTimes(FindTrackedTimesOptions{IssueID: -1}) times, err = GetTrackedTimes(&FindTrackedTimesOptions{IssueID: -1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, times, 0) assert.Len(t, times, 0)
// by User // by User
times, err = GetTrackedTimes(FindTrackedTimesOptions{UserID: 1}) times, err = GetTrackedTimes(&FindTrackedTimesOptions{UserID: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, times, 3) assert.Len(t, times, 3)
assert.Equal(t, int64(400), times[0].Time) assert.Equal(t, int64(400), times[0].Time)
times, err = GetTrackedTimes(FindTrackedTimesOptions{UserID: 3}) times, err = GetTrackedTimes(&FindTrackedTimesOptions{UserID: 3})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, times, 0) assert.Len(t, times, 0)
// by Repo // by Repo
times, err = GetTrackedTimes(FindTrackedTimesOptions{RepositoryID: 2}) times, err = GetTrackedTimes(&FindTrackedTimesOptions{RepositoryID: 2})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, times, 3) assert.Len(t, times, 3)
assert.Equal(t, int64(1), times[0].Time) assert.Equal(t, int64(1), times[0].Time)
@ -66,11 +66,11 @@ func TestGetTrackedTimes(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, issue.RepoID, int64(2)) assert.Equal(t, issue.RepoID, int64(2))
times, err = GetTrackedTimes(FindTrackedTimesOptions{RepositoryID: 1}) times, err = GetTrackedTimes(&FindTrackedTimesOptions{RepositoryID: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, times, 5) assert.Len(t, times, 5)
times, err = GetTrackedTimes(FindTrackedTimesOptions{RepositoryID: 10}) times, err = GetTrackedTimes(&FindTrackedTimesOptions{RepositoryID: 10})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, times, 0) assert.Len(t, times, 0)
} }
@ -78,7 +78,7 @@ func TestGetTrackedTimes(t *testing.T) {
func TestTotalTimes(t *testing.T) { func TestTotalTimes(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
total, err := TotalTimes(FindTrackedTimesOptions{IssueID: 1}) total, err := TotalTimes(&FindTrackedTimesOptions{IssueID: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, total, 1) assert.Len(t, total, 1)
for user, time := range total { for user, time := range total {
@ -86,7 +86,7 @@ func TestTotalTimes(t *testing.T) {
assert.Equal(t, "6min 40s", time) assert.Equal(t, "6min 40s", time)
} }
total, err = TotalTimes(FindTrackedTimesOptions{IssueID: 2}) total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 2})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, total, 2) assert.Len(t, total, 2)
for user, time := range total { for user, time := range total {
@ -99,7 +99,7 @@ func TestTotalTimes(t *testing.T) {
} }
} }
total, err = TotalTimes(FindTrackedTimesOptions{IssueID: 5}) total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 5})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, total, 1) assert.Len(t, total, 1)
for user, time := range total { for user, time := range total {
@ -107,7 +107,7 @@ func TestTotalTimes(t *testing.T) {
assert.Equal(t, "1s", time) assert.Equal(t, "1s", time)
} }
total, err = TotalTimes(FindTrackedTimesOptions{IssueID: 4}) total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 4})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, total, 2) assert.Len(t, total, 2)
} }

@ -125,6 +125,11 @@ func GetNotifications(opts *FindNotificationOptions) (NotificationList, error) {
return getNotifications(x, opts) return getNotifications(x, opts)
} }
// CountNotifications count all notifications that fit to the given options and ignore pagination.
func CountNotifications(opts *FindNotificationOptions) (int64, error) {
return x.Where(opts.ToCond()).Count(&Notification{})
}
// CreateRepoTransferNotification creates notification for the user a repository was transferred to // CreateRepoTransferNotification creates notification for the user a repository was transferred to
func CreateRepoTransferNotification(doer, newOwner *User, repo *Repository) error { func CreateRepoTransferNotification(doer, newOwner *User, repo *Repository) error {
sess := x.NewSession() sess := x.NewSession()

@ -269,7 +269,7 @@ func DeleteOAuth2Application(id, userid int64) error {
} }
// ListOAuth2Applications returns a list of oauth2 applications belongs to given user. // ListOAuth2Applications returns a list of oauth2 applications belongs to given user.
func ListOAuth2Applications(uid int64, listOptions ListOptions) ([]*OAuth2Application, error) { func ListOAuth2Applications(uid int64, listOptions ListOptions) ([]*OAuth2Application, int64, error) {
sess := x. sess := x.
Where("uid=?", uid). Where("uid=?", uid).
Desc("id") Desc("id")
@ -278,11 +278,13 @@ func ListOAuth2Applications(uid int64, listOptions ListOptions) ([]*OAuth2Applic
sess = listOptions.setSessionPagination(sess) sess = listOptions.setSessionPagination(sess)
apps := make([]*OAuth2Application, 0, listOptions.PageSize) apps := make([]*OAuth2Application, 0, listOptions.PageSize)
return apps, sess.Find(&apps) total, err := sess.FindAndCount(&apps)
return apps, total, err
} }
apps := make([]*OAuth2Application, 0, 5) apps := make([]*OAuth2Application, 0, 5)
return apps, sess.Find(&apps) total, err := sess.FindAndCount(&apps)
return apps, total, err
} }
////////////////////////////////////////////////////// //////////////////////////////////////////////////////

@ -52,7 +52,7 @@ func (org *User) GetOwnerTeam() (*Team, error) {
return org.getOwnerTeam(x) return org.getOwnerTeam(x)
} }
func (org *User) getTeams(e Engine) error { func (org *User) loadTeams(e Engine) error {
if org.Teams != nil { if org.Teams != nil {
return nil return nil
} }
@ -62,13 +62,9 @@ func (org *User) getTeams(e Engine) error {
Find(&org.Teams) Find(&org.Teams)
} }
// GetTeams returns paginated teams that belong to organization. // LoadTeams load teams if not loaded.
func (org *User) GetTeams(opts *SearchTeamOptions) error { func (org *User) LoadTeams() error {
if opts.Page != 0 { return org.loadTeams(x)
return org.getTeams(opts.getPaginatedSession())
}
return org.getTeams(x)
} }
// GetMembers returns all members of organization. // GetMembers returns all members of organization.
@ -87,7 +83,7 @@ type FindOrgMembersOpts struct {
} }
// CountOrgMembers counts the organization's members // CountOrgMembers counts the organization's members
func CountOrgMembers(opts FindOrgMembersOpts) (int64, error) { func CountOrgMembers(opts *FindOrgMembersOpts) (int64, error) {
sess := x.Where("org_id=?", opts.OrgID) sess := x.Where("org_id=?", opts.OrgID)
if opts.PublicOnly { if opts.PublicOnly {
sess.And("is_public = ?", true) sess.And("is_public = ?", true)

@ -790,16 +790,6 @@ func GetTeamMembers(teamID int64) ([]*User, error) {
return getTeamMembers(x, teamID) return getTeamMembers(x, teamID)
} }
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)
if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
}
return teams, sess.Find(&teams)
}
func getUserOrgTeams(e Engine, orgID, userID int64) (teams []*Team, err error) { func getUserOrgTeams(e Engine, orgID, userID int64) (teams []*Team, err error) {
return teams, e. return teams, e.
Join("INNER", "team_user", "team_user.team_id = team.id"). Join("INNER", "team_user", "team_user.team_id = team.id").
@ -823,11 +813,6 @@ func GetUserOrgTeams(orgID, userID int64) ([]*Team, error) {
return getUserOrgTeams(x, orgID, userID) return getUserOrgTeams(x, orgID, userID)
} }
// GetUserTeams returns all teams that user belongs across all organizations.
func GetUserTeams(userID int64, listOptions ListOptions) ([]*Team, error) {
return getUserTeams(x, userID, listOptions)
}
// AddTeamMember adds new membership of given team to given organization, // AddTeamMember adds new membership of given team to given organization,
// the user will have membership to given organization automatically when needed. // the user will have membership to given organization automatically when needed.
func AddTeamMember(team *Team, userID int64) error { func AddTeamMember(team *Team, userID int64) error {

@ -286,7 +286,7 @@ func TestGetTeamMembers(t *testing.T) {
func TestGetUserTeams(t *testing.T) { func TestGetUserTeams(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
test := func(userID int64) { test := func(userID int64) {
teams, err := GetUserTeams(userID, ListOptions{}) teams, _, err := SearchTeam(&SearchTeamOptions{UserID: userID})
assert.NoError(t, err) assert.NoError(t, err)
for _, team := range teams { for _, team := range teams {
AssertExistsAndLoadBean(t, &TeamUser{TeamID: team.ID, UID: userID}) AssertExistsAndLoadBean(t, &TeamUser{TeamID: team.ID, UID: userID})

@ -86,7 +86,7 @@ func TestUser_GetOwnerTeam(t *testing.T) {
func TestUser_GetTeams(t *testing.T) { func TestUser_GetTeams(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
org := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) org := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User)
assert.NoError(t, org.GetTeams(&SearchTeamOptions{})) assert.NoError(t, org.LoadTeams())
if assert.Len(t, org.Teams, 4) { if assert.Len(t, org.Teams, 4) {
assert.Equal(t, int64(1), org.Teams[0].ID) assert.Equal(t, int64(1), org.Teams[0].ID)
assert.Equal(t, int64(2), org.Teams[1].ID) assert.Equal(t, int64(2), org.Teams[1].ID)

@ -1125,8 +1125,8 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, overwriteO
// Give access to all members in teams with access to all repositories. // Give access to all members in teams with access to all repositories.
if u.IsOrganization() { if u.IsOrganization() {
if err := u.getTeams(ctx.e); err != nil { if err := u.loadTeams(ctx.e); err != nil {
return fmt.Errorf("GetTeams: %v", err) return fmt.Errorf("loadTeams: %v", err)
} }
for _, t := range u.Teams { for _, t := range u.Teams {
if t.IncludesAllRepositories { if t.IncludesAllRepositories {
@ -1439,7 +1439,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
return err return err
} }
if org.IsOrganization() { if org.IsOrganization() {
if err = org.getTeams(sess); err != nil { if err = org.loadTeams(sess); err != nil {
return err return err
} }
} }
@ -1453,7 +1453,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
} }
// Delete Deploy Keys // Delete Deploy Keys
deployKeys, err := listDeployKeys(sess, repo.ID, ListOptions{}) deployKeys, err := listDeployKeys(sess, &ListDeployKeysOptions{RepoID: repoID})
if err != nil { if err != nil {
return fmt.Errorf("listDeployKeys: %v", err) return fmt.Errorf("listDeployKeys: %v", err)
} }

@ -102,6 +102,11 @@ func (repo *Repository) GetCollaborators(listOptions ListOptions) ([]*Collaborat
return repo.getCollaborators(x, listOptions) return repo.getCollaborators(x, listOptions)
} }
// CountCollaborators returns total number of collaborators for a repository
func (repo *Repository) CountCollaborators() (int64, error) {
return x.Where("repo_id = ? ", repo.ID).Count(&Collaboration{})
}
func (repo *Repository) getCollaboration(e Engine, uid int64) (*Collaboration, error) { func (repo *Repository) getCollaboration(e Engine, uid int64) (*Collaboration, error) {
collaboration := &Collaboration{ collaboration := &Collaboration{
RepoID: repo.ID, RepoID: repo.ID,

@ -111,7 +111,7 @@ func GenerateGitHooks(ctx DBContext, templateRepo, generateRepo *Repository) err
// GenerateWebhooks generates webhooks from a template repository // GenerateWebhooks generates webhooks from a template repository
func GenerateWebhooks(ctx DBContext, templateRepo, generateRepo *Repository) error { func GenerateWebhooks(ctx DBContext, templateRepo, generateRepo *Repository) error {
templateWebhooks, err := GetWebhooksByRepoID(templateRepo.ID, ListOptions{}) templateWebhooks, err := ListWebhooksByOpts(&ListWebhookOptions{RepoID: templateRepo.ID})
if err != nil { if err != nil {
return err return err
} }

@ -291,8 +291,8 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
} }
if newOwner.IsOrganization() { if newOwner.IsOrganization() {
if err := newOwner.getTeams(sess); err != nil { if err := newOwner.loadTeams(sess); err != nil {
return fmt.Errorf("GetTeams: %v", err) return fmt.Errorf("LoadTeams: %v", err)
} }
for _, t := range newOwner.Teams { for _, t := range newOwner.Teams {
if t.IncludesAllRepositories { if t.IncludesAllRepositories {

@ -208,6 +208,11 @@ func FindReviews(opts FindReviewOptions) ([]*Review, error) {
return findReviews(x, opts) return findReviews(x, opts)
} }
// CountReviews returns count of reviews passing FindReviewOptions
func CountReviews(opts FindReviewOptions) (int64, error) {
return x.Where(opts.toCond()).Count(&Review{})
}
// CreateReviewOptions represent the options to create a review. Type, Issue and Reviewer are required. // CreateReviewOptions represent the options to create a review. Type, Issue and Reviewer are required.
type CreateReviewOptions struct { type CreateReviewOptions struct {
Content string Content string

@ -205,6 +205,12 @@ func ListPublicKeys(uid int64, listOptions ListOptions) ([]*PublicKey, error) {
return keys, sess.Find(&keys) return keys, sess.Find(&keys)
} }
// CountPublicKeys count public keys a user has
func CountPublicKeys(userID int64) (int64, error) {
sess := x.Where("owner_id = ? AND type != ?", userID, KeyTypePrincipal)
return sess.Count(&PublicKey{})
}
// ListPublicKeysBySource returns a list of synchronized public keys for a given user and login source. // ListPublicKeysBySource returns a list of synchronized public keys for a given user and login source.
func ListPublicKeysBySource(uid, loginSourceID int64) ([]*PublicKey, error) { func ListPublicKeysBySource(uid, loginSourceID int64) ([]*PublicKey, error) {
keys := make([]*PublicKey, 0, 5) keys := make([]*PublicKey, 0, 5)

@ -264,17 +264,40 @@ func deleteDeployKey(sess Engine, doer *User, id int64) error {
return nil return nil
} }
// ListDeployKeys returns all deploy keys by given repository ID. // ListDeployKeysOptions are options for ListDeployKeys
func ListDeployKeys(repoID int64, listOptions ListOptions) ([]*DeployKey, error) { type ListDeployKeysOptions struct {
return listDeployKeys(x, repoID, listOptions) ListOptions
RepoID int64
KeyID int64
Fingerprint string
}
func (opt ListDeployKeysOptions) toCond() builder.Cond {
cond := builder.NewCond()
if opt.RepoID != 0 {
cond = cond.And(builder.Eq{"repo_id": opt.RepoID})
}
if opt.KeyID != 0 {
cond = cond.And(builder.Eq{"key_id": opt.KeyID})
}
if opt.Fingerprint != "" {
cond = cond.And(builder.Eq{"fingerprint": opt.Fingerprint})
}
return cond
} }
func listDeployKeys(e Engine, repoID int64, listOptions ListOptions) ([]*DeployKey, error) { // ListDeployKeys returns a list of deploy keys matching the provided arguments.
sess := e.Where("repo_id = ?", repoID) func ListDeployKeys(opts *ListDeployKeysOptions) ([]*DeployKey, error) {
if listOptions.Page != 0 { return listDeployKeys(x, opts)
sess = listOptions.setSessionPagination(sess) }
keys := make([]*DeployKey, 0, listOptions.PageSize) func listDeployKeys(e Engine, opts *ListDeployKeysOptions) ([]*DeployKey, error) {
sess := e.Where(opts.toCond())
if opts.Page != 0 {
sess = opts.setSessionPagination(sess)
keys := make([]*DeployKey, 0, opts.PageSize)
return keys, sess.Find(&keys) return keys, sess.Find(&keys)
} }
@ -282,18 +305,7 @@ func listDeployKeys(e Engine, repoID int64, listOptions ListOptions) ([]*DeployK
return keys, sess.Find(&keys) return keys, sess.Find(&keys)
} }
// SearchDeployKeys returns a list of deploy keys matching the provided arguments. // CountDeployKeys returns count deploy keys matching the provided arguments.
func SearchDeployKeys(repoID, keyID int64, fingerprint string) ([]*DeployKey, error) { func CountDeployKeys(opts *ListDeployKeysOptions) (int64, error) {
keys := make([]*DeployKey, 0, 5) return x.Where(opts.toCond()).Count(&DeployKey{})
cond := builder.NewCond()
if repoID != 0 {
cond = cond.And(builder.Eq{"repo_id": repoID})
}
if keyID != 0 {
cond = cond.And(builder.Eq{"key_id": keyID})
}
if fingerprint != "" {
cond = cond.And(builder.Eq{"fingerprint": fingerprint})
}
return keys, x.Where(cond).Find(&keys)
} }

@ -122,6 +122,15 @@ func UpdateAccessToken(t *AccessToken) error {
return err return err
} }
// CountAccessTokens count access tokens belongs to given user by options
func CountAccessTokens(opts ListAccessTokensOptions) (int64, error) {
sess := x.Where("uid=?", opts.UserID)
if len(opts.Name) != 0 {
sess = sess.Where("name=?", opts.Name)
}
return sess.Count(&AccessToken{})
}
// DeleteAccessTokenByID deletes access token by given ID. // DeleteAccessTokenByID deletes access token by given ID.
func DeleteAccessTokenByID(id, userID int64) error { func DeleteAccessTokenByID(id, userID int64) error {
cnt, err := x.ID(id).Delete(&AccessToken{ cnt, err := x.ID(id).Delete(&AccessToken{

@ -184,7 +184,7 @@ func (opts *FindTopicOptions) toConds() builder.Cond {
} }
// FindTopics retrieves the topics via FindTopicOptions // FindTopics retrieves the topics via FindTopicOptions
func FindTopics(opts *FindTopicOptions) (topics []*Topic, err error) { func FindTopics(opts *FindTopicOptions) ([]*Topic, int64, error) {
sess := x.Select("topic.*").Where(opts.toConds()) sess := x.Select("topic.*").Where(opts.toConds())
if opts.RepoID > 0 { if opts.RepoID > 0 {
sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id")
@ -192,7 +192,18 @@ func FindTopics(opts *FindTopicOptions) (topics []*Topic, err error) {
if opts.PageSize != 0 && opts.Page != 0 { if opts.PageSize != 0 && opts.Page != 0 {
sess = opts.setSessionPagination(sess) sess = opts.setSessionPagination(sess)
} }
return topics, sess.Desc("topic.repo_count").Find(&topics) topics := make([]*Topic, 0, 10)
total, err := sess.Desc("topic.repo_count").FindAndCount(&topics)
return topics, total, err
}
// CountTopics counts the number of topics matching the FindTopicOptions
func CountTopics(opts *FindTopicOptions) (int64, error) {
sess := x.Where(opts.toConds())
if opts.RepoID > 0 {
sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id")
}
return sess.Count(new(Topic))
} }
// GetRepoTopicByName retrieves topic from name for a repo if it exist // GetRepoTopicByName retrieves topic from name for a repo if it exist
@ -269,7 +280,7 @@ func DeleteTopic(repoID int64, topicName string) (*Topic, error) {
// SaveTopics save topics to a repository // SaveTopics save topics to a repository
func SaveTopics(repoID int64, topicNames ...string) error { func SaveTopics(repoID int64, topicNames ...string) error {
topics, err := FindTopics(&FindTopicOptions{ topics, _, err := FindTopics(&FindTopicOptions{
RepoID: repoID, RepoID: repoID,
}) })
if err != nil { if err != nil {

@ -17,17 +17,18 @@ func TestAddTopic(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
topics, err := FindTopics(&FindTopicOptions{}) topics, _, err := FindTopics(&FindTopicOptions{})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, topics, totalNrOfTopics) assert.Len(t, topics, totalNrOfTopics)
topics, err = FindTopics(&FindTopicOptions{ topics, total, err := FindTopics(&FindTopicOptions{
ListOptions: ListOptions{Page: 1, PageSize: 2}, ListOptions: ListOptions{Page: 1, PageSize: 2},
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, topics, 2) assert.Len(t, topics, 2)
assert.EqualValues(t, 6, total)
topics, err = FindTopics(&FindTopicOptions{ topics, _, err = FindTopics(&FindTopicOptions{
RepoID: 1, RepoID: 1,
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -35,11 +36,11 @@ func TestAddTopic(t *testing.T) {
assert.NoError(t, SaveTopics(2, "golang")) assert.NoError(t, SaveTopics(2, "golang"))
repo2NrOfTopics = 1 repo2NrOfTopics = 1
topics, err = FindTopics(&FindTopicOptions{}) topics, _, err = FindTopics(&FindTopicOptions{})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, topics, totalNrOfTopics) assert.Len(t, topics, totalNrOfTopics)
topics, err = FindTopics(&FindTopicOptions{ topics, _, err = FindTopics(&FindTopicOptions{
RepoID: 2, RepoID: 2,
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -52,11 +53,11 @@ func TestAddTopic(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 1, topic.RepoCount) assert.EqualValues(t, 1, topic.RepoCount)
topics, err = FindTopics(&FindTopicOptions{}) topics, _, err = FindTopics(&FindTopicOptions{})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, topics, totalNrOfTopics) assert.Len(t, topics, totalNrOfTopics)
topics, err = FindTopics(&FindTopicOptions{ topics, _, err = FindTopics(&FindTopicOptions{
RepoID: 2, RepoID: 2,
}) })
assert.NoError(t, err) assert.NoError(t, err)

@ -1704,7 +1704,7 @@ func GetStarredRepos(userID int64, private bool, listOptions ListOptions) ([]*Re
} }
// GetWatchedRepos returns the repos watched by a particular user // GetWatchedRepos returns the repos watched by a particular user
func GetWatchedRepos(userID int64, private bool, listOptions ListOptions) ([]*Repository, error) { func GetWatchedRepos(userID int64, private bool, listOptions ListOptions) ([]*Repository, int64, error) {
sess := x.Where("watch.user_id=?", userID). sess := x.Where("watch.user_id=?", userID).
And("`watch`.mode<>?", RepoWatchModeDont). And("`watch`.mode<>?", RepoWatchModeDont).
Join("LEFT", "watch", "`repository`.id=`watch`.repo_id") Join("LEFT", "watch", "`repository`.id=`watch`.repo_id")
@ -1716,11 +1716,13 @@ func GetWatchedRepos(userID int64, private bool, listOptions ListOptions) ([]*Re
sess = listOptions.setSessionPagination(sess) sess = listOptions.setSessionPagination(sess)
repos := make([]*Repository, 0, listOptions.PageSize) repos := make([]*Repository, 0, listOptions.PageSize)
return repos, sess.Find(&repos) total, err := sess.FindAndCount(&repos)
return repos, total, err
} }
repos := make([]*Repository, 0, 10) repos := make([]*Repository, 0, 10)
return repos, sess.Find(&repos) total, err := sess.FindAndCount(&repos)
return repos, total, err
} }
// IterateUser iterate users // IterateUser iterate users

@ -16,8 +16,10 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
gouuid "github.com/google/uuid" gouuid "github.com/google/uuid"
"xorm.io/builder"
) )
// HookContentType is the content type of a web hook // HookContentType is the content type of a web hook
@ -387,53 +389,51 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) {
}) })
} }
// GetActiveWebhooksByRepoID returns all active webhooks of repository. // ListWebhookOptions are options to filter webhooks on ListWebhooksByOpts
func GetActiveWebhooksByRepoID(repoID int64) ([]*Webhook, error) { type ListWebhookOptions struct {
return getActiveWebhooksByRepoID(x, repoID) ListOptions
RepoID int64
OrgID int64
IsActive util.OptionalBool
} }
func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) { func (opts *ListWebhookOptions) toCond() builder.Cond {
webhooks := make([]*Webhook, 0, 5) cond := builder.NewCond()
return webhooks, e.Where("is_active=?", true). if opts.RepoID != 0 {
Find(&webhooks, &Webhook{RepoID: repoID}) cond = cond.And(builder.Eq{"webhook.repo_id": opts.RepoID})
} }
if opts.OrgID != 0 {
// GetWebhooksByRepoID returns all webhooks of a repository. cond = cond.And(builder.Eq{"webhook.org_id": opts.OrgID})
func GetWebhooksByRepoID(repoID int64, listOptions ListOptions) ([]*Webhook, error) { }
if listOptions.Page == 0 { if !opts.IsActive.IsNone() {
webhooks := make([]*Webhook, 0, 5) cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.IsTrue()})
return webhooks, x.Find(&webhooks, &Webhook{RepoID: repoID})
} }
return cond
}
sess := listOptions.getPaginatedSession() func listWebhooksByOpts(e Engine, opts *ListWebhookOptions) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, listOptions.PageSize) sess := e.Where(opts.toCond())
return webhooks, sess.Find(&webhooks, &Webhook{RepoID: repoID}) if opts.Page != 0 {
} sess = opts.setSessionPagination(sess)
webhooks := make([]*Webhook, 0, opts.PageSize)
err := sess.Find(&webhooks)
return webhooks, err
}
// GetActiveWebhooksByOrgID returns all active webhooks for an organization. webhooks := make([]*Webhook, 0, 10)
func GetActiveWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) { err := sess.Find(&webhooks)
return getActiveWebhooksByOrgID(x, orgID) return webhooks, err
} }
func getActiveWebhooksByOrgID(e Engine, orgID int64) (ws []*Webhook, err error) { // ListWebhooksByOpts return webhooks based on options
err = e. func ListWebhooksByOpts(opts *ListWebhookOptions) ([]*Webhook, error) {
Where("org_id=?", orgID). return listWebhooksByOpts(x, opts)
And("is_active=?", true).
Find(&ws)
return ws, err
} }
// GetWebhooksByOrgID returns paginated webhooks for an organization. // CountWebhooksByOpts count webhooks based on options and ignore pagination
func GetWebhooksByOrgID(orgID int64, listOptions ListOptions) ([]*Webhook, error) { func CountWebhooksByOpts(opts *ListWebhookOptions) (int64, error) {
if listOptions.Page == 0 { return x.Where(opts.toCond()).Count(&Webhook{})
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})
} }
// GetDefaultWebhooks returns all admin-default webhooks. // GetDefaultWebhooks returns all admin-default webhooks.

@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -118,7 +119,7 @@ func TestGetWebhookByOrgID(t *testing.T) {
func TestGetActiveWebhooksByRepoID(t *testing.T) { func TestGetActiveWebhooksByRepoID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
hooks, err := GetActiveWebhooksByRepoID(1) hooks, err := ListWebhooksByOpts(&ListWebhookOptions{RepoID: 1, IsActive: util.OptionalBoolTrue})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, hooks, 1) { if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(1), hooks[0].ID) assert.Equal(t, int64(1), hooks[0].ID)
@ -128,7 +129,7 @@ func TestGetActiveWebhooksByRepoID(t *testing.T) {
func TestGetWebhooksByRepoID(t *testing.T) { func TestGetWebhooksByRepoID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
hooks, err := GetWebhooksByRepoID(1, ListOptions{}) hooks, err := ListWebhooksByOpts(&ListWebhookOptions{RepoID: 1})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, hooks, 2) { if assert.Len(t, hooks, 2) {
assert.Equal(t, int64(1), hooks[0].ID) assert.Equal(t, int64(1), hooks[0].ID)
@ -138,7 +139,7 @@ func TestGetWebhooksByRepoID(t *testing.T) {
func TestGetActiveWebhooksByOrgID(t *testing.T) { func TestGetActiveWebhooksByOrgID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
hooks, err := GetActiveWebhooksByOrgID(3) hooks, err := ListWebhooksByOpts(&ListWebhookOptions{OrgID: 3, IsActive: util.OptionalBoolTrue})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, hooks, 1) { if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(3), hooks[0].ID) assert.Equal(t, int64(3), hooks[0].ID)
@ -148,7 +149,7 @@ func TestGetActiveWebhooksByOrgID(t *testing.T) {
func TestGetWebhooksByOrgID(t *testing.T) { func TestGetWebhooksByOrgID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
hooks, err := GetWebhooksByOrgID(3, ListOptions{}) hooks, err := ListWebhooksByOpts(&ListWebhookOptions{OrgID: 3})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, hooks, 1) { if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(3), hooks[0].ID) assert.Equal(t, int64(3), hooks[0].ID)

@ -181,6 +181,23 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
if len(links) > 0 { if len(links) > 0 {
ctx.Header().Set("Link", strings.Join(links, ",")) ctx.Header().Set("Link", strings.Join(links, ","))
ctx.AppendAccessControlExposeHeaders("Link")
}
}
// SetTotalCountHeader set "X-Total-Count" header
func (ctx *APIContext) SetTotalCountHeader(total int64) {
ctx.Header().Set("X-Total-Count", fmt.Sprint(total))
ctx.AppendAccessControlExposeHeaders("X-Total-Count")
}
// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
func (ctx *APIContext) AppendAccessControlExposeHeaders(names ...string) {
val := ctx.Header().Get("Access-Control-Expose-Headers")
if len(val) != 0 {
ctx.Header().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
} else {
ctx.Header().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
} }
} }

@ -123,8 +123,8 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
// Team. // Team.
if ctx.Org.IsMember { if ctx.Org.IsMember {
if ctx.Org.IsOwner { if ctx.Org.IsOwner {
if err := org.GetTeams(&models.SearchTeamOptions{}); err != nil { if err := org.LoadTeams(); err != nil {
ctx.ServerError("GetTeams", err) ctx.ServerError("LoadTeams", err)
return return
} }
} else { } else {

@ -10,6 +10,7 @@ import (
"strings" "strings"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
) )
// TagPrefix tags prefix path on the repository // TagPrefix tags prefix path on the repository
@ -160,24 +161,18 @@ func (repo *Repository) GetTag(name string) (*Tag, error) {
} }
// GetTagInfos returns all tag infos of the repository. // GetTagInfos returns all tag infos of the repository.
func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, error) { func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
// TODO this a slow implementation, makes one git command per tag // TODO this a slow implementation, makes one git command per tag
stdout, err := NewCommand("tag").RunInDir(repo.Path) stdout, err := NewCommand("tag").RunInDir(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, 0, err
} }
tagNames := strings.Split(strings.TrimRight(stdout, "\n"), "\n") tagNames := strings.Split(strings.TrimRight(stdout, "\n"), "\n")
tagsTotal := len(tagNames)
if page != 0 { if page != 0 {
skip := (page - 1) * pageSize tagNames = util.PaginateSlice(tagNames, page, pageSize).([]string)
if skip >= len(tagNames) {
return nil, nil
}
if (len(tagNames) - skip) < pageSize {
pageSize = len(tagNames) - skip
}
tagNames = tagNames[skip : skip+pageSize]
} }
var tags = make([]*Tag, 0, len(tagNames)) var tags = make([]*Tag, 0, len(tagNames))
@ -189,13 +184,13 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, error) {
tag, err := repo.GetTag(tagName) tag, err := repo.GetTag(tagName)
if err != nil { if err != nil {
return nil, err return nil, tagsTotal, err
} }
tag.Name = tagName tag.Name = tagName
tags = append(tags, tag) tags = append(tags, tag)
} }
sortTagsByTime(tags) sortTagsByTime(tags)
return tags, nil return tags, tagsTotal, nil
} }
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated) // GetTagType gets the type of the tag, either commit (simple) or tag (annotated)

@ -18,9 +18,10 @@ func TestRepository_GetTags(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
defer bareRepo1.Close() defer bareRepo1.Close()
tags, err := bareRepo1.GetTagInfos(0, 0) tags, total, err := bareRepo1.GetTagInfos(0, 0)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, tags, 1) assert.Len(t, tags, 1)
assert.Equal(t, len(tags), total)
assert.EqualValues(t, "test", tags[0].Name) assert.EqualValues(t, "test", tags[0].Name)
assert.EqualValues(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", tags[0].ID.String()) assert.EqualValues(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", tags[0].ID.String())
assert.EqualValues(t, "tag", tags[0].Type) assert.EqualValues(t, "tag", tags[0].Type)

@ -54,14 +54,14 @@ func TestGiteaUploadRepo(t *testing.T) {
assert.True(t, repo.HasWiki()) assert.True(t, repo.HasWiki())
assert.EqualValues(t, models.RepositoryReady, repo.Status) assert.EqualValues(t, models.RepositoryReady, repo.Status)
milestones, err := models.GetMilestones(models.GetMilestonesOption{ milestones, _, err := models.GetMilestones(models.GetMilestonesOption{
RepoID: repo.ID, RepoID: repo.ID,
State: structs.StateOpen, State: structs.StateOpen,
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, milestones, 1) assert.Len(t, milestones, 1)
milestones, err = models.GetMilestones(models.GetMilestonesOption{ milestones, _, err = models.GetMilestones(models.GetMilestonesOption{
RepoID: repo.ID, RepoID: repo.ID,
State: structs.StateClosed, State: structs.StateClosed,
}) })

@ -5,7 +5,6 @@
package admin package admin
import ( import (
"fmt"
"net/http" "net/http"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@ -47,8 +46,7 @@ func ListUnadoptedRepositories(ctx *context.APIContext) {
ctx.InternalServerError(err) ctx.InternalServerError(err)
} }
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.SetTotalCountHeader(int64(count))
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count")
ctx.JSON(http.StatusOK, repoNames) ctx.JSON(http.StatusOK, repoNames)
} }

@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/modules/cron" "code.gitea.io/gitea/modules/cron"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/routers/api/v1/utils"
) )
@ -36,12 +37,10 @@ func ListCronTasks(ctx *context.APIContext) {
// "403": // "403":
// "$ref": "#/responses/forbidden" // "$ref": "#/responses/forbidden"
tasks := cron.ListTasks() tasks := cron.ListTasks()
listOpts := utils.GetListOptions(ctx) count := len(tasks)
start, end := listOpts.GetStartEnd()
if len(tasks) > listOpts.PageSize { listOpts := utils.GetListOptions(ctx)
tasks = tasks[start:end] tasks = util.PaginateSlice(tasks, listOpts.Page, listOpts.PageSize).(cron.TaskTable)
}
res := make([]structs.Cron, len(tasks)) res := make([]structs.Cron, len(tasks))
for i, task := range tasks { for i, task := range tasks {
@ -53,6 +52,8 @@ func ListCronTasks(ctx *context.APIContext) {
ExecTimes: task.ExecTimes, ExecTimes: task.ExecTimes,
} }
} }
ctx.SetTotalCountHeader(int64(count))
ctx.JSON(http.StatusOK, res) ctx.JSON(http.StatusOK, res)
} }

@ -6,7 +6,6 @@
package admin package admin
import ( import (
"fmt"
"net/http" "net/http"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@ -121,7 +120,6 @@ func GetAllOrgs(ctx *context.APIContext) {
} }
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.SetTotalCountHeader(maxResults)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, &orgs) ctx.JSON(http.StatusOK, &orgs)
} }

@ -423,7 +423,6 @@ func GetAllUsers(ctx *context.APIContext) {
} }
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.SetTotalCountHeader(maxResults)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, &results) ctx.JSON(http.StatusOK, &results)
} }

@ -108,6 +108,12 @@ func ListRepoNotifications(ctx *context.APIContext) {
} }
opts.RepoID = ctx.Repo.Repository.ID opts.RepoID = ctx.Repo.Repository.ID
totalCount, err := models.CountNotifications(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
nl, err := models.GetNotifications(opts) nl, err := models.GetNotifications(opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
@ -119,6 +125,8 @@ func ListRepoNotifications(ctx *context.APIContext) {
return return
} }
ctx.SetTotalCountHeader(totalCount)
ctx.JSON(http.StatusOK, convert.ToNotifications(nl)) ctx.JSON(http.StatusOK, convert.ToNotifications(nl))
} }

@ -68,6 +68,12 @@ func ListNotifications(ctx *context.APIContext) {
return return
} }
totalCount, err := models.CountNotifications(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
nl, err := models.GetNotifications(opts) nl, err := models.GetNotifications(opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
@ -79,6 +85,7 @@ func ListNotifications(ctx *context.APIContext) {
return return
} }
ctx.SetTotalCountHeader(totalCount)
ctx.JSON(http.StatusOK, convert.ToNotifications(nl)) ctx.JSON(http.StatusOK, convert.ToNotifications(nl))
} }

@ -40,16 +40,29 @@ func ListHooks(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/HookList" // "$ref": "#/responses/HookList"
org := ctx.Org.Organization opts := &models.ListWebhookOptions{
orgHooks, err := models.GetWebhooksByOrgID(org.ID, utils.GetListOptions(ctx)) ListOptions: utils.GetListOptions(ctx),
OrgID: ctx.Org.Organization.ID,
}
count, err := models.CountWebhooksByOpts(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetWebhooksByOrgID", err) ctx.InternalServerError(err)
return return
} }
orgHooks, err := models.ListWebhooksByOpts(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
hooks := make([]*api.Hook, len(orgHooks)) hooks := make([]*api.Hook, len(orgHooks))
for i, hook := range orgHooks { for i, hook := range orgHooks {
hooks[i] = convert.ToHook(org.HomeLink(), hook) hooks[i] = convert.ToHook(ctx.Org.Organization.HomeLink(), hook)
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, hooks) ctx.JSON(http.StatusOK, hooks)
} }

@ -49,6 +49,13 @@ func ListLabels(ctx *context.APIContext) {
return return
} }
count, err := models.CountLabelsByOrgID(ctx.Org.Organization.ID)
if err != nil {
ctx.InternalServerError(err)
return
}
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, convert.ToLabelList(labels)) ctx.JSON(http.StatusOK, convert.ToLabelList(labels))
} }

@ -18,15 +18,21 @@ import (
// listMembers list an organization's members // listMembers list an organization's members
func listMembers(ctx *context.APIContext, publicOnly bool) { func listMembers(ctx *context.APIContext, publicOnly bool) {
var members []*models.User opts := &models.FindOrgMembersOpts{
members, _, err := models.FindOrgMembers(&models.FindOrgMembersOpts{
OrgID: ctx.Org.Organization.ID, OrgID: ctx.Org.Organization.ID,
PublicOnly: publicOnly, PublicOnly: publicOnly,
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
}) }
count, err := models.CountOrgMembers(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
members, _, err := models.FindOrgMembers(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err) ctx.InternalServerError(err)
return return
} }
@ -35,6 +41,7 @@ func listMembers(ctx *context.APIContext, publicOnly bool) {
apiMembers[i] = convert.ToUser(member, ctx.User) apiMembers[i] = convert.ToUser(member, ctx.User)
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, apiMembers) ctx.JSON(http.StatusOK, apiMembers)
} }

@ -6,7 +6,6 @@
package org package org
import ( import (
"fmt"
"net/http" "net/http"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@ -38,9 +37,8 @@ func listUserOrgs(ctx *context.APIContext, u *models.User) {
apiOrgs[i] = convert.ToOrganization(orgs[i]) apiOrgs[i] = convert.ToOrganization(orgs[i])
} }
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.SetLinkHeader(maxResults, listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.SetTotalCountHeader(int64(maxResults))
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, &apiOrgs) ctx.JSON(http.StatusOK, &apiOrgs)
} }
@ -145,8 +143,7 @@ func GetAll(ctx *context.APIContext) {
} }
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.SetTotalCountHeader(maxResults)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, &orgs) ctx.JSON(http.StatusOK, &orgs)
} }

@ -6,7 +6,6 @@
package org package org
import ( import (
"fmt"
"net/http" "net/http"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@ -44,23 +43,27 @@ func ListTeams(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/TeamList" // "$ref": "#/responses/TeamList"
org := ctx.Org.Organization teams, count, err := models.SearchTeam(&models.SearchTeamOptions{
if err := org.GetTeams(&models.SearchTeamOptions{
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
}); err != nil { OrgID: ctx.Org.Organization.ID,
ctx.Error(http.StatusInternalServerError, "GetTeams", err) })
if err != nil {
ctx.Error(http.StatusInternalServerError, "LoadTeams", err)
return return
} }
apiTeams := make([]*api.Team, len(org.Teams)) apiTeams := make([]*api.Team, len(teams))
for i := range org.Teams { for i := range teams {
if err := org.Teams[i].GetUnits(); err != nil { if err := teams[i].GetUnits(); err != nil {
ctx.Error(http.StatusInternalServerError, "GetUnits", err) ctx.Error(http.StatusInternalServerError, "GetUnits", err)
return return
} }
apiTeams[i] = convert.ToTeam(org.Teams[i]) apiTeams[i] = convert.ToTeam(teams[i])
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, apiTeams) ctx.JSON(http.StatusOK, apiTeams)
} }
@ -84,7 +87,10 @@ func ListUserTeams(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/TeamList" // "$ref": "#/responses/TeamList"
teams, err := models.GetUserTeams(ctx.User.ID, utils.GetListOptions(ctx)) teams, count, err := models.SearchTeam(&models.SearchTeamOptions{
ListOptions: utils.GetListOptions(ctx),
UserID: ctx.User.ID,
})
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserTeams", err) ctx.Error(http.StatusInternalServerError, "GetUserTeams", err)
return return
@ -106,6 +112,8 @@ func ListUserTeams(ctx *context.APIContext) {
apiTeams[i] = convert.ToTeam(teams[i]) apiTeams[i] = convert.ToTeam(teams[i])
apiTeams[i].Organization = apiOrg apiTeams[i].Organization = apiOrg
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, apiTeams) ctx.JSON(http.StatusOK, apiTeams)
} }
@ -327,17 +335,19 @@ func GetTeamMembers(ctx *context.APIContext) {
ctx.NotFound() ctx.NotFound()
return return
} }
team := ctx.Org.Team
if err := team.GetMembers(&models.SearchMembersOptions{ if err := ctx.Org.Team.GetMembers(&models.SearchMembersOptions{
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
}); err != nil { }); err != nil {
ctx.Error(http.StatusInternalServerError, "GetTeamMembers", err) ctx.Error(http.StatusInternalServerError, "GetTeamMembers", err)
return return
} }
members := make([]*api.User, len(team.Members)) members := make([]*api.User, len(ctx.Org.Team.Members))
for i, member := range team.Members { for i, member := range ctx.Org.Team.Members {
members[i] = convert.ToUser(member, ctx.User) members[i] = convert.ToUser(member, ctx.User)
} }
ctx.SetTotalCountHeader(int64(ctx.Org.Team.NumMembers))
ctx.JSON(http.StatusOK, members) ctx.JSON(http.StatusOK, members)
} }
@ -687,8 +697,7 @@ func SearchTeam(ctx *context.APIContext) {
} }
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.SetTotalCountHeader(maxResults)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, map[string]interface{}{ ctx.JSON(http.StatusOK, map[string]interface{}{
"ok": true, "ok": true,
"data": apiTeams, "data": apiTeams,

@ -282,9 +282,8 @@ func ListBranches(ctx *context.APIContext) {
} }
} }
ctx.SetLinkHeader(int(totalNumOfBranches), listOptions.PageSize) ctx.SetLinkHeader(totalNumOfBranches, listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalNumOfBranches)) ctx.SetTotalCountHeader(int64(totalNumOfBranches))
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, &apiBranches) ctx.JSON(http.StatusOK, &apiBranches)
} }

@ -47,15 +47,24 @@ func ListCollaborators(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/UserList" // "$ref": "#/responses/UserList"
count, err := ctx.Repo.Repository.CountCollaborators()
if err != nil {
ctx.InternalServerError(err)
return
}
collaborators, err := ctx.Repo.Repository.GetCollaborators(utils.GetListOptions(ctx)) collaborators, err := ctx.Repo.Repository.GetCollaborators(utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
return return
} }
users := make([]*api.User, len(collaborators)) users := make([]*api.User, len(collaborators))
for i, collaborator := range collaborators { for i, collaborator := range collaborators {
users[i] = convert.ToUser(collaborator.User, ctx.User) users[i] = convert.ToUser(collaborator.User, ctx.User)
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, users) ctx.JSON(http.StatusOK, users)
} }

@ -200,16 +200,16 @@ func GetAllCommits(ctx *context.APIContext) {
} }
} }
ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize)
ctx.SetTotalCountHeader(commitsCountTotal)
// kept for backwards compatibility // kept for backwards compatibility
ctx.Header().Set("X-Page", strconv.Itoa(listOptions.Page)) ctx.Header().Set("X-Page", strconv.Itoa(listOptions.Page))
ctx.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize)) ctx.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
ctx.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10)) ctx.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10))
ctx.Header().Set("X-PageCount", strconv.Itoa(pageCount)) ctx.Header().Set("X-PageCount", strconv.Itoa(pageCount))
ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount)) ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount))
ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-Total", "X-PageCount", "X-HasMore")
ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", commitsCountTotal))
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, X-PerPage, X-Total, X-PageCount, X-HasMore, Link")
ctx.JSON(http.StatusOK, &apiCommits) ctx.JSON(http.StatusOK, &apiCommits)
} }

@ -62,6 +62,8 @@ func ListForks(ctx *context.APIContext) {
} }
apiForks[i] = convert.ToRepo(fork, access) apiForks[i] = convert.ToRepo(fork, access)
} }
ctx.SetTotalCountHeader(int64(ctx.Repo.Repository.NumForks))
ctx.JSON(http.StatusOK, apiForks) ctx.JSON(http.StatusOK, apiForks)
} }

@ -48,9 +48,20 @@ func ListHooks(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/HookList" // "$ref": "#/responses/HookList"
hooks, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID, utils.GetListOptions(ctx)) opts := &models.ListWebhookOptions{
ListOptions: utils.GetListOptions(ctx),
RepoID: ctx.Repo.Repository.ID,
}
count, err := models.CountWebhooksByOpts(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetWebhooksByRepoID", err) ctx.InternalServerError(err)
return
}
hooks, err := models.ListWebhooksByOpts(opts)
if err != nil {
ctx.InternalServerError(err)
return return
} }
@ -58,6 +69,8 @@ func ListHooks(ctx *context.APIContext) {
for i := range hooks { for i := range hooks {
apiHooks[i] = convert.ToHook(ctx.Repo.RepoLink, hooks[i]) apiHooks[i] = convert.ToHook(ctx.Repo.RepoLink, hooks[i])
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, &apiHooks) ctx.JSON(http.StatusOK, &apiHooks)
} }

@ -232,8 +232,7 @@ func SearchIssues(ctx *context.APIContext) {
} }
ctx.SetLinkHeader(int(filteredCount), setting.UI.IssuePagingNum) ctx.SetLinkHeader(int(filteredCount), setting.UI.IssuePagingNum)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", filteredCount)) ctx.SetTotalCountHeader(filteredCount)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues)) ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues))
} }
@ -442,8 +441,7 @@ func ListIssues(ctx *context.APIContext) {
} }
ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize) ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", filteredCount)) ctx.SetTotalCountHeader(filteredCount)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues)) ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues))
} }

@ -68,17 +68,25 @@ func ListIssueComments(ctx *context.APIContext) {
} }
issue.Repo = ctx.Repo.Repository issue.Repo = ctx.Repo.Repository
comments, err := models.FindComments(models.FindCommentsOptions{ opts := &models.FindCommentsOptions{
IssueID: issue.ID, IssueID: issue.ID,
Since: since, Since: since,
Before: before, Before: before,
Type: models.CommentTypeComment, Type: models.CommentTypeComment,
}) }
comments, err := models.FindComments(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "FindComments", err) ctx.Error(http.StatusInternalServerError, "FindComments", err)
return return
} }
totalCount, err := models.CountComments(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
if err := models.CommentList(comments).LoadPosters(); err != nil { if err := models.CommentList(comments).LoadPosters(); err != nil {
ctx.Error(http.StatusInternalServerError, "LoadPosters", err) ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
return return
@ -89,6 +97,8 @@ func ListIssueComments(ctx *context.APIContext) {
comment.Issue = issue comment.Issue = issue
apiComments[i] = convert.ToComment(comments[i]) apiComments[i] = convert.ToComment(comments[i])
} }
ctx.SetTotalCountHeader(totalCount)
ctx.JSON(http.StatusOK, &apiComments) ctx.JSON(http.StatusOK, &apiComments)
} }
@ -138,18 +148,26 @@ func ListRepoIssueComments(ctx *context.APIContext) {
return return
} }
comments, err := models.FindComments(models.FindCommentsOptions{ opts := &models.FindCommentsOptions{
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
Type: models.CommentTypeComment, Type: models.CommentTypeComment,
Since: since, Since: since,
Before: before, Before: before,
}) }
comments, err := models.FindComments(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "FindComments", err) ctx.Error(http.StatusInternalServerError, "FindComments", err)
return return
} }
totalCount, err := models.CountComments(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
if err = models.CommentList(comments).LoadPosters(); err != nil { if err = models.CommentList(comments).LoadPosters(); err != nil {
ctx.Error(http.StatusInternalServerError, "LoadPosters", err) ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
return return
@ -171,6 +189,8 @@ func ListRepoIssueComments(ctx *context.APIContext) {
for i := range comments { for i := range comments {
apiComments[i] = convert.ToComment(comments[i]) apiComments[i] = convert.ToComment(comments[i])
} }
ctx.SetTotalCountHeader(totalCount)
ctx.JSON(http.StatusOK, &apiComments) ctx.JSON(http.StatusOK, &apiComments)
} }

@ -225,11 +225,18 @@ func GetStopwatches(ctx *context.APIContext) {
return return
} }
count, err := models.CountUserStopwatches(ctx.User.ID)
if err != nil {
ctx.InternalServerError(err)
return
}
apiSWs, err := convert.ToStopWatches(sws) apiSWs, err := convert.ToStopWatches(sws)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "APIFormat", err) ctx.Error(http.StatusInternalServerError, "APIFormat", err)
return return
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, apiSWs) ctx.JSON(http.StatusOK, apiSWs)
} }

@ -83,7 +83,7 @@ func ListTrackedTimes(ctx *context.APIContext) {
return return
} }
opts := models.FindTrackedTimesOptions{ opts := &models.FindTrackedTimesOptions{
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
RepositoryID: ctx.Repo.Repository.ID, RepositoryID: ctx.Repo.Repository.ID,
IssueID: issue.ID, IssueID: issue.ID,
@ -119,6 +119,12 @@ func ListTrackedTimes(ctx *context.APIContext) {
} }
} }
count, err := models.CountTrackedTimes(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
trackedTimes, err := models.GetTrackedTimes(opts) trackedTimes, err := models.GetTrackedTimes(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err) ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
@ -128,6 +134,8 @@ func ListTrackedTimes(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
return return
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes))
} }
@ -423,7 +431,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
return return
} }
opts := models.FindTrackedTimesOptions{ opts := &models.FindTrackedTimesOptions{
UserID: user.ID, UserID: user.ID,
RepositoryID: ctx.Repo.Repository.ID, RepositoryID: ctx.Repo.Repository.ID,
} }
@ -493,7 +501,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
return return
} }
opts := models.FindTrackedTimesOptions{ opts := &models.FindTrackedTimesOptions{
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
RepositoryID: ctx.Repo.Repository.ID, RepositoryID: ctx.Repo.Repository.ID,
} }
@ -530,6 +538,12 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
} }
} }
count, err := models.CountTrackedTimes(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
trackedTimes, err := models.GetTrackedTimes(opts) trackedTimes, err := models.GetTrackedTimes(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err) ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
@ -539,6 +553,8 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
return return
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes))
} }
@ -573,7 +589,7 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/TrackedTimeList" // "$ref": "#/responses/TrackedTimeList"
opts := models.FindTrackedTimesOptions{ opts := &models.FindTrackedTimesOptions{
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
UserID: ctx.User.ID, UserID: ctx.User.ID,
} }
@ -584,6 +600,12 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
return return
} }
count, err := models.CountTrackedTimes(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
trackedTimes, err := models.GetTrackedTimes(opts) trackedTimes, err := models.GetTrackedTimes(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetTrackedTimesByUser", err) ctx.Error(http.StatusInternalServerError, "GetTrackedTimesByUser", err)
@ -595,5 +617,6 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
return return
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes))
} }

@ -75,26 +75,29 @@ func ListDeployKeys(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/DeployKeyList" // "$ref": "#/responses/DeployKeyList"
var keys []*models.DeployKey opts := &models.ListDeployKeysOptions{
var err error ListOptions: utils.GetListOptions(ctx),
RepoID: ctx.Repo.Repository.ID,
KeyID: ctx.FormInt64("key_id"),
Fingerprint: ctx.FormString("fingerprint"),
}
fingerprint := ctx.FormString("fingerprint") keys, err := models.ListDeployKeys(opts)
keyID := ctx.FormInt64("key_id") if err != nil {
if fingerprint != "" || keyID != 0 { ctx.InternalServerError(err)
keys, err = models.SearchDeployKeys(ctx.Repo.Repository.ID, keyID, fingerprint) return
} else {
keys, err = models.ListDeployKeys(ctx.Repo.Repository.ID, utils.GetListOptions(ctx))
} }
count, err := models.CountDeployKeys(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "ListDeployKeys", err) ctx.InternalServerError(err)
return return
} }
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name) apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
apiKeys := make([]*api.DeployKey, len(keys)) apiKeys := make([]*api.DeployKey, len(keys))
for i := range keys { for i := range keys {
if err = keys[i].GetContent(); err != nil { if err := keys[i].GetContent(); err != nil {
ctx.Error(http.StatusInternalServerError, "GetContent", err) ctx.Error(http.StatusInternalServerError, "GetContent", err)
return return
} }
@ -104,6 +107,7 @@ func ListDeployKeys(ctx *context.APIContext) {
} }
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, &apiKeys) ctx.JSON(http.StatusOK, &apiKeys)
} }

@ -55,6 +55,13 @@ func ListLabels(ctx *context.APIContext) {
return return
} }
count, err := models.CountLabelsByRepoID(ctx.Repo.Repository.ID)
if err != nil {
ctx.InternalServerError(err)
return
}
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, convert.ToLabelList(labels)) ctx.JSON(http.StatusOK, convert.ToLabelList(labels))
} }

@ -57,7 +57,7 @@ func ListMilestones(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/MilestoneList" // "$ref": "#/responses/MilestoneList"
milestones, err := models.GetMilestones(models.GetMilestonesOption{ milestones, total, err := models.GetMilestones(models.GetMilestonesOption{
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
State: api.StateType(ctx.FormString("state")), State: api.StateType(ctx.FormString("state")),
@ -72,6 +72,8 @@ func ListMilestones(ctx *context.APIContext) {
for i := range milestones { for i := range milestones {
apiMilestones[i] = convert.ToAPIMilestone(milestones[i]) apiMilestones[i] = convert.ToAPIMilestone(milestones[i])
} }
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, &apiMilestones) ctx.JSON(http.StatusOK, &apiMilestones)
} }

@ -119,8 +119,7 @@ func ListPullRequests(ctx *context.APIContext) {
} }
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.SetTotalCountHeader(maxResults)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, &apiPrs) ctx.JSON(http.StatusOK, &apiPrs)
} }
@ -1232,13 +1231,14 @@ func GetPullRequestCommits(ctx *context.APIContext) {
apiCommits = append(apiCommits, apiCommit) apiCommits = append(apiCommits, apiCommit)
} }
ctx.SetLinkHeader(int(totalNumberOfCommits), listOptions.PageSize) ctx.SetLinkHeader(totalNumberOfCommits, listOptions.PageSize)
ctx.SetTotalCountHeader(int64(totalNumberOfCommits))
ctx.Header().Set("X-Page", strconv.Itoa(listOptions.Page)) ctx.Header().Set("X-Page", strconv.Itoa(listOptions.Page))
ctx.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize)) ctx.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalNumberOfCommits))
ctx.Header().Set("X-PageCount", strconv.Itoa(totalNumberOfPages)) ctx.Header().Set("X-PageCount", strconv.Itoa(totalNumberOfPages))
ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages)) ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages))
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, X-PerPage, X-Total, X-PageCount, X-HasMore, Link") ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-PageCount", "X-HasMore")
ctx.JSON(http.StatusOK, &apiCommits) ctx.JSON(http.StatusOK, &apiCommits)
} }

@ -78,14 +78,21 @@ func ListPullReviews(ctx *context.APIContext) {
return return
} }
allReviews, err := models.FindReviews(models.FindReviewOptions{ opts := models.FindReviewOptions{
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
Type: models.ReviewTypeUnknown, Type: models.ReviewTypeUnknown,
IssueID: pr.IssueID, IssueID: pr.IssueID,
}) }
allReviews, err := models.FindReviews(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
count, err := models.CountReviews(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "FindReviews", err) ctx.InternalServerError(err)
return return
} }
@ -95,6 +102,7 @@ func ListPullReviews(ctx *context.APIContext) {
return return
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, &apiReviews) ctx.JSON(http.StatusOK, &apiReviews)
} }

@ -5,7 +5,6 @@
package repo package repo
import ( import (
"fmt"
"net/http" "net/http"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@ -142,8 +141,7 @@ func ListReleases(ctx *context.APIContext) {
} }
ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize) ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprint(filteredCount)) ctx.SetTotalCountHeader(filteredCount)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, rels) ctx.JSON(http.StatusOK, rels)
} }

@ -230,8 +230,7 @@ func Search(ctx *context.APIContext) {
} }
ctx.SetLinkHeader(int(count), opts.PageSize) ctx.SetLinkHeader(int(count), opts.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.SetTotalCountHeader(count)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, api.SearchResults{ ctx.JSON(http.StatusOK, api.SearchResults{
OK: true, OK: true,
Data: results, Data: results,

@ -52,5 +52,7 @@ func ListStargazers(ctx *context.APIContext) {
for i, stargazer := range stargazers { for i, stargazer := range stargazers {
users[i] = convert.ToUser(stargazer, ctx.User) users[i] = convert.ToUser(stargazer, ctx.User)
} }
ctx.SetTotalCountHeader(int64(ctx.Repo.Repository.NumStars))
ctx.JSON(http.StatusOK, users) ctx.JSON(http.StatusOK, users)
} }

@ -204,8 +204,7 @@ func getCommitStatuses(ctx *context.APIContext, sha string) {
} }
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.SetTotalCountHeader(maxResults)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, apiStatuses) ctx.JSON(http.StatusOK, apiStatuses)
} }
@ -267,5 +266,6 @@ func GetCombinedCommitStatusByRef(ctx *context.APIContext) {
combiStatus := convert.ToCombinedStatus(statuses, convert.ToRepo(repo, ctx.Repo.AccessMode)) combiStatus := convert.ToCombinedStatus(statuses, convert.ToRepo(repo, ctx.Repo.AccessMode))
// TODO: ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, combiStatus) ctx.JSON(http.StatusOK, combiStatus)
} }

@ -52,5 +52,7 @@ func ListSubscribers(ctx *context.APIContext) {
for i, subscriber := range subscribers { for i, subscriber := range subscribers {
users[i] = convert.ToUser(subscriber, ctx.User) users[i] = convert.ToUser(subscriber, ctx.User)
} }
ctx.SetTotalCountHeader(int64(ctx.Repo.Repository.NumWatches))
ctx.JSON(http.StatusOK, users) ctx.JSON(http.StatusOK, users)
} }

@ -50,7 +50,7 @@ func ListTags(ctx *context.APIContext) {
listOpts := utils.GetListOptions(ctx) listOpts := utils.GetListOptions(ctx)
tags, err := ctx.Repo.GitRepo.GetTagInfos(listOpts.Page, listOpts.PageSize) tags, total, err := ctx.Repo.GitRepo.GetTagInfos(listOpts.Page, listOpts.PageSize)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetTags", err) ctx.Error(http.StatusInternalServerError, "GetTags", err)
return return
@ -61,6 +61,7 @@ func ListTags(ctx *context.APIContext) {
apiTags[i] = convert.ToTag(ctx.Repo.Repository, tags[i]) apiTags[i] = convert.ToTag(ctx.Repo.Repository, tags[i])
} }
ctx.SetTotalCountHeader(int64(total))
ctx.JSON(http.StatusOK, &apiTags) ctx.JSON(http.StatusOK, &apiTags)
} }

@ -47,12 +47,13 @@ func ListTopics(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/TopicNames" // "$ref": "#/responses/TopicNames"
topics, err := models.FindTopics(&models.FindTopicOptions{ opts := &models.FindTopicOptions{
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
}) }
topics, total, err := models.FindTopics(opts)
if err != nil { if err != nil {
log.Error("ListTopics failed: %v", err)
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return
} }
@ -61,6 +62,8 @@ func ListTopics(ctx *context.APIContext) {
for i, topic := range topics { for i, topic := range topics {
topicNames[i] = topic.Name topicNames[i] = topic.Name
} }
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, map[string]interface{}{ ctx.JSON(http.StatusOK, map[string]interface{}{
"topics": topicNames, "topics": topicNames,
}) })
@ -164,15 +167,15 @@ func AddTopic(ctx *context.APIContext) {
} }
// Prevent adding more topics than allowed to repo // Prevent adding more topics than allowed to repo
topics, err := models.FindTopics(&models.FindTopicOptions{ count, err := models.CountTopics(&models.FindTopicOptions{
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
}) })
if err != nil { if err != nil {
log.Error("AddTopic failed: %v", err) log.Error("CountTopics failed: %v", err)
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return
} }
if len(topics) >= 25 { if count >= 25 {
ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{
"message": "Exceeding maximum allowed topics per repo.", "message": "Exceeding maximum allowed topics per repo.",
}) })
@ -269,21 +272,13 @@ func TopicSearch(ctx *context.APIContext) {
// "403": // "403":
// "$ref": "#/responses/forbidden" // "$ref": "#/responses/forbidden"
if ctx.User == nil { opts := &models.FindTopicOptions{
ctx.Error(http.StatusForbidden, "UserIsNil", "Only owners could change the topics.") Keyword: ctx.FormString("q"),
return ListOptions: utils.GetListOptions(ctx),
} }
kw := ctx.FormString("q") topics, total, err := models.FindTopics(opts)
listOptions := utils.GetListOptions(ctx)
topics, err := models.FindTopics(&models.FindTopicOptions{
Keyword: kw,
ListOptions: listOptions,
})
if err != nil { if err != nil {
log.Error("SearchTopics failed: %v", err)
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return
} }
@ -292,6 +287,8 @@ func TopicSearch(ctx *context.APIContext) {
for i, topic := range topics { for i, topic := range topics {
topicResponses[i] = convert.ToTopicResponse(topic) topicResponses[i] = convert.ToTopicResponse(topic)
} }
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, map[string]interface{}{ ctx.JSON(http.StatusOK, map[string]interface{}{
"topics": topicResponses, "topics": topicResponses,
}) })

@ -44,9 +44,16 @@ func ListAccessTokens(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/AccessTokenList" // "$ref": "#/responses/AccessTokenList"
tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{UserID: ctx.User.ID, ListOptions: utils.GetListOptions(ctx)}) opts := models.ListAccessTokensOptions{UserID: ctx.User.ID, ListOptions: utils.GetListOptions(ctx)}
count, err := models.CountAccessTokens(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
tokens, err := models.ListAccessTokens(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err) ctx.InternalServerError(err)
return return
} }
@ -58,6 +65,8 @@ func ListAccessTokens(ctx *context.APIContext) {
TokenLastEight: tokens[i].TokenLastEight, TokenLastEight: tokens[i].TokenLastEight,
} }
} }
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, &apiTokens) ctx.JSON(http.StatusOK, &apiTokens)
} }
@ -242,7 +251,7 @@ func ListOauth2Applications(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/OAuth2ApplicationList" // "$ref": "#/responses/OAuth2ApplicationList"
apps, err := models.ListOAuth2Applications(ctx.User.ID, utils.GetListOptions(ctx)) apps, total, err := models.ListOAuth2Applications(ctx.User.ID, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err) ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err)
return return
@ -253,6 +262,8 @@ func ListOauth2Applications(ctx *context.APIContext) {
apiApps[i] = convert.ToOAuth2Application(apps[i]) apiApps[i] = convert.ToOAuth2Application(apps[i])
apiApps[i].ClientSecret = "" // Hide secret on application list apiApps[i].ClientSecret = "" // Hide secret on application list
} }
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, &apiApps) ctx.JSON(http.StatusOK, &apiApps)
} }

@ -29,6 +29,8 @@ func listUserFollowers(ctx *context.APIContext, u *models.User) {
ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err) ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err)
return return
} }
ctx.SetTotalCountHeader(int64(u.NumFollowers))
responseAPIUsers(ctx, users) responseAPIUsers(ctx, users)
} }
@ -93,6 +95,8 @@ func listUserFollowing(ctx *context.APIContext, u *models.User) {
ctx.Error(http.StatusInternalServerError, "GetFollowing", err) ctx.Error(http.StatusInternalServerError, "GetFollowing", err)
return return
} }
ctx.SetTotalCountHeader(int64(u.NumFollowing))
responseAPIUsers(ctx, users) responseAPIUsers(ctx, users)
} }

@ -28,6 +28,13 @@ func listGPGKeys(ctx *context.APIContext, uid int64, listOptions models.ListOpti
apiKeys[i] = convert.ToGPGKey(keys[i]) apiKeys[i] = convert.ToGPGKey(keys[i])
} }
total, err := models.CountUserGPGKeys(uid)
if err != nil {
ctx.InternalServerError(err)
return
}
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, &apiKeys) ctx.JSON(http.StatusOK, &apiKeys)
} }

@ -47,6 +47,7 @@ func composePublicKeysAPILink() string {
func listPublicKeys(ctx *context.APIContext, user *models.User) { func listPublicKeys(ctx *context.APIContext, user *models.User) {
var keys []*models.PublicKey var keys []*models.PublicKey
var err error var err error
var count int
fingerprint := ctx.FormString("fingerprint") fingerprint := ctx.FormString("fingerprint")
username := ctx.Params("username") username := ctx.Params("username")
@ -60,7 +61,15 @@ func listPublicKeys(ctx *context.APIContext, user *models.User) {
// Unrestricted // Unrestricted
keys, err = models.SearchPublicKey(0, fingerprint) keys, err = models.SearchPublicKey(0, fingerprint)
} }
count = len(keys)
} else { } else {
total, err2 := models.CountPublicKeys(user.ID)
if err2 != nil {
ctx.InternalServerError(err)
return
}
count = int(total)
// Use ListPublicKeys // Use ListPublicKeys
keys, err = models.ListPublicKeys(user.ID, utils.GetListOptions(ctx)) keys, err = models.ListPublicKeys(user.ID, utils.GetListOptions(ctx))
} }
@ -79,6 +88,7 @@ func listPublicKeys(ctx *context.APIContext, user *models.User) {
} }
} }
ctx.SetTotalCountHeader(int64(count))
ctx.JSON(http.StatusOK, &apiKeys) ctx.JSON(http.StatusOK, &apiKeys)
} }

@ -6,7 +6,6 @@ package user
import ( import (
"net/http" "net/http"
"strconv"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@ -43,8 +42,7 @@ func listUserRepos(ctx *context.APIContext, u *models.User, private bool) {
} }
ctx.SetLinkHeader(int(count), opts.PageSize) ctx.SetLinkHeader(int(count), opts.PageSize)
ctx.Header().Set("X-Total-Count", strconv.FormatInt(count, 10)) ctx.SetTotalCountHeader(count)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, &apiRepos) ctx.JSON(http.StatusOK, &apiRepos)
} }
@ -130,8 +128,7 @@ func ListMyRepos(ctx *context.APIContext) {
} }
ctx.SetLinkHeader(int(count), opts.ListOptions.PageSize) ctx.SetLinkHeader(int(count), opts.ListOptions.PageSize)
ctx.Header().Set("X-Total-Count", strconv.FormatInt(count, 10)) ctx.SetTotalCountHeader(count)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, &results) ctx.JSON(http.StatusOK, &results)
} }

@ -92,6 +92,8 @@ func GetMyStarredRepos(ctx *context.APIContext) {
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "getStarredRepos", err) ctx.Error(http.StatusInternalServerError, "getStarredRepos", err)
} }
ctx.SetTotalCountHeader(int64(ctx.User.NumStars))
ctx.JSON(http.StatusOK, &repos) ctx.JSON(http.StatusOK, &repos)
} }

@ -6,7 +6,6 @@
package user package user
import ( import (
"fmt"
"net/http" "net/http"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@ -73,8 +72,7 @@ func Search(ctx *context.APIContext) {
} }
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.SetTotalCountHeader(maxResults)
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
ctx.JSON(http.StatusOK, map[string]interface{}{ ctx.JSON(http.StatusOK, map[string]interface{}{
"ok": true, "ok": true,

@ -14,23 +14,22 @@ import (
"code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/routers/api/v1/utils"
) )
// getWatchedRepos returns the repos that the user with the specified userID is // getWatchedRepos returns the repos that the user with the specified userID is watching
// watching func getWatchedRepos(user *models.User, private bool, listOptions models.ListOptions) ([]*api.Repository, int64, error) {
func getWatchedRepos(user *models.User, private bool, listOptions models.ListOptions) ([]*api.Repository, error) { watchedRepos, total, err := models.GetWatchedRepos(user.ID, private, listOptions)
watchedRepos, err := models.GetWatchedRepos(user.ID, private, listOptions)
if err != nil { if err != nil {
return nil, err return nil, 0, err
} }
repos := make([]*api.Repository, len(watchedRepos)) repos := make([]*api.Repository, len(watchedRepos))
for i, watched := range watchedRepos { for i, watched := range watchedRepos {
access, err := models.AccessLevel(user, watched) access, err := models.AccessLevel(user, watched)
if err != nil { if err != nil {
return nil, err return nil, 0, err
} }
repos[i] = convert.ToRepo(watched, access) repos[i] = convert.ToRepo(watched, access)
} }
return repos, nil return repos, total, nil
} }
// GetWatchedRepos returns the repos that the user specified in ctx is watching // GetWatchedRepos returns the repos that the user specified in ctx is watching
@ -60,10 +59,12 @@ func GetWatchedRepos(ctx *context.APIContext) {
user := GetUserByParams(ctx) user := GetUserByParams(ctx)
private := user.ID == ctx.User.ID private := user.ID == ctx.User.ID
repos, err := getWatchedRepos(user, private, utils.GetListOptions(ctx)) repos, total, err := getWatchedRepos(user, private, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err)
} }
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, &repos) ctx.JSON(http.StatusOK, &repos)
} }
@ -87,10 +88,12 @@ func GetMyWatchedRepos(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/RepositoryList" // "$ref": "#/responses/RepositoryList"
repos, err := getWatchedRepos(ctx.User, true, utils.GetListOptions(ctx)) repos, total, err := getWatchedRepos(ctx.User, true, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err)
} }
ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, &repos) ctx.JSON(http.StatusOK, &repos)
} }

@ -107,7 +107,7 @@ func Home(ctx *context.Context) {
return return
} }
var opts = models.FindOrgMembersOpts{ var opts = &models.FindOrgMembersOpts{
OrgID: org.ID, OrgID: org.ID,
PublicOnly: true, PublicOnly: true,
ListOptions: models.ListOptions{Page: 1, PageSize: 25}, ListOptions: models.ListOptions{Page: 1, PageSize: 25},
@ -122,7 +122,7 @@ func Home(ctx *context.Context) {
opts.PublicOnly = !isMember && !ctx.User.IsAdmin opts.PublicOnly = !isMember && !ctx.User.IsAdmin
} }
members, _, err := models.FindOrgMembers(&opts) members, _, err := models.FindOrgMembers(opts)
if err != nil { if err != nil {
ctx.ServerError("FindOrgMembers", err) ctx.ServerError("FindOrgMembers", err)
return return

@ -31,7 +31,7 @@ func Members(ctx *context.Context) {
page = 1 page = 1
} }
var opts = models.FindOrgMembersOpts{ var opts = &models.FindOrgMembersOpts{
OrgID: org.ID, OrgID: org.ID,
PublicOnly: true, PublicOnly: true,
} }
@ -54,7 +54,7 @@ func Members(ctx *context.Context) {
pager := context.NewPagination(int(total), setting.UI.MembersPagingNum, page, 5) pager := context.NewPagination(int(total), setting.UI.MembersPagingNum, page, 5)
opts.ListOptions.Page = page opts.ListOptions.Page = page
opts.ListOptions.PageSize = setting.UI.MembersPagingNum opts.ListOptions.PageSize = setting.UI.MembersPagingNum
members, membersIsPublic, err := models.FindOrgMembers(&opts) members, membersIsPublic, err := models.FindOrgMembers(opts)
if err != nil { if err != nil {
ctx.ServerError("GetMembers", err) ctx.ServerError("GetMembers", err)
return return

@ -186,7 +186,7 @@ func Webhooks(ctx *context.Context) {
ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks" ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks"
ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc") ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc")
ws, err := models.GetWebhooksByOrgID(ctx.Org.Organization.ID, models.ListOptions{}) ws, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{OrgID: ctx.Org.Organization.ID})
if err != nil { if err != nil {
ctx.ServerError("GetWebhooksByOrgId", err) ctx.ServerError("GetWebhooksByOrgId", err)
return return

@ -378,7 +378,7 @@ func Issues(ctx *context.Context) {
var err error var err error
// Get milestones // Get milestones
ctx.Data["Milestones"], err = models.GetMilestones(models.GetMilestonesOption{ ctx.Data["Milestones"], _, err = models.GetMilestones(models.GetMilestonesOption{
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
State: api.StateType(ctx.FormString("state")), State: api.StateType(ctx.FormString("state")),
}) })
@ -395,7 +395,7 @@ func Issues(ctx *context.Context) {
// RetrieveRepoMilestonesAndAssignees find all the milestones and assignees of a repository // RetrieveRepoMilestonesAndAssignees find all the milestones and assignees of a repository
func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repository) { func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repository) {
var err error var err error
ctx.Data["OpenMilestones"], err = models.GetMilestones(models.GetMilestonesOption{ ctx.Data["OpenMilestones"], _, err = models.GetMilestones(models.GetMilestonesOption{
RepoID: repo.ID, RepoID: repo.ID,
State: api.StateOpen, State: api.StateOpen,
}) })
@ -403,7 +403,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repos
ctx.ServerError("GetMilestones", err) ctx.ServerError("GetMilestones", err)
return return
} }
ctx.Data["ClosedMilestones"], err = models.GetMilestones(models.GetMilestonesOption{ ctx.Data["ClosedMilestones"], _, err = models.GetMilestones(models.GetMilestonesOption{
RepoID: repo.ID, RepoID: repo.ID,
State: api.StateClosed, State: api.StateClosed,
}) })
@ -1265,7 +1265,7 @@ func ViewIssue(ctx *context.Context) {
} else { } else {
ctx.Data["CanUseTimetracker"] = false ctx.Data["CanUseTimetracker"] = false
} }
if ctx.Data["WorkingUsers"], err = models.TotalTimes(models.FindTrackedTimesOptions{IssueID: issue.ID}); err != nil { if ctx.Data["WorkingUsers"], err = models.TotalTimes(&models.FindTrackedTimesOptions{IssueID: issue.ID}); err != nil {
ctx.ServerError("TotalTimes", err) ctx.ServerError("TotalTimes", err)
return return
} }
@ -2584,8 +2584,8 @@ func handleTeamMentions(ctx *context.Context) {
} }
if isAdmin { if isAdmin {
if err := ctx.Repo.Owner.GetTeams(&models.SearchTeamOptions{}); err != nil { if err := ctx.Repo.Owner.LoadTeams(); err != nil {
ctx.ServerError("GetTeams", err) ctx.ServerError("LoadTeams", err)
return return
} }
} else { } else {

@ -53,17 +53,12 @@ func Milestones(ctx *context.Context) {
page = 1 page = 1
} }
var total int state := structs.StateOpen
var state structs.StateType if isShowClosed {
if !isShowClosed {
total = int(stats.OpenCount)
state = structs.StateOpen
} else {
total = int(stats.ClosedCount)
state = structs.StateClosed state = structs.StateClosed
} }
miles, err := models.GetMilestones(models.GetMilestonesOption{ miles, total, err := models.GetMilestones(models.GetMilestonesOption{
ListOptions: models.ListOptions{ ListOptions: models.ListOptions{
Page: page, Page: page,
PageSize: setting.UI.IssuePagingNum, PageSize: setting.UI.IssuePagingNum,
@ -106,7 +101,7 @@ func Milestones(ctx *context.Context) {
ctx.Data["Keyword"] = keyword ctx.Data["Keyword"] = keyword
ctx.Data["IsShowClosed"] = isShowClosed ctx.Data["IsShowClosed"] = isShowClosed
pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, 5) pager := context.NewPagination(int(total), setting.UI.IssuePagingNum, page, 5)
pager.AddParam(ctx, "state", "State") pager.AddParam(ctx, "state", "State")
pager.AddParam(ctx, "q", "Keyword") pager.AddParam(ctx, "q", "Keyword")
ctx.Data["Page"] = pager ctx.Data["Page"] = pager

@ -1002,7 +1002,7 @@ func DeployKeys(ctx *context.Context) {
ctx.Data["PageIsSettingsKeys"] = true ctx.Data["PageIsSettingsKeys"] = true
ctx.Data["DisableSSH"] = setting.SSH.Disabled ctx.Data["DisableSSH"] = setting.SSH.Disabled
keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID, models.ListOptions{}) keys, err := models.ListDeployKeys(&models.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID})
if err != nil { if err != nil {
ctx.ServerError("ListDeployKeys", err) ctx.ServerError("ListDeployKeys", err)
return return
@ -1018,7 +1018,7 @@ func DeployKeysPost(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys")
ctx.Data["PageIsSettingsKeys"] = true ctx.Data["PageIsSettingsKeys"] = true
keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID, models.ListOptions{}) keys, err := models.ListDeployKeys(&models.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID})
if err != nil { if err != nil {
ctx.ServerError("ListDeployKeys", err) ctx.ServerError("ListDeployKeys", err)
return return

@ -674,7 +674,7 @@ func renderLanguageStats(ctx *context.Context) {
} }
func renderRepoTopics(ctx *context.Context) { func renderRepoTopics(ctx *context.Context) {
topics, err := models.FindTopics(&models.FindTopicOptions{ topics, _, err := models.FindTopics(&models.FindTopicOptions{
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
}) })
if err != nil { if err != nil {

@ -41,7 +41,7 @@ func Webhooks(ctx *context.Context) {
ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks" ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks"
ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.io/en-us/webhooks/") ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.io/en-us/webhooks/")
ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID, models.ListOptions{}) ws, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID})
if err != nil { if err != nil {
ctx.ServerError("GetWebhooksByRepoID", err) ctx.ServerError("GetWebhooksByRepoID", err)
return return

@ -132,7 +132,7 @@ func createCodeComment(doer *models.User, repo *models.Repository, issue *models
head := pr.GetGitRefName() head := pr.GetGitRefName()
if line > 0 { if line > 0 {
if reviewID != 0 { if reviewID != 0 {
first, err := models.FindComments(models.FindCommentsOptions{ first, err := models.FindComments(&models.FindCommentsOptions{
ReviewID: reviewID, ReviewID: reviewID,
Line: line, Line: line,
TreePath: treePath, TreePath: treePath,

@ -14,6 +14,8 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/sync" "code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util"
"github.com/gobwas/glob" "github.com/gobwas/glob"
) )
@ -187,7 +189,10 @@ func PrepareWebhooks(repo *models.Repository, event models.HookEventType, p api.
} }
func prepareWebhooks(repo *models.Repository, event models.HookEventType, p api.Payloader) error { func prepareWebhooks(repo *models.Repository, event models.HookEventType, p api.Payloader) error {
ws, err := models.GetActiveWebhooksByRepoID(repo.ID) ws, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{
RepoID: repo.ID,
IsActive: util.OptionalBoolTrue,
})
if err != nil { if err != nil {
return fmt.Errorf("GetActiveWebhooksByRepoID: %v", err) return fmt.Errorf("GetActiveWebhooksByRepoID: %v", err)
} }
@ -195,7 +200,10 @@ func prepareWebhooks(repo *models.Repository, event models.HookEventType, p api.
// check if repo belongs to org and append additional webhooks // check if repo belongs to org and append additional webhooks
if repo.MustOwner().IsOrganization() { if repo.MustOwner().IsOrganization() {
// get hooks for org // get hooks for org
orgHooks, err := models.GetActiveWebhooksByOrgID(repo.OwnerID) orgHooks, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{
OrgID: repo.OwnerID,
IsActive: util.OptionalBoolTrue,
})
if err != nil { if err != nil {
return fmt.Errorf("GetActiveWebhooksByOrgID: %v", err) return fmt.Errorf("GetActiveWebhooksByOrgID: %v", err)
} }

Loading…
Cancel
Save