electrodb 1.4.0 → 1.4.4

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.
package/notes CHANGED
@@ -1,31 +1,937 @@
1
- x validate field names work with complex types
2
- - validate that validation works with complex types
3
- - validate enum works with complex types?
4
- x build out response types for at least partial and null
5
-
6
- For Docs
7
- x enum strings must be marked `as const`
8
- x set properties on attributes must be typed with a `?:`
9
- - hidden attribute property disables `get`
10
- x update methods examples have changed
11
- x sets leverage arrays
12
- x default is applied before `required`
13
- x required cannot be removed
14
- - removing `cast` from documentation
15
-
16
- Needs implementing:
17
- x add cast: "list", "set" (maybe dont)
18
- x find usages of `.name` in errors etc and replace with `.path`
19
- x sets only allowed if client is present
20
- x No "removing" required attribute?
21
- x No "never" objects in update
22
- x does dynamodb accept empty objects/~arrays~/sets? if so, reconsider the behaviour to only call getters/setters if object/array not empty
23
- x Use client to make sets if it is passed!
24
-
25
- Questions
26
- x What happens when you have a required attribute, but it has a default, and you try to put
27
- x Is getReadOnly property only grabbing from top level?
28
- Thoughts
29
- x revisit thoughts on required, but with default, put implementation
1
+ import { Entity, Service, CollectionItem } from "electrodb";
30
2
 
3
+ const StatusTypes = ["Open", "Closed", ""] as const;
4
+ const IsNotTicket = "";
5
+ const NotYetViewed = "#";
6
+ const IssueTicket = "Issue";
7
+ const PullRequestTicket = "PullRequest";
8
+ const TicketTypes = [IssueTicket, PullRequestTicket, IsNotTicket] as const
31
9
 
