Merge pull request #1925 from github/nora/add-remove-context-action
Add remove context menu action
This commit is contained in:
@@ -66,6 +66,7 @@
|
||||
"onCommand:codeQLDatabasesExperimental.setSelectedItemContextMenu",
|
||||
"onCommand:codeQLDatabasesExperimental.renameItemContextMenu",
|
||||
"onCommand:codeQLDatabasesExperimental.openOnGitHubContextMenu",
|
||||
"onCommand:codeQLDatabasesExperimental.removeItemContextMenu",
|
||||
"onCommand:codeQL.quickQuery",
|
||||
"onCommand:codeQL.restartQueryServer",
|
||||
"onWebviewPanel:resultsView",
|
||||
@@ -392,6 +393,10 @@
|
||||
"command": "codeQLDatabasesExperimental.openOnGitHubContextMenu",
|
||||
"title": "Open on GitHub"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabasesExperimental.removeItemContextMenu",
|
||||
"title": "Remove"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabases.chooseDatabaseFolder",
|
||||
"title": "Choose Database from Folder",
|
||||
@@ -791,6 +796,10 @@
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
{
|
||||
"command": "codeQLDatabasesExperimental.removeItemContextMenu",
|
||||
"when": "view == codeQLDatabasesExperimental && viewItem =~ /canBeRemoved/"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabasesExperimental.setSelectedItemContextMenu",
|
||||
"when": "view == codeQLDatabasesExperimental && viewItem =~ /canBeSelected/"
|
||||
@@ -1043,6 +1052,10 @@
|
||||
"command": "codeQLDatabasesExperimental.openOnGitHubContextMenu",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabasesExperimental.removeItemContextMenu",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLDatabases.setCurrentDatabase",
|
||||
"when": "false"
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
renameLocalList,
|
||||
renameRemoteList,
|
||||
SelectedDbItem,
|
||||
SelectedDbItemKind,
|
||||
} from "./db-config";
|
||||
import * as chokidar from "chokidar";
|
||||
import { DisposableObject, DisposeHandler } from "../../pure/disposable-object";
|
||||
@@ -22,7 +23,13 @@ import {
|
||||
LocalDatabaseDbItem,
|
||||
LocalListDbItem,
|
||||
RemoteUserDefinedListDbItem,
|
||||
DbItem,
|
||||
DbItemKind,
|
||||
} from "../db-item";
|
||||
import {
|
||||
compareSelectedKindIsEqual,
|
||||
mapDbItemToSelectedDbItem,
|
||||
} from "../db-item-selection";
|
||||
|
||||
export class DbConfigStore extends DisposableObject {
|
||||
public readonly onDidChangeConfig: AppEvent<void>;
|
||||
@@ -88,6 +95,96 @@ export class DbConfigStore extends DisposableObject {
|
||||
await this.writeConfig(config);
|
||||
}
|
||||
|
||||
public async removeDbItem(dbItem: DbItem): Promise<void> {
|
||||
if (!this.config) {
|
||||
throw Error("Cannot remove item if config is not loaded");
|
||||
}
|
||||
|
||||
const config = cloneDbConfig(this.config);
|
||||
const selectedItem: SelectedDbItem | undefined = config.selected;
|
||||
|
||||
// Remove item from databases
|
||||
switch (dbItem.kind) {
|
||||
case DbItemKind.LocalList:
|
||||
config.databases.local.lists = config.databases.local.lists.filter(
|
||||
(list) => list.name !== dbItem.listName,
|
||||
);
|
||||
break;
|
||||
case DbItemKind.RemoteUserDefinedList:
|
||||
config.databases.remote.repositoryLists =
|
||||
config.databases.remote.repositoryLists.filter(
|
||||
(list) => list.name !== dbItem.listName,
|
||||
);
|
||||
break;
|
||||
case DbItemKind.LocalDatabase:
|
||||
// When we start using local databases these need to be removed from disk as well.
|
||||
if (dbItem.parentListName) {
|
||||
const parent = config.databases.local.lists.find(
|
||||
(list) => list.name === dbItem.parentListName,
|
||||
);
|
||||
if (!parent) {
|
||||
throw Error(`Cannot find parent list '${dbItem.parentListName}'`);
|
||||
} else {
|
||||
parent.databases = parent.databases.filter(
|
||||
(db) => db.name !== dbItem.databaseName,
|
||||
);
|
||||
}
|
||||
}
|
||||
config.databases.local.databases =
|
||||
config.databases.local.databases.filter(
|
||||
(db) => db.name !== dbItem.databaseName,
|
||||
);
|
||||
break;
|
||||
case DbItemKind.RemoteRepo:
|
||||
if (dbItem.parentListName) {
|
||||
const parent = config.databases.remote.repositoryLists.find(
|
||||
(list) => list.name === dbItem.parentListName,
|
||||
);
|
||||
if (!parent) {
|
||||
throw Error(`Cannot find parent list '${dbItem.parentListName}'`);
|
||||
} else {
|
||||
parent.repositories = parent.repositories.filter(
|
||||
(repo) => repo !== dbItem.repoFullName,
|
||||
);
|
||||
}
|
||||
}
|
||||
config.databases.remote.repositories =
|
||||
config.databases.remote.repositories.filter(
|
||||
(repo) => repo !== dbItem.repoFullName,
|
||||
);
|
||||
break;
|
||||
case DbItemKind.RemoteOwner:
|
||||
config.databases.remote.owners = config.databases.remote.owners.filter(
|
||||
(owner) => owner !== dbItem.ownerName,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw Error(`Type '${dbItem.kind}' cannot be removed`);
|
||||
}
|
||||
|
||||
// Remove item from selected
|
||||
const removedItem = mapDbItemToSelectedDbItem(dbItem);
|
||||
if (selectedItem && removedItem) {
|
||||
// if removedItem has a parentList, check if parentList is selectedItem
|
||||
if (
|
||||
removedItem.kind === SelectedDbItemKind.LocalUserDefinedList ||
|
||||
removedItem.kind === SelectedDbItemKind.RemoteUserDefinedList
|
||||
) {
|
||||
if (
|
||||
(selectedItem.kind === SelectedDbItemKind.LocalDatabase ||
|
||||
selectedItem.kind === SelectedDbItemKind.RemoteRepository) &&
|
||||
removedItem.listName === selectedItem.listName
|
||||
) {
|
||||
config.selected = undefined;
|
||||
}
|
||||
}
|
||||
if (compareSelectedKindIsEqual(removedItem, selectedItem)) {
|
||||
config.selected = undefined;
|
||||
}
|
||||
}
|
||||
await this.writeConfig(config);
|
||||
}
|
||||
|
||||
public async addRemoteRepo(
|
||||
repoNwo: string,
|
||||
parentList?: string,
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { DbItem, DbItemKind, LocalDbItem, RemoteDbItem } from "./db-item";
|
||||
import { SelectedDbItem, SelectedDbItemKind } from "./config/db-config";
|
||||
import {
|
||||
SelectedDbItem,
|
||||
SelectedDbItemKind,
|
||||
SelectedLocalDatabase,
|
||||
SelectedLocalUserDefinedList,
|
||||
SelectedRemoteOwner,
|
||||
SelectedRemoteRepository,
|
||||
} from "./config/db-config";
|
||||
|
||||
export function getSelectedDbItem(dbItems: DbItem[]): DbItem | undefined {
|
||||
for (const dbItem of dbItems) {
|
||||
@@ -80,15 +87,51 @@ export function mapDbItemToSelectedDbItem(
|
||||
case DbItemKind.LocalDatabase:
|
||||
return {
|
||||
kind: SelectedDbItemKind.LocalDatabase,
|
||||
listName: dbItem?.parentListName,
|
||||
databaseName: dbItem.databaseName,
|
||||
listName: dbItem?.parentListName,
|
||||
};
|
||||
|
||||
case DbItemKind.RemoteRepo:
|
||||
return {
|
||||
kind: SelectedDbItemKind.RemoteRepository,
|
||||
listName: dbItem?.parentListName,
|
||||
repositoryName: dbItem.repoFullName,
|
||||
listName: dbItem?.parentListName,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function compareSelectedKindIsEqual(
|
||||
item1: SelectedDbItem,
|
||||
item2: SelectedDbItem,
|
||||
): boolean {
|
||||
if (item1.kind === item2.kind) {
|
||||
switch (item1.kind) {
|
||||
case SelectedDbItemKind.LocalUserDefinedList:
|
||||
case SelectedDbItemKind.RemoteUserDefinedList:
|
||||
case SelectedDbItemKind.RemoteSystemDefinedList:
|
||||
return (
|
||||
item1.listName === (item2 as SelectedLocalUserDefinedList).listName
|
||||
);
|
||||
case SelectedDbItemKind.RemoteOwner:
|
||||
return item1.ownerName === (item2 as SelectedRemoteOwner).ownerName;
|
||||
case SelectedDbItemKind.LocalDatabase: {
|
||||
const selectedItem = item2 as SelectedLocalDatabase;
|
||||
return (
|
||||
item1.databaseName === selectedItem.databaseName &&
|
||||
item1.listName === selectedItem.listName
|
||||
);
|
||||
}
|
||||
case SelectedDbItemKind.RemoteRepository: {
|
||||
const selectedItem = item2 as SelectedRemoteRepository;
|
||||
return (
|
||||
item1.repositoryName === selectedItem.repositoryName &&
|
||||
item1.listName === selectedItem.listName
|
||||
);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,14 @@ export class DbManager {
|
||||
}
|
||||
}
|
||||
|
||||
public async removeDbItem(dbItem: DbItem): Promise<void> {
|
||||
await this.dbConfigStore.removeDbItem(dbItem);
|
||||
|
||||
// Updating the expanded items takes care of cleaning up
|
||||
// any non-existent items.
|
||||
await this.updateExpandedItems(this.getExpandedItems());
|
||||
}
|
||||
|
||||
public async updateDbItemExpandedState(
|
||||
dbItem: DbItem,
|
||||
itemExpanded: boolean,
|
||||
|
||||
@@ -107,6 +107,12 @@ export class DbPanel extends DisposableObject {
|
||||
(treeViewItem: DbTreeViewItem) => this.renameItem(treeViewItem),
|
||||
),
|
||||
);
|
||||
this.push(
|
||||
commandRunner(
|
||||
"codeQLDatabasesExperimental.removeItemContextMenu",
|
||||
(treeViewItem: DbTreeViewItem) => this.removeItem(treeViewItem),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private async openConfigFile(): Promise<void> {
|
||||
@@ -363,6 +369,15 @@ export class DbPanel extends DisposableObject {
|
||||
await this.dbManager.renameList(dbItem, newName);
|
||||
}
|
||||
|
||||
private async removeItem(treeViewItem: DbTreeViewItem): Promise<void> {
|
||||
if (treeViewItem.dbItem === undefined) {
|
||||
throw new Error(
|
||||
"Not a removable database item. Please select a valid item.",
|
||||
);
|
||||
}
|
||||
await this.dbManager.removeDbItem(treeViewItem.dbItem);
|
||||
}
|
||||
|
||||
private async onDidCollapseElement(
|
||||
event: TreeViewExpansionEvent<DbTreeViewItem>,
|
||||
): Promise<void> {
|
||||
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
import {
|
||||
createLocalDatabaseDbItem,
|
||||
createLocalListDbItem,
|
||||
createRemoteOwnerDbItem,
|
||||
createRemoteRepoDbItem,
|
||||
createRemoteUserDefinedListDbItem,
|
||||
} from "../../../factories/db-item-factories";
|
||||
import { createMockApp } from "../../../__mocks__/appMock";
|
||||
@@ -365,4 +367,133 @@ describe("db config store", () => {
|
||||
configStore.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
describe("db and list deletion", () => {
|
||||
let app: App;
|
||||
let configPath: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
app = createMockApp({
|
||||
extensionPath,
|
||||
workspaceStoragePath: tempWorkspaceStoragePath,
|
||||
});
|
||||
|
||||
configPath = join(tempWorkspaceStoragePath, "workspace-databases.json");
|
||||
});
|
||||
|
||||
it("should remove a single db item", async () => {
|
||||
// Initial set up
|
||||
const dbConfig = createDbConfig({
|
||||
remoteOwners: ["owner1", "owner2"],
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.RemoteOwner,
|
||||
ownerName: "owner1",
|
||||
},
|
||||
});
|
||||
|
||||
await writeJSON(configPath, dbConfig);
|
||||
|
||||
const configStore = new DbConfigStore(app);
|
||||
await configStore.initialize();
|
||||
|
||||
// Remove
|
||||
const currentDbItem = createRemoteOwnerDbItem({
|
||||
ownerName: "owner1",
|
||||
});
|
||||
await configStore.removeDbItem(currentDbItem);
|
||||
|
||||
// Read the config file
|
||||
const updatedDbConfig = (await readJSON(configPath)) as DbConfig;
|
||||
|
||||
// Check that the config file has been updated
|
||||
const updatedRemoteDbs = updatedDbConfig.databases.remote;
|
||||
expect(updatedRemoteDbs.owners).toHaveLength(1);
|
||||
expect(updatedRemoteDbs.owners[0]).toEqual("owner2");
|
||||
|
||||
expect(updatedDbConfig.selected).toEqual(undefined);
|
||||
|
||||
configStore.dispose();
|
||||
});
|
||||
|
||||
it("should remove a list db item", async () => {
|
||||
// Initial set up
|
||||
const dbConfig = createDbConfig({
|
||||
remoteLists: [
|
||||
{
|
||||
name: "list1",
|
||||
repositories: ["owner/repo1", "owner/repo2"],
|
||||
},
|
||||
],
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.RemoteUserDefinedList,
|
||||
listName: "list1",
|
||||
},
|
||||
});
|
||||
|
||||
await writeJSON(configPath, dbConfig);
|
||||
|
||||
const configStore = new DbConfigStore(app);
|
||||
await configStore.initialize();
|
||||
|
||||
// Remove
|
||||
const currentDbItem = createRemoteUserDefinedListDbItem({
|
||||
listName: "list1",
|
||||
});
|
||||
await configStore.removeDbItem(currentDbItem);
|
||||
|
||||
// Read the config file
|
||||
const updatedDbConfig = (await readJSON(configPath)) as DbConfig;
|
||||
|
||||
// Check that the config file has been updated
|
||||
const updatedRemoteDbs = updatedDbConfig.databases.remote;
|
||||
expect(updatedRemoteDbs.repositoryLists).toHaveLength(0);
|
||||
|
||||
expect(updatedDbConfig.selected).toEqual(undefined);
|
||||
|
||||
configStore.dispose();
|
||||
});
|
||||
|
||||
it("should remove a db item in a list", async () => {
|
||||
// Initial set up
|
||||
const dbConfig = createDbConfig({
|
||||
remoteLists: [
|
||||
{
|
||||
name: "list1",
|
||||
repositories: ["owner/repo1", "owner/repo2"],
|
||||
},
|
||||
],
|
||||
selected: {
|
||||
kind: SelectedDbItemKind.RemoteRepository,
|
||||
repositoryName: "owner/repo1",
|
||||
listName: "list1",
|
||||
},
|
||||
});
|
||||
|
||||
await writeJSON(configPath, dbConfig);
|
||||
|
||||
const configStore = new DbConfigStore(app);
|
||||
await configStore.initialize();
|
||||
|
||||
// Remove
|
||||
const currentDbItem = createRemoteRepoDbItem({
|
||||
repoFullName: "owner/repo1",
|
||||
parentListName: "list1",
|
||||
});
|
||||
await configStore.removeDbItem(currentDbItem);
|
||||
|
||||
// Read the config file
|
||||
const updatedDbConfig = (await readJSON(configPath)) as DbConfig;
|
||||
|
||||
// Check that the config file has been updated
|
||||
const updatedRemoteDbs = updatedDbConfig.databases.remote;
|
||||
expect(updatedRemoteDbs.repositoryLists[0].repositories).toHaveLength(1);
|
||||
expect(updatedRemoteDbs.repositoryLists[0].repositories[0]).toEqual(
|
||||
"owner/repo2",
|
||||
);
|
||||
|
||||
expect(updatedDbConfig.selected).toEqual(undefined);
|
||||
|
||||
configStore.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user