github-issue-tower-defence-management 1.24.0 → 1.26.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 (69) hide show
  1. package/.github/workflows/test.yml +2 -2
  2. package/CHANGELOG.md +15 -0
  3. package/bin/adapter/repositories/GraphqlProjectRepository.js +12 -0
  4. package/bin/adapter/repositories/GraphqlProjectRepository.js.map +1 -1
  5. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +9 -0
  6. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
  7. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js +5 -1
  8. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js.map +1 -1
  9. package/bin/domain/entities/ClaudeWindowUsage.js +3 -0
  10. package/bin/domain/entities/ClaudeWindowUsage.js.map +1 -0
  11. package/bin/domain/entities/Comment.js +3 -0
  12. package/bin/domain/entities/Comment.js.map +1 -0
  13. package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +81 -0
  14. package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -0
  15. package/bin/domain/usecases/StartPreparationUseCase.js +104 -0
  16. package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -0
  17. package/bin/domain/usecases/adapter-interfaces/ClaudeRepository.js +3 -0
  18. package/bin/domain/usecases/adapter-interfaces/ClaudeRepository.js.map +1 -0
  19. package/bin/domain/usecases/adapter-interfaces/IssueCommentRepository.js +3 -0
  20. package/bin/domain/usecases/adapter-interfaces/IssueCommentRepository.js.map +1 -0
  21. package/bin/domain/usecases/adapter-interfaces/LocalCommandRunner.js +3 -0
  22. package/bin/domain/usecases/adapter-interfaces/LocalCommandRunner.js.map +1 -0
  23. package/package.json +1 -1
  24. package/src/adapter/repositories/CheerioProjectRepository.test.ts +1 -0
  25. package/src/adapter/repositories/GraphqlProjectRepository.test.ts +1 -0
  26. package/src/adapter/repositories/GraphqlProjectRepository.ts +14 -1
  27. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +1 -0
  28. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +15 -1
  29. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +162 -34
  30. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +6 -0
  31. package/src/domain/entities/ClaudeWindowUsage.ts +5 -0
  32. package/src/domain/entities/Comment.ts +5 -0
  33. package/src/domain/entities/Project.ts +1 -0
  34. package/src/domain/usecases/GetStoryObjectMapUseCase.test.ts +1 -0
  35. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +669 -0
  36. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +132 -0
  37. package/src/domain/usecases/StartPreparationUseCase.test.ts +716 -0
  38. package/src/domain/usecases/StartPreparationUseCase.ts +191 -0
  39. package/src/domain/usecases/adapter-interfaces/ClaudeRepository.ts +5 -0
  40. package/src/domain/usecases/adapter-interfaces/IssueCommentRepository.ts +7 -0
  41. package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +11 -0
  42. package/src/domain/usecases/adapter-interfaces/LocalCommandRunner.ts +7 -0
  43. package/src/domain/usecases/adapter-interfaces/ProjectRepository.ts +1 -0
  44. package/types/adapter/repositories/GraphqlProjectRepository.d.ts +2 -1
  45. package/types/adapter/repositories/GraphqlProjectRepository.d.ts.map +1 -1
  46. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +4 -1
  47. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
  48. package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts +1 -0
  49. package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts.map +1 -1
  50. package/types/domain/entities/ClaudeWindowUsage.d.ts +6 -0
  51. package/types/domain/entities/ClaudeWindowUsage.d.ts.map +1 -0
  52. package/types/domain/entities/Comment.d.ts +6 -0
  53. package/types/domain/entities/Comment.d.ts.map +1 -0
  54. package/types/domain/entities/Project.d.ts +1 -0
  55. package/types/domain/entities/Project.d.ts.map +1 -1
  56. package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts +24 -0
  57. package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -0
  58. package/types/domain/usecases/StartPreparationUseCase.d.ts +37 -0
  59. package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -0
  60. package/types/domain/usecases/adapter-interfaces/ClaudeRepository.d.ts +5 -0
  61. package/types/domain/usecases/adapter-interfaces/ClaudeRepository.d.ts.map +1 -0
  62. package/types/domain/usecases/adapter-interfaces/IssueCommentRepository.d.ts +7 -0
  63. package/types/domain/usecases/adapter-interfaces/IssueCommentRepository.d.ts.map +1 -0
  64. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +10 -0
  65. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
  66. package/types/domain/usecases/adapter-interfaces/LocalCommandRunner.d.ts +8 -0
  67. package/types/domain/usecases/adapter-interfaces/LocalCommandRunner.d.ts.map +1 -0
  68. package/types/domain/usecases/adapter-interfaces/ProjectRepository.d.ts +1 -0
  69. package/types/domain/usecases/adapter-interfaces/ProjectRepository.d.ts.map +1 -1
