@nocobase/database 0.7.6-alpha.2 → 0.8.0-alpha.4

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.
Files changed (61) hide show
  1. package/lib/collection.d.ts +1 -1
  2. package/lib/collection.js +1 -0
  3. package/lib/database.d.ts +17 -2
  4. package/lib/database.js +53 -41
  5. package/lib/features/ReferencesMap.d.ts +15 -0
  6. package/lib/features/ReferencesMap.js +60 -0
  7. package/lib/features/referential-integrity-check.d.ts +8 -0
  8. package/lib/features/referential-integrity-check.js +89 -0
  9. package/lib/fields/belongs-to-field.d.ts +2 -0
  10. package/lib/fields/belongs-to-field.js +16 -2
  11. package/lib/fields/belongs-to-many-field.d.ts +1 -0
  12. package/lib/fields/belongs-to-many-field.js +8 -2
  13. package/lib/fields/context-field.d.ts +2 -1
  14. package/lib/fields/context-field.js +12 -8
  15. package/lib/fields/field.d.ts +2 -1
  16. package/lib/fields/field.js +1 -0
  17. package/lib/fields/formula-field.js +1 -1
  18. package/lib/fields/has-many-field.d.ts +2 -0
  19. package/lib/fields/has-many-field.js +19 -1
  20. package/lib/fields/has-one-field.d.ts +2 -0
  21. package/lib/fields/has-one-field.js +16 -2
  22. package/lib/filter-parser.js +5 -3
  23. package/lib/model.d.ts +1 -0
  24. package/lib/options-parser.js +3 -2
  25. package/lib/relation-repository/belongs-to-many-repository.d.ts +3 -3
  26. package/lib/relation-repository/hasmany-repository.d.ts +2 -2
  27. package/lib/relation-repository/hasone-repository.d.ts +2 -4
  28. package/lib/relation-repository/multiple-relation-repository.d.ts +2 -5
  29. package/lib/relation-repository/relation-repository.js +2 -2
  30. package/lib/relation-repository/single-relation-repository.d.ts +1 -1
  31. package/lib/repository.d.ts +25 -15
  32. package/lib/repository.js +3 -1
  33. package/lib/types.d.ts +43 -0
  34. package/lib/types.js +5 -0
  35. package/package.json +3 -3
  36. package/src/__tests__/fields/belongs-to-field.test.ts +112 -4
  37. package/src/__tests__/fields/has-many-field.test.ts +83 -0
  38. package/src/__tests__/relation-repository/hasone-repository.test.ts +3 -3
  39. package/src/__tests__/repository/find.test.ts +10 -0
  40. package/src/collection.ts +3 -1
  41. package/src/database.ts +64 -15
  42. package/src/features/ReferencesMap.ts +64 -0
  43. package/src/features/referential-integrity-check.ts +61 -0
  44. package/src/fields/belongs-to-field.ts +21 -1
  45. package/src/fields/belongs-to-many-field.ts +6 -0
  46. package/src/fields/context-field.ts +5 -7
  47. package/src/fields/field.ts +4 -3
  48. package/src/fields/formula-field.ts +4 -4
  49. package/src/fields/has-many-field.ts +25 -2
  50. package/src/fields/has-one-field.ts +22 -2
  51. package/src/filter-parser.ts +5 -1
  52. package/src/model.ts +1 -0
  53. package/src/options-parser.ts +4 -2
  54. package/src/relation-repository/belongs-to-many-repository.ts +3 -3
  55. package/src/relation-repository/hasmany-repository.ts +8 -5
  56. package/src/relation-repository/hasone-repository.ts +2 -4
  57. package/src/relation-repository/multiple-relation-repository.ts +3 -4
  58. package/src/relation-repository/relation-repository.ts +1 -1
  59. package/src/relation-repository/single-relation-repository.ts +1 -1
  60. package/src/repository.ts +41 -19
  61. package/src/types.ts +64 -0
@@ -41,7 +41,7 @@ export declare class Collection<TModelAttributes extends {} = any, TCreationAttr
41
41
  get filterTargetKey(): string;
