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}