-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add pagination examples for RESTClient and GQLClient #102
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,20 @@ | ||
package gh_test | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"log" | ||
"net/http" | ||
"os" | ||
"regexp" | ||
"time" | ||
|
||
gh "github.com/cli/go-gh" | ||
"github.com/cli/go-gh/pkg/api" | ||
"github.com/cli/go-gh/pkg/tableprinter" | ||
"github.com/cli/go-gh/pkg/term" | ||
graphql "github.com/cli/shurcooL-graphql" | ||
"github.com/shurcooL/githubv4" | ||
) | ||
|
||
// Execute 'gh issue list -R cli/cli', and print the output. | ||
|
@@ -70,29 +72,66 @@ func ExampleRESTClient_request() { | |
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// URL to cli/cli release v2.14.2 checksums.txt | ||
assetURL := "repos/cli/cli/releases/assets/71589494" | ||
resp, err := client.Request("GET", assetURL, nil) | ||
response, err := client.Request(http.MethodGet, assetURL, nil) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
defer response.Body.Close() | ||
f, err := os.CreateTemp("", "*_checksums.txt") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
defer f.Close() | ||
|
||
_, err = io.Copy(f, resp.Body) | ||
_, err = io.Copy(f, response.Body) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
fmt.Printf("Asset downloaded to %s\n", f.Name()) | ||
} | ||
|
||
// Get releases from cli/cli repository using REST API with paginated results. | ||
func ExampleRESTClient_pagination() { | ||
var linkRE = regexp.MustCompile(`<([^>]+)>;\s*rel="([^"]+)"`) | ||
findNextPage := func(response *http.Response) (string, bool) { | ||
for _, m := range linkRE.FindAllStringSubmatch(response.Header.Get("Link"), -1) { | ||
if len(m) > 2 && m[2] == "next" { | ||
return m[1], true | ||
} | ||
} | ||
return "", false | ||
} | ||
client, err := gh.RESTClient(nil) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
requestPath := "repos/cli/cli/releases" | ||
page := 1 | ||
for { | ||
response, err := client.Request(http.MethodGet, requestPath, nil) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
data := []struct{ Name string }{} | ||
decoder := json.NewDecoder(response.Body) | ||
err = decoder.Decode(&data) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
if err := response.Body.Close(); err != nil { | ||
log.Fatal(err) | ||
} | ||
fmt.Printf("Page: %d\n", page) | ||
fmt.Println(data) | ||
var hasNextPage bool | ||
if requestPath, hasNextPage = findNextPage(response); !hasNextPage { | ||
break | ||
} | ||
page++ | ||
} | ||
} | ||
|
||
// Query tags from cli/cli repository using GQL API. | ||
func ExampleGQLClient_simple() { | ||
client, err := gh.GQLClient(nil) | ||
|
@@ -155,12 +194,12 @@ func ExampleGQLClient_advanced() { | |
} | ||
|
||
// Add a star to the cli/go-gh repository using the GQL API. | ||
func ExampleGQLClient_Mutate_simple() { | ||
func ExampleGQLClient_mutate_simple() { | ||
client, err := gh.GQLClient(nil) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
var m struct { | ||
var mutation struct { | ||
AddStar struct { | ||
Starrable struct { | ||
Repository struct { | ||
|
@@ -172,16 +211,60 @@ func ExampleGQLClient_Mutate_simple() { | |
} | ||
} `graphql:"addStar(input: $input)"` | ||
} | ||
// Note that the shurcooL/githubv4 package has defined input structs generated from the | ||
// GraphQL schema that can be used instead of writing your own. | ||
type AddStarInput struct { | ||
StarrableID string `json:"starrableId"` | ||
} | ||
variables := map[string]interface{}{ | ||
"input": githubv4.AddStarInput{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit torn: on one hand I do appreciate that the new example has fewer dependencies, but I also did find value in this example demonstrating the use of the shurcooL library that ensures typed inputs for each GraphQL mutation. I would actually recommend the use of that library to API clients such as extensions. @mntlty: thoughts? #99 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there's two use cases of what the examples here are for.
If it's the first, having another package when a Personally I learned about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My thoughts on this are that as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @samcoe I like that compromise! |
||
StarrableID: githubv4.NewID("R_kgDOF_MgQQ"), | ||
"input": AddStarInput{ | ||
StarrableID: "R_kgDOF_MgQQ", | ||
}, | ||
} | ||
err = client.Mutate("AddStar", &m, variables) | ||
err = client.Mutate("AddStar", &mutation, variables) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
fmt.Println(m.AddStar.Starrable.Repository.StargazerCount) | ||
fmt.Println(mutation.AddStar.Starrable.Repository.StargazerCount) | ||
} | ||
|
||
// Query releases from cli/cli repository using GQL API with paginated results. | ||
func ExampleGQLClient_pagination() { | ||
client, err := gh.GQLClient(nil) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
var query struct { | ||
Repository struct { | ||
Releases struct { | ||
Nodes []struct { | ||
Name string | ||
} | ||
PageInfo struct { | ||
HasNextPage bool | ||
EndCursor string | ||
} | ||
} `graphql:"releases(first: 30, after: $endCursor)"` | ||
} `graphql:"repository(owner: $owner, name: $name)"` | ||
} | ||
variables := map[string]interface{}{ | ||
"owner": graphql.String("cli"), | ||
"name": graphql.String("cli"), | ||
"endCursor": (*graphql.String)(nil), | ||
} | ||
page := 1 | ||
for { | ||
if err := client.Query("RepositoryReleases", &query, variables); err != nil { | ||
log.Fatal(err) | ||
} | ||
fmt.Printf("Page: %d\n", page) | ||
fmt.Println(query.Repository.Releases.Nodes) | ||
if !query.Repository.Releases.PageInfo.HasNextPage { | ||
break | ||
} | ||
variables["endCursor"] = graphql.String(query.Repository.Releases.PageInfo.EndCursor) | ||
page++ | ||
} | ||
} | ||
|
||
// Get repository for the current directory. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thought for the future: having to do this as an extension author feels a bit low-level and requires understanding of certain details in the GitHub API. I wonder if we could provide facilities to encapsulate pagination from go-gh in the future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed #23