@coderich/autograph 0.11.1 → 0.13.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.
Files changed (56) hide show
  1. package/index.js +4 -6
  2. package/package.json +30 -44
  3. package/src/data/DataLoader.js +77 -70
  4. package/src/data/Emitter.js +89 -0
  5. package/src/data/Loader.js +33 -0
  6. package/src/data/Pipeline.js +88 -96
  7. package/src/data/Resolver.js +304 -0
  8. package/src/data/Transaction.js +49 -0
  9. package/src/query/Query.js +159 -334
  10. package/src/query/QueryBuilder.js +228 -114
  11. package/src/query/QueryResolver.js +110 -216
  12. package/src/query/QueryResolverTransaction.js +16 -0
  13. package/src/schema/Schema.js +593 -0
  14. package/src/service/AppService.js +38 -0
  15. package/src/service/ErrorService.js +7 -0
  16. package/CHANGELOG.md +0 -41
  17. package/LICENSE +0 -21
  18. package/README.md +0 -76
  19. package/src/.DS_Store +0 -0
  20. package/src/core/.DS_Store +0 -0
  21. package/src/core/Boom.js +0 -9
  22. package/src/core/EventEmitter.js +0 -95
  23. package/src/core/Resolver.js +0 -124
  24. package/src/core/Schema.js +0 -55
  25. package/src/core/ServerResolver.js +0 -15
  26. package/src/data/.DS_Store +0 -0
  27. package/src/data/DataService.js +0 -120
  28. package/src/data/DataTransaction.js +0 -96
  29. package/src/data/Field.js +0 -83
  30. package/src/data/Model.js +0 -223
  31. package/src/data/TreeMap.js +0 -78
  32. package/src/data/Type.js +0 -50
  33. package/src/driver/.DS_Store +0 -0
  34. package/src/driver/MongoDriver.js +0 -227
  35. package/src/driver/index.js +0 -11
  36. package/src/graphql/.DS_Store +0 -0
  37. package/src/graphql/ast/.DS_Store +0 -0
  38. package/src/graphql/ast/Field.js +0 -206
  39. package/src/graphql/ast/Model.js +0 -145
  40. package/src/graphql/ast/Node.js +0 -291
  41. package/src/graphql/ast/Schema.js +0 -133
  42. package/src/graphql/ast/Type.js +0 -26
  43. package/src/graphql/ast/TypeDefApi.js +0 -93
  44. package/src/graphql/extension/.DS_Store +0 -0
  45. package/src/graphql/extension/api.js +0 -193
  46. package/src/graphql/extension/framework.js +0 -71
  47. package/src/graphql/extension/type.js +0 -34
  48. package/src/query/.DS_Store +0 -0
  49. package/src/query/QueryBuilderTransaction.js +0 -26
  50. package/src/query/QueryService.js +0 -111
  51. package/src/service/.DS_Store +0 -0
  52. package/src/service/app.service.js +0 -319
  53. package/src/service/decorator.service.js +0 -114
  54. package/src/service/event.service.js +0 -66
  55. package/src/service/graphql.service.js +0 -92
  56. package/src/service/schema.service.js +0 -95
