@coderich/autograph 0.13.0 → 0.13.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coderich/autograph",
3
3
  "main": "index.js",
4
- "version": "0.13.0",
4
+ "version": "0.13.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -23,6 +23,7 @@
23
23
  "dataloader": "2.2.2",
24
24
  "deepmerge": "4.3.1",
25
25
  "fill-range": "7.0.1",
26
+ "graphql-parse-resolve-info": "4.13.0",
26
27
  "lodash.get": "4.4.2",
27
28
  "lodash.merge": "4.6.2",
28
29
  "lodash.uniqwith": "4.5.0",
@@ -75,6 +75,7 @@ module.exports = class Query {
75
75
  const { input, where, sort, before, after, isNative, isCursorPaging } = this.#query;
76
76
 
77
77
  const query = this.clone({
78
+ model: this.#model.key,
78
79
  select: Object.values(this.#model.fields).map(field => field.key),
79
80
  input: this.#model.walk(input, node => node.value !== undefined && Object.assign(node, { key: node.field.key })),
80
81
  where: isNative ? where : this.#model.walk(where, node => Object.assign(node, { key: node.field.key })),
@@ -1,5 +1,5 @@
1
1
  const Query = require('./Query');
2
- const { getGQLReturnType, mergeDeep } = require('../service/AppService');
2
+ const { getGQLReturnType, getGQLSelectFields, mergeDeep } = require('../service/AppService');
3
3
 
4
4
  module.exports = class QueryBuilder {
5
5
  #config;
@@ -41,7 +41,9 @@ module.exports = class QueryBuilder {
41
41
  * For use in GraphQL resolver methods to return the "correct" response
42
42
  */
43
43
  resolve(info) {
44
- switch (getGQLReturnType(`${info.returnType}`)) {
44
+ this.info(info);
45
+
46
+ switch (getGQLReturnType(info)) {
45
47
  case 'array': return this.many();
46
48
  case 'number': return this.count();
47
49
  case 'connection': return { count: () => this.count(), edges: () => this.many(), pageInfo: () => this.many() };
@@ -65,6 +67,11 @@ module.exports = class QueryBuilder {
65
67
  return this;
66
68
  }
67
69
 
70
+ info(info) {
71
+ this.select(getGQLSelectFields(this.#query.model, info));
72
+ return this;
73
+ }
74
+
68
75
  native(clause) {
69
76
  this.#propCheck('native', 'id', 'where');
70
77
  this.#query.isNative = true;
@@ -83,7 +90,6 @@ module.exports = class QueryBuilder {
83
90
  }
84
91
 
85
92
  select(...select) {
86
- this.#propCheck('select');
87
93
  select = select.flat();
88
94
  this.#query.select = select;
89
95
  this.#query.args.select = select;
@@ -61,11 +61,19 @@ module.exports = class Schema {
61
61
  * Merge typeDefs and resolvers
62
62
  */
63
63
  merge(schema = {}) {
64
+ // Normalize schema input
64
65
  if (typeof schema === 'string') schema = { typeDefs: schema };
65
66
  else if (schema instanceof Schema) schema = schema.toObject();
66
- const { typeDefs, resolvers } = schema;
67
- if (typeDefs) this.#typeDefs = mergeTypeDefs([parse(typeDefs), this.#typeDefs], { noLocation: true, reverseDirectives: true, onFieldTypeConflict: a => a });
68
- if (resolvers) this.#resolvers = mergeDeep(this.#resolvers, resolvers);
67
+
68
+ if (schema.typeDefs) {
69
+ const typeDefs = Util.ensureArray(schema.typeDefs).map(td => (typeof td === 'string' ? parse(td) : td));
70
+ this.#typeDefs = mergeTypeDefs([typeDefs, this.#typeDefs], { noLocation: true, reverseDirectives: true, onFieldTypeConflict: a => a });
71
+ }
72
+
73
+ if (schema.resolvers) {
74
+ this.#resolvers = mergeDeep(this.#resolvers, schema.resolvers);
75
+ }
76
+
69
77
  return this;
70
78
  }
71
79
 
@@ -75,7 +83,8 @@ module.exports = class Schema {
75
83
  parse() {
76
84
  if (this.#schema) return this.#schema;
77
85
 
78
- this.#schema = { models: {}, indexes: [] };
86
+ // const schema = buildASTSchema(this.#typeDefs);
87
+ this.#schema = { types: {}, models: {}, indexes: [] };
79
88
  let model, field, isField, isList;
80
89
  const thunks = [];
81
90
 
@@ -86,6 +95,8 @@ module.exports = class Schema {
86
95
  if (!allowedKinds.includes(node.kind)) return false;
87
96
 
88
97
  if (modelKinds.includes(node.kind) && !operations.includes(name)) {
98
+ // this.#schema.types[name] = schema.getType(name);
99
+
89
100
  model = this.#schema.models[name] = {
90
101
  name,
91
102
  key: name,
@@ -344,6 +355,7 @@ module.exports = class Schema {
344
355
  scope: AutoGraphMixed #
345
356
  meta: AutoGraphMixed # Custom input "meta" field for mutations
346
357
  source: AutoGraphMixed # Data source (default: "default")
358
+ decorate: AutoGraphMixed # Decorator (default: "default")
347
359
  embed: Boolean # Mark this an embedded model (default false)
348
360
  persist: Boolean # Persist this model (default true)
349
361
  ) on OBJECT | INTERFACE
@@ -381,15 +393,15 @@ module.exports = class Schema {
381
393
 
382
394
  static #api(schema) {
383
395
  // These models are for creating types
384
- const readModels = Object.values(schema.models).filter(model => model.crud.includes('r'));
385
- const createModels = Object.values(schema.models).filter(model => model.crud.includes('c'));
386
- const updateModels = Object.values(schema.models).filter(model => model.crud.includes('u'));
396
+ const readModels = Object.values(schema.models).filter(model => model.crud?.includes('r'));
397
+ const createModels = Object.values(schema.models).filter(model => model.crud?.includes('c'));
398
+ const updateModels = Object.values(schema.models).filter(model => model.crud?.includes('u'));
387
399
 
388
400
  // These are for defining schema queries/mutations
389
401
  const entityModels = Object.values(schema.models).filter(model => model.isEntity);
390
- const queryModels = entityModels.filter(model => model.crud.includes('r'));
391
- const mutationModels = entityModels.filter(model => ['c', 'u', 'd'].some(el => model.crud.includes(el)));
392
- const subscriptionModels = entityModels.filter(model => model.crud.includes('s'));
402
+ const queryModels = entityModels.filter(model => model.crud?.includes('r'));
403
+ const mutationModels = entityModels.filter(model => ['c', 'u', 'd'].some(el => model.crud?.includes(el)));
404
+ const subscriptionModels = entityModels.filter(model => model.crud?.includes('s'));
393
405
 
394
406
  return {
395
407
  typeDefs: `
@@ -415,7 +427,7 @@ module.exports = class Schema {
415
427
  `)}
416
428
 
417
429
  ${readModels.map((model) => {
418
- const fields = Object.values(model.fields).filter(field => field.crud.includes('r'));
430
+ const fields = Object.values(model.fields).filter(field => field.crud?.includes('r'));
419
431
  const connectionFields = fields.filter(field => field.isConnection);
420
432
 
421
433
  return `
@@ -443,7 +455,7 @@ module.exports = class Schema {
443
455
  })}
444
456
 
445
457
  ${createModels.map((model) => {
446
- const fields = Object.values(model.fields).filter(field => field.crud.includes('c') && !field.isVirtual);
458
+ const fields = Object.values(model.fields).filter(field => field.crud?.includes('c') && !field.isVirtual);
447
459
 
448
460
  return `
449
461
  input ${model}InputCreate {
@@ -453,7 +465,7 @@ module.exports = class Schema {
453
465
  })}
454
466
 
455
467
  ${updateModels.map((model) => {
456
- const fields = Object.values(model.fields).filter(field => field.crud.includes('u') && !field.isVirtual);
468
+ const fields = Object.values(model.fields).filter(field => field.crud?.includes('u') && !field.isVirtual);
457
469
 
458
470
  return `
459
471
  input ${model}InputUpdate {
@@ -484,9 +496,9 @@ module.exports = class Schema {
484
496
  ${mutationModels.map((model) => {
485
497
  const api = [];
486
498
  const meta = model.meta ? `meta: ${model.meta}` : '';
487
- if (model.crud.includes('c')) api.push(`create${model}(input: ${model}InputCreate! ${meta}): ${model}!`);
488
- if (model.crud.includes('u')) api.push(`update${model}(id: ID! input: ${model}InputUpdate ${meta}): ${model}!`);
489
- if (model.crud.includes('d')) api.push(`delete${model}(id: ID! ${meta}): ${model}!`);
499
+ if (model.crud?.includes('c')) api.push(`create${model}(input: ${model}InputCreate! ${meta}): ${model}!`);
500
+ if (model.crud?.includes('u')) api.push(`update${model}(id: ID! input: ${model}InputUpdate ${meta}): ${model}!`);
501
+ if (model.crud?.includes('d')) api.push(`delete${model}(id: ID! ${meta}): ${model}!`);
490
502
  return api.join('\n');
491
503
  })}
492
504
  }
@@ -518,12 +530,12 @@ module.exports = class Schema {
518
530
  }, {}),
519
531
  Query: queryModels.reduce((prev, model) => {
520
532
  return Object.assign(prev, {
521
- [`get${model}`]: (doc, args, context, info) => context.autograph.resolver.match(model).args(args).one({ required: true }),
533
+ [`get${model}`]: (doc, args, context, info) => context.autograph.resolver.match(model).args(args).info(info).one({ required: true }),
522
534
  [`find${model}`]: (doc, args, context, info) => {
523
535
  return {
524
- edges: () => context.autograph.resolver.match(model).args(args).many(),
525
- count: () => context.autograph.resolver.match(model).args(args).count(),
526
- pageInfo: () => context.autograph.resolver.match(model).args(args).many(),
536
+ edges: () => context.autograph.resolver.match(model).args(args).info(info).many(),
537
+ count: () => context.autograph.resolver.match(model).args(args).info(info).count(),
538
+ pageInfo: () => context.autograph.resolver.match(model).args(args).info(info).many(),
527
539
  };
528
540
  },
529
541
  });
@@ -532,7 +544,7 @@ module.exports = class Schema {
532
544
  const { id } = args;
533
545
  const [modelName] = fromGUID(id);
534
546
  const model = schema.models[modelName];
535
- return context.autograph.resolver.match(model).id(id).one().then((result) => {
547
+ return context.autograph.resolver.match(model).id(id).info(info).one().then((result) => {
536
548
  if (result == null) return result;
537
549
  result.__typename = modelName; // eslint-disable-line no-underscore-dangle
538
550
  return result;
@@ -541,9 +553,9 @@ module.exports = class Schema {
541
553
  }),
542
554
  ...(mutationModels.length ? {
543
555
  Mutation: mutationModels.reduce((prev, model) => {
544
- if (model.crud.includes('c')) prev[`create${model}`] = (doc, args, context, info) => context.autograph.resolver.match(model).args(args).save(args.input);
545
- if (model.crud.includes('u')) prev[`update${model}`] = (doc, args, context, info) => context.autograph.resolver.match(model).args(args).save(args.input);
546
- if (model.crud.includes('d')) prev[`delete${model}`] = (doc, args, context, info) => context.autograph.resolver.match(model).args(args).delete();
556
+ if (model.crud?.includes('c')) prev[`create${model}`] = (doc, args, context, info) => context.autograph.resolver.match(model).args(args).info(info).save(args.input);
557
+ if (model.crud?.includes('u')) prev[`update${model}`] = (doc, args, context, info) => context.autograph.resolver.match(model).args(args).info(info).save(args.input);
558
+ if (model.crud?.includes('d')) prev[`delete${model}`] = (doc, args, context, info) => context.autograph.resolver.match(model).args(args).info(info).delete();
547
559
  return prev;
548
560
  }, {}),
549
561
  } : {}),
@@ -552,7 +564,7 @@ module.exports = class Schema {
552
564
  [model]: Object.values(model.fields).filter(field => field.model?.isEntity).reduce((prev2, field) => {
553
565
  return Object.assign(prev2, {
554
566
  [field]: (doc, args, context, info) => {
555
- return context.autograph.resolver.match(field.model).where({ [field.linkBy]: doc[field.linkField.name] }).args(args).resolve(info);
567
+ return context.autograph.resolver.match(field.model).where({ [field.linkBy]: doc[field.linkField.name] }).args(args).info(info).resolve(info);
556
568
  },
557
569
  });
558
570
  }, {}),
@@ -1,9 +1,11 @@
1
+ const get = require('lodash.get');
1
2
  const Util = require('@coderich/util');
2
3
  const PicoMatch = require('picomatch');
3
4
  const FillRange = require('fill-range');
4
5
  const ObjectHash = require('object-hash');
5
6
  const ObjectId = require('bson-objectid');
6
7
  const DeepMerge = require('deepmerge');
8
+ const { parseResolveInfo, simplifyParsedResolveInfoFragmentWithType } = require('graphql-parse-resolve-info');
7
9
 
8
10
  exports.isGlob = str => PicoMatch.scan(str).isGlob;
9
11
  exports.globToRegex = (glob, options = {}) => PicoMatch.makeRe(glob, { nocase: true, ...options, expandRange: (a, b) => `(${FillRange(a, b, { toRegex: true })})` });
@@ -26,11 +28,19 @@ exports.finalizeWhereClause = (obj, arrayOp = '$in') => {
26
28
  }, {});
27
29
  };
28
30
 
29
- exports.getGQLReturnType = (returnType) => {
31
+ exports.getGQLReturnType = (info) => {
32
+ const returnType = `${info.returnType}`;
30
33
  const typeMap = { array: /^\[.+\].?$/, connection: /.+Connection!?$/, number: /^(Int|Float)!?$/, scalar: /.*/ };
31
34
  return Object.entries(typeMap).find(([type, pattern]) => returnType.match(pattern))[0];
32
35
  };
33
36
 
37
+ exports.getGQLSelectFields = (model, info) => {
38
+ const parsed = parseResolveInfo(info, { noLocation: true });
39
+ const { fields } = simplifyParsedResolveInfoFragmentWithType(parsed, info.returnType);
40
+ const node = get(fields, `edges.fieldsByTypeName.${model}Edge.node.fieldsByTypeName.${model}`);
41
+ return Object.keys(node || fields);
42
+ };
43
+
34
44
  exports.removeUndefinedDeep = (obj) => {
35
45
  return Util.unflatten(Object.entries(Util.flatten(obj)).reduce((prev, [key, value]) => {
36
46
  return value === undefined ? prev : Object.assign(prev, { [key]: value });