tantan-typeorm-gs 1.0.0

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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/bun.lock +300 -0
  4. package/docs/authentication.md +118 -0
  5. package/docs/configuration.md +680 -0
  6. package/docs/contributing.md +177 -0
  7. package/docs/crud.md +351 -0
  8. package/docs/custom-client.md +165 -0
  9. package/docs/development.md +243 -0
  10. package/docs/entities.md +502 -0
  11. package/docs/find-options.md +296 -0
  12. package/docs/google-cloud.md +101 -0
  13. package/docs/installation.md +51 -0
  14. package/docs/integration-testing.md +178 -0
  15. package/docs/limitations.md +223 -0
  16. package/docs/pagination.md +384 -0
  17. package/docs/performance.md +171 -0
  18. package/docs/security.md +153 -0
  19. package/docs/sorting.md +0 -0
  20. package/docs/supported-features.md +310 -0
  21. package/docs/synchronization.md +203 -0
  22. package/package.json +43 -0
  23. package/src/client/index.ts +589 -0
  24. package/src/core/data-source.ts +47 -0
  25. package/src/core/driver.ts +290 -0
  26. package/src/core/error.ts +144 -0
  27. package/src/core/memory.ts +152 -0
  28. package/src/core/query/interpreter.ts +949 -0
  29. package/src/core/query/runner.ts +1297 -0
  30. package/src/core/query/types.ts +155 -0
  31. package/src/core/schema-builder.ts +64 -0
  32. package/src/core/types.ts +113 -0
  33. package/src/core/utils.ts +13 -0
  34. package/src/index.ts +3 -0
  35. package/tantan-typeorm-gs.code-workspace +8 -0
  36. package/test/base/0001-data-source.test.ts +252 -0
  37. package/test/base/0002-operator.test.ts +616 -0
  38. package/test/base/0003-select.test.ts +281 -0
  39. package/test/base/0004-aggregate.test.ts +213 -0
  40. package/test/base/0005-transcation.test.ts +1566 -0
  41. package/test/base/0006-relation.test.ts +1611 -0
  42. package/test/base/0007-logging.test.ts +182 -0
  43. package/test/base/0008-soft-delete.test.ts +649 -0
  44. package/test/google-sheets/0001-client.test.ts +1705 -0
  45. package/test/google-sheets/0002-worksheet-management.test.ts +408 -0
  46. package/test/google-sheets/0003-data-source.test.ts +1935 -0
  47. package/test/google-sheets/0004-transactions.test.ts +952 -0
  48. package/test/google-sheets/0005-operator.test.ts +1124 -0
  49. package/test/google-sheets/0006-object-criteria.test.ts +283 -0
  50. package/test/google-sheets/0007-performance.test.ts +532 -0
  51. package/test/public-api.test.ts +34 -0
  52. package/tsconfig.build.json +21 -0
  53. package/tsconfig.json +34 -0
