@coderich/autograph 0.10.3 → 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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coderich/autograph",
3
3
  "author": "Richard Livolsi (coderich)",
4
- "version": "0.10.3",
4
+ "version": "0.10.4",
5
5
  "description": "AutoGraph",
6
6
  "keywords": [
7
7
  "graphql",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "scripts": {
25
25
  "start": "APP_ROOT_PATH=$(pwd) node ./test/server",
26
- "test": "APP_ROOT_PATH=$(pwd) ratchet test --forceExit",
26
+ "test": "APP_ROOT_PATH=$(pwd) ratchet test",
27
27
  "test:debug": "APP_ROOT_PATH=$(pwd) node --inspect-brk ./node_modules/jest/bin/jest.js --watch --runInBand --logHeapUsage",
28
28
  "lint": "APP_ROOT_PATH=$(pwd) ratchet lint",
29
29
  "inspect": "APP_ROOT_PATH=$(pwd) node --expose-gc --inspect=9222 ./src/server",
@@ -37,10 +37,18 @@ module.exports = class extends Schema {
37
37
  });
38
38
  }
39
39
 
40
+ disconnect() {
41
+ return Promise.all(Object.values(this.drivers).map(({ dao }) => dao.disconnect()));
42
+ }
43
+
40
44
  initialize() {
41
45
  super.initialize();
42
46
  this.models = super.getModels().map(model => new Model(this, model, this.drivers[model.getDriverName()]));
43
- this.models.forEach(model => model.initialize());
47
+ return this;
48
+ }
49
+
50
+ finalize() {
51
+ super.finalize();
44
52
  this.models.forEach(model => model.referentialIntegrity(identifyOnDeletes(this.models, model)));
45
53
  return this;
46
54
  }
