@coderich/autograph 0.13.106 → 0.13.108

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.106",
4
+ "version": "0.13.108",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -67,13 +67,11 @@ module.exports = class QueryResolver extends QueryBuilder {
67
67
  }
68
68
  case 'pullOne': {
69
69
  return this.#get(query).then((doc) => {
70
- const [key] = Object.keys(input);
71
- const $query = Object.assign(query.toObject(), { doc });
72
- const args = { query: $query, resolver: this.#resolver, context: this.#context };
73
- const values = get(this.#model.transformers.create.transform(input, args), key, []);
74
- const $doc = Util.pathmap(key, doc, (arr) => {
75
- if (arr == null) return arr;
76
- return arr.filter(el => values.every(v => `${v}` !== `${el}`));
70
+ const [[path, inputs]] = Object.entries(input);
71
+ const [key] = path.split('.');
72
+ const $doc = Util.pathmap(path, doc, (mixed) => { // Pathmap because nested arrays
73
+ if (mixed == null) return mixed;
74
+ return mixed.filter(el => inputs.every(v => `${v}` !== `${el}`));
77
75
  });
78
76
  return this.#resolver.match(this.#model.name).id(doc.id).save({ [key]: get($doc, key) });
79
77
  });
@@ -86,12 +84,21 @@ module.exports = class QueryResolver extends QueryBuilder {
86
84
  }
87
85
  case 'spliceOne': {
88
86
  return this.#get(query).then((doc) => {
89
- const [key] = Object.keys(input);
90
- const $query = Object.assign(query.toObject(), { doc });
91
- const args = { query: $query, resolver: this.#resolver, context: this.#context };
92
- const [find, replace] = get(this.#model.transformers.create.transform(input, args), key);
93
- const $input = { [key]: (get(doc, key) || []).map(el => (`${el}` === `${find}` ? replace : el)) };
94
- return this.#resolver.match(this.#model.name).id(doc.id).save($input);
87
+ const [[path, [find, replace]]] = Object.entries(input);
88
+ const [key] = path.split('.');
89
+ const $doc = Util.pathmap(path, doc, (mixed) => { // Pathmap because nested arrays
90
+ if (mixed == null) return mixed;
91
+ if (Array.isArray(mixed)) return mixed.map(el => (`${el}` === `${find}` ? replace : el));
92
+ if (`${mixed}` === `${find}`) return replace;
93
+ return mixed;
94
+ });
95
+ return this.#resolver.match(this.#model.name).id(doc.id).save({ [key]: get($doc, key) });
96
+ });
97
+ }
98
+ case 'spliceMany': {
99
+ const [[key, values]] = Object.entries(input);
100
+ return this.#find(query).then((docs) => {
101
+ return Promise.all(docs.map(doc => this.#resolver.match(this.#model.name).id(doc.id).splice(key, ...values)));
95
102
  });
96
103
  }
97
104
  case 'deleteOne': {
@@ -123,17 +130,16 @@ module.exports = class QueryResolver extends QueryBuilder {
123
130
  #resolveReferentialIntegrity(doc) {
124
131
  const txn = this.#resolver;
125
132
 
126
- return Util.promiseChain(this.#model.referentialIntegrity.map(({ model, field, path }) => () => {
127
- const { onDelete, isArray, fkField } = field;
133
+ return Util.promiseChain(this.#model.referentialIntegrity.map(({ model, field, isArray, path }) => () => {
134
+ const { onDelete, fkField } = field;
128
135
  const id = doc[fkField];
129
136
  const $path = path.join('.');
130
137
  const where = field.isVirtual ? { [field.model.pkField]: get(doc, field.linkBy) } : { [$path]: id };
131
138
 
132
139
  switch (onDelete) {
133
140
  case 'cascade': return isArray ? txn.match(model).where(where).pull($path, id) : txn.match(model).where(where).remove();
134
- case 'nullify': return txn.match(model).where(where).save({ [$path]: null });
141
+ case 'nullify': return isArray ? txn.match(model).where(where).splice($path, id, null) : txn.match(model).where(where).save({ [$path]: null });
135
142
  case 'restrict': return txn.match(model).where(where).count().then(count => (count ? Promise.reject(new Error('Restricted')) : count));
136
- case 'defer': return Promise.resolve(); // Used for embedded models (could be improved)
137
143
  default: throw new Error(`Unknown onDelete operator: '${onDelete}'`);
138
144
  }
139
145
  }));
@@ -142,6 +142,7 @@ module.exports = class Schema {
142
142
  crud: 'crud', // For use when creating API Queries and Mutations
143
143
  scope: 'crud', // For use when defining types (how it's field.model reference can be used)
144
144
  pkField: 'id',
145
+ isEmbedded: true,
145
146
  isPersistable: true,
146
147
  source: this.#config.dataSources?.default,
147
148
  loader: this.#config.dataLoaders?.default,
@@ -156,6 +157,7 @@ module.exports = class Schema {
156
157
  },
157
158
  directives: {},
158
159
  ignorePaths: [],
160
+ referentialIntegrity: [],
159
161
  toString: () => name,
160
162
  };
161
163
  }
@@ -202,8 +204,12 @@ module.exports = class Schema {
202
204
  } else if (node.kind === Kind.DIRECTIVE) {
203
205
  target.directives[name] = target.directives[name] || {};
204
206
 
205
- if (name === directives.model) model.isMarkedModel = true;
206
- else if (name === directives.index) this.#schema.indexes.push({ model });
207
+ if (name === directives.model) {
208
+ model.isMarkedModel = true;
209
+ model.isEmbedded = false;
210
+ } else if (name === directives.index) {
211
+ this.#schema.indexes.push({ model });
212
+ }
207
213
 
208
214
  node.arguments.forEach((arg) => {
209
215
  const key = arg.name.value;
@@ -521,6 +527,9 @@ module.exports = class Schema {
521
527
  $field.isScalar = scalars.includes($field.type);
522
528
  $field.generator ??= $model.generator;
523
529
 
530
+ // Referential Integrity Setup
531
+ if ($field.onDelete) $field.model.referentialIntegrity.push(...this.#findModelPathsToField($model, $field));
532
+
524
533
  // Merge Enums and Scalar type definitions
525
534
  const enumer = this.#schema.enums[$field.type];
526
535
  const scalar = this.#schema.scalars[$field.type];
@@ -573,15 +582,6 @@ module.exports = class Schema {
573
582
  return { key, name, type, on };
574
583
  });
575
584
 
576
- // Resolve referential integrity
577
- const onDeleteFields = Array.from(new Set(Object.values(this.#schema.models).reduce((prev, curr) => {
578
- return prev.concat(Object.values(curr.fields).filter(el => el.onDelete)).flat();
579
- }, [])));
580
-
581
- Object.values(this.#schema.models).forEach(($model) => {
582
- $model.referentialIntegrity = Schema.#identifyOnDeletes(Object.values(this.#schema.models).filter(m => m.isEntity), onDeleteFields.filter(f => `${f.model}` === `${$model}`));
583
- });
584
-
585
585
  // Helper methods
586
586
  const resolvePathCache = {};
587
587
  this.#schema.resolvePath = (path, prop = 'key') => {
@@ -611,6 +611,8 @@ module.exports = class Schema {
611
611
  },
612
612
  });
613
613
 
614
+ // console.log(this.#schema.models.Person.referentialIntegrity);
615
+
614
616
  // Return schema
615
617
  return this.#schema;
616
618
  }
@@ -639,6 +641,23 @@ module.exports = class Schema {
639
641
  return this.#config.makeExecutableSchema(this.toObject());
640
642
  }
641
643
 
644
+ #findModelPathsToField(model, field) {
645
+ if (!model.isEmbedded) return [{ model, field, path: [`${field}`], isArray: field.isArray }];
646
+
647
+ const arr = [];
648
+
649
+ Object.values(this.#schema.models).forEach((m) => {
650
+ Util.traverse(Object.values(m.fields), (f, info) => {
651
+ const path = info.path.concat(f.name);
652
+ if (f.isEmbedded) return { value: Object.values(f.model.fields), info: { path, isArray: info.isArray || f.isArray } };
653
+ if (f.type === model.name) arr.push({ model: m, field, path: path.concat(`${field}`), isArray: info.isArray || field.isArray || f.isArray });
654
+ return null;
655
+ }, { path: [], isArray: false });
656
+ });
657
+
658
+ return arr;
659
+ }
660
+
642
661
  static #resolveNodeValue(node) {
