github-issue-tower-defence-management 1.153.6 → 1.154.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.
- package/CHANGELOG.md +14 -0
- package/README.md +6 -3
- package/bin/adapter/repositories/GraphqlProjectRepository.js +102 -114
- package/bin/adapter/repositories/GraphqlProjectRepository.js.map +1 -1
- package/bin/adapter/repositories/RestProjectRepository.js +95 -0
- package/bin/adapter/repositories/RestProjectRepository.js.map +1 -0
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +26 -6
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
- package/bin/adapter/repositories/issue/RestIssueRepository.js +6 -0
- package/bin/adapter/repositories/issue/RestIssueRepository.js.map +1 -1
- package/bin/adapter/repositories/projectFieldDefinition.js +88 -0
- package/bin/adapter/repositories/projectFieldDefinition.js.map +1 -0
- package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js +10 -16
- package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +3 -0
- package/src/adapter/repositories/GraphqlProjectRepository.diskCache.test.ts +16 -4
- package/src/adapter/repositories/GraphqlProjectRepository.test.ts +8 -27
- package/src/adapter/repositories/GraphqlProjectRepository.ts +139 -151
- package/src/adapter/repositories/GraphqlProjectRepositoryProjectLocation.test.ts +296 -0
- package/src/adapter/repositories/RestProjectRepository.test.ts +277 -0
- package/src/adapter/repositories/RestProjectRepository.ts +129 -0
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +58 -0
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +36 -6
- package/src/adapter/repositories/issue/RestIssueRepository.test.ts +19 -0
- package/src/adapter/repositories/issue/RestIssueRepository.ts +13 -0
- package/src/adapter/repositories/projectFieldDefinition.test.ts +189 -0
- package/src/adapter/repositories/projectFieldDefinition.ts +131 -0
- package/src/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.test.ts +211 -200
- package/src/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.ts +13 -18
- package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +5 -0
- package/types/adapter/repositories/GraphqlProjectRepository.d.ts +6 -1
- package/types/adapter/repositories/GraphqlProjectRepository.d.ts.map +1 -1
- package/types/adapter/repositories/RestProjectRepository.d.ts +18 -0
- package/types/adapter/repositories/RestProjectRepository.d.ts.map +1 -0
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +6 -2
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
- package/types/adapter/repositories/issue/RestIssueRepository.d.ts +1 -0
- package/types/adapter/repositories/issue/RestIssueRepository.d.ts.map +1 -1
- package/types/adapter/repositories/projectFieldDefinition.d.ts +17 -0
- package/types/adapter/repositories/projectFieldDefinition.d.ts.map +1 -0
- package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts +2 -2
- package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +2 -0
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProjectFieldDefinition,
|
|
3
|
+
convertToFieldOptionColor,
|
|
4
|
+
projectFromDefinition,
|
|
5
|
+
} from './projectFieldDefinition';
|
|
6
|
+
|
|
7
|
+
describe('convertToFieldOptionColor', () => {
|
|
8
|
+
it('should preserve PINK so the Todo by human status button renders pink', () => {
|
|
9
|
+
expect(convertToFieldOptionColor('PINK')).toEqual('PINK');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should preserve ORANGE so the Unread status button renders orange', () => {
|
|
13
|
+
expect(convertToFieldOptionColor('ORANGE')).toEqual('ORANGE');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should preserve the remaining GitHub project option colors', () => {
|
|
17
|
+
expect(convertToFieldOptionColor('RED')).toEqual('RED');
|
|
18
|
+
expect(convertToFieldOptionColor('YELLOW')).toEqual('YELLOW');
|
|
19
|
+
expect(convertToFieldOptionColor('GREEN')).toEqual('GREEN');
|
|
20
|
+
expect(convertToFieldOptionColor('BLUE')).toEqual('BLUE');
|
|
21
|
+
expect(convertToFieldOptionColor('PURPLE')).toEqual('PURPLE');
|
|
22
|
+
expect(convertToFieldOptionColor('GRAY')).toEqual('GRAY');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should fall back to GRAY for an unknown color value', () => {
|
|
26
|
+
expect(convertToFieldOptionColor('UNKNOWN')).toEqual('GRAY');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('projectFromDefinition', () => {
|
|
31
|
+
const statusField: ProjectFieldDefinition = {
|
|
32
|
+
fieldId: 'PVTSSF_status',
|
|
33
|
+
databaseId: 12940049,
|
|
34
|
+
name: 'Status',
|
|
35
|
+
options: [
|
|
36
|
+
{
|
|
37
|
+
id: 'f75ad846',
|
|
38
|
+
name: 'Unread',
|
|
39
|
+
color: 'ORANGE',
|
|
40
|
+
description: '',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
const storyField: ProjectFieldDefinition = {
|
|
45
|
+
fieldId: 'PVTSSF_story',
|
|
46
|
+
databaseId: 133939017,
|
|
47
|
+
name: 'story',
|
|
48
|
+
options: [
|
|
49
|
+
{
|
|
50
|
+
id: '6dc26727',
|
|
51
|
+
name: 'regular / workflow management',
|
|
52
|
+
color: 'BLUE',
|
|
53
|
+
description: 'workflow',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: '4ada6c4c',
|
|
57
|
+
name: 'regular / entertainment',
|
|
58
|
+
color: 'BLUE',
|
|
59
|
+
description: '',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
it('should map the status field and every optional field it finds by name', () => {
|
|
65
|
+
const project = projectFromDefinition({
|
|
66
|
+
id: 'PVT_project',
|
|
67
|
+
url: 'https://github.com/users/HiromiShikata/projects/48',
|
|
68
|
+
databaseId: 1403371,
|
|
69
|
+
name: 'UMINO',
|
|
70
|
+
fields: [
|
|
71
|
+
statusField,
|
|
72
|
+
storyField,
|
|
73
|
+
{
|
|
74
|
+
fieldId: 'PVTF_nextactiondate',
|
|
75
|
+
databaseId: 35978365,
|
|
76
|
+
name: 'nextactiondate',
|
|
77
|
+
options: [],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
fieldId: 'PVTSSF_nextactionhour',
|
|
81
|
+
databaseId: 133948391,
|
|
82
|
+
name: 'nextactionhour',
|
|
83
|
+
options: [],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
fieldId: 'PVTF_remaining',
|
|
87
|
+
databaseId: 1,
|
|
88
|
+
name: 'remaining estimation minutes',
|
|
89
|
+
options: [],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
fieldId: 'PVTF_depended',
|
|
93
|
+
databaseId: 156128545,
|
|
94
|
+
name: 'Depended Issue URL separated by comma',
|
|
95
|
+
options: [],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
fieldId: 'PVTF_completion',
|
|
99
|
+
databaseId: 2,
|
|
100
|
+
name: 'Completion Date (50% Confidence)',
|
|
101
|
+
options: [],
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(project).toEqual({
|
|
107
|
+
id: 'PVT_project',
|
|
108
|
+
url: 'https://github.com/users/HiromiShikata/projects/48',
|
|
109
|
+
databaseId: 1403371,
|
|
110
|
+
name: 'UMINO',
|
|
111
|
+
status: {
|
|
112
|
+
name: 'Status',
|
|
113
|
+
fieldId: 'PVTSSF_status',
|
|
114
|
+
statuses: statusField.options,
|
|
115
|
+
},
|
|
116
|
+
nextActionDate: {
|
|
117
|
+
name: 'nextactiondate',
|
|
118
|
+
fieldId: 'PVTF_nextactiondate',
|
|
119
|
+
},
|
|
120
|
+
nextActionHour: {
|
|
121
|
+
name: 'nextactionhour',
|
|
122
|
+
fieldId: 'PVTSSF_nextactionhour',
|
|
123
|
+
},
|
|
124
|
+
story: {
|
|
125
|
+
name: 'story',
|
|
126
|
+
fieldId: 'PVTSSF_story',
|
|
127
|
+
databaseId: 133939017,
|
|
128
|
+
stories: storyField.options,
|
|
129
|
+
workflowManagementStory: storyField.options[0],
|
|
130
|
+
},
|
|
131
|
+
remainingEstimationMinutes: {
|
|
132
|
+
name: 'remaining estimation minutes',
|
|
133
|
+
fieldId: 'PVTF_remaining',
|
|
134
|
+
},
|
|
135
|
+
dependedIssueUrlSeparatedByComma: {
|
|
136
|
+
name: 'Depended Issue URL separated by comma',
|
|
137
|
+
fieldId: 'PVTF_depended',
|
|
138
|
+
},
|
|
139
|
+
completionDate50PercentConfidence: {
|
|
140
|
+
name: 'Completion Date (50% Confidence)',
|
|
141
|
+
fieldId: 'PVTF_completion',
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should leave every optional field null when the project carries only the status field', () => {
|
|
147
|
+
const project = projectFromDefinition({
|
|
148
|
+
id: 'PVT_project',
|
|
149
|
+
url: 'https://github.com/orgs/X-Mile/projects/7',
|
|
150
|
+
databaseId: 7,
|
|
151
|
+
name: 'minimal',
|
|
152
|
+
fields: [statusField],
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
expect(project.story).toBeNull();
|
|
156
|
+
expect(project.nextActionDate).toBeNull();
|
|
157
|
+
expect(project.nextActionHour).toBeNull();
|
|
158
|
+
expect(project.remainingEstimationMinutes).toBeNull();
|
|
159
|
+
expect(project.dependedIssueUrlSeparatedByComma).toBeNull();
|
|
160
|
+
expect(project.completionDate50PercentConfidence).toBeNull();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should leave the story null when no option names the workflow management story', () => {
|
|
164
|
+
const project = projectFromDefinition({
|
|
165
|
+
id: 'PVT_project',
|
|
166
|
+
url: 'https://github.com/users/HiromiShikata/projects/48',
|
|
167
|
+
databaseId: 1403371,
|
|
168
|
+
name: 'UMINO',
|
|
169
|
+
fields: [
|
|
170
|
+
statusField,
|
|
171
|
+
{ ...storyField, options: [storyField.options[1]] },
|
|
172
|
+
],
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(project.story).toBeNull();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should throw when the project has no status field', () => {
|
|
179
|
+
expect(() =>
|
|
180
|
+
projectFromDefinition({
|
|
181
|
+
id: 'PVT_project',
|
|
182
|
+
url: 'https://github.com/users/HiromiShikata/projects/48',
|
|
183
|
+
databaseId: 1403371,
|
|
184
|
+
name: 'UMINO',
|
|
185
|
+
fields: [storyField],
|
|
186
|
+
}),
|
|
187
|
+
).toThrow('status field is not found');
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { FieldOption, Project } from '../../domain/entities/Project';
|
|
2
|
+
import {
|
|
3
|
+
DEPENDED_ISSUE_URL_FIELD_NAME,
|
|
4
|
+
NEXT_ACTION_DATE_FIELD_NAME,
|
|
5
|
+
NEXT_ACTION_HOUR_FIELD_NAME,
|
|
6
|
+
STORY_FIELD_NAME,
|
|
7
|
+
} from '../../domain/entities/RequiredProjectField';
|
|
8
|
+
import { normalizeFieldName } from './utils';
|
|
9
|
+
|
|
10
|
+
export type ProjectFieldDefinition = {
|
|
11
|
+
fieldId: string;
|
|
12
|
+
databaseId: number;
|
|
13
|
+
name: string;
|
|
14
|
+
options: FieldOption[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ProjectDefinition = {
|
|
18
|
+
id: string;
|
|
19
|
+
url: string;
|
|
20
|
+
databaseId: number;
|
|
21
|
+
name: string;
|
|
22
|
+
fields: ProjectFieldDefinition[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const convertToFieldOptionColor = (
|
|
26
|
+
color: string,
|
|
27
|
+
): FieldOption['color'] => {
|
|
28
|
+
switch (color) {
|
|
29
|
+
case 'RED':
|
|
30
|
+
case 'YELLOW':
|
|
31
|
+
case 'GREEN':
|
|
32
|
+
case 'BLUE':
|
|
33
|
+
case 'PURPLE':
|
|
34
|
+
case 'ORANGE':
|
|
35
|
+
case 'PINK':
|
|
36
|
+
case 'GRAY':
|
|
37
|
+
return color;
|
|
38
|
+
default:
|
|
39
|
+
return 'GRAY';
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const projectFromDefinition = (
|
|
44
|
+
definition: ProjectDefinition,
|
|
45
|
+
): Project => {
|
|
46
|
+
const nextActionDate = definition.fields.find(
|
|
47
|
+
(field) =>
|
|
48
|
+
normalizeFieldName(field.name) ===
|
|
49
|
+
normalizeFieldName(NEXT_ACTION_DATE_FIELD_NAME),
|
|
50
|
+
);
|
|
51
|
+
const nextActionHour = definition.fields.find(
|
|
52
|
+
(field) =>
|
|
53
|
+
normalizeFieldName(field.name) ===
|
|
54
|
+
normalizeFieldName(NEXT_ACTION_HOUR_FIELD_NAME),
|
|
55
|
+
);
|
|
56
|
+
const status = definition.fields.find(
|
|
57
|
+
(field) => normalizeFieldName(field.name) === 'status',
|
|
58
|
+
);
|
|
59
|
+
if (!status) {
|
|
60
|
+
throw new Error('status field is not found');
|
|
61
|
+
}
|
|
62
|
+
const story = definition.fields.find(
|
|
63
|
+
(field) =>
|
|
64
|
+
normalizeFieldName(field.name) === normalizeFieldName(STORY_FIELD_NAME),
|
|
65
|
+
);
|
|
66
|
+
const workflowManagementStory = story?.options.find((option) =>
|
|
67
|
+
normalizeFieldName(option.name).includes('workflowmanagement'),
|
|
68
|
+
);
|
|
69
|
+
const remainingEstimationMinutes = definition.fields.find(
|
|
70
|
+
(field) => normalizeFieldName(field.name) === 'remainingestimationminutes',
|
|
71
|
+
);
|
|
72
|
+
const dependedIssueUrlSeparatedByComma = definition.fields.find((field) =>
|
|
73
|
+
normalizeFieldName(field.name).startsWith(
|
|
74
|
+
normalizeFieldName(DEPENDED_ISSUE_URL_FIELD_NAME),
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
const completionDate50PercentConfidence = definition.fields.find((field) =>
|
|
78
|
+
normalizeFieldName(field.name).startsWith('completiondate'),
|
|
79
|
+
);
|
|
80
|
+
return {
|
|
81
|
+
id: definition.id,
|
|
82
|
+
url: definition.url,
|
|
83
|
+
databaseId: definition.databaseId,
|
|
84
|
+
name: definition.name,
|
|
85
|
+
status: {
|
|
86
|
+
name: status.name,
|
|
87
|
+
fieldId: status.fieldId,
|
|
88
|
+
statuses: status.options,
|
|
89
|
+
},
|
|
90
|
+
nextActionDate: nextActionDate
|
|
91
|
+
? {
|
|
92
|
+
name: nextActionDate.name,
|
|
93
|
+
fieldId: nextActionDate.fieldId,
|
|
94
|
+
}
|
|
95
|
+
: null,
|
|
96
|
+
nextActionHour: nextActionHour
|
|
97
|
+
? {
|
|
98
|
+
name: nextActionHour.name,
|
|
99
|
+
fieldId: nextActionHour.fieldId,
|
|
100
|
+
}
|
|
101
|
+
: null,
|
|
102
|
+
story:
|
|
103
|
+
story && workflowManagementStory
|
|
104
|
+
? {
|
|
105
|
+
name: story.name,
|
|
106
|
+
fieldId: story.fieldId,
|
|
107
|
+
databaseId: story.databaseId,
|
|
108
|
+
stories: story.options,
|
|
109
|
+
workflowManagementStory,
|
|
110
|
+
}
|
|
111
|
+
: null,
|
|
112
|
+
remainingEstimationMinutes: remainingEstimationMinutes
|
|
113
|
+
? {
|
|
114
|
+
name: remainingEstimationMinutes.name,
|
|
115
|
+
fieldId: remainingEstimationMinutes.fieldId,
|
|
116
|
+
}
|
|
117
|
+
: null,
|
|
118
|
+
dependedIssueUrlSeparatedByComma: dependedIssueUrlSeparatedByComma
|
|
119
|
+
? {
|
|
120
|
+
name: dependedIssueUrlSeparatedByComma.name,
|
|
121
|
+
fieldId: dependedIssueUrlSeparatedByComma.fieldId,
|
|
122
|
+
}
|
|
123
|
+
: null,
|
|
124
|
+
completionDate50PercentConfidence: completionDate50PercentConfidence
|
|
125
|
+
? {
|
|
126
|
+
name: completionDate50PercentConfidence.name,
|
|
127
|
+
fieldId: completionDate50PercentConfidence.fieldId,
|
|
128
|
+
}
|
|
129
|
+
: null,
|
|
130
|
+
};
|
|
131
|
+
};
|