@coderich/autograph 0.9.10 → 0.10.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/CHANGELOG.md +5 -0
- package/index.js +2 -0
- package/package.json +10 -7
- package/src/.DS_Store +0 -0
- package/src/core/Resolver.js +7 -1
- package/src/core/SchemaDecorator.js +46 -0
- package/src/core/ServerResolver.js +7 -93
- package/src/data/.DS_Store +0 -0
- package/src/data/DataLoader.js +28 -26
- package/src/data/Model.js +22 -10
- package/src/data/stream/DataHydrator.js +58 -0
- package/src/data/stream/ResultSet.js +34 -0
- package/src/data/stream/ResultSetItem.js +158 -0
- package/src/data/stream/ResultSetItemProxy.js +161 -0
- package/src/driver/MongoDriver.js +46 -18
- package/src/graphql/ast/.DS_Store +0 -0
- package/src/graphql/ast/Field.js +1 -2
- package/src/graphql/ast/Model.js +4 -0
- package/src/graphql/ast/Node.js +0 -7
- package/src/graphql/ast/Schema.js +2 -3
- package/src/graphql/ast/SchemaDecorator.js +141 -0
- package/src/graphql/ast/TypeDefApi.js +93 -0
- package/src/graphql/extension/api.js +1 -16
- package/src/graphql/extension/framework.js +0 -1
- package/src/query/Query.js +1 -19
- package/src/query/QueryBuilder.js +3 -2
- package/src/query/QueryResolver.js +6 -3
- package/src/service/app.service.js +6 -0
- package/src/service/decorator.service.js +21 -288
- package/src/service/event.service.js +1 -0
- package/src/service/graphql.service.js +1 -1
- package/src/data/ResultSet2.js +0 -210
- package/src/data/ResultSet3.js +0 -186
package/src/query/Query.js
CHANGED
|
@@ -251,7 +251,7 @@ module.exports = class Query {
|
|
|
251
251
|
return {
|
|
252
252
|
isNative: Boolean(this.props.native),
|
|
253
253
|
model: model.getKey(),
|
|
254
|
-
|
|
254
|
+
shape: model.getShape(),
|
|
255
255
|
method: this.props.method,
|
|
256
256
|
select: this.props.$select,
|
|
257
257
|
joins: this.props.joins,
|
|
@@ -292,22 +292,4 @@ module.exports = class Query {
|
|
|
292
292
|
options: this.props.options,
|
|
293
293
|
};
|
|
294
294
|
}
|
|
295
|
-
|
|
296
|
-
static getSchema(model, name = false) {
|
|
297
|
-
return model.getSelectFields().reduce((prev, field) => {
|
|
298
|
-
const key = name ? field.getName() : field.getKey();
|
|
299
|
-
// const modelRef = field.getModelRef();
|
|
300
|
-
// const isEmbedded = field.isEmbedded();
|
|
301
|
-
|
|
302
|
-
return Object.assign(prev, {
|
|
303
|
-
[key]: {
|
|
304
|
-
field,
|
|
305
|
-
alias: name ? field.getKey() : field.getName(),
|
|
306
|
-
type: field.getDataType(),
|
|
307
|
-
isArray: field.isArray(),
|
|
308
|
-
// schema: isEmbedded ? Query.getSchema(modelRef, name) : null,
|
|
309
|
-
},
|
|
310
|
-
});
|
|
311
|
-
}, {});
|
|
312
|
-
}
|
|
313
295
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const Query = require('./Query');
|
|
2
2
|
const QueryResolver = require('./QueryResolver');
|
|
3
|
-
const { unravelObject } = require('../service/app.service');
|
|
3
|
+
const { toKeyObj, unravelObject } = require('../service/app.service');
|
|
4
4
|
|
|
5
5
|
/*
|
|
6
6
|
* QueryBuilder
|
|
@@ -14,7 +14,8 @@ module.exports = class QueryBuilder {
|
|
|
14
14
|
|
|
15
15
|
// Chainable commands
|
|
16
16
|
this.id = (id) => { this.query.id(id); return this; };
|
|
17
|
-
this.select = (select) => { this.query.select(select); return this; };
|
|
17
|
+
// this.select = (select) => { this.query.select(select); return this; };
|
|
18
|
+
this.select = (select) => { this.query.select(Object.entries(toKeyObj(select)).reduce((prev, [key, value]) => Object.assign(prev, { [key.replace(/edges.node./g, '')]: !!value }), {})); return this; };
|
|
18
19
|
this.where = (where) => { this.query.where(where); return this; };
|
|
19
20
|
this.match = (match) => { this.query.match(match); return this; };
|
|
20
21
|
this.native = (native) => { this.query.native(native); return this; };
|
|
@@ -33,10 +33,13 @@ module.exports = class QueryResolver {
|
|
|
33
33
|
const { model, input, flags } = query.toObject();
|
|
34
34
|
model.appendDefaultFields(query, input);
|
|
35
35
|
|
|
36
|
-
return createSystemEvent('Mutation', { method: 'create', query }, () => {
|
|
36
|
+
return createSystemEvent('Mutation', { method: 'create', query }, async () => {
|
|
37
37
|
const $input = model.serialize(query, model.appendCreateFields(input));
|
|
38
38
|
query.$input($input);
|
|
39
|
-
|
|
39
|
+
if (!get(flags, 'novalidate')) await model.validate(query, $input);
|
|
40
|
+
const doc = await this.resolver.resolve(query);
|
|
41
|
+
query.doc(doc);
|
|
42
|
+
return doc;
|
|
40
43
|
});
|
|
41
44
|
}
|
|
42
45
|
|
|
@@ -170,7 +173,7 @@ module.exports = class QueryResolver {
|
|
|
170
173
|
return this.findMany(query.method('findMany'));
|
|
171
174
|
}
|
|
172
175
|
|
|
173
|
-
|
|
176
|
+
resolve() {
|
|
174
177
|
const { model, method, flags } = this.query.toObject();
|
|
175
178
|
|
|
176
179
|
return this[method](this.query).then((data) => {
|
|
@@ -69,6 +69,12 @@ exports.renameObjectKey = (obj, oldKey, newKey) => {
|
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
exports.deleteKeys = (obj, keys) => {
|
|
73
|
+
if (Array.isArray(obj)) obj.map(item => exports.deleteKeys(item, keys));
|
|
74
|
+
else if (obj === Object(obj)) { keys.forEach(key => delete obj[key]); Object.values(obj).forEach(v => exports.deleteKeys(v, keys)); }
|
|
75
|
+
return obj;
|
|
76
|
+
};
|
|
77
|
+
|
|
72
78
|
exports.getDeep = (obj, path, defaultValue) => {
|
|
73
79
|
const [prop, ...rest] = path.split('.');
|
|
74
80
|
const normalize = data => (Array.isArray(data) ? _.flatten(data) : data);
|
|
@@ -1,275 +1,27 @@
|
|
|
1
|
-
const { get, set, remove, merge } = require('lodash');
|
|
2
1
|
const GraphqlFields = require('graphql-fields');
|
|
3
|
-
const Boom = require('../core/Boom');
|
|
4
|
-
const Query = require('../query/Query');
|
|
5
|
-
const { createSystemEvent } = require('./event.service');
|
|
6
|
-
const { guidToId, unrollGuid, ucFirst, getDeep, ensureArray, objectContaining } = require('./app.service');
|
|
7
|
-
|
|
8
|
-
const findParentField = (name, embed, model) => {
|
|
9
|
-
const schema = model.getSchema();
|
|
10
|
-
const fieldName = ucFirst(embed.isArray() ? embed.getName().replace(/s$/, '') : embed.getName());
|
|
11
|
-
const parentModelName = name.substr(0, name.lastIndexOf(fieldName));
|
|
12
|
-
const parentModel = schema.getModel(parentModelName);
|
|
13
|
-
const field = model.getFields().find(f => f.getType() === parentModelName) || parentModel.getFields().find(f => f.getType() === model.getName());
|
|
14
|
-
if (!field) throw Boom.badData(`Unable to locate parent field: '${model.getName()} -> ${parentModelName}'`);
|
|
15
|
-
return field;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const findParentAndContainerCreate = (name, doc, input, model, embeds) => {
|
|
19
|
-
let parent = doc;
|
|
20
|
-
|
|
21
|
-
const container = embeds.reduce((prev, embed, i) => {
|
|
22
|
-
// If further nested; must find the correct container
|
|
23
|
-
if (i > 0) {
|
|
24
|
-
const subField = findParentField(name, embed, model);
|
|
25
|
-
prev = prev.find(el => `${el.id}` === `${input[subField]}`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
parent = prev;
|
|
29
|
-
return getDeep(prev, embed.getName());
|
|
30
|
-
}, doc);
|
|
31
|
-
|
|
32
|
-
return { parent, container };
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const findParentAndContainerUpdate = (fieldPath, doc, id, embeds) => {
|
|
36
|
-
const data = embeds.map((embed, i, arr) => getDeep(doc, arr.slice(0, i + 1).join('.')));
|
|
37
|
-
const [tail, prev = tail, head = prev] = [data[0], data[data.length - 2], data[data.length - 1]];
|
|
38
|
-
const container = head.find(el => `${id}` === `${el.id}`);
|
|
39
|
-
const parent = head === prev ? doc : prev.find(p => p[fieldPath.split('.').pop()].find(el => `${id}` === `${el.id}`));
|
|
40
|
-
return { tail, parent, container };
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const resolveQuery = (method, name, resolver, model, embeds = []) => {
|
|
44
|
-
const [base] = embeds;
|
|
45
|
-
const curr = embeds[embeds.length - 1];
|
|
46
|
-
const fieldPath = embeds.map(field => field.getName()).join('.');
|
|
47
2
|
|
|
3
|
+
const resolveQuery = (method, name, resolver, model) => {
|
|
48
4
|
return async (root, args, context, info) => {
|
|
49
|
-
const {
|
|
50
|
-
|
|
51
|
-
// Embedded document handler
|
|
52
|
-
if (fieldPath.length) {
|
|
53
|
-
switch (method) {
|
|
54
|
-
case 'get': {
|
|
55
|
-
// Readjust the where clause
|
|
56
|
-
const where = get(args, 'where', {});
|
|
57
|
-
set(where, `${fieldPath}.id`, args.id);
|
|
58
|
-
set(args, 'where', where);
|
|
59
|
-
|
|
60
|
-
return resolver.query(context, base.getModel(), args, info).then(([result]) => {
|
|
61
|
-
const arr = ensureArray(getDeep(result, fieldPath, []));
|
|
62
|
-
return arr.find(el => `${el.id}` === `${args.id}`);
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
case 'find': {
|
|
66
|
-
// Readjust the where clause
|
|
67
|
-
const where = get(args, 'where', {});
|
|
68
|
-
const $where = set({}, `${fieldPath}`, where);
|
|
69
|
-
set(args, 'where', $where);
|
|
70
|
-
|
|
71
|
-
return resolver.query(context, base.getModel(), args, info).then((results) => {
|
|
72
|
-
const arr = results.map(result => ensureArray(getDeep(result, fieldPath, []))).flat();
|
|
73
|
-
return arr.filter(el => objectContaining(el, where));
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
case 'count': {
|
|
77
|
-
// Readjust the where clause
|
|
78
|
-
const where = get(args, 'where', {});
|
|
79
|
-
const $where = set({}, `${fieldPath}`, where);
|
|
80
|
-
set(args, 'where', $where);
|
|
81
|
-
|
|
82
|
-
return resolver.query(context, base.getModel(), args, info).then((results) => {
|
|
83
|
-
const arr = results.map(result => ensureArray(getDeep(result, fieldPath, []))).flat();
|
|
84
|
-
return arr.filter(el => objectContaining(el, where)).length;
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
case 'create': {
|
|
88
|
-
const field = findParentField(name, curr, model);
|
|
89
|
-
const path = fieldPath.split('.').slice(0, -1).concat('id').join('.');
|
|
90
|
-
const input = unrollGuid(autograph, model, args.input);
|
|
91
|
-
const meta = args.meta || {};
|
|
92
|
-
const id = guidToId(autograph, get(input, field.getName()));
|
|
93
|
-
|
|
94
|
-
// Get overall document
|
|
95
|
-
const where = { [path]: id };
|
|
96
|
-
const doc = await autograph.resolver.match(base.getModel()).where(where).one();
|
|
97
|
-
if (!doc) throw Boom.notFound(`${base.getModel().getName()} Not Found`);
|
|
98
|
-
|
|
99
|
-
// Get parent and container within document
|
|
100
|
-
const query = new Query({ resolver: autograph.resolver, model, input, where, meta, root: doc });
|
|
101
|
-
const { container } = findParentAndContainerCreate(name, doc, input, model, embeds);
|
|
102
|
-
model.appendDefaultFields(query, input);
|
|
103
|
-
|
|
104
|
-
return createSystemEvent('Mutation', { method: 'create', query }, async () => {
|
|
105
|
-
let $$input = ensureArray(input.$input || input);
|
|
106
|
-
$$input = await Promise.all($$input.map(el => model.appendCreateFields(el, true)));
|
|
107
|
-
container.push(...$$input);
|
|
108
|
-
const $update = { [base.getName()]: get(doc, base.getName()) };
|
|
109
|
-
return autograph.resolver.match(base.getModel()).id(doc.id).flags({ novalidate: true }).save($update).then(($doc) => {
|
|
110
|
-
return getDeep(doc, fieldPath).pop();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
case 'update': {
|
|
115
|
-
// Get overall document
|
|
116
|
-
const id = guidToId(autograph, args.id);
|
|
117
|
-
const where = { [`${fieldPath}.id`]: id };
|
|
118
|
-
const meta = args.meta || {};
|
|
119
|
-
const input = unrollGuid(autograph, model, args.input || {});
|
|
120
|
-
const doc = await autograph.resolver.match(base.getModel()).where(where).one();
|
|
121
|
-
if (!doc) throw Boom.notFound(`${base.getModel().getName()} Not Found`);
|
|
122
|
-
|
|
123
|
-
// Get parent and container within document
|
|
124
|
-
const query = new Query({ resolver: autograph.resolver, model, input, where, meta, root: doc });
|
|
125
|
-
const { tail, container } = findParentAndContainerUpdate(fieldPath, doc, id, embeds);
|
|
126
|
-
|
|
127
|
-
return createSystemEvent('Mutation', { method: 'update', query }, async () => {
|
|
128
|
-
const $input = await model.appendUpdateFields(input);
|
|
129
|
-
merge(container, $input); // Must mutate object here
|
|
130
|
-
const $update = { [base.getName()]: tail };
|
|
131
|
-
doc[base.getName()] = tail; // Deficiency in how update works; must pass entire doc
|
|
132
|
-
return autograph.resolver.match(base.getModel()).id(doc.id).flags({ novalidate: true }).save($update).then(() => container);
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
case 'delete': {
|
|
136
|
-
const id = guidToId(autograph, args.id);
|
|
137
|
-
const where = { [`${fieldPath}.id`]: id };
|
|
138
|
-
const meta = args.meta || {};
|
|
139
|
-
const doc = await autograph.resolver.match(base.getModel()).where(where).one();
|
|
140
|
-
if (!doc) throw Boom.notFound(`${base.getModel()} Not Found`);
|
|
141
|
-
|
|
142
|
-
// Get parent and container within document
|
|
143
|
-
const query = new Query({ resolver: autograph.resolver, model, where, meta, root: doc });
|
|
144
|
-
const { tail, parent, container } = findParentAndContainerUpdate(fieldPath, doc, id, embeds);
|
|
145
|
-
|
|
146
|
-
return createSystemEvent('Mutation', { method: 'delete', query }, () => {
|
|
147
|
-
const key = fieldPath.split('.').pop();
|
|
148
|
-
remove(parent[key], el => `${el.id}` === `${id}`);
|
|
149
|
-
const $update = { [base.getName()]: tail };
|
|
150
|
-
doc[base.getName()] = tail; // Deficiency in how update works; must pass entire doc
|
|
151
|
-
return autograph.resolver.match(base.getModel()).id(doc.id).flags({ novalidate: true }).save($update).then(() => container);
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
default: {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
5
|
+
const queryInfo = { fields: GraphqlFields(info, {}, { processArguments: true }) };
|
|
159
6
|
|
|
160
7
|
switch (method) {
|
|
161
|
-
case 'get': return resolver.get(context, model, args, true,
|
|
8
|
+
case 'get': return resolver.get(context, model, args, true, queryInfo);
|
|
162
9
|
case 'find': {
|
|
163
10
|
return {
|
|
164
|
-
edges: () => resolver.query(context, model, args,
|
|
165
|
-
pageInfo: () => resolver.query(context, model, args,
|
|
166
|
-
count: () => resolver.count(context, model, args,
|
|
11
|
+
edges: () => resolver.query(context, model, args, queryInfo),
|
|
12
|
+
pageInfo: () => resolver.query(context, model, args, queryInfo),
|
|
13
|
+
count: () => resolver.count(context, model, args, queryInfo),
|
|
167
14
|
};
|
|
168
15
|
}
|
|
169
|
-
case 'count': return resolver.count(context, model, args,
|
|
170
|
-
case 'create': return resolver.create(context, model, args,
|
|
171
|
-
case 'update': return resolver.update(context, model, args,
|
|
172
|
-
case 'delete': return resolver.delete(context, model, args,
|
|
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);
|
|
173
20
|
default: return null;
|
|
174
21
|
}
|
|
175
22
|
};
|
|
176
23
|
};
|
|
177
24
|
|
|
178
|
-
const makeEmbeddedAPI = (model, method, parent) => {
|
|
179
|
-
let gql = '';
|
|
180
|
-
const modelName = model.getName();
|
|
181
|
-
const fields = model.getEmbeddedFields().filter(field => field !== parent && field.isEmbeddedApi());
|
|
182
|
-
|
|
183
|
-
if (fields.length) {
|
|
184
|
-
fields.forEach((field) => {
|
|
185
|
-
const modelRef = field.getModelRef();
|
|
186
|
-
const fieldName = ucFirst(field.isArray() ? field.getName().replace(/s$/, '') : field.getName());
|
|
187
|
-
const name = `${modelName}${fieldName}`;
|
|
188
|
-
|
|
189
|
-
switch (method) {
|
|
190
|
-
case 'create': {
|
|
191
|
-
if (field.hasFieldScope('c')) gql += exports.makeCreateAPI(name, modelRef, field);
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
case 'read': {
|
|
195
|
-
if (field.hasFieldScope('r')) gql += exports.makeReadAPI(name, modelRef, field);
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
case 'update': {
|
|
199
|
-
if (field.hasFieldScope('u')) gql += exports.makeUpdateAPI(name, modelRef, field);
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
case 'delete': {
|
|
203
|
-
if (field.hasFieldScope('d')) gql += exports.makeDeleteAPI(name, modelRef, field);
|
|
204
|
-
break;
|
|
205
|
-
}
|
|
206
|
-
default: {
|
|
207
|
-
throw new Error(`Unknown method '${method}'`);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return gql;
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
const makeEmbeddedResolver = (model, resolver, type, embeds = []) => {
|
|
217
|
-
const obj = {};
|
|
218
|
-
|
|
219
|
-
const modelName = model.getName();
|
|
220
|
-
const fields = model.getEmbeddedFields().filter(field => embeds.indexOf(field) === -1 && field.isEmbeddedApi());
|
|
221
|
-
|
|
222
|
-
fields.forEach((field) => {
|
|
223
|
-
const modelRef = field.getModelRef();
|
|
224
|
-
const fieldName = ucFirst(field.isArray() ? field.getName().replace(/s$/, '') : field.getName());
|
|
225
|
-
const name = `${modelName}${fieldName}`;
|
|
226
|
-
|
|
227
|
-
switch (type) {
|
|
228
|
-
case 'query': {
|
|
229
|
-
Object.assign(obj, exports.makeQueryResolver(name, modelRef, resolver, embeds.concat(field)));
|
|
230
|
-
break;
|
|
231
|
-
}
|
|
232
|
-
case 'mutation': {
|
|
233
|
-
Object.assign(obj, exports.makeMutationResolver(name, modelRef, resolver, embeds.concat(field)));
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
default: {
|
|
237
|
-
throw new Error(`Unknown type '${type}'`);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
return obj;
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
exports.makeInputSplice = (model, embed = false) => {
|
|
246
|
-
let gql = '';
|
|
247
|
-
const fields = model.getArrayFields().filter(field => field.hasGQLScope('c', 'u', 'd') && field.isSpliceable());
|
|
248
|
-
|
|
249
|
-
if (fields.length) {
|
|
250
|
-
gql += fields.map((field) => {
|
|
251
|
-
const embedded = field.isEmbedded() ? exports.makeInputSplice(field.getModelRef(), true) : '';
|
|
252
|
-
|
|
253
|
-
return `
|
|
254
|
-
${embedded}
|
|
255
|
-
input ${model.getName()}${ucFirst(field.getName())}InputSplice {
|
|
256
|
-
with: ${field.getGQLType('InputWhere', { splice: true })}
|
|
257
|
-
put: ${field.getGQLType('InputUpdate', { splice: true })}
|
|
258
|
-
${embedded.length ? `splice: ${field.getModelRef().getName()}InputSplice` : ''}
|
|
259
|
-
}
|
|
260
|
-
`;
|
|
261
|
-
}).join('\n\n');
|
|
262
|
-
|
|
263
|
-
gql += `
|
|
264
|
-
input ${model.getName()}InputSplice {
|
|
265
|
-
${fields.map(field => `${field.getName()}: ${model.getName()}${ucFirst(field.getName())}InputSplice`)}
|
|
266
|
-
}
|
|
267
|
-
`;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return gql;
|
|
271
|
-
};
|
|
272
|
-
|
|
273
25
|
// APIs
|
|
274
26
|
exports.makeCreateAPI = (name, model, parent) => {
|
|
275
27
|
let gql = '';
|
|
@@ -279,8 +31,6 @@ exports.makeCreateAPI = (name, model, parent) => {
|
|
|
279
31
|
gql += `create${name}(input: ${model.getName()}InputCreate! ${meta}): ${model.getName()}!`;
|
|
280
32
|
}
|
|
281
33
|
|
|
282
|
-
gql += makeEmbeddedAPI(model, 'create', parent);
|
|
283
|
-
|
|
284
34
|
return gql;
|
|
285
35
|
};
|
|
286
36
|
|
|
@@ -303,8 +53,6 @@ exports.makeReadAPI = (name, model, parent) => {
|
|
|
303
53
|
`;
|
|
304
54
|
}
|
|
305
55
|
|
|
306
|
-
gql += makeEmbeddedAPI(model, 'read', parent);
|
|
307
|
-
|
|
308
56
|
return gql;
|
|
309
57
|
};
|
|
310
58
|
|
|
@@ -312,21 +60,10 @@ exports.makeUpdateAPI = (name, model, parent) => {
|
|
|
312
60
|
let gql = '';
|
|
313
61
|
|
|
314
62
|
if (model.hasGQLScope('u')) {
|
|
315
|
-
const spliceFields = model.getArrayFields().filter(field => field.hasGQLScope('c', 'u', 'd') && field.isSpliceable());
|
|
316
63
|
const meta = model.getMeta() ? `meta: ${model.getMeta()}` : '';
|
|
317
|
-
|
|
318
|
-
gql += `
|
|
319
|
-
update${name}(
|
|
320
|
-
id: ID!
|
|
321
|
-
input: ${model.getName()}InputUpdate
|
|
322
|
-
# ${!spliceFields.length ? '' : `splice: ${model.getName()}InputSplice`}
|
|
323
|
-
${meta}
|
|
324
|
-
): ${model.getName()}!
|
|
325
|
-
`;
|
|
64
|
+
gql += `update${name}(id: ID! input: ${model.getName()}InputUpdate ${meta}): ${model.getName()}!`;
|
|
326
65
|
}
|
|
327
66
|
|
|
328
|
-
gql += makeEmbeddedAPI(model, 'update', parent);
|
|
329
|
-
|
|
330
67
|
return gql;
|
|
331
68
|
};
|
|
332
69
|
|
|
@@ -338,8 +75,6 @@ exports.makeDeleteAPI = (name, model, parent) => {
|
|
|
338
75
|
gql += `delete${name}(id: ID! ${meta}): ${model.getName()}!`;
|
|
339
76
|
}
|
|
340
77
|
|
|
341
|
-
gql += makeEmbeddedAPI(model, 'delete', parent);
|
|
342
|
-
|
|
343
78
|
return gql;
|
|
344
79
|
};
|
|
345
80
|
|
|
@@ -357,25 +92,23 @@ exports.makeSubscriptionAPI = (name, model, parent) => {
|
|
|
357
92
|
};
|
|
358
93
|
|
|
359
94
|
// Resolvers
|
|
360
|
-
exports.makeQueryResolver = (name, model, resolver
|
|
95
|
+
exports.makeQueryResolver = (name, model, resolver) => {
|
|
361
96
|
const obj = {};
|
|
362
|
-
const [field] = embeds.slice(-1);
|
|
363
97
|
|
|
364
|
-
if (
|
|
365
|
-
obj[`get${name}`] = resolveQuery('get', name, resolver, model
|
|
366
|
-
obj[`find${name}`] = resolveQuery('find', name, resolver, model
|
|
98
|
+
if (model.hasGQLScope('r')) {
|
|
99
|
+
obj[`get${name}`] = resolveQuery('get', name, resolver, model);
|
|
100
|
+
obj[`find${name}`] = resolveQuery('find', name, resolver, model);
|
|
367
101
|
}
|
|
368
102
|
|
|
369
|
-
return
|
|
103
|
+
return obj;
|
|
370
104
|
};
|
|
371
105
|
|
|
372
|
-
exports.makeMutationResolver = (name, model, resolver
|
|
106
|
+
exports.makeMutationResolver = (name, model, resolver) => {
|
|
373
107
|
const obj = {};
|
|
374
|
-
const [field] = embeds.slice(-1);
|
|
375
108
|
|
|
376
|
-
if (
|
|
377
|
-
if (
|
|
378
|
-
if (
|
|
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);
|
|
379
112
|
|
|
380
|
-
return
|
|
113
|
+
return obj;
|
|
381
114
|
};
|
|
@@ -56,6 +56,7 @@ exports.createSystemEvent = (name, mixed = {}, thunk = () => {}) => {
|
|
|
56
56
|
return middleware().then(thunk);
|
|
57
57
|
}).then((result) => {
|
|
58
58
|
event.result = result;
|
|
59
|
+
if (event.crud === 'create') event.doc = event.query.toObject().doc;
|
|
59
60
|
return systemEvent.emit('system', { type: `post${type}`, data: event }).then((postResult = result) => postResult);
|
|
60
61
|
}).then((result) => {
|
|
61
62
|
if (name === 'Response') return result;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { get } = require('lodash');
|
|
2
2
|
const { Kind, parse, print } = require('graphql');
|
|
3
3
|
const { validate } = require('graphql/validation');
|
|
4
|
-
const { makeExecutableSchema } = require('graphql-tools');
|
|
4
|
+
const { makeExecutableSchema } = require('@graphql-tools/schema');
|
|
5
5
|
|
|
6
6
|
//
|
|
7
7
|
const mergePairs = [
|
package/src/data/ResultSet2.js
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
const { get } = require('lodash');
|
|
2
|
-
const DataService = require('./DataService');
|
|
3
|
-
const { map, ensureArray, keyPaths, mapPromise, toGUID, hashObject } = require('../service/app.service');
|
|
4
|
-
|
|
5
|
-
module.exports = class ResultSet {
|
|
6
|
-
constructor(query, data, adjustForPagination = true) {
|
|
7
|
-
const { resolver, model, sort, first, after, last, before } = query.toObject();
|
|
8
|
-
const fields = model.getFields().filter(f => f.getName() !== 'id');
|
|
9
|
-
|
|
10
|
-
const rs = map(data, (doc) => {
|
|
11
|
-
if (doc == null || typeof doc !== 'object') return doc;
|
|
12
|
-
|
|
13
|
-
//
|
|
14
|
-
const cache = new Map();
|
|
15
|
-
|
|
16
|
-
// Base definition all results have
|
|
17
|
-
const definition = {
|
|
18
|
-
id: {
|
|
19
|
-
get() { return doc.id || doc[model.idKey()]; },
|
|
20
|
-
set(id) { doc.id = id; }, // Embedded array of documents need to set id
|
|
21
|
-
enumerable: true,
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
$id: {
|
|
25
|
-
get() { return toGUID(model.getName(), this.id); },
|
|
26
|
-
enumerable: false,
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
$$cursor: {
|
|
30
|
-
get() {
|
|
31
|
-
const sortPaths = keyPaths(sort);
|
|
32
|
-
const sortValues = sortPaths.reduce((prv, path) => Object.assign(prv, { [path]: get(this, path) }), {});
|
|
33
|
-
const sortJSON = JSON.stringify(sortValues);
|
|
34
|
-
return Buffer.from(sortJSON).toString('base64');
|
|
35
|
-
},
|
|
36
|
-
enumerable: false,
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
$$model: {
|
|
40
|
-
value: model,
|
|
41
|
-
enumerable: false,
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
$$data: {
|
|
45
|
-
value: data,
|
|
46
|
-
enumerable: false,
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
$$isResultSetItem: {
|
|
50
|
-
value: true,
|
|
51
|
-
enumerable: false,
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
$$save: {
|
|
55
|
-
get() { return input => resolver.match(model).id(this.id).save({ ...this, ...input }); },
|
|
56
|
-
enumerable: false,
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
$$remove: {
|
|
60
|
-
get() { return () => resolver.match(model).id(this.id).remove(); },
|
|
61
|
-
enumerable: false,
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
$$delete: {
|
|
65
|
-
get() { return () => resolver.match(model).id(this.id).delete(); },
|
|
66
|
-
enumerable: false,
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
toObject: {
|
|
70
|
-
get() {
|
|
71
|
-
return () => map(this, obj => Object.entries(obj).reduce((prev, [key, value]) => {
|
|
72
|
-
if (value === undefined) return prev;
|
|
73
|
-
prev[key] = get(value, '$$isResultSet') ? value.toObject() : value;
|
|
74
|
-
return prev;
|
|
75
|
-
}, {}));
|
|
76
|
-
},
|
|
77
|
-
enumerable: false,
|
|
78
|
-
configurable: true,
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
fields.forEach((field) => {
|
|
83
|
-
const key = field.getKey();
|
|
84
|
-
const name = field.getName();
|
|
85
|
-
const $name = `$${name}`;
|
|
86
|
-
const value = doc[key];
|
|
87
|
-
|
|
88
|
-
// Field attributes
|
|
89
|
-
definition[name] = {
|
|
90
|
-
get() {
|
|
91
|
-
if (cache.has(name)) return cache.get(name);
|
|
92
|
-
let $value = field.deserialize(query, value);
|
|
93
|
-
$value = $value != null && field.isEmbedded() ? new ResultSet(query.model(field.getModelRef()), $value, false) : $value;
|
|
94
|
-
cache.set(name, $value);
|
|
95
|
-
return $value;
|
|
96
|
-
},
|
|
97
|
-
set($value) {
|
|
98
|
-
cache.set(name, $value);
|
|
99
|
-
},
|
|
100
|
-
enumerable: true,
|
|
101
|
-
configurable: true, // Allows things like delete
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
// Hydrated field attributes
|
|
105
|
-
definition[`$${name}`] = {
|
|
106
|
-
get() {
|
|
107
|
-
return (args = {}) => {
|
|
108
|
-
// Ensure where clause
|
|
109
|
-
args.where = args.where || {};
|
|
110
|
-
|
|
111
|
-
// Cache
|
|
112
|
-
const cacheKey = `${$name}-${hashObject(args)}`;
|
|
113
|
-
if (cache.has(cacheKey)) return cache.get(cacheKey);
|
|
114
|
-
|
|
115
|
-
const promise = new Promise((resolve, reject) => {
|
|
116
|
-
(() => {
|
|
117
|
-
const $value = this[name];
|
|
118
|
-
|
|
119
|
-
if (field.isScalar() || field.isEmbedded()) return Promise.resolve($value);
|
|
120
|
-
|
|
121
|
-
const modelRef = field.getModelRef();
|
|
122
|
-
|
|
123
|
-
if (field.isArray()) {
|
|
124
|
-
if (field.isVirtual()) {
|
|
125
|
-
args.where[[field.getVirtualField()]] = this.id; // Is where[[field.getVirtualField()]] correct?
|
|
126
|
-
return resolver.match(modelRef).merge(args).many();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Not a "required" query + strip out nulls
|
|
130
|
-
args.where.id = $value;
|
|
131
|
-
return resolver.match(modelRef).merge(args).many();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (field.isVirtual()) {
|
|
135
|
-
args.where[[field.getVirtualField()]] = this.id;
|
|
136
|
-
return resolver.match(modelRef).merge(args).one();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return resolver.match(modelRef).id($value).one({ required: field.isRequired() });
|
|
140
|
-
})().then((results) => {
|
|
141
|
-
if (results == null) return field.resolve(query, results); // Allow field to determine
|
|
142
|
-
return mapPromise(results, result => field.resolve(query, result)).then(() => results); // Resolve the inside fields but still return "results"!!!!
|
|
143
|
-
}).then((resolved) => {
|
|
144
|
-
resolve(resolved);
|
|
145
|
-
}).catch((e) => {
|
|
146
|
-
reject(e);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
cache.set(cacheKey, promise);
|
|
151
|
-
return promise;
|
|
152
|
-
};
|
|
153
|
-
},
|
|
154
|
-
enumerable: false,
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// Field count (let's assume it's a Connection Type - meaning dont try with anything else)
|
|
158
|
-
definition[`$${name}:count`] = {
|
|
159
|
-
get() {
|
|
160
|
-
return (q = {}) => {
|
|
161
|
-
q.where = q.where || {};
|
|
162
|
-
if (field.isVirtual()) q.where[field.getVirtualField()] = this.id;
|
|
163
|
-
else q.where.id = this[name];
|
|
164
|
-
return resolver.match(field.getModelRef()).merge(q).count();
|
|
165
|
-
};
|
|
166
|
-
},
|
|
167
|
-
enumerable: false,
|
|
168
|
-
};
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
// Create and return ResultSetItem
|
|
172
|
-
return Object.defineProperties({}, definition);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
let hasNextPage = false;
|
|
176
|
-
let hasPreviousPage = false;
|
|
177
|
-
if (adjustForPagination && rs.length) (({ hasPreviousPage, hasNextPage } = DataService.paginateResultSet(rs, first, after, last, before)));
|
|
178
|
-
|
|
179
|
-
return Object.defineProperties(rs, {
|
|
180
|
-
$$pageInfo: {
|
|
181
|
-
get() {
|
|
182
|
-
const edges = ensureArray(rs);
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
startCursor: get(edges, '0.$$cursor', ''),
|
|
186
|
-
endCursor: get(edges, `${edges.length - 1}.$$cursor`, ''),
|
|
187
|
-
hasPreviousPage,
|
|
188
|
-
hasNextPage,
|
|
189
|
-
};
|
|
190
|
-
},
|
|
191
|
-
enumerable: false,
|
|
192
|
-
},
|
|
193
|
-
$$isResultSet: {
|
|
194
|
-
value: true,
|
|
195
|
-
enumerable: false,
|
|
196
|
-
},
|
|
197
|
-
toObject: {
|
|
198
|
-
get() {
|
|
199
|
-
return () => map(this, doc => Object.entries(doc).reduce((prev, [key, value]) => {
|
|
200
|
-
if (value === undefined) return prev;
|
|
201
|
-
prev[key] = get(value, '$$isResultSet') ? value.toObject() : value;
|
|
202
|
-
return prev;
|
|
203
|
-
}, {}));
|
|
204
|
-
},
|
|
205
|
-
enumerable: false,
|
|
206
|
-
configurable: true,
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
};
|