graphql

package module
v0.0.0-...-7b6ee99 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 12, 2022 License: MIT Imports: 21 Imported by: 0

README

go-graphql-client

Unit tests

Preface: This is a fork of https://github.com/shurcooL/graphql with extended features (subscription client, named operation)

The subscription client follows Apollo client specification https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md, using websocket protocol with https://github.com/nhooyr/websocket, a minimal and idiomatic WebSocket library for Go.

Package graphql provides a GraphQL client implementation.

For more information, see package github.com/shurcooL/githubv4, which is a specialized version targeting GitHub GraphQL API v4. That package is driving the feature development.

Status: In active early research and development. The API will change when opportunities for improvement are discovered; it is not yet frozen.

Note: Before v0.8.0, QueryRaw, MutateRaw and Subscribe methods return *json.RawMessage. This output type is redundant to be decoded. From v0.8.0, the output type is changed to []byte.

Installation

go-graphql-client requires Go version 1.16 or later. For older Go versions, downgrade the library version below v0.7.1.

go get -u github.com/zainirfan13/graphql-client

Usage

Construct a GraphQL client, specifying the GraphQL server URL. Then, you can use it to make GraphQL queries and mutations.

client := graphql.NewClient("https://example.com/graphql", nil)
// Use client...
Authentication

Some GraphQL servers may require authentication. The graphql 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 with the right scopes. Then:

import "golang.org/x/oauth2"

func main() {
	src := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: os.Getenv("GRAPHQL_TOKEN")},
	)
	httpClient := oauth2.NewClient(context.Background(), src)

	client := graphql.NewClient("https://example.com/graphql", httpClient)
	// Use client...
Simple Query

To make a GraphQL query, you need to define a corresponding Go type.

For example, to make the following GraphQL query:

query {
	me {
		name
	}
}

You can define this variable:

var query struct {
	Me struct {
		Name string
	}
}

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

err := client.Query(context.Background(), &query, nil)
if err != nil {
	// Handle error.
}
fmt.Println(query.Me.Name)

// Output: Luke Skywalker
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:

{
	human(id: "1000") {
		name
		height(unit: METER)
	}
}

You can define this variable:

var q struct {
	Human struct {
		Name   string
		Height float64 `graphql:"height(unit: METER)"`
	} `graphql:"human(id: \"1000\")"`
}

Then call client.Query:

err := client.Query(context.Background(), &q, nil)
if err != nil {
	// Handle error.
}
fmt.Println(q.Human.Name)
fmt.Println(q.Human.Height)

// Output:
// Luke Skywalker
// 1.72

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:

var q struct {
	Human struct {
		Name   string
		Height float64 `graphql:"height(unit: $unit)"`
	} `graphql:"human(id: $id)"`
}

Then, define a variables map with their values:

variables := map[string]interface{}{
	"id":   graphql.ID(id),
	"unit": starwars.LengthUnit("METER"),
}

Finally, call client.Query providing variables:

err := client.Query(context.Background(), &q, variables)
if err != nil {
	// Handle error.
}
Custom scalar tag

Because the generator reflects recursively struct objects, it can't know if the struct is a custom scalar such as JSON. To avoid expansion of the field during query generation, let's add the tag scalar:"true" to the custom scalar. If the scalar implements the JSON decoder interface, it will be automatically decoded.

struct {
	Viewer struct {
		ID         interface{}
		Login      string
		CreatedAt  time.Time
		DatabaseID int
	}
}

// Output:
// {
//   viewer {
//	   id
//		 login
//		 createdAt
//		 databaseId
//   }	
// }

struct {
	Viewer struct {
		ID         interface{}
		Login      string
		CreatedAt  time.Time
		DatabaseID int
	} `scalar:"true"`
}

// Output
// { viewer }
Skip GraphQL field
struct {
  Viewer struct {
		ID         interface{} `graphql:"-"`
		Login      string
		CreatedAt  time.Time `graphql:"-"`
		DatabaseID int
  }
}

// Output
// {viewer{login,databaseId}}
Inline Fragments

Some GraphQL queries contain inline fragments. You can use the graphql struct field tag to express them.

For example, to make the following GraphQL query:

{
	hero(episode: "JEDI") {
		name
		... on Droid {
			primaryFunction
		}
		... on Human {
			height
		}
	}
}

You can define this variable:

var q struct {
	Hero struct {
		Name  string
		Droid struct {
			PrimaryFunction string
		} `graphql:"... on Droid"`
		Human struct {
			Height float64
		} `graphql:"... on Human"`
	} `graphql:"hero(episode: \"JEDI\")"`
}

