Skip to content

Commit 09bae13

Browse files
committed
Add cancelling of variant analysis to view
This implements the "Stop query" button on the view. It moves some of the logic of actually cancelling the variant analysis to the manager instead of being in the query history to allow better re-use of the code.
1 parent 93054e1 commit 09bae13

File tree

10 files changed

+134
-25
lines changed

10 files changed

+134
-25
lines changed

extensions/ql-vscode/src/extension.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,14 @@ async function activateWithInstalledDistribution(
953953
})
954954
);
955955

956+
ctx.subscriptions.push(
957+
commandRunner('codeQL.cancelVariantAnalysis', async (
958+
variantAnalysisId: number,
959+
) => {
960+
await variantAnalysisManager.cancelVariantAnalysis(variantAnalysisId);
961+
})
962+
);
963+
956964
ctx.subscriptions.push(
957965
commandRunner('codeQL.openVariantAnalysis', async () => {
958966
await variantAnalysisManager.promptOpenVariantAnalysis();

extensions/ql-vscode/src/pure/interface-types.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -445,11 +445,6 @@ export interface SetVariantAnalysisMessage {
445445
variantAnalysis: VariantAnalysis;
446446
}
447447

448-
export type StopVariantAnalysisMessage = {
449-
t: 'stopVariantAnalysis';
450-
variantAnalysisId: number;
451-
}
452-
453448
export type VariantAnalysisState = {
454449
variantAnalysisId: number;
455450
}
@@ -481,15 +476,19 @@ export interface OpenLogsMessage {
481476
t: 'openLogs';
482477
}
483478

479+
export interface CancelVariantAnalysisMessage {
480+
t: 'cancelVariantAnalysis';
481+
}
482+
484483
export type ToVariantAnalysisMessage =
485484
| SetVariantAnalysisMessage
486485
| SetRepoResultsMessage
487486
| SetRepoStatesMessage;
488487

489488
export type FromVariantAnalysisMessage =
490489
| ViewLoadedMsg
491-
| StopVariantAnalysisMessage
492490
| RequestRepositoryResultsMessage
493491
| OpenQueryFileMessage
494492
| OpenQueryTextMessage
495-
| OpenLogsMessage;
493+
| OpenLogsMessage
494+
| CancelVariantAnalysisMessage;

extensions/ql-vscode/src/query-history.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import * as fs from 'fs-extra';
4040
import { CliVersionConstraint } from './cli';
4141
import { HistoryItemLabelProvider } from './history-item-label-provider';
4242
import { Credentials } from './authentication';
43-
import { cancelRemoteQuery, cancelVariantAnalysis } from './remote-queries/gh-api/gh-actions-api-client';
43+
import { cancelRemoteQuery } from './remote-queries/gh-api/gh-actions-api-client';
4444
import { RemoteQueriesManager } from './remote-queries/remote-queries-manager';
4545
import { RemoteQueryHistoryItem } from './remote-queries/remote-query-history-item';
4646
import { ResultsView } from './interface';
@@ -1119,9 +1119,7 @@ export class QueryHistoryManager extends DisposableObject {
11191119
const credentials = await this.getCredentials();
11201120
await cancelRemoteQuery(credentials, item.remoteQuery);
11211121
} else if (item.t === 'variant-analysis') {
1122-
void showAndLogInformationMessage('Cancelling variant analysis. This may take a while.');
1123-
const credentials = await this.getCredentials();
1124-
await cancelVariantAnalysis(credentials, item.variantAnalysis);
1122+
await commands.executeCommand('codeQL.cancelVariantAnalysis', item.variantAnalysis.id);
11251123
}
11261124
}
11271125
});

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ import { VariantAnalysisResultsManager } from './variant-analysis-results-manage
2222
import { getControllerRepo } from './run-remote-query';
2323
import { processUpdatedVariantAnalysis, processVariantAnalysisRepositoryTask } from './variant-analysis-processor';
2424
import PQueue from 'p-queue';
25-
import { createTimestampFile, showAndLogErrorMessage } from '../helpers';
25+
import { createTimestampFile, showAndLogErrorMessage, showAndLogInformationMessage } from '../helpers';
2626
import * as fs from 'fs-extra';
27+
import { cancelVariantAnalysis } from './gh-api/gh-actions-api-client';
2728

