@nocobase/database 0.7.4-alpha.7 → 0.7.6-alpha.1
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 +7 -2
- package/lib/collection.js +20 -8
- package/lib/database.d.ts +2 -2
- package/lib/database.js +30 -6
- package/lib/decorators/must-have-filter-decorator.d.ts +2 -0
- package/lib/decorators/must-have-filter-decorator.js +25 -0
- package/lib/{transaction-decorator.d.ts → decorators/transaction-decorator.d.ts} +0 -0
- package/lib/{transaction-decorator.js → decorators/transaction-decorator.js} +1 -4
- package/lib/errors/identifier-error.d.ts +3 -0
- package/lib/errors/identifier-error.js +16 -0
- package/lib/fields/belongs-to-field.js +9 -0
- package/lib/fields/belongs-to-many-field.js +10 -0
- package/lib/fields/date-field.d.ts +1 -1
- package/lib/fields/date-field.js +1 -1
- package/lib/fields/field.d.ts +1 -1
- package/lib/fields/field.js +2 -3
- package/lib/fields/formula-field.d.ts +5 -5
- package/lib/fields/formula-field.js +123 -110
- package/lib/fields/has-many-field.js +9 -0
- package/lib/fields/has-one-field.js +9 -0
- package/lib/fields/index.d.ts +3 -1
- package/lib/fields/index.js +34 -1
- package/lib/fields/number-field.d.ts +6 -0
- package/lib/fields/number-field.js +10 -1
- package/lib/fields/radio-field.d.ts +3 -1
- package/lib/fields/radio-field.js +14 -11
- package/lib/fields/sequence-field.d.ts +31 -0
- package/lib/fields/sequence-field.js +299 -0
- package/lib/fields/sort-field.d.ts +4 -4
- package/lib/fields/sort-field.js +93 -80
- package/lib/filter-parser.js +10 -2
- package/lib/index.d.ts +1 -1
- package/lib/index.js +7 -0
- package/lib/magic-attribute-model.d.ts +1 -0
- package/lib/magic-attribute-model.js +207 -6
- package/lib/mock-database.js +1 -1
- package/lib/model.d.ts +5 -0
- package/lib/model.js +48 -6
- package/lib/operators/array.js +17 -9
- package/lib/operators/ne.d.ts +4 -0
- package/lib/operators/ne.js +3 -1
- package/lib/relation-repository/belongs-to-many-repository.js +6 -0
- package/lib/relation-repository/multiple-relation-repository.js +32 -9
- package/lib/relation-repository/relation-repository.js +7 -1
- package/lib/relation-repository/single-relation-repository.js +28 -0
- package/lib/repository.d.ts +6 -3
- package/lib/repository.js +42 -9
- package/lib/update-associations.js +48 -71
- package/lib/utils.d.ts +9 -0
- package/lib/utils.js +126 -0
- package/package.json +5 -3
- package/src/__tests__/collection.test.ts +47 -0
- package/src/__tests__/database.test.ts +2 -0
- package/src/__tests__/field-options/inddex.test.ts +43 -0
- package/src/__tests__/fields/array.test.ts +66 -0
- package/src/__tests__/fields/belongs-to-field.test.ts +35 -0
- package/src/__tests__/fields/belongs-to-many-field.test.ts +45 -0
- package/src/__tests__/fields/has-many-field.test.ts +22 -0
- package/src/__tests__/fields/has-one-field.test.ts +21 -0
- package/src/__tests__/fields/sequence-field.test.ts +455 -0
- package/src/__tests__/magic-attribute-model.test.ts +24 -0
- package/src/__tests__/model.changedWithAssociations.test.ts +46 -0
- package/src/__tests__/operator/ne.test.ts +12 -0
- package/src/__tests__/relation-repository/appends.test.ts +64 -0
- package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +23 -0
- package/src/__tests__/relation-repository/has-many-repository.test.ts +20 -0
- package/src/__tests__/relation-repository/hasone-repository.test.ts +68 -1
- package/src/__tests__/repository/find.test.ts +35 -0
- package/src/__tests__/repository/update.test.ts +35 -0
- package/src/__tests__/repository.test.ts +15 -0
- package/src/collection.ts +28 -13
- package/src/database.ts +18 -6
- package/src/decorators/must-have-filter-decorator.ts +17 -0
- package/src/{transaction-decorator.ts → decorators/transaction-decorator.ts} +1 -2
- package/src/errors/identifier-error.ts +6 -0
- package/src/fields/belongs-to-field.ts +9 -0
- package/src/fields/belongs-to-many-field.ts +15 -0
- package/src/fields/date-field.ts +1 -1
- package/src/fields/field.ts +3 -3
- package/src/fields/formula-field.ts +13 -13
- package/src/fields/has-many-field.ts +10 -0
- package/src/fields/has-one-field.ts +13 -0
- package/src/fields/index.ts +5 -2
- package/src/fields/number-field.ts +10 -0
- package/src/fields/radio-field.ts +22 -24
- package/src/fields/sequence-field.ts +200 -0
- package/src/fields/sort-field.ts +9 -9
- package/src/filter-parser.ts +6 -5
- package/src/index.ts +1 -1
- package/src/magic-attribute-model.ts +188 -6
- package/src/mock-database.ts +1 -1
- package/src/model.ts +34 -1
- package/src/operators/array.ts +17 -10
- package/src/operators/ne.ts +10 -6
- package/src/relation-repository/belongs-to-many-repository.ts +4 -0
- package/src/relation-repository/multiple-relation-repository.ts +26 -11
- package/src/relation-repository/relation-repository.ts +5 -1
- package/src/relation-repository/single-relation-repository.ts +27 -1
- package/src/repository.ts +40 -10
- package/src/update-associations.ts +41 -53
- package/src/utils.ts +71 -0
|
@@ -9,6 +9,9 @@ describe('has one repository', () => {
|
|
|
9
9
|
let User: Collection;
|
|
10
10
|
let Profile: Collection;
|
|
11
11
|
|
|
12
|
+
let A1: Collection;
|
|
13
|
+
let A2: Collection;
|
|
14
|
+
|
|
12
15
|
afterEach(async () => {
|
|
13
16
|
await db.close();
|
|
14
17
|
});
|
|
@@ -25,12 +28,75 @@ describe('has one repository', () => {
|
|
|
25
28
|
|
|
26
29
|
Profile = db.collection({
|
|
27
30
|
name: 'profiles',
|
|
28
|
-
fields: [
|
|
31
|
+
fields: [
|
|
32
|
+
{ type: 'string', name: 'avatar' },
|
|
33
|
+
{
|
|
34
|
+
type: 'hasMany',
|
|
35
|
+
name: 'a1',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'hasMany',
|
|
39
|
+
name: 'a2',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
A1 = db.collection({
|
|
45
|
+
name: 'a1',
|
|
46
|
+
fields: [{ type: 'string', name: 'name' }],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
A2 = db.collection({
|
|
50
|
+
name: 'a2',
|
|
51
|
+
fields: [{ type: 'string', name: 'name' }],
|
|
29
52
|
});
|
|
30
53
|
|
|
31
54
|
await db.sync();
|
|
32
55
|
});
|
|
33
56
|
|
|
57
|
+
test('find with appends', async () => {
|
|
58
|
+
const user = await User.repository.create({
|
|
59
|
+
values: {
|
|
60
|
+
name: 'u1',
|
|
61
|
+
profile: {
|
|
62
|
+
avatar: 'avatar',
|
|
63
|
+
a1: [
|
|
64
|
+
{
|
|
65
|
+
name: 'a11',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'a12',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'a13',
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
a2: [
|
|
75
|
+
{
|
|
76
|
+
name: 'a21',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'a22',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'a23',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const UserProfileRepository = new HasOneRepository(User, 'profile', user['id']);
|
|
90
|
+
|
|
91
|
+
const profile = await UserProfileRepository.find({
|
|
92
|
+
appends: ['a1', 'a2'],
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const data = profile.toJSON();
|
|
96
|
+
expect(data['a1']).toBeDefined();
|
|
97
|
+
expect(data['a2']).toBeDefined();
|
|
98
|
+
});
|
|
99
|
+
|
|
34
100
|
test('find', async () => {
|
|
35
101
|
const user = await User.repository.create({
|
|
36
102
|
values: { name: 'u1' },
|
|
@@ -70,6 +136,7 @@ describe('has one repository', () => {
|
|
|
70
136
|
avatar: 'new_updated_avatar',
|
|
71
137
|
},
|
|
72
138
|
});
|
|
139
|
+
|
|
73
140
|
expect((await UserProfileRepository.find())['avatar']).toEqual('new_updated_avatar');
|
|
74
141
|
await UserProfileRepository.destroy();
|
|
75
142
|
expect(await UserProfileRepository.find()).toBeNull();
|
|
@@ -10,6 +10,9 @@ describe('repository find', () => {
|
|
|
10
10
|
let Comment: Collection;
|
|
11
11
|
let Tag: Collection;
|
|
12
12
|
|
|
13
|
+
let A1: Collection;
|
|
14
|
+
let A2: Collection;
|
|
15
|
+
|
|
13
16
|
afterEach(async () => {
|
|
14
17
|
await db.close();
|
|
15
18
|
});
|
|
@@ -23,9 +26,21 @@ describe('repository find', () => {
|
|
|
23
26
|
{ type: 'string', name: 'name' },
|
|
24
27
|
{ type: 'integer', name: 'age' },
|
|
25
28
|
{ type: 'hasMany', name: 'posts' },
|
|
29
|
+
{ type: 'belongsToMany', name: 'a1' },
|
|
30
|
+
{ type: 'belongsToMany', name: 'a2' },
|
|
26
31
|
],
|
|
27
32
|
});
|
|
28
33
|
|
|
34
|
+
A1 = db.collection({
|
|
35
|
+
name: 'a1',
|
|
36
|
+
fields: [{ type: 'string', name: 'name' }],
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
A2 = db.collection({
|
|
40
|
+
name: 'a2',
|
|
41
|
+
fields: [{ type: 'string', name: 'name' }],
|
|
42
|
+
});
|
|
43
|
+
|
|
29
44
|
Post = db.collection({
|
|
30
45
|
name: 'posts',
|
|
31
46
|
fields: [
|
|
@@ -73,11 +88,15 @@ describe('repository find', () => {
|
|
|
73
88
|
name: 'u1',
|
|
74
89
|
age: 10,
|
|
75
90
|
posts: [{ title: 'u1t1', comments: [{ content: 'u1t1c1' }], abc1: [{ name: 't1' }] }],
|
|
91
|
+
a1: [{ name: 'u1a11' }, { name: 'u1a12' }],
|
|
92
|
+
a2: [{ name: 'u1a21' }, { name: 'u1a22' }],
|
|
76
93
|
},
|
|
77
94
|
{
|
|
78
95
|
name: 'u2',
|
|
79
96
|
age: 20,
|
|
80
97
|
posts: [{ title: 'u2t1', comments: [{ content: 'u2t1c1' }] }],
|
|
98
|
+
a1: [{ name: 'u2a11' }, { name: 'u2a12' }],
|
|
99
|
+
a2: [{ name: 'u2a21' }, { name: 'u2a22' }],
|
|
81
100
|
},
|
|
82
101
|
{
|
|
83
102
|
name: 'u3',
|
|
@@ -161,6 +180,22 @@ describe('repository find', () => {
|
|
|
161
180
|
});
|
|
162
181
|
|
|
163
182
|
describe('find with appends', () => {
|
|
183
|
+
test('toJSON', async () => {
|
|
184
|
+
const user = await User.repository.findOne({
|
|
185
|
+
filter: {
|
|
186
|
+
name: 'u1',
|
|
187
|
+
},
|
|
188
|
+
appends: ['a1', 'a2'],
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const data = user.toJSON();
|
|
192
|
+
|
|
193
|
+
expect(user['a1']).toBeDefined();
|
|
194
|
+
expect(user['a2']).toBeDefined();
|
|
195
|
+
expect(data['a1'][0].createdAt).toBeDefined();
|
|
196
|
+
expect(data['a2'][0].createdAt).toBeDefined();
|
|
197
|
+
});
|
|
198
|
+
|
|
164
199
|
test('filter attribute', async () => {
|
|
165
200
|
const user = await User.repository.findOne({
|
|
166
201
|
filter: {
|
|
@@ -57,4 +57,39 @@ describe('update', () => {
|
|
|
57
57
|
|
|
58
58
|
expect(p1.toJSON()['tags']).toEqual([]);
|
|
59
59
|
});
|
|
60
|
+
|
|
61
|
+
it('should not update items without filter or filterByPk', async () => {
|
|
62
|
+
await db.getRepository('posts').create({
|
|
63
|
+
values: {
|
|
64
|
+
title: 'p1',
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
let error;
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
await db.getRepository('posts').update({
|
|
72
|
+
values: {
|
|
73
|
+
title: 'p3',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
} catch (e) {
|
|
77
|
+
error = e;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
expect(error).not.toBeUndefined();
|
|
81
|
+
|
|
82
|
+
const p1 = await db.getRepository('posts').findOne();
|
|
83
|
+
expect(p1.toJSON()['title']).toEqual('p1');
|
|
84
|
+
|
|
85
|
+
await db.getRepository('posts').update({
|
|
86
|
+
values: {
|
|
87
|
+
title: 'p3',
|
|
88
|
+
},
|
|
89
|
+
filterByTk: p1.get('id') as number,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await p1.reload();
|
|
93
|
+
expect(p1.toJSON()['title']).toEqual('p3');
|
|
94
|
+
});
|
|
60
95
|
});
|
|
@@ -234,6 +234,21 @@ describe('repository.create', () => {
|
|
|
234
234
|
const comments = await Comment.model.findAll();
|
|
235
235
|
expect(comments.map((m) => m.get('postId'))).toEqual([post.get('id'), post.get('id'), post.get('id')]);
|
|
236
236
|
});
|
|
237
|
+
|
|
238
|
+
it('can create with array of values', async () => {
|
|
239
|
+
const users = await User.repository.create({
|
|
240
|
+
values: [
|
|
241
|
+
{
|
|
242
|
+
name: 'u1',
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: 'u2',
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
expect(users.length).toEqual(2);
|
|
251
|
+
});
|
|
237
252
|
});
|
|
238
253
|
|
|
239
254
|
describe('repository.update', () => {
|
package/src/collection.ts
CHANGED
|
@@ -7,12 +7,13 @@ import {
|
|
|
7
7
|
QueryInterfaceDropTableOptions,
|
|
8
8
|
SyncOptions,
|
|
9
9
|
Transactionable,
|
|
10
|
-
Utils
|
|
10
|
+
Utils,
|
|
11
11
|
} from 'sequelize';
|
|
12
12
|
import { Database } from './database';
|
|
13
13
|
import { Field, FieldOptions } from './fields';
|
|
14
14
|
import { Model } from './model';
|
|
15
15
|
import { Repository } from './repository';
|
|
16
|
+
import { checkIdentifier, md5 } from './utils';
|
|
16
17
|
|
|
17
18
|
export type RepositoryType = typeof Repository;
|
|
18
19
|
|
|
@@ -66,8 +67,11 @@ export class Collection<
|
|
|
66
67
|
|
|
67
68
|
constructor(options: CollectionOptions, context?: CollectionContext) {
|
|
68
69
|
super();
|
|
70
|
+
this.checkOptions(options);
|
|
71
|
+
|
|
69
72
|
this.context = context;
|
|
70
73
|
this.options = options;
|
|
74
|
+
|
|
71
75
|
this.bindFieldEventListener();
|
|
72
76
|
this.modelInit();
|
|
73
77
|
this.setFields(options.fields);
|
|
@@ -75,6 +79,10 @@ export class Collection<
|
|
|
75
79
|
this.setSortable(options.sortable);
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
private checkOptions(options: CollectionOptions) {
|
|
83
|
+
checkIdentifier(options.name);
|
|
84
|
+
}
|
|
85
|
+
|
|
78
86
|
private sequelizeModelOptions() {
|
|
79
87
|
const { name, tableName } = this.options;
|
|
80
88
|
return {
|
|
@@ -134,10 +142,8 @@ export class Collection<
|
|
|
134
142
|
}
|
|
135
143
|
|
|
136
144
|
private bindFieldEventListener() {
|
|
137
|
-
this.on('field.afterAdd', (field: Field) =>
|
|
138
|
-
|
|
139
|
-
});
|
|
140
|
-
this.on('field.afterRemove', (field) => field.unbind());
|
|
145
|
+
this.on('field.afterAdd', (field: Field) => field.bind());
|
|
146
|
+
this.on('field.afterRemove', (field: Field) => field.unbind());
|
|
141
147
|
}
|
|
142
148
|
|
|
143
149
|
forEachField(callback: (field: Field) => void) {
|
|
@@ -161,6 +167,8 @@ export class Collection<
|
|
|
161
167
|
}
|
|
162
168
|
|
|
163
169
|
setField(name: string, options: FieldOptions): Field {
|
|
170
|
+
checkIdentifier(name);
|
|
171
|
+
|
|
164
172
|
const { database } = this.context;
|
|
165
173
|
|
|
166
174
|
const field = database.buildField(
|
|
@@ -170,6 +178,7 @@ export class Collection<
|
|
|
170
178
|
collection: this,
|
|
171
179
|
},
|
|
172
180
|
);
|
|
181
|
+
|
|
173
182
|
this.removeField(name);
|
|
174
183
|
this.fields.set(name, field);
|
|
175
184
|
this.emit('field.afterAdd', field);
|
|
@@ -217,7 +226,7 @@ export class Collection<
|
|
|
217
226
|
return this.db.collectionExistsInDb(this.name, options);
|
|
218
227
|
}
|
|
219
228
|
|
|
220
|
-
removeField(name) {
|
|
229
|
+
removeField(name: string): void | Field {
|
|
221
230
|
if (!this.fields.has(name)) {
|
|
222
231
|
return;
|
|
223
232
|
}
|
|
@@ -285,7 +294,7 @@ export class Collection<
|
|
|
285
294
|
this.setField(options.name || name, options);
|
|
286
295
|
}
|
|
287
296
|
|
|
288
|
-
addIndex(index: any) {
|
|
297
|
+
addIndex(index: string | string[] | { fields: string[]; unique?: boolean; [key: string]: any }) {
|
|
289
298
|
if (!index) {
|
|
290
299
|
return;
|
|
291
300
|
}
|
|
@@ -331,7 +340,13 @@ export class Collection<
|
|
|
331
340
|
// @ts-ignore
|
|
332
341
|
this.model._indexes = this.model.options.indexes
|
|
333
342
|
// @ts-ignore
|
|
334
|
-
.map((index) => Utils.nameIndex(this.model._conformIndex(index), tableName))
|
|
343
|
+
.map((index) => Utils.nameIndex(this.model._conformIndex(index), tableName))
|
|
344
|
+
.map((item) => {
|
|
345
|
+
if (item.name && item.name.length > 63) {
|
|
346
|
+
item.name = 'i_' + md5(item.name);
|
|
347
|
+
}
|
|
348
|
+
return item;
|
|
349
|
+
});
|
|
335
350
|
}
|
|
336
351
|
|
|
337
352
|
removeIndex(fields: any) {
|
|
@@ -347,22 +362,22 @@ export class Collection<
|
|
|
347
362
|
}
|
|
348
363
|
|
|
349
364
|
async sync(syncOptions?: SyncOptions) {
|
|
350
|
-
const modelNames = [this.model.name];
|
|
365
|
+
const modelNames = new Set([this.model.name]);
|
|
351
366
|
|
|
352
|
-
const associations = this.model
|
|
367
|
+
const { associations } = this.model;
|
|
353
368
|
|
|
354
369
|
for (const associationKey in associations) {
|
|
355
370
|
const association = associations[associationKey];
|
|
356
|
-
modelNames.
|
|
371
|
+
modelNames.add(association.target.name);
|
|
357
372
|
if ((<any>association).through) {
|
|
358
|
-
modelNames.
|
|
373
|
+
modelNames.add((<any>association).through.model.name);
|
|
359
374
|
}
|
|
360
375
|
}
|
|
361
376
|
|
|
362
377
|
const models: ModelCtor<Model>[] = [];
|
|
363
378
|
// @ts-ignore
|
|
364
379
|
this.context.database.sequelize.modelManager.forEachModel((model) => {
|
|
365
|
-
if (modelNames.
|
|
380
|
+
if (modelNames.has(model.name)) {
|
|
366
381
|
models.push(model);
|
|
367
382
|
}
|
|
368
383
|
});
|
package/src/database.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { Collection, CollectionOptions, RepositoryType } from './collection';
|
|
|
21
21
|
import { ImporterReader, ImportFileExtension } from './collection-importer';
|
|
22
22
|
import * as FieldTypes from './fields';
|
|
23
23
|
import { Field, FieldContext, RelationField } from './fields';
|
|
24
|
-
import { Migrations } from './migration';
|
|
24
|
+
import { MigrationItem, Migrations } from './migration';
|
|
25
25
|
import { Model } from './model';
|
|
26
26
|
import { ModelHook } from './model-hook';
|
|
27
27
|
import extendOperators from './operators';
|
|
@@ -81,14 +81,16 @@ class DatabaseVersion {
|
|
|
81
81
|
},
|
|
82
82
|
mysql: {
|
|
83
83
|
sql: 'select version() as version',
|
|
84
|
-
get: (v) =>
|
|
84
|
+
get: (v) => {
|
|
85
|
+
const m = /([\d+\.]+)/.exec(v);
|
|
86
|
+
return m[0];
|
|
87
|
+
},
|
|
85
88
|
},
|
|
86
89
|
postgres: {
|
|
87
90
|
sql: 'select version() as version',
|
|
88
91
|
get: (v) => {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
return semver.minVersion(keys.shift()).version;
|
|
92
|
+
const m = /([\d+\.]+)/.exec(v);
|
|
93
|
+
return semver.minVersion(m[0]).version;
|
|
92
94
|
},
|
|
93
95
|
},
|
|
94
96
|
};
|
|
@@ -203,9 +205,17 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|
|
203
205
|
opts.tableName = `${this.options.tablePrefix}${opts.tableName || opts.modelName || opts.name.plural}`;
|
|
204
206
|
}
|
|
205
207
|
});
|
|
208
|
+
|
|
209
|
+
this.on('afterCreate', async (instance) => {
|
|
210
|
+
instance?.toChangedWithAssociations?.();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
this.on('afterUpdate', async (instance) => {
|
|
214
|
+
instance?.toChangedWithAssociations?.();
|
|
215
|
+
});
|
|
206
216
|
}
|
|
207
217
|
|
|
208
|
-
addMigration(item) {
|
|
218
|
+
addMigration(item: MigrationItem) {
|
|
209
219
|
return this.migrations.add(item);
|
|
210
220
|
}
|
|
211
221
|
|
|
@@ -369,9 +379,11 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|
|
369
379
|
buildField(options, context: FieldContext) {
|
|
370
380
|
const { type } = options;
|
|
371
381
|
const Field = this.fieldTypes.get(type);
|
|
382
|
+
|
|
372
383
|
if (!Field) {
|
|
373
384
|
throw Error(`unsupported field type ${type}`);
|
|
374
385
|
}
|
|
386
|
+
|
|
375
387
|
return new Field(options, context);
|
|
376
388
|
}
|
|
377
389
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const mustHaveFilter = () => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
2
|
+
const oldValue = descriptor.value;
|
|
3
|
+
|
|
4
|
+
descriptor.value = function () {
|
|
5
|
+
const options = arguments[0];
|
|
6
|
+
|
|
7
|
+
if (!options?.filter && !options?.filterByTk && !options?.forceUpdate) {
|
|
8
|
+
throw new Error(`must provide filter or filterByTk for ${propertyKey} call, or set forceUpdate to true`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return oldValue.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
return descriptor;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default mustHaveFilter;
|
|
@@ -37,13 +37,12 @@ export function transactionWrapperBuilder(transactionGenerator) {
|
|
|
37
37
|
throw new Error(`please provide transactionInjector for ${name} call`);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const results = await oldValue.
|
|
40
|
+
const results = await oldValue.call(this, callArguments);
|
|
41
41
|
|
|
42
42
|
await transaction.commit();
|
|
43
43
|
|
|
44
44
|
return results;
|
|
45
45
|
} catch (err) {
|
|
46
|
-
console.error({ err });
|
|
47
46
|
await transaction.rollback();
|
|
48
47
|
throw err;
|
|
49
48
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { omit } from 'lodash';
|
|
2
2
|
import { BelongsToOptions as SequelizeBelongsToOptions, Utils } from 'sequelize';
|
|
3
|
+
import { checkIdentifier } from '../utils';
|
|
3
4
|
import { BaseRelationFieldOptions, RelationField } from './relation-field';
|
|
4
5
|
|
|
5
6
|
export class BelongsToField extends RelationField {
|
|
@@ -42,10 +43,18 @@ export class BelongsToField extends RelationField {
|
|
|
42
43
|
this.options.foreignKey = association.foreignKey;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
try {
|
|
47
|
+
checkIdentifier(this.options.foreignKey);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
this.unbind();
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
if (!this.options.sourceKey) {
|
|
46
54
|
// @ts-ignore
|
|
47
55
|
this.options.sourceKey = association.sourceKey;
|
|
48
56
|
}
|
|
57
|
+
|
|
49
58
|
this.collection.addIndex([this.options.foreignKey]);
|
|
50
59
|
return true;
|
|
51
60
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { omit } from 'lodash';
|
|
2
2
|
import { BelongsToManyOptions as SequelizeBelongsToManyOptions, Utils } from 'sequelize';
|
|
3
3
|
import { Collection } from '../collection';
|
|
4
|
+
import { checkIdentifier } from '../utils';
|
|
4
5
|
import { MultipleRelationFieldOptions, RelationField } from './relation-field';
|
|
5
6
|
|
|
6
7
|
export class BelongsToManyField extends RelationField {
|
|
@@ -23,6 +24,7 @@ export class BelongsToManyField extends RelationField {
|
|
|
23
24
|
database.addPendingField(this);
|
|
24
25
|
return false;
|
|
25
26
|
}
|
|
27
|
+
|
|
26
28
|
const through = this.through;
|
|
27
29
|
|
|
28
30
|
let Through: Collection;
|
|
@@ -33,6 +35,7 @@ export class BelongsToManyField extends RelationField {
|
|
|
33
35
|
Through = database.collection({
|
|
34
36
|
name: through,
|
|
35
37
|
});
|
|
38
|
+
|
|
36
39
|
Object.defineProperty(Through.model, 'isThrough', { value: true });
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -49,15 +52,27 @@ export class BelongsToManyField extends RelationField {
|
|
|
49
52
|
if (!this.options.foreignKey) {
|
|
50
53
|
this.options.foreignKey = association.foreignKey;
|
|
51
54
|
}
|
|
55
|
+
|
|
52
56
|
if (!this.options.sourceKey) {
|
|
53
57
|
this.options.sourceKey = association.sourceKey;
|
|
54
58
|
}
|
|
59
|
+
|
|
55
60
|
if (!this.options.otherKey) {
|
|
56
61
|
this.options.otherKey = association.otherKey;
|
|
57
62
|
}
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
checkIdentifier(this.options.foreignKey);
|
|
66
|
+
checkIdentifier(this.options.otherKey);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
this.unbind();
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
|
|
58
72
|
if (!this.options.through) {
|
|
59
73
|
this.options.through = this.through;
|
|
60
74
|
}
|
|
75
|
+
|
|
61
76
|
Through.addIndex([this.options.foreignKey]);
|
|
62
77
|
Through.addIndex([this.options.otherKey]);
|
|
63
78
|
return true;
|
package/src/fields/date-field.ts
CHANGED
package/src/fields/field.ts
CHANGED
|
@@ -5,10 +5,11 @@ import {
|
|
|
5
5
|
ModelIndexesOptions,
|
|
6
6
|
QueryInterfaceOptions,
|
|
7
7
|
SyncOptions,
|
|
8
|
-
Transactionable
|
|
8
|
+
Transactionable,
|
|
9
9
|
} from 'sequelize';
|
|
10
10
|
import { Collection } from '../collection';
|
|
11
11
|
import { Database } from '../database';
|
|
12
|
+
import { checkIdentifier } from '../utils';
|
|
12
13
|
|
|
13
14
|
export interface FieldContext {
|
|
14
15
|
database: Database;
|
|
@@ -53,7 +54,6 @@ export abstract class Field {
|
|
|
53
54
|
this.init();
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
// TODO
|
|
57
57
|
async sync(syncOptions: SyncOptions) {
|
|
58
58
|
await this.collection.sync({
|
|
59
59
|
...syncOptions,
|
|
@@ -179,7 +179,7 @@ export abstract class Field {
|
|
|
179
179
|
unbind() {
|
|
180
180
|
const { model } = this.context.collection;
|
|
181
181
|
model.removeAttribute(this.name);
|
|
182
|
-
if (this.options.index) {
|
|
182
|
+
if (this.options.index || this.options.unique) {
|
|
183
183
|
this.context.collection.removeIndex([this.name]);
|
|
184
184
|
}
|
|
185
185
|
}
|
|
@@ -16,7 +16,7 @@ export class FormulaField extends Field {
|
|
|
16
16
|
return result;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
async
|
|
19
|
+
initFieldData = async ({ transaction }) => {
|
|
20
20
|
const { expression, name } = this.options;
|
|
21
21
|
|
|
22
22
|
const records = await this.collection.repository.find({
|
|
@@ -42,7 +42,7 @@ export class FormulaField extends Field {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
async
|
|
45
|
+
caculateField = async (instance) => {
|
|
46
46
|
const { expression, name } = this.options;
|
|
47
47
|
const scope = instance.toJSON();
|
|
48
48
|
let result;
|
|
@@ -55,7 +55,7 @@ export class FormulaField extends Field {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
async
|
|
58
|
+
updateFieldData = async (instance, { transaction }) => {
|
|
59
59
|
if (this.collection.name === instance.collectionName && instance.name === this.options.name) {
|
|
60
60
|
this.options = Object.assign(this.options, instance.options);
|
|
61
61
|
const { name, expression } = this.options;
|
|
@@ -64,7 +64,7 @@ export class FormulaField extends Field {
|
|
|
64
64
|
order: [this.collection.model.primaryKeyAttribute],
|
|
65
65
|
transaction,
|
|
66
66
|
});
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
for (const record of records) {
|
|
69
69
|
const scope = record.toJSON();
|
|
70
70
|
const result = this.caculate(expression, scope);
|
|
@@ -84,18 +84,18 @@ export class FormulaField extends Field {
|
|
|
84
84
|
|
|
85
85
|
bind() {
|
|
86
86
|
super.bind();
|
|
87
|
-
this.on('afterSync', this.initFieldData
|
|
88
|
-
|
|
89
|
-
this.on('
|
|
90
|
-
this.on('
|
|
87
|
+
this.on('afterSync', this.initFieldData);
|
|
88
|
+
// TODO: should not depends on fields table (which is defined by other plugin)
|
|
89
|
+
this.database.on('fields.afterUpdate', this.updateFieldData);
|
|
90
|
+
this.on('beforeSave', this.caculateField);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
unbind() {
|
|
94
94
|
super.unbind();
|
|
95
|
-
this.off('
|
|
96
|
-
|
|
97
|
-
this.database.off('fields.afterUpdate', this.updateFieldData
|
|
98
|
-
this.off('afterSync', this.initFieldData
|
|
95
|
+
this.off('beforeSave', this.caculateField);
|
|
96
|
+
// TODO: should not depends on fields table
|
|
97
|
+
this.database.off('fields.afterUpdate', this.updateFieldData);
|
|
98
|
+
this.off('afterSync', this.initFieldData);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -103,4 +103,4 @@ export interface FormulaFieldOptions extends BaseColumnFieldOptions {
|
|
|
103
103
|
type: 'formula';
|
|
104
104
|
|
|
105
105
|
expression: string;
|
|
106
|
-
}
|
|
106
|
+
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
Utils
|
|
9
9
|
} from 'sequelize';
|
|
10
10
|
import { Collection } from '../collection';
|
|
11
|
+
import { checkIdentifier } from '../utils';
|
|
11
12
|
import { MultipleRelationFieldOptions, RelationField } from './relation-field';
|
|
12
13
|
|
|
13
14
|
export interface HasManyFieldOptions extends HasManyOptions {
|
|
@@ -98,6 +99,7 @@ export class HasManyField extends RelationField {
|
|
|
98
99
|
as: this.name,
|
|
99
100
|
foreignKey: this.foreignKey,
|
|
100
101
|
});
|
|
102
|
+
|
|
101
103
|
// inverse relation
|
|
102
104
|
// this.TargetModel.belongsTo(collection.model);
|
|
103
105
|
|
|
@@ -107,6 +109,14 @@ export class HasManyField extends RelationField {
|
|
|
107
109
|
if (!this.options.foreignKey) {
|
|
108
110
|
this.options.foreignKey = association.foreignKey;
|
|
109
111
|
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
checkIdentifier(this.options.foreignKey);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
this.unbind();
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
|
|
110
120
|
if (!this.options.sourceKey) {
|
|
111
121
|
// @ts-ignore
|
|
112
122
|
this.options.sourceKey = association.sourceKey;
|