Skip to content

Commit e9c8991

Browse files
Copilotalexr00
andcommitted
Extract common PR review methods into shared utility functions
Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
1 parent 100d581 commit e9c8991

3 files changed

Lines changed: 372 additions & 296 deletions

File tree

src/github/activityBarViewProvider.ts

Lines changed: 53 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
import * as vscode from 'vscode';
77
import { openPullRequestOnGitHub } from '../commands';
88
import { FolderRepositoryManager } from './folderRepositoryManager';
9-
import { GithubItemStateEnum, IAccount, isITeam, ITeam, PullRequestMergeability, reviewerId, ReviewEventEnum, ReviewState } from './interface';
9+
import { IAccount, ReviewEventEnum, ReviewState } from './interface';
1010
import { PullRequestModel } from './pullRequestModel';
1111
import { getDefaultMergeMethod } from './pullRequestOverview';
1212
import { PullRequestView } from './pullRequestOverviewCommon';
13+
import { PullRequestReviewHelpers, ReviewContext } from './pullRequestReviewCommon';
1314
import { isInCodespaces, parseReviewers } from './utils';
14-
import { MergeArguments, PullRequest, ReviewType, SubmitReviewReply } from './views';
15+
import { MergeArguments, PullRequest, ReviewType } from './views';
1516
import { IComment } from '../common/comment';
1617
import { emojify, ensureEmojis } from '../common/emoji';
1718
import { disposeAll } from '../common/lifecycle';
@@ -57,29 +58,11 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
5758
}
5859

5960
private async updateBranch(message: IRequestMessage<string>): Promise<void> {
60-
if (this._folderRepositoryManager.repository.state.workingTreeChanges.length > 0 || this._folderRepositoryManager.repository.state.indexChanges.length > 0) {
61-
await vscode.window.showErrorMessage(vscode.l10n.t('The pull request branch cannot be updated when the there changed files in the working tree or index. Stash or commit all change and then try again.'), { modal: true });
62-
return this._replyMessage(message, {});
63-
}
64-
const mergeSucceeded = await this._folderRepositoryManager.tryMergeBaseIntoHead(this._item, true);
65-
if (!mergeSucceeded) {
66-
this._replyMessage(message, {});
67-
}
68-
// The mergability of the PR doesn't update immediately. Poll.
69-
let mergability = PullRequestMergeability.Unknown;
70-
let attemptsRemaining = 5;
71-
do {
72-
mergability = (await this._item.getMergeability()).mergeability;
73-
attemptsRemaining--;
74-
await new Promise(c => setTimeout(c, 1000));
75-
} while (attemptsRemaining > 0 && mergability === PullRequestMergeability.Unknown);
76-
77-
const result: Partial<PullRequest> = {
78-
events: await this._item.getTimelineEvents(),
79-
mergeable: mergability,
80-
};
81-
await this.refresh();
82-
this._replyMessage(message, result);
61+
return PullRequestReviewHelpers.updateBranch(
62+
this.getReviewContext(),
63+
message,
64+
() => this.refresh()
65+
);
8366
}
8467

8568
protected override async _onDidReceiveMessage(message: IRequestMessage<any>) {
@@ -122,47 +105,11 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
122105
}
123106

124107
private async checkoutDefaultBranch(message: IRequestMessage<string>): Promise<void> {
125-
try {
126-
const defaultBranch = await this._folderRepositoryManager.getPullRequestRepositoryDefaultBranch(this._item);
127-
const prBranch = this._folderRepositoryManager.repository.state.HEAD?.name;
128-
await this._folderRepositoryManager.checkoutDefaultBranch(defaultBranch);
129-
if (prBranch) {
130-
await this._folderRepositoryManager.cleanupAfterPullRequest(prBranch, this._item);
131-
}
132-
} finally {
133-
// Complete webview promise so that button becomes enabled again
134-
this._replyMessage(message, {});
135-
}
108+
return PullRequestReviewHelpers.checkoutDefaultBranch(this.getReviewContext(), message);
136109
}
137110

