Skip to content

Commit 93054e1

Browse files
authored
Merge pull request #1683 from github/koesie10/fix-duplicate-downloads
Fix duplicate variant analysis results downloads
2 parents b062f61 + a661daa commit 93054e1

File tree

7 files changed

+347
-5
lines changed

7 files changed

+347
-5
lines changed

extensions/ql-vscode/src/remote-queries/variant-analysis-manager.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import { createTimestampFile, showAndLogErrorMessage } from '../helpers';
2626
import * as fs from 'fs-extra';
2727

2828
export class VariantAnalysisManager extends DisposableObject implements VariantAnalysisViewManager<VariantAnalysisView> {
29+
private static readonly REPO_STATES_FILENAME = 'repo_states.json';
30+
2931
private readonly _onVariantAnalysisAdded = this.push(new EventEmitter<VariantAnalysis>());
3032
public readonly onVariantAnalysisAdded = this._onVariantAnalysisAdded.event;
3133
private readonly _onVariantAnalysisStatusUpdated = this.push(new EventEmitter<VariantAnalysis>());
@@ -40,6 +42,8 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
4042
private static readonly maxConcurrentDownloads = 3;
4143
private readonly queue = new PQueue({ concurrency: VariantAnalysisManager.maxConcurrentDownloads });
4244

45+
private readonly repoStates = new Map<number, Record<number, VariantAnalysisScannedRepositoryState>>();
46+
4347
constructor(
4448
private readonly ctx: ExtensionContext,
4549
private readonly storagePath: string,
@@ -60,6 +64,15 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
6064
this._onVariantAnalysisRemoved.fire(variantAnalysis);
6165
} else {
6266
await this.setVariantAnalysis(variantAnalysis);
67+
68+
try {
69+
const repoStates = await fs.readJson(this.getRepoStatesStoragePath(variantAnalysis.id));
70+
this.repoStates.set(variantAnalysis.id, repoStates);
71+
} catch (e) {
72+
// Ignore this error, we simply might not have downloaded anything yet
73+
this.repoStates.set(variantAnalysis.id, {});
74+
}
75+
6376
if (!await isVariantAnalysisComplete(variantAnalysis, this.makeResultDownloadChecker(variantAnalysis))) {
6477
void commands.executeCommand('codeQL.monitorVariantAnalysis', variantAnalysis);
6578
}
@@ -120,6 +133,10 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
120133
return this.variantAnalyses.get(variantAnalysisId);
121134
}
122135

136+
public async getRepoStates(variantAnalysisId: number): Promise<VariantAnalysisScannedRepositoryState[]> {
137+
return Object.values(this.repoStates.get(variantAnalysisId) ?? {});
138+
}
139+
123140
public get variantAnalysesSize(): number {
124141
return this.variantAnalyses.size;
125142
}
@@ -152,6 +169,8 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
152169

153170
await this.prepareStorageDirectory(variantAnalysis.id);
154171

172+
this.repoStates.set(variantAnalysis.id, {});
173+
155174
this._onVariantAnalysisAdded.fire(variantAnalysis);
156175
}
157176

@@ -166,6 +185,14 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
166185

167186
private async onRepoStateUpdated(variantAnalysisId: number, repoState: VariantAnalysisScannedRepositoryState): Promise<void> {
168187
await this.getView(variantAnalysisId)?.updateRepoState(repoState);
188+
189+
let repoStates = this.repoStates.get(variantAnalysisId);
190+
if (!repoStates) {
191+
repoStates = {};
192+
this.repoStates.set(variantAnalysisId, repoStates);
193+
}
194+
195+
repoStates[repoState.repositoryId] = repoState;
169196
}
170197

