...

Source file src/github.com/shurcooL/graphql/graphql.go

Documentation: github.com/shurcooL/graphql

     1  package graphql
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net/http"
    10  
    11  	"github.com/shurcooL/graphql/internal/jsonutil"
    12  	"golang.org/x/net/context/ctxhttp"
    13  )
    14  
    15  // Client is a GraphQL client.
    16  type Client struct {
    17  	url        string // GraphQL server URL.
    18  	httpClient *http.Client
    19  }
    20  
    21  // NewClient creates a GraphQL client targeting the specified GraphQL server URL.
    22  // If httpClient is nil, then http.DefaultClient is used.
    23  func NewClient(url string, httpClient *http.Client) *Client {
    24  	if httpClient == nil {
    25  		httpClient = http.DefaultClient
    26  	}
    27  	return &Client{
    28  		url:        url,
    29  		httpClient: httpClient,
    30  	}
    31  }
    32  
    33  // Query executes a single GraphQL query request,
    34  // with a query derived from q, populating the response into it.
    35  // q should be a pointer to struct that corresponds to the GraphQL schema.
    36  func (c *Client) Query(ctx context.Context, q interface{}, variables map[string]interface{}) error {
    37  	return c.do(ctx, queryOperation, q, variables)
    38  }
    39  
    40  // Mutate executes a single GraphQL mutation request,
    41  // with a mutation derived from m, populating the response into it.
    42  // m should be a pointer to struct that corresponds to the GraphQL schema.
    43  func (c *Client) Mutate(ctx context.Context, m interface{}, variables map[string]interface{}) error {
    44  	return c.do(ctx, mutationOperation, m, variables)
    45  }
    46  
    47  // do executes a single GraphQL operation.
    48  func (c *Client) do(ctx context.Context, op operationType, v interface{}, variables map[string]interface{}) error {
    49  	var query string
    50  	switch op {
    51  	case queryOperation:
    52  		query = constructQuery(v, variables)
    53  	case mutationOperation:
    54  		query = constructMutation(v, variables)
    55  	}
    56  	in := struct {
    57  		Query     string                 `json:"query"`
    58  		Variables map[string]interface{} `json:"variables,omitempty"`
    59  	}{
    60  		Query:     query,
    61  		Variables: variables,
    62  	}
    63  	var buf bytes.Buffer
    64  	err := json.NewEncoder(&buf).Encode(in)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	resp, err := ctxhttp.Post(ctx, c.httpClient, c.url, "application/json", &buf)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	defer resp.Body.Close()
    73  	if resp.StatusCode != http.StatusOK {
    74  		body, _ := ioutil.ReadAll(resp.Body)
    75  		return fmt.Errorf("non-200 OK status code: %v body: %q", resp.Status, body)
    76  	}
    77  	var out struct {
    78  		Data   *json.RawMessage
    79  		Errors errors
    80  		//Extensions interface{} // Unused.
    81  	}
    82  	err = json.NewDecoder(resp.Body).Decode(&out)
    83  	if err != nil {
    84  		// TODO: Consider including response body in returned error, if deemed helpful.
    85  		return err
    86  	}
    87  	if out.Data != nil {
    88  		err := jsonutil.UnmarshalGraphQL(*out.Data, v)
    89  		if err != nil {
    90  			// TODO: Consider including response body in returned error, if deemed helpful.
    91  			return err
    92  		}
    93  	}
    94  	if len(out.Errors) > 0 {
    95  		return out.Errors
    96  	}
    97  	return nil
    98  }
    99  
   100  // errors represents the "errors" array in a response from a GraphQL server.
   101  // If returned via error interface, the slice is expected to contain at least 1 element.
   102  //
   103  // Specification: https://facebook.github.io/graphql/#sec-Errors.
   104  type errors []struct {
   105  	Message   string
   106  	Locations []struct {
   107  		Line   int
   108  		Column int
   109  	}
   110  }
   111  
   112  // Error implements error interface.
   113  func (e errors) Error() string {
   114  	return e[0].Message
   115  }
   116  
   117  type operationType uint8
   118  
   119  const (
   120  	queryOperation operationType = iota
   121  	mutationOperation
   122  	//subscriptionOperation // Unused.
   123  )
   124  

View as plain text