@@ -1,232 +1,126 @@
1
- const { get, set, isEmpty } = require('lodash');
2
- const Boom = require('../core/Boom');
3
- const QueryService = require('./QueryService');
4
- const DataService = require('../data/DataService');
5
- const { createSystemEvent } = require('../service/event.service');
6
- const { mergeDeep, hashObject, getGQLReturnType } = require('../service/app.service');
7
-
8
- module.exports = class QueryResolver {
9
- constructor(query) {
10
- this.query = query;
11
- this.resolver = query.toObject().resolver;
12
- this.context = this.resolver.getContext();
13
- }
14
-
15
- autoResolve(query) {
16
- const { args } = query.toObject();
17
- const [,,, info] = args;
18
-
19
- switch (getGQLReturnType(`${info.returnType}`)) {
20
- case 'array': {
21
- return new QueryResolver(this.query.clone().method('findMany')).resolve();
1
+ const get = require('lodash.get');
2
+ const Util = require('@coderich/util');
3
+ const QueryBuilder = require('./QueryBuilder');
4
+ const { mergeDeep } = require('../service/AppService');
5
+
6
+ module.exports = class QueryResolver extends QueryBuilder {
7
+ #model;
8
+ #schema;
9
+ #config;
10
+ #context;
11
+ #resolver;
12
+
13
+ constructor(config) {
14
+ const { schema, context, resolver, query } = config;
15
+ super(config);
16
+ this.#config = config;
17
+ this.#schema = schema;
18
+ this.#context = context;
19
+ this.#resolver = resolver;
20
+ this.#model = schema.models[query.model];
21
+ }
22
+
23
+ terminate() {
24
+ const query = super.terminate();
25
+ const { op, input } = query.toObject();
26
+
27
+ // Resolve
28
+ switch (op) {
29
+ case 'findOne': case 'findMany': case 'count': case 'createOne': {
30
+ return this.#resolver.resolve(query);
22
31
  }
23
- case 'number': {
24
- return new QueryResolver(this.query.clone().method('count')).resolve();
32
+ case 'createMany': {
33
+ return this.#resolver.transaction(false).run(Promise.all(input.map(el => this.#resolver.match(this.#model.name).save(el))));
25
34
  }
26
- case 'connection': {
27
- return Promise.resolve({
28
- count: () => new QueryResolver(this.query.clone().method('count')).resolve(),
29
- edges: () => new QueryResolver(this.query.clone().method('findMany')).resolve(),
30
- pageInfo: () => new QueryResolver(this.query.clone().method('findMany')).resolve(),
35
+ case 'updateOne': {
36
+ return this.#get(query).then((doc) => {
37
+ const merged = mergeDeep({}, Util.unflatten(doc), Util.unflatten(input));
38
+ return this.#resolver.resolve(query.clone({ doc, input: merged }));
31
39
  });
32
40
  }
33
- case 'scalar': default: {
34
- return new QueryResolver(this.query.clone().method('findOne')).resolve();
41
+ case 'updateMany': {
42
+ return this.#find(query).then((docs) => {
43
+ return this.#resolver.transaction(false).run(Promise.all(docs.map(doc => this.#resolver.match(this.#model.name).id(doc.id).save(input))));
44
+ });
35
45
  }
36
- }
37
- }
38
-
39
- findOne(query) {
40
- return createSystemEvent('Query', { query }, async () => {
41
- return this.resolver.resolve(query);
42
- });
43
- }
44
-
45
- findMany(query) {
46
- return createSystemEvent('Query', { query }, async () => {
47
- return this.resolver.resolve(query);
48
- });
49
- }
50
-
51
- count(query) {
52
- return createSystemEvent('Query', { query }, async () => {
53
- return this.resolver.resolve(query);
54
- });
55
- }
56
-
57
- createOne(query) {
58
- const { model, input } = query.toObject();
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)));
68
- });
69
- }
70
-
71
- createMany(query) {
72
- const { model, input, transaction } = query.toObject();
73
- const txn = this.resolver.transaction(transaction);
74
- input.forEach(arg => txn.match(model).save(arg));
75
- return txn.run();
76
- }
77
-
78
- async updateOne(query) {
79
- const { model, match, input, flags } = query.toObject();
80
- const inputShape = model.getShape('update', 'input');
81
- const docShape = model.getShape('update', 'doc');
82
-
83
- return this.resolver.match(model).match(match).one({ required: true }).then((doc) => {
84
- const merged = mergeDeep(doc, input);
85
- const docHash = hashObject(doc);
86
- const skipUnchanged = get(flags, 'skipUnchanged');
87
-
88
- // Prevent udpates when no data has changed
89
- if (skipUnchanged && docHash === hashObject(merged)) return doc;
90
-
91
- return createSystemEvent('Mutation', { query: query.doc(doc).merged(merged) }, async () => {
92
- // Prevent udpates when no data has changed (because merged can be mutated)
93
- if (skipUnchanged && docHash === hashObject(merged)) return doc;
94
-
95
- // Process
96
- const payload = model.shapeObject(inputShape, merged, query);
97
- await model.validateObject(inputShape, payload, query.payload(payload));
98
- const $doc = model.shapeObject(docShape, payload, query, undefined, undefined, true);
99
- return this.resolver.resolve(query.$doc($doc));
100
- });
101
- });
102
- }
103
-
104
- updateMany(query) {
105
- const { model, input, match, transaction, flags } = query.toObject();
106
-
107
- return this.resolver.match(model).match(match).many(flags).then((docs) => {
108
- const txn = this.resolver.transaction(transaction);
109
- docs.forEach(doc => txn.match(model).id(doc.id).save(input, flags));
110
- return txn.run();
111
- });
112
- }
113
-
114
- deleteOne(query) {
115
- const { model, id } = query.toObject();
116
-
117
- return this.resolver.match(model).id(id).one({ required: true }).then(async (doc) => {
118
- return createSystemEvent('Mutation', { query: query.doc(doc) }, () => {
119
- return QueryService.resolveReferentialIntegrity(query).then(() => {
120
- return this.resolver.resolve(query).then(() => doc);
46
+ case 'pushOne': {
47
+ return this.#get(query).then(async (doc) => {
48
+ const [key] = Object.keys(input);
49
+ const values = get(await query.pipeline('input', input), key);
50
+ const $input = { [key]: (get(doc, key) || []).concat(...values) };
51
+ return this.#resolver.match(this.#model.name).id(doc.id).save($input);
121
52
  });
122
- });
123
- });
124
- }
125
-
126
- deleteMany(query) {
127
- const { model, match, transaction, flags } = query.toObject();
128
-
129
- return this.resolver.match(model).where(match).flags(flags).many().then((docs) => {
130
- const txn = this.resolver.transaction(transaction);
131
- docs.forEach(doc => txn.match(model).id(doc.id).delete());
132
- return txn.run();
133
- });
134
- }
135
-
136
- pushOne(query) {
137
- const { args } = query.toObject();
138
- const [key, ...values] = args;
139
- return this.splice(query.args([key, null, values]));
140
- }
141
-
142
- pushMany(query) {
143
- const { args } = query.toObject();
144
- const { model, match, transaction, flags } = query.toObject();
145
- const [key, ...values] = args;
146
-
147
- return this.resolver.match(model).match(match).flags(flags).many().then((docs) => {
148
- const txn = this.resolver.transaction(transaction);
149
- docs.forEach(doc => txn.match(model).id(doc.id).push(key, ...values));
150
- return txn.run();
151
- });
152
- }
153
-
154
- pullOne(query) {
155
- const { args } = query.toObject();
156
- const [key, ...values] = args;
157
- return this.splice(query.args([key, values]));
158
- }
159
-
160
- pullMany(query) {
161
- const { model, match, transaction, args, flags } = query.toObject();
162
- const [key, ...values] = args;
163
-
164
- return this.resolver.match(model).match(match).flags(flags).many().then((docs) => {
165
- const txn = this.resolver.transaction(transaction);
166
- docs.forEach(doc => txn.match(model).id(doc.id).pull(key, ...values));
167
- return txn.run();
168
- });
169
- }
170
-
171
- spliceOne(query) {
172
- const { args } = query.toObject();
173
- const [key, ...values] = args;
174
- return this.splice(query.args([key, ...values]));
175
- }
176
-
177
- spliceMany(query) {
178
- const { model, match, transaction, args, flags } = query.toObject();
179
- const [key, ...values] = args;
180
-
181
- return this.resolver.match(model).match(match).flags(flags).many().then((docs) => {
182
- const txn = this.resolver.transaction(transaction);
183
- docs.forEach(doc => txn.match(model).id(doc.id).splice(key, ...values));
184
- return txn.run();
185
- });
53
+ }
54
+ case 'pushMany': {
55
+ const [[key, values]] = Object.entries(input[0]);
56
+ return this.#find(query).then((docs) => {
57
+ return this.#resolver.transaction(false).run(Promise.all(docs.map(doc => this.#resolver.match(this.#model.name).id(doc.id).push(key, values))));
58
+ });
59
+ }
60
+ case 'pullOne': {
61
+ return this.#get(query).then(async (doc) => {
62
+ const [key] = Object.keys(input);
63
+ const values = get(await query.pipeline('input', input), key);
64
+ const $input = { [key]: (get(doc, key) || []).filter(el => values.every(v => `${v}` !== `${el}`)) };
65
+ return this.#resolver.match(this.#model.name).id(doc.id).save($input);
66
+ });
67
+ }
68
+ case 'pullMany': {
69
+ const [[key, values]] = Object.entries(input[0]);
70
+ return this.#find(query).then((docs) => {
71
+ return this.#resolver.transaction(false).run(Promise.all(docs.map(doc => this.#resolver.match(this.#model.name).id(doc.id).pull(key, values))));
72
+ });
73
+ }
74
+ case 'spliceOne': {
75
+ return this.#get(query).then(async (doc) => {
76
+ const [key] = Object.keys(input);
77
+ const [find, replace] = get(await query.pipeline('input', input), key);
78
+ const $input = { [key]: (get(doc, key) || []).map(el => (`${el}` === `${find}` ? replace : el)) };
79
+ return this.#resolver.match(this.#model.name).id(doc.id).save($input);
80
+ });
81
+ }
82
+ case 'deleteOne': {
83
+ return this.#get(query).then((doc) => {
84
+ return this.#resolveReferentialIntegrity(query).then(() => {
85
+ return this.#resolver.resolve(query).then(() => doc);
86
+ });
87
+ });
88
+ }
89
+ case 'deleteMany': {
90
+ return this.#find(query).then((docs) => {
91
+ return this.#resolver.transaction(false).run(Promise.all(docs.map(doc => this.#resolver.match(this.#model.name).id(doc.id).delete())));
92
+ });
93
+ }
94
+ default: {
95
+ throw new Error(`Unknown operation "${op}"`);
96
+ }
97
+ }
186
98
  }