10
+ type Status = typeof StatusTypes[number];
11
+
12
+ function toStatusCode(status: unknown): Status | undefined {
13
+ for (let index in StatusTypes) {
14
+ if (StatusTypes[index] === status) {
15
+ return index as Status;
16
+ }
17
+ }
18
+ return undefined;
19
+ }
20
+
21
+ function toStatusString(code: unknown): Status | undefined {
22
+ for (let index in StatusTypes) {
23
+ if (index === code) {
24
+ return StatusTypes[index];
25
+ }
26
+ }
27
+ return undefined;
28
+ }
29
+
30
+
31
+ const issues = new Entity({
32
+ model: {
33
+ entity: "issues",
34
+ service: "versioncontrol",
35
+ version: "1"
36
+ },
37
+ attributes: {
38
+ issueNumber: {
39
+ type: "string",
40
+ },
41
+ repoName: {
42
+ type: "string"
43
+ },
44
+ repoOwner: {
45
+ type: "string"
46
+ },
47
+ username: {
48
+ type: "string",
49
+ },
50
+ ticketType: {
51
+ type: TicketTypes,
52
+ set: () => IssueTicket,
53
+ readOnly: true
54
+ },
55
+ ticketNumber: {
56
+ type: "string",
57
+ readOnly: true,
58
+ watch: ["issueNumber"],
59
+ set: (_, {issueNumber}) => issueNumber
60
+ },
61
+ status: {
62
+ type: StatusTypes,
63
+ default: "Open",
64
+ set: (val) => toStatusCode(val),
65
+ get: (val) => toStatusString(val),
66
+ },
67
+ subject: {
68
+ type: "string"
69
+ },
70
+ body: {
71
+ type: "string"
72
+ },
73
+ createdAt: {
74
+ type: "string",
75
+ set: () => new Date().toISOString().split('T')[0],
76
+ readOnly: true,
77
+ },
78
+ updatedAt: {
79
+ type: "string",
80
+ watch: "*",
81
+ set: () => new Date().toISOString().split('T')[0],
82
+ readOnly: true,
83
+ },
84
+ },
85
+ indexes: {
86
+ issue: {
87
+ collection: "issueReview",
88
+ pk: {
89
+ composite: ["repoOwner", "repoName", "issueNumber"],
90
+ field: "pk"
91
+ },
92
+ sk: {
93
+ composite: [],
94
+ field: "sk"
95
+ }
96
+ },
97
+ created: {
98
+ collection: ["owned", "managed"],
99
+ index: "gsi1pk-gsi1sk-index",
100
+ pk: {
101
+ field: "gsi1pk",
102
+ composite: ["username"]
103
+ },
104
+ sk: {
105
+ field: "gsi1sk",
106
+ composite: ["createdAt"]
107
+ }
108
+ },
109
+ todos: {
110
+ collection: "activity",
111
+ index: "gsi2pk-gsi2sk-index",
112
+ pk: {
113
+ composite: ["repoOwner", "repoName"],
114
+ field: "gsi2pk"
115
+ },
116
+ sk: {
117
+ composite: ["createdAt"],
118
+ field: "gsi2sk"
119
+ }
120
+ },
121
+ _: {
122
+ collection: "subscribers",
123
+ index: "gsi4pk-gsi4sk-index",
124
+ pk: {
125
+ composite: ["repoOwner", "repoName", "ticketNumber"],
126
+ field: "gsi4pk"
127
+ },
128
+ sk: {
129
+ composite: [],
130
+ field: "gsi4sk"
131
+ }
132
+ }
133
+ }
134
+ });
135
+
136
+ const issueComments = new Entity({
137
+ model: {
138
+ entity: "issueComment",
139
+ service: "versioncontrol",
140
+ version: "1"
141
+ },
142
+ attributes: {
143
+ issueNumber: {
144
+ type: "string",
145
+ },
146
+ commentId: {
147
+ type: "string"
148
+ },
149
+ username: {
150
+ type: "string"
151
+ },
152
+ replyTo: {
153
+ type: "string"
154
+ },
155
+ replyViewed: {
156
+ type: "string",
157
+ default: NotYetViewed,
158
+ get: (replyViewed): string | void => {
159
+ if (replyViewed !== NotYetViewed) {
160
+ return replyViewed
161
+ }
162
+ },
163
+ set: (replyViewed) => {
164
+ if (replyViewed === undefined) {
165
+ return NotYetViewed;
166
+ }
167
+ return replyViewed;
168
+ }
169
+ },
170
+ repoName: {
171
+ type: "string"
172
+ },
173
+ repoOwner: {
174
+ type: "string"
175
+ },
176
+ body: {
177
+ type: "string"
178
+ },
179
+ createdAt: {
180
+ type: "string",
181
+ set: () => new Date().toISOString().split('T')[0],
182
+ readOnly: true,
183
+ },
184
+ updatedAt: {
185
+ type: "string",
186
+ watch: "*",
187
+ set: () => new Date().toISOString().split('T')[0],
188
+ readOnly: true,
189
+ },
190
+ },
191
+ indexes: {
192
+ comments: {
193
+ collection: "issueReview",
194
+ pk: {
195
+ composite: ["repoOwner", "repoName", "issueNumber"],
196
+ field: "pk"
197
+ },
198
+ sk: {
199
+ composite: ["commentId"],
200
+ field: "sk"
201
+ }
202
+ },
203
+ created: {
204
+ collection: "conversations",
205
+ index: "gsi1pk-gsi1sk-index",
206
+ pk: {
207
+ field: "gsi1pk",
208
+ composite: ["username"]
209
+ },
210
+ sk: {
211
+ field: "gsi1sk",
212
+ composite: ["repoOwner", "repoName", "issueNumber"]
213
+ }
214
+ },
215
+ replies: {
216
+ collection: "inbox",
217
+ index: "gsi2pk-gsi2sk-index",
218
+ pk: {
219
+ composite: ["replyTo"],
220
+ field: "gsi2pk"
221
+ },
222
+ sk: {
223
+ composite: ["createdAt", "replyViewed"],
224
+ field: "gsi2sk"
225
+ }
226
+ }
227
+ }
228
+ });
229
+
230
+ const pullRequests = new Entity({
231
+ model: {
232
+ entity: "pullRequest",
233
+ service: "versioncontrol",
234
+ version: "1"
235
+ },
236
+ attributes: {
237
+ pullRequestNumber: {
238
+ type: "string",
239
+ required: true,
240
+ },
241
+ repoName: {
242
+ type: "string",
243
+ required: true,
244
+ },
245
+ repoOwner: {
246
+ type: "string",
247
+ required: true,
248
+ },
249
+ username: {
250
+ type: "string",
251
+ required: true,
252
+ },
253
+ ticketType: {
254
+ type: TicketTypes,
255
+ default: () => PullRequestTicket,
256
+ set: () => PullRequestTicket,
257
+ readOnly: true
258
+ },
259
+ ticketNumber: {
260
+ type: "string",
261
+ readOnly: true,
262
+ watch: ["pullRequestNumber"],
263
+ set: (_, {issueNumber}) => issueNumber
264
+ },
265
+ status: {
266
+ type: StatusTypes,
267
+ default: "Open",
268
+ set: (val) => toStatusCode(val),
269
+ get: (val) => toStatusString(val)
270
+ },
271
+ reviewers: {
272
+ type: "list",
273
+ items: {
274
+ type: "map",
275
+ properties: {
276
+ username: {
277
+ type: "string",
278
+ required: true,
279
+ },
280
+ approved: {
281
+ type: "boolean",
282
+ required: true,
283
+ },
284
+ createdAt: {
285
+ type: "string",
286
+ default: () => new Date().toISOString().split('T')[0],
287
+ readOnly: true,
288
+ },
289
+ }
290
+ }
291
+ },
292
+ createdAt: {
293
+ type: "string",
294
+ set: () => new Date().toISOString().split('T')[0],
295
+ readOnly: true,
296
+ },
297
+ updatedAt: {
298
+ type: "string",
299
+ watch: "*",
300
+ set: () => new Date().toISOString().split('T')[0],
301
+ readOnly: true,
302
+ },
303
+ },
304
+ indexes: {
305
+ pullRequest: {
306
+ collection: "PRReview",
307
+ pk: {
308
+ composite: ["repoOwner", "repoName", "pullRequestNumber"],
309
+ field: "pk"
310
+ },
311
+ sk: {
312
+ composite: [],
313
+ field: "sk"
314
+ }
315
+ },
316
+ created: {
317
+ collection: ["owned", "managed"],
318
+ index: "gsi1pk-gsi1sk-index",
319
+ pk: {
320
+ field: "gsi1pk",
321
+ composite: ["username"]
322
+ },
323
+ sk: {
324
+ field: "gsi1sk",
325
+ composite: ["createdAt"]
326
+ }
327
+ },
328
+ enhancements: {
329
+ collection: "activity",
330
+ index: "gsi2pk-gsi2sk-index",
331
+ pk: {
332
+ field: "gsi2pk",
333
+ composite: ["repoOwner", "repoName"],
334
+ },
335
+ sk: {
336
+ field: "gsi2sk",
337
+ composite: ["createdAt"],
338
+
339
+ }
340
+ },
341
+ _: {
342
+ collection: "subscribers",
343
+ index: "gsi4pk-gsi4sk-index",
344
+ pk: {
345
+ composite: ["repoOwner", "repoName", "ticketNumber"],
346
+ field: "gsi4pk"
347
+ },
348
+ sk: {
349
+ composite: [],
350
+ field: "gsi4sk"
351
+ }
352
+ }
353
+ }
354
+ });
355
+
356
+ const pullRequestComments = new Entity({
357
+ model: {
358
+ entity: "pullRequestComment",
359
+ service: "versioncontrol",
360
+ version: "1"
361
+ },
362
+ attributes: {
363
+ repoName: {
364
+ type: "string"
365
+ },
366
+ username: {
367
+ type: "string"
368
+ },
369
+ repoOwner: {
370
+ type: "string"
371
+ },
372
+ pullRequestNumber: {
373
+ type: "string"
374
+ },
375
+ commentId: {
376
+ type: "string"
377
+ },
378
+ replyTo: {
379
+ type: "string"
380
+ },
381
+ replyViewed: {
382
+ type: "string",
383
+ default: NotYetViewed,
384
+ get: (replyViewed) => {
385
+ return replyViewed !== NotYetViewed
386
+ ? replyViewed
387
+ : undefined;
388
+ },
389
+ set: (replyViewed) => {
390
+ if (replyViewed === undefined) {
391
+ return NotYetViewed;
392
+ }
393
+ return replyViewed;
394
+ }
395
+ },
396
+ createdAt: {
397
+ type: "string",
398
+ set: () => new Date().toISOString().split('T')[0],
399
+ readOnly: true,
400
+ },
401
+ updatedAt: {
402
+ type: "string",
403
+ watch: "*",
404
+ set: () => new Date().toISOString().split('T')[0],
405
+ readOnly: true,
406
+ },
407
+ },
408
+ indexes: {
409
+ comments: {
410
+ collection: "PRReview",
411
+ pk: {
412
+ composite: ["repoOwner", "repoName", "pullRequestNumber"],
413
+ field: "pk"
414
+ },
415
+ sk: {
416
+ composite: ["commentId"],
417
+ field: "sk"
418
+ }
419
+ },
420
+ created: {
421
+ collection: "conversations",
422
+ index: "gsi1pk-gsi1sk-index",
423
+ pk: {
424
+ field: "gsi1pk",
425
+ composite: ["username"]
426
+ },
427
+ sk: {
428
+ field: "gsi1sk",
429
+ composite: ["repoOwner", "repoName"]
430
+ }
431
+ },
432
+ replies: {
433
+ collection: "inbox",
434
+ index: "gsi2pk-gsi2sk-index",
435
+ pk: {
436
+ composite: ["replyTo"],
437
+ field: "gsi2pk"
438
+ },
439
+ sk: {
440
+ composite: ["updatedAt", "replyViewed"],
441
+ field: "gsi2sk"
442
+ }
443
+ },
444
+ }
445
+ });
446
+
447
+ const repositories = new Entity({
448
+ model: {
449
+ entity: "repositories",
450
+ service: "versioncontrol",
451
+ version: "1"
452
+ },
453
+ attributes: {
454
+ repoName: {
455
+ type: "string"
456
+ },
457
+ repoOwner: {
458
+ type: "string"
459
+ },
460
+ about: {
461
+ type: "string"
462
+ },
463
+ username: {
464
+ type: "string",
465
+ readOnly: true,
466
+ watch: ["repoOwner"],
467
+ set: (_, {repoOwner}) => repoOwner
468
+ },
469
+ description: {
470
+ type: "string"
471
+ },
472
+ isPrivate: {
473
+ type: "boolean"
474
+ },
475
+ license: {
476
+ type: ["apache-2.0", "mit", "none"] as const,
477
+ default: "none"
478
+ },
479
+ defaultBranch: {
480
+ type: "string",
481
+ default: "main"
482
+ },
483
+ topics: {
484
+ type: "set",
485
+ items: "string"
486
+ },
487
+ followers: {
488
+ type: "set",
489
+ items: "string"
490
+ },
491
+ stars: {
492
+ type: "set",
493
+ items: "string"
494
+ },
495
+ createdAt: {
496
+ type: "string",
497
+ set: () => new Date().toISOString().split('T')[0],
498
+ readOnly: true,
499
+ },
500
+ updatedAt: {
501
+ type: "string",
502
+ watch: "*",
503
+ set: () => new Date().toISOString().split('T')[0],
504
+ readOnly: true,
505
+ },
506
+ },
507
+ indexes: {
508
+ repositories: {
509
+ collection: "alerts",
510
+ pk: {
511
+ composite: ["repoOwner"],
512
+ field: "pk"
513
+ },
514
+ sk: {
515
+ composite: ["repoName"],
516
+ field: "sk"
517
+ }
518
+ },
519
+ created: {
520
+ collection: "owned",
521
+ index: "gsi1pk-gsi1sk-index",
522
+ pk: {
523
+ composite: ["username"],
524
+ field: "gsi1pk"
525
+ },
526
+ sk: {
527
+ composite: ["isPrivate", "createdAt"],
528
+ field: "gsi1sk"
529
+ }
530
+ },
531
+ }
532
+ });
533
+
534
+ const subscriptions = new Entity({
535
+ model: {
536
+ entity: "subscription",
537
+ service: "versioncontrol",
538
+ version: "1"
539
+ },
540
+ attributes: {
541
+ repoName: {
542
+ type: "string",
543
+ required: true,
544
+ },
545
+ repoOwner: {
546
+ type: "string",
547
+ required: true,
548
+ },
549
+ username: {
550
+ type: "string",
551
+ required: true,
552
+ },
553
+ ticketNumber: {
554
+ type: "string",
555
+ default: () => IsNotTicket,
556
+ set: (ticketNumber) => {
557
+ if (ticketNumber === IsNotTicket) {
558
+ return "";
559
+ } else {
560
+ return ticketNumber;
561
+ }
562
+ },
563
+ get: (ticketNumber) => {
564
+ if (ticketNumber === "") {
565
+ return IsNotTicket;
566
+ } else {
567
+ return ticketNumber;
568
+ }
569
+ }
570
+ },
571
+ ticketType: {
572
+ type: TicketTypes,
573
+ default: () => IsNotTicket,
574
+ set: (ticketType) => {
575
+ return ticketType === IsNotTicket
576
+ ? "#"
577
+ : ticketType;
578
+ },
579
+ get: (ticketType) => {
580
+ if (ticketType === "") {
581
+ return IsNotTicket;
582
+ } else {
583
+ return ticketType;
584
+ }
585
+ }
586
+ },
587
+ createdAt: {
588
+ type: "string",
589
+ set: () => new Date().toISOString().split('T')[0],
590
+ readOnly: true,
591
+ },
592
+ updatedAt: {
593
+ type: "string",
594
+ watch: "*",
595
+ set: () => new Date().toISOString().split('T')[0],
596
+ readOnly: true,
597
+ },
598
+ },
599
+ indexes: {
600
+ repository: {
601
+ pk: {
602
+ composite: ["repoOwner", "repoName"],
603
+ field: "pk"
604
+ },
605
+ sk: {
606
+ composite: ["username", "ticketType", "ticketNumber"],
607
+ field: "sk"
608
+ }
609
+ },
610
+ user: {
611
+ collection: "watching",
612
+ index: "gsi3pk-gsi3sk-index",
613
+ pk: {
614
+ composite: ["username"],
615
+ field: "gsi3pk"
616
+ },
617
+ sk: {
618
+ composite: ["ticketType", "ticketNumber"],
619
+ field: "gsi3sk"
620
+ }
621
+ },
622
+ tickets: {
623
+ collection: "subscribers",
624
+ index: "gsi4pk-gsi4sk-index",
625
+ pk: {
626
+ composite: ["repoOwner", "repoName", "ticketNumber"],
627
+ field: "gsi4pk"
628
+ },
629
+ sk: {
630
+ composite: ["ticketType", "username"],
631
+ field: "gsi4sk"
632
+ }
633
+ }
634
+ }
635
+ });
636
+
637
+ const users = new Entity({
638
+ model: {
639
+ entity: "user",
640
+ service: "versioncontrol",
641
+ version: "1"
642
+ },
643
+ attributes: {
644
+ username: {
645
+ type: "string"
646
+ },
647
+ fullName: {
648
+ type: "string"
649
+ },
650
+ photo: {
651
+ type: "string"
652
+ },
653
+ bio: {
654
+ type: "string"
655
+ },
656
+ location: {
657
+ type: "string"
658
+ },
659
+ pinned: {
660
+ type: "any"
661
+ },
662
+ following: {
663
+ type: "set",
664
+ items: "string"
665
+ },
666
+ followers: {
667
+ type: "set",
668
+ items: "string"
669
+ },
670
+ createdAt: {
671
+ type: "string",
672
+ set: () => new Date().toISOString().split('T')[0],
673
+ readOnly: true,
674
+ },
675
+ updatedAt: {
676
+ type: "string",
677
+ watch: "*",
678
+ set: () => new Date().toISOString().split('T')[0],
679
+ readOnly: true,
680
+ },
681
+ },
682
+ indexes: {
683
+ user: {
684
+ collection: "overview",
685
+ pk: {
686
+ composite: ["username"],
687
+ field: "pk"
688
+ },
689
+ sk: {
690
+ composite: [],
691
+ field: "sk"
692
+ }
693
+ },
694
+ _: {
695
+ collection: "owned",
696
+ index: "gsi1pk-gsi1sk-index",
697
+ pk: {
698
+ composite: ["username"],
699
+ field: "gsi1pk"
700
+ },
701
+ sk: {
702
+ field: "gsi1sk",
703
+ composite: []
704
+ }
705
+ },
706
+ subscriptions: {
707
+ collection: "watching",
708
+ index: "gsi3pk-gsi3sk-index",
709
+ pk: {
710
+ composite: ["username"],
711
+ field: "gsi3pk"
712
+ },
713
+ sk: {
714
+ composite: [],
715
+ field: "gsi3sk"
716
+ }
717
+ }
718
+ }
719
+ });
720
+
721
+ const versioncontrol = new Service({
722
+ users,
723
+ issues,
724
+ repositories,
725
+ pullRequests,
726
+ subscriptions,
727
+ issueComments,
728
+ pullRequestComments,
729
+ }, {table: "your_table_name"});
730
+
731
+ type IssueIds = Parameters<typeof issues.get>[0][0];
732
+ type IssueCommentIds = Parameters<typeof issueComments.get>[0][0];
733
+ type PullRequestIds = Parameters<typeof pullRequests.get>[0][0];
734
+ type PullRequestCommentIds = Parameters<typeof pullRequestComments.get>[0][0];
735
+ type OwnedItems = CollectionItem<typeof versioncontrol, "owned">;
736
+
737
+ function isIssueCommentIds(comment: any): comment is IssueCommentIds {
738
+ return comment.issueNumber !== undefined && comment.username !== undefined;
739
+ }
740
+
741
+ function isPullRequestCommentIds(comment: any): comment is PullRequestCommentIds {
742
+ return comment.pullRequestNumber !== undefined && comment.username !== undefined;
743
+ }
744
+
745
+ // Get Public Repositories By Username
746
+ async function getPublicRepository(username: string) {
747
+ return versioncontrol.entities.repositories.query.created({username, isPrivate: false});
748
+ }
749
+
750
+ // Get PullRequest and Associated Comments -- newest comments first
751
+ async function reviewPullRequest(pr: PullRequestIds) {
752
+ return versioncontrol.collections.PRReview(pr)
753
+ .page(null, {params: {ScanIndexForward: false}})
754
+ }
755
+
756
+ // Get Issue and Associated Comments -- newest comments first
757
+ async function reviewIssue(issue: IssueIds) {
758
+ return versioncontrol.collections.issueReview(issue).go({params: {ScanIndexForward: false}});
759
+ }
760
+
761
+ // Get PullRequests Created By User
762
+ async function getUserPullRequests(username: string, status: Status = "") {
763
+ return versioncontrol.entities.pullRequests
764
+ .query.created({username, status})
765
+ .go({params: {ScanIndexForward: false}});
766
+ }
767
+
768
+ // Close pull request -- guards: can only be performed by user who opened PR or the repo owner
769
+ async function closePullRequest(user: string, pr: PullRequestIds) {
770
+ return versioncontrol.entities.pullRequests
771
+ .update(pr)
772
+ .set({status: "Closed"})
773
+ .where(({username, repoOwner}, {eq}) => `
774
+ ${eq(username, user)} OR ${eq(repoOwner, user)}
775
+ `)
776
+ .go()
777
+ }
778
+
779
+ // Get all user info, repos, pull requests, and issues in one query
780
+ async function getFirstPageLoad(username: string) {
781
+ const results: OwnedItems = {
782
+ issues: [],
783
+ pullRequests: [],
784
+ repositories: [],
785
+ users: [],
786
+ };
787
+
788
+ let page = null;
789
+
790
+ do {
791
+ const [next, data] = await versioncontrol.collections.owned({username}).page();
792
+ results.issues = results.issues.concat(data.issues);
793
+ results.pullRequests = results.pullRequests.concat(data.pullRequests);
794
+ results.repositories = results.repositories.concat(data.repositories);
795
+ results.users = results.users.concat(data.users);
796
+ page = next;
797
+ } while (page !== null);
798
+
799
+ return results;
800
+ }
801
+
802
+ // Get Subscriptions for a given Repository, PullRequest, or Issue.
803
+ async function getSubscribed(repoOwner: string, repoName: string, ticketNumber: string = IsNotTicket) {
804
+ return versioncontrol.collections.subscribers({repoOwner, repoName, ticketNumber}).go();
805
+ }
806
+
807
+ const MinDate = "0000-00-00";
808
+ const MaxDate = "9999-99-99";
809
+
810
+ // Get unread comment replies
811
+ async function getUnreadComments(user: string) {
812
+ const start = {
813
+ createdAt: MinDate,
814
+ replyViewed: NotYetViewed
815
+ };
816
+ const end = {
817
+ createdAt: MaxDate,
818
+ replyViewed: NotYetViewed
819
+ };
820
+ let [issues, pullRequests] = await Promise.all([
821
+ versioncontrol.entities
822
+ .issueComments.query
823
+ .replies({replyTo: user})
824
+ .between(start, end)
825
+ .go(),
826
+
827
+ versioncontrol.entities
828
+ .pullRequestComments.query
829
+ .replies({replyTo: user})
830
+ .between(start, end)
831
+ .go()
832
+ ]);
833
+
834
+ return {
835
+ issues,
836
+ pullRequests
837
+ };
838
+ }
839
+
840
+ // Mark comment reply as read -- guards: can only be done by the user who was being replied to
841
+ async function readReply(user: string, comment: IssueCommentIds): Promise<boolean>;
842
+ async function readReply(user: string, comment: PullRequestCommentIds): Promise<boolean>;
843
+ async function readReply(user: string, comment: any): Promise<boolean> {
844
+ const replyViewed = new Date().toISOString().split('T')[0];
845
+ if (isIssueCommentIds(comment)) {
846
+ return await versioncontrol.entities.issueComments
847
+ .patch(comment)
848
+ .set({replyViewed})
849
+ .where(({replyTo}, {eq}) => eq(replyTo, user))
850
+ .go()
851
+ .then(() => true)
852
+ .catch(() => false);
853
+ } else if (isPullRequestCommentIds(comment)) {
854
+ return await versioncontrol.entities.pullRequestComments
855
+ .patch(comment)
856
+ .set({replyViewed})
857
+ .where(({replyTo}, {eq}) => eq(replyTo, user))
858
+ .go()
859
+ .then(() => true)
860
+ .catch(() => false);
861
+ } else {
862
+ return false;
863
+ }
864
+ }
865
+
866
+ async function approvePullRequest(repoOwner: string, repoName: string, pullRequestNumber: string, username: string) {
867
+ const pullRequest = await versioncontrol.entities.pullRequests
868
+ .get({repoOwner, repoName, pullRequestNumber})
869
+ .go();
870
+
871
+ if (!pullRequest || !pullRequest.reviewers) {
872
+ return false;
873
+ }
874
+
875
+ let index: number = -1;
876
+
877
+ for (let i = 0; i < pullRequest.reviewers.length; i++) {
878
+ const reviewer = pullRequest.reviewers[i];
879
+ if (reviewer.username === username) {
880
+ index = i;
881
+ }
882
+ }
883
+
884
+ if (index === -1) {
885
+ return false;
886
+ }
887
+
888
+ return versioncontrol.entities.pullRequests
889
+ .update({repoOwner, repoName, pullRequestNumber})
890
+ .data(({reviewers}, {set}) => {
891
+ set(reviewers[index].approved, true);
892
+ })
893
+ .where(({reviewers}, {eq}) => `
894
+ ${eq(reviewers[index].username, username)};
895
+ `)
896
+ .go()
897
+ .then(() => true)
898
+ .catch(() => false);
899
+ }
900
+
901
+ async function followRepository(repoOwner: string, repoName: string, follower: string) {
902
+ await versioncontrol.entities
903
+ .repositories.update({repoOwner, repoName})
904
+ .add({followers: [follower]})
905
+ .go()
906
+ }
907
+
908
+ // getPublicRepository("abc");
909
+ reviewPullRequest({
910
+ pullRequestNumber: "110",
911
+ repoName: "electrodb",
912
+ repoOwner: "tywalch"
913
+ });
914
+
915
+ reviewIssue({
916
+ issueNumber: "281",
917
+ repoName: "electrodb",
918
+ repoOwner: "tywalch"
919
+ });
920
+
921
+ getUserPullRequests("tywalch");
922
+ getUserPullRequests("tywalch", "Closed");
923
+
924
+ closePullRequest("tywalch", {
925
+ pullRequestNumber: "301",
926
+ repoName: "electrodb",
927
+ repoOwner: "tywalch",
928
+ });
929
+
930
+ getFirstPageLoad("tywalch");
931
+ getSubscribed
932
+ getUnreadComments
933
+ readReply
934
+ readReply
935
+ readReply
936
+ approvePullRequest
937
+ followRepository