Skip to content

Commit 01fdbd4

Browse files
authored
Handle rate limit better in create view (#8547)
1 parent 5ea3ac5 commit 01fdbd4

File tree

3 files changed

+88
-11
lines changed

3 files changed

+88
-11
lines changed

src/github/createPRViewProvider.ts

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
PullRequestDefaults,
1111
titleAndBodyFrom,
1212
} from './folderRepositoryManager';
13-
import { GitHubRepository } from './githubRepository';
13+
import { GitHubRepository, isRateLimitError, ViewerPermission } from './githubRepository';
1414
import { IAccount, ILabel, IMilestone, IProject, isITeam, ITeam, MergeMethod, RepoAccessAndMergeMethods } from './interface';
1515
import { BaseBranchMetadata, PullRequestGitHelper } from './pullRequestGitHelper';
1616
import { PullRequestModel } from './pullRequestModel';
@@ -211,13 +211,47 @@ export abstract class BaseCreatePullRequestViewProvider<T extends BasePullReques
211211

212212
const defaultBaseBranch = detectedBaseMetadata?.branch ?? this._pullRequestDefaults.base;
213213

214-
const [defaultTitleAndDescription, mergeConfiguration, viewerPermission, mergeQueueMethodForBranch, labels] = await Promise.all([
215-
this.getTitleAndDescription(defaultCompareBranch, defaultBaseBranch),
216-
this.getMergeConfiguration(defaultBaseRemote.owner, defaultBaseRemote.repositoryName),
217-
defaultOrigin.getViewerPermission(),
218-
this._folderRepositoryManager.mergeQueueMethodForBranch(defaultBaseBranch, defaultBaseRemote.owner, defaultBaseRemote.repositoryName),
219-
this.getPullRequestDefaultLabels(defaultBaseRemote)
220-
]);
214+
let defaultTitleAndDescription: { title: string; description: string };
215+
let mergeConfiguration: RepoAccessAndMergeMethods;
216+
let viewerPermission: ViewerPermission;
217+
let mergeQueueMethodForBranch: MergeMethod | undefined;
218+
let labels: ILabel[];
219+
try {
220+
[defaultTitleAndDescription, mergeConfiguration, viewerPermission, mergeQueueMethodForBranch, labels] = await Promise.all([
221+
this.getTitleAndDescription(defaultCompareBranch, defaultBaseBranch),
222+
this.getMergeConfiguration(defaultBaseRemote.owner, defaultBaseRemote.repositoryName),
223+
defaultOrigin.getViewerPermission(),
224+
this._folderRepositoryManager.mergeQueueMethodForBranch(defaultBaseBranch, defaultBaseRemote.owner, defaultBaseRemote.repositoryName),
225+
this.getPullRequestDefaultLabels(defaultBaseRemote)
226+
]);
227+
} catch (e) {
228+
if (isRateLimitError(e)) {
229+
vscode.window.showErrorMessage(vscode.l10n.t('GitHub API rate limit exceeded. Please wait and try again.'));
230+
}
231+
Logger.error(`Error initializing create pull request view: ${e}`, BaseCreatePullRequestViewProvider.ID);
232+
return {
233+
canModifyBranches: true,
234+
defaultBaseRemote,
235+
defaultBaseBranch,
236+
defaultCompareRemote,
237+
defaultCompareBranch: this._defaultCompareBranch,
238+
defaultTitle: '',
239+
defaultDescription: '',
240+
baseHasMergeQueue: false,
241+
remoteCount: remotes.length,
242+
autoMergeDefault: false,
243+
createError: '',
244+
isDraftDefault: false,
245+
isDarkTheme: vscode.window.activeColorTheme.kind === vscode.ColorThemeKind.Dark,
246+
generateTitleAndDescriptionTitle: undefined,
247+
creating: false,
248+
initializeWithGeneratedTitleAndDescription: false,
249+
preReviewState: PreReviewState.None,
250+
preReviewer: undefined,
251+
reviewing: false,
252+
usingTemplate: false,
253+
};
254+
}
221255

222256
const defaultCreateOption = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<'lastUsed' | 'create' | 'createDraft' | 'createAutoMerge'>(DEFAULT_CREATE_OPTION, 'lastUsed');
223257
const lastCreateMethod: { autoMerge: boolean, mergeMethod: MergeMethod | undefined, isDraft: boolean } | undefined = this._folderRepositoryManager.context.workspaceState.get<{ autoMerge: boolean, mergeMethod: MergeMethod, isDraft } | undefined>(PREVIOUS_CREATE_METHOD, undefined);
@@ -994,7 +1028,18 @@ Don't forget to commit your template file to the repository so that it can be us
9941028
}
9951029

