@coderich/autograph 0.11.0 → 0.12.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,41 @@
1
+ # CHANGELOG
2
+
3
+ ## v0.10.x
4
+ - Replaced ResultSet -> POJOs
5
+ - Removed all $field methods (auto populated)
6
+ - Removed .toObject()
7
+ - $model $save remove $delete $lookup $cursor $pageInfo
8
+ - Removed embedded API completely
9
+ - Removed Directives
10
+ - embedApi -> no replacement
11
+ - enforce -> use pipeline methods
12
+ - resolve -> use graphql resolvers
13
+ - @value -> use @field.instruct directive
14
+ - Removed Model.tform() -> use Model.shapeObject(shape, data)
15
+ - Removed Transformer + Rule -> use Pipeline
16
+ - Removed many pre-defined rules + transformers
17
+ - Moved "validator" to dev dependency -> isEmail
18
+ - Added QueryBuilder.resolve() terminal command
19
+ - Exported SchemaDecorator -> Schema
20
+ - Removed embedded schema SystemEvents (internal emitter also removed)
21
+ - Removed spread of arguments in QueryBuilder terminal commands (must pass in array)
22
+ - Mutate "merged" instead of "input"
23
+ - Validate "payload"
24
+
25
+ ## v0.9.x
26
+ - Subscriptions API
27
+ - postMutation no longer mutates "doc" and adds "result"
28
+ - Added onDelete defer option
29
+
30
+ ## v0.8.x
31
+ - Engine 14+
32
+
33
+ ## v0.7.x
34
+ - Complete overhaul of Query to Mongo Driver (pagination, sorting, counts, etc)
35
+ - Removed countModel Queries from the API (now available as `count` property on `Connetion` types)
36
+ - Dropped Neo4J (temporarily)
37
+
38
+ ## v0.6.x
39
+ - Mongo driver no longer checks for `version` directive
40
+ - Models no longer share a Connection type; removing the need to use `... on Model` for GraphQL queries
41
+ - Added `@field(connection: Boolean)` parameter to specifically indicate fields that should return a Connection type
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coderich/autograph",
3
3
  "author": "Richard Livolsi (coderich)",
4
- "version": "0.11.0",
4
+ "version": "0.12.0",
5
5
  "description": "AutoGraph",
6
6
  "keywords": [
7
7
  "graphql",
@@ -34,22 +34,19 @@
34
34
  "dataloader": "^2.0.0",
35
35
  "deepmerge": "^4.2.2",
36
36
  "fill-range": "^7.0.1",
37
+ "flat": "^5.0.2",
37
38
  "glob": "^7.1.6",
38
39
  "graphql-fields": "^2.0.3",
39
40
  "lodash": "^4.17.21",
40
- "mongodb": "^4.8.0",
41
+ "mongodb": "4.8.1",
41
42
  "object-hash": "^2.0.1",
42
43
  "picomatch": "^2.1.1"
43
44
  },
44
45
  "devDependencies": {
45
- "@coderich/ratchet": "^1.5.7",
46
+ "@coderich/ratchet": "^1.5.8",
46
47
  "@graphql-tools/schema": "^9.0.1",
47
48
  "graphql": "^15.5.0",
48
49
  "mongodb-memory-server": "^8.7.2",
49
- "neo4j-driver": "^4.0.0",
50
- "neodb": "^3.0.0",
51
- "redis": "^2.8.0",
52
- "redis-mock": "^0.47.0",
53
50
  "validator": "^13.7.0"
54
51
  },
