feat(api): add Link header in ListForks (#38052)

Fixes #38051.

Disclosure: writing of the integration test was AI assisted.

---------

Signed-off-by: Eugenio Paolantonio <eugenio.paolantonio@suse.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Eugenio Paolantonio
2026-06-10 17:34:10 +00:00
committed by GitHub
co-authored by GitHub wxiaoguang
parent 19d1e1d334
commit fa89785d33
2 changed files with 34 additions and 2 deletions
+3 -1
View File
@@ -55,7 +55,8 @@ func ListForks(ctx *context.APIContext) {
// "404": // "404":
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"
forks, total, err := repo_service.FindForks(ctx, ctx.Repo.Repository, ctx.Doer, utils.GetListOptions(ctx)) listOptions := utils.GetListOptions(ctx)
forks, total, err := repo_service.FindForks(ctx, ctx.Repo.Repository, ctx.Doer, listOptions)
if err != nil { if err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return return
@@ -79,6 +80,7 @@ func ListForks(ctx *context.APIContext) {
apiForks[i] = convert.ToRepo(ctx, fork, permission) apiForks[i] = convert.ToRepo(ctx, fork, permission)
} }
ctx.SetLinkHeader(total, listOptions.PageSize)
ctx.SetTotalCountHeader(total) ctx.SetTotalCountHeader(total)
ctx.JSON(http.StatusOK, apiForks) ctx.JSON(http.StatusOK, apiForks)
} }
+31 -1
View File
@@ -89,7 +89,7 @@ func testAPIForkListLimitedAndPrivateRepos(t *testing.T) {
assert.Equal(t, "0", resp.Header().Get("X-Total-Count")) assert.Equal(t, "0", resp.Header().Get("X-Total-Count"))
}) })
t.Run("Logged in", func(t *testing.T) { t.Run("LoggedIn", func(t *testing.T) {
defer tests.PrintCurrentTest(t)() defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/forks").AddTokenAuth(user1Token) req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/forks").AddTokenAuth(user1Token)
@@ -107,6 +107,36 @@ func testAPIForkListLimitedAndPrivateRepos(t *testing.T) {
assert.Len(t, forks, 2) assert.Len(t, forks, 2)
assert.Equal(t, "2", resp.Header().Get("X-Total-Count")) assert.Equal(t, "2", resp.Header().Get("X-Total-Count"))
}) })
t.Run("RespHeaderLinks", func(t *testing.T) {
t.Run("Page1", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/forks?page=1&limit=1").AddTokenAuth(user1Token)
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "2", resp.Header().Get("X-Total-Count"))
linkHeader := resp.Header().Get("Link")
assert.NotEmpty(t, linkHeader, "Link header should not be empty")
assert.Contains(t, linkHeader, `rel="next"`)
assert.Contains(t, linkHeader, `rel="last"`)
assert.Contains(t, linkHeader, `/api/v1/repos/user2/repo1/forks?limit=1&page=2>`)
forks := DecodeJSON(t, resp, []*api.Repository{})
assert.Len(t, forks, 1)
})
t.Run("Page2", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/forks?page=2&limit=1").AddTokenAuth(user1Token)
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "2", resp.Header().Get("X-Total-Count"))
forks := DecodeJSON(t, resp, []*api.Repository{})
assert.Len(t, forks, 1)
})
})
} }
func testGetPrivateReposForks(t *testing.T) { func testGetPrivateReposForks(t *testing.T) {