Skip to content

Commit 1ab3dea

Browse files
authored
Merge pull request #3455 from github/koesie10/extension-managed-location
Fix incomplete removal of databases
2 parents 650b09c + 82741e9 commit 1ab3dea

7 files changed

Lines changed: 77 additions & 7 deletions

File tree

extensions/ql-vscode/src/databases/database-fetcher.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ async function databaseArchiveFetcher(
399399
nameOverride,
400400
{
401401
addSourceArchiveFolder,
402+
extensionManagedLocation: unzipPath,
402403
},
403404
);
404405
return item;

extensions/ql-vscode/src/databases/local-databases/database-item-impl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ export class DatabaseItemImpl implements DatabaseItem {
6666
return this.options.origin;
6767
}
6868

69+
public get extensionManagedLocation(): string | undefined {
70+
return this.options.extensionManagedLocation;
71+
}
72+
6973
public resolveSourceFile(uriStr: string | undefined): Uri {
7074
const sourceArchive = this.sourceArchive;
7175
const uri = uriStr ? Uri.parse(uriStr, true) : undefined;

extensions/ql-vscode/src/databases/local-databases/database-item.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ export interface DatabaseItem {
3131
*/
3232
readonly origin: DatabaseOrigin | undefined;
3333

34+
/**
35+
* The location of the base storage location as managed by the extension, or undefined
36+
* if unknown or not managed by the extension.
37+
*/
38+
readonly extensionManagedLocation: string | undefined;
39+
3440
/** If the database is invalid, describes why. */
3541
readonly error: Error | undefined;
3642

extensions/ql-vscode/src/databases/local-databases/database-manager.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ function eventFired<T>(
8282
}
8383

8484
type OpenDatabaseOptions = {
85+
/**
86+
* A location that is managed by the extension.
87+
*/
88+
extensionManagedLocation?: string;
8589
isTutorialDatabase?: boolean;
8690
/**
8791
* Whether to add a workspace folder containing the source archive to the workspace. Default is true.
@@ -141,6 +145,7 @@ export class DatabaseManager extends DisposableObject {
141145
makeSelected = true,
142146
displayName?: string,
143147
{
148+
extensionManagedLocation,
144149
isTutorialDatabase = false,
145150
addSourceArchiveFolder = addDatabaseSourceToWorkspace(),
146151
}: OpenDatabaseOptions = {},
@@ -149,6 +154,7 @@ export class DatabaseManager extends DisposableObject {
149154
uri,
150155
origin,
151156
displayName,
157+
extensionManagedLocation,
152158
);
153159

154160
return await this.addExistingDatabaseItem(
@@ -202,6 +208,7 @@ export class DatabaseManager extends DisposableObject {
202208
uri: vscode.Uri,
203209
origin: DatabaseOrigin | undefined,
204210
displayName: string | undefined,
211+
extensionManagedLocation?: string,
205212
): Promise<DatabaseItemImpl> {
206213
const contents = await DatabaseResolver.resolveDatabaseContents(uri);
207214
const fullOptions: FullDatabaseOptions = {
@@ -210,6 +217,7 @@ export class DatabaseManager extends DisposableObject {
210217
dateAdded: Date.now(),
211218
language: await this.getPrimaryLanguage(uri.fsPath),
212219
origin,
220+
extensionManagedLocation,
213221
};
214222
const databaseItem = new DatabaseItemImpl(uri, contents, fullOptions);
215223

@@ -370,6 +378,7 @@ export class DatabaseManager extends DisposableObject {
370378
let dateAdded = undefined;
371379
let language = undefined;
372380
let origin = undefined;
381+
let extensionManagedLocation = undefined;
373382
if (state.options) {
374383
if (typeof state.options.displayName === "string") {
375384
displayName = state.options.displayName;
@@ -379,6 +388,7 @@ export class DatabaseManager extends DisposableObject {
379388
}
380389
language = state.options.language;
381390
origin = state.options.origin;
391+
extensionManagedLocation = state.options.extensionManagedLocation;
382392
}
383393

384394
const dbBaseUri = vscode.Uri.parse(state.uri, true);
@@ -392,6 +402,7 @@ export class DatabaseManager extends DisposableObject {
392402
dateAdded,
393403
language,
394404
origin,
405+
extensionManagedLocation,
395406
};
396407
const item = new DatabaseItemImpl(dbBaseUri, undefined, fullOptions);
397408

@@ -583,15 +594,20 @@ export class DatabaseManager extends DisposableObject {
583594
// Remove this database item from the allow-list
584595
await this.deregisterDatabase(item);
585596

597+
// Find whether we know directly which directory we should remove
598+
const directoryToRemove = item.extensionManagedLocation
599+
? vscode.Uri.file(item.extensionManagedLocation)
600+
: item.databaseUri;
601+
586602
// Delete folder from file system only if it is controlled by the extension
587-
if (this.isExtensionControlledLocation(item.databaseUri)) {
603+
if (this.isExtensionControlledLocation(directoryToRemove)) {
588604
void extLogger.log("Deleting database from filesystem.");
589-
await remove(item.databaseUri.fsPath).then(
590-
() => void extLogger.log(`Deleted '${item.databaseUri.fsPath}'`),
605+
await remove(directoryToRemove.fsPath).then(
606+
() => void extLogger.log(`Deleted '${directoryToRemove.fsPath}'`),
591607
(e: unknown) =>
592608
void extLogger.log(
593609
`Failed to delete '${
594-
item.databaseUri.fsPath
610+
directoryToRemove.fsPath
595611
}'. Reason: ${getErrorMessage(e)}`,
596612
),
597613
);

extensions/ql-vscode/src/databases/local-databases/database-options.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ export interface DatabaseOptions {
55
dateAdded?: number | undefined;
66
language?: string;
77
origin?: DatabaseOrigin;
8+
extensionManagedLocation?: string;
89
}
910

1011
export interface FullDatabaseOptions extends DatabaseOptions {
1112
dateAdded: number | undefined;
1213
language: string | undefined;
1314
origin: DatabaseOrigin | undefined;
15+
extensionManagedLocation: string | undefined;
1416
}

extensions/ql-vscode/test/factories/databases/databases.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ export function mockDbOptions(): FullDatabaseOptions {
1414
origin: {
1515
type: "folder",
1616
},
17+
extensionManagedLocation: undefined,
1718
};
1819
}
1920

2021
export function createMockDB(
21-
dir: DirResult,
22+
dir: DirResult | string,
2223
dbOptions = mockDbOptions(),
2324
// source archive location must be a real(-ish) location since
2425
// tests will add this to the workspace location
@@ -38,10 +39,18 @@ export function createMockDB(
3839
);
3940
}
4041

41-
export function sourceLocationUri(dir: DirResult) {
42+
export function sourceLocationUri(dir: DirResult | string) {
43+
if (typeof dir === "string") {
44+
return Uri.file(join(dir, "src.zip"));
45+
}
46+
4247
return Uri.file(join(dir.name, "src.zip"));
4348
}
4449

45-
export function dbLocationUri(dir: DirResult) {
50+
export function dbLocationUri(dir: DirResult | string) {
51+
if (typeof dir === "string") {
52+
return Uri.file(join(dir, "db"));
53+
}
54+
4655
return Uri.file(join(dir.name, "db"));
4756
}

extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,37 @@ describe("local databases", () => {
242242
await expect(pathExists(mockDbItem.databaseUri.fsPath)).resolves.toBe(
243243
false,
244244
);
245+
await expect(pathExists(dir.name)).resolves.toBe(true);
246+
});
247+
248+
it("should remove a database item with an extension managed location", async () => {
249+
const dbLocation = join(dir.name, "org-repo-12");
250+
await ensureDir(dbLocation);
251+
252+
const mockDbItem = createMockDB(dbLocation, {
253+
...mockDbOptions(),
254+
extensionManagedLocation: dbLocation,
255+
});
256+
await ensureDir(mockDbItem.databaseUri.fsPath);
257+
258+
// pretend that this item is the first workspace folder in the list
259+
jest
260+
.spyOn(mockDbItem, "belongsToSourceArchiveExplorerUri")
261+
.mockReturnValue(true);
262+
263+
await (databaseManager as any).addDatabaseItem(mockDbItem);
264+
265+
updateSpy.mockClear();
266+
267+
await databaseManager.removeDatabaseItem(mockDbItem);
268+
269+
expect(databaseManager.databaseItems).toEqual([]);
270+
expect(updateSpy).toHaveBeenCalledWith("databaseList", []);
271+
// should remove the folder
272+
expect(workspace.updateWorkspaceFolders).toHaveBeenCalledWith(0, 1);
273+
274+
// should delete the complete extension managed location
275+
await expect(pathExists(dbLocation)).resolves.toBe(false);
245276
});
246277

247278
it("should remove a database item outside of the extension controlled area", async () => {
@@ -604,6 +635,7 @@ describe("local databases", () => {
604635
origin: {
605636
type: "folder",
606637
},
638+
extensionManagedLocation: undefined,
607639
};
608640
mockDbItem = createMockDB(dir, options);
609641

0 commit comments

Comments
 (0)