'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** * Copyright (c) 2015, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ exports.execute = execute; var _iterall = require('iterall'); var _error = require('graphql/error'); var _find = require('graphql/jsutils/find'); var _find2 = _interopRequireDefault(_find); var _invariant = require('graphql/jsutils/invariant'); var _invariant2 = _interopRequireDefault(_invariant); var _isNullish = require('graphql/jsutils/isNullish'); var _isNullish2 = _interopRequireDefault(_isNullish); var _typeFromAST = require('graphql/utilities/typeFromAST'); var _language = require('graphql/language'); var _values = require('./values'); var _definition = require('graphql/type/definition'); var _schema = require('graphql/type/schema'); var _introspection = require('graphql/type/introspection'); var _directives = require('graphql/type/directives'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Implements the "Evaluating requests" section of the GraphQL specification. * * If the arguments to this function do not result in a legal execution context, * a GraphQLError will be thrown immediately explaining the invalid input. */ /** * 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 */ /** * The result of execution. `data` is the result of executing the * query, `errors` is null if no errors occurred, and is a * non-empty array if an error occurred. */ function execute(schema, documentAST, rootValue, contextValue, variableValues, operationName) { (0, _invariant2.default)(schema, 'Must provide schema'); (0, _invariant2.default)(schema instanceof _schema.GraphQLSchema, 'Schema must be an instance of GraphQLSchema. Also ensure that there are ' + 'not multiple versions of GraphQL installed in your node_modules directory.'); // If a valid context cannot be created due to incorrect arguments, // this will throw an error. var context = buildExecutionContext(schema, documentAST, rootValue, contextValue, variableValues, operationName); // Return 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 not throw. var data = void 0; try { data = executeOperation(context, context.operation, rootValue); } catch (error) { // 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. context.errors.push(error); data = null; } if (!context.errors.length) { return { data: data }; } return { data: data, errors: context.errors }; } /** * 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. */ function buildExecutionContext(schema, documentAST, rootValue, contextValue, rawVariableValues, operationName) { var errors = []; var operation = void 0; var fragments = Object.create(null); documentAST.definitions.forEach(function (definition) { switch (definition.kind) { case _language.Kind.OPERATION_DEFINITION: if (!operationName && operation) { throw new _error.GraphQLError('Must provide operation name if query contains multiple operations.'); } if (!operationName || definition.name && definition.name.value === operationName) { operation = definition; } break; case _language.Kind.FRAGMENT_DEFINITION: fragments[definition.name.value] = definition; break; default: throw new _error.GraphQLError('GraphQL cannot execute a request containing a ' + definition.kind + '.', [definition]); } }); if (!operation) { if (operationName) { throw new _error.GraphQLError('Unknown operation named "' + operationName + '".'); } else { throw new _error.GraphQLError('Must provide an operation.'); } } var variableValues = (0, _values.getVariableValues)(schema, operation.variableDefinitions || [], rawVariableValues || {}); return { schema: schema, fragments: fragments, rootValue: rootValue, contextValue: contextValue, operation: operation, variableValues: variableValues, errors: errors }; } /** * Implements the "Evaluating operations" section of the spec. */ function executeOperation(exeContext, operation, rootValue) { var type = getOperationRootType(exeContext.schema, operation); var fields = collectFields(exeContext, type, operation.selectionSet, Object.create(null), Object.create(null)); var path = []; if (operation.operation === 'mutation') { return executeFieldsSerially(exeContext, type, rootValue, path, fields); } return executeFields(exeContext, type, rootValue, path, fields); } /** * Extracts the root type of the operation from the schema. */ function getOperationRootType(schema, operation) { switch (operation.operation) { case 'query': return schema.getQueryType(); case 'mutation': var mutationType = schema.getMutationType(); if (!mutationType) { throw new _error.GraphQLError('Schema is not configured for mutations', [operation]); } return mutationType; case 'subscription': var subscriptionType = schema.getSubscriptionType(); if (!subscriptionType) { throw new _error.GraphQLError('Schema is not configured for subscriptions', [operation]); } return subscriptionType; default: throw new _error.GraphQLError('Can only execute queries, mutations and subscriptions', [operation]); } } /** * Implements the "Evaluating selection sets" section of the spec * for "write" mode. */ function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields) { return Object.keys(fields).reduce(function (results, responseName) { var fieldASTs = fields[responseName]; var fieldPath = path.concat([responseName]); var result = resolveField(exeContext, parentType, sourceValue, fieldASTs, fieldPath); if (result === undefined) { return results; } results[responseName] = result; return results; }, {}); } /** * Implements the "Evaluating selection sets" section of the spec * for "read" mode. */ function executeFields(exeContext, parentType, sourceValue, path, fields) { var finalResults = Object.keys(fields).reduce(function (results, responseName) { var fieldASTs = fields[responseName]; var fieldPath = path.concat([responseName]); var result = resolveField(exeContext, parentType, sourceValue, fieldASTs, fieldPath); if (result === undefined) { return results; } results[responseName] = result; return results; }, Object.create(null)); return finalResults; } /** * Given a selectionSet, adds all of the fields in that selection to * the passed in map of fields, and returns it at the end. * * CollectFields requires the "runtime type" of an object. For a field which * returns and Interface or Union type, the "runtime type" will be the actual * Object type returned by that field. */ function collectFields(exeContext, runtimeType, selectionSet, fields, visitedFragmentNames) { for (var i = 0; i < selectionSet.selections.length; i++) { var selection = selectionSet.selections[i]; switch (selection.kind) { case _language.Kind.FIELD: if (!shouldIncludeNode(exeContext, selection.directives)) { continue; } var _name = getFieldEntryKey(selection); if (!fields[_name]) { fields[_name] = []; } fields[_name].push(selection); break; case _language.Kind.INLINE_FRAGMENT: if (!shouldIncludeNode(exeContext, selection.directives) || !doesFragmentConditionMatch(exeContext, selection, runtimeType)) { continue; } collectFields(exeContext, runtimeType, selection.selectionSet, fields, visitedFragmentNames); break; case _language.Kind.FRAGMENT_SPREAD: var fragName = selection.name.value; if (visitedFragmentNames[fragName] || !shouldIncludeNode(exeContext, selection.directives)) { continue; } visitedFragmentNames[fragName] = true; var fragment = exeContext.fragments[fragName]; if (!fragment || !doesFragmentConditionMatch(exeContext, fragment, runtimeType)) { continue; } collectFields(exeContext, runtimeType, fragment.selectionSet, fields, visitedFragmentNames); break; } } return fields; } /** * Determines if a field should be included based on the @include and @skip * directives, where @skip has higher precidence than @include. */ function shouldIncludeNode(exeContext, directives) { var skipAST = directives && (0, _find2.default)(directives, function (directive) { return directive.name.value === _directives.GraphQLSkipDirective.name; }); if (skipAST) { var _getArgumentValues = (0, _values.getArgumentValues)(_directives.GraphQLSkipDirective.args, skipAST.arguments, exeContext.variableValues), skipIf = _getArgumentValues.if; if (skipIf === true) { return false; } } var includeAST = directives && (0, _find2.default)(directives, function (directive) { return directive.name.value === _directives.GraphQLIncludeDirective.name; }); if (includeAST) { var _getArgumentValues2 = (0, _values.getArgumentValues)(_directives.GraphQLIncludeDirective.args, includeAST.arguments, exeContext.variableValues), includeIf = _getArgumentValues2.if; if (includeIf === false) { return false; } } return true; } /** * Determines if a fragment is applicable to the given type. */ function doesFragmentConditionMatch(exeContext, fragment, type) { var typeConditionAST = fragment.typeCondition; if (!typeConditionAST) { return true; } var conditionalType = (0, _typeFromAST.typeFromAST)(exeContext.schema, typeConditionAST); if (conditionalType === type) { return true; } if ((0, _definition.isAbstractType)(conditionalType)) { var abstractType = conditionalType; return exeContext.schema.isPossibleType(abstractType, type); } return false; } /** * Implements the logic to compute the key of a given field's entry */ function getFieldEntryKey(node) { return node.alias ? node.alias.value : node.name.value; } /** * Resolves the field on the given source object. In particular, this * figures out the value that the field returns by calling its resolve function, * then calls completeValue to serialize scalars, or execute * the sub-selection-set for objects. */ function resolveField(exeContext, parentType, source, fieldASTs, path) { var fieldAST = fieldASTs[0]; var fieldName = fieldAST.name.value; var fieldDef = getFieldDef(exeContext.schema, parentType, fieldName); if (!fieldDef) { return; } var returnType = fieldDef.type; var resolveFn = fieldDef.resolve || defaultResolveFn; // 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. var args = (0, _values.getArgumentValues)(fieldDef.args, fieldAST.arguments, 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. var context = exeContext.contextValue; // The resolve function's optional fourth argument is a collection of // information about the current execution state. var info = { fieldName: fieldName, fieldASTs: fieldASTs, returnType: returnType, parentType: parentType, path: path, schema: exeContext.schema, fragments: exeContext.fragments, rootValue: exeContext.rootValue, operation: exeContext.operation, variableValues: exeContext.variableValues }; // Get the resolve function, regardless of if its result is normal // or abrupt (error). var result = resolveOrError(resolveFn, source, args, context, info); return completeValueCatchingError(exeContext, returnType, fieldASTs, info, path, result); } // Isolates the "ReturnOrAbrupt" behavior to not de-opt the `resolveField` // function. Returns the result of resolveFn or the abrupt-return Error object. function resolveOrError(resolveFn, source, args, context, info) { try { return resolveFn(source, args, context, info); } catch (error) { // Sometimes a non-error is thrown, wrap it as an Error for a // consistent interface. return error instanceof Error ? error : new Error(error); } } // This is a small wrapper around completeValue which detects and logs errors // in the execution context. function completeValueCatchingError(exeContext, returnType, fieldASTs, info, path, result) { // If the field type is non-nullable, then it is resolved without any // protection from errors, however it still properly locates the error. if (returnType instanceof _definition.GraphQLNonNull) { return completeValueWithLocatedError(exeContext, returnType, fieldASTs, info, path, result); } // Otherwise, error protection is applied, logging the error and resolving // a null value for this field if one is encountered. try { var completed = completeValueWithLocatedError(exeContext, returnType, fieldASTs, info, path, result); return completed; } catch (error) { // If `completeValueWithLocatedError` returned abruptly (threw an error), // log the error and return null. exeContext.errors.push(error); return null; } } // This is a small wrapper around completeValue which annotates errors with // location information. function completeValueWithLocatedError(exeContext, returnType, fieldASTs, info, path, result) { try { var completed = completeValue(exeContext, returnType, fieldASTs, info, path, result); return completed; } catch (error) { throw (0, _error.locatedError)(error, fieldASTs, path); } } /** * Implements the instructions for completeValue as defined in the * "Field entries" 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 evaluating all sub-selections. */ function completeValue(exeContext, returnType, fieldASTs, 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 (returnType instanceof _definition.GraphQLNonNull) { var completed = completeValue(exeContext, returnType.ofType, fieldASTs, 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-ish (null, undefined, or NaN) then return null. if ((0, _isNullish2.default)(result)) { return null; } // If field type is List, complete each item in the list with the inner type if (returnType instanceof _definition.GraphQLList) { return completeListValue(exeContext, returnType, fieldASTs, 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 (returnType instanceof _definition.GraphQLScalarType || returnType instanceof _definition.GraphQLEnumType) { 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 (returnType instanceof _definition.GraphQLInterfaceType || returnType instanceof _definition.GraphQLUnionType) { return completeAbstractValue(exeContext, returnType, fieldASTs, info, path, result); } // If field type is Object, execute and complete all sub-selections. if (returnType instanceof _definition.GraphQLObjectType) { return completeObjectValue(exeContext, returnType, fieldASTs, info, path, result); } // Not reachable. All possible output types have been considered. throw new Error('Cannot complete value of unexpected type "' + String(returnType) + '".'); } /** * Complete a list value by completing each item in the list with the * inner type */ function completeListValue(exeContext, returnType, fieldASTs, info, path, result) { (0, _invariant2.default)((0, _iterall.isCollection)(result), '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. var itemType = returnType.ofType; var completedResults = []; (0, _iterall.forEach)(result, function (item, index) { // No need to modify the info object containing the path, // since from here on it is not ever accessed by resolver functions. var fieldPath = path.concat([index]); var completedItem = completeValueCatchingError(exeContext, itemType, fieldASTs, info, fieldPath, item); completedResults.push(completedItem); }); return completedResults; } /** * Complete a Scalar or Enum by serializing to a valid value, returning * null if serialization is not possible. */ function completeLeafValue(returnType, result) { (0, _invariant2.default)(returnType.serialize, 'Missing serialize method on type'); var serializedResult = returnType.serialize(result); if ((0, _isNullish2.default)(serializedResult)) { throw new Error('Expected a value of type "' + String(returnType) + '" but ' + ('received: ' + String(result))); } 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, fieldASTs, info, path, result) { var runtimeType = returnType.resolveType ? returnType.resolveType(result, exeContext.contextValue, info) : defaultResolveTypeFn(result, exeContext.contextValue, info, returnType); if (!(runtimeType instanceof _definition.GraphQLObjectType)) { throw new _error.GraphQLError('Abstract type ' + returnType.name + ' must resolve to an Object type at ' + ('runtime for field ' + info.parentType.name + '.' + info.fieldName + ' with ') + ('value "' + String(result) + '", received "' + String(runtimeType) + '".'), fieldASTs); } if (!exeContext.schema.isPossibleType(returnType, runtimeType)) { throw new _error.GraphQLError('Runtime Object type "' + runtimeType.name + '" is not a possible type ' + ('for "' + returnType.name + '".'), fieldASTs); } return completeObjectValue(exeContext, runtimeType, fieldASTs, info, path, result); } /** * Complete an Object value by executing all sub-selections. */ function completeObjectValue(exeContext, returnType, fieldASTs, info, path, result) { // 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 && !returnType.isTypeOf(result, exeContext.contextValue, info)) { throw new _error.GraphQLError('Expected value of type "' + returnType.name + '" but got: ' + String(result) + '.', fieldASTs); } // Collect sub-fields to execute to complete this value. var subFieldASTs = Object.create(null); var visitedFragmentNames = Object.create(null); for (var i = 0; i < fieldASTs.length; i++) { var selectionSet = fieldASTs[i].selectionSet; if (selectionSet) { subFieldASTs = collectFields(exeContext, returnType, selectionSet, subFieldASTs, visitedFragmentNames); } } return executeFields(exeContext, returnType, result, path, subFieldASTs); } /** * If a resolveType function is not given, then a default resolve behavior is * used which tests each possible type for the abstract type by calling * isTypeOf for the object being coerced, returning the first type that matches. */ function defaultResolveTypeFn(value, context, info, abstractType) { var possibleTypes = info.schema.getPossibleTypes(abstractType); for (var i = 0; i < possibleTypes.length; i++) { var type = possibleTypes[i]; if (type.isTypeOf && type.isTypeOf(value, context, info)) { return type; } } } /** * 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. */ function defaultResolveFn(source, args, context, _ref) { var fieldName = _ref.fieldName; // ensure source is a value for which property access is acceptable. if ((typeof source === 'undefined' ? 'undefined' : _typeof(source)) === 'object' || typeof source === 'function') { var property = source[fieldName]; return typeof property === 'function' ? source[fieldName]() : property; } } /** * This method looks up the field on the given type defintion. * It has special casing for the two introspection fields, __schema * 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 could get automatically * added to the query type, but that would require mutating type * definitions, which would cause issues. */ function getFieldDef(schema, parentType, fieldName) { if (fieldName === _introspection.SchemaMetaFieldDef.name && schema.getQueryType() === parentType) { return _introspection.SchemaMetaFieldDef; } else if (fieldName === _introspection.TypeMetaFieldDef.name && schema.getQueryType() === parentType) { return _introspection.TypeMetaFieldDef; } else if (fieldName === _introspection.TypeNameMetaFieldDef.name) { return _introspection.TypeNameMetaFieldDef; } return parentType.getFields()[fieldName]; }