github-issue-tower-defence-management 1.174.2 → 1.175.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.
- package/CHANGELOG.md +14 -0
- package/bin/adapter/entry-points/console/consoleDoneStore.js +0 -2
- package/bin/adapter/entry-points/console/consoleDoneStore.js.map +1 -1
- package/bin/adapter/entry-points/console/consoleTabNames.js +0 -1
- package/bin/adapter/entry-points/console/consoleTabNames.js.map +1 -1
- package/bin/adapter/entry-points/console/dashboardComposeService.js +2 -4
- package/bin/adapter/entry-points/console/dashboardComposeService.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-g4SqIzCh.js → index-BhDothvr.js} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/bin/domain/entities/WorkflowStatus.js +1 -6
- package/bin/domain/entities/WorkflowStatus.js.map +1 -1
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +4 -5
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js +0 -39
- package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js.map +1 -1
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js +4 -5
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js +15 -1
- package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js.map +1 -1
- package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js +0 -2
- package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js.map +1 -1
- package/bin/domain/usecases/dashboard/ComposeDashboardUseCase.js +2 -3
- package/bin/domain/usecases/dashboard/ComposeDashboardUseCase.js.map +1 -1
- package/bin/domain/usecases/dashboard/GenerateDashboardRowUseCase.js +0 -1
- package/bin/domain/usecases/dashboard/GenerateDashboardRowUseCase.js.map +1 -1
- package/bin/domain/usecases/findLastAgentDeclaringReport.js +12 -0
- package/bin/domain/usecases/findLastAgentDeclaringReport.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/console/consoleDoneStore.ts +0 -2
- package/src/adapter/entry-points/console/consoleTabNames.ts +0 -1
- package/src/adapter/entry-points/console/dashboardComposeService.test.ts +4 -8
- package/src/adapter/entry-points/console/dashboardComposeService.ts +1 -3
- package/src/adapter/entry-points/console/dashboardEmitterComposerKey.test.ts +12 -17
- package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +4 -32
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +1 -18
- package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.stories.tsx +3 -7
- package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.test.tsx +2 -4
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.test.tsx +1 -1
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleNavigation.test.ts +6 -8
- package/src/adapter/entry-points/console/ui/src/features/console/logic/tabAdvance.test.ts +8 -17
- package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +0 -2
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +3 -3
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +5 -6
- package/src/adapter/entry-points/console/ui-dist/assets/{index-g4SqIzCh.js → index-BhDothvr.js} +1 -1
- package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/src/adapter/entry-points/console/webServer.test.ts +4 -6
- package/src/adapter/entry-points/handlers/consoleListsWriter.test.ts +30 -26
- package/src/adapter/entry-points/handlers/dashboardRowWriter.test.ts +5 -6
- package/src/domain/entities/WorkflowStatus.ts +0 -5
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +32 -0
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +7 -8
- package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.test.ts +198 -863
- package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.ts +0 -68
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +8 -11
- package/src/domain/usecases/SetupTowerDefenceProjectUseCase.test.ts +89 -51
- package/src/domain/usecases/SetupTowerDefenceProjectUseCase.ts +30 -2
- package/src/domain/usecases/console/GenerateConsoleListsUseCase.test.ts +62 -72
- package/src/domain/usecases/console/GenerateConsoleListsUseCase.ts +0 -8
- package/src/domain/usecases/dashboard/ComposeDashboardUseCase.test.ts +19 -28
- package/src/domain/usecases/dashboard/ComposeDashboardUseCase.ts +2 -3
- package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.test.ts +11 -12
- package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.ts +0 -3
- package/src/domain/usecases/findLastAgentDeclaringReport.test.ts +71 -0
- package/src/domain/usecases/findLastAgentDeclaringReport.ts +17 -0
- package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.test.ts +2 -2
- package/types/adapter/entry-points/console/consoleDoneStore.d.ts.map +1 -1
- package/types/adapter/entry-points/console/consoleTabNames.d.ts.map +1 -1
- package/types/adapter/entry-points/console/dashboardComposeService.d.ts.map +1 -1
- package/types/domain/entities/WorkflowStatus.d.ts +0 -1
- package/types/domain/entities/WorkflowStatus.d.ts.map +1 -1
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.d.ts.map +1 -1
- package/types/domain/usecases/RevertOrphanedPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts +1 -0
- package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts.map +1 -1
- package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts +1 -2
- package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts.map +1 -1
- package/types/domain/usecases/dashboard/ComposeDashboardUseCase.d.ts.map +1 -1
- package/types/domain/usecases/dashboard/GenerateDashboardRowUseCase.d.ts +0 -1
- package/types/domain/usecases/dashboard/GenerateDashboardRowUseCase.d.ts.map +1 -1
- package/types/domain/usecases/findLastAgentDeclaringReport.d.ts +5 -0
- package/types/domain/usecases/findLastAgentDeclaringReport.d.ts.map +1 -0
|
@@ -10,7 +10,6 @@ import { isAuthorAuthorizedForAutoStatusCheck } from './isAuthorAuthorizedForAut
|
|
|
10
10
|
import {
|
|
11
11
|
AWAITING_QUALITY_CHECK_STATUS_NAME,
|
|
12
12
|
AWAITING_WORKSPACE_STATUS_NAME,
|
|
13
|
-
DEFAULT_STATUS_NAME,
|
|
14
13
|
} from '../entities/WorkflowStatus';
|
|
15
14
|
|
|
16
15
|
// GitHub rejects field mutations against archived project items with
|
|
@@ -96,10 +95,6 @@ export class RevertNotReadyReviewQueueIssueUseCase {
|
|
|
96
95
|
const relatedOpenPrUrlsByIssueUrl =
|
|
97
96
|
this.buildRelatedOpenPrUrlsByIssueUrl(issues);
|
|
98
97
|
|
|
99
|
-
const unreadPullRequests = issues.filter(
|
|
100
|
-
(issue) => issue.status === DEFAULT_STATUS_NAME && issue.isPr,
|
|
101
|
-
);
|
|
102
|
-
|
|
103
98
|
const labelsNotRequiringPullRequest =
|
|
104
99
|
resolveLabelsNotRequiringPullRequest(params);
|
|
105
100
|
const willBeEvaluated = (item: Issue): boolean =>
|
|
@@ -117,9 +112,6 @@ export class RevertNotReadyReviewQueueIssueUseCase {
|
|
|
117
112
|
.flatMap(
|
|
118
113
|
(issue) => relatedOpenPrUrlsByIssueUrl.get(issue.url) ?? [],
|
|
119
114
|
),
|
|
120
|
-
...unreadPullRequests
|
|
121
|
-
.filter(willBeEvaluated)
|
|
122
|
-
.map((pullRequest) => pullRequest.url),
|
|
123
115
|
]),
|
|
124
116
|
),
|
|
125
117
|
);
|
|
@@ -194,66 +186,6 @@ export class RevertNotReadyReviewQueueIssueUseCase {
|
|
|
194
186
|
throw error;
|
|
195
187
|
}
|
|
196
188
|
}
|
|
197
|
-
|
|
198
|
-
const projectStory = project.story;
|
|
199
|
-
|
|
200
|
-
for (const pullRequest of unreadPullRequests) {
|
|
201
|
-
if (
|
|
202
|
-
!isAuthorAuthorizedForAutoStatusCheck(
|
|
203
|
-
pullRequest.author,
|
|
204
|
-
allowedIssueAuthors,
|
|
205
|
-
)
|
|
206
|
-
) {
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
try {
|
|
211
|
-
const { rejections } = await this.issueRejectionEvaluator.evaluate(
|
|
212
|
-
pullRequest,
|
|
213
|
-
labelsNotRequiringPullRequest,
|
|
214
|
-
{ resolvedOpenPrByUrl },
|
|
215
|
-
);
|
|
216
|
-
if (rejections.length > 0) {
|
|
217
|
-
if (!pullRequest.assignees.includes(params.manager)) {
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
try {
|
|
221
|
-
await this.issueRepository.updateStatus(
|
|
222
|
-
project,
|
|
223
|
-
pullRequest,
|
|
224
|
-
awaitingWorkspaceStatusOption.id,
|
|
225
|
-
);
|
|
226
|
-
} catch (error) {
|
|
227
|
-
if (isArchivedProjectItemError(error)) {
|
|
228
|
-
console.warn(
|
|
229
|
-
`RevertNotReadyReviewQueueIssueUseCase: project item is archived and cannot be updated, skipping revert. prUrl: ${pullRequest.url}`,
|
|
230
|
-
);
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
throw error;
|
|
234
|
-
}
|
|
235
|
-
if (projectStory) {
|
|
236
|
-
await this.issueRepository.updateStory(
|
|
237
|
-
{ ...project, story: projectStory },
|
|
238
|
-
pullRequest,
|
|
239
|
-
projectStory.workflowManagementStory.id,
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
await this.issueCommentRepository.createComment(
|
|
243
|
-
pullRequest,
|
|
244
|
-
`Auto Status Check: REJECTED\n${rejections.map((r) => `- ${r.detail}`).join('\n')}`,
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
} catch (error) {
|
|
248
|
-
if (isTimeoutError(error)) {
|
|
249
|
-
console.warn(
|
|
250
|
-
`RevertNotReadyReviewQueueIssueUseCase: request timed out, skipping pull request for this cycle. prUrl: ${pullRequest.url} error: ${error instanceof Error ? error.message : String(error)}`,
|
|
251
|
-
);
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
throw error;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
189
|
};
|
|
258
190
|
|
|
259
191
|
// Derives, for each issue, the set of open pull request URLs that reference it
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
RETURNED_TO_AWAITING_WORKSPACE_MESSAGE_HEAD,
|
|
24
24
|
} from './returnedToAwaitingWorkspaceMessage';
|
|
25
25
|
import { extractNextStepAgent } from './extractNextStepAgent';
|
|
26
|
+
import { findLastAgentDeclaringReport } from './findLastAgentDeclaringReport';
|
|
26
27
|
import { ensureAgentOptionAndGetId } from './ensureAgentOptionAndGetId';
|
|
27
28
|
import { normalizeReportBody } from './normalizeReportBody';
|
|
28
29
|
|
|
@@ -349,17 +350,13 @@ export class RevertOrphanedPreparationUseCase {
|
|
|
349
350
|
comments: Comment[],
|
|
350
351
|
allowedIssueAuthors: string[] | null | undefined,
|
|
351
352
|
): string | null => {
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
(
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
) && comment.content.startsWith('From: :robot:'),
|
|
360
|
-
);
|
|
361
|
-
return lastReportComment
|
|
362
|
-
? extractNextStepAgent(lastReportComment.content)
|
|
353
|
+
const lastAgentDeclaringReport = findLastAgentDeclaringReport(
|
|
354
|
+
comments,
|
|
355
|
+
(author) =>
|
|
356
|
+
isAuthorAuthorizedForAutoStatusCheck(author, allowedIssueAuthors),
|
|
357
|
+
);
|
|
358
|
+
return lastAgentDeclaringReport
|
|
359
|
+
? extractNextStepAgent(lastAgentDeclaringReport.content)
|
|
363
360
|
: null;
|
|
364
361
|
};
|
|
365
362
|
|
|
@@ -7,7 +7,6 @@ import { Issue } from '../entities/Issue';
|
|
|
7
7
|
import {
|
|
8
8
|
AWAITING_QUALITY_CHECK_STATUS_NAME,
|
|
9
9
|
AWAITING_WORKSPACE_STATUS_NAME,
|
|
10
|
-
DEFAULT_STATUS_NAME,
|
|
11
10
|
DONE_STATUS_NAME,
|
|
12
11
|
FAILED_PREPARATION_STATUS_NAME,
|
|
13
12
|
ICEBOX_STATUS_NAME,
|
|
@@ -80,9 +79,8 @@ const buildIssue = (overrides: Partial<Issue>): Issue => ({
|
|
|
80
79
|
});
|
|
81
80
|
|
|
82
81
|
describe('SetupTowerDefenceProjectUseCase', () => {
|
|
83
|
-
it('should define exactly the
|
|
82
|
+
it('should define exactly the 10 required statuses in the documented order with the documented colors and no descriptions', () => {
|
|
84
83
|
expect(REQUIRED_WORKFLOW_STATUSES).toEqual([
|
|
85
|
-
{ name: DEFAULT_STATUS_NAME, color: 'ORANGE' },
|
|
86
84
|
{ name: AWAITING_WORKSPACE_STATUS_NAME, color: 'BLUE' },
|
|
87
85
|
{ name: PREPARATION_STATUS_NAME, color: 'YELLOW' },
|
|
88
86
|
{ name: FAILED_PREPARATION_STATUS_NAME, color: 'RED' },
|
|
@@ -190,9 +188,9 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
190
188
|
mock<Pick<IssueRepository, 'getAllIssues' | 'updateStatus'>>();
|
|
191
189
|
const statuses: FieldOption[] = [
|
|
192
190
|
{
|
|
193
|
-
id: '
|
|
194
|
-
name:
|
|
195
|
-
color: '
|
|
191
|
+
id: 'aws-id',
|
|
192
|
+
name: AWAITING_WORKSPACE_STATUS_NAME,
|
|
193
|
+
color: 'BLUE',
|
|
196
194
|
description: '',
|
|
197
195
|
},
|
|
198
196
|
{
|
|
@@ -222,13 +220,7 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
222
220
|
project,
|
|
223
221
|
[
|
|
224
222
|
{
|
|
225
|
-
id: '
|
|
226
|
-
name: DEFAULT_STATUS_NAME,
|
|
227
|
-
color: 'ORANGE',
|
|
228
|
-
description: '',
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
id: null,
|
|
223
|
+
id: 'aws-id',
|
|
232
224
|
name: AWAITING_WORKSPACE_STATUS_NAME,
|
|
233
225
|
color: 'BLUE',
|
|
234
226
|
description: '',
|
|
@@ -328,7 +320,6 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
328
320
|
expect(mockProjectRepository.updateStatusList).toHaveBeenCalledTimes(1);
|
|
329
321
|
const [, payload] = mockProjectRepository.updateStatusList.mock.calls[0];
|
|
330
322
|
expect(payload.map((status) => status.name)).toEqual([
|
|
331
|
-
DEFAULT_STATUS_NAME,
|
|
332
323
|
AWAITING_WORKSPACE_STATUS_NAME,
|
|
333
324
|
PREPARATION_STATUS_NAME,
|
|
334
325
|
FAILED_PREPARATION_STATUS_NAME,
|
|
@@ -369,7 +360,7 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
369
360
|
expect(payload[1].color).toBe(REQUIRED_WORKFLOW_STATUSES[1].color);
|
|
370
361
|
});
|
|
371
362
|
|
|
372
|
-
it('should reuse the default template "Todo" option ID for
|
|
363
|
+
it('should reuse the default template "Todo" option ID for Awaiting Workspace so a newly added item lands in Awaiting Workspace', async () => {
|
|
373
364
|
const mockProjectRepository =
|
|
374
365
|
mock<Pick<ProjectRepository, 'getByUrl' | 'updateStatusList'>>();
|
|
375
366
|
const mockIssueRepository =
|
|
@@ -411,8 +402,10 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
411
402
|
|
|
412
403
|
expect(mockProjectRepository.updateStatusList).toHaveBeenCalledTimes(1);
|
|
413
404
|
const [, payload] = mockProjectRepository.updateStatusList.mock.calls[0];
|
|
414
|
-
const
|
|
415
|
-
|
|
405
|
+
const awaitingWorkspaceEntry = payload.find(
|
|
406
|
+
(s) => s.name === AWAITING_WORKSPACE_STATUS_NAME,
|
|
407
|
+
);
|
|
408
|
+
expect(awaitingWorkspaceEntry?.id).toBe('template-todo');
|
|
416
409
|
const todoByHumanEntry = payload.find((s) => s.name === TODO_STATUS_NAME);
|
|
417
410
|
expect(todoByHumanEntry?.id).toBeNull();
|
|
418
411
|
expect(payload.some((s) => s.name === LEGACY_TODO_STATUS_NAME)).toBe(false);
|
|
@@ -453,7 +446,7 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
453
446
|
const [, payload] = mockProjectRepository.updateStatusList.mock.calls[0];
|
|
454
447
|
const inTmuxEntry = payload.find((s) => s.name === IN_TMUX_STATUS_NAME);
|
|
455
448
|
expect(inTmuxEntry).toBeDefined();
|
|
456
|
-
expect(inTmuxEntry?.id).toBe('id-
|
|
449
|
+
expect(inTmuxEntry?.id).toBe('id-6');
|
|
457
450
|
expect(payload.some((s) => s.name === LEGACY_IN_TMUX_STATUS_NAME)).toBe(
|
|
458
451
|
false,
|
|
459
452
|
);
|
|
@@ -505,7 +498,7 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
505
498
|
if (s.name === TODO_BY_AGENT_STATUS_NAME) {
|
|
506
499
|
return { ...s, id: 'preexisting-todo-agent-id' };
|
|
507
500
|
}
|
|
508
|
-
if (s.name ===
|
|
501
|
+
if (s.name === AWAITING_WORKSPACE_STATUS_NAME) {
|
|
509
502
|
return { ...s, description: 'stale description' };
|
|
510
503
|
}
|
|
511
504
|
return s;
|
|
@@ -583,7 +576,7 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
583
576
|
if (s.name === IN_TMUX_BY_AGENT_STATUS_NAME) {
|
|
584
577
|
return { ...s, id: 'preexisting-agent-id' };
|
|
585
578
|
}
|
|
586
|
-
if (s.name ===
|
|
579
|
+
if (s.name === AWAITING_WORKSPACE_STATUS_NAME) {
|
|
587
580
|
return { ...s, description: 'stale description' };
|
|
588
581
|
}
|
|
589
582
|
return s;
|
|
@@ -657,49 +650,43 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
657
650
|
const statuses: FieldOption[] = [
|
|
658
651
|
{
|
|
659
652
|
id: 'id-0',
|
|
660
|
-
name: DEFAULT_STATUS_NAME,
|
|
661
|
-
color: 'ORANGE',
|
|
662
|
-
description: '',
|
|
663
|
-
},
|
|
664
|
-
{
|
|
665
|
-
id: 'id-1',
|
|
666
653
|
name: AWAITING_WORKSPACE_STATUS_NAME,
|
|
667
654
|
color: 'BLUE',
|
|
668
655
|
description: '',
|
|
669
656
|
},
|
|
670
657
|
{
|
|
671
|
-
id: 'id-
|
|
658
|
+
id: 'id-1',
|
|
672
659
|
name: PREPARATION_STATUS_NAME,
|
|
673
660
|
color: 'YELLOW',
|
|
674
661
|
description: '',
|
|
675
662
|
},
|
|
676
663
|
{
|
|
677
|
-
id: 'id-
|
|
664
|
+
id: 'id-2',
|
|
678
665
|
name: FAILED_PREPARATION_STATUS_NAME,
|
|
679
666
|
color: 'RED',
|
|
680
667
|
description: '',
|
|
681
668
|
},
|
|
682
669
|
{
|
|
683
|
-
id: 'id-
|
|
670
|
+
id: 'id-3',
|
|
684
671
|
name: AWAITING_QUALITY_CHECK_STATUS_NAME,
|
|
685
672
|
color: 'GREEN',
|
|
686
673
|
description: '',
|
|
687
674
|
},
|
|
688
675
|
{
|
|
689
|
-
id: 'id-
|
|
676
|
+
id: 'id-4',
|
|
690
677
|
name: LEGACY_TODO_STATUS_NAME,
|
|
691
678
|
color: 'PINK',
|
|
692
679
|
description: '',
|
|
693
680
|
},
|
|
694
|
-
{ id: 'id-
|
|
681
|
+
{ id: 'id-5', name: PC_TODO_STATUS_NAME, color: 'PINK', description: '' },
|
|
695
682
|
{
|
|
696
|
-
id: 'id-
|
|
683
|
+
id: 'id-6',
|
|
697
684
|
name: LEGACY_IN_TMUX_STATUS_NAME,
|
|
698
685
|
color: 'RED',
|
|
699
686
|
description: '',
|
|
700
687
|
},
|
|
701
|
-
{ id: 'id-
|
|
702
|
-
{ id: 'id-
|
|
688
|
+
{ id: 'id-7', name: DONE_STATUS_NAME, color: 'PURPLE', description: '' },
|
|
689
|
+
{ id: 'id-8', name: ICEBOX_STATUS_NAME, color: 'GRAY', description: '' },
|
|
703
690
|
];
|
|
704
691
|
const project = buildProject(statuses);
|
|
705
692
|
mockProjectRepository.getByUrl.mockResolvedValue(project);
|
|
@@ -720,7 +707,6 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
720
707
|
const [, payload] = mockProjectRepository.updateStatusList.mock.calls[0];
|
|
721
708
|
|
|
722
709
|
expect(payload.map((s) => s.name)).toEqual([
|
|
723
|
-
DEFAULT_STATUS_NAME,
|
|
724
710
|
AWAITING_WORKSPACE_STATUS_NAME,
|
|
725
711
|
PREPARATION_STATUS_NAME,
|
|
726
712
|
FAILED_PREPARATION_STATUS_NAME,
|
|
@@ -733,9 +719,9 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
733
719
|
ICEBOX_STATUS_NAME,
|
|
734
720
|
]);
|
|
735
721
|
|
|
736
|
-
expect(payload.find((s) => s.name === TODO_STATUS_NAME)?.id).toBe('id-
|
|
722
|
+
expect(payload.find((s) => s.name === TODO_STATUS_NAME)?.id).toBe('id-4');
|
|
737
723
|
expect(payload.find((s) => s.name === IN_TMUX_STATUS_NAME)?.id).toBe(
|
|
738
|
-
'id-
|
|
724
|
+
'id-6',
|
|
739
725
|
);
|
|
740
726
|
expect(payload.some((s) => s.name === PC_TODO_STATUS_NAME)).toBe(false);
|
|
741
727
|
expect(payload.some((s) => s.name === LEGACY_TODO_STATUS_NAME)).toBe(false);
|
|
@@ -751,12 +737,6 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
751
737
|
mock<Pick<IssueRepository, 'getAllIssues' | 'updateStatus'>>();
|
|
752
738
|
const todoStatusId = 'todo-status-id';
|
|
753
739
|
const statuses: FieldOption[] = [
|
|
754
|
-
{
|
|
755
|
-
id: 'id-0',
|
|
756
|
-
name: DEFAULT_STATUS_NAME,
|
|
757
|
-
color: 'ORANGE',
|
|
758
|
-
description: '',
|
|
759
|
-
},
|
|
760
740
|
{
|
|
761
741
|
id: 'atb-id',
|
|
762
742
|
name: LEGACY_AWAITING_TASK_BREAKDOWN_STATUS_NAME,
|
|
@@ -858,7 +838,6 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
858
838
|
),
|
|
859
839
|
).toBe(false);
|
|
860
840
|
expect(payload.map((s) => s.name)).toEqual([
|
|
861
|
-
DEFAULT_STATUS_NAME,
|
|
862
841
|
AWAITING_WORKSPACE_STATUS_NAME,
|
|
863
842
|
PREPARATION_STATUS_NAME,
|
|
864
843
|
FAILED_PREPARATION_STATUS_NAME,
|
|
@@ -942,12 +921,6 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
942
921
|
const statuses: FieldOption[] = [
|
|
943
922
|
{
|
|
944
923
|
id: 'id-0',
|
|
945
|
-
name: DEFAULT_STATUS_NAME,
|
|
946
|
-
color: 'ORANGE',
|
|
947
|
-
description: '',
|
|
948
|
-
},
|
|
949
|
-
{
|
|
950
|
-
id: 'id-1',
|
|
951
924
|
name: LEGACY_AWAITING_TASK_BREAKDOWN_STATUS_NAME,
|
|
952
925
|
color: 'ORANGE',
|
|
953
926
|
description: '',
|
|
@@ -1012,7 +985,6 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
1012
985
|
const [, payload] = mockProjectRepository.updateStatusList.mock.calls[0];
|
|
1013
986
|
|
|
1014
987
|
expect(payload.map((s) => s.name)).toEqual([
|
|
1015
|
-
DEFAULT_STATUS_NAME,
|
|
1016
988
|
AWAITING_WORKSPACE_STATUS_NAME,
|
|
1017
989
|
PREPARATION_STATUS_NAME,
|
|
1018
990
|
FAILED_PREPARATION_STATUS_NAME,
|
|
@@ -1040,4 +1012,70 @@ describe('SetupTowerDefenceProjectUseCase', () => {
|
|
|
1040
1012
|
),
|
|
1041
1013
|
).toBe(false);
|
|
1042
1014
|
});
|
|
1015
|
+
|
|
1016
|
+
it('should migrate issues from Unread to Awaiting Workspace when both statuses exist in the project', async () => {
|
|
1017
|
+
const mockProjectRepository =
|
|
1018
|
+
mock<Pick<ProjectRepository, 'getByUrl' | 'updateStatusList'>>();
|
|
1019
|
+
const mockIssueRepository =
|
|
1020
|
+
mock<Pick<IssueRepository, 'getAllIssues' | 'updateStatus'>>();
|
|
1021
|
+
const awaitingWorkspaceId = 'aws-status-id';
|
|
1022
|
+
const statuses: FieldOption[] = [
|
|
1023
|
+
{ id: 'unread-id', name: 'Unread', color: 'ORANGE', description: '' },
|
|
1024
|
+
{
|
|
1025
|
+
id: awaitingWorkspaceId,
|
|
1026
|
+
name: AWAITING_WORKSPACE_STATUS_NAME,
|
|
1027
|
+
color: 'BLUE',
|
|
1028
|
+
description: '',
|
|
1029
|
+
},
|
|
1030
|
+
...buildCanonicalStatuses().filter(
|
|
1031
|
+
(s) => s.name !== AWAITING_WORKSPACE_STATUS_NAME,
|
|
1032
|
+
),
|
|
1033
|
+
];
|
|
1034
|
+
const project = buildProject(statuses);
|
|
1035
|
+
mockProjectRepository.getByUrl.mockResolvedValue(project);
|
|
1036
|
+
mockProjectRepository.updateStatusList.mockResolvedValue([]);
|
|
1037
|
+
|
|
1038
|
+
const unreadIssue1 = buildIssue({
|
|
1039
|
+
number: 1,
|
|
1040
|
+
url: 'https://github.com/test-org/test-repo/issues/1',
|
|
1041
|
+
itemId: 'item-1',
|
|
1042
|
+
status: 'Unread',
|
|
1043
|
+
});
|
|
1044
|
+
const unreadIssue2 = buildIssue({
|
|
1045
|
+
number: 2,
|
|
1046
|
+
url: 'https://github.com/test-org/test-repo/issues/2',
|
|
1047
|
+
itemId: 'item-2',
|
|
1048
|
+
status: 'Unread',
|
|
1049
|
+
});
|
|
1050
|
+
const awsIssue = buildIssue({
|
|
1051
|
+
number: 3,
|
|
1052
|
+
url: 'https://github.com/test-org/test-repo/issues/3',
|
|
1053
|
+
itemId: 'item-3',
|
|
1054
|
+
status: AWAITING_WORKSPACE_STATUS_NAME,
|
|
1055
|
+
});
|
|
1056
|
+
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
1057
|
+
project: mock<Project>(),
|
|
1058
|
+
issues: [unreadIssue1, unreadIssue2, awsIssue],
|
|
1059
|
+
cacheUsed: false,
|
|
1060
|
+
});
|
|
1061
|
+
mockIssueRepository.updateStatus.mockResolvedValue(undefined);
|
|
1062
|
+
|
|
1063
|
+
const useCase = new SetupTowerDefenceProjectUseCase(
|
|
1064
|
+
mockProjectRepository,
|
|
1065
|
+
mockIssueRepository,
|
|
1066
|
+
);
|
|
1067
|
+
await useCase.run({ projectUrl: project.url });
|
|
1068
|
+
|
|
1069
|
+
expect(mockIssueRepository.updateStatus).toHaveBeenCalledTimes(2);
|
|
1070
|
+
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
1071
|
+
project,
|
|
1072
|
+
unreadIssue1,
|
|
1073
|
+
awaitingWorkspaceId,
|
|
1074
|
+
);
|
|
1075
|
+
expect(mockIssueRepository.updateStatus).toHaveBeenCalledWith(
|
|
1076
|
+
project,
|
|
1077
|
+
unreadIssue2,
|
|
1078
|
+
awaitingWorkspaceId,
|
|
1079
|
+
);
|
|
1080
|
+
});
|
|
1043
1081
|
});
|
|
@@ -2,7 +2,7 @@ import { FieldOption } from '../entities/Project';
|
|
|
2
2
|
import { ProjectRepository } from './adapter-interfaces/ProjectRepository';
|
|
3
3
|
import { IssueRepository } from './adapter-interfaces/IssueRepository';
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
AWAITING_WORKSPACE_STATUS_NAME,
|
|
6
6
|
IN_TMUX_STATUS_NAME,
|
|
7
7
|
LEGACY_AWAITING_TASK_BREAKDOWN_STATUS_NAME,
|
|
8
8
|
LEGACY_IN_TMUX_STATUS_NAME,
|
|
@@ -28,22 +28,50 @@ export class SetupTowerDefenceProjectUseCase {
|
|
|
28
28
|
private static readonly LEGACY_STATUS_NAMES: Readonly<
|
|
29
29
|
Record<string, string>
|
|
30
30
|
> = {
|
|
31
|
-
[
|
|
31
|
+
[AWAITING_WORKSPACE_STATUS_NAME]: LEGACY_TODO_STATUS_NAME,
|
|
32
32
|
[TODO_STATUS_NAME]: LEGACY_TODO_STATUS_NAME,
|
|
33
33
|
[IN_TMUX_STATUS_NAME]: LEGACY_IN_TMUX_STATUS_NAME,
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
private static readonly UNREAD_MIGRATED_STATUS_NAME = 'Unread' as const;
|
|
37
|
+
|
|
36
38
|
private static readonly MIGRATED_FROM_NAMES: ReadonlySet<string> = new Set([
|
|
37
39
|
LEGACY_TODO_STATUS_NAME,
|
|
38
40
|
LEGACY_IN_TMUX_STATUS_NAME,
|
|
39
41
|
PC_TODO_STATUS_NAME,
|
|
40
42
|
LEGACY_AWAITING_TASK_BREAKDOWN_STATUS_NAME,
|
|
43
|
+
SetupTowerDefenceProjectUseCase.UNREAD_MIGRATED_STATUS_NAME,
|
|
41
44
|
]);
|
|
42
45
|
|
|
43
46
|
run = async (params: { projectUrl: string }): Promise<void> => {
|
|
44
47
|
const project = await this.projectRepository.getByUrl(params.projectUrl);
|
|
45
48
|
const existing = project.status.statuses;
|
|
46
49
|
|
|
50
|
+
const unreadStatus = existing.find(
|
|
51
|
+
(s) =>
|
|
52
|
+
s.name === SetupTowerDefenceProjectUseCase.UNREAD_MIGRATED_STATUS_NAME,
|
|
53
|
+
);
|
|
54
|
+
if (unreadStatus) {
|
|
55
|
+
const awaitingWorkspaceStatus = existing.find(
|
|
56
|
+
(s) => s.name === AWAITING_WORKSPACE_STATUS_NAME,
|
|
57
|
+
);
|
|
58
|
+
if (awaitingWorkspaceStatus) {
|
|
59
|
+
const { issues } = await this.issueRepository.getAllIssues(project.id);
|
|
60
|
+
const unreadIssues = issues.filter(
|
|
61
|
+
(issue) =>
|
|
62
|
+
issue.status ===
|
|
63
|
+
SetupTowerDefenceProjectUseCase.UNREAD_MIGRATED_STATUS_NAME,
|
|
64
|
+
);
|
|
65
|
+
for (const issue of unreadIssues) {
|
|
66
|
+
await this.issueRepository.updateStatus(
|
|
67
|
+
project,
|
|
68
|
+
issue,
|
|
69
|
+
awaitingWorkspaceStatus.id,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
47
75
|
const awaitingTaskBreakdownStatus = existing.find(
|
|
48
76
|
(s) => s.name === LEGACY_AWAITING_TASK_BREAKDOWN_STATUS_NAME,
|
|
49
77
|
);
|