187
99
 
188
- splice(query) {
189
- const { model, match, args } = query.toObject();
190
- const docShape = model.getShape('update', 'doc');
191
- const inputShape = model.getShape('update', 'input');
192
- const spliceShape = model.getShape('update', 'splice');
193
- const [key, from, to] = args;
194
-
195
- // Can only splice arrays
196
- const field = model.getField(key);
197
- const isArray = field.isArray();
198
- const path = `${model}.${field}`;
199
- if (!isArray) throw Boom.badRequest(`Cannot splice field '${path}'`, { path });
200
-
201
- return this.resolver.match(model).match(match).one({ required: true }).then(async (doc) => {
202
- const array = get(doc, key) || [];
203
- const $to = model.shapeObject(spliceShape, { [key]: to }, query)[key] || to;
204
- const $from = model.shapeObject(spliceShape, { [key]: from }, query)[key] || from;
205
- set(doc, key, DataService.spliceEmbeddedArray(array, $from, $to));
206
-
207
- return createSystemEvent('Mutation', { query: query.method('updateOne').doc(doc).merged(doc) }, async () => {
208
- const payload = model.shapeObject(inputShape, doc, query);
209
- await model.validateObject(inputShape, payload, query.payload(payload));
210
- return this.resolver.resolve(query.$doc(model.shapeObject(docShape, payload, query)));
211
- });
212
- });
100
+ #get(query) {
101
+ return this.#resolver.match(this.#model.name).id(query.toObject().id).one({ required: true });
213
102
  }
