@nocobase/database 0.8.0-alpha.8 → 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 -31
  120. package/lib/fields/sequence-field.js +0 -299
  121. package/src/__tests__/fields/sequence-field.test.ts +0 -455
  122. package/src/fields/sequence-field.ts +0 -200
@@ -0,0 +1,994 @@
1
+ import Database from '../../database';
2
+ import { InheritedCollection } from '../../inherited-collection';
3
+ import { mockDatabase } from '../index';
4
+ import pgOnly from './helper';
5
+
6
+ pgOnly()('collection inherits', () => {
7
+ let db: Database;
8
+
9
+ beforeEach(async () => {
10
+ db = mockDatabase();
11
+ await db.clean({ drop: true });
12
+ });
13
+
14
+ afterEach(async () => {
15
+ await db.close();
16
+ });
17
+
18
+ it('should create inherits with table name contains upperCase', async () => {
19
+ db.collection({
20
+ name: 'parent',
21
+ fields: [{ name: 'field1', type: 'date' }],
22
+ });
23
+ await db.sync({
24
+ force: false,
25
+ alter: {
26
+ drop: false,
27
+ },
28
+ });
29
+ db.collection({
30
+ name: 'abcABC',
31
+ inherits: ['parent'],
32
+ fields: [{ type: 'string', name: 'name' }],
33
+ });
34
+ await db.sync({
35
+ force: false,
36
+ alter: {
37
+ drop: false,
38
+ },
39
+ });
40
+ });
41
+
42
+ it('should create inherits from empty table', async () => {
43
+ const empty = db.collection({
44
+ name: 'empty',
45
+ timestamps: false,
46
+ autoGenId: false,
47
+ fields: [],
48
+ });
49
+
50
+ await db.sync({
51
+ force: false,
52
+ alter: {
53
+ drop: false,
54
+ },
55
+ });
56
+
57
+ const parent2 = db.collection({
58
+ name: 'parent2',
59
+ });
60
+
61
+ const inherits = db.collection({
62
+ name: 'inherits',
63
+ inherits: ['empty', 'parent2'],
64
+ fields: [{ type: 'string', name: 'name' }],
65
+ });
66
+
67
+ await db.sync({
68
+ force: false,
69
+ alter: {
70
+ drop: false,
71
+ },
72
+ });
73
+
74
+ expect(inherits instanceof InheritedCollection).toBeTruthy();
75
+
76
+ const record = await inherits.repository.create({
77
+ values: {
78
+ name: 'test',
79
+ },
80
+ });
81
+
82
+ expect(record.get('name')).toEqual('test');
83
+ });
84
+
85
+ it('should not throw error when fields have same type with parent', async () => {
86
+ db.collection({
87
+ name: 'parent',
88
+ fields: [{ name: 'field1', type: 'date' }],
89
+ });
90
+
91
+ expect(() => {
92
+ db.collection({
93
+ name: 'child',
94
+ inherits: ['parent'],
95
+ fields: [
96
+ {
97
+ name: 'field1',
98
+ type: 'date',
99
+ otherOptions: true,
100
+ },
101
+ ],
102
+ });
103
+ }).not.toThrowError();
104
+ });
105
+
106
+ it('should throw error when fields conflict with parent ', async () => {
107
+ db.collection({
108
+ name: 'parent',
109
+ fields: [{ name: 'field1', type: 'string' }],
110
+ });
111
+
112
+ expect(() => {
113
+ db.collection({
114
+ name: 'child',
115
+ inherits: ['parent'],
116
+ fields: [{ name: 'field1', type: 'integer' }],
117
+ });
118
+ }).toThrowError();
119
+
120
+ await db.sync();
121
+ });
122
+
123
+ it.skip('should not conflict when fields have same DateType', async () => {
124
+ db.collection({
125
+ name: 'parent',
126
+ fields: [{ name: 'field1', type: 'string' }],
127
+ });
128
+
129
+ expect(() => {
130
+ db.collection({
131
+ name: 'child',
132
+ inherits: ['parent'],
133
+ fields: [
134
+ {
135
+ name: 'field1',
136
+ type: 'sequence',
137
+ patterns: [
138
+ {
139
+ type: 'integer',
140
+ },
141
+ ],
142
+ },
143
+ ],
144
+ });
145
+ }).not.toThrowError();
146
+ });
147
+
148
+ it('should create inherits with lazy parents', async () => {
149
+ const child = db.collection({
150
+ name: 'child',
151
+ inherits: ['delay-parents'],
152
+ });
153
+
154
+ expect(child.getField('parent-field')).toBeFalsy();
155
+
156
+ db.collection({
157
+ name: 'delay-parents',
158
+ fields: [
159
+ {
160
+ type: 'string',
161
+ name: 'parent-field',
162
+ },
163
+ ],
164
+ });
165
+
166
+ expect(child.getField('parent-field')).toBeTruthy();
167
+ });
168
+
169
+ it('should create inherits with multiple lazy parents', async () => {
170
+ const child = db.collection({
171
+ name: 'child',
172
+ inherits: ['parent1', 'parent2'],
173
+ });
174
+
175
+ expect(child.getField('parent1-field')).toBeFalsy();
176
+
177
+ db.collection({
178
+ name: 'parent1',
179
+ fields: [
180
+ {
181
+ type: 'string',
182
+ name: 'parent1-field',
183
+ },
184
+ ],
185
+ });
186
+
187
+ expect(child.getField('parent1-field')).toBeFalsy();
188
+
189
+ db.collection({
190
+ name: 'parent2',
191
+ fields: [
192
+ {
193
+ type: 'string',
194
+ name: 'parent2-field',
195
+ },
196
+ ],
197
+ });
198
+
199
+ expect(child.getField('parent1-field')).toBeTruthy();
200
+ });
201
+
202
+ it('should inherit from no id table', async () => {
203
+ const interfaceCollection = db.collection({
204
+ name: 'a',
205
+ fields: [
206
+ {
207
+ type: 'string',
208
+ name: 'name',
209
+ },
210
+ ],
211
+ timestamps: false,
212
+ });
213
+
214
+ interfaceCollection.model.removeAttribute('id');
215
+ const child = db.collection({
216
+ name: 'b',
217
+ inherits: ['a'],
218
+ });
219
+
220
+ await db.sync();
221
+
222
+ const childInstance = await child.repository.create({
223
+ values: {
224
+ name: 'test',
225
+ },
226
+ });
227
+
228
+ expect(childInstance.get('name')).toBe('test');
229
+ });
230
+
231
+ it('should pass empty inherits params', async () => {
232
+ const table1 = db.collection({
233
+ name: 'table1',
234
+ fields: [{ type: 'string', name: 'name' }],
235
+ });
236
+
237
+ const table2 = db.collection({
238
+ name: 'table2',
239
+ inherits: [],
240
+ });
241
+
242
+ expect(table2).not.toBeInstanceOf(InheritedCollection);
243
+ });
244
+
245
+ it('should remove Node after collection destroy', async () => {
246
+ const table1 = db.collection({
247
+ name: 'table1',
248
+ fields: [{ type: 'string', name: 'name' }],
249
+ });
250
+
251
+ db.collection({
252
+ name: 'table2',
253
+ fields: [{ type: 'string', name: 'integer' }],
254
+ });
255
+
256
+ const collection3 = db.collection({
257
+ name: 'table3',
258
+ inherits: ['table1', 'table2'],
259
+ });
260
+
261
+ await db.removeCollection(collection3.name);
262
+
263
+ expect(db.inheritanceMap.getNode('table3')).toBeUndefined();
264
+ expect(table1.isParent()).toBeFalsy();
265
+ });
266
+
267
+ it('can update relation with child table', async () => {
268
+ const A = db.collection({
269
+ name: 'a',
270
+ fields: [
271
+ {
272
+ name: 'a-field',
273
+ type: 'string',
274
+ },
275
+ {
276
+ name: 'a1s',
277
+ type: 'hasMany',
278
+ target: 'a1',
279
+ foreignKey: 'aId',
280
+ },
281
+ ],
282
+ });
283
+
284
+ const A1 = db.collection({
285
+ name: 'a1',
286
+ inherits: ['a'],
287
+ fields: [
288
+ {
289
+ name: 'a1-field',
290
+ type: 'string',
291
+ },
292
+ ],
293
+ });
294
+
295
+ await db.sync();
296
+
297
+ await A.repository.create({
298
+ values: {
299
+ 'a-field': 'a-1',
300
+ },
301
+ });
302
+
303
+ let a11 = await A1.repository.create({
304
+ values: {
305
+ 'a1-field': 'a1-1',
306
+ 'a-field': 'a1-1',
307
+ },
308
+ });
309
+
310
+ const a12 = await A1.repository.create({
311
+ values: {
312
+ 'a1-field': 'a1-2',
313
+ 'a-field': 'a1-2',
314
+ },
315
+ });
316
+
317
+ await A1.repository.update({
318
+ filterByTk: a11.get('id'),
319
+ values: {
320
+ a1s: [{ id: a12.get('id') }],
321
+ },
322
+ });
323
+
324
+ a11 = await A1.repository.findOne({
325
+ filter: {
326
+ 'a1-field': 'a1-1',
327
+ },
328
+ appends: ['a1s'],
329
+ });
330
+
331
+ const a11a1s = a11.get('a1s');
332
+ expect(a11a1s[0].get('id')).toBe(a12.get('id'));
333
+ });
334
+
335
+ it('can create relation with child table', async () => {
336
+ const A = db.collection({
337
+ name: 'a',
338
+ fields: [
339
+ {
340
+ name: 'af',
341
+ type: 'string',
342
+ },
343
+ {
344
+ name: 'bs',
345
+ type: 'hasMany',
346
+ target: 'b',
347
+ },
348
+ ],
349
+ });
350
+
351
+ const B = db.collection({
352
+ name: 'b',
353
+ inherits: ['a'],
354
+ fields: [
355
+ {
356
+ name: 'bf',
357
+ type: 'string',
358
+ },
359
+ {
360
+ name: 'a',
361
+ type: 'belongsTo',
362
+ target: 'a',
363
+ },
364
+ ],
365
+ });
366
+
367
+ await db.sync();
368
+
369
+ const a1 = await B.repository.create({
370
+ values: {
371
+ af: 'a1',
372
+ bs: [{ bf: 'b1' }, { bf: 'b2' }],
373
+ },
374
+ });
375
+
376
+ expect(a1.get('bs').length).toBe(2);
377
+
378
+ const b1 = await B.repository.findOne({
379
+ filter: {
380
+ af: 'a1',
381
+ },
382
+ appends: ['bs'],
383
+ });
384
+
385
+ expect(b1.get('bs').length).toBe(2);
386
+ });
387
+
388
+ it('should inherit belongsToMany field', async () => {
389
+ db.collection({
390
+ name: 'person',
391
+ fields: [
392
+ {
393
+ name: 'name',
394
+ type: 'string',
395
+ },
396
+ {
397
+ name: 'tags',
398
+ type: 'belongsToMany',
399
+ },
400
+ ],
401
+ });
402
+
403
+ db.collection({
404
+ name: 'tags',
405
+ fields: [
406
+ {
407
+ name: 'name',
408
+ type: 'string',
409
+ },
410
+ {
411
+ name: 'person',
412
+ type: 'belongsToMany',
413
+ },
414
+ ],
415
+ });
416
+
417
+ db.collection({
418
+ name: 'students',
419
+ inherits: 'person',
420
+ fields: [
421
+ {
422
+ name: 'score',
423
+ type: 'integer',
424
+ },
425
+ ],
426
+ });
427
+
428
+ await db.sync();
429
+
430
+ await db.getCollection('students').repository.create({
431
+ values: {
432
+ name: 'John',
433
+ score: 100,
434
+ tags: [
435
+ {
436
+ name: 't1',
437
+ },
438
+ {
439
+ name: 't2',
440
+ },
441
+ {
442
+ name: 't3',
443
+ },
444
+ ],
445
+ },
446
+ });
447
+
448
+ await db.getCollection('person').repository.create({
449
+ values: {
450
+ name: 'Max',
451
+ tags: [
452
+ {
453
+ name: 't2',
454
+ },
455
+ {
456
+ name: 't4',
457
+ },
458
+ ],
459
+ },
460
+ });
461
+
462
+ const john = await db.getCollection('students').repository.findOne({
463
+ appends: ['tags'],
464
+ });
465
+
466
+ expect(john.get('name')).toBe('John');
467
+ expect(john.get('tags')).toHaveLength(3);
468
+
469
+ const max = await db.getCollection('person').repository.findOne({
470
+ appends: ['tags'],
471
+ filter: {
472
+ name: 'Max',
473
+ },
474
+ });
475
+
476
+ expect(max.get('name')).toBe('Max');
477
+ expect(max.get('tags')).toHaveLength(2);
478
+ });
479
+
480
+ it('should inherit hasMany field', async () => {
481
+ db.collection({
482
+ name: 'person',
483
+ fields: [
484
+ {
485
+ name: 'name',
486
+ type: 'string',
487
+ },
488
+ {
489
+ name: 'pets',
490
+ type: 'hasMany',
491
+ },
492
+ ],
493
+ });
494
+
495
+ db.collection({
496
+ name: 'pets',
497
+ fields: [
498
+ {
499
+ name: 'name',
500
+ type: 'string',
501
+ },
502
+ ],
503
+ });
504
+
505
+ db.collection({
506
+ name: 'students',
507
+ inherits: 'person',
508
+ fields: [
509
+ {
510
+ name: 'score',
511
+ type: 'integer',
512
+ },
513
+ ],
514
+ });
515
+
516
+ await db.sync();
517
+
518
+ await db.getCollection('person').repository.create({
519
+ values: {
520
+ name: 'Max',
521
+ pets: [
522
+ {
523
+ name: 'doge1',
524
+ },
525
+ {
526
+ name: 'kitty1',
527
+ },
528
+ ],
529
+ },
530
+ });
531
+
532
+ await db.getCollection('students').repository.create({
533
+ values: {
534
+ name: 'John',
535
+ score: 100,
536
+ pets: [
537
+ {
538
+ name: 'doge',
539
+ },
540
+ {
541
+ name: 'kitty',
542
+ },
543
+ {
544
+ name: 'doge2',
545
+ },
546
+ ],
547
+ },
548
+ });
549
+
550
+ const john = await db.getCollection('students').repository.findOne({
551
+ appends: ['pets'],
552
+ });
553
+
554
+ expect(john.get('name')).toBe('John');
555
+ expect(john.get('pets')).toHaveLength(3);
556
+ });
557
+
558
+ it('can inherit from multiple collections', async () => {
559
+ const a = db.collection({
560
+ name: 'a',
561
+ fields: [{ type: 'string', name: 'a1' }],
562
+ });
563
+
564
+ const b = db.collection({
565
+ name: 'b',
566
+ fields: [{ type: 'string', name: 'b1' }],
567
+ });
568
+
569
+ const c = db.collection({
570
+ name: 'c',
571
+ inherits: ['a', 'b'],
572
+ fields: [
573
+ { type: 'bigInt', name: 'id', autoIncrement: true },
574
+ { type: 'string', name: 'c1' },
575
+ ],
576
+ });
577
+
578
+ await db.sync();
579
+
580
+ expect(c.getField('a1')).toBeTruthy();
581
+ expect(c.getField('b1')).toBeTruthy();
582
+ expect(c.getField('c1')).toBeTruthy();
583
+
584
+ const c1 = await c.repository.create({
585
+ values: {
586
+ a1: 'a1',
587
+ b1: 'b1',
588
+ c1: 'c1',
589
+ },
590
+ });
591
+
592
+ expect(c1.get('a1')).toBe('a1');
593
+ expect(c1.get('b1')).toBe('b1');
594
+ expect(c1.get('c1')).toBe('c1');
595
+
596
+ const a2 = await a.repository.create({
597
+ values: {
598
+ a1: 'a2',
599
+ },
600
+ });
601
+
602
+ expect(a2.get('id')).toEqual(2);
603
+
604
+ db.collection({
605
+ name: 'd',
606
+ inherits: ['c'],
607
+ });
608
+
609
+ await db.sync();
610
+ });
611
+
612
+ it('should update inherit field when parent field update', async () => {
613
+ db.collection({
614
+ name: 'person',
615
+ fields: [{ name: 'name', type: 'string', title: 'parent-name' }],
616
+ });
617
+
618
+ db.collection({
619
+ name: 'students',
620
+ inherits: 'person',
621
+ });
622
+
623
+ expect(db.getCollection('students').getField('name').get('title')).toBe('parent-name');
624
+
625
+ db.getCollection('person').setField('name', { type: 'string', title: 'new-name' });
626
+
627
+ expect(db.getCollection('person').getField('name').get('title')).toBe('new-name');
628
+ expect(db.getCollection('students').getField('name').get('title')).toBe('new-name');
629
+ });
630
+
631
+ it('should not replace child field when parent field update', async () => {
632
+ db.collection({
633
+ name: 'person',
634
+ fields: [{ name: 'name', type: 'string', title: 'parent-name' }],
635
+ });
636
+
637
+ db.collection({
638
+ name: 'students',
639
+ inherits: 'person',
640
+ fields: [{ name: 'name', type: 'string', title: 'student-name' }],
641
+ });
642
+
643
+ expect(db.getCollection('students').getField('name').get('title')).toBe('student-name');
644
+
645
+ db.getCollection('person').setField('name', { type: 'string', title: 'new-name' });
646
+
647
+ expect(db.getCollection('person').getField('name').get('title')).toBe('new-name');
648
+ expect(db.getCollection('students').getField('name').get('title')).toBe('student-name');
649
+ });
650
+
651
+ it('should replace child association target', async () => {
652
+ db.collection({
653
+ name: 'person',
654
+ fields: [
655
+ { name: 'name', type: 'string' },
656
+ { type: 'hasOne', name: 'profile', foreignKey: 'personId' },
657
+ ],
658
+ });
659
+
660
+ const Profile = db.collection({
661
+ name: 'profiles',
662
+ fields: [
663
+ { name: 'age', type: 'integer' },
664
+ {
665
+ type: 'belongsTo',
666
+ name: 'person',
667
+ foreignKey: 'personId',
668
+ },
669
+ ],
670
+ });
671
+
672
+ db.collection({
673
+ name: 'students',
674
+ inherits: 'person',
675
+ fields: [
676
+ { name: 'score', type: 'integer' },
677
+ {
678
+ type: 'hasOne',
679
+ name: 'profile',
680
+ target: 'studentProfiles',
681
+ },
682
+ ],
683
+ });
684
+
685
+ db.collection({
686
+ name: 'teachers',
687
+ inherits: 'person',
688
+ fields: [{ name: 'salary', type: 'integer' }],
689
+ });
690
+
691
+ db.collection({
692
+ name: 'studentProfiles',
693
+ fields: [{ name: 'grade', type: 'string' }],
694
+ });
695
+
696
+ await db.sync();
697
+
698
+ const student = await db.getCollection('students').repository.create({
699
+ values: {
700
+ name: 'foo',
701
+ score: 100,
702
+ profile: {
703
+ grade: 'A',
704
+ },
705
+ },
706
+ });
707
+
708
+ expect(student.get('profile').get('grade')).toBe('A');
709
+
710
+ const teacher = await db.getCollection('teachers').repository.create({
711
+ values: {
712
+ name: 'bar',
713
+ salary: 1000,
714
+ profile: {
715
+ age: 30,
716
+ },
717
+ },
718
+ });
719
+
720
+ expect(teacher.get('profile').get('age')).toBe(30);
721
+ });
722
+
723
+ it('should replace hasOne association field', async () => {
724
+ const person = db.collection({
725
+ name: 'person',
726
+ fields: [
727
+ { name: 'name', type: 'string' },
728
+ { type: 'hasOne', name: 'profile', target: 'profiles', foreignKey: 'person_id' },
729
+ ],
730
+ });
731
+
732
+ const profile = db.collection({
733
+ name: 'profiles',
734
+ fields: [
735
+ { name: 'age', type: 'integer' },
736
+ {
737
+ type: 'belongsTo',
738
+ name: 'person',
739
+ },
740
+ ],
741
+ });
742
+
743
+ const student = db.collection({
744
+ name: 'students',
745
+ inherits: 'person',
746
+ fields: [{ name: 'profile', type: 'hasOne', target: 'studentProfiles', foreignKey: 'student_id' }],
747
+ });
748
+
749
+ const studentProfile = db.collection({
750
+ name: 'studentProfiles',
751
+ fields: [{ name: 'score', type: 'integer' }],
752
+ });
753
+
754
+ await db.sync();
755
+
756
+ const student1 = await student.repository.create({
757
+ values: {
758
+ name: 'student-1',
759
+ profile: {
760
+ score: '100',
761
+ },
762
+ },
763
+ });
764
+
765
+ let person1 = await person.repository.findOne();
766
+ await person.repository
767
+ .relation('profile')
768
+ .of(person1.get('id'))
769
+ .create({
770
+ values: {
771
+ age: 30,
772
+ },
773
+ });
774
+
775
+ person1 = await person.repository.findOne({
776
+ appends: ['profile'],
777
+ });
778
+
779
+ expect(person1.get('profile').get('age')).toBe(30);
780
+
781
+ expect(student1.get('profile').get('score')).toBe(100);
782
+ });
783
+
784
+ it('should inherit hasOne association field', async () => {
785
+ const person = db.collection({
786
+ name: 'person',
787
+ fields: [
788
+ { name: 'name', type: 'string' },
789
+ { type: 'hasOne', name: 'profile' },
790
+ ],
791
+ });
792
+
793
+ const profile = db.collection({
794
+ name: 'profiles',
795
+ fields: [
796
+ { name: 'age', type: 'integer' },
797
+ {
798
+ type: 'belongsTo',
799
+ name: 'person',
800
+ },
801
+ ],
802
+ });
803
+
804
+ db.collection({
805
+ name: 'students',
806
+ inherits: 'person',
807
+ fields: [{ name: 'score', type: 'integer' }],
808
+ });
809
+
810
+ db.collection({
811
+ name: 'teachers',
812
+ inherits: 'person',
813
+ fields: [{ name: 'salary', type: 'integer' }],
814
+ });
815
+
816
+ await db.sync();
817
+
818
+ await db.getCollection('students').repository.create({
819
+ values: {
820
+ name: 'foo',
821
+ score: 100,
822
+ profile: {
823
+ age: 18,
824
+ },
825
+ },
826
+ });
827
+
828
+ await db.getCollection('teachers').repository.create({
829
+ values: {
830
+ name: 'bar',
831
+ salary: 1000,
832
+ profile: {
833
+ age: 30,
834
+ },
835
+ },
836
+ });
837
+
838
+ const studentFoo = await db.getCollection('students').repository.findOne({
839
+ appends: ['profile'],
840
+ });
841
+
842
+ const teacherBar = await db.getCollection('teachers').repository.findOne({
843
+ appends: ['profile'],
844
+ });
845
+
846
+ expect(studentFoo.get('profile').age).toBe(18);
847
+ expect(teacherBar.get('profile').age).toBe(30);
848
+ });
849
+
850
+ it('should inherit from Collection', async () => {
851
+ const person = db.collection({
852
+ name: 'person',
853
+ fields: [{ name: 'name', type: 'string' }],
854
+ });
855
+
856
+ const student = db.collection({
857
+ name: 'student',
858
+ inherits: 'person',
859
+ fields: [{ name: 'score', type: 'integer' }],
860
+ });
861
+
862
+ await db.sync();
863
+
864
+ const StudentRepository = student.repository;
865
+
866
+ await StudentRepository.create({
867
+ values: { name: 'student1' },
868
+ });
869
+
870
+ expect(await person.repository.count()).toBe(1);
871
+ });
872
+
873
+ it('should create inherited table', async () => {
874
+ const person = db.collection({
875
+ name: 'person',
876
+ fields: [{ name: 'name', type: 'string' }],
877
+ });
878
+
879
+ const student = db.collection({
880
+ name: 'student',
881
+ inherits: 'person',
882
+ fields: [{ name: 'score', type: 'integer' }],
883
+ });
884
+
885
+ await db.sync();
886
+
887
+ const studentTableInfo = await db.sequelize.getQueryInterface().describeTable(student.model.tableName);
888
+
889
+ expect(studentTableInfo.score).toBeDefined();
890
+ expect(studentTableInfo.name).toBeDefined();
891
+ expect(studentTableInfo.id).toBeDefined();
892
+ expect(studentTableInfo.createdAt).toBeDefined();
893
+ expect(studentTableInfo.updatedAt).toBeDefined();
894
+ });
895
+
896
+ it('should get parent fields', async () => {
897
+ const root = db.collection({
898
+ name: 'root',
899
+ fields: [{ name: 'rootField', type: 'string' }],
900
+ });
901
+
902
+ const parent1 = db.collection({
903
+ name: 'parent1',
904
+ inherits: 'root',
905
+ fields: [{ name: 'parent1Field', type: 'string' }],
906
+ });
907
+
908
+ const parent2 = db.collection({
909
+ name: 'parent2',
910
+ inherits: 'parent1',
911
+ fields: [{ name: 'parent2Field', type: 'string' }],
912
+ });
913
+
914
+ const parent21 = db.collection({
915
+ name: 'parent21',
916
+ fields: [{ name: 'parent21Field', type: 'string' }],
917
+ });
918
+
919
+ const child: InheritedCollection = db.collection({
920
+ name: 'child',
921
+ inherits: ['parent2', 'parent21'],
922
+ fields: [{ name: 'childField', type: 'string' }],
923
+ }) as InheritedCollection;
924
+
925
+ const parentFields = child.parentFields();
926
+ expect(parentFields.size).toBe(4);
927
+ });
928
+
929
+ it('should sync parent fields', async () => {
930
+ const person = db.collection({
931
+ name: 'person',
932
+ fields: [{ name: 'name', type: 'string' }],
933
+ });
934
+
935
+ const student = db.collection({
936
+ name: 'student',
937
+ inherits: 'person',
938
+ fields: [{ name: 'score', type: 'integer' }],
939
+ });
940
+
941
+ await db.sync();
942
+
943
+ expect(student.fields.get('name')).toBeDefined();
944
+
945
+ // add new field to parent
946
+ person.setField('age', { type: 'integer' });
947
+
948
+ await db.sync();
949
+
950
+ expect(student.fields.get('age')).toBeDefined();
951
+
952
+ const student1 = await db.getCollection('student').repository.create({
953
+ values: {
954
+ name: 'student1',
955
+ age: 10,
956
+ score: 100,
957
+ },
958
+ });
959
+
960
+ expect(student1.get('name')).toBe('student1');
961
+ expect(student1.get('age')).toBe(10);
962
+ });
963
+
964
+ it('should destroy fields on parents table', async () => {
965
+ const profile = db.collection({
966
+ name: 'profiles',
967
+ fields: [{ type: 'string', name: 'name' }],
968
+ });
969
+
970
+ const person = db.collection({
971
+ name: 'person',
972
+ fields: [
973
+ { name: 'name', type: 'string' },
974
+ {
975
+ type: 'hasOne',
976
+ name: 'profile',
977
+ },
978
+ ],
979
+ });
980
+
981
+ const student = db.collection({
982
+ name: 'student',
983
+ inherits: 'person',
984
+ });
985
+
986
+ await db.sync();
987
+
988
+ person.removeField('profile');
989
+
990
+ person.setField('profile', { type: 'belongsTo' });
991
+
992
+ await db.sync();
993
+ });
994
+ });