values.mjs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. import { inspect } from '../jsutils/inspect.mjs';
  2. import { keyMap } from '../jsutils/keyMap.mjs';
  3. import { printPathArray } from '../jsutils/printPathArray.mjs';
  4. import { GraphQLError } from '../error/GraphQLError.mjs';
  5. import { Kind } from '../language/kinds.mjs';
  6. import { print } from '../language/printer.mjs';
  7. import { isInputType, isNonNullType } from '../type/definition.mjs';
  8. import { coerceInputValue } from '../utilities/coerceInputValue.mjs';
  9. import { typeFromAST } from '../utilities/typeFromAST.mjs';
  10. import { valueFromAST } from '../utilities/valueFromAST.mjs';
  11. /**
  12. * Prepares an object map of variableValues of the correct type based on the
  13. * provided variable definitions and arbitrary input. If the input cannot be
  14. * parsed to match the variable definitions, a GraphQLError will be thrown.
  15. *
  16. * Note: The returned value is a plain Object with a prototype, since it is
  17. * exposed to user code. Care should be taken to not pull values from the
  18. * Object prototype.
  19. */
  20. export function getVariableValues(schema, varDefNodes, inputs, options) {
  21. const errors = [];
  22. const maxErrors =
  23. options === null || options === void 0 ? void 0 : options.maxErrors;
  24. try {
  25. const coerced = coerceVariableValues(
  26. schema,
  27. varDefNodes,
  28. inputs,
  29. (error) => {
  30. if (maxErrors != null && errors.length >= maxErrors) {
  31. throw new GraphQLError(
  32. 'Too many errors processing variables, error limit reached. Execution aborted.',
  33. );
  34. }
  35. errors.push(error);
  36. },
  37. );
  38. if (errors.length === 0) {
  39. return {
  40. coerced,
  41. };
  42. }
  43. } catch (error) {
  44. errors.push(error);
  45. }
  46. return {
  47. errors,
  48. };
  49. }
  50. function coerceVariableValues(schema, varDefNodes, inputs, onError) {
  51. const coercedValues = {};
  52. for (const varDefNode of varDefNodes) {
  53. const varName = varDefNode.variable.name.value;
  54. const varType = typeFromAST(schema, varDefNode.type);
  55. if (!isInputType(varType)) {
  56. // Must use input types for variables. This should be caught during
  57. // validation, however is checked again here for safety.
  58. const varTypeStr = print(varDefNode.type);
  59. onError(
  60. new GraphQLError(
  61. `Variable "$${varName}" expected value of type "${varTypeStr}" which cannot be used as an input type.`,
  62. {
  63. nodes: varDefNode.type,
  64. },
  65. ),
  66. );
  67. continue;
  68. }
  69. if (!hasOwnProperty(inputs, varName)) {
  70. if (varDefNode.defaultValue) {
  71. coercedValues[varName] = valueFromAST(varDefNode.defaultValue, varType);
  72. } else if (isNonNullType(varType)) {
  73. const varTypeStr = inspect(varType);
  74. onError(
  75. new GraphQLError(
  76. `Variable "$${varName}" of required type "${varTypeStr}" was not provided.`,
  77. {
  78. nodes: varDefNode,
  79. },
  80. ),
  81. );
  82. }
  83. continue;
  84. }
  85. const value = inputs[varName];
  86. if (value === null && isNonNullType(varType)) {
  87. const varTypeStr = inspect(varType);
  88. onError(
  89. new GraphQLError(
  90. `Variable "$${varName}" of non-null type "${varTypeStr}" must not be null.`,
  91. {
  92. nodes: varDefNode,
  93. },
  94. ),
  95. );
  96. continue;
  97. }
  98. coercedValues[varName] = coerceInputValue(
  99. value,
  100. varType,
  101. (path, invalidValue, error) => {
  102. let prefix =
  103. `Variable "$${varName}" got invalid value ` + inspect(invalidValue);
  104. if (path.length > 0) {
  105. prefix += ` at "${varName}${printPathArray(path)}"`;
  106. }
  107. onError(
  108. new GraphQLError(prefix + '; ' + error.message, {
  109. nodes: varDefNode,
  110. originalError: error,
  111. }),
  112. );
  113. },
  114. );
  115. }
  116. return coercedValues;
  117. }
  118. /**
  119. * Prepares an object map of argument values given a list of argument
  120. * definitions and list of argument AST nodes.
  121. *
  122. * Note: The returned value is a plain Object with a prototype, since it is
  123. * exposed to user code. Care should be taken to not pull values from the
  124. * Object prototype.
  125. */
  126. export function getArgumentValues(def, node, variableValues) {
  127. var _node$arguments;
  128. const coercedValues = {}; // FIXME: https://github.com/graphql/graphql-js/issues/2203
  129. /* c8 ignore next */
  130. const argumentNodes =
  131. (_node$arguments = node.arguments) !== null && _node$arguments !== void 0
  132. ? _node$arguments
  133. : [];
  134. const argNodeMap = keyMap(argumentNodes, (arg) => arg.name.value);
  135. for (const argDef of def.args) {
  136. const name = argDef.name;
  137. const argType = argDef.type;
  138. const argumentNode = argNodeMap[name];
  139. if (!argumentNode) {
  140. if (argDef.defaultValue !== undefined) {
  141. coercedValues[name] = argDef.defaultValue;
  142. } else if (isNonNullType(argType)) {
  143. throw new GraphQLError(
  144. `Argument "${name}" of required type "${inspect(argType)}" ` +
  145. 'was not provided.',
  146. {
  147. nodes: node,
  148. },
  149. );
  150. }
  151. continue;
  152. }
  153. const valueNode = argumentNode.value;
  154. let isNull = valueNode.kind === Kind.NULL;
  155. if (valueNode.kind === Kind.VARIABLE) {
  156. const variableName = valueNode.name.value;
  157. if (
  158. variableValues == null ||
  159. !hasOwnProperty(variableValues, variableName)
  160. ) {
  161. if (argDef.defaultValue !== undefined) {
  162. coercedValues[name] = argDef.defaultValue;
  163. } else if (isNonNullType(argType)) {
  164. throw new GraphQLError(
  165. `Argument "${name}" of required type "${inspect(argType)}" ` +
  166. `was provided the variable "$${variableName}" which was not provided a runtime value.`,
  167. {
  168. nodes: valueNode,
  169. },
  170. );
  171. }
  172. continue;
  173. }
  174. isNull = variableValues[variableName] == null;
  175. }
  176. if (isNull && isNonNullType(argType)) {
  177. throw new GraphQLError(
  178. `Argument "${name}" of non-null type "${inspect(argType)}" ` +
  179. 'must not be null.',
  180. {
  181. nodes: valueNode,
  182. },
  183. );
  184. }
  185. const coercedValue = valueFromAST(valueNode, argType, variableValues);
  186. if (coercedValue === undefined) {
  187. // Note: ValuesOfCorrectTypeRule validation should catch this before
  188. // execution. This is a runtime check to ensure execution does not
  189. // continue with an invalid argument value.
  190. throw new GraphQLError(
  191. `Argument "${name}" has invalid value ${print(valueNode)}.`,
  192. {
  193. nodes: valueNode,
  194. },
  195. );
  196. }
  197. coercedValues[name] = coercedValue;
  198. }
  199. return coercedValues;
  200. }
  201. /**
  202. * Prepares an object map of argument values given a directive definition
  203. * and a AST node which may contain directives. Optionally also accepts a map
  204. * of variable values.
  205. *
  206. * If the directive does not exist on the node, returns undefined.
  207. *
  208. * Note: The returned value is a plain Object with a prototype, since it is
  209. * exposed to user code. Care should be taken to not pull values from the
  210. * Object prototype.
  211. */
  212. export function getDirectiveValues(directiveDef, node, variableValues) {
  213. var _node$directives;
  214. const directiveNode =
  215. (_node$directives = node.directives) === null || _node$directives === void 0
  216. ? void 0
  217. : _node$directives.find(
  218. (directive) => directive.name.value === directiveDef.name,
  219. );
  220. if (directiveNode) {
  221. return getArgumentValues(directiveDef, directiveNode, variableValues);
  222. }
  223. }
  224. function hasOwnProperty(obj, prop) {
  225. return Object.prototype.hasOwnProperty.call(obj, prop);
  226. }