55
52
  "peerDependencies": {
package/src/.DS_Store ADDED
Binary file
Binary file
Binary file
package/src/data/Model.js CHANGED
@@ -135,7 +135,7 @@ module.exports = class extends Model {
135
135
  return shape;
136
136
  }
137
137
 
138
- shapeObject(shape, obj, query, root) {
138
+ shapeObject(shape, obj, query, root, base) {
139
139
  const { serdes, model } = shape;
140
140
  const { context, resolver, doc = {}, flags = {} } = query.toObject();
141
141
  const { pipeline } = flags;
@@ -143,8 +143,11 @@ module.exports = class extends Model {
143
143
  if (!pipeline) return obj;
144
144
  // const filters = pipeline === true ? [] : Object.entries(pipeline).map(([k, v]) => (v === false ? k : null)).filter(Boolean);
145
145
 
146
+ // base is the base model
147
+ base = base || model;
148
+
146
149
  return map(obj, (parent) => {
147
- // "root" is the base of the object
150
+ // root is the base data object
148
151
  root = root || parent;
149
152
 
150
153
  // Lookup helper functions
@@ -158,7 +161,7 @@ module.exports = class extends Model {
158
161
 
159
162
  // Transform value
160
163
  const transformedValue = transformers.reduce((value, t) => {
161
- const v = t({ model, field, path, docPath, rootPath, parentPath, startValue, value, resolver, context });
164
+ const v = t({ base, model, field, path, docPath, rootPath, parentPath, startValue, value, resolver, context });
162
165
  return v === undefined ? value : v;
163
166
  }, startValue);
164
167
 
@@ -167,22 +170,25 @@ module.exports = class extends Model {
167
170
  if (!instructed && subShape && typeof transformedValue !== 'object') return prev;
168
171
 
169
172
  // Rename key & assign value
170
- prev[to] = (!subShape || transformedValue == null) ? transformedValue : this.shapeObject(subShape, transformedValue, query, root);
173
+ prev[to] = (!subShape || transformedValue == null) ? transformedValue : this.shapeObject(subShape, transformedValue, query, root, base);
171
174
 
172
175
  return prev;
173
176
  }, {});
174
177
  });
175
178
  }
176
179
 
177
- validateObject(shape, obj, query, root, silent = false) {
180
+ validateObject(shape, obj, query, root, base, silent = false) {
178
181
  const { model } = shape;
179
182
  const { context, resolver, doc = {}, flags = {} } = query.toObject();
180
183
  const { validate = true } = flags;
181
184
 
182
185
  if (!validate) return Promise.resolve();
183
186
 
187
+ // base is the base model
188
+ base = base || model;
189
+
184
190
  return mapPromise(obj, (parent) => {
185
- // "root" is the base of the object
191
+ // root is the base data object
186
192
  root = root || parent;
187
193
 
188
194
  // Lookup helper functions
@@ -195,10 +201,10 @@ module.exports = class extends Model {
195
201
 
196
202
  return Promise.all(validators.map((v) => {
197
203
  return new Promise((resolve, reject) => {
198
- return Promise.resolve(v({ model, field, path, docPath, rootPath, parentPath, startValue: value, value, resolver, context })).then(resolve).catch(reject);
204
+ return Promise.resolve(v({ base, model, field, path, docPath, rootPath, parentPath, startValue: value, value, resolver, context })).then(resolve).catch(reject);
199
205
  });
200
206
  })).then(() => {
201
- return subShape ? this.validateObject(subShape, value, query, root, true) : Promise.resolve();
207
+ return subShape ? this.validateObject(subShape, value, query, root, base, true) : Promise.resolve();
202
208
  });
203
209
  }));
204
210
  }).then(() => {
Binary file
@@ -1,4 +1,5 @@
1
1
  const Util = require('util');
2
+ const Flat = require('flat');
2
3
  const { get } = require('lodash');
3
4
  const { MongoClient, ObjectId } = require('mongodb');
4
5
  const { map, ensureArray, proxyDeep, toKeyObj, globToRegex, proxyPromise, isScalarDataType, promiseRetry } = require('../service/app.service');
@@ -64,12 +65,11 @@ module.exports = class MongoDriver {
64
65
  }
65
66
 
66
67
  createOne({ model, input, options, flags }) {
67
- // console.log(JSON.stringify(input, null, 2));
68
68
  return this.query(model, 'insertOne', input, options, flags).then(result => Object.assign(input, { id: result.insertedId }));
69
69
  }
70
70
 
71
71
  updateOne({ model, where, $doc, options, flags }) {
72
- const $update = { $set: $doc };
72
+ const $update = { $set: Flat.flatten($doc, { safe: true }) };
73
73
  return this.query(model, 'updateOne', where, $update, options, flags).then(() => $doc);
74
74
  }
75
75
 
Binary file
Binary file
@@ -117,7 +117,7 @@ module.exports = class Node {
117
117
  }
118
118
 
119
119
  getVirtualRef() {
120
- return this.getDirectiveArg('link', 'by');
120
+ return this.getDirectiveArg('join', 'by');
121
121
  }
122
122
 
123
123
  getAuthz() {
@@ -153,7 +153,7 @@ module.exports = class Node {
153
153
  * Is the field virtual; does it's value come from another model
154
154
  */
155
155
  isVirtual() {
156
- return Boolean(this.getDirectiveArg('link', 'by'));
156
+ return Boolean(this.getDirectiveArg('join', 'by'));
157
157
  }
158
158
 
159
159
  /**
@@ -128,6 +128,6 @@ module.exports = class Schema extends TypeDefApi {
128
128
  }
129
129
 
130
130
  toString() {
131
- return print(this.typeDefs);
131
+ return print(this.schema.typeDefs);
132
132
  }
133
133
  };
Binary file
@@ -107,8 +107,6 @@ module.exports = (schema) => {
107
107
  `type Query {
108
108
  node(id: ID!): Node
109
109
  ${entityModels.map(model => makeReadAPI(model.getName(), model))}
110
- ${entityModels.map(model => makeReadAPI(`${model.getName()}Create`, model))}
111
- ${entityModels.map(model => makeReadAPI(`${model.getName()}Update`, model))}
112
110
  }`,
113
111
 
114
112
  `type Mutation {
@@ -8,7 +8,6 @@ module.exports = (schema) => {
8
8
  scalar AutoGraphMixed
9
9
  scalar AutoGraphDriver
10
10
  scalar AutoGraphDateTime @field(transform: toDate)
11
- enum AutoGraphSchemaEnum { ${schema.getModels().map(m => m.getName()).join(' ')} }
12
11
  enum AutoGraphPipelineEnum { ${Object.keys(Pipeline).join(' ')} }
13
12
  enum AutoGraphAuthzEnum { private protected public }
14
13
  enum AutoGraphOnDeleteEnum { cascade nullify restrict defer }
@@ -56,10 +55,10 @@ module.exports = (schema) => {
56
55
  deserialize: [AutoGraphPipelineEnum!]
57
56
  ) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION | SCALAR
58
57
 
59
- directive @link(
60
- to: AutoGraphMixed # The MODEL to link to (default's to modelRef)
58
+ directive @join(
59
+ to: AutoGraphMixed # The MODEL to join to (default's to modelRef)
61
60
  by: AutoGraphMixed! # The FIELD to match yourself by
62
- use: AutoGraphMixed # The VALUE to use (default's to @link'd value); useful for many-to-many relationships
61
+ use: AutoGraphMixed # The VALUE to use (default's to @join'd value); useful for many-to-many relationships
63
62
  ) on FIELD_DEFINITION
64
63
 
65
64
  directive @index(
Binary file
@@ -10,7 +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
+ // this.terminated = false; // Prevent accidental re-use of the QueryBuilder
14
14
  this.query = new Query({ model, resolver });
15
15
 
16
16
  // Chainable commands
@@ -58,10 +58,10 @@ module.exports = class QueryBuilder {
58
58
  }
59
59
 
60
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'));
61
+ // // Do not allow re-use
62
+ // if (this.terminated) return Promise.reject(new Error('This query has already been executed'));
63
+ // this.terminated = true;
63
64
 
64
- this.terminated = true;
65
65
  let method, crud, input, flags = {};
66
66
  const { id, where } = this.query.toObject();
67
67
 
@@ -3,7 +3,7 @@ const Boom = require('../core/Boom');
3
3
  const QueryService = require('./QueryService');
4
4
  const DataService = require('../data/DataService');
5
5
  const { createSystemEvent } = require('../service/event.service');
6
- const { objectIntersectionEqual, mergeDeep, getGQLReturnType } = require('../service/app.service');
6
+ const { mergeDeep, getGQLReturnType } = require('../service/app.service');
7
7
 
8
8
  module.exports = class QueryResolver {
9
9
  constructor(query) {
@@ -81,8 +81,6 @@ module.exports = class QueryResolver {
81
81
  const docShape = model.getShape('update', 'doc');
82
82
 
83
83
  return this.resolver.match(model).match(match).one({ required: true }).then((doc) => {
84
- if (objectIntersectionEqual(doc, input)) return doc; // If no changes do not perform query
85
-
86
84
  const merged = mergeDeep(doc, input);
87
85
 
88
86
  return createSystemEvent('Mutation', { query: query.doc(doc).merged(merged) }, async () => {
Binary file
@@ -130,9 +130,6 @@ exports.castCmp = (type, value) => {
130
130
  }
131
131
  };
132
132
 
133
- /**
134
- * Returns true if b is a subset of a
135
- */
136
133
  exports.objectContaining = (a, b) => {
137
134
  if (a === b) return true;
138
135
 
@@ -140,7 +137,7 @@ exports.objectContaining = (a, b) => {
140
137
  return exports.keyPathLeafs(b).every((leaf) => {
141
138
  const $a = _.get(a, leaf, { a: 'a' });
142
139
  const $b = _.get(b, leaf, { b: 'b' });
143
- if (Array.isArray($b)) return $b.every(bb => exports.ensureArray($a).some(aa => exports.objectContaining(aa, bb)));
140
+ if (Array.isArray($b)) return $b.some(bb => exports.ensureArray($a).some(aa => exports.objectContaining(aa, bb)));
144
141
  if (exports.isScalarValue($a) && exports.isScalarValue($b)) return PicoMatch.isMatch(`${$a}`, `${$b}`, { nocase: true });
145
142
  return exports.hashObject($a) === exports.hashObject($b);
146
143
  });
@@ -149,23 +146,6 @@ exports.objectContaining = (a, b) => {
149
146
  return exports.hashObject(a) === exports.hashObject(b);
150
147
  };
151
148
 
152
- /**
153
- * Returns true if (b intersection a) are equal
154
- */
155
- exports.objectIntersectionEqual = (a, b) => {
156
- if (a === b) return true;
157
-
158
- if (exports.isPlainObject(b)) {
159
- return exports.keyPathLeafs(b).every((leaf) => {
160
- const $a = _.get(a, leaf, { a: 'a' });
161
- const $b = _.get(b, leaf, { b: 'b' });
162
- return exports.hashObject($a) === exports.hashObject($b);
163
- });
164
- }
165
-
166
- return exports.hashObject(a) === exports.hashObject(b);
167
- };
168
-
169
149
  /**
170
150
  * Transform an object with dot.notation keys into an expanded object.
171
151
  * eg. { 'user.name': 'richard' } => { user: { name: 'richard' } }
@@ -29,7 +29,7 @@ const makeMiddleware = () => {
29
29
  query.match($$where);
30
30
  }
31
31
 
32
- if (sort) {
32
+ if (sort && Object.keys(sort).length) {
33
33
  query.$sort(QueryService.resolveSortBy(query));
34
34
  }
35
35