@coderich/autograph 0.10.0 → 0.10.3

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 (44) hide show
  1. package/CHANGELOG.md +20 -3
  2. package/index.js +2 -8
  3. package/package.json +5 -7
  4. package/src/.DS_Store +0 -0
  5. package/src/core/EventEmitter.js +2 -4
  6. package/src/core/Resolver.js +32 -57
  7. package/src/core/Schema.js +5 -38
  8. package/src/data/.DS_Store +0 -0
  9. package/src/data/DataLoader.js +71 -32
  10. package/src/data/DataService.js +82 -59
  11. package/src/data/Field.js +59 -126
  12. package/src/data/Model.js +113 -105
  13. package/src/data/Pipeline.js +184 -0
  14. package/src/data/Type.js +38 -74
  15. package/src/driver/MongoDriver.js +27 -22
  16. package/src/graphql/.DS_Store +0 -0
  17. package/src/graphql/ast/Field.js +46 -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 +105 -112
  21. package/src/graphql/extension/api.js +20 -18
  22. package/src/graphql/extension/framework.js +27 -33
  23. package/src/graphql/extension/type.js +2 -2
  24. package/src/query/Query.js +82 -14
  25. package/src/query/QueryBuilder.js +38 -30
  26. package/src/query/QueryBuilderTransaction.js +3 -3
  27. package/src/query/QueryResolver.js +77 -41
  28. package/src/query/QueryService.js +24 -42
  29. package/src/service/app.service.js +70 -13
  30. package/src/service/event.service.js +30 -73
  31. package/src/service/graphql.service.js +0 -9
  32. package/src/service/schema.service.js +5 -3
  33. package/src/core/GraphQL.js +0 -21
  34. package/src/core/Rule.js +0 -107
  35. package/src/core/SchemaDecorator.js +0 -46
  36. package/src/core/Transformer.js +0 -68
  37. package/src/data/Memoizer.js +0 -39
  38. package/src/data/ResultSet.js +0 -205
  39. package/src/data/stream/DataHydrator.js +0 -58
  40. package/src/data/stream/ResultSet.js +0 -34
  41. package/src/data/stream/ResultSetItem.js +0 -158
  42. package/src/data/stream/ResultSetItemProxy.js +0 -161
  43. package/src/graphql/ast/SchemaDecorator.js +0 -141
  44. package/src/graphql/directive/authz.directive.js +0 -84
package/src/data/Type.js CHANGED
@@ -1,6 +1,5 @@
1
1
  const Type = require('../graphql/ast/Type');
2
- const Rule = require('../core/Rule');
3
- const Transformer = require('../core/Transformer');
2
+ const Pipeline = require('./Pipeline');
4
3
 