Alternatively, you can define the struct types corresponding to inline fragments, and use them as embedded fields in your query:

type (
	DroidFragment struct {
		PrimaryFunction string
	}
	HumanFragment struct {
		Height float64
	}
)

var q struct {
	Hero struct {
		Name          string
		DroidFragment `graphql:"... on Droid"`
		HumanFragment `graphql:"... on Human"`
	} `graphql:"hero(episode: \"JEDI\")"`
}

Then call client.Query:

err := client.Query(context.Background(), &q, nil)
if err != nil {
	// Handle error.
}
fmt.Println(q.Hero.Name)
fmt.Println(q.Hero.PrimaryFunction)
fmt.Println(q.Hero.Height)

// Output:
// R2-D2
// Astromech
// 0
Specify GraphQL type name

The GraphQL type is automatically inferred from Go type by reflection. However, it's cumbersome in some use cases, e.g lowercase names. In Go, a type name with a first lowercase letter is considered private. If we need to reuse it for other packages, there are 2 approaches: type alias or implement GetGraphQLType method.

type UserReviewInput struct {
	Review string
	UserID string
}

// type alias
type user_review_input UserReviewInput
// or implement GetGraphQLType method
func (u UserReviewInput) GetGraphQLType() string { return "user_review_input" }

variables := map[string]interface{}{
  "input": UserReviewInput{}
}

//query arguments without GetGraphQLType() defined
//($input: UserReviewInput!)
//query arguments with GetGraphQLType() defined:w
//($input: user_review_input!)
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($ep: Episode!, $review: ReviewInput!) {
	createReview(episode: $ep, review: $review) {
		stars
		commentary
	}
}
variables {
	"ep": "JEDI",
	"review": {
		"stars": 5,
		"commentary": "This is a great movie!"
	}
}

You can define:

var m struct {
	CreateReview struct {
		Stars      int
		Commentary string
	} `graphql:"createReview(episode: $ep, review: $review)"`
}
variables := map[string]interface{}{
	"ep": starwars.Episode("JEDI"),
	"review": starwars.ReviewInput{
		Stars:      5,
		Commentary: "This is a great movie!",
	},
}

Then call client.Mutate:

err := client.Mutate(context.Background(), &m, variables)
if err != nil {
	// Handle error.
}
fmt.Printf("Created a %v star review: %v\n", m.CreateReview.Stars, m.CreateReview.Commentary)

// Output:
// Created a 5 star review: This is a great movie!
Mutations Without Fields

Sometimes, you don't need any fields returned from a mutation. Doing that is easy.

For example, to make the following GraphQL mutation:

mutation($ep: Episode!, $review: ReviewInput!) {
	createReview(episode: $ep, review: $review)
}
variables {
	"ep": "JEDI",
	"review": {
		"stars": 5,
		"commentary": "This is a great movie!"
	}
}

You can define:

var m struct {
	CreateReview string `graphql:"createReview(episode: $ep, review: $review)"`
}
variables := map[string]interface{}{
	"ep": starwars.Episode("JEDI"),
	"review": starwars.ReviewInput{
		Stars:      5,
		Commentary: "This is a great movie!",
	},
}

Then call client.Mutate:

err := client.Mutate(context.Background(), &m, variables)
if err != nil {
	// Handle error.
}
fmt.Printf("Created a review: %s.\n", m.CreateReview)

// Output:
// Created a review: .
Subscription
Usage

Construct a Subscription client, specifying the GraphQL server URL.

client := graphql.NewSubscriptionClient("wss://example.com/graphql")
defer client.Close()

// Subscribe subscriptions
// ...
// finally run the client
client.Run()
Subscribe

To make a GraphQL subscription, you need to define a corresponding Go type.

For example, to make the following GraphQL query:

subscription {
	me {
		name
	}
}

You can define this variable:

var subscription struct {
	Me struct {
		Name string
	}
}

Then call client.Subscribe, passing a pointer to it:

subscriptionId, err := client.Subscribe(&query, nil, func(dataValue []byte, errValue error) error {
	if errValue != nil {
		// handle error
		// if returns error, it will failback to `onError` event
		return nil
	}
	data := query{}
	err := json.Unmarshal(dataValue, &data)

	fmt.Println(query.Me.Name)

	// Output: Luke Skywalker
	return nil
})

if err != nil {
	// Handle error.
}
Stop the subscription

You can programmatically stop the subscription while the client is running by using the Unsubscribe method, or returning a special error to stop it in the callback.

subscriptionId, err := client.Subscribe(&query, nil, func(dataValue []byte, errValue error) error {
	// ...
	// return this error to stop the subscription in the callback
	return graphql.ErrSubscriptionStopped
})

