@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.
@@ -251,7 +251,7 @@ module.exports = class Query {
251
251
  return {
252
252
  isNative: Boolean(this.props.native),
253
253
  model: model.getKey(),
254
- schema: Query.getSchema(model),
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
- return get(flags, 'novalidate') ? this.resolver.resolve(query) : model.validate(query, $input).then(() => this.resolver.resolve(query));
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
- async resolve() {
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 { autograph } = context;
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, info);
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, info),
165
- pageInfo: () => resolver.query(context, model, args, info),
166
- count: () => resolver.count(context, model, args, info),
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, info);
170
- case 'create': return resolver.create(context, model, args, { fields: GraphqlFields(info, {}, { processArguments: true }) });
171
- case 'update': return resolver.update(context, model, args, { fields: GraphqlFields(info, {}, { processArguments: true }) });
172
- case 'delete': return resolver.delete(context, model, args, { fields: GraphqlFields(info, {}, { processArguments: true }) });
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, embeds = []) => {
95
+ exports.makeQueryResolver = (name, model, resolver) => {
361
96
  const obj = {};
362
- const [field] = embeds.slice(-1);
363
97
 
364
- if ((!field || field.hasFieldScope('r')) && model.hasGQLScope('r')) {
365
- obj[`get${name}`] = resolveQuery('get', name, resolver, model, embeds);
366
- obj[`find${name}`] = resolveQuery('find', name, resolver, model, embeds);
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 Object.assign(obj, makeEmbeddedResolver(model, resolver, 'query', embeds));
103
+ return obj;
370
104
  };
371
105
 
372
- exports.makeMutationResolver = (name, model, resolver, embeds = []) => {
106
+ exports.makeMutationResolver = (name, model, resolver) => {
373
107
  const obj = {};
374
- const [field] = embeds.slice(-1);
375
108
 
376
- if ((!field || field.hasFieldScope('c')) && model.hasGQLScope('c')) obj[`create${name}`] = resolveQuery('create', name, resolver, model, embeds);
377
- if ((!field || field.hasFieldScope('u')) && model.hasGQLScope('u')) obj[`update${name}`] = resolveQuery('update', name, resolver, model, embeds);
378
- if ((!field || field.hasFieldScope('d')) && model.hasGQLScope('d')) obj[`delete${name}`] = resolveQuery('delete', name, resolver, model, embeds);
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 Object.assign(obj, makeEmbeddedResolver(model, resolver, 'mutation', embeds));
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 = [
@@ -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
- };