@coderich/autograph 0.9.12 → 0.9.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/.DS_Store +0 -0
- package/src/core/Resolver.js +2 -1
- package/src/data/.DS_Store +0 -0
- package/src/data/Model.js +20 -0
- package/src/data/ResultSet.js +200 -146
- package/src/data/ResultSetItem.js +178 -0
- package/src/data/ResultSetNew.js +47 -0
- package/src/data/ResultSetObj.js +299 -0
- package/src/data/{ResultSet2.js → ResultSetOriginal.js} +70 -72
- package/src/driver/MongoDriver.js +4 -4
- package/src/graphql/ast/SchemaDecorator.js +2 -2
- package/src/query/Query.js +1 -19
- package/src/service/app.service.js +12 -1
- package/src/data/ResultSet3.js +0 -186
- package/src/graphql/core/.DS_Store +0 -0
- package/src/graphql/core/Field.js +0 -20
- package/src/graphql/core/GraphQL.js +0 -21
- package/src/graphql/core/Model.js +0 -23
- package/src/graphql/core/Node.js +0 -34
- package/src/graphql/core/Schema.js +0 -63
package/package.json
CHANGED
package/src/.DS_Store
CHANGED
|
Binary file
|
package/src/core/Resolver.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { isEmpty } = require('lodash');
|
|
1
2
|
const Model = require('../data/Model');
|
|
2
3
|
const Query = require('../query/Query');
|
|
3
4
|
const ResultSet = require('../data/ResultSet');
|
|
@@ -89,7 +90,7 @@ module.exports = class Resolver {
|
|
|
89
90
|
// This is needed in SF tests...
|
|
90
91
|
const key = model.idKey();
|
|
91
92
|
const { where, method } = query.toDriver();
|
|
92
|
-
if (Object.prototype.hasOwnProperty.call(where, key) && where[key]
|
|
93
|
+
if (Object.prototype.hasOwnProperty.call(where, key) && isEmpty(where[key])) return Promise.resolve(method === 'findMany' ? [] : null);
|
|
93
94
|
|
|
94
95
|
//
|
|
95
96
|
return this.loaders.get(`${model}`).load(query);
|
package/src/data/.DS_Store
CHANGED
|
Binary file
|
package/src/data/Model.js
CHANGED
|
@@ -160,4 +160,24 @@ module.exports = class extends Model {
|
|
|
160
160
|
}, {});
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
|
+
|
|
164
|
+
getShape(serdes = 'deserialize', recursive = true) {
|
|
165
|
+
return this.getSelectFields().map((field) => {
|
|
166
|
+
const [from, to] = serdes === 'serialize' ? [field.getName(), field.getKey()] : [field.getKey(), field.getName()];
|
|
167
|
+
const shape = recursive && field.isEmbedded() ? field.getModelRef().getShape(serdes, recursive) : null;
|
|
168
|
+
return { from, to, type: field.getDataType(), isArray: field.isArray(), shape };
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
shape(data, serdes = (() => { throw new Error('No Sir Sir SerDes!'); }), shape) {
|
|
173
|
+
shape = shape || this.getShape(serdes);
|
|
174
|
+
|
|
175
|
+
return map(data, (doc) => {
|
|
176
|
+
return shape.reduce((prev, { from, to, shape: subShape }) => {
|
|
177
|
+
const value = doc[from];
|
|
178
|
+
if (value === undefined) return prev;
|
|
179
|
+
return Object.assign(prev, { [to]: subShape ? this.shape(value, serdes, subShape) : value });
|
|
180
|
+
}, {});
|
|
181
|
+
});
|
|
182
|
+
}
|
|
163
183
|
};
|
package/src/data/ResultSet.js
CHANGED
|
@@ -2,169 +2,50 @@ const { get } = require('lodash');
|
|
|
2
2
|
const DataService = require('./DataService');
|
|
3
3
|
const { map, ensureArray, keyPaths, mapPromise, toGUID, hashObject } = require('../service/app.service');
|
|
4
4
|
|
|
5
|
+
const modelCache = new WeakMap();
|
|
6
|
+
|
|
5
7
|
module.exports = class ResultSet {
|
|
6
8
|
constructor(query, data, adjustForPagination = true) {
|
|
7
9
|
if (data == null) return data;
|
|
8
|
-
const { resolver, model, sort, first, after, last, before } = query.toObject();
|
|
9
|
-
const fields = model.getFields().filter(f => f.getName() !== 'id');
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
if (doc == null || typeof doc !== 'object') return doc;
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
const cache = new Map();
|
|
16
|
-
|
|
17
|
-
const definition = fields.reduce((prev, field) => {
|
|
18
|
-
const key = field.getKey();
|
|
19
|
-
const name = field.getName();
|
|
20
|
-
const $name = `$${name}`;
|
|
21
|
-
const value = doc[key];
|
|
22
|
-
|
|
23
|
-
// Field attributes
|
|
24
|
-
prev[name] = {
|
|
25
|
-
get() {
|
|
26
|
-
if (cache.has(name)) return cache.get(name);
|
|
27
|
-
let $value = field.deserialize(query, value);
|
|
28
|
-
$value = $value != null && field.isEmbedded() ? new ResultSet(query.model(field.getModelRef()), $value, false) : $value;
|
|
29
|
-
cache.set(name, $value);
|
|
30
|
-
return $value;
|
|
31
|
-
},
|
|
32
|
-
set($value) {
|
|
33
|
-
cache.set(name, $value);
|
|
34
|
-
},
|
|
35
|
-
enumerable: true,
|
|
36
|
-
configurable: true, // Allows things like delete
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// Hydrated field attributes
|
|
40
|
-
prev[`$${name}`] = {
|
|
41
|
-
get() {
|
|
42
|
-
return (args = {}) => {
|
|
43
|
-
// Ensure where clause
|
|
44
|
-
args.where = args.where || {};
|
|
45
|
-
|
|
46
|
-
// Cache
|
|
47
|
-
const cacheKey = `${$name}-${hashObject(args)}`;
|
|
48
|
-
if (cache.has(cacheKey)) return cache.get(cacheKey);
|
|
49
|
-
|
|
50
|
-
const promise = new Promise((resolve, reject) => {
|
|
51
|
-
(() => {
|
|
52
|
-
const $value = this[name];
|
|
53
|
-
|
|
54
|
-
if (field.isScalar() || field.isEmbedded()) return Promise.resolve($value);
|
|
55
|
-
|
|
56
|
-
const modelRef = field.getModelRef();
|
|
57
|
-
|
|
58
|
-
if (field.isArray()) {
|
|
59
|
-
if (field.isVirtual()) {
|
|
60
|
-
args.where[[field.getVirtualField()]] = this.id; // Is where[[field.getVirtualField()]] correct?
|
|
61
|
-
return resolver.match(modelRef).merge(args).many();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Not a "required" query + strip out nulls
|
|
65
|
-
args.where.id = $value;
|
|
66
|
-
return resolver.match(modelRef).merge(args).many();
|
|
67
|
-
}
|
|
11
|
+
const { resolver, model, sort, first, after, last, before } = query.toObject();
|
|
68
12
|
|
|
69
|
-
|
|
70
|
-
args.where[[field.getVirtualField()]] = this.id;
|
|
71
|
-
return resolver.match(modelRef).merge(args).one();
|
|
72
|
-
}
|
|
13
|
+
ResultSet.ensureModelCache(model);
|
|
73
14
|
|
|
74
|
-
|
|
75
|
-
})().then((results) => {
|
|
76
|
-
if (results == null) return field.resolve(query, results); // Allow field to determine
|
|
77
|
-
return mapPromise(results, result => field.resolve(query, result)).then(() => results); // Resolve the inside fields but still return "results"!!!!
|
|
78
|
-
}).then((resolved) => {
|
|
79
|
-
resolve(resolved);
|
|
80
|
-
}).catch((e) => {
|
|
81
|
-
reject(e);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
cache.set(cacheKey, promise);
|
|
86
|
-
return promise;
|
|
87
|
-
};
|
|
88
|
-
},
|
|
89
|
-
enumerable: false,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
// Field count (let's assume it's a Connection Type - meaning dont try with anything else)
|
|
93
|
-
prev[`$${name}:count`] = {
|
|
94
|
-
get() {
|
|
95
|
-
return (q = {}) => {
|
|
96
|
-
q.where = q.where || {};
|
|
97
|
-
if (field.isVirtual()) q.where[field.getVirtualField()] = this.id;
|
|
98
|
-
else q.where.id = this[name];
|
|
99
|
-
return resolver.match(field.getModelRef()).merge(q).count();
|
|
100
|
-
};
|
|
101
|
-
},
|
|
102
|
-
enumerable: false,
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
return prev;
|
|
106
|
-
}, {
|
|
107
|
-
id: {
|
|
108
|
-
get() { return doc.id || doc[model.idKey()]; },
|
|
109
|
-
set(id) { doc.id = id; }, // Embedded array of documents need to set id
|
|
110
|
-
enumerable: true,
|
|
111
|
-
},
|
|
15
|
+
const { template, fieldDefs } = modelCache.get(model);
|
|
112
16
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
enumerable: false,
|
|
116
|
-
},
|
|
17
|
+
const rs = map(data, (doc) => {
|
|
18
|
+
if (doc == null || typeof doc !== 'object') return doc;
|
|
117
19
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
20
|
+
const instance = Object.create(template, {
|
|
21
|
+
$$services: {
|
|
22
|
+
value: {
|
|
23
|
+
cache: new Map(),
|
|
24
|
+
data: doc,
|
|
25
|
+
resolver,
|
|
26
|
+
query,
|
|
27
|
+
sort,
|
|
124
28
|
},
|
|
125
29
|
enumerable: false,
|
|
126
30
|
},
|
|
31
|
+
});
|
|
127
32
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
},
|
|
132
|
-
|
|
133
|
-
$$isResultSetItem: {
|
|
134
|
-
value: true,
|
|
135
|
-
enumerable: false,
|
|
136
|
-
},
|
|
137
|
-
|
|
138
|
-
$$save: {
|
|
139
|
-
get() { return input => resolver.match(model).id(this.id).save({ ...this, ...input }); },
|
|
140
|
-
enumerable: false,
|
|
33
|
+
return new Proxy(instance, {
|
|
34
|
+
ownKeys(target) {
|
|
35
|
+
return Reflect.ownKeys(target).concat('id', fieldDefs.map(d => d.name));
|
|
141
36
|
},
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
get() { return () => resolver.match(model).id(this.id).remove(); },
|
|
145
|
-
enumerable: false,
|
|
37
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
38
|
+
return (prop === 'id' || fieldDefs.find(el => el.name === prop)) ? { enumerable: true, configurable: true } : Reflect.getOwnPropertyDescriptor(target, prop);
|
|
146
39
|
},
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
get() { return () => resolver.match(model).id(this.id).delete(); },
|
|
150
|
-
enumerable: false,
|
|
40
|
+
getPrototypeOf() {
|
|
41
|
+
return { $$services: instance.$$services };
|
|
151
42
|
},
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (value === undefined) return prev;
|
|
157
|
-
prev[key] = get(value, '$$isResultSet') ? value.toObject() : value;
|
|
158
|
-
return prev;
|
|
159
|
-
}, {}));
|
|
160
|
-
},
|
|
161
|
-
enumerable: false,
|
|
162
|
-
configurable: true,
|
|
43
|
+
deleteProperty(target, prop) {
|
|
44
|
+
const { key = prop } = fieldDefs.find(d => d.name === prop);
|
|
45
|
+
delete instance[prop];
|
|
46
|
+
delete instance.$$services.data[key];
|
|
163
47
|
},
|
|
164
48
|
});
|
|
165
|
-
|
|
166
|
-
// Create and return ResultSetItem
|
|
167
|
-
return Object.defineProperties({}, definition);
|
|
168
49
|
});
|
|
169
50
|
|
|
170
51
|
let hasNextPage = false;
|
|
@@ -202,4 +83,177 @@ module.exports = class ResultSet {
|
|
|
202
83
|
},
|
|
203
84
|
});
|
|
204
85
|
}
|
|
86
|
+
|
|
87
|
+
static ensureModelCache(model) {
|
|
88
|
+
if (!modelCache.has(model)) {
|
|
89
|
+
const fields = model.getFields().filter(f => f.getName() !== 'id');
|
|
90
|
+
|
|
91
|
+
const fieldDefs = fields.map(field => ({
|
|
92
|
+
field,
|
|
93
|
+
key: field.getKey(),
|
|
94
|
+
name: field.getName(),
|
|
95
|
+
isArray: field.isArray(),
|
|
96
|
+
isScalar: field.isScalar(),
|
|
97
|
+
isVirtual: field.isVirtual(),
|
|
98
|
+
isRequired: field.isRequired(),
|
|
99
|
+
isEmbedded: field.isEmbedded(),
|
|
100
|
+
modelRef: field.getModelRef(),
|
|
101
|
+
virtualField: field.getVirtualField(),
|
|
102
|
+
// deserialize: field.deserialize.bind(field),
|
|
103
|
+
// fieldResolve: field.resolve.bind(field),
|
|
104
|
+
get useDefaultResolver() { return Boolean((this.isScalar || this.isEmbedded) && !field.getResolvers().length); },
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
const template = ResultSet.makeModelTemplate(model, fieldDefs);
|
|
108
|
+
|
|
109
|
+
modelCache.set(model, { template, fieldDefs });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static makeModelTemplate(model, fieldDefs) {
|
|
114
|
+
const definition = fieldDefs.reduce((prev, fieldDef) => {
|
|
115
|
+
const { field, key, name, isArray, isScalar, isVirtual, isRequired, isEmbedded, modelRef, virtualField, useDefaultResolver } = fieldDef;
|
|
116
|
+
const $name = `$${name}`;
|
|
117
|
+
|
|
118
|
+
// Deserialized field attributes
|
|
119
|
+
prev[name] = {
|
|
120
|
+
get() {
|
|
121
|
+
if (this.$$services.cache.has(name)) return this.$$services.cache.get(name);
|
|
122
|
+
let $value = field.deserialize(this.$$services.query, this.$$services.data[key]);
|
|
123
|
+
if ($value != null && isEmbedded) {
|
|
124
|
+
$value = new ResultSet(this.$$services.query.model(modelRef), $value, false);
|
|
125
|
+
}
|
|
126
|
+
this.$$services.cache.set(name, $value);
|
|
127
|
+
return $value;
|
|
128
|
+
},
|
|
129
|
+
set($value) {
|
|
130
|
+
this.$$services.cache.set(name, $value);
|
|
131
|
+
},
|
|
132
|
+
enumerable: true,
|
|
133
|
+
configurable: true, // Allows things like delete
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Fully deserialized, hydrated, and resolved field attributes
|
|
137
|
+
prev[$name] = {
|
|
138
|
+
get() {
|
|
139
|
+
return (args = {}) => {
|
|
140
|
+
// Grab deserialized value
|
|
141
|
+
const $value = this[name];
|
|
142
|
+
|
|
143
|
+
// Default resolver return immediately!
|
|
144
|
+
if (useDefaultResolver) return $value;
|
|
145
|
+
|
|
146
|
+
// There are FIELD resolvers to run
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
return new Promise((res) => {
|
|
149
|
+
// Scalars and Embeds do not need DB lookup
|
|
150
|
+
if (isScalar || isEmbedded) return res($value);
|
|
151
|
+
|
|
152
|
+
// Ensure where clause for DB lookup
|
|
153
|
+
args.where = args.where || {};
|
|
154
|
+
|
|
155
|
+
if (isArray) {
|
|
156
|
+
if (isVirtual) {
|
|
157
|
+
args.where[[virtualField]] = this.id; // Is where[[virtualField]] correct?
|
|
158
|
+
return res(this.$$services.resolver.match(modelRef).merge(args).many());
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Not a "required" query + strip out nulls
|
|
162
|
+
args.where.id = $value;
|
|
163
|
+
return res(this.$$services.resolver.match(modelRef).merge(args).many());
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (isVirtual) {
|
|
167
|
+
args.where[[virtualField]] = this.id;
|
|
168
|
+
return res(this.$$services.resolver.match(modelRef).merge(args).one());
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return res(this.$$services.resolver.match(modelRef).id($value).one({ required: isRequired }));
|
|
172
|
+
}).then((results) => {
|
|
173
|
+
if (results == null) return field.resolve(this.$$services.query, results); // Allow field to determine
|
|
174
|
+
return mapPromise(results, result => field.resolve(this.$$services.query, result)).then(() => results); // Resolve the inside fields but still return "results"!!!!
|
|
175
|
+
}).then(resolve).catch(reject);
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
},
|
|
179
|
+
enumerable: false,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Field count (let's assume it's a Connection Type - meaning dont try with anything else)
|
|
183
|
+
prev[`${$name}:count`] = {
|
|
184
|
+
get() {
|
|
185
|
+
return (q = {}) => {
|
|
186
|
+
q.where = q.where || {};
|
|
187
|
+
if (isVirtual) q.where[virtualField] = this.id;
|
|
188
|
+
else q.where.id = this[name];
|
|
189
|
+
return this.$$services.resolver.match(modelRef).merge(q).count();
|
|
190
|
+
};
|
|
191
|
+
},
|
|
192
|
+
enumerable: false,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return prev;
|
|
196
|
+
}, {
|
|
197
|
+
id: {
|
|
198
|
+
get() { return this.$$services.data.id || this.$$services.data[model.idKey()]; },
|
|
199
|
+
set(id) { this.$$services.data.id = id; }, // Embedded array of documents need to set id
|
|
200
|
+
enumerable: true,
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
$id: {
|
|
204
|
+
get() { return toGUID(model.getName(), this.id); },
|
|
205
|
+
enumerable: false,
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
$$cursor: {
|
|
209
|
+
get() {
|
|
210
|
+
const sortPaths = keyPaths(this.$$services.sort);
|
|
211
|
+
const sortValues = sortPaths.reduce((prv, path) => Object.assign(prv, { [path]: get(this, path) }), {});
|
|
212
|
+
const sortJSON = JSON.stringify(sortValues);
|
|
213
|
+
return Buffer.from(sortJSON).toString('base64');
|
|
214
|
+
},
|
|
215
|
+
enumerable: false,
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
$$model: {
|
|
219
|
+
value: model,
|
|
220
|
+
enumerable: false,
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
$$isResultSetItem: {
|
|
224
|
+
value: true,
|
|
225
|
+
enumerable: false,
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
$$save: {
|
|
229
|
+
get() { return input => this.$$services.resolver.match(model).id(this.id).save({ ...this, ...input }); },
|
|
230
|
+
enumerable: false,
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
$$remove: {
|
|
234
|
+
get() { return () => this.$$services.resolver.match(model).id(this.id).remove(); },
|
|
235
|
+
enumerable: false,
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
$$delete: {
|
|
239
|
+
get() { return () => this.$$services.resolver.match(model).id(this.id).delete(); },
|
|
240
|
+
enumerable: false,
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
toObject: {
|
|
244
|
+
get() {
|
|
245
|
+
return () => map(this, obj => Object.entries(obj).reduce((prev, [key, value]) => {
|
|
246
|
+
if (value === undefined) return prev;
|
|
247
|
+
prev[key] = get(value, '$$isResultSet') ? value.toObject() : value;
|
|
248
|
+
return prev;
|
|
249
|
+
}, {}));
|
|
250
|
+
},
|
|
251
|
+
enumerable: false,
|
|
252
|
+
configurable: true,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// return Object.defineProperties({}, definition);
|
|
257
|
+
return Object.create(null, definition);
|
|
258
|
+
}
|
|
205
259
|
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
const { get } = require('lodash');
|
|
2
|
+
const { map, keyPaths, mapPromise, toGUID, hashObject } = require('../service/app.service');
|
|
3
|
+
|
|
4
|
+
module.exports = class ResultSetItem {
|
|
5
|
+
constructor(model) {
|
|
6
|
+
const fields = model.getFields().filter(f => f.getName() !== 'id');
|
|
7
|
+
this.resultSetItem = this.createResultSetItem(fields);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
create(query, data) {
|
|
11
|
+
if (data == null || typeof data !== 'object') return data;
|
|
12
|
+
Object.assign(this, query.toObject());
|
|
13
|
+
this.data = data;
|
|
14
|
+
this.query = query;
|
|
15
|
+
return this.resultSetItem;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
createResultSetItem(fields) {
|
|
19
|
+
const self = this;
|
|
20
|
+
const cache = new Map();
|
|
21
|
+
|
|
22
|
+
const definition = fields.reduce((prev, field) => {
|
|
23
|
+
const key = field.getKey();
|
|
24
|
+
const name = field.getName();
|
|
25
|
+
const $name = `$${name}`;
|
|
26
|
+
|
|
27
|
+
// Deserialized field attributes
|
|
28
|
+
prev[name] = {
|
|
29
|
+
get() {
|
|
30
|
+
const value = self.data[key];
|
|
31
|
+
if (cache.has(name)) return cache.get(name);
|
|
32
|
+
let $value = field.deserialize(self.query, value);
|
|
33
|
+
if ($value != null && field.isEmbedded()) {
|
|
34
|
+
const modelRef = field.getModelRef();
|
|
35
|
+
const newQuery = self.query.model(modelRef);
|
|
36
|
+
$value = map($value, $v => modelRef.getResultSetItem().create(newQuery, $v));
|
|
37
|
+
}
|
|
38
|
+
cache.set(name, $value);
|
|
39
|
+
return $value;
|
|
40
|
+
},
|
|
41
|
+
set($value) {
|
|
42
|
+
cache.set(name, $value);
|
|
43
|
+
},
|
|
44
|
+
enumerable: true,
|
|
45
|
+
configurable: true, // Allows things like delete
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Fully deserialized, hydrated, and resolved field attributes
|
|
49
|
+
prev[$name] = {
|
|
50
|
+
get() {
|
|
51
|
+
return (args = {}) => {
|
|
52
|
+
// Ensure where clause
|
|
53
|
+
args.where = args.where || {};
|
|
54
|
+
|
|
55
|
+
// Cache
|
|
56
|
+
const cacheKey = `${$name}-${hashObject(args)}`;
|
|
57
|
+
if (cache.has(cacheKey)) return cache.get(cacheKey);
|
|
58
|
+
|
|
59
|
+
const promise = new Promise((resolve, reject) => {
|
|
60
|
+
(() => {
|
|
61
|
+
const $value = this[name];
|
|
62
|
+
|
|
63
|
+
if (field.isScalar() || field.isEmbedded()) return Promise.resolve($value);
|
|
64
|
+
|
|
65
|
+
const modelRef = field.getModelRef();
|
|
66
|
+
|
|
67
|
+
if (field.isArray()) {
|
|
68
|
+
if (field.isVirtual()) {
|
|
69
|
+
args.where[[field.getVirtualField()]] = this.id; // Is where[[field.getVirtualField()]] correct?
|
|
70
|
+
return self.resolver.match(modelRef).merge(args).many();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Not a "required" query + strip out nulls
|
|
74
|
+
args.where.id = $value;
|
|
75
|
+
return self.resolver.match(modelRef).merge(args).many();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (field.isVirtual()) {
|
|
79
|
+
args.where[[field.getVirtualField()]] = this.id;
|
|
80
|
+
return self.resolver.match(modelRef).merge(args).one();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return self.resolver.match(modelRef).id($value).one({ required: field.isRequired() });
|
|
84
|
+
})().then((results) => {
|
|
85
|
+
if (results == null) return field.resolve(self.query, results); // Allow field to determine
|
|
86
|
+
return mapPromise(results, result => field.resolve(self.query, result)).then(() => results); // Resolve the inside fields but still return "results"!!!!
|
|
87
|
+
}).then((resolved) => {
|
|
88
|
+
resolve(resolved);
|
|
89
|
+
}).catch((e) => {
|
|
90
|
+
reject(e);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
cache.set(cacheKey, promise);
|
|
95
|
+
return promise;
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
enumerable: false,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Field count (let's assume it's a Connection Type - meaning dont try with anything else)
|
|
102
|
+
prev[`${$name}:count`] = {
|
|
103
|
+
get() {
|
|
104
|
+
return (q = {}) => {
|
|
105
|
+
q.where = q.where || {};
|
|
106
|
+
if (field.isVirtual()) q.where[field.getVirtualField()] = this.id;
|
|
107
|
+
else q.where.id = this[name];
|
|
108
|
+
return self.resolver.match(field.getModelRef()).merge(q).count();
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
enumerable: false,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return prev;
|
|
115
|
+
}, {
|
|
116
|
+
id: {
|
|
117
|
+
get() { return self.data.id || self.data[self.model.idKey()]; },
|
|
118
|
+
set(id) { self.data.id = id; }, // Embedded array of documents need to set id
|
|
119
|
+
enumerable: true,
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
$id: {
|
|
123
|
+
get() { return toGUID(self.model.getName(), this.id); },
|
|
124
|
+
enumerable: false,
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
$$cursor: {
|
|
128
|
+
get() {
|
|
129
|
+
const sortPaths = keyPaths(self.sort);
|
|
130
|
+
const sortValues = sortPaths.reduce((prv, path) => Object.assign(prv, { [path]: get(this, path) }), {});
|
|
131
|
+
const sortJSON = JSON.stringify(sortValues);
|
|
132
|
+
return Buffer.from(sortJSON).toString('base64');
|
|
133
|
+
},
|
|
134
|
+
enumerable: false,
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
$$model: {
|
|
138
|
+
value: self.model,
|
|
139
|
+
enumerable: false,
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
$$isResultSetItem: {
|
|
143
|
+
value: true,
|
|
144
|
+
enumerable: false,
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
$$save: {
|
|
148
|
+
get() { return input => self.resolver.match(self.model).id(this.id).save({ ...this, ...input }); },
|
|
149
|
+
enumerable: false,
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
$$remove: {
|
|
153
|
+
get() { return () => self.resolver.match(self.model).id(this.id).remove(); },
|
|
154
|
+
enumerable: false,
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
$$delete: {
|
|
158
|
+
get() { return () => self.resolver.match(self.model).id(this.id).delete(); },
|
|
159
|
+
enumerable: false,
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
toObject: {
|
|
163
|
+
get() {
|
|
164
|
+
return () => map(this, obj => Object.entries(obj).reduce((prev, [key, value]) => {
|
|
165
|
+
if (value === undefined) return prev;
|
|
166
|
+
prev[key] = get(value, '$$isResultSet') ? value.toObject() : value;
|
|
167
|
+
return prev;
|
|
168
|
+
}, {}));
|
|
169
|
+
},
|
|
170
|
+
enumerable: false,
|
|
171
|
+
configurable: true,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Create and return ResultSetItem
|
|
176
|
+
return Object.defineProperties({}, definition);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const { get } = require('lodash');
|
|
2
|
+
const DataService = require('./DataService');
|
|
3
|
+
const { map, ensureArray } = require('../service/app.service');
|
|
4
|
+
|
|
5
|
+
module.exports = class ResultSet {
|
|
6
|
+
constructor(query, data, adjustForPagination = true) {
|
|
7
|
+
if (data == null) return data;
|
|
8
|
+
|
|
9
|
+
const { model, first, after, last, before } = query.toObject();
|
|
10
|
+
const rs = map(data, doc => model.getResultSetItem().create(query, doc));
|
|
11
|
+
|
|
12
|
+
let hasNextPage = false;
|
|
13
|
+
let hasPreviousPage = false;
|
|
14
|
+
if (adjustForPagination && rs.length) (({ hasPreviousPage, hasNextPage } = DataService.paginateResultSet(rs, first, after, last, before)));
|
|
15
|
+
|
|
16
|
+
return Object.defineProperties(rs, {
|
|
17
|
+
$$pageInfo: {
|
|
18
|
+
get() {
|
|
19
|
+
const edges = ensureArray(rs);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
startCursor: get(edges, '0.$$cursor', ''),
|
|
23
|
+
endCursor: get(edges, `${edges.length - 1}.$$cursor`, ''),
|
|
24
|
+
hasPreviousPage,
|
|
25
|
+
hasNextPage,
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
enumerable: false,
|
|
29
|
+
},
|
|
30
|
+
$$isResultSet: {
|
|
31
|
+
value: true,
|
|
32
|
+
enumerable: false,
|
|
33
|
+
},
|
|
34
|
+
toObject: {
|
|
35
|
+
get() {
|
|
36
|
+
return () => map(this, doc => Object.entries(doc).reduce((prev, [key, value]) => {
|
|
37
|
+
if (value === undefined) return prev;
|
|
38
|
+
prev[key] = get(value, '$$isResultSet') ? value.toObject() : value;
|
|
39
|
+
return prev;
|
|
40
|
+
}, {}));
|
|
41
|
+
},
|
|
42
|
+
enumerable: false,
|
|
43
|
+
configurable: true,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|