@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
@@ -0,0 +1,455 @@
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
+
221
+ describe('date pattern', () => {
222
+ it('default to current createdAt as YYYYMMDD', async () => {
223
+ db.collection({
224
+ name: 'tests',
225
+ fields: [
226
+ {
227
+ type: 'sequence',
228
+ name: 'name',
229
+ patterns: [
230
+ { type: 'date' }
231
+ ]
232
+ }
233
+ ],
234
+ });
235
+ await db.sync();
236
+
237
+ const now = new Date();
238
+ const YYYYMMDD = moment(now).format('YYYYMMDD');
239
+
240
+ const TestModel = db.getModel('tests');
241
+ const item1 = await TestModel.create();
242
+ expect(item1.get('name')).toBe(YYYYMMDD);
243
+ });
244
+
245
+ it('field option', async () => {
246
+ db.collection({
247
+ name: 'tests',
248
+ fields: [
249
+ {
250
+ type: 'sequence',
251
+ name: 'name',
252
+ patterns: [
253
+ { type: 'date', options: { field: 'date' } }
254
+ ]
255
+ },
256
+ {
257
+ type: 'date',
258
+ name: 'date'
259
+ }
260
+ ],
261
+ });
262
+ await db.sync();
263
+
264
+ const date = new Date(2022, 7, 1);
265
+ const YYYYMMDD = moment(date).format('YYYYMMDD');
266
+
267
+ const TestModel = db.getModel('tests');
268
+ const item1 = await TestModel.create({
269
+ date
270
+ });
271
+ expect(item1.get('name')).toBe(YYYYMMDD);
272
+ });
273
+
274
+ it('format option', async () => {
275
+ db.collection({
276
+ name: 'tests',
277
+ fields: [
278
+ {
279
+ type: 'sequence',
280
+ name: 'name',
281
+ patterns: [
282
+ { type: 'date', options: { format: 'YYYY-MM-DD' } }
283
+ ]
284
+ }
285
+ ],
286
+ });
287
+ await db.sync();
288
+
289
+ const now = new Date();
290
+ const YYYYMMDD = moment(now).format('YYYY-MM-DD');
291
+
292
+ const TestModel = db.getModel('tests');
293
+ const item1 = await TestModel.create();
294
+ expect(item1.get('name')).toBe(YYYYMMDD);
295
+ });
296
+ });
297
+
298
+ describe('mixed pattern', () => {
299
+ it('all patterns', async () => {
300
+ db.collection({
301
+ name: 'tests',
302
+ fields: [
303
+ {
304
+ type: 'sequence',
305
+ name: 'name',
306
+ patterns: [
307
+ { type: 'string', options: { value: 'A' } },
308
+ { type: 'date' },
309
+ { type: 'integer' }
310
+ ]
311
+ }
312
+ ],
313
+ });
314
+ await db.sync();
315
+
316
+ const now = new Date();
317
+ const YYYYMMDD = moment(now).format('YYYYMMDD');
318
+
319
+ const TestModel = db.getModel('tests');
320
+ const item1 = await TestModel.create();
321
+ expect(item1.get('name')).toBe(`A${YYYYMMDD}0`);
322
+
323
+ const item2 = await TestModel.create();
324
+ expect(item2.get('name')).toBe(`A${YYYYMMDD}1`);
325
+ });
326
+
327
+ it('changed after generated', async () => {
328
+ const testsCollection = db.collection({
329
+ name: 'tests',
330
+ fields: [
331
+ {
332
+ type: 'sequence',
333
+ name: 'name',
334
+ patterns: [
335
+ { type: 'string', options: { value: 'A' } },
336
+ { type: 'date' },
337
+ { type: 'integer' }
338
+ ]
339
+ }
340
+ ],
341
+ });
342
+ await db.sync();
343
+
344
+ const now = new Date();
345
+ const YYYYMMDD = moment(now).format('YYYYMMDD');
346
+
347
+ const TestModel = db.getModel('tests');
348
+ const item1 = await TestModel.create();
349
+ expect(item1.get('name')).toBe(`A${YYYYMMDD}0`);
350
+
351
+ testsCollection.setField('name', {
352
+ type: 'sequence',
353
+ patterns: [
354
+ { type: 'string', options: { value: 'A' } },
355
+ { type: 'date' },
356
+ // change options but no difference with default
357
+ { type: 'integer', options: { digits: 1 } }
358
+ ]
359
+ });
360
+
361
+ const item2 = await TestModel.create();
362
+ expect(item2.get('name')).toBe(`A${YYYYMMDD}1`);
363
+
364
+ testsCollection.setField('name', {
365
+ type: 'sequence',
366
+ patterns: [
367
+ { type: 'string', options: { value: 'A' } },
368
+ { type: 'date' },
369
+ { type: 'integer', options: { digits: 2 } }
370
+ ]
371
+ });
372
+
373
+ const item3 = await TestModel.create();
374
+ expect(item3.get('name')).toBe(`A${YYYYMMDD}00`);
375
+
376
+ testsCollection.setField('name', {
377
+ type: 'sequence',
378
+ patterns: [
379
+ { type: 'string', options: { value: 'a' } },
380
+ { type: 'date' },
381
+ { type: 'integer', options: { digits: 2 } }
382
+ ]
383
+ });
384
+
385
+ const item4 = await TestModel.create();
386
+ expect(item4.get('name')).toBe(`a${YYYYMMDD}01`);
387
+
388
+ testsCollection.setField('name', {
389
+ type: 'sequence',
390
+ patterns: [
391
+ { type: 'date' },
392
+ { type: 'integer', options: { digits: 2 } }
393
+ ]
394
+ });
395
+
396
+ const item5 = await TestModel.create();
397
+ expect(item5.get('name')).toBe(`${YYYYMMDD}00`);
398
+ });
399
+ });
400
+
401
+ describe('multiple serial fields', () => {
402
+ it('2 fields', async () => {
403
+ const testsCollection = db.collection({
404
+ name: 'tests',
405
+ fields: [
406
+ {
407
+ type: 'sequence',
408
+ name: 'name',
409
+ patterns: [
410
+ { type: 'string', options: { value: 'A' } },
411
+ { type: 'date' },
412
+ { type: 'integer', options: { digits: 2, cycle: '0 0 * * *' } }
413
+ ]
414
+ },
415
+ {
416
+ type: 'sequence',
417
+ name: 'code',
418
+ patterns: [
419
+ { type: 'string', options: { value: 'C' } },
420
+ { type: 'integer', options: { digits: 4 }}
421
+ ]
422
+ }
423
+ ]
424
+ });
425
+ await db.sync();
426
+
427
+ const now = new Date();
428
+ const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
429
+ const NOW = moment(now).format('YYYYMMDD');
430
+ const YESTERDAY = moment(yesterday).format('YYYYMMDD');
431
+
432
+ const TestModel = db.getModel('tests');
433
+ const item1 = await TestModel.create({ createdAt: yesterday });
434
+ expect(item1.get('name')).toBe(`A${YESTERDAY}00`);
435
+ expect(item1.get('code')).toBe(`C0000`);
436
+
437
+ const item2 = await TestModel.create();
438
+ expect(item2.get('name')).toBe(`A${NOW}00`);
439
+ expect(item2.get('code')).toBe(`C0001`);
440
+
441
+ testsCollection.setField('name', {
442
+ type: 'sequence',
443
+ patterns: [
444
+ { type: 'string', options: { value: 'a' } },
445
+ { type: 'date' },
446
+ { type: 'integer', options: { digits: 1 } }
447
+ ]
448
+ });
449
+
450
+ const item3 = await TestModel.create();
451
+ expect(item3.get('name')).toBe(`a${NOW}0`);
452
+ expect(item3.get('code')).toBe(`C0002`);
453
+ });
454
+ });
455
+ });
@@ -12,6 +12,30 @@ describe('magic-attribute-model', () => {
12
12
  await db.close();
13
13
  });
