github-issue-tower-defence-management 2.4.0 → 2.5.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 (32) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +13 -3
  3. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  4. package/bin/adapter/entry-points/handlers/subscriptionDisabledTokenHandler.js +27 -0
  5. package/bin/adapter/entry-points/handlers/subscriptionDisabledTokenHandler.js.map +1 -0
  6. package/bin/domain/usecases/HandleScheduledEventUseCase.js +1 -11
  7. package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
  8. package/bin/domain/usecases/SubscriptionDisabledIssueUseCase.js +42 -0
  9. package/bin/domain/usecases/SubscriptionDisabledIssueUseCase.js.map +1 -0
  10. package/bin/domain/usecases/TriagerApprovalDispatchUseCase.js +9 -4
  11. package/bin/domain/usecases/TriagerApprovalDispatchUseCase.js.map +1 -1
  12. package/package.json +1 -1
  13. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +0 -8
  14. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +17 -4
  15. package/src/adapter/entry-points/handlers/subscriptionDisabledTokenHandler.ts +41 -0
  16. package/src/adapter/repositories/githubGraphqlClient.test.ts +2 -2
  17. package/src/domain/usecases/HandleScheduledEventUseCase.test.ts +0 -56
  18. package/src/domain/usecases/HandleScheduledEventUseCase.ts +0 -13
  19. package/src/domain/usecases/SubscriptionDisabledIssueUseCase.test.ts +197 -0
  20. package/src/domain/usecases/SubscriptionDisabledIssueUseCase.ts +74 -0
  21. package/src/domain/usecases/TriagerApprovalDispatchUseCase.test.ts +83 -0
  22. package/src/domain/usecases/TriagerApprovalDispatchUseCase.ts +12 -4
  23. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  24. package/types/adapter/entry-points/handlers/subscriptionDisabledTokenHandler.d.ts +9 -0
  25. package/types/adapter/entry-points/handlers/subscriptionDisabledTokenHandler.d.ts.map +1 -0
  26. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts +1 -4
  27. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
  28. package/types/domain/usecases/SubscriptionDisabledIssueUseCase.d.ts +16 -0
  29. package/types/domain/usecases/SubscriptionDisabledIssueUseCase.d.ts.map +1 -0
  30. package/types/domain/usecases/TriagerApprovalDispatchUseCase.d.ts.map +1 -1
  31. package/src/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.test.ts +0 -1638
  32. package/src/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.ts +0 -192
