Merge pull request #1820 from github/koesie10/export-progress

Add progress notification to exporting results
This commit is contained in:
Koen Vlaswinkel
2022-12-02 14:55:28 +01:00
committed by GitHub
2 changed files with 102 additions and 14 deletions

View File

@@ -1235,9 +1235,11 @@ async function activateWithInstalledDistribution(
); );
ctx.subscriptions.push( ctx.subscriptions.push(
commandRunner( commandRunnerWithProgress(
"codeQL.exportVariantAnalysisResults", "codeQL.exportVariantAnalysisResults",
async ( async (
progress: ProgressCallback,
token: CancellationToken,
variantAnalysisId: number, variantAnalysisId: number,
filterSort?: RepositoriesFilterSortStateWithIds, filterSort?: RepositoriesFilterSortStateWithIds,
) => { ) => {
@@ -1246,8 +1248,14 @@ async function activateWithInstalledDistribution(
variantAnalysisManager, variantAnalysisManager,
variantAnalysisId, variantAnalysisId,
filterSort, filterSort,
progress,
token,
); );
}, },
{
title: "Exporting variant analysis results",
cancellable: true,
},
), ),
); );

View File

@@ -3,6 +3,7 @@ import { ensureDir, writeFile } from "fs-extra";
import { import {
commands, commands,
CancellationToken,
ExtensionContext, ExtensionContext,
Uri, Uri,
ViewColumn, ViewColumn,
@@ -10,7 +11,7 @@ import {
workspace, workspace,
} from "vscode"; } from "vscode";
import { Credentials } from "../authentication"; import { Credentials } from "../authentication";
import { UserCancellationException } from "../commandRunner"; import { ProgressCallback, UserCancellationException } from "../commandRunner";
import { showInformationMessageWithAction } from "../helpers"; import { showInformationMessageWithAction } from "../helpers";
import { extLogger } from "../common"; import { extLogger } from "../common";
import { QueryHistoryManager } from "../query-history"; import { QueryHistoryManager } from "../query-history";
@@ -133,6 +134,8 @@ export async function exportRemoteQueryAnalysisResults(
); );
} }
const MAX_VARIANT_ANALYSIS_EXPORT_PROGRESS_STEPS = 2;
/** /**
* Exports the results of the given or currently-selected remote query. * Exports the results of the given or currently-selected remote query.
* The user is prompted to select the export format. * The user is prompted to select the export format.
@@ -141,7 +144,9 @@ export async function exportVariantAnalysisResults(
ctx: ExtensionContext, ctx: ExtensionContext,
variantAnalysisManager: VariantAnalysisManager, variantAnalysisManager: VariantAnalysisManager,
variantAnalysisId: number, variantAnalysisId: number,
filterSort?: RepositoriesFilterSortStateWithIds, filterSort: RepositoriesFilterSortStateWithIds | undefined,
progress: ProgressCallback,
token: CancellationToken,
): Promise<void> { ): Promise<void> {
const variantAnalysis = await variantAnalysisManager.getVariantAnalysis( const variantAnalysis = await variantAnalysisManager.getVariantAnalysis(
variantAnalysisId, variantAnalysisId,
@@ -155,6 +160,10 @@ export async function exportVariantAnalysisResults(
); );
} }
if (token.isCancellationRequested) {
throw new UserCancellationException("Cancelled");
}
const repoStates = await variantAnalysisManager.getRepoStates( const repoStates = await variantAnalysisManager.getRepoStates(
variantAnalysisId, variantAnalysisId,
); );
@@ -163,11 +172,21 @@ export async function exportVariantAnalysisResults(
`Exporting variant analysis results for variant analysis with id ${variantAnalysis.id}`, `Exporting variant analysis results for variant analysis with id ${variantAnalysis.id}`,
); );
progress({
maxStep: MAX_VARIANT_ANALYSIS_EXPORT_PROGRESS_STEPS,
step: 0,
message: "Determining export format",
});
const exportFormat = await determineExportFormat(); const exportFormat = await determineExportFormat();
if (!exportFormat) { if (!exportFormat) {
return; return;
} }
if (token.isCancellationRequested) {
throw new UserCancellationException("Cancelled");
}
async function* getAnalysesResults(): AsyncGenerator< async function* getAnalysesResults(): AsyncGenerator<
[VariantAnalysisScannedRepository, VariantAnalysisScannedRepositoryResult] [VariantAnalysisScannedRepository, VariantAnalysisScannedRepositoryResult]
> { > {
@@ -241,6 +260,8 @@ export async function exportVariantAnalysisResults(
variantAnalysis, variantAnalysis,
getAnalysesResults(), getAnalysesResults(),
exportFormat, exportFormat,
progress,
token,
); );
} }
@@ -252,7 +273,19 @@ export async function exportVariantAnalysisAnalysisResults(
[VariantAnalysisScannedRepository, VariantAnalysisScannedRepositoryResult] [VariantAnalysisScannedRepository, VariantAnalysisScannedRepositoryResult]
>, >,
exportFormat: "gist" | "local", exportFormat: "gist" | "local",
progress: ProgressCallback,
token: CancellationToken,
) { ) {
if (token.isCancellationRequested) {
throw new UserCancellationException("Cancelled");
}
progress({
maxStep: MAX_VARIANT_ANALYSIS_EXPORT_PROGRESS_STEPS,
step: 1,
message: "Generating Markdown files",
});
const { markdownFiles, summaries } = await generateVariantAnalysisMarkdown( const { markdownFiles, summaries } = await generateVariantAnalysisMarkdown(
variantAnalysis, variantAnalysis,
analysesResults, analysesResults,
@@ -269,6 +302,8 @@ export async function exportVariantAnalysisAnalysisResults(
description, description,
markdownFiles, markdownFiles,
exportFormat, exportFormat,
progress,
token,
); );
} }
@@ -311,11 +346,22 @@ export async function exportResults(
description: string, description: string,
markdownFiles: MarkdownFile[], markdownFiles: MarkdownFile[],
exportFormat: "gist" | "local", exportFormat: "gist" | "local",
progress?: ProgressCallback,
token?: CancellationToken,
) { ) {
if (token?.isCancellationRequested) {
throw new UserCancellationException("Cancelled");
}
if (exportFormat === "gist") { if (exportFormat === "gist") {
await exportToGist(ctx, description, markdownFiles); await exportToGist(ctx, description, markdownFiles, progress, token);
} else if (exportFormat === "local") { } else if (exportFormat === "local") {
await exportToLocalMarkdown(exportedResultsPath, markdownFiles); await exportToLocalMarkdown(
exportedResultsPath,
markdownFiles,
progress,
token,
);
} }
} }
@@ -323,9 +369,21 @@ export async function exportToGist(
ctx: ExtensionContext, ctx: ExtensionContext,
description: string, description: string,
markdownFiles: MarkdownFile[], markdownFiles: MarkdownFile[],
progress?: ProgressCallback,
token?: CancellationToken,
) { ) {
progress?.({
maxStep: MAX_VARIANT_ANALYSIS_EXPORT_PROGRESS_STEPS,
step: 2,
message: "Creating Gist",
});
const credentials = await Credentials.initialize(ctx); const credentials = await Credentials.initialize(ctx);
if (token?.isCancellationRequested) {
throw new UserCancellationException("Cancelled");
}
// Convert markdownFiles to the appropriate format for uploading to gist // Convert markdownFiles to the appropriate format for uploading to gist
const gistFiles = markdownFiles.reduce((acc, cur) => { const gistFiles = markdownFiles.reduce((acc, cur) => {
acc[`${cur.fileName}.md`] = { content: cur.content.join("\n") }; acc[`${cur.fileName}.md`] = { content: cur.content.join("\n") };
@@ -334,13 +392,17 @@ export async function exportToGist(
const gistUrl = await createGist(credentials, description, gistFiles); const gistUrl = await createGist(credentials, description, gistFiles);
if (gistUrl) { if (gistUrl) {
const shouldOpenGist = await showInformationMessageWithAction( // This needs to use .then to ensure we aren't keeping the progress notification open. We shouldn't await the
// "Open gist" button click.
void showInformationMessageWithAction(
"Variant analysis results exported to gist.", "Variant analysis results exported to gist.",
"Open gist", "Open gist",
); ).then((shouldOpenGist) => {
if (shouldOpenGist) { if (!shouldOpenGist) {
await commands.executeCommand("vscode.open", Uri.parse(gistUrl)); return;
} }
return commands.executeCommand("vscode.open", Uri.parse(gistUrl));
});
} }
} }
@@ -386,20 +448,38 @@ const buildVariantAnalysisGistDescription = (
async function exportToLocalMarkdown( async function exportToLocalMarkdown(
exportedResultsPath: string, exportedResultsPath: string,
markdownFiles: MarkdownFile[], markdownFiles: MarkdownFile[],
progress?: ProgressCallback,
token?: CancellationToken,
) { ) {
if (token?.isCancellationRequested) {
throw new UserCancellationException("Cancelled");
}
progress?.({
maxStep: MAX_VARIANT_ANALYSIS_EXPORT_PROGRESS_STEPS,
step: 2,
message: "Creating local Markdown files",
});
await ensureDir(exportedResultsPath); await ensureDir(exportedResultsPath);
for (const markdownFile of markdownFiles) { for (const markdownFile of markdownFiles) {
const filePath = join(exportedResultsPath, `${markdownFile.fileName}.md`); const filePath = join(exportedResultsPath, `${markdownFile.fileName}.md`);
await writeFile(filePath, markdownFile.content.join("\n"), "utf8"); await writeFile(filePath, markdownFile.content.join("\n"), "utf8");
} }
const shouldOpenExportedResults = await showInformationMessageWithAction(
// This needs to use .then to ensure we aren't keeping the progress notification open. We shouldn't await the
// "Open exported results" button click.
void showInformationMessageWithAction(
`Variant analysis results exported to \"${exportedResultsPath}\".`, `Variant analysis results exported to \"${exportedResultsPath}\".`,
"Open exported results", "Open exported results",
); ).then(async (shouldOpenExportedResults) => {
if (shouldOpenExportedResults) { if (!shouldOpenExportedResults) {
return;
}
const summaryFilePath = join(exportedResultsPath, "_summary.md"); const summaryFilePath = join(exportedResultsPath, "_summary.md");
const summaryFile = await workspace.openTextDocument(summaryFilePath); const summaryFile = await workspace.openTextDocument(summaryFilePath);
await window.showTextDocument(summaryFile, ViewColumn.One); await window.showTextDocument(summaryFile, ViewColumn.One);
await commands.executeCommand("revealFileInOS", Uri.file(summaryFilePath)); await commands.executeCommand("revealFileInOS", Uri.file(summaryFilePath));
} });
} }