@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.
Files changed (97) hide show
  1. package/lib/collection.d.ts +7 -2
  2. package/lib/collection.js +20 -8
  3. package/lib/database.d.ts +2 -2
  4. package/lib/database.js +6 -4
  5. package/lib/decorators/must-have-filter-decorator.d.ts +2 -0
  6. package/lib/decorators/must-have-filter-decorator.js +25 -0
  7. package/lib/{transaction-decorator.d.ts → decorators/transaction-decorator.d.ts} +0 -0
  8. package/lib/{transaction-decorator.js → decorators/transaction-decorator.js} +1 -4
  9. package/lib/errors/identifier-error.d.ts +3 -0
  10. package/lib/errors/identifier-error.js +16 -0
  11. package/lib/fields/belongs-to-field.js +9 -0
  12. package/lib/fields/belongs-to-many-field.js +10 -0
  13. package/lib/fields/date-field.d.ts +1 -1
  14. package/lib/fields/date-field.js +1 -1
  15. package/lib/fields/field.d.ts +1 -1
  16. package/lib/fields/field.js +2 -3
  17. package/lib/fields/formula-field.d.ts +5 -5
  18. package/lib/fields/formula-field.js +123 -110
  19. package/lib/fields/has-many-field.js +9 -0
  20. package/lib/fields/has-one-field.js +9 -0
  21. package/lib/fields/index.d.ts +3 -1
  22. package/lib/fields/index.js +34 -1
  23. package/lib/fields/number-field.d.ts +6 -0
  24. package/lib/fields/number-field.js +10 -1
  25. package/lib/fields/radio-field.d.ts +3 -1
  26. package/lib/fields/radio-field.js +14 -11
  27. package/lib/fields/sequence-field.d.ts +31 -0
  28. package/lib/fields/sequence-field.js +299 -0
  29. package/lib/fields/sort-field.d.ts +4 -4
  30. package/lib/fields/sort-field.js +93 -80
  31. package/lib/filter-parser.js +10 -2
  32. package/lib/index.d.ts +1 -1
  33. package/lib/index.js +7 -0
  34. package/lib/magic-attribute-model.d.ts +1 -0
  35. package/lib/magic-attribute-model.js +207 -6
  36. package/lib/mock-database.js +1 -1
  37. package/lib/operators/array.js +17 -9
  38. package/lib/operators/ne.d.ts +4 -0
  39. package/lib/operators/ne.js +3 -1
  40. package/lib/relation-repository/belongs-to-many-repository.js +6 -0
  41. package/lib/relation-repository/multiple-relation-repository.js +32 -9
  42. package/lib/relation-repository/relation-repository.js +7 -1
  43. package/lib/relation-repository/single-relation-repository.js +28 -0
  44. package/lib/repository.d.ts +5 -3
  45. package/lib/repository.js +40 -9
  46. package/lib/update-associations.js +48 -71
  47. package/lib/utils.d.ts +9 -0
  48. package/lib/utils.js +126 -0
  49. package/package.json +5 -3
  50. package/src/__tests__/collection.test.ts +47 -0
  51. package/src/__tests__/database.test.ts +2 -0
  52. package/src/__tests__/field-options/inddex.test.ts +43 -0
  53. package/src/__tests__/fields/array.test.ts +66 -0
  54. package/src/__tests__/fields/belongs-to-field.test.ts +35 -0
  55. package/src/__tests__/fields/belongs-to-many-field.test.ts +45 -0
  56. package/src/__tests__/fields/has-many-field.test.ts +22 -0
  57. package/src/__tests__/fields/has-one-field.test.ts +21 -0
  58. package/src/__tests__/fields/sequence-field.test.ts +455 -0
  59. package/src/__tests__/magic-attribute-model.test.ts +24 -0
  60. package/src/__tests__/operator/ne.test.ts +12 -0
  61. package/src/__tests__/relation-repository/appends.test.ts +64 -0
  62. package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +23 -0
  63. package/src/__tests__/relation-repository/has-many-repository.test.ts +20 -0
  64. package/src/__tests__/relation-repository/hasone-repository.test.ts +68 -1
  65. package/src/__tests__/repository/find.test.ts +35 -0
  66. package/src/__tests__/repository/update.test.ts +35 -0
  67. package/src/__tests__/repository.test.ts +15 -0
  68. package/src/collection.ts +28 -13
  69. package/src/database.ts +11 -7
  70. package/src/decorators/must-have-filter-decorator.ts +17 -0
  71. package/src/{transaction-decorator.ts → decorators/transaction-decorator.ts} +1 -2
  72. package/src/errors/identifier-error.ts +6 -0
  73. package/src/fields/belongs-to-field.ts +9 -0
  74. package/src/fields/belongs-to-many-field.ts +15 -0
  75. package/src/fields/date-field.ts +1 -1
  76. package/src/fields/field.ts +3 -3
  77. package/src/fields/formula-field.ts +13 -13
  78. package/src/fields/has-many-field.ts +10 -0
  79. package/src/fields/has-one-field.ts +13 -0
  80. package/src/fields/index.ts +5 -2
  81. package/src/fields/number-field.ts +10 -0
  82. package/src/fields/radio-field.ts +22 -24
  83. package/src/fields/sequence-field.ts +200 -0
  84. package/src/fields/sort-field.ts +9 -9
  85. package/src/filter-parser.ts +6 -5
  86. package/src/index.ts +1 -1
  87. package/src/magic-attribute-model.ts +188 -6
  88. package/src/mock-database.ts +1 -1
  89. package/src/operators/array.ts +17 -10
  90. package/src/operators/ne.ts +10 -6
  91. package/src/relation-repository/belongs-to-many-repository.ts +4 -0
  92. package/src/relation-repository/multiple-relation-repository.ts +26 -11
  93. package/src/relation-repository/relation-repository.ts +5 -1
  94. package/src/relation-repository/single-relation-repository.ts +27 -1
  95. package/src/repository.ts +37 -10
  96. package/src/update-associations.ts +41 -53
  97. package/src/utils.ts +71 -0
