UNPKG

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