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,649 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import
3
+ {
4
+ Entity,
5
+ PrimaryGeneratedColumn,
6
+ Column,
7
+ DeleteDateColumn,
8
+ ManyToOne,
9
+ OneToMany,
10
+ JoinColumn,
11
+ } from 'typeorm';
12
+ import
13
+ {
14
+ createGoogleSheetsDataSource,
15
+ type GoogleSheetsRow,
16
+ } from '../../src';
17
+ import { Memory } from '../../src/core/memory';
18
+
19
+ describe('GoogleSheetsDeleteOprations', () =>
20
+ {
21
+ @Entity('soft_delete_users')
22
+ class SoftDeleteUser
23
+ {
24
+ @PrimaryGeneratedColumn()
25
+ id!: number;
26
+
27
+ @Column()
28
+ name!: string;
29
+
30
+ @DeleteDateColumn({
31
+ nullable: true,
32
+ })
33
+ deletedAt!: Date | null;
34
+
35
+ @OneToMany(
36
+ () => SoftDeletePost,
37
+ (post) => post.user,
38
+ )
39
+ posts!: SoftDeletePost[];
40
+ }
41
+
42
+ @Entity('soft_delete_posts')
43
+ class SoftDeletePost
44
+ {
45
+ @PrimaryGeneratedColumn()
46
+ id!: number;
47
+
48
+ @Column()
49
+ title!: string;
50
+
51
+ @Column()
52
+ userId!: number;
53
+
54
+ @DeleteDateColumn({
55
+ nullable: true,
56
+ })
57
+ deletedAt!: Date | null;
58
+
59
+ @ManyToOne(
60
+ () => SoftDeleteUser,
61
+ (user) => user.posts,
62
+ )
63
+ @JoinColumn({
64
+ name: 'userId',
65
+ })
66
+ user!: SoftDeleteUser;
67
+ }
68
+
69
+ async function createDataSource(
70
+ rows: GoogleSheetsRow[] = [],
71
+ postRows: GoogleSheetsRow[] = [],
72
+ )
73
+ {
74
+ const client =
75
+ new Memory({
76
+ soft_delete_users: rows,
77
+ soft_delete_posts: postRows,
78
+ });
79
+
80
+ await client.insertHeaders(
81
+ 'soft_delete_users',
82
+ [
83
+ 'id',
84
+ 'name',
85
+ 'deletedAt',
86
+ ],
87
+ );
88
+
89
+ await client.insertHeaders(
90
+ 'soft_delete_posts',
91
+ [
92
+ 'id',
93
+ 'title',
94
+ 'userId',
95
+ 'deletedAt',
96
+ ],
97
+ );
98
+
99
+ const dataSource =
100
+ createGoogleSheetsDataSource({
101
+ type: 'google-sheets',
102
+
103
+ client,
104
+
105
+ entities: [
106
+ SoftDeleteUser,
107
+ SoftDeletePost,
108
+ ],
109
+ });
110
+
111
+ await dataSource.initialize();
112
+
113
+ return {
114
+ client,
115
+ dataSource,
116
+ };
117
+ }
118
+
119
+ test(
120
+ 'should support DeleteDateColumn',
121
+ async () =>
122
+ {
123
+ const {
124
+ client,
125
+ dataSource,
126
+ } =
127
+ await createDataSource([
128
+ {
129
+ id: 1,
130
+ name: 'John',
131
+ deletedAt: null,
132
+ },
133
+ ]);
134
+
135
+ try
136
+ {
137
+ const repository =
138
+ dataSource.getRepository(
139
+ SoftDeleteUser,
140
+ );
141
+
142
+ const user =
143
+ await repository.findOneBy({
144
+ id: 1,
145
+ });
146
+
147
+ expect(user)
148
+ .toBeDefined();
149
+
150
+ expect(user!.deletedAt)
151
+ .toBeNull();
152
+
153
+ const rows =
154
+ await client.getRows(
155
+ 'soft_delete_users',
156
+ );
157
+
158
+ expect(rows)
159
+ .toEqual([
160
+ {
161
+ id: 1,
162
+ name: 'John',
163
+ deletedAt: null,
164
+ },
165
+ ]);
166
+ }
167
+ finally
168
+ {
169
+ if (dataSource.isInitialized)
170
+ {
171
+ await dataSource.destroy();
172
+ }
173
+ }
174
+ },
175
+ );
176
+
177
+ test(
178
+ 'should soft delete entity',
179
+ async () =>
180
+ {
181
+ const {
182
+ client,
183
+ dataSource,
184
+ } =
185
+ await createDataSource([
186
+ {
187
+ id: 1,
188
+ name: 'John',
189
+ deletedAt: null,
190
+ },
191
+ ]);
192
+
193
+ try
194
+ {
195
+ const repository =
196
+ dataSource.getRepository(
197
+ SoftDeleteUser,
198
+ );
199
+
200
+ const result =
201
+ await repository.softDelete(
202
+ 1,
203
+ );
204
+
205
+ expect(result.affected)
206
+ .toBe(1);
207
+
208
+ const rows =
209
+ await client.getRows(
210
+ 'soft_delete_users',
211
+ );
212
+
213
+ expect(rows[0]!.deletedAt)
214
+ .toBeInstanceOf(Date);
215
+ }
216
+ finally
217
+ {
218
+ if (dataSource.isInitialized)
219
+ {
220
+ await dataSource.destroy();
221
+ }
222
+ }
223
+ },
224
+ );
225
+
226
+ test(
227
+ 'should restore soft deleted entity',
228
+ async () =>
229
+ {
230
+ const {
231
+ client,
232
+ dataSource,
233
+ } =
234
+ await createDataSource([
235
+ {
236
+ id: 1,
237
+ name: 'John',
238
+ deletedAt: new Date(),
239
+ },
240
+ ]);
241
+
242
+ try
243
+ {
244
+ const repository =
245
+ dataSource.getRepository(
246
+ SoftDeleteUser,
247
+ );
248
+
249
+ const result =
250
+ await repository.restore(
251
+ 1,
252
+ );
253
+
254
+ expect(result.affected)
255
+ .toBe(1);
256
+
257
+ const rows =
258
+ await client.getRows(
259
+ 'soft_delete_users',
260
+ );
261
+
262
+ expect(rows[0]!.deletedAt)
263
+ .toBeNull();
264
+ }
265
+ finally
266
+ {
267
+ if (dataSource.isInitialized)
268
+ {
269
+ await dataSource.destroy();
270
+ }
271
+ }
272
+ },
273
+ );
274
+
275
+ test(
276
+ 'should exclude soft deleted entity from normal find',
277
+ async () =>
278
+ {
279
+ const {
280
+ dataSource,
281
+ } =
282
+ await createDataSource([
283
+ {
284
+ id: 1,
285
+ name: 'Deleted User',
286
+ deletedAt: new Date(),
287
+ },
288
+ {
289
+ id: 2,
290
+ name: 'Active User',
291
+ deletedAt: null,
292
+ },
293
+ ]);
294
+
295
+ try
296
+ {
297
+ const repository =
298
+ dataSource.getRepository(
299
+ SoftDeleteUser,
300
+ );
301
+
302
+ const users =
303
+ await repository.find();
304
+
305
+ expect(users)
306
+ .toHaveLength(1);
307
+
308
+ expect(users[0]!.id)
309
+ .toBe(2);
310
+
311
+ expect(users[0]!.name)
312
+ .toBe('Active User');
313
+ }
314
+ finally
315
+ {
316
+ if (dataSource.isInitialized)
317
+ {
318
+ await dataSource.destroy();
319
+ }
320
+ }
321
+ },
322
+ );
323
+
324
+ test(
325
+ 'should include soft deleted entity with withDeleted',
326
+ async () =>
327
+ {
328
+ const {
329
+ dataSource,
330
+ } =
331
+ await createDataSource([
332
+ {
333
+ id: 1,
334
+ name: 'Deleted User',
335
+ deletedAt: new Date(),
336
+ },
337
+ {
338
+ id: 2,
339
+ name: 'Active User',
340
+ deletedAt: null,
341
+ },
342
+ ]);
343
+
344
+ try
345
+ {
346
+ const repository =
347
+ dataSource.getRepository(
348
+ SoftDeleteUser,
349
+ );
350
+
351
+ const users =
352
+ await repository.find({
353
+ withDeleted: true,
354
+ });
355
+
356
+ expect(users)
357
+ .toHaveLength(2);
358
+
359
+ expect(
360
+ users.some(
361
+ (item) => item.id === 1,
362
+ ),
363
+ )
364
+ .toBe(true);
365
+
366
+ expect(
367
+ users.some(
368
+ (item) => item.id === 2,
369
+ ),
370
+ )
371
+ .toBe(true);
372
+ }
373
+ finally
374
+ {
375
+ if (dataSource.isInitialized)
376
+ {
377
+ await dataSource.destroy();
378
+ }
379
+ }
380
+ },
381
+ );
382
+
383
+ test(
384
+ 'should soft remove entity',
385
+ async () =>
386
+ {
387
+ const {
388
+ dataSource,
389
+ } =
390
+ await createDataSource([
391
+ {
392
+ id: 1,
393
+ name: 'John',
394
+ deletedAt: null,
395
+ },
396
+ ]);
397
+
398
+ try
399
+ {
400
+ const repository =
401
+ dataSource.getRepository(
402
+ SoftDeleteUser,
403
+ );
404
+
405
+ const user =
406
+ await repository.findOneBy({
407
+ id: 1,
408
+ });
409
+
410
+ expect(user)
411
+ .toBeDefined();
412
+
413
+ await repository.softRemove(
414
+ user!,
415
+ );
416
+
417
+ const found =
418
+ await repository.findOne({
419
+ where: {
420
+ id: 1,
421
+ },
422
+ });
423
+
424
+ expect(found)
425
+ .toBeNull();
426
+
427
+ const deleted =
428
+ await repository.findOne({
429
+ where: {
430
+ id: 1,
431
+ },
432
+ withDeleted: true,
433
+ });
434
+
435
+ expect(deleted)
436
+ .not
437
+ .toBeNull();
438
+
439
+ expect(deleted!.deletedAt)
440
+ .toBeInstanceOf(Date);
441
+ }
442
+ finally
443
+ {
444
+ if (dataSource.isInitialized)
445
+ {
446
+ await dataSource.destroy();
447
+ }
448
+ }
449
+ },
450
+ );
451
+
452
+ test(
453
+ 'should exclude soft deleted relations',
454
+ async () =>
455
+ {
456
+ const {
457
+ dataSource,
458
+ } =
459
+ await createDataSource(
460
+ [
461
+ {
462
+ id: 1,
463
+ name: 'John',
464
+ deletedAt: null,
465
+ },
466
+ ],
467
+ [
468
+ {
469
+ id: 1,
470
+ title: 'Active Post',
471
+ userId: 1,
472
+ deletedAt: null,
473
+ },
474
+ {
475
+ id: 2,
476
+ title: 'Deleted Post',
477
+ userId: 1,
478
+ deletedAt: new Date(),
479
+ },
480
+ ],
481
+ );
482
+
483
+ try
484
+ {
485
+ const repository =
486
+ dataSource.getRepository(
487
+ SoftDeleteUser,
488
+ );
489
+
490
+ const user =
491
+ await repository.findOne({
492
+ where: {
493
+ id: 1,
494
+ },
495
+ relations: {
496
+ posts: true,
497
+ },
498
+ });
499
+
500
+ expect(user)
501
+ .toBeDefined();
502
+
503
+ expect(user!.posts)
504
+ .toHaveLength(1);
505
+
506
+ expect(user!.posts[0]!.id)
507
+ .toBe(1);
508
+
509
+ expect(user!.posts[0]!.title)
510
+ .toBe('Active Post');
511
+ }
512
+ finally
513
+ {
514
+ if (dataSource.isInitialized)
515
+ {
516
+ await dataSource.destroy();
517
+ }
518
+ }
519
+ },
520
+ );
521
+
522
+ test(
523
+ 'should include soft deleted relations with withDeleted',
524
+ async () =>
525
+ {
526
+ const {
527
+ dataSource,
528
+ } =
529
+ await createDataSource(
530
+ [
531
+ {
532
+ id: 1,
533
+ name: 'John',
534
+ deletedAt: null,
535
+ },
536
+ ],
537
+ [
538
+ {
539
+ id: 1,
540
+ title: 'Active Post',
541
+ userId: 1,
542
+ deletedAt: null,
543
+ },
544
+ {
545
+ id: 2,
546
+ title: 'Deleted Post',
547
+ userId: 1,
548
+ deletedAt: new Date(),
549
+ },
550
+ ],
551
+ );
552
+
553
+ try
554
+ {
555
+ const repository =
556
+ dataSource.getRepository(
557
+ SoftDeleteUser,
558
+ );
559
+
560
+ const user =
561
+ await repository.findOne({
562
+ where: {
563
+ id: 1,
564
+ },
565
+ relations: {
566
+ posts: true,
567
+ },
568
+ withDeleted: true,
569
+ });
570
+
571
+ expect(user)
572
+ .toBeDefined();
573
+
574
+ expect(user!.posts)
575
+ .toHaveLength(2);
576
+
577
+ expect(
578
+ user!.posts.some(
579
+ (post) => post.id === 1,
580
+ ),
581
+ )
582
+ .toBe(true);
583
+
584
+ expect(
585
+ user!.posts.some(
586
+ (post) => post.id === 2,
587
+ ),
588
+ )
589
+ .toBe(true);
590
+ }
591
+ finally
592
+ {
593
+ if (dataSource.isInitialized)
594
+ {
595
+ await dataSource.destroy();
596
+ }
597
+ }
598
+ },
599
+ );
600
+
601
+ test(
602
+ 'should hard delete entity',
603
+ async () =>
604
+ {
605
+ const {
606
+ client,
607
+ dataSource,
608
+ } =
609
+ await createDataSource([
610
+ {
611
+ id: 1,
612
+ name: 'John',
613
+ deletedAt: new Date(),
614
+ },
615
+ ]);
616
+
617
+ try
618
+ {
619
+ const repository =
620
+ dataSource.getRepository(
621
+ SoftDeleteUser,
622
+ );
623
+
624
+ const result =
625
+ await repository.delete(
626
+ 1,
627
+ );
628
+
629
+ expect(result.affected)
630
+ .toBe(1);
631
+
632
+ const rows =
633
+ await client.getRows(
634
+ 'soft_delete_users',
635
+ );
636
+
637
+ expect(rows)
638
+ .toEqual([]);
639
+ }
640
+ finally
641
+ {
642
+ if (dataSource.isInitialized)
643
+ {
644
+ await dataSource.destroy();
645
+ }
646
+ }
647
+ },
648
+ );
649
+ });