UNPKG

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