UNPKG

9.83 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.SchemaUtils = undefined;
7
8var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
9
10var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
11
12var _keys = require('babel-runtime/core-js/object/keys');
13
14var _keys2 = _interopRequireDefault(_keys);
15
16var _path = require('path');
17
18var _path2 = _interopRequireDefault(_path);
19
20var _SyntaxTree = require('./SyntaxTree');
21
22var _GQLBase = require('./GQLBase');
23
24var _GQLEnum = require('./GQLEnum');
25
26var _GQLInterface = require('./GQLInterface');
27
28var _GQLScalar = require('./GQLScalar');
29
30var _neTypes = require('ne-types');
31
32var _utils = require('./utils');
33
34var _lodash = require('lodash');
35
36var _events = require('events');
37
38var _events2 = _interopRequireDefault(_events);
39
40var _graphql = require('graphql');
41
42function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
43
44/**
45 * The SchemaUtils is used by tools such as GQLExpressMiddleware in order to
46 * apply GraphQL Lattice specifics to the build schema.
47 *
48 * @class SchemaUtils
49 */
50let SchemaUtils = exports.SchemaUtils = class SchemaUtils extends _events2.default {
51 /**
52 * Calls all the Lattice post-schema creation routines on a given Schema
53 * using data from a supplied array of classes.
54 *
55 * @param {GraphQLSchema} schema the schema to post-process
56 * @param {Array<GQLBase>} Classes the Classes from which to drive post
57 * processing data from
58 */
59 static injectAll(schema, Classes) {
60 SchemaUtils.injectInterfaceResolvers(schema, Classes);
61 SchemaUtils.injectEnums(schema, Classes);
62 SchemaUtils.injectScalars(schema, Classes);
63 SchemaUtils.injectComments(schema, Classes);
64 }
65
66 /**
67 * Until such time as I can get the reference Facebook GraphQL AST parser to
68 * read and apply descriptions or until such time as I employ the Apollo
69 * AST parser, providing a `static get apiDocs()` getter is the way to get
70 * your descriptions into the proper fields, post schema creation.
71 *
72 * This method walks the types in the registered classes and the supplied
73 * schema type. It then injects the written comments such that they can
74 * be exposed in graphiql and to applications or code that read the meta
75 * fields of a built schema
76 *
77 * @memberof SchemaUtils
78 * @method ⌾⠀injectComments
79 * @static
80 * @since 2.7.0
81 *
82 * @param {Object} schema a built GraphQLSchema object created via buildSchema
83 * or some other alternative but compatible manner
84 * @param {Function[]} Classes these are GQLBase extended classes used to
85 * manipulate the schema with.
86 */
87 static injectComments(schema, Classes) {
88 const {
89 DOC_CLASS, DOC_FIELDS, DOC_QUERIES, DOC_MUTATORS, DOC_SUBSCRIPTIONS,
90 DOC_QUERY, DOC_MUTATION, DOC_SUBSCRIPTION
91 } = _GQLBase.GQLBase;
92
93 for (let Class of Classes) {
94 const docs = Class.apiDocs();
95 const query = schema._typeMap.Query;
96 const mutation = schema._typeMap.Mutation;
97 const subscription = schema._typeMap.Subscription;
98 let type;
99
100 if (type = schema._typeMap[Class.name]) {
101 let fields = type._fields;
102 let values = type._values;
103
104 if (docs[DOC_CLASS]) {
105 type.description = docs[DOC_CLASS];
106 }
107
108 for (let field of (0, _keys2.default)(docs[DOC_FIELDS] || {})) {
109 if (fields && field in fields) {
110 fields[field].description = docs[DOC_FIELDS][field];
111 }
112 if (values) {
113 for (let value of values) {
114 if (value.name === field) {
115 value.description = docs[DOC_FIELDS][field];
116 }
117 }
118 }
119 }
120 }
121
122 for (let [_type, _CONST, _topCONST] of [[query, DOC_QUERIES, DOC_QUERY], [mutation, DOC_MUTATORS, DOC_MUTATION], [subscription, DOC_SUBSCRIPTIONS, DOC_SUBSCRIPTION]]) {
123 if (_type && ((0, _keys2.default)(docs[_CONST] || {}).length || docs[_topCONST] && docs[_topCONST].length)) {
124 let fields = _type._fields;
125
126 if (docs[_topCONST]) {
127 _type.description = docs[_topCONST];
128 }
129
130 for (let field of (0, _keys2.default)(docs[_CONST])) {
131 if (field in fields) {
132 fields[field].description = docs[_CONST][field];
133 }
134 }
135 }
136 }
137 }
138 }
139
140 /**
141 * Somewhat like `injectComments` and other similar methods, the
142 * `injectInterfaceResolvers` method walks the registered classes and
143 * finds `GQLInterface` types and applies their `resolveType()`
144 * implementations.
145 *
146 * @memberof SchemaUtils
147 * @method ⌾⠀injectInterfaceResolvers
148 * @static
149 *
150 * @param {Object} schema a built GraphQLSchema object created via buildSchema
151 * or some other alternative but compatible manner
152 * @param {Function[]} Classes these are GQLBase extended classes used to
153 * manipulate the schema with.
154 */
155 static injectInterfaceResolvers(schema, Classes) {
156 for (let Class of Classes) {
157 if (Class.GQL_TYPE === _graphql.GraphQLInterfaceType) {
158 schema._typeMap[Class.name].resolveType = schema._typeMap[Class.name]._typeConfig.resolveType = Class.resolveType;
159 }
160 }
161 }
162
163 /**
164 * Somewhat like `injectComments` and other similar methods, the
165 * `injectInterfaceResolvers` method walks the registered classes and
166 * finds `GQLInterface` types and applies their `resolveType()`
167 * implementations.
168 *
169 * @memberof SchemaUtils
170 * @method ⌾⠀injectEnums
171 * @static
172 *
173 * @param {Object} schema a built GraphQLSchema object created via buildSchema
174 * or some other alternative but compatible manner
175 * @param {Function[]} Classes these are GQLBase extended classes used to
176 * manipulate the schema with.
177 */
178 static injectEnums(schema, Classes) {
179 for (let Class of Classes) {
180 if (Class.GQL_TYPE === _graphql.GraphQLEnumType) {
181 const __enum = schema._typeMap[Class.name];
182 const values = Class.values;
183
184 for (let value of __enum._values) {
185 if (value.name in values) {
186 (0, _lodash.merge)(value, values[value.name]);
187 }
188 }
189 }
190 }
191 }
192
193 /**
194 * GQLScalar types must define three methods to have a valid implementation.
195 * They are serialize, parseValue and parseLiteral. See their docs for more
196 * info on how to do so.
197 *
198 * This code finds each scalar and adds their implementation details to the
199 * generated schema type config.
200 *
201 * @memberof SchemaUtils
202 * @method ⌾⠀injectScalars
203 * @static
204 *
205 * @param {Object} schema a built GraphQLSchema object created via buildSchema
206 * or some other alternative but compatible manner
207 * @param {Function[]} Classes these are GQLBase extended classes used to
208 * manipulate the schema with.
209 */
210 static injectScalars(schema, Classes) {
211 for (let Class of Classes) {
212 if (Class.GQL_TYPE === _graphql.GraphQLScalarType) {
213 // @ComputedType
214 const type = schema._typeMap[Class.name];
215
216 // @ComputedType
217 const { serialize, parseValue, parseLiteral } = Class;
218
219 if (!serialize || !parseValue || !parseLiteral) {
220 // @ComputedType
221 _utils.LatticeLogs.error(`Scalar type ${Class.name} has invaild impl.`);
222 continue;
223 }
224
225 (0, _lodash.merge)(type._scalarConfig, {
226 serialize,
227 parseValue,
228 parseLiteral
229 });
230 }
231 }
232 }
233
234 /**
235 * A function that combines the IDL schemas of all the supplied classes and
236 * returns that value to the middleware getter.
237 *
238 * @static
239 * @memberof GQLExpressMiddleware
240 * @method ⌾⠀generateSchemaSDL
241 *
242 * @return {string} a dynamically generated GraphQL IDL schema string
243 */
244 static generateSchemaSDL(Classes, logOutput = true) {
245 let schema = _SyntaxTree.SyntaxTree.EmptyDocument();
246 let log = (...args) => {
247 if (logOutput) {
248 console.log(...args);
249 }
250 };
251
252 for (let Class of Classes) {
253 let classSchema = Class.SCHEMA;
254
255 if ((0, _neTypes.typeOf)(classSchema) === 'Symbol') {
256 let handler = Class.handler;
257 let filename = _path2.default.basename(Class.handler.path);
258
259 classSchema = handler.getSchema();
260 log(`\nRead schema (%s)\n%s\n%s\n`, filename, '-'.repeat(14 + filename.length), classSchema.replace(/^/gm, ' '));
261 }
262
263 schema.appendDefinitions(classSchema);
264 }
265
266 log('\nGenerated GraphQL Schema\n----------------\n%s', schema);
267
268 return schema.toString();
269 }
270
271 /**
272 * An asynchronous function used to parse the supplied classes for each
273 * ones resolvers and mutators. These are all combined into a single root
274 * object passed to express-graphql.
275 *
276 * @static
277 * @memberof SchemaUtils
278 * @method ⌾⠀createMergedRoot
279 *
280 * @param {Array<GQLBase>} Classes the GQLBase extended class objects or
281 * functions from which to merge the RESOLVERS and MUTATORS functions.
282 * @param {Object} requestData for Express apss, this will be an object
283 * containing { req, res, gql } where those are the Express request and
284 * response object as well as the GraphQL parameters for the request.
285 * @return {Promise<Object>} a Promise resolving to an Object containing all
286 * the functions described in both Query and Mutation types.
287 */
288 static createMergedRoot(Classes, requestData, separateByType = false) {
289 return (0, _asyncToGenerator3.default)(function* () {
290 const root = {};
291
292 for (let Class of Classes) {
293 (0, _lodash.merge)(root, (
294 // $FlowFixMe
295 yield Class.getMergedRoot(requestData, separateByType)));
296 }
297
298 return root;
299 })();
300 }
301};
302exports.default = SchemaUtils;
303//# sourceMappingURL=SchemaUtils.js.map
\No newline at end of file