Skip to content

Commit 6cf351f

Browse files
robertbrignullkoesie10
authored andcommitted
Output percentage during results download
1 parent 5c5c2e4 commit 6cf351f

File tree

7 files changed

+60
-72
lines changed

7 files changed

+60
-72
lines changed

extensions/ql-vscode/src/extension.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,6 @@ async function activateWithInstalledDistribution(
633633
);
634634
await ensureDir(variantAnalysisStorageDir);
635635
const variantAnalysisResultsManager = new VariantAnalysisResultsManager(
636-
app.credentials,
637636
cliServer,
638637
extLogger,
639638
);

extensions/ql-vscode/src/remote-queries/gh-api/gh-api-client.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,6 @@ export async function getVariantAnalysisRepo(
8181
return response.data;
8282
}
8383

84-
export async function getVariantAnalysisRepoResult(
85-
credentials: Credentials,
86-
downloadUrl: string,
87-
): Promise<ArrayBuffer> {
88-
const octokit = await credentials.getOctokit();
89-
const response = await octokit.request(`GET ${downloadUrl}`);
90-
91-
return response.data;
92-
}
93-
9484
export async function getRepositoryFromNwo(
9585
credentials: Credentials,
9686
owner: string,

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export class VariantAnalysisManager
6868
implements VariantAnalysisViewManager<VariantAnalysisView>
6969
{
7070
private static readonly REPO_STATES_FILENAME = "repo_states.json";
71+
private static readonly DOWNLOAD_PERCENTAGE_UPDATE_DELAY_MS = 3000;
7172

7273
private readonly _onVariantAnalysisAdded = this.push(
7374
new EventEmitter<VariantAnalysis>(),
@@ -514,10 +515,27 @@ export class VariantAnalysisManager
514515
await this.onRepoStateUpdated(variantAnalysis.id, repoState);
515516

516517
try {
518+
let lastRepoStateUpdate = 0;
519+
const updateRepoStateCallback = async (downloadPercentage: number) => {
520+
const now = new Date().getTime();
521+
if (
522+
lastRepoStateUpdate <
523+
now - VariantAnalysisManager.DOWNLOAD_PERCENTAGE_UPDATE_DELAY_MS
524+
) {
525+
lastRepoStateUpdate = now;
526+
await this.onRepoStateUpdated(variantAnalysis.id, {
527+
repositoryId: scannedRepo.repository.id,
528+
downloadStatus:
529+
VariantAnalysisScannedRepositoryDownloadStatus.InProgress,
530+
downloadPercentage,
531+
});
532+
}
533+
};
517534
await this.variantAnalysisResultsManager.download(
518535
variantAnalysis.id,
519536
repoTask,
520537
this.getVariantAnalysisStorageLocation(variantAnalysis.id),
538+
updateRepoStateCallback,
521539
);
522540
} catch (e) {
523541
repoState.downloadStatus =

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import {
2+
appendFileSync,
23
pathExists,
34
mkdir,
45
outputJson,
5-
writeFileSync,
66
readJson,
77
} from "fs-extra";
8+
import fetch from "node-fetch";
89
import { EOL } from "os";
910
import { join } from "path";
1011

11-
import { Credentials } from "../common/authentication";
1212
import { Logger } from "../common";
1313
import { AnalysisAlert, AnalysisRawResults } from "./shared/analysis-result";
1414
import { sarifParser } from "../sarif-parser";
@@ -21,7 +21,6 @@ import {
2121
VariantAnalysisScannedRepositoryResult,
2222
} from "./shared/variant-analysis";
2323
import { DisposableObject, DisposeHandler } from "../pure/disposable-object";
24-
import { getVariantAnalysisRepoResult } from "./gh-api/gh-api-client";
2524
import { EventEmitter } from "vscode";
2625
import { unzipFile } from "../pure/zip";
2726

@@ -63,7 +62,6 @@ export class VariantAnalysisResultsManager extends DisposableObject {
6362
readonly onResultLoaded = this._onResultLoaded.event;
6463

6564
constructor(
66-
private readonly credentials: Credentials,
6765
private readonly cliServer: CodeQLCliServer,
6866
private readonly logger: Logger,
6967
) {
@@ -75,6 +73,7 @@ export class VariantAnalysisResultsManager extends DisposableObject {
7573
variantAnalysisId: number,
7674
repoTask: VariantAnalysisRepositoryTask,
7775
variantAnalysisStoragePath: string,
76+
onDownloadPercentageChanged: (downloadPercentage: number) => Promise<void>,
7877
): Promise<void> {
7978
if (!repoTask.artifactUrl) {
8079
throw new Error("Missing artifact URL");
@@ -85,11 +84,6 @@ export class VariantAnalysisResultsManager extends DisposableObject {
8584
repoTask.repository.fullName,
8685
);
8786

88-
const result = await getVariantAnalysisRepoResult(
89-
this.credentials,
90-
repoTask.artifactUrl,
91-
);
92-
9387
if (!(await pathExists(resultDirectory))) {
9488
await mkdir(resultDirectory, { recursive: true });
9589
}
@@ -100,12 +94,22 @@ export class VariantAnalysisResultsManager extends DisposableObject {
10094
);
10195

10296
const zipFilePath = join(resultDirectory, "results.zip");
97+
98+
const response = await fetch(repoTask.artifactUrl);
99+
let amountDownloaded = 0;
100+
for await (const chunk of response.body) {
101+
appendFileSync(zipFilePath, Buffer.from(chunk));
102+
amountDownloaded += chunk.length;
103+
await onDownloadPercentageChanged(
104+
Math.floor((amountDownloaded / response.size) * 100),
105+
);
106+
}
107+
103108
const unzippedFilesDirectory = join(
104109
resultDirectory,
105110
VariantAnalysisResultsManager.RESULTS_DIRECTORY,
106111
);
107112

108-
writeFileSync(zipFilePath, Buffer.from(result));
109113
await unzipFile(zipFilePath, unzippedFilesDirectory);
110114

111115
this._onResultDownloaded.fire({

extensions/ql-vscode/test/unit-tests/remote-queries/gh-api/gh-api-client.test.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import { faker } from "@faker-js/faker";
2-
31
import {
42
getRepositoryFromNwo,
53
getVariantAnalysis,
64
getVariantAnalysisRepo,
7-
getVariantAnalysisRepoResult,
85
submitVariantAnalysis,
96
} from "../../../../src/remote-queries/gh-api/gh-api-client";
107
import { createMockSubmission } from "../../../factories/remote-queries/shared/variant-analysis-submission";
@@ -69,23 +66,6 @@ describe("getVariantAnalysisRepo", () => {
6966
});
7067
});
7168

72-
describe("getVariantAnalysisRepoResult", () => {
73-
it("returns the variant analysis repo result", async () => {
74-
await mockServer.loadScenario("problem-query-success");
75-
76-
const result = await getVariantAnalysisRepoResult(
77-
testCredentialsWithRealOctokit(),
78-
`https://objects-origin.githubusercontent.com/codeql-query-console/codeql-variant-analysis-repo-tasks/${variantAnalysisId}/${repoTaskId}/${faker.datatype.uuid()}`,
79-
);
80-
81-
expect(result).toBeDefined();
82-
expect(result).toBeInstanceOf(ArrayBuffer);
83-
expect(result.byteLength).toBe(
84-
variantAnalysisRepoJson_response.body.artifact_size_in_bytes,
85-
);
86-
});
87-
});
88-
8969
describe("getRepositoryFromNwo", () => {
9070
it("returns the repository", async () => {
9171
await mockServer.loadScenario("problem-query-success");

extensions/ql-vscode/test/vscode-tests/cli-integration/remote-queries/variant-analysis-manager.test.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import * as ghApiClient from "../../../../src/remote-queries/gh-api/gh-api-clien
2121
import * as ghActionsApiClient from "../../../../src/remote-queries/gh-api/gh-actions-api-client";
2222
import * as fs from "fs-extra";
2323
import { join } from "path";
24+
import { Response } from "node-fetch";
25+
import * as fetchModule from "node-fetch";
2426

2527
import { VariantAnalysisManager } from "../../../../src/remote-queries/variant-analysis-manager";
2628
import { CodeQLCliServer } from "../../../../src/cli";
@@ -95,7 +97,6 @@ describe("Variant Analysis Manager", () => {
9597
cli = extension.cliServer;
9698
app = createMockApp({});
9799
variantAnalysisResultsManager = new VariantAnalysisResultsManager(
98-
app.credentials,
99100
cli,
100101
extLogger,
101102
);
@@ -334,32 +335,21 @@ describe("Variant Analysis Manager", () => {
334335
});
335336

336337
describe("autoDownloadVariantAnalysisResult", () => {
337-
let arrayBuffer: ArrayBuffer;
338-
339338
let getVariantAnalysisRepoStub: jest.SpiedFunction<
340339
typeof ghApiClient.getVariantAnalysisRepo
341340
>;
342341
let getVariantAnalysisRepoResultStub: jest.SpiedFunction<
343-
typeof ghApiClient.getVariantAnalysisRepoResult
342+
typeof fetchModule.default
344343
>;
345344

346345
let repoStatesPath: string;
347346

348347
beforeEach(async () => {
349-
const sourceFilePath = join(
350-
__dirname,
351-
"../data/variant-analysis-results.zip",
352-
);
353-
arrayBuffer = fs.readFileSync(sourceFilePath).buffer;
354-
355348
getVariantAnalysisRepoStub = jest.spyOn(
356349
ghApiClient,
357350
"getVariantAnalysisRepo",
358351
);
359-
getVariantAnalysisRepoResultStub = jest.spyOn(
360-
ghApiClient,
361-
"getVariantAnalysisRepoResult",
362-
);
352+
getVariantAnalysisRepoResultStub = jest.spyOn(fetchModule, "default");
363353

364354
repoStatesPath = join(
365355
storagePath,
@@ -374,7 +364,6 @@ describe("Variant Analysis Manager", () => {
374364
delete dummyRepoTask.artifact_url;
375365

376366
getVariantAnalysisRepoStub.mockResolvedValue(dummyRepoTask);
377-
getVariantAnalysisRepoResultStub.mockResolvedValue(arrayBuffer);
378367
});
379368

380369
it("should not try to download the result", async () => {
@@ -395,7 +384,15 @@ describe("Variant Analysis Manager", () => {
395384
dummyRepoTask = createMockVariantAnalysisRepoTask();
396385

397386
getVariantAnalysisRepoStub.mockResolvedValue(dummyRepoTask);
398-
getVariantAnalysisRepoResultStub.mockResolvedValue(arrayBuffer);
387+
388+
const sourceFilePath = join(
389+
__dirname,
390+
"../data/variant-analysis-results.zip",
391+
);
392+
const arrayBuffer = fs.readFileSync(sourceFilePath).buffer;
393+
getVariantAnalysisRepoResultStub.mockResolvedValue(
394+
new Response(arrayBuffer),
395+
);
399396
});
400397

401398
it("should return early if variant analysis is cancelled", async () => {

extensions/ql-vscode/test/vscode-tests/cli-integration/remote-queries/variant-analysis-results-manager.test.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,18 @@ import { CodeQLExtensionInterface } from "../../../../src/extension";
33
import { extLogger } from "../../../../src/common";
44
import * as fs from "fs-extra";
55
import { join, resolve } from "path";
6+
import { Response, RequestInfo, RequestInit } from "node-fetch";
7+
import * as fetchModule from "node-fetch";
68

79
import { VariantAnalysisResultsManager } from "../../../../src/remote-queries/variant-analysis-results-manager";
810
import { CodeQLCliServer } from "../../../../src/cli";
911
import { storagePath } from "../global.helper";
1012
import { faker } from "@faker-js/faker";
11-
import * as ghApiClient from "../../../../src/remote-queries/gh-api/gh-api-client";
1213
import { createMockVariantAnalysisRepositoryTask } from "../../../factories/remote-queries/shared/variant-analysis-repo-tasks";
1314
import {
1415
VariantAnalysisRepositoryTask,
1516
VariantAnalysisScannedRepositoryResult,
1617
} from "../../../../src/remote-queries/shared/variant-analysis";
17-
import { testCredentialsWithStub } from "../../../factories/authentication";
18-
import { Credentials } from "../../../../src/common/authentication";
1918

2019
jest.setTimeout(10_000);
2120

@@ -44,7 +43,6 @@ describe(VariantAnalysisResultsManager.name, () => {
4443
jest.spyOn(extLogger, "log").mockResolvedValue(undefined);
4544

4645
variantAnalysisResultsManager = new VariantAnalysisResultsManager(
47-
testCredentialsWithStub(),
4846
cli,
4947
extLogger,
5048
);
@@ -89,6 +87,7 @@ describe(VariantAnalysisResultsManager.name, () => {
8987
variantAnalysisId,
9088
dummyRepoTask,
9189
variantAnalysisStoragePath,
90+
() => Promise.resolve(),
9291
),
9392
).rejects.toThrow("Missing artifact URL");
9493
});
@@ -98,7 +97,7 @@ describe(VariantAnalysisResultsManager.name, () => {
9897
let arrayBuffer: ArrayBuffer;
9998

10099
let getVariantAnalysisRepoResultStub: jest.SpiedFunction<
101-
typeof ghApiClient.getVariantAnalysisRepoResult
100+
typeof fetchModule.default
102101
>;
103102

104103
beforeEach(async () => {
@@ -109,22 +108,21 @@ describe(VariantAnalysisResultsManager.name, () => {
109108
arrayBuffer = fs.readFileSync(sourceFilePath).buffer;
110109

111110
getVariantAnalysisRepoResultStub = jest
112-
.spyOn(ghApiClient, "getVariantAnalysisRepoResult")
113-
.mockImplementation(
114-
(_credentials: Credentials, downloadUrl: string) => {
115-
if (downloadUrl === dummyRepoTask.artifactUrl) {
116-
return Promise.resolve(arrayBuffer);
117-
}
118-
return Promise.reject(new Error("Unexpected artifact URL"));
119-
},
120-
);
111+
.spyOn(fetchModule, "default")
112+
.mockImplementation((url: RequestInfo, _init?: RequestInit) => {
113+
if (url === dummyRepoTask.artifactUrl) {
114+
return Promise.resolve(new Response(arrayBuffer));
115+
}
116+
return Promise.reject(new Error("Unexpected artifact URL"));
117+
});
121118
});
122119

123120
it("should call the API to download the results", async () => {
124121
await variantAnalysisResultsManager.download(
125122
variantAnalysisId,
126123
dummyRepoTask,
127124
variantAnalysisStoragePath,
125+
() => Promise.resolve(),
128126
);
129127

130128
expect(getVariantAnalysisRepoResultStub).toHaveBeenCalledTimes(1);
@@ -135,6 +133,7 @@ describe(VariantAnalysisResultsManager.name, () => {
135133
variantAnalysisId,
136134
dummyRepoTask,
137135
variantAnalysisStoragePath,
136+
() => Promise.resolve(),
138137
);
139138

140139
expect(fs.existsSync(`${repoTaskStorageDirectory}/results.zip`)).toBe(
@@ -147,6 +146,7 @@ describe(VariantAnalysisResultsManager.name, () => {
147146
variantAnalysisId,
148147
dummyRepoTask,
149148
variantAnalysisStoragePath,
149+
() => Promise.resolve(),
150150
);
151151

152152
expect(
@@ -160,6 +160,7 @@ describe(VariantAnalysisResultsManager.name, () => {
160160
variantAnalysisId,
161161
dummyRepoTask,
162162
variantAnalysisStoragePath,
163+
() => Promise.resolve(),
163164
);
164165

165166
expect(
@@ -185,7 +186,6 @@ describe(VariantAnalysisResultsManager.name, () => {
185186

186187
beforeEach(() => {
187188
variantAnalysisResultsManager = new VariantAnalysisResultsManager(
188-
testCredentialsWithStub(),
189189
cli,
190190
extLogger,
191191
);

0 commit comments

Comments
 (0)