171198
public async monitorVariantAnalysis(
@@ -180,6 +207,10 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
180207
variantAnalysis: VariantAnalysis,
181208
cancellationToken: CancellationToken
182209
): Promise<void> {
210+
if (this.repoStates.get(variantAnalysis.id)?.[scannedRepo.repository.id]?.downloadStatus === VariantAnalysisScannedRepositoryDownloadStatus.Succeeded) {
211+
return;
212+
}
213+
183214
const repoState = {
184215
repositoryId: scannedRepo.repository.id,
185216
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.Pending,
@@ -216,11 +247,19 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
216247
repoState.downloadStatus = VariantAnalysisScannedRepositoryDownloadStatus.InProgress;
217248
await this.onRepoStateUpdated(variantAnalysis.id, repoState);
218249

219-
await this.variantAnalysisResultsManager.download(credentials, variantAnalysis.id, repoTask, this.getVariantAnalysisStorageLocation(variantAnalysis.id));
250+
try {
251+
await this.variantAnalysisResultsManager.download(credentials, variantAnalysis.id, repoTask, this.getVariantAnalysisStorageLocation(variantAnalysis.id));
252+
} catch (e) {
253+
repoState.downloadStatus = VariantAnalysisScannedRepositoryDownloadStatus.Failed;
254+
await this.onRepoStateUpdated(variantAnalysis.id, repoState);
255+
throw new Error(`Could not download the results for variant analysis with id: ${variantAnalysis.id}. Error: ${getErrorMessage(e)}`);
256+
}
220257
}
221258

222259
repoState.downloadStatus = VariantAnalysisScannedRepositoryDownloadStatus.Succeeded;
223260
await this.onRepoStateUpdated(variantAnalysis.id, repoState);
261+
262+
await fs.outputJson(this.getRepoStatesStoragePath(variantAnalysis.id), this.repoStates.get(variantAnalysis.id));
224263
}
225264

226265
public async enqueueDownload(
@@ -242,6 +281,13 @@ export class VariantAnalysisManager extends DisposableObject implements VariantA
242281
);
243282
}
244283

