Skip to content

Commit 845e225

Browse files
AXON-113: Fix Bitbucket DC 8.0 and later bugs with 404s (#106)
* Can open a PR * task can be created, editted, and deleted * task now show up properly * refactors to be pretty * small fixes * nits * nits * nits
1 parent 78234a2 commit 845e225

File tree

2 files changed

+156
-2
lines changed

2 files changed

+156
-2
lines changed

src/bitbucket/bitbucket-server/pullRequests.ts

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,38 @@ export class ServerPullRequestApi implements PullRequestApi {
180180
}
181181

182182
async getTasks(pr: PullRequest): Promise<Task[]> {
183+
try {
184+
return this.getTasks_v8(pr);
185+
} catch (error) {
186+
if (error.message && error.message['status-code'] === 404) {
187+
return this.getTasks_v0(pr);
188+
} else {
189+
throw error;
190+
}
191+
}
192+
}
193+
194+
private async getTasks_v8(pr: PullRequest) {
183195
const { ownerSlug, repoSlug } = pr.site;
196+
let { data } = await this.client.get(
197+
`/rest/api/1.0/projects/${ownerSlug}/repos/${repoSlug}/pull-requests/${pr.data.id}/blocker-comments`,
198+
);
199+
if (!data.values) {
200+
return [];
201+
}
184202

203+
const accumulatedTasks = data.values as any[];
204+
while (data.next) {
205+
const nextPage = await this.client.get(data.next);
206+
data = nextPage.data;
207+
accumulatedTasks.push(...(data.values || []));
208+
}
209+
210+
return accumulatedTasks.map((task: any) => this.convertDataToTask_v8(task, pr.site));
211+
}
212+
213+
private async getTasks_v0(pr: PullRequest) {
214+
const { ownerSlug, repoSlug } = pr.site;
185215
let { data } = await this.client.get(
186216
`/rest/api/1.0/projects/${ownerSlug}/repos/${repoSlug}/pull-requests/${pr.data.id}/tasks`,
187217
);
@@ -201,9 +231,22 @@ export class ServerPullRequestApi implements PullRequestApi {
201231
}
202232

203233
async postTask(site: BitbucketSite, prId: string, content: string, commentId?: string): Promise<Task> {
234+
try {
235+
return this.postTask_v8(site, prId, content, commentId);
236+
} catch (error) {
237+
if (error.message && error.message['status-code'] === 404) {
238+
// Retry with the legacy endpoint
239+
return this.postTask_v0(site, prId, content, commentId);
240+
} else {
241+
throw error;
242+
}
243+
}
244+
}
245+
246+
private async postTask_v0(site: BitbucketSite, prId: string, content: string, commentId?: string) {
204247
const bbApi = await clientForSite(site);
205248
const repo = await bbApi.repositories.get(site);
206-
let { data } = await this.client.post(`/rest/api/latest/tasks`, {
249+
const { data } = await this.client.post(`/rest/api/latest/tasks`, {
207250
anchor: {
208251
id: commentId,
209252
type: 'COMMENT',
@@ -218,7 +261,57 @@ export class ServerPullRequestApi implements PullRequestApi {
218261

219262
return this.convertDataToTask(data, site);
220263
}
264+
265+
private async postTask_v8(site: BitbucketSite, prId: string, content: string, commentId?: string) {
266+
const bbApi = await clientForSite(site);
267+
const repo = await bbApi.repositories.get(site);
268+
const ownerSlug = site.ownerSlug;
269+
const data = await this.client.post(
270+
`/rest/api/latest/projects/${ownerSlug}/repos/${repo.name}/pull-requests/${prId}/comments`,
271+
{
272+
id: commentId,
273+
state: 'OPEN',
274+
version: 1,
275+
severity: 'BLOCKER',
276+
text: content,
277+
properties: {},
278+
},
279+
);
280+
return this.convertDataToTask_v8(data.data, site);
281+
}
282+
221283
async editTask(site: BitbucketSite, prId: string, task: Task): Promise<Task> {
284+
try {
285+
return this.editTask_v8(site, prId, task);
286+
} catch (error) {
287+
if (error.message && error.message['status-code'] === 404) {
288+
return this.editTask_v0(site, prId, task);
289+
} else {
290+
throw error;
291+
}
292+
}
293+
}
294+
295+
private async editTask_v8(site: BitbucketSite, pullRequestId: string, task: Task) {
296+
const projectKey = site.ownerSlug;
297+
const repositorySlug = site.repoSlug;
298+
const commentId = task.commentId;
299+
const { data } = await this.client.put(
300+
`/rest/api/1.0/projects/${projectKey}/repos/${repositorySlug}/pull-requests/${pullRequestId}/comments/${commentId}`,
301+
{
302+
id: task.id,
303+
state: task.isComplete ? 'RESOLVED' : 'OPEN',
304+
version: task.version,
305+
severity: 'BLOCKER',
306+
text: task.content,
307+
properties: {},
308+
},
309+
);
310+
311+
return this.convertDataToTask_v8(data, site);
312+
}
313+
314+
private async editTask_v0(site: BitbucketSite, prId: string, task: Task) {
222315
const { data } = await this.client.put(`/rest/api/1.0/tasks/${task.id}`, {
223316
id: task.id,
224317
text: task.content,
@@ -229,9 +322,50 @@ export class ServerPullRequestApi implements PullRequestApi {
229322
}
230323

231324
async deleteTask(site: BitbucketSite, prId: string, task: Task): Promise<void> {
325+
try {
326+
return this.deleteTask_v8(site, prId, task);
327+
} catch (error) {
328+
if (error.message && error.message['status-code'] === 404) {
329+
return this.deleteTask_v0(site, prId, task);
330+
} else {
331+
throw error;
332+
}
333+
}
334+
}
335+
336+
private async deleteTask_v8(site: BitbucketSite, pullRequestId: string, task: Task) {
337+
const projectKey = site.ownerSlug;
338+
const repositorySlug = site.repoSlug;
339+
const commentId = task.commentId;
340+
341+
await this.client.delete(
342+
`/rest/api/1.0/projects/${projectKey}/repos/${repositorySlug}/pull-requests/${pullRequestId}/comments/${commentId}`,
343+
{},
344+
{ version: task.version },
345+
);
346+
}
347+
348+
private async deleteTask_v0(site: BitbucketSite, prId: string, task: Task) {
232349
await this.client.delete(`/rest/api/1.0/tasks/${task.id}`, {});
233350
}
234351

352+
convertDataToTask_v8(taskData: any, site: BitbucketSite): Task {
353+
const user = taskData.author ? ServerPullRequestApi.toUser(site.details, taskData.author) : UnknownUser;
354+
const taskBelongsToUser: boolean = user.accountId === site.details.userId;
355+
return {
356+
commentId: taskData.id,
357+
creator: ServerPullRequestApi.toUser(site.details, taskData.author),
358+
created: taskData.createdDate,
359+
updated: taskData.updatedDate,
360+
isComplete: taskData.state !== 'OPEN',
361+
editable: taskBelongsToUser && taskData.permittedOperations.editable,
362+
deletable: taskBelongsToUser && taskData.permittedOperations.deletable,
363+
id: taskData.id,
364+
content: taskData.text,
365+
version: taskData.version,
366+
};
367+
}
368+
235369
convertDataToTask(taskData: any, site: BitbucketSite): Task {
236370
const user = taskData.author ? ServerPullRequestApi.toUser(site.details, taskData.author) : UnknownUser;
237371
const taskBelongsToUser: boolean = user.accountId === site.details.userId;
@@ -785,15 +919,34 @@ export class ServerPullRequestApi implements PullRequestApi {
785919
}
786920

787921
private async getTaskCount(site: BitbucketSite, prId: string): Promise<number> {
788-
const { ownerSlug, repoSlug } = site;
922+
try {
923+
return this.getTaskCount_v8(site, prId);
924+
} catch (error) {
925+
if (error.message && error.message['status-code'] === 404) {
926+
// Retry with the legacy endpoint
927+
return this.getTaskCount_v0(site, prId);
928+
} else {
929+
throw error;
930+
}
931+
}
932+
}
789933

934+
private async getTaskCount_v0(site: BitbucketSite, prId: string): Promise<number> {
935+
const { ownerSlug, repoSlug } = site;
790936
const { data } = await this.client.get(
791937
`/rest/api/1.0/projects/${ownerSlug}/repos/${repoSlug}/pull-requests/${prId}/tasks/count`,
792938
);
793939

794940
return data;
795941
}
796942

943+
private getTaskCount_v8(site: BitbucketSite, prId: string): number | PromiseLike<number> {
944+
const { ownerSlug, repoSlug } = site;
945+
return this.client.get(
946+
`/rest/api/1.0/projects/${ownerSlug}/repos/${repoSlug}/pull-requests/${prId}/blocker-comments?count=true`,
947+
);
948+
}
949+
797950
static toUser(site: DetailedSiteInfo, input: any): User {
798951
return {
799952
accountId: input.slug!,

src/bitbucket/model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export type Task = {
8181
editable: boolean;
8282
deletable: boolean;
8383
content: string;
84+
version?: number;
8485
};
8586

8687
export type Comment = {

0 commit comments

Comments
 (0)