2829
export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager<VariantAnalysisView> {
2930
private static readonly REPO_STATES_FILENAME = 'repo_states.json';
@@ -281,6 +282,25 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
281282
);
282283
}
283284

285+
public async cancelVariantAnalysis(variantAnalysisId: number) {
286+
const variantAnalysis = this.variantAnalyses.get(variantAnalysisId);
287+
if (!variantAnalysis) {
288+
throw new Error(`No variant analysis with id: ${variantAnalysisId}`);
289+
}
290+
291+
if (!variantAnalysis.actionsWorkflowRunId) {
292+
throw new Error(`No workflow run id for variant analysis ${variantAnalysis.query.name}`);
293+
}
294+
295+
const credentials = await Credentials.initialize(this.ctx);
296+
if (!credentials) {
297+
throw Error('Error authenticating with GitHub');
298+
}
299+
300+
void showAndLogInformationMessage('Cancelling variant analysis. This may take a while.');
301+
await cancelVariantAnalysis(credentials, variantAnalysis);
302+
}
303+
284304
private getRepoStatesStoragePath(variantAnalysisId: number): string {
285305
return path.join(
286306
this.getVariantAnalysisStorageLocation(variantAnalysisId),

extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
9191
await this.onWebViewLoaded();
9292

9393
break;
94-
case 'stopVariantAnalysis':
95-
void logger.log(`Stop variant analysis: ${msg.variantAnalysisId}`);
94+
case 'cancelVariantAnalysis':
95+
void commands.executeCommand('codeQL.cancelVariantAnalysis', this.variantAnalysisId);
9696
break;
9797
case 'requestRepositoryResults':
9898
void commands.executeCommand('codeQL.loadVariantAnalysisRepoResults', this.variantAnalysisId, msg.repositoryFullName);

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ const openQueryText = () => {
3030
});
3131
};
3232

