github-issue-tower-defence-management 1.35.2 → 1.36.1

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 (72) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/bin/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.js +1 -27
  3. package/bin/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.js.map +1 -1
  4. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +1 -27
  5. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  6. package/bin/adapter/repositories/BaseGitHubRepository.js +3 -7
  7. package/bin/adapter/repositories/BaseGitHubRepository.js.map +1 -1
  8. package/bin/adapter/repositories/CheerioProjectRepository.js +11 -11
  9. package/bin/adapter/repositories/CheerioProjectRepository.js.map +1 -1
  10. package/bin/adapter/repositories/FetchWebhookRepository.js +5 -1
  11. package/bin/adapter/repositories/FetchWebhookRepository.js.map +1 -1
  12. package/bin/adapter/repositories/GraphqlProjectRepository.js +14 -16
  13. package/bin/adapter/repositories/GraphqlProjectRepository.js.map +1 -1
  14. package/bin/adapter/repositories/KySlackRepository.js +212 -0
  15. package/bin/adapter/repositories/KySlackRepository.js.map +1 -0
  16. package/bin/adapter/repositories/issue/ApiV3IssueRepository.js +11 -13
  17. package/bin/adapter/repositories/issue/ApiV3IssueRepository.js.map +1 -1
  18. package/bin/adapter/repositories/issue/CheerioIssueRepository.js +2 -3
  19. package/bin/adapter/repositories/issue/CheerioIssueRepository.js.map +1 -1
  20. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js +47 -63
  21. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js.map +1 -1
  22. package/bin/adapter/repositories/issue/InternalGraphqlIssueRepository.js +12 -8
  23. package/bin/adapter/repositories/issue/InternalGraphqlIssueRepository.js.map +1 -1
  24. package/bin/adapter/repositories/issue/RestIssueRepository.js +36 -69
  25. package/bin/adapter/repositories/issue/RestIssueRepository.js.map +1 -1
  26. package/bin/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.js +9 -1
  27. package/bin/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.js.map +1 -1
  28. package/jest.config.js +2 -1
  29. package/package.json +2 -3
  30. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.test.ts +0 -78
  31. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.ts +1 -33
  32. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +0 -78
  33. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +1 -33
  34. package/src/adapter/repositories/BaseGitHubRepository.test.ts +43 -33
  35. package/src/adapter/repositories/BaseGitHubRepository.ts +4 -8
  36. package/src/adapter/repositories/CheerioProjectRepository.ts +19 -23
  37. package/src/adapter/repositories/FetchWebhookRepository.ts +2 -1
  38. package/src/adapter/repositories/GraphqlProjectRepository.ts +63 -69
  39. package/src/adapter/repositories/{AxiosSlackRepository.test.ts → KySlackRepository.test.ts} +4 -4
  40. package/src/adapter/repositories/KySlackRepository.ts +297 -0
  41. package/src/adapter/repositories/issue/ApiV3IssueRepository.ts +31 -33
  42. package/src/adapter/repositories/issue/CheerioIssueRepository.test.ts +3 -3
  43. package/src/adapter/repositories/issue/CheerioIssueRepository.ts +2 -3
  44. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +30 -22
  45. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +178 -191
  46. package/src/adapter/repositories/issue/InternalGraphqlIssueRepository.ts +12 -8
  47. package/src/adapter/repositories/issue/RestIssueRepository.ts +51 -91
  48. package/src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.test.ts +20 -0
  49. package/src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.ts +10 -1
  50. package/types/adapter/entry-points/function/getStoryObjectMap.d.ts +1 -1
  51. package/types/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.d.ts +1 -1
  52. package/types/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.d.ts.map +1 -1
  53. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts +1 -1
  54. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  55. package/types/adapter/repositories/BaseGitHubRepository.d.ts +1 -1
  56. package/types/adapter/repositories/BaseGitHubRepository.d.ts.map +1 -1
  57. package/types/adapter/repositories/CheerioProjectRepository.d.ts.map +1 -1
  58. package/types/adapter/repositories/FetchWebhookRepository.d.ts.map +1 -1
  59. package/types/adapter/repositories/GraphqlProjectRepository.d.ts.map +1 -1
  60. package/types/adapter/repositories/{AxiosSlackRepository.d.ts → KySlackRepository.d.ts} +3 -2
  61. package/types/adapter/repositories/KySlackRepository.d.ts.map +1 -0
  62. package/types/adapter/repositories/issue/ApiV3IssueRepository.d.ts.map +1 -1
  63. package/types/adapter/repositories/issue/CheerioIssueRepository.d.ts.map +1 -1
  64. package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts.map +1 -1
  65. package/types/adapter/repositories/issue/InternalGraphqlIssueRepository.d.ts.map +1 -1
  66. package/types/adapter/repositories/issue/RestIssueRepository.d.ts.map +1 -1
  67. package/types/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.d.ts.map +1 -1
  68. package/types/index.d.ts +1 -1
  69. package/bin/adapter/repositories/AxiosSlackRepository.js +0 -188
  70. package/bin/adapter/repositories/AxiosSlackRepository.js.map +0 -1
  71. package/src/adapter/repositories/AxiosSlackRepository.ts +0 -218
  72. package/types/adapter/repositories/AxiosSlackRepository.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AssignNoAssigneeIssueToManagerUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.ts"],"names":[],"mappings":";;;AAIA,MAAa,qCAAqC;IAChD,YACW,eAA4D;QAA5D,oBAAe,GAAf,eAAe,CAA6C;QAGvE,QAAG,GAAG,KAAK,EAAE,KAIZ,EAAiB,EAAE;YAClB,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACpB,OAAO;YACT,CAAC;YACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjC,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;oBACzD,SAAS;gBACX,CAAC;gBACD,MAAM,IAAI,CAAC,eAAe,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;gBACtE,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC,CAAC;IAjBC,CAAC;CAkBL;AArBD,sFAqBC"}
