From 72f253e39f5a5adf974e31312e977365de987275 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 1 Dec 2022 11:08:32 +0100 Subject: [PATCH 1/7] Add exporting of in-progress/cancelled results This will allow exporting results for a variant analysis which is cancelled or in-progress. Repositories for which the results are not yet available or which have not yet been downloaded will not be exported. The header of the summary file is incorrect, but this will be fixed in a follow-up PR. --- .../src/remote-queries/export-results.ts | 23 +++++++-- .../VariantAnalysis.stories.tsx | 1 + .../VariantAnalysisActions.stories.tsx | 14 ++++++ .../view/variant-analysis/VariantAnalysis.tsx | 1 + .../VariantAnalysisActions.tsx | 34 ++++++++----- .../VariantAnalysisHeader.tsx | 15 ++++++ .../__tests__/VariantAnalysisActions.spec.tsx | 48 +++++++++++++++---- 7 files changed, 111 insertions(+), 25 deletions(-) diff --git a/extensions/ql-vscode/src/remote-queries/export-results.ts b/extensions/ql-vscode/src/remote-queries/export-results.ts index 33c5de803..646ea0740 100644 --- a/extensions/ql-vscode/src/remote-queries/export-results.ts +++ b/extensions/ql-vscode/src/remote-queries/export-results.ts @@ -2,12 +2,12 @@ import * as path from "path"; import * as fs from "fs-extra"; import { - window, commands, - Uri, ExtensionContext, - workspace, + Uri, ViewColumn, + window, + workspace, } from "vscode"; import { Credentials } from "../authentication"; import { UserCancellationException } from "../commandRunner"; @@ -29,6 +29,7 @@ import { assertNever } from "../pure/helpers-pure"; import { VariantAnalysis, VariantAnalysisScannedRepository, + VariantAnalysisScannedRepositoryDownloadStatus, VariantAnalysisScannedRepositoryResult, } from "./shared/variant-analysis"; import { @@ -156,6 +157,10 @@ export async function exportVariantAnalysisResults( ); } + const repoStates = await variantAnalysisManager.getRepoStates( + variantAnalysisId, + ); + void extLogger.log( `Exporting variant analysis results for variant analysis with id ${variantAnalysis.id}`, ); @@ -181,6 +186,18 @@ export async function exportVariantAnalysisResults( } for (const repo of repositories) { + const repoState = repoStates.find( + (r) => r.repositoryId === repo.repository.id, + ); + + // Do not export if it has not yet completed or the download has not yet succeeded. + if ( + repoState?.downloadStatus !== + VariantAnalysisScannedRepositoryDownloadStatus.Succeeded + ) { + continue; + } + if (repo.resultCount == 0) { yield [ repo, diff --git a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysis.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysis.stories.tsx index 505c9553e..a28b122d7 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysis.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysis.stories.tsx @@ -43,6 +43,7 @@ const variantAnalysis: VariantAnalysisDomainModel = { private: false, }, analysisStatus: VariantAnalysisRepoStatus.Succeeded, + resultCount: 100, }, { repository: { diff --git a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx index 2def22117..3b966a304 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx @@ -47,10 +47,24 @@ InProgress.args = { variantAnalysisStatus: VariantAnalysisStatus.InProgress, }; +export const InProgressWithResults = Template.bind({}); +InProgressWithResults.args = { + variantAnalysisStatus: VariantAnalysisStatus.InProgress, + showResultActions: true, +}; + +export const InProgressWithoutDownloadedRepos = Template.bind({}); +InProgressWithoutDownloadedRepos.args = { + variantAnalysisStatus: VariantAnalysisStatus.InProgress, + showResultActions: true, + exportResultsDisabled: true, +}; + export const Succeeded = Template.bind({}); Succeeded.args = { ...InProgress.args, variantAnalysisStatus: VariantAnalysisStatus.Succeeded, + showResultActions: true, }; export const Failed = Template.bind({}); diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx index 82f74c734..f4d553e5e 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx @@ -144,6 +144,7 @@ export function VariantAnalysis({ <> void; stopQueryDisabled?: boolean; + showResultActions?: boolean; onCopyRepositoryListClick: () => void; onExportResultsClick: () => void; + exportResultsDisabled?: boolean; }; const Container = styled.div` @@ -26,12 +28,28 @@ const Button = styled(VSCodeButton)` export const VariantAnalysisActions = ({ variantAnalysisStatus, onStopQueryClick, + stopQueryDisabled, + showResultActions, onCopyRepositoryListClick, onExportResultsClick, - stopQueryDisabled, -}: Props) => { + exportResultsDisabled, +}: VariantAnalysisActionsProps) => { return ( + {showResultActions && ( + <> + + + + )} {variantAnalysisStatus === VariantAnalysisStatus.InProgress && ( - - - )} ); }; diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx index ed0d5b8d4..afd6744d4 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx @@ -6,6 +6,8 @@ import { getTotalResultCount, hasRepoScanCompleted, VariantAnalysis, + VariantAnalysisScannedRepositoryDownloadStatus, + VariantAnalysisScannedRepositoryState, } from "../../remote-queries/shared/variant-analysis"; import { QueryDetails } from "./QueryDetails"; import { VariantAnalysisActions } from "./VariantAnalysisActions"; @@ -15,6 +17,7 @@ import { basename } from "../common/path"; export type VariantAnalysisHeaderProps = { variantAnalysis: VariantAnalysis; + repositoryStates?: VariantAnalysisScannedRepositoryState[]; onOpenQueryFileClick: () => void; onViewQueryTextClick: () => void; @@ -40,6 +43,7 @@ const Row = styled.div` export const VariantAnalysisHeader = ({ variantAnalysis, + repositoryStates, onOpenQueryFileClick, onViewQueryTextClick, onStopQueryClick, @@ -62,6 +66,15 @@ export const VariantAnalysisHeader = ({ const hasSkippedRepos = useMemo(() => { return getSkippedRepoCount(variantAnalysis.skippedRepos) > 0; }, [variantAnalysis.skippedRepos]); + const hasDownloadedRepos = useMemo(() => { + return ( + repositoryStates?.some( + (repo) => + repo.downloadStatus === + VariantAnalysisScannedRepositoryDownloadStatus.Succeeded, + ) ?? false + ); + }, [repositoryStates]); return ( @@ -74,10 +87,12 @@ export const VariantAnalysisHeader = ({ /> 0} onStopQueryClick={onStopQueryClick} onCopyRepositoryListClick={onCopyRepositoryListClick} onExportResultsClick={onExportResultsClick} stopQueryDisabled={!variantAnalysis.actionsWorkflowRunId} + exportResultsDisabled={!hasDownloadedRepos} /> { const onStopQueryClick = jest.fn(); @@ -15,51 +18,78 @@ describe(VariantAnalysisActions.name, () => { onExportResultsClick.mockReset(); }); - const render = (variantAnalysisStatus: VariantAnalysisStatus) => + const render = ( + props: Pick & + Partial, + ) => reactRender( , ); it("renders 1 button when in progress", async () => { - const { container } = render(VariantAnalysisStatus.InProgress); + const { container } = render({ + variantAnalysisStatus: VariantAnalysisStatus.InProgress, + }); expect(container.querySelectorAll("vscode-button").length).toEqual(1); }); it("renders the stop query button when in progress", async () => { - render(VariantAnalysisStatus.InProgress); + render({ + variantAnalysisStatus: VariantAnalysisStatus.InProgress, + }); await userEvent.click(screen.getByText("Stop query")); expect(onStopQueryClick).toHaveBeenCalledTimes(1); }); + it("renders 3 buttons when in progress with results", async () => { + const { container } = render({ + variantAnalysisStatus: VariantAnalysisStatus.InProgress, + showResultActions: true, + }); + + expect(container.querySelectorAll("vscode-button").length).toEqual(3); + }); + it("renders 2 buttons when succeeded", async () => { - const { container } = render(VariantAnalysisStatus.Succeeded); + const { container } = render({ + variantAnalysisStatus: VariantAnalysisStatus.Succeeded, + showResultActions: true, + }); expect(container.querySelectorAll("vscode-button").length).toEqual(2); }); it("renders the copy repository list button when succeeded", async () => { - render(VariantAnalysisStatus.Succeeded); + render({ + variantAnalysisStatus: VariantAnalysisStatus.Succeeded, + showResultActions: true, + }); await userEvent.click(screen.getByText("Copy repository list")); expect(onCopyRepositoryListClick).toHaveBeenCalledTimes(1); }); it("renders the export results button when succeeded", async () => { - render(VariantAnalysisStatus.Succeeded); + render({ + variantAnalysisStatus: VariantAnalysisStatus.Succeeded, + showResultActions: true, + }); await userEvent.click(screen.getByText("Export results")); expect(onExportResultsClick).toHaveBeenCalledTimes(1); }); it("does not render any buttons when failed", () => { - const { container } = render(VariantAnalysisStatus.Failed); + const { container } = render({ + variantAnalysisStatus: VariantAnalysisStatus.Failed, + }); expect(container.querySelectorAll("vscode-button").length).toEqual(0); }); From 0f7cf2d935f16c30c29238b6937793dd8d84e4eb Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 1 Dec 2022 11:19:43 +0100 Subject: [PATCH 2/7] Take into account filters for action buttons This will disable the export and copy buttons when no results would be exported by executing the command. In contrast to the "normal" filtering in the view, this will also take into account the checkboxes since those are also used in the extension host. --- .../view/variant-analysis/VariantAnalysis.tsx | 2 + .../VariantAnalysisActions.tsx | 8 +++- .../VariantAnalysisHeader.tsx | 45 ++++++++++++++++--- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx index f4d553e5e..023f2d80a 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx @@ -145,6 +145,8 @@ export function VariantAnalysis({ void; onExportResultsClick: () => void; + copyRepositoryListDisabled?: boolean; exportResultsDisabled?: boolean; }; @@ -32,13 +33,18 @@ export const VariantAnalysisActions = ({ showResultActions, onCopyRepositoryListClick, onExportResultsClick, + copyRepositoryListDisabled, exportResultsDisabled, }: VariantAnalysisActionsProps) => { return ( {showResultActions && ( <> -