if err != nil {
	// Handle error.
}

// unsubscribe the subscription while the client is running with the subscription ID
client.Unsubscribe(subscriptionId)
Authentication

The subscription client is authenticated with GraphQL server through connection params:

client := graphql.NewSubscriptionClient("wss://example.com/graphql").
	WithConnectionParams(map[string]interface{}{
		"headers": map[string]string{
				"authentication": "...",
		},
	})

Options
client.
	//  write timeout of websocket client
	WithTimeout(time.Minute). 
	// When the websocket server was stopped, the client will retry connecting every second until timeout
	WithRetryTimeout(time.Minute).
	// sets loging function to print out received messages. By default, nothing is printed
	WithLog(log.Println).
	// max size of response message
	WithReadLimit(10*1024*1024).
	// these operation event logs won't be printed
	WithoutLogTypes(graphql.GQL_DATA, graphql.GQL_CONNECTION_KEEP_ALIVE)

Events
// OnConnected event is triggered when the websocket connected to GraphQL server sucessfully
client.OnConnected(fn func())

// OnDisconnected event is triggered when the websocket client was disconnected
client.OnDisconnected(fn func())

// OnConnected event is triggered when there is any connection error. This is bottom exception handler level
// If this function is empty, or returns nil, the error is ignored
// If returns error, the websocket connection will be terminated
client.OnError(onError func(sc *SubscriptionClient, err error) error)
Custom HTTP Client

Use WithWebSocketOptions to customize the HTTP client which is used by the subscription client.

client.WithWebSocketOptions(WebsocketOptions{
	HTTPClient: &http.Client{
		Transport: http.DefaultTransport,
		Timeout: time.Minute,
	}
})
Custom WebSocket client

By default the subscription client uses nhooyr WebSocket client. If you need to customize the client, or prefer using Gorilla WebSocket, let's follow the Websocket interface and replace the constructor with WithWebSocket method:

// WebsocketHandler abstracts WebSocket connection functions
// ReadJSON and WriteJSON data of a frame from the WebSocket connection.
// Close the WebSocket connection.
type WebsocketConn interface {
	ReadJSON(v interface{}) error
	WriteJSON(v interface{}) error
	Close() error
	// SetReadLimit sets the maximum size in bytes for a message read from the peer. If a
	// message exceeds the limit, the connection sends a close message to the peer
	// and returns ErrReadLimit to the application.
	SetReadLimit(limit int64)
}

// WithWebSocket replaces customized websocket client constructor
func (sc *SubscriptionClient) WithWebSocket(fn func(sc *SubscriptionClient) (WebsocketConn, error)) *SubscriptionClient

Example


// the default websocket constructor
func newWebsocketConn(sc *SubscriptionClient) (WebsocketConn, error) {
	options := &websocket.DialOptions{
		Subprotocols: []string{"graphql-ws"},
	}
	c, _, err := websocket.Dial(sc.GetContext(), sc.GetURL(), options)
	if err != nil {
		return nil, err
	}

	// The default WebsocketHandler implementation using nhooyr's
	return &WebsocketHandler{
		ctx:     sc.GetContext(),
		Conn:    c,
		timeout: sc.GetTimeout(),
	}, nil
}

client := graphql.NewSubscriptionClient("wss://example.com/graphql")
defer client.Close()

client.WithWebSocket(newWebsocketConn)

client.Run()
Options

There are extensible parts in the GraphQL query that we sometimes use. They are optional so that we shouldn't required them in the method. To make it flexible, we can abstract these options as optional arguments that follow this interface.

type Option interface {
	Type() OptionType
	String() string
}

client.Query(ctx context.Context, q interface{}, variables map[string]interface{}, options ...Option) error

Currently we support 2 option types: operation_name and operation_directive. The operation name option is built-in because it is unique. We can use the option directly with OperationName

// query MyQuery {
//	...
// }
client.Query(ctx, &q, variables, graphql.OperationName("MyQuery"))

In contrast, operation directive is various and customizable on different GraphQL servers. There isn't any built-in directive in the library. You need to define yourself. For example:

// define @cached directive for Hasura queries
// https://hasura.io/docs/latest/graphql/cloud/response-caching.html#enable-caching
type cachedDirective struct {
	ttl int
}

func (cd cachedDirective) Type() OptionType {
	// operation_directive
	return graphql.OptionTypeOperationDirective
}

func (cd cachedDirective) String() string {
	if cd.ttl <= 0 {
		return "@cached"
	}
	return fmt.Sprintf("@cached(ttl: %d)", cd.ttl)
}

