UNPKG

@graphql-tools/executor

Version:

Fork of GraphQL.js' execute function

141 lines (140 loc) • 6.28 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getVariableValues = getVariableValues; const graphql_1 = require("graphql"); const utils_1 = require("@graphql-tools/utils"); const validateInputValue_js_1 = require("./validateInputValue.js"); /** * 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. */ function getVariableValues(schema, varDefNodes, inputs, options) { const errors = []; const maxErrors = options?.maxErrors; const hideSuggestions = options?.hideSuggestions; try { const variableValues = coerceVariableValues(schema, varDefNodes, inputs, error => { if (maxErrors != null && errors.length >= maxErrors) { throw (0, utils_1.createGraphQLError)('Too many errors processing variables, error limit reached. Execution aborted.'); } errors.push(error); }, hideSuggestions); if (errors.length === 0) { return { variableValues }; } } catch (error) { errors.push(error); } return { errors }; } function coerceVariableValues(schema, varDefNodes, inputs, onError, hideSuggestions) { const coercedValues = {}; const sources = {}; for (const varDefNode of varDefNodes) { const varName = varDefNode.variable.name.value; const varType = (0, graphql_1.typeFromAST)(schema, varDefNode.type); if (!(0, graphql_1.isInputType)(varType)) { // Must use input types for variables. This should be caught during // validation, however is checked again here for safety. const varTypeStr = (0, graphql_1.print)(varDefNode.type); onError((0, utils_1.createGraphQLError)(`Variable "$${varName}" expected value of type "${varTypeStr}" which cannot be used as an input type.`, { nodes: varDefNode.type })); continue; } const signature = { name: varName, type: varType, default: varDefNode.defaultValue == null ? undefined : { literal: varDefNode.defaultValue }, }; if (!(0, utils_1.hasOwnProperty)(inputs, varName)) { sources[varName] = { signature }; if (varDefNode.defaultValue) { coercedValues[varName] = (0, graphql_1.valueFromAST)(varDefNode.defaultValue, varType); } else if ((0, graphql_1.isNonNullType)(varType)) { const varTypeStr = (0, utils_1.inspect)(varType); onError((0, utils_1.createGraphQLError)(`Variable "$${varName}" of required type "${varTypeStr}" was not provided.`, { nodes: varDefNode, })); } continue; } const value = inputs[varName]; const sourceValue = snapshotInputValue(value, varType); sources[varName] = { signature, value: sourceValue }; if (value === null && (0, graphql_1.isNonNullType)(varType)) { const varTypeStr = (0, utils_1.inspect)(varType); onError((0, utils_1.createGraphQLError)(`Variable "$${varName}" of non-null type "${varTypeStr}" must not be null.`, { nodes: varDefNode, })); continue; } let errored = false; const callback = (path, invalidValue, error) => { errored = true; let prefix = `Variable "$${varName}" got invalid value ` + (0, utils_1.inspect)(invalidValue); if (path.length > 0) { prefix += ` at "${varName}${(0, utils_1.printPathArray)(path)}"`; } onError((0, utils_1.createGraphQLError)(prefix + '; ' + error.message, { nodes: varDefNode, originalError: error, })); }; coercedValues[varName] = graphql_1.coerceInputValue(sourceValue, varType, callback); if (coercedValues[varName] === undefined && !errored) { (0, validateInputValue_js_1.validateInputValue)(sourceValue, varType, callback, hideSuggestions); } } return { coerced: coercedValues, sources, }; } function snapshotInputValue(value, type) { if ((0, graphql_1.isNonNullType)(type)) { return snapshotInputValue(value, type.ofType); } if (value == null) { return value; } if ((0, graphql_1.isListType)(type)) { if (!isIterableInput(value)) { return snapshotInputValue(value, type.ofType); } return Array.from(value, item => snapshotInputValue(item, type.ofType)); } // Arrays are objects in JS; do not spread them into `{}` or invalid list // inputs (e.g. `[]` for an input object) would be accepted after defaults. // Snapshot into a null-prototype object so Object.prototype keys (e.g. // `toString`) are not treated as own fields, while still reading declared // fields with normal property access (prototype getters stay visible). if ((0, graphql_1.isInputObjectType)(type) && typeof value === 'object' && !Array.isArray(value)) { const obj = value; const snapshot = Object.create(null); for (const key of Object.keys(obj)) { snapshot[key] = obj[key]; } for (const field of Object.values(type.getFields())) { const fieldValue = obj[field.name]; if (fieldValue !== undefined) { snapshot[field.name] = snapshotInputValue(fieldValue, field.type); } } return snapshot; } return value; } function isIterableInput(value) { // Match graphql-js: only call Array.from when the iterator is callable. // `Symbol.iterator in value` is true for `{ [Symbol.iterator]: null }`, which // would otherwise snapshot to `[]` instead of a singleton list item. return (value != null && typeof value === 'object' && typeof value[Symbol.iterator] === 'function'); }