Skip to content

Commit 53b6eb1

Browse files
committed
Wrap shurcool Errors in GQLError (#104)
1 parent eee6ff1 commit 53b6eb1

File tree

5 files changed

+96
-13
lines changed

5 files changed

+96
-13
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da
88
github.com/cli/browser v1.1.0
99
github.com/cli/safeexec v1.0.0
10-
github.com/cli/shurcooL-graphql v0.0.2
10+
github.com/cli/shurcooL-graphql v0.0.3
1111
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
1212
github.com/henvic/httpretty v0.0.6
1313
github.com/itchyny/gojq v0.12.8

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ github.com/cli/browser v1.1.0 h1:xOZBfkfY9L9vMBgqb1YwRirGu6QFaQ5dP/vXt5ENSOY=
1010
github.com/cli/browser v1.1.0/go.mod h1:HKMQAt9t12kov91Mn7RfZxyJQQgWgyS/3SZswlZ5iTI=
1111
github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
1212
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
13-
github.com/cli/shurcooL-graphql v0.0.2 h1:rwP5/qQQ2fM0TzkUTwtt6E2LbIYf6R+39cUXTa04NYk=
14-
github.com/cli/shurcooL-graphql v0.0.2/go.mod h1:tlrLmw/n5Q/+4qSvosT+9/W5zc8ZMjnJeYBxSdb4nWA=
13+
github.com/cli/shurcooL-graphql v0.0.3 h1:CtpPxyGDs136/+ZeyAfUKYmcQBjDlq5aqnrDCW5Ghh8=
14+
github.com/cli/shurcooL-graphql v0.0.3/go.mod h1:tlrLmw/n5Q/+4qSvosT+9/W5zc8ZMjnJeYBxSdb4nWA=
1515
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1616
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1717
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

pkg/api/errors.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,14 @@ type GQLError struct {
4545
// GQLErrorItem stores additional information about an error response
4646
// returned from the GitHub GraphQL API.
4747
type GQLErrorItem struct {
48-
Message string
49-
Path []interface{}
50-
Type string
48+
Message string
49+
Locations []struct {
50+
Line int
51+
Column int
52+
}
53+
Path []interface{}
54+
Extensions map[string]interface{}
55+
Type string
5156
}
5257

5358
// Allow GQLError to satisfy error interface.

pkg/api/gql_client.go

+33-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"encoding/json"
7+
"errors"
78
"fmt"
89
"io"
910
"net/http"
@@ -109,7 +110,22 @@ func (c *GQLClient) Do(query string, variables map[string]interface{}, response
109110
// to the GitHub GraphQL schema.
110111
// Provided input will be set as a variable named input.
111112
func (c *GQLClient) MutateWithContext(ctx context.Context, name string, m interface{}, variables map[string]interface{}) error {
112-
return c.client.MutateNamed(ctx, name, m, variables)
113+
err := c.client.MutateNamed(ctx, name, m, variables)
114+
var gqlErrs graphql.Errors
115+
if err != nil && errors.As(err, &gqlErrs) {
116+
items := make([]GQLErrorItem, len(gqlErrs))
117+
for i, e := range gqlErrs {
118+
items[i] = GQLErrorItem{
119+
Message: e.Message,
120+
Locations: e.Locations,
121+
Path: e.Path,
122+
Extensions: e.Extensions,
123+
Type: e.Type,
124+
}
125+
}
126+
err = &GQLError{items}
127+
}
128+
return err
113129
}
114130

115131
// Mutate wraps MutateWithContext using context.Background.
@@ -123,7 +139,22 @@ func (c *GQLClient) Mutate(name string, m interface{}, variables map[string]inte
123139
// The query argument should be a pointer to struct that corresponds
124140
// to the GitHub GraphQL schema.
125141
func (c *GQLClient) QueryWithContext(ctx context.Context, name string, q interface{}, variables map[string]interface{}) error {
126-
return c.client.QueryNamed(ctx, name, q, variables)
142+
err := c.client.QueryNamed(ctx, name, q, variables)
143+
var gqlErrs graphql.Errors
144+
if err != nil && errors.As(err, &gqlErrs) {
145+
items := make([]GQLErrorItem, len(gqlErrs))
146+
for i, e := range gqlErrs {
147+
items[i] = GQLErrorItem{
148+
Message: e.Message,
149+
Locations: e.Locations,
150+
Path: e.Path,
151+
Extensions: e.Extensions,
152+
Type: e.Type,
153+
}
154+
}
155+
err = &GQLError{items}
156+
}
157+
return err
127158
}
128159

129160
// Query wraps QueryWithContext using context.Background.

pkg/api/gql_client_test.go

+52-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package api
22

33
import (
44
"context"
5+
"errors"
56
"net/http"
67
"testing"
78
"time"
@@ -32,7 +33,7 @@ func TestGQLClient(t *testing.T) {
3233
assert.Equal(t, "hubot", res.Viewer.Login)
3334
}
3435

35-
func TestGQLClientError(t *testing.T) {
36+
func TestGQLClientDoError(t *testing.T) {
3637
stubConfig(t, testConfig())
3738
t.Cleanup(gock.Off)
3839

@@ -48,7 +49,56 @@ func TestGQLClientError(t *testing.T) {
4849

4950
res := struct{ Organization struct{ Name string } }{}
5051
err = client.Do("QUERY", nil, &res)
51-
assert.EqualError(t, err, "GraphQL: Could not resolve to an Organization with the login of 'cli'. (organization)")
52+
var gqlErr *GQLError
53+
assert.True(t, errors.As(err, &gqlErr))
54+
assert.EqualError(t, gqlErr, "GraphQL: Could not resolve to an Organization with the login of 'cli'. (organization)")
55+
assert.True(t, gock.IsDone(), printPendingMocks(gock.Pending()))
56+
}
57+
58+
func TestGQLClientQueryError(t *testing.T) {
59+
stubConfig(t, testConfig())
60+
t.Cleanup(gock.Off)
61+
62+
gock.New("https://api.github.com").
63+
Post("/graphql").
64+
MatchHeader("Authorization", "token abc123").
65+
BodyString(`{"query":"query QUERY{organization{name}}"}`).
66+
Reply(200).
67+
JSON(`{"errors":[{"type":"NOT_FOUND","path":["organization"],"message":"Could not resolve to an Organization with the login of 'cli'."}]}`)
68+
69+
client, err := DefaultGQLClient()
70+
assert.NoError(t, err)
71+
72+
var res struct{ Organization struct{ Name string } }
73+
err = client.Query("QUERY", &res, nil)
74+
var gqlErr *GQLError
75+
assert.True(t, errors.As(err, &gqlErr))
76+
assert.EqualError(t, gqlErr, "GraphQL: Could not resolve to an Organization with the login of 'cli'. (organization)")
77+
assert.True(t, gock.IsDone(), printPendingMocks(gock.Pending()))
78+
}
79+
80+
func TestGQLClientMutateError(t *testing.T) {
81+
stubConfig(t, testConfig())
82+
t.Cleanup(gock.Off)
83+
84+
gock.New("https://api.github.com").
85+
Post("/graphql").
86+
MatchHeader("Authorization", "token abc123").
87+
BodyString(`{"query":"mutation MUTATE($input:ID!){updateRepository{repository{name}}}","variables":{"input":"variables"}}`).
88+
Reply(200).
89+
JSON(`{"errors":[{"type":"NOT_FOUND","path":["organization"],"message":"Could not resolve to an Organization with the login of 'cli'."}]}`)
90+
91+
client, err := DefaultGQLClient()
92+
assert.NoError(t, err)
93+
94+
var mutation struct {
95+
UpdateRepository struct{ Repository struct{ Name string } }
96+
}
97+
variables := map[string]interface{}{"input": "variables"}
98+
err = client.Mutate("MUTATE", &mutation, variables)
99+
var gqlErr *GQLError
100+
assert.True(t, errors.As(err, &gqlErr))
101+
assert.EqualError(t, gqlErr, "GraphQL: Could not resolve to an Organization with the login of 'cli'. (organization)")
52102
assert.True(t, gock.IsDone(), printPendingMocks(gock.Pending()))
53103
}
54104

@@ -192,7 +242,6 @@ func TestGQLClientDoWithContext(t *testing.T) {
192242

193243
for _, tt := range tests {
194244
t.Run(tt.name, func(t *testing.T) {
195-
// given
196245
t.Cleanup(gock.Off)
197246
gock.New("https://api.github.com").
198247
Post("/graphql").
@@ -209,11 +258,9 @@ func TestGQLClientDoWithContext(t *testing.T) {
209258
vars := map[string]interface{}{"var": "test"}
210259
res := struct{ Viewer struct{ Login string } }{}
211260

212-
// when
213261
ctx := tt.getCtx()
214262
gotErr := client.DoWithContext(ctx, "QUERY", vars, &res)
215263

216-
// then
217264
assert.True(t, gock.IsDone(), printPendingMocks(gock.Pending()))
218265
assert.EqualError(t, gotErr, tt.wantErrMsg)
219266
})

0 commit comments

Comments
 (0)