@@ -0,0 +1,132 @@
1
+ import { IssueRepository } from './adapter-interfaces/IssueRepository';
2
+ import { ProjectRepository } from './adapter-interfaces/ProjectRepository';
3
+ import { IssueCommentRepository } from './adapter-interfaces/IssueCommentRepository';
4
+
5
+ export class IssueNotFoundError extends Error {
6
+ constructor(issueUrl: string) {
7
+ super(`Issue not found: ${issueUrl}`);
8
+ this.name = 'IssueNotFoundError';
9
+ }
10
+ }
11
+ export class IllegalIssueStatusError extends Error {
12
+ constructor(
13
+ issueUrl: string,
14
+ currentStatus: string | null,
15
+ expectedStatus: string | null,
16
+ ) {
17
+ super(
18
+ `Illegal issue status for ${issueUrl}: expected ${expectedStatus}, but got ${currentStatus}`,
19
+ );
20
+ this.name = 'IllegalIssueStatusError';
21
+ }
22
+ }
23
+ type RejectedReasonType =
24
+ | 'NO_REPORT'
25
+ | 'PULL_REQUEST_NOT_FOUND'
26
+ | 'MULTIPLE_PULL_REQUESTS_FOUND'
27
+ | 'PULL_REQUEST_CONFLICTED'
28
+ | 'ANY_CI_JOB_FAILED'
29
+ | 'ANY_REVIEW_COMMENT_NOT_RESOLVED';
30
+
31
+ export class NotifyFinishedIssuePreparationUseCase {
32
+ constructor(
33
+ private readonly projectRepository: Pick<ProjectRepository, 'getByUrl'>,
34
+ private readonly issueRepository: Pick<
35
+ IssueRepository,
36
+ 'get' | 'update' | 'findRelatedOpenPRs'
37
+ >,
38
+ private readonly issueCommentRepository: Pick<
39
+ IssueCommentRepository,
40
+ 'getCommentsFromIssue' | 'createComment'
41
+ >,
42
+ ) {}
43
+
44
+ run = async (params: {
45
+ projectUrl: string;
46
+ issueUrl: string;
47
+ preparationStatus: string;
48
+ awaitingWorkspaceStatus: string;
49
+ awaitingQualityCheckStatus: string;
50
+ thresholdForAutoReject: number;
51
+ }): Promise<void> => {
52
+ const project = await this.projectRepository.getByUrl(params.projectUrl);
53
+
54
+ const issue = await this.issueRepository.get(params.issueUrl, project);
55
+
56
+ if (!issue) {
57
+ throw new IssueNotFoundError(params.issueUrl);
58
+ } else if (issue.status !== params.preparationStatus) {
59
+ throw new IllegalIssueStatusError(
60
+ params.issueUrl,
61
+ issue.status,
62
+ params.preparationStatus,
63
+ );
64
+ }
65
+ const comments =
66
+ await this.issueCommentRepository.getCommentsFromIssue(issue);
67
+
68
+ const lastTargetComments = comments.slice(
69
+ -params.thresholdForAutoReject * 2,
70
+ );
71
+ if (
72
+ lastTargetComments.filter((comment) =>
73
+ comment.content.startsWith('Auto Status Check: REJECTED'),
74
+ ).length >= params.thresholdForAutoReject
75
+ ) {
76
+ issue.status = params.awaitingQualityCheckStatus;
77
+ await this.issueRepository.update(issue, project);
78
+ await this.issueCommentRepository.createComment(
79
+ issue,
80
+ `Failed to pass the check autimatically for ${params.thresholdForAutoReject} times`,
81
+ );
82
+ return;
83
+ }
84
+
85
+ const rejectedReasons: RejectedReasonType[] = [];
86
+ const lastComment = comments[comments.length - 1];
87
+ if (!lastComment || lastComment.content.startsWith('Auto Status Check: ')) {
88
+ rejectedReasons.push('NO_REPORT');
89
+ }
90
+
91
+ const hasCategoryLabel = issue.labels.some((label) =>
92
+ label.startsWith('category:'),
93
+ );
94
+ if (!hasCategoryLabel) {
95
+ const relatedOpenPrs = await this.issueRepository.findRelatedOpenPRs(
96
+ issue.url,
97
+ );
98
+ if (relatedOpenPrs.length <= 0) {
99
+ rejectedReasons.push('PULL_REQUEST_NOT_FOUND');
100
+ } else if (relatedOpenPrs.length > 1) {
101
+ rejectedReasons.push('MULTIPLE_PULL_REQUESTS_FOUND');
102
+ } else {
103
+ const pr = relatedOpenPrs[0];
104
+ if (pr.isConflicted) {
105
+ rejectedReasons.push('PULL_REQUEST_CONFLICTED');
106
+ }
107
+ if (!pr.isPassedAllCiJob) {
108
+ rejectedReasons.push('ANY_CI_JOB_FAILED');
109
+ }
110
+ if (!pr.isResolvedAllReviewComments) {
111
+ rejectedReasons.push('ANY_REVIEW_COMMENT_NOT_RESOLVED');
112
+ }
113
+ }
114
+ }
115
+
116
+ if (rejectedReasons.length <= 0) {
117
+ issue.status = params.awaitingQualityCheckStatus;
118
+ await this.issueRepository.update(issue, project);
119
+ return;
120
+ }
121
+
122
+ issue.status = params.awaitingWorkspaceStatus;
123
+ await this.issueRepository.update(issue, project);
124
+
125
+ await this.issueCommentRepository.createComment(
126
+ issue,
127
+ `
128
+ Auto Status Check: REJECTED
129
+ ${JSON.stringify(rejectedReasons)}`,
130
+ );
131
+ };
132
+ }