1 | import { defaultFieldResolver, isScalarType, getNamedType, Kind, print, buildASTSchema, extendSchema, isSchema, isSpecifiedScalarType, isEnumType, isUnionType, isObjectType, isInterfaceType, GraphQLEnumType, GraphQLScalarType, GraphQLUnionType, GraphQLObjectType, GraphQLInterfaceType } from 'graphql';
|
2 | import { mapSchema, MapperKind, forEachField, getDirectives, parseGraphQLSDL, isDocumentNode, forEachDefaultValue, serializeInputValue, healSchema, parseInputValue, mergeDeep, SchemaDirectiveVisitor, pruneSchema } from '@graphql-tools/utils';
|
3 |
|
4 |
|
5 |
|
6 | function addSchemaLevelResolver(schema, fn) {
|
7 |
|
8 | const fnToRunOnlyOnce = runAtMostOncePerRequest(fn);
|
9 | return mapSchema(schema, {
|
10 | [MapperKind.ROOT_FIELD]: (fieldConfig, _fieldName, typeName, schema) => {
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | const subscription = schema.getSubscriptionType();
|
16 | if (subscription != null && subscription.name === typeName) {
|
17 | return {
|
18 | ...fieldConfig,
|
19 | resolve: wrapResolver(fieldConfig.resolve, fn),
|
20 | };
|
21 | }
|
22 | return {
|
23 | ...fieldConfig,
|
24 | resolve: wrapResolver(fieldConfig.resolve, fnToRunOnlyOnce),
|
25 | };
|
26 | },
|
27 | });
|
28 | }
|
29 |
|
30 | function wrapResolver(innerResolver, outerResolver) {
|
31 | return (obj, args, ctx, info) => resolveMaybePromise(outerResolver(obj, args, ctx, info), root => {
|
32 | if (innerResolver != null) {
|
33 | return innerResolver(root, args, ctx, info);
|
34 | }
|
35 | return defaultFieldResolver(root, args, ctx, info);
|
36 | });
|
37 | }
|
38 | function isPromise(maybePromise) {
|
39 | return maybePromise && typeof maybePromise.then === 'function';
|
40 | }
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | function resolveMaybePromise(maybePromise, fulfillmentCallback) {
|
46 | if (isPromise(maybePromise)) {
|
47 | return maybePromise.then(fulfillmentCallback);
|
48 | }
|
49 | return fulfillmentCallback(maybePromise);
|
50 | }
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 | function runAtMostOncePerRequest(fn) {
|
58 | let value;
|
59 | const randomNumber = Math.random();
|
60 | return (root, args, ctx, info) => {
|
61 | if (!info.operation['__runAtMostOnce']) {
|
62 | info.operation['__runAtMostOnce'] = {};
|
63 | }
|
64 | if (!info.operation['__runAtMostOnce'][randomNumber]) {
|
65 | info.operation['__runAtMostOnce'][randomNumber] = true;
|
66 | value = fn(root, args, ctx, info);
|
67 | }
|
68 | return value;
|
69 | };
|
70 | }
|
71 |
|
72 | function assertResolversPresent(schema, resolverValidationOptions = {}) {
|
73 | const { requireResolversForArgs, requireResolversForNonScalar, requireResolversForAllFields, } = resolverValidationOptions;
|
74 | if (requireResolversForAllFields && (requireResolversForArgs || requireResolversForNonScalar)) {
|
75 | throw new TypeError('requireResolversForAllFields takes precedence over the more specific assertions. ' +
|
76 | 'Please configure either requireResolversForAllFields or requireResolversForArgs / ' +
|
77 | 'requireResolversForNonScalar, but not a combination of them.');
|
78 | }
|
79 | forEachField(schema, (field, typeName, fieldName) => {
|
80 |
|
81 | if (requireResolversForAllFields) {
|
82 | expectResolver('requireResolversForAllFields', requireResolversForAllFields, field, typeName, fieldName);
|
83 | }
|
84 |
|
85 | if (requireResolversForArgs && field.args.length > 0) {
|
86 | expectResolver('requireResolversForArgs', requireResolversForArgs, field, typeName, fieldName);
|
87 | }
|
88 |
|
89 | if (requireResolversForNonScalar !== 'ignore' && !isScalarType(getNamedType(field.type))) {
|
90 | expectResolver('requireResolversForNonScalar', requireResolversForNonScalar, field, typeName, fieldName);
|
91 | }
|
92 | });
|
93 | }
|
94 | function expectResolver(validator, behavior, field, typeName, fieldName) {
|
95 | if (!field.resolve) {
|
96 | const message = `Resolver missing for "${typeName}.${fieldName}".
|
97 | To disable this validator, use:
|
98 | resolverValidationOptions: {
|
99 | ${validator}: 'ignore'
|
100 | }`;
|
101 | if (behavior === 'error') {
|
102 | throw new Error(message);
|
103 | }
|
104 | if (behavior === 'warn') {
|
105 |
|
106 | console.warn(message);
|
107 | }
|
108 | return;
|
109 | }
|
110 | if (typeof field.resolve !== 'function') {
|
111 | throw new Error(`Resolver "${typeName}.${fieldName}" must be a function`);
|
112 | }
|
113 | }
|
114 |
|
115 | function attachDirectiveResolvers(schema, directiveResolvers) {
|
116 | if (typeof directiveResolvers !== 'object') {
|
117 | throw new Error(`Expected directiveResolvers to be of type object, got ${typeof directiveResolvers}`);
|
118 | }
|
119 | if (Array.isArray(directiveResolvers)) {
|
120 | throw new Error('Expected directiveResolvers to be of type object, got Array');
|
121 | }
|
122 | return mapSchema(schema, {
|
123 | [MapperKind.OBJECT_FIELD]: fieldConfig => {
|
124 | const newFieldConfig = { ...fieldConfig };
|
125 | const directives = getDirectives(schema, fieldConfig);
|
126 | Object.keys(directives).forEach(directiveName => {
|
127 | if (directiveResolvers[directiveName]) {
|
128 | const resolver = directiveResolvers[directiveName];
|
129 | const originalResolver = newFieldConfig.resolve != null ? newFieldConfig.resolve : defaultFieldResolver;
|
130 | const directiveArgs = directives[directiveName];
|
131 | newFieldConfig.resolve = (source, originalArgs, context, info) => {
|
132 | return resolver(() => new Promise((resolve, reject) => {
|
133 | const result = originalResolver(source, originalArgs, context, info);
|
134 | if (result instanceof Error) {
|
135 | reject(result);
|
136 | }
|
137 | resolve(result);
|
138 | }), source, directiveArgs, context, info);
|
139 | };
|
140 | }
|
141 | });
|
142 | return newFieldConfig;
|
143 | },
|
144 | });
|
145 | }
|
146 |
|
147 | const isExtensionNode = (def) => def.kind === Kind.OBJECT_TYPE_EXTENSION ||
|
148 | def.kind === Kind.INTERFACE_TYPE_EXTENSION ||
|
149 | def.kind === Kind.INPUT_OBJECT_TYPE_EXTENSION ||
|
150 | def.kind === Kind.UNION_TYPE_EXTENSION ||
|
151 | def.kind === Kind.ENUM_TYPE_EXTENSION ||
|
152 | def.kind === Kind.SCALAR_TYPE_EXTENSION ||
|
153 | def.kind === Kind.SCHEMA_EXTENSION;
|
154 | function filterAndExtractExtensionDefinitions(ast) {
|
155 | const extensionDefs = [];
|
156 | const typesDefs = [];
|
157 | ast.definitions.forEach(def => {
|
158 | if (isExtensionNode(def)) {
|
159 | extensionDefs.push(def);
|
160 | }
|
161 | else {
|
162 | typesDefs.push(def);
|
163 | }
|
164 | });
|
165 | return {
|
166 | typesAst: {
|
167 | ...ast,
|
168 | definitions: typesDefs,
|
169 | },
|
170 | extensionsAst: {
|
171 | ...ast,
|
172 | definitions: extensionDefs,
|
173 | },
|
174 | };
|
175 | }
|
176 | function filterExtensionDefinitions(ast) {
|
177 | const { typesAst } = filterAndExtractExtensionDefinitions(ast);
|
178 | return typesAst;
|
179 | }
|
180 | function extractExtensionDefinitions(ast) {
|
181 | const { extensionsAst } = filterAndExtractExtensionDefinitions(ast);
|
182 | return extensionsAst;
|
183 | }
|
184 |
|
185 | function concatenateTypeDefs(typeDefinitionsAry, calledFunctionRefs = new Set()) {
|
186 | const resolvedTypeDefinitions = new Set();
|
187 | typeDefinitionsAry.forEach((typeDef) => {
|
188 | if (typeof typeDef === 'function') {
|
189 | if (!calledFunctionRefs.has(typeDef)) {
|
190 | calledFunctionRefs.add(typeDef);
|
191 | resolvedTypeDefinitions.add(concatenateTypeDefs(typeDef(), calledFunctionRefs));
|
192 | }
|
193 | }
|
194 | else if (typeof typeDef === 'string') {
|
195 | resolvedTypeDefinitions.add(typeDef.trim());
|
196 | }
|
197 | else if (typeDef.kind !== undefined) {
|
198 | resolvedTypeDefinitions.add(print(typeDef).trim());
|
199 | }
|
200 | else {
|
201 | const type = typeof typeDef;
|
202 | throw new Error(`typeDef array must contain only strings, documents, or functions, got ${type}`);
|
203 | }
|
204 | });
|
205 | return [...resolvedTypeDefinitions].join('\n');
|
206 | }
|
207 |
|
208 | function buildSchemaFromTypeDefinitions(typeDefinitions, parseOptions, noExtensionExtraction) {
|
209 | const document = buildDocumentFromTypeDefinitions(typeDefinitions, parseOptions);
|
210 | if (noExtensionExtraction) {
|
211 | return buildASTSchema(document);
|
212 | }
|
213 | const { typesAst, extensionsAst } = filterAndExtractExtensionDefinitions(document);
|
214 | const backcompatOptions = { commentDescriptions: true };
|
215 | let schema = buildASTSchema(typesAst, backcompatOptions);
|
216 | if (extensionsAst.definitions.length > 0) {
|
217 | schema = extendSchema(schema, extensionsAst, backcompatOptions);
|
218 | }
|
219 | return schema;
|
220 | }
|
221 | function buildDocumentFromTypeDefinitions(typeDefinitions, parseOptions) {
|
222 | let document;
|
223 | if (typeof typeDefinitions === 'string') {
|
224 | document = parseGraphQLSDL('', typeDefinitions, parseOptions).document;
|
225 | }
|
226 | else if (Array.isArray(typeDefinitions)) {
|
227 | document = parseGraphQLSDL('', concatenateTypeDefs(typeDefinitions), parseOptions).document;
|
228 | }
|
229 | else if (isDocumentNode(typeDefinitions)) {
|
230 | document = typeDefinitions;
|
231 | }
|
232 | else {
|
233 | const type = typeof typeDefinitions;
|
234 | throw new Error(`typeDefs must be a string, array or schema AST, got ${type}`);
|
235 | }
|
236 | return document;
|
237 | }
|
238 |
|
239 | function chainResolvers(resolvers) {
|
240 | return (root, args, ctx, info) => resolvers.reduce((prev, curResolver) => {
|
241 | if (curResolver != null) {
|
242 | return curResolver(prev, args, ctx, info);
|
243 | }
|
244 | return defaultFieldResolver(prev, args, ctx, info);
|
245 | }, root);
|
246 | }
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 | function decorateWithLogger(fn, logger, hint) {
|
254 | const resolver = fn != null ? fn : defaultFieldResolver;
|
255 | const logError = (e) => {
|
256 |
|
257 | const newE = new Error();
|
258 | newE.stack = e.stack;
|
259 |
|
260 | if (hint) {
|
261 | newE['originalMessage'] = e.message;
|
262 | newE.message = `Error in resolver ${hint}\n${e.message}`;
|
263 | }
|
264 | logger.log(newE);
|
265 | };
|
266 | return (root, args, ctx, info) => {
|
267 | try {
|
268 | const result = resolver(root, args, ctx, info);
|
269 |
|
270 | if (result && typeof result.then === 'function' && typeof result.catch === 'function') {
|
271 | result.catch((reason) => {
|
272 |
|
273 | const error = reason instanceof Error ? reason : new Error(reason);
|
274 | logError(error);
|
275 |
|
276 | return reason;
|
277 | });
|
278 | }
|
279 | return result;
|
280 | }
|
281 | catch (e) {
|
282 | logError(e);
|
283 |
|
284 | throw e;
|
285 | }
|
286 | };
|
287 | }
|
288 |
|
289 |
|
290 | function checkForResolveTypeResolver(schema, requireResolversForResolveType) {
|
291 | mapSchema(schema, {
|
292 | [MapperKind.ABSTRACT_TYPE]: type => {
|
293 | if (!type.resolveType) {
|
294 | const message = `Type "${type.name}" is missing a "__resolveType" resolver. Pass 'ignore' into ` +
|
295 | '"resolverValidationOptions.requireResolversForResolveType" to disable this error.';
|
296 | if (requireResolversForResolveType === 'error') {
|
297 | throw new Error(message);
|
298 | }
|
299 | if (requireResolversForResolveType === 'warn') {
|
300 |
|
301 | console.warn(message);
|
302 | }
|
303 | }
|
304 | return undefined;
|
305 | },
|
306 | });
|
307 | }
|
308 |
|
309 | function extendResolversFromInterfaces(schema, resolvers) {
|
310 | const typeNames = Object.keys({
|
311 | ...schema.getTypeMap(),
|
312 | ...resolvers,
|
313 | });
|
314 | const extendedResolvers = {};
|
315 | typeNames.forEach(typeName => {
|
316 | const type = schema.getType(typeName);
|
317 | if ('getInterfaces' in type) {
|
318 | const allInterfaceResolvers = type
|
319 | .getInterfaces()
|
320 | .map(iFace => resolvers[iFace.name])
|
321 | .filter(interfaceResolvers => interfaceResolvers != null);
|
322 | extendedResolvers[typeName] = {};
|
323 | allInterfaceResolvers.forEach(interfaceResolvers => {
|
324 | Object.keys(interfaceResolvers).forEach(fieldName => {
|
325 | if (fieldName === '__isTypeOf' || !fieldName.startsWith('__')) {
|
326 | extendedResolvers[typeName][fieldName] = interfaceResolvers[fieldName];
|
327 | }
|
328 | });
|
329 | });
|
330 | const typeResolvers = resolvers[typeName];
|
331 | extendedResolvers[typeName] = {
|
332 | ...extendedResolvers[typeName],
|
333 | ...typeResolvers,
|
334 | };
|
335 | }
|
336 | else {
|
337 | const typeResolvers = resolvers[typeName];
|
338 | if (typeResolvers != null) {
|
339 | extendedResolvers[typeName] = typeResolvers;
|
340 | }
|
341 | }
|
342 | });
|
343 | return extendedResolvers;
|
344 | }
|
345 |
|
346 | function addResolversToSchema(schemaOrOptions, legacyInputResolvers, legacyInputValidationOptions) {
|
347 | const options = isSchema(schemaOrOptions)
|
348 | ? {
|
349 | schema: schemaOrOptions,
|
350 | resolvers: legacyInputResolvers,
|
351 | resolverValidationOptions: legacyInputValidationOptions,
|
352 | }
|
353 | : schemaOrOptions;
|
354 | let { schema, resolvers: inputResolvers, defaultFieldResolver, resolverValidationOptions = {}, inheritResolversFromInterfaces = false, updateResolversInPlace = false, } = options;
|
355 | const { requireResolversToMatchSchema = 'error', requireResolversForResolveType } = resolverValidationOptions;
|
356 | const resolvers = inheritResolversFromInterfaces
|
357 | ? extendResolversFromInterfaces(schema, inputResolvers)
|
358 | : inputResolvers;
|
359 | Object.keys(resolvers).forEach(typeName => {
|
360 | const resolverValue = resolvers[typeName];
|
361 | const resolverType = typeof resolverValue;
|
362 | if (typeName === '__schema') {
|
363 | if (resolverType !== 'function') {
|
364 | throw new Error(`"${typeName}" defined in resolvers, but has invalid value "${resolverValue}". A schema resolver's value must be of type object or function.`);
|
365 | }
|
366 | }
|
367 | else {
|
368 | if (resolverType !== 'object') {
|
369 | throw new Error(`"${typeName}" defined in resolvers, but has invalid value "${resolverValue}". The resolver's value must be of type object.`);
|
370 | }
|
371 | const type = schema.getType(typeName);
|
372 | if (type == null) {
|
373 | if (requireResolversToMatchSchema === 'ignore') {
|
374 | return;
|
375 | }
|
376 | throw new Error(`"${typeName}" defined in resolvers, but not in schema`);
|
377 | }
|
378 | else if (isSpecifiedScalarType(type)) {
|
379 |
|
380 | Object.keys(resolverValue).forEach(fieldName => {
|
381 | if (fieldName.startsWith('__')) {
|
382 | type[fieldName.substring(2)] = resolverValue[fieldName];
|
383 | }
|
384 | else {
|
385 | type[fieldName] = resolverValue[fieldName];
|
386 | }
|
387 | });
|
388 | }
|
389 | else if (isEnumType(type)) {
|
390 | const values = type.getValues();
|
391 | Object.keys(resolverValue).forEach(fieldName => {
|
392 | if (!fieldName.startsWith('__') &&
|
393 | !values.some(value => value.name === fieldName) &&
|
394 | requireResolversToMatchSchema &&
|
395 | requireResolversToMatchSchema !== 'ignore') {
|
396 | throw new Error(`${type.name}.${fieldName} was defined in resolvers, but not present within ${type.name}`);
|
397 | }
|
398 | });
|
399 | }
|
400 | else if (isUnionType(type)) {
|
401 | Object.keys(resolverValue).forEach(fieldName => {
|
402 | if (!fieldName.startsWith('__') &&
|
403 | requireResolversToMatchSchema &&
|
404 | requireResolversToMatchSchema !== 'ignore') {
|
405 | throw new Error(`${type.name}.${fieldName} was defined in resolvers, but ${type.name} is not an object or interface type`);
|
406 | }
|
407 | });
|
408 | }
|
409 | else if (isObjectType(type) || isInterfaceType(type)) {
|
410 | Object.keys(resolverValue).forEach(fieldName => {
|
411 | if (!fieldName.startsWith('__')) {
|
412 | const fields = type.getFields();
|
413 | const field = fields[fieldName];
|
414 | if (field == null && requireResolversToMatchSchema && requireResolversToMatchSchema !== 'ignore') {
|
415 | throw new Error(`${typeName}.${fieldName} defined in resolvers, but not in schema`);
|
416 | }
|
417 | const fieldResolve = resolverValue[fieldName];
|
418 | if (typeof fieldResolve !== 'function' && typeof fieldResolve !== 'object') {
|
419 | throw new Error(`Resolver ${typeName}.${fieldName} must be object or function`);
|
420 | }
|
421 | }
|
422 | });
|
423 | }
|
424 | }
|
425 | });
|
426 | schema = updateResolversInPlace
|
427 | ? addResolversToExistingSchema(schema, resolvers, defaultFieldResolver)
|
428 | : createNewSchemaWithResolvers(schema, resolvers, defaultFieldResolver);
|
429 | if (requireResolversForResolveType || requireResolversForResolveType !== 'ignore') {
|
430 | checkForResolveTypeResolver(schema, requireResolversForResolveType);
|
431 | }
|
432 | return schema;
|
433 | }
|
434 | function addResolversToExistingSchema(schema, resolvers, defaultFieldResolver) {
|
435 | const typeMap = schema.getTypeMap();
|
436 | Object.keys(resolvers).forEach(typeName => {
|
437 | if (typeName !== '__schema') {
|
438 | const type = schema.getType(typeName);
|
439 | const resolverValue = resolvers[typeName];
|
440 | if (isScalarType(type)) {
|
441 | Object.keys(resolverValue).forEach(fieldName => {
|
442 | var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
443 | if (fieldName.startsWith('__')) {
|
444 | type[fieldName.substring(2)] = resolverValue[fieldName];
|
445 | }
|
446 | else if (fieldName === 'astNode' && type.astNode != null) {
|
447 | type.astNode = {
|
448 | ...type.astNode,
|
449 | description: (_c = (_b = (_a = resolverValue) === null || _a === void 0 ? void 0 : _a.astNode) === null || _b === void 0 ? void 0 : _b.description) !== null && _c !== void 0 ? _c : type.astNode.description,
|
450 | directives: ((_d = type.astNode.directives) !== null && _d !== void 0 ? _d : []).concat((_g = (_f = (_e = resolverValue) === null || _e === void 0 ? void 0 : _e.astNode) === null || _f === void 0 ? void 0 : _f.directives) !== null && _g !== void 0 ? _g : []),
|
451 | };
|
452 | }
|
453 | else if (fieldName === 'extensionASTNodes' && type.extensionASTNodes != null) {
|
454 | type.extensionASTNodes = ((_h = []) !== null && _h !== void 0 ? _h : type.extensionASTNodes).concat((_k = (_j = resolverValue) === null || _j === void 0 ? void 0 : _j.extensionASTNodes) !== null && _k !== void 0 ? _k : []);
|
455 | }
|
456 | else if (fieldName === 'extensions' &&
|
457 | type.extensions != null &&
|
458 | resolverValue.extensions != null) {
|
459 | type.extensions = Object.assign({}, type.extensions, resolverValue.extensions);
|
460 | }
|
461 | else {
|
462 | type[fieldName] = resolverValue[fieldName];
|
463 | }
|
464 | });
|
465 | }
|
466 | else if (isEnumType(type)) {
|
467 | const config = type.toConfig();
|
468 | const enumValueConfigMap = config.values;
|
469 | Object.keys(resolverValue).forEach(fieldName => {
|
470 | var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
471 | if (fieldName.startsWith('__')) {
|
472 | config[fieldName.substring(2)] = resolverValue[fieldName];
|
473 | }
|
474 | else if (fieldName === 'astNode' && config.astNode != null) {
|
475 | config.astNode = {
|
476 | ...config.astNode,
|
477 | description: (_c = (_b = (_a = resolverValue) === null || _a === void 0 ? void 0 : _a.astNode) === null || _b === void 0 ? void 0 : _b.description) !== null && _c !== void 0 ? _c : config.astNode.description,
|
478 | directives: ((_d = config.astNode.directives) !== null && _d !== void 0 ? _d : []).concat((_g = (_f = (_e = resolverValue) === null || _e === void 0 ? void 0 : _e.astNode) === null || _f === void 0 ? void 0 : _f.directives) !== null && _g !== void 0 ? _g : []),
|
479 | };
|
480 | }
|
481 | else if (fieldName === 'extensionASTNodes' && config.extensionASTNodes != null) {
|
482 | config.extensionASTNodes = config.extensionASTNodes.concat((_j = (_h = resolverValue) === null || _h === void 0 ? void 0 : _h.extensionASTNodes) !== null && _j !== void 0 ? _j : []);
|
483 | }
|
484 | else if (fieldName === 'extensions' &&
|
485 | type.extensions != null &&
|
486 | resolverValue.extensions != null) {
|
487 | type.extensions = Object.assign({}, type.extensions, resolverValue.extensions);
|
488 | }
|
489 | else if (enumValueConfigMap[fieldName]) {
|
490 | enumValueConfigMap[fieldName].value = resolverValue[fieldName];
|
491 | }
|
492 | });
|
493 | typeMap[typeName] = new GraphQLEnumType(config);
|
494 | }
|
495 | else if (isUnionType(type)) {
|
496 | Object.keys(resolverValue).forEach(fieldName => {
|
497 | if (fieldName.startsWith('__')) {
|
498 | type[fieldName.substring(2)] = resolverValue[fieldName];
|
499 | }
|
500 | });
|
501 | }
|
502 | else if (isObjectType(type) || isInterfaceType(type)) {
|
503 | Object.keys(resolverValue).forEach(fieldName => {
|
504 | if (fieldName.startsWith('__')) {
|
505 |
|
506 | type[fieldName.substring(2)] = resolverValue[fieldName];
|
507 | return;
|
508 | }
|
509 | const fields = type.getFields();
|
510 | const field = fields[fieldName];
|
511 | if (field != null) {
|
512 | const fieldResolve = resolverValue[fieldName];
|
513 | if (typeof fieldResolve === 'function') {
|
514 |
|
515 | field.resolve = fieldResolve;
|
516 | }
|
517 | else {
|
518 | setFieldProperties(field, fieldResolve);
|
519 | }
|
520 | }
|
521 | });
|
522 | }
|
523 | }
|
524 | });
|
525 |
|
526 | forEachDefaultValue(schema, serializeInputValue);
|
527 |
|
528 | healSchema(schema);
|
529 |
|
530 | forEachDefaultValue(schema, parseInputValue);
|
531 | if (defaultFieldResolver != null) {
|
532 | forEachField(schema, field => {
|
533 | if (!field.resolve) {
|
534 | field.resolve = defaultFieldResolver;
|
535 | }
|
536 | });
|
537 | }
|
538 | return schema;
|
539 | }
|
540 | function createNewSchemaWithResolvers(schema, resolvers, defaultFieldResolver) {
|
541 | schema = mapSchema(schema, {
|
542 | [MapperKind.SCALAR_TYPE]: type => {
|
543 | const config = type.toConfig();
|
544 | const resolverValue = resolvers[type.name];
|
545 | if (!isSpecifiedScalarType(type) && resolverValue != null) {
|
546 | Object.keys(resolverValue).forEach(fieldName => {
|
547 | var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
548 | if (fieldName.startsWith('__')) {
|
549 | config[fieldName.substring(2)] = resolverValue[fieldName];
|
550 | }
|
551 | else if (fieldName === 'astNode' && config.astNode != null) {
|
552 | config.astNode = {
|
553 | ...config.astNode,
|
554 | description: (_c = (_b = (_a = resolverValue) === null || _a === void 0 ? void 0 : _a.astNode) === null || _b === void 0 ? void 0 : _b.description) !== null && _c !== void 0 ? _c : config.astNode.description,
|
555 | directives: ((_d = config.astNode.directives) !== null && _d !== void 0 ? _d : []).concat((_g = (_f = (_e = resolverValue) === null || _e === void 0 ? void 0 : _e.astNode) === null || _f === void 0 ? void 0 : _f.directives) !== null && _g !== void 0 ? _g : []),
|
556 | };
|
557 | }
|
558 | else if (fieldName === 'extensionASTNodes' && config.extensionASTNodes != null) {
|
559 | config.extensionASTNodes = config.extensionASTNodes.concat((_j = (_h = resolverValue) === null || _h === void 0 ? void 0 : _h.extensionASTNodes) !== null && _j !== void 0 ? _j : []);
|
560 | }
|
561 | else if (fieldName === 'extensions' &&
|
562 | config.extensions != null &&
|
563 | resolverValue.extensions != null) {
|
564 | config.extensions = Object.assign({}, type.extensions, resolverValue.extensions);
|
565 | }
|
566 | else {
|
567 | config[fieldName] = resolverValue[fieldName];
|
568 | }
|
569 | });
|
570 | return new GraphQLScalarType(config);
|
571 | }
|
572 | },
|
573 | [MapperKind.ENUM_TYPE]: type => {
|
574 | const resolverValue = resolvers[type.name];
|
575 | const config = type.toConfig();
|
576 | const enumValueConfigMap = config.values;
|
577 | if (resolverValue != null) {
|
578 | Object.keys(resolverValue).forEach(fieldName => {
|
579 | var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
580 | if (fieldName.startsWith('__')) {
|
581 | config[fieldName.substring(2)] = resolverValue[fieldName];
|
582 | }
|
583 | else if (fieldName === 'astNode' && config.astNode != null) {
|
584 | config.astNode = {
|
585 | ...config.astNode,
|
586 | description: (_c = (_b = (_a = resolverValue) === null || _a === void 0 ? void 0 : _a.astNode) === null || _b === void 0 ? void 0 : _b.description) !== null && _c !== void 0 ? _c : config.astNode.description,
|
587 | directives: ((_d = config.astNode.directives) !== null && _d !== void 0 ? _d : []).concat((_g = (_f = (_e = resolverValue) === null || _e === void 0 ? void 0 : _e.astNode) === null || _f === void 0 ? void 0 : _f.directives) !== null && _g !== void 0 ? _g : []),
|
588 | };
|
589 | }
|
590 | else if (fieldName === 'extensionASTNodes' && config.extensionASTNodes != null) {
|
591 | config.extensionASTNodes = config.extensionASTNodes.concat((_j = (_h = resolverValue) === null || _h === void 0 ? void 0 : _h.extensionASTNodes) !== null && _j !== void 0 ? _j : []);
|
592 | }
|
593 | else if (fieldName === 'extensions' &&
|
594 | config.extensions != null &&
|
595 | resolverValue.extensions != null) {
|
596 | config.extensions = Object.assign({}, type.extensions, resolverValue.extensions);
|
597 | }
|
598 | else if (enumValueConfigMap[fieldName]) {
|
599 | enumValueConfigMap[fieldName].value = resolverValue[fieldName];
|
600 | }
|
601 | });
|
602 | return new GraphQLEnumType(config);
|
603 | }
|
604 | },
|
605 | [MapperKind.UNION_TYPE]: type => {
|
606 | const resolverValue = resolvers[type.name];
|
607 | if (resolverValue != null) {
|
608 | const config = type.toConfig();
|
609 | Object.keys(resolverValue).forEach(fieldName => {
|
610 | if (fieldName.startsWith('__')) {
|
611 | config[fieldName.substring(2)] = resolverValue[fieldName];
|
612 | }
|
613 | });
|
614 | return new GraphQLUnionType(config);
|
615 | }
|
616 | },
|
617 | [MapperKind.OBJECT_TYPE]: type => {
|
618 | const resolverValue = resolvers[type.name];
|
619 | if (resolverValue != null) {
|
620 | const config = type.toConfig();
|
621 | Object.keys(resolverValue).forEach(fieldName => {
|
622 | if (fieldName.startsWith('__')) {
|
623 | config[fieldName.substring(2)] = resolverValue[fieldName];
|
624 | }
|
625 | });
|
626 | return new GraphQLObjectType(config);
|
627 | }
|
628 | },
|
629 | [MapperKind.INTERFACE_TYPE]: type => {
|
630 | const resolverValue = resolvers[type.name];
|
631 | if (resolverValue != null) {
|
632 | const config = type.toConfig();
|
633 | Object.keys(resolverValue).forEach(fieldName => {
|
634 | if (fieldName.startsWith('__')) {
|
635 | config[fieldName.substring(2)] = resolverValue[fieldName];
|
636 | }
|
637 | });
|
638 | return new GraphQLInterfaceType(config);
|
639 | }
|
640 | },
|
641 | [MapperKind.COMPOSITE_FIELD]: (fieldConfig, fieldName, typeName) => {
|
642 | const resolverValue = resolvers[typeName];
|
643 | if (resolverValue != null) {
|
644 | const fieldResolve = resolverValue[fieldName];
|
645 | if (fieldResolve != null) {
|
646 | const newFieldConfig = { ...fieldConfig };
|
647 | if (typeof fieldResolve === 'function') {
|
648 |
|
649 | newFieldConfig.resolve = fieldResolve;
|
650 | }
|
651 | else {
|
652 | setFieldProperties(newFieldConfig, fieldResolve);
|
653 | }
|
654 | return newFieldConfig;
|
655 | }
|
656 | }
|
657 | },
|
658 | });
|
659 | if (defaultFieldResolver != null) {
|
660 | schema = mapSchema(schema, {
|
661 | [MapperKind.OBJECT_FIELD]: fieldConfig => ({
|
662 | ...fieldConfig,
|
663 | resolve: fieldConfig.resolve != null ? fieldConfig.resolve : defaultFieldResolver,
|
664 | }),
|
665 | });
|
666 | }
|
667 | return schema;
|
668 | }
|
669 | function setFieldProperties(field, propertiesObj) {
|
670 | Object.keys(propertiesObj).forEach(propertyName => {
|
671 | field[propertyName] = propertiesObj[propertyName];
|
672 | });
|
673 | }
|
674 |
|
675 | function addErrorLoggingToSchema(schema, logger) {
|
676 | if (!logger) {
|
677 | throw new Error('Must provide a logger');
|
678 | }
|
679 | if (typeof logger.log !== 'function') {
|
680 | throw new Error('Logger.log must be a function');
|
681 | }
|
682 | return mapSchema(schema, {
|
683 | [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => ({
|
684 | ...fieldConfig,
|
685 | resolve: decorateWithLogger(fieldConfig.resolve, logger, `${typeName}.${fieldName}`),
|
686 | }),
|
687 | });
|
688 | }
|
689 |
|
690 | function decorateToCatchUndefined(fn, hint) {
|
691 | const resolve = fn == null ? defaultFieldResolver : fn;
|
692 | return (root, args, ctx, info) => {
|
693 | const result = resolve(root, args, ctx, info);
|
694 | if (typeof result === 'undefined') {
|
695 | throw new Error(`Resolver for "${hint}" returned undefined`);
|
696 | }
|
697 | return result;
|
698 | };
|
699 | }
|
700 | function addCatchUndefinedToSchema(schema) {
|
701 | return mapSchema(schema, {
|
702 | [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => ({
|
703 | ...fieldConfig,
|
704 | resolve: decorateToCatchUndefined(fieldConfig.resolve, `${typeName}.${fieldName}`),
|
705 | }),
|
706 | });
|
707 | }
|
708 |
|
709 |
|
710 |
|
711 |
|
712 |
|
713 |
|
714 |
|
715 |
|
716 |
|
717 |
|
718 |
|
719 |
|
720 |
|
721 |
|
722 |
|
723 |
|
724 |
|
725 |
|
726 |
|
727 |
|
728 |
|
729 |
|
730 |
|
731 |
|
732 |
|
733 |
|
734 |
|
735 |
|
736 |
|
737 |
|
738 |
|
739 |
|
740 |
|
741 |
|
742 |
|
743 |
|
744 |
|
745 |
|
746 |
|
747 |
|
748 |
|
749 |
|
750 |
|
751 |
|
752 |
|
753 | function makeExecutableSchema({ typeDefs, resolvers = {}, logger, allowUndefinedInResolve = true, resolverValidationOptions = {}, directiveResolvers, schemaDirectives, schemaTransforms: userProvidedSchemaTransforms, parseOptions = {}, inheritResolversFromInterfaces = false, pruningOptions, updateResolversInPlace = false, noExtensionExtraction = false, }) {
|
754 |
|
755 | if (typeof resolverValidationOptions !== 'object') {
|
756 | throw new Error('Expected `resolverValidationOptions` to be an object');
|
757 | }
|
758 | if (!typeDefs) {
|
759 | throw new Error('Must provide typeDefs');
|
760 | }
|
761 |
|
762 | const schemaTransforms = [
|
763 | schema => {
|
764 |
|
765 | const resolverMap = Array.isArray(resolvers) ? resolvers.reduce(mergeDeep, {}) : resolvers;
|
766 | const schemaWithResolvers = addResolversToSchema({
|
767 | schema,
|
768 | resolvers: resolverMap,
|
769 | resolverValidationOptions,
|
770 | inheritResolversFromInterfaces,
|
771 | updateResolversInPlace,
|
772 | });
|
773 | if (Object.keys(resolverValidationOptions).length > 0) {
|
774 | assertResolversPresent(schemaWithResolvers, resolverValidationOptions);
|
775 | }
|
776 | return schemaWithResolvers;
|
777 | },
|
778 | ];
|
779 | if (!allowUndefinedInResolve) {
|
780 | schemaTransforms.push(addCatchUndefinedToSchema);
|
781 | }
|
782 | if (logger != null) {
|
783 | schemaTransforms.push(schema => addErrorLoggingToSchema(schema, logger));
|
784 | }
|
785 | if (typeof resolvers['__schema'] === 'function') {
|
786 |
|
787 |
|
788 | schemaTransforms.push(schema => addSchemaLevelResolver(schema, resolvers['__schema']));
|
789 | }
|
790 | if (userProvidedSchemaTransforms) {
|
791 | schemaTransforms.push(schema => userProvidedSchemaTransforms.reduce((s, schemaTransform) => schemaTransform(s), schema));
|
792 | }
|
793 |
|
794 |
|
795 | if (directiveResolvers != null) {
|
796 | schemaTransforms.push(schema => attachDirectiveResolvers(schema, directiveResolvers));
|
797 | }
|
798 | if (schemaDirectives != null) {
|
799 | schemaTransforms.push(schema => {
|
800 | SchemaDirectiveVisitor.visitSchemaDirectives(schema, schemaDirectives);
|
801 | return schema;
|
802 | });
|
803 | }
|
804 | if (pruningOptions) {
|
805 | schemaTransforms.push(pruneSchema);
|
806 | }
|
807 | const schemaFromTypeDefs = buildSchemaFromTypeDefinitions(typeDefs, parseOptions, noExtensionExtraction);
|
808 | return schemaTransforms.reduce((schema, schemaTransform) => schemaTransform(schema), schemaFromTypeDefs);
|
809 | }
|
810 |
|
811 | export { addCatchUndefinedToSchema, addErrorLoggingToSchema, addResolversToSchema, addSchemaLevelResolver, assertResolversPresent, attachDirectiveResolvers, buildDocumentFromTypeDefinitions, buildSchemaFromTypeDefinitions, chainResolvers, checkForResolveTypeResolver, concatenateTypeDefs, decorateWithLogger, extendResolversFromInterfaces, extractExtensionDefinitions, filterAndExtractExtensionDefinitions, filterExtensionDefinitions, makeExecutableSchema };
|
812 |
|