@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.
- package/lib/collection.d.ts +1 -1
- package/lib/collection.js +1 -0
- package/lib/database.d.ts +17 -2
- package/lib/database.js +53 -41
- package/lib/features/ReferencesMap.d.ts +15 -0
- package/lib/features/ReferencesMap.js +60 -0
- package/lib/features/referential-integrity-check.d.ts +8 -0
- package/lib/features/referential-integrity-check.js +89 -0
- package/lib/fields/belongs-to-field.d.ts +2 -0
- package/lib/fields/belongs-to-field.js +16 -2
- package/lib/fields/belongs-to-many-field.d.ts +1 -0
- package/lib/fields/belongs-to-many-field.js +8 -2
- package/lib/fields/context-field.d.ts +2 -1
- package/lib/fields/context-field.js +12 -8
- package/lib/fields/field.d.ts +2 -1
- package/lib/fields/field.js +1 -0
- package/lib/fields/has-many-field.d.ts +2 -0
- package/lib/fields/has-many-field.js +19 -1
- package/lib/fields/has-one-field.d.ts +2 -0
- package/lib/fields/has-one-field.js +16 -2
- package/lib/filter-parser.js +5 -3
- package/lib/model.d.ts +1 -0
- package/lib/options-parser.js +3 -2
- package/lib/relation-repository/belongs-to-many-repository.d.ts +3 -3
- package/lib/relation-repository/hasmany-repository.d.ts +2 -2
- package/lib/relation-repository/hasone-repository.d.ts +2 -4
- package/lib/relation-repository/multiple-relation-repository.d.ts +2 -5
- package/lib/relation-repository/relation-repository.js +2 -2
- package/lib/relation-repository/single-relation-repository.d.ts +1 -1
- package/lib/repository.d.ts +25 -15
- package/lib/repository.js +3 -1
- package/lib/types.d.ts +43 -0
- package/lib/types.js +5 -0
- package/package.json +3 -3
- package/src/__tests__/fields/belongs-to-field.test.ts +112 -4
- package/src/__tests__/fields/has-many-field.test.ts +83 -0
- package/src/__tests__/relation-repository/hasone-repository.test.ts +3 -3
- package/src/__tests__/repository/find.test.ts +10 -0
- package/src/collection.ts +3 -1
- package/src/database.ts +64 -15
- package/src/features/ReferencesMap.ts +64 -0
- package/src/features/referential-integrity-check.ts +61 -0
- package/src/fields/belongs-to-field.ts +21 -1
- package/src/fields/belongs-to-many-field.ts +6 -0
- package/src/fields/context-field.ts +5 -7
- package/src/fields/field.ts +4 -3
- package/src/fields/has-many-field.ts +25 -2
- package/src/fields/has-one-field.ts +22 -2
- package/src/filter-parser.ts +5 -1
- package/src/model.ts +1 -0
- package/src/options-parser.ts +4 -2
- package/src/relation-repository/belongs-to-many-repository.ts +3 -3
- package/src/relation-repository/hasmany-repository.ts +8 -5
- package/src/relation-repository/hasone-repository.ts +2 -4
- package/src/relation-repository/multiple-relation-repository.ts +3 -4
- package/src/relation-repository/relation-repository.ts +1 -1
- package/src/relation-repository/single-relation-repository.ts +1 -1
- package/src/repository.ts +41 -19
- 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
|
-
}
|
|
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
|
|
package/lib/filter-parser.js
CHANGED
|
@@ -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;
|
package/lib/options-parser.js
CHANGED
|
@@ -261,8 +261,9 @@ class OptionsParser {
|
|
|
261
261
|
if (!appends) return filterParams;
|
|
262
262
|
/**
|
|
263
263
|
* set include params
|
|
264
|
-
* @param
|
|
265
|
-
* @param
|
|
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,
|
|
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?:
|
|
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,
|
|
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?:
|
|
8
|
-
findOne(options?:
|
|
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,
|
|
2
|
+
import { CommonFindOptions, CountOptions, DestroyOptions, Filter, FindOneOptions, FindOptions, TargetKey, TK, UpdateOptions } from '../repository';
|
|
3
3
|
import { RelationRepository } from './relation-repository';
|
|
4
|
-
export
|
|
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>;
|
package/lib/repository.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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<
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/database",
|
|
3
|
-
"version": "0.
|
|
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.
|
|
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": "
|
|
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
|
-
|
|
68
|
-
name:
|
|
69
|
-
fields: [
|
|
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
|
-
|
|
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
|
});
|