138111
private reRequestReview(message: IRequestMessage<string>): void {
139-
let targetReviewer: ReviewState | undefined;
140-
const userReviewers: IAccount[] = [];
141-
const teamReviewers: ITeam[] = [];
142-
143-
for (const reviewer of this._existingReviewers) {
144-
let id = reviewer.reviewer.id;
145-
if (id && ((reviewer.state === 'REQUESTED') || (id === message.args))) {
146-
if (id === message.args) {
147-
targetReviewer = reviewer;
148-
}
149-
}
150-
}
151-
152-
if (targetReviewer && isITeam(targetReviewer.reviewer)) {
153-
teamReviewers.push(targetReviewer.reviewer);
154-
} else if (targetReviewer && !isITeam(targetReviewer.reviewer)) {
155-
userReviewers.push(targetReviewer.reviewer);
156-
}
157-
158-
this._item.requestReview(userReviewers, teamReviewers, true).then(() => {
159-
if (targetReviewer) {
160-
targetReviewer.state = 'REQUESTED';
161-
}
162-
this._replyMessage(message, {
163-
reviewers: this._existingReviewers,
164-
});
165-
});
112+
return PullRequestReviewHelpers.reRequestReview(this.getReviewContext(), message);
166113
}
167114

168115
public async refresh(): Promise<void> {
@@ -173,9 +120,22 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
173120
}
174121

175122
private getCurrentUserReviewState(reviewers: ReviewState[], currentUser: IAccount): string | undefined {
176-
const review = reviewers.find(r => reviewerId(r.reviewer) === currentUser.login);
177-
// There will always be a review. If not then the PR shouldn't have been or fetched/shown for the current user
178-
return review?.state;
123+
return PullRequestReviewHelpers.getCurrentUserReviewState(reviewers, currentUser);
124+
}
125+
126+
/**
127+
* Get the review context for helper functions
128+
*/
129+
private getReviewContext(): ReviewContext {
130+
return {
131+
item: this._item,
132+
folderRepositoryManager: this._folderRepositoryManager,
133+
existingReviewers: this._existingReviewers,
134+
postMessage: (message: any) => this._postMessage(message),
135+
replyMessage: (message: IRequestMessage<any>, response: any) => this._replyMessage(message, response),
136+
throwError: (message: IRequestMessage<any> | undefined, error: string) => this._throwError(message, error),
137+
getTimeline: () => this._item.getTimelineEvents()
138+
};
179139
}
180140

181141
private _prDisposables: vscode.Disposable[] | undefined = undefined;
@@ -346,58 +306,24 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
346306
}
347307

348308
private updateReviewers(review?: ReviewEvent): void {
349-
if (review && review.state) {
350-
const existingReviewer = this._existingReviewers.find(
351-
reviewer => review.user.login === reviewerId(reviewer.reviewer),
352-
);
353-
if (existingReviewer) {
354-
existingReviewer.state = review.state;
355-
} else {
356-
this._existingReviewers.push({
357-
reviewer: review.user,
358-
state: review.state,
359-
});
360-
}
361-
}
309+
PullRequestReviewHelpers.updateReviewers(this._existingReviewers, review);
362310
}
363311

364312
private async doReviewCommand(context: { body: string }, reviewType: ReviewType, action: (body: string) => Promise<ReviewEvent>) {
365-
const submittingMessage = {
366-
command: 'pr.submitting-review',
367-
lastReviewType: reviewType
368-
};
369-
this._postMessage(submittingMessage);
370-
try {
371-
const review = await action(context.body);
372-
this.updateReviewers(review);
373-
const reviewMessage: SubmitReviewReply & { command: string } = {
374-
command: 'pr.append-review',
375-
events: [],
376-
reviewers: this._existingReviewers,
377-
reviewedEvent: review,
378-
};
379-
await this._postMessage(reviewMessage);
380-
} catch (e) {
381-
vscode.window.showErrorMessage(vscode.l10n.t('Submitting review failed. {0}', formatError(e)));
382-
this._throwError(undefined, `${formatError(e)}`);
383-
this._postMessage({ command: 'pr.append-review' });
384-
}
313+
return PullRequestReviewHelpers.doReviewCommand(
314+
this.getReviewContext(),
315+
context,
316+
reviewType,
317+
action
318+
);
385319
}
386320