5
4
  module.exports = class extends Type {
6
5
  constructor(field) {
@@ -8,79 +7,44 @@ module.exports = class extends Type {
8
7
  this.field = field;
9
8
  }
10
9
 
11
- getRules() {
12
- const rules = [];
13
- const scalarType = this.field.getScalarRef();
10
+ getStructures() {
11
+ const type = this.field.getType();
14
12
  const enumType = this.field.getEnumRef();
15
-
16
- if (scalarType) {
17
- Object.entries(scalarType.getDirectiveArgs('field', {})).forEach(([key, value]) => {
18
- if (!Array.isArray(value)) value = [value];
19
- if (key === 'enforce') rules.push(...value.map(r => Rule.getInstances()[r]));
20
- });
21
- }
22
-
23
- if (enumType) {
24
- const values = enumType.getValue();
25
- rules.push(Rule.allow(...values));
26
- }
27
-
28
- return rules;
29
- }
30
-
31
- getTransformers() {
32
- const transformers = [];
33
13
  const scalarType = this.field.getScalarRef();
34
-
35
- if (scalarType) {
36
- Object.entries(scalarType.getDirectiveArgs('field', {})).forEach(([key, value]) => {
37
- if (!Array.isArray(value)) value = [value];
38
- if (key === 'transform') transformers.push(...value.map(t => Transformer.getInstances()[t]));
39
- });
40
- }
41
-
42
- return transformers;
43
- }
44
-
45
- getSerializers() {
46
- const transformers = [];
47
- const scalarType = this.field.getScalarRef();
48
-
49
- if (scalarType) {
50
- Object.entries(scalarType.getDirectiveArgs('field', {})).forEach(([key, value]) => {
51
- if (!Array.isArray(value)) value = [value];
52
- if (key === 'serialize') transformers.push(...value.map(t => Transformer.getInstances()[t]));
53
- });
54
- }
55
-
56
- return transformers;
57
- }
58
-
59
- getDeserializers() {
60
- const transformers = [];
61
- const scalarType = this.field.getScalarRef();
62
-
63
- if (scalarType) {
64
- Object.entries(scalarType.getDirectiveArgs('field', {})).forEach(([key, value]) => {
65
- if (!Array.isArray(value)) value = [value];
66
- if (key === 'deserialize') transformers.push(...value.map(t => Transformer.getInstances()[t]));
67
- });
68
- }
69
-
70
- return transformers;
71
- }
72
-
73
- getResolvers() {
74
- const resolvers = [];
75
- const scalarType = this.field.getScalarRef();
76
-
77
- if (scalarType) {
78
- Object.entries(scalarType.getDirectiveArgs('field', {})).forEach(([key, value]) => {
79
- if (!Array.isArray(value)) value = [value];
80
- if (key === 'resolve') resolvers.push(...value.map(t => Transformer.getInstances()[t]));
81
- });
82
- }
83
-
84
- return resolvers;
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
+ };
27
+
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 }));
34
+ if (!scalarType) return structures;
35
+
36
+ return Object.entries(scalarType.getDirectiveArgs('field', {})).reduce((prev, [key, value]) => {
37
+ if (!Array.isArray(value)) value = [value];
38
+ if (key === 'validate') prev.validators.push(...value.map(t => Pipeline[t]));
39
+ if (key === 'instruct') prev.instructs.push(...value.map(t => Pipeline[t]));
40
+ if (key === 'restruct') prev.restructs.push(...value.map(t => Pipeline[t]));
41
+ if (key === 'destruct') prev.destructs.push(...value.map(t => Pipeline[t]));
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]));
45
+ if (key === 'serialize') prev.serializers.push(...value.map(t => Pipeline[t]));
46
+ if (key === 'deserialize') prev.deserializers.push(...value.map(t => Pipeline[t]));
47
+ return prev;
48
+ }, structures);
85
49
  }
86
50
  };
@@ -1,6 +1,7 @@
1
- const { get, has } = require('lodash');
2
- const { MongoClient, ObjectID } = require('mongodb');
3
- const { proxyDeep, toKeyObj, globToRegex, proxyPromise, isScalarDataType, promiseRetry } = require('../service/app.service');
1
+ const Util = require('util');
2
+ const { get } = require('lodash');
3
+ const { MongoClient, ObjectId } = require('mongodb');
4
+ const { map, ensureArray, proxyDeep, toKeyObj, globToRegex, proxyPromise, isScalarDataType, promiseRetry } = require('../service/app.service');
4
5
 