14
14
 
15
+ it('case 0', async () => {
16
+ db.registerModels({ MagicAttributeModel });
17
+
18
+ const Test = db.collection({
19
+ name: 'tests',
20
+ model: 'MagicAttributeModel',
21
+ fields: [
22
+ { type: 'string', name: 'title' },
23
+ { type: 'json', name: 'options' },
24
+ ],
25
+ });
26
+
27
+ await db.sync();
28
+
29
+ const test = await Test.model.create({
30
+ title: 'aa',
31
+ 'x-component-props': { key1: 'val1', arr1: [1, 2, 3], arr2: [4, 5] },
32
+ });
33
+
34
+ test.set('x-component-props', { arr2: [1, 2, 3] });
35
+
36
+ expect(test.previous('options')['x-component-props']['arr2']).toEqual([4, 5]);
37
+ });
38
+
15
39
  it('case 1', async () => {
16
40
  db.registerModels({ MagicAttributeModel });
17
41
 
@@ -30,4 +30,16 @@ describe('ne operator', () => {
30
30
 
31
31
  expect(results).toEqual(1);
32
32
  });
33
+
34
+ it('compare with null', async () => {
35
+ await db.getRepository('tests').create({});
36
+
37
+ const results = await db.getRepository('tests').count({
38
+ filter: {
39
+ 'name.$ne': null,
40
+ },
41
+ });
42
+
43
+ expect(results).toBe(0);
44
+ });
33
45
  });