1
+ {"version":3,"file":"AssignNoAssigneeIssueToManagerUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.ts"],"names":[],"mappings":";;;AAIA,MAAa,qCAAqC;IAChD,YACW,eAA4D;QAA5D,oBAAe,GAAf,eAAe,CAA6C;QAGvE,QAAG,GAAG,KAAK,EAAE,KAIZ,EAAiB,EAAE;YAClB,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACpB,OAAO;YACT,CAAC;YACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjC,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;oBACzD,SAAS;gBACX,CAAC;gBACD,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,eAAe,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;gBACxE,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,IAAI,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC,EAAE,CAAC;wBAC1B,MAAM,CAAC,CAAC;oBACV,CAAC;oBACD,MAAM,IAAI,KAAK,CACb,uCAAuC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,OAAO,EAAE,CACjE,CAAC;gBACJ,CAAC;gBACD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC,CAAC;IA1BC,CAAC;CA2BL;AA9BD,sFA8BC"}
package/jest.config.js CHANGED
@@ -3,8 +3,9 @@ module.exports = {
3
3
  testEnvironment: 'node',
4
4
  transform: {
5
5
  '^.+\\.ts?$': 'ts-jest',
6
+ '^.+\\.js$': ['ts-jest', { tsconfig: { allowJs: true } }],
6
7
  },
7
- transformIgnorePatterns: ['<rootDir>/node_modules/'],
8
+ transformIgnorePatterns: ['<rootDir>/node_modules/(?!(ky)/)'],
8
9
  collectCoverage: true,
9
10
  coverageDirectory: 'reports/coverage',
10
11
  reporters: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.35.2",
3
+ "version": "1.36.1",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -72,14 +72,13 @@
72
72
  "typescript": "^5.6.3"
73
73
  },
