diff --git a/graphql.go b/graphql.go index 05c29b7..a8ea9c0 100644 --- a/graphql.go +++ b/graphql.go @@ -35,17 +35,17 @@ import ( "context" "encoding/json" "fmt" + "github.com/pkg/errors" "io" "mime/multipart" "net/http" - - "github.com/pkg/errors" + "strings" ) // Client is a client for interacting with a GraphQL API. type Client struct { endpoint string - httpClient *http.Client + httpClient httpDoer useMultipartForm bool // closeReq will close the request body immediately allowing for reuse of client @@ -143,9 +143,9 @@ func (c *Client) runWithJSON(ctx context.Context, req *Request, resp interface{} } return errors.Wrap(err, "decoding response") } + if len(gr.Errors) > 0 { - // return first error - return gr.Errors[0] + return gr.Errors } return nil } @@ -214,9 +214,9 @@ func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp inter } return errors.Wrap(err, "decoding response") } + if len(gr.Errors) > 0 { - // return first error - return gr.Errors[0] + return gr.Errors } return nil } @@ -224,7 +224,7 @@ func (c *Client) runWithPostFields(ctx context.Context, req *Request, resp inter // WithHTTPClient specifies the underlying http.Client to use when // making requests. // NewClient(endpoint, WithHTTPClient(specificHTTPClient)) -func WithHTTPClient(httpclient *http.Client) ClientOption { +func WithHTTPClient(httpclient httpDoer) ClientOption { return func(client *Client) { client.httpClient = httpclient } @@ -249,8 +249,25 @@ func ImmediatelyCloseReqBody() ClientOption { // modify the behaviour of the Client. type ClientOption func(*Client) +type graphErrs []graphErr + +func (e graphErrs) Error() string { + if len(e) == 1 { + return fmt.Sprintf("1 error occurred:\n\t* %s\n\n", e[0]) + } + + points := make([]string, len(e)) + for i, err := range e { + points[i] = fmt.Sprintf("* %s", err) + } + + return fmt.Sprintf( + "%d errors occurred:\n\t%s\n\n", + len(e), strings.Join(points, "\n\t")) +} + type graphErr struct { - Message string + Message string `json:"message"` } func (e graphErr) Error() string { @@ -258,8 +275,8 @@ func (e graphErr) Error() string { } type graphResponse struct { - Data interface{} - Errors []graphErr + Data interface{} `json:"data"` + Errors graphErrs `json:"errors"` } // Request is a GraphQL request. @@ -322,3 +339,7 @@ type File struct { Name string R io.Reader } + +type httpDoer interface { + Do(req *http.Request) (*http.Response, error) +} diff --git a/graphql_json_test.go b/graphql_json_test.go index a973d2d..17800d5 100644 --- a/graphql_json_test.go +++ b/graphql_json_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "net/http" "net/http/httptest" + "strings" "testing" "time" @@ -92,7 +93,8 @@ func TestDoJSONBadRequestErr(t *testing.T) { var responseData map[string]interface{} err := client.Run(ctx, &Request{q: "query {}"}, &responseData) is.Equal(calls, 1) // calls - is.Equal(err.Error(), "graphql: miscellaneous message as to why the the request was bad") + is.Equal(len(err.(graphErrs)), 1) + is.True(strings.Contains(err.Error(), "graphql: miscellaneous message as to why the the request was bad")) } func TestQueryJSON(t *testing.T) { diff --git a/graphql_multipart_test.go b/graphql_multipart_test.go index b52da2c..ce31860 100644 --- a/graphql_multipart_test.go +++ b/graphql_multipart_test.go @@ -114,7 +114,7 @@ func TestDoErr(t *testing.T) { var responseData map[string]interface{} err := client.Run(ctx, &Request{q: "query {}"}, &responseData) is.True(err != nil) - is.Equal(err.Error(), "graphql: Something went wrong") + is.True(strings.Contains(err.Error(), "graphql: Something went wrong")) } func TestDoServerErr(t *testing.T) { @@ -164,7 +164,7 @@ func TestDoBadRequestErr(t *testing.T) { defer cancel() var responseData map[string]interface{} err := client.Run(ctx, &Request{q: "query {}"}, &responseData) - is.Equal(err.Error(), "graphql: miscellaneous message as to why the the request was bad") + is.True(strings.Contains(err.Error(), "graphql: miscellaneous message as to why the the request was bad")) } func TestDoNoResponse(t *testing.T) {