@@ -0,0 +1,1611 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import
3
+ {
4
+ Column,
5
+ Entity,
6
+ JoinColumn,
7
+ JoinTable,
8
+ ManyToMany,
9
+ ManyToOne,
10
+ OneToMany,
11
+ PrimaryGeneratedColumn,
12
+ } from 'typeorm';
13
+ import { Memory } from '../../src/core/memory';
14
+ import { createGoogleSheetsDataSource } from '../../src/core/data-source';
15
+
16
+ describe('GoogleSheetsDataSource - Relation', () =>
17
+ {
18
+ @Entity()
19
+ class Role
20
+ {
21
+ @PrimaryGeneratedColumn()
22
+ id!: number;
23
+
24
+ @Column()
25
+ name!: string;
26
+
27
+ @ManyToMany(
28
+ () => User,
29
+ (user) => user.roles,
30
+ )
31
+ users!: User[];
32
+ }
33
+
34
+ @Entity()
35
+ class User
36
+ {
37
+ @PrimaryGeneratedColumn()
38
+ id!: number;
39
+
40
+ @Column()
41
+ name!: string;
42
+
43
+ @OneToMany(
44
+ () => Post,
45
+ (post) => post.user,
46
+ )
47
+ posts!: Post[];
48
+
49
+ @ManyToMany(
50
+ () => Role,
51
+ (role) => role.users,
52
+ )
53
+ @JoinTable({
54
+ name: 'user_role',
55
+ joinColumn: {
56
+ name: 'userId',
57
+ referencedColumnName: 'id',
58
+ },
59
+ inverseJoinColumn: {
60
+ name: 'roleId',
61
+ referencedColumnName: 'id',
62
+ },
63
+ })
64
+ roles!: Role[];
65
+ }
66
+
67
+ @Entity()
68
+ class Category
69
+ {
70
+ @PrimaryGeneratedColumn()
71
+ id!: number;
72
+
73
+ @Column()
74
+ name!: string;
75
+ }
76
+
77
+ @Entity()
78
+ class Post
79
+ {
80
+ @PrimaryGeneratedColumn()
81
+ id!: number;
82
+
83
+ @Column()
84
+ title!: string;
85
+
86
+ @Column()
87
+ userId!: number;
88
+
89
+ @Column()
90
+ categoryId!: number;
91
+
92
+ @ManyToOne(
93
+ () => User,
94
+ (user) => user.posts,
95
+ )
96
+ @JoinColumn({
97
+ name: 'userId',
98
+ })
99
+ user!: User;
100
+
101
+ @ManyToOne(
102
+ () => Category,
103
+ )
104
+ @JoinColumn({
105
+ name: 'categoryId',
106
+ })
107
+ category!: Category;
108
+ }
109
+
110
+ const entities = [User, Post, Category, Role]
111
+
112
+ test('loads ManyToOne relation', async () =>
113
+ {
114
+ const dataSource = createGoogleSheetsDataSource({
115
+ type: 'google-sheets',
116
+ client: new Memory({
117
+ user: [
118
+ { id: 1, name: 'Basuni' },
119
+ { id: 2, name: 'Budi' },
120
+ ],
121
+ post: [
122
+ {
123
+ id: 1,
124
+ title: 'Post A',
125
+ userId: 1,
126
+ },
127
+ {
128
+ id: 2,
129
+ title: 'Post B',
130
+ userId: 1,
131
+ },
132
+ {
133
+ id: 3,
134
+ title: 'Post C',
135
+ userId: 2,
136
+ },
137
+ ],
138
+ }),
139
+ entities
140
+ });
141
+
142
+ await dataSource.initialize();
143
+
144
+ const repository = dataSource.getRepository(Post);
145
+
146
+ const posts = await repository
147
+ .createQueryBuilder('post')
148
+ .leftJoinAndSelect(
149
+ 'post.user',
150
+ 'user',
151
+ )
152
+ .orderBy(
153
+ 'post.id',
154
+ 'ASC',
155
+ )
156
+ .getMany();
157
+
158
+ expect(posts).toHaveLength(3);
159
+
160
+ expect(posts[0]).toMatchObject({
161
+ id: 1,
162
+ title: 'Post A',
163
+ userId: 1,
164
+ user: {
165
+ id: 1,
166
+ name: 'Basuni',
167
+ },
168
+ });
169
+
170
+ expect(posts[1]).toMatchObject({
171
+ id: 2,
172
+ title: 'Post B',
173
+ userId: 1,
174
+ user: {
175
+ id: 1,
176
+ name: 'Basuni',
177
+ },
178
+ });
179
+
180
+ expect(posts[2]).toMatchObject({
181
+ id: 3,
182
+ title: 'Post C',
183
+ userId: 2,
184
+ user: {
185
+ id: 2,
186
+ name: 'Budi',
187
+ },
188
+ });
189
+
190
+ await dataSource.destroy();
191
+ });
192
+
193
+ test('loads OneToMany relation', async () =>
194
+ {
195
+ const dataSource = createGoogleSheetsDataSource({
196
+ type: 'google-sheets',
197
+ client: new Memory({
198
+ user: [
199
+ { id: 1, name: 'Basuni' },
200
+ { id: 2, name: 'Budi' },
201
+ ],
202
+ post: [
203
+ {
204
+ id: 1,
205
+ title: 'Post A',
206
+ userId: 1,
207
+ },
208
+ {
209
+ id: 2,
210
+ title: 'Post B',
211
+ userId: 1,
212
+ },
213
+ {
214
+ id: 3,
215
+ title: 'Post C',
216
+ userId: 2,
217
+ },
218
+ ],
219
+ }),
220
+ entities
221
+ });
222
+
223
+ await dataSource.initialize();
224
+
225
+ const repository = dataSource.getRepository(User);
226
+
227
+ const users = await repository
228
+ .createQueryBuilder('user')
229
+ .leftJoinAndSelect(
230
+ 'user.posts',
231
+ 'post',
232
+ )
233
+ .orderBy(
234
+ 'user.id',
235
+ 'ASC',
236
+ )
237
+ .addOrderBy(
238
+ 'post.id',
239
+ 'ASC',
240
+ )
241
+ .getMany();
242
+
243
+ expect(users).toHaveLength(2);
244
+
245
+ expect(users[0]).toMatchObject({
246
+ id: 1,
247
+ name: 'Basuni',
248
+ posts: [
249
+ {
250
+ id: 1,
251
+ title: 'Post A',
252
+ userId: 1,
253
+ },
254
+ {
255
+ id: 2,
256
+ title: 'Post B',
257
+ userId: 1,
258
+ },
259
+ ],
260
+ });
261
+
262
+ expect(users[1]).toMatchObject({
263
+ id: 2,
264
+ name: 'Budi',
265
+ posts: [
266
+ {
267
+ id: 3,
268
+ title: 'Post C',
269
+ userId: 2,
270
+ },
271
+ ],
272
+ });
273
+
274
+ await dataSource.destroy();
275
+ });
276
+
277
+ test('filters entities by ManyToOne relation', async () =>
278
+ {
279
+ const dataSource = createGoogleSheetsDataSource({
280
+ type: 'google-sheets',
281
+ client: new Memory({
282
+ user: [
283
+ { id: 1, name: 'Basuni' },
284
+ { id: 2, name: 'Budi' },
285
+ ],
286
+ post: [
287
+ {
288
+ id: 1,
289
+ title: 'Post A',
290
+ userId: 1,
291
+ },
292
+ {
293
+ id: 2,
294
+ title: 'Post B',
295
+ userId: 1,
296
+ },
297
+ {
298
+ id: 3,
299
+ title: 'Post C',
300
+ userId: 2,
301
+ },
302
+ ],
303
+ }),
304
+ entities
305
+ });
306
+
307
+ await dataSource.initialize();
308
+
309
+ const repository = dataSource.getRepository(Post);
310
+
311
+ const posts = await repository
312
+ .createQueryBuilder('post')
313
+ .leftJoinAndSelect(
314
+ 'post.user',
315
+ 'user',
316
+ )
317
+ .where(
318
+ 'user.name = :name',
319
+ {
320
+ name: 'Basuni',
321
+ },
322
+ )
323
+ .orderBy(
324
+ 'post.id',
325
+ 'ASC',
326
+ )
327
+ .getMany();
328
+
329
+ expect(posts).toHaveLength(2);
330
+
331
+ expect(posts.map((post) => post.id)).toEqual([
332
+ 1,
333
+ 2,
334
+ ]);
335
+
336
+ expect(posts.every(
337
+ (post) => post.user?.name === 'Basuni',
338
+ )).toBe(true);
339
+
340
+ await dataSource.destroy();
341
+ });
342
+
343
+
344
+ test('selects columns from relation', async () =>
345
+ {
346
+ const dataSource =
347
+ createGoogleSheetsDataSource({
348
+ type: 'google-sheets',
349
+ client:
350
+ new Memory({
351
+ user: [
352
+ {
353
+ id: 1,
354
+ name: 'Basuni',
355
+ },
356
+ {
357
+ id: 2,
358
+ name: 'Budi',
359
+ },
360
+ ],
361
+ post: [
362
+ {
363
+ id: 1,
364
+ title: 'Post A',
365
+ userId: 1,
366
+ },
367
+ {
368
+ id: 2,
369
+ title: 'Post B',
370
+ userId: 1,
371
+ },
372
+ {
373
+ id: 3,
374
+ title: 'Post C',
375
+ userId: 2,
376
+ },
377
+ ],
378
+ }),
379
+ entities
380
+ });
381
+
382
+ await dataSource.initialize();
383
+
384
+ const repository =
385
+ dataSource.getRepository(Post);
386
+
387
+ const posts =
388
+ await repository
389
+ .createQueryBuilder('post')
390
+ .leftJoin(
391
+ 'post.user',
392
+ 'user',
393
+ )
394
+ .select([
395
+ 'post.id',
396
+ 'post.title',
397
+ 'user.name',
398
+ ])
399
+ .orderBy(
400
+ 'post.id',
401
+ 'ASC',
402
+ )
403
+ .getRawMany();
404
+
405
+ expect(posts).toEqual([
406
+ {
407
+ post_id: 1,
408
+ post_title: 'Post A',
409
+ user_name: 'Basuni',
410
+ },
411
+ {
412
+ post_id: 2,
413
+ post_title: 'Post B',
414
+ user_name: 'Basuni',
415
+ },
416
+ {
417
+ post_id: 3,
418
+ post_title: 'Post C',
419
+ user_name: 'Budi',
420
+ },
421
+ ]);
422
+
423
+ await dataSource.destroy();
424
+ });
425
+
426
+ test('selects columns from filtered relation', async () =>
427
+ {
428
+ const dataSource =
429
+ createGoogleSheetsDataSource({
430
+ type: 'google-sheets',
431
+ client:
432
+ new Memory({
433
+ user: [
434
+ {
435
+ id: 1,
436
+ name: 'Basuni',
437
+ },
438
+ {
439
+ id: 2,
440
+ name: 'Budi',
441
+ },
442
+ ],
443
+ post: [
444
+ {
445
+ id: 1,
446
+ title: 'Post A',
447
+ userId: 1,
448
+ },
449
+ {
450
+ id: 2,
451
+ title: 'Post B',
452
+ userId: 1,
453
+ },
454
+ {
455
+ id: 3,
456
+ title: 'Post C',
457
+ userId: 2,
458
+ },
459
+ ],
460
+ }),
461
+ entities
462
+ });
463
+
464
+ await dataSource.initialize();
465
+
466
+ const repository =
467
+ dataSource.getRepository(Post);
468
+
469
+ const posts =
470
+ await repository
471
+ .createQueryBuilder('post')
472
+ .leftJoin(
473
+ 'post.user',
474
+ 'user',
475
+ )
476
+ .where(
477
+ 'user.name = :name',
478
+ {
479
+ name: 'Basuni',
480
+ },
481
+ )
482
+ .select([
483
+ 'post.id',
484
+ 'post.title',
485
+ 'user.name',
486
+ ])
487
+ .orderBy(
488
+ 'post.id',
489
+ 'ASC',
490
+ )
491
+ .getRawMany();
492
+
493
+ expect(posts).toEqual([
494
+ {
495
+ post_id: 1,
496
+ post_title: 'Post A',
497
+ user_name: 'Basuni',
498
+ },
499
+ {
500
+ post_id: 2,
501
+ post_title: 'Post B',
502
+ user_name: 'Basuni',
503
+ },
504
+ ]);
505
+
506
+ await dataSource.destroy();
507
+ });
508
+
509
+ test('loads nested ManyToOne relations', async () =>
510
+ {
511
+ const dataSource =
512
+ createGoogleSheetsDataSource({
513
+ type: 'google-sheets',
514
+ client:
515
+ new Memory({
516
+ user: [
517
+ { id: 1, name: 'Basuni' },
518
+ { id: 2, name: 'Budi' },
519
+ ],
520
+ category: [
521
+ { id: 1, name: 'Technology' },
522
+ { id: 2, name: 'Business' },
523
+ ],
524
+ post: [
525
+ {
526
+ id: 1,
527
+ title: 'Post A',
528
+ userId: 1,
529
+ categoryId: 1,
530
+ },
531
+ {
532
+ id: 2,
533
+ title: 'Post B',
534
+ userId: 1,
535
+ categoryId: 2,
536
+ },
537
+ {
538
+ id: 3,
539
+ title: 'Post C',
540
+ userId: 2,
541
+ categoryId: 1,
542
+ },
543
+ ],
544
+ }),
545
+ entities
546
+ });
547
+
548
+ await dataSource.initialize();
549
+
550
+ const repository =
551
+ dataSource.getRepository(Post);
552
+
553
+ const posts =
554
+ await repository
555
+ .createQueryBuilder('post')
556
+ .leftJoinAndSelect(
557
+ 'post.user',
558
+ 'user',
559
+ )
560
+ .leftJoinAndSelect(
561
+ 'post.category',
562
+ 'category',
563
+ )
564
+ .orderBy(
565
+ 'post.id',
566
+ 'ASC',
567
+ )
568
+ .getMany();
569
+
570
+ expect(posts).toHaveLength(3);
571
+
572
+ expect(posts[0]).toMatchObject({
573
+ id: 1,
574
+ title: 'Post A',
575
+ user: {
576
+ id: 1,
577
+ name: 'Basuni',
578
+ },
579
+ category: {
580
+ id: 1,
581
+ name: 'Technology',
582
+ },
583
+ });
584
+
585
+ expect(posts[1]).toMatchObject({
586
+ id: 2,
587
+ title: 'Post B',
588
+ user: {
589
+ id: 1,
590
+ name: 'Basuni',
591
+ },
592
+ category: {
593
+ id: 2,
594
+ name: 'Business',
595
+ },
596
+ });
597
+
598
+ expect(posts[2]).toMatchObject({
599
+ id: 3,
600
+ title: 'Post C',
601
+ user: {
602
+ id: 2,
603
+ name: 'Budi',
604
+ },
605
+ category: {
606
+ id: 1,
607
+ name: 'Technology',
608
+ },
609
+ });
610
+
611
+ await dataSource.destroy();
612
+ });
613
+
614
+ test('loads nested OneToMany relations', async () =>
615
+ {
616
+ const dataSource =
617
+ createGoogleSheetsDataSource({
618
+ type: 'google-sheets',
619
+ client:
620
+ new Memory({
621
+ user: [
622
+ { id: 1, name: 'Basuni' },
623
+ { id: 2, name: 'Budi' },
624
+ ],
625
+ category: [
626
+ { id: 1, name: 'Technology' },
627
+ { id: 2, name: 'Business' },
628
+ ],
629
+ post: [
630
+ {
631
+ id: 1,
632
+ title: 'Post A',
633
+ userId: 1,
634
+ categoryId: 1,
635
+ },
636
+ {
637
+ id: 2,
638
+ title: 'Post B',
639
+ userId: 1,
640
+ categoryId: 2,
641
+ },
642
+ {
643
+ id: 3,
644
+ title: 'Post C',
645
+ userId: 2,
646
+ categoryId: 1,
647
+ },
648
+ ],
649
+ }),
650
+ entities
651
+ });
652
+
653
+ await dataSource.initialize();
654
+
655
+ const repository =
656
+ dataSource.getRepository(User);
657
+
658
+ const users =
659
+ await repository
660
+ .createQueryBuilder('user')
661
+ .leftJoinAndSelect(
662
+ 'user.posts',
663
+ 'post',
664
+ )
665
+ .leftJoinAndSelect(
666
+ 'post.category',
667
+ 'category',
668
+ )
669
+ .orderBy(
670
+ 'user.id',
671
+ 'ASC',
672
+ )
673
+ .addOrderBy(
674
+ 'post.id',
675
+ 'ASC',
676
+ )
677
+ .getMany();
678
+
679
+ expect(users).toHaveLength(2);
680
+
681
+ expect(users[0]).toMatchObject({
682
+ id: 1,
683
+ name: 'Basuni',
684
+ posts: [
685
+ {
686
+ id: 1,
687
+ title: 'Post A',
688
+ category: {
689
+ id: 1,
690
+ name: 'Technology',
691
+ },
692
+ },
693
+ {
694
+ id: 2,
695
+ title: 'Post B',
696
+ category: {
697
+ id: 2,
698
+ name: 'Business',
699
+ },
700
+ },
701
+ ],
702
+ });
703
+
704
+ expect(users[1]).toMatchObject({
705
+ id: 2,
706
+ name: 'Budi',
707
+ posts: [
708
+ {
709
+ id: 3,
710
+ title: 'Post C',
711
+ category: {
712
+ id: 1,
713
+ name: 'Technology',
714
+ },
715
+ },
716
+ ],
717
+ });
718
+
719
+ await dataSource.destroy();
720
+ });
721
+
722
+ test('keeps entity when OneToMany relation is empty', async () =>
723
+ {
724
+ const dataSource =
725
+ createGoogleSheetsDataSource({
726
+ type: 'google-sheets',
727
+ client:
728
+ new Memory({
729
+ user: [
730
+ { id: 1, name: 'Basuni' },
731
+ { id: 2, name: 'Budi' },
732
+ { id: 3, name: 'Caca' },
733
+ ],
734
+ post: [
735
+ {
736
+ id: 1,
737
+ title: 'Post A',
738
+ userId: 1,
739
+ },
740
+ {
741
+ id: 2,
742
+ title: 'Post B',
743
+ userId: 1,
744
+ },
745
+ {
746
+ id: 3,
747
+ title: 'Post C',
748
+ userId: 2,
749
+ },
750
+ ],
751
+ }),
752
+ entities
753
+ });
754
+
755
+ await dataSource.initialize();
756
+
757
+ const repository =
758
+ dataSource.getRepository(User);
759
+
760
+ const users =
761
+ await repository
762
+ .createQueryBuilder('user')
763
+ .leftJoinAndSelect(
764
+ 'user.posts',
765
+ 'post',
766
+ )
767
+ .orderBy(
768
+ 'user.id',
769
+ 'ASC',
770
+ )
771
+ .addOrderBy(
772
+ 'post.id',
773
+ 'ASC',
774
+ )
775
+ .getMany();
776
+
777
+ expect(users).toHaveLength(3);
778
+
779
+ expect(users[0]).toMatchObject({
780
+ id: 1,
781
+ name: 'Basuni',
782
+ posts: [
783
+ {
784
+ id: 1,
785
+ title: 'Post A',
786
+ },
787
+ {
788
+ id: 2,
789
+ title: 'Post B',
790
+ },
791
+ ],
792
+ });
793
+
794
+ expect(users[1]).toMatchObject({
795
+ id: 2,
796
+ name: 'Budi',
797
+ posts: [
798
+ {
799
+ id: 3,
800
+ title: 'Post C',
801
+ },
802
+ ],
803
+ });
804
+
805
+ expect(users[2]).toMatchObject({
806
+ id: 3,
807
+ name: 'Caca',
808
+ posts: [],
809
+ });
810
+
811
+ await dataSource.destroy();
812
+ });
813
+
814
+ test('loads OneToMany relations with pagination', async () =>
815
+ {
816
+ const dataSource =
817
+ createGoogleSheetsDataSource({
818
+ type: 'google-sheets',
819
+ client:
820
+ new Memory({
821
+ user: [
822
+ { id: 1, name: 'Basuni' },
823
+ { id: 2, name: 'Budi' },
824
+ ],
825
+ post: [
826
+ {
827
+ id: 1,
828
+ title: 'Post A',
829
+ userId: 1,
830
+ },
831
+ {
832
+ id: 2,
833
+ title: 'Post B',
834
+ userId: 1,
835
+ },
836
+ {
837
+ id: 3,
838
+ title: 'Post C',
839
+ userId: 2,
840
+ },
841
+ ],
842
+ }),
843
+ entities
844
+ });
845
+
846
+ await dataSource.initialize();
847
+
848
+ const repository =
849
+ dataSource.getRepository(User);
850
+
851
+ const users =
852
+ await repository
853
+ .createQueryBuilder('user')
854
+ .leftJoinAndSelect(
855
+ 'user.posts',
856
+ 'post',
857
+ )
858
+ .orderBy(
859
+ 'user.id',
860
+ 'ASC',
861
+ )
862
+ .addOrderBy(
863
+ 'post.id',
864
+ 'ASC',
865
+ )
866
+ .take(1)
867
+ .getMany();
868
+
869
+ expect(users).toHaveLength(1);
870
+
871
+ expect(users[0]).toMatchObject({
872
+ id: 1,
873
+ name: 'Basuni',
874
+ posts: [
875
+ {
876
+ id: 1,
877
+ title: 'Post A',
878
+ },
879
+ {
880
+ id: 2,
881
+ title: 'Post B',
882
+ },
883
+ ],
884
+ });
885
+
886
+ await dataSource.destroy();
887
+ });
888
+
889
+ test('loads multiple OneToMany relations with pagination', async () =>
890
+ {
891
+ const dataSource =
892
+ createGoogleSheetsDataSource({
893
+ type: 'google-sheets',
894
+ client:
895
+ new Memory({
896
+ user: [
897
+ { id: 1, name: 'Basuni' },
898
+ { id: 2, name: 'Budi' },
899
+ { id: 3, name: 'Citra' },
900
+ ],
901
+ post: [
902
+ {
903
+ id: 1,
904
+ title: 'Post A',
905
+ userId: 1,
906
+ },
907
+ {
908
+ id: 2,
909
+ title: 'Post B',
910
+ userId: 1,
911
+ },
912
+ {
913
+ id: 3,
914
+ title: 'Post C',
915
+ userId: 2,
916
+ },
917
+ {
918
+ id: 4,
919
+ title: 'Post D',
920
+ userId: 3,
921
+ },
922
+ ],
923
+ }),
924
+ entities
925
+ });
926
+
927
+ await dataSource.initialize();
928
+
929
+ const repository =
930
+ dataSource.getRepository(User);
931
+
932
+ const users =
933
+ await repository
934
+ .createQueryBuilder('user')
935
+ .leftJoinAndSelect(
936
+ 'user.posts',
937
+ 'post',
938
+ )
939
+ .orderBy(
940
+ 'user.id',
941
+ 'ASC',
942
+ )
943
+ .addOrderBy(
944
+ 'post.id',
945
+ 'ASC',
946
+ )
947
+ .take(2)
948
+ .getMany();
949
+
950
+ expect(users).toHaveLength(2);
951
+
952
+ expect(users[0]).toMatchObject({
953
+ id: 1,
954
+ name: 'Basuni',
955
+ posts: [
956
+ {
957
+ id: 1,
958
+ title: 'Post A',
959
+ },
960
+ {
961
+ id: 2,
962
+ title: 'Post B',
963
+ },
964
+ ],
965
+ });
966
+
967
+ expect(users[1]).toMatchObject({
968
+ id: 2,
969
+ name: 'Budi',
970
+ posts: [
971
+ {
972
+ id: 3,
973
+ title: 'Post C',
974
+ },
975
+ ],
976
+ });
977
+
978
+ await dataSource.destroy();
979
+ });
980
+
981
+ test('loads OneToMany relations with offset pagination', async () =>
982
+ {
983
+ const dataSource =
984
+ createGoogleSheetsDataSource({
985
+ type: 'google-sheets',
986
+ client:
987
+ new Memory({
988
+ user: [
989
+ { id: 1, name: 'Basuni' },
990
+ { id: 2, name: 'Budi' },
991
+ { id: 3, name: 'Citra' },
992
+ ],
993
+ post: [
994
+ {
995
+ id: 1,
996
+ title: 'Post A',
997
+ userId: 1,
998
+ },
999
+ {
1000
+ id: 2,
1001
+ title: 'Post B',
1002
+ userId: 1,
1003
+ },
1004
+ {
1005
+ id: 3,
1006
+ title: 'Post C',
1007
+ userId: 2,
1008
+ },
1009
+ {
1010
+ id: 4,
1011
+ title: 'Post D',
1012
+ userId: 3,
1013
+ },
1014
+ ],
1015
+ }),
1016
+ entities
1017
+ });
1018
+
1019
+ await dataSource.initialize();
1020
+
1021
+ const repository =
1022
+ dataSource.getRepository(User);
1023
+
1024
+ const users =
1025
+ await repository
1026
+ .createQueryBuilder('user')
1027
+ .leftJoinAndSelect(
1028
+ 'user.posts',
1029
+ 'post',
1030
+ )
1031
+ .orderBy(
1032
+ 'user.id',
1033
+ 'ASC',
1034
+ )
1035
+ .addOrderBy(
1036
+ 'post.id',
1037
+ 'ASC',
1038
+ )
1039
+ .skip(1)
1040
+ .take(2)
1041
+ .getMany();
1042
+
1043
+ expect(users).toHaveLength(2);
1044
+
1045
+ expect(users[0]).toMatchObject({
1046
+ id: 2,
1047
+ name: 'Budi',
1048
+ posts: [
1049
+ {
1050
+ id: 3,
1051
+ title: 'Post C',
1052
+ },
1053
+ ],
1054
+ });
1055
+
1056
+ expect(users[1]).toMatchObject({
1057
+ id: 3,
1058
+ name: 'Citra',
1059
+ posts: [
1060
+ {
1061
+ id: 4,
1062
+ title: 'Post D',
1063
+ },
1064
+ ],
1065
+ });
1066
+
1067
+ await dataSource.destroy();
1068
+ });
1069
+
1070
+ test('loads OneToMany relations with uneven children and pagination', async () =>
1071
+ {
1072
+ const dataSource =
1073
+ createGoogleSheetsDataSource({
1074
+ type: 'google-sheets',
1075
+ client:
1076
+ new Memory({
1077
+ user: [
1078
+ { id: 1, name: 'Basuni' },
1079
+ { id: 2, name: 'Budi' },
1080
+ { id: 3, name: 'Citra' },
1081
+ { id: 4, name: 'Doni' },
1082
+ ],
1083
+ post: [
1084
+ {
1085
+ id: 1,
1086
+ title: 'Post A',
1087
+ userId: 1,
1088
+ },
1089
+ {
1090
+ id: 2,
1091
+ title: 'Post B',
1092
+ userId: 1,
1093
+ },
1094
+ {
1095
+ id: 3,
1096
+ title: 'Post C',
1097
+ userId: 1,
1098
+ },
1099
+ {
1100
+ id: 4,
1101
+ title: 'Post D',
1102
+ userId: 3,
1103
+ },
1104
+ {
1105
+ id: 5,
1106
+ title: 'Post E',
1107
+ userId: 4,
1108
+ },
1109
+ {
1110
+ id: 6,
1111
+ title: 'Post F',
1112
+ userId: 4,
1113
+ },
1114
+ ],
1115
+ }),
1116
+ entities
1117
+ });
1118
+
1119
+ await dataSource.initialize();
1120
+
1121
+ const repository =
1122
+ dataSource.getRepository(User);
1123
+
1124
+ const users =
1125
+ await repository
1126
+ .createQueryBuilder('user')
1127
+ .leftJoinAndSelect(
1128
+ 'user.posts',
1129
+ 'post',
1130
+ )
1131
+ .orderBy(
1132
+ 'user.id',
1133
+ 'ASC',
1134
+ )
1135
+ .addOrderBy(
1136
+ 'post.id',
1137
+ 'ASC',
1138
+ )
1139
+ .skip(1)
1140
+ .take(2)
1141
+ .getMany();
1142
+
1143
+ expect(users).toHaveLength(2);
1144
+
1145
+ expect(users[0]).toMatchObject({
1146
+ id: 2,
1147
+ name: 'Budi',
1148
+ posts: [],
1149
+ });
1150
+
1151
+ expect(users[1]).toMatchObject({
1152
+ id: 3,
1153
+ name: 'Citra',
1154
+ posts: [
1155
+ {
1156
+ id: 4,
1157
+ title: 'Post D',
1158
+ },
1159
+ ],
1160
+ });
1161
+
1162
+ await dataSource.destroy();
1163
+ });
1164
+
1165
+ test('loads ManyToMany relation', async () =>
1166
+ {
1167
+ const dataSource =
1168
+ createGoogleSheetsDataSource({
1169
+ type: 'google-sheets',
1170
+ client:
1171
+ new Memory({
1172
+ user: [
1173
+ {
1174
+ id: 1,
1175
+ name: 'Basuni',
1176
+ },
1177
+ {
1178
+ id: 2,
1179
+ name: 'Budi',
1180
+ },
1181
+ ],
1182
+ role: [
1183
+ {
1184
+ id: 1,
1185
+ name: 'Admin',
1186
+ },
1187
+ {
1188
+ id: 2,
1189
+ name: 'Developer',
1190
+ },
1191
+ ],
1192
+ user_role: [
1193
+ {
1194
+ userId: 1,
1195
+ roleId: 1,
1196
+ },
1197
+ {
1198
+ userId: 1,
1199
+ roleId: 2,
1200
+ },
1201
+ {
1202
+ userId: 2,
1203
+ roleId: 2,
1204
+ },
1205
+ ],
1206
+ post: [],
1207
+ category: [],
1208
+ }),
1209
+ entities
1210
+ });
1211
+
1212
+ await dataSource.initialize();
1213
+
1214
+ const repository =
1215
+ dataSource.getRepository(User);
1216
+
1217
+ const users =
1218
+ await repository
1219
+ .createQueryBuilder('user')
1220
+ .leftJoinAndSelect(
1221
+ 'user.roles',
1222
+ 'role',
1223
+ )
1224
+ .orderBy(
1225
+ 'user.id',
1226
+ 'ASC',
1227
+ )
1228
+ .addOrderBy(
1229
+ 'role.id',
1230
+ 'ASC',
1231
+ )
1232
+ .getMany();
1233
+
1234
+ expect(users).toHaveLength(2);
1235
+
1236
+ expect(users[0]).toMatchObject({
1237
+ id: 1,
1238
+ name: 'Basuni',
1239
+ roles: [
1240
+ {
1241
+ id: 1,
1242
+ name: 'Admin',
1243
+ },
1244
+ {
1245
+ id: 2,
1246
+ name: 'Developer',
1247
+ },
1248
+ ],
1249
+ });
1250
+
1251
+ expect(users[1]).toMatchObject({
1252
+ id: 2,
1253
+ name: 'Budi',
1254
+ roles: [
1255
+ {
1256
+ id: 2,
1257
+ name: 'Developer',
1258
+ },
1259
+ ],
1260
+ });
1261
+
1262
+ await dataSource.destroy();
1263
+ });
1264
+
1265
+ test('loads ManyToMany relation with pagination', async () =>
1266
+ {
1267
+ const dataSource =
1268
+ createGoogleSheetsDataSource({
1269
+ type: 'google-sheets',
1270
+ client:
1271
+ new Memory({
1272
+ user: [
1273
+ {
1274
+ id: 1,
1275
+ name: 'Basuni',
1276
+ },
1277
+ {
1278
+ id: 2,
1279
+ name: 'Budi',
1280
+ },
1281
+ {
1282
+ id: 3,
1283
+ name: 'Citra',
1284
+ },
1285
+ ],
1286
+ role: [
1287
+ {
1288
+ id: 1,
1289
+ name: 'Admin',
1290
+ },
1291
+ {
1292
+ id: 2,
1293
+ name: 'Developer',
1294
+ },
1295
+ {
1296
+ id: 3,
1297
+ name: 'Manager',
1298
+ },
1299
+ ],
1300
+ user_role: [
1301
+ {
1302
+ userId: 1,
1303
+ roleId: 1,
1304
+ },
1305
+ {
1306
+ userId: 1,
1307
+ roleId: 2,
1308
+ },
1309
+ {
1310
+ userId: 2,
1311
+ roleId: 2,
1312
+ },
1313
+ {
1314
+ userId: 2,
1315
+ roleId: 3,
1316
+ },
1317
+ {
1318
+ userId: 3,
1319
+ roleId: 3,
1320
+ },
1321
+ ],
1322
+ post: [],
1323
+ category: [],
1324
+ }),
1325
+ entities: [
1326
+ User,
1327
+ Post,
1328
+ Category,
1329
+ Role,
1330
+ ],
1331
+ });
1332
+
1333
+ await dataSource.initialize();
1334
+
1335
+ const repository =
1336
+ dataSource.getRepository(User);
1337
+
1338
+ const users =
1339
+ await repository
1340
+ .createQueryBuilder('user')
1341
+ .leftJoinAndSelect(
1342
+ 'user.roles',
1343
+ 'role',
1344
+ )
1345
+ .orderBy(
1346
+ 'user.id',
1347
+ 'ASC',
1348
+ )
1349
+ .addOrderBy(
1350
+ 'role.id',
1351
+ 'ASC',
1352
+ )
1353
+ .skip(1)
1354
+ .take(1)
1355
+ .getMany();
1356
+
1357
+ expect(users).toHaveLength(1);
1358
+
1359
+ expect(users[0]).toMatchObject({
1360
+ id: 2,
1361
+ name: 'Budi',
1362
+ roles: [
1363
+ {
1364
+ id: 2,
1365
+ name: 'Developer',
1366
+ },
1367
+ {
1368
+ id: 3,
1369
+ name: 'Manager',
1370
+ },
1371
+ ],
1372
+ });
1373
+
1374
+ await dataSource.destroy();
1375
+ });
1376
+
1377
+ test('keeps entity when ManyToMany relation is empty', async () =>
1378
+ {
1379
+ const dataSource =
1380
+ createGoogleSheetsDataSource({
1381
+ type: 'google-sheets',
1382
+ client:
1383
+ new Memory({
1384
+ user: [
1385
+ { id: 1, name: 'Basuni' },
1386
+ { id: 2, name: 'Budi' },
1387
+ ],
1388
+ role: [
1389
+ { id: 1, name: 'Admin' },
1390
+ ],
1391
+ user_role: [
1392
+ { userId: 1, roleId: 1 },
1393
+ ],
1394
+ post: [],
1395
+ category: [],
1396
+ }),
1397
+ entities
1398
+ });
1399
+
1400
+ await dataSource.initialize();
1401
+
1402
+ const repository =
1403
+ dataSource.getRepository(User);
1404
+
1405
+ const users =
1406
+ await repository
1407
+ .createQueryBuilder('user')
1408
+ .leftJoinAndSelect(
1409
+ 'user.roles',
1410
+ 'role',
1411
+ )
1412
+ .orderBy(
1413
+ 'user.id',
1414
+ 'ASC',
1415
+ )
1416
+ .getMany();
1417
+
1418
+ expect(users).toHaveLength(2);
1419
+
1420
+ expect(users[0]).toMatchObject({
1421
+ id: 1,
1422
+ name: 'Basuni',
1423
+ roles: [
1424
+ {
1425
+ id: 1,
1426
+ name: 'Admin',
1427
+ },
1428
+ ],
1429
+ });
1430
+
1431
+ expect(users[1]).toMatchObject({
1432
+ id: 2,
1433
+ name: 'Budi',
1434
+ roles: [],
1435
+ });
1436
+
1437
+ await dataSource.destroy();
1438
+ });
1439
+
1440
+ test('filters entities by ManyToMany relation', async () =>
1441
+ {
1442
+ const dataSource =
1443
+ createGoogleSheetsDataSource({
1444
+ type: 'google-sheets',
1445
+ client:
1446
+ new Memory({
1447
+ user: [
1448
+ { id: 1, name: 'Basuni' },
1449
+ { id: 2, name: 'Budi' },
1450
+ { id: 3, name: 'Citra' },
1451
+ ],
1452
+ role: [
1453
+ { id: 1, name: 'Admin' },
1454
+ { id: 2, name: 'Developer' },
1455
+ { id: 3, name: 'Manager' },
1456
+ ],
1457
+ user_role: [
1458
+ { userId: 1, roleId: 1 },
1459
+ { userId: 1, roleId: 2 },
1460
+ { userId: 2, roleId: 2 },
1461
+ { userId: 3, roleId: 3 },
1462
+ ],
1463
+ post: [],
1464
+ category: [],
1465
+ }),
1466
+ entities
1467
+ });
1468
+
1469
+ await dataSource.initialize();
1470
+
1471
+ const repository =
1472
+ dataSource.getRepository(User);
1473
+
1474
+ const users =
1475
+ await repository
1476
+ .createQueryBuilder('user')
1477
+ .leftJoinAndSelect(
1478
+ 'user.roles',
1479
+ 'role',
1480
+ )
1481
+ .where(
1482
+ 'role.name = :name',
1483
+ {
1484
+ name: 'Developer',
1485
+ },
1486
+ )
1487
+ .orderBy(
1488
+ 'user.id',
1489
+ 'ASC',
1490
+ )
1491
+ .getMany();
1492
+
1493
+ expect(users).toHaveLength(2);
1494
+
1495
+ expect(users[0]).toMatchObject({
1496
+ id: 1,
1497
+ name: 'Basuni',
1498
+ roles: [
1499
+ {
1500
+ id: 2,
1501
+ name: 'Developer',
1502
+ },
1503
+ ],
1504
+ });
1505
+
1506
+ expect(users[1]).toMatchObject({
1507
+ id: 2,
1508
+ name: 'Budi',
1509
+ roles: [
1510
+ {
1511
+ id: 2,
1512
+ name: 'Developer',
1513
+ },
1514
+ ],
1515
+ });
1516
+
1517
+ await dataSource.destroy();
1518
+ });
1519
+
1520
+ test('filters ManyToMany relation without unrelated relations', async () =>
1521
+ {
1522
+ const dataSource =
1523
+ createGoogleSheetsDataSource({
1524
+ type: 'google-sheets',
1525
+ client:
1526
+ new Memory({
1527
+ user: [
1528
+ { id: 1, name: 'Basuni' },
1529
+ { id: 2, name: 'Budi' },
1530
+ { id: 3, name: 'Citra' },
1531
+ ],
1532
+ role: [
1533
+ { id: 1, name: 'Admin' },
1534
+ { id: 2, name: 'Developer' },
1535
+ { id: 3, name: 'Manager' },
1536
+ ],
1537
+ user_role: [
1538
+ { userId: 1, roleId: 1 },
1539
+ { userId: 1, roleId: 2 },
1540
+ { userId: 2, roleId: 2 },
1541
+ { userId: 3, roleId: 3 },
1542
+ ],
1543
+ post: [],
1544
+ category: [],
1545
+ }),
1546
+ entities: [
1547
+ User,
1548
+ Post,
1549
+ Category,
1550
+ Role,
1551
+ ],
1552
+ });
1553
+
1554
+ await dataSource.initialize();
1555
+
1556
+ const repository =
1557
+ dataSource.getRepository(User);
1558
+
1559
+ const users =
1560
+ await repository
1561
+ .createQueryBuilder('user')
1562
+ .leftJoinAndSelect(
1563
+ 'user.roles',
1564
+ 'role',
1565
+ )
1566
+ .where(
1567
+ 'role.name = :name',
1568
+ {
1569
+ name: 'Developer',
1570
+ },
1571
+ )
1572
+ .orderBy(
1573
+ 'user.id',
1574
+ 'ASC',
1575
+ )
1576
+ .getMany();
1577
+
1578
+ expect(users).toHaveLength(2);
1579
+
1580
+ expect(users[0]).toMatchObject({
1581
+ id: 1,
1582
+ name: 'Basuni',
1583
+ roles: [
1584
+ {
1585
+ id: 2,
1586
+ name: 'Developer',
1587
+ },
1588
+ ],
1589
+ });
1590
+
1591
+ expect(users[1]).toMatchObject({
1592
+ id: 2,
1593
+ name: 'Budi',
1594
+ roles: [
1595
+ {
1596
+ id: 2,
1597
+ name: 'Developer',
1598
+ },
1599
+ ],
1600
+ });
1601
+
1602
+ expect(
1603
+ users[0]!.roles,
1604
+ ).not.toContainEqual({
1605
+ id: 1,
1606
+ name: 'Admin',
1607
+ });
1608
+
1609
+ await dataSource.destroy();
1610
+ });
1611
+ });