From f4a866b04b2021813bd91c24c9305caf640bfaf6 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Fri, 23 Feb 2024 11:17:25 +0000 Subject: [PATCH] Add logic to trigger model evaluation run (#3397) --- extensions/ql-vscode/src/extension.ts | 1 + .../src/model-editor/model-editor-module.ts | 5 + .../src/model-editor/model-editor-view.ts | 7 + .../src/model-editor/model-evaluator.ts | 120 ++++++++++++++---- .../src/model-editor/modeling-store.ts | 2 +- .../variant-analysis-manager.ts | 6 +- .../model-editor/model-editor-view.test.ts | 3 + 7 files changed, 117 insertions(+), 27 deletions(-) diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 8e4680139..53a3b12df 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -977,6 +977,7 @@ async function activateWithInstalledDistribution( const modelEditorModule = await ModelEditorModule.initialize( app, dbm, + variantAnalysisManager, cliServer, qs, tmpDir.name, diff --git a/extensions/ql-vscode/src/model-editor/model-editor-module.ts b/extensions/ql-vscode/src/model-editor/model-editor-module.ts index 6acd88d7f..7551835d1 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-module.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-module.ts @@ -31,6 +31,7 @@ import { getModelsAsDataLanguage } from "./languages"; import { INITIAL_MODE } from "./shared/mode"; import { isSupportedLanguage } from "./supported-languages"; import { DefaultNotifier, checkConsistency } from "./consistency-check"; +import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; export class ModelEditorModule extends DisposableObject { private readonly queryStorageDir: string; @@ -43,6 +44,7 @@ export class ModelEditorModule extends DisposableObject { private constructor( private readonly app: App, private readonly databaseManager: DatabaseManager, + private readonly variantAnalysisManager: VariantAnalysisManager, private readonly cliServer: CodeQLCliServer, private readonly queryRunner: QueryRunner, baseQueryStorageDir: string, @@ -65,6 +67,7 @@ export class ModelEditorModule extends DisposableObject { public static async initialize( app: App, databaseManager: DatabaseManager, + variantAnalysisManager: VariantAnalysisManager, cliServer: CodeQLCliServer, queryRunner: QueryRunner, queryStorageDir: string, @@ -72,6 +75,7 @@ export class ModelEditorModule extends DisposableObject { const modelEditorModule = new ModelEditorModule( app, databaseManager, + variantAnalysisManager, cliServer, queryRunner, queryStorageDir, @@ -240,6 +244,7 @@ export class ModelEditorModule extends DisposableObject { this.modelingEvents, this.modelConfig, this.databaseManager, + this.variantAnalysisManager, this.cliServer, this.queryRunner, this.queryStorageDir, diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 664ecb728..31ca255f6 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -59,6 +59,7 @@ import { runSuggestionsQuery } from "./suggestion-queries"; import { parseAccessPathSuggestionRowsToOptions } from "./suggestions-bqrs"; import { ModelEvaluator } from "./model-evaluator"; import type { ModelEvaluationRunState } from "./shared/model-evaluation-run-state"; +import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; export class ModelEditorView extends AbstractWebview< ToModelEditorMessage, @@ -77,6 +78,7 @@ export class ModelEditorView extends AbstractWebview< private readonly modelingEvents: ModelingEvents, private readonly modelConfig: ModelConfigListener, private readonly databaseManager: DatabaseManager, + private readonly variantAnalysisManager: VariantAnalysisManager, private readonly cliServer: CodeQLCliServer, private readonly queryRunner: QueryRunner, private readonly queryStorageDir: string, @@ -115,9 +117,13 @@ export class ModelEditorView extends AbstractWebview< this.languageDefinition = getModelsAsDataLanguage(language); this.modelEvaluator = new ModelEvaluator( + this.app.logger, + this.cliServer, modelingStore, modelingEvents, + this.variantAnalysisManager, databaseItem, + language, this.updateModelEvaluationRun.bind(this), ); this.push(this.modelEvaluator); @@ -801,6 +807,7 @@ export class ModelEditorView extends AbstractWebview< this.modelingEvents, this.modelConfig, this.databaseManager, + this.variantAnalysisManager, this.cliServer, this.queryRunner, this.queryStorageDir, diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index d3920cdb2..5e1e9dc55 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -3,14 +3,24 @@ import type { ModelingEvents } from "./modeling-events"; import type { DatabaseItem } from "../databases/local-databases"; import type { ModelEvaluationRun } from "./model-evaluation-run"; import { DisposableObject } from "../common/disposable-object"; -import { sleep } from "../common/time"; import type { ModelEvaluationRunState } from "./shared/model-evaluation-run-state"; +import type { BaseLogger } from "../common/logging"; +import type { CodeQLCliServer } from "../codeql-cli/cli"; +import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; +import type { QueryLanguage } from "../common/query-language"; +import { resolveCodeScanningQueryPack } from "../variant-analysis/code-scanning-pack"; +import { withProgress } from "../common/vscode/progress"; +import type { VariantAnalysis } from "../variant-analysis/shared/variant-analysis"; export class ModelEvaluator extends DisposableObject { public constructor( + private readonly logger: BaseLogger, + private readonly cliServer: CodeQLCliServer, private readonly modelingStore: ModelingStore, private readonly modelingEvents: ModelingEvents, + private readonly variantAnalysisManager: VariantAnalysisManager, private readonly dbItem: DatabaseItem, + private readonly language: QueryLanguage, private readonly updateView: ( run: ModelEvaluationRunState, ) => Promise, @@ -28,18 +38,48 @@ export class ModelEvaluator extends DisposableObject { }; this.modelingStore.updateModelEvaluationRun(this.dbItem, evaluationRun); - // For now, just wait 5 seconds and then update the store. - // In the future, this will be replaced with the actual evaluation process. - void sleep(5000).then(() => { - const completedEvaluationRun: ModelEvaluationRun = { - isPreparing: false, - variantAnalysisId: undefined, - }; - this.modelingStore.updateModelEvaluationRun( - this.dbItem, - completedEvaluationRun, - ); - }); + // Build pack + const qlPack = await resolveCodeScanningQueryPack( + this.logger, + this.cliServer, + this.language, + ); + + if (!qlPack) { + this.modelingStore.updateModelEvaluationRun(this.dbItem, undefined); + throw new Error("Unable to trigger evaluation run"); + } + + // Submit variant analysis and monitor progress + return withProgress( + async (progress, token) => { + let variantAnalysisId: number | undefined = undefined; + try { + variantAnalysisId = + await this.variantAnalysisManager.runVariantAnalysis( + qlPack, + progress, + token, + ); + } catch (e) { + this.modelingStore.updateModelEvaluationRun(this.dbItem, undefined); + throw e; + } + + if (variantAnalysisId) { + this.monitorVariantAnalysis(variantAnalysisId); + } else { + this.modelingStore.updateModelEvaluationRun(this.dbItem, undefined); + throw new Error( + "Unable to trigger variant analysis for evaluation run", + ); + } + }, + { + title: "Run Variant Analysis", + cancellable: true, + }, + ); } public async stopEvaluation() { @@ -55,19 +95,51 @@ export class ModelEvaluator extends DisposableObject { private registerToModelingEvents() { this.push( this.modelingEvents.onModelEvaluationRunChanged(async (event) => { - if ( - event.evaluationRun && - event.dbUri === this.dbItem.databaseUri.toString() - ) { - const run: ModelEvaluationRunState = { - isPreparing: event.evaluationRun.isPreparing, - - // TODO: Get variant analysis from id. - variantAnalysis: undefined, - }; - await this.updateView(run); + if (event.dbUri === this.dbItem.databaseUri.toString()) { + if (!event.evaluationRun) { + await this.updateView({ + isPreparing: false, + variantAnalysis: undefined, + }); + } else { + const variantAnalysis = await this.getVariantAnalysisForRun( + event.evaluationRun, + ); + const run: ModelEvaluationRunState = { + isPreparing: event.evaluationRun.isPreparing, + variantAnalysis, + }; + await this.updateView(run); + } } }), ); } + + private async getVariantAnalysisForRun( + evaluationRun: ModelEvaluationRun, + ): Promise { + if (evaluationRun.variantAnalysisId) { + return await this.variantAnalysisManager.getVariantAnalysis( + evaluationRun.variantAnalysisId, + ); + } + return undefined; + } + + private monitorVariantAnalysis(variantAnalysisId: number) { + this.push( + this.variantAnalysisManager.onVariantAnalysisStatusUpdated( + async (variantAnalysis) => { + // Make sure it's the variant analysis we're interested in + if (variantAnalysisId === variantAnalysis.id) { + await this.updateView({ + isPreparing: false, + variantAnalysis, + }); + } + }, + ), + ); + } } diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index a9fda24f6..364db48a9 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -378,7 +378,7 @@ export class ModelingStore extends DisposableObject { public updateModelEvaluationRun( dbItem: DatabaseItem, - evaluationRun: ModelEvaluationRun, + evaluationRun: ModelEvaluationRun | undefined, ) { this.changeModelEvaluationRun(dbItem, (state) => { state.modelEvaluationRun = evaluationRun; diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 3fc650f9f..7e59ae17b 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -298,7 +298,7 @@ export class VariantAnalysisManager qlPackDetails: QlPackDetails, progress: ProgressCallback, token: CancellationToken, - ): Promise { + ): Promise { await saveBeforeStart(); progress({ @@ -379,7 +379,7 @@ export class VariantAnalysisManager } catch (e: unknown) { // If the error is handled by the handleRequestError function, we don't need to throw if (e instanceof RequestError && handleRequestError(e, this.app.logger)) { - return; + return undefined; } throw e; @@ -405,6 +405,8 @@ export class VariantAnalysisManager "codeQL.monitorNewVariantAnalysis", processedVariantAnalysis, ); + + return processedVariantAnalysis.id; } public async rehydrateVariantAnalysis(variantAnalysis: VariantAnalysis) { diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts index ee2cf8bb1..e410c5ad1 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts @@ -12,6 +12,7 @@ import { createMockModelingStore } from "../../../__mocks__/model-editor/modelin import type { ModelConfigListener } from "../../../../src/config"; import { createMockModelingEvents } from "../../../__mocks__/model-editor/modelingEventsMock"; import { QueryLanguage } from "../../../../src/common/query-language"; +import type { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager"; describe("ModelEditorView", () => { const app = createMockApp({}); @@ -21,6 +22,7 @@ describe("ModelEditorView", () => { onDidChangeConfiguration: jest.fn(), }); const databaseManager = mockEmptyDatabaseManager(); + const variantAnalysisManager = mockedObject({}); const cliServer = mockedObject({}); const queryRunner = mockedObject({}); const queryStorageDir = "/a/b/c/d"; @@ -48,6 +50,7 @@ describe("ModelEditorView", () => { modelingEvents, modelConfig, databaseManager, + variantAnalysisManager, cliServer, queryRunner, queryStorageDir,