@coderich/autograph 0.12.0 → 0.13.1

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 +84 -101
  7. package/src/data/Resolver.js +304 -0
  8. package/src/data/Transaction.js +49 -0
  9. package/src/query/Query.js +159 -335
  10. package/src/query/QueryBuilder.js +228 -114
  11. package/src/query/QueryResolver.js +110 -205
  12. package/src/query/QueryResolverTransaction.js +16 -0
  13. package/src/schema/Schema.js +602 -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 -161
  29. package/src/data/Field.js +0 -83
  30. package/src/data/Model.js +0 -214
  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,221 +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, 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 } = 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
-
86
- return createSystemEvent('Mutation', { query: query.doc(doc).merged(merged) }, async () => {
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)));
90
- });
91
- });
92
- }
93
-
94
- updateMany(query) {
95
- const { model, input, match, transaction, flags } = query.toObject();
96
-
97
- return this.resolver.match(model).match(match).many(flags).then((docs) => {
98
- const txn = this.resolver.transaction(transaction);
99
- docs.forEach(doc => txn.match(model).id(doc.id).save(input, flags));
100
- return txn.run();
101
- });
102
- }
103
-
104
- deleteOne(query) {
105
- const { model, id } = query.toObject();
106
-
107
- return this.resolver.match(model).id(id).one({ required: true }).then(async (doc) => {
108
- return createSystemEvent('Mutation', { query: query.doc(doc) }, () => {
109
- return QueryService.resolveReferentialIntegrity(query).then(() => {
110
- 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);
111
52
  });
112
- });
113
- });
114
- }
115
-
116
- deleteMany(query) {
117
- const { model, match, transaction, flags } = query.toObject();
118
-
119
- return this.resolver.match(model).where(match).flags(flags).many().then((docs) => {
120
- const txn = this.resolver.transaction(transaction);
121
- docs.forEach(doc => txn.match(model).id(doc.id).delete());
122
- return txn.run();
123
- });
124
- }
125
-
126
- pushOne(query) {
127
- const { args } = query.toObject();
128
- const [key, ...values] = args;
129
- return this.splice(query.args([key, null, values]));
130
- }
131
-
132
- pushMany(query) {
133
- const { args } = query.toObject();
134
- const { model, match, transaction, flags } = query.toObject();
135
- const [key, ...values] = args;
136
-
137
- return this.resolver.match(model).match(match).flags(flags).many().then((docs) => {
138
- const txn = this.resolver.transaction(transaction);
139
- docs.forEach(doc => txn.match(model).id(doc.id).push(key, ...values));
140
- return txn.run();
141
- });
142
- }
143
-
144
- pullOne(query) {
145
- const { args } = query.toObject();
146
- const [key, ...values] = args;
147
- return this.splice(query.args([key, values]));
148
- }
149
-
150
- pullMany(query) {
151
- const { model, match, transaction, args, flags } = query.toObject();
152
- const [key, ...values] = args;
153
-
154
- return this.resolver.match(model).match(match).flags(flags).many().then((docs) => {
155
- const txn = this.resolver.transaction(transaction);
156
- docs.forEach(doc => txn.match(model).id(doc.id).pull(key, ...values));
157
- return txn.run();
158
- });
159
- }
160
-
161
- spliceOne(query) {
162
- const { args } = query.toObject();
163
- const [key, ...values] = args;
164
- return this.splice(query.args([key, ...values]));
165
- }
166
-
167
- spliceMany(query) {
168
- const { model, match, transaction, args, flags } = query.toObject();
169
- const [key, ...values] = args;
170
-
171
- return this.resolver.match(model).match(match).flags(flags).many().then((docs) => {
172
- const txn = this.resolver.transaction(transaction);
173
- docs.forEach(doc => txn.match(model).id(doc.id).splice(key, ...values));
174
- return txn.run();
175
- });
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
+ }
176
98
  }
177
99
 
178
- splice(query) {
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');
183
- const [key, from, to] = args;
184
-
185
- // Can only splice arrays
186
- const field = model.getField(key);
187
- const isArray = field.isArray();
188
- if (!isArray) throw Boom.badRequest(`Cannot splice field '${model}.${field}'`);
189
-
190
- return this.resolver.match(model).match(match).one({ required: true }).then(async (doc) => {
191
- const array = get(doc, key) || [];
192
- const $to = model.shapeObject(spliceShape, { [key]: to }, query)[key] || to;
193
- const $from = model.shapeObject(spliceShape, { [key]: from }, query)[key] || from;
194
- set(doc, key, DataService.spliceEmbeddedArray(array, $from, $to));
195
-
196
- return createSystemEvent('Mutation', { query: query.method('updateOne').doc(doc).merged(doc) }, async () => {
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)));
200
- });
201
- });
100
+ #get(query) {
101
+ return this.#resolver.match(this.#model.name).id(query.toObject().id).one({ required: true });
202
102
  }
203
103
 
204
- first(query) {
205
- 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 }));
206
106
  }
207
107
 
208
- last(query) {
209
- return this.findMany(query.method('findMany'));
210
- }
108
+ #resolveReferentialIntegrity(query) {
109
+ const { id } = query.toObject();
110
+ const txn = this.#resolver.transaction(false);
211
111
 
212
- async resolve() {
213
- 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 };
214
116
 
215
- return this[method](this.query).then((data) => {
216
- if (flags.required && isEmpty(data)) throw Boom.notFound(`${model} Not Found`);
217
- if (data == null) return null; // Explicitly return null here
218
- return data;
219
- });
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
+ })));
220
125
  }
221
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
+ };