@coderich/autograph 0.10.0 → 0.10.1

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 (42) hide show
  1. package/CHANGELOG.md +21 -3
  2. package/index.js +2 -6
  3. package/package.json +4 -4
  4. package/src/.DS_Store +0 -0
  5. package/src/core/EventEmitter.js +2 -4
  6. package/src/core/Resolver.js +43 -60
  7. package/src/core/Schema.js +3 -36
  8. package/src/data/.DS_Store +0 -0
  9. package/src/data/DataLoader.js +71 -32
  10. package/src/data/DataService.js +59 -58
  11. package/src/data/Field.js +71 -121
  12. package/src/data/Model.js +98 -108
  13. package/src/data/Pipeline.js +174 -0
  14. package/src/data/Type.js +19 -74
  15. package/src/driver/MongoDriver.js +21 -19
  16. package/src/graphql/.DS_Store +0 -0
  17. package/src/graphql/ast/Field.js +43 -24
  18. package/src/graphql/ast/Model.js +5 -16
  19. package/src/graphql/ast/Node.js +0 -25
  20. package/src/graphql/ast/Schema.js +107 -111
  21. package/src/graphql/extension/api.js +20 -18
  22. package/src/graphql/extension/framework.js +25 -33
  23. package/src/graphql/extension/type.js +2 -2
  24. package/src/query/Query.js +72 -14
  25. package/src/query/QueryBuilder.js +38 -30
  26. package/src/query/QueryBuilderTransaction.js +3 -3
  27. package/src/query/QueryResolver.js +92 -42
  28. package/src/query/QueryService.js +31 -34
  29. package/src/service/app.service.js +67 -9
  30. package/src/service/event.service.js +5 -79
  31. package/src/service/schema.service.js +5 -3
  32. package/src/core/Rule.js +0 -107
  33. package/src/core/SchemaDecorator.js +0 -46
  34. package/src/core/Transformer.js +0 -68
  35. package/src/data/Memoizer.js +0 -39
  36. package/src/data/ResultSet.js +0 -205
  37. package/src/data/stream/DataHydrator.js +0 -58
  38. package/src/data/stream/ResultSet.js +0 -34
  39. package/src/data/stream/ResultSetItem.js +0 -158
  40. package/src/data/stream/ResultSetItemProxy.js +0 -161
  41. package/src/graphql/ast/SchemaDecorator.js +0 -141
  42. package/src/graphql/directive/authz.directive.js +0 -84
@@ -1,7 +1,7 @@
1
1
  const { get } = require('lodash');
2
2
  const { Kind } = require('graphql');
3
3
  const ServerResolver = require('../../core/ServerResolver');
4
- const { ucFirst, fromGUID } = require('../../service/app.service');
4
+ const { ucFirst, toGUID, fromGUID } = require('../../service/app.service');
5
5
  const { findGQLModels } = require('../../service/schema.service');
6
6
  const { makeCreateAPI, makeReadAPI, makeUpdateAPI, makeDeleteAPI, makeSubscriptionAPI, makeQueryResolver, makeMutationResolver } = require('../../service/decorator.service');
7
7
 
