@nocobase/database 0.7.4-alpha.4 → 0.7.5-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 +6 -4
- 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/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 +5 -3
- package/lib/repository.js +40 -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__/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 +11 -7
- 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/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 +37 -10
- package/src/update-associations.ts +41 -53
- package/src/utils.ts +71 -0
package/src/operators/array.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Op, Sequelize } from 'sequelize';
|
|
2
|
-
import {
|
|
2
|
+
import { isMySQL, isPg } from './utils';
|
|
3
3
|
|
|
4
4
|
const getFieldName = (ctx) => {
|
|
5
5
|
const fieldName = ctx.fieldName;
|
|
@@ -13,10 +13,11 @@ const escape = (value, ctx) => {
|
|
|
13
13
|
|
|
14
14
|
const sqliteExistQuery = (value, ctx) => {
|
|
15
15
|
const fieldName = getFieldName(ctx);
|
|
16
|
+
const name = ctx.fullName === fieldName ? `"${ctx.model.name}"."${fieldName}"` : `"${fieldName}"`;
|
|
16
17
|
|
|
17
18
|
const sqlArray = `(${value.map((v) => `'${v}'`).join(', ')})`;
|
|
18
19
|
|
|
19
|
-
const subQuery = `exists (select * from json_each(${
|
|
20
|
+
const subQuery = `exists (select * from json_each(${name}) where json_each.value in ${sqlArray})`;
|
|
20
21
|
|
|
21
22
|
return subQuery;
|
|
22
23
|
};
|
|
@@ -53,7 +54,8 @@ export default {
|
|
|
53
54
|
value = escape(JSON.stringify(value.sort()), ctx);
|
|
54
55
|
|
|
55
56
|
if (isMySQL(ctx)) {
|
|
56
|
-
|
|
57
|
+
const name = ctx.fullName === fieldName ? `\`${ctx.model.name}\`.\`${fieldName}\`` : `\`${fieldName}\``;
|
|
58
|
+
return Sequelize.literal(`JSON_CONTAINS(${name}, ${value}) AND JSON_CONTAINS(${value}, ${name})`);
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
return {
|
|
@@ -66,11 +68,13 @@ export default {
|
|
|
66
68
|
value = escape(JSON.stringify(value), ctx);
|
|
67
69
|
|
|
68
70
|
if (isPg(ctx)) {
|
|
69
|
-
|
|
71
|
+
const name = ctx.fullName === fieldName ? `"${ctx.model.name}"."${fieldName}"` : `"${fieldName}"`;
|
|
72
|
+
return Sequelize.literal(`not (${name} <@ ${value}::JSONB and ${name} @> ${value}::JSONB)`);
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
if (isMySQL(ctx)) {
|
|
73
|
-
|
|
76
|
+
const name = ctx.fullName === fieldName ? `\`${ctx.model.name}\`.\`${fieldName}\`` : `\`${fieldName}\``;
|
|
77
|
+
return Sequelize.literal(`not (JSON_CONTAINS(${name}, ${value}) AND JSON_CONTAINS(${value}, ${name}))`);
|
|
74
78
|
}
|
|
75
79
|
return {
|
|
76
80
|
[Op.ne]: Sequelize.literal(`json(${value})`),
|
|
@@ -81,8 +85,9 @@ export default {
|
|
|
81
85
|
const fieldName = getFieldName(ctx);
|
|
82
86
|
|
|
83
87
|
if (isPg(ctx)) {
|
|
88
|
+
const name = ctx.fullName === fieldName ? `"${ctx.model.name}"."${fieldName}"` : `"${fieldName}"`;
|
|
84
89
|
return Sequelize.literal(
|
|
85
|
-
`${
|
|
90
|
+
`${name} ?| ${escape(
|
|
86
91
|
value.map((i) => `${i}`),
|
|
87
92
|
ctx,
|
|
88
93
|
)}`,
|
|
@@ -91,8 +96,8 @@ export default {
|
|
|
91
96
|
|
|
92
97
|
if (isMySQL(ctx)) {
|
|
93
98
|
value = escape(JSON.stringify(value), ctx);
|
|
94
|
-
|
|
95
|
-
return Sequelize.literal(`JSON_OVERLAPS(${
|
|
99
|
+
const name = ctx.fullName === fieldName ? `\`${ctx.model.name}\`.\`${fieldName}\`` : `\`${fieldName}\``;
|
|
100
|
+
return Sequelize.literal(`JSON_OVERLAPS(${name}, ${value})`);
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
const subQuery = sqliteExistQuery(value, ctx);
|
|
@@ -105,9 +110,10 @@ export default {
|
|
|
105
110
|
|
|
106
111
|
if (isPg(ctx)) {
|
|
107
112
|
const fieldName = getFieldName(ctx);
|
|
113
|
+
const name = ctx.fullName === fieldName ? `"${ctx.model.name}"."${fieldName}"` : `"${fieldName}"`;
|
|
108
114
|
// pg single quote
|
|
109
115
|
where = Sequelize.literal(
|
|
110
|
-
`not (${
|
|
116
|
+
`not (${name} ?| ${escape(
|
|
111
117
|
value.map((i) => `${i}`),
|
|
112
118
|
ctx,
|
|
113
119
|
)})`,
|
|
@@ -115,7 +121,8 @@ export default {
|
|
|
115
121
|
} else if (isMySQL(ctx)) {
|
|
116
122
|
const fieldName = getFieldName(ctx);
|
|
117
123
|
value = escape(JSON.stringify(value), ctx);
|
|
118
|
-
|
|
124
|
+
const name = ctx.fullName === fieldName ? `\`${ctx.model.name}\`.\`${fieldName}\`` : `\`${fieldName}\``;
|
|
125
|
+
where = Sequelize.literal(`NOT JSON_OVERLAPS(${name}, ${value})`);
|
|
119
126
|
} else {
|
|
120
127
|
const subQuery = sqliteExistQuery(value, ctx);
|
|
121
128
|
|
package/src/operators/ne.ts
CHANGED
|
@@ -2,11 +2,15 @@ import { Op } from 'sequelize';
|
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
4
|
$ne(val, ctx) {
|
|
5
|
-
return
|
|
6
|
-
|
|
7
|
-
[Op.ne]:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
return val === null
|
|
6
|
+
? {
|
|
7
|
+
[Op.ne]: null,
|
|
8
|
+
}
|
|
9
|
+
: {
|
|
10
|
+
[Op.or]: {
|
|
11
|
+
[Op.ne]: val,
|
|
12
|
+
[Op.is]: null,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
11
15
|
},
|
|
12
16
|
};
|
|
@@ -31,6 +31,10 @@ interface IBelongsToManyRepository<M extends Model> {
|
|
|
31
31
|
export class BelongsToManyRepository extends MultipleRelationRepository implements IBelongsToManyRepository<any> {
|
|
32
32
|
@transaction()
|
|
33
33
|
async create(options?: CreateBelongsToManyOptions): Promise<any> {
|
|
34
|
+
if (Array.isArray(options.values)) {
|
|
35
|
+
return Promise.all(options.values.map((record) => this.create({ ...options, values: record })));
|
|
36
|
+
}
|
|
37
|
+
|
|
34
38
|
const transaction = await this.getTransaction(options);
|
|
35
39
|
|
|
36
40
|
const createAccessor = this.accessors().create;
|
|
@@ -9,11 +9,12 @@ import {
|
|
|
9
9
|
FindOptions,
|
|
10
10
|
TargetKey,
|
|
11
11
|
TK,
|
|
12
|
-
UpdateOptions
|
|
12
|
+
UpdateOptions,
|
|
13
13
|
} from '../repository';
|
|
14
14
|
import { updateModelByValues } from '../update-associations';
|
|
15
15
|
import { UpdateGuard } from '../update-guard';
|
|
16
16
|
import { RelationRepository, transaction } from './relation-repository';
|
|
17
|
+
import { handleAppendsQuery } from '../utils';
|
|
17
18
|
|
|
18
19
|
export interface FindAndCountOptions extends CommonFindOptions {}
|
|
19
20
|
|
|
@@ -52,16 +53,30 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|
|
52
53
|
group: `${this.targetModel.name}.${this.targetKey()}`,
|
|
53
54
|
transaction,
|
|
54
55
|
})
|
|
55
|
-
).map((row) =>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
).map((row) => {
|
|
57
|
+
return { row, pk: row.get(this.targetKey()) };
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (ids.length == 0) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return await handleAppendsQuery({
|
|
65
|
+
templateModel: ids[0].row,
|
|
66
|
+
queryPromises: findOptions.include.map((include) => {
|
|
67
|
+
return sourceModel[getAccessor]({
|
|
68
|
+
...omit(findOptions, ['limit', 'offset']),
|
|
69
|
+
include: [include],
|
|
70
|
+
where: {
|
|
71
|
+
[this.targetKey()]: {
|
|
72
|
+
[Op.in]: ids.map((id) => id.pk),
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
transaction,
|
|
76
|
+
}).then((rows) => {
|
|
77
|
+
return { rows, include };
|
|
78
|
+
});
|
|
79
|
+
}),
|
|
65
80
|
});
|
|
66
81
|
}
|
|
67
82
|
|
|
@@ -7,7 +7,7 @@ import FilterParser from '../filter-parser';
|
|
|
7
7
|
import { Model } from '../model';
|
|
8
8
|
import { OptionsParser } from '../options-parser';
|
|
9
9
|
import { CreateOptions, Filter, FindOptions } from '../repository';
|
|
10
|
-
import { transactionWrapperBuilder } from '../transaction-decorator';
|
|
10
|
+
import { transactionWrapperBuilder } from '../decorators/transaction-decorator';
|
|
11
11
|
import { updateAssociations } from '../update-associations';
|
|
12
12
|
import { UpdateGuard } from '../update-guard';
|
|
13
13
|
|
|
@@ -54,6 +54,10 @@ export abstract class RelationRepository {
|
|
|
54
54
|
|
|
55
55
|
@transaction()
|
|
56
56
|
async create(options?: CreateOptions): Promise<any> {
|
|
57
|
+
if (Array.isArray(options.values)) {
|
|
58
|
+
return Promise.all(options.values.map((record) => this.create({ ...options, values: record })));
|
|
59
|
+
}
|
|
60
|
+
|
|
57
61
|
const createAccessor = this.accessors().create;
|
|
58
62
|
|
|
59
63
|
const guard = UpdateGuard.fromOptions(this.targetModel, options);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import lodash from 'lodash';
|
|
1
|
+
import lodash, { omit } from 'lodash';
|
|
2
2
|
import { SingleAssociationAccessors, Transactionable } from 'sequelize';
|
|
3
3
|
import { Model } from '../model';
|
|
4
4
|
import { Appends, Except, Fields, Filter, TargetKey, UpdateOptions } from '../repository';
|
|
5
5
|
import { updateModelByValues } from '../update-associations';
|
|
6
6
|
import { RelationRepository, transaction } from './relation-repository';
|
|
7
|
+
import { handleAppendsQuery } from '../utils';
|
|
7
8
|
|
|
8
9
|
export interface SingleRelationFindOption extends Transactionable {
|
|
9
10
|
fields?: Fields;
|
|
@@ -45,6 +46,7 @@ export abstract class SingleRelationRepository extends RelationRepository {
|
|
|
45
46
|
|
|
46
47
|
async find(options?: SingleRelationFindOption): Promise<Model<any>> {
|
|
47
48
|
const transaction = await this.getTransaction(options);
|
|
49
|
+
|
|
48
50
|
const findOptions = this.buildQueryOptions({
|
|
49
51
|
...options,
|
|
50
52
|
});
|
|
@@ -52,6 +54,30 @@ export abstract class SingleRelationRepository extends RelationRepository {
|
|
|
52
54
|
const getAccessor = this.accessors().get;
|
|
53
55
|
const sourceModel = await this.getSourceModel(transaction);
|
|
54
56
|
|
|
57
|
+
if (findOptions?.include?.length > 0) {
|
|
58
|
+
const templateModel = await sourceModel[getAccessor]({
|
|
59
|
+
...findOptions,
|
|
60
|
+
includeIgnoreAttributes: false,
|
|
61
|
+
transaction,
|
|
62
|
+
attributes: [this.targetKey()],
|
|
63
|
+
group: `${this.targetModel.name}.${this.targetKey()}`,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const results = await handleAppendsQuery({
|
|
67
|
+
templateModel,
|
|
68
|
+
queryPromises: findOptions.include.map((include) => {
|
|
69
|
+
return sourceModel[getAccessor]({
|
|
70
|
+
...findOptions,
|
|
71
|
+
include: [include],
|
|
72
|
+
}).then((row) => {
|
|
73
|
+
return { rows: [row], include };
|
|
74
|
+
});
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return results[0];
|
|
79
|
+
}
|
|
80
|
+
|
|
55
81
|
return await sourceModel[getAccessor]({
|
|
56
82
|
...findOptions,
|
|
57
83
|
transaction,
|
package/src/repository.ts
CHANGED
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
} from 'sequelize';
|
|
14
14
|
import { Collection } from './collection';
|
|
15
15
|
import { Database } from './database';
|
|
16
|
+
import mustHaveFilter from './decorators/must-have-filter-decorator';
|
|
17
|
+
import { transactionWrapperBuilder } from './decorators/transaction-decorator';
|
|
16
18
|
import { RelationField } from './fields';
|
|
17
19
|
import FilterParser from './filter-parser';
|
|
18
20
|
import { Model } from './model';
|
|
@@ -22,9 +24,9 @@ import { BelongsToRepository } from './relation-repository/belongs-to-repository
|
|
|
22
24
|
import { HasManyRepository } from './relation-repository/hasmany-repository';
|
|
23
25
|
import { HasOneRepository } from './relation-repository/hasone-repository';
|
|
24
26
|
import { RelationRepository } from './relation-repository/relation-repository';
|
|
25
|
-
import { transactionWrapperBuilder } from './transaction-decorator';
|
|
26
27
|
import { updateAssociations, updateModelByValues } from './update-associations';
|
|
27
28
|
import { UpdateGuard } from './update-guard';
|
|
29
|
+
import { handleAppendsQuery } from './utils';
|
|
28
30
|
|
|
29
31
|
const debug = require('debug')('noco-database');
|
|
30
32
|
|
|
@@ -98,7 +100,7 @@ interface FindAndCountOptions extends Omit<SequelizeAndCountOptions, 'where' | '
|
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
export interface CreateOptions extends SequelizeCreateOptions {
|
|
101
|
-
values?: Values;
|
|
103
|
+
values?: Values | Values[];
|
|
102
104
|
whitelist?: WhiteList;
|
|
103
105
|
blacklist?: BlackList;
|
|
104
106
|
updateAssociationValues?: AssociationKeysToBeUpdate;
|
|
@@ -224,18 +226,34 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
224
226
|
group: `${model.name}.${primaryKeyField}`,
|
|
225
227
|
transaction,
|
|
226
228
|
})
|
|
227
|
-
).map((row) =>
|
|
229
|
+
).map((row) => {
|
|
230
|
+
return { row, pk: row.get(primaryKeyField) };
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
if (ids.length == 0) {
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
228
236
|
|
|
229
237
|
const where = {
|
|
230
238
|
[primaryKeyField]: {
|
|
231
|
-
[Op.in]: ids,
|
|
239
|
+
[Op.in]: ids.map((id) => id['pk']),
|
|
232
240
|
},
|
|
233
241
|
};
|
|
234
242
|
|
|
235
|
-
return await
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
243
|
+
return await handleAppendsQuery({
|
|
244
|
+
queryPromises: opts.include.map((include) => {
|
|
245
|
+
return model
|
|
246
|
+
.findAll({
|
|
247
|
+
...omit(opts, ['limit', 'offset']),
|
|
248
|
+
include: include,
|
|
249
|
+
where,
|
|
250
|
+
transaction,
|
|
251
|
+
})
|
|
252
|
+
.then((rows) => {
|
|
253
|
+
return { rows, include };
|
|
254
|
+
});
|
|
255
|
+
}),
|
|
256
|
+
templateModel: ids[0].row,
|
|
239
257
|
});
|
|
240
258
|
}
|
|
241
259
|
|
|
@@ -286,7 +304,14 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
286
304
|
* @param options
|
|
287
305
|
*/
|
|
288
306
|
@transaction()
|
|
289
|
-
async create
|
|
307
|
+
async create(options: CreateOptions) {
|
|
308
|
+
if (Array.isArray(options.values)) {
|
|
309
|
+
return this.createMany({
|
|
310
|
+
...options,
|
|
311
|
+
records: options.values,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
290
315
|
const transaction = await this.getTransaction(options);
|
|
291
316
|
|
|
292
317
|
const guard = UpdateGuard.fromOptions(this.model, { ...options, action: 'create' });
|
|
@@ -335,6 +360,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
335
360
|
const instance = await this.create({ values, transaction });
|
|
336
361
|
instances.push(instance);
|
|
337
362
|
}
|
|
363
|
+
|
|
338
364
|
return instances;
|
|
339
365
|
}
|
|
340
366
|
|
|
@@ -345,7 +371,8 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
345
371
|
* @param options
|
|
346
372
|
*/
|
|
347
373
|
@transaction()
|
|
348
|
-
|
|
374
|
+
@mustHaveFilter()
|
|
375
|
+
async update(options: UpdateOptions & { forceUpdate?: boolean }) {
|
|
349
376
|
const transaction = await this.getTransaction(options);
|
|
350
377
|
const guard = UpdateGuard.fromOptions(this.model, options);
|
|
351
378
|
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
HasOne,
|
|
7
7
|
Hookable,
|
|
8
8
|
ModelCtor,
|
|
9
|
-
Transactionable
|
|
9
|
+
Transactionable
|
|
10
10
|
} from 'sequelize';
|
|
11
11
|
import { Model } from './model';
|
|
12
12
|
import { UpdateGuard } from './update-guard';
|
|
@@ -130,37 +130,44 @@ export async function updateAssociations(instance: Model, values: any, options:
|
|
|
130
130
|
|
|
131
131
|
const keys = Object.keys(values);
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
133
|
+
try {
|
|
134
|
+
for (const key of Object.keys(modelAssociations(instance))) {
|
|
135
|
+
if (keys.includes(key)) {
|
|
136
|
+
await updateAssociation(instance, key, values[key], {
|
|
137
|
+
...options,
|
|
138
|
+
transaction,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
139
141
|
}
|
|
140
|
-
}
|
|
141
142
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
143
|
+
// update through table values
|
|
144
|
+
for (const belongsToMany of belongsToManyAssociations(instance)) {
|
|
145
|
+
// @ts-ignore
|
|
146
|
+
const throughModel = belongsToMany.through.model;
|
|
147
|
+
const throughModelName = throughModel.name;
|
|
148
|
+
|
|
149
|
+
if (values[throughModelName] && options.sourceModel) {
|
|
150
|
+
const where = {
|
|
151
|
+
[belongsToMany.foreignKey]: instance.get(belongsToMany.sourceKey),
|
|
152
|
+
[belongsToMany.otherKey]: options.sourceModel.get(belongsToMany.targetKey),
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
await throughModel.update(values[throughModel.name], {
|
|
156
|
+
where,
|
|
157
|
+
context: options.context,
|
|
158
|
+
transaction,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
159
161
|
}
|
|
160
|
-
}
|
|
161
162
|
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
if (newTransaction) {
|
|
164
|
+
await transaction.commit();
|
|
165
|
+
}
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (newTransaction) {
|
|
168
|
+
await transaction.rollback();
|
|
169
|
+
}
|
|
170
|
+
throw error;
|
|
164
171
|
}
|
|
165
172
|
}
|
|
166
173
|
|
|
@@ -251,7 +258,11 @@ export async function updateSingleAssociation(
|
|
|
251
258
|
return false;
|
|
252
259
|
}
|
|
253
260
|
|
|
254
|
-
|
|
261
|
+
if (Array.isArray(value)) {
|
|
262
|
+
throw new Error(`The value of '${key}' cannot be in array format`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const { context, updateAssociationValues = [], transaction } = options;
|
|
255
266
|
const keys = getKeysByPrefix(updateAssociationValues, key);
|
|
256
267
|
|
|
257
268
|
try {
|
|
@@ -261,9 +272,6 @@ export async function updateSingleAssociation(
|
|
|
261
272
|
const removeAssociation = async () => {
|
|
262
273
|
await model[setAccessor](null, { transaction });
|
|
263
274
|
model.setDataValue(key, null);
|
|
264
|
-
if (!options.transaction) {
|
|
265
|
-
await transaction.commit();
|
|
266
|
-
}
|
|
267
275
|
return true;
|
|
268
276
|
};
|
|
269
277
|
|
|
@@ -273,19 +281,12 @@ export async function updateSingleAssociation(
|
|
|
273
281
|
|
|
274
282
|
if (isStringOrNumber(value)) {
|
|
275
283
|
await model[setAccessor](value, { context, transaction });
|
|
276
|
-
if (!options.transaction) {
|
|
277
|
-
await transaction.commit();
|
|
278
|
-
}
|
|
279
284
|
return true;
|
|
280
285
|
}
|
|
281
286
|
|
|
282
287
|
if (value instanceof Model) {
|
|
283
288
|
await model[setAccessor](value, { context, transaction });
|
|
284
289
|
model.setDataValue(key, value);
|
|
285
|
-
|
|
286
|
-
if (!options.transaction) {
|
|
287
|
-
await transaction.commit();
|
|
288
|
-
}
|
|
289
290
|
return true;
|
|
290
291
|
}
|
|
291
292
|
|
|
@@ -323,9 +324,6 @@ export async function updateSingleAssociation(
|
|
|
323
324
|
updateAssociationValues: keys,
|
|
324
325
|
});
|
|
325
326
|
model.setDataValue(key, instance);
|
|
326
|
-
if (!options.transaction) {
|
|
327
|
-
await transaction.commit();
|
|
328
|
-
}
|
|
329
327
|
return true;
|
|
330
328
|
}
|
|
331
329
|
}
|
|
@@ -342,13 +340,7 @@ export async function updateSingleAssociation(
|
|
|
342
340
|
if (association.targetKey) {
|
|
343
341
|
model.setDataValue(association.foreignKey, instance[dataKey]);
|
|
344
342
|
}
|
|
345
|
-
if (!options.transaction) {
|
|
346
|
-
await transaction.commit();
|
|
347
|
-
}
|
|
348
343
|
} catch (error) {
|
|
349
|
-
if (!options.transaction) {
|
|
350
|
-
await transaction.rollback();
|
|
351
|
-
}
|
|
352
344
|
throw error;
|
|
353
345
|
}
|
|
354
346
|
}
|
|
@@ -376,7 +368,7 @@ export async function updateMultipleAssociation(
|
|
|
376
368
|
return false;
|
|
377
369
|
}
|
|
378
370
|
|
|
379
|
-
const { context, updateAssociationValues = [], transaction
|
|
371
|
+
const { context, updateAssociationValues = [], transaction } = options;
|
|
380
372
|
const keys = getKeysByPrefix(updateAssociationValues, key);
|
|
381
373
|
|
|
382
374
|
try {
|
|
@@ -467,11 +459,7 @@ export async function updateMultipleAssociation(
|
|
|
467
459
|
}
|
|
468
460
|
|
|
469
461
|
model.setDataValue(key, list1.concat(list3));
|
|
470
|
-
if (!options.transaction) {
|
|
471
|
-
await transaction.commit();
|
|
472
|
-
}
|
|
473
462
|
} catch (error) {
|
|
474
|
-
await transaction.rollback();
|
|
475
463
|
throw error;
|
|
476
464
|
}
|
|
477
465
|
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { Model } from './model';
|
|
3
|
+
import { IdentifierError } from './errors/identifier-error';
|
|
4
|
+
|
|
5
|
+
type HandleAppendsQueryOptions = {
|
|
6
|
+
templateModel: any;
|
|
7
|
+
queryPromises: Array<any>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export async function handleAppendsQuery(options: HandleAppendsQueryOptions) {
|
|
11
|
+
const { templateModel, queryPromises } = options;
|
|
12
|
+
|
|
13
|
+
const primaryKey = templateModel.constructor.primaryKeyAttribute;
|
|
14
|
+
|
|
15
|
+
const results = await Promise.all(queryPromises);
|
|
16
|
+
|
|
17
|
+
let rows: Array<Model>;
|
|
18
|
+
|
|
19
|
+
for (const appendedResult of results) {
|
|
20
|
+
if (!rows) {
|
|
21
|
+
rows = appendedResult.rows;
|
|
22
|
+
|
|
23
|
+
if (rows.length == 0) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const modelOptions = templateModel['_options'];
|
|
28
|
+
for (const row of rows) {
|
|
29
|
+
row['_options'] = {
|
|
30
|
+
...row['_options'],
|
|
31
|
+
include: modelOptions['include'],
|
|
32
|
+
includeNames: modelOptions['includeNames'],
|
|
33
|
+
includeMap: modelOptions['includeMap'],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < appendedResult.rows.length; i++) {
|
|
40
|
+
const appendingRow = appendedResult.rows[i];
|
|
41
|
+
const key = appendedResult.include.association;
|
|
42
|
+
const val = appendingRow.get(key);
|
|
43
|
+
|
|
44
|
+
const rowKey = appendingRow.get(primaryKey);
|
|
45
|
+
|
|
46
|
+
const targetIndex = rows.findIndex((row) => row.get(primaryKey) === rowKey);
|
|
47
|
+
|
|
48
|
+
if (targetIndex === -1) {
|
|
49
|
+
throw new Error('target row not found');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
rows[targetIndex].set(key, val, {
|
|
53
|
+
raw: true,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return rows;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function md5(value: string) {
|
|
62
|
+
return crypto.createHash('md5').update(value).digest('hex');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const MAX_IDENTIFIER_LENGTH = 63;
|
|
66
|
+
|
|
67
|
+
export function checkIdentifier(value: string) {
|
|
68
|
+
if (value.length > MAX_IDENTIFIER_LENGTH) {
|
|
69
|
+
throw new IdentifierError(`Identifier ${value} is too long`);
|
|
70
|
+
}
|
|
71
|
+
}
|