mirror of
https://github.com/thegeeklab/github-releases-notifier.git
synced 2024-11-15 04:20:39 +00:00
318 lines
9.8 KiB
Markdown
318 lines
9.8 KiB
Markdown
githubql
|
||
========
|
||
|
||
[![Build Status](https://travis-ci.org/shurcooL/githubql.svg?branch=master)](https://travis-ci.org/shurcooL/githubql) [![GoDoc](https://godoc.org/github.com/shurcooL/githubql?status.svg)](https://godoc.org/github.com/shurcooL/githubql)
|
||
|
||
Package `githubql` is a client library for accessing GitHub GraphQL API v4 (https://developer.github.com/v4/).
|
||
|
||
If you're looking for a client library for GitHub REST API v3, the recommended package is [`github.com/google/go-github/github`](https://godoc.org/github.com/google/go-github/github).
|
||
|
||
**Status:** In active early research and development. The API will change when opportunities for improvement are discovered; it is not yet frozen.
|
||
|
||
Focus
|
||
-----
|
||
|
||
- Friendly, simple and powerful API.
|
||
- Correctness, high performance and efficiency.
|
||
- Support all of GitHub GraphQL API v4 via code generation from schema.
|
||
|
||
### Roadmap
|
||
|
||
Currently implemented:
|
||
|
||
- [x] Basic and intermediate queries.
|
||
- [x] All mutations.
|
||
- [x] Query minification before network transfer.
|
||
- [x] Scalars.
|
||
- [ ] Improved support (https://github.com/shurcooL/githubql/issues/9).
|
||
- [x] Specifying arguments and passing variables.
|
||
- [x] Thorough test coverage.
|
||
- [x] Initial basic tests (hacky but functional).
|
||
- [x] Better organized, medium sized tests.
|
||
- [x] Aliases.
|
||
- [ ] Documentation.
|
||
- [x] Improved support.
|
||
- [x] [Inline fragments](http://graphql.org/learn/queries/#inline-fragments).
|
||
- [x] Generate all of objects, enums, input objects, etc.
|
||
- [x] Clean up GitHub documentation to pass `golint`.
|
||
- [x] Unions.
|
||
- [x] Functional.
|
||
- [x] Improved support (https://github.com/shurcooL/githubql/issues/10).
|
||
- [ ] Directives (haven't tested yet, but expect it to be supported).
|
||
- [ ] Research and complete, document the rest of GraphQL features.
|
||
- [ ] Fully document (and add tests for edge cases) the `graphql` struct field tag.
|
||
- [ ] Extremely clean, beautiful, idiomatic Go code (100% coverage, 0 lines of hacky code).
|
||
- [x] Document all public identifiers.
|
||
- [ ] Clean up implementations of some private helpers (currently functional, but hacky).
|
||
|
||
Future:
|
||
|
||
- [ ] GitHub Enterprise support (when it's available; GitHub themselves haven't released support yet, see [here](https://platform.github.community/t/is-graphql-available-for-github-enterprise/1224)).
|
||
- [ ] Frontend support (e.g., using `githubql` in frontend Go code via [GopherJS](https://github.com/gopherjs/gopherjs)).
|
||
- [ ] Local error detection (maybe).
|
||
- [ ] Calculating a rate limit score before running the call.
|
||
- [ ] Avoiding making network calls when rate limit quota exceeded and not yet reset.
|
||
- [ ] Support for OpenTracing.
|
||
|
||
Known unknowns:
|
||
|
||
- Whether or not the current API design will scale to support all of advanced GraphQL specification features, and future changes. So far, things are looking great, no major blockers found. I am constantly evaluating it against alternative API designs that I've considered and prototyped myself, and new ones that I become aware of.
|
||
- I have only explored roughly 80% of the GraphQL specification (Working Draft – October 2016).
|
||
- Performance, allocations, memory usage under heavy workloads in long-running processes.
|
||
- Optimal long-term package/code layout (i.e., whether to split off some of the parts into smaller sub-packages).
|
||
|
||
Installation
|
||
------------
|
||
|
||
`githubql` requires Go version 1.8 or later.
|
||
|
||
```bash
|
||
go get -u github.com/shurcooL/githubql
|
||
```
|
||
|
||
Usage
|
||
-----
|
||
|
||
### Authentication
|
||
|
||
GitHub GraphQL API v4 [requires authentication](https://developer.github.com/v4/guides/forming-calls/#authenticating-with-graphql). The `githubql` package does not directly handle authentication. Instead, when creating a new client, you're expected to pass an `http.Client` that performs authentication. The easiest and recommended way to do this is to use the [`golang.org/x/oauth2`](https://golang.org/x/oauth2) package. You'll need an OAuth token from GitHub (for example, a [personal API token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/)) with the right scopes. Then:
|
||
|
||
```Go
|
||
import "golang.org/x/oauth2"
|
||
|
||
func main() {
|
||
src := oauth2.StaticTokenSource(
|
||
&oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")},
|
||
)
|
||
httpClient := oauth2.NewClient(context.Background(), src)
|
||
|
||
client := githubql.NewClient(httpClient)
|
||
// Use client...
|
||
```
|
||
|
||
### Simple Query
|
||
|
||
To make a query, you need to define a Go type that corresponds to the GitHub GraphQL schema, and contains the fields you're interested in querying. You can look up the GitHub GraphQL schema at https://developer.github.com/v4/reference/query/.
|
||
|
||
For example, to make the following GraphQL query:
|
||
|
||
```GraphQL
|
||
query {
|
||
viewer {
|
||
login
|
||
createdAt
|
||
}
|
||
}
|
||
```
|
||
|
||
You can define this variable:
|
||
|
||
```Go
|
||
var query struct {
|
||
Viewer struct {
|
||
Login githubql.String
|
||
CreatedAt githubql.DateTime
|
||
}
|
||
}
|
||
```
|
||
|
||
And call `client.Query`, passing a pointer to it:
|
||
|
||
```Go
|
||
err := client.Query(context.Background(), &query, nil)
|
||
if err != nil {
|
||
// Handle error.
|
||
}
|
||
fmt.Println(" Login:", query.Viewer.Login)
|
||
fmt.Println("CreatedAt:", query.Viewer.CreatedAt)
|
||
|
||
// Output:
|
||
// Login: gopher
|
||
// CreatedAt: 2017-05-26 21:17:14 +0000 UTC
|
||
```
|
||
|
||
### Arguments and Variables
|
||
|
||
Often, you'll want to specify arguments on some fields. You can use the `graphql` struct field tag for this.
|
||
|
||
For example, to make the following GraphQL query:
|
||
|
||
```GraphQL
|
||
{
|
||
repository(owner: "octocat", name: "Hello-World") {
|
||
description
|
||
}
|
||
}
|
||
```
|
||
|
||
You can define this variable:
|
||
|
||
```Go
|
||
var q struct {
|
||
Repository struct {
|
||
Description githubql.String
|
||
} `graphql:"repository(owner: \"octocat\", name: \"Hello-World\")"`
|
||
}
|
||
```
|
||
|
||
And call `client.Query`:
|
||
|
||
```Go
|
||
err := client.Query(context.Background(), &q, nil)
|
||
if err != nil {
|
||
// Handle error.
|
||
}
|
||
fmt.Println(q.Repository.Description)
|
||
|
||
// Output:
|
||
// My first repository on GitHub!
|
||
```
|
||
|
||
However, that'll only work if the arguments are constant and known in advance. Otherwise, you will need to make use of variables. Replace the constants in the struct field tag with variable names:
|
||
|
||
```Go
|
||
// fetchRepoDescription fetches description of repo with owner and name.
|
||
func fetchRepoDescription(ctx context.Context, owner, name string) (string, error) {
|
||
var q struct {
|
||
Repository struct {
|
||
Description githubql.String
|
||
} `graphql:"repository(owner: $owner, name: $name)"`
|
||
}
|
||
```
|
||
|
||
Then, define a `variables` map with their values:
|
||
|
||
```Go
|
||
variables := map[string]interface{}{
|
||
"owner": githubql.String(owner),
|
||
"name": githubql.String(name),
|
||
}
|
||
```
|
||
|
||
Finally, call `client.Query` providing `variables`:
|
||
|
||
```Go
|
||
err := client.Query(ctx, &q, variables)
|
||
return string(q.Repository.Description), err
|
||
}
|
||
```
|
||
|
||
### Pagination
|
||
|
||
Imagine you wanted to get a complete list of comments in an issue, and not just the first 10 or so. To do that, you'll need to perform multiple queries and use pagination information. For example:
|
||
|
||
```Go
|
||
type comment struct {
|
||
Body githubql.String
|
||
Author struct {
|
||
Login githubql.String
|
||
AvatarURL githubql.URI `graphql:"avatarUrl(size: 72)"`
|
||
}
|
||
ViewerCanReact githubql.Boolean
|
||
}
|
||
var q struct {
|
||
Repository struct {
|
||
Issue struct {
|
||
Comments struct {
|
||
Nodes []comment
|
||
PageInfo struct {
|
||
EndCursor githubql.String
|
||
HasNextPage githubql.Boolean
|
||
}
|
||
} `graphql:"comments(first: 100, after: $commentsCursor)"` // 100 per page.
|
||
} `graphql:"issue(number: $issueNumber)"`
|
||
} `graphql:"repository(owner: $repositoryOwner, name: $repositoryName)"`
|
||
}
|
||
variables := map[string]interface{}{
|
||
"repositoryOwner": githubql.String(owner),
|
||
"repositoryName": githubql.String(name),
|
||
"issueNumber": githubql.Int(issue),
|
||
"commentsCursor": (*githubql.String)(nil), // Null after argument to get first page.
|
||
}
|
||
|
||
// Get comments from all pages.
|
||
var allComments []comment
|
||
for {
|
||
err := s.clQL.Query(ctx, &q, variables)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
allComments = append(allComments, q.Repository.Issue.Comments.Nodes...)
|
||
if !q.Repository.Issue.Comments.PageInfo.HasNextPage {
|
||
break
|
||
}
|
||
variables["commentsCursor"] = githubql.NewString(q.Repository.Issue.Comments.PageInfo.EndCursor)
|
||
}
|
||
```
|
||
|
||
There is more than one way to perform pagination. Consider additional fields inside [`PageInfo`](https://developer.github.com/v4/reference/object/pageinfo/) object.
|
||
|
||
### Mutations
|
||
|
||
Mutations often require information that you can only find out by performing a query first. Let's suppose you've already done that.
|
||
|
||
For example, to make the following GraphQL mutation:
|
||
|
||
```GraphQL
|
||
mutation($input: AddReactionInput!) {
|
||
addReaction(input: $input) {
|
||
reaction {
|
||
content
|
||
}
|
||
subject {
|
||
id
|
||
}
|
||
}
|
||
}
|
||
variables {
|
||
"input": {
|
||
"subjectId": "MDU6SXNzdWUyMTc5NTQ0OTc=",
|
||
"content": "HOORAY"
|
||
}
|
||
}
|
||
```
|
||
|
||
You can define:
|
||
|
||
```Go
|
||
var m struct {
|
||
AddReaction struct {
|
||
Reaction struct {
|
||
Content githubql.ReactionContent
|
||
}
|
||
Subject struct {
|
||
ID githubql.ID
|
||
}
|
||
} `graphql:"addReaction(input: $input)"`
|
||
}
|
||
input := githubql.AddReactionInput{
|
||
SubjectID: targetIssue.ID, // ID of the target issue from a previous query.
|
||
Content: githubql.Hooray,
|
||
}
|
||
```
|
||
|
||
And call `client.Mutate`:
|
||
|
||
```Go
|
||
err := client.Mutate(context.Background(), &m, input, nil)
|
||
if err != nil {
|
||
// Handle error.
|
||
}
|
||
fmt.Printf("Added a %v reaction to subject with ID %#v!\n", m.AddReaction.Reaction.Content, m.AddReaction.Subject.ID)
|
||
|
||
// Output:
|
||
// Added a HOORAY reaction to subject with ID "MDU6SXNzdWUyMTc5NTQ0OTc="!
|
||
```
|
||
|
||
Directories
|
||
-----------
|
||
|
||
| Path | Synopsis |
|
||
|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
|
||
| [example/githubqldev](https://godoc.org/github.com/shurcooL/githubql/example/githubqldev) | githubqldev is a test program currently being used for developing githubql package. |
|
||
|
||
License
|
||
-------
|
||
|
||
- [MIT License](LICENSE)
|