@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.
- package/lib/collection.d.ts +7 -3
- package/lib/collection.js +126 -22
- package/lib/database.d.ts +13 -7
- package/lib/database.js +39 -8
- package/lib/decorators/must-have-filter-decorator.js +4 -0
- package/lib/decorators/transaction-decorator.js +1 -0
- package/lib/features/ReferencesMap.js +14 -9
- package/lib/field-repository/array-field-repository.d.ts +28 -0
- package/lib/field-repository/array-field-repository.js +208 -0
- package/lib/fields/array-field.d.ts +1 -1
- package/lib/fields/array-field.js +15 -11
- package/lib/fields/belongs-to-field.d.ts +9 -1
- package/lib/fields/belongs-to-field.js +21 -7
- package/lib/fields/belongs-to-many-field.d.ts +4 -0
- package/lib/fields/belongs-to-many-field.js +24 -0
- package/lib/fields/field.d.ts +3 -2
- package/lib/fields/field.js +19 -13
- package/lib/fields/has-many-field.d.ts +2 -1
- package/lib/fields/has-many-field.js +18 -10
- package/lib/fields/has-one-field.d.ts +1 -0
- package/lib/fields/has-one-field.js +22 -10
- package/lib/fields/index.d.ts +3 -3
- package/lib/fields/index.js +14 -34
- package/lib/fields/relation-field.d.ts +2 -1
- package/lib/fields/relation-field.js +16 -0
- package/lib/fields/set-field.d.ts +10 -0
- package/lib/fields/set-field.js +35 -0
- package/lib/fields/sort-field.d.ts +1 -1
- package/lib/fields/sort-field.js +1 -1
- package/lib/filter-match.d.ts +1 -0
- package/lib/filter-match.js +84 -0
- package/lib/filter-parser.d.ts +2 -2
- package/lib/index.d.ts +5 -1
- package/lib/index.js +56 -0
- package/lib/inherited-collection.d.ts +13 -0
- package/lib/inherited-collection.js +210 -0
- package/lib/inherited-map.d.ts +21 -0
- package/lib/inherited-map.js +203 -0
- package/lib/model-hook.d.ts +1 -1
- package/lib/model.d.ts +1 -0
- package/lib/model.js +47 -0
- package/lib/operators/array.d.ts +1 -25
- package/lib/operators/association.d.ts +1 -9
- package/lib/operators/boolean.d.ts +1 -12
- package/lib/operators/date.d.ts +1 -33
- package/lib/operators/empty.d.ts +2 -25
- package/lib/operators/ne.d.ts +1 -13
- package/lib/operators/notIn.d.ts +1 -9
- package/lib/options-parser.d.ts +3 -2
- package/lib/options-parser.js +16 -1
- package/lib/relation-repository/relation-repository.d.ts +2 -2
- package/lib/relation-repository/single-relation-repository.js +2 -2
- package/lib/repository.d.ts +18 -10
- package/lib/repository.js +172 -38
- package/lib/sync-runner.d.ts +4 -0
- package/lib/sync-runner.js +181 -0
- package/lib/types.d.ts +2 -2
- package/lib/update-associations.d.ts +1 -0
- package/lib/update-associations.js +21 -2
- package/lib/update-guard.d.ts +3 -3
- package/lib/utils.js +5 -0
- package/package.json +4 -4
- package/src/__tests__/bigint.test.ts +48 -0
- package/src/__tests__/collection.test.ts +48 -13
- package/src/__tests__/database.test.ts +10 -0
- package/src/__tests__/field-repository/array-field-repository.test.ts +94 -0
- package/src/__tests__/fields/set.test.ts +37 -0
- package/src/__tests__/filter-match.test.ts +52 -0
- package/src/__tests__/inhertits/collection-inherits-sync.test.ts +38 -0
- package/src/__tests__/inhertits/collection-inherits.test.ts +994 -0
- package/src/__tests__/inhertits/helper.ts +3 -0
- package/src/__tests__/inhertits/inherited-map.test.ts +27 -0
- package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +2 -0
- package/src/__tests__/relation-repository/has-many-repository.test.ts +1 -1
- package/src/__tests__/repository/destroy.test.ts +122 -1
- package/src/__tests__/repository/update-many.test.ts +57 -0
- package/src/__tests__/update-association-values.test.ts +232 -0
- package/src/__tests__/update-associations.test.ts +6 -1
- package/src/collection.ts +90 -8
- package/src/database.ts +53 -14
- package/src/decorators/must-have-filter-decorator.ts +4 -0
- package/src/decorators/transaction-decorator.ts +1 -0
- package/src/features/ReferencesMap.ts +20 -9
- package/src/features/referential-integrity-check.ts +1 -0
- package/src/field-repository/array-field-repository.ts +155 -0
- package/src/fields/array-field.ts +4 -4
- package/src/fields/belongs-to-field.ts +26 -10
- package/src/fields/belongs-to-many-field.ts +34 -0
- package/src/fields/field.ts +26 -13
- package/src/fields/has-many-field.ts +17 -9
- package/src/fields/has-one-field.ts +23 -9
- package/src/fields/index.ts +5 -5
- package/src/fields/relation-field.ts +16 -0
- package/src/fields/set-field.ts +25 -0
- package/src/fields/sort-field.ts +5 -4
- package/src/filter-match.ts +49 -0
- package/src/filter-parser.ts +2 -2
- package/src/index.ts +5 -2
- package/src/inherited-collection.ts +112 -0
- package/src/inherited-map.ts +97 -0
- package/src/model-hook.ts +2 -3
- package/src/model.ts +43 -3
- package/src/operators/array.ts +1 -1
- package/src/operators/association.ts +1 -1
- package/src/operators/boolean.ts +1 -1
- package/src/operators/date.ts +1 -1
- package/src/operators/empty.ts +1 -1
- package/src/operators/ne.ts +1 -1
- package/src/operators/notIn.ts +2 -1
- package/src/options-parser.ts +20 -4
- package/src/relation-repository/relation-repository.ts +2 -2
- package/src/relation-repository/single-relation-repository.ts +2 -2
- package/src/repository.ts +144 -30
- package/src/sync-runner.ts +162 -0
- package/src/types.ts +2 -2
- package/src/update-associations.ts +23 -7
- package/src/update-guard.ts +3 -3
- package/src/utils.ts +5 -1
- package/lib/fields/sequence-field.d.ts +0 -32
- package/lib/fields/sequence-field.js +0 -300
- package/src/__tests__/fields/sequence-field.test.ts +0 -480
- 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
|
-
}
|