/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * */ import find from '../polyfills/find'; import { GraphQLError } from '../error/GraphQLError'; import inspect from '../jsutils/inspect'; import invariant from '../jsutils/invariant'; import keyMap from '../jsutils/keyMap'; import { coerceValue } from '../utilities/coerceValue'; import { typeFromAST } from '../utilities/typeFromAST'; import { valueFromAST } from '../utilities/valueFromAST'; import { Kind } from '../language/kinds'; import { print } from '../language/printer'; import { isInputType, isNonNullType } from '../type/definition'; /** * Prepares an object map of variableValues of the correct type based on the * provided variable definitions and arbitrary input. If the input cannot be * parsed to match the variable definitions, a GraphQLError will be thrown. * * Note: The returned value is a plain Object with a prototype, since it is * exposed to user code. Care should be taken to not pull values from the * Object prototype. */ export function getVariableValues(schema, varDefNodes, inputs) { var errors = []; var coercedValues = {}; for (var i = 0; i < varDefNodes.length; i++) { var varDefNode = varDefNodes[i]; var varName = varDefNode.variable.name.value; var varType = typeFromAST(schema, varDefNode.type); if (!isInputType(varType)) { // Must use input types for variables. This should be caught during // validation, however is checked again here for safety. errors.push(new GraphQLError("Variable \"$".concat(varName, "\" expected value of type ") + "\"".concat(print(varDefNode.type), "\" which cannot be used as an input type."), varDefNode.type)); } else { var hasValue = hasOwnProperty(inputs, varName); var value = hasValue ? inputs[varName] : undefined; if (!hasValue && varDefNode.defaultValue) { // If no value was provided to a variable with a default value, // use the default value. coercedValues[varName] = valueFromAST(varDefNode.defaultValue, varType); } else if ((!hasValue || value === null) && isNonNullType(varType)) { // If no value or a nullish value was provided to a variable with a // non-null type (required), produce an error. errors.push(new GraphQLError(hasValue ? "Variable \"$".concat(varName, "\" of non-null type ") + "\"".concat(inspect(varType), "\" must not be null.") : "Variable \"$".concat(varName, "\" of required type ") + "\"".concat(inspect(varType), "\" was not provided."), varDefNode)); } else if (hasValue) { if (value === null) { // If the explicit value `null` was provided, an entry in the coerced // values must exist as the value `null`. coercedValues[varName] = null; } else { // Otherwise, a non-null value was provided, coerce it to the expected // type or report an error if coercion fails. var coerced = coerceValue(value, varType, varDefNode); var coercionErrors = coerced.errors; if (coercionErrors) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = coercionErrors[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var error = _step.value; error.message = "Variable \"$".concat(varName, "\" got invalid value ").concat(inspect(value), "; ") + error.message; } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } errors.push.apply(errors, coercionErrors); } else { coercedValues[varName] = coerced.value; } } } } } return errors.length === 0 ? { errors: undefined, coerced: coercedValues } : { errors: errors, coerced: undefined }; } /** * Prepares an object map of argument values given a list of argument * definitions and list of argument AST nodes. * * Note: The returned value is a plain Object with a prototype, since it is * exposed to user code. Care should be taken to not pull values from the * Object prototype. */ export function getArgumentValues(def, node, variableValues) { var coercedValues = {}; var argDefs = def.args; var argNodes = node.arguments; if (!argDefs || !argNodes) { return coercedValues; } var argNodeMap = keyMap(argNodes, function (arg) { return arg.name.value; }); for (var i = 0; i < argDefs.length; i++) { var argDef = argDefs[i]; var name = argDef.name; var argType = argDef.type; var argumentNode = argNodeMap[name]; var hasValue = void 0; var isNull = void 0; if (argumentNode && argumentNode.value.kind === Kind.VARIABLE) { var variableName = argumentNode.value.name.value; hasValue = variableValues != null && hasOwnProperty(variableValues, variableName); isNull = variableValues != null && variableValues[variableName] === null; } else { hasValue = argumentNode != null; isNull = argumentNode != null && argumentNode.value.kind === Kind.NULL; } if (!hasValue && argDef.defaultValue !== undefined) { // If no argument was provided where the definition has a default value, // use the default value. coercedValues[name] = argDef.defaultValue; } else if ((!hasValue || isNull) && isNonNullType(argType)) { // If no argument or a null value was provided to an argument with a // non-null type (required), produce a field error. if (isNull) { throw new GraphQLError("Argument \"".concat(name, "\" of non-null type \"").concat(inspect(argType), "\" ") + 'must not be null.', argumentNode.value); } else if (argumentNode && argumentNode.value.kind === Kind.VARIABLE) { var _variableName = argumentNode.value.name.value; throw new GraphQLError("Argument \"".concat(name, "\" of required type \"").concat(inspect(argType), "\" ") + "was provided the variable \"$".concat(_variableName, "\" ") + 'which was not provided a runtime value.', argumentNode.value); } else { throw new GraphQLError("Argument \"".concat(name, "\" of required type \"").concat(inspect(argType), "\" ") + 'was not provided.', node); } } else if (hasValue) { if (argumentNode.value.kind === Kind.NULL) { // If the explicit value `null` was provided, an entry in the coerced // values must exist as the value `null`. coercedValues[name] = null; } else if (argumentNode.value.kind === Kind.VARIABLE) { var _variableName2 = argumentNode.value.name.value; !variableValues ? invariant(0, 'Must exist for hasValue to be true.') : void 0; // Note: This does no further checking that this variable is correct. // This assumes that this query has been validated and the variable // usage here is of the correct type. coercedValues[name] = variableValues[_variableName2]; } else { var valueNode = argumentNode.value; var coercedValue = valueFromAST(valueNode, argType, variableValues); if (coercedValue === undefined) { // Note: ValuesOfCorrectType validation should catch this before // execution. This is a runtime check to ensure execution does not // continue with an invalid argument value. throw new GraphQLError("Argument \"".concat(name, "\" has invalid value ").concat(print(valueNode), "."), argumentNode.value); } coercedValues[name] = coercedValue; } } } return coercedValues; } /** * Prepares an object map of argument values given a directive definition * and a AST node which may contain directives. Optionally also accepts a map * of variable values. * * If the directive does not exist on the node, returns undefined. * * Note: The returned value is a plain Object with a prototype, since it is * exposed to user code. Care should be taken to not pull values from the * Object prototype. */ export function getDirectiveValues(directiveDef, node, variableValues) { var directiveNode = node.directives && find(node.directives, function (directive) { return directive.name.value === directiveDef.name; }); if (directiveNode) { return getArgumentValues(directiveDef, directiveNode, variableValues); } } function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }