Skip to content

Commit 8f7112e

Browse files
committed
make error coordinate schema extension opt-in and non-serializable
1 parent bb5409c commit 8f7112e

File tree

5 files changed

+127
-32
lines changed

5 files changed

+127
-32
lines changed

packages/executor/src/execution/execute.ts

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ export interface ExecutionContext<TVariables = any, TContext = any> {
127127
signal?: AbortSignal;
128128
onSignalAbort?(handler: () => void): void;
129129
signalPromise?: Promise<never>;
130+
schemaCoordinateInErrors?: boolean;
130131
}
131132

132133
export interface FormattedExecutionResult<
@@ -247,6 +248,7 @@ export interface ExecutionArgs<TData = any, TVariables = any, TContext = any> {
247248
typeResolver?: Maybe<GraphQLTypeResolver<any, TContext>>;
248249
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, TContext>>;
249250
signal?: AbortSignal;
251+
schemaCoordinateInErrors?: boolean;
250252
}
251253

252254
/**
@@ -419,6 +421,7 @@ export function buildExecutionContext<TData = any, TVariables = any, TContext =
419421
typeResolver,
420422
subscribeFieldResolver,
421423
signal,
424+
schemaCoordinateInErrors,
422425
} = args;
423426

424427
signal?.throwIfAborted();
@@ -531,6 +534,7 @@ export function buildExecutionContext<TData = any, TVariables = any, TContext =
531534
signal,
532535
onSignalAbort,
533536
signalPromise,
537+
schemaCoordinateInErrors,
534538
};
535539
}
536540

@@ -746,14 +750,24 @@ function executeField(
746750
let result: unknown;
747751
for (let rawErrorItem of rawError.errors) {
748752
rawErrorItem = coerceError(rawErrorItem);
749-
const error = locatedError(rawErrorItem, fieldNodes, pathToArray(path), info);
753+
const error = locatedError(
754+
rawErrorItem,
755+
fieldNodes,
756+
pathToArray(path),
757+
exeContext.schemaCoordinateInErrors && info,
758+
);
750759
result = handleFieldError(error, returnType, errors);
751760
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
752761
}
753762
return result;
754763
}
755764
rawError = coerceError(rawError);
756-
const error = locatedError(rawError, fieldNodes, pathToArray(path), info);
765+
const error = locatedError(
766+
rawError,
767+
fieldNodes,
768+
pathToArray(path),
769+
exeContext.schemaCoordinateInErrors && info,
770+
);
757771
const handledError = handleFieldError(error, returnType, errors);
758772
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
759773
return handledError;
@@ -765,14 +779,24 @@ function executeField(
765779
let result: unknown;
766780
for (let rawErrorItem of rawError.errors) {
767781
rawErrorItem = coerceError(rawErrorItem);
768-
const error = locatedError(rawErrorItem, fieldNodes, pathToArray(path), info);
782+
const error = locatedError(
783+
rawErrorItem,
784+
fieldNodes,
785+
pathToArray(path),
786+
exeContext.schemaCoordinateInErrors && info,
787+
);
769788
result = handleFieldError(error, returnType, errors);
770789
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
771790
}
772791
return result;
773792
}
774793
const coercedError = coerceError(rawError);
775-
const error = locatedError(coercedError, fieldNodes, pathToArray(path), info);
794+
const error = locatedError(
795+
coercedError,
796+
fieldNodes,
797+
pathToArray(path),
798+
exeContext.schemaCoordinateInErrors && info,
799+
);
776800
const handledError = handleFieldError(error, returnType, errors);
777801
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
778802
return handledError;
@@ -1041,7 +1065,12 @@ async function completeAsyncIteratorValue(
10411065
}
10421066
} catch (rawError) {
10431067
const coercedError = coerceError(rawError);
1044-
const error = locatedError(coercedError, fieldNodes, pathToArray(itemPath), info);
1068+
const error = locatedError(
1069+
coercedError,
1070+
fieldNodes,
1071+
pathToArray(itemPath),
1072+
exeContext.schemaCoordinateInErrors && info,
1073+
);
10451074
completedResults.push(handleFieldError(error, itemType, errors));
10461075
break;
10471076
}
@@ -1201,7 +1230,12 @@ function completeListItemValue(
12011230
completedResults.push(
12021231
completedItem.then(undefined, rawError => {
12031232
rawError = coerceError(rawError);
1204-
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath), info);
1233+
const error = locatedError(
1234+
rawError,
1235+
fieldNodes,
1236+
pathToArray(itemPath),
1237+
exeContext.schemaCoordinateInErrors && info,
1238+
);
12051239
const handledError = handleFieldError(error, itemType, errors);
12061240
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
12071241
return handledError;
@@ -1214,7 +1248,12 @@ function completeListItemValue(
12141248
completedResults.push(completedItem);
12151249
} catch (rawError) {
12161250
const coercedError = coerceError(rawError);
1217-
const error = locatedError(coercedError, fieldNodes, pathToArray(itemPath), info);
1251+
const error = locatedError(
1252+
coercedError,
1253+
fieldNodes,
1254+
pathToArray(itemPath),
1255+
exeContext.schemaCoordinateInErrors && info,
1256+
);
12181257
const handledError = handleFieldError(error, itemType, errors);
12191258
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
12201259
completedResults.push(handledError);
@@ -1789,13 +1828,23 @@ function executeSubscription(exeContext: ExecutionContext): MaybePromise<AsyncIt
17891828
return result
17901829
.then(result => assertEventStream(result, exeContext.signal, exeContext.onSignalAbort))
17911830
.then(undefined, error => {
1792-
throw locatedError(error, fieldNodes, pathToArray(path), info);
1831+
throw locatedError(
1832+
error,
1833+
fieldNodes,
1834+
pathToArray(path),
1835+
exeContext.schemaCoordinateInErrors && info,
1836+
);
17931837
});
17941838
}
17951839

17961840
return assertEventStream(result, exeContext.signal, exeContext.onSignalAbort);
17971841
} catch (error) {
1798-
throw locatedError(error, fieldNodes, pathToArray(path), info);
1842+
throw locatedError(
1843+
error,
1844+
fieldNodes,
1845+
pathToArray(path),
1846+
exeContext.schemaCoordinateInErrors && info,
1847+
);
17991848
}
18001849
}
18011850

@@ -1921,15 +1970,25 @@ function executeStreamField(
19211970
// to take a second callback for the error case.
19221971
completedItem = completedItem.then(undefined, rawError => {
19231972
rawError = coerceError(rawError);
1924-
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath), info);
1973+
const error = locatedError(
1974+
rawError,
1975+
fieldNodes,
1976+
pathToArray(itemPath),
1977+
exeContext.schemaCoordinateInErrors && info,
1978+
);
19251979
const handledError = handleFieldError(error, itemType, asyncPayloadRecord.errors);
19261980
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
19271981
return handledError;
19281982
});
19291983
}
19301984
} catch (rawError) {
19311985
const coercedError = coerceError(rawError);
1932-
const error = locatedError(coercedError, fieldNodes, pathToArray(itemPath), info);
1986+
const error = locatedError(
1987+
coercedError,
1988+
fieldNodes,
1989+
pathToArray(itemPath),
1990+
exeContext.schemaCoordinateInErrors && info,
1991+
);
19331992
completedItem = handleFieldError(error, itemType, asyncPayloadRecord.errors);
19341993
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
19351994
}
@@ -1977,7 +2036,12 @@ async function executeStreamIteratorItem(
19772036
item = value;
19782037
} catch (rawError) {
19792038
const coercedError = coerceError(rawError);
1980-
const error = locatedError(coercedError, fieldNodes, pathToArray(itemPath), info);
2039+
const error = locatedError(
2040+
coercedError,
2041+
fieldNodes,
2042+
pathToArray(itemPath),
2043+
exeContext.schemaCoordinateInErrors && info,
2044+
);
19812045
const value = handleFieldError(error, itemType, asyncPayloadRecord.errors);
19822046
// don't continue if iterator throws
19832047
return { done: true, value };
@@ -1996,15 +2060,25 @@ async function executeStreamIteratorItem(
19962060

19972061
if (isPromise(completedItem)) {
19982062
completedItem = completedItem.then(undefined, rawError => {
1999-
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath), info);
2063+
const error = locatedError(
2064+
rawError,
2065+
fieldNodes,
2066+
pathToArray(itemPath),
2067+
exeContext.schemaCoordinateInErrors && info,
2068+
);
20002069
const handledError = handleFieldError(error, itemType, asyncPayloadRecord.errors);
20012070
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
20022071
return handledError;
20032072
});
20042073
}
20052074
return { done: false, value: completedItem };
20062075
} catch (rawError) {
2007-
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath), info);
2076+
const error = locatedError(
2077+
rawError,
2078+
fieldNodes,
2079+
pathToArray(itemPath),
2080+
exeContext.schemaCoordinateInErrors && info,
2081+
);
20082082
const value = handleFieldError(error, itemType, asyncPayloadRecord.errors);
20092083
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
20102084
return { done: false, value };

packages/executor/src/execution/locatedError.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.

packages/executor/src/execution/normalizedExecutor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export const executorFromSchema = memoize1(function executorFromSchema(
4949
rootValue: request.rootValue,
5050
contextValue: request.context,
5151
signal: request.signal || request.info?.signal,
52+
schemaCoordinateInErrors: request.schemaCoordinateInErrors,
5253
});
5354
};
5455
});

packages/utils/src/Interfaces.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ export interface ExecutionRequest<
9393
subgraphName?: string;
9494
info?: GraphQLResolveInfo;
9595
signal?: AbortSignal;
96+
/**
97+
* Enable/Disable the addition of field schema coordinate in GraphQL Errors extension
98+
*
99+
* Note: Schema Coordinate are exposed using Symbol.for('schemaCoordinate') so that it's not
100+
* serialized. Exposing schema coordinate can ease the discovery of private schemas.
101+
*/
102+
schemaCoordinateInErrors?: boolean;
96103
}
97104

