feat(cmd): pagination unification + fj api --paginate
Before this, only `release list` walked pages. `repo list`, `pr list` (the
non-filter branch), and `issue list` all passed `PageSize: limit` directly
to the gitea SDK — which silently caps PageSize at 50, so any request for
more than 50 results was truncated to 50 with no warning. `--limit` was
effectively a per-page hint, not a real limit.
## Changes
- New `cmd/paginate.go` — generic `paginateGitea[T any]` that walks pages
until the response is short or the limit is reached. Uses Go 1.20
generics so each list command keeps its existing typed slice without
conversion overhead.
- `repo list` — paginates ListUserRepos.
- `pr list` — paginates ListRepoPullRequests in both branches:
- With client-side filters (assignee, author, labels, search, draft,
head, base): pull all pages then filter+limit.
- Without filters: paginate up to limit.
- `issue list` — paginates ListRepoIssues. Overshoots 2x because the API
returns both issues AND PRs and we filter PRs out client-side; the
overshoot keeps us bounded but reduces the chance of returning fewer
results than `--limit`.
## `fj api --paginate`
Mirrors `gh api --paginate`:
- Follows RFC 5988 `Link: rel="next"` headers (Forgejo emits these on
list endpoints).
- Concatenates each page's JSON array into a single array via
`concatPaginatedJSON`. If a page is not a JSON array, errors with a
clear message — `--paginate` only makes sense for paginatable endpoints.
- GET-only (errors on POST/PUT/DELETE).
- Reuses the same auth and custom headers across pages; the body-size
limit applies per-page.
Refactored the request execution into a `doOnce` closure so the loop body
isn't a copy of the single-request path.
Verified live:
$ fj api 'repos/public/claude-code-proxy/commits?limit=2' \
--paginate --jq '. | length'
44
(44 = total commits in the repo, walked via Link headers from a 2-per-page
starting query.)
Out of scope for this commit, deferred:
- De-duplicating cmd/aliases.go ↔ cmd/actions.go subtrees (the type
mismatch they caused is already fixed in the prior commit; the
duplication itself is polish).
This commit is contained in:
parent
0c181df1d1
commit
133fb2fea4
5 changed files with 199 additions and 66 deletions
45
cmd/pr.go
45
cmd/pr.go
|
|
@ -252,39 +252,32 @@ func runPRList(cmd *cobra.Command, args []string) error {
|
|||
needsClientFilter := assignee != "" || author != "" || len(labels) > 0 || search != "" || draft || head != "" || base != ""
|
||||
|
||||
ios.StartSpinner("Fetching pull requests...")
|
||||
// When client-side filtering is needed, pull pages until exhausted (no
|
||||
// limit) so we can apply filters; otherwise paginate up to the user's
|
||||
// limit. Either way, paginate — `PageSize: limit` capped at 50 silently.
|
||||
fetchPage := func(page, pageSize int) ([]*gitea.PullRequest, error) {
|
||||
batch, _, err := client.ListRepoPullRequests(owner, name, gitea.ListPullRequestsOptions{
|
||||
State: stateType,
|
||||
ListOptions: gitea.ListOptions{Page: page, PageSize: pageSize},
|
||||
})
|
||||
return batch, err
|
||||
}
|
||||
var prs []*gitea.PullRequest
|
||||
if needsClientFilter {
|
||||
page := 1
|
||||
for {
|
||||
batch, _, err := client.ListRepoPullRequests(owner, name, gitea.ListPullRequestsOptions{
|
||||
State: stateType,
|
||||
ListOptions: gitea.ListOptions{Page: page, PageSize: 50},
|
||||
})
|
||||
if err != nil {
|
||||
ios.StopSpinner()
|
||||
return fmt.Errorf("failed to list pull requests: %w", err)
|
||||
prs, err = paginateGitea(0, fetchPage) // pull all, then filter + limit
|
||||
if err == nil {
|
||||
prs = filterPRs(prs, author, assignee, labels, search, draft, head, base)
|
||||
if limit > 0 && len(prs) > limit {
|
||||
prs = prs[:limit]
|
||||
}
|
||||
prs = append(prs, batch...)
|
||||
if len(batch) < 50 {
|
||||
break
|
||||
}
|
||||
page++
|
||||
}
|
||||
prs = filterPRs(prs, author, assignee, labels, search, draft, head, base)
|
||||
if len(prs) > limit {
|
||||
prs = prs[:limit]
|
||||
}
|
||||
} else {
|
||||
prs, _, err = client.ListRepoPullRequests(owner, name, gitea.ListPullRequestsOptions{
|
||||
State: stateType,
|
||||
ListOptions: gitea.ListOptions{PageSize: limit},
|
||||
})
|
||||
if err != nil {
|
||||
ios.StopSpinner()
|
||||
return fmt.Errorf("failed to list pull requests: %w", err)
|
||||
}
|
||||
prs, err = paginateGitea(limit, fetchPage)
|
||||
}
|
||||
ios.StopSpinner()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list pull requests: %w", err)
|
||||
}
|
||||
|
||||
if wantJSON(cmd) {
|
||||
return outputJSON(cmd, prs)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue