Merge pull request #3073 from github/koesie10/download-multiple-github-databases
Allow downloading multiple databases from GitHub
This commit is contained in:
@@ -2,10 +2,7 @@ import { window } from "vscode";
|
|||||||
import { Octokit } from "@octokit/rest";
|
import { Octokit } from "@octokit/rest";
|
||||||
import { showNeverAskAgainDialog } from "../common/vscode/dialog";
|
import { showNeverAskAgainDialog } from "../common/vscode/dialog";
|
||||||
import { getLanguageDisplayName } from "../common/query-language";
|
import { getLanguageDisplayName } from "../common/query-language";
|
||||||
import {
|
import { downloadGitHubDatabaseFromUrl } from "./database-fetcher";
|
||||||
downloadGitHubDatabaseFromUrl,
|
|
||||||
promptForLanguage,
|
|
||||||
} from "./database-fetcher";
|
|
||||||
import { withProgress } from "../common/vscode/progress";
|
import { withProgress } from "../common/vscode/progress";
|
||||||
import { DatabaseManager } from "./local-databases";
|
import { DatabaseManager } from "./local-databases";
|
||||||
import { CodeQLCliServer } from "../codeql-cli/cli";
|
import { CodeQLCliServer } from "../codeql-cli/cli";
|
||||||
@@ -66,40 +63,46 @@ export async function downloadDatabaseFromGitHub(
|
|||||||
cliServer: CodeQLCliServer,
|
cliServer: CodeQLCliServer,
|
||||||
commandManager: AppCommandManager,
|
commandManager: AppCommandManager,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const languages = databases.map((database) => database.language);
|
const selectedDatabases = await promptForDatabases(databases);
|
||||||
|
if (selectedDatabases.length === 0) {
|
||||||
const language = await promptForLanguage(languages, undefined);
|
|
||||||
if (!language) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const database = databases.find((database) => database.language === language);
|
await Promise.all(
|
||||||
if (!database) {
|
selectedDatabases.map((database) =>
|
||||||
return;
|
withProgress(
|
||||||
}
|
async (progress) => {
|
||||||
|
await downloadGitHubDatabaseFromUrl(
|
||||||
|
database.url,
|
||||||
|
database.id,
|
||||||
|
database.created_at,
|
||||||
|
database.commit_oid ?? null,
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
octokit,
|
||||||
|
progress,
|
||||||
|
databaseManager,
|
||||||
|
storagePath,
|
||||||
|
cliServer,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
await withProgress(async (progress) => {
|
await commandManager.execute("codeQLDatabases.focus");
|
||||||
await downloadGitHubDatabaseFromUrl(
|
void window.showInformationMessage(
|
||||||
database.url,
|
`Downloaded ${getLanguageDisplayName(
|
||||||
database.id,
|
database.language,
|
||||||
database.created_at,
|
)} database from GitHub.`,
|
||||||
database.commit_oid ?? null,
|
);
|
||||||
owner,
|
},
|
||||||
repo,
|
{
|
||||||
octokit,
|
title: `Adding ${getLanguageDisplayName(
|
||||||
progress,
|
database.language,
|
||||||
databaseManager,
|
)} database from GitHub`,
|
||||||
storagePath,
|
},
|
||||||
cliServer,
|
),
|
||||||
true,
|
),
|
||||||
false,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
await commandManager.execute("codeQLDatabases.focus");
|
|
||||||
void window.showInformationMessage(
|
|
||||||
`Downloaded ${getLanguageDisplayName(language)} database from GitHub.`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,3 +129,34 @@ function joinLanguages(languages: string[]): string {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function promptForDatabases(
|
||||||
|
databases: CodeqlDatabase[],
|
||||||
|
): Promise<CodeqlDatabase[]> {
|
||||||
|
if (databases.length === 1) {
|
||||||
|
return databases;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = databases
|
||||||
|
.map((database) => {
|
||||||
|
const bytesToDisplayMB = `${(database.size / (1024 * 1024)).toFixed(
|
||||||
|
1,
|
||||||
|
)} MB`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: getLanguageDisplayName(database.language),
|
||||||
|
description: bytesToDisplayMB,
|
||||||
|
database,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => a.label.localeCompare(b.label));
|
||||||
|
|
||||||
|
const selectedItems = await window.showQuickPick(items, {
|
||||||
|
title: "Select databases to download",
|
||||||
|
placeHolder: "Databases found in this repository",
|
||||||
|
ignoreFocusOut: true,
|
||||||
|
canPickMany: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return selectedItems?.map((selectedItem) => selectedItem.database) ?? [];
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { faker } from "@faker-js/faker";
|
import { faker } from "@faker-js/faker";
|
||||||
import { Octokit } from "@octokit/rest";
|
import { Octokit } from "@octokit/rest";
|
||||||
import { mockedObject } from "../../utils/mocking.helpers";
|
import { QuickPickItem, window } from "vscode";
|
||||||
|
import { mockedObject, mockedQuickPickItem } from "../../utils/mocking.helpers";
|
||||||
import {
|
import {
|
||||||
askForGitHubDatabaseDownload,
|
askForGitHubDatabaseDownload,
|
||||||
downloadDatabaseFromGitHub,
|
downloadDatabaseFromGitHub,
|
||||||
@@ -103,15 +104,14 @@ describe("downloadDatabaseFromGitHub", () => {
|
|||||||
created_at: faker.date.past().toISOString(),
|
created_at: faker.date.past().toISOString(),
|
||||||
commit_oid: faker.git.commitSha(),
|
commit_oid: faker.git.commitSha(),
|
||||||
language: "swift",
|
language: "swift",
|
||||||
|
size: 27389673,
|
||||||
url: faker.internet.url({
|
url: faker.internet.url({
|
||||||
protocol: "https",
|
protocol: "https",
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
let promptForLanguageSpy: jest.SpiedFunction<
|
let showQuickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
|
||||||
typeof databaseFetcher.promptForLanguage
|
|
||||||
>;
|
|
||||||
let downloadGitHubDatabaseFromUrlSpy: jest.SpiedFunction<
|
let downloadGitHubDatabaseFromUrlSpy: jest.SpiedFunction<
|
||||||
typeof databaseFetcher.downloadGitHubDatabaseFromUrl
|
typeof databaseFetcher.downloadGitHubDatabaseFromUrl
|
||||||
>;
|
>;
|
||||||
@@ -121,9 +121,13 @@ describe("downloadDatabaseFromGitHub", () => {
|
|||||||
databaseManager = mockedObject<DatabaseManager>({});
|
databaseManager = mockedObject<DatabaseManager>({});
|
||||||
cliServer = mockedObject<CodeQLCliServer>({});
|
cliServer = mockedObject<CodeQLCliServer>({});
|
||||||
|
|
||||||
promptForLanguageSpy = jest
|
showQuickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValue(
|
||||||
.spyOn(databaseFetcher, "promptForLanguage")
|
mockedQuickPickItem([
|
||||||
.mockResolvedValue(databases[0].language);
|
mockedObject<QuickPickItem & { database: CodeqlDatabase }>({
|
||||||
|
database: databases[0],
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
);
|
||||||
downloadGitHubDatabaseFromUrlSpy = jest
|
downloadGitHubDatabaseFromUrlSpy = jest
|
||||||
.spyOn(databaseFetcher, "downloadGitHubDatabaseFromUrl")
|
.spyOn(databaseFetcher, "downloadGitHubDatabaseFromUrl")
|
||||||
.mockResolvedValue(undefined);
|
.mockResolvedValue(undefined);
|
||||||
@@ -157,28 +161,6 @@ describe("downloadDatabaseFromGitHub", () => {
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
expect(promptForLanguageSpy).toHaveBeenCalledWith(["swift"], undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("when not selecting language", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
promptForLanguageSpy.mockResolvedValue(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not download the database", async () => {
|
|
||||||
await downloadDatabaseFromGitHub(
|
|
||||||
octokit,
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
databases,
|
|
||||||
databaseManager,
|
|
||||||
storagePath,
|
|
||||||
cliServer,
|
|
||||||
commandManager,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(downloadGitHubDatabaseFromUrlSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when there are multiple languages", () => {
|
describe("when there are multiple languages", () => {
|
||||||
@@ -189,6 +171,7 @@ describe("downloadDatabaseFromGitHub", () => {
|
|||||||
created_at: faker.date.past().toISOString(),
|
created_at: faker.date.past().toISOString(),
|
||||||
commit_oid: faker.git.commitSha(),
|
commit_oid: faker.git.commitSha(),
|
||||||
language: "swift",
|
language: "swift",
|
||||||
|
size: 27389673,
|
||||||
url: faker.internet.url({
|
url: faker.internet.url({
|
||||||
protocol: "https",
|
protocol: "https",
|
||||||
}),
|
}),
|
||||||
@@ -198,16 +181,23 @@ describe("downloadDatabaseFromGitHub", () => {
|
|||||||
created_at: faker.date.past().toISOString(),
|
created_at: faker.date.past().toISOString(),
|
||||||
commit_oid: null,
|
commit_oid: null,
|
||||||
language: "go",
|
language: "go",
|
||||||
|
size: 2930572385,
|
||||||
url: faker.internet.url({
|
url: faker.internet.url({
|
||||||
protocol: "https",
|
protocol: "https",
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
promptForLanguageSpy.mockResolvedValue(databases[1].language);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("downloads the correct database", async () => {
|
it("downloads a single selected language", async () => {
|
||||||
|
showQuickPickSpy.mockResolvedValue(
|
||||||
|
mockedQuickPickItem([
|
||||||
|
mockedObject<QuickPickItem & { database: CodeqlDatabase }>({
|
||||||
|
database: databases[1],
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
await downloadDatabaseFromGitHub(
|
await downloadDatabaseFromGitHub(
|
||||||
octokit,
|
octokit,
|
||||||
owner,
|
owner,
|
||||||
@@ -235,10 +225,113 @@ describe("downloadDatabaseFromGitHub", () => {
|
|||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
expect(promptForLanguageSpy).toHaveBeenCalledWith(
|
expect(showQuickPickSpy).toHaveBeenCalledWith(
|
||||||
["swift", "go"],
|
[
|
||||||
undefined,
|
expect.objectContaining({
|
||||||
|
label: "Go",
|
||||||
|
description: "2794.8 MB",
|
||||||
|
database: databases[1],
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
label: "Swift",
|
||||||
|
description: "26.1 MB",
|
||||||
|
database: databases[0],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
expect.anything(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("downloads multiple selected languages", async () => {
|
||||||
|
showQuickPickSpy.mockResolvedValue(
|
||||||
|
mockedQuickPickItem([
|
||||||
|
mockedObject<QuickPickItem & { database: CodeqlDatabase }>({
|
||||||
|
database: databases[0],
|
||||||
|
}),
|
||||||
|
mockedObject<QuickPickItem & { database: CodeqlDatabase }>({
|
||||||
|
database: databases[1],
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
await downloadDatabaseFromGitHub(
|
||||||
|
octokit,
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
databases,
|
||||||
|
databaseManager,
|
||||||
|
storagePath,
|
||||||
|
cliServer,
|
||||||
|
commandManager,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(2);
|
||||||
|
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith(
|
||||||
|
databases[0].url,
|
||||||
|
databases[0].id,
|
||||||
|
databases[0].created_at,
|
||||||
|
databases[0].commit_oid,
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
octokit,
|
||||||
|
expect.anything(),
|
||||||
|
databaseManager,
|
||||||
|
storagePath,
|
||||||
|
cliServer,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith(
|
||||||
|
databases[1].url,
|
||||||
|
databases[1].id,
|
||||||
|
databases[1].created_at,
|
||||||
|
databases[1].commit_oid,
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
octokit,
|
||||||
|
expect.anything(),
|
||||||
|
databaseManager,
|
||||||
|
storagePath,
|
||||||
|
cliServer,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
expect(showQuickPickSpy).toHaveBeenCalledWith(
|
||||||
|
[
|
||||||
|
expect.objectContaining({
|
||||||
|
label: "Go",
|
||||||
|
description: "2794.8 MB",
|
||||||
|
database: databases[1],
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
label: "Swift",
|
||||||
|
description: "26.1 MB",
|
||||||
|
database: databases[0],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
expect.anything(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when not selecting language", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
showQuickPickSpy.mockResolvedValue(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not download the database", async () => {
|
||||||
|
await downloadDatabaseFromGitHub(
|
||||||
|
octokit,
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
databases,
|
||||||
|
databaseManager,
|
||||||
|
storagePath,
|
||||||
|
cliServer,
|
||||||
|
commandManager,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(downloadGitHubDatabaseFromUrlSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user