@nocobase/database 0.7.7-alpha.1 → 0.8.0-alpha.5

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 (59) 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/has-many-field.d.ts +2 -0
  18. package/lib/fields/has-many-field.js +19 -1
  19. package/lib/fields/has-one-field.d.ts +2 -0
  20. package/lib/fields/has-one-field.js +16 -2
  21. package/lib/filter-parser.js +5 -3
  22. package/lib/model.d.ts +1 -0
  23. package/lib/options-parser.js +3 -2
  24. package/lib/relation-repository/belongs-to-many-repository.d.ts +3 -3
  25. package/lib/relation-repository/hasmany-repository.d.ts +2 -2
  26. package/lib/relation-repository/hasone-repository.d.ts +2 -4
  27. package/lib/relation-repository/multiple-relation-repository.d.ts +2 -5
  28. package/lib/relation-repository/relation-repository.js +2 -2
  29. package/lib/relation-repository/single-relation-repository.d.ts +1 -1
  30. package/lib/repository.d.ts +25 -15
  31. package/lib/repository.js +3 -1
  32. package/lib/types.d.ts +43 -0
  33. package/lib/types.js +5 -0
  34. package/package.json +3 -3
  35. package/src/__tests__/fields/belongs-to-field.test.ts +112 -4
  36. package/src/__tests__/fields/has-many-field.test.ts +83 -0
  37. package/src/__tests__/relation-repository/hasone-repository.test.ts +3 -3
  38. package/src/__tests__/repository/find.test.ts +10 -0
  39. package/src/collection.ts +3 -1
  40. package/src/database.ts +64 -15
  41. package/src/features/ReferencesMap.ts +64 -0
  42. package/src/features/referential-integrity-check.ts +61 -0
  43. package/src/fields/belongs-to-field.ts +21 -1
  44. package/src/fields/belongs-to-many-field.ts +6 -0
  45. package/src/fields/context-field.ts +5 -7
  46. package/src/fields/field.ts +4 -3
  47. package/src/fields/has-many-field.ts +25 -2
  48. package/src/fields/has-one-field.ts +22 -2
  49. package/src/filter-parser.ts +5 -1
  50. package/src/model.ts +1 -0
  51. package/src/options-parser.ts +4 -2
  52. package/src/relation-repository/belongs-to-many-repository.ts +3 -3
  53. package/src/relation-repository/hasmany-repository.ts +8 -5
  54. package/src/relation-repository/hasone-repository.ts +2 -4
  55. package/src/relation-repository/multiple-relation-repository.ts +3 -4
  56. package/src/relation-repository/relation-repository.ts +1 -1
  57. package/src/relation-repository/single-relation-repository.ts +1 -1
  58. package/src/repository.ts +41 -19
  59. package/src/types.ts +64 -0
@@ -1,5 +1,6 @@
1
1
  import { AssociationScope, DataType, ForeignKeyOptions, HasManyOptions, HasManyOptions as SequelizeHasManyOptions } from 'sequelize';
2
2
  import { MultipleRelationFieldOptions, RelationField } from './relation-field';
