Skip to content

Commit

Permalink
Merge pull request #423 from graphql-go/formatted-error-private-origi…
Browse files Browse the repository at this point in the history
…nal-error

errors: adds OriginalError support
  • Loading branch information
chris-ramon authored Dec 3, 2018
2 parents 8000299 + 7995f6a commit 0894364
Show file tree
Hide file tree
Showing 5 changed files with 653 additions and 371 deletions.
35 changes: 19 additions & 16 deletions abstract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ func TestResolveTypeOnInterfaceYieldsUsefulError(t *testing.T) {
}
}`

originalError := gqlerrors.NewFormattedError(`Runtime Object type "Human" is not a possible type for "Pet".`)
expected := &graphql.Result{
Data: map[string]interface{}{
"pets": []interface{}{
Expand All @@ -505,27 +506,27 @@ func TestResolveTypeOnInterfaceYieldsUsefulError(t *testing.T) {
nil,
},
},
Errors: []gqlerrors.FormattedError{
{
Message: `Runtime Object type "Human" is not a possible type for "Pet".`,
Locations: []location.SourceLocation{
{
Line: 2,
Column: 7,
},
},
Path: []interface{}{
"pets",
2,
Errors: []gqlerrors.FormattedError{gqlerrors.FormatError(gqlerrors.Error{
Message: originalError.Message,
Locations: []location.SourceLocation{
{
Line: 2,
Column: 7,
},
},
},
Path: []interface{}{
"pets",
2,
},
OriginalError: originalError,
})},
}

result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: query,
})

if len(result.Errors) == 0 {
t.Fatalf("wrong result, expected errors: %v, got: %v", len(expected.Errors), len(result.Errors))
}
Expand Down Expand Up @@ -618,6 +619,7 @@ func TestResolveTypeOnUnionYieldsUsefulError(t *testing.T) {
}
}`

