Skip to content

Commit d00624c

Browse files
authored
Merge pull request #2420 from github/koesie10/cancel-monitor-on-404
Cancel monitoring variant analysis on 404 response
2 parents e9552df + 2d2a1fb commit d00624c

5 files changed

Lines changed: 140 additions & 6 deletions

File tree

extensions/ql-vscode/src/common/commands.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ export type VariantAnalysisCommands = {
251251
"codeQL.monitorRehydratedVariantAnalysis": (
252252
variantAnalysis: VariantAnalysis,
253253
) => Promise<void>;
254+
"codeQL.monitorReauthenticatedVariantAnalysis": (
255+
variantAnalysis: VariantAnalysis,
256+
) => Promise<void>;
254257
"codeQL.openVariantAnalysisLogs": (
255258
variantAnalysisId: number,
256259
) => Promise<void>;

extensions/ql-vscode/src/common/vscode/authentication.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as Octokit from "@octokit/rest";
33
import { retry } from "@octokit/plugin-retry";
44
import { Credentials } from "../authentication";
55

6-
const GITHUB_AUTH_PROVIDER_ID = "github";
6+
export const GITHUB_AUTH_PROVIDER_ID = "github";
77

88
// We need 'repo' scope for triggering workflows, 'gist' scope for exporting results to Gist,
99
// and 'read:packages' for reading private CodeQL packages.

extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {
55
getVariantAnalysisRepo,
66
} from "./gh-api/gh-api-client";
77
import {
8+
authentication,
9+
AuthenticationSessionsChangeEvent,
810
CancellationToken,
911
env,
1012
EventEmitter,
@@ -72,6 +74,7 @@ import {
7274
REPO_STATES_FILENAME,
7375
writeRepoStates,
7476
} from "./repo-states-store";
77+
import { GITHUB_AUTH_PROVIDER_ID } from "../common/vscode/authentication";
7578

7679
export class VariantAnalysisManager
7780
extends DisposableObject
@@ -131,6 +134,10 @@ export class VariantAnalysisManager
131134
this.variantAnalysisResultsManager.onResultLoaded(
132135
this.onRepoResultLoaded.bind(this),
133136
);
137+
138+
this.push(
139+
authentication.onDidChangeSessions(this.onDidChangeSessions.bind(this)),
140+
);
134141
}
135142

136143
getCommands(): VariantAnalysisCommands {
@@ -144,6 +151,8 @@ export class VariantAnalysisManager
144151
this.monitorVariantAnalysis.bind(this),
145152
"codeQL.monitorRehydratedVariantAnalysis":
146153
this.monitorVariantAnalysis.bind(this),
154+
"codeQL.monitorReauthenticatedVariantAnalysis":
155+
this.monitorVariantAnalysis.bind(this),
147156
"codeQL.openVariantAnalysisLogs": this.openVariantAnalysisLogs.bind(this),
148157
"codeQL.openVariantAnalysisView": this.showView.bind(this),
149158
"codeQL.runVariantAnalysis":
@@ -504,6 +513,38 @@ export class VariantAnalysisManager
504513
repoStates[repoState.repositoryId] = repoState;
505514
}
506515

516+
private async onDidChangeSessions(
517+
event: AuthenticationSessionsChangeEvent,
518+
): Promise<void> {
519+
if (event.provider.id !== GITHUB_AUTH_PROVIDER_ID) {
520+
return;
521+
}
522+
523+
for (const variantAnalysis of this.variantAnalyses.values()) {
524+
if (
525+
this.variantAnalysisMonitor.isMonitoringVariantAnalysis(
526+
variantAnalysis.id,
527+
)
528+
) {
529+
continue;
530+
}
531+
532+
if (
533+
await isVariantAnalysisComplete(
534+
variantAnalysis,
535+
this.makeResultDownloadChecker(variantAnalysis),
536+
)
537+
) {
538+
continue;
539+
}
540+
541+
void this.app.commands.execute(
542+
"codeQL.monitorReauthenticatedVariantAnalysis",
543+
variantAnalysis,
544+
);
545+
}
546+
}
547+
507548
public async monitorVariantAnalysis(
508549
variantAnalysis: VariantAnalysis,
509550
): Promise<void> {

extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { env, EventEmitter } from "vscode";
22
import { getVariantAnalysis } from "./gh-api/gh-api-client";
3+
import { RequestError } from "@octokit/request-error";
34

45
import {
56
isFinalVariantAnalysisStatus,
@@ -27,6 +28,8 @@ export class VariantAnalysisMonitor extends DisposableObject {
2728
);
2829
readonly onVariantAnalysisChange = this._onVariantAnalysisChange.event;
2930

31+
private readonly monitoringVariantAnalyses = new Set<number>();
32+
3033
constructor(
3134
private readonly app: App,
3235
private readonly shouldCancelMonitor: (
@@ -36,9 +39,37 @@ export class VariantAnalysisMonitor extends DisposableObject {
3639
super();
3740
}
3841

42+
public isMonitoringVariantAnalysis(variantAnalysisId: number): boolean {
43+
return this.monitoringVariantAnalyses.has(variantAnalysisId);
44+
}
45+
3946
public async monitorVariantAnalysis(
4047
variantAnalysis: VariantAnalysis,
4148
): Promise<void> {
49+
if (this.monitoringVariantAnalyses.has(variantAnalysis.id)) {
50+
void extLogger.log(
51+
`Already monitoring variant analysis ${variantAnalysis.id}`,
52+
);
53+
return;
54+
}
55+
56+
this.monitoringVariantAnalyses.add(variantAnalysis.id);
57+
try {
58+
await this._monitorVariantAnalysis(variantAnalysis);
59+
} finally {
60+
this.monitoringVariantAnalyses.delete(variantAnalysis.id);
61+
}
62+
}
63+
64+
private async _monitorVariantAnalysis(
65+
variantAnalysis: VariantAnalysis,
66+
): Promise<void> {
67+
const variantAnalysisLabel = `${variantAnalysis.query.name} (${
68+
variantAnalysis.query.language
69+
}) [${new Date(variantAnalysis.executionStartTime).toLocaleString(
70+
env.language,
71+
)}]`;
72+
4273
let attemptCount = 0;
4374
const scannedReposDownloaded: number[] = [];
4475

@@ -61,11 +92,7 @@ export class VariantAnalysisMonitor extends DisposableObject {
6192
} catch (e) {
6293
const errorMessage = getErrorMessage(e);
6394

64-
const message = `Error while monitoring variant analysis ${
65-
variantAnalysis.query.name
66-
} (${variantAnalysis.query.language}) [${new Date(
67-
variantAnalysis.executionStartTime,
68-
).toLocaleString(env.language)}]: ${errorMessage}`;
95+
const message = `Error while monitoring variant analysis ${variantAnalysisLabel}: ${errorMessage}`;
6996

7097
// If we have already shown this error to the user, don't show it again.
7198
if (lastErrorShown === errorMessage) {
@@ -75,6 +102,19 @@ export class VariantAnalysisMonitor extends DisposableObject {
75102
lastErrorShown = errorMessage;
76103
}
77104

105+
if (e instanceof RequestError && e.status === 404) {
106+
// We want to show the error message to the user, but we don't want to
107+
// keep polling for the variant analysis if it no longer exists.
108+
// Therefore, this block is down here rather than at the top of the
109+
// catch block.
110+
void extLogger.log(
111+
`Variant analysis ${variantAnalysisLabel} no longer exists or is no longer accessible, stopping monitoring.`,
112+
);
113+
// Cancel monitoring on 404, as this probably means the user does not have access to it anymore
114+
// e.g. lost access to repo, or repo was deleted
115+
return;
116+
}
117+
78118
continue;
79119
}
80120

extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as ghApiClient from "../../../../src/variant-analysis/gh-api/gh-api-client";
2+
import { RequestError } from "@octokit/request-error";
23
import { VariantAnalysisMonitor } from "../../../../src/variant-analysis/variant-analysis-monitor";
34
import {
45
VariantAnalysis as VariantAnalysisApiResponse,
@@ -297,6 +298,55 @@ describe("Variant Analysis Monitor", () => {
297298
expect(mockEecuteCommand).not.toBeCalled();
298299
});
299300
});
301+
302+
describe("when a 404 is returned", () => {
303+
let showAndLogWarningMessageSpy: jest.SpiedFunction<
304+
typeof helpers.showAndLogWarningMessage
305+
>;
306+
307+
beforeEach(async () => {
308+
showAndLogWarningMessageSpy = jest
309+
.spyOn(helpers, "showAndLogWarningMessage")
310+
.mockResolvedValue(undefined);
311+
312+
const scannedRepos = createMockScannedRepos([
313+
"pending",
314+
"in_progress",
315+
"in_progress",
316+
"in_progress",
317+
"pending",
318+
"pending",
319+
]);
320+
mockApiResponse = createMockApiResponse("in_progress", scannedRepos);
321+
mockGetVariantAnalysis.mockResolvedValueOnce(mockApiResponse);
322+
323+
mockGetVariantAnalysis.mockRejectedValueOnce(
324+
new RequestError("Not Found", 404, {
325+
request: {
326+
method: "GET",
327+
url: "",
328+
headers: {},
329+
},
330+
response: {
331+
status: 404,
332+
headers: {},
333+
url: "",
334+
data: {},
335+
},
336+
}),
337+
);
338+
});
339+
340+
it("should stop requesting the variant analysis", async () => {
341+
await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis);
342+
343+
expect(mockGetVariantAnalysis).toHaveBeenCalledTimes(2);
344+
expect(showAndLogWarningMessageSpy).toHaveBeenCalledTimes(1);
345+
expect(showAndLogWarningMessageSpy).toHaveBeenCalledWith(
346+
expect.stringMatching(/not found/i),
347+
);
348+
});
349+
});
300350
});
301351

302352
function limitNumberOfAttemptsToMonitor() {

0 commit comments

Comments
 (0)