@nocobase/database 0.8.0-alpha.9 → 0.8.1-alpha.3

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 (122) hide show
  1. package/lib/collection.d.ts +7 -3
  2. package/lib/collection.js +126 -22
  3. package/lib/database.d.ts +13 -7
  4. package/lib/database.js +39 -8
  5. package/lib/decorators/must-have-filter-decorator.js +4 -0
  6. package/lib/decorators/transaction-decorator.js +1 -0
  7. package/lib/features/ReferencesMap.js +14 -9
  8. package/lib/field-repository/array-field-repository.d.ts +28 -0
  9. package/lib/field-repository/array-field-repository.js +208 -0
  10. package/lib/fields/array-field.d.ts +1 -1
  11. package/lib/fields/array-field.js +15 -11
  12. package/lib/fields/belongs-to-field.d.ts +9 -1
  13. package/lib/fields/belongs-to-field.js +21 -7
  14. package/lib/fields/belongs-to-many-field.d.ts +4 -0
  15. package/lib/fields/belongs-to-many-field.js +24 -0
  16. package/lib/fields/field.d.ts +3 -2
  17. package/lib/fields/field.js +19 -13
  18. package/lib/fields/has-many-field.d.ts +2 -1
  19. package/lib/fields/has-many-field.js +18 -10
  20. package/lib/fields/has-one-field.d.ts +1 -0
  21. package/lib/fields/has-one-field.js +22 -10
  22. package/lib/fields/index.d.ts +3 -3
  23. package/lib/fields/index.js +14 -34
  24. package/lib/fields/relation-field.d.ts +2 -1
  25. package/lib/fields/relation-field.js +16 -0
  26. package/lib/fields/set-field.d.ts +10 -0
  27. package/lib/fields/set-field.js +35 -0
  28. package/lib/fields/sort-field.d.ts +1 -1
  29. package/lib/fields/sort-field.js +1 -1
  30. package/lib/filter-match.d.ts +1 -0
  31. package/lib/filter-match.js +84 -0
  32. package/lib/filter-parser.d.ts +2 -2
  33. package/lib/index.d.ts +5 -1
  34. package/lib/index.js +56 -0
  35. package/lib/inherited-collection.d.ts +13 -0
  36. package/lib/inherited-collection.js +210 -0
  37. package/lib/inherited-map.d.ts +21 -0
  38. package/lib/inherited-map.js +203 -0
  39. package/lib/model-hook.d.ts +1 -1
  40. package/lib/model.d.ts +1 -0
  41. package/lib/model.js +47 -0
  42. package/lib/operators/array.d.ts +1 -25
  43. package/lib/operators/association.d.ts +1 -9
  44. package/lib/operators/boolean.d.ts +1 -12
  45. package/lib/operators/date.d.ts +1 -33
  46. package/lib/operators/empty.d.ts +2 -25
  47. package/lib/operators/ne.d.ts +1 -13
  48. package/lib/operators/notIn.d.ts +1 -9
  49. package/lib/options-parser.d.ts +3 -2
  50. package/lib/options-parser.js +16 -1
  51. package/lib/relation-repository/relation-repository.d.ts +2 -2
  52. package/lib/relation-repository/single-relation-repository.js +2 -2
  53. package/lib/repository.d.ts +18 -10
  54. package/lib/repository.js +172 -38
  55. package/lib/sync-runner.d.ts +4 -0
  56. package/lib/sync-runner.js +181 -0
  57. package/lib/types.d.ts +2 -2
  58. package/lib/update-associations.d.ts +1 -0
  59. package/lib/update-associations.js +21 -2
  60. package/lib/update-guard.d.ts +3 -3
  61. package/lib/utils.js +5 -0
  62. package/package.json +4 -4
  63. package/src/__tests__/bigint.test.ts +48 -0
  64. package/src/__tests__/collection.test.ts +48 -13
  65. package/src/__tests__/database.test.ts +10 -0
  66. package/src/__tests__/field-repository/array-field-repository.test.ts +94 -0
  67. package/src/__tests__/fields/set.test.ts +37 -0
  68. package/src/__tests__/filter-match.test.ts +52 -0
  69. package/src/__tests__/inhertits/collection-inherits-sync.test.ts +38 -0
  70. package/src/__tests__/inhertits/collection-inherits.test.ts +994 -0
  71. package/src/__tests__/inhertits/helper.ts +3 -0
  72. package/src/__tests__/inhertits/inherited-map.test.ts +27 -0
  73. package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +2 -0
  74. package/src/__tests__/relation-repository/has-many-repository.test.ts +1 -1
  75. package/src/__tests__/repository/destroy.test.ts +122 -1
  76. package/src/__tests__/repository/update-many.test.ts +57 -0
  77. package/src/__tests__/update-association-values.test.ts +232 -0
  78. package/src/__tests__/update-associations.test.ts +6 -1
  79. package/src/collection.ts +90 -8
  80. package/src/database.ts +53 -14
  81. package/src/decorators/must-have-filter-decorator.ts +4 -0
  82. package/src/decorators/transaction-decorator.ts +1 -0
  83. package/src/features/ReferencesMap.ts +20 -9
  84. package/src/features/referential-integrity-check.ts +1 -0
  85. package/src/field-repository/array-field-repository.ts +155 -0
  86. package/src/fields/array-field.ts +4 -4
  87. package/src/fields/belongs-to-field.ts +26 -10
  88. package/src/fields/belongs-to-many-field.ts +34 -0
  89. package/src/fields/field.ts +26 -13
  90. package/src/fields/has-many-field.ts +17 -9
  91. package/src/fields/has-one-field.ts +23 -9
  92. package/src/fields/index.ts +5 -5
  93. package/src/fields/relation-field.ts +16 -0
  94. package/src/fields/set-field.ts +25 -0
  95. package/src/fields/sort-field.ts +5 -4
  96. package/src/filter-match.ts +49 -0
  97. package/src/filter-parser.ts +2 -2
  98. package/src/index.ts +5 -2
  99. package/src/inherited-collection.ts +112 -0
  100. package/src/inherited-map.ts +97 -0
  101. package/src/model-hook.ts +2 -3
  102. package/src/model.ts +43 -3
  103. package/src/operators/array.ts +1 -1
  104. package/src/operators/association.ts +1 -1
  105. package/src/operators/boolean.ts +1 -1
  106. package/src/operators/date.ts +1 -1
  107. package/src/operators/empty.ts +1 -1
  108. package/src/operators/ne.ts +1 -1
  109. package/src/operators/notIn.ts +2 -1
  110. package/src/options-parser.ts +20 -4
  111. package/src/relation-repository/relation-repository.ts +2 -2
  112. package/src/relation-repository/single-relation-repository.ts +2 -2
  113. package/src/repository.ts +144 -30
  114. package/src/sync-runner.ts +162 -0
  115. package/src/types.ts +2 -2
  116. package/src/update-associations.ts +23 -7
  117. package/src/update-guard.ts +3 -3
  118. package/src/utils.ts +5 -1
  119. package/lib/fields/sequence-field.d.ts +0 -32
  120. package/lib/fields/sequence-field.js +0 -300
  121. package/src/__tests__/fields/sequence-field.test.ts +0 -480
  122. package/src/fields/sequence-field.ts +0 -202
