@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.
- package/index.js +4 -6
- package/package.json +30 -44
- package/src/data/DataLoader.js +77 -70
- package/src/data/Emitter.js +89 -0
- package/src/data/Loader.js +33 -0
- package/src/data/Pipeline.js +88 -96
- package/src/data/Resolver.js +304 -0
- package/src/data/Transaction.js +49 -0
- package/src/query/Query.js +159 -334
- package/src/query/QueryBuilder.js +228 -114
- package/src/query/QueryResolver.js +110 -216
- package/src/query/QueryResolverTransaction.js +16 -0
- package/src/schema/Schema.js +593 -0
- package/src/service/AppService.js +38 -0
- package/src/service/ErrorService.js +7 -0
- package/CHANGELOG.md +0 -41
- package/LICENSE +0 -21
- package/README.md +0 -76
- package/src/.DS_Store +0 -0
- package/src/core/.DS_Store +0 -0
- package/src/core/Boom.js +0 -9
- package/src/core/EventEmitter.js +0 -95
- package/src/core/Resolver.js +0 -124
- package/src/core/Schema.js +0 -55
- package/src/core/ServerResolver.js +0 -15
- package/src/data/.DS_Store +0 -0
- package/src/data/DataService.js +0 -120
- package/src/data/DataTransaction.js +0 -96
- package/src/data/Field.js +0 -83
- package/src/data/Model.js +0 -223
- package/src/data/TreeMap.js +0 -78
- package/src/data/Type.js +0 -50
- package/src/driver/.DS_Store +0 -0
- package/src/driver/MongoDriver.js +0 -227
- package/src/driver/index.js +0 -11
- package/src/graphql/.DS_Store +0 -0
- package/src/graphql/ast/.DS_Store +0 -0
- package/src/graphql/ast/Field.js +0 -206
- package/src/graphql/ast/Model.js +0 -145
- package/src/graphql/ast/Node.js +0 -291
- package/src/graphql/ast/Schema.js +0 -133
- package/src/graphql/ast/Type.js +0 -26
- package/src/graphql/ast/TypeDefApi.js +0 -93
- package/src/graphql/extension/.DS_Store +0 -0
- package/src/graphql/extension/api.js +0 -193
- package/src/graphql/extension/framework.js +0 -71
- package/src/graphql/extension/type.js +0 -34
- package/src/query/.DS_Store +0 -0
- package/src/query/QueryBuilderTransaction.js +0 -26
- package/src/query/QueryService.js +0 -111
- package/src/service/.DS_Store +0 -0
- package/src/service/app.service.js +0 -319
- package/src/service/decorator.service.js +0 -114
- package/src/service/event.service.js +0 -66
- package/src/service/graphql.service.js +0 -92
- package/src/service/schema.service.js +0 -95
|
@@ -1,232 +1,126 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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 '
|
|
24
|
-
return
|
|
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 '
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
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 '
|
|
34
|
-
return
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
189
|
-
|
|
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
|
-
|
|
216
|
-
return this.
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
108
|
+
#resolveReferentialIntegrity(query) {
|
|
109
|
+
const { id } = query.toObject();
|
|
110
|
+
const txn = this.#resolver.transaction(false);
|
|
222
111
|
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
+
};
|