// query MyQuery @cached {
//	...
// }
client.Query(ctx, &q, variables, graphql.OperationName("MyQuery"), cachedDirective{})
Execute pre-built query

The Exec function allows you to executing pre-built queries. While using reflection to build queries is convenient as you get some resemblance of type safety, it gets very cumbersome when you need to create queries semi-dynamically. For instance, imagine you are building a CLI tool to query data from a graphql endpoint and you want users to be able to narrow down the query by passing cli flags or something.

// filters would be built dynamically somehow from the command line flags
filters := []string{
   `fieldA: {subfieldA: {_eq: "a"}}`,
   `fieldB: {_eq: "b"}`,
   ...
}

query := "query{something(where: {" + strings.Join(filters, ", ") + "}){id}}"
res := struct {
	Somethings []Something
}{}

if err := client.Exec(ctx, query, &res, map[string]any{}); err != nil {
	panic(err)
}

subscription := "subscription{something(where: {" + strings.Join(filters, ", ") + "}){id}}"
subscriptionId, err := subscriptionClient.Exec(subscription, nil, func(dataValue []byte, errValue error) error {
	if errValue != nil {
		// handle error
		// if returns error, it will failback to `onError` event
		return nil
	}
	data := query{}
	err := json.Unmarshal(dataValue, &data)
	// ...
})

If you prefer decoding JSON yourself, use ExecRaw instead.

query := `query{something(where: { foo: { _eq: "bar" }}){id}}`
var res struct {
	Somethings []Something `json:"something"`
}

raw, err := client.ExecRaw(ctx, query, map[string]any{}) 
if err != nil {
	panic(err)
}

err = json.Unmarshal(raw, &res)
With operation name (deprecated)

Operation name is still on API decision plan https://github.com/shurcooL/graphql/issues/12. However, in my opinion separate methods are easier choice to avoid breaking changes

func (c *Client) NamedQuery(ctx context.Context, name string, q interface{}, variables map[string]interface{}) error

func (c *Client) NamedMutate(ctx context.Context, name string, q interface{}, variables map[string]interface{}) error

func (sc *SubscriptionClient) NamedSubscribe(name string, v interface{}, variables map[string]interface{}, handler func(message []byte, err error) error) (string, error)
Raw bytes response

In the case we developers want to decode JSON response ourself. Moreover, the default UnmarshalGraphQL function isn't ideal with complicated nested interfaces

func (c *Client) QueryRaw(ctx context.Context, q interface{}, variables map[string]interface{}) ([]byte, error)

func (c *Client) MutateRaw(ctx context.Context, q interface{}, variables map[string]interface{}) ([]byte, error)

func (c *Client) NamedQueryRaw(ctx context.Context, name string, q interface{}, variables map[string]interface{}) ([]byte, error)

func (c *Client) NamedMutateRaw(ctx context.Context, name string, q interface{}, variables map[string]interface{}) ([]byte, error)
Multiple mutations with ordered map

You might need to make multiple mutations in single query. It's not very convenient with structs so you can use ordered map [][2]interface{} instead.

For example, to make the following GraphQL mutation:

mutation($login1: String!, $login2: String!, $login3: String!) {
	createUser(login: $login1) { login }
	createUser(login: $login2) { login }
	createUser(login: $login3) { login }
}
variables {
	"login1": "grihabor",
	"login2": "diman",
	"login3": "indigo"
}

You can define:

type CreateUser struct {
	Login string
}
m := [][2]interface{}{
	{"createUser(login: $login1)", &CreateUser{}},
	{"createUser(login: $login2)", &CreateUser{}},
	{"createUser(login: $login3)", &CreateUser{}},
}
variables := map[string]interface{}{
	"login1": "grihabor",
	"login2": "diman",
	"login3": "indigo",
}
Debugging and Unit test

Enable debug mode with the WithDebug function. If the request is failed, the request and response information will be included in extensions[].internal property.

{
	"errors": [
		{
			"message":"Field 'user' is missing required arguments: login",
			"extensions": {
				"internal": {
					"request": {
						"body":"{\"query\":\"{user{name}}\"}",
						"headers": {
							"Content-Type": ["application/json"]
						}
					},
					"response": {
						"body":"{\"errors\": [{\"message\": \"Field 'user' is missing required arguments: login\",\"locations\": [{\"line\": 7,\"column\": 3}]}]}",
						"headers": {
							"Content-Type": ["application/json"]
						}
					}
				}
			},
			"locations": [
				{
					"line":7,
					"column":3
				}
			]
		}
	]
}

