@coderich/autograph 0.13.109 → 0.14.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/package.json +2 -3
- package/src/data/DataLoader.js +28 -19
- package/src/data/Emitter.js +55 -14
- package/src/data/Pipeline.js +0 -1
- package/src/data/Resolver.js +4 -22
- package/src/data/Transformer.js +4 -2
- package/src/query/Query.js +25 -6
- package/src/schema/Schema.js +19 -33
- package/src/service/AppService.js +1 -1
- package/src/data/__mocks__/Emitter.js +0 -11
- package/src/data/__mocks__/Pipeline.js +0 -32
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coderich/autograph",
|
|
3
3
|
"main": "index.js",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.14.0",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"clinic": "clinic flame -- node ./test/server"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@coderich/util": "
|
|
19
|
+
"@coderich/util": "2.2.0",
|
|
20
20
|
"@graphql-tools/merge": "9.0.0",
|
|
21
21
|
"@graphql-tools/resolvers-composition": "7.0.0",
|
|
22
22
|
"@hapi/boom": "10.0.1",
|
|
@@ -38,7 +38,6 @@
|
|
|
38
38
|
"@graphql-tools/schema": "10.0.0",
|
|
39
39
|
"clinic": "13.0.0",
|
|
40
40
|
"graphql": "16.8.1",
|
|
41
|
-
"mongodb": "5.9.2",
|
|
42
41
|
"mongodb-memory-server": "8.13.0"
|
|
43
42
|
},
|
|
44
43
|
"peerDependencies": {
|
package/src/data/DataLoader.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const get = require('lodash.get');
|
|
2
2
|
const Util = require('@coderich/util');
|
|
3
3
|
const DataLoader = require('dataloader');
|
|
4
|
-
const { hashObject } = require('../service/AppService');
|
|
5
4
|
|
|
6
5
|
module.exports = class Loader {
|
|
7
6
|
#model;
|
|
@@ -11,7 +10,7 @@ module.exports = class Loader {
|
|
|
11
10
|
constructor(model, resolver) {
|
|
12
11
|
this.#model = model;
|
|
13
12
|
this.#resolver = resolver;
|
|
14
|
-
this.#model.loader.cacheKeyFn ??= query =>
|
|
13
|
+
this.#model.loader.cacheKeyFn ??= query => query.toCacheKey();
|
|
15
14
|
this.#loader = new DataLoader(keys => this.#resolve(keys), this.#model.loader);
|
|
16
15
|
}
|
|
17
16
|
|
|
@@ -33,10 +32,9 @@ module.exports = class Loader {
|
|
|
33
32
|
const $query = query.toDriver().toObject();
|
|
34
33
|
const key = $query.batch ?? '__default__';
|
|
35
34
|
let [values] = key === '__default__' ? [] : Object.values(Util.flatten($query.where, { safe: true }));
|
|
36
|
-
values =
|
|
37
|
-
const $values = values.map(value => (value instanceof RegExp ? value : new RegExp(`${value}`, 'i')));
|
|
35
|
+
values = Loader.#dedup(Util.ensureArray(values));
|
|
38
36
|
prev[key] = prev[key] || [];
|
|
39
|
-
prev[key].push({ query, $query, values,
|
|
37
|
+
prev[key].push({ query, $query, values, i });
|
|
40
38
|
return prev;
|
|
41
39
|
}, {});
|
|
42
40
|
|
|
@@ -47,36 +45,37 @@ module.exports = class Loader {
|
|
|
47
45
|
}
|
|
48
46
|
default: {
|
|
49
47
|
// Collect all the values for the where clause
|
|
50
|
-
const values =
|
|
48
|
+
const values = Loader.#dedup(batches.map(batch => batch.values).flat());
|
|
51
49
|
const $query = { ...batches[0].$query, op: 'findMany', where: { [key]: values } };
|
|
52
50
|
|
|
53
|
-
//
|
|
54
51
|
if (values.length < 3) {
|
|
55
52
|
return batches.map(batch => this.#model.source.client.resolve(batch.$query).then(data => ({ data, ...batch })));
|
|
56
53
|
}
|
|
57
54
|
|
|
58
|
-
// Collect all the $values (Regular Expressions) to match doc (result) data by
|
|
59
|
-
const $values = Array.from(new Set(batches.map(batch => batch.$values).flat()));
|
|
60
|
-
const docsByRegExpKey = $values.reduce((map, re) => map.set(re, []), new Map());
|
|
61
|
-
|
|
62
55
|
// Now we perform 1 query, instead of many smaller ones
|
|
63
56
|
return this.#model.source.client.resolve($query).then((docs) => {
|
|
64
|
-
|
|
57
|
+
const docsByKey = new Map();
|
|
58
|
+
|
|
65
59
|
docs.forEach((doc) => {
|
|
66
60
|
Util.pathmap(key, doc, (value) => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
});
|
|
61
|
+
Util.ensureArray(value).forEach((v) => {
|
|
62
|
+
const k = `${v}`;
|
|
63
|
+
if (!docsByKey.has(k)) docsByKey.set(k, []);
|
|
64
|
+
docsByKey.get(k).push(doc);
|
|
73
65
|
});
|
|
74
66
|
return value;
|
|
75
67
|
});
|
|
76
68
|
});
|
|
77
69
|
|
|
78
70
|
return batches.map((batch) => {
|
|
79
|
-
const matches = Array.from(new Set(batch
|
|
71
|
+
const matches = Array.from(new Set(batch.values.flatMap((v) => {
|
|
72
|
+
if (v instanceof RegExp) {
|
|
73
|
+
const result = [];
|
|
74
|
+
docsByKey.forEach((d, k) => { if (v.test(k)) result.push(...d); });
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
return docsByKey.get(`${v}`) || [];
|
|
78
|
+
})));
|
|
80
79
|
const data = batch.$query.op === 'findOne' ? matches[0] : matches;
|
|
81
80
|
return { data, ...batch };
|
|
82
81
|
});
|
|
@@ -103,6 +102,16 @@ module.exports = class Loader {
|
|
|
103
102
|
// }));
|
|
104
103
|
}
|
|
105
104
|
|
|
105
|
+
// Deduplicate an array of values that may contain RegExp objects.
|
|
106
|
+
// new Set() compares by reference, so two RegExp literals with identical patterns
|
|
107
|
+
// are treated as distinct. Using toString() as the Map key handles this correctly
|
|
108
|
+
// while preserving the actual RegExp instance as the value.
|
|
109
|
+
static #dedup(arr) {
|
|
110
|
+
const seen = new Map();
|
|
111
|
+
arr.forEach(v => seen.set(v instanceof RegExp ? v.toString() : v, v));
|
|
112
|
+
return Array.from(seen.values());
|
|
113
|
+
}
|
|
114
|
+
|
|
106
115
|
static #paginateResults(rs, query) {
|
|
107
116
|
let hasNextPage = false;
|
|
108
117
|
let hasPreviousPage = false;
|
package/src/data/Emitter.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const EventEmitter = require('events');
|
|
1
|
+
const EventEmitter = require('node:events');
|
|
2
2
|
const Util = require('@coderich/util');
|
|
3
3
|
const { AbortEarlyError } = require('../service/ErrorService');
|
|
4
4
|
|
|
@@ -9,28 +9,36 @@ const { AbortEarlyError } = require('../service/ErrorService');
|
|
|
9
9
|
* If it expects more than 1 we block and wait for it to finish.
|
|
10
10
|
*/
|
|
11
11
|
class Emitter extends EventEmitter {
|
|
12
|
+
#cache = new Map();
|
|
13
|
+
|
|
14
|
+
#invalidate(event) {
|
|
15
|
+
this.#cache.delete(event);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#getListeners(event) {
|
|
19
|
+
if (!this.#cache.has(event)) {
|
|
20
|
+
const [basicFuncs, nextFuncs] = this.rawListeners(event).reduce((prev, wrapper) => {
|
|
21
|
+
const { listener = wrapper } = wrapper;
|
|
22
|
+
wrapper.priority = listener.priority ?? 0;
|
|
23
|
+
return prev[listener.length < 2 ? 0 : 1].push(wrapper) && prev;
|
|
24
|
+
}, [[], []]);
|
|
25
|
+
this.#cache.set(event, { basicFuncs: basicFuncs.sort(Emitter.sort), nextFuncs: nextFuncs.sort(Emitter.sort) });
|
|
26
|
+
}
|
|
27
|
+
return this.#cache.get(event);
|
|
28
|
+
}
|
|
29
|
+
|
|
12
30
|
emit(event, data) {
|
|
13
|
-
|
|
14
|
-
const [basicFuncs, nextFuncs] = this.rawListeners(event).reduce((prev, wrapper) => {
|
|
15
|
-
const { listener = wrapper } = wrapper;
|
|
16
|
-
const isBasic = listener.length < 2;
|
|
17
|
-
wrapper.priority = listener.priority ?? 0;
|
|
18
|
-
return prev[isBasic ? 0 : 1].push(wrapper) && prev;
|
|
19
|
-
}, [[], []]);
|
|
20
|
-
|
|
21
|
-
// // Basic functions are not designed to be bound to the query execution so we need an isolated resolver from any transactions
|
|
22
|
-
// const resolver = data?.resolver?.clone();
|
|
23
|
-
// const basicData = { ...data, resolver };
|
|
31
|
+
const { basicFuncs, nextFuncs } = this.#getListeners(event);
|
|
24
32
|
|
|
25
33
|
return new Promise((resolve, reject) => {
|
|
26
34
|
// Basic functions run first; if they return a value they abort the flow of execution
|
|
27
|
-
basicFuncs.
|
|
35
|
+
basicFuncs.forEach((fn) => {
|
|
28
36
|
const value = fn(data);
|
|
29
37
|
if (value !== undefined && !(value instanceof Promise)) throw new AbortEarlyError(value);
|
|
30
38
|
});
|
|
31
39
|
|
|
32
40
|
// Next functions are async and control the timing of the next phase
|
|
33
|
-
Promise.all(nextFuncs.
|
|
41
|
+
Promise.all(nextFuncs.map((fn) => {
|
|
34
42
|
return new Promise((next, err) => {
|
|
35
43
|
Promise.resolve().then(() => fn(data, next)).catch(err);
|
|
36
44
|
}).then((result) => {
|
|
@@ -45,14 +53,47 @@ class Emitter extends EventEmitter {
|
|
|
45
53
|
|
|
46
54
|
on(event, listener, priority = 0) {
|
|
47
55
|
listener.priority = priority;
|
|
56
|
+
this.#invalidate(event);
|
|
48
57
|
return super.on(event, listener);
|
|
49
58
|
}
|
|
50
59
|
|
|
60
|
+
addListener(event, listener, priority = 0) {
|
|
61
|
+
return this.on(event, listener, priority);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
once(event, listener, priority = 0) {
|
|
65
|
+
listener.priority = priority;
|
|
66
|
+
this.#invalidate(event);
|
|
67
|
+
return super.once(event, listener);
|
|
68
|
+
}
|
|
69
|
+
|
|
51
70
|
prependListener(event, listener, priority = 0) {
|
|
52
71
|
listener.priority = priority;
|
|
72
|
+
this.#invalidate(event);
|
|
53
73
|
return super.prependListener(event, listener);
|
|
54
74
|
}
|
|
55
75
|
|
|
76
|
+
prependOnceListener(event, listener, priority = 0) {
|
|
77
|
+
listener.priority = priority;
|
|
78
|
+
this.#invalidate(event);
|
|
79
|
+
return super.prependOnceListener(event, listener);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
removeListener(event, listener) {
|
|
83
|
+
this.#invalidate(event);
|
|
84
|
+
return super.removeListener(event, listener);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
off(event, listener) {
|
|
88
|
+
return this.removeListener(event, listener);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
removeAllListeners(event) {
|
|
92
|
+
if (event) this.#invalidate(event);
|
|
93
|
+
else this.#cache.clear();
|
|
94
|
+
return super.removeAllListeners(event);
|
|
95
|
+
}
|
|
96
|
+
|
|
56
97
|
/**
|
|
57
98
|
* Syntactic sugar to listen on query keys
|
|
58
99
|
*/
|
package/src/data/Pipeline.js
CHANGED
|
@@ -63,7 +63,6 @@ module.exports = class Pipeline {
|
|
|
63
63
|
Pipeline.define('$construct', params => Pipeline.resolve(params, 'construct'), { ignoreNull: false });
|
|
64
64
|
Pipeline.define('$restruct', params => Pipeline.resolve(params, 'restruct'), { ignoreNull: false });
|
|
65
65
|
Pipeline.define('$serialize', params => Pipeline.resolve(params, 'serialize'), { ignoreNull: false });
|
|
66
|
-
Pipeline.define('$deserialize', params => Pipeline.resolve(params, 'deserialize'), { ignoreNull: false });
|
|
67
66
|
Pipeline.define('$validate', params => Pipeline.resolve(params, 'validate'), { ignoreNull: false });
|
|
68
67
|
|
|
69
68
|
//
|
package/src/data/Resolver.js
CHANGED
|
@@ -245,7 +245,7 @@ module.exports = class Resolver {
|
|
|
245
245
|
model = this.#schema.models[model];
|
|
246
246
|
|
|
247
247
|
return Object.defineProperties(Util.map(result, (doc) => {
|
|
248
|
-
const $doc = model.
|
|
248
|
+
const $doc = model.docTransform(doc);
|
|
249
249
|
|
|
250
250
|
// Assign useful/needed meta data
|
|
251
251
|
return Object.defineProperties($doc, {
|
|
@@ -307,44 +307,26 @@ module.exports = class Resolver {
|
|
|
307
307
|
const tquery = $query.transform(false);
|
|
308
308
|
const query = tquery.toObject();
|
|
309
309
|
const type = query.isMutation ? 'Mutation' : 'Query';
|
|
310
|
-
const event = this.#
|
|
310
|
+
const event = { schema: this.#schema, context: this.#context, resolver: this, query };
|
|
311
311
|
|
|
312
312
|
return Emitter.emit(`pre${type}`, event).then(async (resultEarly) => {
|
|
313
|
-
if (resultEarly !== undefined) return resultEarly;
|
|
314
|
-
// if (query.crud === 'update' && Util.isEqual({ added: {}, updated: {}, deleted: {} }, Util.changeset(query.doc, query.input))) return query.doc;
|
|
313
|
+
if (resultEarly !== undefined) return resultEarly;
|
|
315
314
|
|
|
316
315
|
if (['create', 'update'].includes(query.crud)) {
|
|
317
|
-
tquery.validate(); //
|
|
316
|
+
tquery.validate(); // sets async $thunks (e.g. ensureFK)
|
|
318
317
|
await Promise.all([...query.input.$thunks]);
|
|
319
318
|
await Emitter.emit('validate', event);
|
|
320
319
|
}
|
|
321
320
|
|
|
322
321
|
return thunk(tquery);
|
|
323
322
|
}).then((result) => {
|
|
324
|
-
event.result = result; // backwards compat
|
|
325
323
|
query.result = result;
|
|
326
324
|
return Emitter.emit(`post${type}`, event);
|
|
327
325
|
}).then((result = query.result) => result).catch((e) => {
|
|
328
326
|
throw Boom.boomify(e);
|
|
329
|
-
// const { data = {} } = e;
|
|
330
|
-
// throw Boom.boomify(e, { data: { ...event, ...data } });
|
|
331
327
|
});
|
|
332
328
|
}
|
|
333
329
|
|
|
334
|
-
#createEvent(query) {
|
|
335
|
-
const event = { schema: this.#schema, context: this.#context, resolver: this, query };
|
|
336
|
-
|
|
337
|
-
// Backwards compat
|
|
338
|
-
Object.assign(event, query);
|
|
339
|
-
query.match = event.args.where;
|
|
340
|
-
query.toObject = () => query;
|
|
341
|
-
event.merged = event.input;
|
|
342
|
-
event.input = Util.unflatten(event.args?.input, { safe: true });
|
|
343
|
-
event.doc ??= {};
|
|
344
|
-
|
|
345
|
-
return event;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
330
|
static $loader(name, resolver, config) {
|
|
349
331
|
if (!name) return loaders;
|
|
350
332
|
if (!resolver) return loaders[name];
|
package/src/data/Transformer.js
CHANGED
|
@@ -9,6 +9,8 @@ module.exports = class Transformer {
|
|
|
9
9
|
keepUndefined: false, // If true, will preserve undefined values
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
#callArgs = {}; // Ephemeral per-call merge of #config.args + transform() args; never persisted
|
|
13
|
+
|
|
12
14
|
#operation = {
|
|
13
15
|
set: (target, prop, startValue, proxy) => {
|
|
14
16
|
if (this.#config.shape[prop]) {
|
|
@@ -16,7 +18,7 @@ module.exports = class Transformer {
|
|
|
16
18
|
|
|
17
19
|
const result = this.#config.shape[prop].reduce((value, t) => {
|
|
18
20
|
previousValue = value;
|
|
19
|
-
if (typeof t === 'function') return Util.uvl(t({ startValue, value, ...this.#
|
|
21
|
+
if (typeof t === 'function') return Util.uvl(t({ startValue, value, ...this.#callArgs }), value);
|
|
20
22
|
prop = t; // rename key
|
|
21
23
|
return value;
|
|
22
24
|
}, startValue);
|
|
@@ -64,7 +66,7 @@ module.exports = class Transformer {
|
|
|
64
66
|
|
|
65
67
|
transform(mixed, args = {}) {
|
|
66
68
|
args.thunks ??= [];
|
|
67
|
-
this.args
|
|
69
|
+
this.#callArgs = { ...this.#config.args, ...args };
|
|
68
70
|
|
|
69
71
|
const transformed = Util.map(mixed, (data) => {
|
|
70
72
|
const thunks = Object.defineProperty({}, '$thunks', { value: args.thunks });
|
package/src/query/Query.js
CHANGED
|
@@ -39,19 +39,18 @@ module.exports = class Query {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
toCacheKey() {
|
|
42
|
-
return {
|
|
42
|
+
return JSON.stringify({
|
|
43
43
|
op: this.#query.op,
|
|
44
44
|
select: this.#query.select,
|
|
45
45
|
where: this.#query.where,
|
|
46
46
|
sort: this.#query.sort,
|
|
47
|
-
joins: this.#query.joins,
|
|
48
47
|
skip: this.#query.skip,
|
|
49
48
|
limit: this.#query.limit,
|
|
50
49
|
before: this.#query.before,
|
|
51
50
|
after: this.#query.after,
|
|
52
51
|
first: this.#query.first,
|
|
53
52
|
last: this.#query.last,
|
|
54
|
-
};
|
|
53
|
+
});
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
/**
|
|
@@ -134,14 +133,25 @@ module.exports = class Query {
|
|
|
134
133
|
query.batch = (op === 'findOne' || op === 'findMany') && Object.keys(query.where).length === 1 ? Object.keys(query.where)[0] : '__default__';
|
|
135
134
|
|
|
136
135
|
// Construct joins
|
|
136
|
+
const joinsByPath = {};
|
|
137
137
|
query.joins = [];
|
|
138
138
|
|
|
139
|
+
// Recursively search a join tree for the first join matching a target model key
|
|
140
|
+
const findJoin = (joins, modelKey) => {
|
|
141
|
+
for (const j of joins) {
|
|
142
|
+
if (j.to === modelKey) return j;
|
|
143
|
+
const found = findJoin(j.children, modelKey);
|
|
144
|
+
if (found) return found;
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
};
|
|
148
|
+
|
|
139
149
|
this.#model.walk(joinData, (node) => {
|
|
140
150
|
const { model, field, key, value, isLeaf, path, run } = node;
|
|
141
151
|
|
|
142
152
|
if (field.join) {
|
|
143
153
|
let isArray;
|
|
144
|
-
const join = { ...field.join, where: {} };
|
|
154
|
+
const join = { ...field.join, where: {}, children: [] };
|
|
145
155
|
|
|
146
156
|
if (run.length > 1) {
|
|
147
157
|
join.from = path.reduce((prev, curr, i) => {
|
|
@@ -153,12 +163,21 @@ module.exports = class Query {
|
|
|
153
163
|
|
|
154
164
|
join.isArray = isArray || model.resolvePath(join.from).isArray;
|
|
155
165
|
|
|
156
|
-
|
|
166
|
+
// Find the nearest ancestor FK join by scanning ancestor paths from closest to farthest.
|
|
167
|
+
// Joins reached through embedded fields have no FK ancestor and stay at the root level.
|
|
168
|
+
const parentJoin = path.slice(0, -1).reduceRight((found, _, i) => found || joinsByPath[path.slice(0, i + 1).join('.')], null);
|
|
169
|
+
|
|
170
|
+
if (parentJoin) {
|
|
171
|
+
parentJoin.children.push(join);
|
|
172
|
+
} else {
|
|
173
|
+
query.joins.push(join);
|
|
174
|
+
}
|
|
175
|
+
joinsByPath[path.join('.')] = join;
|
|
157
176
|
}
|
|
158
177
|
|
|
159
178
|
if (isLeaf) {
|
|
160
179
|
const $model = field.model || model;
|
|
161
|
-
const join = query.joins
|
|
180
|
+
const join = findJoin(query.joins, $model.key);
|
|
162
181
|
const $value = Util.map(value, el => (isGlob(el) ? globToRegex(el) : el));
|
|
163
182
|
const $$value = Array.isArray($value) ? { $in: $value } : $value;
|
|
164
183
|
const from = field.model ? join.from : key;
|
package/src/schema/Schema.js
CHANGED
|
@@ -16,7 +16,7 @@ const scalarKinds = [Kind.SCALAR_TYPE_DEFINITION, Kind.SCALAR_TYPE_EXTENSION];
|
|
|
16
16
|
const fieldKinds = [Kind.FIELD_DEFINITION];
|
|
17
17
|
const modelKinds = [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION].concat(interfaceKinds);
|
|
18
18
|
const allowedKinds = modelKinds.concat(fieldKinds).concat(Kind.DOCUMENT, Kind.NON_NULL_TYPE, Kind.NAMED_TYPE, Kind.LIST_TYPE, Kind.DIRECTIVE).concat(scalarKinds).concat(enumKinds);
|
|
19
|
-
const pipelines = ['validate', 'construct', 'restruct', 'instruct', 'normalize', 'serialize'
|
|
19
|
+
const pipelines = ['validate', 'construct', 'restruct', 'instruct', 'normalize', 'serialize'];
|
|
20
20
|
const createPipelines = ['validate', 'construct', 'instruct', 'normalize', 'serialize'];
|
|
21
21
|
const updatePipelines = ['validate', 'restruct', 'instruct', 'normalize', 'serialize'];
|
|
22
22
|
// const validatePipelines = ['validate', 'instruct', 'normalize', 'serialize'];
|
|
@@ -97,7 +97,7 @@ module.exports = class Schema {
|
|
|
97
97
|
try {
|
|
98
98
|
const $td = typeof td === 'string' ? parse(td) : td;
|
|
99
99
|
return $td;
|
|
100
|
-
} catch
|
|
100
|
+
} catch {
|
|
101
101
|
console.log(`Unable to parse typeDef (being ignored):\n${td}`); // eslint-disable-line
|
|
102
102
|
return null;
|
|
103
103
|
}
|
|
@@ -153,7 +153,6 @@ module.exports = class Schema {
|
|
|
153
153
|
create: new Transformer({ args: { schema: this.#schema, path: [] } }),
|
|
154
154
|
update: new Transformer({ args: { schema: this.#schema, path: [] } }),
|
|
155
155
|
where: new Transformer({ args: { schema: this.#schema, path: [] } }),
|
|
156
|
-
doc: new Transformer({ args: { schema: this.#schema, path: [] } }),
|
|
157
156
|
},
|
|
158
157
|
directives: {},
|
|
159
158
|
ignorePaths: [],
|
|
@@ -452,33 +451,6 @@ module.exports = class Schema {
|
|
|
452
451
|
|
|
453
452
|
$model.transformers.sort = $model.transformers.where.clone({ defaults: {} });
|
|
454
453
|
|
|
455
|
-
$model.transformers.doc.config({
|
|
456
|
-
shape: Object.values($model.fields).reduce((prev, curr) => {
|
|
457
|
-
const args = { model: $model, field: curr };
|
|
458
|
-
|
|
459
|
-
const rules = [
|
|
460
|
-
curr.name, // Rename key
|
|
461
|
-
a => Pipeline.$deserialize({ ...a, ...args, path: a.path.concat(curr.name) }),
|
|
462
|
-
];
|
|
463
|
-
|
|
464
|
-
if (curr.isArray) rules.unshift(({ value }) => (value == null ? value : Util.ensureArray(value)));
|
|
465
|
-
|
|
466
|
-
if (curr.isEmbedded) {
|
|
467
|
-
rules.unshift(a => Util.map(a.value, (value, i) => {
|
|
468
|
-
const path = a.path.concat(curr.name);
|
|
469
|
-
if (curr.isArray) path.push(i);
|
|
470
|
-
return curr.model.transformers.doc.transform(value, { ...args, query: a.query, context: a.context, path });
|
|
471
|
-
}));
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
return Object.assign(prev, { [curr.key]: rules });
|
|
475
|
-
}, {}),
|
|
476
|
-
defaults: Object.values($model.fields).reduce((prev, curr) => {
|
|
477
|
-
if (curr.defaultValue === undefined) return prev;
|
|
478
|
-
return Object.assign(prev, { [curr.key]: curr.defaultValue });
|
|
479
|
-
}, {}),
|
|
480
|
-
});
|
|
481
|
-
|
|
482
454
|
$model.transformers.validate.config({
|
|
483
455
|
strictSchema: true,
|
|
484
456
|
shape: Object.values($model.fields).reduce((prev, curr) => {
|
|
@@ -499,6 +471,21 @@ module.exports = class Schema {
|
|
|
499
471
|
}, {}),
|
|
500
472
|
});
|
|
501
473
|
|
|
474
|
+
// Deserialize/docs special case handling for performance
|
|
475
|
+
const docFields = Object.values($model.fields);
|
|
476
|
+
$model.docTransform = (doc) => {
|
|
477
|
+
if (doc == null) return doc;
|
|
478
|
+
const out = {};
|
|
479
|
+
for (const docField of docFields) {
|
|
480
|
+
let value = docField.key in doc ? doc[docField.key] : docField.defaultValue;
|
|
481
|
+
if (value === undefined) continue; // eslint-disable-line
|
|
482
|
+
if (docField.isArray) value = value == null ? value : Util.ensureArray(value);
|
|
483
|
+
if (docField.isEmbedded) value = Util.map(value, v => docField.model.docTransform(v));
|
|
484
|
+
out[docField.name] = value;
|
|
485
|
+
}
|
|
486
|
+
return out;
|
|
487
|
+
};
|
|
488
|
+
|
|
502
489
|
Util.traverse(Object.values($model.fields), (f, info) => {
|
|
503
490
|
const path = info.path.concat(f.name);
|
|
504
491
|
if (f.isEmbedded) return { value: Object.values(f.model.fields), info: { path } };
|
|
@@ -720,7 +707,6 @@ module.exports = class Schema {
|
|
|
720
707
|
construct: [AutoGraphPipelineEnum!]
|
|
721
708
|
restruct: [AutoGraphPipelineEnum!]
|
|
722
709
|
serialize: [AutoGraphPipelineEnum!]
|
|
723
|
-
deserialize: [AutoGraphPipelineEnum!]
|
|
724
710
|
validate: [AutoGraphPipelineEnum!]
|
|
725
711
|
|
|
726
712
|
# TEMP TO APPEASE TRANSITION
|
|
@@ -903,7 +889,7 @@ module.exports = class Schema {
|
|
|
903
889
|
`,
|
|
904
890
|
resolvers: {
|
|
905
891
|
Node: {
|
|
906
|
-
__resolveType: (doc, args, context, info) => doc.__typename,
|
|
892
|
+
__resolveType: (doc, args, context, info) => doc.__typename,
|
|
907
893
|
},
|
|
908
894
|
...queryModels.reduce((prev, model) => {
|
|
909
895
|
return Object.assign(prev, {
|
|
@@ -926,7 +912,7 @@ module.exports = class Schema {
|
|
|
926
912
|
const model = schema.models[modelName];
|
|
927
913
|
return context[schema.namespace].resolver.match(model).id(id).info(info).one().then((result) => {
|
|
928
914
|
if (result == null) return result;
|
|
929
|
-
result.__typename = modelName;
|
|
915
|
+
result.__typename = modelName;
|
|
930
916
|
return result;
|
|
931
917
|
});
|
|
932
918
|
},
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
const API = jest.requireActual('../Emitter');
|
|
2
|
-
const { mergeDeep } = require('../../service/AppService');
|
|
3
|
-
|
|
4
|
-
const { emit } = API;
|
|
5
|
-
|
|
6
|
-
API.emit = (eventName, data) => {
|
|
7
|
-
if (API.cloneData) data = { ...data, query: mergeDeep({}, data.query) };
|
|
8
|
-
return emit.call(API, eventName, data);
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
module.exports = API;
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* We need to export Pipeline as a POJO so jest can spy on it
|
|
3
|
-
* This is because all the methods are defined using Object.defineProperty
|
|
4
|
-
*/
|
|
5
|
-
const Pipeline = jest.requireActual('../Pipeline');
|
|
6
|
-
const Util = require('@coderich/util');
|
|
7
|
-
|
|
8
|
-
const API = {};
|
|
9
|
-
|
|
10
|
-
Pipeline.resolve = (params, pipeline) => {
|
|
11
|
-
const transformers = params.field.pipelines[pipeline] || [];
|
|
12
|
-
|
|
13
|
-
return transformers.reduce((value, t) => {
|
|
14
|
-
return Util.uvl(API[t]({ ...params, value }), value);
|
|
15
|
-
}, params.value);
|
|
16
|
-
|
|
17
|
-
// return Util.pipeline(transformers.map(t => (value) => {
|
|
18
|
-
// return API[t]({ ...params, value });
|
|
19
|
-
// }), params.value);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
Object.getOwnPropertyNames(Pipeline).reduce((prev, key) => {
|
|
23
|
-
return Object.assign(prev, { [key]: Pipeline[key] });
|
|
24
|
-
}, API);
|
|
25
|
-
|
|
26
|
-
// For those defined outside of Pipeline.js itself
|
|
27
|
-
API.define = (key, ...args) => {
|
|
28
|
-
Pipeline.define(key, ...args);
|
|
29
|
-
API[key] = Pipeline[key];
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
module.exports = API;
|