Skip to content

Commit 109ee04

Browse files
authored
Merge pull request #1816 from github/koesie10/export-cancelled-results
Add exporting of in-progress/cancelled results
2 parents 9c51974 + 0f7cf2d commit 109ee04

7 files changed

Lines changed: 150 additions & 25 deletions

File tree

extensions/ql-vscode/src/remote-queries/export-results.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import { join } from "path";
22
import { ensureDir, writeFile } from "fs-extra";
33

44
import {
5-
window,
65
commands,
7-
Uri,
86
ExtensionContext,
9-
workspace,
7+
Uri,
108
ViewColumn,
9+
window,
10+
workspace,
1111
} from "vscode";
1212
import { Credentials } from "../authentication";
1313
import { UserCancellationException } from "../commandRunner";
@@ -30,6 +30,7 @@ import { assertNever } from "../pure/helpers-pure";
3030
import {
3131
VariantAnalysis,
3232
VariantAnalysisScannedRepository,
33+
VariantAnalysisScannedRepositoryDownloadStatus,
3334
VariantAnalysisScannedRepositoryResult,
3435
} from "./shared/variant-analysis";
3536
import {
@@ -154,6 +155,10 @@ export async function exportVariantAnalysisResults(
154155
);
155156
}
156157

158+
const repoStates = await variantAnalysisManager.getRepoStates(
159+
variantAnalysisId,
160+
);
161+
157162
void extLogger.log(
158163
`Exporting variant analysis results for variant analysis with id ${variantAnalysis.id}`,
159164
);
@@ -179,6 +184,18 @@ export async function exportVariantAnalysisResults(
179184
}
180185