33+
const stopQuery = () => {
34+
vscode.postMessage({
35+
t: 'cancelVariantAnalysis',
36+
});
37+
};
38+
3339
const openLogs = () => {
3440
vscode.postMessage({
3541
t: 'openLogs',
@@ -88,7 +94,7 @@ export function VariantAnalysis({
8894
variantAnalysis={variantAnalysis}
8995
onOpenQueryFileClick={openQueryFile}
9096
onViewQueryTextClick={openQueryText}
91-
onStopQueryClick={() => console.log('Stop query')}
97+
onStopQueryClick={stopQuery}
9298
onCopyRepositoryListClick={() => console.log('Copy repository list')}
9399
onExportResultsClick={() => console.log('Export results')}
94100
onViewLogsClick={openLogs}

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisActions.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type Props = {
77
variantAnalysisStatus: VariantAnalysisStatus;
88

99
onStopQueryClick: () => void;
10+
stopQueryDisabled?: boolean;
1011

1112
onCopyRepositoryListClick: () => void;
1213
onExportResultsClick: () => void;
@@ -26,12 +27,13 @@ export const VariantAnalysisActions = ({
2627
variantAnalysisStatus,
2728
onStopQueryClick,
2829
onCopyRepositoryListClick,
29-
onExportResultsClick
30+
onExportResultsClick,
31+
stopQueryDisabled,
3032
}: Props) => {
3133
return (
3234
<Container>
3335
{variantAnalysisStatus === VariantAnalysisStatus.InProgress && (
34-
<Button appearance="secondary" onClick={onStopQueryClick}>
36+
<Button appearance="secondary" onClick={onStopQueryClick} disabled={stopQueryDisabled}>
3537
Stop query
3638
</Button>
3739
)}

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export const VariantAnalysisHeader = ({
7373
onStopQueryClick={onStopQueryClick}
7474
onCopyRepositoryListClick={onCopyRepositoryListClick}
7575
onExportResultsClick={onExportResultsClick}
76+
stopQueryDisabled={!variantAnalysis.actionsWorkflowRunId}
7677
/>
7778
</Row>
7879
<VariantAnalysisStats

extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { CodeQLExtensionInterface } from '../../../extension';
55
import { logger } from '../../../logging';
66
import * as config from '../../../config';
77
import * as ghApiClient from '../../../remote-queries/gh-api/gh-api-client';
8+
import * as ghActionsApiClient from '../../../remote-queries/gh-api/gh-actions-api-client';
89
import { Credentials } from '../../../authentication';
910
import * as fs from 'fs-extra';
1011
import * as path from 'path';
@@ -508,4 +509,80 @@ describe('Variant Analysis Manager', async function() {
508509
});
509510
});
510511
});
512+
513+
describe('cancelVariantAnalysis', async () => {
514+
let variantAnalysis: VariantAnalysis;
515+
let mockCancelVariantAnalysis: sinon.SinonStub;
516+
let getOctokitStub: sinon.SinonStub;
517+
518+
let variantAnalysisStorageLocation: string;
519+
520+
beforeEach(async () => {
521+
variantAnalysis = createMockVariantAnalysis({});
522+
523+
mockCancelVariantAnalysis = sandbox.stub(ghActionsApiClient, 'cancelVariantAnalysis');
524+
525+
variantAnalysisStorageLocation = variantAnalysisManager.getVariantAnalysisStorageLocation(variantAnalysis.id);
526+
await createTimestampFile(variantAnalysisStorageLocation);
527+
await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis);
528+
});
529+
530+
afterEach(() => {
531+
fs.rmSync(variantAnalysisStorageLocation, { recursive: true });
532+
});
533+
534+
describe('when the credentials are invalid', () => {
535+
beforeEach(async () => {
536+
sandbox.stub(Credentials, 'initialize').resolves(undefined);
537+
});
538+
539+
it('should return early', async () => {
540+
try {
541+
await variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id);
542+
} catch (error: any) {
543+
expect(error.message).to.equal('Error authenticating with GitHub');
544+
}
545+
});
546+
});
547+
548+
describe('when the credentials are valid', () => {
549+
let mockCredentials: Credentials;
550+
551+
beforeEach(async () => {
552+
mockCredentials = {
553+
getOctokit: () => Promise.resolve({
554+
request: getOctokitStub
555+
})
556+
} as unknown as Credentials;
557+
sandbox.stub(Credentials, 'initialize').resolves(mockCredentials);
558+
});
559+
560+
it('should return early if the variant analysis is not found', async () => {
561+
try {
562+
await variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id + 100);
563+
} catch (error: any) {
564+
expect(error.message).to.equal('No variant analysis with id: ' + (variantAnalysis.id + 100));
565+
}
566+
});
567+
568+
it('should return early if the variant analysis does not have an actions workflow run id', async () => {
569+
await variantAnalysisManager.onVariantAnalysisUpdated({
570+
...variantAnalysis,
571+
actionsWorkflowRunId: undefined,
572+
});
573+
574+
try {
575+
await variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id);
576+
} catch (error: any) {
577+
expect(error.message).to.equal('No workflow run id for variant analysis a-query-name');
578+
}
579+
});
580+
581+
it('should return cancel if valid', async () => {
582+
await variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id);
583+
584+
expect(mockCancelVariantAnalysis).to.have.been.calledWith(mockCredentials, variantAnalysis);
585+
});
586+
});
587+
});
511588
});

