From 283c797d2d89be823cd82573b3c2497872246a4f Mon Sep 17 00:00:00 2001 From: Ivan Goncharov Date: Fri, 22 Oct 2021 17:40:30 +0300 Subject: [PATCH 1/2] Change type of error extensions from anonymous Record to named interfaces Backport of #3315 --- src/error/GraphQLError.d.ts | 15 ++++++++++++++- src/error/index.d.ts | 6 +++++- src/index.d.ts | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/error/GraphQLError.d.ts b/src/error/GraphQLError.d.ts index 99001fd26f..5357db7d0d 100644 --- a/src/error/GraphQLError.d.ts +++ b/src/error/GraphQLError.d.ts @@ -4,6 +4,19 @@ import { ASTNode } from '../language/ast'; import { Source } from '../language/source'; import { SourceLocation } from '../language/location'; +/** + * Custom extensions + * + * @remarks + * Use a unique identifier name for your extension, for example the name of + * your library or project. Do not use a shortened identifier as this increases + * the risk of conflicts. We recommend you add at most one extension field, + * an object which can contain all the values you need. + */ +export interface GraphQLErrorExtensions { + [attributeName: string]: any; +} + /** * A GraphQLError describes an Error found during the parse, validate, or * execute phases of performing a GraphQL operation. In addition to a message @@ -18,7 +31,7 @@ export class GraphQLError extends Error { positions?: Maybe>, path?: Maybe>, originalError?: Maybe, - extensions?: Maybe<{ [key: string]: any }>, + extensions?: Maybe<{ [key: string]: GraphQLErrorExtensions }>, ); /** diff --git a/src/error/index.d.ts b/src/error/index.d.ts index 9373b7167d..38f7b7e3b6 100644 --- a/src/error/index.d.ts +++ b/src/error/index.d.ts @@ -1,4 +1,8 @@ -export { GraphQLError, printError } from './GraphQLError'; +export { + GraphQLError, + GraphQLErrorExtensions, + printError, +} from './GraphQLError'; export { syntaxError } from './syntaxError'; export { locatedError } from './locatedError'; export { formatError, GraphQLFormattedError } from './formatError'; diff --git a/src/index.d.ts b/src/index.d.ts index 104436ddce..13a465b4e1 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -364,6 +364,7 @@ export { printError, formatError, GraphQLFormattedError, + GraphQLErrorExtensions, } from './error/index'; // Utilities for operating on GraphQL type schema and parsed sources. From 033aadbc6080b4786c87a443d7104e3b0681e787 Mon Sep 17 00:00:00 2001 From: Ivan Goncharov Date: Tue, 26 Oct 2021 13:44:25 +0300 Subject: [PATCH 2/2] GraphQLError: fix empty `locations` if error got nodes without locations Backport of #3325 --- src/error/GraphQLError.js | 68 +++++++++------------ src/error/__tests__/GraphQLError-test.js | 12 ++++ src/validation/__tests__/validation-test.js | 1 - 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/src/error/GraphQLError.js b/src/error/GraphQLError.js index 3040787939..c0e98cedad 100644 --- a/src/error/GraphQLError.js +++ b/src/error/GraphQLError.js @@ -87,51 +87,33 @@ export class GraphQLError extends Error { super(message); // Compute list of blame nodes. - const _nodes = Array.isArray(nodes) - ? nodes.length !== 0 - ? nodes - : undefined - : nodes - ? [nodes] - : undefined; + const _nodes = undefinedIfEmpty( + Array.isArray(nodes) ? nodes : nodes ? [nodes] : undefined, + ); + + let nodeLocations = []; + for (const { loc } of _nodes ?? []) { + if (loc != null) { + nodeLocations.push(loc); + } + } + nodeLocations = undefinedIfEmpty(nodeLocations); // Compute locations in the source for the given nodes/positions. - let _source = source; - if (!_source && _nodes) { - _source = _nodes[0].loc?.source; - } + const _source = source ?? nodeLocations?.[0].source; - let _positions = positions; - if (!_positions && _nodes) { - _positions = _nodes.reduce((list, node) => { - if (node.loc) { - list.push(node.loc.start); - } - return list; - }, []); - } - if (_positions && _positions.length === 0) { - _positions = undefined; - } + const _positions = positions ?? nodeLocations?.map((loc) => loc.start); - let _locations; - if (positions && source) { - _locations = positions.map((pos) => getLocation(source, pos)); - } else if (_nodes) { - _locations = _nodes.reduce((list, node) => { - if (node.loc) { - list.push(getLocation(node.loc.source, node.loc.start)); - } - return list; - }, []); - } + const _locations = + positions && source + ? positions.map((pos) => getLocation(source, pos)) + : nodeLocations?.map((loc) => getLocation(loc.source, loc.start)); - let _extensions = extensions; - if (_extensions == null && originalError != null) { - const originalExtensions = originalError.extensions; - if (isObjectLike(originalExtensions)) { - _extensions = originalExtensions; - } + let _extensions = extensions ?? undefined; + + const originalExtensions = originalError?.extensions; + if (isObjectLike(originalExtensions)) { + _extensions = originalExtensions; } Object.defineProperties((this: any), { @@ -218,6 +200,12 @@ export class GraphQLError extends Error { } } +function undefinedIfEmpty( + array: $ReadOnlyArray | void, +): $ReadOnlyArray | void { + return array === undefined || array.length === 0 ? undefined : array; +} + /** * Prints a GraphQLError to a string, representing useful location information * about the error's position in the source. diff --git a/src/error/__tests__/GraphQLError-test.js b/src/error/__tests__/GraphQLError-test.js index b72d31b174..ef32e7450e 100644 --- a/src/error/__tests__/GraphQLError-test.js +++ b/src/error/__tests__/GraphQLError-test.js @@ -96,6 +96,18 @@ describe('GraphQLError', () => { }); }); + it('converts node without location to undefined source, positions and locations', () => { + const documentNode = parse('{ foo }', { noLocation: true }); + + const e = new GraphQLError('msg', documentNode); + expect(e).to.deep.include({ + nodes: [documentNode], + source: undefined, + positions: undefined, + locations: undefined, + }); + }); + it('converts source and positions to locations', () => { const e = new GraphQLError('msg', null, source, [6]); expect(e).to.have.property('source', source); diff --git a/src/validation/__tests__/validation-test.js b/src/validation/__tests__/validation-test.js index b1113d2d01..1186f5898b 100644 --- a/src/validation/__tests__/validation-test.js +++ b/src/validation/__tests__/validation-test.js @@ -136,7 +136,6 @@ describe('Validate: Limit maximum number of validation errors', () => { function invalidFieldError(fieldName: string) { return { message: `Cannot query field "${fieldName}" on type "QueryRoot".`, - locations: [], }; }