Skip to content

Commit 7f34fca

Browse files
committed
Update commands for importing databases
1. Add commands for importing an archive, folder, or from internet 2. Add new icons for all of them 3. Ensure that each command can only retrieve databases through a single mechanism
1 parent e42a39e commit 7f34fca

File tree

9 files changed

+131
-30
lines changed

9 files changed

+131
-30
lines changed
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 12 additions & 0 deletions
Loading

extensions/ql-vscode/package.json

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
"onView:codeQLQueryHistory",
2828
"onView:test-explorer",
2929
"onCommand:codeQL.checkForUpdatesToCLI",
30-
"onCommand:codeQL.chooseDatabase",
30+
"onCommand:codeQL.chooseDatabaseFolder",
31+
"onCommand:codeQL.chooseDatabaseArchive",
32+
"onCommand:codeQL.chooseDatabaseInternet",
3133
"onCommand:codeQL.setCurrentDatabase",
3234
"onCommand:codeQL.downloadDatabase",
3335
"onCommand:codeQLDatabases.chooseDatabase",
@@ -173,11 +175,27 @@
173175
"title": "CodeQL: Quick Query"
174176
},
175177
{
176-
"command": "codeQL.chooseDatabase",
177-
"title": "CodeQL: Choose Database",
178+
"command": "codeQL.chooseDatabaseFolder",
179+
"title": "Choose Database from Folder",
178180
"icon": {
179-
"light": "media/light/plus.svg",
180-
"dark": "media/dark/plus.svg"
181+
"light": "media/light/folder-opened-plus.svg",
182+
"dark": "media/dark/folder-opened-plus.svg"
183+
}
184+
},
185+
{
186+
"command": "codeQL.chooseDatabaseArchive",
187+
"title": "Choose Database from Archive",
188+
"icon": {
189+
"light": "media/light/archive-plus.svg",
190+
"dark": "media/dark/archive-plus.svg"
191+
}
192+
},
193+
{
194+
"command": "codeQL.chooseDatabaseInternet",
195+
"title": "Download database",
196+
"icon": {
197+
"light": "media/light/cloud-download.svg",
198+
"dark": "media/dark/cloud-download.svg"
181199
}
182200
},
183201
{
@@ -294,7 +312,17 @@
294312
"group": "navigation"
295313
},
296314
{
297-
"command": "codeQL.chooseDatabase",
315+
"command": "codeQL.chooseDatabaseFolder",
316+
"when": "view == codeQLDatabases",
317+
"group": "navigation"
318+
},
319+
{
320+
"command": "codeQL.chooseDatabaseArchive",
321+
"when": "view == codeQLDatabases",
322+
"group": "navigation"
323+
},
324+
{
325+
"command": "codeQL.chooseDatabaseInternet",
298326
"when": "view == codeQLDatabases",
299327
"group": "navigation"
300328
}
@@ -365,7 +393,7 @@
365393
{
366394
"command": "codeQL.setCurrentDatabase",
367395
"group": "9_qlCommands",
368-
"when": "resourceScheme == codeql-zip-archive || explorerResourceIsFolder"
396+
"when": "resourceScheme == codeql-zip-archive || explorerResourceIsFolder || resourceExtname == .zip"
369397
},
370398
{
371399
"command": "codeQL.runQuery",

extensions/ql-vscode/src/databaseFetcher.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,19 @@ import * as path from "path";
66
import { DatabaseManager, DatabaseItem } from "./databases";
77
import { ProgressCallback, showAndLogErrorMessage, withProgress } from "./helpers";
88

9-
export default async function promptFetchDatabase(dbm: DatabaseManager, storagePath: string) {
9+
/**
10+
* Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file.
11+
*
12+
* @param databasesManager the DatabaseManager
13+
* @param storagePath where to store the unzipped database.
14+
*/
15+
export default async function promptFetchDatabase(databasesManager: DatabaseManager, storagePath: string): Promise<DatabaseItem | undefined> {
16+
let item: DatabaseItem | undefined = undefined;
17+
1018
try {
1119
const databaseUrl = await window.showInputBox({
1220
prompt: 'Enter URL of zipfile of database to download'
1321
});
14-
1522
if (databaseUrl) {
1623
validateUrl(databaseUrl);
1724

@@ -20,15 +27,26 @@ export default async function promptFetchDatabase(dbm: DatabaseManager, storageP
2027
title: 'Adding database from URL',
2128
cancellable: false,
2229
};
23-
await withProgress(progressOptions, async progress => await databaseFetcher(databaseUrl, dbm, storagePath, progress));
30+
await withProgress(progressOptions, async progress => (item = await databaseArchiveFetcher(databaseUrl, databasesManager, storagePath, progress)));
2431
commands.executeCommand('codeQLDatabases.focus');
2532
}
2633
} catch (e) {
2734
showAndLogErrorMessage(e.message);
2835
}
36+
37+
return item;
2938
}
3039

31-
export async function databaseFetcher(
40+
/**
41+
* Fetches an archive database. The database might be on the internet
42+
* or in the local filesystem.
43+
*
44+
* @param databaseUrl URL from which to grab the database
45+
* @param databasesManager the DatabaseManager
46+
* @param storagePath where to store the unzipped database.
47+
* @param progressCallback optional callback to send progress messages to
48+
*/
49+
export async function databaseArchiveFetcher(
3250
databaseUrl: string,
3351
databasesManager: DatabaseManager,
3452
storagePath: string,
@@ -59,7 +77,6 @@ export async function databaseFetcher(
5977

6078
const dbPath = await findDirWithFile(unzipPath, '.dbinfo', 'codeql-database.yml');
6179
if (dbPath) {
62-
// might need to upgrade before importing...
6380
const item = await databasesManager.openDatabase(Uri.parse(dbPath));
6481
databasesManager.setCurrentDatabaseItem(item);
6582
return item;
@@ -104,8 +121,7 @@ function validateUrl(databaseUrl: string) {
104121

105122
async function readAndUnzip(databaseUrl: string, unzipPath: string) {
106123
const unzipStream = unzipper.Extract({
107-
path: unzipPath,
108-
verbose: true
124+
path: unzipPath
109125
});
110126

111127
await new Promise((resolve, reject) => {

extensions/ql-vscode/src/databases-ui.ts

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { logger } from './logging';
88
import { clearCacheInDatabase, UserCancellationException } from './run-queries';
99
import * as qsClient from './queryserver-client';
1010
import { upgradeDatabase } from './upgrades';
11-
import { databaseFetcher } from './databaseFetcher';
11+
import promptFetchDatabase, { databaseArchiveFetcher } from './databaseFetcher';
1212

1313
type ThemableIconPath = { light: string; dark: string } | string;
1414

@@ -148,13 +148,13 @@ function getFirst(list: Uri[] | undefined): Uri | undefined {
148148
* XXX: no validation is done other than checking the directory name
149149
* to make sure it really is a database directory.
150150
*/
151-
async function chooseDatabaseDir(): Promise<Uri | undefined> {
151+
async function chooseDatabaseDir(byFolder: boolean): Promise<Uri | undefined> {
152152
const chosen = await window.showOpenDialog({
153-
openLabel: 'Choose Database folder or archive',
154-
canSelectFiles: true,
155-
canSelectFolders: true,
153+
openLabel: byFolder ? 'Choose Database folder' : 'Choose Database archive',
154+
canSelectFiles: !byFolder,
155+
canSelectFolders: byFolder,
156156
canSelectMany: false,
157-
157+
filters: byFolder ? {} : { Archives: ['zip'] }
158158
});
159159
return getFirst(chosen);
160160
}
@@ -174,7 +174,9 @@ export class DatabaseUI extends DisposableObject {
174174
this.treeDataProvider = this.push(new DatabaseTreeDataProvider(ctx, databaseManager));
175175
this.push(window.createTreeView('codeQLDatabases', { treeDataProvider: this.treeDataProvider }));
176176

177-
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabase', this.handleChooseDatabase));
177+
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseFolder', this.handleChooseDatabaseFolder));
178+
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseArchive', this.handleChooseDatabaseArchive));
179+
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabaseInternet', this.handleChooseDatabaseInternet));
178180
ctx.subscriptions.push(commands.registerCommand('codeQL.setCurrentDatabase', this.handleSetCurrentDatabase));
179181
ctx.subscriptions.push(commands.registerCommand('codeQL.upgradeCurrentDatabase', this.handleUpgradeCurrentDatabase));
180182
ctx.subscriptions.push(commands.registerCommand('codeQL.clearCache', this.handleClearCache));
@@ -191,15 +193,28 @@ export class DatabaseUI extends DisposableObject {
191193
await this.databaseManager.setCurrentDatabaseItem(databaseItem);
192194
}
193195

194-
private handleChooseDatabase = async (): Promise<DatabaseItem | undefined> => {
196+
private handleChooseDatabaseFolder = async (): Promise<DatabaseItem | undefined> => {
197+
try {
198+
return await this.chooseAndSetDatabase(true);
199+
} catch (e) {
200+
showAndLogErrorMessage(e.message);
201+
return undefined;
202+
}
203+
}
204+
205+
private handleChooseDatabaseArchive = async (): Promise<DatabaseItem | undefined> => {
195206
try {
196-
return await this.chooseAndSetDatabase();
207+
return await this.chooseAndSetDatabase(false);
197208
} catch (e) {
198209
showAndLogErrorMessage(e.message);
199210
return undefined;
200211
}
201212
}
202213

214+
private handleChooseDatabaseInternet = async (): Promise<DatabaseItem | undefined> => {
215+
return await promptFetchDatabase(this.databaseManager, this.storagePath);
216+
}
217+
203218
private handleSortByName = async () => {
204219
if (this.treeDataProvider.sortOrder === SortOrder.NameAsc) {
205220
this.treeDataProvider.sortOrder = SortOrder.NameDesc;
@@ -275,6 +290,11 @@ export class DatabaseUI extends DisposableObject {
275290
}
276291

277292
private handleSetCurrentDatabase = async (uri: Uri): Promise<DatabaseItem | undefined> => {
293+
// Assume user has selected an archive if the file has a .zip extension
294+
if (uri.path.endsWith('.zip')) {
295+
return await databaseArchiveFetcher(uri.toString(), this.databaseManager, this.storagePath);
296+
}
297+
278298
return await this.setCurrentDatabase(uri);
279299
}
280300

@@ -312,7 +332,7 @@ export class DatabaseUI extends DisposableObject {
312332
*/
313333
public async getDatabaseItem(): Promise<DatabaseItem | undefined> {
314334
if (this.databaseManager.currentDatabaseItem === undefined) {
315-
await this.chooseAndSetDatabase();
335+
await this.chooseAndSetDatabase(false);
316336
}
317337

318338
return this.databaseManager.currentDatabaseItem;
@@ -332,17 +352,21 @@ export class DatabaseUI extends DisposableObject {
332352
* Ask the user for a database directory. Returns the chosen database, or `undefined` if the
333353
* operation was canceled.
334354
*/
335-
private async chooseAndSetDatabase(): Promise<DatabaseItem | undefined> {
336-
const uri = await chooseDatabaseDir();
355+
private async chooseAndSetDatabase(byFolder: boolean): Promise<DatabaseItem | undefined> {
356+
const uri = await chooseDatabaseDir(byFolder);
337357

338-
if (uri?.path.endsWith('.zip')) {
339-
return await databaseFetcher(uri.toString(), this.databaseManager, this.storagePath);
358+
if (!uri) {
359+
return undefined;
340360
}
341-
else if (uri !== undefined) {
361+
362+
if (byFolder) {
363+
// we are selecting a database folder
342364
return await this.setCurrentDatabase(uri);
343365
}
344366
else {
345-
return undefined;
367+
// we are selecting a database archive. Must unzip into a workspace-controlled area
368+
// before importing.
369+
return await databaseArchiveFetcher(uri.toString(), this.databaseManager, this.storagePath);
346370
}
347371
}
348372
}

0 commit comments

Comments
 (0)