npm-cli-gh-issue-preparator 1.5.0 → 1.7.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/repositories/GitHubIssueRepository.js +47 -0
- package/bin/adapter/repositories/GitHubIssueRepository.js.map +1 -1
- package/bin/adapter/repositories/GitHubProjectRepository.js +71 -0
- package/bin/adapter/repositories/GitHubProjectRepository.js.map +1 -1
- package/bin/domain/entities/Comment.js +3 -0
- package/bin/domain/entities/Comment.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/repositories/GitHubIssueRepository.test.ts +174 -0
- package/src/adapter/repositories/GitHubIssueRepository.ts +61 -0
- package/src/adapter/repositories/GitHubProjectRepository.test.ts +152 -1
- package/src/adapter/repositories/GitHubProjectRepository.ts +87 -0
- package/src/domain/entities/Comment.ts +5 -0
- package/src/domain/entities/Issue.ts +3 -0
- package/src/domain/entities/Project.ts +1 -0
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +4 -0
- package/src/domain/usecases/StartPreparationUseCase.test.ts +12 -0
- package/src/domain/usecases/adapter-interfaces/ProjectRepository.ts +1 -0
- package/types/adapter/repositories/GitHubIssueRepository.d.ts +1 -0
- package/types/adapter/repositories/GitHubIssueRepository.d.ts.map +1 -1
- package/types/adapter/repositories/GitHubProjectRepository.d.ts +1 -0
- package/types/adapter/repositories/GitHubProjectRepository.d.ts.map +1 -1
- package/types/domain/entities/Comment.d.ts +6 -0
- package/types/domain/entities/Comment.d.ts.map +1 -0
- package/types/domain/entities/Issue.d.ts +2 -0
- package/types/domain/entities/Issue.d.ts.map +1 -1
- package/types/domain/entities/Project.d.ts +1 -0
- package/types/domain/entities/Project.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/ProjectRepository.d.ts +1 -0
- package/types/domain/usecases/adapter-interfaces/ProjectRepository.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [1.7.0](https://github.com/HiromiShikata/npm-cli-gh-issue-preparator/compare/v1.6.0...v1.7.0) (2026-01-31)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **core:** add Comment domain entity with author, content, createdAt ([8e3e861](https://github.com/HiromiShikata/npm-cli-gh-issue-preparator/commit/8e3e8617284f818cf72f9e4c33afac9748b2661a))
|
|
7
|
+
|
|
8
|
+
# [1.6.0](https://github.com/HiromiShikata/npm-cli-gh-issue-preparator/compare/v1.5.0...v1.6.0) (2026-01-26)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **core:** add prepareStatus method to ProjectRepository ([931b2ed](https://github.com/HiromiShikata/npm-cli-gh-issue-preparator/commit/931b2ed9e4298235b48afa0b6a82d81a49ea78d2))
|
|
14
|
+
|
|
1
15
|
# [1.5.0](https://github.com/HiromiShikata/npm-cli-gh-issue-preparator/compare/v1.4.0...v1.5.0) (2026-01-20)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -52,6 +52,15 @@ class GitHubIssueRepository {
|
|
|
52
52
|
name
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
comments(first: 100) {
|
|
56
|
+
nodes {
|
|
57
|
+
author {
|
|
58
|
+
login
|
|
59
|
+
}
|
|
60
|
+
body
|
|
61
|
+
createdAt
|
|
62
|
+
}
|
|
63
|
+
}
|
|
55
64
|
}
|
|
56
65
|
... on PullRequest {
|
|
57
66
|
url
|
|
@@ -62,6 +71,15 @@ class GitHubIssueRepository {
|
|
|
62
71
|
name
|
|
63
72
|
}
|
|
64
73
|
}
|
|
74
|
+
comments(first: 100) {
|
|
75
|
+
nodes {
|
|
76
|
+
author {
|
|
77
|
+
login
|
|
78
|
+
}
|
|
79
|
+
body
|
|
80
|
+
createdAt
|
|
81
|
+
}
|
|
82
|
+
}
|
|
65
83
|
}
|
|
66
84
|
}
|
|
67
85
|
fieldValues(first: 20) {
|
|
@@ -109,6 +127,15 @@ class GitHubIssueRepository {
|
|
|
109
127
|
name
|
|
110
128
|
}
|
|
111
129
|
}
|
|
130
|
+
comments(first: 100) {
|
|
131
|
+
nodes {
|
|
132
|
+
author {
|
|
133
|
+
login
|
|
134
|
+
}
|
|
135
|
+
body
|
|
136
|
+
createdAt
|
|
137
|
+
}
|
|
138
|
+
}
|
|
112
139
|
}
|
|
113
140
|
... on PullRequest {
|
|
114
141
|
url
|
|
@@ -119,6 +146,15 @@ class GitHubIssueRepository {
|
|
|
119
146
|
name
|
|
120
147
|
}
|
|
121
148
|
}
|
|
149
|
+
comments(first: 100) {
|
|
150
|
+
nodes {
|
|
151
|
+
author {
|
|
152
|
+
login
|
|
153
|
+
}
|
|
154
|
+
body
|
|
155
|
+
createdAt
|
|
156
|
+
}
|
|
157
|
+
}
|
|
122
158
|
}
|
|
123
159
|
}
|
|
124
160
|
fieldValues(first: 20) {
|
|
@@ -149,6 +185,15 @@ class GitHubIssueRepository {
|
|
|
149
185
|
}
|
|
150
186
|
`;
|
|
151
187
|
}
|
|
188
|
+
mapCommentsToEntity(commentNodes) {
|
|
189
|
+
if (!commentNodes)
|
|
190
|
+
return [];
|
|
191
|
+
return commentNodes.map((node) => ({
|
|
192
|
+
author: node.author?.login || '',
|
|
193
|
+
content: node.body,
|
|
194
|
+
createdAt: new Date(node.createdAt),
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
152
197
|
async getStatusOptionId(project, statusName) {
|
|
153
198
|
const { owner, projectNumber } = this.parseProjectInfo(project);
|
|
154
199
|
const query = `
|
|
@@ -291,6 +336,7 @@ class GitHubIssueRepository {
|
|
|
291
336
|
title: item.content.title,
|
|
292
337
|
labels: item.content.labels?.nodes?.map((l) => l.name) || [],
|
|
293
338
|
status,
|
|
339
|
+
comments: this.mapCommentsToEntity(item.content.comments?.nodes),
|
|
294
340
|
});
|
|
295
341
|
}
|
|
296
342
|
hasNextPage = projectData.items.pageInfo.hasNextPage;
|
|
@@ -394,6 +440,7 @@ class GitHubIssueRepository {
|
|
|
394
440
|
title: item.content.title,
|
|
395
441
|
labels: item.content.labels?.nodes?.map((l) => l.name) || [],
|
|
396
442
|
status,
|
|
443
|
+
comments: this.mapCommentsToEntity(item.content.comments?.nodes),
|
|
397
444
|
};
|
|
398
445
|
}
|
|
399
446
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GitHubIssueRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/GitHubIssueRepository.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"GitHubIssueRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/GitHubIssueRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -46,12 +46,14 @@ class GitHubProjectRepository {
|
|
|
46
46
|
fields(first: 100) {
|
|
47
47
|
nodes {
|
|
48
48
|
... on ProjectV2SingleSelectField {
|
|
49
|
+
id
|
|
49
50
|
name
|
|
50
51
|
options {
|
|
51
52
|
name
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
... on ProjectV2Field {
|
|
56
|
+
id
|
|
55
57
|
name
|
|
56
58
|
}
|
|
57
59
|
}
|
|
@@ -66,12 +68,14 @@ class GitHubProjectRepository {
|
|
|
66
68
|
fields(first: 100) {
|
|
67
69
|
nodes {
|
|
68
70
|
... on ProjectV2SingleSelectField {
|
|
71
|
+
id
|
|
69
72
|
name
|
|
70
73
|
options {
|
|
71
74
|
name
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
77
|
... on ProjectV2Field {
|
|
78
|
+
id
|
|
75
79
|
name
|
|
76
80
|
}
|
|
77
81
|
}
|
|
@@ -116,6 +120,73 @@ class GitHubProjectRepository {
|
|
|
116
120
|
name: project.title,
|
|
117
121
|
statuses,
|
|
118
122
|
customFieldNames: fields.map((f) => f.name),
|
|
123
|
+
statusFieldId: statusField?.id ?? null,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
async prepareStatus(name, project) {
|
|
127
|
+
if (project.statuses.includes(name)) {
|
|
128
|
+
return project;
|
|
129
|
+
}
|
|
130
|
+
if (!project.statusFieldId) {
|
|
131
|
+
throw new Error(`Status field not found in project "${project.name}". ` +
|
|
132
|
+
`Cannot add status "${name}".`);
|
|
133
|
+
}
|
|
134
|
+
const existingOptions = project.statuses.map((statusName) => ({
|
|
135
|
+
name: statusName,
|
|
136
|
+
color: 'GRAY',
|
|
137
|
+
description: '',
|
|
138
|
+
}));
|
|
139
|
+
const newOptions = [
|
|
140
|
+
...existingOptions,
|
|
141
|
+
{ name, color: 'GRAY', description: '' },
|
|
142
|
+
];
|
|
143
|
+
const mutation = `
|
|
144
|
+
mutation($fieldId: ID!, $singleSelectOptions: [ProjectV2SingleSelectFieldOptionInput!]!) {
|
|
145
|
+
updateProjectV2Field(input: {
|
|
146
|
+
fieldId: $fieldId
|
|
147
|
+
singleSelectOptions: $singleSelectOptions
|
|
148
|
+
}) {
|
|
149
|
+
projectV2Field {
|
|
150
|
+
... on ProjectV2SingleSelectField {
|
|
151
|
+
id
|
|
152
|
+
name
|
|
153
|
+
options {
|
|
154
|
+
name
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
const response = await fetch('https://api.github.com/graphql', {
|
|
162
|
+
method: 'POST',
|
|
163
|
+
headers: {
|
|
164
|
+
Authorization: `Bearer ${this.token}`,
|
|
165
|
+
'Content-Type': 'application/json',
|
|
166
|
+
},
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
query: mutation,
|
|
169
|
+
variables: {
|
|
170
|
+
fieldId: project.statusFieldId,
|
|
171
|
+
singleSelectOptions: newOptions,
|
|
172
|
+
},
|
|
173
|
+
}),
|
|
174
|
+
});
|
|
175
|
+
const responseData = await response.json();
|
|
176
|
+
if (!isGitHubApiResponse(responseData)) {
|
|
177
|
+
throw new Error('Invalid API response format');
|
|
178
|
+
}
|
|
179
|
+
if (!response.ok) {
|
|
180
|
+
throw new Error(`GitHub API error: ${JSON.stringify(responseData)}`);
|
|
181
|
+
}
|
|
182
|
+
if (typeof responseData === 'object' &&
|
|
183
|
+
responseData !== null &&
|
|
184
|
+
'errors' in responseData) {
|
|
185
|
+
throw new Error(`GraphQL errors: ${JSON.stringify(responseData.errors)}`);
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
...project,
|
|
189
|
+
statuses: [...project.statuses, name],
|
|
119
190
|
};
|
|
120
191
|
}
|
|
121
192
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GitHubProjectRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/GitHubProjectRepository.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"GitHubProjectRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/GitHubProjectRepository.ts"],"names":[],"mappings":";;;AA6BA,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAa,uBAAuB;IAClC,YAA6B,KAAa;QAAb,UAAK,GAAL,KAAK,CAAQ;IAAG,CAAC;IAEtC,qBAAqB,CAAC,GAAW;QAIvC,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAC1E,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO;gBACL,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;gBAClB,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC;aAC3B,CAAC;QACJ,CAAC;QAED,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAC5E,IAAI,SAAS,EAAE,CAAC;YACd,OAAO;gBACL,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;gBACnB,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;aAC5B,CAAC;QACJ,CAAC;QAED,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CACzB,gDAAgD,CACjD,CAAC;QACF,IAAI,SAAS,EAAE,CAAC;YACd,OAAO;gBACL,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;gBACnB,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;aAC5B,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,+BAA+B,GAAG,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAW;QACxB,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC;QAEjE,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA+CpB,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,gCAAgC,EAAE;YAC7D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE;gBACrC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,YAAY;gBACnB,SAAS,EAAE;oBACT,KAAK;oBACL,MAAM,EAAE,QAAQ,CAAC,aAAa,EAAE,EAAE,CAAC;iBACpC;aACF,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,qBAAqB,SAAS,EAAE,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,YAAY,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACpD,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,MAAM,GAAsB,YAAY,CAAC;QAC/C,MAAM,OAAO,GACX,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC;QACvE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,sBAAsB,GAAG,EAAE,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;QACpC,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAa,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAE1E,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,IAAI,EAAE,OAAO,CAAC,KAAK;YACnB,QAAQ;YACR,gBAAgB,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC3C,aAAa,EAAE,WAAW,EAAE,EAAE,IAAI,IAAI;SACvC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,OAAgB;QAChD,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,sCAAsC,OAAO,CAAC,IAAI,KAAK;gBACrD,sBAAsB,IAAI,IAAI,CACjC,CAAC;QACJ,CAAC;QAED,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YAC5D,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,MAAM;YACb,WAAW,EAAE,EAAE;SAChB,CAAC,CAAC,CAAC;QAEJ,MAAM,UAAU,GAAG;YACjB,GAAG,eAAe;YAClB,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE;SACzC,CAAC;QAEF,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;KAiBhB,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,gCAAgC,EAAE;YAC7D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE;gBACrC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,QAAQ;gBACf,SAAS,EAAE;oBACT,OAAO,EAAE,OAAO,CAAC,aAAa;oBAC9B,mBAAmB,EAAE,UAAU;iBAChC;aACF,CAAC;SACH,CAAC,CAAC;QAEH,MAAM,YAAY,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEpD,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,IACE,OAAO,YAAY,KAAK,QAAQ;YAChC,YAAY,KAAK,IAAI;YACrB,QAAQ,IAAI,YAAY,EACxB,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,OAAO;YACL,GAAG,OAAO;YACV,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC;SACtC,CAAC;IACJ,CAAC;CACF;AAtND,0DAsNC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Comment.js","sourceRoot":"","sources":["../../../src/domain/entities/Comment.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
12
12
|
name: 'Test Project',
|
|
13
13
|
statuses: ['Awaiting Workspace', 'Preparation', 'Done'],
|
|
14
14
|
customFieldNames: ['Status', 'workspace'],
|
|
15
|
+
statusFieldId: 'status-field-id',
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
const mockUserProject: Project = {
|
|
@@ -20,6 +21,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
20
21
|
name: 'User Project',
|
|
21
22
|
statuses: ['Todo', 'Done'],
|
|
22
23
|
customFieldNames: ['Status'],
|
|
24
|
+
statusFieldId: 'status-field-id-user',
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
beforeEach(() => {
|
|
@@ -103,6 +105,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
103
105
|
title: 'Test Issue 1',
|
|
104
106
|
labels: ['bug', 'category:impl'],
|
|
105
107
|
status: 'Awaiting Workspace',
|
|
108
|
+
comments: [],
|
|
106
109
|
});
|
|
107
110
|
expect(issues[1]).toEqual({
|
|
108
111
|
id: 'issue-2',
|
|
@@ -110,6 +113,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
110
113
|
title: 'Test Issue 2',
|
|
111
114
|
labels: ['enhancement'],
|
|
112
115
|
status: 'Preparation',
|
|
116
|
+
comments: [],
|
|
113
117
|
});
|
|
114
118
|
});
|
|
115
119
|
|
|
@@ -198,6 +202,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
198
202
|
title: 'User Issue',
|
|
199
203
|
labels: ['enhancement'],
|
|
200
204
|
status: 'Todo',
|
|
205
|
+
comments: [],
|
|
201
206
|
});
|
|
202
207
|
});
|
|
203
208
|
|
|
@@ -439,6 +444,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
439
444
|
title: 'Issue Without Status',
|
|
440
445
|
labels: ['bug'],
|
|
441
446
|
status: '',
|
|
447
|
+
comments: [],
|
|
442
448
|
});
|
|
443
449
|
});
|
|
444
450
|
});
|
|
@@ -451,6 +457,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
451
457
|
title: 'Test Issue',
|
|
452
458
|
labels: ['bug'],
|
|
453
459
|
status: 'Preparation',
|
|
460
|
+
comments: [],
|
|
454
461
|
};
|
|
455
462
|
|
|
456
463
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -512,6 +519,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
512
519
|
title: 'Test Issue',
|
|
513
520
|
labels: ['bug'],
|
|
514
521
|
status: 'Done',
|
|
522
|
+
comments: [],
|
|
515
523
|
};
|
|
516
524
|
|
|
517
525
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -569,6 +577,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
569
577
|
title: 'Test Issue',
|
|
570
578
|
labels: ['bug'],
|
|
571
579
|
status: 'NonExistentStatus',
|
|
580
|
+
comments: [],
|
|
572
581
|
};
|
|
573
582
|
|
|
574
583
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -608,6 +617,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
608
617
|
title: 'Test Issue',
|
|
609
618
|
labels: ['bug'],
|
|
610
619
|
status: 'Preparation',
|
|
620
|
+
comments: [],
|
|
611
621
|
};
|
|
612
622
|
|
|
613
623
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -626,6 +636,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
626
636
|
title: 'Test Issue',
|
|
627
637
|
labels: ['bug'],
|
|
628
638
|
status: 'Preparation',
|
|
639
|
+
comments: [],
|
|
629
640
|
};
|
|
630
641
|
|
|
631
642
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -645,6 +656,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
645
656
|
title: 'Test Issue',
|
|
646
657
|
labels: ['bug'],
|
|
647
658
|
status: 'Preparation',
|
|
659
|
+
comments: [],
|
|
648
660
|
};
|
|
649
661
|
|
|
650
662
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -684,6 +696,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
684
696
|
title: 'Test Issue',
|
|
685
697
|
labels: ['bug'],
|
|
686
698
|
status: 'Preparation',
|
|
699
|
+
comments: [],
|
|
687
700
|
};
|
|
688
701
|
|
|
689
702
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -728,6 +741,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
728
741
|
title: 'Test Issue',
|
|
729
742
|
labels: ['bug'],
|
|
730
743
|
status: 'Preparation',
|
|
744
|
+
comments: [],
|
|
731
745
|
};
|
|
732
746
|
|
|
733
747
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -774,6 +788,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
774
788
|
title: 'Test Issue',
|
|
775
789
|
labels: ['bug'],
|
|
776
790
|
status: 'Preparation',
|
|
791
|
+
comments: [],
|
|
777
792
|
};
|
|
778
793
|
|
|
779
794
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -818,6 +833,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
818
833
|
title: 'Test Issue',
|
|
819
834
|
labels: ['bug'],
|
|
820
835
|
status: 'Todo',
|
|
836
|
+
comments: [],
|
|
821
837
|
};
|
|
822
838
|
|
|
823
839
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -870,6 +886,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
870
886
|
title: 'Test Issue',
|
|
871
887
|
labels: ['bug'],
|
|
872
888
|
status: 'Preparation',
|
|
889
|
+
comments: [],
|
|
873
890
|
};
|
|
874
891
|
|
|
875
892
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -943,6 +960,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
943
960
|
title: 'Test Issue',
|
|
944
961
|
labels: ['bug'],
|
|
945
962
|
status: 'Preparation',
|
|
963
|
+
comments: [],
|
|
946
964
|
});
|
|
947
965
|
});
|
|
948
966
|
|
|
@@ -1053,6 +1071,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
1053
1071
|
title: 'User Issue',
|
|
1054
1072
|
labels: ['feature'],
|
|
1055
1073
|
status: 'Todo',
|
|
1074
|
+
comments: [],
|
|
1056
1075
|
});
|
|
1057
1076
|
});
|
|
1058
1077
|
|
|
@@ -1110,6 +1129,7 @@ describe('GitHubIssueRepository', () => {
|
|
|
1110
1129
|
title: 'Issue Without Status',
|
|
1111
1130
|
labels: [],
|
|
1112
1131
|
status: '',
|
|
1132
|
+
comments: [],
|
|
1113
1133
|
});
|
|
1114
1134
|
});
|
|
1115
1135
|
|
|
@@ -1234,7 +1254,161 @@ describe('GitHubIssueRepository', () => {
|
|
|
1234
1254
|
title: 'Issue With Null Labels',
|
|
1235
1255
|
labels: [],
|
|
1236
1256
|
status: 'Done',
|
|
1257
|
+
comments: [],
|
|
1237
1258
|
});
|
|
1238
1259
|
});
|
|
1260
|
+
|
|
1261
|
+
it('should fetch and map comments correctly', async () => {
|
|
1262
|
+
mockFetch.mockResolvedValueOnce({
|
|
1263
|
+
ok: true,
|
|
1264
|
+
json: jest.fn().mockResolvedValue({
|
|
1265
|
+
data: {
|
|
1266
|
+
organization: {
|
|
1267
|
+
projectV2: {
|
|
1268
|
+
items: {
|
|
1269
|
+
totalCount: 1,
|
|
1270
|
+
pageInfo: {
|
|
1271
|
+
endCursor: null,
|
|
1272
|
+
hasNextPage: false,
|
|
1273
|
+
},
|
|
1274
|
+
nodes: [
|
|
1275
|
+
{
|
|
1276
|
+
id: 'issue-with-comments',
|
|
1277
|
+
content: {
|
|
1278
|
+
url: 'https://github.com/owner/repo/issues/1',
|
|
1279
|
+
title: 'Issue With Comments',
|
|
1280
|
+
number: 1,
|
|
1281
|
+
labels: {
|
|
1282
|
+
nodes: [],
|
|
1283
|
+
},
|
|
1284
|
+
comments: {
|
|
1285
|
+
nodes: [
|
|
1286
|
+
{
|
|
1287
|
+
author: {
|
|
1288
|
+
login: 'user1',
|
|
1289
|
+
},
|
|
1290
|
+
body: 'First comment',
|
|
1291
|
+
createdAt: '2024-01-15T10:00:00Z',
|
|
1292
|
+
},
|
|
1293
|
+
{
|
|
1294
|
+
author: {
|
|
1295
|
+
login: 'user2',
|
|
1296
|
+
},
|
|
1297
|
+
body: 'Second comment',
|
|
1298
|
+
createdAt: '2024-01-16T12:00:00Z',
|
|
1299
|
+
},
|
|
1300
|
+
],
|
|
1301
|
+
},
|
|
1302
|
+
},
|
|
1303
|
+
fieldValues: {
|
|
1304
|
+
nodes: [
|
|
1305
|
+
{
|
|
1306
|
+
name: 'Todo',
|
|
1307
|
+
field: {
|
|
1308
|
+
name: 'Status',
|
|
1309
|
+
},
|
|
1310
|
+
},
|
|
1311
|
+
],
|
|
1312
|
+
},
|
|
1313
|
+
},
|
|
1314
|
+
],
|
|
1315
|
+
},
|
|
1316
|
+
},
|
|
1317
|
+
},
|
|
1318
|
+
},
|
|
1319
|
+
}),
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
const result = await repository.get(
|
|
1323
|
+
'https://github.com/owner/repo/issues/1',
|
|
1324
|
+
mockProject,
|
|
1325
|
+
);
|
|
1326
|
+
|
|
1327
|
+
expect(result).toEqual({
|
|
1328
|
+
id: 'issue-with-comments',
|
|
1329
|
+
url: 'https://github.com/owner/repo/issues/1',
|
|
1330
|
+
title: 'Issue With Comments',
|
|
1331
|
+
labels: [],
|
|
1332
|
+
status: 'Todo',
|
|
1333
|
+
comments: [
|
|
1334
|
+
{
|
|
1335
|
+
author: 'user1',
|
|
1336
|
+
content: 'First comment',
|
|
1337
|
+
createdAt: new Date('2024-01-15T10:00:00Z'),
|
|
1338
|
+
},
|
|
1339
|
+
{
|
|
1340
|
+
author: 'user2',
|
|
1341
|
+
content: 'Second comment',
|
|
1342
|
+
createdAt: new Date('2024-01-16T12:00:00Z'),
|
|
1343
|
+
},
|
|
1344
|
+
],
|
|
1345
|
+
});
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
it('should handle comments with null author', async () => {
|
|
1349
|
+
mockFetch.mockResolvedValueOnce({
|
|
1350
|
+
ok: true,
|
|
1351
|
+
json: jest.fn().mockResolvedValue({
|
|
1352
|
+
data: {
|
|
1353
|
+
organization: {
|
|
1354
|
+
projectV2: {
|
|
1355
|
+
items: {
|
|
1356
|
+
totalCount: 1,
|
|
1357
|
+
pageInfo: {
|
|
1358
|
+
endCursor: null,
|
|
1359
|
+
hasNextPage: false,
|
|
1360
|
+
},
|
|
1361
|
+
nodes: [
|
|
1362
|
+
{
|
|
1363
|
+
id: 'issue-null-author',
|
|
1364
|
+
content: {
|
|
1365
|
+
url: 'https://github.com/owner/repo/issues/1',
|
|
1366
|
+
title: 'Issue With Null Author Comment',
|
|
1367
|
+
number: 1,
|
|
1368
|
+
labels: {
|
|
1369
|
+
nodes: [],
|
|
1370
|
+
},
|
|
1371
|
+
comments: {
|
|
1372
|
+
nodes: [
|
|
1373
|
+
{
|
|
1374
|
+
author: null,
|
|
1375
|
+
body: 'Comment from deleted user',
|
|
1376
|
+
createdAt: '2024-01-15T10:00:00Z',
|
|
1377
|
+
},
|
|
1378
|
+
],
|
|
1379
|
+
},
|
|
1380
|
+
},
|
|
1381
|
+
fieldValues: {
|
|
1382
|
+
nodes: [
|
|
1383
|
+
{
|
|
1384
|
+
name: 'Todo',
|
|
1385
|
+
field: {
|
|
1386
|
+
name: 'Status',
|
|
1387
|
+
},
|
|
1388
|
+
},
|
|
1389
|
+
],
|
|
1390
|
+
},
|
|
1391
|
+
},
|
|
1392
|
+
],
|
|
1393
|
+
},
|
|
1394
|
+
},
|
|
1395
|
+
},
|
|
1396
|
+
},
|
|
1397
|
+
}),
|
|
1398
|
+
});
|
|
1399
|
+
|
|
1400
|
+
const result = await repository.get(
|
|
1401
|
+
'https://github.com/owner/repo/issues/1',
|
|
1402
|
+
mockProject,
|
|
1403
|
+
);
|
|
1404
|
+
|
|
1405
|
+
expect(result?.comments).toEqual([
|
|
1406
|
+
{
|
|
1407
|
+
author: '',
|
|
1408
|
+
content: 'Comment from deleted user',
|
|
1409
|
+
createdAt: new Date('2024-01-15T10:00:00Z'),
|
|
1410
|
+
},
|
|
1411
|
+
]);
|
|
1412
|
+
});
|
|
1239
1413
|
});
|
|
1240
1414
|
});
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { IssueRepository } from '../../domain/usecases/adapter-interfaces/IssueRepository';
|
|
2
2
|
import { Issue } from '../../domain/entities/Issue';
|
|
3
3
|
import { Project } from '../../domain/entities/Project';
|
|
4
|
+
import { Comment } from '../../domain/entities/Comment';
|
|
5
|
+
|
|
6
|
+
type CommentNode = {
|
|
7
|
+
author: {
|
|
8
|
+
login: string;
|
|
9
|
+
} | null;
|
|
10
|
+
body: string;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
};
|
|
4
13
|
|
|
5
14
|
type ProjectItem = {
|
|
6
15
|
id: string;
|
|
@@ -11,6 +20,9 @@ type ProjectItem = {
|
|
|
11
20
|
labels: {
|
|
12
21
|
nodes: Array<{ name: string }>;
|
|
13
22
|
};
|
|
23
|
+
comments?: {
|
|
24
|
+
nodes: CommentNode[];
|
|
25
|
+
};
|
|
14
26
|
};
|
|
15
27
|
fieldValues?: {
|
|
16
28
|
nodes: Array<{
|
|
@@ -156,6 +168,15 @@ export class GitHubIssueRepository implements IssueRepository {
|
|
|
156
168
|
name
|
|
157
169
|
}
|
|
158
170
|
}
|
|
171
|
+
comments(first: 100) {
|
|
172
|
+
nodes {
|
|
173
|
+
author {
|
|
174
|
+
login
|
|
175
|
+
}
|
|
176
|
+
body
|
|
177
|
+
createdAt
|
|
178
|
+
}
|
|
179
|
+
}
|
|
159
180
|
}
|
|
160
181
|
... on PullRequest {
|
|
161
182
|
url
|
|
@@ -166,6 +187,15 @@ export class GitHubIssueRepository implements IssueRepository {
|
|
|
166
187
|
name
|
|
167
188
|
}
|
|
168
189
|
}
|
|
190
|
+
comments(first: 100) {
|
|
191
|
+
nodes {
|
|
192
|
+
author {
|
|
193
|
+
login
|
|
194
|
+
}
|
|
195
|
+
body
|
|
196
|
+
createdAt
|
|
197
|
+
}
|
|
198
|
+
}
|
|
169
199
|
}
|
|
170
200
|
}
|
|
171
201
|
fieldValues(first: 20) {
|
|
@@ -213,6 +243,15 @@ export class GitHubIssueRepository implements IssueRepository {
|
|
|
213
243
|
name
|
|
214
244
|
}
|
|
215
245
|
}
|
|
246
|
+
comments(first: 100) {
|
|
247
|
+
nodes {
|
|
248
|
+
author {
|
|
249
|
+
login
|
|
250
|
+
}
|
|
251
|
+
body
|
|
252
|
+
createdAt
|
|
253
|
+
}
|
|
254
|
+
}
|
|
216
255
|
}
|
|
217
256
|
... on PullRequest {
|
|
218
257
|
url
|
|
@@ -223,6 +262,15 @@ export class GitHubIssueRepository implements IssueRepository {
|
|
|
223
262
|
name
|
|
224
263
|
}
|
|
225
264
|
}
|
|
265
|
+
comments(first: 100) {
|
|
266
|
+
nodes {
|
|
267
|
+
author {
|
|
268
|
+
login
|
|
269
|
+
}
|
|
270
|
+
body
|
|
271
|
+
createdAt
|
|
272
|
+
}
|
|
273
|
+
}
|
|
226
274
|
}
|
|
227
275
|
}
|
|
228
276
|
fieldValues(first: 20) {
|
|
@@ -254,6 +302,17 @@ export class GitHubIssueRepository implements IssueRepository {
|
|
|
254
302
|
`;
|
|
255
303
|
}
|
|
256
304
|
|
|
305
|
+
private mapCommentsToEntity(
|
|
306
|
+
commentNodes: CommentNode[] | undefined,
|
|
307
|
+
): Comment[] {
|
|
308
|
+
if (!commentNodes) return [];
|
|
309
|
+
return commentNodes.map((node) => ({
|
|
310
|
+
author: node.author?.login || '',
|
|
311
|
+
content: node.body,
|
|
312
|
+
createdAt: new Date(node.createdAt),
|
|
313
|
+
}));
|
|
314
|
+
}
|
|
315
|
+
|
|
257
316
|
private async getStatusOptionId(
|
|
258
317
|
project: Project,
|
|
259
318
|
statusName: string,
|
|
@@ -425,6 +484,7 @@ export class GitHubIssueRepository implements IssueRepository {
|
|
|
425
484
|
title: item.content.title,
|
|
426
485
|
labels: item.content.labels?.nodes?.map((l) => l.name) || [],
|
|
427
486
|
status,
|
|
487
|
+
comments: this.mapCommentsToEntity(item.content.comments?.nodes),
|
|
428
488
|
});
|
|
429
489
|
}
|
|
430
490
|
|
|
@@ -553,6 +613,7 @@ export class GitHubIssueRepository implements IssueRepository {
|
|
|
553
613
|
title: item.content.title,
|
|
554
614
|
labels: item.content.labels?.nodes?.map((l) => l.name) || [],
|
|
555
615
|
status,
|
|
616
|
+
comments: this.mapCommentsToEntity(item.content.comments?.nodes),
|
|
556
617
|
};
|
|
557
618
|
}
|
|
558
619
|
}
|
|
@@ -27,6 +27,7 @@ describe('GitHubProjectRepository', () => {
|
|
|
27
27
|
fields: {
|
|
28
28
|
nodes: [
|
|
29
29
|
{
|
|
30
|
+
id: 'status-field-id',
|
|
30
31
|
name: 'Status',
|
|
31
32
|
options: [
|
|
32
33
|
{ name: 'Awaiting workspace' },
|
|
@@ -34,7 +35,7 @@ describe('GitHubProjectRepository', () => {
|
|
|
34
35
|
{ name: 'Done' },
|
|
35
36
|
],
|
|
36
37
|
},
|
|
37
|
-
{ name: 'workspace' },
|
|
38
|
+
{ id: 'workspace-field-id', name: 'workspace' },
|
|
38
39
|
],
|
|
39
40
|
},
|
|
40
41
|
},
|
|
@@ -51,6 +52,7 @@ describe('GitHubProjectRepository', () => {
|
|
|
51
52
|
name: 'Test Project',
|
|
52
53
|
statuses: ['Awaiting workspace', 'Preparation', 'Done'],
|
|
53
54
|
customFieldNames: ['Status', 'workspace'],
|
|
55
|
+
statusFieldId: 'status-field-id',
|
|
54
56
|
});
|
|
55
57
|
|
|
56
58
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
@@ -80,6 +82,7 @@ describe('GitHubProjectRepository', () => {
|
|
|
80
82
|
fields: {
|
|
81
83
|
nodes: [
|
|
82
84
|
{
|
|
85
|
+
id: 'user-status-field-id',
|
|
83
86
|
name: 'Status',
|
|
84
87
|
options: [{ name: 'Todo' }, { name: 'Done' }],
|
|
85
88
|
},
|
|
@@ -99,6 +102,7 @@ describe('GitHubProjectRepository', () => {
|
|
|
99
102
|
name: 'User Project',
|
|
100
103
|
statuses: ['Todo', 'Done'],
|
|
101
104
|
customFieldNames: ['Status'],
|
|
105
|
+
statusFieldId: 'user-status-field-id',
|
|
102
106
|
});
|
|
103
107
|
|
|
104
108
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
@@ -192,6 +196,7 @@ describe('GitHubProjectRepository', () => {
|
|
|
192
196
|
fields: {
|
|
193
197
|
nodes: [
|
|
194
198
|
{
|
|
199
|
+
id: 'repo-status-field-id',
|
|
195
200
|
name: 'Status',
|
|
196
201
|
options: [{ name: 'Open' }, { name: 'Closed' }],
|
|
197
202
|
},
|
|
@@ -211,6 +216,7 @@ describe('GitHubProjectRepository', () => {
|
|
|
211
216
|
name: 'Repo Project',
|
|
212
217
|
statuses: ['Open', 'Closed'],
|
|
213
218
|
customFieldNames: ['Status'],
|
|
219
|
+
statusFieldId: 'repo-status-field-id',
|
|
214
220
|
});
|
|
215
221
|
});
|
|
216
222
|
|
|
@@ -229,6 +235,7 @@ describe('GitHubProjectRepository', () => {
|
|
|
229
235
|
fields: {
|
|
230
236
|
nodes: [
|
|
231
237
|
{
|
|
238
|
+
id: 'priority-field-id',
|
|
232
239
|
name: 'Priority',
|
|
233
240
|
},
|
|
234
241
|
],
|
|
@@ -247,6 +254,150 @@ describe('GitHubProjectRepository', () => {
|
|
|
247
254
|
name: 'Project Without Status',
|
|
248
255
|
statuses: [],
|
|
249
256
|
customFieldNames: ['Priority'],
|
|
257
|
+
statusFieldId: null,
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('prepareStatus', () => {
|
|
262
|
+
it('should return project as-is when status already exists', async () => {
|
|
263
|
+
const project = {
|
|
264
|
+
id: 'project-id',
|
|
265
|
+
url: 'https://github.com/orgs/test-org/projects/1',
|
|
266
|
+
name: 'Test Project',
|
|
267
|
+
statuses: ['Todo', 'In Progress', 'Done'],
|
|
268
|
+
customFieldNames: ['Status'],
|
|
269
|
+
statusFieldId: 'status-field-id',
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const result = await repository.prepareStatus('In Progress', project);
|
|
273
|
+
|
|
274
|
+
expect(result).toEqual(project);
|
|
275
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should add new status when it does not exist', async () => {
|
|
279
|
+
const project = {
|
|
280
|
+
id: 'project-id',
|
|
281
|
+
url: 'https://github.com/orgs/test-org/projects/1',
|
|
282
|
+
name: 'Test Project',
|
|
283
|
+
statuses: ['Todo', 'Done'],
|
|
284
|
+
customFieldNames: ['Status'],
|
|
285
|
+
statusFieldId: 'status-field-id',
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
mockFetch.mockResolvedValueOnce({
|
|
289
|
+
ok: true,
|
|
290
|
+
json: jest.fn().mockResolvedValue({
|
|
291
|
+
data: {
|
|
292
|
+
updateProjectV2Field: {
|
|
293
|
+
projectV2Field: {
|
|
294
|
+
id: 'status-field-id',
|
|
295
|
+
name: 'Status',
|
|
296
|
+
options: [
|
|
297
|
+
{ name: 'Todo' },
|
|
298
|
+
{ name: 'Done' },
|
|
299
|
+
{ name: 'In Progress' },
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
}),
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const result = await repository.prepareStatus('In Progress', project);
|
|
308
|
+
|
|
309
|
+
expect(result).toEqual({
|
|
310
|
+
...project,
|
|
311
|
+
statuses: ['Todo', 'Done', 'In Progress'],
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
315
|
+
'https://api.github.com/graphql',
|
|
316
|
+
expect.objectContaining({
|
|
317
|
+
method: 'POST',
|
|
318
|
+
headers: {
|
|
319
|
+
Authorization: 'Bearer test-token',
|
|
320
|
+
'Content-Type': 'application/json',
|
|
321
|
+
},
|
|
322
|
+
}),
|
|
323
|
+
);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should throw error when status field is not found', async () => {
|
|
327
|
+
const project = {
|
|
328
|
+
id: 'project-id',
|
|
329
|
+
url: 'https://github.com/orgs/test-org/projects/1',
|
|
330
|
+
name: 'Test Project',
|
|
331
|
+
statuses: [],
|
|
332
|
+
customFieldNames: ['Priority'],
|
|
333
|
+
statusFieldId: null,
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
await expect(
|
|
337
|
+
repository.prepareStatus('New Status', project),
|
|
338
|
+
).rejects.toThrow('Status field not found in project "Test Project"');
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('should throw error when GitHub API returns error', async () => {
|
|
342
|
+
const project = {
|
|
343
|
+
id: 'project-id',
|
|
344
|
+
url: 'https://github.com/orgs/test-org/projects/1',
|
|
345
|
+
name: 'Test Project',
|
|
346
|
+
statuses: ['Todo'],
|
|
347
|
+
customFieldNames: ['Status'],
|
|
348
|
+
statusFieldId: 'status-field-id',
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
mockFetch.mockResolvedValueOnce({
|
|
352
|
+
ok: false,
|
|
353
|
+
json: jest.fn().mockResolvedValue({ errors: ['API Error'] }),
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
await expect(
|
|
357
|
+
repository.prepareStatus('New Status', project),
|
|
358
|
+
).rejects.toThrow('GitHub API error');
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('should throw error when response format is invalid', async () => {
|
|
362
|
+
const project = {
|
|
363
|
+
id: 'project-id',
|
|
364
|
+
url: 'https://github.com/orgs/test-org/projects/1',
|
|
365
|
+
name: 'Test Project',
|
|
366
|
+
statuses: ['Todo'],
|
|
367
|
+
customFieldNames: ['Status'],
|
|
368
|
+
statusFieldId: 'status-field-id',
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
mockFetch.mockResolvedValueOnce({
|
|
372
|
+
ok: true,
|
|
373
|
+
json: jest.fn().mockResolvedValue(null),
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
await expect(
|
|
377
|
+
repository.prepareStatus('New Status', project),
|
|
378
|
+
).rejects.toThrow('Invalid API response format');
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('should throw error when response has GraphQL errors', async () => {
|
|
382
|
+
const project = {
|
|
383
|
+
id: 'project-id',
|
|
384
|
+
url: 'https://github.com/orgs/test-org/projects/1',
|
|
385
|
+
name: 'Test Project',
|
|
386
|
+
statuses: ['Todo'],
|
|
387
|
+
customFieldNames: ['Status'],
|
|
388
|
+
statusFieldId: 'status-field-id',
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
mockFetch.mockResolvedValueOnce({
|
|
392
|
+
ok: true,
|
|
393
|
+
json: jest.fn().mockResolvedValue({
|
|
394
|
+
errors: [{ message: 'Permission denied' }],
|
|
395
|
+
}),
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
await expect(
|
|
399
|
+
repository.prepareStatus('New Status', project),
|
|
400
|
+
).rejects.toThrow('GraphQL errors');
|
|
250
401
|
});
|
|
251
402
|
});
|
|
252
403
|
});
|
|
@@ -2,6 +2,7 @@ import { ProjectRepository } from '../../domain/usecases/adapter-interfaces/Proj
|
|
|
2
2
|
import { Project } from '../../domain/entities/Project';
|
|
3
3
|
|
|
4
4
|
type GitHubProjectField = {
|
|
5
|
+
id?: string;
|
|
5
6
|
name: string;
|
|
6
7
|
options?: Array<{ name: string }>;
|
|
7
8
|
};
|
|
@@ -80,12 +81,14 @@ export class GitHubProjectRepository implements ProjectRepository {
|
|
|
80
81
|
fields(first: 100) {
|
|
81
82
|
nodes {
|
|
82
83
|
... on ProjectV2SingleSelectField {
|
|
84
|
+
id
|
|
83
85
|
name
|
|
84
86
|
options {
|
|
85
87
|
name
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
... on ProjectV2Field {
|
|
91
|
+
id
|
|
89
92
|
name
|
|
90
93
|
}
|
|
91
94
|
}
|
|
@@ -100,12 +103,14 @@ export class GitHubProjectRepository implements ProjectRepository {
|
|
|
100
103
|
fields(first: 100) {
|
|
101
104
|
nodes {
|
|
102
105
|
... on ProjectV2SingleSelectField {
|
|
106
|
+
id
|
|
103
107
|
name
|
|
104
108
|
options {
|
|
105
109
|
name
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
112
|
... on ProjectV2Field {
|
|
113
|
+
id
|
|
109
114
|
name
|
|
110
115
|
}
|
|
111
116
|
}
|
|
@@ -157,6 +162,88 @@ export class GitHubProjectRepository implements ProjectRepository {
|
|
|
157
162
|
name: project.title,
|
|
158
163
|
statuses,
|
|
159
164
|
customFieldNames: fields.map((f) => f.name),
|
|
165
|
+
statusFieldId: statusField?.id ?? null,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async prepareStatus(name: string, project: Project): Promise<Project> {
|
|
170
|
+
if (project.statuses.includes(name)) {
|
|
171
|
+
return project;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!project.statusFieldId) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`Status field not found in project "${project.name}". ` +
|
|
177
|
+
`Cannot add status "${name}".`,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const existingOptions = project.statuses.map((statusName) => ({
|
|
182
|
+
name: statusName,
|
|
183
|
+
color: 'GRAY',
|
|
184
|
+
description: '',
|
|
185
|
+
}));
|
|
186
|
+
|
|
187
|
+
const newOptions = [
|
|
188
|
+
...existingOptions,
|
|
189
|
+
{ name, color: 'GRAY', description: '' },
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
const mutation = `
|
|
193
|
+
mutation($fieldId: ID!, $singleSelectOptions: [ProjectV2SingleSelectFieldOptionInput!]!) {
|
|
194
|
+
updateProjectV2Field(input: {
|
|
195
|
+
fieldId: $fieldId
|
|
196
|
+
singleSelectOptions: $singleSelectOptions
|
|
197
|
+
}) {
|
|
198
|
+
projectV2Field {
|
|
199
|
+
... on ProjectV2SingleSelectField {
|
|
200
|
+
id
|
|
201
|
+
name
|
|
202
|
+
options {
|
|
203
|
+
name
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
210
|
+
|
|
211
|
+
const response = await fetch('https://api.github.com/graphql', {
|
|
212
|
+
method: 'POST',
|
|
213
|
+
headers: {
|
|
214
|
+
Authorization: `Bearer ${this.token}`,
|
|
215
|
+
'Content-Type': 'application/json',
|
|
216
|
+
},
|
|
217
|
+
body: JSON.stringify({
|
|
218
|
+
query: mutation,
|
|
219
|
+
variables: {
|
|
220
|
+
fieldId: project.statusFieldId,
|
|
221
|
+
singleSelectOptions: newOptions,
|
|
222
|
+
},
|
|
223
|
+
}),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const responseData: unknown = await response.json();
|
|
227
|
+
|
|
228
|
+
if (!isGitHubApiResponse(responseData)) {
|
|
229
|
+
throw new Error('Invalid API response format');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!response.ok) {
|
|
233
|
+
throw new Error(`GitHub API error: ${JSON.stringify(responseData)}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (
|
|
237
|
+
typeof responseData === 'object' &&
|
|
238
|
+
responseData !== null &&
|
|
239
|
+
'errors' in responseData
|
|
240
|
+
) {
|
|
241
|
+
throw new Error(`GraphQL errors: ${JSON.stringify(responseData.errors)}`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
...project,
|
|
246
|
+
statuses: [...project.statuses, name],
|
|
160
247
|
};
|
|
161
248
|
}
|
|
162
249
|
}
|
|
@@ -17,6 +17,7 @@ describe('NotifyFinishedIssuePreparationUseCase', () => {
|
|
|
17
17
|
name: 'Test Project',
|
|
18
18
|
statuses: ['Preparation', 'Awaiting Quality Check', 'Done'],
|
|
19
19
|
customFieldNames: ['workspace'],
|
|
20
|
+
statusFieldId: 'status-field-id',
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
beforeEach(() => {
|
|
@@ -24,6 +25,7 @@ describe('NotifyFinishedIssuePreparationUseCase', () => {
|
|
|
24
25
|
|
|
25
26
|
mockProjectRepository = {
|
|
26
27
|
getByUrl: jest.fn(),
|
|
28
|
+
prepareStatus: jest.fn(),
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
mockIssueRepository = {
|
|
@@ -45,6 +47,7 @@ describe('NotifyFinishedIssuePreparationUseCase', () => {
|
|
|
45
47
|
title: 'Test Issue',
|
|
46
48
|
labels: [],
|
|
47
49
|
status: 'Preparation',
|
|
50
|
+
comments: [],
|
|
48
51
|
};
|
|
49
52
|
|
|
50
53
|
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
@@ -90,6 +93,7 @@ describe('NotifyFinishedIssuePreparationUseCase', () => {
|
|
|
90
93
|
title: 'Test Issue',
|
|
91
94
|
labels: [],
|
|
92
95
|
status: 'Done',
|
|
96
|
+
comments: [],
|
|
93
97
|
};
|
|
94
98
|
|
|
95
99
|
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
@@ -16,11 +16,13 @@ describe('StartPreparationUseCase', () => {
|
|
|
16
16
|
name: 'Test Project',
|
|
17
17
|
statuses: ['Awaiting Workspace', 'Preparation', 'Done'],
|
|
18
18
|
customFieldNames: ['workspace'],
|
|
19
|
+
statusFieldId: 'status-field-id',
|
|
19
20
|
};
|
|
20
21
|
beforeEach(() => {
|
|
21
22
|
jest.resetAllMocks();
|
|
22
23
|
mockProjectRepository = {
|
|
23
24
|
getByUrl: jest.fn(),
|
|
25
|
+
prepareStatus: jest.fn(),
|
|
24
26
|
};
|
|
25
27
|
mockIssueRepository = {
|
|
26
28
|
getAllOpened: jest.fn(),
|
|
@@ -44,6 +46,7 @@ describe('StartPreparationUseCase', () => {
|
|
|
44
46
|
title: 'Issue 1',
|
|
45
47
|
labels: ['category:impl'],
|
|
46
48
|
status: 'Awaiting Workspace',
|
|
49
|
+
comments: [],
|
|
47
50
|
},
|
|
48
51
|
];
|
|
49
52
|
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
@@ -79,6 +82,7 @@ describe('StartPreparationUseCase', () => {
|
|
|
79
82
|
title: 'Issue 1',
|
|
80
83
|
labels: [],
|
|
81
84
|
status: 'Awaiting Workspace',
|
|
85
|
+
comments: [],
|
|
82
86
|
},
|
|
83
87
|
{
|
|
84
88
|
id: '2',
|
|
@@ -86,6 +90,7 @@ describe('StartPreparationUseCase', () => {
|
|
|
86
90
|
title: 'Issue 2',
|
|
87
91
|
labels: [],
|
|
88
92
|
status: 'Awaiting Workspace',
|
|
93
|
+
comments: [],
|
|
89
94
|
},
|
|
90
95
|
];
|
|
91
96
|
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
@@ -117,6 +122,7 @@ describe('StartPreparationUseCase', () => {
|
|
|
117
122
|
title: `Issue ${i + 1}`,
|
|
118
123
|
labels: [],
|
|
119
124
|
status: 'Preparation',
|
|
125
|
+
comments: [],
|
|
120
126
|
}));
|
|
121
127
|
const awaitingIssues: Issue[] = [
|
|
122
128
|
{
|
|
@@ -125,6 +131,7 @@ describe('StartPreparationUseCase', () => {
|
|
|
125
131
|
title: 'Issue 7',
|
|
126
132
|
labels: [],
|
|
127
133
|
status: 'Awaiting Workspace',
|
|
134
|
+
comments: [],
|
|
128
135
|
},
|
|
129
136
|
];
|
|
130
137
|
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
@@ -153,6 +160,7 @@ describe('StartPreparationUseCase', () => {
|
|
|
153
160
|
title: 'Issue 1',
|
|
154
161
|
labels: ['category:impl'],
|
|
155
162
|
status: 'Awaiting Workspace',
|
|
163
|
+
comments: [],
|
|
156
164
|
},
|
|
157
165
|
];
|
|
158
166
|
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
@@ -183,6 +191,7 @@ describe('StartPreparationUseCase', () => {
|
|
|
183
191
|
title: 'Issue 1',
|
|
184
192
|
labels: ['category:impl'],
|
|
185
193
|
status: 'Awaiting Workspace',
|
|
194
|
+
comments: [],
|
|
186
195
|
},
|
|
187
196
|
];
|
|
188
197
|
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
@@ -211,6 +220,7 @@ describe('StartPreparationUseCase', () => {
|
|
|
211
220
|
title: 'Issue 1',
|
|
212
221
|
labels: [],
|
|
213
222
|
status: 'Awaiting Workspace',
|
|
223
|
+
comments: [],
|
|
214
224
|
};
|
|
215
225
|
let popCallCount = 0;
|
|
216
226
|
const issuesWithMockedPop: Issue[] = [awaitingIssue, awaitingIssue];
|
|
@@ -253,6 +263,7 @@ describe('StartPreparationUseCase', () => {
|
|
|
253
263
|
title: `Issue ${i + 1}`,
|
|
254
264
|
labels: [],
|
|
255
265
|
status: 'Awaiting Workspace',
|
|
266
|
+
comments: [],
|
|
256
267
|
}));
|
|
257
268
|
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
258
269
|
mockIssueRepository.getAllOpened.mockResolvedValueOnce(awaitingIssues);
|
|
@@ -278,6 +289,7 @@ describe('StartPreparationUseCase', () => {
|
|
|
278
289
|
title: `Issue ${i + 1}`,
|
|
279
290
|
labels: [],
|
|
280
291
|
status: 'Awaiting Workspace',
|
|
292
|
+
comments: [],
|
|
281
293
|
}));
|
|
282
294
|
mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
|
|
283
295
|
mockIssueRepository.getAllOpened.mockResolvedValueOnce(awaitingIssues);
|
|
@@ -6,6 +6,7 @@ export declare class GitHubIssueRepository implements IssueRepository {
|
|
|
6
6
|
constructor(token: string);
|
|
7
7
|
private parseProjectInfo;
|
|
8
8
|
private buildProjectItemsQuery;
|
|
9
|
+
private mapCommentsToEntity;
|
|
9
10
|
private getStatusOptionId;
|
|
10
11
|
getAllOpened(project: Project): Promise<Issue[]>;
|
|
11
12
|
update(issue: Issue, project: Project): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GitHubIssueRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/GitHubIssueRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAC3F,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"GitHubIssueRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/GitHubIssueRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAC3F,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AA8HxD,qBAAa,qBAAsB,YAAW,eAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,MAAM;IAE1C,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,sBAAsB;IA6J9B,OAAO,CAAC,mBAAmB;YAWb,iBAAiB;IA4GzB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IA0EhD,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA2DrD,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;CAsErE"}
|
|
@@ -5,5 +5,6 @@ export declare class GitHubProjectRepository implements ProjectRepository {
|
|
|
5
5
|
constructor(token: string);
|
|
6
6
|
private parseGitHubProjectUrl;
|
|
7
7
|
getByUrl(url: string): Promise<Project>;
|
|
8
|
+
prepareStatus(name: string, project: Project): Promise<Project>;
|
|
8
9
|
}
|
|
9
10
|
//# sourceMappingURL=GitHubProjectRepository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GitHubProjectRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/GitHubProjectRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4DAA4D,CAAC;AAC/F,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"GitHubProjectRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/GitHubProjectRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4DAA4D,CAAC;AAC/F,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAiCxD,qBAAa,uBAAwB,YAAW,iBAAiB;IACnD,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,MAAM;IAE1C,OAAO,CAAC,qBAAqB;IAiCvB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkGvC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;CAgFtE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Comment.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/Comment.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Issue.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/Issue.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"Issue.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/Issue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,MAAM,KAAK,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Project.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/Project.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"Project.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/Project.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/ProjectRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"ProjectRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/ProjectRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjE"}
|