@coderich/autograph 0.10.1 → 0.10.4

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.
package/src/data/Type.js CHANGED
@@ -11,20 +11,39 @@ module.exports = class extends Type {
11
11
  const type = this.field.getType();
12
12
  const enumType = this.field.getEnumRef();
13
13
  const scalarType = this.field.getScalarRef();
14
- const structures = { instructs: [], restructs: [], destructs: [], constructs: [], $serializers: [], serializers: [], $deserializers: [], deserializers: [], transformers: [] };
14
+ const structures = {
15
+ validators: [],
16
+ instructs: [],
17
+ restructs: [],
18
+ destructs: [],
19
+ constructs: [],
20
+ normalizers: [],
21
+ $serializers: [],
22
+ $deserializers: [],
23
+ serializers: [],
24
+ deserializers: [],
25
+ transforms: [],
26
+ };
15
27
 
16
- if (enumType) structures.serializers.push(Pipeline.define(`$allow:${type}`, Pipeline.allow(...enumType.getValue()), { configurable: true }));
28
+ // Built-in pipelines
29
+ structures.castValue = Pipeline.castValue;
30
+ structures.defaultValue = Pipeline.defaultValue;
31
+ structures.ensureArrayValue = Pipeline.ensureArrayValue;
32
+
33
+ if (enumType) structures.validators.push(Pipeline.define(`allow:${type}`, Pipeline.Allow(...enumType.getValue()), { configurable: true }));
17
34
  if (!scalarType) return structures;
18
35
 
19
36
  return Object.entries(scalarType.getDirectiveArgs('field', {})).reduce((prev, [key, value]) => {
20
37
  if (!Array.isArray(value)) value = [value];
38
+ if (key === 'validate') prev.validators.push(...value.map(t => Pipeline[t]));
21
39
  if (key === 'instruct') prev.instructs.push(...value.map(t => Pipeline[t]));
22
40
  if (key === 'restruct') prev.restructs.push(...value.map(t => Pipeline[t]));
23
41
  if (key === 'destruct') prev.destructs.push(...value.map(t => Pipeline[t]));
24
42
  if (key === 'construct') prev.constructs.push(...value.map(t => Pipeline[t]));
43
+ if (key === 'transform') prev.transforms.push(...value.map(t => Pipeline[t]));
44
+ if (key === 'normalize') prev.normalizers.push(...value.map(t => Pipeline[t]));
25
45
  if (key === 'serialize') prev.serializers.push(...value.map(t => Pipeline[t]));
26
46
  if (key === 'deserialize') prev.deserializers.push(...value.map(t => Pipeline[t]));
27
- if (key === 'transform') prev.transformers.push(...value.map(t => Pipeline[t]));
28
47
  return prev;
29
48
  }, structures);
30
49
  }
@@ -1,6 +1,6 @@
1
1
  const Util = require('util');
2
2
  const { get } = require('lodash');
3
- const { MongoClient, ObjectID } = require('mongodb');
3
+ const { MongoClient, ObjectId } = require('mongodb');
4
4
  const { map, ensureArray, proxyDeep, toKeyObj, globToRegex, proxyPromise, isScalarDataType, promiseRetry } = require('../service/app.service');
5
5
 