643
662
  if (node == null) return node;
644
663
 
@@ -661,7 +680,7 @@ module.exports = class Schema {
661
680
 
662
681
  enum AutoGraphIndexEnum { unique }
663
682
  enum AutoGraphAuthzEnum { private protected public } # DELETE WHEN MIGRATED
664
- enum AutoGraphOnDeleteEnum { cascade nullify restrict defer }
683
+ enum AutoGraphOnDeleteEnum { cascade nullify restrict }
665
684
  enum AutoGraphPipelineEnum { ${Object.keys(Pipeline).filter(k => !k.startsWith('$')).join(' ')} }
666
685
 
667
686
  directive @${model}(
@@ -961,26 +980,4 @@ module.exports = class Schema {
961
980
  before: String
962
981
  `;
963
982
  }
964
-
965
- static #findPathToField(model, field, path = []) {
966
- const { target, embeds } = Object.values(model.fields).reduce((prev, curr) => {
967
- if (curr === field) prev.target = curr;
968
- else if (curr.isEmbedded) prev.embeds.push(curr);
969
- return prev;
970
- }, { embeds: [] });
971
-
972
- if (target) return path.concat(target.name);
973
- if (embeds.length) return embeds.map(f => Schema.#findPathToField(f.model, field, path.concat(f.name))).filter(Boolean)[0];
974
- return undefined;
975
- }
976
-
977
- static #identifyOnDeletes(models, fields) {
978
- return models.map((model) => {
979
- return fields.map((field) => {
980
- const path = Schema.#findPathToField(model, field);
981
- if (!path) return null;
982
- return { model, field, path };
983
- });
984
- }).flat().filter(Boolean);
985
- }
986
983
  };