3
+ import { Reference } from '../features/ReferencesMap';
3
4
  export interface HasManyFieldOptions extends HasManyOptions {
4
5
  /**
5
6
  * The name of the field to use as the key for the association in the source table. Defaults to the primary
@@ -55,6 +56,7 @@ export interface HasManyFieldOptions extends HasManyOptions {
55
56
  }
56
57
  export declare class HasManyField extends RelationField {
57
58
  get foreignKey(): any;
59
+ reference(association: any): Reference;
58
60
  bind(): boolean;
59
61
  unbind(): void;
60
62
  }
@@ -45,6 +45,17 @@ class HasManyField extends _relationField.RelationField {
45
45
  return _sequelize().Utils.camelize([model.options.name.singular, this.sourceKey || model.primaryKeyAttribute].join('_'));
46
46
  }
47
47
 
48
+ reference(association) {
49
+ const sourceKey = association.sourceKey;
50
+ return {
51
+ sourceCollectionName: this.database.modelCollection.get(association.target).name,
52
+ sourceField: association.foreignKey,
53
+ targetField: sourceKey,
54
+ targetCollectionName: this.database.modelCollection.get(association.source).name,
55
+ onDelete: this.options.onDelete
56
+ };
57
+ }
58
+
48
59
  bind() {
49
60
  const _this$context = this.context,
50
61
  database = _this$context.database,
@@ -62,7 +73,7 @@ class HasManyField extends _relationField.RelationField {
62
73
 
63
74
  const association = collection.model.hasMany(Target, _objectSpread(_objectSpread({
64
75
  constraints: false
65
- }, (0, _lodash().omit)(this.options, ['name', 'type', 'target'])), {}, {
76
+ }, (0, _lodash().omit)(this.options, ['name', 'type', 'target', 'onDelete'])), {}, {
66
77
  as: this.name,
67
78
  foreignKey: this.foreignKey
68
79
  })); // inverse relation
@@ -99,6 +110,7 @@ class HasManyField extends _relationField.RelationField {
99
110
  tcoll.addIndex([this.options.foreignKey]);
100
111
  }
101
112
 
113
+ this.database.referenceMap.addReference(this.reference(association));
102
114
  return true;
103
115
  }
104
116
 
@@ -121,6 +133,12 @@ class HasManyField extends _relationField.RelationField {
121
133
 
122
134
  if (!field) {
123
135
  tcoll.model.removeAttribute(foreignKey);
136
+ }
137
+
138
+ const association = collection.model.associations[this.name];
139
+
140
+ if (association) {
141
+ this.database.referenceMap.removeReference(this.reference(association));
124
142
  } // 删掉 model 的关联字段
125
143
 
126
144
 
@@ -1,5 +1,6 @@
1
1
  import { AssociationScope, DataType, ForeignKeyOptions, HasOneOptions, HasOneOptions as SequelizeHasOneOptions } from 'sequelize';
2
2
  import { BaseRelationFieldOptions, RelationField } from './relation-field';
3
+ import { Reference } from '../features/ReferencesMap';
3
4
  export interface HasOneFieldOptions extends HasOneOptions {
4
5
  /**
5
6
  * The name of the field to use as the key for the association in the source table. Defaults to the primary
@@ -56,6 +57,7 @@ export interface HasOneFieldOptions extends HasOneOptions {
56
57
  export declare class HasOneField extends RelationField {
57
58
  get target(): any;
58
59
  get foreignKey(): any;
60
+ reference(association: any): Reference;
59
61
  bind(): boolean;
60
62
  unbind(): void;
61
63
  }
@@ -52,6 +52,17 @@ class HasOneField extends _relationField.RelationField {
52
52
  return _sequelize().Utils.camelize([model.options.name.singular, model.primaryKeyAttribute].join('_'));
53
53
  }
54
54
 
55
+ reference(association) {
56
+ const sourceKey = association.sourceKey;
57
+ return {
58
+ sourceCollectionName: this.database.modelCollection.get(association.target).name,
59
+ sourceField: association.foreignKey,
60
+ targetField: sourceKey,
61
+ targetCollectionName: this.database.modelCollection.get(association.source).name,
62
+ onDelete: this.options.onDelete
63
+ };
64
+ }
65
+
55
66
  bind() {
56
67
  const _this$context = this.context,
57
68
  database = _this$context.database,
@@ -65,7 +76,7 @@ class HasOneField extends _relationField.RelationField {
65
76
 
66
77
  const association = collection.model.hasOne(Target, _objectSpread(_objectSpread({
67
78
  constraints: false
68
- }, (0, _lodash().omit)(this.options, ['name', 'type', 'target'])), {}, {
79
+ }, (0, _lodash().omit)(this.options, ['name', 'type', 'target', 'onDelete'])), {}, {
69
80
  as: this.name,
70
81
  foreignKey: this.foreignKey
71
82
  })); // 建立关系之后从 pending 列表中删除
@@ -100,6 +111,7 @@ class HasOneField extends _relationField.RelationField {
100
111
  tcoll.addIndex([this.options.foreignKey]);
101
112
  }
102
113
 
114
+ this.database.referenceMap.addReference(this.reference(association));
103
115
  return true;
104
116
  }
105
117
 
@@ -122,8 +134,10 @@ class HasOneField extends _relationField.RelationField {
122
134
 
123
135
  if (!field) {
124
136
  tcoll.model.removeAttribute(foreignKey);
125
- } // 删掉 model 的关联字段
137
+ }
126
138
 
139
+ const association = collection.model.associations[this.name];
140
+ this.database.referenceMap.removeReference(this.reference(association)); // 删掉 model 的关联字段
127
141
 
128
142
  delete collection.model.associations[this.name]; // @ts-ignore
129
143
 
@@ -115,9 +115,6 @@ class FilterParser {
115
115
  debug('flattened filter %o', flattenedFilter);
116
116
  const include = {};
117
117
  const where = {};
118
-
119
- const filter2 = _lodash().default.cloneDeep(flattenedFilter);
120
-
121
118
  let skipPrefix = null;
122
119
  const associations = model.associations;
123
120
  debug('associations %O', associations);
@@ -130,6 +127,11 @@ class FilterParser {
130
127
  // 处理 filter 条件
131
128
  if (skipPrefix && key.startsWith(skipPrefix)) {
132
129
  continue;
130
+ } // skip empty logic operator value
131
+
132
+
133
+ if ((key == '$or' || key == '$and') && Array.isArray(value) && value.length == 0) {
134
+ continue;
133
135
  }
134
136
 
135
137
  debug('handle filter key "%s: "%s"', key, value);
package/lib/model.d.ts CHANGED
@@ -7,6 +7,7 @@ interface IModel {
7
7
  export declare class Model<TModelAttributes extends {} = any, TCreationAttributes extends {} = TModelAttributes> extends SequelizeModel<TModelAttributes, TCreationAttributes> implements IModel {
8
8
  static database: Database;
9
9
  static collection: Collection;
10
+ [key: string]: any;
10
11
  protected _changedWithAssociations: Set<unknown>;
11
12
  protected _previousDataValuesWithAssociations: {};
12
13
  toChangedWithAssociations(): void;
@@ -261,8 +261,9 @@ class OptionsParser {
261
261
  if (!appends) return filterParams;
262
262
  /**
263
263
  * set include params
264
- * @param includeRoot
265
- * @param appends
264
+ * @param model
265
+ * @param queryParams
266
+ * @param append
266
267
  */
267
268
 
268
269
  const setInclude = (model, queryParams, append) => {
@@ -1,14 +1,14 @@
1
1
  import { Transaction } from 'sequelize';
2
2
  import { Model } from '../model';
3
- import { CreateOptions, DestroyOptions, FindOptions, TargetKey, UpdateOptions } from '../repository';
4
- import { FindAndCountOptions, FindOneOptions, MultipleRelationRepository } from './multiple-relation-repository';
3
+ import { CreateOptions, DestroyOptions, FindOneOptions, FindOptions, TargetKey, UpdateOptions } from '../repository';
4
+ import { FindAndCountOptions, MultipleRelationRepository } from './multiple-relation-repository';
5
5
  import { AssociatedOptions, PrimaryKeyWithThroughValues } from './types';
6
6
  declare type CreateBelongsToManyOptions = CreateOptions;
7
7
  interface IBelongsToManyRepository<M extends Model> {
8
8
  find(options?: FindOptions): Promise<M[]>;
9
9
  findAndCount(options?: FindAndCountOptions): Promise<[M[], number]>;
10
10
  findOne(options?: FindOneOptions): Promise<M>;
11
- create(options?: CreateBelongsToManyOptions): Promise<M>;
11
+ create(options?: CreateOptions): Promise<M>;
12
12
  update(options?: UpdateOptions): Promise<M>;
13
13
  destroy(options?: number | string | number[] | string[] | DestroyOptions): Promise<Boolean>;
14
14
  set(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import { Model } from '../model';
2
- import { CreateOptions, DestroyOptions, FindOptions, TargetKey, TK, UpdateOptions } from '../repository';
3
- import { AssociatedOptions, FindAndCountOptions, FindOneOptions, MultipleRelationRepository } from './multiple-relation-repository';
2
+ import { CreateOptions, DestroyOptions, FindOneOptions, FindOptions, TargetKey, TK, UpdateOptions } from '../repository';
3
+ import { AssociatedOptions, FindAndCountOptions, MultipleRelationRepository } from './multiple-relation-repository';
4
4
  interface IHasManyRepository<M extends Model> {
5
5
  find(options?: FindOptions): Promise<M>;
6
6
  findAndCount(options?: FindAndCountOptions): Promise<[M[], number]>;
@@ -1,11 +1,9 @@
1
1
  import { Model } from '../model';
2
2
  import { CreateOptions } from '../repository';
3
3
  import { SingleRelationFindOption, SingleRelationRepository } from './single-relation-repository';
4
- interface HasOneFindOptions extends SingleRelationFindOption {
5
- }
6
4
  interface IHasOneRepository<M extends Model> {
7
- find(options?: HasOneFindOptions): Promise<M>;
8
- findOne(options?: HasOneFindOptions): Promise<M>;
5
+ find(options?: SingleRelationFindOption): Promise<M>;
6
+ findOne(options?: SingleRelationFindOption): Promise<M>;
9
7
  create(options?: CreateOptions): Promise<M>;
10
8
  update(options?: any): Promise<M>;
11
9
  destroy(): Promise<Boolean>;
@@ -1,10 +1,7 @@
1
1
  import { MultiAssociationAccessors, Transaction, Transactionable } from 'sequelize';
2
- import { CommonFindOptions, CountOptions, DestroyOptions, Filter, FilterByTk, FindOptions, TargetKey, TK, UpdateOptions } from '../repository';
2
+ import { CommonFindOptions, CountOptions, DestroyOptions, Filter, FindOneOptions, FindOptions, TargetKey, TK, UpdateOptions } from '../repository';
3
3
  import { RelationRepository } from './relation-repository';
4
- export interface FindAndCountOptions extends CommonFindOptions {
5
- }
6
- export interface FindOneOptions extends CommonFindOptions, FilterByTk {
7
- }
4
+ export declare type FindAndCountOptions = CommonFindOptions;
8
5
  export interface AssociatedOptions extends Transactionable {
9
6
  tk?: TK;
10
7
  }
@@ -15,12 +15,12 @@ function _lodash() {
15
15
  return data;
16
16
  }
17
17
 
18
+ var _transactionDecorator = require("../decorators/transaction-decorator");
19
+
18
20
  var _filterParser = _interopRequireDefault(require("../filter-parser"));
19
21
 
20
22
  var _optionsParser = require("../options-parser");
21
23
 
22
- var _transactionDecorator = require("../decorators/transaction-decorator");
23
-
24
24
  var _updateAssociations = require("../update-associations");
25
25
 
26
26
  var _updateGuard = require("../update-guard");
@@ -14,7 +14,7 @@ interface SetOption extends Transactionable {
14
14
  export declare abstract class SingleRelationRepository extends RelationRepository {
15
15
  remove(options?: Transactionable): Promise<void>;
16
16
  set(options: TargetKey | SetOption): Promise<void>;
17
- find(options?: SingleRelationFindOption): Promise<Model<any>>;
17
+ find(options?: SingleRelationFindOption): Promise<Model<any> | null>;
18
18
  findOne(options?: SingleRelationFindOption): Promise<Model<any>>;
19
19
  destroy(options?: Transactionable): Promise<Boolean>;
20
20
  update(options: UpdateOptions): Promise<any>;
@@ -1,7 +1,10 @@
1
- import { Association, BulkCreateOptions, CreateOptions as SequelizeCreateOptions, DestroyOptions as SequelizeDestroyOptions, FindAndCountOptions as SequelizeAndCountOptions, FindOptions as SequelizeFindOptions, ModelCtor, Transactionable, UpdateOptions as SequelizeUpdateOptions } from 'sequelize';
1
+ /// <reference types="node" />
2
+ import { Association, BulkCreateOptions, CountOptions as SequelizeCountOptions, CreateOptions as SequelizeCreateOptions, DestroyOptions as SequelizeDestroyOptions, FindAndCountOptions as SequelizeAndCountOptions, FindOptions as SequelizeFindOptions, ModelCtor, Transactionable, UpdateOptions as SequelizeUpdateOptions } from 'sequelize';
3
+ import { WhereOperators } from 'sequelize/types/lib/model';
2
4
  import { Collection } from './collection';
3
5
  import { Database } from './database';
4
6
  import { Model } from './model';
7
+ import operators from './operators';
5
8
  import { BelongsToManyRepository } from './relation-repository/belongs-to-many-repository';
6
9
  import { BelongsToRepository } from './relation-repository/belongs-to-repository';
7
10
  import { HasManyRepository } from './relation-repository/hasmany-repository';
@@ -18,7 +21,23 @@ export interface FilterAble {
18
21
  }
19
22
  export declare type TargetKey = string | number;
20
23
  export declare type TK = TargetKey | TargetKey[];
21
- export declare type Filter = any;
24
+ declare type FieldValue = string | number | bigint | boolean | Date | Buffer | null | FieldValue[] | FilterWithOperator;
25
+ declare type Operators = keyof typeof operators & keyof WhereOperators;
26
+ export declare type FilterWithOperator = {
27
+ [key: string]: {
28
+ [K in Operators]: FieldValue;
29
+ } | FieldValue;
30
+ };
31
+ export declare type FilterWithValue = {
32
+ [key: string]: FieldValue;
33
+ };
34
+ declare type FilterAnd = {
35
+ $and: Filter[];
36
+ };
37
+ declare type FilterOr = {
38
+ $or: Filter[];
39
+ };
40
+ export declare type Filter = FilterWithOperator | FilterWithValue | FilterAnd | FilterOr;
22
41
  export declare type Appends = string[];
23
42
  export declare type Except = string[];
24
43
  export declare type Fields = string[];
@@ -27,16 +46,14 @@ export declare type WhiteList = string[];
27
46
  export declare type BlackList = string[];
28
47
  export declare type AssociationKeysToBeUpdate = string[];
29
48
  export declare type Values = any;
30
- export interface CountOptions extends Omit<SequelizeCreateOptions, 'distinct' | 'where' | 'include'>, Transactionable {
31
- fields?: Fields;
49
+ export interface CountOptions extends Omit<SequelizeCountOptions, 'distinct' | 'where' | 'include'>, Transactionable {
32
50
  filter?: Filter;
33
51
  context?: any;
34
52
  }
35
53
  export interface FilterByTk {
36
54
  filterByTk?: TargetKey;
37
55
  }
38
- export interface FindOptions extends SequelizeFindOptions, CommonFindOptions, FilterByTk {
39
- }
56
+ export declare type FindOptions = SequelizeFindOptions & CommonFindOptions & FilterByTk;
40
57
  export interface CommonFindOptions extends Transactionable {
41
58
  filter?: Filter;
42
59
  fields?: Fields;
@@ -45,21 +62,14 @@ export interface CommonFindOptions extends Transactionable {
45
62
  sort?: Sort;
46
63
  context?: any;
47
64
  }
48
- interface FindOneOptions extends FindOptions {
49
- }
65
+ export declare type FindOneOptions = Omit<FindOptions, 'limit'>;
50
66
  export interface DestroyOptions extends SequelizeDestroyOptions {
51
67
  filter?: Filter;
52
68
  filterByTk?: TargetKey | TargetKey[];
53
69
  truncate?: boolean;
54
70
  context?: any;
55
71
  }
56
- interface FindAndCountOptions extends Omit<SequelizeAndCountOptions, 'where' | 'include' | 'order'> {
57
- filter?: Filter;
58
- fields?: Fields;
59
- except?: Except;
60
- appends?: Appends;
61
- sort?: Sort;
62
- }
72
+ declare type FindAndCountOptions = Omit<SequelizeAndCountOptions, 'where' | 'include' | 'order'> & CommonFindOptions;
63
73
  export interface CreateOptions extends SequelizeCreateOptions {
64
74
  values?: Values | Values[];
65
75
  whitelist?: WhiteList;
package/lib/repository.js CHANGED
@@ -219,7 +219,9 @@ class Repository {
219
219
  options = _objectSpread(_objectSpread({}, options), {}, {
220
220
  transaction
221
221
  });
222
- return [yield _this3.find(options), yield _this3.count(options)];
222
+ const count = yield _this3.count(options);
223
+ const results = count ? yield _this3.find(options) : [];
224
+ return [results, count];
223
225
  })();
224
226
  }
225
227
  /**
package/lib/types.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ import type { Model } from './model';
2
+ import type { ValidationOptions } from 'sequelize/types/lib/instance-validator';
3
+ import type { HookReturn } from 'sequelize/types/lib/hooks';
4
+ import type { CreateOptions, DestroyOptions, SaveOptions, SyncOptions, UpdateOptions } from 'sequelize/types';
5
+ import { Collection, CollectionOptions } from './collection';
6
+ export declare type CollectionNameType = string;
7
+ export declare type ModelSyncEventType = 'beforeSync' | 'afterSync';
8
+ export declare type ModelValidateEventType = 'beforeValidate' | 'afterValidate';
9
+ export declare type ModelCreateEventType = 'beforeCreate' | 'afterCreate';
10
+ export declare type ModelUpdateEventType = 'beforeUpdate' | 'afterUpdate';
11
+ export declare type ModelSaveEventType = 'beforeSave' | 'afterSave';
12
+ export declare type ModelDestroyEventType = 'beforeDestroy' | 'afterDestroy';
13
+ export declare type ModelCreateWithAssociationsEventType = 'afterCreateWithAssociations';
14
+ export declare type ModelUpdateWithAssociationsEventType = 'afterUpdateWithAssociations';
15
+ export declare type ModelSaveWithAssociationsEventType = 'afterSaveWithAssociations';
16
+ export declare type ModelValidateEventTypes = ModelValidateEventType | `${CollectionNameType}.${ModelValidateEventType}`;
17
+ export declare type ModelCreateEventTypes = ModelCreateEventType | `${CollectionNameType}.${ModelCreateEventType}`;
18
+ export declare type ModelUpdateEventTypes = ModelUpdateEventType | `${CollectionNameType}.${ModelUpdateEventType}`;
19
+ export declare type ModelSaveEventTypes = ModelSaveEventType | `${CollectionNameType}.${ModelSaveEventType}`;
20
+ export declare type ModelDestroyEventTypes = ModelDestroyEventType | `${CollectionNameType}.${ModelDestroyEventType}`;
21
+ export declare type ModelCreateWithAssociationsEventTypes = ModelCreateWithAssociationsEventType | `${CollectionNameType}.${ModelCreateWithAssociationsEventType}`;
22
+ export declare type ModelUpdateWithAssociationsEventTypes = ModelUpdateWithAssociationsEventType | `${CollectionNameType}.${ModelUpdateWithAssociationsEventType}`;
23
+ export declare type ModelSaveWithAssociationsEventTypes = ModelSaveWithAssociationsEventType | `${CollectionNameType}.${ModelSaveWithAssociationsEventType}`;
24
+ export declare type ModelEventTypes = ModelSyncEventType | ModelValidateEventTypes | ModelCreateEventTypes | ModelUpdateEventTypes | ModelSaveEventTypes | ModelDestroyEventTypes | ModelCreateWithAssociationsEventTypes | ModelUpdateWithAssociationsEventTypes | ModelSaveWithAssociationsEventTypes;
25
+ export declare type DatabaseBeforeDefineCollectionEventType = 'beforeDefineCollection';
26
+ export declare type DatabaseAfterDefineCollectionEventType = 'afterDefineCollection';
27
+ export declare type DatabaseBeforeRemoveCollectionEventType = 'beforeRemoveCollection';
28
+ export declare type DatabaseAfterRemoveCollectionEventType = 'afterRemoveCollection';
29
+ export declare type DatabaseEventTypes = DatabaseBeforeDefineCollectionEventType | DatabaseAfterDefineCollectionEventType | DatabaseBeforeRemoveCollectionEventType | DatabaseAfterRemoveCollectionEventType;
30
+ export declare type EventType = ModelEventTypes | DatabaseEventTypes | string;
31
+ export type { HookReturn };
32
+ export declare type SyncListener = (options?: SyncOptions) => HookReturn;
33
+ export declare type ValidateListener = (model: Model, options?: ValidationOptions) => HookReturn;
34
+ export declare type CreateListener = (model: Model, options?: CreateOptions) => HookReturn;
35
+ export declare type UpdateListener = (model: Model, options?: UpdateOptions) => HookReturn;
36
+ export declare type SaveListener = (model: Model, options?: SaveOptions) => HookReturn;
37
+ export declare type DestroyListener = (model: Model, options?: DestroyOptions) => HookReturn;
38
+ export declare type CreateWithAssociationsListener = (model: Model, options?: CreateOptions) => HookReturn;
39
+ export declare type UpdateWithAssociationsListener = (model: Model, options?: UpdateOptions) => HookReturn;
40
+ export declare type SaveWithAssociationsListener = (model: Model, options?: SaveOptions) => HookReturn;
41
+ export declare type BeforeDefineCollectionListener = (options: CollectionOptions) => void;
42
+ export declare type AfterDefineCollectionListener = (collection: Collection) => void;
43
+ export declare type RemoveCollectionListener = (collection: Collection) => void;
package/lib/types.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/database",
3
- "version": "0.7.7-alpha.1",
3
+ "version": "0.8.0-alpha.5",
4
4
  "description": "",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -12,7 +12,7 @@
12
12
  }
13
13
  ],
14
14
  "dependencies": {
15
- "@nocobase/utils": "0.7.7-alpha.1",
15
+ "@nocobase/utils": "0.8.0-alpha.5",
16
16
  "async-mutex": "^0.3.2",
17
17
  "cron-parser": "4.4.0",
18
18
  "deepmerge": "^4.2.2",
@@ -32,5 +32,5 @@
32
32
  "url": "git+https://github.com/nocobase/nocobase.git",
33
33
  "directory": "packages/database"
34
34
  },
35
- "gitHead": "f82374e6f9daaf71ba63eaf156468ea7ddc042da"
35
+ "gitHead": "335f62b0098089cc43a0488ea5c76136a655f97e"
36
36
  }
@@ -64,10 +64,13 @@ describe('belongs to field', () => {
64
64
  });
65
65
 
66
66
  it('custom targetKey and foreignKey', async () => {
67
- const Post = db.collection({
68
- name: 'posts',
69
- fields: [{ type: 'string', name: 'key', unique: true }],
67
+ db.collection({
68
+ name: "posts",
69
+ fields: [
70
+ { type: "string", name: "key" },
71
+ ]
70
72
  });
73
+
71
74
  const Comment = db.collection({
72
75
  name: 'comments',
73
76
  fields: [
@@ -79,6 +82,7 @@ describe('belongs to field', () => {
79
82
  },
80
83
  ],
81
84
  });
85
+
82
86
  const association = Comment.model.associations.post;
83
87
  expect(association).toBeDefined();
84
88
  expect(association.foreignKey).toBe('postKey');
@@ -99,7 +103,7 @@ describe('belongs to field', () => {
99
103
  let error;
100
104
 
101
105
  try {
102
- const Comment = db.collection({
106
+ db.collection({
103
107
  name: 'comments1',
104
108
  fields: [
105
109
  {
@@ -114,6 +118,7 @@ describe('belongs to field', () => {
114
118
  error = e;
115
119
  }
116
120
 
121
+
117
122
  expect(error).toBeInstanceOf(IdentifierError);
118
123
  });
119
124
 
@@ -194,4 +199,107 @@ describe('belongs to field', () => {
194
199
  const association = Post.model.associations;
195
200
  expect(association['comments']).toBeDefined();
196
201
  });
202
+
203
+ describe('foreign constraints', () => {
204
+ it('should set null on delete', async () => {
205
+ const Product = db.collection({
206
+ name: 'products',
207
+ fields: [{ type: 'string', name: 'name' }],
208
+ });
209
+
210
+ const Order = db.collection({
211
+ name: 'order',
212
+ fields: [{ type: 'belongsTo', name: 'product', onDelete: 'SET NULL' }],
213
+ });
214
+
215
+ await db.sync();
216
+
217
+ const p = await Product.repository.create({ values: { name: 'p1' } });
218
+ const o = await Order.repository.create({ values: { product: p.id } });
219
+
220
+ expect(o.productId).toBe(p.id);
221
+
222
+ await Product.repository.destroy({
223
+ filterByTk: p.id,
224
+ });
225
+
226
+ const newO = await o.reload();
227
+
228
+ expect(newO.productId).toBeNull();
229
+ });
230
+
231
+ it('should delete reference map item when field unbind', async () => {
232
+ const Product = db.collection({
233
+ name: 'products',
234
+ fields: [{ type: 'string', name: 'name' }],
235
+ });
236
+
237
+ const Order = db.collection({
238
+ name: 'order',
239
+ fields: [{ type: 'belongsTo', name: 'product', onDelete: 'CASCADE' }],
240
+ });
241
+
242
+ await db.sync();
243
+
244
+ Order.removeField('product');
245
+
246
+ expect(db.referenceMap.getReferences(Product.name)).toHaveLength(0);
247
+ });
248
+
249
+ it('should delete cascade', async () => {
250
+ const Product = db.collection({
251
+ name: 'products',
252
+ fields: [{ type: 'string', name: 'name' }],
253
+ });
254
+
255
+ const Order = db.collection({
256
+ name: 'order',
257
+ fields: [{ type: 'belongsTo', name: 'product', onDelete: 'CASCADE' }],
258
+ });
259
+
260
+ await db.sync();
261
+ const p = await Product.repository.create({ values: { name: 'p1' } });
262
+ await Order.repository.create({ values: { product: p.id } });
263
+ await Order.repository.create({ values: { product: p.id } });
264
+
265
+ expect(await Order.repository.count({ filter: { productId: p.id } })).toBe(2);
266
+
267
+ await Product.repository.destroy({
268
+ filterByTk: p.id,
269
+ });
270
+
271
+ expect(await Order.repository.count({ filter: { productId: p.id } })).toBe(0);
272
+ });
273
+
274
+ it('should delete restrict', async () => {
275
+ const Product = db.collection({
276
+ name: 'products',
277
+ fields: [{ type: 'string', name: 'name' }],
278
+ });
279
+
280
+ const Order = db.collection({
281
+ name: 'order',
282
+ fields: [{ type: 'belongsTo', name: 'product', onDelete: 'RESTRICT' }],
283
+ });
284
+
285
+ await db.sync();
286
+
287
+ const p = await Product.repository.create({ values: { name: 'p1' } });
288
+ const o = await Order.repository.create({ values: { product: p.id } });
289
+
290
+ expect(o.productId).toBe(p.id);
291
+
292
+ let error = null;
293
+
294
+ try {
295
+ await Product.repository.destroy({
296
+ filterByTk: p.id,
297
+ });
298
+ } catch (e) {
299
+ error = e;
300
+ }
301
+
302
+ expect(error).not.toBeNull();
303
+ });
304
+ });
197
305
  });
@@ -171,4 +171,87 @@ describe('has many field', () => {
171
171
 
172
172
  expect(error).toBeInstanceOf(IdentifierError);
173
173
  });
174
+
175
+ describe('foreign key constraint', function () {
176
+ it('should cascade delete', async () => {
177
+ const Post = db.collection({
178
+ name: 'posts',
179
+ fields: [
180
+ { type: 'string', name: 'title' },
181
+ { type: 'hasMany', name: 'comments', onDelete: 'CASCADE' },
182
+ ],
183
+ });
184
+
185
+ const Comment = db.collection({
186
+ name: 'comments',
187
+ fields: [
188
+ { type: 'string', name: 'content' },
189
+ { type: 'belongsTo', name: 'post', onDelete: "CASCADE" },
190
+ ],
191
+ });
192
+
193
+ await db.sync();
194
+
195
+ const post = await Post.repository.create({
196
+ values: {
197
+ title: 'post1',
198
+ },
199
+ });
200
+
201
+ const comment = await Comment.repository.create({
202
+ values: {
203
+ content: 'comment1',
204
+ postId: post.id,
205
+ },
206
+ });
207
+
208
+ await Post.repository.destroy({
209
+ filterByTk: post.id,
210
+ });
211
+
212
+ expect(await Comment.repository.count()).toEqual(0);
213
+ });
214
+
215
+ it('should throw error when foreign key constraint is violated', async function () {
216
+ const Post = db.collection({
217
+ name: 'posts',
218
+ fields: [
219
+ { type: 'string', name: 'title' },
220
+ { type: 'hasMany', name: 'comments', onDelete: 'RESTRICT' },
221
+ ],
222
+ });
223
+
224
+ const Comment = db.collection({
225
+ name: 'comments',
226
+ fields: [{ type: 'string', name: 'content' }],
227
+ });
228
+
229
+ await db.sync();
230
+
231
+ const post = await Post.repository.create({
232
+ values: {
233
+ title: 'post1',
234
+ },
235
+ });
236
+
237
+ const comment = await Comment.repository.create({
238
+ values: {
239
+ content: 'comment1',
240
+ postId: post.id,
241
+ },
242
+ });
243
+
244
+ let error;
245
+
246
+ try {
247
+ await Post.repository.destroy({
248
+ filterByTk: post.id,
249
+ });
250
+ } catch (e) {
251
+ error = e;
252
+ }
253
+
254
+ expect(error).toBeDefined();
255
+ });
256
+ });
174
257
  });