@@ -0,0 +1,64 @@
1
+ import Database, { BelongsToRepository, Collection, mockDatabase } from '@nocobase/database';
2
+
3
+ describe('appends', () => {
4
+ let db: Database;
5
+
6
+ let User: Collection;
7
+ let Post: Collection;
8
+
9
+ let A1: Collection;
10
+ let A2: Collection;
11
+
12
+ afterEach(async () => {
13
+ await db.close();
14
+ });
15
+
16
+ beforeEach(async () => {
17
+ db = mockDatabase();
18
+
19
+ User = db.collection({
20
+ name: 'users',
21
+ fields: [
22
+ { type: 'string', name: 'name' },
23
+ { type: 'hasMany', name: 'posts' },
24
+ ],
25
+ });
26
+
27
+ Post = db.collection({
28
+ name: 'posts',
29
+ fields: [
30
+ { type: 'string', name: 'title' },
31
+ {
32
+ type: 'belongsTo',
33
+ name: 'user',
34
+ },
35
+ ],
36
+ });
37
+
38
+ await db.sync();
39
+ });
40
+
41
+ it('should find with appends', async () => {
42
+ await User.repository.create({
43
+ values: {
44
+ name: 'u1',
45
+ posts: [
46
+ {
47
+ title: 'p1',
48
+ },
49
+ ],
50
+ },
51
+ });
52
+
53
+ const p1 = await Post.repository.findOne({});
54
+
55
+ const repository = new BelongsToRepository(Post, 'user', p1['id']);
56
+
57
+ const user = await repository.findOne({
58
+ appends: ['posts'],
59
+ });
60
+
61
+ const data = user.toJSON();
62
+ expect(data['posts'][0]['title']).toEqual('p1');
63
+ });
64
+ });
@@ -235,6 +235,29 @@ describe('belongs to many', () => {
235
235
  expect(findFilterResult[0].name).toEqual('t2');
236
236
  });
237
237
 
238
+ test('create with array', async () => {
239
+ const p1 = await Post.repository.create({
240
+ values: {
241
+ title: 'p1',
242
+ },
243
+ });
244
+
245
+ const PostTagRepository = new BelongsToManyRepository(Post, 'tags', p1.id);
246
+
247
+ const results = await PostTagRepository.create({
248
+ values: [
249
+ {
250
+ name: 't1',
251
+ },
252
+ {
253
+ name: 't2',
254
+ },
255
+ ],
256
+ });
257
+
258
+ expect(results.length).toEqual(2);
259
+ });
260
+
238
261
  test('find and count', async () => {
239
262
  const p1 = await Post.repository.create({
240
263
  values: {
@@ -203,6 +203,26 @@ describe('has many repository', () => {
203
203
  expect(post.userId).toEqual(u1.id);
204
204
  });
205
205
 
206
+ test('create with array', async () => {
207
+ const u1 = await User.repository.create({
208
+ values: { name: 'u1' },
209
+ });
210
+
211
+ const UserPostRepository = new HasManyRepository(User, 'posts', u1.id);
212
+ const posts = await UserPostRepository.create({
213
+ values: [
214
+ {
215
+ title: 't1',
216
+ },
217
+ {
218
+ title: 't2',
219
+ },
220
+ ],
221
+ });
222
+
223
+ expect(posts.length).toEqual(2);
224
+ });
225
+
206
226
  test('update', async () => {
207
227
  const u1 = await User.repository.create({
208
228
  values: { name: 'u1' },