mirror of
https://github.com/thegeeklab/github-releases-notifier.git
synced 2024-11-15 04:20:39 +00:00
122 lines
3.2 KiB
Go
122 lines
3.2 KiB
Go
|
package githubql
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
|
||
|
"github.com/shurcooL/githubql/internal/jsonutil"
|
||
|
"github.com/shurcooL/go/ctxhttp"
|
||
|
)
|
||
|
|
||
|
// Client is a GitHub GraphQL API v4 client.
|
||
|
type Client struct {
|
||
|
httpClient *http.Client
|
||
|
}
|
||
|
|
||
|
// NewClient creates a new GitHub GraphQL API v4 client with the provided http.Client.
|
||
|
// If it's nil, then http.DefaultClient is used.
|
||
|
//
|
||
|
// Note that GitHub GraphQL API v4 requires authentication, so
|
||
|
// the provided http.Client is expected to take care of that.
|
||
|
func NewClient(httpClient *http.Client) *Client {
|
||
|
if httpClient == nil {
|
||
|
httpClient = http.DefaultClient
|
||
|
}
|
||
|
return &Client{
|
||
|
httpClient: httpClient,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Query executes a single GraphQL query request,
|
||
|
// with a query derived from q, populating the response into it.
|
||
|
// q should be a pointer to struct that corresponds to the GitHub GraphQL schema.
|
||
|
func (c *Client) Query(ctx context.Context, q interface{}, variables map[string]interface{}) error {
|
||
|
return c.do(ctx, queryOperation, q, variables)
|
||
|
}
|
||
|
|
||
|
// Mutate executes a single GraphQL mutation request,
|
||
|
// with a mutation derived from m, populating the response into it.
|
||
|
// m should be a pointer to struct that corresponds to the GitHub GraphQL schema.
|
||
|
// Provided input will be set as a variable named "input".
|
||
|
func (c *Client) Mutate(ctx context.Context, m interface{}, input Input, variables map[string]interface{}) error {
|
||
|
if variables == nil {
|
||
|
variables = map[string]interface{}{"input": input}
|
||
|
} else {
|
||
|
variables["input"] = input
|
||
|
}
|
||
|
return c.do(ctx, mutationOperation, m, variables)
|
||
|
}
|
||
|
|
||
|
// do executes a single GraphQL operation.
|
||
|
func (c *Client) do(ctx context.Context, op operationType, v interface{}, variables map[string]interface{}) error {
|
||
|
var query string
|
||
|
switch op {
|
||
|
case queryOperation:
|
||
|
query = constructQuery(v, variables)
|
||
|
case mutationOperation:
|
||
|
query = constructMutation(v, variables)
|
||
|
}
|
||
|
in := struct {
|
||
|
Query string `json:"query"`
|
||
|
Variables map[string]interface{} `json:"variables,omitempty"`
|
||
|
}{
|
||
|
Query: query,
|
||
|
Variables: variables,
|
||
|
}
|
||
|
var buf bytes.Buffer
|
||
|
err := json.NewEncoder(&buf).Encode(in)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
resp, err := ctxhttp.Post(ctx, c.httpClient, "https://api.github.com/graphql", "application/json", &buf)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
if resp.StatusCode != http.StatusOK {
|
||
|
return fmt.Errorf("unexpected status: %v", resp.Status)
|
||
|
}
|
||
|
var out struct {
|
||
|
Data json.RawMessage
|
||
|
Errors errors
|
||
|
//Extensions interface{} // Unused.
|
||
|
}
|
||
|
err = json.NewDecoder(resp.Body).Decode(&out)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if len(out.Errors) > 0 {
|
||
|
return out.Errors
|
||
|
}
|
||
|
err = jsonutil.UnmarshalGraphQL(out.Data, v)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// errors represents the "errors" array in a response from a GraphQL server.
|
||
|
// If returned via error interface, the slice is expected to contain at least 1 element.
|
||
|
//
|
||
|
// Specification: https://facebook.github.io/graphql/#sec-Errors.
|
||
|
type errors []struct {
|
||
|
Message string
|
||
|
Locations []struct {
|
||
|
Line int
|
||
|
Column int
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Error implements error interface.
|
||
|
func (e errors) Error() string {
|
||
|
return e[0].Message
|
||
|
}
|
||
|
|
||
|
type operationType uint8
|
||
|
|
||
|
const (
|
||
|
queryOperation operationType = iota
|
||
|
mutationOperation
|
||
|
//subscriptionOperation // Unused.
|
||
|
)
|