0
0
mirror of https://github.com/thegeeklab/github-releases-notifier.git synced 2024-09-21 16:42:46 +02:00
github-releases-notifier/vendor/github.com/shurcooL/githubql
2017-08-08 12:40:09 +02:00
..
internal Vendor all deps 2017-08-08 12:40:09 +02:00
doc.go Vendor all deps 2017-08-08 12:40:09 +02:00
enum.go Vendor all deps 2017-08-08 12:40:09 +02:00
githubql.go Vendor all deps 2017-08-08 12:40:09 +02:00
hacky.go Vendor all deps 2017-08-08 12:40:09 +02:00
input.go Vendor all deps 2017-08-08 12:40:09 +02:00
LICENSE Vendor all deps 2017-08-08 12:40:09 +02:00
README.md Vendor all deps 2017-08-08 12:40:09 +02:00
scalar.go Vendor all deps 2017-08-08 12:40:09 +02:00

githubql

Build Status GoDoc

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.

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:

  • Basic and intermediate queries.
  • All mutations.
  • Query minification before network transfer.
  • Scalars.
  • Specifying arguments and passing variables.
  • Thorough test coverage.
    • Initial basic tests (hacky but functional).
    • Better organized, medium sized tests.
  • Aliases.
    • Documentation.
    • Improved support.
  • Inline fragments.
  • Generate all of objects, enums, input objects, etc.
    • Clean up GitHub documentation to pass golint.
  • Unions.
  • 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).
    • 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).
  • Frontend support (e.g., using githubql in frontend Go code via 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.

go get -u github.com/shurcooL/githubql

Usage

Authentication

GitHub GraphQL API v4 requires authentication. 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 package. You'll need an OAuth token from GitHub (for example, a personal API token) with the right scopes. Then:

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:

query {
	viewer {
		login
		createdAt
	}
}

You can define this variable:

var query struct {
	Viewer struct {
		Login     githubql.String
		CreatedAt githubql.DateTime
	}
}

And call client.Query, passing a pointer to it:

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:

{
	repository(owner: "octocat", name: "Hello-World") {
		description
	}
}

You can define this variable:

var q struct {
	Repository struct {
		Description githubql.String
	} `graphql:"repository(owner: \"octocat\", name: \"Hello-World\")"`
}

And call client.Query:

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:

// 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:

	variables := map[string]interface{}{
		"owner": githubql.String(owner),
		"name":  githubql.String(name),
	}

Finally, call client.Query providing variables:

	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:

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 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:

mutation($input: AddReactionInput!) {
	addReaction(input: $input) {
		reaction {
			content
		}
		subject {
			id
		}
	}
}
variables {
	"input": {
		"subjectId": "MDU6SXNzdWUyMTc5NTQ0OTc=",
		"content": "HOORAY"
	}
}

You can define:

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:

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 githubqldev is a test program currently being used for developing githubql package.

License