From 041d0236f130e7bc3e5d0e854707f7512508443e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 18:06:42 +0200 Subject: [PATCH 01/53] Add info about list endpoints to CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eb515f26854d..3e8f00b1272f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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). +### Endpoints returning lists should + * support pagination (`page` & `limit` options in query) + * add `X-Total-Count` header ([example](https://github.com/go-gitea/gitea/blob/e76f8cac9a2ba727bec6b5beab2496be9dafabef/routers/api/v1/repo/issue.go#L445)) + ## Developer Certificate of Origin (DCO) From 0b2f8587a8c99ccaf4154395d2d28f9995447f0e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 18:16:52 +0200 Subject: [PATCH 02/53] Let List Notifications return X-Total-Count header --- models/notification.go | 5 +++++ routers/api/v1/notify/repo.go | 9 +++++++++ routers/api/v1/notify/user.go | 9 +++++++++ 3 files changed, 23 insertions(+) diff --git a/models/notification.go b/models/notification.go index c4c7728ad9f6..30bb7596a8a1 100644 --- a/models/notification.go +++ b/models/notification.go @@ -125,6 +125,11 @@ func GetNotifications(opts *FindNotificationOptions) (NotificationList, error) { 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 func CreateRepoTransferNotification(doer, newOwner *User, repo *Repository) error { sess := x.NewSession() diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go index af55d1d49c05..ab9f19c3383b 100644 --- a/routers/api/v1/notify/repo.go +++ b/routers/api/v1/notify/repo.go @@ -5,6 +5,7 @@ package notify import ( + "fmt" "net/http" "strings" "time" @@ -108,6 +109,12 @@ func ListRepoNotifications(ctx *context.APIContext) { } opts.RepoID = ctx.Repo.Repository.ID + totalCount, err := models.CountNotifications(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + nl, err := models.GetNotifications(opts) if err != nil { ctx.InternalServerError(err) @@ -119,6 +126,8 @@ func ListRepoNotifications(ctx *context.APIContext) { return } + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalCount)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, convert.ToNotifications(nl)) } diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go index 475a541bdc60..4dc6dc64311b 100644 --- a/routers/api/v1/notify/user.go +++ b/routers/api/v1/notify/user.go @@ -5,6 +5,7 @@ package notify import ( + "fmt" "net/http" "strings" "time" @@ -69,6 +70,12 @@ func ListNotifications(ctx *context.APIContext) { return } + totalCount, err := models.CountNotifications(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + nl, err := models.GetNotifications(opts) if err != nil { ctx.InternalServerError(err) @@ -80,6 +87,8 @@ func ListNotifications(ctx *context.APIContext) { return } + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalCount)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, convert.ToNotifications(nl)) } From b1f875f8fef16fa2c4e697514b7acf2000c653ad Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 18:22:24 +0200 Subject: [PATCH 03/53] ListAccessTokens too --- models/token.go | 9 +++++++++ routers/api/v1/user/app.go | 14 ++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/models/token.go b/models/token.go index 357afe44a7c0..8e1f91d43f1c 100644 --- a/models/token.go +++ b/models/token.go @@ -122,6 +122,15 @@ func UpdateAccessToken(t *AccessToken) error { 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. func DeleteAccessTokenByID(id, userID int64) error { cnt, err := x.ID(id).Delete(&AccessToken{ diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 9f355a828950..a3ed849082ca 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -44,9 +44,16 @@ func ListAccessTokens(ctx *context.APIContext) { // "200": // "$ref": "#/responses/AccessTokenList" - tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{UserID: ctx.User.ID, ListOptions: utils.GetListOptions(ctx)}) + opts := models.ListAccessTokensOptions{UserID: ctx.User.ID, ListOptions: utils.GetListOptions(ctx)} + + count, err := models.CountAccessTokens(opts) if err != nil { - ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err) + ctx.InternalServerError(err) + return + } + tokens, err := models.ListAccessTokens(opts) + if err != nil { + ctx.InternalServerError(err) return } @@ -58,6 +65,9 @@ func ListAccessTokens(ctx *context.APIContext) { TokenLastEight: tokens[i].TokenLastEight, } } + + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &apiTokens) } From c1f2c9c43ef072729e795eb9dd1aef39415013cf Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 18:48:20 +0200 Subject: [PATCH 04/53] ListCronTasks --- routers/api/v1/admin/cron.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/routers/api/v1/admin/cron.go b/routers/api/v1/admin/cron.go index 2531346fcb0c..22a41bb04bc9 100644 --- a/routers/api/v1/admin/cron.go +++ b/routers/api/v1/admin/cron.go @@ -5,12 +5,14 @@ package admin import ( + "fmt" "net/http" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/cron" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -36,12 +38,10 @@ func ListCronTasks(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" tasks := cron.ListTasks() - listOpts := utils.GetListOptions(ctx) - start, end := listOpts.GetStartEnd() + count := len(tasks) - if len(tasks) > listOpts.PageSize { - tasks = tasks[start:end] - } + listOpts := utils.GetListOptions(ctx) + tasks = util.PaginateSlice(tasks, listOpts.Page, listOpts.PageSize).(cron.TaskTable) res := make([]structs.Cron, len(tasks)) for i, task := range tasks { @@ -53,6 +53,9 @@ func ListCronTasks(ctx *context.APIContext) { ExecTimes: task.ExecTimes, } } + + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, res) } From 29657d96eaa1d5a2f68c9e948de33759b1870309 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 18:49:01 +0200 Subject: [PATCH 05/53] ListForks --- routers/api/v1/repo/fork.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index 55fcefaccc38..0ccc938f33d1 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -62,6 +62,8 @@ func ListForks(ctx *context.APIContext) { } apiForks[i] = convert.ToRepo(fork, access) } + + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.Repo.Repository.NumForks)) ctx.JSON(http.StatusOK, apiForks) } From 28662da3fdee0aa7df83bcb3a24bc4d011cce760 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 18:49:36 +0200 Subject: [PATCH 06/53] ListStargazers --- routers/api/v1/repo/star.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go index 3af0a4ac125a..d1a85564c9e0 100644 --- a/routers/api/v1/repo/star.go +++ b/routers/api/v1/repo/star.go @@ -5,6 +5,7 @@ package repo import ( + "fmt" "net/http" "code.gitea.io/gitea/modules/context" @@ -52,5 +53,8 @@ func ListStargazers(ctx *context.APIContext) { for i, stargazer := range stargazers { users[i] = convert.ToUser(stargazer, ctx.User) } + + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.Repo.Repository.NumStars)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, users) } From cab3f6679fd2d7de97d55d701ba88e43a4bc4622 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 18:50:02 +0200 Subject: [PATCH 07/53] Add only TODOs --- routers/api/v1/org/hook.go | 2 ++ routers/api/v1/org/label.go | 1 + routers/api/v1/org/member.go | 1 + routers/api/v1/org/team.go | 6 ++++++ routers/api/v1/repo/collaborators.go | 2 ++ routers/api/v1/repo/hook.go | 2 ++ routers/api/v1/repo/issue_comment.go | 4 ++++ routers/api/v1/repo/issue_stopwatch.go | 1 + routers/api/v1/repo/issue_tracked_time.go | 5 +++++ routers/api/v1/repo/key.go | 1 + routers/api/v1/repo/label.go | 1 + routers/api/v1/repo/milestone.go | 2 ++ routers/api/v1/repo/pull_review.go | 1 + routers/api/v1/repo/status.go | 1 + routers/api/v1/repo/subscriber.go | 2 ++ routers/api/v1/repo/tag.go | 1 + routers/api/v1/repo/teams.go | 1 + routers/api/v1/repo/topic.go | 4 ++++ routers/api/v1/user/app.go | 2 ++ routers/api/v1/user/follower.go | 4 ++++ routers/api/v1/user/gpg_key.go | 1 + routers/api/v1/user/key.go | 1 + routers/api/v1/user/star.go | 2 ++ routers/api/v1/user/watch.go | 2 ++ 24 files changed, 50 insertions(+) diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go index ed827c48d43f..5d72135093f3 100644 --- a/routers/api/v1/org/hook.go +++ b/routers/api/v1/org/hook.go @@ -50,6 +50,8 @@ func ListHooks(ctx *context.APIContext) { for i, hook := range orgHooks { hooks[i] = convert.ToHook(org.HomeLink(), hook) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, hooks) } diff --git a/routers/api/v1/org/label.go b/routers/api/v1/org/label.go index 76061f163aac..0515576f616b 100644 --- a/routers/api/v1/org/label.go +++ b/routers/api/v1/org/label.go @@ -49,6 +49,7 @@ func ListLabels(ctx *context.APIContext) { return } + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, convert.ToLabelList(labels)) } diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 09abad25572d..7a049e0dca4c 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -35,6 +35,7 @@ func listMembers(ctx *context.APIContext, publicOnly bool) { apiMembers[i] = convert.ToUser(member, ctx.User) } + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, apiMembers) } diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index bc86bbcbe3d9..a006d01b2ce7 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -62,6 +62,8 @@ func ListTeams(ctx *context.APIContext) { apiTeams[i] = convert.ToTeam(org.Teams[i]) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, apiTeams) } @@ -107,6 +109,8 @@ func ListUserTeams(ctx *context.APIContext) { apiTeams[i] = convert.ToTeam(teams[i]) apiTeams[i].Organization = apiOrg } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, apiTeams) } @@ -339,6 +343,8 @@ func GetTeamMembers(ctx *context.APIContext) { for i, member := range team.Members { members[i] = convert.ToUser(member, ctx.User) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, members) } diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index 078af1f6ff8e..d7c79b96c3ad 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -56,6 +56,8 @@ func ListCollaborators(ctx *context.APIContext) { for i, collaborator := range collaborators { users[i] = convert.ToUser(collaborator.User, ctx.User) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, users) } diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index da0a2c501c8a..c5d55320c4d8 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -58,6 +58,8 @@ func ListHooks(ctx *context.APIContext) { for i := range hooks { apiHooks[i] = convert.ToHook(ctx.Repo.RepoLink, hooks[i]) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &apiHooks) } diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index d62ca813149a..992fc1f1d3f4 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -89,6 +89,8 @@ func ListIssueComments(ctx *context.APIContext) { comment.Issue = issue apiComments[i] = convert.ToComment(comments[i]) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &apiComments) } @@ -171,6 +173,8 @@ func ListRepoIssueComments(ctx *context.APIContext) { for i := range comments { apiComments[i] = convert.ToComment(comments[i]) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &apiComments) } diff --git a/routers/api/v1/repo/issue_stopwatch.go b/routers/api/v1/repo/issue_stopwatch.go index a4a2261b9a10..3cbcaab0075c 100644 --- a/routers/api/v1/repo/issue_stopwatch.go +++ b/routers/api/v1/repo/issue_stopwatch.go @@ -231,5 +231,6 @@ func GetStopwatches(ctx *context.APIContext) { return } + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, apiSWs) } diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index ad774b563bcc..410aa46382e0 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -129,6 +129,8 @@ func ListTrackedTimes(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) } @@ -540,6 +542,8 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) } @@ -596,5 +600,6 @@ func ListMyTrackedTimes(ctx *context.APIContext) { return } + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) } diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index b1b465ca113f..91f54ed256a3 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -104,6 +104,7 @@ func ListDeployKeys(ctx *context.APIContext) { } } + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &apiKeys) } diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go index f71683f6ece1..de5532626734 100644 --- a/routers/api/v1/repo/label.go +++ b/routers/api/v1/repo/label.go @@ -55,6 +55,7 @@ func ListLabels(ctx *context.APIContext) { return } + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, convert.ToLabelList(labels)) } diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go index fc8b4efdbb24..e77c67659e0b 100644 --- a/routers/api/v1/repo/milestone.go +++ b/routers/api/v1/repo/milestone.go @@ -72,6 +72,8 @@ func ListMilestones(ctx *context.APIContext) { for i := range milestones { apiMilestones[i] = convert.ToAPIMilestone(milestones[i]) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &apiMilestones) } diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 323904f45c0e..b79139c63e69 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -95,6 +95,7 @@ func ListPullReviews(ctx *context.APIContext) { return } + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &apiReviews) } diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index 95c3f00a72e4..dad61fbad23b 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -267,5 +267,6 @@ func GetCombinedCommitStatusByRef(ctx *context.APIContext) { combiStatus := convert.ToCombinedStatus(statuses, convert.ToRepo(repo, ctx.Repo.AccessMode)) + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, combiStatus) } diff --git a/routers/api/v1/repo/subscriber.go b/routers/api/v1/repo/subscriber.go index 37bf3c29d466..4c80426ef8d8 100644 --- a/routers/api/v1/repo/subscriber.go +++ b/routers/api/v1/repo/subscriber.go @@ -52,5 +52,7 @@ func ListSubscribers(ctx *context.APIContext) { for i, subscriber := range subscribers { users[i] = convert.ToUser(subscriber, ctx.User) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, users) } diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index c95fb63f859e..38e503846442 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -61,6 +61,7 @@ func ListTags(ctx *context.APIContext) { apiTags[i] = convert.ToTag(ctx.Repo.Repository, tags[i]) } + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &apiTags) } diff --git a/routers/api/v1/repo/teams.go b/routers/api/v1/repo/teams.go index 1348205ec95b..15ef1f69fbf8 100644 --- a/routers/api/v1/repo/teams.go +++ b/routers/api/v1/repo/teams.go @@ -57,6 +57,7 @@ func ListTeams(ctx *context.APIContext) { apiTeams[i] = convert.ToTeam(teams[i]) } + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, apiTeams) } diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index c612c2942c48..109dd7aa007b 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -61,6 +61,8 @@ func ListTopics(ctx *context.APIContext) { for i, topic := range topics { topicNames[i] = topic.Name } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, map[string]interface{}{ "topics": topicNames, }) @@ -292,6 +294,8 @@ func TopicSearch(ctx *context.APIContext) { for i, topic := range topics { topicResponses[i] = convert.ToTopicResponse(topic) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, map[string]interface{}{ "topics": topicResponses, }) diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index a3ed849082ca..5d381a8085da 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -268,6 +268,8 @@ func ListOauth2Applications(ctx *context.APIContext) { apiApps[i] = convert.ToOAuth2Application(apps[i]) apiApps[i].ClientSecret = "" // Hide secret on application list } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &apiApps) } diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index 4d316425cd09..70a23db7a1ea 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -29,6 +29,8 @@ func listUserFollowers(ctx *context.APIContext, u *models.User) { ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err) return } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) responseAPIUsers(ctx, users) } @@ -93,6 +95,8 @@ func listUserFollowing(ctx *context.APIContext, u *models.User) { ctx.Error(http.StatusInternalServerError, "GetFollowing", err) return } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) responseAPIUsers(ctx, users) } diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index ec03e305ba1b..e6cc3c743502 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -28,6 +28,7 @@ func listGPGKeys(ctx *context.APIContext, uid int64, listOptions models.ListOpti apiKeys[i] = convert.ToGPGKey(keys[i]) } + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &apiKeys) } diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index 780cdf417c03..0b0bb1bf68dc 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -79,6 +79,7 @@ func listPublicKeys(ctx *context.APIContext, user *models.User) { } } + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &apiKeys) } diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index 937dcb477f27..57c4de51159a 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -92,6 +92,8 @@ func GetMyStarredRepos(ctx *context.APIContext) { if err != nil { ctx.Error(http.StatusInternalServerError, "getStarredRepos", err) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &repos) } diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go index ab656f3229ff..1f0bb62d115d 100644 --- a/routers/api/v1/user/watch.go +++ b/routers/api/v1/user/watch.go @@ -91,6 +91,8 @@ func GetMyWatchedRepos(ctx *context.APIContext) { if err != nil { ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) } + + // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, &repos) } From b9336711fb7a3f9b52c41bae2a90434eebf7460b Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 18:53:03 +0200 Subject: [PATCH 08/53] nit --- routers/api/v1/repo/fork.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index 0ccc938f33d1..bb265edf785b 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -64,6 +64,7 @@ func ListForks(ctx *context.APIContext) { } ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.Repo.Repository.NumForks)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, apiForks) } From e22371a53c292f973c9e5ba7caa02b739675c08f Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 19:44:28 +0200 Subject: [PATCH 09/53] models: webhook: introduce ListWebhooksByOpts --- models/repo_generate.go | 2 +- models/webhook.go | 73 +++++++++++++++++-------------------- models/webhook_test.go | 9 +++-- routers/api/v1/org/hook.go | 5 ++- routers/api/v1/repo/hook.go | 5 ++- routers/web/org/setting.go | 2 +- routers/web/repo/webhook.go | 2 +- services/webhook/webhook.go | 12 +++++- 8 files changed, 60 insertions(+), 50 deletions(-) diff --git a/models/repo_generate.go b/models/repo_generate.go index 1cf73bc55e6a..66682903f1e9 100644 --- a/models/repo_generate.go +++ b/models/repo_generate.go @@ -111,7 +111,7 @@ func GenerateGitHooks(ctx DBContext, templateRepo, generateRepo *Repository) err // GenerateWebhooks generates webhooks from a template repository func GenerateWebhooks(ctx DBContext, templateRepo, generateRepo *Repository) error { - templateWebhooks, err := GetWebhooksByRepoID(templateRepo.ID, ListOptions{}) + templateWebhooks, err := ListWebhooksByOpts(&ListWebhookOptions{RepoID: templateRepo.ID}) if err != nil { return err } diff --git a/models/webhook.go b/models/webhook.go index 138ba26bde09..d46a987d8814 100644 --- a/models/webhook.go +++ b/models/webhook.go @@ -16,8 +16,10 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" gouuid "github.com/google/uuid" + "xorm.io/builder" ) // HookContentType is the content type of a web hook @@ -387,53 +389,46 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) { }) } -// GetActiveWebhooksByRepoID returns all active webhooks of repository. -func GetActiveWebhooksByRepoID(repoID int64) ([]*Webhook, error) { - return getActiveWebhooksByRepoID(x, repoID) +// ListWebhookOptions are options to filter webhooks on ListWebhooksByOpts +type ListWebhookOptions struct { + ListOptions + RepoID int64 + OrgID int64 + IsActive util.OptionalBool } -func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) { - webhooks := make([]*Webhook, 0, 5) - return webhooks, e.Where("is_active=?", true). - Find(&webhooks, &Webhook{RepoID: repoID}) -} - -// GetWebhooksByRepoID returns all webhooks of a repository. -func GetWebhooksByRepoID(repoID int64, listOptions ListOptions) ([]*Webhook, error) { - if listOptions.Page == 0 { - webhooks := make([]*Webhook, 0, 5) - return webhooks, x.Find(&webhooks, &Webhook{RepoID: repoID}) +func (opts *ListWebhookOptions) toCond() builder.Cond { + cond := builder.NewCond() + if opts.RepoID != 0 { + cond = cond.And(builder.Eq{"webhook.repo_id": opts.RepoID}) } - - sess := listOptions.getPaginatedSession() - webhooks := make([]*Webhook, 0, listOptions.PageSize) - - return webhooks, sess.Find(&webhooks, &Webhook{RepoID: repoID}) -} - -// GetActiveWebhooksByOrgID returns all active webhooks for an organization. -func GetActiveWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) { - return getActiveWebhooksByOrgID(x, orgID) + 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 } -func getActiveWebhooksByOrgID(e Engine, orgID int64) (ws []*Webhook, err error) { - err = e. - Where("org_id=?", orgID). - And("is_active=?", true). - Find(&ws) - return ws, err -} +func listWebhooksByOpts(e Engine, opts *ListWebhookOptions) ([]*Webhook, error) { + sess := e.Where(opts.toCond()) -// 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}) + if opts.Page != 0 { + sess = opts.setSessionPagination(sess) + webhooks := make([]*Webhook, 0, opts.PageSize) + err := sess.Find(&webhooks) + return webhooks, err } - sess := listOptions.getPaginatedSession() - ws := make([]*Webhook, 0, listOptions.PageSize) - return ws, sess.Find(&ws, &Webhook{OrgID: orgID}) + webhooks := make([]*Webhook, 0, 10) + err := sess.Find(&webhooks) + return webhooks, err +} + +// ListWebhooksByOpts return webhooks based on options +func ListWebhooksByOpts(opts *ListWebhookOptions) ([]*Webhook, error) { + return listWebhooksByOpts(x, opts) } // GetDefaultWebhooks returns all admin-default webhooks. diff --git a/models/webhook_test.go b/models/webhook_test.go index 84520666c0b2..625d643856c5 100644 --- a/models/webhook_test.go +++ b/models/webhook_test.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" ) @@ -118,7 +119,7 @@ func TestGetWebhookByOrgID(t *testing.T) { func TestGetActiveWebhooksByRepoID(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - hooks, err := GetActiveWebhooksByRepoID(1) + hooks, err := ListWebhooksByOpts(&ListWebhookOptions{RepoID: 1, IsActive: util.OptionalBoolTrue}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(1), hooks[0].ID) @@ -128,7 +129,7 @@ func TestGetActiveWebhooksByRepoID(t *testing.T) { func TestGetWebhooksByRepoID(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - hooks, err := GetWebhooksByRepoID(1, ListOptions{}) + hooks, err := ListWebhooksByOpts(&ListWebhookOptions{RepoID: 1}) assert.NoError(t, err) if assert.Len(t, hooks, 2) { assert.Equal(t, int64(1), hooks[0].ID) @@ -138,7 +139,7 @@ func TestGetWebhooksByRepoID(t *testing.T) { func TestGetActiveWebhooksByOrgID(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - hooks, err := GetActiveWebhooksByOrgID(3) + hooks, err := ListWebhooksByOpts(&ListWebhookOptions{OrgID: 3, IsActive: util.OptionalBoolTrue}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(3), hooks[0].ID) @@ -148,7 +149,7 @@ func TestGetActiveWebhooksByOrgID(t *testing.T) { func TestGetWebhooksByOrgID(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - hooks, err := GetWebhooksByOrgID(3, ListOptions{}) + hooks, err := ListWebhooksByOpts(&ListWebhookOptions{OrgID: 3}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(3), hooks[0].ID) diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go index 5d72135093f3..0d6d053374ac 100644 --- a/routers/api/v1/org/hook.go +++ b/routers/api/v1/org/hook.go @@ -41,7 +41,10 @@ func ListHooks(ctx *context.APIContext) { // "$ref": "#/responses/HookList" org := ctx.Org.Organization - orgHooks, err := models.GetWebhooksByOrgID(org.ID, utils.GetListOptions(ctx)) + orgHooks, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{ + ListOptions: utils.GetListOptions(ctx), + OrgID: org.ID, + }) if err != nil { ctx.Error(http.StatusInternalServerError, "GetWebhooksByOrgID", err) return diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index c5d55320c4d8..e2233177d8d7 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -48,7 +48,10 @@ func ListHooks(ctx *context.APIContext) { // "200": // "$ref": "#/responses/HookList" - hooks, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID, utils.GetListOptions(ctx)) + hooks, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{ + ListOptions: utils.GetListOptions(ctx), + RepoID: ctx.Repo.Repository.ID, + }) if err != nil { ctx.Error(http.StatusInternalServerError, "GetWebhooksByRepoID", err) return diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index e848939187ce..57b878a862b0 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -186,7 +186,7 @@ func Webhooks(ctx *context.Context) { ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks" 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 { ctx.ServerError("GetWebhooksByOrgId", err) return diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go index 946801cb7649..9dbf7ef475b1 100644 --- a/routers/web/repo/webhook.go +++ b/routers/web/repo/webhook.go @@ -41,7 +41,7 @@ func Webhooks(ctx *context.Context) { ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks" ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.io/en-us/webhooks/") - ws, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID, models.ListOptions{}) + ws, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID}) if err != nil { ctx.ServerError("GetWebhooksByRepoID", err) return diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go index 46002c895c3b..00b2ef05b87c 100644 --- a/services/webhook/webhook.go +++ b/services/webhook/webhook.go @@ -14,6 +14,8 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/sync" + "code.gitea.io/gitea/modules/util" + "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 { - ws, err := models.GetActiveWebhooksByRepoID(repo.ID) + ws, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{ + RepoID: repo.ID, + IsActive: util.OptionalBoolTrue, + }) if err != nil { 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 if repo.MustOwner().IsOrganization() { // 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 { return fmt.Errorf("GetActiveWebhooksByOrgID: %v", err) } From 6897deefa17798cc15fcdb7da8a4964f5b55c86a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 19:48:17 +0200 Subject: [PATCH 10/53] ListHooks --- models/webhook.go | 5 +++++ routers/api/v1/org/hook.go | 24 +++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/models/webhook.go b/models/webhook.go index d46a987d8814..79ce70a0de01 100644 --- a/models/webhook.go +++ b/models/webhook.go @@ -431,6 +431,11 @@ func ListWebhooksByOpts(opts *ListWebhookOptions) ([]*Webhook, error) { return listWebhooksByOpts(x, opts) } +// CountWebhooksByOpts count webhooks based on options and ignore pagination +func CountWebhooksByOpts(opts *ListWebhookOptions) (int64, error) { + return x.Where(opts.toCond()).Count(&Webhook{}) +} + // GetDefaultWebhooks returns all admin-default webhooks. func GetDefaultWebhooks() ([]*Webhook, error) { return getDefaultWebhooks(x) diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go index 0d6d053374ac..59c73b1eb562 100644 --- a/routers/api/v1/org/hook.go +++ b/routers/api/v1/org/hook.go @@ -5,6 +5,7 @@ package org import ( + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -40,21 +41,30 @@ func ListHooks(ctx *context.APIContext) { // "200": // "$ref": "#/responses/HookList" - org := ctx.Org.Organization - orgHooks, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{ + opts := &models.ListWebhookOptions{ ListOptions: utils.GetListOptions(ctx), - OrgID: org.ID, - }) + OrgID: ctx.Org.Organization.ID, + } + + count, err := models.CountWebhooksByOpts(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + + orgHooks, err := models.ListWebhooksByOpts(opts) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetWebhooksByOrgID", err) + ctx.InternalServerError(err) return } + hooks := make([]*api.Hook, len(orgHooks)) for i, hook := range orgHooks { - hooks[i] = convert.ToHook(org.HomeLink(), hook) + hooks[i] = convert.ToHook(ctx.Org.Organization.HomeLink(), hook) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, hooks) } From 5a9a23c03f1d04eb0020023db633edb1d092d741 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 20:10:16 +0200 Subject: [PATCH 11/53] ListLabels --- models/issue_label.go | 5 +++++ routers/api/v1/org/label.go | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/models/issue_label.go b/models/issue_label.go index d1ff4692366d..47a5210d9cef 100644 --- a/models/issue_label.go +++ b/models/issue_label.go @@ -556,6 +556,11 @@ func GetLabelsByOrgID(orgID int64, sortType string, listOptions 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{}) +} + // .___ // | | ______ ________ __ ____ // | |/ ___// ___/ | \_/ __ \ diff --git a/routers/api/v1/org/label.go b/routers/api/v1/org/label.go index 0515576f616b..49b0f152a572 100644 --- a/routers/api/v1/org/label.go +++ b/routers/api/v1/org/label.go @@ -49,7 +49,14 @@ func ListLabels(ctx *context.APIContext) { return } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + count, err := models.CountLabelsByOrgID(ctx.Org.Organization.ID) + if err != nil { + ctx.InternalServerError(err) + return + } + + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, convert.ToLabelList(labels)) } From 0b8962082d1d1900ad7028d3ccf55ddea18dc982 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 20:15:54 +0200 Subject: [PATCH 12/53] Org#ListMembers --- models/org.go | 2 +- routers/api/v1/org/member.go | 24 ++++++++++++++++-------- routers/web/org/home.go | 4 ++-- routers/web/org/members.go | 4 ++-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/models/org.go b/models/org.go index 58fb26b1bb51..a66bc9fdeb60 100644 --- a/models/org.go +++ b/models/org.go @@ -87,7 +87,7 @@ type FindOrgMembersOpts struct { } // 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) if opts.PublicOnly { sess.And("is_public = ?", true) diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 7a049e0dca4c..898e87bd5830 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -5,6 +5,7 @@ package org import ( + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -18,24 +19,31 @@ import ( // listMembers list an organization's members func listMembers(ctx *context.APIContext, publicOnly bool) { - var members []*models.User - - members, _, err := models.FindOrgMembers(&models.FindOrgMembersOpts{ + opts := &models.FindOrgMembersOpts{ OrgID: ctx.Org.Organization.ID, PublicOnly: publicOnly, ListOptions: utils.GetListOptions(ctx), - }) + } + + count, err := models.CountOrgMembers(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + + members, _, err := models.FindOrgMembers(opts) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err) + ctx.InternalServerError(err) return } apiMembers := make([]*api.User, len(members)) - for i, member := range members { - apiMembers[i] = convert.ToUser(member, ctx.User) + for i := range members { + apiMembers[i] = convert.ToUser(members[i], ctx.User) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, apiMembers) } diff --git a/routers/web/org/home.go b/routers/web/org/home.go index aad0a2a90b33..0bb3e1d89517 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -108,7 +108,7 @@ func Home(ctx *context.Context) { return } - var opts = models.FindOrgMembersOpts{ + var opts = &models.FindOrgMembersOpts{ OrgID: org.ID, PublicOnly: true, ListOptions: models.ListOptions{Page: 1, PageSize: 25}, @@ -123,7 +123,7 @@ func Home(ctx *context.Context) { opts.PublicOnly = !isMember && !ctx.User.IsAdmin } - members, _, err := models.FindOrgMembers(&opts) + members, _, err := models.FindOrgMembers(opts) if err != nil { ctx.ServerError("FindOrgMembers", err) return diff --git a/routers/web/org/members.go b/routers/web/org/members.go index 934529d7d7eb..c7ca59332934 100644 --- a/routers/web/org/members.go +++ b/routers/web/org/members.go @@ -31,7 +31,7 @@ func Members(ctx *context.Context) { page = 1 } - var opts = models.FindOrgMembersOpts{ + var opts = &models.FindOrgMembersOpts{ OrgID: org.ID, PublicOnly: true, } @@ -54,7 +54,7 @@ func Members(ctx *context.Context) { pager := context.NewPagination(int(total), setting.UI.MembersPagingNum, page, 5) opts.ListOptions.Page = page opts.ListOptions.PageSize = setting.UI.MembersPagingNum - members, membersIsPublic, err := models.FindOrgMembers(&opts) + members, membersIsPublic, err := models.FindOrgMembers(opts) if err != nil { ctx.ServerError("GetMembers", err) return From c6102e7eaad5668bc5183b4fd783ead2d2a2e9ac Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 20:19:42 +0200 Subject: [PATCH 13/53] GetTeamMembers --- routers/api/v1/org/team.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index a006d01b2ce7..71bbe0b4223f 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -332,19 +332,20 @@ func GetTeamMembers(ctx *context.APIContext) { ctx.NotFound() return } - team := ctx.Org.Team - if err := team.GetMembers(&models.SearchMembersOptions{ + + if err := ctx.Org.Team.GetMembers(&models.SearchMembersOptions{ ListOptions: utils.GetListOptions(ctx), }); err != nil { ctx.Error(http.StatusInternalServerError, "GetTeamMembers", err) return } - members := make([]*api.User, len(team.Members)) - for i, member := range team.Members { + members := make([]*api.User, len(ctx.Org.Team.Members)) + for i, member := range ctx.Org.Team.Members { members[i] = convert.ToUser(member, ctx.User) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.Org.Team.NumMembers)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, members) } From bf5acccae92f34bf77e0bcaf1418e794e2414adf Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 20:36:15 +0200 Subject: [PATCH 14/53] OrgListTeams --- models/access.go | 2 +- models/org.go | 12 ++++-------- models/org_test.go | 2 +- models/repo.go | 6 +++--- models/repo_transfer.go | 4 ++-- modules/context/org.go | 4 ++-- routers/api/v1/org/team.go | 20 ++++++++++++-------- routers/web/repo/issue.go | 4 ++-- 8 files changed, 27 insertions(+), 27 deletions(-) diff --git a/models/access.go b/models/access.go index d35b900cfdf9..5d0b0b06cfe1 100644 --- a/models/access.go +++ b/models/access.go @@ -246,7 +246,7 @@ func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (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 } diff --git a/models/org.go b/models/org.go index a66bc9fdeb60..3de6bd14b273 100644 --- a/models/org.go +++ b/models/org.go @@ -52,7 +52,7 @@ func (org *User) GetOwnerTeam() (*Team, error) { return org.getOwnerTeam(x) } -func (org *User) getTeams(e Engine) error { +func (org *User) loadTeams(e Engine) error { if org.Teams != nil { return nil } @@ -62,13 +62,9 @@ func (org *User) getTeams(e Engine) error { Find(&org.Teams) } -// GetTeams returns paginated teams that belong to organization. -func (org *User) GetTeams(opts *SearchTeamOptions) error { - if opts.Page != 0 { - return org.getTeams(opts.getPaginatedSession()) - } - - return org.getTeams(x) +// LoadTeams load teams if not loaded. +func (org *User) LoadTeams() error { + return org.loadTeams(x) } // GetMembers returns all members of organization. diff --git a/models/org_test.go b/models/org_test.go index e494e502dd31..7ba9d8ee88b8 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -86,7 +86,7 @@ func TestUser_GetOwnerTeam(t *testing.T) { func TestUser_GetTeams(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) org := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) - assert.NoError(t, org.GetTeams(&SearchTeamOptions{})) + assert.NoError(t, org.LoadTeams()) if assert.Len(t, org.Teams, 4) { assert.Equal(t, int64(1), org.Teams[0].ID) assert.Equal(t, int64(2), org.Teams[1].ID) diff --git a/models/repo.go b/models/repo.go index de8a2825313a..9ba53b67ef5f 100644 --- a/models/repo.go +++ b/models/repo.go @@ -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. if u.IsOrganization() { - if err := u.GetTeams(&SearchTeamOptions{}); err != nil { - return fmt.Errorf("GetTeams: %v", err) + if err := u.LoadTeams(); err != nil { + return fmt.Errorf("LoadTeams: %v", err) } for _, t := range u.Teams { if t.IncludesAllRepositories { @@ -1439,7 +1439,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error { return err } if org.IsOrganization() { - if err = org.getTeams(sess); err != nil { + if err = org.loadTeams(sess); err != nil { return err } } diff --git a/models/repo_transfer.go b/models/repo_transfer.go index d7ef0a8ca6b3..09b6290293d5 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -291,8 +291,8 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e } if newOwner.IsOrganization() { - if err := newOwner.getTeams(sess); err != nil { - return fmt.Errorf("GetTeams: %v", err) + if err := newOwner.loadTeams(sess); err != nil { + return fmt.Errorf("LoadTeams: %v", err) } for _, t := range newOwner.Teams { if t.IncludesAllRepositories { diff --git a/modules/context/org.go b/modules/context/org.go index 527ccfbcaa8d..fd67212a10c4 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -123,8 +123,8 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { // Team. if ctx.Org.IsMember { if ctx.Org.IsOwner { - if err := org.GetTeams(&models.SearchTeamOptions{}); err != nil { - ctx.ServerError("GetTeams", err) + if err := org.LoadTeams(); err != nil { + ctx.ServerError("LoadTeams", err) return } } else { diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 71bbe0b4223f..e6855fad259c 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -46,24 +46,28 @@ func ListTeams(ctx *context.APIContext) { // "$ref": "#/responses/TeamList" org := ctx.Org.Organization - if err := org.GetTeams(&models.SearchTeamOptions{ + teams, count, err := models.SearchTeam(&models.SearchTeamOptions{ ListOptions: utils.GetListOptions(ctx), - }); err != nil { - ctx.Error(http.StatusInternalServerError, "GetTeams", err) + OrgID: org.ID, + }) + + if err != nil { + ctx.Error(http.StatusInternalServerError, "LoadTeams", err) return } - apiTeams := make([]*api.Team, len(org.Teams)) - for i := range org.Teams { - if err := org.Teams[i].GetUnits(); err != nil { + apiTeams := make([]*api.Team, len(teams)) + for i := range teams { + if err := teams[i].GetUnits(); err != nil { ctx.Error(http.StatusInternalServerError, "GetUnits", err) return } - apiTeams[i] = convert.ToTeam(org.Teams[i]) + apiTeams[i] = convert.ToTeam(teams[i]) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, apiTeams) } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 9639ea82014a..b18720598828 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2586,8 +2586,8 @@ func handleTeamMentions(ctx *context.Context) { } if isAdmin { - if err := ctx.Repo.Owner.GetTeams(&models.SearchTeamOptions{}); err != nil { - ctx.ServerError("GetTeams", err) + if err := ctx.Repo.Owner.LoadTeams(); err != nil { + ctx.ServerError("LoadTeams", err) return } } else { From 516354d79b25f486c1b88f1de71e7eb5fe5846d1 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 20:36:43 +0200 Subject: [PATCH 15/53] fix --- routers/api/v1/org/team.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index e6855fad259c..c9fdd8e99c3c 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -45,10 +45,9 @@ func ListTeams(ctx *context.APIContext) { // "200": // "$ref": "#/responses/TeamList" - org := ctx.Org.Organization teams, count, err := models.SearchTeam(&models.SearchTeamOptions{ ListOptions: utils.GetListOptions(ctx), - OrgID: org.ID, + OrgID: ctx.Org.Organization.ID, }) if err != nil { From 55e00215059a8d56c51bb144d9420f98b063dd26 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 20:41:33 +0200 Subject: [PATCH 16/53] ListUserTeams --- models/org_team.go | 15 --------------- models/org_team_test.go | 2 +- routers/api/v1/org/team.go | 8 ++++++-- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/models/org_team.go b/models/org_team.go index 6226772b9a82..c380c8cd8ef5 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -790,16 +790,6 @@ func GetTeamMembers(teamID int64) ([]*User, error) { 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) { return teams, e. 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) } -// 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, // the user will have membership to given organization automatically when needed. func AddTeamMember(team *Team, userID int64) error { diff --git a/models/org_team_test.go b/models/org_team_test.go index 38e36cf82ccf..9dc26fd814a6 100644 --- a/models/org_team_test.go +++ b/models/org_team_test.go @@ -286,7 +286,7 @@ func TestGetTeamMembers(t *testing.T) { func TestGetUserTeams(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) test := func(userID int64) { - teams, err := GetUserTeams(userID, ListOptions{}) + teams, _, err := SearchTeam(&SearchTeamOptions{UserID: userID}) assert.NoError(t, err) for _, team := range teams { AssertExistsAndLoadBean(t, &TeamUser{TeamID: team.ID, UID: userID}) diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index c9fdd8e99c3c..55d5130efbca 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -90,7 +90,10 @@ func ListUserTeams(ctx *context.APIContext) { // "200": // "$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 { ctx.Error(http.StatusInternalServerError, "GetUserTeams", err) return @@ -113,7 +116,8 @@ func ListUserTeams(ctx *context.APIContext) { apiTeams[i].Organization = apiOrg } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, apiTeams) } From d04f6c5997e012989255ef62a7a5e238a9a65a7a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 20:50:00 +0200 Subject: [PATCH 17/53] ListCollaborators --- models/repo_collaboration.go | 5 +++++ routers/api/v1/repo/collaborators.go | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index b9488f5e2e3e..a8b715bbcfe6 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -102,6 +102,11 @@ func (repo *Repository) GetCollaborators(listOptions ListOptions) ([]*Collaborat 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) { collaboration := &Collaboration{ RepoID: repo.ID, diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index d7c79b96c3ad..b790af14d56b 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -7,6 +7,7 @@ package repo import ( "errors" + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -47,17 +48,25 @@ func ListCollaborators(ctx *context.APIContext) { // "200": // "$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)) if err != nil { ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) return } + users := make([]*api.User, len(collaborators)) for i, collaborator := range collaborators { users[i] = convert.ToUser(collaborator.User, ctx.User) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, users) } From 0b4984e748675a5365ec9e0f24a6ee3f436144bd Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 20:53:28 +0200 Subject: [PATCH 18/53] fix bug --- models/repo.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/repo.go b/models/repo.go index 9ba53b67ef5f..7635ade2cd07 100644 --- a/models/repo.go +++ b/models/repo.go @@ -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. if u.IsOrganization() { - if err := u.LoadTeams(); err != nil { - return fmt.Errorf("LoadTeams: %v", err) + if err := u.loadTeams(ctx.e); err != nil { + return fmt.Errorf("loadTeams: %v", err) } for _, t := range u.Teams { if t.IncludesAllRepositories { From 99dbf98dc8fd37ed2c3cb0a962cbbf8d86e89507 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 21:00:31 +0200 Subject: [PATCH 19/53] Repo#ListHooks --- routers/api/v1/repo/hook.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index e2233177d8d7..4adb140f8f1b 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -6,6 +6,7 @@ package repo import ( + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -48,12 +49,20 @@ func ListHooks(ctx *context.APIContext) { // "200": // "$ref": "#/responses/HookList" - hooks, err := models.ListWebhooksByOpts(&models.ListWebhookOptions{ + opts := &models.ListWebhookOptions{ ListOptions: utils.GetListOptions(ctx), RepoID: ctx.Repo.Repository.ID, - }) + } + + count, err := models.CountWebhooksByOpts(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + + hooks, err := models.ListWebhooksByOpts(opts) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetWebhooksByRepoID", err) + ctx.InternalServerError(err) return } @@ -62,7 +71,8 @@ func ListHooks(ctx *context.APIContext) { apiHooks[i] = convert.ToHook(ctx.Repo.RepoLink, hooks[i]) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &apiHooks) } From 6c243acb146175ea3718b9620ac1757ec423139c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 21:10:41 +0200 Subject: [PATCH 20/53] ListIssueComments, ListRepoIssueComments --- models/issue.go | 2 +- models/issue_comment.go | 9 ++++++-- routers/api/v1/repo/issue_comment.go | 31 ++++++++++++++++++++++------ services/pull/review.go | 2 +- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/models/issue.go b/models/issue.go index 225dfee20f09..f252ebc9dcfe 100644 --- a/models/issue.go +++ b/models/issue.go @@ -214,7 +214,7 @@ func (issue *Issue) loadCommentsByType(e Engine, tp CommentType) (err error) { if issue.Comments != nil { return nil } - issue.Comments, err = findComments(e, FindCommentsOptions{ + issue.Comments, err = findComments(e, &FindCommentsOptions{ IssueID: issue.ID, Type: tp, }) diff --git a/models/issue_comment.go b/models/issue_comment.go index ae2dc0811fd0..355d146ca33a 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -1005,7 +1005,7 @@ func (opts *FindCommentsOptions) toConds() builder.Cond { return cond } -func findComments(e Engine, opts FindCommentsOptions) ([]*Comment, error) { +func findComments(e Engine, opts *FindCommentsOptions) ([]*Comment, error) { comments := make([]*Comment, 0, 10) sess := e.Where(opts.toConds()) if opts.RepoID > 0 { @@ -1025,10 +1025,15 @@ func findComments(e Engine, opts FindCommentsOptions) ([]*Comment, error) { } // FindComments returns all comments according options -func FindComments(opts FindCommentsOptions) ([]*Comment, error) { +func FindComments(opts *FindCommentsOptions) ([]*Comment, error) { return findComments(x, opts) } +// CountComments count all comments according options by ignoring pagination +func CountComments(opts *FindCommentsOptions) (int64, error) { + return x.Where(opts.toConds()).Count(&Comment{}) +} + // UpdateComment updates information of comment. func UpdateComment(c *Comment, doer *User) error { sess := x.NewSession() diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 992fc1f1d3f4..b05f797efdfe 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -7,6 +7,7 @@ package repo import ( "errors" + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -68,17 +69,25 @@ func ListIssueComments(ctx *context.APIContext) { } issue.Repo = ctx.Repo.Repository - comments, err := models.FindComments(models.FindCommentsOptions{ + opts := &models.FindCommentsOptions{ IssueID: issue.ID, Since: since, Before: before, Type: models.CommentTypeComment, - }) + } + + comments, err := models.FindComments(opts) if err != nil { ctx.Error(http.StatusInternalServerError, "FindComments", err) return } + totalCount, err := models.CountComments(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + if err := models.CommentList(comments).LoadPosters(); err != nil { ctx.Error(http.StatusInternalServerError, "LoadPosters", err) return @@ -90,7 +99,8 @@ func ListIssueComments(ctx *context.APIContext) { apiComments[i] = convert.ToComment(comments[i]) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalCount)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &apiComments) } @@ -140,18 +150,26 @@ func ListRepoIssueComments(ctx *context.APIContext) { return } - comments, err := models.FindComments(models.FindCommentsOptions{ + opts := &models.FindCommentsOptions{ ListOptions: utils.GetListOptions(ctx), RepoID: ctx.Repo.Repository.ID, Type: models.CommentTypeComment, Since: since, Before: before, - }) + } + + comments, err := models.FindComments(opts) if err != nil { ctx.Error(http.StatusInternalServerError, "FindComments", err) return } + totalCount, err := models.CountComments(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + if err = models.CommentList(comments).LoadPosters(); err != nil { ctx.Error(http.StatusInternalServerError, "LoadPosters", err) return @@ -174,7 +192,8 @@ func ListRepoIssueComments(ctx *context.APIContext) { apiComments[i] = convert.ToComment(comments[i]) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalCount)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &apiComments) } diff --git a/services/pull/review.go b/services/pull/review.go index b07e21fad977..3aa45706201a 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -132,7 +132,7 @@ func createCodeComment(doer *models.User, repo *models.Repository, issue *models head := pr.GetGitRefName() if line > 0 { if reviewID != 0 { - first, err := models.FindComments(models.FindCommentsOptions{ + first, err := models.FindComments(&models.FindCommentsOptions{ ReviewID: reviewID, Line: line, TreePath: treePath, From 251fe3ddb217683d05eedd1dd4291adbbf6aa28f Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 21:26:09 +0200 Subject: [PATCH 21/53] Fix models/issue_stopwatch.go --- models/issue_stopwatch.go | 58 +++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/models/issue_stopwatch.go b/models/issue_stopwatch.go index b72dcaf60c67..35472d7f4c85 100644 --- a/models/issue_stopwatch.go +++ b/models/issue_stopwatch.go @@ -9,6 +9,8 @@ import ( "time" "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" ) // Stopwatch represents a stopwatch for time tracking. @@ -61,6 +63,10 @@ func StopwatchExists(userID, issueID int64) bool { // HasUserStopwatch returns true if the user has a stopwatch func HasUserStopwatch(userID int64) (exists bool, sw *Stopwatch, err error) { + return hasUserStopwatch(x, userID) +} + +func hasUserStopwatch(e Engine, userID int64) (exists bool, sw *Stopwatch, err error) { sw = new(Stopwatch) exists, err = x. Where("user_id = ?", userID). @@ -70,11 +76,23 @@ func HasUserStopwatch(userID int64) (exists bool, sw *Stopwatch, err error) { // CreateOrStopIssueStopwatch will create or remove a stopwatch and will log it into issue's timeline. func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { - sw, exists, err := getStopwatch(x, user.ID, issue.ID) + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + if err := createOrStopIssueStopwatch(sess, user, issue); err != nil { + return err + } + return sess.Commit() +} + +func createOrStopIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error { + sw, exists, err := getStopwatch(e, user.ID, issue.ID) if err != nil { return err } - if err := issue.loadRepo(x); err != nil { + if err := issue.loadRepo(e); err != nil { return err } @@ -90,11 +108,11 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { Time: timediff, } - if _, err := x.Insert(tt); err != nil { + if _, err := e.Insert(tt); err != nil { return err } - if _, err := CreateComment(&CreateCommentOptions{ + if _, err := createComment(e, &CreateCommentOptions{ Doer: user, Issue: issue, Repo: issue.Repo, @@ -104,21 +122,21 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { }); err != nil { return err } - if _, err := x.Delete(sw); err != nil { + if _, err := e.Delete(sw); err != nil { return err } } else { // if another stopwatch is running: stop it - exists, sw, err := HasUserStopwatch(user.ID) + exists, sw, err := hasUserStopwatch(e, user.ID) if err != nil { return err } if exists { - issue, err := getIssueByID(x, sw.IssueID) + issue, err := getIssueByID(e, sw.IssueID) if err != nil { return err } - if err := CreateOrStopIssueStopwatch(user, issue); err != nil { + if err := createOrStopIssueStopwatch(e, user, issue); err != nil { return err } } @@ -129,11 +147,11 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { IssueID: issue.ID, } - if _, err := x.Insert(sw); err != nil { + if _, err := e.Insert(sw); err != nil { return err } - if _, err := CreateComment(&CreateCommentOptions{ + if _, err := createComment(e, &CreateCommentOptions{ Doer: user, Issue: issue, Repo: issue.Repo, @@ -147,21 +165,33 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { // CancelStopwatch removes the given stopwatch and logs it into issue's timeline. func CancelStopwatch(user *User, issue *Issue) error { - sw, exists, err := getStopwatch(x, user.ID, issue.ID) + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + if err := cancelStopwatch(sess, user, issue); err != nil { + return err + } + return sess.Commit() +} + +func cancelStopwatch(e *xorm.Session, user *User, issue *Issue) error { + sw, exists, err := getStopwatch(e, user.ID, issue.ID) if err != nil { return err } if exists { - if _, err := x.Delete(sw); err != nil { + if _, err := e.Delete(sw); err != nil { return err } - if err := issue.loadRepo(x); err != nil { + if err := issue.loadRepo(e); err != nil { return err } - if _, err := CreateComment(&CreateCommentOptions{ + if _, err := createComment(e, &CreateCommentOptions{ Doer: user, Issue: issue, Repo: issue.Repo, From a4577d87819ec8ddc4b5200e428c6d2fb05bff4a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 21:30:15 +0200 Subject: [PATCH 22/53] GetStopwatches --- models/issue_stopwatch.go | 5 +++++ routers/api/v1/repo/issue_stopwatch.go | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/models/issue_stopwatch.go b/models/issue_stopwatch.go index 35472d7f4c85..d2d317e259ca 100644 --- a/models/issue_stopwatch.go +++ b/models/issue_stopwatch.go @@ -55,6 +55,11 @@ func GetUserStopwatches(userID int64, listOptions ListOptions) ([]*Stopwatch, er return sws, nil } +// CountUserStopwatches return count of all stopwatches of a user +func CountUserStopwatches(userID int64) (int64, error) { + return x.Where("stopwatch.user_id = ?", userID).Count(&Stopwatch{}) +} + // StopwatchExists returns true if the stopwatch exists func StopwatchExists(userID, issueID int64) bool { _, exists, _ := getStopwatch(x, userID, issueID) diff --git a/routers/api/v1/repo/issue_stopwatch.go b/routers/api/v1/repo/issue_stopwatch.go index 3cbcaab0075c..6280ff4534bb 100644 --- a/routers/api/v1/repo/issue_stopwatch.go +++ b/routers/api/v1/repo/issue_stopwatch.go @@ -6,6 +6,7 @@ package repo import ( "errors" + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -225,12 +226,19 @@ func GetStopwatches(ctx *context.APIContext) { return } + count, err := models.CountUserStopwatches(ctx.User.ID) + if err != nil { + ctx.InternalServerError(err) + return + } + apiSWs, err := convert.ToStopWatches(sws) if err != nil { ctx.Error(http.StatusInternalServerError, "APIFormat", err) return } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, apiSWs) } From badb37ed6090f6bdce024924558a623d6fe214eb Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 21:37:32 +0200 Subject: [PATCH 23/53] TrackedTimes --- integrations/api_issue_tracked_time_test.go | 2 +- models/issue.go | 2 +- models/issue_tracked_time.go | 27 +++++++++------- models/issue_tracked_time_test.go | 22 ++++++------- routers/api/v1/repo/issue_tracked_time.go | 35 ++++++++++++++++----- routers/web/repo/issue.go | 2 +- 6 files changed, 58 insertions(+), 32 deletions(-) diff --git a/integrations/api_issue_tracked_time_test.go b/integrations/api_issue_tracked_time_test.go index 1a0ee99a43ad..9731c5a93b6c 100644 --- a/integrations/api_issue_tracked_time_test.go +++ b/integrations/api_issue_tracked_time_test.go @@ -30,7 +30,7 @@ func TestAPIGetTrackedTimes(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusOK) var apiTimes api.TrackedTimeList 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.Len(t, apiTimes, 3) diff --git a/models/issue.go b/models/issue.go index f252ebc9dcfe..8b83612c2a4f 100644 --- a/models/issue.go +++ b/models/issue.go @@ -89,7 +89,7 @@ func init() { func (issue *Issue) loadTotalTimes(e Engine) (err error) { 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 { return err } diff --git a/models/issue_tracked_time.go b/models/issue_tracked_time.go index 43f2e13784b7..0af31206dbbd 100644 --- a/models/issue_tracked_time.go +++ b/models/issue_tracked_time.go @@ -79,8 +79,8 @@ type FindTrackedTimesOptions struct { CreatedBeforeUnix int64 } -// ToCond will convert each condition into a xorm-Cond -func (opts *FindTrackedTimesOptions) ToCond() builder.Cond { +// toCond will convert each condition into a xorm-Cond +func (opts *FindTrackedTimesOptions) toCond() builder.Cond { cond := builder.NewCond().And(builder.Eq{"tracked_time.deleted": false}) if opts.IssueID != 0 { cond = cond.And(builder.Eq{"issue_id": opts.IssueID}) @@ -103,14 +103,14 @@ func (opts *FindTrackedTimesOptions) ToCond() builder.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 -func (opts *FindTrackedTimesOptions) ToSession(e Engine) Engine { +// 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 { sess := e if opts.RepositoryID > 0 || opts.MilestoneID > 0 { 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 { sess = opts.setEnginePagination(sess) @@ -119,18 +119,23 @@ func (opts *FindTrackedTimesOptions) ToSession(e Engine) Engine { return sess } -func getTrackedTimes(e Engine, options FindTrackedTimesOptions) (trackedTimes TrackedTimeList, err error) { - err = options.ToSession(e).Find(&trackedTimes) +func getTrackedTimes(e Engine, options *FindTrackedTimesOptions) (trackedTimes TrackedTimeList, err error) { + err = options.toSession(e).Find(&trackedTimes) return } // 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) } +// CountTrackedTimes returns count of tracked times that fit to the given options. +func CountTrackedTimes(opts *FindTrackedTimesOptions) (int64, error) { + return x.Where(opts.toCond()).Count(&TrackedTime{}) +} + 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 @@ -188,7 +193,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 -func TotalTimes(options FindTrackedTimesOptions) (map[*User]string, error) { +func TotalTimes(options *FindTrackedTimesOptions) (map[*User]string, error) { trackedTimes, err := GetTrackedTimes(options) if err != nil { return nil, err @@ -288,7 +293,7 @@ func deleteTimes(e Engine, opts FindTrackedTimesOptions) (removedTime int64, err 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 } diff --git a/models/issue_tracked_time_test.go b/models/issue_tracked_time_test.go index e30de6cb69d4..9de1dd699d6f 100644 --- a/models/issue_tracked_time_test.go +++ b/models/issue_tracked_time_test.go @@ -38,27 +38,27 @@ func TestGetTrackedTimes(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) // by Issue - times, err := GetTrackedTimes(FindTrackedTimesOptions{IssueID: 1}) + times, err := GetTrackedTimes(&FindTrackedTimesOptions{IssueID: 1}) assert.NoError(t, err) assert.Len(t, times, 1) 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.Len(t, times, 0) // by User - times, err = GetTrackedTimes(FindTrackedTimesOptions{UserID: 1}) + times, err = GetTrackedTimes(&FindTrackedTimesOptions{UserID: 1}) assert.NoError(t, err) assert.Len(t, times, 3) 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.Len(t, times, 0) // by Repo - times, err = GetTrackedTimes(FindTrackedTimesOptions{RepositoryID: 2}) + times, err = GetTrackedTimes(&FindTrackedTimesOptions{RepositoryID: 2}) assert.NoError(t, err) assert.Len(t, times, 3) assert.Equal(t, int64(1), times[0].Time) @@ -66,11 +66,11 @@ func TestGetTrackedTimes(t *testing.T) { assert.NoError(t, err) assert.Equal(t, issue.RepoID, int64(2)) - times, err = GetTrackedTimes(FindTrackedTimesOptions{RepositoryID: 1}) + times, err = GetTrackedTimes(&FindTrackedTimesOptions{RepositoryID: 1}) assert.NoError(t, err) assert.Len(t, times, 5) - times, err = GetTrackedTimes(FindTrackedTimesOptions{RepositoryID: 10}) + times, err = GetTrackedTimes(&FindTrackedTimesOptions{RepositoryID: 10}) assert.NoError(t, err) assert.Len(t, times, 0) } @@ -78,7 +78,7 @@ func TestGetTrackedTimes(t *testing.T) { func TestTotalTimes(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - total, err := TotalTimes(FindTrackedTimesOptions{IssueID: 1}) + total, err := TotalTimes(&FindTrackedTimesOptions{IssueID: 1}) assert.NoError(t, err) assert.Len(t, total, 1) for user, time := range total { @@ -86,7 +86,7 @@ func TestTotalTimes(t *testing.T) { assert.Equal(t, "6min 40s", time) } - total, err = TotalTimes(FindTrackedTimesOptions{IssueID: 2}) + total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 2}) assert.NoError(t, err) assert.Len(t, total, 2) 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.Len(t, total, 1) for user, time := range total { @@ -107,7 +107,7 @@ func TestTotalTimes(t *testing.T) { assert.Equal(t, "1s", time) } - total, err = TotalTimes(FindTrackedTimesOptions{IssueID: 4}) + total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 4}) assert.NoError(t, err) assert.Len(t, total, 2) } diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index 410aa46382e0..c1816085e29d 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -84,7 +84,7 @@ func ListTrackedTimes(ctx *context.APIContext) { return } - opts := models.FindTrackedTimesOptions{ + opts := &models.FindTrackedTimesOptions{ ListOptions: utils.GetListOptions(ctx), RepositoryID: ctx.Repo.Repository.ID, IssueID: issue.ID, @@ -120,6 +120,12 @@ func ListTrackedTimes(ctx *context.APIContext) { } } + count, err := models.CountTrackedTimes(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + trackedTimes, err := models.GetTrackedTimes(opts) if err != nil { ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err) @@ -130,7 +136,8 @@ func ListTrackedTimes(ctx *context.APIContext) { return } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) } @@ -426,7 +433,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) { return } - opts := models.FindTrackedTimesOptions{ + opts := &models.FindTrackedTimesOptions{ UserID: user.ID, RepositoryID: ctx.Repo.Repository.ID, } @@ -496,7 +503,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) { return } - opts := models.FindTrackedTimesOptions{ + opts := &models.FindTrackedTimesOptions{ ListOptions: utils.GetListOptions(ctx), RepositoryID: ctx.Repo.Repository.ID, } @@ -533,6 +540,12 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) { } } + count, err := models.CountTrackedTimes(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + trackedTimes, err := models.GetTrackedTimes(opts) if err != nil { ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err) @@ -543,7 +556,8 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) { return } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) } @@ -578,7 +592,7 @@ func ListMyTrackedTimes(ctx *context.APIContext) { // "200": // "$ref": "#/responses/TrackedTimeList" - opts := models.FindTrackedTimesOptions{ + opts := &models.FindTrackedTimesOptions{ ListOptions: utils.GetListOptions(ctx), UserID: ctx.User.ID, } @@ -589,6 +603,12 @@ func ListMyTrackedTimes(ctx *context.APIContext) { return } + count, err := models.CountTrackedTimes(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + trackedTimes, err := models.GetTrackedTimes(opts) if err != nil { ctx.Error(http.StatusInternalServerError, "GetTrackedTimesByUser", err) @@ -600,6 +620,7 @@ func ListMyTrackedTimes(ctx *context.APIContext) { return } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index b18720598828..d4f63122bede 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1265,7 +1265,7 @@ func ViewIssue(ctx *context.Context) { } else { 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) return } From c45c9d0a0396878b61696d08a806b9252ec6d2a7 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 21:58:44 +0200 Subject: [PATCH 24/53] refactor models.ListDeployKeys --- models/repo.go | 2 +- models/ssh_key_deploy.go | 55 +++++++++++++++++++++---------------- routers/api/v1/repo/key.go | 19 ++++++------- routers/web/repo/setting.go | 4 +-- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/models/repo.go b/models/repo.go index 7635ade2cd07..f2e3f5b58bc3 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1453,7 +1453,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error { } // Delete Deploy Keys - deployKeys, err := listDeployKeys(sess, repo.ID, ListOptions{}) + deployKeys, err := listDeployKeys(sess, &ListDeployKeysOptions{RepoID: repoID}) if err != nil { return fmt.Errorf("listDeployKeys: %v", err) } diff --git a/models/ssh_key_deploy.go b/models/ssh_key_deploy.go index 3189bcf456a6..b3192d3c4651 100644 --- a/models/ssh_key_deploy.go +++ b/models/ssh_key_deploy.go @@ -264,36 +264,43 @@ func deleteDeployKey(sess Engine, doer *User, id int64) error { return nil } -// ListDeployKeys returns all deploy keys by given repository ID. -func ListDeployKeys(repoID int64, listOptions ListOptions) ([]*DeployKey, error) { - return listDeployKeys(x, repoID, listOptions) +// ListDeployKeysOptions are options for ListDeployKeys +type ListDeployKeysOptions struct { + ListOptions + RepoID int64 + KeyID int64 + Fingerprint string +} + +func (opt ListDeployKeysOptions) toCond() builder.Cond { + cond := builder.NewCond() + if opt.RepoID != 0 { + cond = cond.And(builder.Eq{"repo_id": opt.RepoID}) + } + if opt.KeyID != 0 { + cond = cond.And(builder.Eq{"key_id": opt.KeyID}) + } + if opt.Fingerprint != "" { + cond = cond.And(builder.Eq{"fingerprint": opt.Fingerprint}) + } + return cond +} + +// 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, repoID int64, listOptions ListOptions) ([]*DeployKey, error) { - sess := e.Where("repo_id = ?", repoID) - if listOptions.Page != 0 { - sess = listOptions.setSessionPagination(sess) +func listDeployKeys(e Engine, opts *ListDeployKeysOptions) ([]*DeployKey, error) { + sess := e.Where(opts.toCond()) - keys := make([]*DeployKey, 0, listOptions.PageSize) + if opts.Page != 0 { + sess = opts.setSessionPagination(sess) + + keys := make([]*DeployKey, 0, opts.PageSize) return keys, sess.Find(&keys) } keys := make([]*DeployKey, 0, 5) return keys, sess.Find(&keys) } - -// SearchDeployKeys returns a list of deploy keys matching the provided arguments. -func SearchDeployKeys(repoID, keyID int64, fingerprint string) ([]*DeployKey, error) { - keys := make([]*DeployKey, 0, 5) - 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) -} diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index 91f54ed256a3..bd569273d4c2 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -75,26 +75,23 @@ func ListDeployKeys(ctx *context.APIContext) { // "200": // "$ref": "#/responses/DeployKeyList" - var keys []*models.DeployKey - var err error - - fingerprint := ctx.Query("fingerprint") - keyID := ctx.QueryInt64("key_id") - 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)) + opts := &models.ListDeployKeysOptions{ + ListOptions: utils.GetListOptions(ctx), + RepoID: ctx.Repo.Repository.ID, + KeyID: ctx.QueryInt64("key_id"), + Fingerprint: ctx.Query("fingerprint"), } + keys, err := models.ListDeployKeys(opts) if err != nil { - ctx.Error(http.StatusInternalServerError, "ListDeployKeys", err) + ctx.InternalServerError(err) return } apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name) apiKeys := make([]*api.DeployKey, len(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) return } diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index 0a84f15bf0bf..e9a2f69b728e 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -1002,7 +1002,7 @@ func DeployKeys(ctx *context.Context) { ctx.Data["PageIsSettingsKeys"] = true 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 { ctx.ServerError("ListDeployKeys", err) return @@ -1018,7 +1018,7 @@ func DeployKeysPost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") 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 { ctx.ServerError("ListDeployKeys", err) return From 6c8ec0a61705f0fdb39643e2ccc342ff32876867 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 22:00:44 +0200 Subject: [PATCH 25/53] Repo#ListDeployKeys --- models/ssh_key_deploy.go | 5 +++++ routers/api/v1/repo/key.go | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/models/ssh_key_deploy.go b/models/ssh_key_deploy.go index b3192d3c4651..e7d486b9f56c 100644 --- a/models/ssh_key_deploy.go +++ b/models/ssh_key_deploy.go @@ -304,3 +304,8 @@ func listDeployKeys(e Engine, opts *ListDeployKeysOptions) ([]*DeployKey, error) keys := make([]*DeployKey, 0, 5) return keys, sess.Find(&keys) } + +// CountDeployKeys returns count deploy keys matching the provided arguments. +func CountDeployKeys(opts *ListDeployKeysOptions) (int64, error) { + return x.Where(opts.toCond()).Count(&DeployKey{}) +} diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index bd569273d4c2..d54c21760c47 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -88,6 +88,12 @@ func ListDeployKeys(ctx *context.APIContext) { return } + count, err := models.CountDeployKeys(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name) apiKeys := make([]*api.DeployKey, len(keys)) for i := range keys { @@ -101,7 +107,8 @@ func ListDeployKeys(ctx *context.APIContext) { } } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &apiKeys) } From 32161804c57e3a1cc72aacfcdab01f87353578a1 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 22:02:43 +0200 Subject: [PATCH 26/53] Update models/issue_stopwatch.go --- models/issue_stopwatch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/issue_stopwatch.go b/models/issue_stopwatch.go index d2d317e259ca..4bef6b98d997 100644 --- a/models/issue_stopwatch.go +++ b/models/issue_stopwatch.go @@ -57,7 +57,7 @@ func GetUserStopwatches(userID int64, listOptions ListOptions) ([]*Stopwatch, er // CountUserStopwatches return count of all stopwatches of a user func CountUserStopwatches(userID int64) (int64, error) { - return x.Where("stopwatch.user_id = ?", userID).Count(&Stopwatch{}) + return x.Where("user_id = ?", userID).Count(&Stopwatch{}) } // StopwatchExists returns true if the stopwatch exists From 5a3963230644c30383a30dcc7caa9e04b95dae1f Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Jul 2021 23:18:52 +0200 Subject: [PATCH 27/53] CONTRIBUTING: mention Access-Control-Expose-Headers --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e8f00b1272f..46bb530bdc82 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -210,6 +210,7 @@ An endpoint which changes/edits an object expects all fields to be optional (exc ### Endpoints returning lists should * support pagination (`page` & `limit` options in query) * add `X-Total-Count` header ([example](https://github.com/go-gitea/gitea/blob/e76f8cac9a2ba727bec6b5beab2496be9dafabef/routers/api/v1/repo/issue.go#L445)) + * add `X-Total-Count` to the `Access-Control-Expose-Headers` header ## Developer Certificate of Origin (DCO) From 27398cfc82eb031113da74fb862445dde159c8bb Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 00:51:34 +0200 Subject: [PATCH 28/53] fix CountComments --- models/issue_comment.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/models/issue_comment.go b/models/issue_comment.go index 355d146ca33a..2e924661e6bf 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -1031,7 +1031,11 @@ func FindComments(opts *FindCommentsOptions) ([]*Comment, error) { // CountComments count all comments according options by ignoring pagination func CountComments(opts *FindCommentsOptions) (int64, error) { - return x.Where(opts.toConds()).Count(&Comment{}) + 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. From 7ae321de972a3d50535c12d64b9e543ab1f47dc8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 01:02:58 +0200 Subject: [PATCH 29/53] Repo#ListLabels --- models/issue_label.go | 5 +++++ routers/api/v1/repo/label.go | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/models/issue_label.go b/models/issue_label.go index 47a5210d9cef..5d50203b5a5b 100644 --- a/models/issue_label.go +++ b/models/issue_label.go @@ -444,6 +444,11 @@ func GetLabelsByRepoID(repoID int64, sortType string, listOptions 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{}) +} + // ________ // \_____ \_______ ____ // / | \_ __ \/ ___\ diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go index de5532626734..27adc3c85fb1 100644 --- a/routers/api/v1/repo/label.go +++ b/routers/api/v1/repo/label.go @@ -55,7 +55,14 @@ func ListLabels(ctx *context.APIContext) { return } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + count, err := models.CountLabelsByRepoID(ctx.Repo.Repository.ID) + if err != nil { + ctx.InternalServerError(err) + return + } + + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, convert.ToLabelList(labels)) } From 26965987cd6572eed0204ffe5af4fac483f04781 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 01:19:48 +0200 Subject: [PATCH 30/53] ListMilestones --- models/issue_milestone.go | 24 ++++++++++++++++------- models/issue_milestone_test.go | 8 ++++---- modules/migrations/gitea_uploader_test.go | 4 ++-- routers/api/v1/repo/milestone.go | 6 ++++-- routers/web/repo/issue.go | 6 +++--- routers/web/repo/milestone.go | 13 ++++-------- 6 files changed, 34 insertions(+), 27 deletions(-) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 5e934cde0a02..e6976a46c71e 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -380,24 +380,33 @@ type GetMilestonesOption struct { SortType string } -// GetMilestones returns milestones filtered by GetMilestonesOption's -func GetMilestones(opts GetMilestonesOption) (MilestoneList, error) { - sess := x.Where("repo_id = ?", opts.RepoID) +func (opts GetMilestonesOption) toCond() builder.Cond { + cond := builder.NewCond() + if opts.RepoID != 0 { + cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) + } switch opts.State { case api.StateClosed: - sess = sess.And("is_closed = ?", true) + cond = cond.And(builder.Eq{"is_closed": true}) case api.StateAll: break // api.StateOpen: default: - sess = sess.And("is_closed = ?", false) + cond = cond.And(builder.Eq{"is_closed": false}) } 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 { sess = opts.setSessionPagination(sess) } @@ -420,7 +429,8 @@ func GetMilestones(opts GetMilestonesOption) (MilestoneList, error) { } miles := make([]*Milestone, 0, opts.PageSize) - return miles, sess.Find(&miles) + total, err := sess.FindAndCount(&miles) + return miles, total, err } // SearchMilestones search milestones diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go index 5406129884fb..1472c951eb40 100644 --- a/models/issue_milestone_test.go +++ b/models/issue_milestone_test.go @@ -50,7 +50,7 @@ func TestGetMilestonesByRepoID(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) test := func(repoID int64, state api.StateType) { repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository) - milestones, err := GetMilestones(GetMilestonesOption{ + milestones, _, err := GetMilestones(GetMilestonesOption{ RepoID: repo.ID, State: state, }) @@ -87,7 +87,7 @@ func TestGetMilestonesByRepoID(t *testing.T) { test(3, api.StateClosed) test(3, api.StateAll) - milestones, err := GetMilestones(GetMilestonesOption{ + milestones, _, err := GetMilestones(GetMilestonesOption{ RepoID: NonexistentID, State: api.StateOpen, }) @@ -100,7 +100,7 @@ func TestGetMilestones(t *testing.T) { repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) test := func(sortType string, sortCond func(*Milestone) int) { for _, page := range []int{0, 1} { - milestones, err := GetMilestones(GetMilestonesOption{ + milestones, _, err := GetMilestones(GetMilestonesOption{ ListOptions: ListOptions{ Page: page, PageSize: setting.UI.IssuePagingNum, @@ -117,7 +117,7 @@ func TestGetMilestones(t *testing.T) { } assert.True(t, sort.IntsAreSorted(values)) - milestones, err = GetMilestones(GetMilestonesOption{ + milestones, _, err = GetMilestones(GetMilestonesOption{ ListOptions: ListOptions{ Page: page, PageSize: setting.UI.IssuePagingNum, diff --git a/modules/migrations/gitea_uploader_test.go b/modules/migrations/gitea_uploader_test.go index 5f36d545846d..2f854ba368a3 100644 --- a/modules/migrations/gitea_uploader_test.go +++ b/modules/migrations/gitea_uploader_test.go @@ -54,14 +54,14 @@ func TestGiteaUploadRepo(t *testing.T) { assert.True(t, repo.HasWiki()) assert.EqualValues(t, models.RepositoryReady, repo.Status) - milestones, err := models.GetMilestones(models.GetMilestonesOption{ + milestones, _, err := models.GetMilestones(models.GetMilestonesOption{ RepoID: repo.ID, State: structs.StateOpen, }) assert.NoError(t, err) assert.Len(t, milestones, 1) - milestones, err = models.GetMilestones(models.GetMilestonesOption{ + milestones, _, err = models.GetMilestones(models.GetMilestonesOption{ RepoID: repo.ID, State: structs.StateClosed, }) diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go index e77c67659e0b..5ea68dc07c4b 100644 --- a/routers/api/v1/repo/milestone.go +++ b/routers/api/v1/repo/milestone.go @@ -6,6 +6,7 @@ package repo import ( + "fmt" "net/http" "strconv" "time" @@ -57,7 +58,7 @@ func ListMilestones(ctx *context.APIContext) { // "200": // "$ref": "#/responses/MilestoneList" - milestones, err := models.GetMilestones(models.GetMilestonesOption{ + milestones, total, err := models.GetMilestones(models.GetMilestonesOption{ ListOptions: utils.GetListOptions(ctx), RepoID: ctx.Repo.Repository.ID, State: api.StateType(ctx.Query("state")), @@ -73,7 +74,8 @@ func ListMilestones(ctx *context.APIContext) { apiMilestones[i] = convert.ToAPIMilestone(milestones[i]) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &apiMilestones) } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index d4f63122bede..90ed9018aa92 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -378,7 +378,7 @@ func Issues(ctx *context.Context) { var err error // Get milestones - ctx.Data["Milestones"], err = models.GetMilestones(models.GetMilestonesOption{ + ctx.Data["Milestones"], _, err = models.GetMilestones(models.GetMilestonesOption{ RepoID: ctx.Repo.Repository.ID, State: api.StateType(ctx.Query("state")), }) @@ -395,7 +395,7 @@ func Issues(ctx *context.Context) { // RetrieveRepoMilestonesAndAssignees find all the milestones and assignees of a repository func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repository) { var err error - ctx.Data["OpenMilestones"], err = models.GetMilestones(models.GetMilestonesOption{ + ctx.Data["OpenMilestones"], _, err = models.GetMilestones(models.GetMilestonesOption{ RepoID: repo.ID, State: api.StateOpen, }) @@ -403,7 +403,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repos ctx.ServerError("GetMilestones", err) return } - ctx.Data["ClosedMilestones"], err = models.GetMilestones(models.GetMilestonesOption{ + ctx.Data["ClosedMilestones"], _, err = models.GetMilestones(models.GetMilestonesOption{ RepoID: repo.ID, State: api.StateClosed, }) diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go index 4cdca38dd02b..41abf867e12e 100644 --- a/routers/web/repo/milestone.go +++ b/routers/web/repo/milestone.go @@ -54,17 +54,12 @@ func Milestones(ctx *context.Context) { page = 1 } - var total int - var state structs.StateType - if !isShowClosed { - total = int(stats.OpenCount) - state = structs.StateOpen - } else { - total = int(stats.ClosedCount) + state := structs.StateOpen + if isShowClosed { state = structs.StateClosed } - miles, err := models.GetMilestones(models.GetMilestonesOption{ + miles, total, err := models.GetMilestones(models.GetMilestonesOption{ ListOptions: models.ListOptions{ Page: page, PageSize: setting.UI.IssuePagingNum, @@ -107,7 +102,7 @@ func Milestones(ctx *context.Context) { ctx.Data["Keyword"] = keyword 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, "q", "Keyword") ctx.Data["Page"] = pager From faf881796556306e9daa8f7167d079f1bd627b58 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 01:25:22 +0200 Subject: [PATCH 31/53] ListPullReviews --- models/review.go | 5 +++++ routers/api/v1/repo/pull_review.go | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/models/review.go b/models/review.go index acb54d970fdc..1ffff8feb647 100644 --- a/models/review.go +++ b/models/review.go @@ -208,6 +208,11 @@ func FindReviews(opts FindReviewOptions) ([]*Review, error) { 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. type CreateReviewOptions struct { Content string diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index b79139c63e69..6cb25bf51c2e 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -78,14 +78,21 @@ func ListPullReviews(ctx *context.APIContext) { return } - allReviews, err := models.FindReviews(models.FindReviewOptions{ + opts := models.FindReviewOptions{ ListOptions: utils.GetListOptions(ctx), Type: models.ReviewTypeUnknown, IssueID: pr.IssueID, - }) + } + + allReviews, err := models.FindReviews(opts) + if err != nil { + ctx.InternalServerError(err) + return + } + count, err := models.CountReviews(opts) if err != nil { - ctx.Error(http.StatusInternalServerError, "FindReviews", err) + ctx.InternalServerError(err) return } @@ -95,7 +102,8 @@ func ListPullReviews(ctx *context.APIContext) { return } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &apiReviews) } From 5eacdb7ffa3f9876342b6223f8327d0340d9a863 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 01:28:20 +0200 Subject: [PATCH 32/53] fix getLatestCommitStatus --- models/commit_status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/commit_status.go b/models/commit_status.go index 1105c3b17315..4193ad0d08d4 100644 --- a/models/commit_status.go +++ b/models/commit_status.go @@ -160,7 +160,7 @@ func getLatestCommitStatus(e Engine, repoID int64, sha string, listOptions ListO if len(ids) == 0 { 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 From e32426afdd34257edb3c2e8f2c049da7d5471eb8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 01:29:35 +0200 Subject: [PATCH 33/53] todo ... --- routers/api/v1/repo/status.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index dad61fbad23b..fed5a823fac9 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -268,5 +268,6 @@ func GetCombinedCommitStatusByRef(ctx *context.APIContext) { combiStatus := convert.ToCombinedStatus(statuses, convert.ToRepo(repo, ctx.Repo.AccessMode)) // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + // ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, combiStatus) } From a57a8d155e0b0811b737b618f4fa5c7e337b6e63 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 01:32:26 +0200 Subject: [PATCH 34/53] ListSubscribers --- routers/api/v1/repo/subscriber.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routers/api/v1/repo/subscriber.go b/routers/api/v1/repo/subscriber.go index 4c80426ef8d8..e6d67936acbe 100644 --- a/routers/api/v1/repo/subscriber.go +++ b/routers/api/v1/repo/subscriber.go @@ -5,6 +5,7 @@ package repo import ( + "fmt" "net/http" "code.gitea.io/gitea/modules/context" @@ -53,6 +54,7 @@ func ListSubscribers(ctx *context.APIContext) { users[i] = convert.ToUser(subscriber, ctx.User) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.Repo.Repository.NumWatches)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, users) } From 69ae323e47843fdfa545d00aab47dcc6b3bb40b4 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 01:34:22 +0200 Subject: [PATCH 35/53] no pagination supportet here --- routers/api/v1/repo/teams.go | 1 - 1 file changed, 1 deletion(-) diff --git a/routers/api/v1/repo/teams.go b/routers/api/v1/repo/teams.go index 15ef1f69fbf8..1348205ec95b 100644 --- a/routers/api/v1/repo/teams.go +++ b/routers/api/v1/repo/teams.go @@ -57,7 +57,6 @@ func ListTeams(ctx *context.APIContext) { apiTeams[i] = convert.ToTeam(teams[i]) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.JSON(http.StatusOK, apiTeams) } From 9334a423f6ff33342431a9d4bda0538266af3718 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 01:40:09 +0200 Subject: [PATCH 36/53] ListTags --- modules/git/repo_tag.go | 19 +++++++------------ modules/git/repo_tag_test.go | 3 ++- routers/api/v1/repo/tag.go | 5 +++-- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index d91c3ca97973..44d7a048bc76 100644 --- a/modules/git/repo_tag.go +++ b/modules/git/repo_tag.go @@ -10,6 +10,7 @@ import ( "strings" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" ) // 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. -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 stdout, err := NewCommand("tag").RunInDir(repo.Path) if err != nil { - return nil, err + return nil, 0, err } tagNames := strings.Split(strings.TrimRight(stdout, "\n"), "\n") + tagsTotal := len(tagNames) if page != 0 { - skip := (page - 1) * pageSize - if skip >= len(tagNames) { - return nil, nil - } - if (len(tagNames) - skip) < pageSize { - pageSize = len(tagNames) - skip - } - tagNames = tagNames[skip : skip+pageSize] + tagNames = util.PaginateSlice(tagNames, page, pageSize).([]string) } 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) if err != nil { - return nil, err + return nil, tagsTotal, err } tag.Name = tagName tags = append(tags, tag) } sortTagsByTime(tags) - return tags, nil + return tags, tagsTotal, nil } // GetTagType gets the type of the tag, either commit (simple) or tag (annotated) diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go index 6feae8d91325..cfab9edd8f42 100644 --- a/modules/git/repo_tag_test.go +++ b/modules/git/repo_tag_test.go @@ -18,9 +18,10 @@ func TestRepository_GetTags(t *testing.T) { assert.NoError(t, err) defer bareRepo1.Close() - tags, err := bareRepo1.GetTagInfos(0, 0) + tags, total, err := bareRepo1.GetTagInfos(0, 0) assert.NoError(t, err) assert.Len(t, tags, 1) + assert.Equal(t, len(tags), total) assert.EqualValues(t, "test", tags[0].Name) assert.EqualValues(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", tags[0].ID.String()) assert.EqualValues(t, "tag", tags[0].Type) diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index 38e503846442..09a606e7d661 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -50,7 +50,7 @@ func ListTags(ctx *context.APIContext) { 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 { ctx.Error(http.StatusInternalServerError, "GetTags", err) return @@ -61,7 +61,8 @@ func ListTags(ctx *context.APIContext) { apiTags[i] = convert.ToTag(ctx.Repo.Repository, tags[i]) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &apiTags) } From 0d4632db0bb320afa98b530ab3d4c82e00706441 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 01:49:22 +0200 Subject: [PATCH 37/53] ListOauth2Applications --- models/oauth2_application.go | 8 +++++--- routers/api/v1/user/app.go | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/models/oauth2_application.go b/models/oauth2_application.go index 2aa9fbd3d921..9cf236f0cb42 100644 --- a/models/oauth2_application.go +++ b/models/oauth2_application.go @@ -269,7 +269,7 @@ func DeleteOAuth2Application(id, userid int64) error { } // 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. Where("uid=?", uid). Desc("id") @@ -278,11 +278,13 @@ func ListOAuth2Applications(uid int64, listOptions ListOptions) ([]*OAuth2Applic sess = listOptions.setSessionPagination(sess) 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) - return apps, sess.Find(&apps) + total, err := sess.FindAndCount(&apps) + return apps, total, err } ////////////////////////////////////////////////////// diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 5d381a8085da..69c07b935a0b 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -257,7 +257,7 @@ func ListOauth2Applications(ctx *context.APIContext) { // "200": // "$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 { ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err) return @@ -269,7 +269,8 @@ func ListOauth2Applications(ctx *context.APIContext) { apiApps[i].ClientSecret = "" // Hide secret on application list } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &apiApps) } From 02ada6549dda942b9189943f2c42a3d63eb190f7 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 01:55:54 +0200 Subject: [PATCH 38/53] ListTopics --- models/topic.go | 5 +++++ routers/api/v1/repo/topic.go | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/models/topic.go b/models/topic.go index 19c572fefebf..359dea8523b6 100644 --- a/models/topic.go +++ b/models/topic.go @@ -195,6 +195,11 @@ func FindTopics(opts *FindTopicOptions) (topics []*Topic, err error) { return topics, sess.Desc("topic.repo_count").Find(&topics) } +// CountTopicsByRepoID return amount of topics a specific repo has +func CountTopicsByRepoID(repoID int64) (int64, error) { + return x.Where("repo_id =?", repoID).Count(&RepoTopic{}) +} + // GetRepoTopicByName retrieves topic from name for a repo if it exist func GetRepoTopicByName(repoID int64, topicName string) (*Topic, error) { return getRepoTopicByName(x, repoID, topicName) diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index 109dd7aa007b..096fa87cf255 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -5,6 +5,7 @@ package repo import ( + "fmt" "net/http" "strings" @@ -52,7 +53,12 @@ func ListTopics(ctx *context.APIContext) { RepoID: ctx.Repo.Repository.ID, }) if err != nil { - log.Error("ListTopics failed: %v", err) + ctx.InternalServerError(err) + return + } + + count, err := models.CountTopicsByRepoID(ctx.Repo.Repository.ID) + if err != nil { ctx.InternalServerError(err) return } @@ -62,7 +68,8 @@ func ListTopics(ctx *context.APIContext) { topicNames[i] = topic.Name } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, map[string]interface{}{ "topics": topicNames, }) From 02d19a72a18d78df6f6772bc42626d5e53288817 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 02:01:57 +0200 Subject: [PATCH 39/53] TopicSearch --- models/topic.go | 13 +++++++++---- routers/api/v1/repo/topic.go | 31 ++++++++++++++++--------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/models/topic.go b/models/topic.go index 359dea8523b6..973b68dbc9a2 100644 --- a/models/topic.go +++ b/models/topic.go @@ -184,7 +184,7 @@ func (opts *FindTopicOptions) toConds() builder.Cond { } // FindTopics retrieves the topics via FindTopicOptions -func FindTopics(opts *FindTopicOptions) (topics []*Topic, err error) { +func FindTopics(opts *FindTopicOptions) ([]*Topic, error) { sess := x.Select("topic.*").Where(opts.toConds()) if opts.RepoID > 0 { sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") @@ -192,12 +192,17 @@ func FindTopics(opts *FindTopicOptions) (topics []*Topic, err error) { if opts.PageSize != 0 && opts.Page != 0 { sess = opts.setSessionPagination(sess) } + topics := make([]*Topic, 0, 10) return topics, sess.Desc("topic.repo_count").Find(&topics) } -// CountTopicsByRepoID return amount of topics a specific repo has -func CountTopicsByRepoID(repoID int64) (int64, error) { - return x.Where("repo_id =?", repoID).Count(&RepoTopic{}) +// CountTopics count topics via FindTopicOptions +func CountTopics(opts *FindTopicOptions) (int64, error) { + sess := x.Select("topic.*").Where(opts.toConds()) + if opts.RepoID > 0 { + sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") + } + return sess.Count(&Topic{}) } // GetRepoTopicByName retrieves topic from name for a repo if it exist diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index 096fa87cf255..99743cb89cf6 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -48,16 +48,18 @@ func ListTopics(ctx *context.APIContext) { // "200": // "$ref": "#/responses/TopicNames" - topics, err := models.FindTopics(&models.FindTopicOptions{ + opts := &models.FindTopicOptions{ ListOptions: utils.GetListOptions(ctx), RepoID: ctx.Repo.Repository.ID, - }) + } + + topics, err := models.FindTopics(opts) if err != nil { ctx.InternalServerError(err) return } - count, err := models.CountTopicsByRepoID(ctx.Repo.Repository.ID) + count, err := models.CountTopics(opts) if err != nil { ctx.InternalServerError(err) return @@ -278,21 +280,19 @@ func TopicSearch(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - if ctx.User == nil { - ctx.Error(http.StatusForbidden, "UserIsNil", "Only owners could change the topics.") - return + opts := &models.FindTopicOptions{ + Keyword: ctx.Query("q"), + ListOptions: utils.GetListOptions(ctx), } - kw := ctx.Query("q") - - listOptions := utils.GetListOptions(ctx) + topics, err := models.FindTopics(opts) + if err != nil { + ctx.InternalServerError(err) + return + } - topics, err := models.FindTopics(&models.FindTopicOptions{ - Keyword: kw, - ListOptions: listOptions, - }) + count, err := models.CountTopics(opts) if err != nil { - log.Error("SearchTopics failed: %v", err) ctx.InternalServerError(err) return } @@ -302,7 +302,8 @@ func TopicSearch(ctx *context.APIContext) { topicResponses[i] = convert.ToTopicResponse(topic) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, map[string]interface{}{ "topics": topicResponses, }) From 01936226f7fea7dadfe271a9150dc16a0a77dbc8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 02:04:23 +0200 Subject: [PATCH 40/53] Followers and Following --- routers/api/v1/user/follower.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index 70a23db7a1ea..82cb98bda54f 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -6,6 +6,7 @@ package user import ( + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -30,7 +31,8 @@ func listUserFollowers(ctx *context.APIContext, u *models.User) { return } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", u.NumFollowers)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") responseAPIUsers(ctx, users) } @@ -96,7 +98,8 @@ func listUserFollowing(ctx *context.APIContext, u *models.User) { return } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", u.NumFollowing)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") responseAPIUsers(ctx, users) } From b132c7a0007b5898109621f64a8b61e78de1d3e0 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 02:10:30 +0200 Subject: [PATCH 41/53] listGPGKeys --- models/gpg_key.go | 5 +++++ routers/api/v1/user/gpg_key.go | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/models/gpg_key.go b/models/gpg_key.go index 74ffb82a545b..1072813b1b71 100644 --- a/models/gpg_key.go +++ b/models/gpg_key.go @@ -71,6 +71,11 @@ func listGPGKeys(e Engine, uid int64, listOptions ListOptions) ([]*GPGKey, error 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. func GetGPGKeyByID(keyID int64) (*GPGKey, error) { key := new(GPGKey) diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index e6cc3c743502..35edf7274335 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -28,7 +28,14 @@ func listGPGKeys(ctx *context.APIContext, uid int64, listOptions models.ListOpti apiKeys[i] = convert.ToGPGKey(keys[i]) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + total, err := models.CountUserGPGKeys(uid) + if err != nil { + ctx.InternalServerError(err) + return + } + + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &apiKeys) } From dceacf92dc02b30834c7dc2cac0050c7edf29f34 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 02:16:04 +0200 Subject: [PATCH 42/53] listPublicKeys --- models/ssh_key.go | 6 ++++++ routers/api/v1/user/key.go | 13 ++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/models/ssh_key.go b/models/ssh_key.go index 6cda4f1658fb..a2f4c610e404 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -205,6 +205,12 @@ func ListPublicKeys(uid int64, listOptions ListOptions) ([]*PublicKey, error) { 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. func ListPublicKeysBySource(uid, loginSourceID int64) ([]*PublicKey, error) { keys := make([]*PublicKey, 0, 5) diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index 0b0bb1bf68dc..7da9acf57641 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -5,6 +5,7 @@ package user import ( + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -47,6 +48,7 @@ func composePublicKeysAPILink() string { func listPublicKeys(ctx *context.APIContext, user *models.User) { var keys []*models.PublicKey var err error + var count int fingerprint := ctx.Query("fingerprint") username := ctx.Params("username") @@ -60,7 +62,15 @@ func listPublicKeys(ctx *context.APIContext, user *models.User) { // Unrestricted keys, err = models.SearchPublicKey(0, fingerprint) } + count = len(keys) } else { + total, err2 := models.CountPublicKeys(user.ID) + if err2 != nil { + ctx.InternalServerError(err) + return + } + count = int(total) + // Use ListPublicKeys keys, err = models.ListPublicKeys(user.ID, utils.GetListOptions(ctx)) } @@ -79,7 +89,8 @@ func listPublicKeys(ctx *context.APIContext, user *models.User) { } } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &apiKeys) } From fae949e3cce14ba7619a30b1b20aa7d15519996e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 02:19:05 +0200 Subject: [PATCH 43/53] GetMyStarredRepos --- routers/api/v1/user/star.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index 57c4de51159a..9d129736c8a3 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -6,6 +6,7 @@ package user import ( + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -93,7 +94,8 @@ func GetMyStarredRepos(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "getStarredRepos", err) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.User.NumStars)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &repos) } From 6505d16391d122c02b181807ca58dc50235a0ed5 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 02:23:09 +0200 Subject: [PATCH 44/53] getWatchedRepos --- models/user.go | 8 +++++--- routers/api/v1/user/watch.go | 23 +++++++++++++---------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/models/user.go b/models/user.go index a4f94999ee44..1308367e7a42 100644 --- a/models/user.go +++ b/models/user.go @@ -1711,7 +1711,7 @@ func GetStarredRepos(userID int64, private bool, listOptions ListOptions) ([]*Re } // 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). And("`watch`.mode<>?", RepoWatchModeDont). Join("LEFT", "watch", "`repository`.id=`watch`.repo_id") @@ -1723,11 +1723,13 @@ func GetWatchedRepos(userID int64, private bool, listOptions ListOptions) ([]*Re sess = listOptions.setSessionPagination(sess) 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) - return repos, sess.Find(&repos) + total, err := sess.FindAndCount(&repos) + return repos, total, err } // IterateUser iterate users diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go index 1f0bb62d115d..bb9842e0610a 100644 --- a/routers/api/v1/user/watch.go +++ b/routers/api/v1/user/watch.go @@ -5,6 +5,7 @@ package user import ( + "fmt" "net/http" "code.gitea.io/gitea/models" @@ -14,23 +15,22 @@ import ( "code.gitea.io/gitea/routers/api/v1/utils" ) -// getWatchedRepos returns the repos that the user with the specified userID is -// watching -func getWatchedRepos(user *models.User, private bool, listOptions models.ListOptions) ([]*api.Repository, error) { - watchedRepos, err := models.GetWatchedRepos(user.ID, private, listOptions) +// getWatchedRepos returns the repos that the user with the specified userID is watching +func getWatchedRepos(user *models.User, private bool, listOptions models.ListOptions) ([]*api.Repository, int64, error) { + watchedRepos, total, err := models.GetWatchedRepos(user.ID, private, listOptions) if err != nil { - return nil, err + return nil, 0, err } repos := make([]*api.Repository, len(watchedRepos)) for i, watched := range watchedRepos { access, err := models.AccessLevel(user, watched) if err != nil { - return nil, err + return nil, 0, err } 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 @@ -60,10 +60,12 @@ func GetWatchedRepos(ctx *context.APIContext) { user := GetUserByParams(ctx) 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 { ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) } + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &repos) } @@ -87,12 +89,13 @@ func GetMyWatchedRepos(ctx *context.APIContext) { // "200": // "$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 { ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) } - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) + ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, &repos) } From 2f5545f3ab83e0cb5105f5979a135f583696576a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 02:30:35 +0200 Subject: [PATCH 45/53] fix CountTrackedTimes --- models/issue_tracked_time.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/models/issue_tracked_time.go b/models/issue_tracked_time.go index 0af31206dbbd..e7769b41dd41 100644 --- a/models/issue_tracked_time.go +++ b/models/issue_tracked_time.go @@ -131,7 +131,11 @@ func GetTrackedTimes(opts *FindTrackedTimesOptions) (TrackedTimeList, error) { // CountTrackedTimes returns count of tracked times that fit to the given options. func CountTrackedTimes(opts *FindTrackedTimesOptions) (int64, error) { - return x.Where(opts.toCond()).Count(&TrackedTime{}) + 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) { From c706d0f3f37e28f0af0cb8e2f561f7e8d1e8ee3d Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 19:24:09 +0200 Subject: [PATCH 46/53] Fix APIRepoTopic --- models/topic.go | 16 ++++------------ models/topic_test.go | 15 ++++++++------- routers/api/v1/repo/topic.go | 22 +++++----------------- routers/web/repo/view.go | 2 +- 4 files changed, 18 insertions(+), 37 deletions(-) diff --git a/models/topic.go b/models/topic.go index 973b68dbc9a2..af13b0107b07 100644 --- a/models/topic.go +++ b/models/topic.go @@ -184,7 +184,7 @@ func (opts *FindTopicOptions) toConds() builder.Cond { } // FindTopics retrieves the topics via FindTopicOptions -func FindTopics(opts *FindTopicOptions) ([]*Topic, error) { +func FindTopics(opts *FindTopicOptions) ([]*Topic, int64, error) { sess := x.Select("topic.*").Where(opts.toConds()) if opts.RepoID > 0 { sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") @@ -193,16 +193,8 @@ func FindTopics(opts *FindTopicOptions) ([]*Topic, error) { sess = opts.setSessionPagination(sess) } topics := make([]*Topic, 0, 10) - return topics, sess.Desc("topic.repo_count").Find(&topics) -} - -// CountTopics count topics via FindTopicOptions -func CountTopics(opts *FindTopicOptions) (int64, error) { - sess := x.Select("topic.*").Where(opts.toConds()) - if opts.RepoID > 0 { - sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") - } - return sess.Count(&Topic{}) + total, err := sess.Desc("topic.repo_count").FindAndCount(&topics) + return topics, total, err } // GetRepoTopicByName retrieves topic from name for a repo if it exist @@ -279,7 +271,7 @@ func DeleteTopic(repoID int64, topicName string) (*Topic, error) { // SaveTopics save topics to a repository func SaveTopics(repoID int64, topicNames ...string) error { - topics, err := FindTopics(&FindTopicOptions{ + topics, _, err := FindTopics(&FindTopicOptions{ RepoID: repoID, }) if err != nil { diff --git a/models/topic_test.go b/models/topic_test.go index 25232eb981c2..9386a71e35d2 100644 --- a/models/topic_test.go +++ b/models/topic_test.go @@ -17,17 +17,18 @@ func TestAddTopic(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - topics, err := FindTopics(&FindTopicOptions{}) + topics, _, err := FindTopics(&FindTopicOptions{}) assert.NoError(t, err) assert.Len(t, topics, totalNrOfTopics) - topics, err = FindTopics(&FindTopicOptions{ + topics, total, err := FindTopics(&FindTopicOptions{ ListOptions: ListOptions{Page: 1, PageSize: 2}, }) assert.NoError(t, err) assert.Len(t, topics, 2) + assert.EqualValues(t, 6, total) - topics, err = FindTopics(&FindTopicOptions{ + topics, _, err = FindTopics(&FindTopicOptions{ RepoID: 1, }) assert.NoError(t, err) @@ -35,11 +36,11 @@ func TestAddTopic(t *testing.T) { assert.NoError(t, SaveTopics(2, "golang")) repo2NrOfTopics = 1 - topics, err = FindTopics(&FindTopicOptions{}) + topics, _, err = FindTopics(&FindTopicOptions{}) assert.NoError(t, err) assert.Len(t, topics, totalNrOfTopics) - topics, err = FindTopics(&FindTopicOptions{ + topics, _, err = FindTopics(&FindTopicOptions{ RepoID: 2, }) assert.NoError(t, err) @@ -52,11 +53,11 @@ func TestAddTopic(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, topic.RepoCount) - topics, err = FindTopics(&FindTopicOptions{}) + topics, _, err = FindTopics(&FindTopicOptions{}) assert.NoError(t, err) assert.Len(t, topics, totalNrOfTopics) - topics, err = FindTopics(&FindTopicOptions{ + topics, _, err = FindTopics(&FindTopicOptions{ RepoID: 2, }) assert.NoError(t, err) diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index 99743cb89cf6..8a779267d533 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -53,13 +53,7 @@ func ListTopics(ctx *context.APIContext) { RepoID: ctx.Repo.Repository.ID, } - topics, err := models.FindTopics(opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - count, err := models.CountTopics(opts) + topics, total, err := models.FindTopics(opts) if err != nil { ctx.InternalServerError(err) return @@ -70,7 +64,7 @@ func ListTopics(ctx *context.APIContext) { topicNames[i] = topic.Name } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, map[string]interface{}{ "topics": topicNames, @@ -175,7 +169,7 @@ func AddTopic(ctx *context.APIContext) { } // Prevent adding more topics than allowed to repo - topics, err := models.FindTopics(&models.FindTopicOptions{ + topics, _, err := models.FindTopics(&models.FindTopicOptions{ RepoID: ctx.Repo.Repository.ID, }) if err != nil { @@ -285,13 +279,7 @@ func TopicSearch(ctx *context.APIContext) { ListOptions: utils.GetListOptions(ctx), } - topics, err := models.FindTopics(opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - count, err := models.CountTopics(opts) + topics, total, err := models.FindTopics(opts) if err != nil { ctx.InternalServerError(err) return @@ -302,7 +290,7 @@ func TopicSearch(ctx *context.APIContext) { topicResponses[i] = convert.ToTopicResponse(topic) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) + ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") ctx.JSON(http.StatusOK, map[string]interface{}{ "topics": topicResponses, diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 21bd80c40667..6f0ab4af4b80 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -674,7 +674,7 @@ func renderLanguageStats(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, }) if err != nil { From 0ed695b3f81d78493fea9acf13a43269f301454a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 19:51:03 +0200 Subject: [PATCH 47/53] Add TestAPITopicSearch --- integrations/api_repo_topic_test.go | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/integrations/api_repo_topic_test.go b/integrations/api_repo_topic_test.go index 5e42bc64bf92..bab17a68be5a 100644 --- a/integrations/api_repo_topic_test.go +++ b/integrations/api_repo_topic_test.go @@ -7,6 +7,7 @@ package integrations import ( "fmt" "net/http" + "net/url" "testing" "code.gitea.io/gitea/models" @@ -15,6 +16,38 @@ import ( "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) { defer prepareTestEnv(t)() user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of repo2 From 443561758d6cfaeddecc2f5d5571b2d1c49f5194 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 27 Jul 2021 20:14:39 +0200 Subject: [PATCH 48/53] refert back routers/api/v1/org/member.go Co-authored-by: zeripath --- routers/api/v1/org/member.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 898e87bd5830..c30f69c503fb 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -38,8 +38,8 @@ func listMembers(ctx *context.APIContext, publicOnly bool) { } apiMembers := make([]*api.User, len(members)) - for i := range members { - apiMembers[i] = convert.ToUser(members[i], ctx.User) + for i, member := range members { + apiMembers[i] = convert.ToUser(member, ctx.User) } ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) From 3861004720c0e57fee3e5e707c810d4099542c43 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Wed, 28 Jul 2021 19:30:44 +0100 Subject: [PATCH 49/53] Just use count topics instead of getting the topics from the db Signed-off-by: Andrew Thornton --- models/topic.go | 9 +++++++++ routers/api/v1/repo/topic.go | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/models/topic.go b/models/topic.go index af13b0107b07..80dca4e532da 100644 --- a/models/topic.go +++ b/models/topic.go @@ -197,6 +197,15 @@ func FindTopics(opts *FindTopicOptions) ([]*Topic, int64, error) { return topics, total, err } +// CountTopics counts the number of topics matching the FindTopicOptions +func CountTopics(opts *FindTopicOptions) (int64, error) { + sess := x.Select("topic.*").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 func GetRepoTopicByName(repoID int64, topicName string) (*Topic, error) { return getRepoTopicByName(x, repoID, topicName) diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index 8a779267d533..49ee698b4a3f 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -169,15 +169,15 @@ func AddTopic(ctx *context.APIContext) { } // 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, }) if err != nil { - log.Error("AddTopic failed: %v", err) + log.Error("CountTopics failed: %v", err) ctx.InternalServerError(err) return } - if len(topics) >= 25 { + if count >= 25 { ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ "message": "Exceeding maximum allowed topics per repo.", }) From 8958bbf98ddc64043a2de4b8f06297a2d9aa594c Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Thu, 29 Jul 2021 20:22:56 +0100 Subject: [PATCH 50/53] oops Signed-off-by: Andrew Thornton --- models/topic.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/topic.go b/models/topic.go index 80dca4e532da..7fc34f5bef19 100644 --- a/models/topic.go +++ b/models/topic.go @@ -199,7 +199,7 @@ func FindTopics(opts *FindTopicOptions) ([]*Topic, int64, error) { // CountTopics counts the number of topics matching the FindTopicOptions func CountTopics(opts *FindTopicOptions) (int64, error) { - sess := x.Select("topic.*").Where(opts.toConds()) + sess := x.Where(opts.toConds()) if opts.RepoID > 0 { sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") } From 7aae98cc5d4113f1e9918b7ee7dd09f67c189e3e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 10 Aug 2021 01:44:08 +0200 Subject: [PATCH 51/53] introduce helper func and use them for SetLinkHeader related func --- modules/context/api.go | 17 +++++++++++++++++ routers/api/v1/admin/org.go | 4 +--- routers/api/v1/admin/user.go | 3 +-- routers/api/v1/org/org.go | 9 +++------ routers/api/v1/org/team.go | 3 +-- routers/api/v1/repo/branch.go | 5 ++--- routers/api/v1/repo/commits.go | 8 ++++---- routers/api/v1/repo/issue.go | 6 ++---- routers/api/v1/repo/pull.go | 10 +++++----- routers/api/v1/repo/release.go | 4 +--- routers/api/v1/repo/repo.go | 3 +-- routers/api/v1/repo/status.go | 3 +-- routers/api/v1/user/repo.go | 7 ++----- routers/api/v1/user/user.go | 4 +--- 14 files changed, 42 insertions(+), 44 deletions(-) diff --git a/modules/context/api.go b/modules/context/api.go index b543c8bac826..47ea8acfe05f 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -181,6 +181,23 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) { if len(links) > 0 { 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, ", ")) } } diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index 1356276f07f4..f1a766d544d7 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -6,7 +6,6 @@ package admin import ( - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -121,7 +120,6 @@ func GetAllOrgs(ctx *context.APIContext) { } ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, &orgs) } diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 6bc9b849b1fc..e5a75da759ea 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -423,7 +423,6 @@ func GetAllUsers(ctx *context.APIContext) { } ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, &results) } diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 5c16594f89d1..39ce896cd670 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -6,7 +6,6 @@ package org import ( - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -38,9 +37,8 @@ func listUserOrgs(ctx *context.APIContext, u *models.User) { apiOrgs[i] = convert.ToOrganization(orgs[i]) } - ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetLinkHeader(maxResults, listOptions.PageSize) + ctx.SetTotalCountHeader(int64(maxResults)) ctx.JSON(http.StatusOK, &apiOrgs) } @@ -145,8 +143,7 @@ func GetAll(ctx *context.APIContext) { } ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, &orgs) } diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 3c3353b59dc7..cdfafe068587 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -702,8 +702,7 @@ func SearchTeam(ctx *context.APIContext) { } ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, map[string]interface{}{ "ok": true, "data": apiTeams, diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 85c1681dfec1..8653b0bc809a 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -282,9 +282,8 @@ func ListBranches(ctx *context.APIContext) { } } - ctx.SetLinkHeader(int(totalNumOfBranches), listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalNumOfBranches)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetLinkHeader(totalNumOfBranches, listOptions.PageSize) + ctx.SetTotalCountHeader(int64(totalNumOfBranches)) ctx.JSON(http.StatusOK, &apiBranches) } diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index 9950a7d4567a..c6fe39cae881 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -200,16 +200,16 @@ func GetAllCommits(ctx *context.APIContext) { } } + ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize) + ctx.SetTotalCountHeader(commitsCountTotal) + // kept for backwards compatibility ctx.Header().Set("X-Page", strconv.Itoa(listOptions.Page)) ctx.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize)) ctx.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10)) ctx.Header().Set("X-PageCount", strconv.Itoa(pageCount)) ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount)) - - 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.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-Total", "X-PageCount", "X-HasMore") ctx.JSON(http.StatusOK, &apiCommits) } diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 172ad480318a..97450300da1e 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -232,8 +232,7 @@ func SearchIssues(ctx *context.APIContext) { } ctx.SetLinkHeader(int(filteredCount), setting.UI.IssuePagingNum) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", filteredCount)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(filteredCount) ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues)) } @@ -442,8 +441,7 @@ func ListIssues(ctx *context.APIContext) { } ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", filteredCount)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(filteredCount) ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues)) } diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 9be6228bfdf8..0a903101c753 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -119,8 +119,7 @@ func ListPullRequests(ctx *context.APIContext) { } ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, &apiPrs) } @@ -1232,13 +1231,14 @@ func GetPullRequestCommits(ctx *context.APIContext) { 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-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-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) } diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 97b90079f264..6438c6e9b4ff 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -5,7 +5,6 @@ package repo import ( - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -142,8 +141,7 @@ func ListReleases(ctx *context.APIContext) { } ctx.SetLinkHeader(int(filteredCount), listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprint(filteredCount)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(filteredCount) ctx.JSON(http.StatusOK, rels) } diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 9c93408832e6..458aacdabf00 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -230,8 +230,7 @@ func Search(ctx *context.APIContext) { } ctx.SetLinkHeader(int(count), opts.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, api.SearchResults{ OK: true, Data: results, diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index 1f16f02ce27d..f761af4422cf 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -204,8 +204,7 @@ func getCommitStatuses(ctx *context.APIContext, sha string) { } ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, apiStatuses) } diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go index 0331abef1004..5116c17ab2af 100644 --- a/routers/api/v1/user/repo.go +++ b/routers/api/v1/user/repo.go @@ -6,7 +6,6 @@ package user import ( "net/http" - "strconv" "code.gitea.io/gitea/models" "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.Header().Set("X-Total-Count", strconv.FormatInt(count, 10)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, &apiRepos) } @@ -130,8 +128,7 @@ func ListMyRepos(ctx *context.APIContext) { } ctx.SetLinkHeader(int(count), opts.ListOptions.PageSize) - ctx.Header().Set("X-Total-Count", strconv.FormatInt(count, 10)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, &results) } diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index de1fd5a5c583..4b4352e04f8c 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -6,7 +6,6 @@ package user import ( - "fmt" "net/http" "strings" @@ -74,8 +73,7 @@ func Search(ctx *context.APIContext) { } ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") + ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, map[string]interface{}{ "ok": true, From 8fa7079fc29fd67b9b7e53c7bf95f5c465063cf3 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 10 Aug 2021 02:00:33 +0200 Subject: [PATCH 52/53] use SetTotalCountHeader everywhere --- routers/api/v1/admin/adopt.go | 4 +--- routers/api/v1/admin/cron.go | 4 +--- routers/api/v1/notify/repo.go | 5 ++--- routers/api/v1/notify/user.go | 4 +--- routers/api/v1/org/hook.go | 4 +--- routers/api/v1/org/label.go | 3 +-- routers/api/v1/org/member.go | 4 +--- routers/api/v1/org/team.go | 10 +++------- routers/api/v1/repo/collaborators.go | 4 +--- routers/api/v1/repo/fork.go | 3 +-- routers/api/v1/repo/hook.go | 4 +--- routers/api/v1/repo/issue_comment.go | 7 ++----- routers/api/v1/repo/issue_stopwatch.go | 4 +--- routers/api/v1/repo/issue_tracked_time.go | 9 +++------ routers/api/v1/repo/key.go | 3 +-- routers/api/v1/repo/label.go | 3 +-- routers/api/v1/repo/milestone.go | 4 +--- routers/api/v1/repo/pull_review.go | 3 +-- routers/api/v1/repo/star.go | 4 +--- routers/api/v1/repo/status.go | 3 +-- routers/api/v1/repo/subscriber.go | 4 +--- routers/api/v1/repo/tag.go | 3 +-- routers/api/v1/repo/topic.go | 7 ++----- routers/api/v1/user/app.go | 6 ++---- routers/api/v1/user/follower.go | 7 ++----- routers/api/v1/user/gpg_key.go | 3 +-- routers/api/v1/user/key.go | 4 +--- routers/api/v1/user/star.go | 4 +--- routers/api/v1/user/watch.go | 8 +++----- 29 files changed, 40 insertions(+), 95 deletions(-) diff --git a/routers/api/v1/admin/adopt.go b/routers/api/v1/admin/adopt.go index fb5888f08371..47c1f454a5bf 100644 --- a/routers/api/v1/admin/adopt.go +++ b/routers/api/v1/admin/adopt.go @@ -5,7 +5,6 @@ package admin import ( - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -47,8 +46,7 @@ func ListUnadoptedRepositories(ctx *context.APIContext) { ctx.InternalServerError(err) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(int64(count)) ctx.JSON(http.StatusOK, repoNames) } diff --git a/routers/api/v1/admin/cron.go b/routers/api/v1/admin/cron.go index 22a41bb04bc9..970fad83880c 100644 --- a/routers/api/v1/admin/cron.go +++ b/routers/api/v1/admin/cron.go @@ -5,7 +5,6 @@ package admin import ( - "fmt" "net/http" "code.gitea.io/gitea/modules/context" @@ -54,8 +53,7 @@ func ListCronTasks(ctx *context.APIContext) { } } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(int64(count)) ctx.JSON(http.StatusOK, res) } diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go index 2e9673eb1ae0..4c3aa7fd6d0a 100644 --- a/routers/api/v1/notify/repo.go +++ b/routers/api/v1/notify/repo.go @@ -5,7 +5,6 @@ package notify import ( - "fmt" "net/http" "strings" "time" @@ -126,8 +125,8 @@ func ListRepoNotifications(ctx *context.APIContext) { return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalCount)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(totalCount) + ctx.JSON(http.StatusOK, convert.ToNotifications(nl)) } diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go index e87686f4046b..b8ffdc284f56 100644 --- a/routers/api/v1/notify/user.go +++ b/routers/api/v1/notify/user.go @@ -5,7 +5,6 @@ package notify import ( - "fmt" "net/http" "strings" "time" @@ -87,8 +86,7 @@ func ListNotifications(ctx *context.APIContext) { return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalCount)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(totalCount) ctx.JSON(http.StatusOK, convert.ToNotifications(nl)) } diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go index 59c73b1eb562..c5982300eba2 100644 --- a/routers/api/v1/org/hook.go +++ b/routers/api/v1/org/hook.go @@ -5,7 +5,6 @@ package org import ( - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -63,8 +62,7 @@ func ListHooks(ctx *context.APIContext) { hooks[i] = convert.ToHook(ctx.Org.Organization.HomeLink(), hook) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, hooks) } diff --git a/routers/api/v1/org/label.go b/routers/api/v1/org/label.go index 53f8dbcabf38..7bb299b8efa2 100644 --- a/routers/api/v1/org/label.go +++ b/routers/api/v1/org/label.go @@ -55,8 +55,7 @@ func ListLabels(ctx *context.APIContext) { return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, convert.ToLabelList(labels)) } diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index c30f69c503fb..97940d59251d 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -5,7 +5,6 @@ package org import ( - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -42,8 +41,7 @@ func listMembers(ctx *context.APIContext, publicOnly bool) { apiMembers[i] = convert.ToUser(member, ctx.User) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, apiMembers) } diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index cdfafe068587..0870e51636c0 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -6,7 +6,6 @@ package org import ( - "fmt" "net/http" "strings" @@ -65,8 +64,7 @@ func ListTeams(ctx *context.APIContext) { apiTeams[i] = convert.ToTeam(teams[i]) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, apiTeams) } @@ -116,8 +114,7 @@ func ListUserTeams(ctx *context.APIContext) { apiTeams[i].Organization = apiOrg } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, apiTeams) } @@ -351,8 +348,7 @@ func GetTeamMembers(ctx *context.APIContext) { members[i] = convert.ToUser(member, ctx.User) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.Org.Team.NumMembers)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(int64(ctx.Org.Team.NumMembers)) ctx.JSON(http.StatusOK, members) } diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index b790af14d56b..d636220f62cf 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -7,7 +7,6 @@ package repo import ( "errors" - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -65,8 +64,7 @@ func ListCollaborators(ctx *context.APIContext) { users[i] = convert.ToUser(collaborator.User, ctx.User) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, users) } diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index bb265edf785b..a3f9aa14f997 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -63,8 +63,7 @@ func ListForks(ctx *context.APIContext) { apiForks[i] = convert.ToRepo(fork, access) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.Repo.Repository.NumForks)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(int64(ctx.Repo.Repository.NumForks)) ctx.JSON(http.StatusOK, apiForks) } diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index 4adb140f8f1b..74860fd72f2c 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -6,7 +6,6 @@ package repo import ( - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -71,8 +70,7 @@ func ListHooks(ctx *context.APIContext) { apiHooks[i] = convert.ToHook(ctx.Repo.RepoLink, hooks[i]) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, &apiHooks) } diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index b05f797efdfe..13e7de46b1f1 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -7,7 +7,6 @@ package repo import ( "errors" - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -99,8 +98,7 @@ func ListIssueComments(ctx *context.APIContext) { apiComments[i] = convert.ToComment(comments[i]) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalCount)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(totalCount) ctx.JSON(http.StatusOK, &apiComments) } @@ -192,8 +190,7 @@ func ListRepoIssueComments(ctx *context.APIContext) { apiComments[i] = convert.ToComment(comments[i]) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalCount)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(totalCount) ctx.JSON(http.StatusOK, &apiComments) } diff --git a/routers/api/v1/repo/issue_stopwatch.go b/routers/api/v1/repo/issue_stopwatch.go index 6280ff4534bb..82a9ffe10bb7 100644 --- a/routers/api/v1/repo/issue_stopwatch.go +++ b/routers/api/v1/repo/issue_stopwatch.go @@ -6,7 +6,6 @@ package repo import ( "errors" - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -238,7 +237,6 @@ func GetStopwatches(ctx *context.APIContext) { return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, apiSWs) } diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index cb0fd531565d..3472d8301e66 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -136,8 +136,7 @@ func ListTrackedTimes(ctx *context.APIContext) { return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) } @@ -556,8 +555,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) { return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) } @@ -620,7 +618,6 @@ func ListMyTrackedTimes(ctx *context.APIContext) { return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes)) } diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index a645671e9f97..c63b308dc88a 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -107,8 +107,7 @@ func ListDeployKeys(ctx *context.APIContext) { } } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, &apiKeys) } diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go index 69a19ad36090..1bc87b6bfb1e 100644 --- a/routers/api/v1/repo/label.go +++ b/routers/api/v1/repo/label.go @@ -61,8 +61,7 @@ func ListLabels(ctx *context.APIContext) { return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, convert.ToLabelList(labels)) } diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go index 6c1f015ff9fe..ef642633b00e 100644 --- a/routers/api/v1/repo/milestone.go +++ b/routers/api/v1/repo/milestone.go @@ -6,7 +6,6 @@ package repo import ( - "fmt" "net/http" "strconv" "time" @@ -74,8 +73,7 @@ func ListMilestones(ctx *context.APIContext) { apiMilestones[i] = convert.ToAPIMilestone(milestones[i]) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, &apiMilestones) } diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 6cb25bf51c2e..55b5178305ed 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -102,8 +102,7 @@ func ListPullReviews(ctx *context.APIContext) { return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, &apiReviews) } diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go index d1a85564c9e0..5fa42c3244e4 100644 --- a/routers/api/v1/repo/star.go +++ b/routers/api/v1/repo/star.go @@ -5,7 +5,6 @@ package repo import ( - "fmt" "net/http" "code.gitea.io/gitea/modules/context" @@ -54,7 +53,6 @@ func ListStargazers(ctx *context.APIContext) { users[i] = convert.ToUser(stargazer, ctx.User) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.Repo.Repository.NumStars)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(int64(ctx.Repo.Repository.NumStars)) ctx.JSON(http.StatusOK, users) } diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index f761af4422cf..b884432f73b8 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -266,7 +266,6 @@ func GetCombinedCommitStatusByRef(ctx *context.APIContext) { combiStatus := convert.ToCombinedStatus(statuses, convert.ToRepo(repo, ctx.Repo.AccessMode)) - // TODO: ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - // ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + // TODO: ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, combiStatus) } diff --git a/routers/api/v1/repo/subscriber.go b/routers/api/v1/repo/subscriber.go index e6d67936acbe..dae92969ba60 100644 --- a/routers/api/v1/repo/subscriber.go +++ b/routers/api/v1/repo/subscriber.go @@ -5,7 +5,6 @@ package repo import ( - "fmt" "net/http" "code.gitea.io/gitea/modules/context" @@ -54,7 +53,6 @@ func ListSubscribers(ctx *context.APIContext) { users[i] = convert.ToUser(subscriber, ctx.User) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.Repo.Repository.NumWatches)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(int64(ctx.Repo.Repository.NumWatches)) ctx.JSON(http.StatusOK, users) } diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index 09a606e7d661..8d42e63a489f 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -61,8 +61,7 @@ func ListTags(ctx *context.APIContext) { apiTags[i] = convert.ToTag(ctx.Repo.Repository, tags[i]) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(int64(total)) ctx.JSON(http.StatusOK, &apiTags) } diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index 971afa670a6f..8e687c75f0aa 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -5,7 +5,6 @@ package repo import ( - "fmt" "net/http" "strings" @@ -64,8 +63,7 @@ func ListTopics(ctx *context.APIContext) { topicNames[i] = topic.Name } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, map[string]interface{}{ "topics": topicNames, }) @@ -290,8 +288,7 @@ func TopicSearch(ctx *context.APIContext) { topicResponses[i] = convert.ToTopicResponse(topic) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, map[string]interface{}{ "topics": topicResponses, }) diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index d448a9fe05e5..f0f7cb4159b6 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -66,8 +66,7 @@ func ListAccessTokens(ctx *context.APIContext) { } } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, &apiTokens) } @@ -264,8 +263,7 @@ func ListOauth2Applications(ctx *context.APIContext) { apiApps[i].ClientSecret = "" // Hide secret on application list } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, &apiApps) } diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index 82cb98bda54f..e273ac6a0207 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -6,7 +6,6 @@ package user import ( - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -31,8 +30,7 @@ func listUserFollowers(ctx *context.APIContext, u *models.User) { return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", u.NumFollowers)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(int64(u.NumFollowers)) responseAPIUsers(ctx, users) } @@ -98,8 +96,7 @@ func listUserFollowing(ctx *context.APIContext, u *models.User) { return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", u.NumFollowing)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(int64(u.NumFollowing)) responseAPIUsers(ctx, users) } diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 35edf7274335..f32d60d03816 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -34,8 +34,7 @@ func listGPGKeys(ctx *context.APIContext, uid int64, listOptions models.ListOpti return } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, &apiKeys) } diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index 7f958b8f7a4b..079795f08237 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -5,7 +5,6 @@ package user import ( - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -89,8 +88,7 @@ func listPublicKeys(ctx *context.APIContext, user *models.User) { } } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(int64(count)) ctx.JSON(http.StatusOK, &apiKeys) } diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index 9d129736c8a3..8ee167685639 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -6,7 +6,6 @@ package user import ( - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -94,8 +93,7 @@ func GetMyStarredRepos(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "getStarredRepos", err) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", ctx.User.NumStars)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(int64(ctx.User.NumStars)) ctx.JSON(http.StatusOK, &repos) } diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go index bb9842e0610a..f32ce7359864 100644 --- a/routers/api/v1/user/watch.go +++ b/routers/api/v1/user/watch.go @@ -5,7 +5,6 @@ package user import ( - "fmt" "net/http" "code.gitea.io/gitea/models" @@ -64,8 +63,8 @@ func GetWatchedRepos(ctx *context.APIContext) { if err != nil { ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, &repos) } @@ -94,8 +93,7 @@ func GetMyWatchedRepos(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) } - ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", total)) - ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count") + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, &repos) } From af4086dcc24a355f045eedad5bfae791ab52c98a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 10 Aug 2021 02:05:30 +0200 Subject: [PATCH 53/53] update docs --- CONTRIBUTING.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46bb530bdc82..026536868d50 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -209,8 +209,7 @@ An endpoint which changes/edits an object expects all fields to be optional (exc ### Endpoints returning lists should * support pagination (`page` & `limit` options in query) - * add `X-Total-Count` header ([example](https://github.com/go-gitea/gitea/blob/e76f8cac9a2ba727bec6b5beab2496be9dafabef/routers/api/v1/repo/issue.go#L445)) - * add `X-Total-Count` to the `Access-Control-Expose-Headers` header + * 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) @@ -236,8 +235,8 @@ on, finishing, and issuing releases. The overall goal is to make a minor release every three or four months, which breaks down into two or three months of general development followed by one month of testing and polishing known as the release freeze. All the feature pull requests should be -merged before feature freeze. And, during the frozen period, a corresponding -release branch is open for fixes backported from main branch. Release candidates +merged before feature freeze. And, during the frozen period, a corresponding +release branch is open for fixes backported from main branch. Release candidates are made during this period for user testing to obtain a final version that is maintained in this branch. A release is maintained by issuing patch releases to only correct critical problems