Skip to content

Commit 0894364

Browse files
authored
Merge pull request #423 from graphql-go/formatted-error-private-original-error
errors: adds OriginalError support
2 parents 8000299 + 7995f6a commit 0894364

File tree

5 files changed

+653
-371
lines changed

5 files changed

+653
-371
lines changed

abstract_test.go

+19-16
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ func TestResolveTypeOnInterfaceYieldsUsefulError(t *testing.T) {
491491
}
492492
}`
493493

494+
originalError := gqlerrors.NewFormattedError(`Runtime Object type "Human" is not a possible type for "Pet".`)
494495
expected := &graphql.Result{
495496
Data: map[string]interface{}{
496497
"pets": []interface{}{
@@ -505,27 +506,27 @@ func TestResolveTypeOnInterfaceYieldsUsefulError(t *testing.T) {
505506
nil,
506507
},
507508
},
508-
Errors: []gqlerrors.FormattedError{
509-
{
510-
Message: `Runtime Object type "Human" is not a possible type for "Pet".`,
511-
Locations: []location.SourceLocation{
512-
{
513-
Line: 2,
514-
Column: 7,
515-
},
516-
},
517-
Path: []interface{}{
518-
"pets",
519-
2,
509+
Errors: []gqlerrors.FormattedError{gqlerrors.FormatError(gqlerrors.Error{
510+
Message: originalError.Message,
511+
Locations: []location.SourceLocation{
512+
{
513+
Line: 2,
514+
Column: 7,
520515
},
521516
},
522-
},
517+
Path: []interface{}{
518+
"pets",
519+
2,
520+
},
521+
OriginalError: originalError,
522+
})},
523523
}
524524

525525
result := graphql.Do(graphql.Params{
526526
Schema: schema,
527527
RequestString: query,
528528
})
529+
529530
if len(result.Errors) == 0 {
530531
t.Fatalf("wrong result, expected errors: %v, got: %v", len(expected.Errors), len(result.Errors))
531532
}
@@ -618,6 +619,7 @@ func TestResolveTypeOnUnionYieldsUsefulError(t *testing.T) {
618619
}
619620
}`
620621

