1 | ;
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.SchemaUtils = undefined;
|
7 |
|
8 | var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
|
9 |
|
10 | var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
|
11 |
|
12 | var _keys = require('babel-runtime/core-js/object/keys');
|
13 |
|
14 | var _keys2 = _interopRequireDefault(_keys);
|
15 |
|
16 | var _path = require('path');
|
17 |
|
18 | var _path2 = _interopRequireDefault(_path);
|
19 |
|
20 | var _SyntaxTree = require('./SyntaxTree');
|
21 |
|
22 | var _GQLBase = require('./GQLBase');
|
23 |
|
24 | var _GQLEnum = require('./GQLEnum');
|
25 |
|
26 | var _GQLInterface = require('./GQLInterface');
|
27 |
|
28 | var _GQLScalar = require('./GQLScalar');
|
29 |
|
30 | var _neTypes = require('ne-types');
|
31 |
|
32 | var _utils = require('./utils');
|
33 |
|
34 | var _lodash = require('lodash');
|
35 |
|
36 | var _events = require('events');
|
37 |
|
38 | var _events2 = _interopRequireDefault(_events);
|
39 |
|
40 | var _graphql = require('graphql');
|
41 |
|
42 | function _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 | */
|
50 | let 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 | };
|
302 | exports.default = SchemaUtils;
|
303 | //# sourceMappingURL=SchemaUtils.js.map |
\ | No newline at end of file |