From c26d786a1cac98ba4f6bf22e65133fdbba60e38d Mon Sep 17 00:00:00 2001 From: Elena Tanasoiu Date: Tue, 4 Oct 2022 10:55:33 +0100 Subject: [PATCH] Emit variantAnalysisAdded event When we first submit the variant analysis for processing, we'd like to update the query history panel. At the moment we're just adding the setup for triggering the event. In a future PR we'll consume this event and change the query history panel accordingly. In order for this to happen we will need to introduce a new `VariantAnalysisHistoryItem` type which will massage the data we get from the API into a type which the Query History panel can consume. Co-authored-by: Shati Patel --- extensions/ql-vscode/src/extension.ts | 8 +++--- .../remote-queries/remote-queries-manager.ts | 7 ++++- .../src/remote-queries/run-remote-query.ts | 6 +++- .../variant-analysis-manager.ts | 9 +++++- .../remote-queries/run-remote-query.test.ts | 28 +++++++++++++------ 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 39c26e2e6..c5d16765a 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -467,16 +467,16 @@ async function activateWithInstalledDistribution( const localQueryResultsView = new ResultsView(ctx, dbm, cliServer, queryServerLogger, labelProvider); ctx.subscriptions.push(localQueryResultsView); - void logger.log('Initializing remote queries manager.'); - const rqm = new RemoteQueriesManager(ctx, cliServer, queryStorageDir, logger); - ctx.subscriptions.push(rqm); - void logger.log('Initializing variant analysis manager.'); const variantAnalysisStorageDir = path.join(ctx.globalStorageUri.fsPath, 'variant-analyses'); await fs.ensureDir(variantAnalysisStorageDir); const variantAnalysisManager = new VariantAnalysisManager(ctx, cliServer, variantAnalysisStorageDir, logger); ctx.subscriptions.push(variantAnalysisManager); + void logger.log('Initializing remote queries manager.'); + const rqm = new RemoteQueriesManager(ctx, cliServer, queryStorageDir, logger, variantAnalysisManager); + ctx.subscriptions.push(rqm); + void logger.log('Initializing query history.'); const qhm = new QueryHistoryManager( qs, diff --git a/extensions/ql-vscode/src/remote-queries/remote-queries-manager.ts b/extensions/ql-vscode/src/remote-queries/remote-queries-manager.ts index 71319ecba..6350c0b5f 100644 --- a/extensions/ql-vscode/src/remote-queries/remote-queries-manager.ts +++ b/extensions/ql-vscode/src/remote-queries/remote-queries-manager.ts @@ -22,6 +22,7 @@ import { assertNever } from '../pure/helpers-pure'; import { QueryStatus } from '../query-status'; import { DisposableObject } from '../pure/disposable-object'; import { AnalysisResults } from './shared/analysis-result'; +import { VariantAnalysisManager } from './variant-analysis-manager'; const autoDownloadMaxSize = 300 * 1024; const autoDownloadMaxCount = 100; @@ -56,6 +57,7 @@ export class RemoteQueriesManager extends DisposableObject { private readonly remoteQueriesMonitor: RemoteQueriesMonitor; private readonly analysesResultsManager: AnalysesResultsManager; + private readonly variantAnalysisManager: VariantAnalysisManager; private readonly view: RemoteQueriesView; constructor( @@ -63,11 +65,13 @@ export class RemoteQueriesManager extends DisposableObject { private readonly cliServer: CodeQLCliServer, private readonly storagePath: string, logger: Logger, + variantAnalysisManager: VariantAnalysisManager, ) { super(); this.analysesResultsManager = new AnalysesResultsManager(ctx, cliServer, storagePath, logger); this.view = new RemoteQueriesView(ctx, logger, this.analysesResultsManager); this.remoteQueriesMonitor = new RemoteQueriesMonitor(ctx, logger); + this.variantAnalysisManager = variantAnalysisManager; this.remoteQueryAddedEventEmitter = this.push(new EventEmitter()); this.remoteQueryRemovedEventEmitter = this.push(new EventEmitter()); @@ -123,7 +127,8 @@ export class RemoteQueriesManager extends DisposableObject { credentials, uri || window.activeTextEditor?.document.uri, false, progress, - token); + token, + this.variantAnalysisManager); if (querySubmission?.query) { const query = querySubmission.query; diff --git a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts b/extensions/ql-vscode/src/remote-queries/run-remote-query.ts index d6505ac67..bc1a4e41b 100644 --- a/extensions/ql-vscode/src/remote-queries/run-remote-query.ts +++ b/extensions/ql-vscode/src/remote-queries/run-remote-query.ts @@ -29,6 +29,7 @@ import { getRepositorySelection, isValidSelection, RepositorySelection } from '. import { parseVariantAnalysisQueryLanguage, VariantAnalysisSubmission } from './shared/variant-analysis'; import { Repository } from './shared/repository'; import { processVariantAnalysis } from './variant-analysis-processor'; +import { VariantAnalysisManager } from './variant-analysis-manager'; export interface QlPack { name: string; @@ -182,7 +183,8 @@ export async function runRemoteQuery( uri: Uri | undefined, dryRun: boolean, progress: ProgressCallback, - token: CancellationToken + token: CancellationToken, + variantAnalysisManager: VariantAnalysisManager ): Promise { if (!(await cliServer.cliConstraints.supportsRemoteQueries())) { throw new Error(`Variant analysis is not supported by this version of CodeQL. Please upgrade to v${cli.CliVersionConstraint.CLI_VERSION_REMOTE_QUERIES @@ -273,6 +275,8 @@ export async function runRemoteQuery( const processedVariantAnalysis = processVariantAnalysis(variantAnalysisSubmission, variantAnalysisResponse); + variantAnalysisManager.onVariantAnalysisSubmitted(processedVariantAnalysis); + void logger.log(`Variant analysis:\n${JSON.stringify(processedVariantAnalysis, null, 2)}`); void showAndLogInformationMessage(`Variant analysis ${processedVariantAnalysis.query.name} submitted for processing`); diff --git a/extensions/ql-vscode/src/remote-queries/variant-analysis-manager.ts b/extensions/ql-vscode/src/remote-queries/variant-analysis-manager.ts index 424ffcca7..50805f79c 100644 --- a/extensions/ql-vscode/src/remote-queries/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/remote-queries/variant-analysis-manager.ts @@ -1,5 +1,5 @@ import * as ghApiClient from './gh-api/gh-api-client'; -import { CancellationToken, ExtensionContext } from 'vscode'; +import { CancellationToken, EventEmitter, ExtensionContext } from 'vscode'; import { DisposableObject } from '../pure/disposable-object'; import { Logger } from '../logging'; import { Credentials } from '../authentication'; @@ -21,6 +21,9 @@ import { VariantAnalysisResultsManager } from './variant-analysis-results-manage import { CodeQLCliServer } from '../cli'; export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager { + private readonly _onVariantAnalysisAdded = this.push(new EventEmitter()); + readonly onVariantAnalysisAdded = this._onVariantAnalysisAdded.event; + private readonly variantAnalysisMonitor: VariantAnalysisMonitor; private readonly variantAnalysisResultsManager: VariantAnalysisResultsManager; private readonly views = new Map(); @@ -73,6 +76,10 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA await this.getView(variantAnalysis.id)?.updateView(variantAnalysis); } + public onVariantAnalysisSubmitted(variantAnalysis: VariantAnalysis): void { + this._onVariantAnalysisAdded.fire(variantAnalysis); + } + private async onRepoStateUpdated(variantAnalysisId: number, repoState: VariantAnalysisScannedRepositoryState): Promise { await this.getView(variantAnalysisId)?.updateRepoState(repoState); } diff --git a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/run-remote-query.test.ts b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/run-remote-query.test.ts index 187169e6a..5a16c9913 100644 --- a/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/run-remote-query.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/cli-integration/remote-queries/run-remote-query.test.ts @@ -1,7 +1,7 @@ import { assert, expect } from 'chai'; import * as path from 'path'; import * as sinon from 'sinon'; -import { CancellationTokenSource, extensions, QuickPickItem, Uri, window } from 'vscode'; +import { CancellationTokenSource, ExtensionContext, extensions, QuickPickItem, Uri, window } from 'vscode'; import * as fs from 'fs-extra'; import * as os from 'os'; import * as yaml from 'js-yaml'; @@ -21,6 +21,9 @@ import { import { Repository } from '../../../remote-queries/gh-api/repository'; import { VariantAnalysisStatus } from '../../../remote-queries/shared/variant-analysis'; import { createMockApiResponse } from '../../factories/remote-queries/gh-api/variant-analysis-api-response'; +import { createMockExtensionContext } from '../../no-workspace'; +import { VariantAnalysisManager } from '../../../remote-queries/variant-analysis-manager'; +import { OutputChannelLogger } from '../../../logging'; describe('Remote queries', function() { const baseDir = path.join(__dirname, '../../../../src/vscode-tests/cli-integration'); @@ -37,6 +40,9 @@ describe('Remote queries', function() { let showQuickPickSpy: sinon.SinonStub; let getRepositoryFromNwoStub: sinon.SinonStub; let liveResultsStub: sinon.SinonStub; + let ctx: ExtensionContext; + let logger: any; + let variantAnalysisManager: VariantAnalysisManager; // use `function` so we have access to `this` beforeEach(async function() { @@ -49,6 +55,10 @@ describe('Remote queries', function() { throw new Error('Extension not initialized. Make sure cli is downloaded and installed properly.'); } + ctx = createMockExtensionContext(); + logger = new OutputChannelLogger('test-logger'); + variantAnalysisManager = new VariantAnalysisManager(ctx, cli, 'fake-storage-dir', logger); + if (!(await cli.cliConstraints.supportsRemoteQueries())) { console.log(`Remote queries are not supported on CodeQL CLI v${CliVersionConstraint.CLI_VERSION_REMOTE_QUERIES }. Skipping this test.`); @@ -94,7 +104,7 @@ describe('Remote queries', function() { it('should run a remote query that is part of a qlpack', async () => { const fileUri = getFile('data-remote-qlpack/in-pack.ql'); - const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token); + const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token, variantAnalysisManager); expect(querySubmissionResult).to.be.ok; const queryPackRootDir = querySubmissionResult!.queryDirPath!; printDirectoryContents(queryPackRootDir); @@ -155,7 +165,7 @@ describe('Remote queries', function() { it('should run a remote query that is not part of a qlpack', async () => { const fileUri = getFile('data-remote-no-qlpack/in-pack.ql'); - const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token); + const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token, variantAnalysisManager); expect(querySubmissionResult).to.be.ok; const queryPackRootDir = querySubmissionResult!.queryDirPath!; @@ -218,7 +228,7 @@ describe('Remote queries', function() { it('should run a remote query that is nested inside a qlpack', async () => { const fileUri = getFile('data-remote-qlpack-nested/subfolder/in-pack.ql'); - const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token); + const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token, variantAnalysisManager); expect(querySubmissionResult).to.be.ok; const queryPackRootDir = querySubmissionResult!.queryDirPath!; @@ -280,7 +290,7 @@ describe('Remote queries', function() { it('should cancel a run before uploading', async () => { const fileUri = getFile('data-remote-no-qlpack/in-pack.ql'); - const promise = runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token); + const promise = runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token, variantAnalysisManager); cancellationTokenSource.token.isCancellationRequested = true; @@ -306,7 +316,7 @@ describe('Remote queries', function() { it('should run a variant analysis that is part of a qlpack', async () => { const fileUri = getFile('data-remote-qlpack/in-pack.ql'); - const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token); + const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token, variantAnalysisManager); expect(querySubmissionResult).to.be.ok; const variantAnalysis = querySubmissionResult!.variantAnalysis!; expect(variantAnalysis.id).to.be.equal(mockApiResponse.id); @@ -319,7 +329,7 @@ describe('Remote queries', function() { it('should run a remote query that is not part of a qlpack', async () => { const fileUri = getFile('data-remote-no-qlpack/in-pack.ql'); - const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token); + const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token, variantAnalysisManager); expect(querySubmissionResult).to.be.ok; const variantAnalysis = querySubmissionResult!.variantAnalysis!; expect(variantAnalysis.id).to.be.equal(mockApiResponse.id); @@ -332,7 +342,7 @@ describe('Remote queries', function() { it('should run a remote query that is nested inside a qlpack', async () => { const fileUri = getFile('data-remote-qlpack-nested/subfolder/in-pack.ql'); - const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token); + const querySubmissionResult = await runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token, variantAnalysisManager); expect(querySubmissionResult).to.be.ok; const variantAnalysis = querySubmissionResult!.variantAnalysis!; expect(variantAnalysis.id).to.be.equal(mockApiResponse.id); @@ -345,7 +355,7 @@ describe('Remote queries', function() { it('should cancel a run before uploading', async () => { const fileUri = getFile('data-remote-no-qlpack/in-pack.ql'); - const promise = runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token); + const promise = runRemoteQuery(cli, credentials, fileUri, true, progress, cancellationTokenSource.token, variantAnalysisManager); cancellationTokenSource.token.isCancellationRequested = true;