5
6
  module.exports = class MongoDriver {
6
7
  constructor(config) {
@@ -24,7 +25,7 @@ module.exports = class MongoDriver {
24
25
  }
25
26
 
26
27
  query(collection, method, ...args) {
27
- if (has(args[args.length - 1], 'debug')) console.log(collection, method, JSON.stringify(args, null, 2));
28
+ if (get(args[args.length - 1], 'debug') === true) console.log(collection, method, Util.inspect(args, { depth: null, showHidden: false, colors: true }));
28
29
  if (method === 'aggregate') args.splice(2);
29
30
  return this.raw(collection)[method](...args);
30
31
  }
@@ -48,10 +49,7 @@ module.exports = class MongoDriver {
48
49
  findMany(query) {
49
50
  const { model, options = {}, flags } = query;
50
51
  Object.assign(options, this.config.query || {});
51
-
52
- return this.query(model, 'aggregate', MongoDriver.aggregateQuery(query), options, flags).then((cursor) => {
53
- return cursor.stream();
54
- });
52
+ return this.query(model, 'aggregate', MongoDriver.aggregateQuery(query), options, flags).then(cursor => cursor.stream());
55
53
  }
56
54
 
57
55
  count(query) {
@@ -128,10 +126,10 @@ module.exports = class MongoDriver {
128
126
  }
129
127
 
130
128
  static idValue(value) {
131
- if (value instanceof ObjectID) return value;
129
+ if (value instanceof ObjectId) return value;
132
130
 
133
131
  try {
134
- const id = ObjectID(value);
132
+ const id = ObjectId(value);
135
133
  return id;
136
134
  } catch (e) {
137
135
  return value;
@@ -142,10 +140,13 @@ module.exports = class MongoDriver {
142
140
  return proxyDeep(toKeyObj(where), {
143
141
  get(target, prop, rec) {
144
142
  const value = Reflect.get(target, prop, rec);
145
- if (Array.isArray(value)) return { $in: value };
146
143
  if (typeof value === 'function') return value.bind(target);
147
- if (typeof value === 'string') { return globToRegex(value, { nocase: true, regex: true }); }
148
- return value;
144
+ const $value = map(value, v => (typeof v === 'string' ? globToRegex(v, { nocase: true, regex: true }) : v));
145
+ if (Array.isArray($value)) {
146
+ // console.log(Util.inspect({ value, $value }, { depth: null, showHidden: false, colors: true }));
147
+ return { $in: $value };
148
+ }
149
+ return $value;
149
150
  },
150
151
  }).toObject();
151
152
  }
@@ -153,13 +154,17 @@ module.exports = class MongoDriver {
153
154
  static getAddFields(query) {
154
155
  const { shape, where } = query;
155
156
 
156
- return shape.reduce((prev, { from, type }) => {
157
- const value = where[from];
157
+ return shape.reduce((prev, { from, type, isArray }) => {
158
+ // Basic checks to see if worth converting for regex
159
+ let value = where[from];
158
160
  if (value === undefined) return prev;
159
161
  if (!isScalarDataType(type)) return prev;
160
- const stype = String((type === 'Float' || type === 'Int' ? 'Number' : type)).toLowerCase();
161
- if (String(typeof value) === `${stype}`) return prev;
162
- return Object.assign(prev, { [from]: { $toString: `$${from}` } });
162
+
163
+ // Do regex conversion
164
+ if (isArray) value = value.$in || value; // Where clause does not always use $in
165
+ if (!ensureArray(value).some(el => el instanceof RegExp)) return prev;
166
+ const conversion = isArray ? { $map: { input: `$${from}`, as: 'el', in: { $toString: '$$el' } } } : { $toString: `$${from}` };
167
+ return Object.assign(prev, { [from]: conversion });
163
168
  }, {});
164
169
  }
165
170
 
@@ -182,7 +187,7 @@ module.exports = class MongoDriver {
182
187
  }
183
188
 
184
189
  static aggregateQuery(query, count = false) {
185
- const { where: $match, sort = {}, skip, limit, joins, shape, after, before, first } = query;
190
+ const { where: $match, sort = {}, skip, limit, joins, after, before, first } = query;
186
191
  const $aggregate = [{ $match }];
187
192
 
188
193
  // Used for $regex matching
@@ -215,9 +220,9 @@ module.exports = class MongoDriver {
215
220
  if (before) $aggregate.push({ $match: { $or: Object.entries(before).reduce((prev, [key, value]) => prev.concat({ [key]: { [sort[key] === 1 ? '$lte' : '$gte']: value } }), []) } });
216
221
  if (first) $aggregate.push({ $limit: first });
217
222
 
218
- // Projection
219
- const $project = MongoDriver.getProjectFields(shape);
220
- $aggregate.push({ $project });
223
+ // // Projection
224
+ // const $project = MongoDriver.getProjectFields(shape);
225
+ // $aggregate.push({ $project });
221
226
  }
222
227
 
223
228
  return $aggregate;
Binary file
@@ -1,4 +1,3 @@
1
- const { get } = require('lodash');
2
1
  const Node = require('./Node');
3
2
  const Type = require('./Type');
4
3
  const { uvl } = require('../../service/app.service');
@@ -44,27 +43,6 @@ module.exports = class Field extends Node {
44
43
  return this.getDirectiveArg('field', 'default');
45
44
  }
46
45
 
47
- resolveBoundValue(query, initialValue) {
48
- // If no bound value, return default value
49
- const defaultValue = uvl(initialValue, this.getDefaultValue());
50
- if (!this.hasBoundValue()) return defaultValue;
51
-
52
- // Grab @value definition, if passive then check for initialValue
53
- const { scope, path, passive = false } = this.getDirectiveArgs('value');
54
- if (passive && initialValue !== undefined) return initialValue;
55
-
56
- // Resolve @value
57
- switch (scope) {
58
- case 'context': {
59
- const { resolver } = query.toObject();
60
- const context = resolver.getContext();
61
- const value = get(context, path);
62
- return uvl((typeof value === 'function') ? value() : value, defaultValue);
63
- }
64
- default: return this[path];
65
- }
66
- }
67
-
68
46
  // Model Methods
69
47
  getSchema() {
70
48
  return this.model.getSchema();
@@ -75,7 +53,8 @@ module.exports = class Field extends Node {
75
53
  }
76
54
 
77
55
  getModelRef() {
78
- return this.schema.getModel(this.getType());
56
+ const refType = this.getDirectiveArg('field', 'id', this.getType());
57
+ return this.schema.getModel(refType);
79
58
  }
80
59
 
81
60
  getFieldRef() {
@@ -90,6 +69,10 @@ module.exports = class Field extends Node {
90
69
  return model ? model.getField(this.getVirtualRef()) : null;
91
70
  }
92
71
 
72
+ getIdModel() {
73
+ return this.getModelRef() || this.getModel();
74
+ }
75
+
93
76
  resolveField() {
94
77
  const field = this.getVirtualField() || this;
95
78
  return field === this ? this : field.resolveField();
@@ -101,7 +84,7 @@ module.exports = class Field extends Node {
101
84
  }
102
85
 
103
86
  isDefaulted() {
104
- return Boolean(this.hasBoundValue() || this.getDefaultValue() != null);
87
+ return Boolean(this.getDefaultValue() != null);
105
88
  }
106
89
 
107
90
  isRequired() {
@@ -117,6 +100,16 @@ module.exports = class Field extends Node {
117
100
  return Boolean(modelRef && !this.isEmbedded());
118
101
  }
119
102
 
103
+ isIdField() {
104
+ return this.isPrimaryKeyId() || this.isFKReference();
105
+ }
106
+
107
+ isPrimaryKeyId() {
108
+ const key = this.getKey();
109
+ const idKey = this.getModel().idKey();
110
+ return key === idKey;
111
+ }
112
+
120
113
  getJoinInfo() {
121
114
  const modelRef = this.getModelRef();
122
115
  if (!modelRef || this.isEmbedded()) return null;
@@ -179,4 +172,33 @@ module.exports = class Field extends Node {
179
172
  if (this.isFKReference()) return this.isArray() ? '[ID]' : 'ID';
180
173
  return this.getGQLType();
181
174
  }
175
+
176
+ initialize() {
177
+ this.props = {
178
+ key: this.getKey(),
179
+ name: this.getName(),
180
+ type: this.getType(),
181
+ model: this.model,
182
+ datatype: this.getDataType(),
183
+ defaultValue: this.getDefaultValue(),
184
+ isArray: this.isArray(),
185
+ isScalar: this.isScalar(),
186
+ isVirtual: this.isVirtual(),
187
+ isRequired: this.isRequired(),
188
+ isEmbedded: this.isEmbedded(),
189
+ isIdField: this.isIdField(),
190
+ isPrimaryKeyId: this.isPrimaryKeyId(),
191
+ isPersistable: this.isPersistable(),
192
+ idModel: this.getIdModel(),
193
+ modelRef: this.getModelRef(),
194
+ virtualRef: this.getVirtualRef(),
195
+ virtualField: this.getVirtualField(),
196
+ };
197
+
198
+ return this;
199
+ }
200
+
201
+ toObject() {
202
+ return this.props;
203
+ }
182
204
  };
@@ -92,10 +92,6 @@ module.exports = class Model extends Node {
92
92
  return this.getFields().filter(field => field.isDefaulted());
93
93
  }
94
94
 
95
- getBoundValueFields() {
96
- return this.getFields().filter(field => field.hasBoundValue());
97
- }
98
-
99
95
  getDataRefFields() {
100
96
  return this.getFields().filter(field => Boolean(field.getDataRef()));
101
97
  }
@@ -104,10 +100,6 @@ module.exports = class Model extends Node {
104
100
  return this.getFields().filter(field => Boolean(field.getModelRef()));
105
101
  }
106
102
 
107
- // getDataRefFields() {
108
- // return this.fields.filter(field => Boolean(field.getDataRef() && !field.isEmbedded()));
109
- // }
110
-
111
103
  getEmbeddedFields() {
112
104
  return this.getFields().filter(field => field.isEmbedded());
113
105
  }
@@ -128,14 +120,6 @@ module.exports = class Model extends Node {
128
120
  return this.getFields().filter(field => field.isPersistable());
129
121
  }
130
122
 
131
- getSerializeFields() {
132
- return this.getFields().filter(field => field.getSerializers().length);
133
- }
134
-
135
- getDeserializeFields() {
136
- return this.getFields().filter(field => field.getDeserializers().length);
137
- }
138
-
139
123
  // Misc
140
124
  getIndexes() {
141
125
  return this.getDirectives('index').map((d) => {
@@ -153,4 +137,9 @@ module.exports = class Model extends Node {
153
137
  }, {});
154
138
  });
155
139
  }
140
+
141
+ initialize() {
142
+ this.fields.forEach(field => field.initialize());
143
+ return this;
144
+ }
156
145
  };
@@ -2,7 +2,6 @@ const { get } = require('lodash');
2
2
  const { Kind } = require('graphql');
3
3
  const { nvl, uvl } = require('../../service/app.service');
4
4
  const { mergeAST } = require('../../service/graphql.service');
5
- // const Memoizer = require('../../data/Memoizer');
6
5
 
7
6
  const operations = ['Query', 'Mutation', 'Subscription'];
8
7
  const modelKinds = [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION, Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION];
@@ -18,7 +17,6 @@ module.exports = class Node {
18
17
  this.toString = () => this.getName();
19
18
  this.nodeType = nodeType;
20
19
  this.name = get(this.ast, 'name.value');
21
- // return new Memoizer(this, Object.getOwnPropertyNames(Node.prototype).filter(m => ['getContext'].indexOf(m) === -1));
22
20
  }
23
21
 
24
22
  // Basic AST Methods
@@ -130,14 +128,6 @@ module.exports = class Node {
130
128
  return this.getDirectiveArg('model', 'meta');
131
129
  }
132
130
 
133
- getSerialize() {
134
- return this.getDirectiveArg('field', 'serialize', this.getDirectiveArg('model', 'serialize'));
135
- }
136
-
137
- getDeserialize() {
138
- return this.getDirectiveArg('field', 'deserialize', this.getDirectiveArg('model', 'deserialize'));
139
- }
140
-
141
131
  // Booleans
142
132
  isModel() {
143
133
  return Boolean(modelKinds.some(k => this.getKind() === k) && operations.every(o => this.getName() !== o));
@@ -166,13 +156,6 @@ module.exports = class Node {
166
156
  return Boolean(this.getDirectiveArg('link', 'by'));
167
157
  }
168
158
 
169
- /**
170
- * Does the model/field have a bound @value directive
171
- */
172
- hasBoundValue() {
173
- return Boolean(this.getDirective('value'));
174
- }
175
-
176
159
  /**
177
160
  * Is a model annotated with @model
178
161
  */
@@ -208,14 +191,6 @@ module.exports = class Node {
208
191
  }
209
192
  }
210
193
 
211
- /**
212
- * Can the field be changed after it's set
213
- */
214
- isImmutable() {
215
- const enforce = this.getDirectiveArg('field', 'enforce', '');
216
- return Boolean(JSON.stringify(enforce).indexOf('immutable') > -1);
217
- }
218
-
219
194
  /**
220
195
  * Define it's behavior at the Data Access Layer
221
196
  *