@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.
- 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 +84 -101
- package/src/data/Resolver.js +304 -0
- package/src/data/Transaction.js +49 -0
- package/src/query/Query.js +159 -335
- package/src/query/QueryBuilder.js +228 -114
- package/src/query/QueryResolver.js +110 -205
- package/src/query/QueryResolverTransaction.js +16 -0
- package/src/schema/Schema.js +602 -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 -161
- package/src/data/Field.js +0 -83
- package/src/data/Model.js +0 -214
- 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,111 +0,0 @@
|
|
|
1
|
-
const { get, set, uniq, flattenDeep } = require('lodash');
|
|
2
|
-
const { keyPaths, ensureArray, isPlainObject } = require('../service/app.service');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The where clause may contain attributes that are NOT in the model
|
|
6
|
-
* This can happen because the where clause reaches into the schema via refs/virtual refs
|
|
7
|
-
*/
|
|
8
|
-
exports.resolveWhereClause = (query) => {
|
|
9
|
-
const { resolver, model, match: where = {} } = query.toObject();
|
|
10
|
-
const shape = model.getShape('create', 'where');
|
|
11
|
-
|
|
12
|
-
const $where = Object.entries(where).reduce((prev, [from, value]) => {
|
|
13
|
-
const el = shape.find(s => s.from === from);
|
|
14
|
-
if (!el) return prev; // There's no knowing what this could be
|
|
15
|
-
|
|
16
|
-
const { isVirtual, isEmbedded, modelRef, virtualRef } = el.field.toObject();
|
|
17
|
-
|
|
18
|
-
if (isVirtual) {
|
|
19
|
-
const ids = Promise.all(ensureArray(value).map(v => resolver.match(modelRef).where(isPlainObject(v) ? v : { id: v }).many().then(docs => docs.map(doc => doc[virtualRef])))).then(results => uniq(flattenDeep(results)));
|
|
20
|
-
return Object.assign(prev, { id: ids });
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (modelRef && !isEmbedded) {
|
|
24
|
-
const ids = Promise.all(ensureArray(value).map(v => (isPlainObject(v) ? resolver.match(modelRef).where(v).many().then(docs => docs.map(doc => doc.id)) : Promise.resolve(v)))).then(results => uniq(flattenDeep(results)));
|
|
25
|
-
return Object.assign(prev, { [from]: ids });
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return Object.assign(prev, { [from]: value });
|
|
29
|
-
}, {});
|
|
30
|
-
|
|
31
|
-
// Resolve
|
|
32
|
-
return Promise.all(keyPaths($where).map(async (path) => {
|
|
33
|
-
const $value = await get($where, path);
|
|
34
|
-
return { path, $value };
|
|
35
|
-
})).then((results) => {
|
|
36
|
-
return results.reduce((prev, { path, $value }) => {
|
|
37
|
-
if (Array.isArray($value) && $value.length === 1) [$value] = $value;
|
|
38
|
-
return set(prev, path, $value);
|
|
39
|
-
}, {});
|
|
40
|
-
});
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
exports.resolveSortBy = (query) => {
|
|
44
|
-
const { model, sort = {} } = query.toObject();
|
|
45
|
-
const shape = model.getShape('create', 'sortBy');
|
|
46
|
-
const $sort = model.shapeObject(shape, sort, query);
|
|
47
|
-
|
|
48
|
-
// Because normalize casts the value (sometimes to an array) need special handling
|
|
49
|
-
keyPaths($sort).forEach((path) => {
|
|
50
|
-
const v = get($sort, path);
|
|
51
|
-
const val = Array.isArray(v) ? v[0] : v;
|
|
52
|
-
const [attr] = path.split('.');
|
|
53
|
-
const field = model.getField(attr);
|
|
54
|
-
const join = field.getJoinInfo();
|
|
55
|
-
|
|
56
|
-
// If you need to sort by something that's in another FK document
|
|
57
|
-
if (join) {
|
|
58
|
-
delete $sort[attr];
|
|
59
|
-
query.joins(Object.assign(join, { as: `_.${field}`, left: true }));
|
|
60
|
-
path = `_.${path}`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
set($sort, path, val.toLowerCase() === 'asc' ? 1 : -1);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
return $sort;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
exports.resolveReferentialIntegrity = (query) => {
|
|
70
|
-
const { id, model, resolver, transaction } = query.toObject();
|
|
71
|
-
const txn = resolver.transaction(transaction);
|
|
72
|
-
|
|
73
|
-
return new Promise((resolve, reject) => {
|
|
74
|
-
try {
|
|
75
|
-
model.referentialIntegrity().forEach(({ model: ref, field, fieldRef, isArray, op }) => {
|
|
76
|
-
const fieldStr = fieldRef ? `${field}.${fieldRef}` : `${field}`;
|
|
77
|
-
const $where = { [fieldStr]: id };
|
|
78
|
-
|
|
79
|
-
switch (op) {
|
|
80
|
-
case 'cascade': {
|
|
81
|
-
if (isArray) {
|
|
82
|
-
txn.match(ref).where($where).pull(fieldStr, id);
|
|
83
|
-
} else {
|
|
84
|
-
txn.match(ref).where($where).remove();
|
|
85
|
-
}
|
|
86
|
-
break;
|
|
87
|
-
}
|
|
88
|
-
case 'nullify': {
|
|
89
|
-
txn.match(ref).where($where).save({ [fieldStr]: null });
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
case 'restrict': {
|
|
93
|
-
txn.match(ref).where($where).count().then(count => (count ? reject(new Error('Restricted')) : count));
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
case 'defer': {
|
|
97
|
-
// Defer to the embedded object
|
|
98
|
-
// Marks the field as an onDelete candidate otherwise it (and the embedded object) will get skipped
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
default: throw new Error(`Unknown onDelete operator: '${op}'`);
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// Execute the transaction
|
|
106
|
-
txn.run().then(results => resolve(results)).catch(e => reject(e));
|
|
107
|
-
} catch (e) {
|
|
108
|
-
txn.rollback().then(() => reject(e)).catch(err => reject(err));
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
};
|
package/src/service/.DS_Store
DELETED
|
Binary file
|
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const PicoMatch = require('picomatch');
|
|
3
|
-
const FillRange = require('fill-range');
|
|
4
|
-
const DeepMerge = require('deepmerge');
|
|
5
|
-
const { ObjectId } = require('mongodb');
|
|
6
|
-
const ObjectHash = require('object-hash');
|
|
7
|
-
|
|
8
|
-
// const combineMerge = (target, source, options) => {
|
|
9
|
-
// const destination = target.slice();
|
|
10
|
-
|
|
11
|
-
// source.forEach((item, index) => {
|
|
12
|
-
// if (typeof destination[index] === 'undefined') {
|
|
13
|
-
// destination[index] = options.cloneUnlessOtherwiseSpecified(item, options);
|
|
14
|
-
// } else if (options.isMergeableObject(item)) {
|
|
15
|
-
// destination[index] = DeepMerge(target[index], item, options);
|
|
16
|
-
// } else if (target.indexOf(item) === -1) {
|
|
17
|
-
// destination.push(item);
|
|
18
|
-
// }
|
|
19
|
-
// });
|
|
20
|
-
|
|
21
|
-
// return destination;
|
|
22
|
-
// };
|
|
23
|
-
|
|
24
|
-
const smartMerge = (target, source, options) => {
|
|
25
|
-
return source;
|
|
26
|
-
// const [el] = target;
|
|
27
|
-
// if (!el || exports.isScalarValue(el)) return source;
|
|
28
|
-
// return combineMerge(target, source, options);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
exports.id = '3d896496-02a3-4ee5-8e42-2115eb215f7e';
|
|
32
|
-
exports.ucFirst = string => string.charAt(0).toUpperCase() + string.slice(1);
|
|
33
|
-
exports.lcFirst = string => string.charAt(0).toLowerCase() + string.slice(1);
|
|
34
|
-
exports.isNumber = value => typeof value === 'number' && Number.isFinite(value);
|
|
35
|
-
exports.isBasicObject = obj => obj != null && typeof obj === 'object' && !(ObjectId.isValid(obj)) && !(obj instanceof Date) && typeof (obj.then) !== 'function';
|
|
36
|
-
exports.isPlainObject = obj => exports.isBasicObject(obj) && !Array.isArray(obj);
|
|
37
|
-
exports.isScalarValue = value => typeof value !== 'object' && typeof value !== 'function';
|
|
38
|
-
exports.isScalarDataType = value => ['String', 'Float', 'Int', 'Boolean', 'DateTime'].indexOf(value) > -1;
|
|
39
|
-
exports.isIdValue = value => exports.isScalarValue(value) || value instanceof ObjectId;
|
|
40
|
-
exports.mergeDeep = (...args) => DeepMerge.all(args, { isMergeableObject: obj => (exports.isPlainObject(obj) || Array.isArray(obj)), arrayMerge: smartMerge });
|
|
41
|
-
exports.uniq = arr => [...new Set(arr.map(a => `${a}`))];
|
|
42
|
-
exports.timeout = ms => new Promise(res => setTimeout(res, ms));
|
|
43
|
-
exports.hashObject = obj => ObjectHash(obj, { respectType: false, respectFunctionNames: false, respectFunctionProperties: false, unorderedArrays: true, ignoreUnknown: true, replacer: r => (r instanceof ObjectId ? `${r}` : r) });
|
|
44
|
-
exports.globToRegex = (glob, options = {}) => PicoMatch.makeRe(glob, { ...options, expandRange: (a, b) => `(${FillRange(a, b, { toRegex: true })})` });
|
|
45
|
-
exports.globToRegexp = (glob, options = {}) => PicoMatch.toRegex(exports.globToRegex(glob, options));
|
|
46
|
-
exports.toGUID = (model, id) => Buffer.from(`${model},${`${id}`}`).toString('base64');
|
|
47
|
-
exports.fromGUID = guid => Buffer.from(`${guid}`, 'base64').toString('ascii').split(',');
|
|
48
|
-
exports.guidToId = (autograph, guid) => (autograph.legacyMode ? guid : exports.uvl(exports.fromGUID(guid)[1], guid));
|
|
49
|
-
exports.ensureArray = a => (Array.isArray(a) ? a : [a].filter(el => el !== undefined));
|
|
50
|
-
exports.uvl = (...values) => values.reduce((prev, value) => (prev === undefined ? value : prev), undefined);
|
|
51
|
-
exports.nvl = (...values) => values.reduce((prev, value) => (prev === null ? value : prev), null);
|
|
52
|
-
exports.stripObjectNulls = obj => Object.entries(obj).reduce((prev, [key, value]) => (value == null ? prev : Object.assign(prev, { [key]: value })), {});
|
|
53
|
-
exports.stripObjectUndefineds = obj => Object.entries(obj).reduce((prev, [key, value]) => (value === undefined ? prev : Object.assign(prev, { [key]: value })), {});
|
|
54
|
-
exports.pushIt = (arr, it) => arr[arr.push(it) - 1];
|
|
55
|
-
exports.toKeyObj = obj => exports.keyPaths(obj).reduce((prev, path) => Object.assign(prev, { [path]: _.get(obj, path) }), {});
|
|
56
|
-
|
|
57
|
-
exports.getGQLReturnType = (returnType) => {
|
|
58
|
-
const typeMap = { array: /^\[.+\].?$/, connection: /.+Connection!?$/, number: /^(Int|Float)!?$/, scalar: /.*/ };
|
|
59
|
-
return Object.entries(typeMap).find(([type, pattern]) => returnType.match(pattern))[0];
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
exports.removeUndefinedDeep = (obj) => {
|
|
63
|
-
return exports.unravelObject(exports.keyPaths(obj).reduce((prev, path) => {
|
|
64
|
-
const value = _.get(obj, path);
|
|
65
|
-
if (value === undefined) return prev;
|
|
66
|
-
return Object.assign(prev, { [path]: value });
|
|
67
|
-
}, {}));
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
exports.renameObjectKey = (obj, oldKey, newKey) => {
|
|
71
|
-
if (oldKey !== newKey) {
|
|
72
|
-
Object.defineProperty(obj, newKey, Object.getOwnPropertyDescriptor(obj, oldKey));
|
|
73
|
-
delete obj[oldKey];
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
exports.deleteKeys = (obj, keys) => {
|
|
78
|
-
if (Array.isArray(obj)) obj.map(item => exports.deleteKeys(item, keys));
|
|
79
|
-
else if (obj === Object(obj)) { keys.forEach(key => delete obj[key]); Object.values(obj).forEach(v => exports.deleteKeys(v, keys)); }
|
|
80
|
-
return obj;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
exports.getDeep = (obj, path, defaultValue) => {
|
|
84
|
-
const [prop, ...rest] = path.split('.');
|
|
85
|
-
const normalize = data => (Array.isArray(data) ? _.flatten(data) : data);
|
|
86
|
-
|
|
87
|
-
return exports.map(obj, (o) => {
|
|
88
|
-
const value = o[prop];
|
|
89
|
-
if (rest.length) return normalize(exports.map(value, v => exports.getDeep(v, rest.join('.'), defaultValue)));
|
|
90
|
-
return value === undefined ? defaultValue : value;
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
exports.map = (mixed, fn) => {
|
|
95
|
-
if (mixed == null) return mixed;
|
|
96
|
-
const isArray = Array.isArray(mixed);
|
|
97
|
-
const arr = isArray ? mixed : [mixed];
|
|
98
|
-
const results = isArray ? arr.map((...args) => fn(...args)) : arr.map(el => fn(el));
|
|
99
|
-
return isArray ? results : results[0];
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
exports.mapPromise = (mixed, fn) => {
|
|
103
|
-
const map = exports.map(mixed, fn);
|
|
104
|
-
return Array.isArray(map) ? Promise.all(map) : Promise.resolve(map);
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
exports.castCmp = (type, value) => {
|
|
108
|
-
switch (type) {
|
|
109
|
-
case 'String': {
|
|
110
|
-
return `${value}`;
|
|
111
|
-
}
|
|
112
|
-
case 'Float': case 'Number': {
|
|
113
|
-
const num = Number(value);
|
|
114
|
-
if (!Number.isNaN(num)) return num;
|
|
115
|
-
return value;
|
|
116
|
-
}
|
|
117
|
-
case 'Int': {
|
|
118
|
-
const num = Number(value);
|
|
119
|
-
if (!Number.isNaN(num)) return parseInt(value, 10);
|
|
120
|
-
return value;
|
|
121
|
-
}
|
|
122
|
-
case 'Boolean': {
|
|
123
|
-
if (value === 'true') return true;
|
|
124
|
-
if (value === 'false') return false;
|
|
125
|
-
return value;
|
|
126
|
-
}
|
|
127
|
-
default: {
|
|
128
|
-
return value;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
exports.objectContaining = (a, b) => {
|
|
134
|
-
if (a === b) return true;
|
|
135
|
-
|
|
136
|
-
if (exports.isPlainObject(b)) {
|
|
137
|
-
return exports.keyPathLeafs(b).every((leaf) => {
|
|
138
|
-
const $a = _.get(a, leaf, { a: 'a' });
|
|
139
|
-
const $b = _.get(b, leaf, { b: 'b' });
|
|
140
|
-
if (Array.isArray($b)) return $b.some(bb => exports.ensureArray($a).some(aa => exports.objectContaining(aa, bb)));
|
|
141
|
-
if (exports.isScalarValue($a) && exports.isScalarValue($b)) return PicoMatch.isMatch(`${$a}`, `${$b}`, { nocase: true });
|
|
142
|
-
return exports.hashObject($a) === exports.hashObject($b);
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return exports.hashObject(a) === exports.hashObject(b);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Transform an object with dot.notation keys into an expanded object.
|
|
151
|
-
* eg. { 'user.name': 'richard' } => { user: { name: 'richard' } }
|
|
152
|
-
*/
|
|
153
|
-
exports.unravelObject = (obj) => {
|
|
154
|
-
if (!exports.isPlainObject(obj)) return obj;
|
|
155
|
-
|
|
156
|
-
return exports.keyPaths(obj).reduce((prev, path) => {
|
|
157
|
-
const splitPath = path.split('.');
|
|
158
|
-
|
|
159
|
-
return _.set(prev, path, _.get(obj, path, splitPath.reduce((val, p, i) => {
|
|
160
|
-
if (val !== undefined) return val;
|
|
161
|
-
const tuple = [splitPath.slice(0, i + 1).join('.'), splitPath.slice(i + 1).join('.')];
|
|
162
|
-
return _.get(obj, tuple);
|
|
163
|
-
}, undefined)));
|
|
164
|
-
}, {});
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
exports.unrollGuid = (autograph, model, data) => {
|
|
168
|
-
if (autograph.legacyMode) return data;
|
|
169
|
-
model = autograph.resolver.toModel(model);
|
|
170
|
-
const fields = model.getDataRefFields().map(field => field.getName());
|
|
171
|
-
|
|
172
|
-
return exports.map(data, (doc) => {
|
|
173
|
-
return Object.entries(doc).reduce((prev, [key, value]) => {
|
|
174
|
-
return Object.assign(prev, { [key]: (fields.indexOf(key) > -1 ? exports.map(value, v => exports.guidToId(autograph, v)) : value) });
|
|
175
|
-
}, {});
|
|
176
|
-
});
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
exports.keyPaths = (obj = {}, keys = [], path) => {
|
|
180
|
-
return Object.entries(obj).reduce((prev, [key, value]) => {
|
|
181
|
-
const keyPath = path ? `${path}.${key}` : key;
|
|
182
|
-
if (exports.isPlainObject(value) && Object.keys(value).length) return exports.keyPaths(value, prev, keyPath);
|
|
183
|
-
return prev.concat(keyPath);
|
|
184
|
-
}, keys);
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
exports.keyPathLeafs = (obj, keys, path) => {
|
|
188
|
-
return exports.keyPaths(obj, keys, path).sort().reverse().filter((leaf, i, arr) => arr.findIndex(el => el.indexOf(leaf) === 0) === i);
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
// exports.keyPaths = (obj, keys = [], path) => {
|
|
192
|
-
// return Object.entries(obj).reduce((prev, [key, value]) => {
|
|
193
|
-
// const keyPath = path ? `${path}.${key}` : key;
|
|
194
|
-
// prev.push(keyPath);
|
|
195
|
-
// if (exports.isPlainObject(value)) return exports.keyPaths(value, prev, keyPath);
|
|
196
|
-
// return prev;
|
|
197
|
-
// }, keys);
|
|
198
|
-
// };
|
|
199
|
-
|
|
200
|
-
exports.queryPaths = (model, obj) => {
|
|
201
|
-
return exports.keyPaths(obj).filter(path => path.indexOf('edges.cursor') === -1).map((path) => {
|
|
202
|
-
return path.replace(/edges|node/gi, '').replace(/^\.+|\.+$/g, '');
|
|
203
|
-
}).filter(a => a);
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
exports.promiseChain = (promises) => {
|
|
207
|
-
return promises.reduce((chain, promise) => {
|
|
208
|
-
return chain.then(chainResults => promise([...chainResults]).then(promiseResult => [...chainResults, promiseResult]));
|
|
209
|
-
}, Promise.resolve([]));
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
exports.promiseRetry = (fn, ms, retries = 5, cond = e => e) => {
|
|
213
|
-
return fn().catch((e) => {
|
|
214
|
-
if (!retries || !cond(e)) throw e;
|
|
215
|
-
return exports.timeout(ms).then(() => exports.promiseRetry(fn, ms, --retries, cond));
|
|
216
|
-
});
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
exports.proxyPromise = (promise) => {
|
|
220
|
-
return new Proxy(promise, {
|
|
221
|
-
get(target, prop, rec) {
|
|
222
|
-
const value = Reflect.get(target, prop, rec);
|
|
223
|
-
if (typeof value === 'function') return value.bind(target);
|
|
224
|
-
return (...args) => promise.then(result => result[prop](...args));
|
|
225
|
-
},
|
|
226
|
-
});
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
exports.proxyDeep = (obj, handler, proxyMap = new WeakMap(), path = '') => {
|
|
230
|
-
obj = obj || {};
|
|
231
|
-
if (proxyMap.has(obj)) return proxyMap.get(obj);
|
|
232
|
-
|
|
233
|
-
const proxy = new Proxy(Object.entries(obj).reduce((prev, [key, value]) => {
|
|
234
|
-
if (Array.isArray(value)) return Object.assign(prev, { [key]: value.map(v => (exports.isPlainObject(v) ? exports.proxyDeep(v, handler, proxyMap, path) : v)) });
|
|
235
|
-
if (exports.isPlainObject(value)) return Object.assign(prev, { [key]: exports.proxyDeep(value, handler, proxyMap, path) });
|
|
236
|
-
return Object.assign(prev, { [key]: value });
|
|
237
|
-
}, {}), handler);
|
|
238
|
-
|
|
239
|
-
const finalProxy = Object.defineProperty(proxy, 'toObject', {
|
|
240
|
-
get() {
|
|
241
|
-
return (getMap = new WeakMap()) => {
|
|
242
|
-
if (getMap.has(this)) return getMap.get(this);
|
|
243
|
-
|
|
244
|
-
const plainObject = Object.entries(this).reduce((prev, [key, value]) => {
|
|
245
|
-
if (Array.isArray(value)) return Object.assign(prev, { [key]: value.map(v => (v.toObject ? v.toObject(getMap) : v)) });
|
|
246
|
-
return Object.assign(prev, { [key]: _.get(value, 'toObject') ? value.toObject(getMap) : value });
|
|
247
|
-
}, {});
|
|
248
|
-
|
|
249
|
-
getMap.set(this, plainObject);
|
|
250
|
-
|
|
251
|
-
return plainObject;
|
|
252
|
-
};
|
|
253
|
-
},
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
proxyMap.set(obj, finalProxy);
|
|
257
|
-
|
|
258
|
-
return finalProxy;
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
exports.resolveDataObject = (obj) => {
|
|
262
|
-
return Promise.all(Object.keys(obj).map(async (key) => {
|
|
263
|
-
const value = await obj[key];
|
|
264
|
-
return { key, value };
|
|
265
|
-
})).then((results) => {
|
|
266
|
-
return results.reduce((prev, { key, value }) => {
|
|
267
|
-
return Object.assign(prev, { [key]: value });
|
|
268
|
-
}, {});
|
|
269
|
-
});
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
exports.seek = (obj, paths, hint) => {
|
|
273
|
-
// We first do a normal get
|
|
274
|
-
const value = _.get(obj, paths);
|
|
275
|
-
if (!hint || value !== undefined) return value;
|
|
276
|
-
|
|
277
|
-
// Normalize paths & hint for traversal
|
|
278
|
-
const $paths = Array.isArray(paths) ? paths : paths.split('.');
|
|
279
|
-
const $hint = exports.unravelObject(hint);
|
|
280
|
-
|
|
281
|
-
// Traverse paths and get as close to the value as possible
|
|
282
|
-
const { currentValue, pathsToGo } = $paths.reduce((prev, path, i, arr) => {
|
|
283
|
-
if (prev.currentValue === undefined) return prev;
|
|
284
|
-
if (!Object.prototype.hasOwnProperty.call(prev.currentValue, path)) return prev;
|
|
285
|
-
prev.currentValue = prev.currentValue[path];
|
|
286
|
-
prev.pathsToGo = arr.slice(i + 1);
|
|
287
|
-
return prev;
|
|
288
|
-
}, { currentValue: obj, pathsToGo: $paths });
|
|
289
|
-
|
|
290
|
-
// Only if we hit an array can we continue
|
|
291
|
-
if (!Array.isArray(currentValue)) return undefined;
|
|
292
|
-
|
|
293
|
-
// If we got to the last segment we need the hint in order to verify
|
|
294
|
-
const lastPath = Boolean(pathsToGo.length === 1);
|
|
295
|
-
const arr = lastPath ? currentValue.filter(v => exports.objectContaining(v, $hint)) : currentValue;
|
|
296
|
-
|
|
297
|
-
// We keep going, recursive, till we find the first value
|
|
298
|
-
return arr.reduce((prev, v) => prev || exports.seek(v, pathsToGo, $hint), undefined);
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
exports.deseek = (shape, obj, paths, hint) => {
|
|
302
|
-
// Normalize paths
|
|
303
|
-
const $paths = (Array.isArray(paths) ? paths : paths.split('.')).map((path) => {
|
|
304
|
-
const item = shape.find(s => s.to === path); // Deserializing from unknown to expected
|
|
305
|
-
return item ? item.from : path;
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
// Normalize hint
|
|
309
|
-
const $hint = Object.entries(exports.toKeyObj(hint)).reduce((prev, [key, value]) => {
|
|
310
|
-
const segments = key.split('.').map((path) => {
|
|
311
|
-
const item = shape.find(s => s.to === path); // Deserializing from unknown to expected
|
|
312
|
-
return item ? item.from : path;
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
return Object.assign(prev, { [segments.join('.')]: value });
|
|
316
|
-
}, {});
|
|
317
|
-
|
|
318
|
-
return exports.seek(obj, $paths, $hint);
|
|
319
|
-
};
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
const GraphqlFields = require('graphql-fields');
|
|
2
|
-
|
|
3
|
-
const resolveQuery = (method, name, resolver, model) => {
|
|
4
|
-
return async (root, args, context, info) => {
|
|
5
|
-
const queryInfo = { fields: GraphqlFields(info, {}, { processArguments: true }) };
|
|
6
|
-
|
|
7
|
-
switch (method) {
|
|
8
|
-
case 'get': return resolver.get(context, model, args, true, queryInfo);
|
|
9
|
-
case 'find': {
|
|
10
|
-
return {
|
|
11
|
-
edges: () => resolver.query(context, model, args, queryInfo),
|
|
12
|
-
pageInfo: () => resolver.query(context, model, args, queryInfo),
|
|
13
|
-
count: () => resolver.count(context, model, args, queryInfo),
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
case 'count': return resolver.count(context, model, args, queryInfo);
|
|
17
|
-
case 'create': return resolver.create(context, model, args, queryInfo);
|
|
18
|
-
case 'update': return resolver.update(context, model, args, queryInfo);
|
|
19
|
-
case 'delete': return resolver.delete(context, model, args, queryInfo);
|
|
20
|
-
default: return null;
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// APIs
|
|
26
|
-
exports.makeCreateAPI = (name, model, parent) => {
|
|
27
|
-
let gql = '';
|
|
28
|
-
|
|
29
|
-
if (model.hasGQLScope('c')) {
|
|
30
|
-
const meta = model.getMeta() ? `meta: ${model.getMeta()}` : '';
|
|
31
|
-
gql += `create${name}(input: ${model.getName()}InputCreate! ${meta}): ${model.getName()}!`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return gql;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
exports.makeReadAPI = (name, model, parent) => {
|
|
38
|
-
let gql = '';
|
|
39
|
-
|
|
40
|
-
if (model.hasGQLScope('r')) {
|
|
41
|
-
gql += `
|
|
42
|
-
get${name}(id: ID!): ${model.getName()}
|
|
43
|
-
find${name}(
|
|
44
|
-
where: ${model.getName()}InputWhere
|
|
45
|
-
sortBy: ${model.getName()}InputSort
|
|
46
|
-
limit: Int
|
|
47
|
-
skip: Int
|
|
48
|
-
first: Int
|
|
49
|
-
after: String
|
|
50
|
-
last: Int
|
|
51
|
-
before: String
|
|
52
|
-
): ${model.getName()}Connection!
|
|
53
|
-
`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return gql;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
exports.makeUpdateAPI = (name, model, parent) => {
|
|
60
|
-
let gql = '';
|
|
61
|
-
|
|
62
|
-
if (model.hasGQLScope('u')) {
|
|
63
|
-
const meta = model.getMeta() ? `meta: ${model.getMeta()}` : '';
|
|
64
|
-
gql += `update${name}(id: ID! input: ${model.getName()}InputUpdate ${meta}): ${model.getName()}!`;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return gql;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
exports.makeDeleteAPI = (name, model, parent) => {
|
|
71
|
-
let gql = '';
|
|
72
|
-
|
|
73
|
-
if (model.hasGQLScope('d')) {
|
|
74
|
-
const meta = model.getMeta() ? `meta: ${model.getMeta()}` : '';
|
|
75
|
-
gql += `delete${name}(id: ID! ${meta}): ${model.getName()}!`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return gql;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
exports.makeSubscriptionAPI = (name, model, parent) => {
|
|
82
|
-
let gql = '';
|
|
83
|
-
|
|
84
|
-
if (model.hasGQLScope('s')) {
|
|
85
|
-
gql += `${name} (
|
|
86
|
-
on: [SubscriptionCrudEnum!]! = [create, update, delete]
|
|
87
|
-
filter: ${name}SubscriptionInputFilter
|
|
88
|
-
): ${name}SubscriptionPayload!`;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return gql;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// Resolvers
|
|
95
|
-
exports.makeQueryResolver = (name, model, resolver) => {
|
|
96
|
-
const obj = {};
|
|
97
|
-
|
|
98
|
-
if (model.hasGQLScope('r')) {
|
|
99
|
-
obj[`get${name}`] = resolveQuery('get', name, resolver, model);
|
|
100
|
-
obj[`find${name}`] = resolveQuery('find', name, resolver, model);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return obj;
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
exports.makeMutationResolver = (name, model, resolver) => {
|
|
107
|
-
const obj = {};
|
|
108
|
-
|
|
109
|
-
if (model.hasGQLScope('c')) obj[`create${name}`] = resolveQuery('create', name, resolver, model);
|
|
110
|
-
if (model.hasGQLScope('u')) obj[`update${name}`] = resolveQuery('update', name, resolver, model);
|
|
111
|
-
if (model.hasGQLScope('d')) obj[`delete${name}`] = resolveQuery('delete', name, resolver, model);
|
|
112
|
-
|
|
113
|
-
return obj;
|
|
114
|
-
};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
const QueryService = require('../query/QueryService');
|
|
2
|
-
const EventEmitter = require('../core/EventEmitter');
|
|
3
|
-
const { ucFirst } = require('./app.service');
|
|
4
|
-
|
|
5
|
-
// Event emitters
|
|
6
|
-
const eventEmitter = new EventEmitter().setMaxListeners(100);
|
|
7
|
-
const systemEvent = new EventEmitter().setMaxListeners(100).on('system', async (event, next) => {
|
|
8
|
-
const { type, data } = event;
|
|
9
|
-
next(await eventEmitter.emit(type, data)); // Return result from user-defined middleware
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const makeEvent = (mixed) => {
|
|
13
|
-
const { query } = mixed;
|
|
14
|
-
const event = query.toObject();
|
|
15
|
-
event.query = query;
|
|
16
|
-
return event;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const makeMiddleware = () => {
|
|
20
|
-
return (mixed) => {
|
|
21
|
-
const { query } = mixed;
|
|
22
|
-
const { model, native, sort, match, batch } = query.toObject();
|
|
23
|
-
|
|
24
|
-
return new Promise(async (resolve) => {
|
|
25
|
-
if (!native) {
|
|
26
|
-
const whereShape = model.getShape('create', 'where');
|
|
27
|
-
const $where = batch ? match : await QueryService.resolveWhereClause(query);
|
|
28
|
-
const $$where = model.shapeObject(whereShape, $where, query);
|
|
29
|
-
query.match($$where);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (sort && Object.keys(sort).length) {
|
|
33
|
-
query.$sort(QueryService.resolveSortBy(query));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
resolve();
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
exports.createSystemEvent = (name, mixed = {}, thunk = () => {}) => {
|
|
43
|
-
let event = mixed;
|
|
44
|
-
let middleware = () => Promise.resolve();
|
|
45
|
-
const type = ucFirst(name);
|
|
46
|
-
|
|
47
|
-
if (name !== 'Response') {
|
|
48
|
-
event = makeEvent(mixed);
|
|
49
|
-
middleware = makeMiddleware();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return systemEvent.emit('system', { type: `pre${type}`, data: event }).then(async (result) => {
|
|
53
|
-
if (result !== undefined) return result; // Allowing middleware to dictate result
|
|
54
|
-
return middleware(mixed).then(thunk);
|
|
55
|
-
}).then((result) => {
|
|
56
|
-
event.result = result;
|
|
57
|
-
if (event.crud === 'create') event.doc = event.query.toObject().doc;
|
|
58
|
-
return systemEvent.emit('system', { type: `post${type}`, data: event }).then((postResult = result) => postResult);
|
|
59
|
-
}).then((result) => {
|
|
60
|
-
if (name === 'Response') return result;
|
|
61
|
-
event.result = result;
|
|
62
|
-
return exports.createSystemEvent('Response', event, (finalResult = result) => finalResult);
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
exports.eventEmitter = eventEmitter;
|