@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
package/src/data/Field.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
const { isEmpty } = require('lodash');
|
|
2
|
-
const Type = require('./Type');
|
|
3
|
-
const Field = require('../graphql/ast/Field');
|
|
4
|
-
const Pipeline = require('./Pipeline');
|
|
5
|
-
|
|
6
|
-
module.exports = class extends Field {
|
|
7
|
-
constructor(model, field) {
|
|
8
|
-
super(model, JSON.parse(JSON.stringify((field.getAST()))));
|
|
9
|
-
this.type = new Type(field);
|
|
10
|
-
this.model = model;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
getStructures() {
|
|
14
|
-
// Grab structures from the underlying type
|
|
15
|
-
const structures = this.type.getStructures();
|
|
16
|
-
const { type, isPrimaryKeyId, isIdField, isRequired, isPersistable, isVirtual, isEmbedded, modelRef } = this.props;
|
|
17
|
-
|
|
18
|
-
// Structures defined on the field
|
|
19
|
-
const $structures = Object.entries(this.getDirectiveArgs('field', {})).reduce((prev, [key, value]) => {
|
|
20
|
-
if (!Array.isArray(value)) value = [value];
|
|
21
|
-
if (key === 'validate') prev.validators.push(...value.map(t => Pipeline[t]));
|
|
22
|
-
if (key === 'instruct') prev.instructs.push(...value.map(t => Pipeline[t]));
|
|
23
|
-
if (key === 'restruct') prev.restructs.push(...value.map(t => Pipeline[t]));
|
|
24
|
-
if (key === 'destruct') prev.destructs.push(...value.map(t => Pipeline[t]));
|
|
25
|
-
if (key === 'construct') prev.constructs.push(...value.map(t => Pipeline[t]));
|
|
26
|
-
if (key === 'transform') prev.transforms.push(...value.map(t => Pipeline[t]));
|
|
27
|
-
if (key === 'normalize') prev.normalizers.push(...value.map(t => Pipeline[t]));
|
|
28
|
-
if (key === 'serialize') prev.serializers.push(...value.map(t => Pipeline[t]));
|
|
29
|
-
if (key === 'deserialize') prev.deserializers.push(...value.map(t => Pipeline[t]));
|
|
30
|
-
return prev;
|
|
31
|
-
}, structures);
|
|
32
|
-
|
|
33
|
-
// IDs (first - shift)
|
|
34
|
-
if (isPrimaryKeyId && type === 'ID') $structures.serializers.unshift(Pipeline.idKey);
|
|
35
|
-
if (isIdField) $structures.$serializers.unshift(Pipeline.idField);
|
|
36
|
-
|
|
37
|
-
// Required (last - push)
|
|
38
|
-
if (isRequired && isPersistable && !isVirtual) $structures.validators.push(Pipeline.required);
|
|
39
|
-
if (modelRef && !isEmbedded) $structures.validators.push(Pipeline.ensureId);
|
|
40
|
-
|
|
41
|
-
return $structures;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
resolve(resolver, doc, args = {}) {
|
|
45
|
-
const { name, isArray, isScalar, isVirtual, isRequired, isEmbedded, modelRef, virtualField } = this.props;
|
|
46
|
-
const value = doc[name];
|
|
47
|
-
|
|
48
|
-
// Default resolver return immediately!
|
|
49
|
-
if (isScalar || isEmbedded) return value;
|
|
50
|
-
|
|
51
|
-
// Ensure where clause for DB lookup
|
|
52
|
-
args.where = args.where || {};
|
|
53
|
-
|
|
54
|
-
if (isArray) {
|
|
55
|
-
if (isVirtual) {
|
|
56
|
-
if (isEmpty(args.where)) args.batch = `${virtualField}`;
|
|
57
|
-
args.where[virtualField] = doc.id;
|
|
58
|
-
return resolver.match(modelRef).merge(args).many();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Not a "required" query + strip out nulls
|
|
62
|
-
if (isEmpty(args.where)) args.batch = 'id';
|
|
63
|
-
args.where.id = value;
|
|
64
|
-
return resolver.match(modelRef).merge(args).many();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (isVirtual) {
|
|
68
|
-
if (isEmpty(args.where)) args.batch = `${virtualField}`;
|
|
69
|
-
args.where[virtualField] = doc.id;
|
|
70
|
-
return resolver.match(modelRef).merge(args).one();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return resolver.match(modelRef).id(value).one({ required: isRequired });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
count(resolver, doc, args = {}) {
|
|
77
|
-
const { name, isVirtual, modelRef, virtualField } = this.props;
|
|
78
|
-
args.where = args.where || {};
|
|
79
|
-
if (isVirtual) args.where[virtualField] = doc.id;
|
|
80
|
-
else args.where.id = doc[name];
|
|
81
|
-
return resolver.match(modelRef).merge(args).count();
|
|
82
|
-
}
|
|
83
|
-
};
|
package/src/data/Model.js
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
const Stream = require('stream');
|
|
2
|
-
const { get } = require('lodash');
|
|
3
|
-
const { flatten } = require('@coderich/util');
|
|
4
|
-
const Field = require('./Field');
|
|
5
|
-
const Model = require('../graphql/ast/Model');
|
|
6
|
-
const { eventEmitter } = require('../service/event.service');
|
|
7
|
-
const { finalizeResults } = require('./DataService');
|
|
8
|
-
const { map, mapPromise, seek, deseek } = require('../service/app.service');
|
|
9
|
-
|
|
10
|
-
module.exports = class extends Model {
|
|
11
|
-
constructor(schema, model, driver) {
|
|
12
|
-
super(schema, JSON.parse(JSON.stringify((model.getAST()))));
|
|
13
|
-
this.driver = driver;
|
|
14
|
-
this.fields = super.getFields().map(field => new Field(this, field));
|
|
15
|
-
this.namedQueries = {};
|
|
16
|
-
this.shapesCache = new Map();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
raw() {
|
|
20
|
-
return this.driver.dao.raw(this.getKey());
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
drop() {
|
|
24
|
-
return this.driver.dao.dropModel(this.getKey());
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
idValue(id) {
|
|
28
|
-
return this.driver.idValue(id);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
idKey() {
|
|
32
|
-
return this.getDirectiveArg('model', 'id', this.driver.idKey());
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
getDriver() {
|
|
36
|
-
return this.driver.dao;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
createNamedQuery(name, fn) {
|
|
40
|
-
this.namedQueries[name] = fn;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
getNamedQueries(name) {
|
|
44
|
-
return this.namedQueries;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
referentialIntegrity(refs) {
|
|
48
|
-
if (refs) this.referentials = refs;
|
|
49
|
-
return this.referentials;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Convenience method to deserialize data from a data source (such as a database)
|
|
54
|
-
*/
|
|
55
|
-
deserialize(mixed, query) {
|
|
56
|
-
const shape = this.getShape();
|
|
57
|
-
|
|
58
|
-
return new Promise((resolve, reject) => {
|
|
59
|
-
if (!(mixed instanceof Stream)) {
|
|
60
|
-
resolve(this.shapeObject(shape, mixed, query));
|
|
61
|
-
} else {
|
|
62
|
-
const results = [];
|
|
63
|
-
mixed.on('data', (data) => { results.push(this.shapeObject(shape, data, query)); });
|
|
64
|
-
mixed.on('end', () => { resolve(results); });
|
|
65
|
-
mixed.on('error', reject);
|
|
66
|
-
}
|
|
67
|
-
}).then(rs => finalizeResults(rs, query));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
getShape(crud = 'read', target = 'doc', paths = [], depth = 0) {
|
|
71
|
-
if (depth++ > 10) return {}; // Prevent infinite circular references
|
|
72
|
-
|
|
73
|
-
// Cache check
|
|
74
|
-
const cacheKey = `${crud}:${target}`;
|
|
75
|
-
if (this.shapesCache.has(cacheKey)) return this.shapesCache.get(cacheKey);
|
|
76
|
-
|
|
77
|
-
const serdes = crud === 'read' ? 'deserialize' : 'serialize';
|
|
78
|
-
const fields = serdes === 'deserialize' ? this.getSelectFields() : this.getPersistableFields();
|
|
79
|
-
const crudMap = { create: ['constructs'], update: ['restructs'], delete: ['destructs'], remove: ['destructs'] };
|
|
80
|
-
const sortKeys = ['isIdField', 'isBasicType', 'isEmbedded'];
|
|
81
|
-
const crudKeys = crudMap[crud] || [];
|
|
82
|
-
|
|
83
|
-
// Define target mapping
|
|
84
|
-
const targetMap = {
|
|
85
|
-
doc: [], // Do nothing...
|
|
86
|
-
// doc: ['defaultValue', 'castValue', 'ensureArrayValue', 'normalizers', 'instructs', ...crudKeys, `$${serdes}rs`, `${serdes}rs`, 'transforms'],
|
|
87
|
-
input: ['defaultValue', 'castValue', 'ensureArrayValue', 'normalizers', 'instructs', ...crudKeys, `$${serdes}rs`, `${serdes}rs`, 'transforms'],
|
|
88
|
-
where: ['castValue', 'instructs', `$${serdes}rs`],
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const structureKeys = targetMap[target] || ['castValue'];
|
|
92
|
-
|
|
93
|
-
// Create sorted shape, recursive
|
|
94
|
-
const shape = fields.sort((a, b) => {
|
|
95
|
-
const aObject = a.toObject();
|
|
96
|
-
const bObject = b.toObject();
|
|
97
|
-
|
|
98
|
-
// PK first
|
|
99
|
-
if (aObject.isPrimaryKeyId) return -1;
|
|
100
|
-
if (bObject.isPrimaryKeyId) return 1;
|
|
101
|
-
|
|
102
|
-
// Arrays last
|
|
103
|
-
if (aObject.isArray && !bObject.isArray) return 1;
|
|
104
|
-
if (bObject.isArray && !aObject.isArray) return -1;
|
|
105
|
-
|
|
106
|
-
// Now, follow sort keys
|
|
107
|
-
const aNum = sortKeys.findIndex(key => aObject[key]);
|
|
108
|
-
const bNum = sortKeys.findIndex(key => bObject[key]);
|
|
109
|
-
if (aNum < bNum) return -1;
|
|
110
|
-
if (aNum > bNum) return 1;
|
|
111
|
-
return 0;
|
|
112
|
-
}).map((field) => {
|
|
113
|
-
let instructed = false;
|
|
114
|
-
const structures = field.getStructures();
|
|
115
|
-
const { key, name, type, isArray, isEmbedded, modelRef } = field.toObject();
|
|
116
|
-
const [from, to] = serdes === 'serialize' ? [name, key] : [key, name];
|
|
117
|
-
const actualTo = target === 'input' || target === 'splice' ? from : to;
|
|
118
|
-
const path = paths.concat(actualTo);
|
|
119
|
-
const subCrud = crud === 'update' && isArray ? 'create' : crud; // Due to limitation to update embedded array
|
|
120
|
-
const subShape = isEmbedded ? modelRef.getShape(subCrud, target, path, depth) : null;
|
|
121
|
-
const transformers = structureKeys.reduce((prev, struct) => {
|
|
122
|
-
const structs = structures[struct];
|
|
123
|
-
if (struct === 'instructs' && structs.length) instructed = true;
|
|
124
|
-
return prev.concat(structs);
|
|
125
|
-
}, []).filter(Boolean);
|
|
126
|
-
return { instructed, field, path, from, to: actualTo, type, isArray, transformers, validators: structures.validators, shape: subShape };
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Adding useful shape info
|
|
130
|
-
shape.crud = crud;
|
|
131
|
-
shape.model = this;
|
|
132
|
-
shape.serdes = serdes;
|
|
133
|
-
shape.target = target;
|
|
134
|
-
// console.log(shape.modelRef);
|
|
135
|
-
|
|
136
|
-
// Cache and return
|
|
137
|
-
this.shapesCache.set(cacheKey, shape);
|
|
138
|
-
return shape;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
shapeObject(shape, obj, query, root, base, toFlat = false) {
|
|
142
|
-
const { serdes, model } = shape;
|
|
143
|
-
const { context, resolver, doc = {}, flags = {} } = query.toObject();
|
|
144
|
-
const { pipeline } = flags;
|
|
145
|
-
|
|
146
|
-
if (!pipeline) return obj;
|
|
147
|
-
// const filters = pipeline === true ? [] : Object.entries(pipeline).map(([k, v]) => (v === false ? k : null)).filter(Boolean);
|
|
148
|
-
|
|
149
|
-
// base is the base model
|
|
150
|
-
base = base || model;
|
|
151
|
-
|
|
152
|
-
return map(obj, (parent) => {
|
|
153
|
-
// root is the base data object
|
|
154
|
-
root = root || parent;
|
|
155
|
-
|
|
156
|
-
// Lookup helper functions
|
|
157
|
-
const docPath = (p, hint) => seek(doc, p, hint); // doc is already serialized; so always a seek
|
|
158
|
-
const rootPath = (p, hint) => (serdes === 'serialize' ? seek(root, p, hint) : deseek(shape, root, p, hint));
|
|
159
|
-
const parentPath = (p, hint) => (serdes === 'serialize' ? seek(parent, p, hint) : deseek(shape, parent, p, hint));
|
|
160
|
-
|
|
161
|
-
return shape.reduce((prev, { instructed, field, from, to, path, type, isArray, defaultValue, transformers = [], shape: subShape }) => {
|
|
162
|
-
const startValue = parent[from];
|
|
163
|
-
// transformers = filters.length ? transformers.filter() : transformers;
|
|
164
|
-
|
|
165
|
-
// Transform value
|
|
166
|
-
const transformedValue = transformers.reduce((value, t) => {
|
|
167
|
-
const v = t({ base, model, field, path, docPath, rootPath, parentPath, startValue, value, resolver, context });
|
|
168
|
-
return v === undefined ? value : v;
|
|
169
|
-
}, startValue);
|
|
170
|
-
|
|
171
|
-
// Determine if key should stay or be removed
|
|
172
|
-
if (!instructed && transformedValue === undefined && !Object.prototype.hasOwnProperty.call(parent, from)) return prev;
|
|
173
|
-
if (!instructed && subShape && typeof transformedValue !== 'object') return prev;
|
|
174
|
-
|
|
175
|
-
// Rename key & assign value
|
|
176
|
-
prev[to] = (!subShape || transformedValue == null) ? transformedValue : this.shapeObject(subShape, transformedValue, query, root, base, toFlat);
|
|
177
|
-
|
|
178
|
-
if (toFlat && get(doc, to) && field.getModelRef()) {
|
|
179
|
-
const val = prev[to];
|
|
180
|
-
delete prev[to];
|
|
181
|
-
Object.assign(prev, flatten({ [to]: val }, { safe: true, depth: 1 }));
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return prev;
|
|
185
|
-
}, {});
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
validateObject(shape, obj, query, root, base, silent = false) {
|
|
190
|
-
const { model } = shape;
|
|
191
|
-
const { context, resolver, doc = {}, flags = {} } = query.toObject();
|
|
192
|
-
const { validate = true } = flags;
|
|
193
|
-
|
|
194
|
-
if (!validate) return Promise.resolve();
|
|
195
|
-
|
|
196
|
-
// base is the base model
|
|
197
|
-
base = base || model;
|
|
198
|
-
|
|
199
|
-
return mapPromise(obj, (parent) => {
|
|
200
|
-
// root is the base data object
|
|
201
|
-
root = root || parent;
|
|
202
|
-
|
|
203
|
-
// Lookup helper functions
|
|
204
|
-
const docPath = (p, hint) => seek(doc, p, hint);
|
|
205
|
-
const rootPath = (p, hint) => seek(root, p, hint);
|
|
206
|
-
const parentPath = (p, hint) => seek(parent, p, hint);
|
|
207
|
-
|
|
208
|
-
return Promise.all(shape.map(({ field, from, path, validators, shape: subShape }) => {
|
|
209
|
-
const value = parent[from]; // It hasn't been shaped yet
|
|
210
|
-
|
|
211
|
-
return Promise.all(validators.map((v) => {
|
|
212
|
-
return new Promise((resolve, reject) => {
|
|
213
|
-
return Promise.resolve(v({ base, model, field, path, docPath, rootPath, parentPath, startValue: value, value, resolver, context })).then(resolve).catch(reject);
|
|
214
|
-
});
|
|
215
|
-
})).then(() => {
|
|
216
|
-
return subShape ? this.validateObject(subShape, value, query, root, base, true) : Promise.resolve();
|
|
217
|
-
});
|
|
218
|
-
}));
|
|
219
|
-
}).then(() => {
|
|
220
|
-
return silent ? Promise.resolve() : eventEmitter.emit('validate', query.toObject());
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
};
|
package/src/data/TreeMap.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TreeMap.
|
|
3
|
-
*
|
|
4
|
-
* A utility class to help build and access elements in a tree-like structure. Used
|
|
5
|
-
* primarly when handling nested transactions.
|
|
6
|
-
*/
|
|
7
|
-
module.exports = class TreeMap {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.map = new Map();
|
|
10
|
-
this.throw = (e = 'Parent not found') => { throw new Error(e); };
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
add(parent, child) {
|
|
14
|
-
if (parent && child) {
|
|
15
|
-
const [, map] = this.get(parent) || this.throw();
|
|
16
|
-
map.set(child, new Map());
|
|
17
|
-
} else {
|
|
18
|
-
this.map.set(parent || child, new Map());
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
get(parent, map = this.map) {
|
|
23
|
-
if (map.has(parent)) return [map, map.get(parent)];
|
|
24
|
-
|
|
25
|
-
return Array.from(map.values()).reduce((value, child) => {
|
|
26
|
-
if (value) return value;
|
|
27
|
-
return this.get(parent, child);
|
|
28
|
-
}, undefined);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
remove(parent) {
|
|
32
|
-
const [map] = this.get(parent) || this.throw();
|
|
33
|
-
const el = map.get(parent);
|
|
34
|
-
map.delete(parent);
|
|
35
|
-
return el;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
elements(map = this.map) {
|
|
39
|
-
return Array.from(map.entries()).reduce((prev, [key, value]) => {
|
|
40
|
-
return prev.concat(key).concat(this.elements(value));
|
|
41
|
-
}, []);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
root(parent) {
|
|
45
|
-
const [root = parent] = this.ascendants(parent);
|
|
46
|
-
return root;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
ascendants(parent) {
|
|
50
|
-
if (!this.get(parent)) this.throw();
|
|
51
|
-
|
|
52
|
-
const traverse = (e) => {
|
|
53
|
-
return Array.from(e.entries()).reduce((prev, [key, value]) => {
|
|
54
|
-
const descendants = this.descendants(key);
|
|
55
|
-
if (descendants.indexOf(parent) > -1) prev.push(key);
|
|
56
|
-
return prev.concat(traverse(value));
|
|
57
|
-
}, []);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
return traverse(this.map);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
descendants(parent) {
|
|
64
|
-
const [, map] = this.get(parent) || this.throw();
|
|
65
|
-
return this.elements(map);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
siblings(parent) {
|
|
69
|
-
const [map] = this.get(parent) || this.throw();
|
|
70
|
-
if (map === this.map) return [];
|
|
71
|
-
return Array.from(map.keys()).filter(node => node !== parent);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
lineage(parent) {
|
|
75
|
-
const root = this.root(parent);
|
|
76
|
-
return [root].concat(this.descendants(root));
|
|
77
|
-
}
|
|
78
|
-
};
|
package/src/data/Type.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
const Type = require('../graphql/ast/Type');
|
|
2
|
-
const Pipeline = require('./Pipeline');
|
|
3
|
-
|
|
4
|
-
module.exports = class extends Type {
|
|
5
|
-
constructor(field) {
|
|
6
|
-
super(field.getAST());
|
|
7
|
-
this.field = field;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
getStructures() {
|
|
11
|
-
const type = this.field.getType();
|
|
12
|
-
const enumType = this.field.getEnumRef();
|
|
13
|
-
const scalarType = this.field.getScalarRef();
|
|
14
|
-
const structures = {
|
|
15
|
-
validators: [],
|
|
16
|
-
instructs: [],
|
|
17
|
-
restructs: [],
|
|
18
|
-
destructs: [],
|
|
19
|
-
constructs: [],
|
|
20
|
-
normalizers: [],
|
|
21
|
-
$serializers: [],
|
|
22
|
-
$deserializers: [],
|
|
23
|
-
serializers: [],
|
|
24
|
-
deserializers: [],
|
|
25
|
-
transforms: [],
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// Built-in pipelines
|
|
29
|
-
structures.castValue = Pipeline.castValue;
|
|
30
|
-
structures.defaultValue = Pipeline.defaultValue;
|
|
31
|
-
structures.ensureArrayValue = Pipeline.ensureArrayValue;
|
|
32
|
-
|
|
33
|
-
if (enumType) structures.validators.push(Pipeline.define(`allow${type}`, Pipeline.Allow(...enumType.getValue()), { configurable: true }));
|
|
34
|
-
if (!scalarType) return structures;
|
|
35
|
-
|
|
36
|
-
return Object.entries(scalarType.getDirectiveArgs('field', {})).reduce((prev, [key, value]) => {
|
|
37
|
-
if (!Array.isArray(value)) value = [value];
|
|
38
|
-
if (key === 'validate') prev.validators.push(...value.map(t => Pipeline[t]));
|
|
39
|
-
if (key === 'instruct') prev.instructs.push(...value.map(t => Pipeline[t]));
|
|
40
|
-
if (key === 'restruct') prev.restructs.push(...value.map(t => Pipeline[t]));
|
|
41
|
-
if (key === 'destruct') prev.destructs.push(...value.map(t => Pipeline[t]));
|
|
42
|
-
if (key === 'construct') prev.constructs.push(...value.map(t => Pipeline[t]));
|
|
43
|
-
if (key === 'transform') prev.transforms.push(...value.map(t => Pipeline[t]));
|
|
44
|
-
if (key === 'normalize') prev.normalizers.push(...value.map(t => Pipeline[t]));
|
|
45
|
-
if (key === 'serialize') prev.serializers.push(...value.map(t => Pipeline[t]));
|
|
46
|
-
if (key === 'deserialize') prev.deserializers.push(...value.map(t => Pipeline[t]));
|
|
47
|
-
return prev;
|
|
48
|
-
}, structures);
|
|
49
|
-
}
|
|
50
|
-
};
|
package/src/driver/.DS_Store
DELETED
|
Binary file
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
const Util = require('util');
|
|
2
|
-
const { get } = require('lodash');
|
|
3
|
-
const { unflatten } = require('@coderich/util');
|
|
4
|
-
const { MongoClient, ObjectId } = require('mongodb');
|
|
5
|
-
const { map, ensureArray, proxyDeep, toKeyObj, globToRegex, proxyPromise, isScalarDataType, promiseRetry } = require('../service/app.service');
|
|
6
|
-
|
|
7
|
-
module.exports = class MongoDriver {
|
|
8
|
-
constructor(config) {
|
|
9
|
-
this.config = config;
|
|
10
|
-
this.config.query = this.config.query || {};
|
|
11
|
-
this.connection = this.connect();
|
|
12
|
-
this.getDirectives = () => get(config, 'directives', {});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
connect() {
|
|
16
|
-
const { uri, options = {} } = this.config;
|
|
17
|
-
options.ignoreUndefined = false;
|
|
18
|
-
return MongoClient.connect(uri, options);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
disconnect() {
|
|
22
|
-
return this.connection.then(client => client.close());
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
raw(collection) {
|
|
26
|
-
return proxyPromise(this.connection.then(client => client.db().collection(collection)));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
query(collection, method, ...args) {
|
|
30
|
-
if (get(args[args.length - 1], 'debug') === true) console.log(collection, method, Util.inspect(args, { depth: null, showHidden: false, colors: true }));
|
|
31
|
-
if (method === 'aggregate') args.splice(2);
|
|
32
|
-
return this.raw(collection)[method](...args);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
resolve(query) {
|
|
36
|
-
const { isNative } = query;
|
|
37
|
-
if (!isNative) query.where = MongoDriver.normalizeWhere(query.where);
|
|
38
|
-
return this[query.method](query);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
findOne(query) {
|
|
42
|
-
return this.findMany(Object.assign(query, { first: 1 })).then((stream) => {
|
|
43
|
-
return new Promise((resolve, reject) => {
|
|
44
|
-
stream.on('data', resolve);
|
|
45
|
-
stream.on('error', reject);
|
|
46
|
-
stream.on('end', resolve);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
findMany(query) {
|
|
52
|
-
const { model, options = {}, flags } = query;
|
|
53
|
-
const $options = { ...this.config.query, ...options };
|
|
54
|
-
return this.query(model, 'aggregate', MongoDriver.aggregateQuery(query), $options, flags).then(cursor => cursor.stream());
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
count(query) {
|
|
58
|
-
const { model, options = {}, flags } = query;
|
|
59
|
-
const $options = { ...this.config.query, ...options };
|
|
60
|
-
|
|
61
|
-
return this.query(model, 'aggregate', MongoDriver.aggregateQuery(query, true), $options, flags).then((cursor) => {
|
|
62
|
-
return cursor.next().then((doc) => {
|
|
63
|
-
return doc ? doc.count : 0;
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
createOne({ model, input, options, flags }) {
|
|
69
|
-
// console.log(JSON.stringify(input, null, 2));
|
|
70
|
-
return this.query(model, 'insertOne', input, options, flags).then(result => Object.assign(input, { id: result.insertedId }));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
updateOne({ model, where, $doc, options, flags }) {
|
|
74
|
-
const $update = { $set: $doc };
|
|
75
|
-
return this.query(model, 'updateOne', where, $update, options, flags).then(() => unflatten($doc, { safe: true }));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
deleteOne({ model, where, options, flags }) {
|
|
79
|
-
return this.query(model, 'deleteOne', where, options, flags).then(() => true);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
dropModel(model) {
|
|
83
|
-
return this.query(model, 'deleteMany');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
createCollection(model) {
|
|
87
|
-
return this.connection.then(client => client.db().createCollection(model)).catch(e => null);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
createIndexes(model, indexes) {
|
|
91
|
-
return Promise.all(indexes.map(({ name, type, on }) => {
|
|
92
|
-
const $fields = on.reduce((prev, field) => Object.assign(prev, { [field]: 1 }), {});
|
|
93
|
-
|
|
94
|
-
switch (type) {
|
|
95
|
-
case 'unique': return this.query(model, 'createIndex', $fields, { name, unique: true });
|
|
96
|
-
default: return null;
|
|
97
|
-
}
|
|
98
|
-
}));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
transaction(ops) {
|
|
102
|
-
return promiseRetry(() => {
|
|
103
|
-
// Create session and start transaction
|
|
104
|
-
return this.connection.then(client => client.startSession({ readPreference: { mode: 'primary' } })).then((session) => {
|
|
105
|
-
session.startTransaction({ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' } });
|
|
106
|
-
const close = () => { session.endSession(); };
|
|
107
|
-
|
|
108
|
-
// Execute each operation with session
|
|
109
|
-
return Promise.all(ops.map(op => op.exec({ session }))).then((results) => {
|
|
110
|
-
results.$commit = () => session.commitTransaction().then(close);
|
|
111
|
-
results.$rollback = () => session.abortTransaction().then(close);
|
|
112
|
-
return results;
|
|
113
|
-
}).catch((e) => {
|
|
114
|
-
close();
|
|
115
|
-
throw e;
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
}, 200, 5, e => e.errorLabels && e.errorLabels.indexOf('TransientTransactionError') > -1);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
static idKey() {
|
|
122
|
-
return '_id';
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
static idValue(value) {
|
|
126
|
-
if (value instanceof ObjectId) return value;
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
const id = ObjectId(value);
|
|
130
|
-
return id;
|
|
131
|
-
} catch (e) {
|
|
132
|
-
return value;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
static normalizeWhere(where) {
|
|
137
|
-
return proxyDeep(toKeyObj(where), {
|
|
138
|
-
get(target, prop, rec) {
|
|
139
|
-
const value = Reflect.get(target, prop, rec);
|
|
140
|
-
if (typeof value === 'function') return value.bind(target);
|
|
141
|
-
const $value = map(value, v => (typeof v === 'string' ? globToRegex(v, { nocase: true, regex: true }) : v));
|
|
142
|
-
if (Array.isArray($value)) {
|
|
143
|
-
// console.log(Util.inspect({ value, $value }, { depth: null, showHidden: false, colors: true }));
|
|
144
|
-
return { $in: $value };
|
|
145
|
-
}
|
|
146
|
-
return $value;
|
|
147
|
-
},
|
|
148
|
-
}).toObject();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
static getAddFields(query) {
|
|
152
|
-
const { shape, where } = query;
|
|
153
|
-
|
|
154
|
-
return shape.reduce((prev, { from, type, isArray }) => {
|
|
155
|
-
// Basic checks to see if worth converting for regex
|
|
156
|
-
let value = where[from];
|
|
157
|
-
if (value === undefined) return prev;
|
|
158
|
-
if (!isScalarDataType(type)) return prev;
|
|
159
|
-
|
|
160
|
-
// Do regex conversion
|
|
161
|
-
if (isArray) value = value.$in || value; // Where clause does not always use $in
|
|
162
|
-
if (!ensureArray(value).some(el => el instanceof RegExp)) return prev;
|
|
163
|
-
const conversion = isArray ? { $map: { input: `$${from}`, as: 'el', in: { $toString: '$$el' } } } : { $toString: `$${from}` };
|
|
164
|
-
return Object.assign(prev, { [from]: conversion });
|
|
165
|
-
}, {});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
static getProjectFields(parentShape, currentShape = { _id: 0, id: '$_id' }, isEmbedded, isEmbeddedArray, path = []) {
|
|
169
|
-
return parentShape.reduce((project, value) => {
|
|
170
|
-
const { from, to, shape: subShape, isArray } = value;
|
|
171
|
-
const $key = isEmbedded && isEmbeddedArray ? `$$embedded.${from}` : `$${path.concat(from).join('.')}`;
|
|
172
|
-
|
|
173
|
-
if (subShape) {
|
|
174
|
-
const $project = MongoDriver.getProjectFields(subShape, {}, true, isArray, path.concat(from));
|
|
175
|
-
Object.assign(project, { [to]: isArray ? { $map: { input: $key, as: 'embedded', in: $project } } : $project });
|
|
176
|
-
} else if (isEmbedded) {
|
|
177
|
-
Object.assign(project, { [to]: $key });
|
|
178
|
-
} else {
|
|
179
|
-
Object.assign(project, { [to]: from === to ? 1 : $key });
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return project;
|
|
183
|
-
}, currentShape);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
static aggregateQuery(query, count = false) {
|
|
187
|
-
const { where: $match, sort = {}, skip, limit, joins, after, before, first } = query;
|
|
188
|
-
const $aggregate = [{ $match }];
|
|
189
|
-
|
|
190
|
-
// Used for $regex matching
|
|
191
|
-
const $addFields = MongoDriver.getAddFields(query);
|
|
192
|
-
if (Object.keys($addFields).length) $aggregate.unshift({ $addFields });
|
|
193
|
-
|
|
194
|
-
if (count) {
|
|
195
|
-
$aggregate.push({ $count: 'count' });
|
|
196
|
-
} else {
|
|
197
|
-
// This is needed to return FK references as an array in the correct order
|
|
198
|
-
// http://www.kamsky.org/stupid-tricks-with-mongodb/using-34-aggregation-to-return-documents-in-same-order-as-in-expression
|
|
199
|
-
// https://jira.mongodb.org/browse/SERVER-7528
|
|
200
|
-
const idKey = MongoDriver.idKey();
|
|
201
|
-
const idMatch = $match[idKey];
|
|
202
|
-
if (typeof idMatch === 'object' && idMatch.$in) {
|
|
203
|
-
$aggregate.push({ $addFields: { __order: { $indexOfArray: [idMatch.$in, `$${idKey}`] } } });
|
|
204
|
-
$aggregate.push({ $sort: { __order: 1 } });
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Joins
|
|
208
|
-
if (joins) $aggregate.push(...joins.map(({ to: from, by: foreignField, from: localField, as }) => ({ $lookup: { from, foreignField, localField, as } })));
|
|
209
|
-
|
|
210
|
-
// Sort, Skip, Limit documents
|
|
211
|
-
if (sort && Object.keys(sort).length) $aggregate.push({ $sort: toKeyObj(sort) });
|
|
212
|
-
if (skip) $aggregate.push({ $skip: skip });
|
|
213
|
-
if (limit) $aggregate.push({ $limit: limit });
|
|
214
|
-
|
|
215
|
-
// Pagination
|
|
216
|
-
if (after) $aggregate.push({ $match: { $or: Object.entries(after).reduce((prev, [key, value]) => prev.concat({ [key]: { [sort[key] === 1 ? '$gte' : '$lte']: value } }), []) } });
|
|
217
|
-
if (before) $aggregate.push({ $match: { $or: Object.entries(before).reduce((prev, [key, value]) => prev.concat({ [key]: { [sort[key] === 1 ? '$lte' : '$gte']: value } }), []) } });
|
|
218
|
-
if (first) $aggregate.push({ $limit: first });
|
|
219
|
-
|
|
220
|
-
// // Projection
|
|
221
|
-
// const $project = MongoDriver.getProjectFields(shape);
|
|
222
|
-
// $aggregate.push({ $project });
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return $aggregate;
|
|
226
|
-
}
|
|
227
|
-
};
|
package/src/driver/index.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/* eslint-disable global-require */
|
|
2
|
-
module.exports = class Driver {
|
|
3
|
-
constructor(name) {
|
|
4
|
-
switch (name) {
|
|
5
|
-
case 'Mongo': return require('./MongoDriver');
|
|
6
|
-
// case 'Neo4jDriver': return require('./Neo4jDriver').Neo4jDriver;
|
|
7
|
-
// case 'Neo4jRestDriver': return require('./Neo4jDriver').Neo4jRestDriver;
|
|
8
|
-
default: throw new Error(`Unknown driver ${name}`);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
};
|
package/src/graphql/.DS_Store
DELETED
|
Binary file
|
|
Binary file
|