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.
- package/.github/workflows/test.yml +2 -2
- package/CHANGELOG.md +15 -0
- package/bin/adapter/repositories/GraphqlProjectRepository.js +12 -0
- package/bin/adapter/repositories/GraphqlProjectRepository.js.map +1 -1
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +9 -0
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
- package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js +5 -1
- package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js.map +1 -1
- package/bin/domain/entities/ClaudeWindowUsage.js +3 -0
- package/bin/domain/entities/ClaudeWindowUsage.js.map +1 -0
- package/bin/domain/entities/Comment.js +3 -0
- package/bin/domain/entities/Comment.js.map +1 -0
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +81 -0
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -0
- package/bin/domain/usecases/StartPreparationUseCase.js +104 -0
- package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/ClaudeRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/ClaudeRepository.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/IssueCommentRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/IssueCommentRepository.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/LocalCommandRunner.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/LocalCommandRunner.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/repositories/CheerioProjectRepository.test.ts +1 -0
- package/src/adapter/repositories/GraphqlProjectRepository.test.ts +1 -0
- package/src/adapter/repositories/GraphqlProjectRepository.ts +14 -1
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +1 -0
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +15 -1
- package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +162 -34
- package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +6 -0
- package/src/domain/entities/ClaudeWindowUsage.ts +5 -0
- package/src/domain/entities/Comment.ts +5 -0
- package/src/domain/entities/Project.ts +1 -0
- package/src/domain/usecases/GetStoryObjectMapUseCase.test.ts +1 -0
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +669 -0
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +132 -0
- package/src/domain/usecases/StartPreparationUseCase.test.ts +716 -0
- package/src/domain/usecases/StartPreparationUseCase.ts +191 -0
- package/src/domain/usecases/adapter-interfaces/ClaudeRepository.ts +5 -0
- package/src/domain/usecases/adapter-interfaces/IssueCommentRepository.ts +7 -0
- package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +11 -0
- package/src/domain/usecases/adapter-interfaces/LocalCommandRunner.ts +7 -0
- package/src/domain/usecases/adapter-interfaces/ProjectRepository.ts +1 -0
- package/types/adapter/repositories/GraphqlProjectRepository.d.ts +2 -1
- package/types/adapter/repositories/GraphqlProjectRepository.d.ts.map +1 -1
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +4 -1
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
- package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts +1 -0
- package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts.map +1 -1
- package/types/domain/entities/ClaudeWindowUsage.d.ts +6 -0
- package/types/domain/entities/ClaudeWindowUsage.d.ts.map +1 -0
- package/types/domain/entities/Comment.d.ts +6 -0
- package/types/domain/entities/Comment.d.ts.map +1 -0
- package/types/domain/entities/Project.d.ts +1 -0
- package/types/domain/entities/Project.d.ts.map +1 -1
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts +24 -0
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -0
- package/types/domain/usecases/StartPreparationUseCase.d.ts +37 -0
- package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/ClaudeRepository.d.ts +5 -0
- package/types/domain/usecases/adapter-interfaces/ClaudeRepository.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/IssueCommentRepository.d.ts +7 -0
- package/types/domain/usecases/adapter-interfaces/IssueCommentRepository.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +10 -0
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/LocalCommandRunner.d.ts +8 -0
- package/types/domain/usecases/adapter-interfaces/LocalCommandRunner.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/ProjectRepository.d.ts +1 -0
- package/types/domain/usecases/adapter-interfaces/ProjectRepository.d.ts.map +1 -1
|
@@ -1,50 +1,178 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import axios, { AxiosHeaders, AxiosResponse } from 'axios';
|
|
2
|
+
import {
|
|
3
|
+
GraphqlProjectItemRepository,
|
|
4
|
+
PAGINATION_DELAY_MS,
|
|
5
|
+
} from './GraphqlProjectItemRepository';
|
|
2
6
|
import { LocalStorageRepository } from '../LocalStorageRepository';
|
|
3
7
|
|
|
8
|
+
jest.mock('axios');
|
|
9
|
+
const mockAxios = jest.mocked(axios);
|
|
10
|
+
|
|
11
|
+
const toAxiosResponse = <T>(data: T): AxiosResponse<T> => ({
|
|
12
|
+
data,
|
|
13
|
+
status: 200,
|
|
14
|
+
statusText: 'OK',
|
|
15
|
+
headers: {},
|
|
16
|
+
config: { headers: new AxiosHeaders() },
|
|
17
|
+
});
|
|
18
|
+
|
|
4
19
|
describe('GraphqlProjectItemRepository', () => {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
const makePageResponse = (hasNextPage: boolean, endCursor: string) =>
|
|
21
|
+
toAxiosResponse({
|
|
22
|
+
data: {
|
|
23
|
+
node: {
|
|
24
|
+
items: {
|
|
25
|
+
totalCount: 2,
|
|
26
|
+
pageInfo: {
|
|
27
|
+
endCursor,
|
|
28
|
+
startCursor: 'cursor-start',
|
|
29
|
+
hasNextPage,
|
|
30
|
+
},
|
|
31
|
+
nodes: [
|
|
32
|
+
{
|
|
33
|
+
id: `item-${endCursor}`,
|
|
34
|
+
fieldValues: {
|
|
35
|
+
nodes: [
|
|
36
|
+
{
|
|
37
|
+
text: 'some text',
|
|
38
|
+
field: { name: 'Status' },
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
content: {
|
|
43
|
+
repository: { nameWithOwner: 'owner/repo' },
|
|
44
|
+
number: 1,
|
|
45
|
+
title: 'Test Issue',
|
|
46
|
+
state: 'OPEN',
|
|
47
|
+
url: 'https://github.com/owner/repo/issues/1',
|
|
48
|
+
body: 'body',
|
|
49
|
+
createdAt: '2024-01-01T00:00:00Z',
|
|
50
|
+
labels: { nodes: [] },
|
|
51
|
+
assignees: { nodes: [] },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('fetchProjectItems', () => {
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
jest.useFakeTimers();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
jest.useRealTimers();
|
|
67
|
+
mockAxios.mockClear();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should sleep between paginated requests to avoid 403', async () => {
|
|
71
|
+
const localStorageRepository = new LocalStorageRepository();
|
|
72
|
+
const repository = new GraphqlProjectItemRepository(
|
|
73
|
+
localStorageRepository,
|
|
74
|
+
'',
|
|
75
|
+
'dummy-token',
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
|
|
79
|
+
mockAxios
|
|
80
|
+
.mockResolvedValueOnce(makePageResponse(true, 'cursor-1'))
|
|
81
|
+
.mockResolvedValueOnce(makePageResponse(false, 'cursor-2'));
|
|
82
|
+
|
|
83
|
+
const resultPromise = repository.fetchProjectItems('test-project-id');
|
|
84
|
+
await jest.advanceTimersByTimeAsync(PAGINATION_DELAY_MS);
|
|
85
|
+
const result = await resultPromise;
|
|
86
|
+
|
|
87
|
+
expect(mockAxios).toHaveBeenCalledTimes(2);
|
|
88
|
+
expect(result).toHaveLength(2);
|
|
89
|
+
expect(setTimeoutSpy).toHaveBeenCalledWith(
|
|
90
|
+
expect.any(Function),
|
|
91
|
+
PAGINATION_DELAY_MS,
|
|
92
|
+
);
|
|
93
|
+
setTimeoutSpy.mockRestore();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should not sleep on first request when there is only one page', async () => {
|
|
97
|
+
const localStorageRepository = new LocalStorageRepository();
|
|
98
|
+
const repository = new GraphqlProjectItemRepository(
|
|
99
|
+
localStorageRepository,
|
|
100
|
+
'',
|
|
101
|
+
'dummy-token',
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
|
|
105
|
+
mockAxios.mockResolvedValueOnce(makePageResponse(false, 'cursor-1'));
|
|
106
|
+
|
|
107
|
+
const result = await repository.fetchProjectItems('test-project-id');
|
|
108
|
+
|
|
109
|
+
expect(mockAxios).toHaveBeenCalledTimes(1);
|
|
110
|
+
expect(result).toHaveLength(1);
|
|
111
|
+
expect(setTimeoutSpy).not.toHaveBeenCalledWith(
|
|
112
|
+
expect.any(Function),
|
|
113
|
+
PAGINATION_DELAY_MS,
|
|
114
|
+
);
|
|
115
|
+
setTimeoutSpy.mockRestore();
|
|
116
|
+
});
|
|
14
117
|
});
|
|
118
|
+
|
|
15
119
|
describe('getProjectItemFields', () => {
|
|
120
|
+
afterEach(() => {
|
|
121
|
+
mockAxios.mockClear();
|
|
122
|
+
});
|
|
123
|
+
|
|
16
124
|
it('should return project item fields', async () => {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
125
|
+
const localStorageRepository = new LocalStorageRepository();
|
|
126
|
+
const repository = new GraphqlProjectItemRepository(
|
|
127
|
+
localStorageRepository,
|
|
128
|
+
'',
|
|
129
|
+
'dummy-token',
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
mockAxios.mockResolvedValueOnce(
|
|
133
|
+
toAxiosResponse({
|
|
134
|
+
data: {
|
|
135
|
+
repository: {
|
|
136
|
+
issue: {
|
|
137
|
+
projectItems: {
|
|
138
|
+
nodes: [
|
|
139
|
+
{
|
|
140
|
+
id: 'item-1',
|
|
141
|
+
fieldValues: {
|
|
142
|
+
nodes: [
|
|
143
|
+
{
|
|
144
|
+
__typename: 'ProjectV2ItemFieldDateValue',
|
|
145
|
+
date: '2024-04-25',
|
|
146
|
+
field: { name: 'NextActionDate' },
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
__typename: 'ProjectV2ItemFieldSingleSelectValue',
|
|
150
|
+
name: 'In Progress',
|
|
151
|
+
field: { name: 'Status' },
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
}),
|
|
25
162
|
);
|
|
26
163
|
|
|
164
|
+
const result = await repository.getProjectItemFields('owner', 'repo', 1);
|
|
165
|
+
|
|
166
|
+
expect(mockAxios).toHaveBeenCalledTimes(1);
|
|
27
167
|
expect(result).toEqual([
|
|
28
|
-
{
|
|
29
|
-
fieldName: 'Assignees',
|
|
30
|
-
fieldValue: '',
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
fieldName: 'Repository',
|
|
34
|
-
fieldValue: 'test-repository',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
fieldName: '',
|
|
38
|
-
fieldValue: '',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
fieldName: '',
|
|
42
|
-
fieldValue: 'In progress test title',
|
|
43
|
-
},
|
|
44
168
|
{
|
|
45
169
|
fieldName: 'NextActionDate',
|
|
46
170
|
fieldValue: '2024-04-25',
|
|
47
171
|
},
|
|
172
|
+
{
|
|
173
|
+
fieldName: 'Status',
|
|
174
|
+
fieldValue: 'In Progress',
|
|
175
|
+
},
|
|
48
176
|
]);
|
|
49
177
|
});
|
|
50
178
|
});
|
|
@@ -18,6 +18,7 @@ export type ProjectItem = {
|
|
|
18
18
|
value: string | null;
|
|
19
19
|
}[];
|
|
20
20
|
};
|
|
21
|
+
export const PAGINATION_DELAY_MS = 5000;
|
|
21
22
|
export class GraphqlProjectItemRepository extends BaseGitHubRepository {
|
|
22
23
|
fetchItemId = async (
|
|
23
24
|
projectId: string,
|
|
@@ -295,6 +296,11 @@ query GetProjectItems($projectId: ID!, $after: String) {
|
|
|
295
296
|
let hasNextPage = true;
|
|
296
297
|
|
|
297
298
|
while (hasNextPage) {
|
|
299
|
+
if (after !== null) {
|
|
300
|
+
await new Promise((resolve) =>
|
|
301
|
+
setTimeout(resolve, PAGINATION_DELAY_MS),
|
|
302
|
+
);
|
|
303
|
+
}
|
|
298
304
|
const data = await callGraphql(projectId, after);
|
|
299
305
|
const projectItems: {
|
|
300
306
|
id: string;
|