Skip to content

Commit 0badbf1

Browse files
Integrated CLI database unbundle command for archive download
Co-authored by: Marc Jaramillo mnj.webdeveloper@gmail.comm Co-authored by: Musab Guma'a mgsium@github.com
1 parent b5cdd83 commit 0badbf1

5 files changed

Lines changed: 48 additions & 11 deletions

File tree

extensions/ql-vscode/src/cli.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,15 @@ export class CodeQLCliServer implements Disposable {
600600
return await this.runJsonCodeQlCliCommand<BQRSInfo>(['bqrs', 'info'], subcommandArgs, 'Reading bqrs header');
601601
}
602602

603+
async databaseUnbundle(archivePath: string, target: string, name?: string): Promise<string> {
604+
const subcommandArgs = [];
605+
if (target) subcommandArgs.push('--target', target);
606+
if (name) subcommandArgs.push('--name', name);
607+
subcommandArgs.push(archivePath);
608+
609+
return await this.runCodeQlCliCommand(['database', 'unbundle'], subcommandArgs, `Extracting ${archivePath} to directory ${target}`);
610+
}
611+
603612
/**
604613
* Gets the results from a bqrs.
605614
* @param bqrsPath The path to the bqrs.

extensions/ql-vscode/src/databaseFetcher.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import fetch, { Response } from 'node-fetch';
2-
import * as unzipper from 'unzipper';
32
import { zip } from 'zip-a-folder';
43
import {
54
Uri,
65
CancellationToken,
76
commands,
87
window,
98
} from 'vscode';
9+
import {CodeQLCliServer} from './cli';
1010
import * as fs from 'fs-extra';
1111
import * as path from 'path';
1212

@@ -28,6 +28,7 @@ import { tmpDir } from './run-queries';
2828
* @param storagePath where to store the unzipped database.
2929
*/
3030
export async function promptImportInternetDatabase(
31+
cli: CodeQLCliServer,
3132
databaseManager: DatabaseManager,
3233
storagePath: string,
3334
progress: ProgressCallback,
@@ -43,6 +44,7 @@ export async function promptImportInternetDatabase(
4344
validateHttpsUrl(databaseUrl);
4445

4546
const item = await databaseArchiveFetcher(
47+
cli,
4648
databaseUrl,
4749
databaseManager,
4850
storagePath,
@@ -67,6 +69,7 @@ export async function promptImportInternetDatabase(
6769
* @param storagePath where to store the unzipped database.
6870
*/
6971
export async function promptImportLgtmDatabase(
72+
cli: CodeQLCliServer,
7073
databaseManager: DatabaseManager,
7174
storagePath: string,
7275
progress: ProgressCallback,
@@ -89,6 +92,7 @@ export async function promptImportLgtmDatabase(
8992
const databaseUrl = await convertToDatabaseUrl(lgtmUrl, progress);
9093
if (databaseUrl) {
9194
const item = await databaseArchiveFetcher(
95+
cli,
9296
databaseUrl,
9397
databaseManager,
9498
storagePath,
@@ -115,6 +119,7 @@ export async function promptImportLgtmDatabase(
115119
* @param storagePath where to store the unzipped database.
116120
*/
117121
export async function importArchiveDatabase(
122+
cli: CodeQLCliServer,
118123
databaseUrl: string,
119124
databaseManager: DatabaseManager,
120125
storagePath: string,
@@ -123,6 +128,7 @@ export async function importArchiveDatabase(
123128
): Promise<DatabaseItem | undefined> {
124129
try {
125130
const item = await databaseArchiveFetcher(
131+
cli,
126132
databaseUrl,
127133
databaseManager,
128134
storagePath,
@@ -155,6 +161,7 @@ export async function importArchiveDatabase(
155161
* @param token cancellation token
156162
*/
157163
async function databaseArchiveFetcher(
164+
cli: CodeQLCliServer,
158165
databaseUrl: string,
159166
databaseManager: DatabaseManager,
160167
storagePath: string,
@@ -173,9 +180,9 @@ async function databaseArchiveFetcher(
173180
const unzipPath = await getStorageFolder(storagePath, databaseUrl);
174181

175182
if (isFile(databaseUrl)) {
176-
await readAndUnzip(databaseUrl, unzipPath, progress);
183+
await readAndUnzip(cli, databaseUrl, unzipPath, progress);
177184
} else {
178-
await fetchAndUnzip(databaseUrl, unzipPath, progress);
185+
await fetchAndUnzip(cli, databaseUrl, unzipPath, progress);
179186
}
180187

181188
progress({
@@ -247,6 +254,7 @@ function validateHttpsUrl(databaseUrl: string) {
247254
}
248255

249256
async function readAndUnzip(
257+
cli: CodeQLCliServer,
250258
zipUrl: string,
251259
unzipPath: string,
252260
progress?: ProgressCallback
@@ -262,11 +270,11 @@ async function readAndUnzip(
262270
// Must get the zip central directory since streaming the
263271
// zip contents may not have correct local file headers.
264272
// Instead, we can only rely on the central directory.
265-
const directory = await unzipper.Open.file(zipFile);
266-
await directory.extract({ path: unzipPath });
273+
await cli.databaseUnbundle(zipFile, unzipPath);
267274
}
268275

269276
async function fetchAndUnzip(
277+
cli: CodeQLCliServer,
270278
databaseUrl: string,
271279
unzipPath: string,
272280
progress?: ProgressCallback
@@ -298,7 +306,7 @@ async function fetchAndUnzip(
298306
.on('error', reject)
299307
);
300308

301-
await readAndUnzip(Uri.file(archivePath).toString(true), unzipPath, progress);
309+
await readAndUnzip(cli, Uri.file(archivePath).toString(true), unzipPath, progress);
302310

303311
// remove archivePath eagerly since these archives can be large.
304312
await fs.remove(archivePath);

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,11 @@ export class DatabaseUI extends DisposableObject {
454454
): Promise<
455455
DatabaseItem | undefined
456456
> => {
457+
if (!this.queryServer) {
458+
throw new Error('Query server not started');
459+
}
457460
return await promptImportInternetDatabase(
461+
this.queryServer.cliServer,
458462
this.databaseManager,
459463
this.storagePath,
460464
progress,
@@ -466,7 +470,11 @@ export class DatabaseUI extends DisposableObject {
466470
progress: ProgressCallback,
467471
token: CancellationToken
468472
): Promise<DatabaseItem | undefined> => {
473+
if (!this.queryServer) {
474+
throw new Error('Query server not started');
475+
}
469476
return await promptImportLgtmDatabase(
477+
this.queryServer.cliServer,
470478
this.databaseManager,
471479
this.storagePath,
472480
progress,
@@ -572,10 +580,15 @@ export class DatabaseUI extends DisposableObject {
572580
token: CancellationToken,
573581
uri: Uri,
574582
): Promise<void> => {
583+
if (!this.queryServer) {
584+
throw new Error('Query server not started');
585+
}
586+
575587
try {
576588
// Assume user has selected an archive if the file has a .zip extension
577589
if (uri.path.endsWith('.zip')) {
578590
await importArchiveDatabase(
591+
this.queryServer.cliServer,
579592
uri.toString(true),
580593
this.databaseManager,
581594
this.storagePath,
@@ -695,8 +708,11 @@ export class DatabaseUI extends DisposableObject {
695708
progress: ProgressCallback,
696709
token: CancellationToken,
697710
): Promise<DatabaseItem | undefined> {
698-
const uri = await chooseDatabaseDir(byFolder);
711+
if (!this.queryServer) {
712+
throw new Error('Query server not started');
713+
}
699714

715+
const uri = await chooseDatabaseDir(byFolder);
700716
if (!uri) {
701717
return undefined;
702718
}
@@ -709,6 +725,7 @@ export class DatabaseUI extends DisposableObject {
709725
// we are selecting a database archive. Must unzip into a workspace-controlled area
710726
// before importing.
711727
return await importArchiveDatabase(
728+
this.queryServer.cliServer,
712729
uri.toString(true),
713730
this.databaseManager,
714731
this.storagePath,

extensions/ql-vscode/src/vscode-tests/cli-integration/databases.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { expect } from 'chai';
55
import { extensions, CancellationToken, Uri, window } from 'vscode';
66

77
import { CodeQLExtensionInterface } from '../../extension';
8+
import { CodeQLCliServer } from '../../cli';
89
import { DatabaseManager } from '../../databases';
910
import { promptImportLgtmDatabase, importArchiveDatabase, promptImportInternetDatabase } from '../../databaseFetcher';
1011
import { ProgressCallback } from '../../commandRunner';
@@ -17,7 +18,8 @@ describe('Databases', function() {
1718
this.timeout(60000);
1819

1920
const LGTM_URL = 'https://lgtm.com/projects/g/aeisenberg/angular-bind-notifier/';
20-
21+
22+
let cli: CodeQLCliServer;
2123
let databaseManager: DatabaseManager;
2224
let sandbox: sinon.SinonSandbox;
2325
let inputBoxStub: sinon.SinonStub;
@@ -53,7 +55,7 @@ describe('Databases', function() {
5355
it('should add a database from a folder', async () => {
5456
const progressCallback = sandbox.spy() as ProgressCallback;
5557
const uri = Uri.file(dbLoc);
56-
let dbItem = await importArchiveDatabase(uri.toString(true), databaseManager, storagePath, progressCallback, {} as CancellationToken);
58+
let dbItem = await importArchiveDatabase(cli, uri.toString(true), databaseManager, storagePath, progressCallback, {} as CancellationToken);
5759
expect(dbItem).to.be.eq(databaseManager.currentDatabaseItem);
5860
expect(dbItem).to.be.eq(databaseManager.databaseItems[0]);
5961
expect(dbItem).not.to.be.undefined;
@@ -64,7 +66,7 @@ describe('Databases', function() {
6466

6567
it('should add a database from lgtm with only one language', async () => {
6668
inputBoxStub.resolves(LGTM_URL);
67-
let dbItem = await promptImportLgtmDatabase(databaseManager, storagePath, progressCallback, {} as CancellationToken);
69+
let dbItem = await promptImportLgtmDatabase(cli, databaseManager, storagePath, progressCallback, {} as CancellationToken);
6870
expect(dbItem).not.to.be.undefined;
6971
dbItem = dbItem!;
7072
expect(dbItem.name).to.eq('aeisenberg_angular-bind-notifier_106179a');
@@ -74,7 +76,7 @@ describe('Databases', function() {
7476
it('should add a database from a url', async () => {
7577
inputBoxStub.resolves(DB_URL);
7678

77-
let dbItem = await promptImportInternetDatabase(databaseManager, storagePath, progressCallback, {} as CancellationToken);
79+
let dbItem = await promptImportInternetDatabase(cli, databaseManager, storagePath, progressCallback, {} as CancellationToken);
7880
expect(dbItem).not.to.be.undefined;
7981
dbItem = dbItem!;
8082
expect(dbItem.name).to.eq('db');

extensions/ql-vscode/src/vscode-tests/cli-integration/queries.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ describe('Queries', function() {
6464
// Add a database
6565
const uri = Uri.file(dbLoc);
6666
const maybeDbItem = await importArchiveDatabase(
67+
cli,
6768
uri.toString(true),
6869
databaseManager,
6970
storagePath,

0 commit comments

Comments
 (0)