diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index e15d647c1e6..00203bd6a99 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -109,10 +109,7 @@ import { handleInstallPackDependencies, } from "./packaging"; import { HistoryItemLabelProvider } from "./query-history/history-item-label-provider"; -import { - exportSelectedVariantAnalysisResults, - exportVariantAnalysisResults, -} from "./variant-analysis/export-results"; +import { exportSelectedVariantAnalysisResults } from "./variant-analysis/export-results"; import { EvalLogViewer } from "./eval-log-viewer"; import { SummaryLanguageSupport } from "./log-insights/summary-language-support"; import { JoinOrderScannerProvider } from "./log-insights/join-order"; @@ -1149,35 +1146,10 @@ async function activateWithInstalledDistribution( ctx.subscriptions.push( commandRunner("codeQL.exportSelectedVariantAnalysisResults", async () => { - await exportSelectedVariantAnalysisResults(qhm); + await exportSelectedVariantAnalysisResults(variantAnalysisManager, qhm); }), ); - ctx.subscriptions.push( - commandRunnerWithProgress( - "codeQL.exportVariantAnalysisResults", - async ( - progress: ProgressCallback, - token: CancellationToken, - variantAnalysisId: number, - filterSort?: RepositoriesFilterSortStateWithIds, - ) => { - await exportVariantAnalysisResults( - variantAnalysisManager, - variantAnalysisId, - filterSort, - app.credentials, - progress, - token, - ); - }, - { - title: "Exporting variant analysis results", - cancellable: true, - }, - ), - ); - ctx.subscriptions.push( commandRunner( "codeQL.loadVariantAnalysisRepoResults", diff --git a/extensions/ql-vscode/src/query-history/query-history-manager.ts b/extensions/ql-vscode/src/query-history/query-history-manager.ts index 8dda7155a52..9647613cd2c 100644 --- a/extensions/ql-vscode/src/query-history/query-history-manager.ts +++ b/extensions/ql-vscode/src/query-history/query-history-manager.ts @@ -1122,8 +1122,7 @@ export class QueryHistoryManager extends DisposableObject { return; } - await commands.executeCommand( - "codeQL.exportVariantAnalysisResults", + await this.variantAnalysisManager.exportResults( finalSingleItem.variantAnalysis.id, ); } diff --git a/extensions/ql-vscode/src/variant-analysis/export-results.ts b/extensions/ql-vscode/src/variant-analysis/export-results.ts index faf3f0ee528..7b5647a00a3 100644 --- a/extensions/ql-vscode/src/variant-analysis/export-results.ts +++ b/extensions/ql-vscode/src/variant-analysis/export-results.ts @@ -9,7 +9,11 @@ import { window, workspace, } from "vscode"; -import { ProgressCallback, UserCancellationException } from "../commandRunner"; +import { + ProgressCallback, + UserCancellationException, + withProgress, +} from "../commandRunner"; import { showInformationMessageWithAction } from "../helpers"; import { extLogger } from "../common"; import { QueryHistoryManager } from "../query-history/query-history-manager"; @@ -37,6 +41,7 @@ import { Credentials } from "../common/authentication"; * Exports the results of the currently-selected variant analysis. */ export async function exportSelectedVariantAnalysisResults( + variantAnalysisManager: VariantAnalysisManager, queryHistoryManager: QueryHistoryManager, ): Promise { const queryHistoryItem = queryHistoryManager.getCurrentQueryHistoryItem(); @@ -46,8 +51,7 @@ export async function exportSelectedVariantAnalysisResults( ); } - return commands.executeCommand( - "codeQL.exportVariantAnalysisResults", + await variantAnalysisManager.exportResults( queryHistoryItem.variantAnalysis.id, ); } @@ -63,108 +67,117 @@ export async function exportVariantAnalysisResults( variantAnalysisId: number, filterSort: RepositoriesFilterSortStateWithIds | undefined, credentials: Credentials, - progress: ProgressCallback, - token: CancellationToken, ): Promise { - const variantAnalysis = await variantAnalysisManager.getVariantAnalysis( - variantAnalysisId, - ); - if (!variantAnalysis) { - void extLogger.log( - `Could not find variant analysis with id ${variantAnalysisId}`, - ); - throw new Error( - "There was an error when trying to retrieve variant analysis information", - ); - } - - if (token.isCancellationRequested) { - throw new UserCancellationException("Cancelled"); - } - - const repoStates = await variantAnalysisManager.getRepoStates( - variantAnalysisId, - ); - - void extLogger.log( - `Exporting variant analysis results for variant analysis with id ${variantAnalysis.id}`, - ); + await withProgress( + async (progress: ProgressCallback, token: CancellationToken) => { + const variantAnalysis = await variantAnalysisManager.getVariantAnalysis( + variantAnalysisId, + ); + if (!variantAnalysis) { + void extLogger.log( + `Could not find variant analysis with id ${variantAnalysisId}`, + ); + throw new Error( + "There was an error when trying to retrieve variant analysis information", + ); + } - progress({ - maxStep: MAX_VARIANT_ANALYSIS_EXPORT_PROGRESS_STEPS, - step: 0, - message: "Determining export format", - }); + if (token.isCancellationRequested) { + throw new UserCancellationException("Cancelled"); + } - const exportFormat = await determineExportFormat(); - if (!exportFormat) { - return; - } + const repoStates = await variantAnalysisManager.getRepoStates( + variantAnalysisId, + ); - if (token.isCancellationRequested) { - throw new UserCancellationException("Cancelled"); - } + void extLogger.log( + `Exporting variant analysis results for variant analysis with id ${variantAnalysis.id}`, + ); - const repositories = filterAndSortRepositoriesWithResults( - variantAnalysis.scannedRepos, - filterSort, - )?.filter( - (repo) => - repo.resultCount && - repoStates.find((r) => r.repositoryId === repo.repository.id) - ?.downloadStatus === - VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, - ); + progress({ + maxStep: MAX_VARIANT_ANALYSIS_EXPORT_PROGRESS_STEPS, + step: 0, + message: "Determining export format", + }); - async function* getAnalysesResults(): AsyncGenerator< - [VariantAnalysisScannedRepository, VariantAnalysisScannedRepositoryResult] - > { - if (!variantAnalysis) { - return; - } + const exportFormat = await determineExportFormat(); + if (!exportFormat) { + return; + } - if (!repositories) { - return; - } + if (token.isCancellationRequested) { + throw new UserCancellationException("Cancelled"); + } - for (const repo of repositories) { - const result = await variantAnalysisManager.loadResults( - variantAnalysis.id, - repo.repository.fullName, - { - skipCacheStore: true, - }, + const repositories = filterAndSortRepositoriesWithResults( + variantAnalysis.scannedRepos, + filterSort, + )?.filter( + (repo) => + repo.resultCount && + repoStates.find((r) => r.repositoryId === repo.repository.id) + ?.downloadStatus === + VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, ); - yield [repo, result]; - } - } - - const exportDirectory = - variantAnalysisManager.getVariantAnalysisStorageLocation( - variantAnalysis.id, - ); + async function* getAnalysesResults(): AsyncGenerator< + [ + VariantAnalysisScannedRepository, + VariantAnalysisScannedRepositoryResult, + ] + > { + if (!variantAnalysis) { + return; + } + + if (!repositories) { + return; + } + + for (const repo of repositories) { + const result = await variantAnalysisManager.loadResults( + variantAnalysis.id, + repo.repository.fullName, + { + skipCacheStore: true, + }, + ); + + yield [repo, result]; + } + } - // The date will be formatted like the following: 20221115T123456Z. The time is in UTC. - const formattedDate = new Date() - .toISOString() - .replace(/[-:]/g, "") - .replace(/\.\d+Z$/, "Z"); - const exportedResultsDirectory = join( - exportDirectory, - "exported-results", - `results_${formattedDate}`, - ); + const exportDirectory = + variantAnalysisManager.getVariantAnalysisStorageLocation( + variantAnalysis.id, + ); + + // The date will be formatted like the following: 20221115T123456Z. The time is in UTC. + const formattedDate = new Date() + .toISOString() + .replace(/[-:]/g, "") + .replace(/\.\d+Z$/, "Z"); + const exportedResultsDirectory = join( + exportDirectory, + "exported-results", + `results_${formattedDate}`, + ); - await exportVariantAnalysisAnalysisResults( - exportedResultsDirectory, - variantAnalysis, - getAnalysesResults(), - repositories?.length ?? 0, - exportFormat, - credentials, - progress, - token, + await exportVariantAnalysisAnalysisResults( + exportedResultsDirectory, + variantAnalysis, + getAnalysesResults(), + repositories?.length ?? 0, + exportFormat, + credentials, + progress, + token, + ); + }, + { + title: "Exporting variant analysis results", + cancellable: true, + }, ); } 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 23f43c567fc..9bd7f201d63 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -67,6 +67,7 @@ import { DbManager } from "../databases/db-manager"; import { App } from "../common/app"; import { redactableError } from "../pure/errors"; import { AppCommandManager, VariantAnalysisCommands } from "../common/commands"; +import { exportVariantAnalysisResults } from "./export-results"; export class VariantAnalysisManager extends DisposableObject @@ -690,6 +691,18 @@ export class VariantAnalysisManager await env.clipboard.writeText(text.join(EOL)); } + public async exportResults( + variantAnalysisId: number, + filterSort?: RepositoriesFilterSortStateWithIds, + ) { + await exportVariantAnalysisResults( + this, + variantAnalysisId, + filterSort, + this.app.credentials, + ); + } + private getRepoStatesStoragePath(variantAnalysisId: number): string { return join( this.getVariantAnalysisStorageLocation(variantAnalysisId), diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts index c47220455e2..4db7406833d 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts @@ -3,6 +3,7 @@ import { VariantAnalysisScannedRepositoryState, } from "./shared/variant-analysis"; import { AppCommandManager } from "../common/commands"; +import { RepositoriesFilterSortStateWithIds } from "../pure/variant-analysis-filter-sort"; export interface VariantAnalysisViewInterface { variantAnalysisId: number; @@ -27,4 +28,8 @@ export interface VariantAnalysisViewManager< openQueryFile(variantAnalysisId: number): Promise; openQueryText(variantAnalysisId: number): Promise; cancelVariantAnalysis(variantAnalysisId: number): Promise; + exportResults( + variantAnalysisId: number, + filterSort?: RepositoriesFilterSortStateWithIds, + ): Promise; } diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts index 0588a20c6e0..ffd07c1468c 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts @@ -138,8 +138,7 @@ export class VariantAnalysisView ); break; case "exportResults": - void commands.executeCommand( - "codeQL.exportVariantAnalysisResults", + await this.manager.exportResults( this.variantAnalysisId, msg.filterSort, ); diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx index 4e932accb23..83d47356348 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx @@ -139,6 +139,7 @@ export function VariantAnalysis({ repositoryIds: selectedRepositoryIds, }, }); + sendTelemetry("variant-analysis-export-results"); }, [filterSortState, selectedRepositoryIds]); if ( diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts index 60f0095aa70..a798fff459a 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts @@ -86,6 +86,7 @@ describe("QueryHistoryManager", () => { onVariantAnalysisRemoved: jest.fn(), removeVariantAnalysis: jest.fn(), cancelVariantAnalysis: jest.fn(), + exportResults: jest.fn(), showView: jest.fn(), } as any as VariantAnalysisManager; @@ -862,7 +863,7 @@ describe("QueryHistoryManager", () => { const item = localQueryHistory[4]; await queryHistoryManager.handleExportResults(item, [item]); - expect(executeCommandSpy).not.toBeCalled(); + expect(variantAnalysisManagerStub.exportResults).not.toBeCalled(); }); it("should export results for a single variant analysis", async () => { @@ -870,8 +871,7 @@ describe("QueryHistoryManager", () => { const item = variantAnalysisHistory[1]; await queryHistoryManager.handleExportResults(item, [item]); - expect(executeCommandSpy).toBeCalledWith( - "codeQL.exportVariantAnalysisResults", + expect(variantAnalysisManagerStub.exportResults).toBeCalledWith( item.variantAnalysis.id, ); }); @@ -882,7 +882,7 @@ describe("QueryHistoryManager", () => { const item1 = variantAnalysisHistory[1]; const item2 = variantAnalysisHistory[3]; await queryHistoryManager.handleExportResults(item1, [item1, item2]); - expect(executeCommandSpy).not.toBeCalled(); + expect(variantAnalysisManagerStub.exportResults).not.toBeCalled(); }); });