@@ -1,480 +0,0 @@
1
- import moment from 'moment';
2
-
3
- import { Database } from '../../database';
4
- import { mockDatabase } from '..';
5
-
6
- describe('string field', () => {
7
- let db: Database;
8
-
9
- beforeEach(async () => {
10
- db = mockDatabase();
11
- });
12
-
13
- afterEach(async () => {
14
- await db.close();
15
- });
16
-
17
- describe('define', () => {
18
- it('without any pattern will throw error', async () => {
19
- expect(() => {
20
- db.collection({
21
- name: 'tests',
22
- fields: [{ type: 'sequence', name: 'name' }],
23
- });
24
- }).toThrow();
25
- });
26
-
27
- it('with empty pattern will throw error', async () => {
28
- expect(() => {
29
- db.collection({
30
- name: 'tests',
31
- fields: [{ type: 'sequence', name: 'name', patterns: [] }],
32
- });
33
- }).toThrow();
34
- });
35
- });
36
-
37
- describe('string pattern', () => {
38
- it('no options', async () => {
39
- expect(() => {
40
- db.collection({
41
- name: 'tests',
42
- fields: [{ type: 'sequence', name: 'name', patterns: [{ type: 'string' }] }],
43
- });
44
- }).toThrow();
45
- });
46
-
47
- it('constant', async () => {
48
- db.collection({
49
- name: 'tests',
50
- fields: [
51
- {
52
- type: 'sequence',
53
- name: 'name',
54
- patterns: [
55
- { type: 'string', options: { value: 'abc' } }
56
- ]
57
- }
58
- ],
59
- });
60
- await db.sync();
61
-
62
- const TestModel = db.getModel('tests');
63
- const item1 = await TestModel.create();
64
- expect(item1.get('name')).toBe('abc');
65
-
66
- const item2 = await TestModel.create();
67
- expect(item2.get('name')).toBe('abc');
68
- });
69
- });
70
-
71
- describe('integer pattern', () => {
72
- it('default start from 0, digits as 1, no cycle', async () => {
73
- db.collection({
74
- name: 'tests',
75
- fields: [
76
- {
77
- type: 'sequence',
78
- name: 'name',
79
- patterns: [
80
- {
81
- type: 'integer'
82
- }
83
- ]
84
- }
85
- ],
86
- });
87
- await db.sync();
88
-
89
- const TestModel = db.getModel('tests');
90
- const item1 = await TestModel.create();
91
- expect(item1.get('name')).toBe('0');
92
-
93
- const item2 = await TestModel.create();
94
- expect(item2.get('name')).toBe('1');
95
- });
96
-
97
- it('start from 9', async () => {
98
- db.collection({
99
- name: 'tests',
100
- fields: [
101
- {
102
- type: 'sequence',
103
- name: 'name',
104
- patterns: [
105
- {
106
- type: 'integer',
107
- options: {
108
- start: 9
109
- }
110
- }
111
- ]
112
- }
113
- ],
114
- });
115
- await db.sync();
116
-
117
- const TestModel = db.getModel('tests');
118
- const item1 = await TestModel.create();
119
- expect(item1.get('name')).toBe('9');
120
-
121
- const item2 = await TestModel.create();
122
- expect(item2.get('name')).toBe('9');
123
- });
124
-
125
- it('start from 0, current set to 9', async () => {
126
- const collection = db.collection({
127
- name: 'tests',
128
- fields: [
129
- {
130
- type: 'sequence',
131
- name: 'name',
132
- patterns: [
133
- {
134
- type: 'integer'
135
- }
136
- ]
137
- }
138
- ],
139
- });
140
- await db.sync();
141
- const field = collection.getField('name');
142
- // set current option in memory
143
- field.options.patterns[0].options = { current: 9 };
144
-
145
- const TestModel = db.getModel('tests');
146
- const item1 = await TestModel.create();
147
- expect(item1.get('name')).toBe('0');
148
- });
149
-
150
- it('digits more than 1, start from 9', async () => {
151
- db.collection({
152
- name: 'tests',
153
- fields: [
154
- {
155
- type: 'sequence',
156
- name: 'name',
157
- patterns: [
158
- {
159
- type: 'integer',
160
- options: {
161
- digits: 2,
162
- start: 9
163
- }
164
- }
165
- ]
166
- }
167
- ],
168
- });
169
- await db.sync();
170
-
171
- const TestModel = db.getModel('tests');
172
- const item1 = await TestModel.create();
173
- expect(item1.get('name')).toBe('09');
174
-
175
- const item2 = await TestModel.create();
176
- expect(item2.get('name')).toBe('10');
177
- });
178
-
179
- it('cycle by day', async () => {
180
- db.collection({
181
- name: 'tests',
182
- fields: [
183
- {
184
- type: 'sequence',
185
- name: 'name',
186
- patterns: [
187
- {
188
- type: 'integer',
189
- options: {
190
- cycle: '0 0 * * * *'
191
- }
192
- }
193
- ]
194
- }
195
- ],
196
- });
197
- await db.sync();
198
-
199
- const now = new Date();
200
- const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
201
-
202
- const TestModel = db.getModel('tests');
203
- const item1 = await TestModel.create({
204
- createdAt: yesterday
205
- });
206
- expect(item1.get('name')).toBe('0');
207
-
208
- const item2 = await TestModel.create({
209
- createdAt: yesterday
210
- });
211
- expect(item2.get('name')).toBe('1');
212
-
213
- const item3 = await TestModel.create();
214
- expect(item3.get('name')).toBe('0');
215
-
216
- const item4 = await TestModel.create();
217
- expect(item4.get('name')).toBe('1');
218
- });
219
-
220
- it('last record has no value of this field', async () => {
221
- const testCollection = db.collection({
222
- name: 'tests',
223
- fields: [],
224
- });
225
- await db.sync();
226
-
227
- const TestModel = db.getModel('tests');
228
- const item1 = await TestModel.create();
229
- expect(item1.get('name')).toBeUndefined();
230
-
231
- testCollection.addField('name', {
232
- type: 'sequence',
233
- patterns: [
234
- {
235
- type: 'integer'
236
- }
237
- ]
238
- });
239
- await db.sync();
240
-
241
- const item2 = await TestModel.create();
242
- expect(item2.get('name')).toBe('0');
243
- });
244
- });
245
-
246
- describe('date pattern', () => {
247
- it('default to current createdAt as YYYYMMDD', async () => {
248
- db.collection({
249
- name: 'tests',
250
- fields: [
251
- {
252
- type: 'sequence',
253
- name: 'name',
254
- patterns: [
255
- { type: 'date' }
256
- ]
257
- }
258
- ],
259
- });
260
- await db.sync();
261
-
262
- const now = new Date();
263
- const YYYYMMDD = moment(now).format('YYYYMMDD');
264
-
265
- const TestModel = db.getModel('tests');
266
- const item1 = await TestModel.create();
267
- expect(item1.get('name')).toBe(YYYYMMDD);
268
- });
269
-
270
- it('field option', async () => {
271
- db.collection({
272
- name: 'tests',
273
- fields: [
274
- {
275
- type: 'sequence',
276
- name: 'name',
277
- patterns: [
278
- { type: 'date', options: { field: 'date' } }
279
- ]
280
- },
281
- {
282
- type: 'date',
283
- name: 'date'
284
- }
285
- ],
286
- });
287
- await db.sync();
288
-
289
- const date = new Date(2022, 7, 1);
290
- const YYYYMMDD = moment(date).format('YYYYMMDD');
291
-
292
- const TestModel = db.getModel('tests');
293
- const item1 = await TestModel.create({
294
- date
295
- });
296
- expect(item1.get('name')).toBe(YYYYMMDD);
297
- });
298
-
299
- it('format option', async () => {
300
- db.collection({
301
- name: 'tests',
302
- fields: [
303
- {
304
- type: 'sequence',
305
- name: 'name',
306
- patterns: [
307
- { type: 'date', options: { format: 'YYYY-MM-DD' } }
308
- ]
309
- }
310
- ],
311
- });
312
- await db.sync();
313
-
314
- const now = new Date();
315
- const YYYYMMDD = moment(now).format('YYYY-MM-DD');
316
-
317
- const TestModel = db.getModel('tests');
318
- const item1 = await TestModel.create();
319
- expect(item1.get('name')).toBe(YYYYMMDD);
320
- });
321
- });
322
-
323
- describe('mixed pattern', () => {
324
- it('all patterns', async () => {
325
- db.collection({
326
- name: 'tests',
327
- fields: [
328
- {
329
- type: 'sequence',
330
- name: 'name',
331
- patterns: [
332
- { type: 'string', options: { value: 'A' } },
333
- { type: 'date' },
334
- { type: 'integer' }
335
- ]
336
- }
337
- ],
338
- });
339
- await db.sync();
340
-
341
- const now = new Date();
342
- const YYYYMMDD = moment(now).format('YYYYMMDD');
343
-
344
- const TestModel = db.getModel('tests');
345
- const item1 = await TestModel.create();
346
- expect(item1.get('name')).toBe(`A${YYYYMMDD}0`);
347
-
348
- const item2 = await TestModel.create();
349
- expect(item2.get('name')).toBe(`A${YYYYMMDD}1`);
350
- });
351
-
352
- it('changed after generated', async () => {
353
- const testsCollection = db.collection({
354
- name: 'tests',
355
- fields: [
356
- {
357
- type: 'sequence',
358
- name: 'name',
359
- patterns: [
360
- { type: 'string', options: { value: 'A' } },
361
- { type: 'date' },
362
- { type: 'integer' }
363
- ]
364
- }
365
- ],
366
- });
367
- await db.sync();
368
-
369
- const now = new Date();
370
- const YYYYMMDD = moment(now).format('YYYYMMDD');
371
-
372
- const TestModel = db.getModel('tests');
373
- const item1 = await TestModel.create();
374
- expect(item1.get('name')).toBe(`A${YYYYMMDD}0`);
375
-
376
- testsCollection.setField('name', {
377
- type: 'sequence',
378
- patterns: [
379
- { type: 'string', options: { value: 'A' } },
380
- { type: 'date' },
381
- // change options but no difference with default
382
- { type: 'integer', options: { digits: 1 } }
383
- ]
384
- });
385
-
386
- const item2 = await TestModel.create();
387
- expect(item2.get('name')).toBe(`A${YYYYMMDD}1`);
388
-
389
- testsCollection.setField('name', {
390
- type: 'sequence',
391
- patterns: [
392
- { type: 'string', options: { value: 'A' } },
393
- { type: 'date' },
394
- { type: 'integer', options: { digits: 2 } }
395
- ]
396
- });
397
-
398
- const item3 = await TestModel.create();
399
- expect(item3.get('name')).toBe(`A${YYYYMMDD}00`);
400
-
401
- testsCollection.setField('name', {
402
- type: 'sequence',
403
- patterns: [
404
- { type: 'string', options: { value: 'a' } },
405
- { type: 'date' },
406
- { type: 'integer', options: { digits: 2 } }
407
- ]
408
- });
409
-
410
- const item4 = await TestModel.create();
411
- expect(item4.get('name')).toBe(`a${YYYYMMDD}01`);
412
-
413
- testsCollection.setField('name', {
414
- type: 'sequence',
415
- patterns: [
416
- { type: 'date' },
417
- { type: 'integer', options: { digits: 2 } }
418
- ]
419
- });
420
-
421
- const item5 = await TestModel.create();
422
- expect(item5.get('name')).toBe(`${YYYYMMDD}00`);
423
- });
424
- });
425
-
426
- describe('multiple serial fields', () => {
427
- it('2 fields', async () => {
428
- const testsCollection = db.collection({
429
- name: 'tests',
430
- fields: [
431
- {
432
- type: 'sequence',
433
- name: 'name',
434
- patterns: [
435
- { type: 'string', options: { value: 'A' } },
436
- { type: 'date' },
437
- { type: 'integer', options: { digits: 2, cycle: '0 0 * * *' } }
438
- ]
439
- },
440
- {
441
- type: 'sequence',
442
- name: 'code',
443
- patterns: [
444
- { type: 'string', options: { value: 'C' } },
445
- { type: 'integer', options: { digits: 4 }}
446
- ]
447
- }
448
- ]
449
- });
450
- await db.sync();
451
-
452
- const now = new Date();
453
- const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
454
- const NOW = moment(now).format('YYYYMMDD');
455
- const YESTERDAY = moment(yesterday).format('YYYYMMDD');
456
-
457
- const TestModel = db.getModel('tests');
458
- const item1 = await TestModel.create({ createdAt: yesterday });
459
- expect(item1.get('name')).toBe(`A${YESTERDAY}00`);
460
- expect(item1.get('code')).toBe(`C0000`);
461
-
462
- const item2 = await TestModel.create();
463
- expect(item2.get('name')).toBe(`A${NOW}00`);
464
- expect(item2.get('code')).toBe(`C0001`);
465
-
466
- testsCollection.setField('name', {
467
- type: 'sequence',
468
- patterns: [
469
- { type: 'string', options: { value: 'a' } },
470
- { type: 'date' },
471
- { type: 'integer', options: { digits: 1 } }
472
- ]
473
- });
474
-
475
- const item3 = await TestModel.create();
476
- expect(item3.get('name')).toBe(`a${NOW}0`);
477
- expect(item3.get('code')).toBe(`C0002`);
478
- });
479
- });
480
- });
@@ -1,202 +0,0 @@
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 && lastRecord.get(this.options.name)) {
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
- matcher: RegExp;
119
-
120
- get dataType() {
121
- return DataTypes.STRING;
122
- }
123
-
124
- constructor(options: SequenceFieldOptions, context: FieldContext) {
125
- super(options, context);
126
- if (!options.patterns || !options.patterns.length) {
127
- throw new Error('at least one pattern should be defined for sequence type');
128
- }
129
- options.patterns.forEach(pattern => {
130
- const P = sequencePatterns.get(pattern.type);
131
- if (!P) {
132
- throw new Error(`pattern type ${pattern.type} is not registered`);
133
- }
134
- if (P.validate) {
135
- const error = P.validate(pattern.options);
136
- if (error) {
137
- throw new Error(error);
138
- }
139
- }
140
- });
141
-
142
- const patterns = options.patterns
143
- .map(({ type, options }) => sequencePatterns.get(type).getMatcher(options));
144
- this.matcher = new RegExp(`^${patterns.map(p => `(${p})`).join('')}$`, 'i');
145
- }
146
-
147
- setValue = async (instance: Model, options) => {
148
- const { name, patterns } = this.options;
149
- // NOTE: only load when value is not set, if null stand for no last record
150
- if (typeof this.options.lastRecord === 'undefined') {
151
- const model = <typeof Model>instance.constructor;
152
- this.options.lastRecord = await model.findOne({
153
- attributes: [model.primaryKeyAttribute, this.options.name, 'createdAt'],
154
- order: [
155
- ['createdAt', 'DESC'],
156
- // TODO(bug): will cause problem if no auto-increment id
157
- [model.primaryKeyAttribute, 'DESC']
158
- ],
159
- transaction: options.transaction
160
- });
161
- }
162
-
163
- const results = patterns.reduce((result, p, i) => {
164
- const item = sequencePatterns.get(p.type).generate.call(this, instance, i, options);
165
- return result.concat(item);
166
- }, []);
167
- instance.set(name, results.join(''));
168
- };
169
-
170
- setLast = (instance: Model, options) => {
171
- this.options.lastRecord = instance;
172
- };
173
-
174
- match(value) {
175
- return typeof value === 'string' ? value.match(this.matcher) : null;
176
- }
177
-
178
- parse(value: string, patternIndex: number): string {
179
- for (let i = 0, index = 0; i < this.options.patterns.length; i += 1) {
180
- const { type, options } = this.options.patterns[i];
181
- const { getLength } = sequencePatterns.get(type);
182
- const length = getLength(options);
183
- if (i === patternIndex) {
184
- return value.substring(index, index + length);
185
- }
186
- index += length;
187
- }
188
- return '';
189
- }
190
-
191
- bind() {
192
- super.bind();
193
- this.on('beforeCreate', this.setValue);
194
- this.on('afterCreate', this.setLast);
195
- }
196
-
197
- unbind() {
198
- super.unbind();
199
- this.off('beforeCreate', this.setValue);
200
- this.off('afterCreate', this.setLast);
201
- }
202
- }