valueFromAST.js.flow 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // @flow strict
  2. import objectValues from '../polyfills/objectValues';
  3. import keyMap from '../jsutils/keyMap';
  4. import inspect from '../jsutils/inspect';
  5. import invariant from '../jsutils/invariant';
  6. import isInvalid from '../jsutils/isInvalid';
  7. import { type ObjMap } from '../jsutils/ObjMap';
  8. import { Kind } from '../language/kinds';
  9. import { type ValueNode } from '../language/ast';
  10. import {
  11. type GraphQLInputType,
  12. isScalarType,
  13. isEnumType,
  14. isInputObjectType,
  15. isListType,
  16. isNonNullType,
  17. } from '../type/definition';
  18. /**
  19. * Produces a JavaScript value given a GraphQL Value AST.
  20. *
  21. * A GraphQL type must be provided, which will be used to interpret different
  22. * GraphQL Value literals.
  23. *
  24. * Returns `undefined` when the value could not be validly coerced according to
  25. * the provided type.
  26. *
  27. * | GraphQL Value | JSON Value |
  28. * | -------------------- | ------------- |
  29. * | Input Object | Object |
  30. * | List | Array |
  31. * | Boolean | Boolean |
  32. * | String | String |
  33. * | Int / Float | Number |
  34. * | Enum Value | Mixed |
  35. * | NullValue | null |
  36. *
  37. */
  38. export function valueFromAST(
  39. valueNode: ?ValueNode,
  40. type: GraphQLInputType,
  41. variables?: ?ObjMap<mixed>,
  42. ): mixed | void {
  43. if (!valueNode) {
  44. // When there is no node, then there is also no value.
  45. // Importantly, this is different from returning the value null.
  46. return;
  47. }
  48. if (isNonNullType(type)) {
  49. if (valueNode.kind === Kind.NULL) {
  50. return; // Invalid: intentionally return no value.
  51. }
  52. return valueFromAST(valueNode, type.ofType, variables);
  53. }
  54. if (valueNode.kind === Kind.NULL) {
  55. // This is explicitly returning the value null.
  56. return null;
  57. }
  58. if (valueNode.kind === Kind.VARIABLE) {
  59. const variableName = valueNode.name.value;
  60. if (!variables || isInvalid(variables[variableName])) {
  61. // No valid return value.
  62. return;
  63. }
  64. const variableValue = variables[variableName];
  65. if (variableValue === null && isNonNullType(type)) {
  66. return; // Invalid: intentionally return no value.
  67. }
  68. // Note: This does no further checking that this variable is correct.
  69. // This assumes that this query has been validated and the variable
  70. // usage here is of the correct type.
  71. return variableValue;
  72. }
  73. if (isListType(type)) {
  74. const itemType = type.ofType;
  75. if (valueNode.kind === Kind.LIST) {
  76. const coercedValues = [];
  77. for (const itemNode of valueNode.values) {
  78. if (isMissingVariable(itemNode, variables)) {
  79. // If an array contains a missing variable, it is either coerced to
  80. // null or if the item type is non-null, it considered invalid.
  81. if (isNonNullType(itemType)) {
  82. return; // Invalid: intentionally return no value.
  83. }
  84. coercedValues.push(null);
  85. } else {
  86. const itemValue = valueFromAST(itemNode, itemType, variables);
  87. if (isInvalid(itemValue)) {
  88. return; // Invalid: intentionally return no value.
  89. }
  90. coercedValues.push(itemValue);
  91. }
  92. }
  93. return coercedValues;
  94. }
  95. const coercedValue = valueFromAST(valueNode, itemType, variables);
  96. if (isInvalid(coercedValue)) {
  97. return; // Invalid: intentionally return no value.
  98. }
  99. return [coercedValue];
  100. }
  101. if (isInputObjectType(type)) {
  102. if (valueNode.kind !== Kind.OBJECT) {
  103. return; // Invalid: intentionally return no value.
  104. }
  105. const coercedObj = Object.create(null);
  106. const fieldNodes = keyMap(valueNode.fields, field => field.name.value);
  107. for (const field of objectValues(type.getFields())) {
  108. const fieldNode = fieldNodes[field.name];
  109. if (!fieldNode || isMissingVariable(fieldNode.value, variables)) {
  110. if (field.defaultValue !== undefined) {
  111. coercedObj[field.name] = field.defaultValue;
  112. } else if (isNonNullType(field.type)) {
  113. return; // Invalid: intentionally return no value.
  114. }
  115. continue;
  116. }
  117. const fieldValue = valueFromAST(fieldNode.value, field.type, variables);
  118. if (isInvalid(fieldValue)) {
  119. return; // Invalid: intentionally return no value.
  120. }
  121. coercedObj[field.name] = fieldValue;
  122. }
  123. return coercedObj;
  124. }
  125. if (isEnumType(type)) {
  126. if (valueNode.kind !== Kind.ENUM) {
  127. return; // Invalid: intentionally return no value.
  128. }
  129. const enumValue = type.getValue(valueNode.value);
  130. if (!enumValue) {
  131. return; // Invalid: intentionally return no value.
  132. }
  133. return enumValue.value;
  134. }
  135. if (isScalarType(type)) {
  136. // Scalars fulfill parsing a literal value via parseLiteral().
  137. // Invalid values represent a failure to parse correctly, in which case
  138. // no value is returned.
  139. let result;
  140. try {
  141. result = type.parseLiteral(valueNode, variables);
  142. } catch (_error) {
  143. return; // Invalid: intentionally return no value.
  144. }
  145. if (isInvalid(result)) {
  146. return; // Invalid: intentionally return no value.
  147. }
  148. return result;
  149. }
  150. // Not reachable. All possible input types have been considered.
  151. invariant(false, 'Unexpected input type: ' + inspect((type: empty)));
  152. }
  153. // Returns true if the provided valueNode is a variable which is not defined
  154. // in the set of variables.
  155. function isMissingVariable(valueNode, variables) {
  156. return (
  157. valueNode.kind === Kind.VARIABLE &&
  158. (!variables || isInvalid(variables[valueNode.name.value]))
  159. );
  160. }