extensions/ql-vscode/src/vscode-tests/no-workspace/query-history.test.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,6 @@ describe('query-history', () => {
338338
describe('handleCancel', () => {
339339
let mockCredentials: Credentials;
340340
let mockCancelRemoteQuery: sinon.SinonStub;
341-
let mockCancelVariantAnalysis: sinon.SinonStub;
342341
let getOctokitStub: sinon.SinonStub;
343342

344343
beforeEach(async () => {
@@ -349,7 +348,6 @@ describe('query-history', () => {
349348
} as unknown as Credentials;
350349
sandbox.stub(Credentials, 'initialize').resolves(mockCredentials);
351350
mockCancelRemoteQuery = sandbox.stub(ghActionsApiClient, 'cancelRemoteQuery');
352-
mockCancelVariantAnalysis = sandbox.stub(ghActionsApiClient, 'cancelVariantAnalysis');
353351
});
354352

355353
describe('if the item is in progress', async () => {
@@ -408,7 +406,7 @@ describe('query-history', () => {
408406
const inProgress1 = variantAnalysisHistory[1];
409407

410408
await queryHistoryManager.handleCancel(inProgress1, [inProgress1]);
411-
expect(mockCancelVariantAnalysis).to.have.been.calledWith(mockCredentials, inProgress1.variantAnalysis);
409+
expect(executeCommandSpy).to.have.been.calledWith('codeQL.cancelVariantAnalysis', inProgress1.variantAnalysis.id);
412410
});
413411

414412
it('should cancel multiple variant analyses', async () => {
@@ -419,8 +417,8 @@ describe('query-history', () => {
419417
const inProgress2 = variantAnalysisHistory[3];
420418

421419
await queryHistoryManager.handleCancel(inProgress1, [inProgress1, inProgress2]);
422-
expect(mockCancelVariantAnalysis).to.have.been.calledWith(mockCredentials, inProgress1.variantAnalysis);
423-
expect(mockCancelVariantAnalysis).to.have.been.calledWith(mockCredentials, inProgress2.variantAnalysis);
420+
expect(executeCommandSpy).to.have.been.calledWith('codeQL.cancelVariantAnalysis', inProgress1.variantAnalysis.id);
421+
expect(executeCommandSpy).to.have.been.calledWith('codeQL.cancelVariantAnalysis', inProgress2.variantAnalysis.id);
424422
});
425423
});
426424

@@ -480,7 +478,7 @@ describe('query-history', () => {
480478
const completedVariantAnalysis = variantAnalysisHistory[0];
481479

482480
await queryHistoryManager.handleCancel(completedVariantAnalysis, [completedVariantAnalysis]);
483-
expect(mockCancelVariantAnalysis).to.not.have.been.calledWith(mockCredentials, completedVariantAnalysis.variantAnalysis);
481+
expect(executeCommandSpy).to.not.have.been.calledWith('codeQL.cancelVariantAnalysis', completedVariantAnalysis.variantAnalysis);
484482
});
485483

486484
it('should not cancel multiple variant analyses', async () => {
@@ -491,8 +489,8 @@ describe('query-history', () => {
491489
const failedVariantAnalysis = variantAnalysisHistory[2];
492490

493491
await queryHistoryManager.handleCancel(completedVariantAnalysis, [completedVariantAnalysis, failedVariantAnalysis]);
494-
expect(mockCancelVariantAnalysis).to.not.have.been.calledWith(mockCredentials, completedVariantAnalysis.variantAnalysis);
495-
expect(mockCancelVariantAnalysis).to.not.have.been.calledWith(mockCredentials, failedVariantAnalysis.variantAnalysis);
492+
expect(executeCommandSpy).to.not.have.been.calledWith('codeQL.cancelVariantAnalysis', completedVariantAnalysis.variantAnalysis.id);
493+
expect(executeCommandSpy).to.not.have.been.calledWith('codeQL.cancelVariantAnalysis', failedVariantAnalysis.variantAnalysis.id);
496494
});
497495
});
498496
});

0 commit comments

Comments
 (0)