@@ -42,8 +42,6 @@ module.exports = class DataLoader extends FBDataLoader {
42
42
  */
43
43
  const whereShape = model.getShape('create', 'where');
44
44
 
45
- // console.log(Object.entries(batchQueries).map(([key, value]) => ({ [key]: value.length })));
46
-
47
45
  return Promise.all(Object.entries(batchQueries).map(([key, values]) => {
48
46
  switch (key) {
49
47
  case defaultBatchName: {
@@ -52,7 +50,7 @@ module.exports = class DataLoader extends FBDataLoader {
52
50
  default: {
53
51
  const keys = Array.from(new Set(values.map(({ where }) => map(where[key], el => `${el}`)).flat()));
54
52
  const batchQuery = new Query({ resolver, model, method: 'findMany', crud: 'read' });
55
- const batchWhere = model.shapeObject(whereShape, { [key]: keys }, batchQuery); // This will add back instructs etc
53
+ const batchWhere = model.shapeObject(whereShape, { ...values[0].where, [key]: keys }, batchQuery); // All where's should be the same - this is for idKey on keys etc
56
54
 
57
55
  return driver.resolve(batchQuery.where(batchWhere).toDriver()).then(data => handleData(data, model, batchQuery)).then((results) => {
58
56
  // One-time data transformation on results to make matching back faster (below)
package/src/data/Model.js CHANGED
@@ -73,6 +73,7 @@ module.exports = class extends Model {
73
73
  const serdes = crud === 'read' ? 'deserialize' : 'serialize';
74
74
  const fields = serdes === 'deserialize' ? this.getSelectFields() : this.getPersistableFields();
75
75
  const crudMap = { create: ['constructs'], update: ['restructs'], delete: ['destructs'], remove: ['destructs'] };
76
+ const sortKeys = ['isIdField', 'isBasicType', 'isEmbedded'];
76
77
  const crudKeys = crudMap[crud] || [];
77
78
 
78
79
  // Define target mapping
@@ -82,10 +83,29 @@ module.exports = class extends Model {
82
83
  // input: ['defaultValue', 'castValue', 'ensureArrayValue'],
83
84
  where: ['castValue', 'instructs', `$${serdes}rs`],
84
85
  };
86
+
85
87
  const structureKeys = targetMap[target] || ['castValue'];
86
88
 
87
- // Create shape, recursive
88
- const shape = fields.map((field) => {
89
+ // Create sorted shape, recursive
90
+ const shape = fields.sort((a, b) => {
91
+ const aObject = a.toObject();
92
+ const bObject = b.toObject();
93
+
94
+ // PK first
95
+ if (aObject.isPrimaryKeyId) return -1;
96
+ if (bObject.isPrimaryKeyId) return 1;
97
+
98
+ // Arrays last
99
+ if (aObject.isArray && !bObject.isArray) return 1;
100
+ if (bObject.isArray && !aObject.isArray) return -1;
101
+
102
+ // Now, follow sort keys
103
+ const aNum = sortKeys.findIndex(key => aObject[key]);
104
+ const bNum = sortKeys.findIndex(key => bObject[key]);
105
+ if (aNum < bNum) return -1;
106
+ if (aNum > bNum) return 1;
107
+ return 0;
108
+ }).map((field) => {
89
109
  let instructed = false;
90
110
  const structures = field.getStructures();
91
111
  const { key, name, type, isArray, isEmbedded, modelRef } = field.toObject();
@@ -95,7 +115,6 @@ module.exports = class extends Model {
95
115
  const subCrud = crud === 'update' && isArray ? 'create' : crud; // Due to limitation to update embedded array
96
116
  const subShape = isEmbedded ? modelRef.getShape(subCrud, target, path) : null;
97
117
  const transformers = structureKeys.reduce((prev, struct) => {
98
- if (instructed) return prev;
99
118
  const structs = structures[struct];
100
119
  if (struct === 'instructs' && structs.length) instructed = true;
101
120
  return prev.concat(structs);
@@ -172,7 +191,11 @@ module.exports = class extends Model {
172
191
  return Promise.all(shape.map(({ field, from, path, validators, shape: subShape }) => {
173
192
  const value = parent[from]; // It hasn't been shaped yet
174
193
 
175
- return Promise.all(validators.map(v => v({ model, field, path, docPath, rootPath, parentPath, startValue: value, value, resolver, context }))).then(() => {
194
+ return Promise.all(validators.map((v) => {
195
+ return new Promise((resolve, reject) => {
196
+ return Promise.resolve(v({ model, field, path, docPath, rootPath, parentPath, startValue: value, value, resolver, context })).then(resolve).catch(reject);
197
+ });
198
+ })).then(() => {
176
199
  return subShape ? this.validateObject(subShape, value, query, root, true) : Promise.resolve();
177
200
  });
178
201
  }));
@@ -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
 
@@ -173,7 +173,7 @@ module.exports = class Field extends Node {
173
173
  return this.getGQLType();
174
174
  }
175
175
 
176
- initialize() {
176
+ finalize() {
177
177
  this.props = {
178
178
  key: this.getKey(),
179
179
  name: this.getName(),
@@ -181,11 +181,13 @@ module.exports = class Field extends Node {
181
181
  model: this.model,
182
182
  datatype: this.getDataType(),
183
183
  defaultValue: this.getDefaultValue(),
184
+ isEnum: this.isEnum(),
184
185
  isArray: this.isArray(),
185
186
  isScalar: this.isScalar(),
186
187
  isVirtual: this.isVirtual(),
187
188
  isRequired: this.isRequired(),
188
189
  isEmbedded: this.isEmbedded(),
190
+ isBasicType: this.isBasicType(),
189
191
  isIdField: this.isIdField(),
190
192
  isPrimaryKeyId: this.isPrimaryKeyId(),
191
193
  isPersistable: this.isPersistable(),
@@ -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
  };
@@ -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,8 +118,8 @@ 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
- // validateSchema(this.schema);
124
123
  return this;
125
124
  }
126
125
 
@@ -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