Skip to content

Commit

Permalink
Errors thrown from resolvers have the execution path
Browse files Browse the repository at this point in the history
This path is also passed in the `info` object to resolvers.
This information is useful for ease of debugging and more detailed logging.
  • Loading branch information
Slava committed Jun 1, 2016
1 parent 359ec76 commit a5ef8c7
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 15 deletions.
15 changes: 15 additions & 0 deletions src/__tests__/starWarsIntrospection-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,13 @@ describe('Star Wars Introspection Tests', () => {
kind: 'LIST'
}
},
{
name: 'secretBackstory',
type: {
name: 'String',
kind: 'SCALAR'
}
},
{
name: 'primaryFunction',
type: {
Expand Down Expand Up @@ -284,6 +291,14 @@ describe('Star Wars Introspection Tests', () => {
}
}
},
{
name: 'secretBackstory',
type: {
name: 'String',
kind: 'SCALAR',
ofType: null
}
},
{
name: 'primaryFunction',
type: {
Expand Down
103 changes: 103 additions & 0 deletions src/__tests__/starWarsQuery-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,4 +364,107 @@ describe('Star Wars Query Tests', () => {
expect(result).to.deep.equal({ data: expected });
});
});

describe('Reporting errors raised in resolvers', () => {
it('Correctly reports error on accessing secretBackstory', async () => {
const query = `
query HeroNameQuery {
hero {
name
secretBackstory
}
}
`;
const expected = {
hero: {
name: 'R2-D2',
secretBackstory: null
}
};
const expectedErrors = [ 'secretBackstory is secret.' ];
const result = await graphql(StarWarsSchema, query);
expect(result.data).to.deep.equal(expected);
expect(result.errors.map(e => e.message)).to.deep.equal(expectedErrors);
expect(
result.errors.map(e => e.originalError.executionPath)).to.deep.equal(
[ [ 'hero', 'secretBackstory' ] ]);
});

it('Correctly reports error on accessing secretBackstory in a list', async () => {
const query = `
query HeroNameQuery {
hero {
name
friends {
name
secretBackstory
}
}
}
`;
const expected = {
hero: {
name: 'R2-D2',
friends: [
{
name: 'Luke Skywalker',
secretBackstory: null,
},
{
name: 'Han Solo',
secretBackstory: null,
},
{
name: 'Leia Organa',
secretBackstory: null,
},
]
}
};
const expectedErrors = [
'secretBackstory is secret.',
'secretBackstory is secret.',
'secretBackstory is secret.',
];
const result = await graphql(StarWarsSchema, query);
expect(result.data).to.deep.equal(expected);
expect(result.errors.map(e => e.message)).to.deep.equal(expectedErrors);
expect(
result.errors.map(e => e.originalError.executionPath)
).to.deep.equal(
[
[ 'hero', 'friends', 0, 'secretBackstory' ],
[ 'hero', 'friends', 1, 'secretBackstory' ],
[ 'hero', 'friends', 2, 'secretBackstory' ],
]);
});

it('Correctly reports error on accessing through an alias', async () => {
const query = `
query HeroNameQuery {
mainHero: hero {
name
story: secretBackstory
}
}
`;
const expected = {
mainHero: {
name: 'R2-D2',
story: null,
}
};
const expectedErrors = [
'secretBackstory is secret.',
];
const result = await graphql(StarWarsSchema, query);
expect(result.data).to.deep.equal(expected);
expect(result.errors.map(e => e.message)).to.deep.equal(expectedErrors);
expect(
result.errors.map(e => e.originalError.executionPath)
).to.deep.equal([ [ 'mainHero', 'story' ] ]);
});


});
});
21 changes: 21 additions & 0 deletions src/__tests__/starWarsSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const episodeEnum = new GraphQLEnumType({
* name: String
* friends: [Character]
* appearsIn: [Episode]
* secretBackstory: String
* }
*/
const characterInterface = new GraphQLInterfaceType({
Expand All @@ -125,6 +126,10 @@ const characterInterface = new GraphQLInterfaceType({
type: new GraphQLList(episodeEnum),
description: 'Which movies they appear in.',
},
secretBackstory: {
type: GraphQLString,
description: 'All secrets about their past.',
},
}),
resolveType: character => {
return getHuman(character.id) ? humanType : droidType;
Expand All @@ -140,6 +145,7 @@ const characterInterface = new GraphQLInterfaceType({
* name: String
* friends: [Character]
* appearsIn: [Episode]
* secretBackstory: String
* }
*/
const humanType = new GraphQLObjectType({
Expand Down Expand Up @@ -168,6 +174,13 @@ const humanType = new GraphQLObjectType({
type: GraphQLString,
description: 'The home planet of the human, or null if unknown.',
},
secretBackstory: {
type: GraphQLString,
description: 'Where are they from and how they came to be who they are.',
resolve: () => {
throw new Error('secretBackstory is secret.');
},
},
}),
interfaces: [ characterInterface ]
});
Expand All @@ -181,6 +194,7 @@ const humanType = new GraphQLObjectType({
* name: String
* friends: [Character]
* appearsIn: [Episode]
* secretBackstory: String
* primaryFunction: String
* }
*/
Expand All @@ -206,6 +220,13 @@ const droidType = new GraphQLObjectType({
type: new GraphQLList(episodeEnum),
description: 'Which movies they appear in.',
},
secretBackstory: {
type: GraphQLString,
description: 'Construction date and the name of the designer.',
resolve: () => {
throw new Error('secretBackstory is secret.');
},
},
primaryFunction: {
type: GraphQLString,
description: 'The primary function of the droid.',
Expand Down
28 changes: 28 additions & 0 deletions src/error/PathedError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* @flow */
/**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/


export class PathedError extends Error {
message: string;
stack: string;
executionPath: Array<string | number>;

constructor(
message: string,
stack?: ?string,
executionPath: Array<string | number>
) {
super(message);
this.message = message;
this.executionPath = executionPath;

Object.defineProperty(this, 'stack', { value: stack || message });
}
}
1 change: 1 addition & 0 deletions src/error/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

export { GraphQLError } from './GraphQLError';
export { PathedError } from './PathedError';
export { syntaxError } from './syntaxError';
export { locatedError } from './locatedError';
export { formatError } from './formatError';
3 changes: 2 additions & 1 deletion src/error/locatedError.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

import { GraphQLError } from './GraphQLError';
import { PathedError } from './PathedError';


/**
Expand All @@ -17,7 +18,7 @@ import { GraphQLError } from './GraphQLError';
* document responsible for the original Error.
*/
export function locatedError(
originalError: ?Error,
originalError: ?PathedError,
nodes: Array<any>
): GraphQLError {
const message = originalError ?
Expand Down
Loading

0 comments on commit a5ef8c7

Please sign in to comment.