| import { devAssert } from '../jsutils/devAssert.mjs'; |
| import { inspect } from '../jsutils/inspect.mjs'; |
| import { invariant } from '../jsutils/invariant.mjs'; |
| import { isIterableObject } from '../jsutils/isIterableObject.mjs'; |
| import { isObjectLike } from '../jsutils/isObjectLike.mjs'; |
| import { isPromise } from '../jsutils/isPromise.mjs'; |
| import { memoize3 } from '../jsutils/memoize3.mjs'; |
| import { addPath, pathToArray } from '../jsutils/Path.mjs'; |
| import { promiseForObject } from '../jsutils/promiseForObject.mjs'; |
| import { promiseReduce } from '../jsutils/promiseReduce.mjs'; |
| import { GraphQLError } from '../error/GraphQLError.mjs'; |
| import { locatedError } from '../error/locatedError.mjs'; |
| import { OperationTypeNode } from '../language/ast.mjs'; |
| import { Kind } from '../language/kinds.mjs'; |
| import { |
| isAbstractType, |
| isLeafType, |
| isListType, |
| isNonNullType, |
| isObjectType, |
| } from '../type/definition.mjs'; |
| import { |
| SchemaMetaFieldDef, |
| TypeMetaFieldDef, |
| TypeNameMetaFieldDef, |
| } from '../type/introspection.mjs'; |
| import { assertValidSchema } from '../type/validate.mjs'; |
| import { |
| collectFields, |
| collectSubfields as _collectSubfields, |
| } from './collectFields.mjs'; |
| import { getArgumentValues, getVariableValues } from './values.mjs'; |
| /** |
| * A memoized collection of relevant subfields with regard to the return |
| * type. Memoizing ensures the subfields are not repeatedly calculated, which |
| * saves overhead when resolving lists of values. |
| */ |
|
|
| const collectSubfields = memoize3((exeContext, returnType, fieldNodes) => |
| _collectSubfields( |
| exeContext.schema, |
| exeContext.fragments, |
| exeContext.variableValues, |
| returnType, |
| fieldNodes, |
| ), |
| ); |
| /** |
| * Terminology |
| * |
| * "Definitions" are the generic name for top-level statements in the document. |
| * Examples of this include: |
| * 1) Operations (such as a query) |
| * 2) Fragments |
| * |
| * "Operations" are a generic name for requests in the document. |
| * Examples of this include: |
| * 1) query, |
| * 2) mutation |
| * |
| * "Selections" are the definitions that can appear legally and at |
| * single level of the query. These include: |
| * 1) field references e.g `a` |
| * 2) fragment "spreads" e.g. `...c` |
| * 3) inline fragment "spreads" e.g. `...on Type { a }` |
| */ |
|
|
| /** |
| * Data that must be available at all points during query execution. |
| * |
| * Namely, schema of the type system that is currently executing, |
| * and the fragments defined in the query document |
| */ |
|
|
| /** |
| * Implements the "Executing requests" section of the GraphQL specification. |
| * |
| * Returns either a synchronous ExecutionResult (if all encountered resolvers |
| * are synchronous), or a Promise of an ExecutionResult that will eventually be |
| * resolved and never rejected. |
| * |
| * If the arguments to this function do not result in a legal execution context, |
| * a GraphQLError will be thrown immediately explaining the invalid input. |
| */ |
| export function execute(args) { |
| // Temporary for v15 to v16 migration. Remove in v17 |
| arguments.length < 2 || |
| devAssert( |
| false, |
| 'graphql@16 dropped long-deprecated support for positional arguments, please pass an object instead.', |
| ); |
| const { schema, document, variableValues, rootValue } = args; // If arguments are missing or incorrect, throw an error. |
|
|
| assertValidExecutionArguments(schema, document, variableValues); // If a valid execution context cannot be created due to incorrect arguments, |
| // a "Response" with only errors is returned. |
|
|
| const exeContext = buildExecutionContext(args); // Return early errors if execution context failed. |
|
|
| if (!('schema' in exeContext)) { |
| return { |
| errors: exeContext, |
| }; |
| } // Return a Promise that will eventually resolve to the data described by |
| // The "Response" section of the GraphQL specification. |
| // |
| // If errors are encountered while executing a GraphQL field, only that |
| // field and its descendants will be omitted, and sibling fields will still |
| // be executed. An execution which encounters errors will still result in a |
| // resolved Promise. |
| // |
| // Errors from sub-fields of a NonNull type may propagate to the top level, |
| // at which point we still log the error and null the parent field, which |
| // in this case is the entire response. |
|
|
| try { |
| const { operation } = exeContext; |
| const result = executeOperation(exeContext, operation, rootValue); |
|
|
| if (isPromise(result)) { |
| return result.then( |
| (data) => buildResponse(data, exeContext.errors), |
| (error) => { |
| exeContext.errors.push(error); |
| return buildResponse(null, exeContext.errors); |
| }, |
| ); |
| } |
|
|
| return buildResponse(result, exeContext.errors); |
| } catch (error) { |
| exeContext.errors.push(error); |
| return buildResponse(null, exeContext.errors); |
| } |
| } |
| /** |
| * Also implements the "Executing requests" section of the GraphQL specification. |
| * However, it guarantees to complete synchronously (or throw an error) assuming |
| * that all field resolvers are also synchronous. |
| */ |
|
|
| export function executeSync(args) { |
| const result = execute(args); // Assert that the execution was synchronous. |
|
|
| if (isPromise(result)) { |
| throw new Error('GraphQL execution failed to complete synchronously.'); |
| } |
|
|
| return result; |
| } |
| /** |
| * Given a completed execution context and data, build the `{ errors, data }` |
| * response defined by the "Response" section of the GraphQL specification. |
| */ |
|
|
| function buildResponse(data, errors) { |
| return errors.length === 0 |
| ? { |
| data, |
| } |
| : { |
| errors, |
| data, |
| }; |
| } |
| /** |
| * Essential assertions before executing to provide developer feedback for |
| * improper use of the GraphQL library. |
| * |
| * @internal |
| */ |
|
|
| export function assertValidExecutionArguments( |
| schema, |
| document, |
| rawVariableValues, |
| ) { |
| document || devAssert(false, 'Must provide document.'); // If the schema used for execution is invalid, throw an error. |
|
|
| assertValidSchema(schema); // Variables, if provided, must be an object. |
|
|
| rawVariableValues == null || |
| isObjectLike(rawVariableValues) || |
| devAssert( |
| false, |
| 'Variables must be provided as an Object where each property is a variable value. Perhaps look to see if an unparsed JSON string was provided.', |
| ); |
| } |
| /** |
| * Constructs a ExecutionContext object from the arguments passed to |
| * execute, which we will pass throughout the other execution methods. |
| * |
| * Throws a GraphQLError if a valid execution context cannot be created. |
| * |
| * @internal |
| */ |
|
|
| export function buildExecutionContext(args) { |
| var _definition$name, _operation$variableDe; |
|
|
| const { |
| schema, |
| document, |
| rootValue, |
| contextValue, |
| variableValues: rawVariableValues, |
| operationName, |
| fieldResolver, |
| typeResolver, |
| subscribeFieldResolver, |
| } = args; |
| let operation; |
| const fragments = Object.create(null); |
|
|
| for (const definition of document.definitions) { |
| switch (definition.kind) { |
| case Kind.OPERATION_DEFINITION: |
| if (operationName == null) { |
| if (operation !== undefined) { |
| return [ |
| new GraphQLError( |
| 'Must provide operation name if query contains multiple operations.', |
| ), |
| ]; |
| } |
|
|
| operation = definition; |
| } else if ( |
| ((_definition$name = definition.name) === null || |
| _definition$name === void 0 |
| ? void 0 |
| : _definition$name.value) === operationName |
| ) { |
| operation = definition; |
| } |
|
|
| break; |
|
|
| case Kind.FRAGMENT_DEFINITION: |
| fragments[definition.name.value] = definition; |
| break; |
|
|
| default: // ignore non-executable definitions |
| } |
| } |
|
|
| if (!operation) { |
| if (operationName != null) { |
| return [new GraphQLError(`Unknown operation named "${operationName}".`)]; |
| } |
|
|
| return [new GraphQLError('Must provide an operation.')]; |
| } // FIXME: https://github.com/graphql/graphql-js/issues/2203 |
|
|
| /* c8 ignore next */ |
|
|
| const variableDefinitions = |
| (_operation$variableDe = operation.variableDefinitions) !== null && |
| _operation$variableDe !== void 0 |
| ? _operation$variableDe |
| : []; |
| const coercedVariableValues = getVariableValues( |
| schema, |
| variableDefinitions, |
| rawVariableValues !== null && rawVariableValues !== void 0 |
| ? rawVariableValues |
| : {}, |
| { |
| maxErrors: 50, |
| }, |
| ); |
|
|
| if (coercedVariableValues.errors) { |
| return coercedVariableValues.errors; |
| } |
|
|
| return { |
| schema, |
| fragments, |
| rootValue, |
| contextValue, |
| operation, |
| variableValues: coercedVariableValues.coerced, |
| fieldResolver: |
| fieldResolver !== null && fieldResolver !== void 0 |
| ? fieldResolver |
| : defaultFieldResolver, |
| typeResolver: |
| typeResolver !== null && typeResolver !== void 0 |
| ? typeResolver |
| : defaultTypeResolver, |
| subscribeFieldResolver: |
| subscribeFieldResolver !== null && subscribeFieldResolver !== void 0 |
| ? subscribeFieldResolver |
| : defaultFieldResolver, |
| errors: [], |
| }; |
| } |
| /** |
| * Implements the "Executing operations" section of the spec. |
| */ |
|
|
| function executeOperation(exeContext, operation, rootValue) { |
| const rootType = exeContext.schema.getRootType(operation.operation); |
|
|
| if (rootType == null) { |
| throw new GraphQLError( |
| `Schema is not configured to execute ${operation.operation} operation.`, |
| { |
| nodes: operation, |
| }, |
| ); |
| } |
|
|
| const rootFields = collectFields( |
| exeContext.schema, |
| exeContext.fragments, |
| exeContext.variableValues, |
| rootType, |
| operation.selectionSet, |
| ); |
| const path = undefined; |
|
|
| switch (operation.operation) { |
| case OperationTypeNode.QUERY: |
| return executeFields(exeContext, rootType, rootValue, path, rootFields); |
|
|
| case OperationTypeNode.MUTATION: |
| return executeFieldsSerially( |
| exeContext, |
| rootType, |
| rootValue, |
| path, |
| rootFields, |
| ); |
|
|
| case OperationTypeNode.SUBSCRIPTION: |
| // TODO: deprecate `subscribe` and move all logic here |
| // Temporary solution until we finish merging execute and subscribe together |
| return executeFields(exeContext, rootType, rootValue, path, rootFields); |
| } |
| } |
| /** |
| * Implements the "Executing selection sets" section of the spec |
| * for fields that must be executed serially. |
| */ |
|
|
| function executeFieldsSerially( |
| exeContext, |
| parentType, |
| sourceValue, |
| path, |
| fields, |
| ) { |
| return promiseReduce( |
| fields.entries(), |
| (results, [responseName, fieldNodes]) => { |
| const fieldPath = addPath(path, responseName, parentType.name); |
| const result = executeField( |
| exeContext, |
| parentType, |
| sourceValue, |
| fieldNodes, |
| fieldPath, |
| ); |
|
|
| if (result === undefined) { |
| return results; |
| } |
|
|
| if (isPromise(result)) { |
| return result.then((resolvedResult) => { |
| results[responseName] = resolvedResult; |
| return results; |
| }); |
| } |
|
|
| results[responseName] = result; |
| return results; |
| }, |
| Object.create(null), |
| ); |
| } |
| /** |
| * Implements the "Executing selection sets" section of the spec |
| * for fields that may be executed in parallel. |
| */ |
|
|
| function executeFields(exeContext, parentType, sourceValue, path, fields) { |
| const results = Object.create(null); |
| let containsPromise = false; |
|
|
| try { |
| for (const [responseName, fieldNodes] of fields.entries()) { |
| const fieldPath = addPath(path, responseName, parentType.name); |
| const result = executeField( |
| exeContext, |
| parentType, |
| sourceValue, |
| fieldNodes, |
| fieldPath, |
| ); |
|
|
| if (result !== undefined) { |
| results[responseName] = result; |
|
|
| if (isPromise(result)) { |
| containsPromise = true; |
| } |
| } |
| } |
| } catch (error) { |
| if (containsPromise) { |
| // Ensure that any promises returned by other fields are handled, as they may also reject. |
| return promiseForObject(results).finally(() => { |
| throw error; |
| }); |
| } |
|
|
| throw error; |
| } // If there are no promises, we can just return the object |
|
|
| if (!containsPromise) { |
| return results; |
| } // Otherwise, results is a map from field name to the result of resolving that |
| // field, which is possibly a promise. Return a promise that will return this |
| // same map, but with any promises replaced with the values they resolved to. |
|
|
| return promiseForObject(results); |
| } |
| /** |
| * Implements the "Executing fields" section of the spec |
| * In particular, this function figures out the value that the field returns by |
| * calling its resolve function, then calls completeValue to complete promises, |
| * serialize scalars, or execute the sub-selection-set for objects. |
| */ |
|
|
| function executeField(exeContext, parentType, source, fieldNodes, path) { |
| var _fieldDef$resolve; |
|
|
| const fieldDef = getFieldDef(exeContext.schema, parentType, fieldNodes[0]); |
|
|
| if (!fieldDef) { |
| return; |
| } |
|
|
| const returnType = fieldDef.type; |
| const resolveFn = |
| (_fieldDef$resolve = fieldDef.resolve) !== null && |
| _fieldDef$resolve !== void 0 |
| ? _fieldDef$resolve |
| : exeContext.fieldResolver; |
| const info = buildResolveInfo( |
| exeContext, |
| fieldDef, |
| fieldNodes, |
| parentType, |
| path, |
| ); // Get the resolve function, regardless of if its result is normal or abrupt (error). |
|
|
| try { |
| // Build a JS object of arguments from the field.arguments AST, using the |
| // variables scope to fulfill any variable references. |
| // TODO: find a way to memoize, in case this field is within a List type. |
| const args = getArgumentValues( |
| fieldDef, |
| fieldNodes[0], |
| exeContext.variableValues, |
| ); // The resolve function's optional third argument is a context value that |
| // is provided to every resolve function within an execution. It is commonly |
| // used to represent an authenticated user, or request-specific caches. |
|
|
| const contextValue = exeContext.contextValue; |
| const result = resolveFn(source, args, contextValue, info); |
| let completed; |
|
|
| if (isPromise(result)) { |
| completed = result.then((resolved) => |
| completeValue(exeContext, returnType, fieldNodes, info, path, resolved), |
| ); |
| } else { |
| completed = completeValue( |
| exeContext, |
| returnType, |
| fieldNodes, |
| info, |
| path, |
| result, |
| ); |
| } |
|
|
| if (isPromise(completed)) { |
| // Note: we don't rely on a `catch` method, but we do expect "thenable" |
| // to take a second callback for the error case. |
| return completed.then(undefined, (rawError) => { |
| const error = locatedError(rawError, fieldNodes, pathToArray(path)); |
| return handleFieldError(error, returnType, exeContext); |
| }); |
| } |
|
|
| return completed; |
| } catch (rawError) { |
| const error = locatedError(rawError, fieldNodes, pathToArray(path)); |
| return handleFieldError(error, returnType, exeContext); |
| } |
| } |
| /** |
| * @internal |
| */ |
|
|
| export function buildResolveInfo( |
| exeContext, |
| fieldDef, |
| fieldNodes, |
| parentType, |
| path, |
| ) { |
| // The resolve function's optional fourth argument is a collection of |
| // information about the current execution state. |
| return { |
| fieldName: fieldDef.name, |
| fieldNodes, |
| returnType: fieldDef.type, |
| parentType, |
| path, |
| schema: exeContext.schema, |
| fragments: exeContext.fragments, |
| rootValue: exeContext.rootValue, |
| operation: exeContext.operation, |
| variableValues: exeContext.variableValues, |
| }; |
| } |
|
|
| function handleFieldError(error, returnType, exeContext) { |
| // If the field type is non-nullable, then it is resolved without any |
| // protection from errors, however it still properly locates the error. |
| if (isNonNullType(returnType)) { |
| throw error; |
| } // Otherwise, error protection is applied, logging the error and resolving |
| // a null value for this field if one is encountered. |
|
|
| exeContext.errors.push(error); |
| return null; |
| } |
| /** |
| * Implements the instructions for completeValue as defined in the |
| * "Value Completion" section of the spec. |
| * |
| * If the field type is Non-Null, then this recursively completes the value |
| * for the inner type. It throws a field error if that completion returns null, |
| * as per the "Nullability" section of the spec. |
| * |
| * If the field type is a List, then this recursively completes the value |
| * for the inner type on each item in the list. |
| * |
| * If the field type is a Scalar or Enum, ensures the completed value is a legal |
| * value of the type by calling the `serialize` method of GraphQL type |
| * definition. |
| * |
| * If the field is an abstract type, determine the runtime type of the value |
| * and then complete based on that type |
| * |
| * Otherwise, the field type expects a sub-selection set, and will complete the |
| * value by executing all sub-selections. |
| */ |
|
|
| function completeValue(exeContext, returnType, fieldNodes, info, path, result) { |
| // If result is an Error, throw a located error. |
| if (result instanceof Error) { |
| throw result; |
| } // If field type is NonNull, complete for inner type, and throw field error |
| // if result is null. |
|
|
| if (isNonNullType(returnType)) { |
| const completed = completeValue( |
| exeContext, |
| returnType.ofType, |
| fieldNodes, |
| info, |
| path, |
| result, |
| ); |
|
|
| if (completed === null) { |
| throw new Error( |
| `Cannot return null for non-nullable field ${info.parentType.name}.${info.fieldName}.`, |
| ); |
| } |
|
|
| return completed; |
| } // If result value is null or undefined then return null. |
|
|
| if (result == null) { |
| return null; |
| } // If field type is List, complete each item in the list with the inner type |
|
|
| if (isListType(returnType)) { |
| return completeListValue( |
| exeContext, |
| returnType, |
| fieldNodes, |
| info, |
| path, |
| result, |
| ); |
| } // If field type is a leaf type, Scalar or Enum, serialize to a valid value, |
| // returning null if serialization is not possible. |
|
|
| if (isLeafType(returnType)) { |
| return completeLeafValue(returnType, result); |
| } // If field type is an abstract type, Interface or Union, determine the |
| // runtime Object type and complete for that type. |
|
|
| if (isAbstractType(returnType)) { |
| return completeAbstractValue( |
| exeContext, |
| returnType, |
| fieldNodes, |
| info, |
| path, |
| result, |
| ); |
| } // If field type is Object, execute and complete all sub-selections. |
|
|
| if (isObjectType(returnType)) { |
| return completeObjectValue( |
| exeContext, |
| returnType, |
| fieldNodes, |
| info, |
| path, |
| result, |
| ); |
| } |
| /* c8 ignore next 6 */ |
| // Not reachable, all possible output types have been considered. |
|
|
| false || |
| invariant( |
| false, |
| 'Cannot complete value of unexpected output type: ' + inspect(returnType), |
| ); |
| } |
| /** |
| * Complete a list value by completing each item in the list with the |
| * inner type |
| */ |
|
|
| function completeListValue( |
| exeContext, |
| returnType, |
| fieldNodes, |
| info, |
| path, |
| result, |
| ) { |
| if (!isIterableObject(result)) { |
| throw new GraphQLError( |
| `Expected Iterable, but did not find one for field "${info.parentType.name}.${info.fieldName}".`, |
| ); |
| } // This is specified as a simple map, however we're optimizing the path |
| // where the list contains no Promises by avoiding creating another Promise. |
|
|
| const itemType = returnType.ofType; |
| let containsPromise = false; |
| const completedResults = Array.from(result, (item, index) => { |
| // No need to modify the info object containing the path, |
| // since from here on it is not ever accessed by resolver functions. |
| const itemPath = addPath(path, index, undefined); |
|
|
| try { |
| let completedItem; |
|
|
| if (isPromise(item)) { |
| completedItem = item.then((resolved) => |
| completeValue( |
| exeContext, |
| itemType, |
| fieldNodes, |
| info, |
| itemPath, |
| resolved, |
| ), |
| ); |
| } else { |
| completedItem = completeValue( |
| exeContext, |
| itemType, |
| fieldNodes, |
| info, |
| itemPath, |
| item, |
| ); |
| } |
|
|
| if (isPromise(completedItem)) { |
| containsPromise = true; // Note: we don't rely on a `catch` method, but we do expect "thenable" |
| // to take a second callback for the error case. |
|
|
| return completedItem.then(undefined, (rawError) => { |
| const error = locatedError( |
| rawError, |
| fieldNodes, |
| pathToArray(itemPath), |
| ); |
| return handleFieldError(error, itemType, exeContext); |
| }); |
| } |
|
|
| return completedItem; |
| } catch (rawError) { |
| const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); |
| return handleFieldError(error, itemType, exeContext); |
| } |
| }); |
| return containsPromise ? Promise.all(completedResults) : completedResults; |
| } |
| /** |
| * Complete a Scalar or Enum by serializing to a valid value, returning |
| * null if serialization is not possible. |
| */ |
|
|
| function completeLeafValue(returnType, result) { |
| const serializedResult = returnType.serialize(result); |
|
|
| if (serializedResult == null) { |
| throw new Error( |
| `Expected \`${inspect(returnType)}.serialize(${inspect(result)})\` to ` + |
| `return non-nullable value, returned: ${inspect(serializedResult)}`, |
| ); |
| } |
|
|
| return serializedResult; |
| } |
| /** |
| * Complete a value of an abstract type by determining the runtime object type |
| * of that value, then complete the value for that type. |
| */ |
|
|
| function completeAbstractValue( |
| exeContext, |
| returnType, |
| fieldNodes, |
| info, |
| path, |
| result, |
| ) { |
| var _returnType$resolveTy; |
|
|
| const resolveTypeFn = |
| (_returnType$resolveTy = returnType.resolveType) !== null && |
| _returnType$resolveTy !== void 0 |
| ? _returnType$resolveTy |
| : exeContext.typeResolver; |
| const contextValue = exeContext.contextValue; |
| const runtimeType = resolveTypeFn(result, contextValue, info, returnType); |
|
|
| if (isPromise(runtimeType)) { |
| return runtimeType.then((resolvedRuntimeType) => |
| completeObjectValue( |
| exeContext, |
| ensureValidRuntimeType( |
| resolvedRuntimeType, |
| exeContext, |
| returnType, |
| fieldNodes, |
| info, |
| result, |
| ), |
| fieldNodes, |
| info, |
| path, |
| result, |
| ), |
| ); |
| } |
|
|
| return completeObjectValue( |
| exeContext, |
| ensureValidRuntimeType( |
| runtimeType, |
| exeContext, |
| returnType, |
| fieldNodes, |
| info, |
| result, |
| ), |
| fieldNodes, |
| info, |
| path, |
| result, |
| ); |
| } |
|
|
| function ensureValidRuntimeType( |
| runtimeTypeName, |
| exeContext, |
| returnType, |
| fieldNodes, |
| info, |
| result, |
| ) { |
| if (runtimeTypeName == null) { |
| throw new GraphQLError( |
| `Abstract type "${returnType.name}" must resolve to an Object type at runtime for field "${info.parentType.name}.${info.fieldName}". Either the "${returnType.name}" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.`, |
| fieldNodes, |
| ); |
| } // releases before 16.0.0 supported returning `GraphQLObjectType` from `resolveType` |
| // TODO: remove in 17.0.0 release |
|
|
| if (isObjectType(runtimeTypeName)) { |
| throw new GraphQLError( |
| 'Support for returning GraphQLObjectType from resolveType was removed in graphql-js@16.0.0 please return type name instead.', |
| ); |
| } |
|
|
| if (typeof runtimeTypeName !== 'string') { |
| throw new GraphQLError( |
| `Abstract type "${returnType.name}" must resolve to an Object type at runtime for field "${info.parentType.name}.${info.fieldName}" with ` + |
| `value ${inspect(result)}, received "${inspect(runtimeTypeName)}".`, |
| ); |
| } |
|
|
| const runtimeType = exeContext.schema.getType(runtimeTypeName); |
|
|
| if (runtimeType == null) { |
| throw new GraphQLError( |
| `Abstract type "${returnType.name}" was resolved to a type "${runtimeTypeName}" that does not exist inside the schema.`, |
| { |
| nodes: fieldNodes, |
| }, |
| ); |
| } |
|
|
| if (!isObjectType(runtimeType)) { |
| throw new GraphQLError( |
| `Abstract type "${returnType.name}" was resolved to a non-object type "${runtimeTypeName}".`, |
| { |
| nodes: fieldNodes, |
| }, |
| ); |
| } |
|
|
| if (!exeContext.schema.isSubType(returnType, runtimeType)) { |
| throw new GraphQLError( |
| `Runtime Object type "${runtimeType.name}" is not a possible type for "${returnType.name}".`, |
| { |
| nodes: fieldNodes, |
| }, |
| ); |
| } |
|
|
| return runtimeType; |
| } |
| /** |
| * Complete an Object value by executing all sub-selections. |
| */ |
|
|
| function completeObjectValue( |
| exeContext, |
| returnType, |
| fieldNodes, |
| info, |
| path, |
| result, |
| ) { |
| // Collect sub-fields to execute to complete this value. |
| const subFieldNodes = collectSubfields(exeContext, returnType, fieldNodes); // If there is an isTypeOf predicate function, call it with the |
| // current result. If isTypeOf returns false, then raise an error rather |
| // than continuing execution. |
|
|
| if (returnType.isTypeOf) { |
| const isTypeOf = returnType.isTypeOf(result, exeContext.contextValue, info); |
|
|
| if (isPromise(isTypeOf)) { |
| return isTypeOf.then((resolvedIsTypeOf) => { |
| if (!resolvedIsTypeOf) { |
| throw invalidReturnTypeError(returnType, result, fieldNodes); |
| } |
|
|
| return executeFields( |
| exeContext, |
| returnType, |
| result, |
| path, |
| subFieldNodes, |
| ); |
| }); |
| } |
|
|
| if (!isTypeOf) { |
| throw invalidReturnTypeError(returnType, result, fieldNodes); |
| } |
| } |
|
|
| return executeFields(exeContext, returnType, result, path, subFieldNodes); |
| } |
|
|
| function invalidReturnTypeError(returnType, result, fieldNodes) { |
| return new GraphQLError( |
| `Expected value of type "${returnType.name}" but got: ${inspect(result)}.`, |
| { |
| nodes: fieldNodes, |
| }, |
| ); |
| } |
| /** |
| * If a resolveType function is not given, then a default resolve behavior is |
| * used which attempts two strategies: |
| * |
| * First, See if the provided value has a `__typename` field defined, if so, use |
| * that value as name of the resolved type. |
| * |
| * Otherwise, test each possible type for the abstract type by calling |
| * isTypeOf for the object being coerced, returning the first type that matches. |
| */ |
|
|
| export const defaultTypeResolver = function ( |
| value, |
| contextValue, |
| info, |
| abstractType, |
| ) { |
| // First, look for `__typename`. |
| if (isObjectLike(value) && typeof value.__typename === 'string') { |
| return value.__typename; |
| } // Otherwise, test each possible type. |
|
|
| const possibleTypes = info.schema.getPossibleTypes(abstractType); |
| const promisedIsTypeOfResults = []; |
|
|
| for (let i = 0; i < possibleTypes.length; i++) { |
| const type = possibleTypes[i]; |
|
|
| if (type.isTypeOf) { |
| const isTypeOfResult = type.isTypeOf(value, contextValue, info); |
|
|
| if (isPromise(isTypeOfResult)) { |
| promisedIsTypeOfResults[i] = isTypeOfResult; |
| } else if (isTypeOfResult) { |
| return type.name; |
| } |
| } |
| } |
|
|
| if (promisedIsTypeOfResults.length) { |
| return Promise.all(promisedIsTypeOfResults).then((isTypeOfResults) => { |
| for (let i = 0; i < isTypeOfResults.length; i++) { |
| if (isTypeOfResults[i]) { |
| return possibleTypes[i].name; |
| } |
| } |
| }); |
| } |
| }; |
| /** |
| * If a resolve function is not given, then a default resolve behavior is used |
| * which takes the property of the source object of the same name as the field |
| * and returns it as the result, or if it's a function, returns the result |
| * of calling that function while passing along args and context value. |
| */ |
|
|
| export const defaultFieldResolver = function ( |
| source, |
| args, |
| contextValue, |
| info, |
| ) { |
| // ensure source is a value for which property access is acceptable. |
| if (isObjectLike(source) || typeof source === 'function') { |
| const property = source[info.fieldName]; |
|
|
| if (typeof property === 'function') { |
| return source[info.fieldName](args, contextValue, info); |
| } |
|
|
| return property; |
| } |
| }; |
| /** |
| * This method looks up the field on the given type definition. |
| * It has special casing for the three introspection fields, |
| * __schema, __type and __typename. __typename is special because |
| * it can always be queried as a field, even in situations where no |
| * other fields are allowed, like on a Union. __schema and __type |
| * could get automatically added to the query type, but that would |
| * require mutating type definitions, which would cause issues. |
| * |
| * @internal |
| */ |
|
|
| export function getFieldDef(schema, parentType, fieldNode) { |
| const fieldName = fieldNode.name.value; |
|
|
| if ( |
| fieldName === SchemaMetaFieldDef.name && |
| schema.getQueryType() === parentType |
| ) { |
| return SchemaMetaFieldDef; |
| } else if ( |
| fieldName === TypeMetaFieldDef.name && |
| schema.getQueryType() === parentType |
| ) { |
| return TypeMetaFieldDef; |
| } else if (fieldName === TypeNameMetaFieldDef.name) { |
| return TypeNameMetaFieldDef; |
| } |
|
|
| return parentType.getFields()[fieldName]; |
| } |