coerceInputValue.mjs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import { didYouMean } from '../jsutils/didYouMean.mjs';
  2. import { inspect } from '../jsutils/inspect.mjs';
  3. import { invariant } from '../jsutils/invariant.mjs';
  4. import { isIterableObject } from '../jsutils/isIterableObject.mjs';
  5. import { isObjectLike } from '../jsutils/isObjectLike.mjs';
  6. import { addPath, pathToArray } from '../jsutils/Path.mjs';
  7. import { printPathArray } from '../jsutils/printPathArray.mjs';
  8. import { suggestionList } from '../jsutils/suggestionList.mjs';
  9. import { GraphQLError } from '../error/GraphQLError.mjs';
  10. import {
  11. isInputObjectType,
  12. isLeafType,
  13. isListType,
  14. isNonNullType,
  15. } from '../type/definition.mjs';
  16. /**
  17. * Coerces a JavaScript value given a GraphQL Input Type.
  18. */
  19. export function coerceInputValue(inputValue, type, onError = defaultOnError) {
  20. return coerceInputValueImpl(inputValue, type, onError, undefined);
  21. }
  22. function defaultOnError(path, invalidValue, error) {
  23. let errorPrefix = 'Invalid value ' + inspect(invalidValue);
  24. if (path.length > 0) {
  25. errorPrefix += ` at "value${printPathArray(path)}"`;
  26. }
  27. error.message = errorPrefix + ': ' + error.message;
  28. throw error;
  29. }
  30. function coerceInputValueImpl(inputValue, type, onError, path) {
  31. if (isNonNullType(type)) {
  32. if (inputValue != null) {
  33. return coerceInputValueImpl(inputValue, type.ofType, onError, path);
  34. }
  35. onError(
  36. pathToArray(path),
  37. inputValue,
  38. new GraphQLError(
  39. `Expected non-nullable type "${inspect(type)}" not to be null.`,
  40. ),
  41. );
  42. return;
  43. }
  44. if (inputValue == null) {
  45. // Explicitly return the value null.
  46. return null;
  47. }
  48. if (isListType(type)) {
  49. const itemType = type.ofType;
  50. if (isIterableObject(inputValue)) {
  51. return Array.from(inputValue, (itemValue, index) => {
  52. const itemPath = addPath(path, index, undefined);
  53. return coerceInputValueImpl(itemValue, itemType, onError, itemPath);
  54. });
  55. } // Lists accept a non-list value as a list of one.
  56. return [coerceInputValueImpl(inputValue, itemType, onError, path)];
  57. }
  58. if (isInputObjectType(type)) {
  59. if (!isObjectLike(inputValue)) {
  60. onError(
  61. pathToArray(path),
  62. inputValue,
  63. new GraphQLError(`Expected type "${type.name}" to be an object.`),
  64. );
  65. return;
  66. }
  67. const coercedValue = {};
  68. const fieldDefs = type.getFields();
  69. for (const field of Object.values(fieldDefs)) {
  70. const fieldValue = inputValue[field.name];
  71. if (fieldValue === undefined) {
  72. if (field.defaultValue !== undefined) {
  73. coercedValue[field.name] = field.defaultValue;
  74. } else if (isNonNullType(field.type)) {
  75. const typeStr = inspect(field.type);
  76. onError(
  77. pathToArray(path),
  78. inputValue,
  79. new GraphQLError(
  80. `Field "${field.name}" of required type "${typeStr}" was not provided.`,
  81. ),
  82. );
  83. }
  84. continue;
  85. }
  86. coercedValue[field.name] = coerceInputValueImpl(
  87. fieldValue,
  88. field.type,
  89. onError,
  90. addPath(path, field.name, type.name),
  91. );
  92. } // Ensure every provided field is defined.
  93. for (const fieldName of Object.keys(inputValue)) {
  94. if (!fieldDefs[fieldName]) {
  95. const suggestions = suggestionList(
  96. fieldName,
  97. Object.keys(type.getFields()),
  98. );
  99. onError(
  100. pathToArray(path),
  101. inputValue,
  102. new GraphQLError(
  103. `Field "${fieldName}" is not defined by type "${type.name}".` +
  104. didYouMean(suggestions),
  105. ),
  106. );
  107. }
  108. }
  109. return coercedValue;
  110. }
  111. if (isLeafType(type)) {
  112. let parseResult; // Scalars and Enums determine if a input value is valid via parseValue(),
  113. // which can throw to indicate failure. If it throws, maintain a reference
  114. // to the original error.
  115. try {
  116. parseResult = type.parseValue(inputValue);
  117. } catch (error) {
  118. if (error instanceof GraphQLError) {
  119. onError(pathToArray(path), inputValue, error);
  120. } else {
  121. onError(
  122. pathToArray(path),
  123. inputValue,
  124. new GraphQLError(`Expected type "${type.name}". ` + error.message, {
  125. originalError: error,
  126. }),
  127. );
  128. }
  129. return;
  130. }
  131. if (parseResult === undefined) {
  132. onError(
  133. pathToArray(path),
  134. inputValue,
  135. new GraphQLError(`Expected type "${type.name}".`),
  136. );
  137. }
  138. return parseResult;
  139. }
  140. /* c8 ignore next 3 */
  141. // Not reachable, all possible types have been considered.
  142. false || invariant(false, 'Unexpected input type: ' + inspect(type));
  143. }