387321
private async doReviewMessage(message: IRequestMessage<string>, action: (body) => Promise<ReviewEvent>) {
388-
try {
389-
const review = await action(message.args);
390-
this.updateReviewers(review);
391-
const reviewMessage: SubmitReviewReply = {
392-
events: [],
393-
reviewedEvent: review,
394-
reviewers: this._existingReviewers,
395-
};
396-
this._replyMessage(message, reviewMessage);
397-
} catch (e) {
398-
vscode.window.showErrorMessage(vscode.l10n.t('Submitting review failed. {0}', formatError(e)));
399-
this._throwError(message, `${formatError(e)}`);
400-
}
322+
return PullRequestReviewHelpers.doReviewMessage(
323+
this.getReviewContext(),
324+
message,
325+
action
326+
);
401327
}
402328

403329
private approvePullRequest(body: string): Promise<ReviewEvent> {
@@ -446,47 +372,27 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
446372
}
447373

448374
private setReadyForReview(message: IRequestMessage<Record<string, unknown>>): void {
449-
this._item
450-
.setReadyForReview()
451-
.then(result => {
452-
this._replyMessage(message, result);
453-
})
454-
.catch(e => {
455-
vscode.window.showErrorMessage(vscode.l10n.t('Unable to set pull request ready for review. {0}', formatError(e)));
456-
this._throwError(message, '');
457-
});
375+
return PullRequestReviewHelpers.setReadyForReview(this.getReviewContext(), message);
458376
}
459377

460378
private async mergePullRequest(
461379
message: IRequestMessage<MergeArguments>,
462380
): Promise<void> {
463-
const { title, description, method } = message.args;
464-
const email = await this._folderRepositoryManager.getPreferredEmail(this._item);
465-
const yes = vscode.l10n.t('Yes');
466-
const confirmation = await vscode.window.showInformationMessage(
467-
vscode.l10n.t('Merge this pull request?'),
468-
{ modal: true },
469-
yes,
470-
);
471-
if (confirmation !== yes) {
472-
this._replyMessage(message, { state: GithubItemStateEnum.Open });
473-
return;
474-
}
475-
try {
476-
const result = await this._item.merge(this._folderRepositoryManager.repository, title, description, method, email);
477-
478-
if (!result.merged) {
479-
vscode.window.showErrorMessage(vscode.l10n.t('Merging pull request failed: {0}', result?.message ?? ''));
381+
return PullRequestReviewHelpers.mergePullRequest(
382+
this.getReviewContext(),
383+
message,
384+
{
385+
confirmMerge: async () => {
386+
const yes = vscode.l10n.t('Yes');
387+
const confirmation = await vscode.window.showInformationMessage(
388+
vscode.l10n.t('Merge this pull request?'),
389+
{ modal: true },
390+
yes,
391+
);
392+
return confirmation === yes;
393+
}
480394
}
481-
482-
this._replyMessage(message, {
483-
state: result.merged ? GithubItemStateEnum.Merged : GithubItemStateEnum.Open,
484-
});
485-
486-
} catch (e) {
487-
vscode.window.showErrorMessage(vscode.l10n.t('Unable to merge pull request. {0}', formatError(e)));
488-
this._throwError(message, '');
489-
}
395+
);
490396
}
491397

492398
private _getHtmlForWebview() {

0 commit comments

Comments
 (0)