originalError := gqlerrors.NewFormattedError(`Runtime Object type "Human" is not a possible type for "Pet".`)
expected := &graphql.Result{
Data: map[string]interface{}{
"pets": []interface{}{
Expand All @@ -633,8 +635,8 @@ func TestResolveTypeOnUnionYieldsUsefulError(t *testing.T) {
},
},
Errors: []gqlerrors.FormattedError{
{
Message: `Runtime Object type "Human" is not a possible type for "Pet".`,
gqlerrors.FormatError(gqlerrors.Error{
Message: originalError.Message,
Locations: []location.SourceLocation{
{
Line: 2,
Expand All @@ -645,7 +647,8 @@ func TestResolveTypeOnUnionYieldsUsefulError(t *testing.T) {
"pets",
2,
},
},
OriginalError: originalError,
}),
},
}

Expand Down
127 changes: 100 additions & 27 deletions executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,18 +515,19 @@ func TestNullsOutErrorSubtrees(t *testing.T) {
"sync": "sync",
"syncError": nil,
}
expectedErrors := []gqlerrors.FormattedError{
{
Message: "Error getting syncError",
Locations: []location.SourceLocation{
{
Line: 3, Column: 7,
},
},
Path: []interface{}{
"syncError",
originalError := errors.New("Error getting syncError")
expectedErrors := []gqlerrors.FormattedError{gqlerrors.FormatError(gqlerrors.Error{
Message: originalError.Error(),
Locations: []location.SourceLocation{
{
Line: 3, Column: 7,
},
},
Path: []interface{}{
"syncError",
},
OriginalError: originalError,
}),
}

data := map[string]interface{}{
Expand Down Expand Up @@ -1296,6 +1297,7 @@ func TestFailsWhenAnIsTypeOfCheckIsNotMet(t *testing.T) {
},
}

originalError := gqlerrors.NewFormattedError(`Expected value of type "SpecialType" but got: graphql_test.testNotSpecialType.`)
expected := &graphql.Result{
Data: map[string]interface{}{
"specials": []interface{}{
Expand All @@ -1305,21 +1307,20 @@ func TestFailsWhenAnIsTypeOfCheckIsNotMet(t *testing.T) {
nil,
},
},
Errors: []gqlerrors.FormattedError{
{
Message: `Expected value of type "SpecialType" but got: graphql_test.testNotSpecialType.`,
Locations: []location.SourceLocation{
{
Line: 1,
Column: 3,
},
},
Path: []interface{}{
"specials",
1,
Errors: []gqlerrors.FormattedError{gqlerrors.FormatError(gqlerrors.Error{
Message: originalError.Message,
Locations: []location.SourceLocation{
{
Line: 1,
Column: 3,
},
},
},
Path: []interface{}{
"specials",
1,
},
OriginalError: originalError,
})},
}

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

var _ gqlerrors.ExtendedError = &extendedError{}

func testErrors(t *testing.T, nameType graphql.Output, extensions map[string]interface{}) *graphql.Result {
func testErrors(t *testing.T, nameType graphql.Output, extensions map[string]interface{}, formatErrorFn func(err error) error) *graphql.Result {
type Hero struct {
Id string `graphql:"id"`
Name string
Expand All @@ -2072,7 +2073,12 @@ func testErrors(t *testing.T, nameType graphql.Output, extensions map[string]int
if hero.Name != "" {
return hero.Name, nil
}

err := fmt.Errorf("Name for character with ID %v could not be fetched.", hero.Id)
if formatErrorFn != nil {
err = formatErrorFn(err)
}

if extensions != nil {
return nil, &extendedError{
error: err,
Expand Down Expand Up @@ -2133,7 +2139,7 @@ func testErrors(t *testing.T, nameType graphql.Output, extensions map[string]int

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

assertJSON(t, `{
"errors": [
Expand Down Expand Up @@ -2167,7 +2173,7 @@ func TestQuery_ErrorPath(t *testing.T) {

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

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

assertJSON(t, `{
"errors": [
Expand Down Expand Up @@ -2232,3 +2238,70 @@ func TestQuery_ErrorExtensions(t *testing.T) {
}
}`, result)
}

func TestQuery_OriginalErrorBuiltin(t *testing.T) {
result := testErrors(t, graphql.String, nil, nil)
originalError := result.Errors[0].OriginalError()
switch originalError.(type) {
case error:
default:
t.Fatalf("unexpected error: %v", reflect.TypeOf(originalError))
}
}

func TestQuery_OriginalErrorExtended(t *testing.T) {
result := testErrors(t, graphql.String, map[string]interface{}{
"code": "CAN_NOT_FETCH_BY_ID",
}, nil)
originalError := result.Errors[0].OriginalError()
switch originalError.(type) {
case *extendedError:
case extendedError:
default:
t.Fatalf("unexpected error: %v", reflect.TypeOf(originalError))
}
}

type customError struct {
error
}

func (e customError) Error() string {
return e.error.Error()
}

func TestQuery_OriginalErrorCustom(t *testing.T) {
result := testErrors(t, graphql.String, nil, func(err error) error {
return customError{error: err}
})
originalError := result.Errors[0].OriginalError()
switch originalError.(type) {
case customError:
default:
t.Fatalf("unexpected error: %v", reflect.TypeOf(originalError))
}
}

func TestQuery_OriginalErrorCustomPtr(t *testing.T) {
result := testErrors(t, graphql.String, nil, func(err error) error {
return &customError{error: err}
})
originalError := result.Errors[0].OriginalError()
switch originalError.(type) {
case *customError:
default:
t.Fatalf("unexpected error: %v", reflect.TypeOf(originalError))
}
}

func TestQuery_OriginalErrorPanic(t *testing.T) {
result := testErrors(t, graphql.String, nil, func(err error) error {
panic(errors.New("panic error"))
})
originalError := result.Errors[0].OriginalError()
switch originalError.(type) {
case error:
default:
t.Fatalf("unexpected error: %v", reflect.TypeOf(originalError))
}
}
20 changes: 13 additions & 7 deletions gqlerrors/formatted.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ type ExtendedError interface {
}

type FormattedError struct {
Message string `json:"message"`
Locations []location.SourceLocation `json:"locations"`
Path []interface{} `json:"path,omitempty"`
Extensions map[string]interface{} `json:"extensions,omitempty"`
Message string `json:"message"`
Locations []location.SourceLocation `json:"locations"`
Path []interface{} `json:"path,omitempty"`
Extensions map[string]interface{} `json:"extensions,omitempty"`
originalError error
}

func (g FormattedError) OriginalError() error {
return g.originalError
}

func (g FormattedError) Error() string {
Expand All @@ -33,9 +38,10 @@ func FormatError(err error) FormattedError {
return err
case *Error:
ret := FormattedError{
Message: err.Error(),
Locations: err.Locations,
Path: err.Path,
Message: err.Error(),
Locations: err.Locations,
Path: err.Path,
originalError: err.OriginalError,
}
if err := err.OriginalError; err != nil {
if extended, ok := err.(ExtendedError); ok {
Expand Down
Loading

0 comments on commit 0894364

Please sign in to comment.