@nocobase/database 0.7.4-alpha.7 → 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
@@ -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 { BaseRelationFieldOptions, RelationField } from './relation-field';
12
13
 
13
14
  export interface HasOneFieldOptions extends HasOneOptions {
@@ -92,21 +93,33 @@ export class HasOneField extends RelationField {
92
93
  database.addPendingField(this);
93
94
  return false;
94
95
  }
96
+
95
97
  const association = collection.model.hasOne(Target, {
96
98
  constraints: false,
97
99
  ...omit(this.options, ['name', 'type', 'target']),
98
100
  as: this.name,
99
101
  foreignKey: this.foreignKey,
100
102
  });
103
+
101
104
  // 建立关系之后从 pending 列表中删除
102
105
  database.removePendingField(this);
106
+
103
107
  if (!this.options.foreignKey) {
104
108
  this.options.foreignKey = association.foreignKey;
105
109
  }
110
+
111
+ try {
112
+ checkIdentifier(this.options.foreignKey);
113
+ } catch (error) {
114
+ this.unbind();
115
+ throw error;
116
+ }
117
+
106
118
  if (!this.options.sourceKey) {
107
119
  // @ts-ignore
108
120
  this.options.sourceKey = association.sourceKey;
109
121
  }
122
+
110
123
  let tcoll: Collection;
111
124
  if (this.target === collection.name) {
112
125
  tcoll = collection;
@@ -24,7 +24,8 @@ import { TimeFieldOptions } from './time-field';
24
24
  import { UidFieldOptions } from './uid-field';
25
25
  import { UUIDFieldOptions } from './uuid-field';
26
26
  import { VirtualFieldOptions } from './virtual-field';
27
- import { FormulaFieldOptions } from './formula-field'
27
+ import { FormulaFieldOptions } from './formula-field';
28
+ import { SequenceFieldOptions } from './sequence-field';
28
29
 
29
30
  export * from './array-field';
30
31
  export * from './belongs-to-field';
@@ -48,6 +49,7 @@ export * from './uid-field';
48
49
  export * from './uuid-field';
49
50
  export * from './virtual-field';
50
51
  export * from './formula-field';
52
+ export { SequenceField } from './sequence-field';
51
53
 
52
54
  export type FieldOptions =
53
55
  | BaseFieldOptions
@@ -75,4 +77,5 @@ export type FieldOptions =
75
77
  | BelongsToFieldOptions
76
78
  | HasOneFieldOptions
77
79
  | HasManyFieldOptions
78
- | BelongsToManyFieldOptions;
80
+ | BelongsToManyFieldOptions
81
+ | SequenceFieldOptions;
@@ -11,6 +11,16 @@ export interface IntegerFieldOptions extends BaseColumnFieldOptions {
11
11
  type: 'integer';
12
12
  }
13
13
 
14
+ export class BigIntField extends Field {
15
+ get dataType() {
16
+ return DataTypes.BIGINT;
17
+ }
18
+ }
19
+
20
+ export interface BigIntFieldOptions extends BaseColumnFieldOptions {
21
+ type: 'bigInt';
22
+ }
23
+
14
24
  export class FloatField extends Field {
15
25
  get dataType() {
16
26
  return DataTypes.FLOAT;
@@ -13,38 +13,36 @@ export class RadioField extends Field {
13
13
  return DataTypes.BOOLEAN;
14
14
  }
15
15
 
16
- init() {
16
+ listener = async (model, { transaction }) => {
17
17
  const { name } = this.options;
18
- this.listener = async (model, { transaction }) => {
19
- if (!model.changed(name as any)) {
20
- return;
21
- }
22
- const value = model.get(name) as boolean;
23
- if (value) {
24
- const M = this.collection.model;
25
- await M.update(
26
- { [name]: false },
27
- {
28
- where: {
29
- [name]: true,
30
- },
31
- transaction,
32
- hooks: false,
18
+ if (!model.changed(name as any)) {
19
+ return;
20
+ }
21
+ const value = model.get(name) as boolean;
22
+ if (value) {
23
+ const M = this.collection.model;
24
+ await M.update(
25
+ { [name]: false },
26
+ {
27
+ where: {
28
+ [name]: true,
33
29
  },
34
- );
35
- }
36
- };
37
- }
30
+ transaction,
31
+ hooks: false,
32
+ },
33
+ );
34
+ }
35
+ };
38
36
 
39
37
  bind() {
40
38
  super.bind();
41
- this.on('beforeCreate', this.listener.bind(this));
42
- this.on('beforeUpdate', this.listener.bind(this));
39
+ this.on('beforeCreate', this.listener);
40
+ this.on('beforeUpdate', this.listener);
43
41
  }
44
42
 
45
43
  unbind() {
46
44
  super.unbind();
47
- this.off('beforeCreate', this.listener.bind(this));
48
- this.off('beforeUpdate', this.listener.bind(this));
45
+ this.off('beforeCreate', this.listener);
46
+ this.off('beforeUpdate', this.listener);
49
47
  }
50
48
  }
@@ -0,0 +1,200 @@
1
+ import { DataTypes, Transactionable } from 'sequelize';
2
+ import parser from 'cron-parser';
3
+ import moment from 'moment';
4
+ import { escapeRegExp } from 'lodash';
5
+
6
+ import { Registry } from '@nocobase/utils';
7
+
8
+ import { Model } from '..';
9
+ import { BaseColumnFieldOptions, Field, FieldContext } from './field';
10
+
11
+ interface Pattern {
12
+ validate?(options): string | null;
13
+ generate(this: SequenceField, instance: Model, index: number): string;
14
+ getLength(options): number;
15
+ getMatcher(options): string;
16
+ }
17
+
18
+ export const sequencePatterns = new Registry<Pattern>();
19
+
20
+ sequencePatterns.register('string', {
21
+ validate(options) {
22
+ if (!options?.value) {
23
+ return 'options.value should be configured as a non-empty string';
24
+ }
25
+ return null;
26
+ },
27
+ generate(instance, index) {
28
+ const { options } = this.options.patterns[index];
29
+ return options.value;
30
+ },
31
+ getLength(options) {
32
+ return options.value.length;
33
+ },
34
+ getMatcher(options) {
35
+ return escapeRegExp(options.value);
36
+ }
37
+ });
38
+
39
+ sequencePatterns.register('integer', {
40
+ generate(instance: Model, index) {
41
+ const { options = {} } = this.options.patterns[index];
42
+ const { digits = 1, start = 0, base = 10, cycle } = options;
43
+ const max = Math.pow(base, digits) - 1;
44
+ const { lastRecord = null } = this.options;
45
+
46
+ if (typeof options.current === 'undefined') {
47
+ if (lastRecord) {
48
+ // if match current pattern
49
+ const matcher = this.match(lastRecord.get(this.options.name));
50
+ if (matcher) {
51
+ const lastNumber = Number.parseInt(matcher[index + 1], base);
52
+ options.current = Number.isNaN(lastNumber) ? start : lastNumber + 1;
53
+ } else {
54
+ options.current = start;
55
+ }
56
+ } else {
57
+ options.current = start;
58
+ }
59
+ } else {
60
+ options.current += 1;
61
+ }
62
+
63
+ // cycle as cron string
64
+ if (cycle && lastRecord) {
65
+ const interval = parser.parseExpression(cycle, { currentDate: <Date>lastRecord.get('createdAt') });
66
+ const next = interval.next();
67
+ if ((<Date>instance.get('createdAt')).getTime() >= next.getTime()) {
68
+ options.current = start;
69
+ }
70
+ }
71
+
72
+ if (options.current > max) {
73
+ options.current = start;
74
+ }
75
+
76
+ // update options
77
+ Object.assign(this.options.patterns[index], { options });
78
+
79
+ return options.current.toString(base).padStart(digits, '0');
80
+ },
81
+
82
+ getLength({ digits = 1 } = {}) {
83
+ return digits;
84
+ },
85
+
86
+ getMatcher(options = {}) {
87
+ const { digits = 1, start = 0, base = 10 } = options;
88
+ const startLen = start ? start.toString(base).length : 1;
89
+ const chars = '0123456789abcdefghijklmnopqrstuvwxyz'.slice(0, base);
90
+ return `[${chars}]{${digits}}`;
91
+ }
92
+ });
93
+
94
+ sequencePatterns.register('date', {
95
+ generate(instance, index) {
96
+ const { options } = this.options.patterns[index];
97
+ return moment(instance.get(options?.field ?? 'createdAt')).format(options?.format ?? 'YYYYMMDD');
98
+ },
99
+ getLength(options) {
100
+ return options.format?.length ?? 8;
101
+ },
102
+ getMatcher(options = {}) {
103
+ return `.{${options?.format?.length ?? 8}}`;
104
+ }
105
+ });
106
+
107
+ interface PatternConfig {
108
+ type: string;
109
+ title?: string;
110
+ options?: any;
111
+ }
112
+ export interface SequenceFieldOptions extends BaseColumnFieldOptions {
113
+ type: 'sequence';
114
+ patterns: PatternConfig[]
115
+ }
116
+
117
+ export class SequenceField extends Field {
118
+ get dataType() {
119
+ return DataTypes.STRING;
120
+ }
121
+
122
+ constructor(options: SequenceFieldOptions, context: FieldContext) {
123
+ super(options, context);
124
+ if (!options.patterns || !options.patterns.length) {
125
+ throw new Error('at least one pattern should be defined for sequence type');
126
+ }
127
+ options.patterns.forEach(pattern => {
128
+ const P = sequencePatterns.get(pattern.type);
129
+ if (!P) {
130
+ throw new Error(`pattern type ${pattern.type} is not registered`);
131
+ }
132
+ if (P.validate) {
133
+ const error = P.validate(pattern.options);
134
+ if (error) {
135
+ throw new Error(error);
136
+ }
137
+ }
138
+ });
139
+
140
+ const patterns = options.patterns
141
+ .map(({ type, options }) => sequencePatterns.get(type).getMatcher(options));
142
+ this.matcher = new RegExp(`^${patterns.map(p => `(${p})`).join('')}$`, 'i');
143
+ }
144
+
145
+ setValue = async (instance: Model, options) => {
146
+ const { name, patterns } = this.options;
147
+ // NOTE: only load when value is not set, if null stand for no last record
148
+ if (typeof this.options.lastRecord === 'undefined') {
149
+ const model = <typeof Model>instance.constructor;
150
+ this.options.lastRecord = await model.findOne({
151
+ attributes: [model.primaryKeyAttribute, this.options.name, 'createdAt'],
152
+ order: [
153
+ ['createdAt', 'DESC'],
154
+ // TODO(bug): will cause problem if no auto-increment id
155
+ [model.primaryKeyAttribute, 'DESC']
156
+ ],
157
+ transaction: options.transaction
158
+ });
159
+ }
160
+
161
+ const results = patterns.reduce((result, p, i) => {
162
+ const item = sequencePatterns.get(p.type).generate.call(this, instance, i, options);
163
+ return result.concat(item);
164
+ }, []);
165
+ instance.set(name, results.join(''));
166
+ };
167
+
168
+ setLast = (instance: Model, options) => {
169
+ this.options.lastRecord = instance;
170
+ };
171
+
172
+ match(value) {
173
+ return value.match(this.matcher);
174
+ }
175
+
176
+ parse(value: string, patternIndex: number): string {
177
+ for (let i = 0, index = 0; i < this.options.patterns.length; i += 1) {
178
+ const { type, options } = this.options.patterns[i];
179
+ const { getLength } = sequencePatterns.get(type);
180
+ const length = getLength(options);
181
+ if (i === patternIndex) {
182
+ return value.substring(index, index + length);
183
+ }
184
+ index += length;
185
+ }
186
+ return '';
187
+ }
188
+
189
+ bind() {
190
+ super.bind();
191
+ this.on('beforeCreate', this.setValue);
192
+ this.on('afterCreate', this.setLast);
193
+ }
194
+
195
+ unbind() {
196
+ super.unbind();
197
+ this.off('beforeCreate', this.setValue);
198
+ this.off('afterCreate', this.setLast);
199
+ }
200
+ }
@@ -10,7 +10,7 @@ export class SortField extends Field {
10
10
  return DataTypes.INTEGER;
11
11
  }
12
12
 
13
- async setSortValue(instance, options) {
13
+ setSortValue = async (instance, options) => {
14
14
  const { name, scopeKey } = this.options;
15
15
  const { model } = this.context.collection;
16
16
 
@@ -34,14 +34,14 @@ export class SortField extends Field {
34
34
  });
35
35
  }
36
36
 
37
- async onScopeChange(instance, options) {
37
+ onScopeChange = async (instance, options) => {
38
38
  const { scopeKey } = this.options;
39
39
  if (scopeKey && !instance.isNewRecord && instance._previousDataValues[scopeKey] != instance[scopeKey]) {
40
40
  await this.setSortValue(instance, options);
41
41
  }
42
42
  }
43
43
 
44
- async initRecordsSortValue({ transaction }) {
44
+ initRecordsSortValue = async ({ transaction }) => {
45
45
  const totalCount = await this.collection.repository.count({
46
46
  transaction,
47
47
  });
@@ -77,16 +77,16 @@ export class SortField extends Field {
77
77
 
78
78
  bind() {
79
79
  super.bind();
80
- this.on('afterSync', this.initRecordsSortValue.bind(this));
81
- this.on('beforeUpdate', this.onScopeChange.bind(this));
82
- this.on('beforeCreate', this.setSortValue.bind(this));
80
+ this.on('afterSync', this.initRecordsSortValue);
81
+ this.on('beforeUpdate', this.onScopeChange);
82
+ this.on('beforeCreate', this.setSortValue);
83
83
  }
84
84
 
85
85
  unbind() {
86
86
  super.unbind();
87
- this.off('beforeUpdate', this.onScopeChange.bind(this));
88
- this.off('beforeCreate', this.setSortValue.bind(this));
89
- this.off('afterSync', this.initRecordsSortValue.bind(this));
87
+ this.off('beforeUpdate', this.onScopeChange);
88
+ this.off('beforeCreate', this.setSortValue);
89
+ this.off('afterSync', this.initRecordsSortValue);
90
90
  }
91
91
  }
92
92
 
@@ -124,12 +124,13 @@ export default class FilterParser {
124
124
  skipPrefix = origins.join('.');
125
125
 
126
126
  const queryValue = lodash.get(unflatten(originalFiler), skipPrefix);
127
-
127
+ const [fieldName, fullName] = this.getFieldNameFromQueryPath(skipPrefix);
128
128
  value = opKey(queryValue, {
129
129
  app: this.context.app,
130
130
  db: this.database,
131
131
  path: skipPrefix,
132
- fieldName: this.getFieldNameFromQueryPath(skipPrefix),
132
+ fullName,
133
+ fieldName,
133
134
  model: this.model,
134
135
  });
135
136
  break;
@@ -230,14 +231,14 @@ export default class FilterParser {
230
231
  private getFieldNameFromQueryPath(queryPath: string) {
231
232
  const paths = queryPath.split('.');
232
233
  let fieldName;
234
+ let fullPaths = [];
233
235
  for (const path of paths) {
234
236
  if (path.startsWith('$') || !lodash.isNaN(parseInt(path))) {
235
237
  continue;
236
238
  }
237
-
239
+ fullPaths.push(path);
238
240
  fieldName = path;
239
241
  }
240
-
241
- return fieldName;
242
+ return [fieldName, fullPaths.join('.')];
242
243
  }
243
244
  }
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { ModelCtor, Op, SyncOptions } from 'sequelize';
1
+ export { DataTypes, ModelCtor, Op, SyncOptions } from 'sequelize';
2
2
  export * from './collection';
3
3
  export * from './database';
4
4
  export { Database as default } from './database';
@@ -1,7 +1,9 @@
1
1
  import { merge } from '@nocobase/utils';
2
2
  import _ from 'lodash';
3
+ import { Utils } from 'sequelize';
3
4
  import Database from './database';
4
5
  import { Model } from './model';
6
+ const Dottie = require('dottie');
5
7
 
6
8
  export class MagicAttributeModel extends Model {
7
9
  get magicAttribute() {
@@ -14,25 +16,205 @@ export class MagicAttributeModel extends Model {
14
16
  if (typeof key === 'string') {
15
17
  const [column] = key.split('.');
16
18
  if ((this.constructor as any).hasAlias(column)) {
17
- return super.set(key, value, options);
19
+ return this.setV1(key, value, options);
18
20
  }
19
21
  if ((this.constructor as any).rawAttributes[column]) {
20
- return super.set(key, value, options);
22
+ return this.setV1(key, value, options);
21
23
  }
22
24
  if (_.isPlainObject(value)) {
23
25
  const opts = super.get(this.magicAttribute) || {};
24
- return super.set(`${this.magicAttribute}.${key}`, merge(opts?.[key], value), options);
26
+ return this.setV1(`${this.magicAttribute}.${key}`, merge(opts?.[key], value), options);
25
27
  }
26
- return super.set(`${this.magicAttribute}.${key}`, value, options);
28
+ return this.setV1(`${this.magicAttribute}.${key}`, value, options);
27
29
  } else {
28
30
  if (!key) {
29
31
  return;
30
32
  }
31
33
  Object.keys(key).forEach((k) => {
32
- this.set(k, key[k], options);
34
+ this.setV1(k, key[k], options);
33
35
  });
34
36
  }
35
- return super.set(key, value, options);
37
+ return this.setV1(key, value, options);
38
+ }
39
+
40
+ setV1(key?: any, value?: any, options?: any) {
41
+ let values;
42
+ let originalValue;
43
+
44
+ if (typeof key === 'object' && key !== null) {
45
+ values = key;
46
+ options = value || {};
47
+
48
+ if (options.reset) {
49
+ // @ts-ignore
50
+ this.dataValues = {};
51
+ for (const key in values) {
52
+ this.changed<any>(key, false);
53
+ }
54
+ }
55
+
56
+ // If raw, and we're not dealing with includes or special attributes, just set it straight on the dataValues object
57
+ // @ts-ignore
58
+ if (
59
+ options.raw &&
60
+ // @ts-ignore
61
+ !(this._options && this._options.include) &&
62
+ !(options && options.attributes) &&
63
+ // @ts-ignore
64
+ !this.constructor._hasDateAttributes &&
65
+ // @ts-ignore
66
+ !this.constructor._hasBooleanAttributes
67
+ ) {
68
+ // @ts-ignore
69
+ if (Object.keys(this.dataValues).length) {
70
+ // @ts-ignore
71
+ Object.assign(this.dataValues, values);
72
+ } else {
73
+ // @ts-ignore
74
+ this.dataValues = values;
75
+ }
76
+ // If raw, .changed() shouldn't be true
77
+ // @ts-ignore
78
+ this._previousDataValues = { ...this.dataValues };
79
+ } else {
80
+ // Loop and call set
81
+ if (options.attributes) {
82
+ const setKeys = (data) => {
83
+ for (const k of data) {
84
+ if (values[k] === undefined) {
85
+ continue;
86
+ }
87
+ this.set(k, values[k], options);
88
+ }
89
+ };
90
+ setKeys(options.attributes);
91
+ // @ts-ignore
92
+ if (this.constructor._hasVirtualAttributes) {
93
+ // @ts-ignore
94
+ setKeys(this.constructor._virtualAttributes);
95
+ }
96
+ // @ts-ignore
97
+ if (this._options.includeNames) {
98
+ // @ts-ignore
99
+ setKeys(this._options.includeNames);
100
+ }
101
+ } else {
102
+ for (const key in values) {
103
+ this.set(key, values[key], options);
104
+ }
105
+ }
106
+
107
+ if (options.raw) {
108
+ // If raw, .changed() shouldn't be true
109
+ // @ts-ignore
110
+ this._previousDataValues = { ...this.dataValues };
111
+ }
112
+ }
113
+ return this;
114
+ }
115
+ if (!options) options = {};
116
+ if (!options.raw) {
117
+ // @ts-ignore
118
+ originalValue = this.dataValues[key];
119
+ }
120
+
121
+ // If not raw, and there's a custom setter
122
+ // @ts-ignore
123
+ if (!options.raw && this._customSetters[key]) {
124
+ // @ts-ignore
125
+ this._customSetters[key].call(this, value, key);
126
+ // custom setter should have changed value, get that changed value
127
+ // TODO: v5 make setters return new value instead of changing internal store
128
+ // @ts-ignore
129
+ const newValue = this.dataValues[key];
130
+ if (!_.isEqual(newValue, originalValue)) {
131
+ // @ts-ignore
132
+ this._previousDataValues[key] = originalValue;
133
+ this.changed(key, true);
134
+ }
135
+ } else {
136
+ // Check if we have included models, and if this key matches the include model names/aliases
137
+ // @ts-ignore
138
+ if (this._options && this._options.include && this._options.includeNames.includes(key)) {
139
+ // Pass it on to the include handler
140
+ // @ts-ignore
141
+ this._setInclude(key, value, options);
142
+ return this;
143
+ }
144
+ // Bunch of stuff we won't do when it's raw
145
+ if (!options.raw) {
146
+ // If attribute is not in model definition, return
147
+ // @ts-ignore
148
+ if (!this._isAttribute(key)) {
149
+ // @ts-ignore
150
+ if (key.includes('.') && this.constructor._jsonAttributes.has(key.split('.')[0])) {
151
+ // @ts-ignore
152
+ const previousNestedValue = Dottie.get(this.dataValues, key);
153
+ if (!_.isEqual(previousNestedValue, value)) {
154
+ // @ts-ignore
155
+ this._previousDataValues = _.cloneDeep(this._previousDataValues);
156
+ // @ts-ignore
157
+ Dottie.set(this.dataValues, key, value);
158
+ this.changed(key.split('.')[0], true);
159
+ }
160
+ }
161
+ return this;
162
+ }
163
+
164
+ // If attempting to set primary key and primary key is already defined, return
165
+ // @ts-ignore
166
+ if (this.constructor._hasPrimaryKeys && originalValue && this.constructor._isPrimaryKey(key)) {
167
+ return this;
168
+ }
169
+
170
+ // If attempting to set read only attributes, return
171
+ // @ts-ignore
172
+ if (
173
+ !this.isNewRecord &&
174
+ // @ts-ignore
175
+ this.constructor._hasReadOnlyAttributes &&
176
+ // @ts-ignore
177
+ this.constructor._readOnlyAttributes.has(key)
178
+ ) {
179
+ return this;
180
+ }
181
+ }
182
+
183
+ // If there's a data type sanitizer
184
+ if (
185
+ !(value instanceof Utils.SequelizeMethod) &&
186
+ // @ts-ignore
187
+ Object.prototype.hasOwnProperty.call(this.constructor._dataTypeSanitizers, key)
188
+ ) {
189
+ // @ts-ignore
190
+ value = this.constructor._dataTypeSanitizers[key].call(this, value, options);
191
+ }
192
+
193
+ // Set when the value has changed and not raw
194
+ if (
195
+ !options.raw &&
196
+ // True when sequelize method
197
+ (value instanceof Utils.SequelizeMethod ||
198
+ // Check for data type type comparators
199
+ // @ts-ignore
200
+ (!(value instanceof Utils.SequelizeMethod) &&
201
+ // @ts-ignore
202
+ this.constructor._dataTypeChanges[key] &&
203
+ // @ts-ignore
204
+ this.constructor._dataTypeChanges[key].call(this, value, originalValue, options)) || // Check default
205
+ // @ts-ignore
206
+ (!this.constructor._dataTypeChanges[key] && !_.isEqual(value, originalValue)))
207
+ ) {
208
+ // @ts-ignore
209
+ this._previousDataValues[key] = originalValue;
210
+ this.changed(key, true);
211
+ }
212
+
213
+ // set data value
214
+ // @ts-ignore
215
+ this.dataValues[key] = value;
216
+ }
217
+ return this;
36
218
  }
37
219
 
38
220
  get(key?: any, value?: any): any {
@@ -20,7 +20,7 @@ export function getConfigByEnv() {
20
20
  database: process.env.DB_DATABASE,
21
21
  host: process.env.DB_HOST,
22
22
  port: process.env.DB_PORT,
23
- dialect: process.env.DB_DIALECT,
23
+ dialect: process.env.DB_DIALECT || 'sqlite',
24
24
  logging: process.env.DB_LOGGING === 'on' ? console.log : false,
25
25
  storage:
26
26
  process.env.DB_STORAGE && process.env.DB_STORAGE !== ':memory:'