74
74
  "dependencies": {
75
- "axios": "^1.14.0",
76
- "axios-retry": "^4.5.0",
77
75
  "cheerio": "^1.0.0",
78
76
  "commander": "^14.0.0",
79
77
  "cookie": "^1.0.1",
80
78
  "dotenv": "^17.0.0",
81
79
  "gh-cookie": "^1.3.22",
82
80
  "googleapis": "^146.0.0",
81
+ "ky": "^0.33.3",
83
82
  "typia": "^12.0.0",
84
83
  "yaml": "^2.6.0"
85
84
  }
@@ -1,6 +1,5 @@
1
1
  import fs from 'fs';
2
2
  import YAML from 'yaml';
3
- import axios from 'axios';
4
3
 
5
4
  jest.mock('fs');
6
5
  jest.mock('gh-cookie', () => ({ getCookieContent: jest.fn() }));
@@ -175,81 +174,4 @@ describe('GetStoryObjectMapUseCaseHandler', () => {
175
174
  );
176
175
  }
177
176
  });
178
-
179
- it('should redact Authorization and cookie headers in verbose mode error', async () => {
180
- expect.assertions(4);
181
- const capturedHandlers: Array<(error: unknown) => unknown> = [];
182
- jest
183
- .spyOn(axios.interceptors.response, 'use')
184
- .mockImplementationOnce((_, errorHandler) => {
185
- if (errorHandler) {
186
- capturedHandlers.push(errorHandler);
187
- }
188
- return 0;
189
- });
190
-
191
- const handler = new GetStoryObjectMapUseCaseHandler();
192
- await handler.handle('config.yml', true);
193
-
194
- expect(capturedHandlers).toHaveLength(1);
195
-
196
- const mockAxiosError = {
197
- message: 'Request failed with status code 401',
198
- code: 'ERR_BAD_RESPONSE',
199
- config: {
200
- url: 'https://api.github.com/graphql',
201
- method: 'post',
202
- headers: {
203
- toJSON: () => ({
204
- Authorization: 'Bearer secret-github-token',
205
- cookie: 'session=secret-cookie-value',
206
- 'Content-Type': 'application/json',
207
- }),
208
- },
209
- },
210
- response: { status: 401 },
211
- };
212
-
213
- try {
214
- capturedHandlers[0](mockAxiosError);
215
- } catch (thrownError) {
216
- if (!(thrownError instanceof Error)) {
217
- return;
218
- }
219
- expect(thrownError.message).not.toContain('secret-github-token');
220
- expect(thrownError.message).not.toContain('secret-cookie-value');
221
- expect(thrownError.message).toContain('[REDACTED]');
222
- }
223
- });
224
-
225
- it('should not expose Authorization header in non-verbose mode error', async () => {
226
- expect.assertions(2);
227
- const capturedHandlers: Array<(error: unknown) => unknown> = [];
228
- jest
229
- .spyOn(axios.interceptors.response, 'use')
230
- .mockImplementationOnce((_, errorHandler) => {
231
- if (errorHandler) {
232
- capturedHandlers.push(errorHandler);
233
- }
234
- return 0;
235
- });
236
-
237
- const handler = new GetStoryObjectMapUseCaseHandler();
238
- await handler.handle('config.yml', false);
239
-
240
- expect(capturedHandlers).toHaveLength(1);
241
-
242
- const mockAxiosError = {
243
- response: { status: 403 },
244
- };
245
-
246
- try {
247
- capturedHandlers[0](mockAxiosError);
248
- } catch (thrownError) {
249
- if (!(thrownError instanceof Error)) {
250
- return;
251
- }
252
- expect(thrownError.message).toBe('API Error: 403');
253
- }
254
- });
255
177
  });
@@ -11,7 +11,6 @@ import { LocalStorageCacheRepository } from '../../repositories/LocalStorageCach
11
11
  import { Issue } from '../../../domain/entities/Issue';
12
12
  import { Project } from '../../../domain/entities/Project';
13
13
  import { BaseGitHubRepository } from '../../repositories/BaseGitHubRepository';
14
- import axios, { AxiosError } from 'axios';
15
14
  import { CheerioProjectRepository } from '../../repositories/CheerioProjectRepository';