@@ -1,192 +0,0 @@
1
- import { Issue } from '../entities/Issue';
2
- import { IssueRepository } from './adapter-interfaces/IssueRepository';
3
- import { Project } from '../entities/Project';
4
- import { StoryObjectMap } from '../entities/StoryObjectMap';
5
- import { encodeForURI } from './utils';
6
- import { ICEBOX_STATUS_NAME } from '../entities/WorkflowStatus';
7
- import { Member } from '../entities/Member';
8
-
9
- export class ConvertCheckboxToIssueInStoryIssueUseCase {
10
- constructor(
11
- readonly issueRepository: Pick<
12
- IssueRepository,
13
- | 'createNewIssue'
14
- | 'updateIssueBody'
15
- | 'updateStory'
16
- | 'getIssueByUrl'
17
- | 'getIssueBodyByUrl'
18
- | 'addIssueToProject'
19
- >,
20
- ) {}
21
-
22
- run = async (input: {
23
- project: Project;
24
- issues: Issue[];
25
- cacheUsed: boolean;
26
- urlOfStoryView: string;
27
- storyObjectMap: StoryObjectMap;
28
- manager: Member['name'];
29
- createTaskFromStoryBodyCheckboxEnabled: boolean;
30
- }): Promise<void> => {
31
- const story = input.project.story;
32
- if (!story) {
33
- return;
34
- }
35
-
36
- for (const storyOption of input.project.story?.stories || []) {
37
- const storyIssue = input.issues
38
- .filter((issue) => storyOption.name.startsWith(issue.title))
39
- .reduce<Issue | null>(
40
- (longest, issue) =>
41
- longest === null || issue.title.length > longest.title.length
42
- ? issue
43
- : longest,
44
- null,
45
- );
46
- const storyObject = input.storyObjectMap.get(storyOption.name);
47
- if (storyOption.name.startsWith('regular / ')) {
48
- continue;
49
- } else if (!storyIssue || !storyObject) {
50
- throw new Error(`Story issue not found: ${storyOption.name}`);
51
- } else if (storyIssue.isClosed) {
52
- continue;
53
- }
54
- const iced = storyIssue.status === ICEBOX_STATUS_NAME;
55
- const noCheckboxWorkFollows =
56
- iced || !input.createTaskFromStoryBodyCheckboxEnabled;
57
- const storyViewLinkAlreadyOnFirstLine =
58
- this.bodyWithStoryViewLinkOnFirstLine(
59
- storyIssue.body,
60
- input.urlOfStoryView,
61
- storyOption.name,
62
- ) === storyIssue.body;
63
- if (noCheckboxWorkFollows && storyViewLinkAlreadyOnFirstLine) {
64
- continue;
65
- }
66
- const freshStoryBody = await this.issueRepository.getIssueBodyByUrl(
67
- storyIssue.url,
68
- );
69
- if (freshStoryBody === null) {
70
- console.warn(
71
- `ConvertCheckboxToIssueInStoryIssueUseCase: story issue not found by URL (possibly deleted), skipping story. storyIssueUrl: ${storyIssue.url}`,
72
- );
73
- continue;
74
- }
75
- let newBody = this.bodyWithStoryViewLinkOnFirstLine(
76
- freshStoryBody,
77
- input.urlOfStoryView,
78
- storyOption.name,
79
- );
80
- if (newBody !== freshStoryBody) {
81
- await this.issueRepository.updateIssueBody(storyIssue, newBody);
82
- }
83
- if (iced || !input.createTaskFromStoryBodyCheckboxEnabled) {
84
- continue;
85
- }
86
- if (!newBody.includes('- [ ] ')) {
87
- continue;
88
- }
89
- const checkboxTextsNotCreatedIssue =
90
- this.findCheckboxTextsNotCreatedIssue(newBody);
91
- for (const checkboxText of checkboxTextsNotCreatedIssue) {
92
- const issueTitle = checkboxText.replace(
93
- 'STORYNAME',
94
- `${storyOption.name} #${storyIssue.number}`,
95
- );
96
- const newIssueBody = `- Parent issue: ${storyIssue.url}`;
97
- const newIssueNumber = await this.issueRepository.createNewIssue(
98
- storyIssue.org,
99
- storyIssue.repo,
100
- issueTitle,
101
- newIssueBody,
102
- [input.manager],
103
- [],
104
- );
105
- const newIssueUrl = `https://github.com/${storyIssue.org}/${storyIssue.repo}/issues/${newIssueNumber}`;
106
- newBody = newBody.replace(
107
- `- [ ] ${checkboxText}`,
108
- `- [ ] ${newIssueUrl}`,
109
- );
110
- await this.issueRepository.updateIssueBody(storyIssue, newBody);
111
- await this.issueRepository.addIssueToProject(
112
- input.project,
113
- newIssueUrl,
114
- );
115
- const newIssue = await this.issueRepository.getIssueByUrl(newIssueUrl);
116
- if (!newIssue) {
117
- throw new Error(`Issue not found: ${newIssueUrl}`);
118
- }
119
- await this.issueRepository.updateStory(
120
- { ...input.project, story: story },
121
- newIssue,
122
- storyOption.id,
123
- );
124
- }
125
- }
126
- };
127
- bodyWithStoryViewLinkOnFirstLine = (
128
- body: string,
129
- urlOfStoryView: string,
130
- storyName: string,
131
- ): string => {
132
- const storyViewLink = this.buildStoryViewLink(urlOfStoryView, storyName);
133
- const storyViewLinkPattern = this.buildStoryViewLinkPattern(
134
- urlOfStoryView,
135
- storyName,
136
- );
137
- const isStoryViewLinkLine = (line: string): boolean =>
138
- storyViewLinkPattern.test(line);
139
- const lines = body.split('\n');
140
- const remainingLines: string[] = [];
141
- for (let i = 0; i < lines.length; i++) {
142
- if (!isStoryViewLinkLine(lines[i])) {
143
- remainingLines.push(lines[i]);
144
- continue;
145
- }
146
- if (i + 1 < lines.length && lines[i + 1].trim() === '') {
147
- i++;
148
- continue;
149
- }
150
- if (
151
- remainingLines.length > 0 &&
152
- remainingLines[remainingLines.length - 1].trim() === ''
153
- ) {
154
- remainingLines.pop();
155
- }
156
- }
157
- while (remainingLines.length > 0 && remainingLines[0].trim() === '') {
158
- remainingLines.shift();
159
- }
160
- return `${storyViewLink}\n\n${remainingLines.join('\n')}`;
161
- };
162
- buildStoryViewLink = (urlOfStoryView: string, storyName: string): string => {
163
- return `${urlOfStoryView}?sliceBy%5Bvalue%5D=${encodeForURI(storyName)}`;
164
- };
165
- buildStoryViewLinkPattern = (
166
- urlOfStoryView: string,
167
- storyName: string,
168
- ): RegExp => {
169
- const escapeForRegularExpression = (value: string): string =>
170
- value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
171
- const storyViewProjectUrl = urlOfStoryView.split('/views/')[0];
172
- const storySliceQuery = `?sliceBy%5Bvalue%5D=${encodeForURI(storyName)}`;
173
- return new RegExp(
174
- `${escapeForRegularExpression(storyViewProjectUrl)}(/views/\\d+)?${escapeForRegularExpression(storySliceQuery)}(?![\\w%])`,
175
- );
176
- };
177
- findCheckboxTextsNotCreatedIssue = (storyIssueBody: string): string[] => {
178
- const regexToFindCheckboxes = /^- \[ ] (.*)$/gm;
179
- const match = storyIssueBody.match(regexToFindCheckboxes);
180
- if (!match) return [];
181
- const checkboxes: string[] = [];
182
- for (let i = 0; i < match.length; i++) {
183
- checkboxes.push(match[i].replace('- [ ] ', '').trim());
184
- }
185
- return checkboxes.filter(
186
- (checkbox) =>
187
- checkbox !== '' &&
188
- !checkbox.match(/^https:\/\/github.com\/.*\/issues\/\d+$/) &&
189
- !checkbox.match(/^#\d+$/),
190
- );
191
- };
192
- }