Merge pull request #1816 from github/koesie10/export-cancelled-results

Add exporting of in-progress/cancelled results
This commit is contained in:
Koen Vlaswinkel
2022-12-02 13:03:37 +01:00
committed by GitHub
7 changed files with 150 additions and 25 deletions

View File

@@ -2,12 +2,12 @@ import { join } from "path";
import { ensureDir, writeFile } from "fs-extra";
import {
window,
commands,
Uri,
ExtensionContext,
workspace,
Uri,
ViewColumn,
window,
workspace,
} from "vscode";
import { Credentials } from "../authentication";
import { UserCancellationException } from "../commandRunner";
@@ -30,6 +30,7 @@ import { assertNever } from "../pure/helpers-pure";
import {
VariantAnalysis,
VariantAnalysisScannedRepository,
VariantAnalysisScannedRepositoryDownloadStatus,
VariantAnalysisScannedRepositoryResult,
} from "./shared/variant-analysis";
import {
@@ -154,6 +155,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}`,
);
@@ -179,6 +184,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,

View File

@@ -43,6 +43,7 @@ const variantAnalysis: VariantAnalysisDomainModel = {
private: false,
},
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
resultCount: 100,
},
{
repository: {

View File

@@ -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({});

View File

@@ -144,6 +144,9 @@ export function VariantAnalysis({
<>
<VariantAnalysisHeader
variantAnalysis={variantAnalysis}
repositoryStates={repoStates}
filterSortState={filterSortState}
selectedRepositoryIds={selectedRepositoryIds}
onOpenQueryFileClick={openQueryFile}
onViewQueryTextClick={openQueryText}
onStopQueryClick={stopQuery}

View File

@@ -3,14 +3,17 @@ import styled from "styled-components";
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react";
import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis";
type Props = {
export type VariantAnalysisActionsProps = {
variantAnalysisStatus: VariantAnalysisStatus;
onStopQueryClick: () => void;
stopQueryDisabled?: boolean;
showResultActions?: boolean;
onCopyRepositoryListClick: () => void;
onExportResultsClick: () => void;
copyRepositoryListDisabled?: boolean;
exportResultsDisabled?: boolean;
};
const Container = styled.div`
@@ -26,12 +29,33 @@ const Button = styled(VSCodeButton)`
export const VariantAnalysisActions = ({
variantAnalysisStatus,
onStopQueryClick,
stopQueryDisabled,
showResultActions,
onCopyRepositoryListClick,
onExportResultsClick,
stopQueryDisabled,
}: Props) => {
copyRepositoryListDisabled,
exportResultsDisabled,
}: VariantAnalysisActionsProps) => {
return (
<Container>
{showResultActions && (
<>
<Button
appearance="secondary"
onClick={onCopyRepositoryListClick}
disabled={copyRepositoryListDisabled}
>
Copy repository list
</Button>
<Button
appearance="primary"
onClick={onExportResultsClick}
disabled={exportResultsDisabled}
>
Export results
</Button>
</>
)}
{variantAnalysisStatus === VariantAnalysisStatus.InProgress && (
<Button
appearance="secondary"
@@ -41,16 +65,6 @@ export const VariantAnalysisActions = ({
Stop query
</Button>
)}
{variantAnalysisStatus === VariantAnalysisStatus.Succeeded && (
<>
<Button appearance="secondary" onClick={onCopyRepositoryListClick}>
Copy repository list
</Button>
<Button appearance="primary" onClick={onExportResultsClick}>
Export results
</Button>
</>
)}
</Container>
);
};

View File

@@ -6,15 +6,25 @@ import {
getTotalResultCount,
hasRepoScanCompleted,
VariantAnalysis,
VariantAnalysisScannedRepositoryDownloadStatus,
VariantAnalysisScannedRepositoryState,
} from "../../remote-queries/shared/variant-analysis";
import { QueryDetails } from "./QueryDetails";
import { VariantAnalysisActions } from "./VariantAnalysisActions";
import { VariantAnalysisStats } from "./VariantAnalysisStats";
import { parseDate } from "../../pure/date";
import { basename } from "../common/path";
import {
defaultFilterSortState,
filterAndSortRepositoriesWithResults,
RepositoriesFilterSortState,
} from "../../pure/variant-analysis-filter-sort";
export type VariantAnalysisHeaderProps = {
variantAnalysis: VariantAnalysis;
repositoryStates?: VariantAnalysisScannedRepositoryState[];
filterSortState?: RepositoriesFilterSortState;
selectedRepositoryIds?: number[];
onOpenQueryFileClick: () => void;
onViewQueryTextClick: () => void;
@@ -40,6 +50,9 @@ const Row = styled.div`
export const VariantAnalysisHeader = ({
variantAnalysis,
repositoryStates,
filterSortState,
selectedRepositoryIds,
onOpenQueryFileClick,
onViewQueryTextClick,
onStopQueryClick,
@@ -62,6 +75,36 @@ export const VariantAnalysisHeader = ({
const hasSkippedRepos = useMemo(() => {
return getSkippedRepoCount(variantAnalysis.skippedRepos) > 0;
}, [variantAnalysis.skippedRepos]);
const filteredRepositories = useMemo(() => {
return filterAndSortRepositoriesWithResults(variantAnalysis.scannedRepos, {
...defaultFilterSortState,
...filterSortState,
repositoryIds: selectedRepositoryIds,
});
}, [filterSortState, selectedRepositoryIds, variantAnalysis.scannedRepos]);
const hasDownloadedRepos = useMemo(() => {
const repositoryStatesById = new Map<
number,
VariantAnalysisScannedRepositoryState
>();
if (repositoryStates) {
for (const repositoryState of repositoryStates) {
repositoryStatesById.set(repositoryState.repositoryId, repositoryState);
}
}
return filteredRepositories?.some((repo) => {
return (
repositoryStatesById.get(repo.repository.id)?.downloadStatus ===
VariantAnalysisScannedRepositoryDownloadStatus.Succeeded
);
});
}, [repositoryStates, filteredRepositories]);
const hasReposWithResults = useMemo(() => {
return filteredRepositories?.some(
(repo) => repo.resultCount && repo.resultCount > 0,
);
}, [filteredRepositories]);
return (
<Container>
@@ -74,10 +117,13 @@ export const VariantAnalysisHeader = ({
/>
<VariantAnalysisActions
variantAnalysisStatus={variantAnalysis.status}
showResultActions={(resultCount ?? 0) > 0}
onStopQueryClick={onStopQueryClick}
onCopyRepositoryListClick={onCopyRepositoryListClick}
onExportResultsClick={onExportResultsClick}
stopQueryDisabled={!variantAnalysis.actionsWorkflowRunId}
exportResultsDisabled={!hasDownloadedRepos}
copyRepositoryListDisabled={!hasReposWithResults}
/>
</Row>
<VariantAnalysisStats

View File

@@ -2,7 +2,10 @@ import * as React from "react";
import { render as reactRender, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { VariantAnalysisStatus } from "../../../remote-queries/shared/variant-analysis";
import { VariantAnalysisActions } from "../VariantAnalysisActions";
import {
VariantAnalysisActions,
VariantAnalysisActionsProps,
} from "../VariantAnalysisActions";
describe(VariantAnalysisActions.name, () => {
const onStopQueryClick = jest.fn();
@@ -15,51 +18,78 @@ describe(VariantAnalysisActions.name, () => {
onExportResultsClick.mockReset();
});
const render = (variantAnalysisStatus: VariantAnalysisStatus) =>
const render = (
props: Pick<VariantAnalysisActionsProps, "variantAnalysisStatus"> &
Partial<VariantAnalysisActionsProps>,
) =>
reactRender(
<VariantAnalysisActions
variantAnalysisStatus={variantAnalysisStatus}
onStopQueryClick={onStopQueryClick}
onCopyRepositoryListClick={onCopyRepositoryListClick}
onExportResultsClick={onExportResultsClick}
{...props}
/>,
);
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);
});