From 736dc46b63be974baade483dc32168a3680ded5a Mon Sep 17 00:00:00 2001 From: reito Date: Thu, 7 Nov 2024 21:50:15 +0800 Subject: [PATCH 01/17] Allow import all database subfolders by selecting a folder --- extensions/ql-vscode/CHANGELOG.md | 2 + extensions/ql-vscode/package.json | 4 + extensions/ql-vscode/src/common/commands.ts | 1 + .../src/databases/local-databases-ui.ts | 92 +++++++++++++++---- 4 files changed, 81 insertions(+), 18 deletions(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 6f54f563141..66738f15fc7 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -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) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 90d02fe4f57..2668c43adbd 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -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" diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index bdb8e792281..7f488c1ca89 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -211,6 +211,7 @@ export type LanguageSelectionCommands = { export type LocalDatabasesCommands = { // Command palette commands "codeQL.chooseDatabaseFolder": () => Promise; + "codeQL.chooseMultipleDatabaseFolder": () => Promise; "codeQL.chooseDatabaseArchive": () => Promise; "codeQL.chooseDatabaseInternet": () => Promise; "codeQL.chooseDatabaseGithub": () => Promise; diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 8a12983951c..353259b5d63 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -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 { 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 { 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 { 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 { + return withProgress( + async (progress) => { + await this.chooseDatabaseFolder(true, progress); + }, + { + title: "Choose a Folder contains all Database Folders", + }, + ); + } + private async handleSetDefaultTourDatabase(): Promise { return withProgress( async () => { @@ -494,7 +510,7 @@ export class DatabaseUI extends DisposableObject { progress: ProgressCallback, ): Promise { 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 { + ): Promise { 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, + ); + } } } From 1ec341a744c694562d6a5a7c662ad366fd42a5c8 Mon Sep 17 00:00:00 2001 From: reito Date: Tue, 12 Nov 2024 23:46:58 +0800 Subject: [PATCH 02/17] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20CHANGELOG.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Andrew Eisenberg --- extensions/ql-vscode/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 66738f15fc7..1ccd70b9644 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,7 +2,7 @@ ## [UNRELEASED] -- Add a palette command that allow user to select a folder and import all database subfolders. +- Add a palette command that allows importing of all databases inside of a parent folder. [3797](https://github.com/github/vscode-codeql/pull/3797) ## 1.16.1 - 6 November 2024 From fb6fac8803e8f92628d3346c4c9dcb2ed458bf9b Mon Sep 17 00:00:00 2001 From: reito Date: Tue, 12 Nov 2024 23:47:13 +0800 Subject: [PATCH 03/17] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20package.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Andrew Eisenberg --- extensions/ql-vscode/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 2668c43adbd..ea992200473 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -841,7 +841,7 @@ }, { "command": "codeQL.chooseMultipleDatabaseFolder", - "title": "CodeQL: Choose Folder contains all Database Folders to import" + "title": "CodeQL: Choose Folder to import all databases contained in it" }, { "command": "codeQL.chooseDatabaseArchive", From 1e749ec7930a38fbd1ae212c3644027492ea1123 Mon Sep 17 00:00:00 2001 From: reito Date: Wed, 13 Nov 2024 11:32:30 +0800 Subject: [PATCH 04/17] feat: move import folders out of original function, optimize logs --- .../src/databases/local-databases-ui.ts | 120 ++++++++++-------- 1 file changed, 65 insertions(+), 55 deletions(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 353259b5d63..d4f3ff0d0fe 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -326,11 +326,10 @@ export class DatabaseUI extends DisposableObject { } private async chooseDatabaseFolder( - subFolder: boolean, progress: ProgressCallback, ): Promise { try { - await this.chooseAndSetDatabase(true, subFolder, progress); + await this.chooseAndSetDatabase(true, progress); } catch (e) { void showAndLogExceptionWithTelemetry( this.app.logger, @@ -345,7 +344,7 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseFolder(): Promise { return withProgress( async (progress) => { - await this.chooseDatabaseFolder(false, progress); + await this.chooseDatabaseFolder(progress); }, { title: "Adding database from folder", @@ -356,7 +355,7 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseFolderFromPalette(): Promise { return withProgress( async (progress) => { - await this.chooseDatabaseFolder(false, progress); + await this.chooseDatabaseFolder(progress); }, { title: "Choose a Database from a Folder", @@ -367,7 +366,7 @@ export class DatabaseUI extends DisposableObject { private async handleChooseMultipleDatabaseFolderFromPalette(): Promise { return withProgress( async (progress) => { - await this.chooseDatabaseFolder(true, progress); + await this.chooseDatabasesParentFolder(progress); }, { title: "Choose a Folder contains all Database Folders", @@ -510,7 +509,7 @@ export class DatabaseUI extends DisposableObject { progress: ProgressCallback, ): Promise { try { - await this.chooseAndSetDatabase(false, false, progress); + await this.chooseAndSetDatabase(false, progress); } catch (e: unknown) { void showAndLogExceptionWithTelemetry( this.app.logger, @@ -978,7 +977,6 @@ export class DatabaseUI extends DisposableObject { */ private async chooseAndSetDatabase( byFolder: boolean, - subFolder: boolean, progress: ProgressCallback, ): Promise { const uri = await chooseDatabaseDir(byFolder); @@ -986,60 +984,72 @@ export class DatabaseUI extends DisposableObject { return undefined; } - if (subFolder) { - if (!byFolder) { - 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", + }); + } 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, + ); + } + } - 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]); - } - } - } + /** + * Ask the user for a parent directory that contains all databases. + * Returns all valid databases, or `undefined` if the operation was canceled. + */ + private async chooseDatabasesParentFolder( + progress: ProgressCallback, + ): Promise { + const uri = await chooseDatabaseDir(true); + if (!uri) { + return undefined; + } - 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).`, - ); + const databases: DatabaseItem[] = []; + const failures: string[] = []; + const entries = await workspace.fs.readDirectory(uri); + for (const [index, entry] of entries.entries()) { + progress({ + step: index + 1, + maxStep: entries.length, + message: `Importing ${entry[0]}`, + }); + + 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]); + } } + } - return databases; + if (failures.length) { + void showAndLogErrorMessage( + this.app.logger, + `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).`, + { fullMessage: `Failed imports: \n${failures.join("\n")}` }, + ); } else { - 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, - ); - } + void showAndLogInformationMessage( + this.app.logger, + `Successfully imported ${databases.length} database(s).`, + ); } + + return databases; } /** From 955f8c8ab4870f502ce2b081c3a51f616e6b49d5 Mon Sep 17 00:00:00 2001 From: reito Date: Wed, 13 Nov 2024 11:38:17 +0800 Subject: [PATCH 05/17] feat: skip 0 folders --- extensions/ql-vscode/src/databases/local-databases-ui.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index d4f3ff0d0fe..66c29a88805 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -1016,6 +1016,7 @@ export class DatabaseUI extends DisposableObject { const databases: DatabaseItem[] = []; const failures: string[] = []; const entries = await workspace.fs.readDirectory(uri); + for (const [index, entry] of entries.entries()) { progress({ step: index + 1, @@ -1040,8 +1041,14 @@ export class DatabaseUI extends DisposableObject { void showAndLogErrorMessage( this.app.logger, `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).`, - { fullMessage: `Failed imports: \n${failures.join("\n")}` }, + { fullMessage: `Failed folders to import:\n${failures.join("\n")}` }, ); + } else if (databases.length === 0) { + void showAndLogErrorMessage( + this.app.logger, + `No database folder to import.`, + ); + return undefined; } else { void showAndLogInformationMessage( this.app.logger, From c84331e1a32a9499f23cd47e1ada7866bf48e7b7 Mon Sep 17 00:00:00 2001 From: reito Date: Wed, 13 Nov 2024 11:45:03 +0800 Subject: [PATCH 06/17] fix: revert extra return type --- extensions/ql-vscode/src/databases/local-databases-ui.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 66c29a88805..6263475615f 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -978,7 +978,7 @@ export class DatabaseUI extends DisposableObject { private async chooseAndSetDatabase( byFolder: boolean, progress: ProgressCallback, - ): Promise { + ): Promise { const uri = await chooseDatabaseDir(byFolder); if (!uri) { return undefined; From 775e6dc354ea4d1d4d5585d7472d4088ea724a51 Mon Sep 17 00:00:00 2001 From: reito Date: Wed, 13 Nov 2024 11:47:30 +0800 Subject: [PATCH 07/17] fix: unify naming --- extensions/ql-vscode/package.json | 2 +- extensions/ql-vscode/src/common/commands.ts | 2 +- extensions/ql-vscode/src/databases/local-databases-ui.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index ea992200473..56d3bf99a87 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -840,7 +840,7 @@ "title": "CodeQL: Choose Database from Folder" }, { - "command": "codeQL.chooseMultipleDatabaseFolder", + "command": "codeQL.chooseDatabaseFoldersParent", "title": "CodeQL: Choose Folder to import all databases contained in it" }, { diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index 7f488c1ca89..302ca6fe0a9 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -211,7 +211,7 @@ export type LanguageSelectionCommands = { export type LocalDatabasesCommands = { // Command palette commands "codeQL.chooseDatabaseFolder": () => Promise; - "codeQL.chooseMultipleDatabaseFolder": () => Promise; + "codeQL.chooseDatabaseFoldersParent": () => Promise; "codeQL.chooseDatabaseArchive": () => Promise; "codeQL.chooseDatabaseInternet": () => Promise; "codeQL.chooseDatabaseGithub": () => Promise; diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 6263475615f..b0484a57093 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -269,8 +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.chooseDatabaseFoldersParent": + this.handleChooseDatabaseFoldersParentFromPalette.bind(this), "codeQL.chooseDatabaseArchive": this.handleChooseDatabaseArchiveFromPalette.bind(this), "codeQL.chooseDatabaseInternet": @@ -363,7 +363,7 @@ export class DatabaseUI extends DisposableObject { ); } - private async handleChooseMultipleDatabaseFolderFromPalette(): Promise { + private async handleChooseDatabaseFoldersParentFromPalette(): Promise { return withProgress( async (progress) => { await this.chooseDatabasesParentFolder(progress); From a93bf1469b66bbbbb217cb61a7c05b831a443c79 Mon Sep 17 00:00:00 2001 From: reito Date: Thu, 14 Nov 2024 00:21:48 +0800 Subject: [PATCH 08/17] Apply suggestions from code review Co-authored-by: Andrew Eisenberg --- extensions/ql-vscode/package.json | 2 +- extensions/ql-vscode/src/databases/local-databases-ui.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 56d3bf99a87..5be8e558566 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -841,7 +841,7 @@ }, { "command": "codeQL.chooseDatabaseFoldersParent", - "title": "CodeQL: Choose Folder to import all databases contained in it" + "title": "CodeQL: Choose Parent Folder and import all databases directly contained in it" }, { "command": "codeQL.chooseDatabaseArchive", diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index b0484a57093..2f720bbc1de 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -1041,7 +1041,7 @@ export class DatabaseUI extends DisposableObject { void showAndLogErrorMessage( this.app.logger, `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).`, - { fullMessage: `Failed folders to import:\n${failures.join("\n")}` }, + { fullMessage: `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s). Failed folders to import:\n ${failures.join("\n ")}` }, ); } else if (databases.length === 0) { void showAndLogErrorMessage( From 8170c46042138b57c2c29feab9c3996c5f88040a Mon Sep 17 00:00:00 2001 From: reito Date: Thu, 14 Nov 2024 00:43:32 +0800 Subject: [PATCH 09/17] feat: extract common logic --- .../src/databases/local-databases-ui.ts | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 2f720bbc1de..a28a02a18a1 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -972,18 +972,14 @@ export class DatabaseUI extends DisposableObject { } /** - * Ask the user for a database directory. Returns the chosen database, or `undefined` if the - * operation was canceled. + * Import database from uri. Returns the imported database, or `undefined` if the + * operation was unsuccessful. */ - private async chooseAndSetDatabase( + private async importDatabase( + uri: Uri, byFolder: boolean, progress: ProgressCallback, ): Promise { - 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 @@ -1001,6 +997,22 @@ export class DatabaseUI extends DisposableObject { } } + /** + * Ask the user for a database directory. Returns the chosen database, or `undefined` if the + * operation was canceled. + */ + private async chooseAndSetDatabase( + byFolder: boolean, + progress: ProgressCallback, + ): Promise { + const uri = await chooseDatabaseDir(byFolder); + if (!uri) { + return undefined; + } + + return await this.importDatabase(uri, byFolder, progress); + } + /** * Ask the user for a parent directory that contains all databases. * Returns all valid databases, or `undefined` if the operation was canceled. @@ -1024,16 +1036,28 @@ export class DatabaseUI extends DisposableObject { message: `Importing ${entry[0]}`, }); - 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", - }); + const subProgress: ProgressCallback = (p) => { + progress({ + step: index + 1, + maxStep: entries.length, + message: `Importing ${entry[0]} (${p.step}/${p.maxStep}): ${p.message}`, + }); + }; + + try { + const fixedUri = await this.fixDbUri(Uri.joinPath(uri, entry[0])); + const database = await this.importDatabase( + fixedUri, + entry[1] === FileType.Directory, + subProgress, + ); + if (database) { databases.push(database); - } catch (e) { + } else { failures.push(entry[0]); } + } catch (e) { + failures.push(entry[0]); } } @@ -1041,7 +1065,9 @@ export class DatabaseUI extends DisposableObject { void showAndLogErrorMessage( this.app.logger, `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).`, - { fullMessage: `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s). Failed folders to import:\n ${failures.join("\n ")}` }, + { + fullMessage: `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s). Failed folders to import:\n - ${failures.join("\n - ")}`, + }, ); } else if (databases.length === 0) { void showAndLogErrorMessage( From 5d4f75b72e4425ada2da5be106c07fe0cc7af4b9 Mon Sep 17 00:00:00 2001 From: reito Date: Thu, 14 Nov 2024 00:59:02 +0800 Subject: [PATCH 10/17] fix: testproj need to be with a dot --- .../src/databases/local-databases-ui.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index a28a02a18a1..dfff074f661 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -980,14 +980,14 @@ export class DatabaseUI extends DisposableObject { byFolder: boolean, progress: ProgressCallback, ): Promise { - if (byFolder && !uri.fsPath.endsWith("testproj")) { + 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. + // 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( @@ -1028,6 +1028,7 @@ export class DatabaseUI extends DisposableObject { const databases: DatabaseItem[] = []; const failures: string[] = []; const entries = await workspace.fs.readDirectory(uri); + const validFileTypes = [FileType.File, FileType.Directory]; for (const [index, entry] of entries.entries()) { progress({ @@ -1044,10 +1045,19 @@ export class DatabaseUI extends DisposableObject { }); }; + if (!validFileTypes.includes(entry[1])) { + void this.app.logger.log( + `Skip import ${entry}, invalid FileType: ${entry[1]}`, + ); + continue; + } + try { - const fixedUri = await this.fixDbUri(Uri.joinPath(uri, entry[0])); + const databaseUri = Uri.joinPath(uri, entry[0]); + void this.app.logger.log(`Importing from ${databaseUri}`); + const database = await this.importDatabase( - fixedUri, + databaseUri, entry[1] === FileType.Directory, subProgress, ); @@ -1056,7 +1066,7 @@ export class DatabaseUI extends DisposableObject { } else { failures.push(entry[0]); } - } catch (e) { + } catch { failures.push(entry[0]); } } From 1f6a7afffa6ffdaa23ef4b6b84cf662cf8c8e6e3 Mon Sep 17 00:00:00 2001 From: reito Date: Thu, 14 Nov 2024 01:02:48 +0800 Subject: [PATCH 11/17] fix: unify descriptions. --- extensions/ql-vscode/src/databases/local-databases-ui.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index dfff074f661..7aa1aab3824 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -369,7 +369,7 @@ export class DatabaseUI extends DisposableObject { await this.chooseDatabasesParentFolder(progress); }, { - title: "Choose a Folder contains all Database Folders", + title: "Choose a Parent Folder contains all Databases to import", }, ); } @@ -1076,7 +1076,7 @@ export class DatabaseUI extends DisposableObject { this.app.logger, `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).`, { - fullMessage: `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s). Failed folders to import:\n - ${failures.join("\n - ")}`, + fullMessage: `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).\nFailed databases to import:\n - ${failures.join("\n - ")}`, }, ); } else if (databases.length === 0) { From 1b007c25860ef7e1e02a007f69d2096b408cd420 Mon Sep 17 00:00:00 2001 From: reito Date: Tue, 19 Nov 2024 23:44:53 +0800 Subject: [PATCH 12/17] Apply suggestions from code review Co-authored-by: Andrew Eisenberg --- extensions/ql-vscode/package.json | 2 +- .../ql-vscode/src/databases/local-databases-ui.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 5be8e558566..c821c31065b 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -841,7 +841,7 @@ }, { "command": "codeQL.chooseDatabaseFoldersParent", - "title": "CodeQL: Choose Parent Folder and import all databases directly contained in it" + "title": "CodeQL: Import All Databases Directly Contained in a Parent Folder" }, { "command": "codeQL.chooseDatabaseArchive", diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 7aa1aab3824..5a3d5995ee1 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -369,7 +369,7 @@ export class DatabaseUI extends DisposableObject { await this.chooseDatabasesParentFolder(progress); }, { - title: "Choose a Parent Folder contains all Databases to import", + title: "Importing all databases contained in parent folder", }, ); } @@ -973,7 +973,7 @@ export class DatabaseUI extends DisposableObject { /** * Import database from uri. Returns the imported database, or `undefined` if the - * operation was unsuccessful. + * operation was unsuccessful or canceled. */ private async importDatabase( uri: Uri, @@ -1041,13 +1041,13 @@ export class DatabaseUI extends DisposableObject { progress({ step: index + 1, maxStep: entries.length, - message: `Importing ${entry[0]} (${p.step}/${p.maxStep}): ${p.message}`, + message: `Importing '${entry[0]}' (${p.step}/${p.maxStep}): ${p.message}`, }); }; if (!validFileTypes.includes(entry[1])) { void this.app.logger.log( - `Skip import ${entry}, invalid FileType: ${entry[1]}`, + `Skipping import for '${entry}', invalid file type: ${entry[1]}`, ); continue; } @@ -1066,8 +1066,8 @@ export class DatabaseUI extends DisposableObject { } else { failures.push(entry[0]); } - } catch { - failures.push(entry[0]); + } catch (e) { + failures.push(`${entry[0]}: ${toErrorMessag(e)}`); } } @@ -1076,7 +1076,7 @@ export class DatabaseUI extends DisposableObject { this.app.logger, `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).`, { - fullMessage: `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).\nFailed databases to import:\n - ${failures.join("\n - ")}`, + fullMessage: `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).\nFailed databases:\n - ${failures.join("\n - ")}`, }, ); } else if (databases.length === 0) { From 17a6076732a21c2e60c7404bade0a1d400762fad Mon Sep 17 00:00:00 2001 From: reito Date: Tue, 19 Nov 2024 23:46:46 +0800 Subject: [PATCH 13/17] fix: get error message --- extensions/ql-vscode/src/databases/local-databases-ui.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 5a3d5995ee1..0a53c985b75 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -1067,7 +1067,7 @@ export class DatabaseUI extends DisposableObject { failures.push(entry[0]); } } catch (e) { - failures.push(`${entry[0]}: ${toErrorMessag(e)}`); + failures.push(`${entry[0]}: ${getErrorMessage(e)}`); } } From 9f1fd2c8af9524e2d370db7c1bd45f2ca28c243a Mon Sep 17 00:00:00 2001 From: reito Date: Tue, 19 Nov 2024 23:49:38 +0800 Subject: [PATCH 14/17] fix: trim error --- extensions/ql-vscode/src/databases/local-databases-ui.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 0a53c985b75..102ffc46285 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -1067,7 +1067,7 @@ export class DatabaseUI extends DisposableObject { failures.push(entry[0]); } } catch (e) { - failures.push(`${entry[0]}: ${getErrorMessage(e)}`); + failures.push(`${entry[0]}: ${getErrorMessage(e)}`.trim()); } } From 5b854bc1cd3bab6e77395a3579b1cfd7c195de9a Mon Sep 17 00:00:00 2001 From: reito Date: Tue, 19 Nov 2024 23:53:37 +0800 Subject: [PATCH 15/17] fix: step message --- extensions/ql-vscode/src/databases/local-databases-ui.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 102ffc46285..975826e5522 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -1034,14 +1034,14 @@ export class DatabaseUI extends DisposableObject { progress({ step: index + 1, maxStep: entries.length, - message: `Importing ${entry[0]}`, + message: `Importing '${entry[0]}'`, }); const subProgress: ProgressCallback = (p) => { progress({ step: index + 1, maxStep: entries.length, - message: `Importing '${entry[0]}' (${p.step}/${p.maxStep}): ${p.message}`, + message: `Importing '${entry[0]}': (${p.step}/${p.maxStep}) ${p.message}`, }); }; From 8b3add82b197e44076f438849c0f720b6560842c Mon Sep 17 00:00:00 2001 From: reito Date: Tue, 19 Nov 2024 23:58:18 +0800 Subject: [PATCH 16/17] fix: remove title of importing status --- .../ql-vscode/src/databases/local-databases-ui.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 975826e5522..cfc53be40b6 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -364,14 +364,9 @@ export class DatabaseUI extends DisposableObject { } private async handleChooseDatabaseFoldersParentFromPalette(): Promise { - return withProgress( - async (progress) => { - await this.chooseDatabasesParentFolder(progress); - }, - { - title: "Importing all databases contained in parent folder", - }, - ); + return withProgress(async (progress) => { + await this.chooseDatabasesParentFolder(progress); + }); } private async handleSetDefaultTourDatabase(): Promise { From e7e95e2511448eb7f5547aa28f01c40e6f8e90e6 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Tue, 19 Nov 2024 08:51:31 -0800 Subject: [PATCH 17/17] Clarify CHANGELOG.md --- extensions/ql-vscode/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 1ccd70b9644..4e93bfd5531 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,7 +2,7 @@ ## [UNRELEASED] -- Add a palette command that allows importing of all databases inside of a parent folder. [3797](https://github.com/github/vscode-codeql/pull/3797) +- Add a palette command that allows importing all databases directly inside of a parent folder. [3797](https://github.com/github/vscode-codeql/pull/3797) ## 1.16.1 - 6 November 2024