UNPKG

4.05 kBJavaScriptView Raw
1import arrayFrom from "../../polyfills/arrayFrom.mjs";
2import didYouMean from "../../jsutils/didYouMean.mjs";
3import suggestionList from "../../jsutils/suggestionList.mjs";
4import naturalCompare from "../../jsutils/naturalCompare.mjs";
5import { GraphQLError } from "../../error/GraphQLError.mjs";
6import { isObjectType, isInterfaceType, isAbstractType } from "../../type/definition.mjs";
7
8/**
9 * Fields on correct type
10 *
11 * A GraphQL document is only valid if all fields selected are defined by the
12 * parent type, or are an allowed meta field such as __typename.
13 */
14export function FieldsOnCorrectTypeRule(context) {
15 return {
16 Field: function Field(node) {
17 var type = context.getParentType();
18
19 if (type) {
20 var fieldDef = context.getFieldDef();
21
22 if (!fieldDef) {
23 // This field doesn't exist, lets look for suggestions.
24 var schema = context.getSchema();
25 var fieldName = node.name.value; // First determine if there are any suggested types to condition on.
26
27 var suggestion = didYouMean('to use an inline fragment on', getSuggestedTypeNames(schema, type, fieldName)); // If there are no suggested types, then perhaps this was a typo?
28
29 if (suggestion === '') {
30 suggestion = didYouMean(getSuggestedFieldNames(type, fieldName));
31 } // Report an error, including helpful suggestions.
32
33
34 context.reportError(new GraphQLError("Cannot query field \"".concat(fieldName, "\" on type \"").concat(type.name, "\".") + suggestion, node));
35 }
36 }
37 }
38 };
39}
40/**
41 * Go through all of the implementations of type, as well as the interfaces that
42 * they implement. If any of those types include the provided field, suggest them,
43 * sorted by how often the type is referenced.
44 */
45
46function getSuggestedTypeNames(schema, type, fieldName) {
47 if (!isAbstractType(type)) {
48 // Must be an Object type, which does not have possible fields.
49 return [];
50 }
51
52 var suggestedTypes = new Set();
53 var usageCount = Object.create(null);
54
55 for (var _i2 = 0, _schema$getPossibleTy2 = schema.getPossibleTypes(type); _i2 < _schema$getPossibleTy2.length; _i2++) {
56 var possibleType = _schema$getPossibleTy2[_i2];
57
58 if (!possibleType.getFields()[fieldName]) {
59 continue;
60 } // This object type defines this field.
61
62
63 suggestedTypes.add(possibleType);
64 usageCount[possibleType.name] = 1;
65
66 for (var _i4 = 0, _possibleType$getInte2 = possibleType.getInterfaces(); _i4 < _possibleType$getInte2.length; _i4++) {
67 var _usageCount$possibleI;
68
69 var possibleInterface = _possibleType$getInte2[_i4];
70
71 if (!possibleInterface.getFields()[fieldName]) {
72 continue;
73 } // This interface type defines this field.
74
75
76 suggestedTypes.add(possibleInterface);
77 usageCount[possibleInterface.name] = ((_usageCount$possibleI = usageCount[possibleInterface.name]) !== null && _usageCount$possibleI !== void 0 ? _usageCount$possibleI : 0) + 1;
78 }
79 }
80
81 return arrayFrom(suggestedTypes).sort(function (typeA, typeB) {
82 // Suggest both interface and object types based on how common they are.
83 var usageCountDiff = usageCount[typeB.name] - usageCount[typeA.name];
84
85 if (usageCountDiff !== 0) {
86 return usageCountDiff;
87 } // Suggest super types first followed by subtypes
88
89
90 if (isInterfaceType(typeA) && schema.isSubType(typeA, typeB)) {
91 return -1;
92 }
93
94 if (isInterfaceType(typeB) && schema.isSubType(typeB, typeA)) {
95 return 1;
96 }
97
98 return naturalCompare(typeA.name, typeB.name);
99 }).map(function (x) {
100 return x.name;
101 });
102}
103/**
104 * For the field name provided, determine if there are any similar field names
105 * that may be the result of a typo.
106 */
107
108
109function getSuggestedFieldNames(type, fieldName) {
110 if (isObjectType(type) || isInterfaceType(type)) {
111 var possibleFieldNames = Object.keys(type.getFields());
112 return suggestionList(fieldName, possibleFieldNames);
113 } // Otherwise, must be a Union type, which does not define fields.
114
115
116 return [];
117}