Because the GraphQL query string is generated in runtime using reflection, it isn't really safe. To assure the GraphQL query is expected, it's necessary to write some unit test for query construction.

// ConstructQuery build GraphQL query string from struct and variables
func ConstructQuery(v interface{}, variables map[string]interface{}, options ...Option) (string, error)

// ConstructQuery build GraphQL mutation string from struct and variables
func ConstructMutation(v interface{}, variables map[string]interface{}, options ...Option) (string, error)

// ConstructSubscription build GraphQL subscription string from struct and variables
func ConstructSubscription(v interface{}, variables map[string]interface{}, options ...Option) (string, error) 

// UnmarshalGraphQL parses the JSON-encoded GraphQL response data and stores
// the result in the GraphQL query data structure pointed to by v.
func UnmarshalGraphQL(data []byte, v interface{}) error 

Directories

Path Synopsis
example/graphqldev graphqldev is a test program currently being used for developing graphql package.
ident Package ident provides functions for parsing and converting identifier names between various naming convention.
internal/jsonutil Package jsonutil provides a function for decoding JSON into a GraphQL query data structure.

References

License

Documentation

Overview

Package graphql provides a GraphQL client implementation.

For more information, see package github.com/zainirfan13/graphql-client

Status: In active early research and development. The API will change when opportunities for improvement are discovered; it is not yet frozen.

For now, see README for more details.

Index

Constants

View Source
const (
	ErrRequestError  = "request_error"
	ErrJsonEncode    = "json_encode_error"
	ErrJsonDecode    = "json_decode_error"
	ErrGraphQLEncode = "graphql_encode_error"
	ErrGraphQLDecode = "graphql_decode_error"
)

Variables

View Source
var ErrSubscriptionStopped = errors.New("subscription stopped")

ErrSubscriptionStopped a special error which forces the subscription stop

Functions

func ConstructMutation

func ConstructMutation(v interface{}, variables map[string]interface{}, options ...Option) (string, error)

ConstructQuery build GraphQL mutation string from struct and variables

func ConstructQuery

func ConstructQuery(v interface{}, variables map[string]interface{}, options ...Option) (string, error)

ConstructQuery build GraphQL query string from struct and variables

func ConstructSubscription

func ConstructSubscription(v interface{}, variables map[string]interface{}, options ...Option) (string, error)

ConstructSubscription build GraphQL subscription string from struct and variables

func ElemSafe

func ElemSafe(v reflect.Value) reflect.Value

func FieldSafe

func FieldSafe(valStruct reflect.Value, i int) reflect.Value

func IndexSafe

func IndexSafe(v reflect.Value, i int) reflect.Value

func UnmarshalGraphQL

func UnmarshalGraphQL(data []byte, v interface{}) error

UnmarshalGraphQL parses the JSON-encoded GraphQL response data and stores the result in the GraphQL query data structure pointed to by v.

The implementation is created on top of the JSON tokenizer available in "encoding/json".Decoder. This function is re-exported from the internal package

Types

type Boolean

type Boolean bool

Boolean represents true or false values. Deprecated.

func NewBoolean

func NewBoolean(v Boolean) *Boolean

NewBoolean is a helper to make a new *Boolean. Deprecated.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a GraphQL client.

func NewClient

func NewClient(url string, httpClient *http.Client) *Client

NewClient creates a GraphQL client targeting the specified GraphQL server URL. If httpClient is nil, then http.DefaultClient is used.

func (*Client) Exec

func (c *Client) Exec(ctx context.Context, query string, v interface{}, variables map[string]interface{}, options ...Option) error

Executes a pre-built query and unmarshals the response into v. Unlike the Query method you have to specify in the query the fields that you want to receive as they are not inferred from v. This method is useful if you need to build the query dynamically.

func (*Client) ExecRaw

func (c *Client) ExecRaw(ctx context.Context, query string, variables map[string]interface{}, options ...Option) ([]byte, error)

Executes a pre-built query and returns the raw json message. Unlike the Query method you have to specify in the query the fields that you want to receive as they are not inferred from the interface. This method is useful if you need to build the query dynamically.

func (*Client) Mutate

func (c *Client) Mutate(ctx context.Context, m interface{}, variables map[string]interface{}, options ...Option) error

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 GraphQL schema.

func (*Client) MutateRaw

func (c *Client) MutateRaw(ctx context.Context, m interface{}, variables map[string]interface{}, options ...Option) ([]byte, error)

MutateRaw 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 GraphQL schema. return raw bytes message.

func (*Client) NamedMutate deprecated

func (c *Client) NamedMutate(ctx context.Context, name string, m interface{}, variables map[string]interface{}, options ...Option) error