622+
originalError := gqlerrors.NewFormattedError(`Runtime Object type "Human" is not a possible type for "Pet".`)
621623
expected := &graphql.Result{
622624
Data: map[string]interface{}{
623625
"pets": []interface{}{
@@ -633,8 +635,8 @@ func TestResolveTypeOnUnionYieldsUsefulError(t *testing.T) {
633635
},
634636
},
635637
Errors: []gqlerrors.FormattedError{
636-
{
637-
Message: `Runtime Object type "Human" is not a possible type for "Pet".`,
638+
gqlerrors.FormatError(gqlerrors.Error{
639+
Message: originalError.Message,
638640
Locations: []location.SourceLocation{
639641
{
640642
Line: 2,
@@ -645,7 +647,8 @@ func TestResolveTypeOnUnionYieldsUsefulError(t *testing.T) {
645647
"pets",
646648
2,
647649
},
648-
},
650+
OriginalError: originalError,
651+
}),
649652
},
650653
}
651654

executor_test.go

+100-27
Original file line numberDiff line numberDiff line change
@@ -515,18 +515,19 @@ func TestNullsOutErrorSubtrees(t *testing.T) {
515515
"sync": "sync",
516516
"syncError": nil,
517517
}
518-
expectedErrors := []gqlerrors.FormattedError{
519-
{
520-
Message: "Error getting syncError",
521-
Locations: []location.SourceLocation{
522-
{
523-
Line: 3, Column: 7,
524-
},
525-
},
526-
Path: []interface{}{
527-
"syncError",
518+
originalError := errors.New("Error getting syncError")
519+
expectedErrors := []gqlerrors.FormattedError{gqlerrors.FormatError(gqlerrors.Error{
520+
Message: originalError.Error(),
521+
Locations: []location.SourceLocation{
522+
{
523+
Line: 3, Column: 7,
528524
},
529525
},
526+
Path: []interface{}{
527+
"syncError",
528+
},
529+
OriginalError: originalError,
530+
}),
530531
}
531532

532533
data := map[string]interface{}{
@@ -1296,6 +1297,7 @@ func TestFailsWhenAnIsTypeOfCheckIsNotMet(t *testing.T) {
12961297
},
12971298
}
12981299

1300+
originalError := gqlerrors.NewFormattedError(`Expected value of type "SpecialType" but got: graphql_test.testNotSpecialType.`)
12991301
expected := &graphql.Result{
13001302
Data: map[string]interface{}{
13011303
"specials": []interface{}{
@@ -1305,21 +1307,20 @@ func TestFailsWhenAnIsTypeOfCheckIsNotMet(t *testing.T) {
13051307
nil,
13061308
},
13071309
},
1308-
Errors: []gqlerrors.FormattedError{
1309-
{
1310-
Message: `Expected value of type "SpecialType" but got: graphql_test.testNotSpecialType.`,
1311-
Locations: []location.SourceLocation{
1312-
{
1313-
Line: 1,
1314-
Column: 3,
1315-
},
1316-
},
1317-
Path: []interface{}{
1318-
"specials",
1319-
1,
1310+
Errors: []gqlerrors.FormattedError{gqlerrors.FormatError(gqlerrors.Error{
1311+
Message: originalError.Message,
1312+
Locations: []location.SourceLocation{
1313+
{
1314+
Line: 1,
1315+
Column: 3,
13201316
},
13211317
},
1322-
},
1318+
Path: []interface{}{
1319+
"specials",
1320+
1,
1321+
},
1322+
OriginalError: originalError,
1323+
})},
13231324
}
13241325

13251326
specialType := graphql.NewObject(graphql.ObjectConfig{
@@ -2045,7 +2046,7 @@ func (err extendedError) Extensions() map[string]interface{} {
20452046

20462047
var _ gqlerrors.ExtendedError = &extendedError{}
20472048

2048-
func testErrors(t *testing.T, nameType graphql.Output, extensions map[string]interface{}) *graphql.Result {
2049+
func testErrors(t *testing.T, nameType graphql.Output, extensions map[string]interface{}, formatErrorFn func(err error) error) *graphql.Result {
20492050
type Hero struct {
20502051
Id string `graphql:"id"`
20512052
Name string
@@ -2072,7 +2073,12 @@ func testErrors(t *testing.T, nameType graphql.Output, extensions map[string]int
20722073
if hero.Name != "" {
20732074
return hero.Name, nil
20742075
}
2076+
20752077
err := fmt.Errorf("Name for character with ID %v could not be fetched.", hero.Id)
2078+
if formatErrorFn != nil {
2079+
err = formatErrorFn(err)
2080+
}
2081+
20762082
if extensions != nil {
20772083
return nil, &extendedError{
20782084
error: err,
@@ -2133,7 +2139,7 @@ func testErrors(t *testing.T, nameType graphql.Output, extensions map[string]int
21332139

21342140
// http://facebook.github.io/graphql/June2018/#example-bc485
21352141
func TestQuery_ErrorPath(t *testing.T) {
2136-
result := testErrors(t, graphql.String, nil)
2142+
result := testErrors(t, graphql.String, nil, nil)
21372143

21382144
assertJSON(t, `{
21392145
"errors": [
@@ -2167,7 +2173,7 @@ func TestQuery_ErrorPath(t *testing.T) {
21672173

21682174
// http://facebook.github.io/graphql/June2018/#example-08b62
21692175
func TestQuery_ErrorPathForNonNullField(t *testing.T) {
2170-
result := testErrors(t, graphql.NewNonNull(graphql.String), nil)
2176+
result := testErrors(t, graphql.NewNonNull(graphql.String), nil, nil)
21712177

21722178
assertJSON(t, `{
21732179
"errors": [
@@ -2201,7 +2207,7 @@ func TestQuery_ErrorExtensions(t *testing.T) {
22012207
result := testErrors(t, graphql.NewNonNull(graphql.String), map[string]interface{}{
22022208
"code": "CAN_NOT_FETCH_BY_ID",
22032209
"timestamp": "Fri Feb 9 14:33:09 UTC 2018",
2204-
})
2210+
}, nil)
22052211

22062212
assertJSON(t, `{
22072213
"errors": [
@@ -2232,3 +2238,70 @@ func TestQuery_ErrorExtensions(t *testing.T) {
22322238
}
22332239
}`, result)
22342240
}
2241+
2242+
func TestQuery_OriginalErrorBuiltin(t *testing.T) {
2243+
result := testErrors(t, graphql.String, nil, nil)
2244+
originalError := result.Errors[0].OriginalError()
2245+
switch originalError.(type) {
2246+
case error:
2247+
default:
2248+
t.Fatalf("unexpected error: %v", reflect.TypeOf(originalError))
2249+
}
2250+
}
2251+
2252+
func TestQuery_OriginalErrorExtended(t *testing.T) {
2253+
result := testErrors(t, graphql.String, map[string]interface{}{
2254+
"code": "CAN_NOT_FETCH_BY_ID",
2255+
}, nil)
2256+
originalError := result.Errors[0].OriginalError()
2257+
switch originalError.(type) {
2258+
case *extendedError:
2259+
case extendedError:
2260+
default:
2261+
t.Fatalf("unexpected error: %v", reflect.TypeOf(originalError))
2262+
}
2263+
}
2264+
2265+
type customError struct {
2266+
error
2267+
}
2268+
2269+
func (e customError) Error() string {
2270+
return e.error.Error()
2271+
}
2272+
2273+
func TestQuery_OriginalErrorCustom(t *testing.T) {
2274+
result := testErrors(t, graphql.String, nil, func(err error) error {
2275+
return customError{error: err}
2276+
})
2277+
originalError := result.Errors[0].OriginalError()
2278+
switch originalError.(type) {
2279+
case customError:
2280+
default:
2281+
t.Fatalf("unexpected error: %v", reflect.TypeOf(originalError))
2282+
}
2283+
}
2284+
2285+
func TestQuery_OriginalErrorCustomPtr(t *testing.T) {
2286+
result := testErrors(t, graphql.String, nil, func(err error) error {
2287+
return &customError{error: err}
2288+
})
2289+
originalError := result.Errors[0].OriginalError()
2290+
switch originalError.(type) {
2291+
case *customError:
2292+
default:
2293+
t.Fatalf("unexpected error: %v", reflect.TypeOf(originalError))
2294+
}
2295+
}
2296+
2297+
func TestQuery_OriginalErrorPanic(t *testing.T) {
2298+
result := testErrors(t, graphql.String, nil, func(err error) error {
2299+
panic(errors.New("panic error"))
2300+
})
2301+
originalError := result.Errors[0].OriginalError()
2302+
switch originalError.(type) {
2303+
case error:
2304+
default:
2305+
t.Fatalf("unexpected error: %v", reflect.TypeOf(originalError))
2306+
}
2307+
}

gqlerrors/formatted.go

+13-7
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@ type ExtendedError interface {
1212
}
1313

1414
type FormattedError struct {
15-
Message string `json:"message"`
16-
Locations []location.SourceLocation `json:"locations"`
17-
Path []interface{} `json:"path,omitempty"`
18-
Extensions map[string]interface{} `json:"extensions,omitempty"`
15+
Message string `json:"message"`
16+
Locations []location.SourceLocation `json:"locations"`
17+
Path []interface{} `json:"path,omitempty"`
18+
Extensions map[string]interface{} `json:"extensions,omitempty"`
19+
originalError error
20+
}
21+
22+
func (g FormattedError) OriginalError() error {
23+
return g.originalError
1924
}
2025

2126
func (g FormattedError) Error() string {
@@ -33,9 +38,10 @@ func FormatError(err error) FormattedError {
3338
return err
3439
case *Error:
3540
ret := FormattedError{
36-
Message: err.Error(),
37-
Locations: err.Locations,
38-
Path: err.Path,
41+
Message: err.Error(),
42+
Locations: err.Locations,
43+
Path: err.Path,
44+
originalError: err.OriginalError,
3945
}
4046
if err := err.OriginalError; err != nil {
4147
if extended, ok := err.(ExtendedError); ok {

0 commit comments

Comments
 (0)