@@ -5,12 +5,14 @@ import (
55 "context"
66 "encoding/json"
77 "fmt"
8- "github.com/runtimeracer/go-graphql-client/internal/jsonutil "
8+ "io "
99 "io/ioutil"
1010 "net/http"
1111 "strings"
1212
1313 "golang.org/x/net/context/ctxhttp"
14+
15+ "github.com/runtimeracer/go-graphql-client/internal/jsonutil"
1416)
1517
1618// Client is a GraphQL client.
@@ -163,12 +165,8 @@ func (c *Client) do(ctx context.Context, op operationType, v interface{}, variab
163165 body , _ := ioutil .ReadAll (resp .Body )
164166 return fmt .Errorf ("non-200 OK status code: %v body: %q" , resp .Status , body )
165167 }
166- var out struct {
167- Data * json.RawMessage
168- Errors errors
169- //Extensions interface{} // Unused.
170- }
171- err = json .NewDecoder (resp .Body ).Decode (& out )
168+ var out graphQLStdOut
169+ out , err = c .unmarshalGraphQLResult (resp .Body )
172170 if err != nil {
173171 // TODO: Consider including response body in returned error, if deemed helpful.
174172 return err
@@ -186,12 +184,49 @@ func (c *Client) do(ctx context.Context, op operationType, v interface{}, variab
186184 return nil
187185}
188186
187+ func (c * Client ) unmarshalGraphQLResult (responseBody io.Reader ) (graphQLStdOut , error ) {
188+ // Try unmarshal into default format
189+ var output graphQLStdOut
190+ err := json .NewDecoder (responseBody ).Decode (& output )
191+ if err != nil {
192+ // TODO: Consider including response body in returned error, if deemed helpful.
193+ // TODO: Add Warning message somehow that default is not working
194+ var extFormat graphQLExtOut
195+ err := json .NewDecoder (responseBody ).Decode (& extFormat )
196+ if err != nil {
197+ // Output too weird or query error
198+ return output , err
199+ }
200+
201+ // Convert Ext to default to meet criteria
202+ output = graphQLStdOut {
203+ Data : extFormat .Data ,
204+ Errors : extFormat .Errors .ConvertToStandard (),
205+ Extensions : extFormat .Extensions ,
206+ }
207+ }
208+ return output , nil
209+ }
210+
211+ type graphQLStdOut struct {
212+ Data * json.RawMessage
213+ Errors errors
214+ Extensions interface {}
215+ }
216+
217+ type graphQLExtOut struct {
218+ Data * json.RawMessage
219+ Errors errorsExt
220+ Extensions interface {}
221+ }
222+
189223// errors represents the "errors" array in a response from a GraphQL server.
190224// If returned via error interface, the slice is expected to contain at least 1 element.
191225//
192226// Specification: https://facebook.github.io/graphql/#sec-Errors.
193- type errors []struct {
194- Message []interface {}
227+ type errors []errorStruct
228+ type errorStruct struct {
229+ Message string
195230 Locations []struct {
196231 Line int
197232 Column int
@@ -200,13 +235,54 @@ type errors []struct {
200235
201236// Error implements error interface.
202237func (e errors ) Error () string {
203- var stringOutput = make ([]string , 0 )
238+ if len (e ) == 0 {
239+ return ""
240+ }
241+ return e [0 ].Message
242+ }
243+
244+ // errorsExt represents the "errors" array in a response from a GraphQL server.
245+ // If returned via error interface, the slice is expected to contain at least 1 element.
246+ // The "Ext" variant of this struct is able to handle non-standard implementations of the error message
247+ type errorsExt []errorsExtStruct
248+ type errorsExtStruct struct {
249+ Message []interface {}
250+ Locations []struct {
251+ Line int
252+ Column int
253+ }
254+ }
255+
256+ // Error implements error interface.
257+ func (e errorsExt ) Error () string {
258+ if len (e ) == 0 {
259+ return ""
260+ }
261+
262+ var stringOutput = make ([]string , len (e [0 ].Message ))
204263 for i := range e [0 ].Message {
205264 stringOutput [i ] = fmt .Sprintf ("%v" , e [0 ].Message [i ])
206265 }
207266 return strings .Join (stringOutput , ";" )
208267}
209268
269+ // ConvertToStandard translates extended error structs into structs matching the GraphQL Standard
270+ func (e errorsExt ) ConvertToStandard () errors {
271+ if len (e ) == 0 {
272+ return nil
273+ }
274+
275+ standardError := make (errors , len (e ))
276+ for i := range e [0 ].Message {
277+ standardError [i ] = errorStruct {
278+ Message : fmt .Sprintf ("%v" , e [0 ].Message [i ]),
279+ Locations : e [0 ].Locations ,
280+ }
281+ }
282+
283+ return standardError
284+ }
285+
210286type operationType uint8
211287
212288const (
0 commit comments