1 2import { GraphQLError } from '../../error'; /** 3 * Copyright (c) 2015-present, Facebook, Inc. 4 * 5 * This source code is licensed under the MIT license found in the 6 * LICENSE file in the root directory of this source tree. 7 * 8 * strict 9 */ 10 11import { print } from '../../language/printer'; 12 13import { isScalarType, isEnumType, isInputObjectType, isListType, isNonNullType, getNullableType, getNamedType } from '../../type/definition'; 14 15import isInvalid from '../../jsutils/isInvalid'; 16import keyMap from '../../jsutils/keyMap'; 17import orList from '../../jsutils/orList'; 18import suggestionList from '../../jsutils/suggestionList'; 19 20export function badValueMessage(typeName, valueName, message) { 21 return 'Expected type ' + typeName + ', found ' + valueName + (message ? '; ' + message : '.'); 22} 23 24export function requiredFieldMessage(typeName, fieldName, fieldTypeName) { 25 return 'Field ' + typeName + '.' + fieldName + ' of required type ' + (fieldTypeName + ' was not provided.'); 26} 27 28export function unknownFieldMessage(typeName, fieldName, message) { 29 return 'Field "' + fieldName + '" is not defined by type ' + typeName + (message ? '; ' + message : '.'); 30} 31 32/** 33 * Value literals of correct type 34 * 35 * A GraphQL document is only valid if all value literals are of the type 36 * expected at their position. 37 */ 38export function ValuesOfCorrectType(context) { 39 return { 40 NullValue: function NullValue(node) { 41 var type = context.getInputType(); 42 if (isNonNullType(type)) { 43 context.reportError(new GraphQLError(badValueMessage(String(type), print(node)), node)); 44 } 45 }, 46 ListValue: function ListValue(node) { 47 // Note: TypeInfo will traverse into a list's item type, so look to the 48 // parent input type to check if it is a list. 49 var type = getNullableType(context.getParentInputType()); 50 if (!isListType(type)) { 51 isValidScalar(context, node); 52 return false; // Don't traverse further. 53 } 54 }, 55 ObjectValue: function ObjectValue(node) { 56 var type = getNamedType(context.getInputType()); 57 if (!isInputObjectType(type)) { 58 isValidScalar(context, node); 59 return false; // Don't traverse further. 60 } 61 // Ensure every required field exists. 62 var inputFields = type.getFields(); 63 var fieldNodeMap = keyMap(node.fields, function (field) { 64 return field.name.value; 65 }); 66 Object.keys(inputFields).forEach(function (fieldName) { 67 var fieldType = inputFields[fieldName].type; 68 var fieldNode = fieldNodeMap[fieldName]; 69 if (!fieldNode && isNonNullType(fieldType)) { 70 context.reportError(new GraphQLError(requiredFieldMessage(type.name, fieldName, String(fieldType)), node)); 71 } 72 }); 73 }, 74 ObjectField: function ObjectField(node) { 75 var parentType = getNamedType(context.getParentInputType()); 76 var fieldType = context.getInputType(); 77 if (!fieldType && isInputObjectType(parentType)) { 78 var suggestions = suggestionList(node.name.value, Object.keys(parentType.getFields())); 79 var didYouMean = suggestions.length !== 0 ? 'Did you mean ' + orList(suggestions) + '?' : undefined; 80 context.reportError(new GraphQLError(unknownFieldMessage(parentType.name, node.name.value, didYouMean), node)); 81 } 82 }, 83 EnumValue: function EnumValue(node) { 84 var type = getNamedType(context.getInputType()); 85 if (!isEnumType(type)) { 86 isValidScalar(context, node); 87 } else if (!type.getValue(node.value)) { 88 context.reportError(new GraphQLError(badValueMessage(type.name, print(node), enumTypeSuggestion(type, node)), node)); 89 } 90 }, 91 92 IntValue: function IntValue(node) { 93 return isValidScalar(context, node); 94 }, 95 FloatValue: function FloatValue(node) { 96 return isValidScalar(context, node); 97 }, 98 StringValue: function StringValue(node) { 99 return isValidScalar(context, node); 100 }, 101 BooleanValue: function BooleanValue(node) { 102 return isValidScalar(context, node); 103 } 104 }; 105} 106 107/** 108 * Any value literal may be a valid representation of a Scalar, depending on 109 * that scalar type. 110 */ 111function isValidScalar(context, node) { 112 // Report any error at the full type expected by the location. 113 var locationType = context.getInputType(); 114 if (!locationType) { 115 return; 116 } 117 118 var type = getNamedType(locationType); 119 120 if (!isScalarType(type)) { 121 context.reportError(new GraphQLError(badValueMessage(String(locationType), print(node), enumTypeSuggestion(type, node)), node)); 122 return; 123 } 124 125 // Scalars determine if a literal value is valid via parseLiteral() which 126 // may throw or return an invalid value to indicate failure. 127 try { 128 var parseResult = type.parseLiteral(node, undefined /* variables */); 129 if (isInvalid(parseResult)) { 130 context.reportError(new GraphQLError(badValueMessage(String(locationType), print(node)), node)); 131 } 132 } catch (error) { 133 // Ensure a reference to the original error is maintained. 134 context.reportError(new GraphQLError(badValueMessage(String(locationType), print(node), error.message), node, undefined, undefined, undefined, error)); 135 } 136} 137 138function enumTypeSuggestion(type, node) { 139 if (isEnumType(type)) { 140 var suggestions = suggestionList(print(node), type.getValues().map(function (value) { 141 return value.name; 142 })); 143 if (suggestions.length !== 0) { 144 return 'Did you mean the enum value ' + orList(suggestions) + '?'; 145 } 146 } 147}