16
15
  import { GetStoryObjectMapUseCase } from '../../../domain/usecases/GetStoryObjectMapUseCase';
17
16
  import { StoryObjectMap } from '../../../domain/entities/StoryObjectMap';
@@ -19,7 +18,7 @@ import { StoryObjectMap } from '../../../domain/entities/StoryObjectMap';
19
18
  export class GetStoryObjectMapUseCaseHandler {
20
19
  handle = async (
21
20
  configFilePath: string,
22
- verbose: boolean,
21
+ _verbose: boolean,
23
22
  allowCacheMinutes?: number,
24
23
  ): Promise<{
25
24
  project: Project;
@@ -27,37 +26,6 @@ export class GetStoryObjectMapUseCaseHandler {
27
26
  cacheUsed: boolean;
28
27
  storyObjectMap: StoryObjectMap;
29
28
  }> => {
30
- axios.interceptors.response.use(
31
- (response) => response,
32
- (error: AxiosError) => {
33
- if (verbose) {
34
- const rawHeaders = error.config?.headers?.toJSON() ?? {};
35
- const sanitizedHeaders: Record<string, unknown> = {};
36
- for (const [key, value] of Object.entries(rawHeaders)) {
37
- sanitizedHeaders[key] =
38
- key.toLowerCase() === 'authorization' ||
39
- key.toLowerCase() === 'cookie'
40
- ? '[REDACTED]'
41
- : value;
42
- }
43
- throw new Error(
44
- `API Error: ${JSON.stringify({
45
- message: error.message,
46
- code: error.code,
47
- status: error.response?.status,
48
- url: error.config?.url,
49
- method: error.config?.method,
50
- headers: sanitizedHeaders,
51
- })}`,
52
- );
53
- }
54
- if (error.response) {
55
- throw new Error(`API Error: ${error.response.status}`);
56
- }
57
- throw new Error('Network Error');
58
- },
59
- );
60
-
61
29
  const configFileContent = fs.readFileSync(configFilePath, 'utf8');
62
30
  const input: unknown = YAML.parse(configFileContent);
63
31
  type inputType = Parameters<GetStoryObjectMapUseCase['run']>[0] & {
@@ -1,6 +1,5 @@
1
1
  import fs from 'fs';
2
2
  import YAML from 'yaml';
3
- import axios from 'axios';
4
3
 
5
4
  jest.mock('fs');
6
5
  jest.mock('gh-cookie', () => ({ getCookieContent: jest.fn() }));
@@ -238,81 +237,4 @@ describe('HandleScheduledEventUseCaseHandler', () => {
238
237
  );
239
238
  }
240
239
  });
241
-
242
- it('should redact Authorization and cookie headers in verbose mode error', async () => {
243
- expect.assertions(4);
244
- const capturedHandlers: Array<(error: unknown) => unknown> = [];
245
- jest
246
- .spyOn(axios.interceptors.response, 'use')
247
- .mockImplementationOnce((_, errorHandler) => {
248
- if (errorHandler) {
249
- capturedHandlers.push(errorHandler);
250
- }
251
- return 0;
252
- });
253
-
254
- const handler = new HandleScheduledEventUseCaseHandler();
255
- await handler.handle('config.yml', true);
256
-
257
- expect(capturedHandlers).toHaveLength(1);
258
-
259
- const mockAxiosError = {
260
- message: 'Request failed with status code 401',
261
- code: 'ERR_BAD_RESPONSE',
262
- config: {
263
- url: 'https://api.github.com/graphql',
264
- method: 'post',
265
- headers: {
266
- toJSON: () => ({
267
- Authorization: 'Bearer secret-github-token',
268
- cookie: 'session=secret-cookie-value',
269
- 'Content-Type': 'application/json',
270
- }),
271
- },
272
- },
273
- response: { status: 401 },
274
- };
275
-
276
- try {
277
- capturedHandlers[0](mockAxiosError);
278
- } catch (thrownError) {
279
- if (!(thrownError instanceof Error)) {
280
- return;
281
- }
282
- expect(thrownError.message).not.toContain('secret-github-token');
283
- expect(thrownError.message).not.toContain('secret-cookie-value');
284
- expect(thrownError.message).toContain('[REDACTED]');
285
- }
286
- });
287
-
288
- it('should not expose Authorization header in non-verbose mode error', async () => {
289
- expect.assertions(2);
290
- const capturedHandlers: Array<(error: unknown) => unknown> = [];
291
- jest
292
- .spyOn(axios.interceptors.response, 'use')
293
- .mockImplementationOnce((_, errorHandler) => {
294
- if (errorHandler) {
295
- capturedHandlers.push(errorHandler);
296
- }
297
- return 0;
298
- });
299
-
300
- const handler = new HandleScheduledEventUseCaseHandler();
301
- await handler.handle('config.yml', false);
302
-
303
- expect(capturedHandlers).toHaveLength(1);
304
-
305
- const mockAxiosError = {
306
- response: { status: 403 },
307
- };
308
-
309
- try {
310
- capturedHandlers[0](mockAxiosError);
311
- } catch (thrownError) {
312
- if (!(thrownError instanceof Error)) {
313
- return;
314
- }
315
- expect(thrownError.message).toBe('API Error: 403');
316
- }
317
- });
318
240
  });