98105
// graphql-js non-exported typings

packages/utils/src/errors.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { ASTNode, GraphQLError, Source, versionInfo } from 'graphql';
2-
import { GraphQLResolveInfo } from './Interfaces.js';
1+
import { locatedError as _locatedError, ASTNode, GraphQLError, Source, versionInfo } from 'graphql';
32
import { Maybe } from './types.js';
43

54
interface GraphQLErrorOptions {
@@ -61,12 +60,34 @@ export function createGraphQLError(message: string, options?: GraphQLErrorOption
6160
);
6261
}
6362

63+
type SchemaCoordinateInfo = { fieldName: string; parentType: { name: string } };
64+
export const ERROR_EXTENSION_SCHEMA_COORDINATE = Symbol.for('graphql.error.schemaCoordinate');
65+
function addSchemaCoordinateToError(error: GraphQLError, info: SchemaCoordinateInfo): void {
66+
// @ts-expect-error extensions can't be Symbol in official GraphQL Error type
67+
error.extensions[ERROR_EXTENSION_SCHEMA_COORDINATE] = `${info.parentType.name}.${info.fieldName}`;
68+
}
69+
70+
export function locatedError(
71+
rawError: unknown,
72+
nodes: ASTNode | ReadonlyArray<ASTNode> | null | undefined,
73+
path: Maybe<ReadonlyArray<string | number>>,
74+
info: SchemaCoordinateInfo | false | null | undefined,
75+
) {
76+
const error = _locatedError(rawError, nodes, path);
77+
78+
if (info) {
79+
addSchemaCoordinateToError(error, info);
80+
}
81+
82+
return error;
83+
}
84+
6485
export function relocatedError(
6586
originalError: GraphQLError,
6687
path?: ReadonlyArray<string | number>,
67-
info?: Maybe<GraphQLResolveInfo>,
88+
info?: SchemaCoordinateInfo | false | null | undefined,
6889
): GraphQLError {
69-
return createGraphQLError(originalError.message, {
90+
const error = createGraphQLError(originalError.message, {
7091
nodes: originalError.nodes,
7192
source: originalError.source,
7293
positions: originalError.positions,
@@ -79,4 +100,10 @@ export function relocatedError(
79100
}
80101
: originalError.extensions,
81102
});
103+
104+
if (info) {
105+
addSchemaCoordinateToError(error, info);
106+
}
107+
108+
return error;
82109
}

0 commit comments

Comments
 (0)