214
103
 
215
- first(query) {
216
- return this.findMany(query.method('findMany'));
104
+ #find(query) {
105
+ return this.#resolver.resolve(query.clone({ op: 'findMany', key: `find${this.#model.name}`, crud: 'read', isMutation: false }));
217
106
  }
218
107
 
219
- last(query) {
220
- return this.findMany(query.method('findMany'));
221
- }
108
+ #resolveReferentialIntegrity(query) {
109
+ const { id } = query.toObject();
110
+ const txn = this.#resolver.transaction(false);
222
111
 
223
- async resolve() {
224
- const { model, method, flags } = this.query.toObject();
112
+ // if (this.#model.name === 'Person') console.log(this.#model.referentialIntegrity);
113
+ return txn.run(Util.promiseChain(this.#model.referentialIntegrity.map(({ model, field, fieldRef, isArray, op }) => () => {
114
+ const fieldStr = fieldRef ? `${field}.${fieldRef}` : `${field.name}`;
115
+ const $where = { [fieldStr]: id };
225
116
 
226
- return this[method](this.query).then((data) => {
227
- if (flags.required && isEmpty(data)) throw Boom.notFound(`${model} Not Found`);
228
- if (data == null) return null; // Explicitly return null here
229
- return data;
230
- });
117
+ switch (op) {
118
+ case 'cascade': return isArray ? txn.match(model).where($where).pull(fieldStr, id) : txn.match(model).where($where).remove();
119
+ case 'nullify': return txn.match(model).where($where).save({ [fieldStr]: null });
120
+ case 'restrict': return txn.match(model).where($where).count().then(count => (count ? Promise.reject(new Error('Restricted')) : count));
121
+ case 'defer': return Promise.resolve(); // Used for embedded models (could be improved)
122
+ default: throw new Error(`Unknown onDelete operator: '${op}'`);
123
+ }
124
+ })));
231
125
  }
232
126
  };
@@ -0,0 +1,16 @@
1
+ const QueryResolver = require('./QueryResolver');
2
+
3
+ module.exports = class QueryResolverTransaction extends QueryResolver {
4
+ #config;
5
+
6
+ constructor(config) {
7
+ super(config);
8
+ this.#config = config;
9
+ }
10
+
11
+ terminate() {
12
+ return this.#config.transaction.then((transaction) => {
13
+ return super.terminate(super.options(transaction));
14
+ });
15
+ }
16
+ };