6
6
  module.exports = class MongoDriver {
@@ -64,15 +64,12 @@ module.exports = class MongoDriver {
64
64
  }
65
65
 
66
66
  createOne({ model, input, options, flags }) {
67
+ // console.log(JSON.stringify(input, null, 2));
67
68
  return this.query(model, 'insertOne', input, options, flags).then(result => Object.assign(input, { id: result.insertedId }));
68
69
  }
69
70
 
70
71
  updateOne({ model, where, $doc, options, flags }) {
71
- const $update = Object.entries($doc).reduce((prev, [key, value]) => {
72
- Object.assign(prev.$set, { [key]: value });
73
- return prev;
74
- }, { $set: {} });
75
-
72
+ const $update = { $set: $doc };
76
73
  return this.query(model, 'updateOne', where, $update, options, flags).then(() => $doc);
77
74
  }
78
75
 
@@ -126,10 +123,10 @@ module.exports = class MongoDriver {
126
123
  }
127
124
 
128
125
  static idValue(value) {
129
- if (value instanceof ObjectID) return value;
126
+ if (value instanceof ObjectId) return value;
130
127
 
131
128
  try {
132
- const id = ObjectID(value);
129
+ const id = ObjectId(value);
133
130
  return id;
134
131
  } catch (e) {
135
132
  return value;
@@ -142,7 +139,10 @@ module.exports = class MongoDriver {
142
139
  const value = Reflect.get(target, prop, rec);
143
140
  if (typeof value === 'function') return value.bind(target);
144
141
  const $value = map(value, v => (typeof v === 'string' ? globToRegex(v, { nocase: true, regex: true }) : v));
145
- if (Array.isArray($value)) return { $in: $value };
142
+ if (Array.isArray($value)) {
143
+ // console.log(Util.inspect({ value, $value }, { depth: null, showHidden: false, colors: true }));
144
+ return { $in: $value };
145
+ }
146
146
  return $value;
147
147
  },
148
148
  }).toObject();
@@ -173,20 +173,25 @@ module.exports = class Field extends Node {
173
173
  return this.getGQLType();
174
174
  }
175
175
 
176
- initialize() {
176
+ finalize() {
177
177
  this.props = {
178
+ key: this.getKey(),
178
179
  name: this.getName(),
179
180
  type: this.getType(),
181
+ model: this.model,
180
182
  datatype: this.getDataType(),
181
183
  defaultValue: this.getDefaultValue(),
184
+ isEnum: this.isEnum(),
182
185
  isArray: this.isArray(),
183
186
  isScalar: this.isScalar(),
184
187
  isVirtual: this.isVirtual(),
185
188
  isRequired: this.isRequired(),
186
189
  isEmbedded: this.isEmbedded(),
190
+ isBasicType: this.isBasicType(),
187
191
  isIdField: this.isIdField(),
188
192
  isPrimaryKeyId: this.isPrimaryKeyId(),
189
193
  isPersistable: this.isPersistable(),
194
+ idModel: this.getIdModel(),
190
195
  modelRef: this.getModelRef(),
191
196
  virtualRef: this.getVirtualRef(),
192
197
  virtualField: this.getVirtualField(),
@@ -138,8 +138,8 @@ module.exports = class Model extends Node {
138
138
  });
139
139
  }
140
140
 
141
- initialize() {
142
- this.fields.forEach(field => field.initialize());
141
+ finalize() {
142
+ this.fields.forEach(field => field.finalize());
143
143
  return this;
144
144
  }
145
145
  };
@@ -2,7 +2,7 @@ const FS = require('fs');
2
2
  const Glob = require('glob');
3
3
  const Merge = require('deepmerge');
4
4
  const { Kind, print, parse, visit } = require('graphql');
5
- const { mergeASTArray, makeExecutableSchema } = require('../../service/graphql.service');
5
+ const { mergeASTArray } = require('../../service/graphql.service');
6
6
  const { deleteKeys } = require('../../service/app.service');
7
7
  const frameworkExt = require('../extension/framework');
8
8
  const typeExt = require('../extension/type');
@@ -90,7 +90,6 @@ module.exports = class Schema extends TypeDefApi {
90
90
  */
91
91
  initialize() {
92
92
  super.initialize(this.schema.typeDefs);
93
- this.getModels().forEach(model => model.initialize());
94
93
  return this;
95
94
  }
96
95
 
@@ -119,14 +118,11 @@ module.exports = class Schema extends TypeDefApi {
119
118
  },
120
119
  });
121
120
 
121
+ this.getModels().forEach(model => model.finalize());
122
122
  this.schema.typeDefs = { kind: Kind.DOCUMENT, definitions };
123
123
  return this;
124
124
  }
