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,952 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import
3
+ {
4
+ Column,
5
+ Entity,
6
+ PrimaryGeneratedColumn,
7
+ } from 'typeorm';
8
+ import { Memory } from '../../src/core/memory';
9
+ import { createGoogleSheetsDataSource, type GoogleSheetsRow } from '../../src';
10
+
11
+ describe(
12
+ 'CRUD Compatibility',
13
+ () =>
14
+ {
15
+ @Entity('crud_users')
16
+ class CrudUser
17
+ {
18
+ @PrimaryGeneratedColumn()
19
+ id!: number;
20
+
21
+ @Column()
22
+ name!: string;
23
+
24
+ @Column()
25
+ email!: string;
26
+ }
27
+
28
+ async function createDataSource(
29
+ rows: GoogleSheetsRow[] = [],
30
+ )
31
+ {
32
+ const client =
33
+ new Memory({
34
+ crud_users: rows,
35
+ });
36
+
37
+ await client.insertHeaders(
38
+ 'crud_users',
39
+ [
40
+ 'id',
41
+ 'name',
42
+ 'email',
43
+ ],
44
+ );
45
+
46
+ const dataSource =
47
+ createGoogleSheetsDataSource({
48
+ type: 'google-sheets',
49
+
50
+ client,
51
+
52
+ entities: [
53
+ CrudUser,
54
+ ],
55
+ });
56
+
57
+ await dataSource.initialize();
58
+
59
+ return {
60
+ client,
61
+ dataSource,
62
+ };
63
+ }
64
+
65
+ test(
66
+ 'should save entity',
67
+ async () =>
68
+ {
69
+ const {
70
+ client,
71
+ dataSource,
72
+ } =
73
+ await createDataSource();
74
+
75
+ try
76
+ {
77
+ const repository =
78
+ dataSource.getRepository(
79
+ CrudUser,
80
+ );
81
+
82
+ const user =
83
+ await repository.save({
84
+ name: 'John',
85
+ email: 'john@example.com',
86
+ });
87
+
88
+ expect(user)
89
+ .toBeDefined();
90
+
91
+ expect(user.name)
92
+ .toBe('John');
93
+
94
+ expect(user.email)
95
+ .toBe('john@example.com');
96
+
97
+ const rows =
98
+ await client.getRows(
99
+ 'crud_users',
100
+ );
101
+
102
+ expect(rows)
103
+ .toHaveLength(1);
104
+
105
+ expect(rows[0])
106
+ .toMatchObject({
107
+ name: 'John',
108
+ email: 'john@example.com',
109
+ });
110
+ }
111
+ finally
112
+ {
113
+ if (dataSource.isInitialized)
114
+ {
115
+ await dataSource.destroy();
116
+ }
117
+ }
118
+ },
119
+ );
120
+
121
+ test(
122
+ 'should update entity',
123
+ async () =>
124
+ {
125
+ const {
126
+ client,
127
+ dataSource,
128
+ } =
129
+ await createDataSource([
130
+ {
131
+ id: 1,
132
+ name: 'John',
133
+ email: 'john@example.com',
134
+ },
135
+ ]);
136
+
137
+ try
138
+ {
139
+ const repository =
140
+ dataSource.getRepository(
141
+ CrudUser,
142
+ );
143
+
144
+ const user =
145
+ await repository.findOneBy({
146
+ id: 1,
147
+ });
148
+
149
+ expect(user)
150
+ .toBeDefined();
151
+
152
+ user!.name =
153
+ 'John Updated';
154
+
155
+ await repository.save(
156
+ user!,
157
+ );
158
+
159
+ const rows =
160
+ await client.getRows(
161
+ 'crud_users',
162
+ );
163
+
164
+ expect(rows)
165
+ .toEqual([
166
+ {
167
+ id: 1,
168
+ name: 'John Updated',
169
+ email: 'john@example.com',
170
+ },
171
+ ]);
172
+ }
173
+ finally
174
+ {
175
+ if (dataSource.isInitialized)
176
+ {
177
+ await dataSource.destroy();
178
+ }
179
+ }
180
+ },
181
+ );
182
+
183
+ test(
184
+ 'should delete entity',
185
+ async () =>
186
+ {
187
+ const {
188
+ client,
189
+ dataSource,
190
+ } =
191
+ await createDataSource([
192
+ {
193
+ id: 1,
194
+ name: 'John',
195
+ email: 'john@example.com',
196
+ },
197
+ {
198
+ id: 2,
199
+ name: 'Jane',
200
+ email: 'jane@example.com',
201
+ },
202
+ ]);
203
+
204
+ try
205
+ {
206
+ const repository =
207
+ dataSource.getRepository(
208
+ CrudUser,
209
+ );
210
+
211
+ const user =
212
+ await repository.findOneBy({
213
+ id: 1,
214
+ });
215
+
216
+ expect(user)
217
+ .toBeDefined();
218
+
219
+ await repository.remove(
220
+ user!,
221
+ );
222
+
223
+ const rows =
224
+ await client.getRows(
225
+ 'crud_users',
226
+ );
227
+
228
+ expect(rows)
229
+ .toEqual([
230
+ {
231
+ id: 2,
232
+ name: 'Jane',
233
+ email: 'jane@example.com',
234
+ },
235
+ ]);
236
+ }
237
+ finally
238
+ {
239
+ if (dataSource.isInitialized)
240
+ {
241
+ await dataSource.destroy();
242
+ }
243
+ }
244
+ },
245
+ );
246
+
247
+ test(
248
+ 'should insert entity',
249
+ async () =>
250
+ {
251
+ const {
252
+ client,
253
+ dataSource,
254
+ } =
255
+ await createDataSource();
256
+
257
+ try
258
+ {
259
+ const repository =
260
+ dataSource.getRepository(
261
+ CrudUser,
262
+ );
263
+
264
+ const result =
265
+ await repository.insert({
266
+ id: 1,
267
+ name: 'John',
268
+ email: 'john@example.com',
269
+ });
270
+
271
+ expect(result)
272
+ .toBeDefined();
273
+
274
+ const rows =
275
+ await client.getRows(
276
+ 'crud_users',
277
+ );
278
+
279
+ expect(rows)
280
+ .toEqual([
281
+ {
282
+ id: 1,
283
+ name: 'John',
284
+ email: 'john@example.com',
285
+ },
286
+ ]);
287
+ }
288
+ finally
289
+ {
290
+ if (dataSource.isInitialized)
291
+ {
292
+ await dataSource.destroy();
293
+ }
294
+ }
295
+ },
296
+ );
297
+
298
+ test(
299
+ 'should remove entity',
300
+ async () =>
301
+ {
302
+ const {
303
+ client,
304
+ dataSource,
305
+ } =
306
+ await createDataSource([
307
+ {
308
+ id: 1,
309
+ name: 'John',
310
+ email: 'john@example.com',
311
+ },
312
+ {
313
+ id: 2,
314
+ name: 'Jane',
315
+ email: 'jane@example.com',
316
+ },
317
+ ]);
318
+
319
+ try
320
+ {
321
+ const repository =
322
+ dataSource.getRepository(
323
+ CrudUser,
324
+ );
325
+
326
+ const result =
327
+ await repository.delete({
328
+ id: 1,
329
+ });
330
+
331
+ expect(result.affected)
332
+ .toBe(1);
333
+
334
+ const rows =
335
+ await client.getRows(
336
+ 'crud_users',
337
+ );
338
+
339
+ expect(rows)
340
+ .toEqual([
341
+ {
342
+ id: 2,
343
+ name: 'Jane',
344
+ email: 'jane@example.com',
345
+ },
346
+ ]);
347
+ }
348
+ finally
349
+ {
350
+ if (dataSource.isInitialized)
351
+ {
352
+ await dataSource.destroy();
353
+ }
354
+ }
355
+ },
356
+ );
357
+
358
+ test(
359
+ 'should bulk insert entities',
360
+ async () =>
361
+ {
362
+ const {
363
+ client,
364
+ dataSource,
365
+ } =
366
+ await createDataSource();
367
+
368
+ try
369
+ {
370
+ const repository =
371
+ dataSource.getRepository(
372
+ CrudUser,
373
+ );
374
+
375
+ const result =
376
+ await repository.insert([
377
+ {
378
+ id: 1,
379
+ name: 'John',
380
+ email: 'john@example.com',
381
+ },
382
+ {
383
+ id: 2,
384
+ name: 'Jane',
385
+ email: 'jane@example.com',
386
+ },
387
+ {
388
+ id: 3,
389
+ name: 'Bob',
390
+ email: 'bob@example.com',
391
+ },
392
+ ]);
393
+
394
+ expect(result)
395
+ .toBeDefined();
396
+
397
+ const rows =
398
+ await client.getRows(
399
+ 'crud_users',
400
+ );
401
+
402
+ expect(rows)
403
+ .toEqual([
404
+ {
405
+ id: 1,
406
+ name: 'John',
407
+ email: 'john@example.com',
408
+ },
409
+ {
410
+ id: 2,
411
+ name: 'Jane',
412
+ email: 'jane@example.com',
413
+ },
414
+ {
415
+ id: 3,
416
+ name: 'Bob',
417
+ email: 'bob@example.com',
418
+ },
419
+ ]);
420
+ }
421
+ finally
422
+ {
423
+ if (dataSource.isInitialized)
424
+ {
425
+ await dataSource.destroy();
426
+ }
427
+ }
428
+ },
429
+ );
430
+
431
+ test(
432
+ 'should bulk update entities',
433
+ async () =>
434
+ {
435
+ const {
436
+ client,
437
+ dataSource,
438
+ } =
439
+ await createDataSource([
440
+ {
441
+ id: 1,
442
+ name: 'John',
443
+ email: 'john@example.com',
444
+ },
445
+ {
446
+ id: 2,
447
+ name: 'Jane',
448
+ email: 'jane@example.com',
449
+ },
450
+ {
451
+ id: 3,
452
+ name: 'Bob',
453
+ email: 'bob@example.com',
454
+ },
455
+ ]);
456
+
457
+ try
458
+ {
459
+ const repository =
460
+ dataSource.getRepository(
461
+ CrudUser,
462
+ );
463
+
464
+ const result =
465
+ await repository.update(
466
+ {
467
+ id: 1,
468
+ },
469
+ {
470
+ name: 'John Updated',
471
+ },
472
+ );
473
+
474
+ expect(result.affected)
475
+ .toBe(1);
476
+
477
+ const rows =
478
+ await client.getRows(
479
+ 'crud_users',
480
+ );
481
+
482
+ expect(rows)
483
+ .toEqual([
484
+ {
485
+ id: 1,
486
+ name: 'John Updated',
487
+ email: 'john@example.com',
488
+ },
489
+ {
490
+ id: 2,
491
+ name: 'Jane',
492
+ email: 'jane@example.com',
493
+ },
494
+ {
495
+ id: 3,
496
+ name: 'Bob',
497
+ email: 'bob@example.com',
498
+ },
499
+ ]);
500
+ }
501
+ finally
502
+ {
503
+ if (dataSource.isInitialized)
504
+ {
505
+ await dataSource.destroy();
506
+ }
507
+ }
508
+ },
509
+ );
510
+
511
+ test(
512
+ 'should bulk delete entities',
513
+ async () =>
514
+ {
515
+ const {
516
+ client,
517
+ dataSource,
518
+ } =
519
+ await createDataSource([
520
+ {
521
+ id: 1,
522
+ name: 'John',
523
+ email: 'john@example.com',
524
+ },
525
+ {
526
+ id: 2,
527
+ name: 'Jane',
528
+ email: 'jane@example.com',
529
+ },
530
+ {
531
+ id: 3,
532
+ name: 'Bob',
533
+ email: 'bob@example.com',
534
+ },
535
+ ]);
536
+
537
+ try
538
+ {
539
+ const repository =
540
+ dataSource.getRepository(
541
+ CrudUser,
542
+ );
543
+
544
+ const result =
545
+ await repository.delete([
546
+ {
547
+ id: 1,
548
+ },
549
+ {
550
+ id: 2,
551
+ },
552
+ ]);
553
+
554
+ expect(result.affected)
555
+ .toBe(2);
556
+
557
+ const rows =
558
+ await client.getRows(
559
+ 'crud_users',
560
+ );
561
+
562
+ expect(rows)
563
+ .toEqual([
564
+ {
565
+ id: 3,
566
+ name: 'Bob',
567
+ email: 'bob@example.com',
568
+ },
569
+ ]);
570
+ }
571
+ finally
572
+ {
573
+ if (dataSource.isInitialized)
574
+ {
575
+ await dataSource.destroy();
576
+ }
577
+ }
578
+ },
579
+ );
580
+
581
+ test(
582
+ 'should find entities',
583
+ async () =>
584
+ {
585
+ const {
586
+ dataSource,
587
+ } =
588
+ await createDataSource([
589
+ {
590
+ id: 1,
591
+ name: 'John',
592
+ email: 'john@example.com',
593
+ },
594
+ {
595
+ id: 2,
596
+ name: 'Jane',
597
+ email: 'jane@example.com',
598
+ },
599
+ ]);
600
+
601
+ try
602
+ {
603
+ const repository =
604
+ dataSource.getRepository(
605
+ CrudUser,
606
+ );
607
+
608
+ const users =
609
+ await repository.find();
610
+
611
+ expect(users)
612
+ .toHaveLength(2);
613
+
614
+ expect(users)
615
+ .toEqual([
616
+ {
617
+ id: 1,
618
+ name: 'John',
619
+ email: 'john@example.com',
620
+ },
621
+ {
622
+ id: 2,
623
+ name: 'Jane',
624
+ email: 'jane@example.com',
625
+ },
626
+ ]);
627
+ }
628
+ finally
629
+ {
630
+ if (dataSource.isInitialized)
631
+ {
632
+ await dataSource.destroy();
633
+ }
634
+ }
635
+ },
636
+ );
637
+
638
+ test(
639
+ 'should find one entity',
640
+ async () =>
641
+ {
642
+ const {
643
+ dataSource,
644
+ } =
645
+ await createDataSource([
646
+ {
647
+ id: 1,
648
+ name: 'John',
649
+ email: 'john@example.com',
650
+ },
651
+ {
652
+ id: 2,
653
+ name: 'Jane',
654
+ email: 'jane@example.com',
655
+ },
656
+ ]);
657
+
658
+ try
659
+ {
660
+ const repository =
661
+ dataSource.getRepository(
662
+ CrudUser,
663
+ );
664
+
665
+ const user =
666
+ await repository.findOneBy({
667
+ id: 2,
668
+ });
669
+
670
+ expect(user)
671
+ .toEqual({
672
+ id: 2,
673
+ name: 'Jane',
674
+ email: 'jane@example.com',
675
+ });
676
+ }
677
+ finally
678
+ {
679
+ if (dataSource.isInitialized)
680
+ {
681
+ await dataSource.destroy();
682
+ }
683
+ }
684
+ },
685
+ );
686
+
687
+ test(
688
+ 'should find entities by criteria',
689
+ async () =>
690
+ {
691
+ const {
692
+ dataSource,
693
+ } =
694
+ await createDataSource([
695
+ {
696
+ id: 1,
697
+ name: 'John',
698
+ email: 'john@example.com',
699
+ },
700
+ {
701
+ id: 2,
702
+ name: 'Jane',
703
+ email: 'jane@example.com',
704
+ },
705
+ {
706
+ id: 3,
707
+ name: 'John',
708
+ email: 'john2@example.com',
709
+ },
710
+ ]);
711
+
712
+ try
713
+ {
714
+ const repository =
715
+ dataSource.getRepository(
716
+ CrudUser,
717
+ );
718
+
719
+ const users =
720
+ await repository.findBy({
721
+ name: 'John',
722
+ });
723
+
724
+ expect(users)
725
+ .toHaveLength(2);
726
+
727
+ expect(users)
728
+ .toEqual([
729
+ {
730
+ id: 1,
731
+ name: 'John',
732
+ email: 'john@example.com',
733
+ },
734
+ {
735
+ id: 3,
736
+ name: 'John',
737
+ email: 'john2@example.com',
738
+ },
739
+ ]);
740
+ }
741
+ finally
742
+ {
743
+ if (dataSource.isInitialized)
744
+ {
745
+ await dataSource.destroy();
746
+ }
747
+ }
748
+ },
749
+ );
750
+
751
+ test(
752
+ 'should find one entity by criteria',
753
+ async () =>
754
+ {
755
+ const {
756
+ dataSource,
757
+ } =
758
+ await createDataSource([
759
+ {
760
+ id: 1,
761
+ name: 'John',
762
+ email: 'john@example.com',
763
+ },
764
+ {
765
+ id: 2,
766
+ name: 'Jane',
767
+ email: 'jane@example.com',
768
+ },
769
+ ]);
770
+
771
+ try
772
+ {
773
+ const repository =
774
+ dataSource.getRepository(
775
+ CrudUser,
776
+ );
777
+
778
+ const user =
779
+ await repository.findOneBy({
780
+ email: 'jane@example.com',
781
+ });
782
+
783
+ expect(user)
784
+ .toEqual({
785
+ id: 2,
786
+ name: 'Jane',
787
+ email: 'jane@example.com',
788
+ });
789
+ }
790
+ finally
791
+ {
792
+ if (dataSource.isInitialized)
793
+ {
794
+ await dataSource.destroy();
795
+ }
796
+ }
797
+ },
798
+ );
799
+
800
+ test(
801
+ 'should find and count entities',
802
+ async () =>
803
+ {
804
+ const {
805
+ dataSource,
806
+ } =
807
+ await createDataSource([
808
+ {
809
+ id: 1,
810
+ name: 'John',
811
+ email: 'john@example.com',
812
+ },
813
+ {
814
+ id: 2,
815
+ name: 'Jane',
816
+ email: 'jane@example.com',
817
+ },
818
+ {
819
+ id: 3,
820
+ name: 'Bob',
821
+ email: 'bob@example.com',
822
+ },
823
+ ]);
824
+
825
+ try
826
+ {
827
+ const repository =
828
+ dataSource.getRepository(
829
+ CrudUser,
830
+ );
831
+
832
+ const [
833
+ users,
834
+ count,
835
+ ] =
836
+ await repository.findAndCount();
837
+
838
+ expect(users)
839
+ .toHaveLength(3);
840
+
841
+ expect(count)
842
+ .toBe(3);
843
+ }
844
+ finally
845
+ {
846
+ if (dataSource.isInitialized)
847
+ {
848
+ await dataSource.destroy();
849
+ }
850
+ }
851
+ },
852
+ );
853
+
854
+ test(
855
+ 'should count entities',
856
+ async () =>
857
+ {
858
+ const {
859
+ dataSource,
860
+ } =
861
+ await createDataSource([
862
+ {
863
+ id: 1,
864
+ name: 'John',
865
+ email: 'john@example.com',
866
+ },
867
+ {
868
+ id: 2,
869
+ name: 'Jane',
870
+ email: 'jane@example.com',
871
+ },
872
+ {
873
+ id: 3,
874
+ name: 'Bob',
875
+ email: 'bob@example.com',
876
+ },
877
+ ]);
878
+
879
+ try
880
+ {
881
+ const repository =
882
+ dataSource.getRepository(
883
+ CrudUser,
884
+ );
885
+
886
+ const count =
887
+ await repository.count();
888
+
889
+ expect(count)
890
+ .toBe(3);
891
+ }
892
+ finally
893
+ {
894
+ if (dataSource.isInitialized)
895
+ {
896
+ await dataSource.destroy();
897
+ }
898
+ }
899
+ },
900
+ );
901
+
902
+ test(
903
+ 'should count entities by criteria',
904
+ async () =>
905
+ {
906
+ const {
907
+ dataSource,
908
+ } =
909
+ await createDataSource([
910
+ {
911
+ id: 1,
912
+ name: 'John',
913
+ email: 'john@example.com',
914
+ },
915
+ {
916
+ id: 2,
917
+ name: 'Jane',
918
+ email: 'jane@example.com',
919
+ },
920
+ {
921
+ id: 3,
922
+ name: 'John',
923
+ email: 'john2@example.com',
924
+ },
925
+ ]);
926
+
927
+ try
928
+ {
929
+ const repository =
930
+ dataSource.getRepository(
931
+ CrudUser,
932
+ );
933
+
934
+ const count =
935
+ await repository.countBy({
936
+ name: 'John',
937
+ });
938
+
939
+ expect(count)
940
+ .toBe(2);
941
+ }
942
+ finally
943
+ {
944
+ if (dataSource.isInitialized)
945
+ {
946
+ await dataSource.destroy();
947
+ }
948
+ }
949
+ },
950
+ );
951
+ },
952
+ );