NamedMutate executes a single GraphQL mutation request, with operation name

Deprecated: this is the shortcut of Mutate method, with NewOperationName option

func (*Client) NamedMutateRaw

func (c *Client) NamedMutateRaw(ctx context.Context, name string, m interface{}, variables map[string]interface{}, options ...Option) ([]byte, error)

NamedMutateRaw executes a single GraphQL mutation request, with operation name return raw bytes message.

func (*Client) NamedQuery deprecated

func (c *Client) NamedQuery(ctx context.Context, name string, q interface{}, variables map[string]interface{}, options ...Option) error

NamedQuery executes a single GraphQL query request, with operation name

Deprecated: this is the shortcut of Query method, with NewOperationName option

func (*Client) NamedQueryRaw

func (c *Client) NamedQueryRaw(ctx context.Context, name string, q interface{}, variables map[string]interface{}, options ...Option) ([]byte, error)

NamedQueryRaw executes a single GraphQL query request, with operation name return raw bytes message.

func (*Client) Query

func (c *Client) Query(ctx context.Context, q interface{}, variables map[string]interface{}, options ...Option) error

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 GraphQL schema.

func (*Client) QueryRaw

func (c *Client) QueryRaw(ctx context.Context, q interface{}, variables map[string]interface{}, options ...Option) ([]byte, error)

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 GraphQL schema. return raw bytes message.

func (*Client) WithDebug

func (c *Client) WithDebug(debug bool) *Client

WithDebug enable debug mode to print internal error detail

func (*Client) WithRequestModifier

func (c *Client) WithRequestModifier(f RequestModifier) *Client

Returns a copy of the client with the request modifier set. This allows you to reuse the same TCP connection for multiple slightly different requests to the same server (i.e. different authentication headers for multitenant applications)

type Error

type Error struct {
	Message    string                 `json:"message"`
	Extensions map[string]interface{} `json:"extensions"`
	Locations  []struct {
		Line   int `json:"line"`
		Column int `json:"column"`
	} `json:"locations"`
}

func (Error) Error

func (e Error) Error() string

Error implements error interface.

type Errors

type Errors []Error

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.

func (Errors) Error

func (e Errors) Error() string

Error implements error interface.

type Float

type Float float64

Float represents signed double-precision fractional values as specified by IEEE 754. Deprecated.

func NewFloat

func NewFloat(v Float) *Float

NewFloat is a helper to make a new *Float. Deprecated.

type GraphQLType

type GraphQLType interface {
	GetGraphQLType() string
}

GraphQLType interface is used to specify the GraphQL type associated with a particular type. If a type implements this interface, the name of the variable used while creating the GraphQL query will be the output of the function defined below.

In the current implementation, the GetGraphQLType function is applied to the zero value of the type to get the GraphQL type. So those who are implementing the function should avoid referencing the value of the type inside the function. Further, by this design, the output of the GetGraphQLType function will be a constant.

type ID

type ID string

ID represents a unique identifier that is Base64 obfuscated. It is often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as "VXNlci0xMA==") or integer (such as 4) input value will be accepted as an ID.

func NewID

func NewID(v interface{}) *ID

NewID is a helper to make a new *ID.

func ToID

func ToID(v interface{}) ID

ToID is a helper for if you need to get the string version of an integer or a string for the id.

type Int

type Int int32

Int represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. Deprecated.

func NewInt

func NewInt(v Int) *Int

NewInt is a helper to make a new *Int. Deprecated.

type OperationMessage

type OperationMessage struct {
	ID      string               `json:"id,omitempty"`
	Type    OperationMessageType `json:"type"`
	Payload json.RawMessage      `json:"payload,omitempty"`
}

OperationMessage represents a subscription operation message

func (OperationMessage) String

func (om OperationMessage) String() string

type OperationMessageType

type OperationMessageType string

OperationMessageType