181186
for (const repo of repositories) {
187+
const repoState = repoStates.find(
188+
(r) => r.repositoryId === repo.repository.id,
189+
);
190+
191+
// Do not export if it has not yet completed or the download has not yet succeeded.
192+
if (
193+
repoState?.downloadStatus !==
194+
VariantAnalysisScannedRepositoryDownloadStatus.Succeeded
195+
) {
196+
continue;
197+
}
198+
182199
if (repo.resultCount == 0) {
183200
yield [
184201
repo,

extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysis.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const variantAnalysis: VariantAnalysisDomainModel = {
4343
private: false,
4444
},
4545
analysisStatus: VariantAnalysisRepoStatus.Succeeded,
46+
resultCount: 100,
4647
},
4748
{
4849
repository: {

extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,24 @@ InProgress.args = {
4747
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
4848
};
4949

50+
export const InProgressWithResults = Template.bind({});
51+
InProgressWithResults.args = {
52+
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
53+
showResultActions: true,
54+
};
55+
56+
export const InProgressWithoutDownloadedRepos = Template.bind({});
57+
InProgressWithoutDownloadedRepos.args = {
58+
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
59+
showResultActions: true,
60+
exportResultsDisabled: true,
61+
};
62+
5063
export const Succeeded = Template.bind({});
5164
Succeeded.args = {
5265
...InProgress.args,
5366
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
67+
showResultActions: true,
5468
};
5569

5670
export const Failed = Template.bind({});

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ export function VariantAnalysis({
144144
<>
145145
<VariantAnalysisHeader
146146
variantAnalysis={variantAnalysis}
147+
repositoryStates={repoStates}
148+
filterSortState={filterSortState}
149+
selectedRepositoryIds={selectedRepositoryIds}
147150
onOpenQueryFileClick={openQueryFile}
148151
onViewQueryTextClick={openQueryText}
149152
onStopQueryClick={stopQuery}

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisActions.tsx

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@ import styled from "styled-components";
33
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react";
44
import { VariantAnalysisStatus } from "../../remote-queries/shared/variant-analysis";
55

6-
type Props = {
6+
export type VariantAnalysisActionsProps = {
77
variantAnalysisStatus: VariantAnalysisStatus;
88

99
onStopQueryClick: () => void;
1010
stopQueryDisabled?: boolean;
1111

12+
showResultActions?: boolean;
1213
onCopyRepositoryListClick: () => void;
1314
onExportResultsClick: () => void;
15+
copyRepositoryListDisabled?: boolean;
16+
exportResultsDisabled?: boolean;
1417
};
1518

1619
const Container = styled.div`
@@ -26,12 +29,33 @@ const Button = styled(VSCodeButton)`
2629
export const VariantAnalysisActions = ({
2730
variantAnalysisStatus,
2831
onStopQueryClick,
32+
stopQueryDisabled,
33+
showResultActions,
2934
onCopyRepositoryListClick,
3035
onExportResultsClick,
31-
stopQueryDisabled,
32-
}: Props) => {
36+
copyRepositoryListDisabled,
37+
exportResultsDisabled,
38+
}: VariantAnalysisActionsProps) => {
3339
return (
3440
<Container>
41+
{showResultActions && (
42+
<>
43+
<Button
44+
appearance="secondary"
45+
onClick={onCopyRepositoryListClick}
46+
disabled={copyRepositoryListDisabled}
47+
>
48+
Copy repository list
49+
</Button>
50+
<Button
51+
appearance="primary"
52+
onClick={onExportResultsClick}
53+
disabled={exportResultsDisabled}
54+
>
55+
Export results
56+
</Button>
57+
</>
58+
)}
3559
{variantAnalysisStatus === VariantAnalysisStatus.InProgress && (
3660
<Button
3761
appearance="secondary"
@@ -41,16 +65,6 @@ export const VariantAnalysisActions = ({
4165
Stop query
4266
</Button>
4367
)}
44-
{variantAnalysisStatus === VariantAnalysisStatus.Succeeded && (
45-
<>
46-
<Button appearance="secondary" onClick={onCopyRepositoryListClick}>
47-
Copy repository list
48-
</Button>
49-
<Button appearance="primary" onClick={onExportResultsClick}>
50-
Export results
51-
</Button>
52-
</>
53-
)}
5468
</Container>
5569
);
5670
};

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,25 @@ import {
66
getTotalResultCount,
77
hasRepoScanCompleted,
88
VariantAnalysis,
9+
VariantAnalysisScannedRepositoryDownloadStatus,
10+
VariantAnalysisScannedRepositoryState,
911
} from "../../remote-queries/shared/variant-analysis";
1012
import { QueryDetails } from "./QueryDetails";
1113
import { VariantAnalysisActions } from "./VariantAnalysisActions";
1214
import { VariantAnalysisStats } from "./VariantAnalysisStats";
1315
import { parseDate } from "../../pure/date";
1416
import { basename } from "../common/path";
17+
import {
18+
defaultFilterSortState,
19+
filterAndSortRepositoriesWithResults,
20+
RepositoriesFilterSortState,
21+
} from "../../pure/variant-analysis-filter-sort";
1522

1623
export type VariantAnalysisHeaderProps = {
1724
variantAnalysis: VariantAnalysis;
25+
repositoryStates?: VariantAnalysisScannedRepositoryState[];
26+
filterSortState?: RepositoriesFilterSortState;
27+
selectedRepositoryIds?: number[];
1828

1929
onOpenQueryFileClick: () => void;
2030
onViewQueryTextClick: () => void;
@@ -40,6 +50,9 @@ const Row = styled.div`
4050

4151
export const VariantAnalysisHeader = ({
4252
variantAnalysis,
53+
repositoryStates,
54+
filterSortState,
55+
selectedRepositoryIds,
4356
onOpenQueryFileClick,
4457
onViewQueryTextClick,
4558
onStopQueryClick,
@@ -62,6 +75,36 @@ export const VariantAnalysisHeader = ({
6275
const hasSkippedRepos = useMemo(() => {
6376
return getSkippedRepoCount(variantAnalysis.skippedRepos) > 0;
6477
}, [variantAnalysis.skippedRepos]);
78+
const filteredRepositories = useMemo(() => {
79+
return filterAndSortRepositoriesWithResults(variantAnalysis.scannedRepos, {
80+
...defaultFilterSortState,
81+
...filterSortState,
82+
repositoryIds: selectedRepositoryIds,
83+
});
84+
}, [filterSortState, selectedRepositoryIds, variantAnalysis.scannedRepos]);
85+
const hasDownloadedRepos = useMemo(() => {
86+
const repositoryStatesById = new Map<
87+
number,
88+
VariantAnalysisScannedRepositoryState
89+
>();
90+
if (repositoryStates) {
91+
for (const repositoryState of repositoryStates) {
92+
repositoryStatesById.set(repositoryState.repositoryId, repositoryState);
93+
}
94+
}
95+
96+
return filteredRepositories?.some((repo) => {
97+
return (
98+
repositoryStatesById.get(repo.repository.id)?.downloadStatus ===
99+
VariantAnalysisScannedRepositoryDownloadStatus.Succeeded
100+
);
101+
});
102+
}, [repositoryStates, filteredRepositories]);
103+
const hasReposWithResults = useMemo(() => {
104+
return filteredRepositories?.some(
105+
(repo) => repo.resultCount && repo.resultCount > 0,
106+
);
107+
}, [filteredRepositories]);
65108

66109
return (
67110
<Container>
@@ -74,10 +117,13 @@ export const VariantAnalysisHeader = ({
74117
/>
75118
<VariantAnalysisActions
76119
variantAnalysisStatus={variantAnalysis.status}
120+
showResultActions={(resultCount ?? 0) > 0}
77121
onStopQueryClick={onStopQueryClick}
78122
onCopyRepositoryListClick={onCopyRepositoryListClick}
79123
onExportResultsClick={onExportResultsClick}
80124
stopQueryDisabled={!variantAnalysis.actionsWorkflowRunId}
125+
exportResultsDisabled={!hasDownloadedRepos}
126+
copyRepositoryListDisabled={!hasReposWithResults}
81127
/>
82128
</Row>
83129
<VariantAnalysisStats

extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import * as React from "react";
22
import { render as reactRender, screen } from "@testing-library/react";
33
import userEvent from "@testing-library/user-event";
44
import { VariantAnalysisStatus } from "../../../remote-queries/shared/variant-analysis";
5-
import { VariantAnalysisActions } from "../VariantAnalysisActions";
5+
import {
6+
VariantAnalysisActions,
7+
VariantAnalysisActionsProps,
8+
} from "../VariantAnalysisActions";
69

710
describe(VariantAnalysisActions.name, () => {
811
const onStopQueryClick = jest.fn();
@@ -15,51 +18,78 @@ describe(VariantAnalysisActions.name, () => {
1518
onExportResultsClick.mockReset();
1619
});
1720

18-
const render = (variantAnalysisStatus: VariantAnalysisStatus) =>
21+
const render = (
22+
props: Pick<VariantAnalysisActionsProps, "variantAnalysisStatus"> &
23+
Partial<VariantAnalysisActionsProps>,
24+
) =>
1925
reactRender(
2026
<VariantAnalysisActions
21-
variantAnalysisStatus={variantAnalysisStatus}
2227
onStopQueryClick={onStopQueryClick}
2328
onCopyRepositoryListClick={onCopyRepositoryListClick}
2429
onExportResultsClick={onExportResultsClick}
30+
{...props}
2531
/>,
2632
);
2733

2834
it("renders 1 button when in progress", async () => {
29-
const { container } = render(VariantAnalysisStatus.InProgress);
35+
const { container } = render({
36+
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
37+
});
3038

3139
expect(container.querySelectorAll("vscode-button").length).toEqual(1);
3240
});
3341

3442
it("renders the stop query button when in progress", async () => {
35-
render(VariantAnalysisStatus.InProgress);
43+
render({
44+
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
45+
});
3646

3747
await userEvent.click(screen.getByText("Stop query"));
3848
expect(onStopQueryClick).toHaveBeenCalledTimes(1);
3949
});
4050

51+
it("renders 3 buttons when in progress with results", async () => {
52+
const { container } = render({
53+
variantAnalysisStatus: VariantAnalysisStatus.InProgress,
54+
showResultActions: true,
55+
});
56+
57+
expect(container.querySelectorAll("vscode-button").length).toEqual(3);
58+
});
59+
4160
it("renders 2 buttons when succeeded", async () => {
42-
const { container } = render(VariantAnalysisStatus.Succeeded);
61+
const { container } = render({
62+
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
63+
showResultActions: true,
64+
});
4365

4466
expect(container.querySelectorAll("vscode-button").length).toEqual(2);
4567
});
4668

4769
it("renders the copy repository list button when succeeded", async () => {
48-
render(VariantAnalysisStatus.Succeeded);
70+
render({
71+
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
72+
showResultActions: true,
73+
});
4974

5075
await userEvent.click(screen.getByText("Copy repository list"));
5176
expect(onCopyRepositoryListClick).toHaveBeenCalledTimes(1);
5277
});
5378

5479
it("renders the export results button when succeeded", async () => {
55-
render(VariantAnalysisStatus.Succeeded);
80+
render({
81+
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
82+
showResultActions: true,
83+
});
5684

5785
await userEvent.click(screen.getByText("Export results"));
5886
expect(onExportResultsClick).toHaveBeenCalledTimes(1);
5987
});
6088

6189
it("does not render any buttons when failed", () => {
62-
const { container } = render(VariantAnalysisStatus.Failed);
90+
const { container } = render({
91+
variantAnalysisStatus: VariantAnalysisStatus.Failed,
92+
});
6393

6494
expect(container.querySelectorAll("vscode-button").length).toEqual(0);
6595
});

0 commit comments

Comments
 (0)