Allow import all database subfolders by selecting a folder

This commit is contained in:
reito
2024-11-07 21:50:15 +08:00
parent 9a0bff6ebb
commit 736dc46b63
4 changed files with 81 additions and 18 deletions

View File

@@ -2,6 +2,8 @@
## [UNRELEASED]
- Add a palette command that allow user to select a folder and import all database subfolders.
## 1.16.1 - 6 November 2024
- Support result columns of type `QlBuiltins::BigInt` in quick evaluations. [#3647](https://github.com/github/vscode-codeql/pull/3647)

View File

@@ -839,6 +839,10 @@
"command": "codeQL.chooseDatabaseFolder",
"title": "CodeQL: Choose Database from Folder"
},
{
"command": "codeQL.chooseMultipleDatabaseFolder",
"title": "CodeQL: Choose Folder contains all Database Folders to import"
},
{
"command": "codeQL.chooseDatabaseArchive",
"title": "CodeQL: Choose Database from Archive"

View File

@@ -211,6 +211,7 @@ export type LanguageSelectionCommands = {
export type LocalDatabasesCommands = {
// Command palette commands
"codeQL.chooseDatabaseFolder": () => Promise<void>;
"codeQL.chooseMultipleDatabaseFolder": () => Promise<void>;
"codeQL.chooseDatabaseArchive": () => Promise<void>;
"codeQL.chooseDatabaseInternet": () => Promise<void>;
"codeQL.chooseDatabaseGithub": () => Promise<void>;

View File

@@ -16,6 +16,7 @@ import {
ThemeIcon,
ThemeColor,
workspace,
FileType,
} from "vscode";
import { pathExists, stat, readdir, remove } from "fs-extra";
@@ -36,6 +37,7 @@ import {
import {
showAndLogExceptionWithTelemetry,
showAndLogErrorMessage,
showAndLogInformationMessage,
} from "../common/logging";
import type { DatabaseFetcher } from "./database-fetcher";
import { asError, asyncFilter, getErrorMessage } from "../common/helpers-pure";
@@ -267,6 +269,8 @@ export class DatabaseUI extends DisposableObject {
"codeQL.getCurrentDatabase": this.handleGetCurrentDatabase.bind(this),
"codeQL.chooseDatabaseFolder":
this.handleChooseDatabaseFolderFromPalette.bind(this),
"codeQL.chooseMultipleDatabaseFolder":
this.handleChooseMultipleDatabaseFolderFromPalette.bind(this),
"codeQL.chooseDatabaseArchive":
this.handleChooseDatabaseArchiveFromPalette.bind(this),
"codeQL.chooseDatabaseInternet":
@@ -322,10 +326,11 @@ export class DatabaseUI extends DisposableObject {
}
private async chooseDatabaseFolder(
subFolder: boolean,
progress: ProgressCallback,
): Promise<void> {
try {
await this.chooseAndSetDatabase(true, progress);
await this.chooseAndSetDatabase(true, subFolder, progress);
} catch (e) {
void showAndLogExceptionWithTelemetry(
this.app.logger,
@@ -340,7 +345,7 @@ export class DatabaseUI extends DisposableObject {
private async handleChooseDatabaseFolder(): Promise<void> {
return withProgress(
async (progress) => {
await this.chooseDatabaseFolder(progress);
await this.chooseDatabaseFolder(false, progress);
},
{
title: "Adding database from folder",
@@ -351,7 +356,7 @@ export class DatabaseUI extends DisposableObject {
private async handleChooseDatabaseFolderFromPalette(): Promise<void> {
return withProgress(
async (progress) => {
await this.chooseDatabaseFolder(progress);
await this.chooseDatabaseFolder(false, progress);
},
{
title: "Choose a Database from a Folder",
@@ -359,6 +364,17 @@ export class DatabaseUI extends DisposableObject {
);
}
private async handleChooseMultipleDatabaseFolderFromPalette(): Promise<void> {
return withProgress(
async (progress) => {
await this.chooseDatabaseFolder(true, progress);
},
{
title: "Choose a Folder contains all Database Folders",
},
);
}
private async handleSetDefaultTourDatabase(): Promise<void> {
return withProgress(
async () => {
@@ -494,7 +510,7 @@ export class DatabaseUI extends DisposableObject {
progress: ProgressCallback,
): Promise<void> {
try {
await this.chooseAndSetDatabase(false, progress);
await this.chooseAndSetDatabase(false, false, progress);
} catch (e: unknown) {
void showAndLogExceptionWithTelemetry(
this.app.logger,
@@ -962,27 +978,67 @@ export class DatabaseUI extends DisposableObject {
*/
private async chooseAndSetDatabase(
byFolder: boolean,
subFolder: boolean,
progress: ProgressCallback,
): Promise<DatabaseItem | undefined> {
): Promise<DatabaseItem[] | DatabaseItem | undefined> {
const uri = await chooseDatabaseDir(byFolder);
if (!uri) {
return undefined;
}
if (byFolder && !uri.fsPath.endsWith("testproj")) {
const fixedUri = await this.fixDbUri(uri);
// we are selecting a database folder
return await this.databaseManager.openDatabase(fixedUri, {
type: "folder",
});
if (subFolder) {
if (!byFolder) {
return undefined;
}
const databases: DatabaseItem[] = [];
const failures: string[] = [];
const entries = await workspace.fs.readDirectory(uri);
for (const entry of entries) {
if (entry[1] === FileType.Directory) {
try {
const fixedUri = await this.fixDbUri(Uri.joinPath(uri, entry[0]));
const database = await this.databaseManager.openDatabase(fixedUri, {
type: "folder",
});
databases.push(database);
} catch (e) {
failures.push(entry[0]);
}
}
}
if (failures.length) {
void showAndLogErrorMessage(
this.app.logger,
`Failed to import ${failures.length} database(s) (${failures.join(
", ",
)}), successfully imported ${databases.length} database(s).`,
);
} else {
void showAndLogInformationMessage(
this.app.logger,
`Successfully imported ${databases.length} database(s).`,
);
}
return databases;
} else {
// we are selecting a database archive or a testproj.
// Unzip archives (if an archive) and copy into a workspace-controlled area
// before importing.
return await this.databaseFetcher.importLocalDatabase(
uri.toString(true),
progress,
);
if (byFolder && !uri.fsPath.endsWith("testproj")) {
const fixedUri = await this.fixDbUri(uri);
// we are selecting a database folder
return await this.databaseManager.openDatabase(fixedUri, {
type: "folder",
});
} else {
// we are selecting a database archive or a testproj.
// Unzip archives (if an archive) and copy into a workspace-controlled area
// before importing.
return await this.databaseFetcher.importLocalDatabase(
uri.toString(true),
progress,
);
}
}
}