const (
	// Client sends this message after plain websocket connection to start the communication with the server
	GQL_CONNECTION_INIT OperationMessageType = "connection_init"
	// The server may responses with this message to the GQL_CONNECTION_INIT from client, indicates the server rejected the connection.
	GQL_CONNECTION_ERROR OperationMessageType = "conn_err"
	// Client sends this message to execute GraphQL operation
	GQL_START OperationMessageType = "start"
	// Client sends this message in order to stop a running GraphQL operation execution (for example: unsubscribe)
	GQL_STOP OperationMessageType = "stop"
	// Server sends this message upon a failing operation, before the GraphQL execution, usually due to GraphQL validation errors (resolver errors are part of GQL_DATA message, and will be added as errors array)
	GQL_ERROR OperationMessageType = "error"
	// The server sends this message to transfter the GraphQL execution result from the server to the client, this message is a response for GQL_START message.
	GQL_DATA OperationMessageType = "data"
	// Server sends this message to indicate that a GraphQL operation is done, and no more data will arrive for the specific operation.
	GQL_COMPLETE OperationMessageType = "complete"
	// Server message that should be sent right after each GQL_CONNECTION_ACK processed and then periodically to keep the client connection alive.
	// The client starts to consider the keep alive message only upon the first received keep alive message from the server.
	GQL_CONNECTION_KEEP_ALIVE OperationMessageType = "ka"
	// The server may responses with this message to the GQL_CONNECTION_INIT from client, indicates the server accepted the connection. May optionally include a payload.
	GQL_CONNECTION_ACK OperationMessageType = "connection_ack"
	// Client sends this message to terminate the connection.
	GQL_CONNECTION_TERMINATE OperationMessageType = "connection_terminate"
	// Unknown operation type, for logging only
	GQL_UNKNOWN OperationMessageType = "unknown"
	// Internal status, for logging only
	GQL_INTERNAL OperationMessageType = "internal"
)

type Option

type Option interface {
	// Type returns the supported type of the renderer
	// available types: operation_name and operation_directive
	Type() OptionType
	// String returns the query component string
	String() string
}

Option abstracts an extra render interface for the query string They are optional parts. By default GraphQL queries can request data without them

func OperationName

func OperationName(name string) Option

OperationName creates the operation name option

type OptionType

type OptionType string

OptionType represents the logic of graphql query construction

const (
	OptionTypeOperationDirective OptionType = "operation_directive"
)

type RequestModifier

type RequestModifier func(*http.Request)

This function allows you to tweak the HTTP request. It might be useful to set authentication headers amongst other things

type String

type String string

String represents textual data as UTF-8 character sequences. This type is most often used by GraphQL to represent free-form human-readable text. Deprecated.

func NewString

func NewString(v String) *String

NewString is a helper to make a new *String. Deprecated.

type SubscriptionClient

type SubscriptionClient struct {
	// contains filtered or unexported fields
}

SubscriptionClient is a GraphQL subscription client.

func NewSubscriptionClient

func NewSubscriptionClient(url string) *SubscriptionClient

func (*SubscriptionClient) Close

func (sc *SubscriptionClient) Close() (err error)

Close closes all subscription channel and websocket as well

func (*SubscriptionClient) Exec

func (sc *SubscriptionClient) Exec(query string, variables map[string]interface{}, handler func(message []byte, err error) error) (string, error)

Exec sends start message to server and open a channel to receive data, with raw query

func (*SubscriptionClient) GetContext

func (sc *SubscriptionClient) GetContext() context.Context

GetContext returns current context of subscription client

func (*SubscriptionClient) GetTimeout

func (sc *SubscriptionClient) GetTimeout() time.Duration

GetContext returns write timeout of websocket client

func (*SubscriptionClient) GetURL

func (sc *SubscriptionClient) GetURL() string

GetURL returns GraphQL server's URL

func (*SubscriptionClient) NamedSubscribe deprecated

func (sc *SubscriptionClient) NamedSubscribe(name string, v interface{}, variables map[string]interface{}, handler func(message []byte, err error) error, options ...Option) (string, error)

NamedSubscribe sends start message to server and open a channel to receive data, with operation name

Deprecated: this is the shortcut of Subscribe method, with NewOperationName option

func (*SubscriptionClient) OnConnected

func (sc *SubscriptionClient) OnConnected(fn func()) *SubscriptionClient

OnConnected event is triggered when the websocket connected to GraphQL server successfully

func (*SubscriptionClient) OnDisconnected

func (sc *SubscriptionClient) OnDisconnected(fn func()) *SubscriptionClient

OnDisconnected event is triggered when the websocket client was disconnected

func (*SubscriptionClient) OnError

func (sc *SubscriptionClient) OnError(onError func(sc *SubscriptionClient, err error) error) *SubscriptionClient

OnConnected event is triggered when there is any connection error. This is bottom exception handler level If this function is empty, or returns nil, the error is ignored If returns error, the websocket connection will be terminated

func (*SubscriptionClient) Reset

func (sc *SubscriptionClient) Reset() error

Reset restart websocket connection and subscriptions

func (*SubscriptionClient) Run

func (sc *SubscriptionClient) Run() error

Run start websocket client and subscriptions. If this function is run with goroutine, it can be stopped after closed

func (*SubscriptionClient) Subscribe