@@ -1,5 +1,5 @@
1
1
  import { Op, Sequelize } from 'sequelize';
2
- import { isPg, isMySQL } from './utils';
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(${fieldName}) where json_each.value in ${sqlArray})`;
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
- return Sequelize.literal(`JSON_CONTAINS(${fieldName}, ${value}) AND JSON_CONTAINS(${value}, ${fieldName})`);
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
- return Sequelize.literal(`not (${fieldName} <@ ${value}::JSONB and ${fieldName} @> ${value}::JSONB)`);
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
- return Sequelize.literal(`not (JSON_CONTAINS(${fieldName}, ${value}) AND JSON_CONTAINS(${value}, ${fieldName}))`);
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
- `${fieldName} ?| ${escape(
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(${fieldName}, ${value})`);
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 (${fieldName} ?| ${escape(
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
- where = Sequelize.literal(`NOT JSON_OVERLAPS(${fieldName}, ${value})`);
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
 
@@ -2,11 +2,15 @@ import { Op } from 'sequelize';
2
2
 
3
3
  export default {
4
4
  $ne(val, ctx) {
5
- return {
6
- [Op.or]: {
7
- [Op.ne]: val,
8
- [Op.is]: null,
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) => row.get(this.targetKey()));
56
-
57
- return await sourceModel[getAccessor]({
58
- ...omit(findOptions, ['limit', 'offset']),
59
- where: {
60
- [this.targetKey()]: {
61
- [Op.in]: ids,
62
- },
63
- },
64
- transaction,
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) => row.get(primaryKeyField));
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 model.findAll({
236
- ...omit(opts, ['limit', 'offset']),
237
- where,
238
- transaction,
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<M extends Model>(options: CreateOptions): Promise<M> {
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
- async update(options: UpdateOptions) {
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
- for (const key of Object.keys(modelAssociations(instance))) {
134
- if (keys.includes(key)) {
135
- await updateAssociation(instance, key, values[key], {
136
- ...options,
137
- transaction,
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
- // update through table values
143
- for (const belongsToMany of belongsToManyAssociations(instance)) {
144
- // @ts-ignore
145
- const throughModel = belongsToMany.through.model;
146
- const throughModelName = throughModel.name;
147
-
148
- if (values[throughModelName] && options.sourceModel) {
149
- const where = {
150
- [belongsToMany.foreignKey]: instance.get(belongsToMany.sourceKey),
151
- [belongsToMany.otherKey]: options.sourceModel.get(belongsToMany.targetKey),
152
- };
153
-
154
- await throughModel.update(values[throughModel.name], {
155
- where,
156
- context: options.context,
157
- transaction,
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
- if (newTransaction) {
163
- await transaction.commit();
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
- const { context, updateAssociationValues = [], transaction = await model.sequelize.transaction() } = options;
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 = await model.sequelize.transaction() } = options;
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
+ }