@@ -21,7 +21,6 @@ import { BaseGitHubRepository } from '../../repositories/BaseGitHubRepository';
21
21
  import { AnalyzeStoriesUseCase } from '../../../domain/usecases/AnalyzeStoriesUseCase';
22
22
  import { ClearDependedIssueURLUseCase } from '../../../domain/usecases/ClearDependedIssueURLUseCase';
23
23
  import { CreateEstimationIssueUseCase } from '../../../domain/usecases/CreateEstimationIssueUseCase';
24
- import axios, { AxiosError } from 'axios';
25
24
  import { ConvertCheckboxToIssueInStoryIssueUseCase } from '../../../domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase';
26
25
  import { ChangeStatusByStoryColorUseCase } from '../../../domain/usecases/ChangeStatusByStoryColorUseCase';
27
26
  import { SetNoStoryIssueToStoryUseCase } from '../../../domain/usecases/SetNoStoryIssueToStoryUseCase';
@@ -40,44 +39,13 @@ import { FetchWebhookRepository } from '../../repositories/FetchWebhookRepositor
40
39
  export class HandleScheduledEventUseCaseHandler {
41
40
  handle = async (
42
41
  configFilePath: string,
43
- verbose: boolean,
42
+ _verbose: boolean,
44
43
  ): Promise<{
45
44
  project: Project;
46
45
  issues: Issue[];
47
46
  cacheUsed: boolean;
48
47
  targetDateTimes: Date[];
49
48
  } | null> => {
50
- axios.interceptors.response.use(
51
- (response) => response,
52
- (error: AxiosError) => {
53
- if (verbose) {
54
- const rawHeaders = error.config?.headers.toJSON() ?? {};
55
- const sanitizedHeaders: Record<string, unknown> = {};
56
- for (const [key, value] of Object.entries(rawHeaders)) {
57
- sanitizedHeaders[key] =
58
- key.toLowerCase() === 'authorization' ||
59
- key.toLowerCase() === 'cookie'
60
- ? '[REDACTED]'
61
- : value;
62
- }
63
- throw new Error(
64
- `API Error: ${JSON.stringify({
65
- message: error.message,
66
- code: error.code,
67
- status: error.response?.status,
68
- url: error.config?.url,
69
- method: error.config?.method,
70
- headers: sanitizedHeaders,
71
- })}`,
72
- );
73
- }
74
- if (error.response) {
75
- throw new Error(`API Error: ${error.response.status}`);
76
- }
77
- throw new Error('Network Error');
78
- },
79
- );
80
-
81
49
  const configFileContent = fs.readFileSync(configFilePath, 'utf8');
82
50
  const input: unknown = YAML.parse(configFileContent);
83
51
  type inputType = Parameters<HandleScheduledEventUseCase['run']>[0] & {
@@ -1,16 +1,25 @@
1
- import fs from 'fs';
2
- import { BaseGitHubRepository } from './BaseGitHubRepository';
3
- import resetAllMocks = jest.resetAllMocks;
4
- import { LocalStorageRepository } from './LocalStorageRepository';
1
+ const mockKyGetText = jest.fn<Promise<string>, []>();
2
+ const mockKyGet = jest.fn(() => ({ text: mockKyGetText }));
5
3
 
6
- const mockAxiosGet = jest.fn<Promise<unknown>, unknown[]>();
7
- jest.mock('axios', () => ({
4
+ jest.mock('ky', () => ({
8
5
  default: {
9
- get: (...args: unknown[]): Promise<unknown> => mockAxiosGet(...args),
6
+ get: mockKyGet,
7
+ post: jest.fn(),
8
+ put: jest.fn(),
9
+ patch: jest.fn(),
10
+ delete: jest.fn(),
11
+ extend: jest.fn(),
12
+ create: jest.fn(),
13
+ stop: jest.fn(),
10
14
  },
11
- get: (...args: unknown[]): Promise<unknown> => mockAxiosGet(...args),
15
+ __esModule: true,
12
16
  }));
13
17
 
18
+ import fs from 'fs';
19
+ import { BaseGitHubRepository } from './BaseGitHubRepository';
20
+ import resetAllMocks = jest.resetAllMocks;
21
+ import { LocalStorageRepository } from './LocalStorageRepository';
22
+
14
23
  const mockGetCookieContent = jest.fn<Promise<unknown>, unknown[]>();
15
24
  jest.mock('gh-cookie', () => ({
16
25
  getCookieContent: (...args: unknown[]): Promise<unknown> =>
@@ -141,7 +150,8 @@ describe('BaseGitHubRepository', () => {
141
150
  ]);
142
151
 
143
152
  beforeEach(() => {
144
- mockAxiosGet.mockReset();
153
+ mockKyGet.mockReset().mockReturnValue({ text: mockKyGetText });
154
+ mockKyGetText.mockReset();
145
155
  mockGetCookieContent.mockReset();
146
156
  mockGetCookieContent.mockResolvedValue(validCookieJson);
147
157
  fs.writeFileSync(refreshCookieJsonFilePath, validCookieJson);
@@ -155,25 +165,25 @@ describe('BaseGitHubRepository', () => {
155
165
 
156
166
  it('should return when HTML contains user-login meta tag for current user (logged in)', async () => {
157
167
  const repository = new RefreshTestRepository();
158
- mockAxiosGet.mockResolvedValueOnce({
159
- data: `<html><head><meta name="user-login" content="${ghUserName}"></head><body><h1>${ghUserName}</h1></body></html>`,
160
- });
168
+ mockKyGetText.mockResolvedValueOnce(
169
+ `<html><head><meta name="user-login" content="${ghUserName}"></head><body><h1>${ghUserName}</h1></body></html>`,
170
+ );
161
171
 
162
172
  await expect(repository.refreshCookie()).resolves.toBeUndefined();
163
173
 
164
- expect(mockAxiosGet).toHaveBeenCalledWith(
174
+ expect(mockKyGet).toHaveBeenCalledWith(
165
175
  `https://github.com/${ghUserName}`,
166
176
  expect.anything(),
167
177
  );
168
- expect(mockAxiosGet).toHaveBeenCalledTimes(1);
178
+ expect(mockKyGet).toHaveBeenCalledTimes(1);
169
179
  });
170
180
 
171
181
  it('should fail when HTML contains username in content but not in user-login meta tag (not logged in)', async () => {
172
182
  const repository = new RefreshTestRepository();
173
183
  const notLoggedInHtml = `<html><head><meta name="user-login" content=""></head><body><h1>${ghUserName}</h1><p>Public profile</p></body></html>`;
174
- mockAxiosGet
175
- .mockResolvedValueOnce({ data: notLoggedInHtml })
176
- .mockResolvedValueOnce({ data: notLoggedInHtml });
184
+ mockKyGetText
185
+ .mockResolvedValueOnce(notLoggedInHtml)
186
+ .mockResolvedValueOnce(notLoggedInHtml);
177
187
 
178
188
  await expect(repository.refreshCookie()).rejects.toThrow(
179
189
  'Failed to refresh cookie',
@@ -182,17 +192,17 @@ describe('BaseGitHubRepository', () => {
182
192
 
183
193
  it('should use profile page URL not homepage to check authentication', async () => {
184
194
  const repository = new RefreshTestRepository();
185
- mockAxiosGet.mockResolvedValueOnce({
186
- data: `<html><head><meta name="user-login" content="${ghUserName}"></head><body></body></html>`,
187
- });
195
+ mockKyGetText.mockResolvedValueOnce(
196
+ `<html><head><meta name="user-login" content="${ghUserName}"></head><body></body></html>`,
197
+ );
188
198
 
189
199
  await repository.refreshCookie();
190
200
 
191
- expect(mockAxiosGet).toHaveBeenCalledWith(
201
+ expect(mockKyGet).toHaveBeenCalledWith(
192
202
  `https://github.com/${ghUserName}`,
193
203
  expect.anything(),
194
204
  );
195
- expect(mockAxiosGet).not.toHaveBeenCalledWith(
205
+ expect(mockKyGet).not.toHaveBeenCalledWith(
196
206
  'https://github.com',
197
207
  expect.anything(),
198
208
  );
@@ -201,9 +211,9 @@ describe('BaseGitHubRepository', () => {
201
211
  it('should throw when both profile page checks fail', async () => {
202
212
  const repository = new RefreshTestRepository();
203
213
  const notLoggedInHtml = `<html><head><meta name="user-login" content=""></head><body></body></html>`;
204
- mockAxiosGet
205
- .mockResolvedValueOnce({ data: notLoggedInHtml })
206
- .mockResolvedValueOnce({ data: notLoggedInHtml });
214
+ mockKyGetText
215
+ .mockResolvedValueOnce(notLoggedInHtml)
216
+ .mockResolvedValueOnce(notLoggedInHtml);
207
217
 
208
218
  await expect(repository.refreshCookie()).rejects.toThrow(
209
219
  'Failed to refresh cookie',
@@ -212,16 +222,16 @@ describe('BaseGitHubRepository', () => {
212
222
 
213
223
  it('should reset cookie cache before regenerating so new cookie is used', async () => {
214
224
  const repository = new RefreshTestRepository();
215
- mockAxiosGet
216
- .mockResolvedValueOnce({
217
- data: `<html><head><meta name="user-login" content=""></head><body></body></html>`,
218
- })
219
- .mockResolvedValueOnce({
220
- data: `<html><head><meta name="user-login" content="${ghUserName}"></head><body></body></html>`,
221
- });
225
+ mockKyGetText
226
+ .mockResolvedValueOnce(
227
+ `<html><head><meta name="user-login" content=""></head><body></body></html>`,
228
+ )
229
+ .mockResolvedValueOnce(
230
+ `<html><head><meta name="user-login" content="${ghUserName}"></head><body></body></html>`,
231
+ );
222
232
 
223
233
  await expect(repository.refreshCookie()).resolves.toBeUndefined();
224
- expect(mockAxiosGet).toHaveBeenCalledTimes(2);
234
+ expect(mockKyGet).toHaveBeenCalledTimes(2);
225
235
  expect(mockGetCookieContent).toHaveBeenCalledTimes(1);
226
236
  });
227
237
  });
@@ -3,7 +3,7 @@ import { serialize } from 'cookie';
3
3
  import { getCookieContent } from 'gh-cookie';
4
4
  import fs from 'fs';
5
5
  import { LocalStorageRepository } from './LocalStorageRepository';
6
- import axios from 'axios';
6
+ import ky from 'ky';
7
7
 
8
8
  interface Cookie {
9
9
  name: string;
@@ -54,7 +54,7 @@ export class BaseGitHubRepository {
54
54
  }
55
55
  return this.cookie;
56
56
  };
57
- createHeader = async (): Promise<object> => {
57
+ createHeader = async (): Promise<Record<string, string>> => {
58
58
  const cookie = await this.getCookie();
59
59
  const headers = {
60
60
  accept:
@@ -169,18 +169,14 @@ export class BaseGitHubRepository {
169
169
  }
170
170
  const profileUrl = `https://github.com/${this.ghUserName}`;
171
171
  const headers = await this.createHeader();
172
- const content = await axios.get<string>(profileUrl, { headers });
173
- const html = content.data;
172
+ const html = await ky.get(profileUrl, { headers }).text();
174
173
  if (html.includes(`meta name="user-login" content="${this.ghUserName}"`)) {
175
174
  return;
176
175
  }
177
176
  this.localStorageRepository.remove(this.jsonFilePath);
178
177
  this.cookie = null;
179
178
  const newHeaders = await this.createHeader();
180
- const newContent = await axios.get<string>(profileUrl, {
181
- headers: newHeaders,
182
- });
183
- const newHtml = newContent.data;
179
+ const newHtml = await ky.get(profileUrl, { headers: newHeaders }).text();
184
180
  if (
185
181
  newHtml.includes(`meta name="user-login" content="${this.ghUserName}"`)
186
182
  ) {
@@ -1,4 +1,4 @@
1
- import axios from 'axios';
1
+ import ky from 'ky';
2
2
  import { BaseGitHubRepository } from './BaseGitHubRepository';
3
3
  import { LocalStorageRepository } from './LocalStorageRepository';
4
4
  import { FieldOption, Project } from '../../domain/entities/Project';
@@ -32,34 +32,30 @@ export class CheerioProjectRepository
32
32
  id: FieldOption['id'] | null;
33
33
  })[],
34
34
  ): Promise<FieldOption[]> => {
35
- const headers = await this.createHeader();
36
- const res = await axios.put<{
37
- memexProjectColumn: {
38
- id: string;
39
- settings: {
40
- width: number;
41
- options: FieldOption[];
42
- };
43
- };
44
- }>(
45
- `https://github.com/memexes/${project.databaseId}/columns`,
46
- {
47
- memexProjectColumnId: project.story?.databaseId,
48
- settings: {
49
- width: 200,
50
- options: newStoryList,
35
+ const browserHeaders = await this.createHeader();
36
+ const raw = await ky
37
+ .put(`https://github.com/memexes/${project.databaseId}/columns`, {
38
+ json: {
39
+ memexProjectColumnId: project.story?.databaseId,
40
+ settings: { width: 200, options: newStoryList },
51
41
  },
52
- },
53
- {
54
42
  headers: {
55
43
  'github-verified-fetch': 'true',
56
44
  origin: 'https://github.com',
57
45
  'x-requested-with': 'XMLHttpRequest',
58
- ...headers,
46
+ ...browserHeaders,
59
47
  },
60
- },
61
- );
62
- return res.data.memexProjectColumn.settings.options.map((v) => ({
48
+ })
49
+ .json<{
50
+ memexProjectColumn: {
51
+ id: string;
52
+ settings: {
53
+ width: number;
54
+ options: FieldOption[];
55
+ };
56
+ };
57
+ }>();
58
+ return raw.memexProjectColumn.settings.options.map((v) => ({
63
59
  id: v.id,
64
60
  name: v.name,
65
61
  color: v.color,
@@ -1,7 +1,8 @@
1
+ import ky from 'ky';
1
2
  import { WebhookRepository } from '../../domain/usecases/adapter-interfaces/WebhookRepository';
2
3
 
3
4
  export class FetchWebhookRepository implements WebhookRepository {
4
5
  async sendGetRequest(url: string): Promise<void> {
5
- await fetch(url);
6
+ await ky.get(url);
6
7
  }
7
8
  }