func (sc *SubscriptionClient) Subscribe(v interface{}, variables map[string]interface{}, handler func(message []byte, err error) error, options ...Option) (string, error)

Subscribe sends start message to server and open a channel to receive data. The handler callback function will receive raw message data or error. If the call return error, onError event will be triggered The function returns subscription ID and error. You can use subscription ID to unsubscribe the subscription

func (*SubscriptionClient) SubscribeRaw

func (sc *SubscriptionClient) SubscribeRaw(query string, variables map[string]interface{}, handler func(message []byte, err error) error) (string, error)

SubscribeRaw sends start message to server and open a channel to receive data, with raw query Deprecated: use Exec instead

func (*SubscriptionClient) Unsubscribe

func (sc *SubscriptionClient) Unsubscribe(id string) error

Unsubscribe sends stop message to server and close subscription channel The input parameter is subscription ID that is returned from Subscribe function

func (*SubscriptionClient) WithConnectionParams

func (sc *SubscriptionClient) WithConnectionParams(params map[string]interface{}) *SubscriptionClient

WithConnectionParams updates connection params for sending to server through GQL_CONNECTION_INIT event It's usually used for authentication handshake

func (*SubscriptionClient) WithLog

func (sc *SubscriptionClient) WithLog(logger func(args ...interface{})) *SubscriptionClient

WithLog sets loging function to print out received messages. By default, nothing is printed

func (*SubscriptionClient) WithReadLimit

func (sc *SubscriptionClient) WithReadLimit(limit int64) *SubscriptionClient

WithReadLimit set max size of response message

func (*SubscriptionClient) WithRetryTimeout

func (sc *SubscriptionClient) WithRetryTimeout(timeout time.Duration) *SubscriptionClient

WithRetryTimeout updates reconnecting timeout. When the websocket server was stopped, the client will retry connecting every second until timeout

func (*SubscriptionClient) WithTimeout

func (sc *SubscriptionClient) WithTimeout(timeout time.Duration) *SubscriptionClient

WithTimeout updates write timeout of websocket client

func (*SubscriptionClient) WithWebSocket

func (sc *SubscriptionClient) WithWebSocket(fn func(sc *SubscriptionClient) (WebsocketConn, error)) *SubscriptionClient

WithWebSocket replaces customized websocket client constructor In default, subscription client uses https://github.com/nhooyr/websocket

func (*SubscriptionClient) WithWebSocketOptions

func (sc *SubscriptionClient) WithWebSocketOptions(options WebsocketOptions) *SubscriptionClient

WithWebSocketOptions provides options to the websocket client

func (*SubscriptionClient) WithoutLogTypes

func (sc *SubscriptionClient) WithoutLogTypes(types ...OperationMessageType) *SubscriptionClient

WithoutLogTypes these operation types won't be printed

type WebsocketConn

type WebsocketConn interface {
	ReadJSON(v interface{}) error
	WriteJSON(v interface{}) error
	Close() error
	// SetReadLimit sets the maximum size in bytes for a message read from the peer. If a
	// message exceeds the limit, the connection sends a close message to the peer
	// and returns ErrReadLimit to the application.
	SetReadLimit(limit int64)
}

WebsocketHandler abstracts WebSocket connection functions ReadJSON and WriteJSON data of a frame from the WebSocket connection. Close the WebSocket connection.

type WebsocketHandler

type WebsocketHandler struct {
	*websocket.Conn
	// contains filtered or unexported fields
}

default websocket handler implementation using https://github.com/nhooyr/websocket

func (*WebsocketHandler) Close

func (wh *WebsocketHandler) Close() error

func (*WebsocketHandler) ReadJSON

func (wh *WebsocketHandler) ReadJSON(v interface{}) error

func (*WebsocketHandler) WriteJSON

func (wh *WebsocketHandler) WriteJSON(v interface{}) error

type WebsocketOptions

type WebsocketOptions struct {
	// HTTPClient is used for the connection.
	HTTPClient *http.Client
}

WebsocketOptions allows implementation agnostic configuration of the websocket client

Directories

Path Synopsis
example
graphqldev
graphqldev is a test program currently being used for developing graphql package.
graphqldev is a test program currently being used for developing graphql package.
subscription
subscription is a test program currently being used for developing graphql package.
subscription is a test program currently being used for developing graphql package.
Package ident provides functions for parsing and converting identifier names between various naming convention.
Package ident provides functions for parsing and converting identifier names between various naming convention.
internal
jsonutil
Package jsonutil provides a function for decoding JSON into a GraphQL query data structure.
Package jsonutil provides a function for decoding JSON into a GraphQL query data structure.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL