Add support for client that uses GET as transport mechanism (#186)
Current implementation always uses POST as the transport mechanism. Adding GET support enables usage of GET queries for caching simply via URL. Some notes: - I left the existing API for creating a new client as is, but the implementation could be much cleaner by introducing some sort of configuration struct when creating a new client - The construction of the query parameters follows the logic from Apollo's client implementation, which can be found here https://github.com/apollographql/apollo-client/blob/8beb4820edc6352996e08f7f73bde3573f1eb666/src/link/http/rewriteURIForGET.ts - Updated integration tests to use both sets of clients. Updating the tests to use a test suite would be cleaner
This commit is contained in:
@@ -29,6 +29,7 @@ When releasing a new version:
|
|||||||
|
|
||||||
- genqlient can now run as a portable binary (i.e. without a local checkout of the repository or `go run`).
|
- genqlient can now run as a portable binary (i.e. without a local checkout of the repository or `go run`).
|
||||||
- You can now enable `use_extensions` in the configuration file, to receive extensions returned by the GraphQL API server. Generated functions will return extensions as `map[string]interface{}`, if enabled.
|
- You can now enable `use_extensions` in the configuration file, to receive extensions returned by the GraphQL API server. Generated functions will return extensions as `map[string]interface{}`, if enabled.
|
||||||
|
- You can now use `graphql.NewClientUsingGet` to create a client that uses query parameters to pass the query to the GraphQL API server.
|
||||||
|
|
||||||
### Bug fixes:
|
### Bug fixes:
|
||||||
|
|
||||||
|
|||||||
+28
@@ -8,6 +8,34 @@ This document describes common questions about genqlient, and provides an index
|
|||||||
|
|
||||||
There's a [doc for that](INTRODUCTION.md)!
|
There's a [doc for that](INTRODUCTION.md)!
|
||||||
|
|
||||||
|
## … use GET requests instead of POST requests?
|
||||||
|
|
||||||
|
You can use `graphql.NewClientUsingGet` to create a client that will use query parameters to create the request. For example:
|
||||||
|
```go
|
||||||
|
ctx := context.Background()
|
||||||
|
client := graphql.NewClientUsingGet("https://api.github.com/graphql", http.DefaultClient)
|
||||||
|
resp, err := getUser(ctx, client, "benjaminjkraft")
|
||||||
|
fmt.Println(resp.User.Name, err)
|
||||||
|
```
|
||||||
|
|
||||||
|
This request will be sent via an HTTP GET request, with the query, operation name and variables encoded in the URL.
|
||||||
|
|
||||||
|
For example, if the query is defined as:
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
query getUser($login: String!) {
|
||||||
|
user(login: $login) {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The URL requested will be:
|
||||||
|
|
||||||
|
`https://api.github.com/graphql?operationName%3DgetUser%26query%3D%0Aquery%20getUser(%24login%3A%20String!)%20%7B%0A%20%20user(login%3A%20%24login)%20%7B%0A%20%20%20%20name%0A%20%20%7D%0A%7D%0A%26variables%3D%7B%22login%22%3A%22benjaminjkraft%22%7D`
|
||||||
|
|
||||||
|
The client does not support mutations, and will return an error if passed a request that attempts one.
|
||||||
|
|
||||||
### … use an API that requires authentication?
|
### … use an API that requires authentication?
|
||||||
|
|
||||||
When you call `graphql.NewClient`, pass in an HTTP client that adds whatever authentication headers you need (typically by wrapping the client's `Transport`). For example:
|
When you call `graphql.NewClient`, pass in an HTTP client that adds whatever authentication headers you need (typically by wrapping the client's `Transport`). For example:
|
||||||
|
|||||||
+93
-8
@@ -4,9 +4,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||||
)
|
)
|
||||||
@@ -53,10 +56,30 @@ type client struct {
|
|||||||
// The typical method of adding authentication headers is to wrap the client's
|
// The typical method of adding authentication headers is to wrap the client's
|
||||||
// Transport to add those headers. See example/caller.go for an example.
|
// Transport to add those headers. See example/caller.go for an example.
|
||||||
func NewClient(endpoint string, httpClient Doer) Client {
|
func NewClient(endpoint string, httpClient Doer) Client {
|
||||||
|
return newClient(endpoint, httpClient, http.MethodPost)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientUsingGet returns a Client which makes requests to the given endpoint,
|
||||||
|
// suitable for most users.
|
||||||
|
//
|
||||||
|
// The client makes GET requests to the given GraphQL endpoint using a GET query,
|
||||||
|
// with the query, operation name and variables encoded as URL parameters.
|
||||||
|
// It will use the given http client, or http.DefaultClient if a nil client is passed.
|
||||||
|
//
|
||||||
|
// The client does not support mutations, and will return an error if passed a request
|
||||||
|
// that attempts one.
|
||||||
|
//
|
||||||
|
// The typical method of adding authentication headers is to wrap the client's
|
||||||
|
// Transport to add those headers. See example/caller.go for an example.
|
||||||
|
func NewClientUsingGet(endpoint string, httpClient Doer) Client {
|
||||||
|
return newClient(endpoint, httpClient, http.MethodGet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClient(endpoint string, httpClient Doer, method string) Client {
|
||||||
if httpClient == nil || httpClient == (*http.Client)(nil) {
|
if httpClient == nil || httpClient == (*http.Client)(nil) {
|
||||||
httpClient = http.DefaultClient
|
httpClient = http.DefaultClient
|
||||||
}
|
}
|
||||||
return &client{httpClient, endpoint, http.MethodPost}
|
return &client{httpClient, endpoint, method}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doer encapsulates the methods from *http.Client needed by Client.
|
// Doer encapsulates the methods from *http.Client needed by Client.
|
||||||
@@ -100,15 +123,14 @@ type Response struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) MakeRequest(ctx context.Context, req *Request, resp *Response) error {
|
func (c *client) MakeRequest(ctx context.Context, req *Request, resp *Response) error {
|
||||||
body, err := json.Marshal(req)
|
var httpReq *http.Request
|
||||||
if err != nil {
|
var err error
|
||||||
return err
|
if c.method == http.MethodGet {
|
||||||
|
httpReq, err = c.createGetRequest(req)
|
||||||
|
} else {
|
||||||
|
httpReq, err = c.createPostRequest(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
httpReq, err := http.NewRequest(
|
|
||||||
c.method,
|
|
||||||
c.endpoint,
|
|
||||||
bytes.NewReader(body))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -142,3 +164,66 @@ func (c *client) MakeRequest(ctx context.Context, req *Request, resp *Response)
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *client) createPostRequest(req *Request) (*http.Request, error) {
|
||||||
|
body, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
httpReq, err := http.NewRequest(
|
||||||
|
c.method,
|
||||||
|
c.endpoint,
|
||||||
|
bytes.NewReader(body))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpReq, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) createGetRequest(req *Request) (*http.Request, error) {
|
||||||
|
parsedURL, err := url.Parse(c.endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
queryParams := parsedURL.Query()
|
||||||
|
queryUpdated := false
|
||||||
|
|
||||||
|
if req.Query != "" {
|
||||||
|
if strings.HasPrefix(strings.TrimSpace(req.Query), "mutation") {
|
||||||
|
return nil, errors.New("client does not support mutations")
|
||||||
|
}
|
||||||
|
queryParams.Set("query", req.Query)
|
||||||
|
queryUpdated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.OpName != "" {
|
||||||
|
queryParams.Set("operationName", req.OpName)
|
||||||
|
queryUpdated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Variables != nil {
|
||||||
|
variables, variablesErr := json.Marshal(req.Variables)
|
||||||
|
if variablesErr != nil {
|
||||||
|
return nil, variablesErr
|
||||||
|
}
|
||||||
|
queryParams.Set("variables", string(variables))
|
||||||
|
queryUpdated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if queryUpdated {
|
||||||
|
parsedURL.RawQuery = queryParams.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
httpReq, err := http.NewRequest(
|
||||||
|
c.method,
|
||||||
|
parsedURL.String(),
|
||||||
|
http.NoBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpReq, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -598,6 +598,13 @@ type MoreUserFieldsHair struct {
|
|||||||
// GetColor returns MoreUserFieldsHair.Color, and is useful for accessing the field via an interface.
|
// GetColor returns MoreUserFieldsHair.Color, and is useful for accessing the field via an interface.
|
||||||
func (v *MoreUserFieldsHair) GetColor() string { return v.Color }
|
func (v *MoreUserFieldsHair) GetColor() string { return v.Color }
|
||||||
|
|
||||||
|
type NewUser struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName returns NewUser.Name, and is useful for accessing the field via an interface.
|
||||||
|
func (v *NewUser) GetName() string { return v.Name }
|
||||||
|
|
||||||
// QueryFragment includes the GraphQL fields of Query requested by the fragment QueryFragment.
|
// QueryFragment includes the GraphQL fields of Query requested by the fragment QueryFragment.
|
||||||
type QueryFragment struct {
|
type QueryFragment struct {
|
||||||
Beings []QueryFragmentBeingsBeing `json:"-"`
|
Beings []QueryFragmentBeingsBeing `json:"-"`
|
||||||
@@ -991,6 +998,14 @@ func (v *UserFields) __premarshalJSON() (*__premarshalUserFields, error) {
|
|||||||
return &retval, nil
|
return &retval, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// __createUserInput is used internally by genqlient
|
||||||
|
type __createUserInput struct {
|
||||||
|
User NewUser `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUser returns __createUserInput.User, and is useful for accessing the field via an interface.
|
||||||
|
func (v *__createUserInput) GetUser() NewUser { return v.User }
|
||||||
|
|
||||||
// __queryWithCustomMarshalInput is used internally by genqlient
|
// __queryWithCustomMarshalInput is used internally by genqlient
|
||||||
type __queryWithCustomMarshalInput struct {
|
type __queryWithCustomMarshalInput struct {
|
||||||
Date time.Time `json:"-"`
|
Date time.Time `json:"-"`
|
||||||
@@ -1290,6 +1305,26 @@ type __queryWithVariablesInput struct {
|
|||||||
// GetId returns __queryWithVariablesInput.Id, and is useful for accessing the field via an interface.
|
// GetId returns __queryWithVariablesInput.Id, and is useful for accessing the field via an interface.
|
||||||
func (v *__queryWithVariablesInput) GetId() string { return v.Id }
|
func (v *__queryWithVariablesInput) GetId() string { return v.Id }
|
||||||
|
|
||||||
|
// createUserCreateUser includes the requested fields of the GraphQL type User.
|
||||||
|
type createUserCreateUser struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetId returns createUserCreateUser.Id, and is useful for accessing the field via an interface.
|
||||||
|
func (v *createUserCreateUser) GetId() string { return v.Id }
|
||||||
|
|
||||||
|
// GetName returns createUserCreateUser.Name, and is useful for accessing the field via an interface.
|
||||||
|
func (v *createUserCreateUser) GetName() string { return v.Name }
|
||||||
|
|
||||||
|
// createUserResponse is returned by createUser on success.
|
||||||
|
type createUserResponse struct {
|
||||||
|
CreateUser createUserCreateUser `json:"createUser"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCreateUser returns createUserResponse.CreateUser, and is useful for accessing the field via an interface.
|
||||||
|
func (v *createUserResponse) GetCreateUser() createUserCreateUser { return v.CreateUser }
|
||||||
|
|
||||||
// failingQueryMeUser includes the requested fields of the GraphQL type User.
|
// failingQueryMeUser includes the requested fields of the GraphQL type User.
|
||||||
type failingQueryMeUser struct {
|
type failingQueryMeUser struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
@@ -3043,6 +3078,39 @@ type simpleQueryResponse struct {
|
|||||||
// GetMe returns simpleQueryResponse.Me, and is useful for accessing the field via an interface.
|
// GetMe returns simpleQueryResponse.Me, and is useful for accessing the field via an interface.
|
||||||
func (v *simpleQueryResponse) GetMe() simpleQueryMeUser { return v.Me }
|
func (v *simpleQueryResponse) GetMe() simpleQueryMeUser { return v.Me }
|
||||||
|
|
||||||
|
func createUser(
|
||||||
|
ctx context.Context,
|
||||||
|
client graphql.Client,
|
||||||
|
user NewUser,
|
||||||
|
) (*createUserResponse, map[string]interface{}, error) {
|
||||||
|
req := &graphql.Request{
|
||||||
|
OpName: "createUser",
|
||||||
|
Query: `
|
||||||
|
mutation createUser ($user: NewUser!) {
|
||||||
|
createUser(input: $user) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Variables: &__createUserInput{
|
||||||
|
User: user,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var data createUserResponse
|
||||||
|
resp := &graphql.Response{Data: &data}
|
||||||
|
|
||||||
|
err = client.MakeRequest(
|
||||||
|
ctx,
|
||||||
|
req,
|
||||||
|
resp,
|
||||||
|
)
|
||||||
|
|
||||||
|
return &data, resp.Extensions, err
|
||||||
|
}
|
||||||
|
|
||||||
func failingQuery(
|
func failingQuery(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client graphql.Client,
|
client graphql.Client,
|
||||||
|
|||||||
@@ -25,14 +25,35 @@ func TestSimpleQuery(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
resp, _, err := simpleQuery(ctx, client)
|
for _, client := range clients {
|
||||||
|
resp, _, err := simpleQuery(ctx, client)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "1", resp.Me.Id)
|
||||||
|
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
||||||
|
assert.Equal(t, 17, resp.Me.LuckyNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMutation(t *testing.T) {
|
||||||
|
_ = `# @genqlient
|
||||||
|
mutation createUser($user: NewUser!) { createUser(input: $user) { id name } }`
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
server := server.RunServer()
|
||||||
|
defer server.Close()
|
||||||
|
postClient := newRoundtripClient(t, server.URL)
|
||||||
|
getClient := newRoundtripGetClient(t, server.URL)
|
||||||
|
|
||||||
|
resp, _, err := createUser(ctx, postClient, NewUser{Name: "Jack"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "5", resp.CreateUser.Id)
|
||||||
|
assert.Equal(t, "Jack", resp.CreateUser.Name)
|
||||||
|
|
||||||
assert.Equal(t, "1", resp.Me.Id)
|
_, _, err = createUser(ctx, getClient, NewUser{Name: "Jill"})
|
||||||
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
require.Errorf(t, err, "client does not support mutations")
|
||||||
assert.Equal(t, 17, resp.Me.LuckyNumber)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerError(t *testing.T) {
|
func TestServerError(t *testing.T) {
|
||||||
@@ -42,30 +63,34 @@ func TestServerError(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
resp, _, err := failingQuery(ctx, client)
|
for _, client := range clients {
|
||||||
// As long as we get some response back, we should still return a full
|
resp, _, err := failingQuery(ctx, client)
|
||||||
// response -- and indeed in this case it should even have another field
|
// As long as we get some response back, we should still return a full
|
||||||
// (which didn't err) set.
|
// response -- and indeed in this case it should even have another field
|
||||||
assert.Error(t, err)
|
// (which didn't err) set.
|
||||||
assert.NotNil(t, resp)
|
assert.Error(t, err)
|
||||||
assert.Equal(t, "1", resp.Me.Id)
|
assert.NotNil(t, resp)
|
||||||
|
assert.Equal(t, "1", resp.Me.Id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNetworkError(t *testing.T) {
|
func TestNetworkError(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
client := newRoundtripClient(t, "https://nothing.invalid/graphql")
|
clients := newRoundtripClients(t, "https://nothing.invalid/graphql")
|
||||||
|
|
||||||
resp, _, err := failingQuery(ctx, client)
|
for _, client := range clients {
|
||||||
// As we guarantee in the README, even on network error you always get a
|
resp, _, err := failingQuery(ctx, client)
|
||||||
// non-nil response; this is so you can write e.g.
|
// As we guarantee in the README, even on network error you always get a
|
||||||
// resp, err := failingQuery(ctx)
|
// non-nil response; this is so you can write e.g.
|
||||||
// return resp.Me.Id, err
|
// resp, err := failingQuery(ctx)
|
||||||
// without a bunch of extra ceremony.
|
// return resp.Me.Id, err
|
||||||
assert.Error(t, err)
|
// without a bunch of extra ceremony.
|
||||||
assert.NotNil(t, resp)
|
assert.Error(t, err)
|
||||||
assert.Equal(t, new(failingQueryResponse), resp)
|
assert.NotNil(t, resp)
|
||||||
|
assert.Equal(t, new(failingQueryResponse), resp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVariables(t *testing.T) {
|
func TestVariables(t *testing.T) {
|
||||||
@@ -80,19 +105,24 @@ func TestVariables(t *testing.T) {
|
|||||||
// this right in Go (without adding `pointer: true` just for this purpose),
|
// this right in Go (without adding `pointer: true` just for this purpose),
|
||||||
// and unmarshal(marshal(resp)) == resp should still hold, so we don't
|
// and unmarshal(marshal(resp)) == resp should still hold, so we don't
|
||||||
// worry about it.
|
// worry about it.
|
||||||
client := graphql.NewClient(server.URL, http.DefaultClient)
|
clients := []graphql.Client{
|
||||||
|
graphql.NewClient(server.URL, http.DefaultClient),
|
||||||
|
graphql.NewClientUsingGet(server.URL, http.DefaultClient),
|
||||||
|
}
|
||||||
|
|
||||||
resp, _, err := queryWithVariables(ctx, client, "2")
|
for _, client := range clients {
|
||||||
require.NoError(t, err)
|
resp, _, err := queryWithVariables(ctx, client, "2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "2", resp.User.Id)
|
assert.Equal(t, "2", resp.User.Id)
|
||||||
assert.Equal(t, "Raven", resp.User.Name)
|
assert.Equal(t, "Raven", resp.User.Name)
|
||||||
assert.Equal(t, -1, resp.User.LuckyNumber)
|
assert.Equal(t, -1, resp.User.LuckyNumber)
|
||||||
|
|
||||||
resp, _, err = queryWithVariables(ctx, client, "374892379482379")
|
resp, _, err = queryWithVariables(ctx, client, "374892379482379")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Zero(t, resp.User)
|
assert.Zero(t, resp.User)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtensions(t *testing.T) {
|
func TestExtensions(t *testing.T) {
|
||||||
@@ -102,12 +132,14 @@ func TestExtensions(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
_, extensions, err := simpleQueryExt(ctx, client)
|
for _, client := range clients {
|
||||||
require.NoError(t, err)
|
_, extensions, err := simpleQueryExt(ctx, client)
|
||||||
assert.NotNil(t, extensions)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, extensions["foobar"], "test")
|
assert.NotNil(t, extensions)
|
||||||
|
assert.Equal(t, extensions["foobar"], "test")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOmitempty(t *testing.T) {
|
func TestOmitempty(t *testing.T) {
|
||||||
@@ -119,22 +151,24 @@ func TestOmitempty(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
resp, _, err := queryWithOmitempty(ctx, client, "2")
|
for _, client := range clients {
|
||||||
require.NoError(t, err)
|
resp, _, err := queryWithOmitempty(ctx, client, "2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "2", resp.User.Id)
|
assert.Equal(t, "2", resp.User.Id)
|
||||||
assert.Equal(t, "Raven", resp.User.Name)
|
assert.Equal(t, "Raven", resp.User.Name)
|
||||||
assert.Equal(t, -1, resp.User.LuckyNumber)
|
assert.Equal(t, -1, resp.User.LuckyNumber)
|
||||||
|
|
||||||
// should return default user, not the user with ID ""
|
// should return default user, not the user with ID ""
|
||||||
resp, _, err = queryWithOmitempty(ctx, client, "")
|
resp, _, err = queryWithOmitempty(ctx, client, "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "1", resp.User.Id)
|
assert.Equal(t, "1", resp.User.Id)
|
||||||
assert.Equal(t, "Yours Truly", resp.User.Name)
|
assert.Equal(t, "Yours Truly", resp.User.Name)
|
||||||
assert.Equal(t, 17, resp.User.LuckyNumber)
|
assert.Equal(t, 17, resp.User.LuckyNumber)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomMarshal(t *testing.T) {
|
func TestCustomMarshal(t *testing.T) {
|
||||||
@@ -146,24 +180,26 @@ func TestCustomMarshal(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
resp, _, err := queryWithCustomMarshal(ctx, client,
|
for _, client := range clients {
|
||||||
time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC))
|
resp, _, err := queryWithCustomMarshal(ctx, client,
|
||||||
require.NoError(t, err)
|
time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Len(t, resp.UsersBornOn, 1)
|
assert.Len(t, resp.UsersBornOn, 1)
|
||||||
user := resp.UsersBornOn[0]
|
user := resp.UsersBornOn[0]
|
||||||
assert.Equal(t, "1", user.Id)
|
assert.Equal(t, "1", user.Id)
|
||||||
assert.Equal(t, "Yours Truly", user.Name)
|
assert.Equal(t, "Yours Truly", user.Name)
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
user.Birthdate)
|
user.Birthdate)
|
||||||
|
|
||||||
resp, _, err = queryWithCustomMarshal(ctx, client,
|
resp, _, err = queryWithCustomMarshal(ctx, client,
|
||||||
time.Date(2021, time.January, 1, 12, 34, 56, 789, time.UTC))
|
time.Date(2021, time.January, 1, 12, 34, 56, 789, time.UTC))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, resp.UsersBornOn, 0)
|
assert.Len(t, resp.UsersBornOn, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomMarshalSlice(t *testing.T) {
|
func TestCustomMarshalSlice(t *testing.T) {
|
||||||
@@ -175,24 +211,26 @@ func TestCustomMarshalSlice(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
resp, _, err := queryWithCustomMarshalSlice(ctx, client,
|
for _, client := range clients {
|
||||||
[]time.Time{time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC)})
|
resp, _, err := queryWithCustomMarshalSlice(ctx, client,
|
||||||
require.NoError(t, err)
|
[]time.Time{time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC)})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Len(t, resp.UsersBornOnDates, 1)
|
assert.Len(t, resp.UsersBornOnDates, 1)
|
||||||
user := resp.UsersBornOnDates[0]
|
user := resp.UsersBornOnDates[0]
|
||||||
assert.Equal(t, "1", user.Id)
|
assert.Equal(t, "1", user.Id)
|
||||||
assert.Equal(t, "Yours Truly", user.Name)
|
assert.Equal(t, "Yours Truly", user.Name)
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
user.Birthdate)
|
user.Birthdate)
|
||||||
|
|
||||||
resp, _, err = queryWithCustomMarshalSlice(ctx, client,
|
resp, _, err = queryWithCustomMarshalSlice(ctx, client,
|
||||||
[]time.Time{time.Date(2021, time.January, 1, 12, 34, 56, 789, time.UTC)})
|
[]time.Time{time.Date(2021, time.January, 1, 12, 34, 56, 789, time.UTC)})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, resp.UsersBornOnDates, 0)
|
assert.Len(t, resp.UsersBornOnDates, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomMarshalOptional(t *testing.T) {
|
func TestCustomMarshalOptional(t *testing.T) {
|
||||||
@@ -209,28 +247,30 @@ func TestCustomMarshalOptional(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
date := time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC)
|
for _, client := range clients {
|
||||||
resp, _, err := queryWithCustomMarshalOptional(ctx, client, &date, nil)
|
date := time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC)
|
||||||
require.NoError(t, err)
|
resp, _, err := queryWithCustomMarshalOptional(ctx, client, &date, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Len(t, resp.UserSearch, 1)
|
assert.Len(t, resp.UserSearch, 1)
|
||||||
user := resp.UserSearch[0]
|
user := resp.UserSearch[0]
|
||||||
assert.Equal(t, "1", user.Id)
|
assert.Equal(t, "1", user.Id)
|
||||||
assert.Equal(t, "Yours Truly", user.Name)
|
assert.Equal(t, "Yours Truly", user.Name)
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
user.Birthdate)
|
user.Birthdate)
|
||||||
|
|
||||||
id := "2"
|
id := "2"
|
||||||
resp, _, err = queryWithCustomMarshalOptional(ctx, client, nil, &id)
|
resp, _, err = queryWithCustomMarshalOptional(ctx, client, nil, &id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, resp.UserSearch, 1)
|
assert.Len(t, resp.UserSearch, 1)
|
||||||
user = resp.UserSearch[0]
|
user = resp.UserSearch[0]
|
||||||
assert.Equal(t, "2", user.Id)
|
assert.Equal(t, "2", user.Id)
|
||||||
assert.Equal(t, "Raven", user.Name)
|
assert.Equal(t, "Raven", user.Name)
|
||||||
assert.Zero(t, user.Birthdate)
|
assert.Zero(t, user.Birthdate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInterfaceNoFragments(t *testing.T) {
|
func TestInterfaceNoFragments(t *testing.T) {
|
||||||
@@ -243,58 +283,60 @@ func TestInterfaceNoFragments(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
resp, _, err := queryWithInterfaceNoFragments(ctx, client, "1")
|
for _, client := range clients {
|
||||||
require.NoError(t, err)
|
resp, _, err := queryWithInterfaceNoFragments(ctx, client, "1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
// We should get the following response:
|
// We should get the following response:
|
||||||
// me: User{Id: 1, Name: "Yours Truly"},
|
// me: User{Id: 1, Name: "Yours Truly"},
|
||||||
// being: User{Id: 1, Name: "Yours Truly"},
|
// being: User{Id: 1, Name: "Yours Truly"},
|
||||||
|
|
||||||
assert.Equal(t, "1", resp.Me.Id)
|
assert.Equal(t, "1", resp.Me.Id)
|
||||||
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
||||||
|
|
||||||
// Check fields both via interface and via type-assertion:
|
// Check fields both via interface and via type-assertion:
|
||||||
assert.Equal(t, "User", resp.Being.GetTypename())
|
assert.Equal(t, "User", resp.Being.GetTypename())
|
||||||
assert.Equal(t, "1", resp.Being.GetId())
|
assert.Equal(t, "1", resp.Being.GetId())
|
||||||
assert.Equal(t, "Yours Truly", resp.Being.GetName())
|
assert.Equal(t, "Yours Truly", resp.Being.GetName())
|
||||||
|
|
||||||
user, ok := resp.Being.(*queryWithInterfaceNoFragmentsBeingUser)
|
user, ok := resp.Being.(*queryWithInterfaceNoFragmentsBeingUser)
|
||||||
require.Truef(t, ok, "got %T, not User", resp.Being)
|
require.Truef(t, ok, "got %T, not User", resp.Being)
|
||||||
assert.Equal(t, "1", user.Id)
|
assert.Equal(t, "1", user.Id)
|
||||||
assert.Equal(t, "Yours Truly", user.Name)
|
assert.Equal(t, "Yours Truly", user.Name)
|
||||||
|
|
||||||
resp, _, err = queryWithInterfaceNoFragments(ctx, client, "3")
|
resp, _, err = queryWithInterfaceNoFragments(ctx, client, "3")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// We should get the following response:
|
// We should get the following response:
|
||||||
// me: User{Id: 1, Name: "Yours Truly"},
|
// me: User{Id: 1, Name: "Yours Truly"},
|
||||||
// being: Animal{Id: 3, Name: "Fido"},
|
// being: Animal{Id: 3, Name: "Fido"},
|
||||||
|
|
||||||
assert.Equal(t, "1", resp.Me.Id)
|
assert.Equal(t, "1", resp.Me.Id)
|
||||||
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
||||||
|
|
||||||
assert.Equal(t, "Animal", resp.Being.GetTypename())
|
assert.Equal(t, "Animal", resp.Being.GetTypename())
|
||||||
assert.Equal(t, "3", resp.Being.GetId())
|
assert.Equal(t, "3", resp.Being.GetId())
|
||||||
assert.Equal(t, "Fido", resp.Being.GetName())
|
assert.Equal(t, "Fido", resp.Being.GetName())
|
||||||
|
|
||||||
animal, ok := resp.Being.(*queryWithInterfaceNoFragmentsBeingAnimal)
|
animal, ok := resp.Being.(*queryWithInterfaceNoFragmentsBeingAnimal)
|
||||||
require.Truef(t, ok, "got %T, not Animal", resp.Being)
|
require.Truef(t, ok, "got %T, not Animal", resp.Being)
|
||||||
assert.Equal(t, "3", animal.Id)
|
assert.Equal(t, "3", animal.Id)
|
||||||
assert.Equal(t, "Fido", animal.Name)
|
assert.Equal(t, "Fido", animal.Name)
|
||||||
|
|
||||||
resp, _, err = queryWithInterfaceNoFragments(ctx, client, "4757233945723")
|
resp, _, err = queryWithInterfaceNoFragments(ctx, client, "4757233945723")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// We should get the following response:
|
// We should get the following response:
|
||||||
// me: User{Id: 1, Name: "Yours Truly"},
|
// me: User{Id: 1, Name: "Yours Truly"},
|
||||||
// being: null
|
// being: null
|
||||||
|
|
||||||
assert.Equal(t, "1", resp.Me.Id)
|
assert.Equal(t, "1", resp.Me.Id)
|
||||||
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
||||||
|
|
||||||
assert.Nil(t, resp.Being)
|
assert.Nil(t, resp.Being)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInterfaceListField(t *testing.T) {
|
func TestInterfaceListField(t *testing.T) {
|
||||||
@@ -306,39 +348,41 @@ func TestInterfaceListField(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
resp, _, err := queryWithInterfaceListField(ctx, client,
|
for _, client := range clients {
|
||||||
[]string{"1", "3", "12847394823"})
|
resp, _, err := queryWithInterfaceListField(ctx, client,
|
||||||
require.NoError(t, err)
|
[]string{"1", "3", "12847394823"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, resp.Beings, 3)
|
require.Len(t, resp.Beings, 3)
|
||||||
|
|
||||||
// We should get the following three beings:
|
// We should get the following three beings:
|
||||||
// User{Id: 1, Name: "Yours Truly"},
|
// User{Id: 1, Name: "Yours Truly"},
|
||||||
// Animal{Id: 3, Name: "Fido"},
|
// Animal{Id: 3, Name: "Fido"},
|
||||||
// null
|
// null
|
||||||
|
|
||||||
// Check fields both via interface and via type-assertion:
|
// Check fields both via interface and via type-assertion:
|
||||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||||
assert.Equal(t, "Yours Truly", resp.Beings[0].GetName())
|
assert.Equal(t, "Yours Truly", resp.Beings[0].GetName())
|
||||||
|
|
||||||
user, ok := resp.Beings[0].(*queryWithInterfaceListFieldBeingsUser)
|
user, ok := resp.Beings[0].(*queryWithInterfaceListFieldBeingsUser)
|
||||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||||
assert.Equal(t, "1", user.Id)
|
assert.Equal(t, "1", user.Id)
|
||||||
assert.Equal(t, "Yours Truly", user.Name)
|
assert.Equal(t, "Yours Truly", user.Name)
|
||||||
|
|
||||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||||
assert.Equal(t, "Fido", resp.Beings[1].GetName())
|
assert.Equal(t, "Fido", resp.Beings[1].GetName())
|
||||||
|
|
||||||
animal, ok := resp.Beings[1].(*queryWithInterfaceListFieldBeingsAnimal)
|
animal, ok := resp.Beings[1].(*queryWithInterfaceListFieldBeingsAnimal)
|
||||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||||
assert.Equal(t, "3", animal.Id)
|
assert.Equal(t, "3", animal.Id)
|
||||||
assert.Equal(t, "Fido", animal.Name)
|
assert.Equal(t, "Fido", animal.Name)
|
||||||
|
|
||||||
assert.Nil(t, resp.Beings[2])
|
assert.Nil(t, resp.Beings[2])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInterfaceListPointerField(t *testing.T) {
|
func TestInterfaceListPointerField(t *testing.T) {
|
||||||
@@ -353,34 +397,36 @@ func TestInterfaceListPointerField(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
resp, _, err := queryWithInterfaceListPointerField(ctx, client,
|
for _, client := range clients {
|
||||||
[]string{"1", "3", "12847394823"})
|
resp, _, err := queryWithInterfaceListPointerField(ctx, client,
|
||||||
require.NoError(t, err)
|
[]string{"1", "3", "12847394823"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, resp.Beings, 3)
|
require.Len(t, resp.Beings, 3)
|
||||||
|
|
||||||
// Check fields both via interface and via type-assertion:
|
// Check fields both via interface and via type-assertion:
|
||||||
assert.Equal(t, "User", (*resp.Beings[0]).GetTypename())
|
assert.Equal(t, "User", (*resp.Beings[0]).GetTypename())
|
||||||
assert.Equal(t, "1", (*resp.Beings[0]).GetId())
|
assert.Equal(t, "1", (*resp.Beings[0]).GetId())
|
||||||
assert.Equal(t, "Yours Truly", (*resp.Beings[0]).GetName())
|
assert.Equal(t, "Yours Truly", (*resp.Beings[0]).GetName())
|
||||||
|
|
||||||
user, ok := (*resp.Beings[0]).(*queryWithInterfaceListPointerFieldBeingsUser)
|
user, ok := (*resp.Beings[0]).(*queryWithInterfaceListPointerFieldBeingsUser)
|
||||||
require.Truef(t, ok, "got %T, not User", *resp.Beings[0])
|
require.Truef(t, ok, "got %T, not User", *resp.Beings[0])
|
||||||
assert.Equal(t, "1", user.Id)
|
assert.Equal(t, "1", user.Id)
|
||||||
assert.Equal(t, "Yours Truly", user.Name)
|
assert.Equal(t, "Yours Truly", user.Name)
|
||||||
|
|
||||||
assert.Equal(t, "Animal", (*resp.Beings[1]).GetTypename())
|
assert.Equal(t, "Animal", (*resp.Beings[1]).GetTypename())
|
||||||
assert.Equal(t, "3", (*resp.Beings[1]).GetId())
|
assert.Equal(t, "3", (*resp.Beings[1]).GetId())
|
||||||
assert.Equal(t, "Fido", (*resp.Beings[1]).GetName())
|
assert.Equal(t, "Fido", (*resp.Beings[1]).GetName())
|
||||||
|
|
||||||
animal, ok := (*resp.Beings[1]).(*queryWithInterfaceListPointerFieldBeingsAnimal)
|
animal, ok := (*resp.Beings[1]).(*queryWithInterfaceListPointerFieldBeingsAnimal)
|
||||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||||
assert.Equal(t, "3", animal.Id)
|
assert.Equal(t, "3", animal.Id)
|
||||||
assert.Equal(t, "Fido", animal.Name)
|
assert.Equal(t, "Fido", animal.Name)
|
||||||
|
|
||||||
assert.Nil(t, resp.Beings[2])
|
assert.Nil(t, resp.Beings[2])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFragments(t *testing.T) {
|
func TestFragments(t *testing.T) {
|
||||||
@@ -407,62 +453,64 @@ func TestFragments(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
resp, _, err := queryWithFragments(ctx, client, []string{"1", "3", "12847394823"})
|
for _, client := range clients {
|
||||||
require.NoError(t, err)
|
resp, _, err := queryWithFragments(ctx, client, []string{"1", "3", "12847394823"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, resp.Beings, 3)
|
require.Len(t, resp.Beings, 3)
|
||||||
|
|
||||||
// We should get the following three beings:
|
// We should get the following three beings:
|
||||||
// User{Id: 1, Name: "Yours Truly"},
|
// User{Id: 1, Name: "Yours Truly"},
|
||||||
// Animal{Id: 3, Name: "Fido"},
|
// Animal{Id: 3, Name: "Fido"},
|
||||||
// null
|
// null
|
||||||
|
|
||||||
// Check fields both via interface and via type-assertion when possible
|
// Check fields both via interface and via type-assertion when possible
|
||||||
// User has, in total, the fields: __typename id name luckyNumber.
|
// User has, in total, the fields: __typename id name luckyNumber.
|
||||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||||
assert.Equal(t, "Yours Truly", resp.Beings[0].GetName())
|
assert.Equal(t, "Yours Truly", resp.Beings[0].GetName())
|
||||||
// (hair and luckyNumber we need to cast for)
|
// (hair and luckyNumber we need to cast for)
|
||||||
|
|
||||||
user, ok := resp.Beings[0].(*queryWithFragmentsBeingsUser)
|
user, ok := resp.Beings[0].(*queryWithFragmentsBeingsUser)
|
||||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||||
assert.Equal(t, "1", user.Id)
|
assert.Equal(t, "1", user.Id)
|
||||||
assert.Equal(t, "Yours Truly", user.Name)
|
assert.Equal(t, "Yours Truly", user.Name)
|
||||||
assert.Equal(t, "Black", user.Hair.Color)
|
assert.Equal(t, "Black", user.Hair.Color)
|
||||||
assert.Equal(t, 17, user.LuckyNumber)
|
assert.Equal(t, 17, user.LuckyNumber)
|
||||||
|
|
||||||
// Animal has, in total, the fields:
|
// Animal has, in total, the fields:
|
||||||
// __typename
|
// __typename
|
||||||
// id
|
// id
|
||||||
// species
|
// species
|
||||||
// owner {
|
// owner {
|
||||||
// id
|
// id
|
||||||
// name
|
// name
|
||||||
// ... on User { luckyNumber }
|
// ... on User { luckyNumber }
|
||||||
// }
|
// }
|
||||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||||
// (hair, species, and owner.* we have to cast for)
|
// (hair, species, and owner.* we have to cast for)
|
||||||
|
|
||||||
animal, ok := resp.Beings[1].(*queryWithFragmentsBeingsAnimal)
|
animal, ok := resp.Beings[1].(*queryWithFragmentsBeingsAnimal)
|
||||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||||
assert.Equal(t, "3", animal.Id)
|
assert.Equal(t, "3", animal.Id)
|
||||||
assert.Equal(t, SpeciesDog, animal.Species)
|
assert.Equal(t, SpeciesDog, animal.Species)
|
||||||
assert.True(t, animal.Hair.HasHair)
|
assert.True(t, animal.Hair.HasHair)
|
||||||
|
|
||||||
assert.Equal(t, "1", animal.Owner.GetId())
|
assert.Equal(t, "1", animal.Owner.GetId())
|
||||||
assert.Equal(t, "Yours Truly", animal.Owner.GetName())
|
assert.Equal(t, "Yours Truly", animal.Owner.GetName())
|
||||||
// (luckyNumber we have to cast for, again)
|
// (luckyNumber we have to cast for, again)
|
||||||
|
|
||||||
owner, ok := animal.Owner.(*queryWithFragmentsBeingsAnimalOwnerUser)
|
owner, ok := animal.Owner.(*queryWithFragmentsBeingsAnimalOwnerUser)
|
||||||
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
||||||
assert.Equal(t, "1", owner.Id)
|
assert.Equal(t, "1", owner.Id)
|
||||||
assert.Equal(t, "Yours Truly", owner.Name)
|
assert.Equal(t, "Yours Truly", owner.Name)
|
||||||
assert.Equal(t, 17, owner.LuckyNumber)
|
assert.Equal(t, 17, owner.LuckyNumber)
|
||||||
|
|
||||||
assert.Nil(t, resp.Beings[2])
|
assert.Nil(t, resp.Beings[2])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNamedFragments(t *testing.T) {
|
func TestNamedFragments(t *testing.T) {
|
||||||
@@ -500,73 +548,75 @@ func TestNamedFragments(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
resp, _, err := queryWithNamedFragments(ctx, client, []string{"1", "3", "12847394823"})
|
for _, client := range clients {
|
||||||
require.NoError(t, err)
|
resp, _, err := queryWithNamedFragments(ctx, client, []string{"1", "3", "12847394823"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, resp.Beings, 3)
|
require.Len(t, resp.Beings, 3)
|
||||||
|
|
||||||
// We should get the following three beings:
|
// We should get the following three beings:
|
||||||
// User{Id: 1, Name: "Yours Truly"},
|
// User{Id: 1, Name: "Yours Truly"},
|
||||||
// Animal{Id: 3, Name: "Fido"},
|
// Animal{Id: 3, Name: "Fido"},
|
||||||
// null
|
// null
|
||||||
|
|
||||||
// Check fields both via interface and via type-assertion when possible
|
// Check fields both via interface and via type-assertion when possible
|
||||||
// User has, in total, the fields: __typename id luckyNumber.
|
// User has, in total, the fields: __typename id luckyNumber.
|
||||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||||
// (luckyNumber, hair we need to cast for)
|
// (luckyNumber, hair we need to cast for)
|
||||||
|
|
||||||
user, ok := resp.Beings[0].(*queryWithNamedFragmentsBeingsUser)
|
user, ok := resp.Beings[0].(*queryWithNamedFragmentsBeingsUser)
|
||||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||||
assert.Equal(t, "1", user.Id)
|
assert.Equal(t, "1", user.Id)
|
||||||
assert.Equal(t, "1", user.UserFields.Id)
|
assert.Equal(t, "1", user.UserFields.Id)
|
||||||
assert.Equal(t, "1", user.UserFields.MoreUserFields.Id)
|
assert.Equal(t, "1", user.UserFields.MoreUserFields.Id)
|
||||||
assert.Equal(t, "1", user.UserFields.LuckyFieldsUser.MoreUserFields.Id)
|
assert.Equal(t, "1", user.UserFields.LuckyFieldsUser.MoreUserFields.Id)
|
||||||
// on UserFields, but we should be able to access directly via embedding:
|
// on UserFields, but we should be able to access directly via embedding:
|
||||||
assert.Equal(t, 17, user.LuckyNumber)
|
assert.Equal(t, 17, user.LuckyNumber)
|
||||||
assert.Equal(t, "Black", user.Hair.Color)
|
assert.Equal(t, "Black", user.Hair.Color)
|
||||||
assert.Equal(t, "Black", user.UserFields.MoreUserFields.Hair.Color)
|
assert.Equal(t, "Black", user.UserFields.MoreUserFields.Hair.Color)
|
||||||
assert.Equal(t, "Black", user.UserFields.LuckyFieldsUser.MoreUserFields.Hair.Color)
|
assert.Equal(t, "Black", user.UserFields.LuckyFieldsUser.MoreUserFields.Hair.Color)
|
||||||
|
|
||||||
// Animal has, in total, the fields:
|
// Animal has, in total, the fields:
|
||||||
// __typename
|
// __typename
|
||||||
// id
|
// id
|
||||||
// hair { hasHair }
|
// hair { hasHair }
|
||||||
// owner { id luckyNumber }
|
// owner { id luckyNumber }
|
||||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||||
// (hair.* and owner.* we have to cast for)
|
// (hair.* and owner.* we have to cast for)
|
||||||
|
|
||||||
animal, ok := resp.Beings[1].(*queryWithNamedFragmentsBeingsAnimal)
|
animal, ok := resp.Beings[1].(*queryWithNamedFragmentsBeingsAnimal)
|
||||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||||
// Check that we filled in *both* ID fields:
|
// Check that we filled in *both* ID fields:
|
||||||
assert.Equal(t, "3", animal.Id)
|
assert.Equal(t, "3", animal.Id)
|
||||||
assert.Equal(t, "3", animal.AnimalFields.Id)
|
assert.Equal(t, "3", animal.AnimalFields.Id)
|
||||||
// on AnimalFields:
|
// on AnimalFields:
|
||||||
assert.True(t, animal.Hair.HasHair)
|
assert.True(t, animal.Hair.HasHair)
|
||||||
assert.Equal(t, "1", animal.Owner.GetId())
|
assert.Equal(t, "1", animal.Owner.GetId())
|
||||||
// (luckyNumber we have to cast for, again)
|
// (luckyNumber we have to cast for, again)
|
||||||
|
|
||||||
owner, ok := animal.Owner.(*AnimalFieldsOwnerUser)
|
owner, ok := animal.Owner.(*AnimalFieldsOwnerUser)
|
||||||
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
||||||
// Check that we filled in *both* ID fields:
|
// Check that we filled in *both* ID fields:
|
||||||
assert.Equal(t, "1", owner.Id)
|
assert.Equal(t, "1", owner.Id)
|
||||||
assert.Equal(t, "1", owner.UserFields.Id)
|
assert.Equal(t, "1", owner.UserFields.Id)
|
||||||
assert.Equal(t, "1", owner.UserFields.MoreUserFields.Id)
|
assert.Equal(t, "1", owner.UserFields.MoreUserFields.Id)
|
||||||
assert.Equal(t, "1", owner.UserFields.LuckyFieldsUser.MoreUserFields.Id)
|
assert.Equal(t, "1", owner.UserFields.LuckyFieldsUser.MoreUserFields.Id)
|
||||||
// on UserFields:
|
// on UserFields:
|
||||||
assert.Equal(t, 17, owner.LuckyNumber)
|
assert.Equal(t, 17, owner.LuckyNumber)
|
||||||
assert.Equal(t, "Black", owner.UserFields.MoreUserFields.Hair.Color)
|
assert.Equal(t, "Black", owner.UserFields.MoreUserFields.Hair.Color)
|
||||||
assert.Equal(t, "Black", owner.UserFields.LuckyFieldsUser.MoreUserFields.Hair.Color)
|
assert.Equal(t, "Black", owner.UserFields.LuckyFieldsUser.MoreUserFields.Hair.Color)
|
||||||
|
|
||||||
// Lucky-based fields we can also get by casting to the fragment-interface.
|
// Lucky-based fields we can also get by casting to the fragment-interface.
|
||||||
luckyOwner, ok := animal.Owner.(LuckyFields)
|
luckyOwner, ok := animal.Owner.(LuckyFields)
|
||||||
require.Truef(t, ok, "got %T, not Lucky", animal.Owner)
|
require.Truef(t, ok, "got %T, not Lucky", animal.Owner)
|
||||||
assert.Equal(t, 17, luckyOwner.GetLuckyNumber())
|
assert.Equal(t, 17, luckyOwner.GetLuckyNumber())
|
||||||
|
|
||||||
assert.Nil(t, resp.Beings[2])
|
assert.Nil(t, resp.Beings[2])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFlatten(t *testing.T) {
|
func TestFlatten(t *testing.T) {
|
||||||
@@ -629,54 +679,56 @@ func TestFlatten(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
server := server.RunServer()
|
server := server.RunServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
client := newRoundtripClient(t, server.URL)
|
clients := newRoundtripClients(t, server.URL)
|
||||||
|
|
||||||
resp, _, err := queryWithFlatten(ctx, client, []string{"1", "3", "12847394823"})
|
for _, client := range clients {
|
||||||
require.NoError(t, err)
|
resp, _, err := queryWithFlatten(ctx, client, []string{"1", "3", "12847394823"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, resp.Beings, 3)
|
require.Len(t, resp.Beings, 3)
|
||||||
|
|
||||||
// We should get the following three beings:
|
// We should get the following three beings:
|
||||||
// User{Id: 1, Name: "Yours Truly"},
|
// User{Id: 1, Name: "Yours Truly"},
|
||||||
// Animal{Id: 3, Name: "Fido"},
|
// Animal{Id: 3, Name: "Fido"},
|
||||||
// null
|
// null
|
||||||
|
|
||||||
// Check fields both via interface and via type-assertion when possible
|
// Check fields both via interface and via type-assertion when possible
|
||||||
// User has, in total, the fields: __typename id luckyNumber.
|
// User has, in total, the fields: __typename id luckyNumber.
|
||||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||||
// (luckyNumber we need to cast for)
|
// (luckyNumber we need to cast for)
|
||||||
|
|
||||||
user, ok := resp.Beings[0].(*QueryFragmentBeingsUser)
|
user, ok := resp.Beings[0].(*QueryFragmentBeingsUser)
|
||||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||||
assert.Equal(t, "1", user.Id)
|
assert.Equal(t, "1", user.Id)
|
||||||
assert.Equal(t, 17, user.InnerLuckyFieldsUser.LuckyNumber)
|
assert.Equal(t, 17, user.InnerLuckyFieldsUser.LuckyNumber)
|
||||||
|
|
||||||
// Animal has, in total, the fields:
|
// Animal has, in total, the fields:
|
||||||
// __typename
|
// __typename
|
||||||
// id
|
// id
|
||||||
// owner { id name ... on User { friends { id name } } }
|
// owner { id name ... on User { friends { id name } } }
|
||||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||||
// (owner.* we have to cast for)
|
// (owner.* we have to cast for)
|
||||||
|
|
||||||
animal, ok := resp.Beings[1].(*QueryFragmentBeingsAnimal)
|
animal, ok := resp.Beings[1].(*QueryFragmentBeingsAnimal)
|
||||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||||
assert.Equal(t, "3", animal.Id)
|
assert.Equal(t, "3", animal.Id)
|
||||||
// on AnimalFields:
|
// on AnimalFields:
|
||||||
assert.Equal(t, "1", animal.Owner.GetId())
|
assert.Equal(t, "1", animal.Owner.GetId())
|
||||||
assert.Equal(t, "Yours Truly", animal.Owner.GetName())
|
assert.Equal(t, "Yours Truly", animal.Owner.GetName())
|
||||||
// (friends.* we have to cast for, again)
|
// (friends.* we have to cast for, again)
|
||||||
|
|
||||||
owner, ok := animal.Owner.(*InnerBeingFieldsUser)
|
owner, ok := animal.Owner.(*InnerBeingFieldsUser)
|
||||||
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
||||||
assert.Equal(t, "1", owner.Id)
|
assert.Equal(t, "1", owner.Id)
|
||||||
assert.Equal(t, "Yours Truly", owner.Name)
|
assert.Equal(t, "Yours Truly", owner.Name)
|
||||||
assert.Len(t, owner.Friends, 1)
|
assert.Len(t, owner.Friends, 1)
|
||||||
assert.Equal(t, "2", owner.Friends[0].Id)
|
assert.Equal(t, "2", owner.Friends[0].Id)
|
||||||
assert.Equal(t, "Raven", owner.Friends[0].Name)
|
assert.Equal(t, "Raven", owner.Friends[0].Name)
|
||||||
|
|
||||||
assert.Nil(t, resp.Beings[2])
|
assert.Nil(t, resp.Beings[2])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGeneratedCode(t *testing.T) {
|
func TestGeneratedCode(t *testing.T) {
|
||||||
|
|||||||
@@ -101,10 +101,25 @@ func (c *roundtripClient) MakeRequest(ctx context.Context, req *graphql.Request,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newRoundtripClients(t *testing.T, endpoint string) []graphql.Client {
|
||||||
|
return []graphql.Client{newRoundtripClient(t, endpoint), newRoundtripGetClient(t, endpoint)}
|
||||||
|
}
|
||||||
|
|
||||||
func newRoundtripClient(t *testing.T, endpoint string) graphql.Client {
|
func newRoundtripClient(t *testing.T, endpoint string) graphql.Client {
|
||||||
transport := &lastResponseTransport{wrapped: http.DefaultTransport}
|
transport := &lastResponseTransport{wrapped: http.DefaultTransport}
|
||||||
|
httpClient := &http.Client{Transport: transport}
|
||||||
return &roundtripClient{
|
return &roundtripClient{
|
||||||
wrapped: graphql.NewClient(endpoint, &http.Client{Transport: transport}),
|
wrapped: graphql.NewClient(endpoint, httpClient),
|
||||||
|
transport: transport,
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRoundtripGetClient(t *testing.T, endpoint string) graphql.Client {
|
||||||
|
transport := &lastResponseTransport{wrapped: http.DefaultTransport}
|
||||||
|
httpClient := &http.Client{Transport: transport}
|
||||||
|
return &roundtripClient{
|
||||||
|
wrapped: graphql.NewClientUsingGet(endpoint, httpClient),
|
||||||
transport: transport,
|
transport: transport,
|
||||||
t: t,
|
t: t,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ type Query {
|
|||||||
fail: Boolean
|
fail: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Mutation {
|
||||||
|
createUser(input: NewUser!): User!
|
||||||
|
}
|
||||||
|
|
||||||
type User implements Being & Lucky {
|
type User implements Being & Lucky {
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
@@ -21,6 +25,10 @@ type User implements Being & Lucky {
|
|||||||
friends: [User!]!
|
friends: [User!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input NewUser {
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
|
||||||
type Hair { color: String } # silly name to confuse the name-generator
|
type Hair { color: String } # silly name to confuse the name-generator
|
||||||
|
|
||||||
type Animal implements Being {
|
type Animal implements Being {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ResolverRoot interface {
|
type ResolverRoot interface {
|
||||||
|
Mutation() MutationResolver
|
||||||
Query() QueryResolver
|
Query() QueryResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +59,10 @@ type ComplexityRoot struct {
|
|||||||
Color func(childComplexity int) int
|
Color func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mutation struct {
|
||||||
|
CreateUser func(childComplexity int, input NewUser) int
|
||||||
|
}
|
||||||
|
|
||||||
Query struct {
|
Query struct {
|
||||||
Being func(childComplexity int, id string) int
|
Being func(childComplexity int, id string) int
|
||||||
Beings func(childComplexity int, ids []string) int
|
Beings func(childComplexity int, ids []string) int
|
||||||
@@ -80,6 +85,9 @@ type ComplexityRoot struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MutationResolver interface {
|
||||||
|
CreateUser(ctx context.Context, input NewUser) (*User, error)
|
||||||
|
}
|
||||||
type QueryResolver interface {
|
type QueryResolver interface {
|
||||||
Me(ctx context.Context) (*User, error)
|
Me(ctx context.Context) (*User, error)
|
||||||
User(ctx context.Context, id *string) (*User, error)
|
User(ctx context.Context, id *string) (*User, error)
|
||||||
@@ -156,6 +164,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.Hair.Color(childComplexity), true
|
return e.complexity.Hair.Color(childComplexity), true
|
||||||
|
|
||||||
|
case "Mutation.createUser":
|
||||||
|
if e.complexity.Mutation.CreateUser == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := ec.field_Mutation_createUser_args(context.TODO(), rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Mutation.CreateUser(childComplexity, args["input"].(NewUser)), true
|
||||||
|
|
||||||
case "Query.being":
|
case "Query.being":
|
||||||
if e.complexity.Query.Being == nil {
|
if e.complexity.Query.Being == nil {
|
||||||
break
|
break
|
||||||
@@ -316,6 +336,20 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
|
|||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
data.MarshalGQL(&buf)
|
data.MarshalGQL(&buf)
|
||||||
|
|
||||||
|
return &graphql.Response{
|
||||||
|
Data: buf.Bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ast.Mutation:
|
||||||
|
return func(ctx context.Context) *graphql.Response {
|
||||||
|
if !first {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
first = false
|
||||||
|
data := ec._Mutation(ctx, rc.Operation.SelectionSet)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
data.MarshalGQL(&buf)
|
||||||
|
|
||||||
return &graphql.Response{
|
return &graphql.Response{
|
||||||
Data: buf.Bytes(),
|
Data: buf.Bytes(),
|
||||||
}
|
}
|
||||||
@@ -360,6 +394,10 @@ type Query {
|
|||||||
fail: Boolean
|
fail: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Mutation {
|
||||||
|
createUser(input: NewUser!): User!
|
||||||
|
}
|
||||||
|
|
||||||
type User implements Being & Lucky {
|
type User implements Being & Lucky {
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
@@ -369,6 +407,10 @@ type User implements Being & Lucky {
|
|||||||
friends: [User!]!
|
friends: [User!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input NewUser {
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
|
||||||
type Hair { color: String } # silly name to confuse the name-generator
|
type Hair { color: String } # silly name to confuse the name-generator
|
||||||
|
|
||||||
type Animal implements Being {
|
type Animal implements Being {
|
||||||
@@ -402,6 +444,21 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...)
|
|||||||
|
|
||||||
// region ***************************** args.gotpl *****************************
|
// region ***************************** args.gotpl *****************************
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Mutation_createUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
args := map[string]interface{}{}
|
||||||
|
var arg0 NewUser
|
||||||
|
if tmp, ok := rawArgs["input"]; ok {
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input"))
|
||||||
|
arg0, err = ec.unmarshalNNewUser2githubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐNewUser(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["input"] = arg0
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
@@ -805,6 +862,48 @@ func (ec *executionContext) _Hair_color(ctx context.Context, field graphql.Colle
|
|||||||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Mutation_createUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "Mutation",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: true,
|
||||||
|
IsResolver: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
rawArgs := field.ArgumentMap(ec.Variables)
|
||||||
|
args, err := ec.field_Mutation_createUser_args(ctx, rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
fc.Args = args
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return ec.resolvers.Mutation().CreateUser(rctx, args["input"].(NewUser))
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*User)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalNUser2ᚖgithubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐUser(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Query_me(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Query_me(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -2609,6 +2708,29 @@ func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field gr
|
|||||||
|
|
||||||
// region **************************** input.gotpl *****************************
|
// region **************************** input.gotpl *****************************
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalInputNewUser(ctx context.Context, obj interface{}) (NewUser, error) {
|
||||||
|
var it NewUser
|
||||||
|
asMap := map[string]interface{}{}
|
||||||
|
for k, v := range obj.(map[string]interface{}) {
|
||||||
|
asMap[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range asMap {
|
||||||
|
switch k {
|
||||||
|
case "name":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name"))
|
||||||
|
it.Name, err = ec.unmarshalNString2string(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
// endregion **************************** input.gotpl *****************************
|
// endregion **************************** input.gotpl *****************************
|
||||||
|
|
||||||
// region ************************** interface.gotpl ***************************
|
// region ************************** interface.gotpl ***************************
|
||||||
@@ -2780,6 +2902,46 @@ func (ec *executionContext) _Hair(ctx context.Context, sel ast.SelectionSet, obj
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mutationImplementors = []string{"Mutation"}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler {
|
||||||
|
fields := graphql.CollectFields(ec.OperationContext, sel, mutationImplementors)
|
||||||
|
ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{
|
||||||
|
Object: "Mutation",
|
||||||
|
})
|
||||||
|
|
||||||
|
out := graphql.NewFieldSet(fields)
|
||||||
|
var invalids uint32
|
||||||
|
for i, field := range fields {
|
||||||
|
innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{
|
||||||
|
Object: field.Name,
|
||||||
|
Field: field,
|
||||||
|
})
|
||||||
|
|
||||||
|
switch field.Name {
|
||||||
|
case "__typename":
|
||||||
|
out.Values[i] = graphql.MarshalString("Mutation")
|
||||||
|
case "createUser":
|
||||||
|
innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
|
||||||
|
return ec._Mutation_createUser(ctx, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, innerFunc)
|
||||||
|
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.Dispatch()
|
||||||
|
if invalids > 0 {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
var queryImplementors = []string{"Query"}
|
var queryImplementors = []string{"Query"}
|
||||||
|
|
||||||
func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler {
|
func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler {
|
||||||
@@ -3670,6 +3832,11 @@ func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.Selecti
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalNNewUser2githubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐNewUser(ctx context.Context, v interface{}) (NewUser, error) {
|
||||||
|
res, err := ec.unmarshalInputNewUser(ctx, v)
|
||||||
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNSpecies2githubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐSpecies(ctx context.Context, v interface{}) (Species, error) {
|
func (ec *executionContext) unmarshalNSpecies2githubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐSpecies(ctx context.Context, v interface{}) (Species, error) {
|
||||||
var res Species
|
var res Species
|
||||||
err := res.UnmarshalGQL(v)
|
err := res.UnmarshalGQL(v)
|
||||||
@@ -3695,6 +3862,10 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalNUser2githubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐUser(ctx context.Context, sel ast.SelectionSet, v User) graphql.Marshaler {
|
||||||
|
return ec._User(ctx, sel, &v)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) marshalNUser2ᚕᚖgithubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐUserᚄ(ctx context.Context, sel ast.SelectionSet, v []*User) graphql.Marshaler {
|
func (ec *executionContext) marshalNUser2ᚕᚖgithubᚗcomᚋKhanᚋgenqlientᚋinternalᚋintegrationᚋserverᚐUserᚄ(ctx context.Context, sel ast.SelectionSet, v []*User) graphql.Marshaler {
|
||||||
ret := make(graphql.Array, len(v))
|
ret := make(graphql.Array, len(v))
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ type Hair struct {
|
|||||||
Color *string `json:"color"`
|
Color *string `json:"color"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NewUser struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/99designs/gqlgen/graphql"
|
"github.com/99designs/gqlgen/graphql"
|
||||||
"github.com/99designs/gqlgen/graphql/handler"
|
"github.com/99designs/gqlgen/graphql/handler"
|
||||||
@@ -73,6 +74,24 @@ func beingByID(id string) Being {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getNewID() string {
|
||||||
|
maxID := 0
|
||||||
|
for _, user := range users {
|
||||||
|
intID, _ := strconv.Atoi(user.ID)
|
||||||
|
if intID > maxID {
|
||||||
|
maxID = intID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, animal := range animals {
|
||||||
|
intID, _ := strconv.Atoi(animal.ID)
|
||||||
|
if intID > maxID {
|
||||||
|
maxID = intID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newID := maxID + 1
|
||||||
|
return strconv.Itoa(newID)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *queryResolver) Me(ctx context.Context) (*User, error) {
|
func (r *queryResolver) Me(ctx context.Context) (*User, error) {
|
||||||
return userByID("1"), nil
|
return userByID("1"), nil
|
||||||
}
|
}
|
||||||
@@ -129,9 +148,16 @@ func (r *queryResolver) Fail(ctx context.Context) (*bool, error) {
|
|||||||
return &f, fmt.Errorf("oh no")
|
return &f, fmt.Errorf("oh no")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m mutationResolver) CreateUser(ctx context.Context, input NewUser) (*User, error) {
|
||||||
|
newUser := User{ID: getNewID(), Name: input.Name, Friends: []*User{}}
|
||||||
|
users = append(users, &newUser)
|
||||||
|
return &newUser, nil
|
||||||
|
}
|
||||||
|
|
||||||
func RunServer() *httptest.Server {
|
func RunServer() *httptest.Server {
|
||||||
gqlgenServer := handler.New(NewExecutableSchema(Config{Resolvers: &resolver{}}))
|
gqlgenServer := handler.New(NewExecutableSchema(Config{Resolvers: &resolver{}}))
|
||||||
gqlgenServer.AddTransport(transport.POST{})
|
gqlgenServer.AddTransport(transport.POST{})
|
||||||
|
gqlgenServer.AddTransport(transport.GET{})
|
||||||
gqlgenServer.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
|
gqlgenServer.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
|
||||||
graphql.RegisterExtension(ctx, "foobar", "test")
|
graphql.RegisterExtension(ctx, "foobar", "test")
|
||||||
return next(ctx)
|
return next(ctx)
|
||||||
@@ -140,10 +166,15 @@ func RunServer() *httptest.Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
resolver struct{}
|
resolver struct{}
|
||||||
queryResolver struct{}
|
queryResolver struct{}
|
||||||
|
mutationResolver struct{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (r *resolver) Mutation() MutationResolver {
|
||||||
|
return &mutationResolver{}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *resolver) Query() QueryResolver { return &queryResolver{} }
|
func (r *resolver) Query() QueryResolver { return &queryResolver{} }
|
||||||
|
|
||||||
//go:generate go run github.com/99designs/gqlgen
|
//go:generate go run github.com/99designs/gqlgen
|
||||||
|
|||||||
Reference in New Issue
Block a user