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`).
|
||||
- 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:
|
||||
|
||||
|
||||
+28
@@ -8,6 +8,34 @@ This document describes common questions about genqlient, and provides an index
|
||||
|
||||
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?
|
||||
|
||||
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"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"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
|
||||
// Transport to add those headers. See example/caller.go for an example.
|
||||
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) {
|
||||
httpClient = http.DefaultClient
|
||||
}
|
||||
return &client{httpClient, endpoint, http.MethodPost}
|
||||
return &client{httpClient, endpoint, method}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
body, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
var httpReq *http.Request
|
||||
var err error
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@@ -142,3 +164,66 @@ func (c *client) MakeRequest(ctx context.Context, req *Request, resp *Response)
|
||||
}
|
||||
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.
|
||||
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.
|
||||
type QueryFragment struct {
|
||||
Beings []QueryFragmentBeingsBeing `json:"-"`
|
||||
@@ -991,6 +998,14 @@ func (v *UserFields) __premarshalJSON() (*__premarshalUserFields, error) {
|
||||
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
|
||||
type __queryWithCustomMarshalInput struct {
|
||||
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.
|
||||
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.
|
||||
type failingQueryMeUser struct {
|
||||
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.
|
||||
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(
|
||||
ctx context.Context,
|
||||
client graphql.Client,
|
||||
|
||||
@@ -25,14 +25,35 @@ func TestSimpleQuery(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
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)
|
||||
assert.Equal(t, "5", resp.CreateUser.Id)
|
||||
assert.Equal(t, "Jack", resp.CreateUser.Name)
|
||||
|
||||
assert.Equal(t, "1", resp.Me.Id)
|
||||
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
||||
assert.Equal(t, 17, resp.Me.LuckyNumber)
|
||||
_, _, err = createUser(ctx, getClient, NewUser{Name: "Jill"})
|
||||
require.Errorf(t, err, "client does not support mutations")
|
||||
}
|
||||
|
||||
func TestServerError(t *testing.T) {
|
||||
@@ -42,30 +63,34 @@ func TestServerError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := newRoundtripClient(t, server.URL)
|
||||
clients := newRoundtripClients(t, server.URL)
|
||||
|
||||
resp, _, err := failingQuery(ctx, client)
|
||||
// As long as we get some response back, we should still return a full
|
||||
// response -- and indeed in this case it should even have another field
|
||||
// (which didn't err) set.
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, "1", resp.Me.Id)
|
||||
for _, client := range clients {
|
||||
resp, _, err := failingQuery(ctx, client)
|
||||
// As long as we get some response back, we should still return a full
|
||||
// response -- and indeed in this case it should even have another field
|
||||
// (which didn't err) set.
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, "1", resp.Me.Id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := newRoundtripClient(t, "https://nothing.invalid/graphql")
|
||||
clients := newRoundtripClients(t, "https://nothing.invalid/graphql")
|
||||
|
||||
resp, _, err := failingQuery(ctx, client)
|
||||
// As we guarantee in the README, even on network error you always get a
|
||||
// non-nil response; this is so you can write e.g.
|
||||
// resp, err := failingQuery(ctx)
|
||||
// return resp.Me.Id, err
|
||||
// without a bunch of extra ceremony.
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, new(failingQueryResponse), resp)
|
||||
for _, client := range clients {
|
||||
resp, _, err := failingQuery(ctx, client)
|
||||
// As we guarantee in the README, even on network error you always get a
|
||||
// non-nil response; this is so you can write e.g.
|
||||
// resp, err := failingQuery(ctx)
|
||||
// return resp.Me.Id, err
|
||||
// without a bunch of extra ceremony.
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, new(failingQueryResponse), resp)
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
// and unmarshal(marshal(resp)) == resp should still hold, so we don't
|
||||
// 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")
|
||||
require.NoError(t, err)
|
||||
for _, client := range clients {
|
||||
resp, _, err := queryWithVariables(ctx, client, "2")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "2", resp.User.Id)
|
||||
assert.Equal(t, "Raven", resp.User.Name)
|
||||
assert.Equal(t, -1, resp.User.LuckyNumber)
|
||||
assert.Equal(t, "2", resp.User.Id)
|
||||
assert.Equal(t, "Raven", resp.User.Name)
|
||||
assert.Equal(t, -1, resp.User.LuckyNumber)
|
||||
|
||||
resp, _, err = queryWithVariables(ctx, client, "374892379482379")
|
||||
require.NoError(t, err)
|
||||
resp, _, err = queryWithVariables(ctx, client, "374892379482379")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Zero(t, resp.User)
|
||||
assert.Zero(t, resp.User)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtensions(t *testing.T) {
|
||||
@@ -102,12 +132,14 @@ func TestExtensions(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := newRoundtripClient(t, server.URL)
|
||||
clients := newRoundtripClients(t, server.URL)
|
||||
|
||||
_, extensions, err := simpleQueryExt(ctx, client)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, extensions)
|
||||
assert.Equal(t, extensions["foobar"], "test")
|
||||
for _, client := range clients {
|
||||
_, extensions, err := simpleQueryExt(ctx, client)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, extensions)
|
||||
assert.Equal(t, extensions["foobar"], "test")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOmitempty(t *testing.T) {
|
||||
@@ -119,22 +151,24 @@ func TestOmitempty(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := newRoundtripClient(t, server.URL)
|
||||
clients := newRoundtripClients(t, server.URL)
|
||||
|
||||
resp, _, err := queryWithOmitempty(ctx, client, "2")
|
||||
require.NoError(t, err)
|
||||
for _, client := range clients {
|
||||
resp, _, err := queryWithOmitempty(ctx, client, "2")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "2", resp.User.Id)
|
||||
assert.Equal(t, "Raven", resp.User.Name)
|
||||
assert.Equal(t, -1, resp.User.LuckyNumber)
|
||||
assert.Equal(t, "2", resp.User.Id)
|
||||
assert.Equal(t, "Raven", resp.User.Name)
|
||||
assert.Equal(t, -1, resp.User.LuckyNumber)
|
||||
|
||||
// should return default user, not the user with ID ""
|
||||
resp, _, err = queryWithOmitempty(ctx, client, "")
|
||||
require.NoError(t, err)
|
||||
// should return default user, not the user with ID ""
|
||||
resp, _, err = queryWithOmitempty(ctx, client, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "1", resp.User.Id)
|
||||
assert.Equal(t, "Yours Truly", resp.User.Name)
|
||||
assert.Equal(t, 17, resp.User.LuckyNumber)
|
||||
assert.Equal(t, "1", resp.User.Id)
|
||||
assert.Equal(t, "Yours Truly", resp.User.Name)
|
||||
assert.Equal(t, 17, resp.User.LuckyNumber)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomMarshal(t *testing.T) {
|
||||
@@ -146,24 +180,26 @@ func TestCustomMarshal(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := newRoundtripClient(t, server.URL)
|
||||
clients := newRoundtripClients(t, server.URL)
|
||||
|
||||
resp, _, err := queryWithCustomMarshal(ctx, client,
|
||||
time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC))
|
||||
require.NoError(t, err)
|
||||
for _, client := range clients {
|
||||
resp, _, err := queryWithCustomMarshal(ctx, client,
|
||||
time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, resp.UsersBornOn, 1)
|
||||
user := resp.UsersBornOn[0]
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
assert.Equal(t,
|
||||
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
user.Birthdate)
|
||||
assert.Len(t, resp.UsersBornOn, 1)
|
||||
user := resp.UsersBornOn[0]
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
assert.Equal(t,
|
||||
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
user.Birthdate)
|
||||
|
||||
resp, _, err = queryWithCustomMarshal(ctx, client,
|
||||
time.Date(2021, time.January, 1, 12, 34, 56, 789, time.UTC))
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, resp.UsersBornOn, 0)
|
||||
resp, _, err = queryWithCustomMarshal(ctx, client,
|
||||
time.Date(2021, time.January, 1, 12, 34, 56, 789, time.UTC))
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, resp.UsersBornOn, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomMarshalSlice(t *testing.T) {
|
||||
@@ -175,24 +211,26 @@ func TestCustomMarshalSlice(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := newRoundtripClient(t, server.URL)
|
||||
clients := newRoundtripClients(t, server.URL)
|
||||
|
||||
resp, _, err := queryWithCustomMarshalSlice(ctx, client,
|
||||
[]time.Time{time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC)})
|
||||
require.NoError(t, err)
|
||||
for _, client := range clients {
|
||||
resp, _, err := queryWithCustomMarshalSlice(ctx, client,
|
||||
[]time.Time{time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC)})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, resp.UsersBornOnDates, 1)
|
||||
user := resp.UsersBornOnDates[0]
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
assert.Equal(t,
|
||||
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
user.Birthdate)
|
||||
assert.Len(t, resp.UsersBornOnDates, 1)
|
||||
user := resp.UsersBornOnDates[0]
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
assert.Equal(t,
|
||||
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
user.Birthdate)
|
||||
|
||||
resp, _, err = queryWithCustomMarshalSlice(ctx, client,
|
||||
[]time.Time{time.Date(2021, time.January, 1, 12, 34, 56, 789, time.UTC)})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, resp.UsersBornOnDates, 0)
|
||||
resp, _, err = queryWithCustomMarshalSlice(ctx, client,
|
||||
[]time.Time{time.Date(2021, time.January, 1, 12, 34, 56, 789, time.UTC)})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, resp.UsersBornOnDates, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomMarshalOptional(t *testing.T) {
|
||||
@@ -209,28 +247,30 @@ func TestCustomMarshalOptional(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
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)
|
||||
resp, _, err := queryWithCustomMarshalOptional(ctx, client, &date, nil)
|
||||
require.NoError(t, err)
|
||||
for _, client := range clients {
|
||||
date := time.Date(2025, time.January, 1, 12, 34, 56, 789, time.UTC)
|
||||
resp, _, err := queryWithCustomMarshalOptional(ctx, client, &date, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, resp.UserSearch, 1)
|
||||
user := resp.UserSearch[0]
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
assert.Equal(t,
|
||||
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
user.Birthdate)
|
||||
assert.Len(t, resp.UserSearch, 1)
|
||||
user := resp.UserSearch[0]
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
assert.Equal(t,
|
||||
time.Date(2025, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
user.Birthdate)
|
||||
|
||||
id := "2"
|
||||
resp, _, err = queryWithCustomMarshalOptional(ctx, client, nil, &id)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, resp.UserSearch, 1)
|
||||
user = resp.UserSearch[0]
|
||||
assert.Equal(t, "2", user.Id)
|
||||
assert.Equal(t, "Raven", user.Name)
|
||||
assert.Zero(t, user.Birthdate)
|
||||
id := "2"
|
||||
resp, _, err = queryWithCustomMarshalOptional(ctx, client, nil, &id)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, resp.UserSearch, 1)
|
||||
user = resp.UserSearch[0]
|
||||
assert.Equal(t, "2", user.Id)
|
||||
assert.Equal(t, "Raven", user.Name)
|
||||
assert.Zero(t, user.Birthdate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceNoFragments(t *testing.T) {
|
||||
@@ -243,58 +283,60 @@ func TestInterfaceNoFragments(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := newRoundtripClient(t, server.URL)
|
||||
clients := newRoundtripClients(t, server.URL)
|
||||
|
||||
resp, _, err := queryWithInterfaceNoFragments(ctx, client, "1")
|
||||
require.NoError(t, err)
|
||||
for _, client := range clients {
|
||||
resp, _, err := queryWithInterfaceNoFragments(ctx, client, "1")
|
||||
require.NoError(t, err)
|
||||
|
||||
// We should get the following response:
|
||||
// me: User{Id: 1, Name: "Yours Truly"},
|
||||
// being: User{Id: 1, Name: "Yours Truly"},
|
||||
// We should get the following response:
|
||||
// me: User{Id: 1, Name: "Yours Truly"},
|
||||
// being: User{Id: 1, Name: "Yours Truly"},
|
||||
|
||||
assert.Equal(t, "1", resp.Me.Id)
|
||||
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
||||
assert.Equal(t, "1", resp.Me.Id)
|
||||
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
||||
|
||||
// Check fields both via interface and via type-assertion:
|
||||
assert.Equal(t, "User", resp.Being.GetTypename())
|
||||
assert.Equal(t, "1", resp.Being.GetId())
|
||||
assert.Equal(t, "Yours Truly", resp.Being.GetName())
|
||||
// Check fields both via interface and via type-assertion:
|
||||
assert.Equal(t, "User", resp.Being.GetTypename())
|
||||
assert.Equal(t, "1", resp.Being.GetId())
|
||||
assert.Equal(t, "Yours Truly", resp.Being.GetName())
|
||||
|
||||
user, ok := resp.Being.(*queryWithInterfaceNoFragmentsBeingUser)
|
||||
require.Truef(t, ok, "got %T, not User", resp.Being)
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
user, ok := resp.Being.(*queryWithInterfaceNoFragmentsBeingUser)
|
||||
require.Truef(t, ok, "got %T, not User", resp.Being)
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
|
||||
resp, _, err = queryWithInterfaceNoFragments(ctx, client, "3")
|
||||
require.NoError(t, err)
|
||||
resp, _, err = queryWithInterfaceNoFragments(ctx, client, "3")
|
||||
require.NoError(t, err)
|
||||
|
||||
// We should get the following response:
|
||||
// me: User{Id: 1, Name: "Yours Truly"},
|
||||
// being: Animal{Id: 3, Name: "Fido"},
|
||||
// We should get the following response:
|
||||
// me: User{Id: 1, Name: "Yours Truly"},
|
||||
// being: Animal{Id: 3, Name: "Fido"},
|
||||
|
||||
assert.Equal(t, "1", resp.Me.Id)
|
||||
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
||||
assert.Equal(t, "1", resp.Me.Id)
|
||||
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
||||
|
||||
assert.Equal(t, "Animal", resp.Being.GetTypename())
|
||||
assert.Equal(t, "3", resp.Being.GetId())
|
||||
assert.Equal(t, "Fido", resp.Being.GetName())
|
||||
assert.Equal(t, "Animal", resp.Being.GetTypename())
|
||||
assert.Equal(t, "3", resp.Being.GetId())
|
||||
assert.Equal(t, "Fido", resp.Being.GetName())
|
||||
|
||||
animal, ok := resp.Being.(*queryWithInterfaceNoFragmentsBeingAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Being)
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
assert.Equal(t, "Fido", animal.Name)
|
||||
animal, ok := resp.Being.(*queryWithInterfaceNoFragmentsBeingAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Being)
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
assert.Equal(t, "Fido", animal.Name)
|
||||
|
||||
resp, _, err = queryWithInterfaceNoFragments(ctx, client, "4757233945723")
|
||||
require.NoError(t, err)
|
||||
resp, _, err = queryWithInterfaceNoFragments(ctx, client, "4757233945723")
|
||||
require.NoError(t, err)
|
||||
|
||||
// We should get the following response:
|
||||
// me: User{Id: 1, Name: "Yours Truly"},
|
||||
// being: null
|
||||
// We should get the following response:
|
||||
// me: User{Id: 1, Name: "Yours Truly"},
|
||||
// being: null
|
||||
|
||||
assert.Equal(t, "1", resp.Me.Id)
|
||||
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
||||
assert.Equal(t, "1", resp.Me.Id)
|
||||
assert.Equal(t, "Yours Truly", resp.Me.Name)
|
||||
|
||||
assert.Nil(t, resp.Being)
|
||||
assert.Nil(t, resp.Being)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceListField(t *testing.T) {
|
||||
@@ -306,39 +348,41 @@ func TestInterfaceListField(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := newRoundtripClient(t, server.URL)
|
||||
clients := newRoundtripClients(t, server.URL)
|
||||
|
||||
resp, _, err := queryWithInterfaceListField(ctx, client,
|
||||
[]string{"1", "3", "12847394823"})
|
||||
require.NoError(t, err)
|
||||
for _, client := range clients {
|
||||
resp, _, err := queryWithInterfaceListField(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:
|
||||
// User{Id: 1, Name: "Yours Truly"},
|
||||
// Animal{Id: 3, Name: "Fido"},
|
||||
// null
|
||||
// We should get the following three beings:
|
||||
// User{Id: 1, Name: "Yours Truly"},
|
||||
// Animal{Id: 3, Name: "Fido"},
|
||||
// null
|
||||
|
||||
// Check fields both via interface and via type-assertion:
|
||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||
assert.Equal(t, "Yours Truly", resp.Beings[0].GetName())
|
||||
// Check fields both via interface and via type-assertion:
|
||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||
assert.Equal(t, "Yours Truly", resp.Beings[0].GetName())
|
||||
|
||||
user, ok := resp.Beings[0].(*queryWithInterfaceListFieldBeingsUser)
|
||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
user, ok := resp.Beings[0].(*queryWithInterfaceListFieldBeingsUser)
|
||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
|
||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||
assert.Equal(t, "Fido", resp.Beings[1].GetName())
|
||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||
assert.Equal(t, "Fido", resp.Beings[1].GetName())
|
||||
|
||||
animal, ok := resp.Beings[1].(*queryWithInterfaceListFieldBeingsAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
assert.Equal(t, "Fido", animal.Name)
|
||||
animal, ok := resp.Beings[1].(*queryWithInterfaceListFieldBeingsAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
assert.Equal(t, "Fido", animal.Name)
|
||||
|
||||
assert.Nil(t, resp.Beings[2])
|
||||
assert.Nil(t, resp.Beings[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceListPointerField(t *testing.T) {
|
||||
@@ -353,34 +397,36 @@ func TestInterfaceListPointerField(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := newRoundtripClient(t, server.URL)
|
||||
clients := newRoundtripClients(t, server.URL)
|
||||
|
||||
resp, _, err := queryWithInterfaceListPointerField(ctx, client,
|
||||
[]string{"1", "3", "12847394823"})
|
||||
require.NoError(t, err)
|
||||
for _, client := range clients {
|
||||
resp, _, err := queryWithInterfaceListPointerField(ctx, client,
|
||||
[]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:
|
||||
assert.Equal(t, "User", (*resp.Beings[0]).GetTypename())
|
||||
assert.Equal(t, "1", (*resp.Beings[0]).GetId())
|
||||
assert.Equal(t, "Yours Truly", (*resp.Beings[0]).GetName())
|
||||
// Check fields both via interface and via type-assertion:
|
||||
assert.Equal(t, "User", (*resp.Beings[0]).GetTypename())
|
||||
assert.Equal(t, "1", (*resp.Beings[0]).GetId())
|
||||
assert.Equal(t, "Yours Truly", (*resp.Beings[0]).GetName())
|
||||
|
||||
user, ok := (*resp.Beings[0]).(*queryWithInterfaceListPointerFieldBeingsUser)
|
||||
require.Truef(t, ok, "got %T, not User", *resp.Beings[0])
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
user, ok := (*resp.Beings[0]).(*queryWithInterfaceListPointerFieldBeingsUser)
|
||||
require.Truef(t, ok, "got %T, not User", *resp.Beings[0])
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
|
||||
assert.Equal(t, "Animal", (*resp.Beings[1]).GetTypename())
|
||||
assert.Equal(t, "3", (*resp.Beings[1]).GetId())
|
||||
assert.Equal(t, "Fido", (*resp.Beings[1]).GetName())
|
||||
assert.Equal(t, "Animal", (*resp.Beings[1]).GetTypename())
|
||||
assert.Equal(t, "3", (*resp.Beings[1]).GetId())
|
||||
assert.Equal(t, "Fido", (*resp.Beings[1]).GetName())
|
||||
|
||||
animal, ok := (*resp.Beings[1]).(*queryWithInterfaceListPointerFieldBeingsAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
assert.Equal(t, "Fido", animal.Name)
|
||||
animal, ok := (*resp.Beings[1]).(*queryWithInterfaceListPointerFieldBeingsAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
assert.Equal(t, "Fido", animal.Name)
|
||||
|
||||
assert.Nil(t, resp.Beings[2])
|
||||
assert.Nil(t, resp.Beings[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFragments(t *testing.T) {
|
||||
@@ -407,62 +453,64 @@ func TestFragments(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := newRoundtripClient(t, server.URL)
|
||||
clients := newRoundtripClients(t, server.URL)
|
||||
|
||||
resp, _, err := queryWithFragments(ctx, client, []string{"1", "3", "12847394823"})
|
||||
require.NoError(t, err)
|
||||
for _, client := range clients {
|
||||
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:
|
||||
// User{Id: 1, Name: "Yours Truly"},
|
||||
// Animal{Id: 3, Name: "Fido"},
|
||||
// null
|
||||
// We should get the following three beings:
|
||||
// User{Id: 1, Name: "Yours Truly"},
|
||||
// Animal{Id: 3, Name: "Fido"},
|
||||
// null
|
||||
|
||||
// Check fields both via interface and via type-assertion when possible
|
||||
// User has, in total, the fields: __typename id name luckyNumber.
|
||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||
assert.Equal(t, "Yours Truly", resp.Beings[0].GetName())
|
||||
// (hair and luckyNumber we need to cast for)
|
||||
// Check fields both via interface and via type-assertion when possible
|
||||
// User has, in total, the fields: __typename id name luckyNumber.
|
||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||
assert.Equal(t, "Yours Truly", resp.Beings[0].GetName())
|
||||
// (hair and luckyNumber we need to cast for)
|
||||
|
||||
user, ok := resp.Beings[0].(*queryWithFragmentsBeingsUser)
|
||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
assert.Equal(t, "Black", user.Hair.Color)
|
||||
assert.Equal(t, 17, user.LuckyNumber)
|
||||
user, ok := resp.Beings[0].(*queryWithFragmentsBeingsUser)
|
||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "Yours Truly", user.Name)
|
||||
assert.Equal(t, "Black", user.Hair.Color)
|
||||
assert.Equal(t, 17, user.LuckyNumber)
|
||||
|
||||
// Animal has, in total, the fields:
|
||||
// __typename
|
||||
// id
|
||||
// species
|
||||
// owner {
|
||||
// id
|
||||
// name
|
||||
// ... on User { luckyNumber }
|
||||
// }
|
||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||
// (hair, species, and owner.* we have to cast for)
|
||||
// Animal has, in total, the fields:
|
||||
// __typename
|
||||
// id
|
||||
// species
|
||||
// owner {
|
||||
// id
|
||||
// name
|
||||
// ... on User { luckyNumber }
|
||||
// }
|
||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||
// (hair, species, and owner.* we have to cast for)
|
||||
|
||||
animal, ok := resp.Beings[1].(*queryWithFragmentsBeingsAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
assert.Equal(t, SpeciesDog, animal.Species)
|
||||
assert.True(t, animal.Hair.HasHair)
|
||||
animal, ok := resp.Beings[1].(*queryWithFragmentsBeingsAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
assert.Equal(t, SpeciesDog, animal.Species)
|
||||
assert.True(t, animal.Hair.HasHair)
|
||||
|
||||
assert.Equal(t, "1", animal.Owner.GetId())
|
||||
assert.Equal(t, "Yours Truly", animal.Owner.GetName())
|
||||
// (luckyNumber we have to cast for, again)
|
||||
assert.Equal(t, "1", animal.Owner.GetId())
|
||||
assert.Equal(t, "Yours Truly", animal.Owner.GetName())
|
||||
// (luckyNumber we have to cast for, again)
|
||||
|
||||
owner, ok := animal.Owner.(*queryWithFragmentsBeingsAnimalOwnerUser)
|
||||
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
||||
assert.Equal(t, "1", owner.Id)
|
||||
assert.Equal(t, "Yours Truly", owner.Name)
|
||||
assert.Equal(t, 17, owner.LuckyNumber)
|
||||
owner, ok := animal.Owner.(*queryWithFragmentsBeingsAnimalOwnerUser)
|
||||
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
||||
assert.Equal(t, "1", owner.Id)
|
||||
assert.Equal(t, "Yours Truly", owner.Name)
|
||||
assert.Equal(t, 17, owner.LuckyNumber)
|
||||
|
||||
assert.Nil(t, resp.Beings[2])
|
||||
assert.Nil(t, resp.Beings[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamedFragments(t *testing.T) {
|
||||
@@ -500,73 +548,75 @@ func TestNamedFragments(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := newRoundtripClient(t, server.URL)
|
||||
clients := newRoundtripClients(t, server.URL)
|
||||
|
||||
resp, _, err := queryWithNamedFragments(ctx, client, []string{"1", "3", "12847394823"})
|
||||
require.NoError(t, err)
|
||||
for _, client := range clients {
|
||||
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:
|
||||
// User{Id: 1, Name: "Yours Truly"},
|
||||
// Animal{Id: 3, Name: "Fido"},
|
||||
// null
|
||||
// We should get the following three beings:
|
||||
// User{Id: 1, Name: "Yours Truly"},
|
||||
// Animal{Id: 3, Name: "Fido"},
|
||||
// null
|
||||
|
||||
// Check fields both via interface and via type-assertion when possible
|
||||
// User has, in total, the fields: __typename id luckyNumber.
|
||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||
// (luckyNumber, hair we need to cast for)
|
||||
// Check fields both via interface and via type-assertion when possible
|
||||
// User has, in total, the fields: __typename id luckyNumber.
|
||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||
// (luckyNumber, hair we need to cast for)
|
||||
|
||||
user, ok := resp.Beings[0].(*queryWithNamedFragmentsBeingsUser)
|
||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "1", user.UserFields.Id)
|
||||
assert.Equal(t, "1", user.UserFields.MoreUserFields.Id)
|
||||
assert.Equal(t, "1", user.UserFields.LuckyFieldsUser.MoreUserFields.Id)
|
||||
// on UserFields, but we should be able to access directly via embedding:
|
||||
assert.Equal(t, 17, user.LuckyNumber)
|
||||
assert.Equal(t, "Black", user.Hair.Color)
|
||||
assert.Equal(t, "Black", user.UserFields.MoreUserFields.Hair.Color)
|
||||
assert.Equal(t, "Black", user.UserFields.LuckyFieldsUser.MoreUserFields.Hair.Color)
|
||||
user, ok := resp.Beings[0].(*queryWithNamedFragmentsBeingsUser)
|
||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, "1", user.UserFields.Id)
|
||||
assert.Equal(t, "1", user.UserFields.MoreUserFields.Id)
|
||||
assert.Equal(t, "1", user.UserFields.LuckyFieldsUser.MoreUserFields.Id)
|
||||
// on UserFields, but we should be able to access directly via embedding:
|
||||
assert.Equal(t, 17, user.LuckyNumber)
|
||||
assert.Equal(t, "Black", user.Hair.Color)
|
||||
assert.Equal(t, "Black", user.UserFields.MoreUserFields.Hair.Color)
|
||||
assert.Equal(t, "Black", user.UserFields.LuckyFieldsUser.MoreUserFields.Hair.Color)
|
||||
|
||||
// Animal has, in total, the fields:
|
||||
// __typename
|
||||
// id
|
||||
// hair { hasHair }
|
||||
// owner { id luckyNumber }
|
||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||
// (hair.* and owner.* we have to cast for)
|
||||
// Animal has, in total, the fields:
|
||||
// __typename
|
||||
// id
|
||||
// hair { hasHair }
|
||||
// owner { id luckyNumber }
|
||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||
// (hair.* and owner.* we have to cast for)
|
||||
|
||||
animal, ok := resp.Beings[1].(*queryWithNamedFragmentsBeingsAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||
// Check that we filled in *both* ID fields:
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
assert.Equal(t, "3", animal.AnimalFields.Id)
|
||||
// on AnimalFields:
|
||||
assert.True(t, animal.Hair.HasHair)
|
||||
assert.Equal(t, "1", animal.Owner.GetId())
|
||||
// (luckyNumber we have to cast for, again)
|
||||
animal, ok := resp.Beings[1].(*queryWithNamedFragmentsBeingsAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||
// Check that we filled in *both* ID fields:
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
assert.Equal(t, "3", animal.AnimalFields.Id)
|
||||
// on AnimalFields:
|
||||
assert.True(t, animal.Hair.HasHair)
|
||||
assert.Equal(t, "1", animal.Owner.GetId())
|
||||
// (luckyNumber we have to cast for, again)
|
||||
|
||||
owner, ok := animal.Owner.(*AnimalFieldsOwnerUser)
|
||||
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
||||
// Check that we filled in *both* ID fields:
|
||||
assert.Equal(t, "1", owner.Id)
|
||||
assert.Equal(t, "1", owner.UserFields.Id)
|
||||
assert.Equal(t, "1", owner.UserFields.MoreUserFields.Id)
|
||||
assert.Equal(t, "1", owner.UserFields.LuckyFieldsUser.MoreUserFields.Id)
|
||||
// on UserFields:
|
||||
assert.Equal(t, 17, owner.LuckyNumber)
|
||||
assert.Equal(t, "Black", owner.UserFields.MoreUserFields.Hair.Color)
|
||||
assert.Equal(t, "Black", owner.UserFields.LuckyFieldsUser.MoreUserFields.Hair.Color)
|
||||
owner, ok := animal.Owner.(*AnimalFieldsOwnerUser)
|
||||
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
||||
// Check that we filled in *both* ID fields:
|
||||
assert.Equal(t, "1", owner.Id)
|
||||
assert.Equal(t, "1", owner.UserFields.Id)
|
||||
assert.Equal(t, "1", owner.UserFields.MoreUserFields.Id)
|
||||
assert.Equal(t, "1", owner.UserFields.LuckyFieldsUser.MoreUserFields.Id)
|
||||
// on UserFields:
|
||||
assert.Equal(t, 17, owner.LuckyNumber)
|
||||
assert.Equal(t, "Black", owner.UserFields.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.
|
||||
luckyOwner, ok := animal.Owner.(LuckyFields)
|
||||
require.Truef(t, ok, "got %T, not Lucky", animal.Owner)
|
||||
assert.Equal(t, 17, luckyOwner.GetLuckyNumber())
|
||||
// Lucky-based fields we can also get by casting to the fragment-interface.
|
||||
luckyOwner, ok := animal.Owner.(LuckyFields)
|
||||
require.Truef(t, ok, "got %T, not Lucky", animal.Owner)
|
||||
assert.Equal(t, 17, luckyOwner.GetLuckyNumber())
|
||||
|
||||
assert.Nil(t, resp.Beings[2])
|
||||
assert.Nil(t, resp.Beings[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlatten(t *testing.T) {
|
||||
@@ -629,54 +679,56 @@ func TestFlatten(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
server := server.RunServer()
|
||||
defer server.Close()
|
||||
client := newRoundtripClient(t, server.URL)
|
||||
clients := newRoundtripClients(t, server.URL)
|
||||
|
||||
resp, _, err := queryWithFlatten(ctx, client, []string{"1", "3", "12847394823"})
|
||||
require.NoError(t, err)
|
||||
for _, client := range clients {
|
||||
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:
|
||||
// User{Id: 1, Name: "Yours Truly"},
|
||||
// Animal{Id: 3, Name: "Fido"},
|
||||
// null
|
||||
// We should get the following three beings:
|
||||
// User{Id: 1, Name: "Yours Truly"},
|
||||
// Animal{Id: 3, Name: "Fido"},
|
||||
// null
|
||||
|
||||
// Check fields both via interface and via type-assertion when possible
|
||||
// User has, in total, the fields: __typename id luckyNumber.
|
||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||
// (luckyNumber we need to cast for)
|
||||
// Check fields both via interface and via type-assertion when possible
|
||||
// User has, in total, the fields: __typename id luckyNumber.
|
||||
assert.Equal(t, "User", resp.Beings[0].GetTypename())
|
||||
assert.Equal(t, "1", resp.Beings[0].GetId())
|
||||
// (luckyNumber we need to cast for)
|
||||
|
||||
user, ok := resp.Beings[0].(*QueryFragmentBeingsUser)
|
||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, 17, user.InnerLuckyFieldsUser.LuckyNumber)
|
||||
user, ok := resp.Beings[0].(*QueryFragmentBeingsUser)
|
||||
require.Truef(t, ok, "got %T, not User", resp.Beings[0])
|
||||
assert.Equal(t, "1", user.Id)
|
||||
assert.Equal(t, 17, user.InnerLuckyFieldsUser.LuckyNumber)
|
||||
|
||||
// Animal has, in total, the fields:
|
||||
// __typename
|
||||
// id
|
||||
// owner { id name ... on User { friends { id name } } }
|
||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||
// (owner.* we have to cast for)
|
||||
// Animal has, in total, the fields:
|
||||
// __typename
|
||||
// id
|
||||
// owner { id name ... on User { friends { id name } } }
|
||||
assert.Equal(t, "Animal", resp.Beings[1].GetTypename())
|
||||
assert.Equal(t, "3", resp.Beings[1].GetId())
|
||||
// (owner.* we have to cast for)
|
||||
|
||||
animal, ok := resp.Beings[1].(*QueryFragmentBeingsAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
// on AnimalFields:
|
||||
assert.Equal(t, "1", animal.Owner.GetId())
|
||||
assert.Equal(t, "Yours Truly", animal.Owner.GetName())
|
||||
// (friends.* we have to cast for, again)
|
||||
animal, ok := resp.Beings[1].(*QueryFragmentBeingsAnimal)
|
||||
require.Truef(t, ok, "got %T, not Animal", resp.Beings[1])
|
||||
assert.Equal(t, "3", animal.Id)
|
||||
// on AnimalFields:
|
||||
assert.Equal(t, "1", animal.Owner.GetId())
|
||||
assert.Equal(t, "Yours Truly", animal.Owner.GetName())
|
||||
// (friends.* we have to cast for, again)
|
||||
|
||||
owner, ok := animal.Owner.(*InnerBeingFieldsUser)
|
||||
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
||||
assert.Equal(t, "1", owner.Id)
|
||||
assert.Equal(t, "Yours Truly", owner.Name)
|
||||
assert.Len(t, owner.Friends, 1)
|
||||
assert.Equal(t, "2", owner.Friends[0].Id)
|
||||
assert.Equal(t, "Raven", owner.Friends[0].Name)
|
||||
owner, ok := animal.Owner.(*InnerBeingFieldsUser)
|
||||
require.Truef(t, ok, "got %T, not User", animal.Owner)
|
||||
assert.Equal(t, "1", owner.Id)
|
||||
assert.Equal(t, "Yours Truly", owner.Name)
|
||||
assert.Len(t, owner.Friends, 1)
|
||||
assert.Equal(t, "2", owner.Friends[0].Id)
|
||||
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) {
|
||||
|
||||
@@ -101,10 +101,25 @@ func (c *roundtripClient) MakeRequest(ctx context.Context, req *graphql.Request,
|
||||
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 {
|
||||
transport := &lastResponseTransport{wrapped: http.DefaultTransport}
|
||||
httpClient := &http.Client{Transport: transport}
|
||||
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,
|
||||
t: t,
|
||||
}
|
||||
|
||||
@@ -12,6 +12,10 @@ type Query {
|
||||
fail: Boolean
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createUser(input: NewUser!): User!
|
||||
}
|
||||
|
||||
type User implements Being & Lucky {
|
||||
id: ID!
|
||||
name: String!
|
||||
@@ -21,6 +25,10 @@ type User implements Being & Lucky {
|
||||
friends: [User!]!
|
||||
}
|
||||
|
||||
input NewUser {
|
||||
name: String!
|
||||
}
|
||||
|
||||
type Hair { color: String } # silly name to confuse the name-generator
|
||||
|
||||
type Animal implements Being {
|
||||
|
||||
@@ -35,6 +35,7 @@ type Config struct {
|
||||
}
|
||||
|
||||
type ResolverRoot interface {
|
||||
Mutation() MutationResolver
|
||||
Query() QueryResolver
|
||||
}
|
||||
|
||||
@@ -58,6 +59,10 @@ type ComplexityRoot struct {
|
||||
Color func(childComplexity int) int
|
||||
}
|
||||
|
||||
Mutation struct {
|
||||
CreateUser func(childComplexity int, input NewUser) int
|
||||
}
|
||||
|
||||
Query struct {
|
||||
Being func(childComplexity int, id 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 {
|
||||
Me(ctx context.Context) (*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
|
||||
|
||||
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":
|
||||
if e.complexity.Query.Being == nil {
|
||||
break
|
||||
@@ -316,6 +336,20 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
|
||||
var buf bytes.Buffer
|
||||
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{
|
||||
Data: buf.Bytes(),
|
||||
}
|
||||
@@ -360,6 +394,10 @@ type Query {
|
||||
fail: Boolean
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createUser(input: NewUser!): User!
|
||||
}
|
||||
|
||||
type User implements Being & Lucky {
|
||||
id: ID!
|
||||
name: String!
|
||||
@@ -369,6 +407,10 @@ type User implements Being & Lucky {
|
||||
friends: [User!]!
|
||||
}
|
||||
|
||||
input NewUser {
|
||||
name: String!
|
||||
}
|
||||
|
||||
type Hair { color: String } # silly name to confuse the name-generator
|
||||
|
||||
type Animal implements Being {
|
||||
@@ -402,6 +444,21 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...)
|
||||
|
||||
// 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) {
|
||||
var err error
|
||||
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)
|
||||
}
|
||||
|
||||
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) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@@ -2609,6 +2708,29 @@ func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field gr
|
||||
|
||||
// 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 *****************************
|
||||
|
||||
// region ************************** interface.gotpl ***************************
|
||||
@@ -2780,6 +2902,46 @@ func (ec *executionContext) _Hair(ctx context.Context, sel ast.SelectionSet, obj
|
||||
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"}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
var res Species
|
||||
err := res.UnmarshalGQL(v)
|
||||
@@ -3695,6 +3862,10 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S
|
||||
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 {
|
||||
ret := make(graphql.Array, len(v))
|
||||
var wg sync.WaitGroup
|
||||
|
||||
@@ -34,6 +34,10 @@ type Hair struct {
|
||||
Color *string `json:"color"`
|
||||
}
|
||||
|
||||
type NewUser struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/99designs/gqlgen/graphql/handler"
|
||||
@@ -73,6 +74,24 @@ func beingByID(id string) Being {
|
||||
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) {
|
||||
return userByID("1"), nil
|
||||
}
|
||||
@@ -129,9 +148,16 @@ func (r *queryResolver) Fail(ctx context.Context) (*bool, error) {
|
||||
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 {
|
||||
gqlgenServer := handler.New(NewExecutableSchema(Config{Resolvers: &resolver{}}))
|
||||
gqlgenServer.AddTransport(transport.POST{})
|
||||
gqlgenServer.AddTransport(transport.GET{})
|
||||
gqlgenServer.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
|
||||
graphql.RegisterExtension(ctx, "foobar", "test")
|
||||
return next(ctx)
|
||||
@@ -140,10 +166,15 @@ func RunServer() *httptest.Server {
|
||||
}
|
||||
|
||||
type (
|
||||
resolver struct{}
|
||||
queryResolver struct{}
|
||||
resolver struct{}
|
||||
queryResolver struct{}
|
||||
mutationResolver struct{}
|
||||
)
|
||||
|
||||
func (r *resolver) Mutation() MutationResolver {
|
||||
return &mutationResolver{}
|
||||
}
|
||||
|
||||
func (r *resolver) Query() QueryResolver { return &queryResolver{} }
|
||||
|
||||
//go:generate go run github.com/99designs/gqlgen
|
||||
|
||||
Reference in New Issue
Block a user