1 | "use strict";
|
2 |
|
3 | var __extends = (this && this.__extends) || (function () {
|
4 | var extendStatics = Object.setPrototypeOf ||
|
5 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
6 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
7 | return function (d, b) {
|
8 | extendStatics(d, b);
|
9 | function __() { this.constructor = d; }
|
10 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
11 | };
|
12 | })();
|
13 | Object.defineProperty(exports, "__esModule", { value: true });
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | var graphql_1 = require("graphql");
|
19 | var values_1 = require("graphql/execution/values");
|
20 | var deprecated_decorator_1 = require("deprecated-decorator");
|
21 | var mergeDeep_1 = require("./mergeDeep");
|
22 |
|
23 |
|
24 | var SchemaError = (function (_super) {
|
25 | __extends(SchemaError, _super);
|
26 | function SchemaError(message) {
|
27 | var _this = _super.call(this, message) || this;
|
28 | _this.message = message;
|
29 | Error.captureStackTrace(_this, _this.constructor);
|
30 | return _this;
|
31 | }
|
32 | return SchemaError;
|
33 | }(Error));
|
34 | exports.SchemaError = SchemaError;
|
35 |
|
36 | function _generateSchema(typeDefinitions, resolveFunctions, logger,
|
37 | // TODO: rename to allowUndefinedInResolve to be consistent
|
38 | allowUndefinedInResolve, resolverValidationOptions, directiveResolvers, parseOptions) {
|
39 | if (typeof resolverValidationOptions !== 'object') {
|
40 | throw new SchemaError('Expected `resolverValidationOptions` to be an object');
|
41 | }
|
42 | if (!typeDefinitions) {
|
43 | throw new SchemaError('Must provide typeDefs');
|
44 | }
|
45 | if (!resolveFunctions) {
|
46 | throw new SchemaError('Must provide resolvers');
|
47 | }
|
48 | var resolvers = Array.isArray(resolveFunctions)
|
49 | ? resolveFunctions
|
50 | .filter(function (resolverObj) { return typeof resolverObj === 'object'; })
|
51 | .reduce(mergeDeep_1.default, {})
|
52 | : resolveFunctions;
|
53 |
|
54 | var schema = buildSchemaFromTypeDefinitions(typeDefinitions, parseOptions);
|
55 | addResolveFunctionsToSchema(schema, resolvers, resolverValidationOptions);
|
56 | assertResolveFunctionsPresent(schema, resolverValidationOptions);
|
57 | if (!allowUndefinedInResolve) {
|
58 | addCatchUndefinedToSchema(schema);
|
59 | }
|
60 | if (logger) {
|
61 | addErrorLoggingToSchema(schema, logger);
|
62 | }
|
63 | if (directiveResolvers) {
|
64 | attachDirectiveResolvers(schema, directiveResolvers);
|
65 | }
|
66 | return schema;
|
67 | }
|
68 | function makeExecutableSchema(_a) {
|
69 | var typeDefs = _a.typeDefs, _b = _a.resolvers, resolvers = _b === void 0 ? {} : _b, connectors = _a.connectors, logger = _a.logger, _c = _a.allowUndefinedInResolve, allowUndefinedInResolve = _c === void 0 ? true : _c, _d = _a.resolverValidationOptions, resolverValidationOptions = _d === void 0 ? {} : _d, _e = _a.directiveResolvers, directiveResolvers = _e === void 0 ? null : _e, _f = _a.parseOptions, parseOptions = _f === void 0 ? {} : _f;
|
70 | var jsSchema = _generateSchema(typeDefs, resolvers, logger, allowUndefinedInResolve, resolverValidationOptions, directiveResolvers, parseOptions);
|
71 | if (typeof resolvers['__schema'] === 'function') {
|
72 |
|
73 |
|
74 | addSchemaLevelResolveFunction(jsSchema, resolvers['__schema']);
|
75 | }
|
76 | if (connectors) {
|
77 |
|
78 |
|
79 | attachConnectorsToContext(jsSchema, connectors);
|
80 | }
|
81 | return jsSchema;
|
82 | }
|
83 | exports.makeExecutableSchema = makeExecutableSchema;
|
84 | function isDocumentNode(typeDefinitions) {
|
85 | return typeDefinitions.kind !== undefined;
|
86 | }
|
87 | function uniq(array) {
|
88 | return array.reduce(function (accumulator, currentValue) {
|
89 | return accumulator.indexOf(currentValue) === -1
|
90 | ? accumulator.concat([currentValue]) : accumulator;
|
91 | }, []);
|
92 | }
|
93 | function concatenateTypeDefs(typeDefinitionsAry, calledFunctionRefs) {
|
94 | if (calledFunctionRefs === void 0) { calledFunctionRefs = []; }
|
95 | var resolvedTypeDefinitions = [];
|
96 | typeDefinitionsAry.forEach(function (typeDef) {
|
97 | if (isDocumentNode(typeDef)) {
|
98 | typeDef = graphql_1.print(typeDef);
|
99 | }
|
100 | if (typeof typeDef === 'function') {
|
101 | if (calledFunctionRefs.indexOf(typeDef) === -1) {
|
102 | calledFunctionRefs.push(typeDef);
|
103 | resolvedTypeDefinitions = resolvedTypeDefinitions.concat(concatenateTypeDefs(typeDef(), calledFunctionRefs));
|
104 | }
|
105 | }
|
106 | else if (typeof typeDef === 'string') {
|
107 | resolvedTypeDefinitions.push(typeDef.trim());
|
108 | }
|
109 | else {
|
110 | var type = typeof typeDef;
|
111 | throw new SchemaError("typeDef array must contain only strings and functions, got " + type);
|
112 | }
|
113 | });
|
114 | return uniq(resolvedTypeDefinitions.map(function (x) { return x.trim(); })).join('\n');
|
115 | }
|
116 | exports.concatenateTypeDefs = concatenateTypeDefs;
|
117 | function buildSchemaFromTypeDefinitions(typeDefinitions, parseOptions) {
|
118 |
|
119 | var myDefinitions = typeDefinitions;
|
120 | var astDocument;
|
121 | if (isDocumentNode(typeDefinitions)) {
|
122 | astDocument = typeDefinitions;
|
123 | }
|
124 | else if (typeof myDefinitions !== 'string') {
|
125 | if (!Array.isArray(myDefinitions)) {
|
126 | var type = typeof myDefinitions;
|
127 | throw new SchemaError("typeDefs must be a string, array or schema AST, got " + type);
|
128 | }
|
129 | myDefinitions = concatenateTypeDefs(myDefinitions);
|
130 | }
|
131 | if (typeof myDefinitions === 'string') {
|
132 | astDocument = graphql_1.parse(myDefinitions, parseOptions);
|
133 | }
|
134 | var backcompatOptions = { commentDescriptions: true };
|
135 |
|
136 | var schema = graphql_1.buildASTSchema(astDocument, backcompatOptions);
|
137 | var extensionsAst = extractExtensionDefinitions(astDocument);
|
138 | if (extensionsAst.definitions.length > 0) {
|
139 |
|
140 | schema = graphql_1.extendSchema(schema, extensionsAst, backcompatOptions);
|
141 | }
|
142 | return schema;
|
143 | }
|
144 | exports.buildSchemaFromTypeDefinitions = buildSchemaFromTypeDefinitions;
|
145 |
|
146 |
|
147 |
|
148 | var oldTypeExtensionDefinitionKind = 'TypeExtensionDefinition';
|
149 | var newExtensionDefinitionKind = 'ObjectTypeExtension';
|
150 | function extractExtensionDefinitions(ast) {
|
151 | var extensionDefs = ast.definitions.filter(function (def) {
|
152 | return def.kind === oldTypeExtensionDefinitionKind ||
|
153 | def.kind === newExtensionDefinitionKind;
|
154 | });
|
155 | return Object.assign({}, ast, {
|
156 | definitions: extensionDefs,
|
157 | });
|
158 | }
|
159 | exports.extractExtensionDefinitions = extractExtensionDefinitions;
|
160 | function forEachField(schema, fn) {
|
161 | var typeMap = schema.getTypeMap();
|
162 | Object.keys(typeMap).forEach(function (typeName) {
|
163 | var type = typeMap[typeName];
|
164 |
|
165 | if (!graphql_1.getNamedType(type).name.startsWith('__') &&
|
166 | type instanceof graphql_1.GraphQLObjectType) {
|
167 | var fields_1 = type.getFields();
|
168 | Object.keys(fields_1).forEach(function (fieldName) {
|
169 | var field = fields_1[fieldName];
|
170 | fn(field, typeName, fieldName);
|
171 | });
|
172 | }
|
173 | });
|
174 | }
|
175 | exports.forEachField = forEachField;
|
176 |
|
177 |
|
178 |
|
179 |
|
180 | var attachConnectorsToContext = deprecated_decorator_1.deprecated({
|
181 | version: '0.7.0',
|
182 | url: 'https://github.com/apollostack/graphql-tools/issues/140',
|
183 | }, function (schema, connectors) {
|
184 | if (!schema || !(schema instanceof graphql_1.GraphQLSchema)) {
|
185 | throw new Error('schema must be an instance of GraphQLSchema. ' +
|
186 | 'This error could be caused by installing more than one version of GraphQL-JS');
|
187 | }
|
188 | if (typeof connectors !== 'object') {
|
189 | var connectorType = typeof connectors;
|
190 | throw new Error("Expected connectors to be of type object, got " + connectorType);
|
191 | }
|
192 | if (Object.keys(connectors).length === 0) {
|
193 | throw new Error('Expected connectors to not be an empty object');
|
194 | }
|
195 | if (Array.isArray(connectors)) {
|
196 | throw new Error('Expected connectors to be of type object, got Array');
|
197 | }
|
198 | if (schema['_apolloConnectorsAttached']) {
|
199 | throw new Error('Connectors already attached to context, cannot attach more than once');
|
200 | }
|
201 | schema['_apolloConnectorsAttached'] = true;
|
202 | var attachconnectorFn = function (root, args, ctx) {
|
203 | if (typeof ctx !== 'object') {
|
204 |
|
205 |
|
206 | var contextType = typeof ctx;
|
207 | throw new Error("Cannot attach connector because context is not an object: " + contextType);
|
208 | }
|
209 | if (typeof ctx.connectors === 'undefined') {
|
210 | ctx.connectors = {};
|
211 | }
|
212 | Object.keys(connectors).forEach(function (connectorName) {
|
213 | var connector = connectors[connectorName];
|
214 | if (!!connector.prototype) {
|
215 | ctx.connectors[connectorName] = new connector(ctx);
|
216 | }
|
217 | else {
|
218 | throw new Error("Connector must be a function or an class");
|
219 | }
|
220 | });
|
221 | return root;
|
222 | };
|
223 | addSchemaLevelResolveFunction(schema, attachconnectorFn);
|
224 | });
|
225 | exports.attachConnectorsToContext = attachConnectorsToContext;
|
226 |
|
227 |
|
228 | function addSchemaLevelResolveFunction(schema, fn) {
|
229 |
|
230 | var rootTypes = [
|
231 | schema.getQueryType(),
|
232 | schema.getMutationType(),
|
233 | schema.getSubscriptionType(),
|
234 | ].filter(function (x) { return !!x; });
|
235 | rootTypes.forEach(function (type) {
|
236 |
|
237 |
|
238 | var rootResolveFn = runAtMostOncePerRequest(fn);
|
239 | var fields = type.getFields();
|
240 | Object.keys(fields).forEach(function (fieldName) {
|
241 |
|
242 |
|
243 | if (type === schema.getSubscriptionType()) {
|
244 | fields[fieldName].resolve = wrapResolver(fields[fieldName].resolve, fn);
|
245 | }
|
246 | else {
|
247 | fields[fieldName].resolve = wrapResolver(fields[fieldName].resolve, rootResolveFn);
|
248 | }
|
249 | });
|
250 | });
|
251 | }
|
252 | exports.addSchemaLevelResolveFunction = addSchemaLevelResolveFunction;
|
253 | function getFieldsForType(type) {
|
254 | if (type instanceof graphql_1.GraphQLObjectType ||
|
255 | type instanceof graphql_1.GraphQLInterfaceType) {
|
256 | return type.getFields();
|
257 | }
|
258 | else {
|
259 | return undefined;
|
260 | }
|
261 | }
|
262 | function addResolveFunctionsToSchema(schema, resolveFunctions, resolverValidationOptions) {
|
263 | if (resolverValidationOptions === void 0) { resolverValidationOptions = {}; }
|
264 | var _a = resolverValidationOptions.allowResolversNotInSchema, allowResolversNotInSchema = _a === void 0 ? false : _a;
|
265 | Object.keys(resolveFunctions).forEach(function (typeName) {
|
266 | var type = schema.getType(typeName);
|
267 | if (!type && typeName !== '__schema') {
|
268 | if (allowResolversNotInSchema) {
|
269 | return;
|
270 | }
|
271 | throw new SchemaError("\"" + typeName + "\" defined in resolvers, but not in schema");
|
272 | }
|
273 | Object.keys(resolveFunctions[typeName]).forEach(function (fieldName) {
|
274 | if (fieldName.startsWith('__')) {
|
275 |
|
276 |
|
277 | type[fieldName.substring(2)] = resolveFunctions[typeName][fieldName];
|
278 | return;
|
279 | }
|
280 | if (type instanceof graphql_1.GraphQLScalarType) {
|
281 | type[fieldName] = resolveFunctions[typeName][fieldName];
|
282 | return;
|
283 | }
|
284 | if (type instanceof graphql_1.GraphQLEnumType) {
|
285 | if (!type.getValue(fieldName)) {
|
286 | throw new SchemaError(typeName + "." + fieldName + " was defined in resolvers, but enum is not in schema");
|
287 | }
|
288 | type.getValue(fieldName)['value'] =
|
289 | resolveFunctions[typeName][fieldName];
|
290 | return;
|
291 | }
|
292 | var fields = getFieldsForType(type);
|
293 | if (!fields) {
|
294 | if (allowResolversNotInSchema) {
|
295 | return;
|
296 | }
|
297 | throw new SchemaError(typeName + " was defined in resolvers, but it's not an object");
|
298 | }
|
299 | if (!fields[fieldName]) {
|
300 | if (allowResolversNotInSchema) {
|
301 | return;
|
302 | }
|
303 | throw new SchemaError(typeName + "." + fieldName + " defined in resolvers, but not in schema");
|
304 | }
|
305 | var field = fields[fieldName];
|
306 | var fieldResolve = resolveFunctions[typeName][fieldName];
|
307 | if (typeof fieldResolve === 'function') {
|
308 |
|
309 | setFieldProperties(field, { resolve: fieldResolve });
|
310 | }
|
311 | else {
|
312 | if (typeof fieldResolve !== 'object') {
|
313 | throw new SchemaError("Resolver " + typeName + "." + fieldName + " must be object or function");
|
314 | }
|
315 | setFieldProperties(field, fieldResolve);
|
316 | }
|
317 | });
|
318 | });
|
319 | }
|
320 | exports.addResolveFunctionsToSchema = addResolveFunctionsToSchema;
|
321 | function setFieldProperties(field, propertiesObj) {
|
322 | Object.keys(propertiesObj).forEach(function (propertyName) {
|
323 | field[propertyName] = propertiesObj[propertyName];
|
324 | });
|
325 | }
|
326 | function assertResolveFunctionsPresent(schema, resolverValidationOptions) {
|
327 | if (resolverValidationOptions === void 0) { resolverValidationOptions = {}; }
|
328 | var _a = resolverValidationOptions.requireResolversForArgs, requireResolversForArgs = _a === void 0 ? false : _a, _b = resolverValidationOptions.requireResolversForNonScalar, requireResolversForNonScalar = _b === void 0 ? false : _b, _c = resolverValidationOptions.requireResolversForAllFields, requireResolversForAllFields = _c === void 0 ? false : _c;
|
329 | if (requireResolversForAllFields &&
|
330 | (requireResolversForArgs || requireResolversForNonScalar)) {
|
331 | throw new TypeError('requireResolversForAllFields takes precedence over the more specific assertions. ' +
|
332 | 'Please configure either requireResolversForAllFields or requireResolversForArgs / ' +
|
333 | 'requireResolversForNonScalar, but not a combination of them.');
|
334 | }
|
335 | forEachField(schema, function (field, typeName, fieldName) {
|
336 |
|
337 | if (requireResolversForAllFields) {
|
338 | expectResolveFunction(field, typeName, fieldName);
|
339 | }
|
340 |
|
341 | if (requireResolversForArgs && field.args.length > 0) {
|
342 | expectResolveFunction(field, typeName, fieldName);
|
343 | }
|
344 |
|
345 | if (requireResolversForNonScalar &&
|
346 | !(graphql_1.getNamedType(field.type) instanceof graphql_1.GraphQLScalarType)) {
|
347 | expectResolveFunction(field, typeName, fieldName);
|
348 | }
|
349 | });
|
350 | }
|
351 | exports.assertResolveFunctionsPresent = assertResolveFunctionsPresent;
|
352 | function expectResolveFunction(field, typeName, fieldName) {
|
353 | if (!field.resolve) {
|
354 | console.warn(
|
355 |
|
356 | "Resolve function missing for \"" + typeName + "." + fieldName + "\". To disable this warning check https://github.com/apollostack/graphql-tools/issues/131");
|
357 | return;
|
358 | }
|
359 | if (typeof field.resolve !== 'function') {
|
360 | throw new SchemaError("Resolver \"" + typeName + "." + fieldName + "\" must be a function");
|
361 | }
|
362 | }
|
363 | function addErrorLoggingToSchema(schema, logger) {
|
364 | if (!logger) {
|
365 | throw new Error('Must provide a logger');
|
366 | }
|
367 | if (typeof logger.log !== 'function') {
|
368 | throw new Error('Logger.log must be a function');
|
369 | }
|
370 | forEachField(schema, function (field, typeName, fieldName) {
|
371 | var errorHint = typeName + "." + fieldName;
|
372 | field.resolve = decorateWithLogger(field.resolve, logger, errorHint);
|
373 | });
|
374 | }
|
375 | exports.addErrorLoggingToSchema = addErrorLoggingToSchema;
|
376 |
|
377 | function wrapResolver(innerResolver, outerResolver) {
|
378 | return function (obj, args, ctx, info) {
|
379 | return Promise.resolve(outerResolver(obj, args, ctx, info)).then(function (root) {
|
380 | if (innerResolver) {
|
381 | return innerResolver(root, args, ctx, info);
|
382 | }
|
383 | return graphql_1.defaultFieldResolver(root, args, ctx, info);
|
384 | });
|
385 | };
|
386 | }
|
387 | function chainResolvers(resolvers) {
|
388 | return function (root, args, ctx, info) {
|
389 | return resolvers.reduce(function (prev, curResolver) {
|
390 | if (curResolver) {
|
391 | return curResolver(prev, args, ctx, info);
|
392 | }
|
393 | return graphql_1.defaultFieldResolver(prev, args, ctx, info);
|
394 | }, root);
|
395 | };
|
396 | }
|
397 | exports.chainResolvers = chainResolvers;
|
398 |
|
399 |
|
400 |
|
401 |
|
402 |
|
403 | function decorateWithLogger(fn, logger, hint) {
|
404 | if (typeof fn === 'undefined') {
|
405 | fn = graphql_1.defaultFieldResolver;
|
406 | }
|
407 | var logError = function (e) {
|
408 |
|
409 | var newE = new Error();
|
410 | newE.stack = e.stack;
|
411 |
|
412 | if (hint) {
|
413 | newE['originalMessage'] = e.message;
|
414 | newE['message'] = "Error in resolver " + hint + "\n" + e.message;
|
415 | }
|
416 | logger.log(newE);
|
417 | };
|
418 | return function (root, args, ctx, info) {
|
419 | try {
|
420 | var result = fn(root, args, ctx, info);
|
421 |
|
422 | if (result &&
|
423 | typeof result.then === 'function' &&
|
424 | typeof result.catch === 'function') {
|
425 | result.catch(function (reason) {
|
426 |
|
427 | var error = reason instanceof Error ? reason : new Error(reason);
|
428 | logError(error);
|
429 |
|
430 | return reason;
|
431 | });
|
432 | }
|
433 | return result;
|
434 | }
|
435 | catch (e) {
|
436 | logError(e);
|
437 |
|
438 | throw e;
|
439 | }
|
440 | };
|
441 | }
|
442 | function addCatchUndefinedToSchema(schema) {
|
443 | forEachField(schema, function (field, typeName, fieldName) {
|
444 | var errorHint = typeName + "." + fieldName;
|
445 | field.resolve = decorateToCatchUndefined(field.resolve, errorHint);
|
446 | });
|
447 | }
|
448 | exports.addCatchUndefinedToSchema = addCatchUndefinedToSchema;
|
449 | function decorateToCatchUndefined(fn, hint) {
|
450 | if (typeof fn === 'undefined') {
|
451 | fn = graphql_1.defaultFieldResolver;
|
452 | }
|
453 | return function (root, args, ctx, info) {
|
454 | var result = fn(root, args, ctx, info);
|
455 | if (typeof result === 'undefined') {
|
456 | throw new Error("Resolve function for \"" + hint + "\" returned undefined");
|
457 | }
|
458 | return result;
|
459 | };
|
460 | }
|
461 |
|
462 |
|
463 |
|
464 |
|
465 |
|
466 |
|
467 | function runAtMostOncePerRequest(fn) {
|
468 | var value;
|
469 | var randomNumber = Math.random();
|
470 | return function (root, args, ctx, info) {
|
471 | if (!info.operation['__runAtMostOnce']) {
|
472 | info.operation['__runAtMostOnce'] = {};
|
473 | }
|
474 | if (!info.operation['__runAtMostOnce'][randomNumber]) {
|
475 | info.operation['__runAtMostOnce'][randomNumber] = true;
|
476 | value = fn(root, args, ctx, info);
|
477 | }
|
478 | return value;
|
479 | };
|
480 | }
|
481 | function attachDirectiveResolvers(schema, directiveResolvers) {
|
482 | if (typeof directiveResolvers !== 'object') {
|
483 | throw new Error("Expected directiveResolvers to be of type object, got " + typeof directiveResolvers);
|
484 | }
|
485 | if (Array.isArray(directiveResolvers)) {
|
486 | throw new Error('Expected directiveResolvers to be of type object, got Array');
|
487 | }
|
488 | forEachField(schema, function (field) {
|
489 | var directives = field.astNode.directives;
|
490 | directives.forEach(function (directive) {
|
491 | var directiveName = directive.name.value;
|
492 | var resolver = directiveResolvers[directiveName];
|
493 | if (resolver) {
|
494 | var originalResolver_1 = field.resolve || graphql_1.defaultFieldResolver;
|
495 | var Directive = schema.getDirective(directiveName);
|
496 | if (typeof Directive === 'undefined') {
|
497 | throw new Error("Directive @" + directiveName + " is undefined. " +
|
498 | 'Please define in schema before using');
|
499 | }
|
500 | var directiveArgs_1 = values_1.getArgumentValues(Directive, directive);
|
501 | field.resolve = function () {
|
502 | var args = [];
|
503 | for (var _i = 0; _i < arguments.length; _i++) {
|
504 | args[_i] = arguments[_i];
|
505 | }
|
506 | var source = args[0], context = args[2], info = args[3];
|
507 | return resolver(function () {
|
508 | try {
|
509 | var promise = originalResolver_1.call.apply(originalResolver_1, [field].concat(args));
|
510 | if (promise instanceof Promise) {
|
511 | return promise;
|
512 | }
|
513 | return Promise.resolve(promise);
|
514 | }
|
515 | catch (error) {
|
516 | return Promise.reject(error);
|
517 | }
|
518 | }, source, directiveArgs_1, context, info);
|
519 | };
|
520 | }
|
521 | });
|
522 | });
|
523 | }
|
524 | exports.attachDirectiveResolvers = attachDirectiveResolvers;
|
525 |
|
\ | No newline at end of file |