@coderich/autograph 0.13.44 → 0.13.46

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.44",
4
+ "version": "0.13.46",
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) => {
@@ -60,7 +60,7 @@ module.exports = class QueryResolver extends QueryBuilder {
60
60
  case 'pullOne': {
61
61
  return this.#get(query).then((doc) => {
62
62
  const [key] = Object.keys(input);
63
- const values = get(this.#model.transformers.input.transform(input), key);
63
+ const values = get(this.#model.transformers.input.transform(input), key, []);
64
64
  const $input = { [key]: (get(doc, key) || []).filter(el => values.every(v => `${v}` !== `${el}`)) };
65
65
  return this.#resolver.match(this.#model.name).id(doc.id).save($input);
66
66
  });
@@ -109,17 +109,17 @@ module.exports = class QueryResolver extends QueryBuilder {
109
109
  const { id } = query.toObject();
110
110
  const txn = this.#resolver.transaction(false);
111
111
 
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 };
112
+ return txn.run(Util.promiseChain(this.#model.referentialIntegrity.map(({ model, field, path }) => () => {
113
+ const { onDelete, isArray } = field;
114
+ const $path = path.join('.');
115
+ const $where = { [$path]: id };
116
116
 
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 });
117
+ switch (onDelete) {
118
+ case 'cascade': return isArray ? txn.match(model).where($where).pull($path, id) : txn.match(model).where($where).remove();
119
+ case 'nullify': return txn.match(model).where($where).save({ [$path]: null });
120
120
  case 'restrict': return txn.match(model).where($where).count().then(count => (count ? Promise.reject(new Error('Restricted')) : count));
121
121
  case 'defer': return Promise.resolve(); // Used for embedded models (could be improved)
122
- default: throw new Error(`Unknown onDelete operator: '${op}'`);
122
+ default: throw new Error(`Unknown onDelete operator: '${onDelete}'`);
123
123
  }
124
124
  })));
125
125
  }
@@ -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,17 @@ 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
+
431
+ if (curr.isEmbedded) {
432
+ rules.unshift(a => Util.map(a.value, (value, i) => {
433
+ const path = a.path.concat(curr.name);
434
+ if (curr.isArray) path.push(i);
435
+ return curr.model.transformers.doc.transform(value, { ...args, query: a.query, context: a.context, path });
436
+ }));
437
+ }
438
+
429
439
  return Object.assign(prev, { [curr.key]: rules });
430
440
  }, {}),
431
441
  defaults: Object.values($model.fields).reduce((prev, curr) => {
@@ -443,6 +453,7 @@ module.exports = class Schema {
443
453
 
444
454
  // Field resolution comes first (unshift)
445
455
  thunks.unshift(($schema) => {
456
+ $field.parent = $model;
446
457
  $field.model = $schema.models[$field.type];
447
458
  $field.crud = Util.uvl($field.crud, $field.model?.scope, 'crud');
448
459
  $field.linkTo ??= $field.model;
@@ -507,8 +518,12 @@ module.exports = class Schema {
507
518
  });
508
519
 
509
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
+
510
525
  Object.values(this.#schema.models).forEach(($model) => {
511
- $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}`));
512
527
  });
513
528
 
514
529
  // Helper methods
@@ -887,21 +902,25 @@ module.exports = class Schema {
887
902
  `;
888
903
  }
889
904
 
890
- static #identifyOnDeletes(models, parentName) {
891
- return models.reduce((prev, model) => {
892
- Object.values(model.fields).filter(f => f.onDelete).forEach((field) => {
893
- if (`${field.model.name}` === `${parentName}`) {
894
- if (model.isEntity) {
895
- prev.push({ model, field, isArray: field.isArray, op: field.onDelete });
896
- }
897
- // else {
898
- // prev.push(...Schema.#identifyOnDeletes(models, model.name).map(od => Object.assign(od, { fieldRef: field.name, isArray: field.isArray, op: field.onDelete })));
899
- // }
900
- }
901
- });
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: [] });
911
+
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
+ }
902
916
 
903
- // Assign model referential integrity
904
- 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}`);
905
- }, []);
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);
906
925
  }
907
926
  };