@coderich/autograph 0.13.21 → 0.13.23
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/package.json +2 -2
- package/src/data/Emitter.js +26 -9
- package/src/data/Resolver.js +17 -33
- package/src/query/Query.js +7 -6
- package/src/query/QueryBuilder.js +2 -2
- package/src/query/QueryResolver.js +1 -1
- package/src/schema/Schema.js +17 -19
- package/src/service/AppService.js +14 -6
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coderich/autograph",
|
|
3
3
|
"main": "index.js",
|
|
4
|
-
"version": "0.13.
|
|
4
|
+
"version": "0.13.23",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dev": "coderich-dev"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@coderich/util": "1.0.
|
|
18
|
+
"@coderich/util": "1.0.3",
|
|
19
19
|
"@graphql-tools/merge": "9.0.0",
|
|
20
20
|
"@graphql-tools/resolvers-composition": "7.0.0",
|
|
21
21
|
"@hapi/boom": "10.0.1",
|
package/src/data/Emitter.js
CHANGED
|
@@ -12,20 +12,21 @@ class Emitter extends EventEmitter {
|
|
|
12
12
|
emit(event, data) {
|
|
13
13
|
// Here we pull out functions with "next" vs those without
|
|
14
14
|
const [basicFuncs, nextFuncs] = this.rawListeners(event).reduce((prev, wrapper) => {
|
|
15
|
-
const listener = wrapper
|
|
15
|
+
const { listener = wrapper } = wrapper;
|
|
16
16
|
const isBasic = listener.length < 2;
|
|
17
|
+
wrapper.priority = listener.priority ?? 0;
|
|
17
18
|
return prev[isBasic ? 0 : 1].push(wrapper) && prev;
|
|
18
19
|
}, [[], []]);
|
|
19
20
|
|
|
20
21
|
return new Promise((resolve, reject) => {
|
|
21
22
|
// Basic functions run first; if they return a value they abort the flow of execution
|
|
22
|
-
basicFuncs.forEach((fn) => {
|
|
23
|
+
basicFuncs.sort(Emitter.sort).forEach((fn) => {
|
|
23
24
|
const value = fn(data);
|
|
24
25
|
if (value !== undefined && !(value instanceof Promise)) throw new AbortEarlyError(value);
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
// Next functions are async and control the timing of the next phase
|
|
28
|
-
Promise.all(nextFuncs.map((fn) => {
|
|
29
|
+
Promise.all(nextFuncs.sort(Emitter.sort).map((fn) => {
|
|
29
30
|
return new Promise((next) => {
|
|
30
31
|
Promise.resolve(fn(data, next));
|
|
31
32
|
}).then((result) => {
|
|
@@ -38,35 +39,45 @@ class Emitter extends EventEmitter {
|
|
|
38
39
|
});
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
on(event, listener, priority = 0) {
|
|
43
|
+
listener.priority = priority;
|
|
44
|
+
return super.on(event, listener);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
prependListener(event, listener, priority = 0) {
|
|
48
|
+
listener.priority = priority;
|
|
49
|
+
return super.prependListener(event, listener);
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
/**
|
|
42
53
|
* Syntactic sugar to listen on query keys
|
|
43
54
|
*/
|
|
44
55
|
onKeys(...args) {
|
|
45
|
-
return this.#createWrapper(...args,
|
|
56
|
+
return this.#createWrapper('key', false, ...args,);
|
|
46
57
|
}
|
|
47
58
|
|
|
48
59
|
/**
|
|
49
60
|
* Syntactic sugar to listen once on query keys
|
|
50
61
|
*/
|
|
51
62
|
onceKeys(...args) {
|
|
52
|
-
return this.#createWrapper(
|
|
63
|
+
return this.#createWrapper('key', true, ...args);
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
/**
|
|
56
67
|
* Syntactic sugar to listen on query models
|
|
57
68
|
*/
|
|
58
69
|
onModels(...args) {
|
|
59
|
-
return this.#createWrapper(
|
|
70
|
+
return this.#createWrapper('model', false, ...args);
|
|
60
71
|
}
|
|
61
72
|
|
|
62
73
|
/**
|
|
63
74
|
* Syntactic sugar to listen once on query models
|
|
64
75
|
*/
|
|
65
76
|
onceModels(...args) {
|
|
66
|
-
return this.#createWrapper(
|
|
77
|
+
return this.#createWrapper('model', true, ...args);
|
|
67
78
|
}
|
|
68
79
|
|
|
69
|
-
#createWrapper(eventName, arr, listener,
|
|
80
|
+
#createWrapper(prop, once, eventName, arr, listener, priority) {
|
|
70
81
|
arr = Util.ensureArray(arr);
|
|
71
82
|
|
|
72
83
|
const wrapper = listener.length < 2 ? (event) => {
|
|
@@ -83,7 +94,13 @@ class Emitter extends EventEmitter {
|
|
|
83
94
|
return next();
|
|
84
95
|
};
|
|
85
96
|
|
|
86
|
-
return this.on(eventName, wrapper);
|
|
97
|
+
return this.on(eventName, wrapper, priority);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static sort(a, b) {
|
|
101
|
+
if (a.priority > b.priority) return -1;
|
|
102
|
+
if (a.priority < b.priority) return 1;
|
|
103
|
+
return 0;
|
|
87
104
|
}
|
|
88
105
|
}
|
|
89
106
|
|
package/src/data/Resolver.js
CHANGED
|
@@ -189,19 +189,11 @@ module.exports = class Resolver {
|
|
|
189
189
|
*/
|
|
190
190
|
async resolve(query) {
|
|
191
191
|
let thunk;
|
|
192
|
-
const
|
|
193
|
-
const oquery = Object.defineProperties(tquery.toObject(), {
|
|
194
|
-
changeset: {
|
|
195
|
-
get: function get() {
|
|
196
|
-
return oquery.crud === 'update' ? Util.changeset(this.doc, this.input) : undefined;
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
});
|
|
200
|
-
const model = this.#schema.models[oquery.model];
|
|
192
|
+
const { model, doc, crud, isMutation, flags } = query.toObject();
|
|
201
193
|
const currSession = this.#sessions.slice(-1).pop();
|
|
202
194
|
|
|
203
|
-
if (
|
|
204
|
-
thunk =
|
|
195
|
+
if (isMutation) {
|
|
196
|
+
thunk = tquery => this.#schema.models[model].source.client.resolve(tquery.toDriver().toObject()).then((results) => {
|
|
205
197
|
// We clear the cache immediately (regardless if we're in transaction or not)
|
|
206
198
|
this.clear(model);
|
|
207
199
|
|
|
@@ -209,16 +201,16 @@ module.exports = class Resolver {
|
|
|
209
201
|
currSession?.thunks.push(...this.#sessions.map(s => () => s.parent.clear(model)));
|
|
210
202
|
|
|
211
203
|
// Return results
|
|
212
|
-
return
|
|
204
|
+
return crud === 'delete' ? doc : results;
|
|
213
205
|
});
|
|
214
206
|
} else {
|
|
215
|
-
thunk =
|
|
207
|
+
thunk = tquery => this.#dataLoaders[model].resolve(tquery);
|
|
216
208
|
}
|
|
217
209
|
|
|
218
|
-
return this.#createSystemEvent(
|
|
219
|
-
return thunk().then((result) => {
|
|
220
|
-
if (
|
|
221
|
-
return
|
|
210
|
+
return this.#createSystemEvent(query, (tquery) => {
|
|
211
|
+
return thunk(tquery).then((result) => {
|
|
212
|
+
if (flags?.required && (result == null || result?.length === 0)) throw Boom.notFound();
|
|
213
|
+
return crud === 'delete' ? result : this.toResultSet(model, result);
|
|
222
214
|
});
|
|
223
215
|
});
|
|
224
216
|
}
|
|
@@ -237,7 +229,7 @@ module.exports = class Resolver {
|
|
|
237
229
|
return (...args) => {
|
|
238
230
|
switch (cmd) {
|
|
239
231
|
case 'save': {
|
|
240
|
-
return queryResolver.save({ ...$doc, ...args[0] });
|
|
232
|
+
return queryResolver.save({ ...$doc, ...args[0] }); // $doc incase it's mutated
|
|
241
233
|
}
|
|
242
234
|
case 'lookup': {
|
|
243
235
|
const field = self.toModel(model).fields[args[0]];
|
|
@@ -274,8 +266,8 @@ module.exports = class Resolver {
|
|
|
274
266
|
}, {});
|
|
275
267
|
}
|
|
276
268
|
|
|
277
|
-
#createSystemEvent(
|
|
278
|
-
const query =
|
|
269
|
+
#createSystemEvent($query, thunk = () => {}) {
|
|
270
|
+
const query = $query.toObject();
|
|
279
271
|
const type = query.isMutation ? 'Mutation' : 'Query';
|
|
280
272
|
const event = { schema: this.#schema, context: this.#context, resolver: this, query };
|
|
281
273
|
|
|
@@ -287,23 +279,15 @@ module.exports = class Resolver {
|
|
|
287
279
|
event.input = event.args?.input;
|
|
288
280
|
|
|
289
281
|
return Emitter.emit(`pre${type}`, event).then(async (resultEarly) => {
|
|
290
|
-
if (resultEarly !== undefined) return resultEarly;
|
|
291
|
-
if (Util.isEqual(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
return thunk().then((result) => {
|
|
282
|
+
if (resultEarly !== undefined) return resultEarly; // Nothing to validate/transform
|
|
283
|
+
// if (query.crud === 'update' && Util.isEqual({ added: {}, updated: {}, deleted: {} }, Util.changeset(query.doc, query.input))) return query.doc;
|
|
284
|
+
const tquery = await $query.transform();
|
|
285
|
+
// await Emitter.emit('validate', event); // We need to re-connect tquery to event
|
|
286
|
+
return thunk(tquery).then((result) => {
|
|
295
287
|
event.result = result; // backwards compat
|
|
296
288
|
query.result = result;
|
|
297
289
|
return Emitter.emit(`post${type}`, event);
|
|
298
290
|
});
|
|
299
|
-
}).then((result = query.result) => {
|
|
300
|
-
event.result = result; // backwards compat
|
|
301
|
-
query.result = result;
|
|
302
|
-
return Emitter.emit('preResponse', event);
|
|
303
|
-
}).then((result = query.result) => {
|
|
304
|
-
event.result = result; // backwards compat
|
|
305
|
-
query.result = result;
|
|
306
|
-
return Emitter.emit('postResponse', event);
|
|
307
291
|
}).then((result = query.result) => result).catch((e) => {
|
|
308
292
|
const { data = {} } = e;
|
|
309
293
|
throw Boom.boomify(e, { data: { ...event, ...data } });
|
package/src/query/Query.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const Util = require('@coderich/util');
|
|
2
2
|
const Pipeline = require('../data/Pipeline');
|
|
3
|
-
const { isGlob, globToRegex, mergeDeep, finalizeWhereClause } = require('../service/AppService');
|
|
3
|
+
const { isGlob, globToRegex, mergeDeep, finalizeWhereClause, JSONParse } = require('../service/AppService');
|
|
4
4
|
|
|
5
5
|
module.exports = class Query {
|
|
6
6
|
#config;
|
|
@@ -49,11 +49,12 @@ module.exports = class Query {
|
|
|
49
49
|
* Run a portion of the pipeline against a data set
|
|
50
50
|
*/
|
|
51
51
|
pipeline(target, data, transformers) {
|
|
52
|
-
data = Util.unflatten(data);
|
|
52
|
+
data = Util.unflatten(data, { safe: true });
|
|
53
53
|
const crudMap = { create: ['$construct', '$serialize'], update: ['$restruct', '$serialize'] };
|
|
54
54
|
const crudLines = crudMap[this.#query.crud] || [];
|
|
55
55
|
const transformerMap = { where: ['$cast', '$instruct', '$serialize'], sort: [], input: [] };
|
|
56
|
-
if (this.#query.isMutation) transformerMap.input = ['$default', '$cast', '$normalize', '$instruct', ...crudLines];
|
|
56
|
+
if (this.#query.isMutation) transformerMap.input = ['$default', '$cast', '$normalize', '$instruct', ...crudLines, '$finalize'];
|
|
57
|
+
// if (this.#query.crud === 'create') transformerMap.input.unshift('$default'); // Cant because embedded documents on update are really "creates"
|
|
57
58
|
transformers = transformers || transformerMap[target];
|
|
58
59
|
return this.#pipeline(this.#query, target, this.#model, data, transformers.map(el => Pipeline[el]));
|
|
59
60
|
}
|
|
@@ -81,8 +82,8 @@ module.exports = class Query {
|
|
|
81
82
|
input: this.#model.walk(input, node => node.value !== undefined && Object.assign(node, { key: node.field.key })),
|
|
82
83
|
where: isNative ? where : this.#model.walk(where, node => Object.assign(node, { key: node.field.key })),
|
|
83
84
|
sort: this.#model.walk(sort, node => Object.assign(node, { key: node.field.key })),
|
|
84
|
-
before: (!isCursorPaging || !before) ? undefined :
|
|
85
|
-
after: (!isCursorPaging || !after) ? undefined :
|
|
85
|
+
before: (!isCursorPaging || !before) ? undefined : JSONParse(Buffer.from(before, 'base64').toString('ascii')),
|
|
86
|
+
after: (!isCursorPaging || !after) ? undefined : JSONParse(Buffer.from(after, 'base64').toString('ascii')),
|
|
86
87
|
$schema: this.#schema.resolvePath,
|
|
87
88
|
});
|
|
88
89
|
|
|
@@ -128,7 +129,7 @@ module.exports = class Query {
|
|
|
128
129
|
const { where = {}, sort = {} } = query;
|
|
129
130
|
const flatSort = Util.flatten(sort, { safe: true });
|
|
130
131
|
const flatWhere = Util.flatten(where, { safe: true });
|
|
131
|
-
const $sort = Util.unflatten(Object.keys(flatSort).reduce((prev, key) => Object.assign(prev, { [key]: {} }), {}));
|
|
132
|
+
const $sort = Util.unflatten(Object.keys(flatSort).reduce((prev, key) => Object.assign(prev, { [key]: {} }), {}), { safe: true });
|
|
132
133
|
|
|
133
134
|
//
|
|
134
135
|
query.sort = this.#model.walk(sort, (node) => {
|
|
@@ -68,7 +68,7 @@ module.exports = class QueryBuilder {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
info(info) {
|
|
71
|
-
this.select(getGQLSelectFields(this.#query.model, info));
|
|
71
|
+
// this.select(getGQLSelectFields(this.#query.model, info));
|
|
72
72
|
return this;
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -82,7 +82,7 @@ module.exports = class QueryBuilder {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
where(clause) {
|
|
85
|
-
this.#propCheck('where', 'native', false);
|
|
85
|
+
this.#propCheck('where', 'native', false); // Allow redefine of "where" because we merge it
|
|
86
86
|
const $clause = mergeDeep(this.#query.where || {}, clause);
|
|
87
87
|
this.#query.where = $clause;
|
|
88
88
|
this.#query.args.where = $clause;
|
|
@@ -34,7 +34,7 @@ module.exports = class QueryResolver extends QueryBuilder {
|
|
|
34
34
|
}
|
|
35
35
|
case 'updateOne': {
|
|
36
36
|
return this.#get(query).then((doc) => {
|
|
37
|
-
const merged = mergeDeep({}, Util.unflatten(doc), Util.unflatten(input));
|
|
37
|
+
const merged = mergeDeep({}, Util.unflatten(doc, { safe: true }), Util.unflatten(input, { safe: true }));
|
|
38
38
|
return this.#resolver.resolve(query.clone({ doc, input: merged }));
|
|
39
39
|
});
|
|
40
40
|
}
|
package/src/schema/Schema.js
CHANGED
|
@@ -589,7 +589,7 @@ module.exports = class Schema {
|
|
|
589
589
|
}
|
|
590
590
|
${connectionFields.length ? `
|
|
591
591
|
extend type ${model} {
|
|
592
|
-
${connectionFields.map(field => `${field}: ${field.model}Connection`)}
|
|
592
|
+
${connectionFields.map(field => `${field}(${Schema.#getConnectionArguments(field.model)}): ${field.model}Connection`)}
|
|
593
593
|
}
|
|
594
594
|
` : ''}
|
|
595
595
|
`;
|
|
@@ -619,16 +619,7 @@ module.exports = class Schema {
|
|
|
619
619
|
node(id: ID!): Node
|
|
620
620
|
${queryModels.map(model => `
|
|
621
621
|
get${model}(id: ID!): ${model}
|
|
622
|
-
find${model}(
|
|
623
|
-
where: ${model}InputWhere
|
|
624
|
-
sortBy: ${model}InputSort
|
|
625
|
-
limit: Int
|
|
626
|
-
skip: Int
|
|
627
|
-
first: Int
|
|
628
|
-
after: String
|
|
629
|
-
last: Int
|
|
630
|
-
before: String
|
|
631
|
-
): ${model}Connection!
|
|
622
|
+
find${model}(${Schema.#getConnectionArguments(model)}): ${model}Connection!
|
|
632
623
|
`)}
|
|
633
624
|
}
|
|
634
625
|
|
|
@@ -713,13 +704,7 @@ module.exports = class Schema {
|
|
|
713
704
|
Query: queryModels.reduce((prev, model) => {
|
|
714
705
|
return Object.assign(prev, {
|
|
715
706
|
[`get${model}`]: (doc, args, context, info) => context[schema.namespace].resolver.match(model).args(args).info(info).one({ required: true }),
|
|
716
|
-
[`find${model}`]: (doc, args, context, info) =>
|
|
717
|
-
return {
|
|
718
|
-
edges: () => context[schema.namespace].resolver.match(model).args(args).info(info).many(),
|
|
719
|
-
count: () => context[schema.namespace].resolver.match(model).args(args).info(info).count(),
|
|
720
|
-
pageInfo: () => context[schema.namespace].resolver.match(model).args(args).info(info).many(),
|
|
721
|
-
};
|
|
722
|
-
},
|
|
707
|
+
[`find${model}`]: (doc, args, context, info) => context[schema.namespace].resolver.match(model).args(args).info(info).resolve(info),
|
|
723
708
|
});
|
|
724
709
|
}, {
|
|
725
710
|
node: (doc, args, context, info) => {
|
|
@@ -746,7 +731,7 @@ module.exports = class Schema {
|
|
|
746
731
|
[model]: Object.values(model.fields).filter(field => field.model?.isEntity).reduce((prev2, field) => {
|
|
747
732
|
return Object.assign(prev2, {
|
|
748
733
|
[field]: (doc, args, context, info) => {
|
|
749
|
-
return
|
|
734
|
+
return doc.$.lookup(field).args(args).info(info).resolve(info);
|
|
750
735
|
},
|
|
751
736
|
});
|
|
752
737
|
}, {}),
|
|
@@ -767,6 +752,19 @@ module.exports = class Schema {
|
|
|
767
752
|
return type;
|
|
768
753
|
}
|
|
769
754
|
|
|
755
|
+
static #getConnectionArguments(model) {
|
|
756
|
+
return `
|
|
757
|
+
where: ${model}InputWhere
|
|
758
|
+
sortBy: ${model}InputSort
|
|
759
|
+
limit: Int
|
|
760
|
+
skip: Int
|
|
761
|
+
first: Int
|
|
762
|
+
after: String
|
|
763
|
+
last: Int
|
|
764
|
+
before: String
|
|
765
|
+
`;
|
|
766
|
+
}
|
|
767
|
+
|
|
770
768
|
static #identifyOnDeletes(models, parentName) {
|
|
771
769
|
return models.reduce((prev, model) => {
|
|
772
770
|
Object.values(model.fields).filter(f => f.onDelete).forEach((field) => {
|
|
@@ -11,8 +11,7 @@ exports.isGlob = str => PicoMatch.scan(str).isGlob;
|
|
|
11
11
|
exports.globToRegex = (glob, options = {}) => PicoMatch.makeRe(glob, { nocase: true, ...options, expandRange: (a, b) => `(${FillRange(a, b, { toRegex: true })})` });
|
|
12
12
|
|
|
13
13
|
const smartMerge = (target, source, options) => source;
|
|
14
|
-
exports.
|
|
15
|
-
exports.isLeafValue = value => Array.isArray(value) || value instanceof Date || ObjectId.isValid(value) || exports.isScalarValue(value);
|
|
14
|
+
exports.isLeafValue = value => Array.isArray(value) || value instanceof Date || ObjectId.isValid(value) || Util.isScalarValue(value);
|
|
16
15
|
exports.mergeDeep = (...args) => DeepMerge.all(args, { isMergeableObject: obj => (Util.isPlainObjectOrArray(obj)), arrayMerge: smartMerge });
|
|
17
16
|
exports.hashObject = obj => ObjectHash(obj, { respectType: false, respectFunctionNames: false, respectFunctionProperties: false, unorderedArrays: true, ignoreUnknown: true, replacer: r => (ObjectId.isValid(r) ? `${r}` : r) });
|
|
18
17
|
exports.fromGUID = guid => Buffer.from(`${guid}`, 'base64').toString('ascii').split(',');
|
|
@@ -39,8 +38,17 @@ exports.getGQLSelectFields = (model, info) => {
|
|
|
39
38
|
return Object.keys(node || fields);
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
exports.removeUndefinedDeep = (obj) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
// exports.removeUndefinedDeep = (obj) => {
|
|
42
|
+
// return Util.unflatten(Object.entries(Util.flatten(obj)).reduce((prev, [key, value]) => {
|
|
43
|
+
// return value === undefined ? prev : Object.assign(prev, { [key]: value });
|
|
44
|
+
// }, {}));
|
|
45
|
+
// };
|
|
46
|
+
|
|
47
|
+
exports.JSONParse = (mixed) => {
|
|
48
|
+
try {
|
|
49
|
+
const json = JSON.parse(mixed);
|
|
50
|
+
return json;
|
|
51
|
+
} catch (e) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
46
54
|
};
|