diff --git a/extensions/ql-vscode/src/databaseFetcher.ts b/extensions/ql-vscode/src/databaseFetcher.ts index a0d0e785079..3574c6cc6ff 100644 --- a/extensions/ql-vscode/src/databaseFetcher.ts +++ b/extensions/ql-vscode/src/databaseFetcher.ts @@ -4,7 +4,7 @@ import { Uri, ProgressOptions, ProgressLocation, commands, window } from "vscode import * as fs from "fs-extra"; import * as path from "path"; import { DatabaseManager, DatabaseItem } from "./databases"; -import { ProgressCallback, showAndLogErrorMessage, withProgress } from "./helpers"; +import { ProgressCallback, showAndLogErrorMessage, withProgress, showAndLogInformationMessage } from "./helpers"; /** * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. @@ -12,7 +12,7 @@ import { ProgressCallback, showAndLogErrorMessage, withProgress } from "./helper * @param databasesManager the DatabaseManager * @param storagePath where to store the unzipped database. */ -export default async function promptFetchDatabase(databasesManager: DatabaseManager, storagePath: string): Promise { +export async function promptImportInternetDatabase(databasesManager: DatabaseManager, storagePath: string): Promise { let item: DatabaseItem | undefined = undefined; try { @@ -20,7 +20,7 @@ export default async function promptFetchDatabase(databasesManager: DatabaseMana prompt: 'Enter URL of zipfile of database to download' }); if (databaseUrl) { - validateUrl(databaseUrl); + validateHttpsUrl(databaseUrl); const progressOptions: ProgressOptions = { location: ProgressLocation.Notification, @@ -30,6 +30,7 @@ export default async function promptFetchDatabase(databasesManager: DatabaseMana await withProgress(progressOptions, async progress => (item = await databaseArchiveFetcher(databaseUrl, databasesManager, storagePath, progress))); commands.executeCommand('codeQLDatabases.focus'); } + showAndLogInformationMessage('Database downloaded and imported successfully.'); } catch (e) { showAndLogErrorMessage(e.message); } @@ -37,6 +38,33 @@ export default async function promptFetchDatabase(databasesManager: DatabaseMana return item; } + +/** + * Imports a database from a local archive. + * + * @param databaseUrl the file url of the archive to import + * @param databasesManager the DatabaseManager + * @param storagePath where to store the unzipped database. + */ +export async function importArchiveDatabase(databaseUrl: string, databasesManager: DatabaseManager, storagePath: string): Promise { + let item: DatabaseItem | undefined = undefined; + try { + const progressOptions: ProgressOptions = { + location: ProgressLocation.Notification, + title: 'Importing database from archive', + cancellable: false, + }; + await withProgress(progressOptions, async progress => (item = await databaseArchiveFetcher(databaseUrl, databasesManager, storagePath, progress))); + commands.executeCommand('codeQLDatabases.focus'); + + showAndLogInformationMessage('Database unzipped and imported successfully.'); + } catch (e) { + showAndLogErrorMessage(e.message); + } + return item; +} + + /** * Fetches an archive database. The database might be on the internet * or in the local filesystem. @@ -46,7 +74,7 @@ export default async function promptFetchDatabase(databasesManager: DatabaseMana * @param storagePath where to store the unzipped database. * @param progressCallback optional callback to send progress messages to */ -export async function databaseArchiveFetcher( +async function databaseArchiveFetcher( databaseUrl: string, databasesManager: DatabaseManager, storagePath: string, @@ -54,7 +82,7 @@ export async function databaseArchiveFetcher( ): Promise { progressCallback?.({ maxStep: 3, - message: 'Downloading database', + message: 'Getting database', step: 1 }); if (!storagePath) { @@ -75,6 +103,7 @@ export async function databaseArchiveFetcher( step: 3 }); + // find the path to the database. The actual database might be in a sub-folder const dbPath = await findDirWithFile(unzipPath, '.dbinfo', 'codeql-database.yml'); if (dbPath) { const item = await databasesManager.openDatabase(Uri.parse(dbPath)); @@ -106,7 +135,7 @@ async function getStorageFolder(storagePath: string, urlStr: string) { } -function validateUrl(databaseUrl: string) { +function validateHttpsUrl(databaseUrl: string) { let uri; try { uri = Uri.parse(databaseUrl, true); diff --git a/extensions/ql-vscode/src/databases-ui.ts b/extensions/ql-vscode/src/databases-ui.ts index 5cd81486103..e35085bd000 100644 --- a/extensions/ql-vscode/src/databases-ui.ts +++ b/extensions/ql-vscode/src/databases-ui.ts @@ -8,7 +8,7 @@ import { logger } from './logging'; import { clearCacheInDatabase, UserCancellationException } from './run-queries'; import * as qsClient from './queryserver-client'; import { upgradeDatabase } from './upgrades'; -import promptFetchDatabase, { databaseArchiveFetcher } from './databaseFetcher'; +import { importArchiveDatabase, promptImportInternetDatabase } from './databaseFetcher'; type ThemableIconPath = { light: string; dark: string } | string; @@ -212,7 +212,7 @@ export class DatabaseUI extends DisposableObject { } private handleChooseDatabaseInternet = async (): Promise => { - return await promptFetchDatabase(this.databaseManager, this.storagePath); + return await promptImportInternetDatabase(this.databaseManager, this.storagePath); } private handleSortByName = async () => { @@ -292,7 +292,7 @@ export class DatabaseUI extends DisposableObject { private handleSetCurrentDatabase = async (uri: Uri): Promise => { // Assume user has selected an archive if the file has a .zip extension if (uri.path.endsWith('.zip')) { - return await databaseArchiveFetcher(uri.toString(), this.databaseManager, this.storagePath); + return await importArchiveDatabase(uri.toString(), this.databaseManager, this.storagePath); } return await this.setCurrentDatabase(uri); @@ -366,7 +366,7 @@ export class DatabaseUI extends DisposableObject { else { // we are selecting a database archive. Must unzip into a workspace-controlled area // before importing. - return await databaseArchiveFetcher(uri.toString(), this.databaseManager, this.storagePath); + return await importArchiveDatabase(uri.toString(), this.databaseManager, this.storagePath); } } } diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index a19ce6eb211..152b2a0ef8c 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -20,7 +20,7 @@ import { displayQuickQuery } from './quick-query'; import { compileAndRunQueryAgainstDatabase, tmpDirDisposal, UserCancellationException } from './run-queries'; import { QLTestAdapterFactory } from './test-adapter'; import { TestUIService } from './test-ui'; -import promptFetchDatabase from './databaseFetcher'; +import { promptImportInternetDatabase } from './databaseFetcher'; /** * extension.ts @@ -335,7 +335,7 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu await qs.restartQueryServer(); helpers.showAndLogInformationMessage('CodeQL Query Server restarted.', { outputLogger: queryServerLogger }); })); - ctx.subscriptions.push(commands.registerCommand('codeQL.downloadDatabase', () => promptFetchDatabase(dbm, getContextStoragePath(ctx)))); + ctx.subscriptions.push(commands.registerCommand('codeQL.downloadDatabase', () => promptImportInternetDatabase(dbm, getContextStoragePath(ctx)))); ctx.subscriptions.push(client.start());