42
42
  get name(): string;
43
43
  get db(): Database;
44
- constructor(options: CollectionOptions, context?: CollectionContext);
44
+ constructor(options: CollectionOptions, context: CollectionContext);
45
45
  private checkOptions;
46
46
  private sequelizeModelOptions;
47
47
  /**
package/lib/collection.js CHANGED
@@ -102,6 +102,7 @@ class Collection extends _events().EventEmitter {
102
102
  this.options = options;
103
103
  this.bindFieldEventListener();
104
104
  this.modelInit();
105
+ this.db.modelCollection.set(this.model, this);
105
106
  this.setFields(options.fields);
106
107
  this.setRepository(options.repository);
107
108
  this.setSortable(options.sortable);
package/lib/database.d.ts CHANGED
@@ -13,6 +13,8 @@ import { Model } from './model';
13
13
  import { ModelHook } from './model-hook';
14
14
  import { RelationRepository } from './relation-repository/relation-repository';
15
15
  import { Repository } from './repository';
16
+ import { AfterDefineCollectionListener, BeforeDefineCollectionListener, CreateListener, CreateWithAssociationsListener, DatabaseAfterDefineCollectionEventType, DatabaseAfterRemoveCollectionEventType, DatabaseBeforeDefineCollectionEventType, DatabaseBeforeRemoveCollectionEventType, DestroyListener, EventType, ModelCreateEventTypes, ModelCreateWithAssociationsEventTypes, ModelDestroyEventTypes, ModelSaveEventTypes, ModelSaveWithAssociationsEventTypes, ModelUpdateEventTypes, ModelUpdateWithAssociationsEventTypes, ModelValidateEventTypes, RemoveCollectionListener, SaveListener, SaveWithAssociationsListener, SyncListener, UpdateListener, UpdateWithAssociationsListener, ValidateListener } from './types';
17
+ import ReferencesMap from './features/ReferencesMap';
16
18
  export interface MergeOptions extends merge.Options {
17
19
  }
18
20
  export interface PendingOptions {
@@ -60,6 +62,7 @@ export declare class Database extends EventEmitter implements AsyncEmitter {
60
62
  collections: Map<string, Collection<any, any>>;
61
63
  pendingFields: Map<string, FieldTypes.RelationField[]>;
62
64
  modelCollection: Map<ModelCtor<any>, Collection<any, any>>;
65
+ referenceMap: ReferencesMap;
63
66
  modelHook: ModelHook;
64
67
  version: DatabaseVersion;
65
68
  delayCollectionExtend: Map<string, {
@@ -67,10 +70,10 @@ export declare class Database extends EventEmitter implements AsyncEmitter {
67
70
  mergeOptions?: any;
68
71
  }[]>;
69
72
  constructor(options: DatabaseOptions);
73
+ initListener(): void;
70
74
  addMigration(item: MigrationItem): void;
71
75
  addMigrations(options: AddMigrationsOptions): void;
72
76
  inDialect(...dialect: string[]): boolean;
73
- private requireModule;
74
77
  /**
75
78
  * Add collection to database
76
79
  * @param options
@@ -105,7 +108,19 @@ export declare class Database extends EventEmitter implements AsyncEmitter {
105
108
  reconnect(): Promise<void>;
106
109
  closed(): any;
107
110
  close(): Promise<void>;
108
- on(event: string | symbol, listener: any): this;
111
+ on(event: EventType, listener: any): this;
112
+ on(event: ModelValidateEventTypes, listener: SyncListener): this;
113
+ on(event: ModelValidateEventTypes, listener: ValidateListener): this;
114
+ on(event: ModelCreateEventTypes, listener: CreateListener): this;
115
+ on(event: ModelUpdateEventTypes, listener: UpdateListener): this;
116
+ on(event: ModelSaveEventTypes, listener: SaveListener): this;
117
+ on(event: ModelDestroyEventTypes, listener: DestroyListener): this;
118
+ on(event: ModelCreateWithAssociationsEventTypes, listener: CreateWithAssociationsListener): this;
119
+ on(event: ModelUpdateWithAssociationsEventTypes, listener: UpdateWithAssociationsListener): this;
120
+ on(event: ModelSaveWithAssociationsEventTypes, listener: SaveWithAssociationsListener): this;
121
+ on(event: DatabaseBeforeDefineCollectionEventType, listener: BeforeDefineCollectionListener): this;
122
+ on(event: DatabaseAfterDefineCollectionEventType, listener: AfterDefineCollectionListener): this;
123
+ on(event: DatabaseBeforeRemoveCollectionEventType | DatabaseAfterRemoveCollectionEventType, listener: RemoveCollectionListener): this;
109
124
  extendCollection(collectionOptions: CollectionOptions, mergeOptions?: MergeOptions): void;
110
125
  import(options: {
111
126
  directory: string;
package/lib/database.js CHANGED
@@ -98,6 +98,10 @@ var _modelHook = require("./model-hook");
98
98
 
99
99
  var _operators = _interopRequireDefault(require("./operators"));
100
100
 
101
+ var _referentialIntegrityCheck = require("./features/referential-integrity-check");
102
+
103
+ var _ReferencesMap = _interopRequireDefault(require("./features/ReferencesMap"));
104
+
101
105
  const _excluded = ["drop"],
102
106
  _excluded2 = ["retry"];
103
107
 
@@ -192,7 +196,8 @@ class DatabaseVersion {
192
196
 
193
197
  class Database extends _events().EventEmitter {
194
198
  constructor(options) {
195
- super();
199
+ super(); // this.setMaxListeners(100);
200
+
196
201
  this.sequelize = void 0;
197
202
  this.migrator = void 0;
198
203
  this.migrations = void 0;
@@ -204,6 +209,7 @@ class Database extends _events().EventEmitter {
204
209
  this.collections = new Map();
205
210
  this.pendingFields = new Map();
206
211
  this.modelCollection = new Map();
212
+ this.referenceMap = new _ReferencesMap.default();
207
213
  this.modelHook = void 0;
208
214
  this.version = void 0;
209
215
  this.delayCollectionExtend = new Map();
@@ -285,6 +291,12 @@ class Database extends _events().EventEmitter {
285
291
  opts.tableName = `${this.options.tablePrefix}${opts.tableName || opts.modelName || opts.name.plural}`;
286
292
  }
287
293
  });
294
+ this.initListener();
295
+ }
296
+
297
+ initListener() {
298
+ var _this2 = this;
299
+
288
300
  this.on('afterCreate', /*#__PURE__*/function () {
289
301
  var _ref = _asyncToGenerator(function* (instance) {
290
302
  var _instance$toChangedWi;
@@ -307,6 +319,19 @@ class Database extends _events().EventEmitter {
307
319
  return _ref2.apply(this, arguments);
308
320
  };
309
321
  }());
322
+ this.on('beforeDestroy', /*#__PURE__*/function () {
323
+ var _ref3 = _asyncToGenerator(function* (instance, options) {
324
+ yield (0, _referentialIntegrityCheck.referentialIntegrityCheck)({
325
+ db: _this2,
326
+ referencedInstance: instance,
327
+ transaction: options.transaction
328
+ });
329
+ });
330
+
331
+ return function (_x3, _x4) {
332
+ return _ref3.apply(this, arguments);
333
+ };
334
+ }());
310
335
  }
