Skip to content

Commit

Permalink
refactor(gqlclient): improve test suite, allow structs for variables (#4
Browse files Browse the repository at this point in the history
)
  • Loading branch information
steebchen committed Jun 4, 2019
1 parent e921467 commit da2e3f8
Show file tree
Hide file tree
Showing 9 changed files with 344 additions and 68 deletions.
2 changes: 1 addition & 1 deletion .github/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ categories:
labels:
- documentation
- title: 🧰 Maintenance
label:
labels:
- ci
- chore
template: |
Expand Down
41 changes: 20 additions & 21 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@ import (
"net/http"

"github.com/pkg/errors"
)

// Request is the payload for GraphQL queries
type Request struct {
Query string `json:"query"`
Variables map[string]interface{} `json:"variables,omitempty"`
OperationName string `json:"operationName,omitempty"`
}
"github.com/steebchen/gqlclient/structs"
)

// Error is a GraphQL Error
type Error struct {
Expand All @@ -30,20 +25,21 @@ type Response struct {
Extensions map[string]interface{}
}

// MustSend is the same as Send, but panics if an error occurs
func (c *Instance) MustSend(dest interface{}, query string, variables map[string]interface{}) *Response {
data, err := c.Send(dest, query, variables)
// request is the payload for GraphQL queries
type request struct {
Query string `json:"query"`
Variables map[string]interface{} `json:"variables,omitempty"`
OperationName string `json:"operationName,omitempty"`
}

// Send a GraphQL request to struct or map
func (c *Client) Send(dest interface{}, query string, variables interface{}) (*Response, error) {
unboxedVars, err := structs.StructToMap(variables)
if err != nil {
panic(err)
return nil, errors.Wrap(err, "StructsToMap failed")
}

return data
}

// Send a GraphQL request and unmarshal it to dest
func (c *Instance) Send(dest interface{}, query string, variables map[string]interface{}) (*Response, error) {
resp, err := c.Raw(query, variables)
resp, err := c.Raw(query, unboxedVars)
if err != nil {
return nil, err
}
Expand All @@ -53,14 +49,17 @@ func (c *Instance) Send(dest interface{}, query string, variables map[string]int
}

// unpack even if there is an error so we can see partial responses
unpackErr := unpack(resp.Data, dest)
unpackErr := structs.Unpack(resp.Data, dest)

return resp, unpackErr
}

// Raw sends a basic GraphQL request with generic types
func (c *Instance) Raw(query string, variables map[string]interface{}) (*Response, error) {
req := &Request{
// Raw sends a basic GraphQL request without any struct types.
// Parameter `variables` can be either a map or a struct
func (c *Client) Raw(query string, variables map[string]interface{}) (*Response, error) {
var err error

req := &request{
Query: query,
Variables: variables,
}
Expand Down
122 changes: 110 additions & 12 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
"github.com/stretchr/testify/require"
)

func TestClient(t *testing.T) {
h := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
func mockServer(t *testing.T) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
b, err := ioutil.ReadAll(r.Body)

if err != nil {
Expand All @@ -32,20 +32,118 @@ func TestClient(t *testing.T) {
panic(err)
}
}))
}

client := New(h.URL).WithHTTPClient(http.DefaultClient)

var data struct {
ID string
Name string
func TestClient_Send(t *testing.T) {
query := `query GetUser { user(id: $id) { id name } }`
variables := map[string]interface{}{
"id": "1",
}

resp, err := client.Send(&data, `query GetUser { user(id: $id) { id name } }`, map[string]interface{}{
"id": "1",
t.Run("struct dest", func(t *testing.T) {
type structType struct {
ID string
Name string
}
var structDest structType

instance := New(mockServer(t).URL)

_, err := instance.Send(&structDest, query, variables)

require.NoError(t, err)
require.Equal(t, structType{
ID: "1",
Name: "bob",
}, structDest)
})

require.NoError(t, err)
t.Run("map dest", func(t *testing.T) {
var mapDest map[string]interface{}

instance := New(mockServer(t).URL)

_, err := instance.Send(&mapDest, query, variables)

require.NoError(t, err)
require.Equal(t, map[string]interface{}{
"id": "1",
"name": "bob",
}, mapDest)
})
}

func TestClient_Send_Variations(t *testing.T) {
query := `query GetUser { user(id: $id) { id name } }`

require.Equal(t, []Error(nil), resp.Errors)
require.Equal(t, "bob", data.Name)
type args struct {
query string
variables interface{}
}

tests := []struct {
name string
instance *Client
args *args
want *Response
wantErr bool
}{
{
name: "map variables",
args: &args{
query: query,
variables: map[string]interface{}{
"id": "1",
},
},
want: &Response{
Data: map[string]interface{}{
"id": "1",
"name": "bob",
},
},
},
{
name: "struct variables",
args: &args{
query: query,
variables: struct {
ID string `json:"id"`
}{
ID: "1",
},
},
want: &Response{
Data: map[string]interface{}{
"id": "1",
"name": "bob",
},
},
},
{
name: "disallow non-object type",
args: &args{
query: query,
variables: "nope",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
instance := New(mockServer(t).URL)
var dest map[string]interface{}
got, err := instance.Send(&dest, tt.args.query, tt.args.variables)

if !tt.wantErr {
require.NoError(t, err)
}

if tt.wantErr && err == nil {
t.Fatalf("want err but got nil. result: %+v", got)
}

require.Equal(t, tt.want, got)
})
}
}
10 changes: 5 additions & 5 deletions gqlclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import (
"net/http"
)

// Instance is gqlclient struct returned with New()
type Instance struct {
// Client is the GraphQL client which is returned by New()
type Client struct {
url string
http *http.Client
}

// New creates a graphql http
func New(url string) *Instance {
c := &Instance{
func New(url string) *Client {
c := &Client{
url: url,
http: http.DefaultClient,
}
Expand All @@ -22,7 +22,7 @@ func New(url string) *Instance {
}

// WithHTTPClient uses a given http client for all requests
func (c *Instance) WithHTTPClient(client *http.Client) *Instance {
func (c *Client) WithHTTPClient(client *http.Client) *Client {
c.http = client
return c
}
47 changes: 47 additions & 0 deletions gqlclient_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package gqlclient

import (
"net/http"
"testing"

"github.com/stretchr/testify/require"
)

func TestNew(t *testing.T) {
srv := mockServer(t)

client := New(srv.URL)

var data struct {
ID string
Name string
}

resp, err := client.Send(&data, `query GetUser { user(id: $id) { id name } }`, map[string]interface{}{
"id": "1",
})

require.NoError(t, err)

require.Equal(t, []Error(nil), resp.Errors)
require.Equal(t, "bob", data.Name)
}

func TestClient_WithHTTPClient(t *testing.T) {
srv := mockServer(t)

client := New(srv.URL)

client.WithHTTPClient(http.DefaultClient)

var data struct {
ID string
Name string
}

_, err := client.Send(&data, `query GetUser { user(id: $id) { id name } }`, map[string]interface{}{
"id": "1",
})

require.NoError(t, err)
}
22 changes: 0 additions & 22 deletions json.go

This file was deleted.

Loading

0 comments on commit da2e3f8

Please sign in to comment.