@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,120 +1,234 @@
|
|
|
1
1
|
const Query = require('./Query');
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/*
|
|
6
|
-
* QueryBuilder
|
|
7
|
-
*
|
|
8
|
-
* Facilitates the creation and execution of a Query. It provides a chainable API to build a query
|
|
9
|
-
* plus a list of terminal commands to execute the query.
|
|
10
|
-
*/
|
|
2
|
+
const { getGQLReturnType, mergeDeep } = require('../service/AppService');
|
|
3
|
+
|
|
11
4
|
module.exports = class QueryBuilder {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// this.stream = (...args) => this.makeTheCall(query, 'stream', args); // Stream records 1 by 1
|
|
57
|
-
// this.rollup = (...args) => this.makeTheCall(query, 'rollup', args); // Like sum, but for nested attributes (eg. Person.rollupAuthoredChaptersPages)
|
|
58
|
-
}
|
|
59
|
-
|
|
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'));
|
|
63
|
-
// this.terminated = true;
|
|
64
|
-
|
|
65
|
-
let method, crud, input, flags = {};
|
|
66
|
-
const { id, where } = this.query.toObject();
|
|
67
|
-
|
|
68
|
-
switch (cmd) {
|
|
69
|
-
case 'resolve': {
|
|
70
|
-
crud = 'read';
|
|
71
|
-
method = 'autoResolve';
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
case 'one': case 'many': {
|
|
75
|
-
crud = 'read';
|
|
76
|
-
[flags] = args;
|
|
77
|
-
method = cmd === 'one' ? 'findOne' : 'findMany';
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
case 'first': case 'last': {
|
|
81
|
-
crud = 'read';
|
|
82
|
-
[, flags] = args;
|
|
83
|
-
method = cmd;
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
|
-
case 'save': {
|
|
87
|
-
[input, flags] = args;
|
|
88
|
-
crud = id || where ? 'update' : 'create';
|
|
89
|
-
if (crud === 'update') { method = id ? 'updateOne' : 'updateMany'; }
|
|
90
|
-
if (crud === 'create') { method = Array.isArray(input) ? 'createMany' : 'createOne'; }
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
case 'push': case 'pull': case 'splice': {
|
|
94
|
-
// [target, input, flags] = args;
|
|
95
|
-
crud = 'update'; // Your logic wants this to be a simple "update". Sub documents systemEvents will emit either "create" or "udpate"
|
|
96
|
-
method = id ? `${cmd}One` : `${cmd}Many`;
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
case 'remove': case 'delete': {
|
|
100
|
-
crud = 'delete';
|
|
101
|
-
[flags] = args;
|
|
102
|
-
if (id) method = 'deleteOne';
|
|
103
|
-
else if (where) method = 'deleteMany';
|
|
104
|
-
else return Promise.reject(new Error('Remove requires an id() or where()'));
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
case 'count': {
|
|
108
|
-
crud = 'read';
|
|
109
|
-
[flags] = args;
|
|
110
|
-
method = 'count';
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
default: {
|
|
114
|
-
return Promise.reject(new Error(`Unknown query command: ${cmd}`));
|
|
115
|
-
}
|
|
5
|
+
#config;
|
|
6
|
+
#query;
|
|
7
|
+
#terminalCommands = ['one', 'many', 'count', 'save', 'delete', 'first', 'last', 'push', 'pull', 'splice'];
|
|
8
|
+
|
|
9
|
+
constructor(config) {
|
|
10
|
+
const { query } = config;
|
|
11
|
+
|
|
12
|
+
this.#config = config;
|
|
13
|
+
|
|
14
|
+
this.#query = Object.defineProperties(query, {
|
|
15
|
+
id: { writable: true, enumerable: true, value: query.id },
|
|
16
|
+
args: { writable: true, enumerable: true, value: query.args || {} },
|
|
17
|
+
flags: { writable: true, enumerable: true, value: query.flags || {} },
|
|
18
|
+
options: { writable: true, enumerable: true, value: query.options || {} },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Aliases
|
|
22
|
+
this.opts = this.options;
|
|
23
|
+
this.sortBy = this.sort;
|
|
24
|
+
this.remove = this.delete;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
isTerminal(cmd) {
|
|
28
|
+
return this.#terminalCommands.includes(cmd);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* When a termnial command is called, terminate() returns the Query object
|
|
33
|
+
* We have to clone() the Query because we mutate this.#config all while the query is being built
|
|
34
|
+
* However there is a "bug" when using .resolve() (below) and the QueryBuilder is re-used to resolve each thunk
|
|
35
|
+
*/
|
|
36
|
+
terminate() {
|
|
37
|
+
return new Query(this.#config).clone();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* For use in GraphQL resolver methods to return the "correct" response
|
|
42
|
+
*/
|
|
43
|
+
resolve(info) {
|
|
44
|
+
switch (getGQLReturnType(`${info.returnType}`)) {
|
|
45
|
+
case 'array': return this.many();
|
|
46
|
+
case 'number': return this.count();
|
|
47
|
+
case 'connection': return { count: () => this.count(), edges: () => this.many(), pageInfo: () => this.many() };
|
|
48
|
+
case 'scalar': default: return this.one();
|
|
116
49
|
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Chainable methods
|
|
54
|
+
*/
|
|
55
|
+
id(id) {
|
|
56
|
+
this.#propCheck('id', 'native', 'sort', 'skip', 'limit', 'before', 'after');
|
|
57
|
+
this.#query.id = id;
|
|
58
|
+
this.#query.where = mergeDeep(this.#query.where || {}, { id });
|
|
59
|
+
this.#query.args.id = id;
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
args(args) {
|
|
64
|
+
Object.entries(args).forEach(([key, value]) => { if (this[key]) this[key](value); }); // Call method only if exists
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
native(clause) {
|
|
69
|
+
this.#propCheck('native', 'id', 'where');
|
|
70
|
+
this.#query.isNative = true;
|
|
71
|
+
this.#query.native = clause;
|
|
72
|
+
this.#query.where = clause;
|
|
73
|
+
this.#query.args.native = clause;
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
where(clause) {
|
|
78
|
+
this.#propCheck('where', 'native', false);
|
|
79
|
+
const $clause = mergeDeep(this.#query.where || {}, clause);
|
|
80
|
+
this.#query.where = $clause;
|
|
81
|
+
this.#query.args.where = $clause;
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
select(...select) {
|
|
86
|
+
this.#propCheck('select');
|
|
87
|
+
select = select.flat();
|
|
88
|
+
this.#query.select = select;
|
|
89
|
+
this.#query.args.select = select;
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
skip(skip) {
|
|
94
|
+
this.#propCheck('skip', 'id');
|
|
95
|
+
this.isClassicPaging = true;
|
|
96
|
+
this.#query.skip = skip;
|
|
97
|
+
this.#query.args.skip = skip;
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
limit(limit) {
|
|
102
|
+
this.#propCheck('limit', 'id');
|
|
103
|
+
this.isClassicPaging = true;
|
|
104
|
+
this.#query.limit = limit;
|
|
105
|
+
this.#query.args.limit = limit;
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
before(before) {
|
|
110
|
+
this.#propCheck('before', 'id');
|
|
111
|
+
this.#query.isCursorPaging = true;
|
|
112
|
+
this.#query.before = before;
|
|
113
|
+
this.#query.args.before = before;
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
after(after) {
|
|
118
|
+
this.#propCheck('after', 'id');
|
|
119
|
+
this.#query.isCursorPaging = true;
|
|
120
|
+
this.#query.after = after;
|
|
121
|
+
this.#query.args.after = after;
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
sort(sort) {
|
|
126
|
+
this.#propCheck('sort', 'id');
|
|
127
|
+
this.#query.sort = sort;
|
|
128
|
+
this.#query.args.sort = sort;
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
meta(meta) {
|
|
133
|
+
this.#query.meta = meta;
|
|
134
|
+
this.#query.args.meta = meta;
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
options(options) {
|
|
139
|
+
Object.assign(this.#query.options, options);
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
flags(flags) {
|
|
144
|
+
Object.assign(this.#query.flags, flags);
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Core terminal commands
|
|
150
|
+
*/
|
|
151
|
+
one(flags) {
|
|
152
|
+
return this.flags(flags).terminate(Object.assign(this.#query, { op: 'findOne', crud: 'read', key: `get${this.#query.model}` }));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
many(flags) {
|
|
156
|
+
return this.flags(flags).terminate(Object.assign(this.#query, { op: 'findMany', crud: 'read', key: `find${this.#query.model}` }));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
count() {
|
|
160
|
+
return this.terminate(Object.assign(this.#query, { op: 'count', crud: 'read', key: `count${this.#query.model}` }));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
save(...args) {
|
|
164
|
+
const { id, where } = this.#query;
|
|
165
|
+
const crud = (id || where ? (args[1] ? 'upsert' : 'update') : 'create'); // eslint-disable-line
|
|
166
|
+
return this.#mutation(crud, ...args);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
delete(...args) {
|
|
170
|
+
const { id, where } = this.#query;
|
|
171
|
+
if (!id && !where) throw new Error('Delete requires id() or where()');
|
|
172
|
+
return this.#mutation('delete', ...args);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Proxy terminial commands
|
|
177
|
+
*/
|
|
178
|
+
first(first) {
|
|
179
|
+
this.#query.isCursorPaging = true;
|
|
180
|
+
this.#query.first = first + 2; // Adding 2 for pagination meta info (hasNext hasPrev)
|
|
181
|
+
this.#query.args.first = first;
|
|
182
|
+
return this.many();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
last(last) {
|
|
186
|
+
this.#query.isCursorPaging = true;
|
|
187
|
+
this.#query.last = last + 2; // Adding 2 for pagination meta info (hasNext hasPrev)
|
|
188
|
+
this.#query.args.last = last;
|
|
189
|
+
return this.many();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Array terminal commands
|
|
194
|
+
*/
|
|
195
|
+
push(path, ...values) {
|
|
196
|
+
values = values.flat();
|
|
197
|
+
return this.#mutation('push', { [path]: values });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
pull(path, ...values) {
|
|
201
|
+
values = values.flat();
|
|
202
|
+
return this.#mutation('pull', { [path]: values });
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
splice(path, ...values) {
|
|
206
|
+
values = values.flat();
|
|
207
|
+
return this.#mutation('splice', { [path]: values });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
*/
|
|
212
|
+
#mutation(crud, ...args) {
|
|
213
|
+
args = args.flat();
|
|
214
|
+
const { id, limit } = this.#query;
|
|
215
|
+
const suffix = id || limit === 1 || (crud === 'create' && args.length < 2) ? 'One' : 'Many';
|
|
216
|
+
let input = suffix === 'One' ? args[0] : args;
|
|
217
|
+
if (input === undefined) input = {};
|
|
218
|
+
this.#query.args.input = input;
|
|
219
|
+
return this.terminate(Object.assign(this.#query, {
|
|
220
|
+
op: `${crud}${suffix}`,
|
|
221
|
+
key: `${crud}${this.#query.model}`,
|
|
222
|
+
crud: ['push', 'pull', 'splice'].includes(crud) ? 'update' : crud,
|
|
223
|
+
input,
|
|
224
|
+
isMutation: true,
|
|
225
|
+
}));
|
|
226
|
+
}
|
|
117
227
|
|
|
118
|
-
|
|
228
|
+
#propCheck(prop, ...checks) {
|
|
229
|
+
if (checks[checks.length - 1] !== false && this.#query[prop]) throw new Error(`Cannot redefine "${prop}"`);
|
|
230
|
+
if (['skip', 'limit'].includes(prop) && this.#query.isCursorPaging) throw new Error(`Cannot use "${prop}" while using Cursor-Style Pagination`);
|
|
231
|
+
if (['first', 'last', 'before', 'after'].includes(prop) && this.isClassicPaging) throw new Error(`Cannot use "${prop}" while using Classic-Style Pagination`);
|
|
232
|
+
checks.forEach((check) => { if (this.#query[check]) throw new Error(`Cannot use "${prop}" while using "${check}"`); });
|
|
119
233
|
}
|
|
120
234
|
};
|