311
336
 
312
337
  addMigration(item) {
@@ -335,7 +360,7 @@ class Database extends _events().EventEmitter {
335
360
  filename = filename.substring(0, filename.lastIndexOf('.')) || filename;
336
361
  this.migrations.add({
337
362
  name: namespace ? `${namespace}/${filename}` : filename,
338
- migration: this.requireModule(file),
363
+ migration: (0, _utils().requireModule)(file),
339
364
  context
340
365
  });
341
366
  }
@@ -349,18 +374,6 @@ class Database extends _events().EventEmitter {
349
374
  inDialect(...dialect) {
350
375
  return dialect.includes(this.sequelize.getDialect());
351
376
  }
352
-
353
- requireModule(module) {
354
- if (typeof module === 'string') {
355
- module = require(module);
356
- }
357
-
358
- if (typeof module !== 'object') {
359
- return module;
360
- }
361
-
362
- return module.__esModule ? module.default : module;
363
- }
364
377
  /**
365
378
  * Add collection to database
366
379
  * @param options
@@ -373,7 +386,6 @@ class Database extends _events().EventEmitter {
373
386
  database: this
374
387
  });
375
388
  this.collections.set(collection.name, collection);
376
- this.modelCollection.set(collection.model, collection);
377
389
  this.emit('afterDefineCollection', collection);
378
390
  return collection;
379
391
  }
@@ -514,19 +526,19 @@ class Database extends _events().EventEmitter {
514
526
  }
515
527
 
516
528
  sync(options) {
517
- var _this2 = this;
529
+ var _this3 = this;
518
530
 
519
531
  return _asyncToGenerator(function* () {
520
- const isMySQL = _this2.sequelize.getDialect() === 'mysql';
532
+ const isMySQL = _this3.sequelize.getDialect() === 'mysql';
521
533
 
522
534
  if (isMySQL) {
523
- yield _this2.sequelize.query('SET FOREIGN_KEY_CHECKS = 0', null);
535
+ yield _this3.sequelize.query('SET FOREIGN_KEY_CHECKS = 0', null);
524
536
  }
525
537
 
526
- const result = yield _this2.sequelize.sync(options);
538
+ const result = yield _this3.sequelize.sync(options);
527
539
 
528
540
  if (isMySQL) {
529
- yield _this2.sequelize.query('SET FOREIGN_KEY_CHECKS = 1', null);
541
+ yield _this3.sequelize.query('SET FOREIGN_KEY_CHECKS = 1', null);
530
542
  }
531
543
 
532
544
  return result;
@@ -534,26 +546,26 @@ class Database extends _events().EventEmitter {
534
546
  }
535
547
 
536
548
  clean(options) {
537
- var _this3 = this;
549
+ var _this4 = this;
538
550
 
539
551
  return _asyncToGenerator(function* () {
540
552
  const drop = options.drop,
541
553
  others = _objectWithoutProperties(options, _excluded);
542
554
 
543
555
  if (drop) {
544
- yield _this3.sequelize.getQueryInterface().dropAllTables(others);
556
+ yield _this4.sequelize.getQueryInterface().dropAllTables(others);
545
557
  }
546
558
  })();
547
559
  }
548
560
 
549
561
  collectionExistsInDb(name, options) {
550
- var _this4 = this;
562
+ var _this5 = this;
551
563
 
552
564
  return _asyncToGenerator(function* () {
553
- const tables = yield _this4.sequelize.getQueryInterface().showAllTables({
565
+ const tables = yield _this5.sequelize.getQueryInterface().showAllTables({
554
566
  transaction: options === null || options === void 0 ? void 0 : options.transaction
555
567
  });
556
- return !!tables.find(table => table === `${_this4.getTablePrefix()}${name}`);
568
+ return !!tables.find(table => table === `${_this5.getTablePrefix()}${name}`);
557
569
  })();
558
570
  }
559
571
 
@@ -562,7 +574,7 @@ class Database extends _events().EventEmitter {
562
574
  }
563
575
 
564
576
  auth(options = {}) {
565
- var _this5 = this;
577
+ var _this6 = this;
566
578
 
567
579
  return _asyncToGenerator(function* () {
568
580
  const _options$retry = options.retry,
@@ -574,9 +586,9 @@ class Database extends _events().EventEmitter {
574
586
  let count = 1;
575
587
 
576
588
  const authenticate = /*#__PURE__*/function () {
577
- var _ref3 = _asyncToGenerator(function* () {
589
+ var _ref4 = _asyncToGenerator(function* () {
578
590
  try {
579
- yield _this5.sequelize.authenticate(others);
591
+ yield _this6.sequelize.authenticate(others);
580
592
  console.log('Connection has been established successfully.');
581
593
  return true;
582
594
  } catch (error) {
@@ -592,7 +604,7 @@ class Database extends _events().EventEmitter {
592
604
  });
593
605
 
594
606
  return function authenticate() {
595
- return _ref3.apply(this, arguments);
607
+ return _ref4.apply(this, arguments);
596
608
  };
597
609
  }();
598
610
 
@@ -601,21 +613,21 @@ class Database extends _events().EventEmitter {
601
613
  }
602
614
 
603
615
  reconnect() {
604
- var _this6 = this;
616
+ var _this7 = this;
605
617
 
606
618
  return _asyncToGenerator(function* () {
607
- if (_this6.isSqliteMemory()) {
619
+ if (_this7.isSqliteMemory()) {
608
620
  return;
609
621
  } // @ts-ignore
610
622
 
611
623
 
612
- const ConnectionManager = _this6.sequelize.dialect.connectionManager.constructor; // @ts-ignore
624
+ const ConnectionManager = _this7.sequelize.dialect.connectionManager.constructor; // @ts-ignore
613
625
 
614
- const connectionManager = new ConnectionManager(_this6.sequelize.dialect, _this6.sequelize); // @ts-ignore
626
+ const connectionManager = new ConnectionManager(_this7.sequelize.dialect, _this7.sequelize); // @ts-ignore
615
627
 
616
- _this6.sequelize.dialect.connectionManager = connectionManager; // @ts-ignore
628
+ _this7.sequelize.dialect.connectionManager = connectionManager; // @ts-ignore
617
629
 
618
- _this6.sequelize.connectionManager = connectionManager;
630
+ _this7.sequelize.connectionManager = connectionManager;
619
631
  })();
620
632
  }
621
633
 
@@ -625,14 +637,14 @@ class Database extends _events().EventEmitter {
625
637
  }
626
638
 
627
639
  close() {
628
- var _this7 = this;
640
+ var _this8 = this;
629
641
 
630
642
  return _asyncToGenerator(function* () {
631
- if (_this7.isSqliteMemory()) {
643
+ if (_this8.isSqliteMemory()) {
632
644
  return;
633
645
  }
634
646
 
635
- return _this7.sequelize.close();
647
+ return _this8.sequelize.close();
636
648
  })();
637
649
  }
638
650
 
@@ -664,7 +676,7 @@ class Database extends _events().EventEmitter {
664
676
  }
665
677
 
666
678
  import(options) {
667
- var _this8 = this;
679
+ var _this9 = this;
668
680
 
669
681
  return _asyncToGenerator(function* () {
670
682
  const reader = new _collectionImporter.ImporterReader(options.directory, options.extensions);
@@ -679,9 +691,9 @@ class Database extends _events().EventEmitter {
679
691
  const module = _step2.value;
680
692
 
681
693
  if (module.extend) {
682
- _this8.extendCollection(module.collectionOptions, module.mergeOptions);
694
+ _this9.extendCollection(module.collectionOptions, module.mergeOptions);
683
695
  } else {
684
- const collection = _this8.collection(module);
696
+ const collection = _this9.collection(module);
685
697
 
686
698
  result.set(collection.name, collection);
687
699
  }
@@ -0,0 +1,15 @@
1
+ export interface Reference {
2
+ sourceCollectionName: string;
3
+ sourceField: string;
4
+ targetField: string;
5
+ targetCollectionName: string;
6
+ onDelete: string;
7
+ }
8
+ declare class ReferencesMap {
9
+ protected map: Map<string, Reference[]>;
10
+ addReference(reference: Reference): void;
11
+ getReferences(collectionName: any): Reference[];
12
+ existReference(reference: Reference): Reference;
13
+ removeReference(reference: Reference): void;
14
+ }
15
+ export default ReferencesMap;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ class ReferencesMap {
9
+ constructor() {
10
+ this.map = new Map();
11
+ }
12
+
13
+ addReference(reference) {
14
+ const existReference = this.existReference(reference);
15
+
16
+ if (existReference) {
17
+ if (reference.onDelete && existReference.onDelete !== reference.onDelete) {
18
+ throw new Error(`On Delete Conflict, exist reference ${JSON.stringify(existReference)}, new reference ${JSON.stringify(reference)}`);
19
+ }
20
+
21
+ return;
22
+ }
23
+
24
+ if (!reference.onDelete) {
25
+ reference.onDelete = 'SET NULL';
26
+ }
27
+
28
+ this.map.set(reference.targetCollectionName, [...(this.map.get(reference.targetCollectionName) || []), reference]);
29
+ }
30
+
31
+ getReferences(collectionName) {
32
+ return this.map.get(collectionName);
33
+ }
34
+
35
+ existReference(reference) {
36
+ const references = this.map.get(reference.targetCollectionName);
37
+
38
+ if (!references) {
39
+ return null;
40
+ }
41
+
42
+ const keys = Object.keys(reference).filter(k => k !== 'onDelete');
43
+ return references.find(ref => keys.every(key => ref[key] === reference[key]));
44
+ }
45
+
46
+ removeReference(reference) {
47
+ const references = this.map.get(reference.targetCollectionName);
48
+
49
+ if (!references) {
50
+ return;
51
+ }
52
+
53
+ const keys = Object.keys(reference);
54
+ this.map.set(reference.targetCollectionName, references.filter(ref => !keys.every(key => ref[key] === reference[key])));
55
+ }
56
+
57
+ }
58
+
59
+ var _default = ReferencesMap;
60
+ exports.default = _default;
@@ -0,0 +1,8 @@
1
+ import Database from '../database';
2
+ import { Model, Transactionable } from 'sequelize';
3
+ interface ReferentialIntegrityCheckOptions extends Transactionable {
4
+ db: Database;
5
+ referencedInstance: Model;
6
+ }
7
+ export declare function referentialIntegrityCheck(options: ReferentialIntegrityCheckOptions): Promise<void>;
8
+ export {};
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.referentialIntegrityCheck = referentialIntegrityCheck;
7
+
8
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
9
+
10
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
11
+
12
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
13
+
14
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
15
+
16
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
17
+
18
+ function referentialIntegrityCheck(_x) {
19
+ return _referentialIntegrityCheck.apply(this, arguments);
20
+ }
21
+
22
+ function _referentialIntegrityCheck() {
23
+ _referentialIntegrityCheck = _asyncToGenerator(function* (options) {
24
+ const referencedInstance = options.referencedInstance,
25
+ db = options.db,
26
+ transaction = options.transaction; // @ts-ignore
27
+
28
+ const collection = db.modelCollection.get(referencedInstance.constructor);
29
+ const collectionName = collection.name;
30
+ const references = db.referenceMap.getReferences(collectionName);
31
+
32
+ if (!references) {
33
+ return;
34
+ }
35
+
36
+ var _iterator = _createForOfIteratorHelper(references),
37
+ _step;
38
+
39
+ try {
40
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
41
+ const reference = _step.value;
42
+ const sourceCollectionName = reference.sourceCollectionName,
43
+ sourceField = reference.sourceField,
44
+ targetField = reference.targetField,
45
+ onDelete = reference.onDelete;
46
+ const sourceCollection = db.collections.get(sourceCollectionName);
47
+ const sourceRepository = sourceCollection.repository;
48
+ const filter = {
49
+ [sourceField]: referencedInstance[targetField]
50
+ };
51
+ const referencingExists = yield sourceRepository.count({
52
+ filter,
53
+ transaction
54
+ });
55
+
56
+ if (!referencingExists) {
57
+ continue;
58
+ }
59
+
60
+ if (onDelete === 'RESTRICT') {
61
+ throw new Error('RESTRICT');
62
+ }
63
+
64
+ if (onDelete === 'CASCADE') {
65
+ yield sourceRepository.destroy({
66
+ filter: filter,
67
+ transaction
68
+ });
69
+ }
70
+
71
+ if (onDelete === 'SET NULL') {
72
+ yield sourceRepository.update({
73
+ filter,
74
+ values: {
75
+ [sourceField]: null
76
+ },
77
+ hooks: false,
78
+ transaction
79
+ });
80
+ }
81
+ }
82
+ } catch (err) {
83
+ _iterator.e(err);
84
+ } finally {
85
+ _iterator.f();
86
+ }
87
+ });
88
+ return _referentialIntegrityCheck.apply(this, arguments);
89
+ }
@@ -1,8 +1,10 @@
1
1
  import { BelongsToOptions as SequelizeBelongsToOptions } from 'sequelize';
2
2
  import { BaseRelationFieldOptions, RelationField } from './relation-field';
3
+ import { Reference } from '../features/ReferencesMap';
3
4
  export declare class BelongsToField extends RelationField {
4
5
  static type: string;
5
6
  get target(): any;
7
+ reference(association: any): Reference;
6
8
  bind(): boolean;
7
9
  unbind(): void;
8
10
  }
@@ -43,6 +43,17 @@ class BelongsToField extends _relationField.RelationField {
43
43
  return target || _sequelize().Utils.pluralize(name);
44
44
  }
45
45
 
46
+ reference(association) {
47
+ const targetKey = association.targetKey;
48
+ return {
49
+ sourceCollectionName: this.database.modelCollection.get(association.source).name,
50
+ sourceField: association.foreignKey,
51
+ targetField: targetKey,
52
+ targetCollectionName: this.database.modelCollection.get(association.target).name,
53
+ onDelete: this.options.onDelete
54
+ };
55
+ }
56
+
46
57
  bind() {
47
58
  const _this$context = this.context,
48
59
  database = _this$context.database,
@@ -63,7 +74,7 @@ class BelongsToField extends _relationField.RelationField {
63
74
  const association = collection.model.belongsTo(Target, _objectSpread({
64
75
  as: this.name,
65
76
  constraints: false
66
- }, (0, _lodash().omit)(this.options, ['name', 'type', 'target']))); // inverse relation
77
+ }, (0, _lodash().omit)(this.options, ['name', 'type', 'target', 'onDelete']))); // inverse relation
67
78
  // this.TargetModel.hasMany(collection.model);
68
79
  // 建立关系之后从 pending 列表中删除
69
80
 
@@ -86,6 +97,7 @@ class BelongsToField extends _relationField.RelationField {
86
97
  }
87
98
 
88
99
  this.collection.addIndex([this.options.foreignKey]);
100
+ this.database.referenceMap.addReference(this.reference(association));
89
101
  return true;
90
102
  }
91
103
 
@@ -105,8 +117,10 @@ class BelongsToField extends _relationField.RelationField {
105
117
 
106
118
  if (!field1 && !field2) {
107
119
  collection.model.removeAttribute(foreignKey);
108
- } // 删掉 model 的关联字段
120
+ }
109
121
 
122
+ const association = collection.model.associations[this.name];
123
+ this.database.referenceMap.removeReference(this.reference(association)); // 删掉 model 的关联字段
110
124
 
111
125
  delete collection.model.associations[this.name]; // @ts-ignore
112
126
 
@@ -2,6 +2,7 @@ import { BelongsToManyOptions as SequelizeBelongsToManyOptions } from 'sequelize
2
2
  import { MultipleRelationFieldOptions, RelationField } from './relation-field';
3
3
  export declare class BelongsToManyField extends RelationField {
4
4
  get through(): any;
5
+ get otherKey(): any;
5
6
  bind(): boolean;
6
7
  unbind(): void;
7
8
  }
@@ -40,6 +40,10 @@ class BelongsToManyField extends _relationField.RelationField {
40
40
  return this.options.through || _sequelize().Utils.camelize([this.context.collection.model.name, this.target].map(name => name.toLowerCase()).sort().join('_'));
41
41
  }
42
42
 
43
+ get otherKey() {
44
+ return this.options.otherKey;
45
+ }
46
+
43
47
  bind() {
44
48
  const _this$context = this.context,
45
49
  database = _this$context.database,
@@ -58,7 +62,8 @@ class BelongsToManyField extends _relationField.RelationField {
58
62
  Through = database.getCollection(through);
59
63
  } else {
60
64
  Through = database.collection({
61
- name: through
65
+ name: through // timestamps: false,
66
+
62
67
  });
63
68
  Object.defineProperty(Through.model, 'isThrough', {
64
69
  value: true
@@ -106,7 +111,8 @@ class BelongsToManyField extends _relationField.RelationField {
106
111
  unbind() {
107
112
  const _this$context2 = this.context,
108
113
  database = _this$context2.database,
109
- collection = _this$context2.collection; // 如果关系字段还没建立就删除了,也同步删除待建立关联的关系字段
114
+ collection = _this$context2.collection;
115
+ const Through = database.getCollection(this.through); // 如果关系字段还没建立就删除了,也同步删除待建立关联的关系字段
110
116
 
111
117
  database.removePendingField(this); // 删掉 model 的关联字段
112
118
 
@@ -1,7 +1,8 @@
1
+ import { Model } from '../model';
1
2
  import { BaseColumnFieldOptions, Field } from './field';
2
3
  export declare class ContextField extends Field {
3
4
  get dataType(): any;
4
- init(): void;
5
+ listener: (model: Model, options: any) => Promise<void>;
5
6
  bind(): void;
6
7
  unbind(): void;
7
8
  }
@@ -34,18 +34,17 @@ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try
34
34
  function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
35
35
 
36
36
  class ContextField extends _field.Field {
37
- get dataType() {
38
- const type = this.options.dataType || 'string';
39
- return _sequelize().DataTypes[type.toUpperCase()] || _sequelize().DataTypes.STRING;
40
- }
37
+ constructor(...args) {
38
+ var _this;
41
39
 
42
- init() {
43
- const _this$options = this.options,
44
- name = _this$options.name,
45
- dataIndex = _this$options.dataIndex;
40
+ super(...args);
41
+ _this = this;
46
42
 
47
43
  this.listener = /*#__PURE__*/function () {
48
44
  var _ref = _asyncToGenerator(function* (model, options) {
45
+ const _this$options = _this.options,
46
+ name = _this$options.name,
47
+ dataIndex = _this$options.dataIndex;
49
48
  const context = options.context;
50
49
  model.set(name, _lodash().default.get(context, dataIndex));
51
50
  model.changed(name, true);
@@ -57,6 +56,11 @@ class ContextField extends _field.Field {
57
56
  }();
58
57
  }
59
58
 
59
+ get dataType() {
60
+ const type = this.options.dataType || 'string';
61
+ return _sequelize().DataTypes[type.toUpperCase()] || _sequelize().DataTypes.STRING;
62
+ }
63
+
60
64
  bind() {
61
65
  super.bind();
62
66
  const createOnly = this.options.createOnly;
@@ -1,6 +1,7 @@
1
1
  import { DataType, ModelAttributeColumnOptions, ModelIndexesOptions, QueryInterfaceOptions, SyncOptions, Transactionable } from 'sequelize';
2
2
  import { Collection } from '../collection';
3
3
  import { Database } from '../database';
4
+ import { ModelEventTypes } from '../types';
4
5
  export interface FieldContext {
5
6
  database: Database;
6
7
  collection: Collection;
@@ -26,7 +27,7 @@ export declare abstract class Field {
26
27
  constructor(options?: any, context?: FieldContext);
27
28
  sync(syncOptions: SyncOptions): Promise<void>;
28
29
  init(): void;
29
- on(eventName: string, listener: (...args: any[]) => void): this;
30
+ on(eventName: ModelEventTypes, listener: (...args: any[]) => void): this;
30
31
  off(eventName: string, listener: (...args: any[]) => void): this;
31
32
  get(name: string): any;
32
33
  remove(): void | Field;
@@ -95,6 +95,7 @@ class Field {
95
95
  }
96
96
 
97
97
  remove() {
98
+ this.collection.removeIndex([this.name]);
98
99
  return this.collection.removeField(this.name);
99
100
  }
100
101