/
checkResultAndHandleErrors.ts
115 lines (98 loc) · 3.91 KB
/
checkResultAndHandleErrors.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { GraphQLResolveInfo, GraphQLOutputType, GraphQLError, responsePathAsArray, locatedError } from 'graphql';
import { AggregateError, getResponseKeyFromInfo, ExecutionResult, relocatedError } from '@graphql-tools/utils';
import { DelegationContext } from './types';
import { resolveExternalValue } from './resolveExternalValue';
export function checkResultAndHandleErrors<TContext extends Record<string, any>>(
result: ExecutionResult,
delegationContext: DelegationContext<TContext>
): any {
const {
context,
info,
fieldName: responseKey = getResponseKey(info),
subschema,
returnType = getReturnType(info),
skipTypeMerging,
onLocatedError,
} = delegationContext;
const { data, unpathedErrors } = mergeDataAndErrors(
result.data == null ? undefined : result.data[responseKey],
result.errors == null ? [] : result.errors,
info != null && info.path ? responsePathAsArray(info.path) : undefined,
onLocatedError
);
return resolveExternalValue(data, unpathedErrors, subschema, context, info, returnType, skipTypeMerging);
}
export function mergeDataAndErrors(
data: any,
errors: ReadonlyArray<GraphQLError>,
path: Array<string | number> | undefined,
onLocatedError?: (originalError: GraphQLError) => GraphQLError,
index = 1
): { data: any; unpathedErrors: Array<GraphQLError> } {
if (data == null) {
if (!errors.length) {
return { data: null, unpathedErrors: [] };
}
if (errors.length === 1) {
const error = onLocatedError ? onLocatedError(errors[0]) : errors[0];
const newPath =
path === undefined ? error.path : error.path === undefined ? path : path.concat(error.path.slice(1));
return { data: relocatedError(errors[0], newPath), unpathedErrors: [] };
}
// We cast path as any for GraphQL.js 14 compat
// locatedError path argument must be defined, but it is just forwarded to a constructor that allows a undefined value
// https://github.com/graphql/graphql-js/blob/b4bff0ba9c15c9d7245dd68556e754c41f263289/src/error/locatedError.js#L25
// https://github.com/graphql/graphql-js/blob/b4bff0ba9c15c9d7245dd68556e754c41f263289/src/error/GraphQLError.js#L19
const combinedError = new AggregateError(errors, errors.map(error => error.message).join(', \n'));
const newError = locatedError(combinedError, undefined as any, path as any);
return { data: newError, unpathedErrors: [] };
}
if (!errors.length) {
return { data, unpathedErrors: [] };
}
const unpathedErrors: Array<GraphQLError> = [];
const errorMap = new Map<string | number, Array<GraphQLError>>();
for (const error of errors) {
const pathSegment = error.path?.[index];
if (pathSegment != null) {
let pathSegmentErrors = errorMap.get(pathSegment);
if (pathSegmentErrors === undefined) {
pathSegmentErrors = [error];
errorMap.set(pathSegment, pathSegmentErrors);
} else {
pathSegmentErrors.push(error);
}
} else {
unpathedErrors.push(error);
}
}
for (const [pathSegment, pathSegmentErrors] of errorMap) {
if (data[pathSegment] !== undefined) {
const { data: newData, unpathedErrors: newErrors } = mergeDataAndErrors(
data[pathSegment],
pathSegmentErrors,
path,
onLocatedError,
index + 1
);
data[pathSegment] = newData;
unpathedErrors.push(...newErrors);
} else {
unpathedErrors.push(...pathSegmentErrors);
}
}
return { data, unpathedErrors };
}
function getResponseKey(info: GraphQLResolveInfo | undefined): string {
if (info == null) {
throw new Error(`Data cannot be extracted from result without an explicit key or source schema.`);
}
return getResponseKeyFromInfo(info);
}
function getReturnType(info: GraphQLResolveInfo | undefined): GraphQLOutputType {
if (info == null) {
throw new Error(`Return type cannot be inferred without a source schema.`);
}
return info.returnType;
}