@@ -10,7 +10,6 @@ const interfaceKinds = [Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTE
10
10
  const getGQLWhereFields = (model) => {
11
11
  return model.getFields().filter((field) => {
12
12
  if (!field.hasGQLScope('r')) return false;
13
- if (field.hasBoundValue() && !field.getDirectiveArg('value', 'passive')) return false;
14
13
  const modelRef = field.getModelRef();
15
14
  if (modelRef && !modelRef.isEmbedded() && !modelRef.isEntity()) return false;
16
15
  return true;
@@ -51,9 +50,9 @@ module.exports = (schema) => {
51
50
  ${model.getFields().filter(field => field.hasGQLScope('r')).map(field => `${field.getName()}${field.getExtendArgs()}: ${field.getPayloadType()}`)}
52
51
  }
53
52
  type ${model.getName()}Connection {
53
+ count: Int!
54
54
  pageInfo: PageInfo!
55
55
  edges: [${model.getName()}Edge]
56
- count: Int!
57
56
  }
58
57
  type ${model.getName()}Edge {
59
58
  node: ${model.getName()}
@@ -133,21 +132,20 @@ module.exports = (schema) => {
133
132
  const isConnection = field.isConnection();
134
133
 
135
134
  return Object.assign(def, {
136
- [fieldName]: (root, args, { autograph }) => {
137
- if (fieldName === 'id') return autograph.legacyMode ? root.id : root.$id;
138
-
139
- const $fieldName = `$${fieldName}`;
135
+ [fieldName]: (doc, args, { autograph }, info) => {
136
+ if (fieldName === 'id') return autograph.legacyMode ? doc.id : toGUID(modelName, doc.id);
140
137
 
138
+ // If this field is a connection we return thunks in order to delay query
139
+ // until the "Connection" resolver (below) is run
141
140
  if (isConnection) {
142
141
  return {
143
- args,
144
- edges: root[$fieldName], // Thunk to the data/edges
145
- pageInfo: root[$fieldName], // You still need the data/edges to get pageInfo!
146
- count: root[`${$fieldName}:count`], // Thunk to $$count
142
+ count: () => field.count(autograph.resolver, doc, args),
143
+ edges: () => field.resolve(autograph.resolver, doc, args),
144
+ pageInfo: () => field.resolve(autograph.resolver, doc, args),
147
145
  };
148
146
  }
149
147
 
150
- return root.$$isResultSetItem ? root[$fieldName](args) : root[fieldName];
148
+ return field.resolve(autograph.resolver, doc, args);
151
149
  },
152
150
  });
153
151
  }, {});
@@ -164,24 +162,28 @@ module.exports = (schema) => {
164
162
  return Object.assign(prev, {
165
163
  [modelName]: fieldResolvers,
166
164
  [`${modelName}Connection`]: {
167
- edges: ({ edges, args }) => edges(args).then(rs => rs.map(node => ({ cursor: get(node, '$$cursor'), node }))),
168
- pageInfo: ({ pageInfo, args }) => pageInfo(args).then(rs => get(rs, '$$pageInfo')),
169
- count: ({ count, args }) => count(args),
165
+ count: ({ count }) => count(),
166
+ edges: ({ edges }) => edges().then(rs => rs.map(node => ({ cursor: get(node, '$$cursor'), node }))),
167
+ pageInfo: ({ pageInfo }) => pageInfo().then(rs => get(rs, '$$pageInfo')),
170
168
  },
171
169
  });
172
170
  }, {
173
171
  Node: {
174
- __resolveType: (root, args, context, info) => root.__typename || fromGUID(root.$id)[0], // eslint-disable-line no-underscore-dangle
172
+ __resolveType: (doc, args, context, info) => doc.__typename, // eslint-disable-line no-underscore-dangle
175
173
  },
176
174
 
177
175
  Query: entityModels.reduce((prev, model) => {
178
176
  return Object.assign(prev, makeQueryResolver(model.getName(), model, resolver));
179
177
  }, {
180
- node: (root, args, context, info) => {
178
+ node: (doc, args, context, info) => {
181
179
  const { id } = args;
182
180
  const [modelName] = fromGUID(id);
183
181
  const model = schema.getModel(modelName);
184
- return resolver.get(context, model, args, false, info);
182
+ return resolver.get(context, model, args, false, info).then((result) => {
183
+ if (result == null) return result;
184
+ result.__typename = modelName; // eslint-disable-line no-underscore-dangle
185
+ return result;
186
+ });
185
187
  },
186
188
  }),
187
189
 
@@ -1,5 +1,6 @@
1
- const Rule = require('../../core/Rule');
2
- const Transformer = require('../../core/Transformer');
1
+ const Pipeline = require('../../data/Pipeline');
2
+
3
+ Pipeline.createPresets();
3
4
 
4
5
  module.exports = (schema) => {
5
6
  return {
@@ -7,51 +8,49 @@ module.exports = (schema) => {
7
8
  scalar AutoGraphMixed
8
9
  scalar AutoGraphDriver
9
10
  scalar AutoGraphDateTime @field(transform: toDate)
10
- enum AutoGraphEnforceEnum { ${Object.keys(Rule.getInstances()).join(' ')} }
11
- enum AutoGraphTransformEnum { ${Object.keys(Transformer.getInstances()).join(' ')} }
11
+ enum AutoGraphPipelineEnum { ${Object.keys(Pipeline).join(' ')} }
12
12
  enum AutoGraphAuthzEnum { private protected public }
13
- enum AutoGraphValueScopeEnum { self context }
14
13
  enum AutoGraphOnDeleteEnum { cascade nullify restrict defer }
15
14
  enum AutoGraphIndexEnum { unique }
16
15
 
17
16
  directive @model(
18
- key: String # Specify it's key during transit
17
+ id: String # Specify db key (default "id")
18
+ key: String # Specify db table/collection name
19
+ createdAt: String # Specify db key (default "createdAt")
20
+ updatedAt: String # Specify db key (default "updatedAt")
21
+ meta: AutoGraphMixed # Custom input "meta" field for mutations
22
+ embed: Boolean # Mark this an embedded model (default false)
23
+ persist: Boolean # Persist this model (default true)
19
24
  gqlScope: AutoGraphMixed # Dictate how GraphQL API behaves
20
25
  dalScope: AutoGraphMixed # Dictate how the DAL behaves
21
26
  fieldScope: AutoGraphMixed # Dictate how a FIELD may use me
22
- meta: AutoGraphMixed # Custom input 'meta' field for mutations
23
- embed: Boolean # Mark this an embedded model (default false)
24
- persist: Boolean # Persist this model (default true)
25
27
  driver: AutoGraphDriver # External data driver
26
28
  authz: AutoGraphAuthzEnum # Access level used for authorization (default: private)
27
29
  namespace: String # Logical grouping of models that can be globbed (useful for authz)
28
-
29
- # Override auto-gen
30
- id: String
31
- createdAt: String
32
- updatedAt: String
33
30
  ) on OBJECT | INTERFACE
34
31
 
35
32
  directive @field(
36
- key: String # Specify it's key during transit
33
+ id: String # Specify the ModelRef this field FK References
34
+ key: String # Specify db key
35
+ persist: Boolean # Persist this field (default true)
36
+ connection: Boolean # Treat this field as a connection type (default false - rolling this out slowly)
37
+ default: AutoGraphMixed # Define a default value
37
38
  ref: AutoGraphMixed # Specify the modelRef field's name (overrides isEmbedded)
38
39
  gqlScope: AutoGraphMixed # Dictate how GraphQL API behaves
39
40
  dalScope: AutoGraphMixed # Dictate how the DAL behaves
40
41
  fieldScope: AutoGraphMixed # Dictate how a FIELD may use me
41
- persist: Boolean # Persist this field (default true)
42
- default: AutoGraphMixed # Define a default value
43
- connection: Boolean # Treat this field as a connection type (default false - rolling this out slowly)
44
-
45
- noRepeat: Boolean
42
+ onDelete: AutoGraphOnDeleteEnum # onDelete behavior
46
43
 
47
44
  authz: AutoGraphAuthzEnum # Access level used for authorization (default: private)
48
- onDelete: AutoGraphOnDeleteEnum
49
45
 
50
- enforce: [AutoGraphEnforceEnum!] #
51
- resolve: [AutoGraphTransformEnum!] # Transforms when resolving
52
- transform: [AutoGraphTransformEnum!] # Transforms when serialize + deserialize
53
- serialize: [AutoGraphTransformEnum!] # Transforms when serialize
54
- deserialize: [AutoGraphTransformEnum!] # Transforms when deserialize
46
+ # Pipeline Structure
47
+ instruct: [AutoGraphPipelineEnum!]
48
+ destruct: [AutoGraphPipelineEnum!]
49
+ restruct: [AutoGraphPipelineEnum!]
50
+ construct: [AutoGraphPipelineEnum!]
51
+ serialize: [AutoGraphPipelineEnum!]
52
+ deserialize: [AutoGraphPipelineEnum!]
53
+ transform: [AutoGraphPipelineEnum!]
55
54
  ) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION | SCALAR
56
55
 
57
56
  directive @link(
@@ -60,13 +59,6 @@ module.exports = (schema) => {
60
59
  use: AutoGraphMixed # The VALUE to use (default's to @link'd value); useful for many-to-many relationships
61
60
  ) on FIELD_DEFINITION
62
61
 
63
- directive @value(
64
- path: String! # The path to the data
65
- # merge: Boolean # Deep merge the data? (default false - overwrite) [does not even look supported at the moment]
66
- passive: Boolean # If value exists leave it alone (default false)
67
- scope: AutoGraphValueScopeEnum # Where to look for the data (default self)
68
- ) on OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | SCALAR
69
-
70
62
  directive @index(
71
63
  name: String
72
64
  on: [AutoGraphMixed!]!
@@ -17,8 +17,8 @@ module.exports = (schema) => {
17
17
  return `
18
18
  extend type ${modelName}${interfacesGQL} {
19
19
  ${id ? `id: ID! @field(key: "${id}", gqlScope: r)` : ''}
20
- ${createdAt ? `createdAt: AutoGraphDateTime @field(key: "${createdAt}", gqlScope: r)` : ''}
21
- ${updatedAt ? `updatedAt: AutoGraphDateTime @field(key: "${updatedAt}", gqlScope: r)` : ''}
20
+ ${createdAt ? `createdAt: AutoGraphDateTime @field(key: "${createdAt}", serialize: createdAt, gqlScope: r)` : ''}
21
+ ${updatedAt ? `updatedAt: AutoGraphDateTime @field(key: "${updatedAt}", serialize: timestamp, gqlScope: r)` : ''}
22
22
  }
23
23
  `;
24
24
  }
@@ -1,15 +1,21 @@
1
1
  const Boom = require('../core/Boom');
2
- const { unravelObject } = require('../service/app.service');
3
2
 
4
3
  module.exports = class Query {
5
4
  constructor(props = {}) {
6
5
  this.props = {};
7
6
  this.timers = {};
7
+ this.isCursorPaging = false;
8
+ this.isClassicPaging = false;
8
9
  this.props.joins = this.props.joins || [];
9
10
  this.props.match = this.props.match || {};
10
11
  this.props.options = this.props.options || {};
11
- this.isClassicPaging = false;
12
- this.isCursorPaging = false;
12
+ this.props.flags = this.props.flags || {
13
+ debug: false,
14
+ silent: false,
15
+ validate: true, // { externals }
16
+ pipeline: true, // { instruct, construct, destruct, restruct, serialize, deserialize, transform, rekey }
17
+ required: false,
18
+ };
13
19
  this.merge(props);
14
20
  }
15
21
 
@@ -22,12 +28,19 @@ module.exports = class Query {
22
28
  id(id) {
23
29
  this.propCheck('id', 'where', 'native', 'sort', 'skip', 'limit', 'before', 'after', 'first', 'last');
24
30
  this.props.id = id;
31
+ this.props.batch = 'id';
32
+ this.props.where = { id };
25
33
  return this.match({ id });
26
34
  }
27
35
 
36
+ batch(batch) {
37
+ this.props.batch = batch;
38
+ return this;
39
+ }
40
+
28
41
  where(where) {
29
42
  this.propCheck('where', 'id', 'native');
30
- this.props.where = unravelObject(where);
43
+ this.props.where = where;
31
44
  return this.match(where);
32
45
  }
33
46
 
@@ -44,12 +57,12 @@ module.exports = class Query {
44
57
  }
45
58
 
46
59
  match(match) {
47
- this.props.match = unravelObject(match);
60
+ this.props.match = match;
48
61
  return this;
49
62
  }
50
63
 
51
64
  select(select) {
52
- this.props.select = unravelObject(select);
65
+ this.props.select = select;
53
66
  return this;
54
67
  }
55
68
 
@@ -70,7 +83,7 @@ module.exports = class Query {
70
83
 
71
84
  sort(sort) {
72
85
  this.propCheck('sort', 'id');
73
- this.props.sort = unravelObject(sort);
86
+ this.props.sort = sort;
74
87
  return this;
75
88
  }
76
89
 
@@ -142,7 +155,7 @@ module.exports = class Query {
142
155
  }
143
156
 
144
157
  flags(flags) {
145
- this.props.flags = flags;
158
+ Object.assign(this.props.flags, flags);
146
159
  return this;
147
160
  }
148
161
 
@@ -161,6 +174,12 @@ module.exports = class Query {
161
174
 
162
175
  resolver(resolver) {
163
176
  this.props.resolver = resolver;
177
+ this.props.context = resolver.getContext();
178
+ return this;
179
+ }
180
+
181
+ context(context) {
182
+ this.props.context = context;
164
183
  return this;
165
184
  }
166
185
 
@@ -175,7 +194,7 @@ module.exports = class Query {
175
194
  }
176
195
 
177
196
  cmd(cmd) {
178
- this.props.cmd = cmd;
197
+ this.props.cmd = cmd; // Terminal cmd from QueryBuilder
179
198
  return this;
180
199
  }
181
200
 
@@ -185,14 +204,32 @@ module.exports = class Query {
185
204
  switch (method) {
186
205
  case 'createOne': case 'createMany': {
187
206
  this.props.crud = 'create';
207
+ this.props.key = `create${this.props.model}`;
188
208
  break;
189
209
  }
190
210
  case 'updateOne': case 'updateMany': {
191
211
  this.props.crud = 'update';
212
+ this.props.key = `update${this.props.model}`;
192
213
  break;
193
214
  }
194
- case 'deleteOne': case 'deleteMay': case 'removeOne': case 'removeMany': {
215
+ case 'deleteOne': case 'deleteMany': case 'removeOne': case 'removeMany': {
195
216
  this.props.crud = 'delete';
217
+ this.props.key = `delete${this.props.model}`;
218
+ break;
219
+ }
220
+ case 'count': {
221
+ this.props.crud = 'read';
222
+ this.props.key = `count${this.props.model}`;
223
+ break;
224
+ }
225
+ case 'findOne': {
226
+ this.props.crud = 'read';
227
+ this.props.key = `get${this.props.model}`;
228
+ break;
229
+ }
230
+ case 'findMany': {
231
+ this.props.crud = 'read';
232
+ this.props.key = `find${this.props.model}`;
196
233
  break;
197
234
  }
198
235
  default: {
@@ -209,6 +246,11 @@ module.exports = class Query {
209
246
  return this;
210
247
  }
211
248
 
249
+ key(key) {
250
+ this.props.key = key;
251
+ return this;
252
+ }
253
+
212
254
  input(input = {}) { // Allows .save(/* empty */);
213
255
  // delete input.id; // We do not want to allow changing id via input
214
256
  this.props.input = input;
@@ -241,10 +283,13 @@ module.exports = class Query {
241
283
  }
242
284
 
243
285
  clone() {
244
- return new Query({ ...this.props });
286
+ const clone = new Query();
287
+ clone.props = { ...this.props };
288
+ return clone;
245
289
  }
246
290
 
247
291
  toDriver() {
292
+ const self = this;
248
293
  const { model } = this.props;
249
294
  const isSorted = Boolean(Object.keys(this.props.$sort || {}).length);
250
295
 
@@ -261,9 +306,22 @@ module.exports = class Query {
261
306
  skip: this.props.skip,
262
307
  limit: this.props.limit,
263
308
  first: isSorted ? this.props.first : undefined,
264
- after: isSorted && this.props.after ? model.normalize(this, JSON.parse(Buffer.from(this.props.after, 'base64').toString('ascii')), 'serialize') : undefined,
265
309
  last: isSorted ? this.props.last : undefined,
266
- before: isSorted && this.props.before ? model.normalize(this, JSON.parse(Buffer.from(this.props.before, 'base64').toString('ascii')), 'serialize') : undefined,
310
+ // before: isSorted && this.props.before ? model.normalize(this, JSON.parse(Buffer.from(this.props.before, 'base64').toString('ascii')), 'serialize') : undefined,
311
+ get before() {
312
+ if (!isSorted || !self.props.before) return undefined;
313
+ const shape = model.getShape('create', 'sort');
314
+ const before = JSON.parse(Buffer.from(self.props.before, 'base64').toString('ascii'));
315
+ const $before = model.shapeObject(shape, before, self);
316
+ return $before;
317
+ },
318
+ get after() {
319
+ if (!isSorted || !self.props.after) return undefined;
320
+ const shape = model.getShape('create', 'sort');
321
+ const after = JSON.parse(Buffer.from(self.props.after, 'base64').toString('ascii'));
322
+ const $after = model.shapeObject(shape, after, self);
323
+ return $after;
324
+ },
267
325
  options: this.props.options,
268
326
  input: this.props.$input,
269
327
  flags: this.props.flags,
@@ -278,7 +336,7 @@ module.exports = class Query {
278
336
 
279
337
  getCacheKey() {
280
338
  return {
281
- // model: `${this.props.model}`,
339
+ cmd: this.props.cmd,
282
340
  method: this.props.method,
283
341
  where: this.props.match,
284
342
  search: this.props.search,
@@ -1,12 +1,12 @@
1
1
  const Query = require('./Query');
2
2
  const QueryResolver = require('./QueryResolver');
3
- const { toKeyObj, unravelObject } = require('../service/app.service');
3
+ const { unravelObject } = require('../service/app.service');
4
4
 
5
5
  /*
6
6
  * QueryBuilder
7
7
  *
8
8
  * Facilitates the creation and execution of a Query. It provides a chainable API to build a query
9
- * plus a list of terminal commands to resolve the query.
9
+ * plus a list of terminal commands to execute the query.
10
10
  */
11
11
  module.exports = class QueryBuilder {
12
12
  constructor(resolver, model) {
@@ -14,13 +14,13 @@ module.exports = class QueryBuilder {
14
14
 
15
15
  // Chainable commands
16
16
  this.id = (id) => { this.query.id(id); return this; };
17
- // this.select = (select) => { this.query.select(select); return this; };
18
- this.select = (select) => { this.query.select(Object.entries(toKeyObj(select)).reduce((prev, [key, value]) => Object.assign(prev, { [key.replace(/edges.node./g, '')]: !!value }), {})); return this; };
19
- this.where = (where) => { this.query.where(where); return this; };
20
- this.match = (match) => { this.query.match(match); return this; };
17
+ this.select = (select) => { this.query.select(unravelObject(select)); return this; };
18
+ // this.select = (select) => { this.query.select(Object.entries(toKeyObj(select)).reduce((prev, [key, value]) => Object.assign(prev, { [key.replace(/edges.node./g, '')]: !!value }), {})); return this; };
19
+ this.where = (where) => { this.query.where(unravelObject(where)); return this; };
20
+ this.match = (match) => { this.query.match(unravelObject(match)); return this; };
21
21
  this.native = (native) => { this.query.native(native); return this; };
22
- this.sort = (sort) => { this.query.sort(sort); return this; };
23
- this.sortBy = (sortBy) => { this.query.sort(sortBy); return this; };
22
+ this.sort = (sort) => { this.query.sort(unravelObject(sort)); return this; };
23
+ this.sortBy = (sortBy) => { this.query.sort(unravelObject(sortBy)); return this; };
24
24
  this.limit = (limit) => { this.query.limit(limit); return this; };
25
25
  this.skip = (skip) => { this.query.skip(skip); return this; };
26
26
  this.before = (cursor) => { this.query.before(cursor); return this; };
@@ -28,64 +28,72 @@ module.exports = class QueryBuilder {
28
28
  this.meta = (meta) => { this.query.meta(meta); return this; };
29
29
  this.flags = (flags) => { this.query.flags(flags); return this; };
30
30
  this.merge = (merge) => { this.query.merge(merge); return this; };
31
+ this.batch = (batch) => { this.query.batch(batch); return this; };
31
32
  this.transaction = (txn) => { this.query.transaction(txn); return this; };
32
33
 
33
34
  // Terminal commands
34
- this.one = (...args) => this.resolve('one', args);
35
- this.many = (...args) => this.resolve('many', args);
36
- this.save = (...args) => this.resolve('save', args.map(arg => unravelObject(arg)));
37
- this.delete = (...args) => this.resolve('delete', args);
38
- this.remove = (...args) => this.resolve('remove', args);
35
+ this.one = (...args) => this.execute('one', args);
36
+ this.many = (...args) => this.execute('many', args);
37
+ this.save = (...args) => this.execute('save', args.map(arg => unravelObject(arg)));
38
+ this.delete = (...args) => this.execute('delete', args);
39
+ this.remove = (...args) => this.execute('remove', args);
40
+ this.resolve = (...args) => this.execute('resolve', args);
39
41
  //
40
- this.count = (...args) => this.resolve('count', args);
41
- this.push = (...args) => this.resolve('push', args.map(arg => unravelObject(arg)));
42
- this.pull = (...args) => this.resolve('pull', args.map(arg => unravelObject(arg)));
43
- this.splice = (...args) => this.resolve('splice', args.map(arg => unravelObject(arg)));
44
- this.first = (...args) => { this.query.first(...args); return this.resolve('first', args); };
45
- this.last = (...args) => { this.query.last(...args); return this.resolve('last', args); };
42
+ this.count = (...args) => this.execute('count', args);
43
+ this.push = (...args) => this.execute('push', args.map(arg => unravelObject(arg)));
44
+ this.pull = (...args) => this.execute('pull', args.map(arg => unravelObject(arg)));
45
+ this.splice = (...args) => this.execute('splice', args.map(arg => unravelObject(arg)));
46
+ this.first = (...args) => { this.query.first(...args); return this.execute('first', args); };
47
+ this.last = (...args) => { this.query.last(...args); return this.execute('last', args); };
46
48
  //
47
49
  // this.min = (...args) => this.makeTheCall(query, 'min', args);
48
50
  // this.max = (...args) => this.makeTheCall(query, 'max', args);
49
51
  // this.avg = (...args) => this.makeTheCall(query, 'avg', args);
52
+ // this.sum = (...args) => this.makeTheCall(query, 'sum', args); // Would sum be different than count?
50
53
  // // Food for thought...
51
54
  // this.archive = (...args) => this.makeTheCall(query, 'archive', args); // Soft Delete
52
55
  // this.stream = (...args) => this.makeTheCall(query, 'stream', args); // Stream records 1 by 1
53
- // this.sum = (...args) => this.makeTheCall(query, 'sum', args); // Would sum be different than count?
54
56
  // this.rollup = (...args) => this.makeTheCall(query, 'rollup', args); // Like sum, but for nested attributes (eg. Person.rollupAuthoredChaptersPages)
55
57
  }
56
58
 
57
- resolve(cmd, args) {
58
- let method, crud, input = {};
59
- let { flags = {} } = this.query.toObject();
59
+ execute(cmd, args) {
60
+ let method, crud, input, flags = {};
60
61
  const { id, where } = this.query.toObject();
61
62
 
62
63
  switch (cmd) {
64
+ case 'resolve': {
65
+ crud = 'read';
66
+ method = 'autoResolve';
67
+ break;
68
+ }
63
69
  case 'one': case 'many': {
64
70
  crud = 'read';
65
- flags = args[0] || flags;
71
+ [flags] = args;
66
72
  method = cmd === 'one' ? 'findOne' : 'findMany';
67
73
  break;
68
74
  }
69
75
  case 'first': case 'last': {
70
76
  crud = 'read';
71
- flags = args[1] || flags;
77
+ [, flags] = args;
72
78
  method = cmd;
73
79
  break;
74
80
  }
75
81
  case 'save': {
82
+ [input, flags] = args;
76
83
  crud = id || where ? 'update' : 'create';
77
- if (crud === 'update') { method = id ? 'updateOne' : 'updateMany'; [input] = args; }
78
- if (crud === 'create') { method = args.length < 2 ? 'createOne' : 'createMany'; input = args.length < 2 ? args[0] || {} : args; }
84
+ if (crud === 'update') { method = id ? 'updateOne' : 'updateMany'; }
85
+ if (crud === 'create') { method = Array.isArray(input) ? 'createMany' : 'createOne'; }
79
86
  break;
80
87
  }
81
88
  case 'push': case 'pull': case 'splice': {
89
+ // [target, input, flags] = args;
82
90
  crud = 'update'; // Your logic wants this to be a simple "update". Sub documents systemEvents will emit either "create" or "udpate"
83
91
  method = id ? `${cmd}One` : `${cmd}Many`;
84
92
  break;
85
93
  }
86
94
  case 'remove': case 'delete': {
87
95
  crud = 'delete';
88
- flags = args[0] || flags;
96
+ [flags] = args;
89
97
  if (id) method = 'deleteOne';
90
98
  else if (where) method = 'deleteMany';
91
99
  else return Promise.reject(new Error('Remove requires an id() or where()'));
@@ -93,7 +101,7 @@ module.exports = class QueryBuilder {
93
101
  }
94
102
  case 'count': {
95
103
  crud = 'read';
96
- flags = args[0] || flags;
104
+ [flags] = args;
97
105
  method = 'count';
98
106
  break;
99
107
  }
@@ -102,6 +110,6 @@ module.exports = class QueryBuilder {
102
110
  }
103
111
  }
104
112
 
105
- return new QueryResolver(this.query.cmd(cmd).method(method).crud(crud).input(input).flags(flags).args(args)).resolve();
113
+ return new QueryResolver(this.query.method(method).cmd(cmd).crud(crud).input(input).flags(flags).args(args)).resolve();
106
114
  }
107
115
  };
@@ -6,7 +6,7 @@ module.exports = class QueryBuilderTransaction extends QueryBuilder {
6
6
  this.query.transaction(transaction);
7
7
  }
8
8
 
9
- resolve(cmd, args) {
9
+ execute(cmd, args) {
10
10
  return new Promise((resolve, reject) => {
11
11
  this.theCall = { cmd, args, resolve, reject };
12
12
  });
@@ -15,10 +15,10 @@ module.exports = class QueryBuilderTransaction extends QueryBuilder {
15
15
  exec(options) {
16
16
  if (!this.theCall) return undefined;
17
17
 
18
- this.query.options(options);
19
18
  const { cmd, args, resolve } = this.theCall;
19
+ this.query.options(options);
20
20
 
21
- return super.resolve(cmd, args).then((result) => {
21
+ return super.execute(cmd, args).then((result) => {
22
22
  resolve(result);
23
23
  return result;
24
24
  });