[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
This commit is contained in:
6543 2021-08-12 14:43:08 +02:00 committed by GitHub
parent ca13e1d56c
commit 2289580bb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 637 additions and 329 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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
} }

View File

@ -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

View File

@ -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)

View File

@ -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,
}) })

View File

@ -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()

View File

@ -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{})
}
// .___ // .___
// | | ______ ________ __ ____ // | | ______ ________ __ ____
// | |/ ___// ___/ | \_/ __ \ // | |/ ___// ___/ | \_/ __ \

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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
} }

View File

@ -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)
} }

View File

@ -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()

View File

@ -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
} }
////////////////////////////////////////////////////// //////////////////////////////////////////////////////

View File

@ -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)

View File

@ -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 {

View File

@ -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})

View File

@ -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)

View File

@ -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)
} }

View File

@ -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,

View File

@ -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
} }

View File

@ -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 {

View File

@ -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

View File

@ -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)

View File

@ -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 listDeployKeys(e Engine, repoID int64, listOptions ListOptions) ([]*DeployKey, error) { func (opt ListDeployKeysOptions) toCond() builder.Cond {
sess := e.Where("repo_id = ?", repoID) cond := builder.NewCond()
if listOptions.Page != 0 { if opt.RepoID != 0 {
sess = listOptions.setSessionPagination(sess) 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
}
keys := make([]*DeployKey, 0, listOptions.PageSize) // ListDeployKeys returns a list of deploy keys matching the provided arguments.
func ListDeployKeys(opts *ListDeployKeysOptions) ([]*DeployKey, error) {
return listDeployKeys(x, opts)
}
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)
} }

View File

@ -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{

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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 {
cond = cond.And(builder.Eq{"webhook.org_id": opts.OrgID})
}
if !opts.IsActive.IsNone() {
cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.IsTrue()})
}
return cond
} }
// GetWebhooksByRepoID returns all webhooks of a repository. func listWebhooksByOpts(e Engine, opts *ListWebhookOptions) ([]*Webhook, error) {
func GetWebhooksByRepoID(repoID int64, listOptions ListOptions) ([]*Webhook, error) { sess := e.Where(opts.toCond())
if listOptions.Page == 0 {
webhooks := make([]*Webhook, 0, 5) if opts.Page != 0 {
return webhooks, x.Find(&webhooks, &Webhook{RepoID: repoID}) sess = opts.setSessionPagination(sess)
webhooks := make([]*Webhook, 0, opts.PageSize)
err := sess.Find(&webhooks)
return webhooks, err
} }
sess := listOptions.getPaginatedSession() webhooks := make([]*Webhook, 0, 10)
webhooks := make([]*Webhook, 0, listOptions.PageSize) err := sess.Find(&webhooks)
return webhooks, err
return webhooks, sess.Find(&webhooks, &Webhook{RepoID: repoID})
} }
// GetActiveWebhooksByOrgID returns all active webhooks for an organization. // ListWebhooksByOpts return webhooks based on options
func GetActiveWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) { func ListWebhooksByOpts(opts *ListWebhookOptions) ([]*Webhook, error) {
return getActiveWebhooksByOrgID(x, orgID) return listWebhooksByOpts(x, opts)
} }
func getActiveWebhooksByOrgID(e Engine, orgID int64) (ws []*Webhook, err error) { // CountWebhooksByOpts count webhooks based on options and ignore pagination
err = e. func CountWebhooksByOpts(opts *ListWebhookOptions) (int64, error) {
Where("org_id=?", orgID). return x.Where(opts.toCond()).Count(&Webhook{})
And("is_active=?", true).
Find(&ws)
return ws, err
}
// GetWebhooksByOrgID returns paginated webhooks for an organization.
func GetWebhooksByOrgID(orgID int64, listOptions ListOptions) ([]*Webhook, error) {
if listOptions.Page == 0 {
ws := make([]*Webhook, 0, 5)
return ws, x.Find(&ws, &Webhook{OrgID: orgID})
}
sess := listOptions.getPaginatedSession()
ws := make([]*Webhook, 0, listOptions.PageSize)
return ws, sess.Find(&ws, &Webhook{OrgID: orgID})
} }
// GetDefaultWebhooks returns all admin-default webhooks. // GetDefaultWebhooks returns all admin-default webhooks.

View File

@ -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)

View File

@ -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, ", "))
} }
} }

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View File

@ -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,
}) })

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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))
} }

View File

@ -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))
} }

View File

@ -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)
} }

View File

@ -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))
} }

View File

@ -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 { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err) ctx.InternalServerError(err)
return
}
members, _, err := models.FindOrgMembers(opts)
if err != nil {
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)
} }

View File

@ -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)
} }

View File

@ -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,

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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))
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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))
} }

View File

@ -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,
fingerprint := ctx.FormString("fingerprint") KeyID: ctx.FormInt64("key_id"),
keyID := ctx.FormInt64("key_id") Fingerprint: ctx.FormString("fingerprint"),
if fingerprint != "" || keyID != 0 {
keys, err = models.SearchDeployKeys(ctx.Repo.Repository.ID, keyID, fingerprint)
} else {
keys, err = models.ListDeployKeys(ctx.Repo.Repository.ID, utils.GetListOptions(ctx))
} }
keys, err := models.ListDeployKeys(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "ListDeployKeys", err) ctx.InternalServerError(err)
return
}
count, err := models.CountDeployKeys(opts)
if err != nil {
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)
} }

View File

@ -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))
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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 { if err != nil {
ctx.Error(http.StatusInternalServerError, "FindReviews", err) ctx.InternalServerError(err)
return
}
count, err := models.CountReviews(opts)
if err != nil {
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)
} }

View File

@ -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)
} }

View File

@ -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,

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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,
}) })

View File

@ -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 { if err != nil {
ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err) ctx.InternalServerError(err)
return
}
tokens, err := models.ListAccessTokens(opts)
if err != nil {
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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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,

View File

@ -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)
} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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,

View File

@ -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)
} }