@coderich/autograph 0.9.16 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/index.js +2 -6
  3. package/package.json +10 -10
  4. package/src/.DS_Store +0 -0
  5. package/src/core/EventEmitter.js +2 -4
  6. package/src/core/Resolver.js +35 -58
  7. package/src/core/Schema.js +5 -38
  8. package/src/core/ServerResolver.js +7 -93
  9. package/src/data/DataLoader.js +68 -27
  10. package/src/data/DataService.js +59 -58
  11. package/src/data/Field.js +71 -96
  12. package/src/data/Model.js +95 -113
  13. package/src/data/Pipeline.js +174 -0
  14. package/src/data/Type.js +19 -60
  15. package/src/driver/MongoDriver.js +53 -28
  16. package/src/graphql/ast/Field.js +44 -26
  17. package/src/graphql/ast/Model.js +5 -16
  18. package/src/graphql/ast/Node.js +0 -32
  19. package/src/graphql/ast/Schema.js +109 -112
  20. package/src/graphql/extension/api.js +22 -35
  21. package/src/graphql/extension/framework.js +25 -33
  22. package/src/graphql/extension/type.js +2 -2
  23. package/src/query/Query.js +73 -15
  24. package/src/query/QueryBuilder.js +37 -28
  25. package/src/query/QueryBuilderTransaction.js +3 -3
  26. package/src/query/QueryResolver.js +93 -44
  27. package/src/query/QueryService.js +31 -34
  28. package/src/service/app.service.js +56 -35
  29. package/src/service/decorator.service.js +21 -288
  30. package/src/service/event.service.js +5 -79
  31. package/src/service/graphql.service.js +0 -9
  32. package/src/service/schema.service.js +5 -3
  33. package/src/core/Rule.js +0 -107
  34. package/src/core/SchemaDecorator.js +0 -46
  35. package/src/core/Transformer.js +0 -68
  36. package/src/data/Memoizer.js +0 -39
  37. package/src/data/ResultSet.js +0 -246
  38. package/src/graphql/ast/SchemaDecorator.js +0 -138
  39. package/src/graphql/directive/authz.directive.js +0 -84
