Skip to content

Commit 7950023

Browse files
author
Nate Smith
authored
Merge pull request cli#541 from cli/issue-list-pagination
Support `issue list --limit=N` where N is greater than 100
2 parents c60ccf9 + a544544 commit 7950023

File tree

2 files changed

+116
-21
lines changed

2 files changed

+116
-21
lines changed

api/queries_issue.go

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -185,20 +185,23 @@ func IssueList(client *Client, repo ghrepo.Interface, state string, labels []str
185185
}
186186

187187
query := fragments + `
188-
query($owner: String!, $repo: String!, $limit: Int, $states: [IssueState!] = OPEN, $labels: [String!], $assignee: String) {
189-
repository(owner: $owner, name: $repo) {
190-
hasIssuesEnabled
191-
issues(first: $limit, orderBy: {field: CREATED_AT, direction: DESC}, states: $states, labels: $labels, filterBy: {assignee: $assignee}) {
192-
nodes {
193-
...issue
194-
}
195-
}
196-
}
197-
}
198-
`
188+
query($owner: String!, $repo: String!, $limit: Int, $endCursor: String, $states: [IssueState!] = OPEN, $labels: [String!], $assignee: String) {
189+
repository(owner: $owner, name: $repo) {
190+
hasIssuesEnabled
191+
issues(first: $limit, after: $endCursor, orderBy: {field: CREATED_AT, direction: DESC}, states: $states, labels: $labels, filterBy: {assignee: $assignee}) {
192+
nodes {
193+
...issue
194+
}
195+
pageInfo {
196+
hasNextPage
197+
endCursor
198+
}
199+
}
200+
}
201+
}
202+
`
199203

200204
variables := map[string]interface{}{
201-
"limit": limit,
202205
"owner": repo.RepoOwner(),
203206
"repo": repo.RepoName(),
204207
"states": states,
@@ -210,25 +213,49 @@ func IssueList(client *Client, repo ghrepo.Interface, state string, labels []str
210213
variables["assignee"] = assigneeString
211214
}
212215

213-
var resp struct {
216+
var response struct {
214217
Repository struct {
215218
Issues struct {
216-
Nodes []Issue
219+
Nodes []Issue
220+
PageInfo struct {
221+
HasNextPage bool
222+
EndCursor string
223+
}
217224
}
218225
HasIssuesEnabled bool
219226
}
220227
}
221228

222-
err := client.GraphQL(query, variables, &resp)
223-
if err != nil {
224-
return nil, err
225-
}
229+
var issues []Issue
230+
pageLimit := min(limit, 100)
226231

227-
if !resp.Repository.HasIssuesEnabled {
228-
return nil, fmt.Errorf("the '%s' repository has disabled issues", ghrepo.FullName(repo))
232+
loop:
233+
for {
234+
variables["limit"] = pageLimit
235+
err := client.GraphQL(query, variables, &response)
236+
if err != nil {
237+
return nil, err
238+
}
239+
if !response.Repository.HasIssuesEnabled {
240+
return nil, fmt.Errorf("the '%s' repository has disabled issues", ghrepo.FullName(repo))
241+
}
242+
243+
for _, issue := range response.Repository.Issues.Nodes {
244+
issues = append(issues, issue)
245+
if len(issues) == limit {
246+
break loop
247+
}
248+
}
249+
250+
if response.Repository.Issues.PageInfo.HasNextPage {
251+
variables["endCursor"] = response.Repository.Issues.PageInfo.EndCursor
252+
pageLimit = min(pageLimit, limit-len(issues))
253+
} else {
254+
break
255+
}
229256
}
230257

231-
return resp.Repository.Issues.Nodes, nil
258+
return issues, nil
232259
}
233260

234261
func IssueByNumber(client *Client, repo ghrepo.Interface, number int) (*Issue, error) {

api/queries_issue_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package api
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"io/ioutil"
7+
"testing"
8+
9+
"github.com/cli/cli/internal/ghrepo"
10+
)
11+
12+
func TestIssueList(t *testing.T) {
13+
http := &FakeHTTP{}
14+
client := NewClient(ReplaceTripper(http))
15+
16+
http.StubResponse(200, bytes.NewBufferString(`
17+
{ "data": { "repository": {
18+
"hasIssuesEnabled": true,
19+
"issues": {
20+
"nodes": [],
21+
"pageInfo": {
22+
"hasNextPage": true,
23+
"endCursor": "ENDCURSOR"
24+
}
25+
}
26+
} } }
27+
`))
28+
http.StubResponse(200, bytes.NewBufferString(`
29+
{ "data": { "repository": {
30+
"hasIssuesEnabled": true,
31+
"issues": {
32+
"nodes": [],
33+
"pageInfo": {
34+
"hasNextPage": false,
35+
"endCursor": "ENDCURSOR"
36+
}
37+
}
38+
} } }
39+
`))
40+
41+
_, err := IssueList(client, ghrepo.FromFullName("OWNER/REPO"), "open", []string{}, "", 251)
42+
if err != nil {
43+
t.Fatalf("unexpected error: %v", err)
44+
}
45+
46+
if len(http.Requests) != 2 {
47+
t.Fatalf("expected 2 HTTP requests, seen %d", len(http.Requests))
48+
}
49+
var reqBody struct {
50+
Query string
51+
Variables map[string]interface{}
52+
}
53+
54+
bodyBytes, _ := ioutil.ReadAll(http.Requests[0].Body)
55+
json.Unmarshal(bodyBytes, &reqBody)
56+
if reqLimit := reqBody.Variables["limit"].(float64); reqLimit != 100 {
57+
t.Errorf("expected 100, got %v", reqLimit)
58+
}
59+
if _, cursorPresent := reqBody.Variables["endCursor"]; cursorPresent {
60+
t.Error("did not expect first request to pass 'endCursor'")
61+
}
62+
63+
bodyBytes, _ = ioutil.ReadAll(http.Requests[1].Body)
64+
json.Unmarshal(bodyBytes, &reqBody)
65+
if endCursor := reqBody.Variables["endCursor"].(string); endCursor != "ENDCURSOR" {
66+
t.Errorf("expected %q, got %q", "ENDCURSOR", endCursor)
67+
}
68+
}

0 commit comments

Comments
 (0)