284+
private getRepoStatesStoragePath(variantAnalysisId: number): string {
285+
return path.join(
286+
this.getVariantAnalysisStorageLocation(variantAnalysisId),
287+
VariantAnalysisManager.REPO_STATES_FILENAME
288+
);
289+
}
290+
245291
/**
246292
* Prepares a directory for storing results for a variant analysis.
247293
* This directory contains a timestamp file, which will be

extensions/ql-vscode/src/remote-queries/variant-analysis-view-manager.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { VariantAnalysis } from './shared/variant-analysis';
1+
import { VariantAnalysis, VariantAnalysisScannedRepositoryState } from './shared/variant-analysis';
22

33
export interface VariantAnalysisViewInterface {
44
variantAnalysisId: number;
@@ -10,4 +10,5 @@ export interface VariantAnalysisViewManager<T extends VariantAnalysisViewInterfa
1010
unregisterView(view: T): void;
1111

1212
getVariantAnalysis(variantAnalysisId: number): Promise<VariantAnalysis | undefined>;
13+
getRepoStates(variantAnalysisId: number): Promise<VariantAnalysisScannedRepositoryState[]>;
1314
}

extensions/ql-vscode/src/remote-queries/variant-analysis-view.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ export class VariantAnalysisView extends AbstractWebview<ToVariantAnalysisMessag
127127
t: 'setVariantAnalysis',
128128
variantAnalysis,
129129
});
130+
131+
const repoStates = await this.manager.getRepoStates(this.variantAnalysisId);
132+
if (repoStates.length === 0) {
133+
return;
134+
}
135+
136+
await this.postMessage({
137+
t: 'setRepoStates',
138+
repoStates,
139+
});
130140
}
131141

132142
private async openQueryFile(): Promise<void> {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export const RepoRow = ({
119119
}
120120
}, [resultsLoaded, resultsLoading]);
121121

122-
const disabled = !status || !isCompletedAnalysisRepoStatus(status);
122+
const disabled = !status || !isCompletedAnalysisRepoStatus(status) || (status === VariantAnalysisRepoStatus.Succeeded && downloadStatus !== VariantAnalysisScannedRepositoryDownloadStatus.Succeeded);
123123
const expandableContentLoaded = status && (status !== VariantAnalysisRepoStatus.Succeeded || resultsLoaded);
124124

125125
return (

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

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import * as React from 'react';
22
import { render as reactRender, screen } from '@testing-library/react';
3-
import { VariantAnalysisRepoStatus } from '../../../remote-queries/shared/variant-analysis';
3+
import {
4+
VariantAnalysisRepoStatus,
5+
VariantAnalysisScannedRepositoryDownloadStatus
6+
} from '../../../remote-queries/shared/variant-analysis';
47
import userEvent from '@testing-library/user-event';
58
import { RepoRow, RepoRowProps } from '../RepoRow';
69
import { createMockRepositoryWithMetadata } from '../../../vscode-tests/factories/remote-queries/shared/repository';
@@ -50,7 +53,7 @@ describe(RepoRow.name, () => {
5053
})).toBeDisabled();
5154
});
5255

53-
it('renders the succeeded state', () => {
56+
it('renders the succeeded state without download status', () => {
5457
render({
5558
status: VariantAnalysisRepoStatus.Succeeded,
5659
resultCount: 178,
@@ -60,6 +63,42 @@ describe(RepoRow.name, () => {
6063
name: 'Success',
6164
})).toBeInTheDocument();
6265
expect(screen.getByText('178')).toBeInTheDocument();
66+
expect(screen.getByRole<HTMLButtonElement>('button', {
67+
expanded: false
68+
})).toBeDisabled();
69+
});
70+
71+
it('renders the succeeded state with pending download status', () => {
72+
render({
73+
status: VariantAnalysisRepoStatus.Succeeded,
74+
resultCount: 178,
75+
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.Pending,
76+
});
77+
78+
expect(screen.getByRole<HTMLButtonElement>('button', {
79+
expanded: false
80+
})).toBeDisabled();
81+
});
82+
83+
it('renders the succeeded state with in progress download status', () => {
84+
render({
85+
status: VariantAnalysisRepoStatus.Succeeded,
86+
resultCount: 178,
87+
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.InProgress,
88+
});
89+
90+
expect(screen.getByRole<HTMLButtonElement>('button', {
91+
expanded: false
92+
})).toBeDisabled();
93+
});
94+
95+
it('renders the succeeded state with succeeded download status', () => {
96+
render({
97+
status: VariantAnalysisRepoStatus.Succeeded,
98+
resultCount: 178,
99+
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.Succeeded,
100+
});
101+
63102
expect(screen.getByRole<HTMLButtonElement>('button', {
64103
expanded: false
65104
})).toBeEnabled();
@@ -217,6 +256,7 @@ describe(RepoRow.name, () => {
217256
it('can expand the repo item when succeeded and loaded', async () => {
218257
render({
219258
status: VariantAnalysisRepoStatus.Succeeded,
259+
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.Succeeded,
220260
interpretedResults: [],
221261
});
222262

@@ -232,6 +272,7 @@ describe(RepoRow.name, () => {
232272
it('can expand the repo item when succeeded and not loaded', async () => {
233273
const { rerender } = render({
234274
status: VariantAnalysisRepoStatus.Succeeded,
275+
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.Succeeded,
235276
});
236277

237278
await userEvent.click(screen.getByRole('button', {

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { render as reactRender, screen } from '@testing-library/react';
33
import userEvent from '@testing-library/user-event';
44
import {
55
VariantAnalysisRepoStatus,
6+
VariantAnalysisScannedRepositoryDownloadStatus,
67
VariantAnalysisStatus
78
} from '../../../remote-queries/shared/variant-analysis';
89
import { VariantAnalysisAnalyzedRepos, VariantAnalysisAnalyzedReposProps } from '../VariantAnalysisAnalyzedRepos';
@@ -110,6 +111,12 @@ describe(VariantAnalysisAnalyzedRepos.name, () => {
110111

111112
it('renders the interpreted result for a succeeded repo', async () => {
112113
render({
114+
repositoryStates: [
115+
{
116+
repositoryId: 2,
117+
downloadStatus: VariantAnalysisScannedRepositoryDownloadStatus.Succeeded,
118+
}
119+
],
113120
repositoryResults: [
114121
{
115122
variantAnalysisId: 1,

0 commit comments

Comments
 (0)