UNPKG

9.87 kBJavaScriptView Raw
1import find from '../polyfills/find';
2import objectValues from '../polyfills/objectValues';
3import inspect from '../jsutils/inspect';
4import toObjMap from '../jsutils/toObjMap';
5import devAssert from '../jsutils/devAssert';
6import instanceOf from '../jsutils/instanceOf';
7import isObjectLike from '../jsutils/isObjectLike';
8import defineToStringTag from '../jsutils/defineToStringTag';
9import { __Schema } from './introspection';
10import { GraphQLDirective, isDirective, specifiedDirectives } from './directives';
11import { isObjectType, isInterfaceType, isUnionType, isInputObjectType, getNamedType } from './definition';
12/**
13 * Test if the given value is a GraphQL schema.
14 */
15
16// eslint-disable-next-line no-redeclare
17export function isSchema(schema) {
18 return instanceOf(schema, GraphQLSchema);
19}
20export function assertSchema(schema) {
21 if (!isSchema(schema)) {
22 throw new Error("Expected ".concat(inspect(schema), " to be a GraphQL schema."));
23 }
24
25 return schema;
26}
27/**
28 * Schema Definition
29 *
30 * A Schema is created by supplying the root types of each type of operation,
31 * query and mutation (optional). A schema definition is then supplied to the
32 * validator and executor.
33 *
34 * Example:
35 *
36 * const MyAppSchema = new GraphQLSchema({
37 * query: MyAppQueryRootType,
38 * mutation: MyAppMutationRootType,
39 * })
40 *
41 * Note: When the schema is constructed, by default only the types that are
42 * reachable by traversing the root types are included, other types must be
43 * explicitly referenced.
44 *
45 * Example:
46 *
47 * const characterInterface = new GraphQLInterfaceType({
48 * name: 'Character',
49 * ...
50 * });
51 *
52 * const humanType = new GraphQLObjectType({
53 * name: 'Human',
54 * interfaces: [characterInterface],
55 * ...
56 * });
57 *
58 * const droidType = new GraphQLObjectType({
59 * name: 'Droid',
60 * interfaces: [characterInterface],
61 * ...
62 * });
63 *
64 * const schema = new GraphQLSchema({
65 * query: new GraphQLObjectType({
66 * name: 'Query',
67 * fields: {
68 * hero: { type: characterInterface, ... },
69 * }
70 * }),
71 * ...
72 * // Since this schema references only the `Character` interface it's
73 * // necessary to explicitly list the types that implement it if
74 * // you want them to be included in the final schema.
75 * types: [humanType, droidType],
76 * })
77 *
78 * Note: If an array of `directives` are provided to GraphQLSchema, that will be
79 * the exact list of directives represented and allowed. If `directives` is not
80 * provided then a default set of the specified directives (e.g. @include and
81 * @skip) will be used. If you wish to provide *additional* directives to these
82 * specified directives, you must explicitly declare them. Example:
83 *
84 * const MyAppSchema = new GraphQLSchema({
85 * ...
86 * directives: specifiedDirectives.concat([ myCustomDirective ]),
87 * })
88 *
89 */
90
91export var GraphQLSchema =
92/*#__PURE__*/
93function () {
94 // Used as a cache for validateSchema().
95 // Referenced by validateSchema().
96 function GraphQLSchema(config) {
97 // If this schema was built from a source known to be valid, then it may be
98 // marked with assumeValid to avoid an additional type system validation.
99 if (config && config.assumeValid) {
100 this.__validationErrors = [];
101 } else {
102 this.__validationErrors = undefined; // Otherwise check for common mistakes during construction to produce
103 // clear and early error messages.
104
105 isObjectLike(config) || devAssert(0, 'Must provide configuration object.');
106 !config.types || Array.isArray(config.types) || devAssert(0, "\"types\" must be Array if provided but got: ".concat(inspect(config.types), "."));
107 !config.directives || Array.isArray(config.directives) || devAssert(0, '"directives" must be Array if provided but got: ' + "".concat(inspect(config.directives), "."));
108 !config.allowedLegacyNames || Array.isArray(config.allowedLegacyNames) || devAssert(0, '"allowedLegacyNames" must be Array if provided but got: ' + "".concat(inspect(config.allowedLegacyNames), "."));
109 }
110
111 this.extensions = config.extensions && toObjMap(config.extensions);
112 this.astNode = config.astNode;
113 this.extensionASTNodes = config.extensionASTNodes;
114 this.__allowedLegacyNames = config.allowedLegacyNames || [];
115 this._queryType = config.query;
116 this._mutationType = config.mutation;
117 this._subscriptionType = config.subscription; // Provide specified directives (e.g. @include and @skip) by default.
118
119 this._directives = config.directives || specifiedDirectives; // Build type map now to detect any errors within this schema.
120
121 var initialTypes = [this._queryType, this._mutationType, this._subscriptionType, __Schema].concat(config.types); // Keep track of all types referenced within the schema.
122
123 var typeMap = Object.create(null); // First by deeply visiting all initial types.
124
125 typeMap = initialTypes.reduce(typeMapReducer, typeMap); // Then by deeply visiting all directive types.
126
127 typeMap = this._directives.reduce(typeMapDirectiveReducer, typeMap); // Storing the resulting map for reference by the schema.
128
129 this._typeMap = typeMap;
130 this._possibleTypeMap = Object.create(null); // Keep track of all implementations by interface name.
131
132 this._implementations = Object.create(null);
133
134 for (var _i2 = 0, _objectValues2 = objectValues(this._typeMap); _i2 < _objectValues2.length; _i2++) {
135 var type = _objectValues2[_i2];
136
137 if (isObjectType(type)) {
138 for (var _i4 = 0, _type$getInterfaces2 = type.getInterfaces(); _i4 < _type$getInterfaces2.length; _i4++) {
139 var iface = _type$getInterfaces2[_i4];
140
141 if (isInterfaceType(iface)) {
142 var impls = this._implementations[iface.name];
143
144 if (impls) {
145 impls.push(type);
146 } else {
147 this._implementations[iface.name] = [type];
148 }
149 }
150 }
151 }
152 }
153 }
154
155 var _proto = GraphQLSchema.prototype;
156
157 _proto.getQueryType = function getQueryType() {
158 return this._queryType;
159 };
160
161 _proto.getMutationType = function getMutationType() {
162 return this._mutationType;
163 };
164
165 _proto.getSubscriptionType = function getSubscriptionType() {
166 return this._subscriptionType;
167 };
168
169 _proto.getTypeMap = function getTypeMap() {
170 return this._typeMap;
171 };
172
173 _proto.getType = function getType(name) {
174 return this.getTypeMap()[name];
175 };
176
177 _proto.getPossibleTypes = function getPossibleTypes(abstractType) {
178 if (isUnionType(abstractType)) {
179 return abstractType.getTypes();
180 }
181
182 return this._implementations[abstractType.name] || [];
183 };
184
185 _proto.isPossibleType = function isPossibleType(abstractType, possibleType) {
186 if (this._possibleTypeMap[abstractType.name] == null) {
187 var map = Object.create(null);
188
189 for (var _i6 = 0, _this$getPossibleType2 = this.getPossibleTypes(abstractType); _i6 < _this$getPossibleType2.length; _i6++) {
190 var type = _this$getPossibleType2[_i6];
191 map[type.name] = true;
192 }
193
194 this._possibleTypeMap[abstractType.name] = map;
195 }
196
197 return Boolean(this._possibleTypeMap[abstractType.name][possibleType.name]);
198 };
199
200 _proto.getDirectives = function getDirectives() {
201 return this._directives;
202 };
203
204 _proto.getDirective = function getDirective(name) {
205 return find(this.getDirectives(), function (directive) {
206 return directive.name === name;
207 });
208 };
209
210 _proto.toConfig = function toConfig() {
211 return {
212 query: this.getQueryType(),
213 mutation: this.getMutationType(),
214 subscription: this.getSubscriptionType(),
215 types: objectValues(this.getTypeMap()),
216 directives: this.getDirectives().slice(),
217 extensions: this.extensions,
218 astNode: this.astNode,
219 extensionASTNodes: this.extensionASTNodes || [],
220 assumeValid: this.__validationErrors !== undefined,
221 allowedLegacyNames: this.__allowedLegacyNames
222 };
223 };
224
225 return GraphQLSchema;
226}(); // Conditionally apply `[Symbol.toStringTag]` if `Symbol`s are supported
227
228defineToStringTag(GraphQLSchema);
229
230function typeMapReducer(map, type) {
231 if (!type) {
232 return map;
233 }
234
235 var namedType = getNamedType(type);
236 var seenType = map[namedType.name];
237
238 if (seenType) {
239 if (seenType !== namedType) {
240 throw new Error("Schema must contain uniquely named types but contains multiple types named \"".concat(namedType.name, "\"."));
241 }
242
243 return map;
244 }
245
246 map[namedType.name] = namedType;
247 var reducedMap = map;
248
249 if (isUnionType(namedType)) {
250 reducedMap = namedType.getTypes().reduce(typeMapReducer, reducedMap);
251 }
252
253 if (isObjectType(namedType)) {
254 reducedMap = namedType.getInterfaces().reduce(typeMapReducer, reducedMap);
255 }
256
257 if (isObjectType(namedType) || isInterfaceType(namedType)) {
258 for (var _i8 = 0, _objectValues4 = objectValues(namedType.getFields()); _i8 < _objectValues4.length; _i8++) {
259 var field = _objectValues4[_i8];
260 var fieldArgTypes = field.args.map(function (arg) {
261 return arg.type;
262 });
263 reducedMap = fieldArgTypes.reduce(typeMapReducer, reducedMap);
264 reducedMap = typeMapReducer(reducedMap, field.type);
265 }
266 }
267
268 if (isInputObjectType(namedType)) {
269 for (var _i10 = 0, _objectValues6 = objectValues(namedType.getFields()); _i10 < _objectValues6.length; _i10++) {
270 var _field = _objectValues6[_i10];
271 reducedMap = typeMapReducer(reducedMap, _field.type);
272 }
273 }
274
275 return reducedMap;
276}
277
278function typeMapDirectiveReducer(map, directive) {
279 // Directives are not validated until validateSchema() is called.
280 if (!isDirective(directive)) {
281 return map;
282 }
283
284 return directive.args.reduce(function (_map, arg) {
285 return typeMapReducer(_map, arg.type);
286 }, map);
287}