125
125
 
126
- makeExecutableSchema() {
127
- return makeExecutableSchema(this.schema);
128
- }
129
-
130
126
  toObject() {
131
127
  return this.schema;
132
128
  }
@@ -163,8 +163,8 @@ module.exports = (schema) => {
163
163
  [modelName]: fieldResolvers,
164
164
  [`${modelName}Connection`]: {
165
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')),
166
+ edges: ({ edges }) => edges().then(rs => rs.map(node => ({ cursor: get(node, '$cursor'), node }))),
167
+ pageInfo: ({ pageInfo }) => pageInfo().then(rs => get(rs, '$pageInfo')),
168
168
  },
169
169
  });
170
170
  }, {
@@ -31,11 +31,11 @@ module.exports = (schema) => {
31
31
 
32
32
  directive @field(
33
33
  id: String # Specify the ModelRef this field FK References
34
+ ref: AutoGraphMixed # Specify the modelRef field's name (overrides isEmbedded)
34
35
  key: String # Specify db key
35
36
  persist: Boolean # Persist this field (default true)
36
37
  connection: Boolean # Treat this field as a connection type (default false - rolling this out slowly)
37
38
  default: AutoGraphMixed # Define a default value
38
- ref: AutoGraphMixed # Specify the modelRef field's name (overrides isEmbedded)
39
39
  gqlScope: AutoGraphMixed # Dictate how GraphQL API behaves
40
40
  dalScope: AutoGraphMixed # Dictate how the DAL behaves
41
41
  fieldScope: AutoGraphMixed # Dictate how a FIELD may use me
@@ -44,13 +44,15 @@ module.exports = (schema) => {
44
44
  authz: AutoGraphAuthzEnum # Access level used for authorization (default: private)
45
45
 
46
46
  # Pipeline Structure
47
+ validate: [AutoGraphPipelineEnum!]
47
48
  instruct: [AutoGraphPipelineEnum!]
48
- destruct: [AutoGraphPipelineEnum!]
49
49
  restruct: [AutoGraphPipelineEnum!]
50
+ destruct: [AutoGraphPipelineEnum!]
50
51
  construct: [AutoGraphPipelineEnum!]
52
+ transform: [AutoGraphPipelineEnum!]
53
+ normalize: [AutoGraphPipelineEnum!]
51
54
  serialize: [AutoGraphPipelineEnum!]
52
55
  deserialize: [AutoGraphPipelineEnum!]
53
- transform: [AutoGraphPipelineEnum!]
54
56
  ) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION | SCALAR
55
57
 
56
58
  directive @link(
@@ -17,7 +17,7 @@ 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}", serialize: createdAt, gqlScope: r)` : ''}
20
+ ${createdAt ? `createdAt: AutoGraphDateTime @field(key: "${createdAt}", construct: createdAt, gqlScope: r)` : ''}
21
21
  ${updatedAt ? `updatedAt: AutoGraphDateTime @field(key: "${updatedAt}", serialize: timestamp, gqlScope: r)` : ''}
22
22
  }
23
23
  `;
@@ -272,6 +272,16 @@ module.exports = class Query {
272
272
  return this;
273
273
  }
274
274
 
275
+ payload(payload) {
276
+ this.props.payload = payload;
277
+ return this;
278
+ }
279
+
280
+ result(result) {
281
+ this.props.result = result;
282
+ return this;
283
+ }
284
+
275
285
  $doc($doc) {
276
286
  this.props.$doc = $doc;
277
287
  return this;
@@ -10,6 +10,7 @@ const { unravelObject } = require('../service/app.service');
10
10
  */
11
11
  module.exports = class QueryBuilder {
12
12
  constructor(resolver, model) {
13
+ this.terminated = false; // Prevent accidental re-use of the QueryBuilder
13
14
  this.query = new Query({ model, resolver });
14
15
 
15
16
  // Chainable commands
@@ -57,6 +58,10 @@ module.exports = class QueryBuilder {
57
58
  }
58
59
 
59
60
  execute(cmd, args) {
61
+ // Do not allow re-use
62
+ if (this.terminated) return Promise.reject(new Error('This query has already been executed'));
63
+
64
+ this.terminated = true;
60
65
  let method, crud, input, flags = {};
61
66
  const { id, where } = this.query.toObject();
62
67
 
@@ -16,7 +16,7 @@ module.exports = class QueryResolver {
16
16
  const { args } = query.toObject();
17
17
  const [,,, info] = args;
18
18
 
19
- switch (getGQLReturnType(info.returnType)) {
19
+ switch (getGQLReturnType(`${info.returnType}`)) {
20
20
  case 'array': {
21
21
  return new QueryResolver(this.query.clone().method('findMany')).resolve();
22
22
  }
@@ -36,42 +36,35 @@ module.exports = class QueryResolver {
36
36
  }
37
37
  }
38
38
 
39
- async findOne(query) {
40
- await QueryService.resolveQuery(query);
41
-
42
- return createSystemEvent('Query', { query }, () => {
39
+ findOne(query) {
40
+ return createSystemEvent('Query', { query }, async () => {
43
41
  return this.resolver.resolve(query);
44
42
  });
45
43
  }
46
44
 
47
- async findMany(query) {
48
- await QueryService.resolveQuery(query);
49
-
50
- return createSystemEvent('Query', { query }, () => {
45
+ findMany(query) {
46
+ return createSystemEvent('Query', { query }, async () => {
51
47
  return this.resolver.resolve(query);
52
48
  });
53
49
  }
54
50
 
55
- async count(query) {
56
- await QueryService.resolveQuery(query);
57
-
58
- return createSystemEvent('Query', { query }, () => {
51
+ count(query) {
52
+ return createSystemEvent('Query', { query }, async () => {
59
53
  return this.resolver.resolve(query);
60
54
  });
61
55
  }
62
56
 
63
- async createOne(query) {
57
+ createOne(query) {
64
58
  const { model, input } = query.toObject();
65
- const shape = model.getShape('create');
66
-
67
- await QueryService.resolveQuery(query);
68
-
69
- return createSystemEvent('Mutation', { query }, async () => {
70
- const $input = model.shapeObject(shape, input, query);
71
- await model.validate(query, $input);
72
- const doc = await this.resolver.resolve(query.$input($input));
73
- query.doc(doc);
74
- return doc;
59
+ const inputShape = model.getShape('create', 'input');
60
+ const docShape = model.getShape('create', 'doc');
61
+ const doc = model.shapeObject(inputShape, {}, query); // We use input shape here
62
+ const merged = mergeDeep(doc, input);
63
+
64
+ return createSystemEvent('Mutation', { query: query.doc(doc).merged(merged) }, async () => {
65
+ const payload = model.shapeObject(inputShape, merged, query);
66
+ await model.validateObject(inputShape, payload, query.payload(payload));
67
+ return this.resolver.resolve(query.$input(model.shapeObject(docShape, payload, query)));
75
68
  });
76
69
  }
77
70
 
@@ -84,17 +77,16 @@ module.exports = class QueryResolver {
84
77
 
85
78
  async updateOne(query) {
86
79
  const { model, match, input } = query.toObject();
80
+ const inputShape = model.getShape('update', 'input');
81
+ const docShape = model.getShape('update', 'doc');
87
82
 
88
- return this.resolver.match(model).match(match).one({ required: true }).then(async (doc) => {
89
- const shape = model.getShape('update');
90
- const merged = model.shapeObject(shape, mergeDeep(doc, input), query);
91
-
92
- await QueryService.resolveQuery(query);
83
+ return this.resolver.match(model).match(match).one({ required: true }).then((doc) => {
84
+ const merged = mergeDeep(doc, input);
93
85
 
94
86
  return createSystemEvent('Mutation', { query: query.doc(doc).merged(merged) }, async () => {
95
- const $doc = model.shapeObject(shape, mergeDeep(doc, input), query);
96
- await model.validate(query, $doc);
97
- return this.resolver.resolve(query.$doc($doc));
87
+ const payload = model.shapeObject(inputShape, merged, query);
88
+ await model.validateObject(inputShape, payload, query.payload(payload));
89
+ return this.resolver.resolve(query.$doc(model.shapeObject(docShape, payload, query)));
98
90
  });
99
91
  });
100
92
  }
@@ -110,11 +102,9 @@ module.exports = class QueryResolver {
110
102
  }
111
103
 
112
104
  deleteOne(query) {
113
- const { model, id, flags } = query.toObject();
114
-
115
- return this.resolver.match(model).id(id).flags(flags).one({ required: true }).then(async (doc) => {
116
- await QueryService.resolveQuery(query);
105
+ const { model, id } = query.toObject();
117
106
 
107
+ return this.resolver.match(model).id(id).one({ required: true }).then(async (doc) => {
118
108
  return createSystemEvent('Mutation', { query: query.doc(doc) }, () => {
119
109
  return QueryService.resolveReferentialIntegrity(query).then(() => {
120
110
  return this.resolver.resolve(query).then(() => doc);
@@ -186,7 +176,10 @@ module.exports = class QueryResolver {
186
176
  }
187
177
 
188
178
  splice(query) {
189
- const { model, match, args, flags = {} } = query.toObject();
179
+ const { model, match, args } = query.toObject();
180
+ const docShape = model.getShape('update', 'doc');
181
+ const inputShape = model.getShape('update', 'input');
182
+ const spliceShape = model.getShape('update', 'splice');
190
183
  const [key, from, to] = args;
191
184
 
192
185
  // Can only splice arrays
@@ -194,20 +187,16 @@ module.exports = class QueryResolver {
194
187
  const isArray = field.isArray();
195
188
  if (!isArray) throw Boom.badRequest(`Cannot splice field '${model}.${field}'`);
196
189
 
197
- return this.resolver.match(model).match(match).flags(flags).one({ required: true }).then(async (doc) => {
190
+ return this.resolver.match(model).match(match).one({ required: true }).then(async (doc) => {
198
191
  const array = get(doc, key) || [];
199
- const paramShape = model.getShape('create', 'spliceTo');
200
- const $to = model.shapeObject(paramShape, { [key]: to }, query)[key] || to;
201
- const $from = model.shapeObject(paramShape, { [key]: from }, query)[key] || from;
192
+ const $to = model.shapeObject(spliceShape, { [key]: to }, query)[key] || to;
193
+ const $from = model.shapeObject(spliceShape, { [key]: from }, query)[key] || from;
202
194
  set(doc, key, DataService.spliceEmbeddedArray(array, $from, $to));
203
195
 
204
- await QueryService.resolveQuery(query);
205
-
206
196
  return createSystemEvent('Mutation', { query: query.method('updateOne').doc(doc).merged(doc) }, async () => {
207
- const shape = model.getShape('update');
208
- const $doc = model.shapeObject(shape, doc, query);
209
- await model.validate(query, $doc);
210
- return this.resolver.resolve(query.$doc($doc));
197
+ const payload = model.shapeObject(inputShape, doc, query);
198
+ await model.validateObject(inputShape, payload, query.payload(payload));
199
+ return this.resolver.resolve(query.$doc(model.shapeObject(docShape, payload, query)));
211
200
  });
212
201
  });
213
202
  }
@@ -223,9 +212,6 @@ module.exports = class QueryResolver {
223
212
  async resolve() {
224
213
  const { model, method, flags } = this.query.toObject();
225
214
 
226
- // const resolveQueryMethods = ['findOne', 'findMany', 'count', 'createOne', 'updateOne', 'deleteOne', 'splice'];
227
- // if (resolveQueryMethods.indexOf(method) > -1) await QueryService.resolveQuery(this.query);
228
-
229
215
  return this[method](this.query).then((data) => {
230
216
  if (flags.required && isEmpty(data)) throw Boom.notFound(`${model} Not Found`);
231
217
  if (data == null) return null; // Explicitly return null here
@@ -6,7 +6,7 @@ const { keyPaths, ensureArray, isPlainObject } = require('../service/app.service
6
6
  * This can happen because the where clause reaches into the schema via refs/virtual refs
7
7
  */
8
8
  exports.resolveWhereClause = (query) => {
9
- const { resolver, model, match: where = {}, flags = {} } = query.toObject();
9
+ const { resolver, model, match: where = {} } = query.toObject();
10
10
  const shape = model.getShape('create', 'where');
11
11
 
12
12
  const $where = Object.entries(where).reduce((prev, [from, value]) => {
@@ -16,12 +16,12 @@ exports.resolveWhereClause = (query) => {
16
16
  const { isVirtual, isEmbedded, modelRef, virtualRef } = el.field.toObject();
17
17
 
18
18
  if (isVirtual) {
19
- const ids = Promise.all(ensureArray(value).map(v => resolver.match(modelRef).where(isPlainObject(v) ? v : { id: v }).many(flags).then(docs => docs.map(doc => doc[virtualRef])))).then(results => uniq(flattenDeep(results)));
19
+ const ids = Promise.all(ensureArray(value).map(v => resolver.match(modelRef).where(isPlainObject(v) ? v : { id: v }).many().then(docs => docs.map(doc => doc[virtualRef])))).then(results => uniq(flattenDeep(results)));
20
20
  return Object.assign(prev, { id: ids });
21
21
  }
22
22
 
23
23
  if (modelRef && !isEmbedded) {
24
- const ids = Promise.all(ensureArray(value).map(v => (isPlainObject(v) ? resolver.match(modelRef).where(v).many(flags).then(docs => docs.map(doc => doc.id)) : Promise.resolve(v)))).then(results => uniq(flattenDeep(results)));
24
+ const ids = Promise.all(ensureArray(value).map(v => (isPlainObject(v) ? resolver.match(modelRef).where(v).many().then(docs => docs.map(doc => doc.id)) : Promise.resolve(v)))).then(results => uniq(flattenDeep(results)));
25
25
  return Object.assign(prev, { [from]: ids });
26
26
  }
27
27
 
@@ -67,7 +67,7 @@ exports.resolveSortBy = (query) => {
67
67
  };
68
68
 
69
69
  exports.resolveReferentialIntegrity = (query) => {
70
- const { id, model, resolver, transaction, flags } = query.toObject();
70
+ const { id, model, resolver, transaction } = query.toObject();
71
71
  const txn = resolver.transaction(transaction);
72
72
 
73
73
  return new Promise((resolve, reject) => {
@@ -79,18 +79,18 @@ exports.resolveReferentialIntegrity = (query) => {
79
79
  switch (op) {
80
80
  case 'cascade': {
81
81
  if (isArray) {
82
- txn.match(ref).where($where).flags(flags).pull(fieldStr, id);
82
+ txn.match(ref).where($where).pull(fieldStr, id);
83
83
  } else {
84
- txn.match(ref).where($where).flags(flags).remove();
84
+ txn.match(ref).where($where).remove();
85
85
  }
86
86
  break;
87
87
  }
88
88
  case 'nullify': {
89
- txn.match(ref).where($where).flags(flags).save({ [fieldStr]: null });
89
+ txn.match(ref).where($where).save({ [fieldStr]: null });
90
90
  break;
91
91
  }
92
92
  case 'restrict': {
93
- txn.match(ref).where($where).flags(flags).count().then(count => (count ? reject(new Error('Restricted')) : count));
93
+ txn.match(ref).where($where).count().then(count => (count ? reject(new Error('Restricted')) : count));
94
94
  break;
95
95
  }
96
96
  case 'defer': {
@@ -109,18 +109,3 @@ exports.resolveReferentialIntegrity = (query) => {
109
109
  }
110
110
  });
111
111
  };
112
-
113
- exports.resolveQuery = async (query) => {
114
- const { model, sort, native, batch, match } = query.toObject();
115
-
116
- if (!native) {
117
- const shape = model.getShape('create', 'where');
118
- const $where = batch ? match : await exports.resolveWhereClause(query);
119
- const $$where = model.shapeObject(shape, $where, query);
120
- query.match($$where);
121
- }
122
-
123
- if (sort) {
124
- query.$sort(exports.resolveSortBy(query));
125
- }
126
- };
@@ -2,7 +2,7 @@ const _ = require('lodash');
2
2
  const PicoMatch = require('picomatch');
3
3
  const FillRange = require('fill-range');
4
4
  const DeepMerge = require('deepmerge');
5
- const { ObjectID } = require('mongodb');
5
+ const { ObjectId } = require('mongodb');
6
6
  const ObjectHash = require('object-hash');
7
7
 
8
8
  // const combineMerge = (target, source, options) => {
@@ -32,15 +32,15 @@ exports.id = '3d896496-02a3-4ee5-8e42-2115eb215f7e';
32
32
  exports.ucFirst = string => string.charAt(0).toUpperCase() + string.slice(1);
33
33
  exports.lcFirst = string => string.charAt(0).toLowerCase() + string.slice(1);
34
34
  exports.isNumber = value => typeof value === 'number' && Number.isFinite(value);
35
- exports.isBasicObject = obj => obj != null && typeof obj === 'object' && !(ObjectID.isValid(obj)) && !(obj instanceof Date) && typeof (obj.then) !== 'function';
35
+ exports.isBasicObject = obj => obj != null && typeof obj === 'object' && !(ObjectId.isValid(obj)) && !(obj instanceof Date) && typeof (obj.then) !== 'function';
36
36
  exports.isPlainObject = obj => exports.isBasicObject(obj) && !Array.isArray(obj);
37
37
  exports.isScalarValue = value => typeof value !== 'object' && typeof value !== 'function';
38
38
  exports.isScalarDataType = value => ['String', 'Float', 'Int', 'Boolean', 'DateTime'].indexOf(value) > -1;
39
- exports.isIdValue = value => exports.isScalarValue(value) || value instanceof ObjectID;
39
+ exports.isIdValue = value => exports.isScalarValue(value) || value instanceof ObjectId;
40
40
  exports.mergeDeep = (...args) => DeepMerge.all(args, { isMergeableObject: obj => (exports.isPlainObject(obj) || Array.isArray(obj)), arrayMerge: smartMerge });
41
41
  exports.uniq = arr => [...new Set(arr.map(a => `${a}`))];
42
42
  exports.timeout = ms => new Promise(res => setTimeout(res, ms));
43
- exports.hashObject = obj => ObjectHash(obj, { respectType: false, respectFunctionNames: false, respectFunctionProperties: false, unorderedArrays: true, ignoreUnknown: true, replacer: r => (r instanceof ObjectID ? `${r}` : r) });
43
+ exports.hashObject = obj => ObjectHash(obj, { respectType: false, respectFunctionNames: false, respectFunctionProperties: false, unorderedArrays: true, ignoreUnknown: true, replacer: r => (r instanceof ObjectId ? `${r}` : r) });
44
44
  exports.globToRegex = (glob, options = {}) => PicoMatch.makeRe(glob, { ...options, expandRange: (a, b) => `(${FillRange(a, b, { toRegex: true })})` });
45
45
  exports.globToRegexp = (glob, options = {}) => PicoMatch.toRegex(exports.globToRegex(glob, options));
46
46
  exports.toGUID = (model, id) => Buffer.from(`${model},${`${id}`}`).toString('base64');
@@ -95,8 +95,7 @@ exports.map = (mixed, fn) => {
95
95
  if (mixed == null) return mixed;
96
96
  const isArray = Array.isArray(mixed);
97
97
  const arr = isArray ? mixed : [mixed];
98
- // const results = arr.map((...args) => fn(...args));
99
- const results = arr.map((el, i, a) => fn(el, isArray ? i : undefined, isArray ? a : undefined));
98
+ const results = isArray ? arr.map((...args) => fn(...args)) : arr.map(el => fn(el));
100
99
  return isArray ? results : results[0];
101
100
  };
102
101
 
@@ -1,3 +1,4 @@
1
+ const QueryService = require('../query/QueryService');
1
2
  const EventEmitter = require('../core/EventEmitter');
2
3
  const { ucFirst } = require('./app.service');
3
4
 
@@ -8,19 +9,49 @@ const systemEvent = new EventEmitter().setMaxListeners(100).on('system', async (
8
9
  next(await eventEmitter.emit(type, data)); // Return result from user-defined middleware
9
10
  });
10
11
 
12
+ const makeEvent = (mixed) => {
13
+ const { query } = mixed;
14
+ const event = query.toObject();
15
+ event.query = query;
16
+ return event;
17
+ };
18
+
19
+ const makeMiddleware = () => {
20
+ return (mixed) => {
21
+ const { query } = mixed;
22
+ const { model, native, sort, match, batch } = query.toObject();
23
+
24
+ return new Promise(async (resolve) => {
25
+ if (!native) {
26
+ const whereShape = model.getShape('create', 'where');
27
+ const $where = batch ? match : await QueryService.resolveWhereClause(query);
28
+ const $$where = model.shapeObject(whereShape, $where, query);
29
+ query.match($$where);
30
+ }
31
+
32
+ if (sort) {
33
+ query.$sort(QueryService.resolveSortBy(query));
34
+ }
35
+
36
+ resolve();
37
+ });
38
+ };
39
+ };
40
+
11
41
  //
12
42
  exports.createSystemEvent = (name, mixed = {}, thunk = () => {}) => {
13
43
  let event = mixed;
44
+ let middleware = () => Promise.resolve();
14
45
  const type = ucFirst(name);
15
46
 
16
- if (name !== 'Setup' && name !== 'Response') {
17
- const { query } = mixed;
18
- event = query.toObject();
19
- event.query = query;
47
+ if (name !== 'Response') {
48
+ event = makeEvent(mixed);
49
+ middleware = makeMiddleware();
20
50
  }
21
51
 
22
- return systemEvent.emit('system', { type: `pre${type}`, data: event }).then((result) => {
23
- return (result !== undefined) ? result : thunk(); // Allowing middleware to dictate result
52
+ return systemEvent.emit('system', { type: `pre${type}`, data: event }).then(async (result) => {
53
+ if (result !== undefined) return result; // Allowing middleware to dictate result
54
+ return middleware(mixed).then(thunk);
24
55
  }).then((result) => {
25
56
  event.result = result;
26
57
  if (event.crud === 'create') event.doc = event.query.toObject().doc;
@@ -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/schema');
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'));
@@ -1,21 +0,0 @@
1
- const { graphql } = require('graphql');
2
-
3
- /**
4
- * GraphQL.
5
- *
6
- * This is a wrapper class to the underlying GraphQL Executable Schema.
7
- * It can be useful for testing and/or exercising the API as an outside caller would.
8
- *
9
- * Reference: https://github.com/graphql/graphql-js/blob/master/src/graphql.js#L32-L33
10
- */
11
- module.exports = class GraphQL {
12
- constructor(schema, resolver) {
13
- this.schema = schema.makeExecutableSchema();
14
- this.contextValue = resolver.getContext();
15
- }
16
-
17
- exec(source, variableValues) {
18
- const { schema, contextValue = {} } = this;
19
- return graphql({ schema, source, variableValues, contextValue });
20
- }
21
- };