Skip to content

Commit

Permalink
AXON-113: Fix Bitbucket DC 8.0 and later bugs with 404s (#106)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
bwieger-atlassian-com authored Feb 12, 2025
1 parent 78234a2 commit 845e225
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 2 deletions.
157 changes: 155 additions & 2 deletions src/bitbucket/bitbucket-server/pullRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,38 @@ export class ServerPullRequestApi implements PullRequestApi {
}

async getTasks(pr: PullRequest): Promise<Task[]> {
try {
return this.getTasks_v8(pr);
} catch (error) {
if (error.message && error.message['status-code'] === 404) {
return this.getTasks_v0(pr);
} else {
throw error;
}
}
}

private async getTasks_v8(pr: PullRequest) {
const { ownerSlug, repoSlug } = pr.site;
let { data } = await this.client.get(
`/rest/api/1.0/projects/${ownerSlug}/repos/${repoSlug}/pull-requests/${pr.data.id}/blocker-comments`,
);
if (!data.values) {
return [];
}

const accumulatedTasks = data.values as any[];
while (data.next) {
const nextPage = await this.client.get(data.next);
data = nextPage.data;
accumulatedTasks.push(...(data.values || []));
}

return accumulatedTasks.map((task: any) => this.convertDataToTask_v8(task, pr.site));
}

private async getTasks_v0(pr: PullRequest) {
const { ownerSlug, repoSlug } = pr.site;
let { data } = await this.client.get(
`/rest/api/1.0/projects/${ownerSlug}/repos/${repoSlug}/pull-requests/${pr.data.id}/tasks`,
);
Expand All @@ -201,9 +231,22 @@ export class ServerPullRequestApi implements PullRequestApi {
}

async postTask(site: BitbucketSite, prId: string, content: string, commentId?: string): Promise<Task> {
try {
return this.postTask_v8(site, prId, content, commentId);
} catch (error) {
if (error.message && error.message['status-code'] === 404) {
// Retry with the legacy endpoint
return this.postTask_v0(site, prId, content, commentId);
} else {
throw error;
}
}
}

private async postTask_v0(site: BitbucketSite, prId: string, content: string, commentId?: string) {
const bbApi = await clientForSite(site);
const repo = await bbApi.repositories.get(site);
let { data } = await this.client.post(`/rest/api/latest/tasks`, {
const { data } = await this.client.post(`/rest/api/latest/tasks`, {
anchor: {
id: commentId,
type: 'COMMENT',
Expand All @@ -218,7 +261,57 @@ export class ServerPullRequestApi implements PullRequestApi {

return this.convertDataToTask(data, site);
}

private async postTask_v8(site: BitbucketSite, prId: string, content: string, commentId?: string) {
const bbApi = await clientForSite(site);
const repo = await bbApi.repositories.get(site);
const ownerSlug = site.ownerSlug;
const data = await this.client.post(
`/rest/api/latest/projects/${ownerSlug}/repos/${repo.name}/pull-requests/${prId}/comments`,
{
id: commentId,
state: 'OPEN',
version: 1,
severity: 'BLOCKER',
text: content,
properties: {},
},
);
return this.convertDataToTask_v8(data.data, site);
}

async editTask(site: BitbucketSite, prId: string, task: Task): Promise<Task> {
try {
return this.editTask_v8(site, prId, task);
} catch (error) {
if (error.message && error.message['status-code'] === 404) {
return this.editTask_v0(site, prId, task);
} else {
throw error;
}
}
}

private async editTask_v8(site: BitbucketSite, pullRequestId: string, task: Task) {
const projectKey = site.ownerSlug;
const repositorySlug = site.repoSlug;
const commentId = task.commentId;
const { data } = await this.client.put(
`/rest/api/1.0/projects/${projectKey}/repos/${repositorySlug}/pull-requests/${pullRequestId}/comments/${commentId}`,
{
id: task.id,
state: task.isComplete ? 'RESOLVED' : 'OPEN',
version: task.version,
severity: 'BLOCKER',
text: task.content,
properties: {},
},
);

return this.convertDataToTask_v8(data, site);
}

private async editTask_v0(site: BitbucketSite, prId: string, task: Task) {
const { data } = await this.client.put(`/rest/api/1.0/tasks/${task.id}`, {
id: task.id,
text: task.content,
Expand All @@ -229,9 +322,50 @@ export class ServerPullRequestApi implements PullRequestApi {
}

async deleteTask(site: BitbucketSite, prId: string, task: Task): Promise<void> {
try {
return this.deleteTask_v8(site, prId, task);
} catch (error) {
if (error.message && error.message['status-code'] === 404) {
return this.deleteTask_v0(site, prId, task);
} else {
throw error;
}
}
}

private async deleteTask_v8(site: BitbucketSite, pullRequestId: string, task: Task) {
const projectKey = site.ownerSlug;
const repositorySlug = site.repoSlug;
const commentId = task.commentId;

await this.client.delete(
`/rest/api/1.0/projects/${projectKey}/repos/${repositorySlug}/pull-requests/${pullRequestId}/comments/${commentId}`,
{},
{ version: task.version },
);
}

private async deleteTask_v0(site: BitbucketSite, prId: string, task: Task) {
await this.client.delete(`/rest/api/1.0/tasks/${task.id}`, {});
}

convertDataToTask_v8(taskData: any, site: BitbucketSite): Task {
const user = taskData.author ? ServerPullRequestApi.toUser(site.details, taskData.author) : UnknownUser;
const taskBelongsToUser: boolean = user.accountId === site.details.userId;
return {
commentId: taskData.id,
creator: ServerPullRequestApi.toUser(site.details, taskData.author),
created: taskData.createdDate,
updated: taskData.updatedDate,
isComplete: taskData.state !== 'OPEN',
editable: taskBelongsToUser && taskData.permittedOperations.editable,
deletable: taskBelongsToUser && taskData.permittedOperations.deletable,
id: taskData.id,
content: taskData.text,
version: taskData.version,
};
}

convertDataToTask(taskData: any, site: BitbucketSite): Task {
const user = taskData.author ? ServerPullRequestApi.toUser(site.details, taskData.author) : UnknownUser;
const taskBelongsToUser: boolean = user.accountId === site.details.userId;
Expand Down Expand Up @@ -785,15 +919,34 @@ export class ServerPullRequestApi implements PullRequestApi {
}

private async getTaskCount(site: BitbucketSite, prId: string): Promise<number> {
const { ownerSlug, repoSlug } = site;
try {
return this.getTaskCount_v8(site, prId);
} catch (error) {
if (error.message && error.message['status-code'] === 404) {
// Retry with the legacy endpoint
return this.getTaskCount_v0(site, prId);
} else {
throw error;
}
}
}

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

return data;
}

private getTaskCount_v8(site: BitbucketSite, prId: string): number | PromiseLike<number> {
const { ownerSlug, repoSlug } = site;
return this.client.get(
`/rest/api/1.0/projects/${ownerSlug}/repos/${repoSlug}/pull-requests/${prId}/blocker-comments?count=true`,
);
}

static toUser(site: DetailedSiteInfo, input: any): User {
return {
accountId: input.slug!,
Expand Down
1 change: 1 addition & 0 deletions src/bitbucket/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export type Task = {
editable: boolean;
deletable: boolean;
content: string;
version?: number;
};

export type Comment = {
Expand Down

0 comments on commit 845e225

Please sign in to comment.