@coderich/autograph 0.8.16 → 0.9.3
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 +4 -0
- package/package.json +1 -1
- package/src/core/Resolver.js +14 -7
- package/src/data/Field.js +10 -0
- package/src/data/Model.js +12 -2
- package/src/data/ResultSet.js +0 -160
- package/src/data/ResultSet2.js +210 -0
- package/src/data/ResultSet3.js +186 -0
- package/src/graphql/ast/Node.js +2 -2
- package/src/graphql/extension/api.js +64 -8
- package/src/graphql/extension/type.js +8 -2
- package/src/service/decorator.service.js +13 -0
- package/src/service/event.service.js +11 -9
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
package/src/core/Resolver.js
CHANGED
|
@@ -7,14 +7,10 @@ const QueryBuilder = require('../query/QueryBuilder');
|
|
|
7
7
|
|
|
8
8
|
module.exports = class Resolver {
|
|
9
9
|
constructor(schema, context = {}) {
|
|
10
|
-
|
|
10
|
+
this.models = schema.getModels();
|
|
11
11
|
this.schema = schema;
|
|
12
12
|
this.context = context;
|
|
13
|
-
this.loaders = models.reduce((prev, model) => prev.set(model
|
|
14
|
-
|
|
15
|
-
// DataLoader Proxy Methods
|
|
16
|
-
this.clear = model => this.loaders.get(model).clearAll();
|
|
17
|
-
this.clearAll = () => models.forEach(model => this.loaders.get(model).clearAll());
|
|
13
|
+
this.loaders = this.models.reduce((prev, model) => prev.set(`${model}`, new DataLoader(this, model)), new Map());
|
|
18
14
|
|
|
19
15
|
//
|
|
20
16
|
this.getSchema = () => this.schema;
|
|
@@ -66,7 +62,7 @@ module.exports = class Resolver {
|
|
|
66
62
|
if (Object.prototype.hasOwnProperty.call(where, key) && where[key] == null) return Promise.resolve(null);
|
|
67
63
|
|
|
68
64
|
//
|
|
69
|
-
return this.loaders.get(model).load(query);
|
|
65
|
+
return this.loaders.get(`${model}`).load(query);
|
|
70
66
|
}
|
|
71
67
|
}
|
|
72
68
|
}
|
|
@@ -93,4 +89,15 @@ module.exports = class Resolver {
|
|
|
93
89
|
toResultSet(model, data) {
|
|
94
90
|
return new ResultSet(new Query({ model: this.toModel(model), resolver: this }), data);
|
|
95
91
|
}
|
|
92
|
+
|
|
93
|
+
// DataLoader Proxy Methods
|
|
94
|
+
clear(model) {
|
|
95
|
+
this.loaders.get(`${model}`).clearAll();
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
clearAll() {
|
|
100
|
+
this.models.forEach(model => this.clear(model));
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
96
103
|
};
|
package/src/data/Field.js
CHANGED
|
@@ -137,4 +137,14 @@ module.exports = class extends Field {
|
|
|
137
137
|
return uvl(this.cast(results.pop()), value);
|
|
138
138
|
});
|
|
139
139
|
}
|
|
140
|
+
|
|
141
|
+
tform(query, value) {
|
|
142
|
+
// Determine transformers
|
|
143
|
+
const transformers = this.getTransformers();
|
|
144
|
+
|
|
145
|
+
// Transform
|
|
146
|
+
return transformers.reduce((prev, transformer) => {
|
|
147
|
+
return transformer(this, prev, query);
|
|
148
|
+
}, this.cast(value));
|
|
149
|
+
}
|
|
140
150
|
};
|
package/src/data/Model.js
CHANGED
|
@@ -115,7 +115,7 @@ module.exports = class extends Model {
|
|
|
115
115
|
// Transform all the data
|
|
116
116
|
return map(data, (doc) => {
|
|
117
117
|
// We want the appendFields + those in the data, deduped
|
|
118
|
-
const fields = [...new Set(appendFields.concat(Object.keys(doc).map(k => this.getField(k))))].filter(
|
|
118
|
+
const fields = [...new Set(appendFields.concat(Object.keys(doc).map(k => this.getField(k))))].filter(Boolean);
|
|
119
119
|
|
|
120
120
|
// Loop through the fields and delegate (renaming keys appropriately)
|
|
121
121
|
return fields.reduce((prev, field) => {
|
|
@@ -129,7 +129,7 @@ module.exports = class extends Model {
|
|
|
129
129
|
normalize(query, data, serdes = (() => { throw new Error('No Sir Sir SerDes!'); }), keysOnly = false) {
|
|
130
130
|
// Transform all the data
|
|
131
131
|
return map(data, (doc) => {
|
|
132
|
-
const fields = Object.keys(doc).map(k => this.getField(k)).filter(
|
|
132
|
+
const fields = Object.keys(doc).map(k => this.getField(k)).filter(Boolean);
|
|
133
133
|
|
|
134
134
|
// Loop through the fields and delegate (renaming keys appropriately)
|
|
135
135
|
return fields.reduce((prev, field) => {
|
|
@@ -150,4 +150,14 @@ module.exports = class extends Model {
|
|
|
150
150
|
})));
|
|
151
151
|
}));
|
|
152
152
|
}
|
|
153
|
+
|
|
154
|
+
tform(query, data) {
|
|
155
|
+
return map(data, (doc) => {
|
|
156
|
+
return Object.keys(doc).map(k => this.getField(k)).filter(Boolean).reduce((prev, curr) => {
|
|
157
|
+
const key = curr.getName();
|
|
158
|
+
const value = doc[key];
|
|
159
|
+
return Object.assign(prev, { [key]: curr.tform(query, value) });
|
|
160
|
+
}, {});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
153
163
|
};
|
package/src/data/ResultSet.js
CHANGED
|
@@ -13,161 +13,6 @@ module.exports = class ResultSet {
|
|
|
13
13
|
//
|
|
14
14
|
const cache = new Map();
|
|
15
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
16
|
const definition = fields.reduce((prev, field) => {
|
|
172
17
|
const key = field.getKey();
|
|
173
18
|
const name = field.getName();
|
|
@@ -284,11 +129,6 @@ module.exports = class ResultSet {
|
|
|
284
129
|
enumerable: false,
|
|
285
130
|
},
|
|
286
131
|
|
|
287
|
-
$$data: {
|
|
288
|
-
value: data,
|
|
289
|
-
enumerable: false,
|
|
290
|
-
},
|
|
291
|
-
|
|
292
132
|
$$isResultSetItem: {
|
|
293
133
|
value: true,
|
|
294
134
|
enumerable: false,
|
|
@@ -0,0 +1,210 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
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
|
+
const cache = new Map();
|
|
14
|
+
|
|
15
|
+
const validKeys = [];
|
|
16
|
+
|
|
17
|
+
const definition = {
|
|
18
|
+
get id() { return doc.id || doc[model.idKey()]; },
|
|
19
|
+
get $id() { return toGUID(model.getName(), this.id); },
|
|
20
|
+
get $$data() { return data; },
|
|
21
|
+
get $$model() { return model; },
|
|
22
|
+
get $$isResultSetItem() { return true; },
|
|
23
|
+
get $$save() { return input => resolver.match(model).id(this.id).save({ ...this, ...input }); },
|
|
24
|
+
get $$remove() { return () => resolver.match(model).id(this.id).remove(); },
|
|
25
|
+
get $$delete() { return () => resolver.match(model).id(this.id).delete(); },
|
|
26
|
+
get $$cursor() {
|
|
27
|
+
return () => {
|
|
28
|
+
const sortPaths = keyPaths(sort);
|
|
29
|
+
const sortValues = sortPaths.reduce((prv, path) => Object.assign(prv, { [path]: get(this, path) }), {});
|
|
30
|
+
const sortJSON = JSON.stringify(sortValues);
|
|
31
|
+
return Buffer.from(sortJSON).toString('base64');
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
get toObject() {
|
|
35
|
+
return () => validKeys.reduce((prev, key) => Object.assign(prev, { [key]: this[key] }), {});
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
fields.forEach((field) => {
|
|
40
|
+
const key = field.getKey();
|
|
41
|
+
const name = field.getName();
|
|
42
|
+
const $name = `$${name}`;
|
|
43
|
+
const value = doc[key];
|
|
44
|
+
validKeys.push(name);
|
|
45
|
+
|
|
46
|
+
// Field attributes
|
|
47
|
+
Object.assign(definition, {
|
|
48
|
+
get [name]() {
|
|
49
|
+
let $value = field.deserialize(query, value);
|
|
50
|
+
$value = $value != null && field.isEmbedded() ? new ResultSet(query.model(field.getModelRef()), $value, false) : $value;
|
|
51
|
+
return $value;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Hydrated field attributes
|
|
56
|
+
Object.assign(definition, {
|
|
57
|
+
get [$name]() {
|
|
58
|
+
return (args = {}) => {
|
|
59
|
+
// Ensure where clause
|
|
60
|
+
args.where = args.where || {};
|
|
61
|
+
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
(() => {
|
|
64
|
+
const $value = this[name];
|
|
65
|
+
|
|
66
|
+
if (field.isScalar() || field.isEmbedded()) return Promise.resolve($value);
|
|
67
|
+
|
|
68
|
+
const modelRef = field.getModelRef();
|
|
69
|
+
|
|
70
|
+
if (field.isArray()) {
|
|
71
|
+
if (field.isVirtual()) {
|
|
72
|
+
args.where[[field.getVirtualField()]] = this.id; // Is where[[field.getVirtualField()]] correct?
|
|
73
|
+
return resolver.match(modelRef).merge(args).many();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Not a "required" query + strip out nulls
|
|
77
|
+
args.where.id = $value;
|
|
78
|
+
return resolver.match(modelRef).merge(args).many();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (field.isVirtual()) {
|
|
82
|
+
args.where[[field.getVirtualField()]] = this.id;
|
|
83
|
+
return resolver.match(modelRef).merge(args).one();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return resolver.match(modelRef).id($value).one({ required: field.isRequired() });
|
|
87
|
+
})().then((results) => {
|
|
88
|
+
if (results == null) return field.resolve(query, results); // Allow field to determine
|
|
89
|
+
return mapPromise(results, result => field.resolve(query, result)).then(() => results); // Resolve the inside fields but still return "results"!!!!
|
|
90
|
+
}).then((resolved) => {
|
|
91
|
+
resolve(resolved);
|
|
92
|
+
}).catch((e) => {
|
|
93
|
+
reject(e);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Field count (let's assume it's a Connection Type - meaning dont try with anything else)
|
|
101
|
+
Object.assign(definition, {
|
|
102
|
+
get [`${$name}:count`]() {
|
|
103
|
+
return (q = {}) => {
|
|
104
|
+
q.where = q.where || {};
|
|
105
|
+
if (field.isVirtual()) q.where[field.getVirtualField()] = this.id;
|
|
106
|
+
else q.where.id = this[name];
|
|
107
|
+
return resolver.match(field.getModelRef()).merge(q).count();
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Create and return ResultSetItem
|
|
114
|
+
const idk = new Proxy(definition, {
|
|
115
|
+
get(target, prop, rec) {
|
|
116
|
+
if (cache.has(prop)) return cache.get(prop);
|
|
117
|
+
const value = Reflect.get(target, prop, rec);
|
|
118
|
+
if (typeof value === 'function') return value.bind(target);
|
|
119
|
+
cache.set(prop, value);
|
|
120
|
+
return value;
|
|
121
|
+
},
|
|
122
|
+
set(target, prop, value) {
|
|
123
|
+
cache.set(prop, value);
|
|
124
|
+
return true;
|
|
125
|
+
},
|
|
126
|
+
ownKeys() {
|
|
127
|
+
return validKeys;
|
|
128
|
+
},
|
|
129
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
130
|
+
if (validKeys.indexOf(prop) === -1) {
|
|
131
|
+
return {
|
|
132
|
+
writable: true,
|
|
133
|
+
enumerable: true,
|
|
134
|
+
configurable: true,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
writable: false,
|
|
140
|
+
enumerable: false,
|
|
141
|
+
configurable: false,
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// console.log(idk);
|
|
147
|
+
// // console.log(idk.toObject());
|
|
148
|
+
return idk;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
let hasNextPage = false;
|
|
152
|
+
let hasPreviousPage = false;
|
|
153
|
+
if (adjustForPagination && rs.length) (({ hasPreviousPage, hasNextPage } = DataService.paginateResultSet(rs, first, after, last, before)));
|
|
154
|
+
|
|
155
|
+
return Object.defineProperties(rs, {
|
|
156
|
+
$$pageInfo: {
|
|
157
|
+
get() {
|
|
158
|
+
const edges = ensureArray(rs);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
startCursor: get(edges, '0.$$cursor', ''),
|
|
162
|
+
endCursor: get(edges, `${edges.length - 1}.$$cursor`, ''),
|
|
163
|
+
hasPreviousPage,
|
|
164
|
+
hasNextPage,
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
enumerable: false,
|
|
168
|
+
},
|
|
169
|
+
$$isResultSet: {
|
|
170
|
+
value: true,
|
|
171
|
+
enumerable: false,
|
|
172
|
+
},
|
|
173
|
+
toObject: {
|
|
174
|
+
get() {
|
|
175
|
+
return () => map(this, doc => Object.entries(doc).reduce((prev, [key, value]) => {
|
|
176
|
+
if (value === undefined) return prev;
|
|
177
|
+
prev[key] = get(value, '$$isResultSet') ? value.toObject() : value;
|
|
178
|
+
return prev;
|
|
179
|
+
}, {}));
|
|
180
|
+
},
|
|
181
|
+
enumerable: false,
|
|
182
|
+
configurable: true,
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
};
|
package/src/graphql/ast/Node.js
CHANGED
|
@@ -265,9 +265,9 @@ module.exports = class Node {
|
|
|
265
265
|
switch (this.nodeType) {
|
|
266
266
|
case 'model': {
|
|
267
267
|
if (!this.isMarkedModel()) return '';
|
|
268
|
-
return nvl(uvl(this.getDirectiveArg('model', 'gqlScope'), '
|
|
268
|
+
return nvl(uvl(this.getDirectiveArg('model', 'gqlScope'), 'cruds'), '');
|
|
269
269
|
}
|
|
270
|
-
case 'field': return nvl(uvl(this.getDirectiveArg('field', 'gqlScope'), '
|
|
270
|
+
case 'field': return nvl(uvl(this.getDirectiveArg('field', 'gqlScope'), 'cruds'), '');
|
|
271
271
|
default: return '';
|
|
272
272
|
}
|
|
273
273
|
}
|
|
@@ -3,7 +3,7 @@ const { Kind } = require('graphql');
|
|
|
3
3
|
const ServerResolver = require('../../core/ServerResolver');
|
|
4
4
|
const { ucFirst, fromGUID } = require('../../service/app.service');
|
|
5
5
|
const { findGQLModels } = require('../../service/schema.service');
|
|
6
|
-
const { makeCreateAPI, makeReadAPI, makeUpdateAPI, makeDeleteAPI, makeInputSplice, makeQueryResolver, makeMutationResolver } = require('../../service/decorator.service');
|
|
6
|
+
const { makeCreateAPI, makeReadAPI, makeUpdateAPI, makeDeleteAPI, makeSubscriptionAPI, makeInputSplice, makeQueryResolver, makeMutationResolver } = require('../../service/decorator.service');
|
|
7
7
|
|
|
8
8
|
const interfaceKinds = [Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION];
|
|
9
9
|
|
|
@@ -20,6 +20,7 @@ const getGQLWhereFields = (model) => {
|
|
|
20
20
|
module.exports = (schema) => {
|
|
21
21
|
const resolver = new ServerResolver();
|
|
22
22
|
const allModels = schema.getModels();
|
|
23
|
+
const entityModels = schema.getEntityModels();
|
|
23
24
|
const markedModels = schema.getMarkedModels();
|
|
24
25
|
const createModels = findGQLModels('c', markedModels, allModels);
|
|
25
26
|
const readModels = findGQLModels('r', markedModels, allModels);
|
|
@@ -55,17 +56,56 @@ module.exports = (schema) => {
|
|
|
55
56
|
extend ${interfaceKinds.indexOf(model.getKind()) > -1 ? 'interface' : 'type'} ${model.getName()} {
|
|
56
57
|
${model.getFields().filter(field => field.hasGQLScope('r')).map(field => `${field.getName()}${field.getExtendArgs()}: ${field.getPayloadType()}`)}
|
|
57
58
|
}
|
|
59
|
+
|
|
58
60
|
type ${model.getName()}Connection {
|
|
59
61
|
pageInfo: PageInfo!
|
|
60
62
|
edges: [${model.getName()}Edge]
|
|
61
63
|
count: Int!
|
|
62
64
|
}
|
|
65
|
+
|
|
63
66
|
type ${model.getName()}Edge {
|
|
64
67
|
node: ${model.getName()}
|
|
65
68
|
cursor: String!
|
|
66
69
|
}
|
|
67
70
|
`),
|
|
68
71
|
|
|
72
|
+
...entityModels.filter(model => model.hasGQLScope('s')).map(model => `
|
|
73
|
+
input ${model.getName()}SubscriptionInputFilter {
|
|
74
|
+
when: [SubscriptionWhenEnum!]! = [preEvent, postEvent]
|
|
75
|
+
where: ${model.getName()}SubscriptionInputWhere! = {}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
input ${model.getName()}SubscriptionInputWhere {
|
|
79
|
+
${getGQLWhereFields(model).map(field => `${field.getName()}: ${field.getModelRef() && !field.isFKReference() ? `${ucFirst(field.getDataRef())}InputWhere` : 'AutoGraphMixed'}`)}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
type ${model.getName()}SubscriptionPayload {
|
|
83
|
+
event: ${model.getName()}SubscriptionPayloadEvent
|
|
84
|
+
query: ${model.getName()}SubscriptionQuery
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type ${model.getName()}SubscriptionPayloadEvent {
|
|
88
|
+
crud: SubscriptionCrudEnum!
|
|
89
|
+
data: ${model.getName()}SubscriptionPayloadEventData!
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type ${model.getName()}SubscriptionPayloadEventData {
|
|
93
|
+
${getGQLWhereFields(model).map(field => `${field.getName()}: ${field.isFKReference() ? 'ID' : field.getGQLType()}`)}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface ${model.getName()}SubscriptionQuery {
|
|
97
|
+
${model.getFields().filter(field => field.hasGQLScope('r')).map(field => `${field.getName()}: ${field.getPayloadType()}`)}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
type ${model.getName()}Create implements ${model.getName()}SubscriptionQuery {
|
|
101
|
+
${model.getFields().filter(field => field.hasGQLScope('r')).map(field => `${field.getName()}: ${field.getPayloadType()}`)}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
type ${model.getName()}Update implements ${model.getName()}SubscriptionQuery {
|
|
105
|
+
${model.getFields().filter(field => field.hasGQLScope('r')).map(field => `${field.getName()}: ${field.getPayloadType()}`)}
|
|
106
|
+
}
|
|
107
|
+
`),
|
|
108
|
+
|
|
69
109
|
...spliceModels.map(model => `
|
|
70
110
|
#input ${model.getName()}InputSplice {
|
|
71
111
|
# with: ${model}InputWhere
|
|
@@ -82,14 +122,21 @@ module.exports = (schema) => {
|
|
|
82
122
|
|
|
83
123
|
`type Query {
|
|
84
124
|
node(id: ID!): Node
|
|
85
|
-
${
|
|
125
|
+
${entityModels.map(model => makeReadAPI(model.getName(), model))}
|
|
126
|
+
${entityModels.map(model => makeReadAPI(`${model.getName()}Create`, model))}
|
|
127
|
+
${entityModels.map(model => makeReadAPI(`${model.getName()}Update`, model))}
|
|
86
128
|
}`,
|
|
87
129
|
|
|
88
130
|
`type Mutation {
|
|
89
131
|
_noop: String
|
|
90
|
-
${
|
|
91
|
-
${
|
|
92
|
-
${
|
|
132
|
+
${entityModels.map(model => makeCreateAPI(model.getName(), model))}
|
|
133
|
+
${entityModels.map(model => makeUpdateAPI(model.getName(), model))}
|
|
134
|
+
${entityModels.map(model => makeDeleteAPI(model.getName(), model))}
|
|
135
|
+
}`,
|
|
136
|
+
|
|
137
|
+
`type Subscription {
|
|
138
|
+
_noop: String
|
|
139
|
+
${entityModels.map(model => makeSubscriptionAPI(model.getName(), model))}
|
|
93
140
|
}`,
|
|
94
141
|
]),
|
|
95
142
|
resolvers: readModels.reduce((prev, model) => {
|
|
@@ -120,6 +167,15 @@ module.exports = (schema) => {
|
|
|
120
167
|
});
|
|
121
168
|
}, {});
|
|
122
169
|
|
|
170
|
+
if (model.isEntity() && model.hasGQLScope('s')) {
|
|
171
|
+
prev[`${model.getName()}SubscriptionQuery`] = {
|
|
172
|
+
__resolveType: root => root.__typename, // eslint-disable-line no-underscore-dangle
|
|
173
|
+
...fieldResolvers,
|
|
174
|
+
};
|
|
175
|
+
prev[`${model.getName()}Create`] = fieldResolvers;
|
|
176
|
+
prev[`${model.getName()}Update`] = fieldResolvers;
|
|
177
|
+
}
|
|
178
|
+
|
|
123
179
|
return Object.assign(prev, {
|
|
124
180
|
[modelName]: fieldResolvers,
|
|
125
181
|
[`${modelName}Connection`]: {
|
|
@@ -130,10 +186,10 @@ module.exports = (schema) => {
|
|
|
130
186
|
});
|
|
131
187
|
}, {
|
|
132
188
|
Node: {
|
|
133
|
-
__resolveType: (root, args, context, info) => root.__typename || fromGUID(root.$id)[0],
|
|
189
|
+
__resolveType: (root, args, context, info) => root.__typename || fromGUID(root.$id)[0], // eslint-disable-line no-underscore-dangle
|
|
134
190
|
},
|
|
135
191
|
|
|
136
|
-
Query:
|
|
192
|
+
Query: entityModels.reduce((prev, model) => {
|
|
137
193
|
return Object.assign(prev, makeQueryResolver(model.getName(), model, resolver));
|
|
138
194
|
}, {
|
|
139
195
|
node: (root, args, context, info) => {
|
|
@@ -144,7 +200,7 @@ module.exports = (schema) => {
|
|
|
144
200
|
},
|
|
145
201
|
}),
|
|
146
202
|
|
|
147
|
-
Mutation:
|
|
203
|
+
Mutation: entityModels.reduce((prev, model) => {
|
|
148
204
|
return Object.assign(prev, makeMutationResolver(model.getName(), model, resolver));
|
|
149
205
|
}, {}),
|
|
150
206
|
}),
|
|
@@ -9,9 +9,13 @@ module.exports = (schema) => {
|
|
|
9
9
|
const createdAt = model.getDirectiveArg('model', 'createdAt', 'createdAt');
|
|
10
10
|
const updatedAt = model.getDirectiveArg('model', 'updatedAt', 'updatedAt');
|
|
11
11
|
|
|
12
|
-
if (model.getKind() === 'ObjectTypeDefinition') {
|
|
12
|
+
if (model.getKind() === 'ObjectTypeDefinition' && (id || createdAt || updatedAt)) {
|
|
13
|
+
const interfaces = [];
|
|
14
|
+
if (id) interfaces.push('Node');
|
|
15
|
+
const interfacesGQL = interfaces.length ? ' implements'.concat(' ', interfaces.join(' & ')) : '';
|
|
16
|
+
|
|
13
17
|
return `
|
|
14
|
-
extend type ${modelName}
|
|
18
|
+
extend type ${modelName}${interfacesGQL} {
|
|
15
19
|
${id ? `id: ID! @field(key: "${id}", gqlScope: r)` : ''}
|
|
16
20
|
${createdAt ? `createdAt: AutoGraphDateTime @field(key: "${createdAt}", gqlScope: r)` : ''}
|
|
17
21
|
${updatedAt ? `updatedAt: AutoGraphDateTime @field(key: "${updatedAt}", gqlScope: r)` : ''}
|
|
@@ -23,6 +27,8 @@ module.exports = (schema) => {
|
|
|
23
27
|
}).concat(`
|
|
24
28
|
interface Node { id: ID! }
|
|
25
29
|
enum SortOrderEnum { asc desc }
|
|
30
|
+
enum SubscriptionCrudEnum { create update delete } # Not going to support "read"
|
|
31
|
+
enum SubscriptionWhenEnum { preEvent postEvent }
|
|
26
32
|
`),
|
|
27
33
|
});
|
|
28
34
|
};
|
|
@@ -343,6 +343,19 @@ exports.makeDeleteAPI = (name, model, parent) => {
|
|
|
343
343
|
return gql;
|
|
344
344
|
};
|
|
345
345
|
|
|
346
|
+
exports.makeSubscriptionAPI = (name, model, parent) => {
|
|
347
|
+
let gql = '';
|
|
348
|
+
|
|
349
|
+
if (model.hasGQLScope('s')) {
|
|
350
|
+
gql += `${name} (
|
|
351
|
+
on: [SubscriptionCrudEnum!]! = [create, update, delete]
|
|
352
|
+
filter: ${name}SubscriptionInputFilter
|
|
353
|
+
): ${name}SubscriptionPayload!`;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return gql;
|
|
357
|
+
};
|
|
358
|
+
|
|
346
359
|
// Resolvers
|
|
347
360
|
exports.makeQueryResolver = (name, model, resolver, embeds = []) => {
|
|
348
361
|
const obj = {};
|
|
@@ -13,19 +13,20 @@ const systemEvent = new EventEmitter().setMaxListeners(100).on('system', async (
|
|
|
13
13
|
|
|
14
14
|
//
|
|
15
15
|
exports.createSystemEvent = (name, mixed = {}, thunk = () => {}) => {
|
|
16
|
-
let event;
|
|
17
|
-
let middleware;
|
|
16
|
+
let event = mixed;
|
|
17
|
+
let middleware = () => Promise.resolve();
|
|
18
18
|
const type = ucFirst(name);
|
|
19
19
|
|
|
20
|
-
if (name !== 'Setup') {
|
|
20
|
+
if (name !== 'Setup' && name !== 'Response') {
|
|
21
21
|
const { method, query } = mixed;
|
|
22
|
-
const { resolver, model, meta, doc, id, input, sort, merged, native, root } = query.toObject();
|
|
22
|
+
const { resolver, model, meta, doc, id, input, sort, merged, native, root, crud } = query.toObject();
|
|
23
23
|
|
|
24
24
|
event = {
|
|
25
25
|
context: resolver.getContext(),
|
|
26
26
|
key: `${method}${model}`,
|
|
27
27
|
resolver,
|
|
28
28
|
method,
|
|
29
|
+
crud,
|
|
29
30
|
model,
|
|
30
31
|
meta,
|
|
31
32
|
id,
|
|
@@ -48,17 +49,18 @@ exports.createSystemEvent = (name, mixed = {}, thunk = () => {}) => {
|
|
|
48
49
|
|
|
49
50
|
resolve();
|
|
50
51
|
});
|
|
51
|
-
} else {
|
|
52
|
-
middleware = () => Promise.resolve();
|
|
53
|
-
event = mixed;
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
return systemEvent.emit('system', { type: `pre${type}`, data: event }).then((result) => {
|
|
57
55
|
if (result !== undefined) return result; // Allowing middleware to dictate result
|
|
58
56
|
return middleware().then(thunk);
|
|
59
57
|
}).then((result) => {
|
|
60
|
-
event.
|
|
61
|
-
return systemEvent.emit('system', { type: `post${type}`, data: event }).then(
|
|
58
|
+
event.result = result;
|
|
59
|
+
return systemEvent.emit('system', { type: `post${type}`, data: event }).then((postResult = result) => postResult);
|
|
60
|
+
}).then((result) => {
|
|
61
|
+
if (name === 'Response') return result;
|
|
62
|
+
event.result = result;
|
|
63
|
+
return exports.createSystemEvent('Response', event, (finalResult = result) => finalResult);
|
|
62
64
|
});
|
|
63
65
|
};
|
|
64
66
|
|