@@ -54,6 +54,11 @@ exports.stripObjectUndefineds = obj => Object.entries(obj).reduce((prev, [key, v
54
54
  exports.pushIt = (arr, it) => arr[arr.push(it) - 1];
55
55
  exports.toKeyObj = obj => exports.keyPaths(obj).reduce((prev, path) => Object.assign(prev, { [path]: _.get(obj, path) }), {});
56
56
 
57
+ exports.getGQLReturnType = (returnType) => {
58
+ const typeMap = { array: /^\[.+\].?$/, connection: /.+Connection!?$/, number: /^(Int|Float)!?$/, scalar: /.*/ };
59
+ return Object.entries(typeMap).find(([type, pattern]) => returnType.match(pattern))[0];
60
+ };
61
+
57
62
  exports.removeUndefinedDeep = (obj) => {
58
63
  return exports.unravelObject(exports.keyPaths(obj).reduce((prev, path) => {
59
64
  const value = _.get(obj, path);
@@ -90,7 +95,8 @@ exports.map = (mixed, fn) => {
90
95
  if (mixed == null) return mixed;
91
96
  const isArray = Array.isArray(mixed);
92
97
  const arr = isArray ? mixed : [mixed];
93
- const results = arr.map((...args) => fn(...args));
98
+ // const results = arr.map((...args) => fn(...args));
99
+ const results = arr.map((el, i, a) => fn(el, isArray ? i : undefined, isArray ? a : undefined));
94
100
  return isArray ? results : results[0];
95
101
  };
96
102
 
@@ -99,32 +105,6 @@ exports.mapPromise = (mixed, fn) => {
99
105
  return Array.isArray(map) ? Promise.all(map) : Promise.resolve(map);
100
106
  };
101
107
 
102
- exports.castCmp = (type, value) => {
103
- switch (type) {
104
- case 'String': {
105
- return `${value}`;
106
- }
107
- case 'Float': case 'Number': {
108
- const num = Number(value);
109
- if (!Number.isNaN(num)) return num;
110
- return value;
111
- }
112
- case 'Int': {
113
- const num = Number(value);
114
- if (!Number.isNaN(num)) return parseInt(value, 10);
115
- return value;
116
- }
117
- case 'Boolean': {
118
- if (value === 'true') return true;
119
- if (value === 'false') return false;
120
- return value;
121
- }
122
- default: {
123
- return value;
124
- }
125
- }
126
- };
127
-
128
108
  exports.objectContaining = (a, b) => {
129
109
  if (a === b) return true;
130
110
 
@@ -141,14 +121,6 @@ exports.objectContaining = (a, b) => {
141
121
  return exports.hashObject(a) === exports.hashObject(b);
142
122
  };
143
123
 
144
- exports.serialize = (field, value) => {
145
- if (!exports.isPlainObject(value)) return value;
146
- const model = field.getModelRef();
147
- if (!model) return value;
148
- const key = model.idKey();
149
- return value[key];
150
- };
151
-
152
124
  /**
153
125
  * Transform an object with dot.notation keys into an expanded object.
154
126
  * eg. { 'user.name': 'richard' } => { user: { name: 'richard' } }
@@ -277,3 +249,52 @@ exports.resolveDataObject = (obj) => {
277
249
  }, {});
278
250
  });
279
251
  };
252
+
253
+ exports.seek = (obj, paths, hint) => {
254
+ // We first do a normal get
255
+ const value = _.get(obj, paths);
256
+ if (!hint || value !== undefined) return value;
257
+
258
+ // Normalize paths & hint for traversal
259
+ const $paths = Array.isArray(paths) ? paths : paths.split('.');
260
+ const $hint = exports.unravelObject(hint);
261
+
262
+ // Traverse paths and get as close to the value as possible
263
+ const { currentValue, pathsToGo } = $paths.reduce((prev, path, i, arr) => {
264
+ if (prev.currentValue === undefined) return prev;
265
+ if (!Object.prototype.hasOwnProperty.call(prev.currentValue, path)) return prev;
266
+ prev.currentValue = prev.currentValue[path];
267
+ prev.pathsToGo = arr.slice(i + 1);
268
+ return prev;
269
+ }, { currentValue: obj, pathsToGo: $paths });
270
+
271
+ // Only if we hit an array can we continue
272
+ if (!Array.isArray(currentValue)) return undefined;
273
+
274
+ // If we got to the last segment we need the hint in order to verify
275
+ const lastPath = Boolean(pathsToGo.length === 1);
276
+ const arr = lastPath ? currentValue.filter(v => exports.objectContaining(v, $hint)) : currentValue;
277
+
278
+ // We keep going, recursive, till we find the first value
279
+ return arr.reduce((prev, v) => prev || exports.seek(v, pathsToGo, $hint), undefined);
280
+ };
281
+
282
+ exports.deseek = (shape, obj, paths, hint) => {
283
+ // Normalize paths
284
+ const $paths = (Array.isArray(paths) ? paths : paths.split('.')).map((path) => {
285
+ const item = shape.find(s => s.to === path); // Deserializing from unknown to expected
286
+ return item ? item.from : path;
287
+ });
288
+
289
+ // Normalize hint
290
+ const $hint = Object.entries(exports.toKeyObj(hint)).reduce((prev, [key, value]) => {
291
+ const segments = key.split('.').map((path) => {
292
+ const item = shape.find(s => s.to === path); // Deserializing from unknown to expected
293
+ return item ? item.from : path;
294
+ });
295
+
296
+ return Object.assign(prev, { [segments.join('.')]: value });
297
+ }, {});
298
+
299
+ return exports.seek(obj, $paths, $hint);
300
+ };
@@ -1,275 +1,27 @@
1
- const { get, set, remove, merge } = require('lodash');
2
1
  const GraphqlFields = require('graphql-fields');
3
- const Boom = require('../core/Boom');
4
- const Query = require('../query/Query');
5
- const { createSystemEvent } = require('./event.service');
6
- const { guidToId, unrollGuid, ucFirst, getDeep, ensureArray, objectContaining } = require('./app.service');
7
-
8
- const findParentField = (name, embed, model) => {
9
- const schema = model.getSchema();
10
- const fieldName = ucFirst(embed.isArray() ? embed.getName().replace(/s$/, '') : embed.getName());
11
- const parentModelName = name.substr(0, name.lastIndexOf(fieldName));
12
- const parentModel = schema.getModel(parentModelName);
13
- const field = model.getFields().find(f => f.getType() === parentModelName) || parentModel.getFields().find(f => f.getType() === model.getName());
14
- if (!field) throw Boom.badData(`Unable to locate parent field: '${model.getName()} -> ${parentModelName}'`);
15
- return field;
16
- };
17
-
18
- const findParentAndContainerCreate = (name, doc, input, model, embeds) => {
19
- let parent = doc;
20
-
21
- const container = embeds.reduce((prev, embed, i) => {
22
- // If further nested; must find the correct container
23
- if (i > 0) {
24
- const subField = findParentField(name, embed, model);
25
- prev = prev.find(el => `${el.id}` === `${input[subField]}`);
26
- }
27
-
28
- parent = prev;
29
- return getDeep(prev, embed.getName());
30
- }, doc);
31
-
32
- return { parent, container };
33
- };
34
-
35
- const findParentAndContainerUpdate = (fieldPath, doc, id, embeds) => {
36
- const data = embeds.map((embed, i, arr) => getDeep(doc, arr.slice(0, i + 1).join('.')));
37
- const [tail, prev = tail, head = prev] = [data[0], data[data.length - 2], data[data.length - 1]];
38
- const container = head.find(el => `${id}` === `${el.id}`);
39
- const parent = head === prev ? doc : prev.find(p => p[fieldPath.split('.').pop()].find(el => `${id}` === `${el.id}`));
40
- return { tail, parent, container };
41
- };
42
-
43
- const resolveQuery = (method, name, resolver, model, embeds = []) => {
44
- const [base] = embeds;
45
- const curr = embeds[embeds.length - 1];
46
- const fieldPath = embeds.map(field => field.getName()).join('.');
47
2
 
3
+ const resolveQuery = (method, name, resolver, model) => {
48
4
  return async (root, args, context, info) => {
49
- const { autograph } = context;
50
-
51
- // Embedded document handler
52
- if (fieldPath.length) {
53
- switch (method) {
54
- case 'get': {
55
- // Readjust the where clause
56
- const where = get(args, 'where', {});
57
- set(where, `${fieldPath}.id`, args.id);
58
- set(args, 'where', where);
59
-
60
- return resolver.query(context, base.getModel(), args, info).then(([result]) => {
61
- const arr = ensureArray(getDeep(result, fieldPath, []));
62
- return arr.find(el => `${el.id}` === `${args.id}`);
63
- });
64
- }
65
- case 'find': {
66
- // Readjust the where clause
67
- const where = get(args, 'where', {});
68
- const $where = set({}, `${fieldPath}`, where);
69
- set(args, 'where', $where);
70
-
71
- return resolver.query(context, base.getModel(), args, info).then((results) => {
72
- const arr = results.map(result => ensureArray(getDeep(result, fieldPath, []))).flat();
73
- return arr.filter(el => objectContaining(el, where));
74
- });
75
- }
76
- case 'count': {
77
- // Readjust the where clause
78
- const where = get(args, 'where', {});
79
- const $where = set({}, `${fieldPath}`, where);
80
- set(args, 'where', $where);
81
-
82
- return resolver.query(context, base.getModel(), args, info).then((results) => {
83
- const arr = results.map(result => ensureArray(getDeep(result, fieldPath, []))).flat();
84
- return arr.filter(el => objectContaining(el, where)).length;
85
- });
86
- }
87
- case 'create': {
88
- const field = findParentField(name, curr, model);
89
- const path = fieldPath.split('.').slice(0, -1).concat('id').join('.');
90
- const input = unrollGuid(autograph, model, args.input);
91
- const meta = args.meta || {};
92
- const id = guidToId(autograph, get(input, field.getName()));
93
-
94
- // Get overall document
95
- const where = { [path]: id };
96
- const doc = await autograph.resolver.match(base.getModel()).where(where).one();
97
- if (!doc) throw Boom.notFound(`${base.getModel().getName()} Not Found`);
98
-
99
- // Get parent and container within document
100
- const query = new Query({ resolver: autograph.resolver, model, input, where, meta, root: doc });
101
- const { container } = findParentAndContainerCreate(name, doc, input, model, embeds);
102
- model.appendDefaultFields(query, input);
103
-
104
- return createSystemEvent('Mutation', { method: 'create', query }, async () => {
105
- let $$input = ensureArray(input.$input || input);
106
- $$input = await Promise.all($$input.map(el => model.appendCreateFields(el, true)));
107
- container.push(...$$input);
108
- const $update = { [base.getName()]: get(doc, base.getName()) };
109
- return autograph.resolver.match(base.getModel()).id(doc.id).flags({ novalidate: true }).save($update).then(($doc) => {
110
- return getDeep(doc, fieldPath).pop();
111
- });
112
- });
113
- }
114
- case 'update': {
115
- // Get overall document
116
- const id = guidToId(autograph, args.id);
117
- const where = { [`${fieldPath}.id`]: id };
118
- const meta = args.meta || {};
119
- const input = unrollGuid(autograph, model, args.input || {});
120
- const doc = await autograph.resolver.match(base.getModel()).where(where).one();
121
- if (!doc) throw Boom.notFound(`${base.getModel().getName()} Not Found`);
122
-
123
- // Get parent and container within document
124
- const query = new Query({ resolver: autograph.resolver, model, input, where, meta, root: doc });
125
- const { tail, container } = findParentAndContainerUpdate(fieldPath, doc, id, embeds);
126
-
127
- return createSystemEvent('Mutation', { method: 'update', query }, async () => {
128
- const $input = await model.appendUpdateFields(input);
129
- merge(container, $input); // Must mutate object here
130
- const $update = { [base.getName()]: tail };
131
- doc[base.getName()] = tail; // Deficiency in how update works; must pass entire doc
132
- return autograph.resolver.match(base.getModel()).id(doc.id).flags({ novalidate: true }).save($update).then(() => container);
133
- });
134
- }
135
- case 'delete': {
136
- const id = guidToId(autograph, args.id);
137
- const where = { [`${fieldPath}.id`]: id };
138
- const meta = args.meta || {};
139
- const doc = await autograph.resolver.match(base.getModel()).where(where).one();
140
- if (!doc) throw Boom.notFound(`${base.getModel()} Not Found`);
141
-
142
- // Get parent and container within document
143
- const query = new Query({ resolver: autograph.resolver, model, where, meta, root: doc });
144
- const { tail, parent, container } = findParentAndContainerUpdate(fieldPath, doc, id, embeds);
145
-
146
- return createSystemEvent('Mutation', { method: 'delete', query }, () => {
147
- const key = fieldPath.split('.').pop();
148
- remove(parent[key], el => `${el.id}` === `${id}`);
149
- const $update = { [base.getName()]: tail };
150
- doc[base.getName()] = tail; // Deficiency in how update works; must pass entire doc
151
- return autograph.resolver.match(base.getModel()).id(doc.id).flags({ novalidate: true }).save($update).then(() => container);
152
- });
153
- }
154
- default: {
155
- return null;
156
- }
157
- }
158
- }
5
+ const queryInfo = { fields: GraphqlFields(info, {}, { processArguments: true }) };
159
6
 
160
7
  switch (method) {
161
- case 'get': return resolver.get(context, model, args, true, info);
8
+ case 'get': return resolver.get(context, model, args, true, queryInfo);
162
9
  case 'find': {
163
10
  return {
164
- edges: () => resolver.query(context, model, args, info),
165
- pageInfo: () => resolver.query(context, model, args, info),
166
- count: () => resolver.count(context, model, args, info),
11
+ edges: () => resolver.query(context, model, args, queryInfo),
12
+ pageInfo: () => resolver.query(context, model, args, queryInfo),
13
+ count: () => resolver.count(context, model, args, queryInfo),
167
14
  };
168
15
  }
169
- case 'count': return resolver.count(context, model, args, info);
170
- case 'create': return resolver.create(context, model, args, { fields: GraphqlFields(info, {}, { processArguments: true }) });
171
- case 'update': return resolver.update(context, model, args, { fields: GraphqlFields(info, {}, { processArguments: true }) });
172
- case 'delete': return resolver.delete(context, model, args, { fields: GraphqlFields(info, {}, { processArguments: true }) });
16
+ case 'count': return resolver.count(context, model, args, queryInfo);
17
+ case 'create': return resolver.create(context, model, args, queryInfo);
18
+ case 'update': return resolver.update(context, model, args, queryInfo);
19
+ case 'delete': return resolver.delete(context, model, args, queryInfo);
173
20
  default: return null;
174
21
  }
175
22
  };
176
23
  };
177
24
 
178
- const makeEmbeddedAPI = (model, method, parent) => {
179
- let gql = '';
180
- const modelName = model.getName();
181
- const fields = model.getEmbeddedFields().filter(field => field !== parent && field.isEmbeddedApi());
182
-
183
- if (fields.length) {
184
- fields.forEach((field) => {
185
- const modelRef = field.getModelRef();
186
- const fieldName = ucFirst(field.isArray() ? field.getName().replace(/s$/, '') : field.getName());
187
- const name = `${modelName}${fieldName}`;
188
-
189
- switch (method) {
190
- case 'create': {
191
- if (field.hasFieldScope('c')) gql += exports.makeCreateAPI(name, modelRef, field);
192
- break;
193
- }
194
- case 'read': {
195
- if (field.hasFieldScope('r')) gql += exports.makeReadAPI(name, modelRef, field);
196
- break;
197
- }
198
- case 'update': {
199
- if (field.hasFieldScope('u')) gql += exports.makeUpdateAPI(name, modelRef, field);
200
- break;
201
- }
202
- case 'delete': {
203
- if (field.hasFieldScope('d')) gql += exports.makeDeleteAPI(name, modelRef, field);
204
- break;
205
- }
206
- default: {
207
- throw new Error(`Unknown method '${method}'`);
208
- }
209
- }
210
- });
211
- }
212
-
213
- return gql;
214
- };
215
-
216
- const makeEmbeddedResolver = (model, resolver, type, embeds = []) => {
217
- const obj = {};
218
-
219
- const modelName = model.getName();
220
- const fields = model.getEmbeddedFields().filter(field => embeds.indexOf(field) === -1 && field.isEmbeddedApi());
221
-
222
- fields.forEach((field) => {
223
- const modelRef = field.getModelRef();
224
- const fieldName = ucFirst(field.isArray() ? field.getName().replace(/s$/, '') : field.getName());
225
- const name = `${modelName}${fieldName}`;
226
-
227
- switch (type) {
228
- case 'query': {
229
- Object.assign(obj, exports.makeQueryResolver(name, modelRef, resolver, embeds.concat(field)));
230
- break;
231
- }
232
- case 'mutation': {
233
- Object.assign(obj, exports.makeMutationResolver(name, modelRef, resolver, embeds.concat(field)));
234
- break;
235
- }
236
- default: {
237
- throw new Error(`Unknown type '${type}'`);
238
- }
239
- }
240
- });
241
-
242
- return obj;
243
- };
244
-
245
- exports.makeInputSplice = (model, embed = false) => {
246
- let gql = '';
247
- const fields = model.getArrayFields().filter(field => field.hasGQLScope('c', 'u', 'd') && field.isSpliceable());
248
-
249
- if (fields.length) {
250
- gql += fields.map((field) => {
251
- const embedded = field.isEmbedded() ? exports.makeInputSplice(field.getModelRef(), true) : '';
252
-
253
- return `
254
- ${embedded}
255
- input ${model.getName()}${ucFirst(field.getName())}InputSplice {
256
- with: ${field.getGQLType('InputWhere', { splice: true })}
257
- put: ${field.getGQLType('InputUpdate', { splice: true })}
258
- ${embedded.length ? `splice: ${field.getModelRef().getName()}InputSplice` : ''}
259
- }
260
- `;
261
- }).join('\n\n');
262
-
263
- gql += `
264
- input ${model.getName()}InputSplice {
265
- ${fields.map(field => `${field.getName()}: ${model.getName()}${ucFirst(field.getName())}InputSplice`)}
266
- }
267
- `;
268
- }
269
-
270
- return gql;
271
- };
272
-
273
25
  // APIs
274
26
  exports.makeCreateAPI = (name, model, parent) => {
275
27
  let gql = '';
@@ -279,8 +31,6 @@ exports.makeCreateAPI = (name, model, parent) => {
279
31
  gql += `create${name}(input: ${model.getName()}InputCreate! ${meta}): ${model.getName()}!`;
280
32
  }
281
33
 
282
- gql += makeEmbeddedAPI(model, 'create', parent);
283
-
284
34
  return gql;
285
35
  };
286
36
 
@@ -303,8 +53,6 @@ exports.makeReadAPI = (name, model, parent) => {
303
53
  `;
304
54
  }
305
55
 
306
- gql += makeEmbeddedAPI(model, 'read', parent);
307
-
308
56
  return gql;
309
57
  };
310
58
 
@@ -312,21 +60,10 @@ exports.makeUpdateAPI = (name, model, parent) => {
312
60
  let gql = '';
313
61
 
314
62
  if (model.hasGQLScope('u')) {
315
- const spliceFields = model.getArrayFields().filter(field => field.hasGQLScope('c', 'u', 'd') && field.isSpliceable());
316
63
  const meta = model.getMeta() ? `meta: ${model.getMeta()}` : '';
317
-
318
- gql += `
319
- update${name}(
320
- id: ID!
321
- input: ${model.getName()}InputUpdate
322
- # ${!spliceFields.length ? '' : `splice: ${model.getName()}InputSplice`}
323
- ${meta}
324
- ): ${model.getName()}!
325
- `;
64
+ gql += `update${name}(id: ID! input: ${model.getName()}InputUpdate ${meta}): ${model.getName()}!`;
326
65
  }
327
66
 
328
- gql += makeEmbeddedAPI(model, 'update', parent);
329
-
330
67
  return gql;
331
68
  };
332
69
 
@@ -338,8 +75,6 @@ exports.makeDeleteAPI = (name, model, parent) => {
338
75
  gql += `delete${name}(id: ID! ${meta}): ${model.getName()}!`;
339
76
  }
340
77
 
341
- gql += makeEmbeddedAPI(model, 'delete', parent);
342
-
343
78
  return gql;
344
79
  };
345
80
 
@@ -357,25 +92,23 @@ exports.makeSubscriptionAPI = (name, model, parent) => {
357
92
  };
358
93
 
359
94
  // Resolvers
360
- exports.makeQueryResolver = (name, model, resolver, embeds = []) => {
95
+ exports.makeQueryResolver = (name, model, resolver) => {
361
96
  const obj = {};
362
- const [field] = embeds.slice(-1);
363
97
 
364
- if ((!field || field.hasFieldScope('r')) && model.hasGQLScope('r')) {
365
- obj[`get${name}`] = resolveQuery('get', name, resolver, model, embeds);
366
- obj[`find${name}`] = resolveQuery('find', name, resolver, model, embeds);
98
+ if (model.hasGQLScope('r')) {
99
+ obj[`get${name}`] = resolveQuery('get', name, resolver, model);
100
+ obj[`find${name}`] = resolveQuery('find', name, resolver, model);
367
101
  }
368
102
 
369
- return Object.assign(obj, makeEmbeddedResolver(model, resolver, 'query', embeds));
103
+ return obj;
370
104
  };
371
105
 
372
- exports.makeMutationResolver = (name, model, resolver, embeds = []) => {
106
+ exports.makeMutationResolver = (name, model, resolver) => {
373
107
  const obj = {};
374
- const [field] = embeds.slice(-1);
375
108
 
376
- if ((!field || field.hasFieldScope('c')) && model.hasGQLScope('c')) obj[`create${name}`] = resolveQuery('create', name, resolver, model, embeds);
377
- if ((!field || field.hasFieldScope('u')) && model.hasGQLScope('u')) obj[`update${name}`] = resolveQuery('update', name, resolver, model, embeds);
378
- if ((!field || field.hasFieldScope('d')) && model.hasGQLScope('d')) obj[`delete${name}`] = resolveQuery('delete', name, resolver, model, embeds);
109
+ if (model.hasGQLScope('c')) obj[`create${name}`] = resolveQuery('create', name, resolver, model);
110
+ if (model.hasGQLScope('u')) obj[`update${name}`] = resolveQuery('update', name, resolver, model);
111
+ if (model.hasGQLScope('d')) obj[`delete${name}`] = resolveQuery('delete', name, resolver, model);
379
112
 
380
- return Object.assign(obj, makeEmbeddedResolver(model, resolver, 'mutation', embeds));
113
+ return obj;
381
114
  };
@@ -1,59 +1,26 @@
1
- const QueryService = require('../query/QueryService');
2
1
  const EventEmitter = require('../core/EventEmitter');
3
- const { ensureArray, ucFirst } = require('./app.service');
2
+ const { ucFirst } = require('./app.service');
4
3
 
5
4
  // Event emitters
6
5
  const eventEmitter = new EventEmitter().setMaxListeners(100);
7
- const internalEmitter = new EventEmitter().setMaxListeners(100);
8
6
  const systemEvent = new EventEmitter().setMaxListeners(100).on('system', async (event, next) => {
9
7
  const { type, data } = event;
10
- await internalEmitter.emit(type, data);
11
8
  next(await eventEmitter.emit(type, data)); // Return result from user-defined middleware
12
9
  });
13
10
 
14
11
  //
15
12
  exports.createSystemEvent = (name, mixed = {}, thunk = () => {}) => {
16
13
  let event = mixed;
17
- let middleware = () => Promise.resolve();
18
14
  const type = ucFirst(name);
19
15
 
20
16
  if (name !== 'Setup' && name !== 'Response') {
21
- const { method, query } = mixed;
22
- const { resolver, model, meta, doc, id, input, sort, merged, native, root, crud } = query.toObject();
23
-
24
- event = {
25
- context: resolver.getContext(),
26
- key: `${method}${model}`,
27
- resolver,
28
- method,
29
- crud,
30
- model,
31
- meta,
32
- id,
33
- input,
34
- query,
35
- doc,
36
- merged,
37
- root,
38
- };
39
-
40
- middleware = () => new Promise(async (resolve) => {
41
- if (!native) {
42
- const $where = await QueryService.resolveWhereClause(query);
43
- query.match(model.serialize(query, $where, true));
44
- }
45
-
46
- if (sort) {
47
- query.$sort(QueryService.resolveSortBy(query));
48
- }
49
-
50
- resolve();
51
- });
17
+ const { query } = mixed;
18
+ event = query.toObject();
19
+ event.query = query;
52
20
  }
53
21
 
54
22
  return systemEvent.emit('system', { type: `pre${type}`, data: event }).then((result) => {
55
- if (result !== undefined) return result; // Allowing middleware to dictate result
56
- return middleware().then(thunk);
23
+ return (result !== undefined) ? result : thunk(); // Allowing middleware to dictate result
57
24
  }).then((result) => {
58
25
  event.result = result;
59
26
  if (event.crud === 'create') event.doc = event.query.toObject().doc;
@@ -66,44 +33,3 @@ exports.createSystemEvent = (name, mixed = {}, thunk = () => {}) => {
66
33
  };
67
34
 
68
35
  exports.eventEmitter = eventEmitter;
69
- exports.internalEmitter = internalEmitter;
70
-
71
-
72
- /**
73
- * Hook into the pre event only!
74
- *
75
- * Kick off system events for embedded fields
76
- */
77
- const eventHandler = (event) => {
78
- const { model, input, method, doc = input, query } = event;
79
-
80
- return Promise.all(model.getEmbeddedFields().map((field) => {
81
- return new Promise((resolve, reject) => {
82
- if (Object.prototype.hasOwnProperty.call(input || {}, field.getName())) {
83
- let i = 0;
84
- const value = input[field.getName()];
85
- const values = ensureArray(value).filter(el => el != null);
86
- const newModel = field.getModelRef();
87
-
88
- if (values.length) {
89
- values.forEach((val) => {
90
- const clone = query.clone().model(newModel).input(val).doc(doc);
91
- exports.createSystemEvent('Mutation', { method, query: clone }, () => {
92
- if (++i >= values.length) resolve();
93
- }).catch(e => reject(e));
94
- // const newEvent = { parent: doc, key: `${method}${field}`, method, model: newModel, resolver, query: new Query(resolver, newModel, { meta }), input: val };
95
- // exports.createSystemEvent('Mutation', newEvent, () => {
96
- // if (++i >= values.length) resolve();
97
- // }).catch(e => reject(e));
98
- });
99
- } else {
100
- resolve();
101
- }
102
- } else {
103
- resolve();
104
- }
105
- });
106
- }));
107
- };
108
-
109
- internalEmitter.on('preMutation', async (event, next) => eventHandler(event).then(next)); // Only preMutation!
@@ -1,7 +1,5 @@
1
1
  const { get } = require('lodash');
2
2
  const { Kind, parse, print } = require('graphql');
3
- const { validate } = require('graphql/validation');
4
- const { makeExecutableSchema } = require('graphql-tools');
5
3
 
6
4
  //
7
5
  const mergePairs = [
@@ -81,13 +79,6 @@ exports.mergeASTArray = (arr) => {
81
79
  }, []).filter(el => !el.deleteFlag);
82
80
  };
83
81
 
84
- exports.validateSchema = (ast) => {
85
- const errs = validate(makeExecutableSchema(ast), ast.typeDefs).filter(({ message }) => message.indexOf('not executable') === -1).map(({ message }) => message);
86
- if (errs.length) throw new Error(errs.join('\n'));
87
- };
88
-
89
- exports.makeExecutableSchema = makeExecutableSchema;
90
-
91
82
  exports.toAST = (a) => {
92
83
  if (typeof a === 'string') return parse(a);
93
84
  if (Array.isArray(a)) return parse(a.map(e => exports.toGQL(e)).join('\n\n'));
@@ -54,11 +54,13 @@ exports.getSchemaData = (schema) => {
54
54
  exports.identifyOnDeletes = (models, parentModel) => {
55
55
  return models.reduce((prev, model) => {
56
56
  model.getOnDeleteFields().forEach((field) => {
57
- if (`${field.getModelRef()}` === `${parentModel}`) {
57
+ const { modelRef, isArray } = field.toObject();
58
+
59
+ if (`${modelRef}` === `${parentModel}`) {
58
60
  if (model.isEntity()) {
59
- prev.push({ model, field, isArray: field.isArray(), op: field.getOnDelete() });
61
+ prev.push({ model, field, isArray, op: field.getOnDelete() });
60
62
  } else {
61
- prev.push(...exports.identifyOnDeletes(models, model).map(od => Object.assign(od, { fieldRef: field, isArray: field.isArray(), op: field.getOnDelete() })));
63
+ prev.push(...exports.identifyOnDeletes(models, model).map(od => Object.assign(od, { fieldRef: field, isArray, op: field.getOnDelete() })));
62
64
  }
63
65
  }
64
66
  });