9961030
private async processRemoteAndBranchResult(githubRepository: GitHubRepository, result: { remote: RemoteInfo, branch: string }, isBase: boolean) {
997-
const [defaultBranch, viewerPermission] = await Promise.all([githubRepository.getDefaultBranch(), githubRepository.getViewerPermission()]);
1031+
let viewerPermission: ViewerPermission;
1032+
try {
1033+
viewerPermission = await githubRepository.getViewerPermission();
1034+
} catch (e) {
1035+
if (isRateLimitError(e)) {
1036+
vscode.window.showErrorMessage(vscode.l10n.t('GitHub API rate limit exceeded. Please wait and try again.'));
1037+
viewerPermission = ViewerPermission.Unknown;
1038+
} else {
1039+
throw e;
1040+
}
1041+
}
1042+
const defaultBranch = await githubRepository.getDefaultBranch();
9981043

9991044
commands.setContext(contexts.CREATE_PR_PERMISSIONS, viewerPermission);
10001045
let chooseResult: ChooseBaseRemoteAndBranchResult | ChooseCompareRemoteAndBranchResult;

src/github/folderRepositoryManager.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { ConflictModel } from './conflictGuide';
1111
import { ConflictResolutionCoordinator } from './conflictResolutionCoordinator';
1212
import { Conflict, ConflictResolutionModel } from './conflictResolutionModel';
1313
import { CredentialStore } from './credentials';
14-
import { CopilotWorkingStatus, GitHubRepository, ItemsData, PULL_REQUEST_PAGE_SIZE, PullRequestChangeEvent, PullRequestData, TeamReviewerRefreshKind, ViewerPermission } from './githubRepository';
14+
import { CopilotWorkingStatus, GitHubRepository, isRateLimitError, ItemsData, PULL_REQUEST_PAGE_SIZE, PullRequestChangeEvent, PullRequestData, TeamReviewerRefreshKind, ViewerPermission } from './githubRepository';
1515
import { PullRequestResponse, PullRequestState } from './graphql';
1616
import { IAccount, ILabel, IMilestone, IProject, IPullRequestsPagingOptions, Issue, ITeam, MergeMethod, PRType, PullRequestMergeability, RepoAccessAndMergeMethods, User } from './interface';
1717
import { IssueModel } from './issueModel';
@@ -2957,7 +2957,16 @@ export class FolderRepositoryManager extends Disposable {
29572957
pushRemote,
29582958
this.credentialStore,
29592959
);
2960-
const permission = await githubRepo.getViewerPermission();
2960+
let permission: ViewerPermission;
2961+
try {
2962+
permission = await githubRepo.getViewerPermission();
2963+
} catch (e) {
2964+
if (isRateLimitError(e)) {
2965+
vscode.window.showErrorMessage(vscode.l10n.t('GitHub API rate limit exceeded. Please wait and try again.'), { modal: true });
2966+
return;
2967+
}
2968+
throw e;
2969+
}
29612970
let selectedRemote: GitHubRemote | undefined;
29622971
if (
29632972
permission === ViewerPermission.Read ||

src/github/githubRepository.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,26 @@ export enum ViewerPermission {
124124
Write = 'WRITE',
125125
}
126126

127+
export class RateLimitError extends Error {
128+
constructor(message?: string) {
129+
super(message ?? 'GitHub API rate limit exceeded');
130+
this.name = 'RateLimitError';
131+
}
132+
}
133+
134+
export function isRateLimitError(e: unknown): boolean {
135+
if (e instanceof RateLimitError) {
136+
return true;
137+
}
138+
if (e instanceof Error) {
139+
const msg = e.message.toLowerCase();
140+
if (msg.includes('rate limit') || msg.includes('secondary rate') || msg.includes('abuse detection')) {
141+
return true;
142+
}
143+
}
144+
return false;
145+
}
146+
127147
export enum TeamReviewerRefreshKind {
128148
None,
129149
Try,
@@ -941,6 +961,9 @@ export class GitHubRepository extends Disposable {
941961
return parseGraphQLViewerPermission(data);
942962
} catch (e) {
943963
Logger.error(`Unable to fetch viewer permission: ${e}`, this.id);
964+
if (isRateLimitError(e)) {
965+
throw new RateLimitError();
966+
}
944967
return ViewerPermission.Unknown;
945968
}
946969
}

0 commit comments

Comments
 (0)