@coderich/autograph 0.13.45 → 0.13.47

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.45",
4
+ "version": "0.13.47",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -1,7 +1,13 @@
1
1
  const Util = require('@coderich/util');
2
2
 
3
3
  module.exports = class Transformer {
4
- #config = { shape: {}, defaults: {}, args: {}, strictSchema: false, keepUndefined: false };
4
+ #config = {
5
+ args: {}, // Arguments passed to each thunk
6
+ shape: {}, // The final shape
7
+ defaults: {}, // Default values applied at beginning of transformation
8
+ strictSchema: false, // If true, will strip away unknown attributes
9
+ keepUndefined: false, // If true, will preserve undefined values
10
+ };
5
11
 
6
12
  #operation = {
7
13
  set: (target, prop, startValue, proxy) => {
@@ -221,6 +221,7 @@ module.exports = class QueryBuilder {
221
221
  const suffix = id || limit === 1 || (crud === 'create' && args.length < 2) ? 'One' : 'Many';
222
222
  let input = suffix === 'One' ? args[0] : args;
223
223
  if (input === undefined) input = {};
224
+ input.id = id;
224
225
  this.#query.args.input = input;
225
226
  return this.terminate(Object.assign(this.#query, {
226
227
  op: `${crud}${suffix}`,
@@ -34,6 +34,10 @@ module.exports = class QueryResolver extends QueryBuilder {
34
34
  }
35
35
  case 'updateOne': {
36
36
  return this.#get(query).then((doc) => {
37
+ // const $input = this.#model.walk(doc, (node) => {
38
+ // if (node.field.defaultValue !== undefined || ['instruct', 'restruct', 'serialize'].some(el => node.field.pipelines[el]?.length)) return node;
39
+ // return false;
40
+ // });
37
41
  const merged = mergeDeep({}, doc, Util.unflatten(input, { safe: true }));
38
42
  return this.#resolver.resolve(query.clone({ doc, input: merged }));
39
43
  });
@@ -60,7 +64,7 @@ module.exports = class QueryResolver extends QueryBuilder {
60
64
  case 'pullOne': {
61
65
  return this.#get(query).then((doc) => {
62
66
  const [key] = Object.keys(input);
63
- const values = get(this.#model.transformers.input.transform(input), key);
67
+ const values = get(this.#model.transformers.input.transform(input), key, []);
64
68
  const $input = { [key]: (get(doc, key) || []).filter(el => values.every(v => `${v}` !== `${el}`)) };
65
69
  return this.#resolver.match(this.#model.name).id(doc.id).save($input);
66
70
  });
@@ -81,7 +85,7 @@ module.exports = class QueryResolver extends QueryBuilder {
81
85
  }
82
86
  case 'deleteOne': {
83
87
  return this.#get(query).then((doc) => {
84
- return this.#resolveReferentialIntegrity(query).then(() => {
88
+ return this.#resolveReferentialIntegrity(doc).then(() => {
85
89
  return this.#resolver.resolve(query.clone({ doc })).then(() => doc);
86
90
  });
87
91
  });
@@ -105,21 +109,21 @@ module.exports = class QueryResolver extends QueryBuilder {
105
109
  return this.#resolver.resolve(query.clone({ op: 'findMany', key: `find${this.#model.name}`, crud: 'read', isMutation: false }));
106
110
  }
107
111
 
108
- #resolveReferentialIntegrity(query) {
109
- const { id } = query.toObject();
112
+ #resolveReferentialIntegrity(doc) {
113
+ const { id } = doc;
110
114
  const txn = this.#resolver.transaction(false);
111
115
 
112
- // if (this.#model.name === 'Person') console.log(this.#model.referentialIntegrity);
113
- return txn.run(Util.promiseChain(this.#model.referentialIntegrity.map(({ model, field, fieldRef, isArray, op }) => () => {
114
- const fieldStr = fieldRef ? `${field}.${fieldRef}` : `${field.name}`;
115
- const $where = { [fieldStr]: id };
116
+ return txn.run(Util.promiseChain(this.#model.referentialIntegrity.map(({ model, field, path }) => () => {
117
+ const { onDelete, isArray } = field;
118
+ const $path = path.join('.');
119
+ const where = field.isVirtual ? { [field.model.pkField]: get(doc, field.linkBy) } : { [$path]: id };
116
120
 
117
- switch (op) {
118
- case 'cascade': return isArray ? txn.match(model).where($where).pull(fieldStr, id) : txn.match(model).where($where).remove();
119
- case 'nullify': return txn.match(model).where($where).save({ [fieldStr]: null });
120
- case 'restrict': return txn.match(model).where($where).count().then(count => (count ? Promise.reject(new Error('Restricted')) : count));
121
+ switch (onDelete) {
122
+ case 'cascade': return isArray ? txn.match(model).where(where).pull($path, id) : txn.match(model).where(where).remove();
123
+ case 'nullify': return txn.match(model).where(where).save({ [$path]: null });
124
+ case 'restrict': return txn.match(model).where(where).count().then(count => (count ? Promise.reject(new Error('Restricted')) : count));
121
125
  case 'defer': return Promise.resolve(); // Used for embedded models (could be improved)
122
- default: throw new Error(`Unknown onDelete operator: '${op}'`);
126
+ default: throw new Error(`Unknown onDelete operator: '${onDelete}'`);
123
127
  }
124
128
  })));
125
129
  }
@@ -129,6 +129,7 @@ module.exports = class Schema {
129
129
  visit(this.#typeDefs, {
130
130
  enter: (node) => {
131
131
  const name = node.name?.value;
132
+
132
133
  if (!allowedKinds.includes(node.kind) || operations.includes(name)) return false;
133
134
 
134
135
  if (modelKinds.includes(node.kind)) {
@@ -424,8 +425,9 @@ module.exports = class Schema {
424
425
  curr.name, // Rename key
425
426
  a => Pipeline.$deserialize({ ...a, ...args, path: a.path.concat(curr.name) }),
426
427
  ];
428
+
427
429
  if (curr.isArray) rules.unshift(({ value }) => (value == null ? value : Util.ensureArray(value)));
428
- // if (curr.isEmbedded) rules.unshift(({ value }) => Util.map(value, v => curr.model.transformers.doc.transform(v)));
430
+
429
431
  if (curr.isEmbedded) {
430
432
  rules.unshift(a => Util.map(a.value, (value, i) => {
431
433
  const path = a.path.concat(curr.name);
@@ -433,6 +435,7 @@ module.exports = class Schema {
433
435
  return curr.model.transformers.doc.transform(value, { ...args, query: a.query, context: a.context, path });
434
436
  }));
435
437
  }
438
+
436
439
  return Object.assign(prev, { [curr.key]: rules });
437
440
  }, {}),
438
441
  defaults: Object.values($model.fields).reduce((prev, curr) => {
@@ -450,6 +453,7 @@ module.exports = class Schema {
450
453
 
451
454
  // Field resolution comes first (unshift)
452
455
  thunks.unshift(($schema) => {
456
+ $field.parent = $model;
453
457
  $field.model = $schema.models[$field.type];
454
458
  $field.crud = Util.uvl($field.crud, $field.model?.scope, 'crud');
455
459
  $field.linkTo ??= $field.model;
@@ -514,8 +518,12 @@ module.exports = class Schema {
514
518
  });
515
519
 
516
520
  // Resolve referential integrity
521
+ const onDeleteFields = Array.from(new Set(Object.values(this.#schema.models).reduce((prev, curr) => {
522
+ return prev.concat(Object.values(curr.fields).filter(el => el.onDelete)).flat();
523
+ }, [])));
524
+
517
525
  Object.values(this.#schema.models).forEach(($model) => {
518
- $model.referentialIntegrity = Schema.#identifyOnDeletes(Object.values(this.#schema.models), $model.name);
526
+ $model.referentialIntegrity = Schema.#identifyOnDeletes(Object.values(this.#schema.models).filter(m => m.isEntity), onDeleteFields.filter(f => `${f.model}` === `${$model}`));
519
527
  });
520
528
 
521
529
  // Helper methods
@@ -894,21 +902,25 @@ module.exports = class Schema {
894
902
  `;
895
903
  }
896
904
 
897
- static #identifyOnDeletes(models, parentName) {
898
- return models.reduce((prev, model) => {
899
- Object.values(model.fields).filter(f => f.onDelete).forEach((field) => {
900
- if (`${field.model.name}` === `${parentName}`) {
901
- if (model.isEntity) {
902
- prev.push({ model, field, isArray: field.isArray, op: field.onDelete });
903
- }
904
- // else {
905
- // prev.push(...Schema.#identifyOnDeletes(models, model.name).map(od => Object.assign(od, { fieldRef: field.name, isArray: field.isArray, op: field.onDelete })));
906
- // }
907
- }
908
- });
905
+ static #findPathToField(model, field, path = []) {
906
+ const { target, embeds } = Object.values(model.fields).reduce((prev, curr) => {
907
+ if (curr === field) prev.target = curr;
908
+ else if (curr.isEmbedded) prev.embeds.push(curr);
909
+ return prev;
910
+ }, { embeds: [] });
909
911
 
910
- // Assign model referential integrity
911
- return Util.filterBy(prev, (a, b) => `${a.model.name}:${a.field.name}:${a.fieldRef}:${a.op}` === `${b.model.name}:${b.field.name}:${b.fieldRef}:${b.op}`);
912
- }, []);
912
+ if (target) return path.concat(target.name);
913
+ if (embeds.length) return embeds.map(f => Schema.#findPathToField(f.model, field, path.concat(f.name))).filter(Boolean)[0];
914
+ return undefined;
915
+ }
916
+
917
+ static #identifyOnDeletes(models, fields) {
918
+ return models.map((model) => {
919
+ return fields.map((field) => {
920
+ const path = Schema.#findPathToField(model, field);
921
+ if (!path) return null;
922
+ return { model, field, path };
923
+ });
924
+ }).flat().filter(Boolean);
913
925
  }
914
926
  };