Add support for adding local lists (#1907)
This commit is contained in:
@@ -129,6 +129,28 @@ export class DbConfigStore extends DisposableObject {
|
||||
await this.writeConfig(config);
|
||||
}
|
||||
|
||||
public async addLocalList(listName: string): Promise<void> {
|
||||
if (!this.config) {
|
||||
throw Error("Cannot add local list if config is not loaded");
|
||||
}
|
||||
|
||||
if (listName === "") {
|
||||
throw Error("List name cannot be empty");
|
||||
}
|
||||
|
||||
if (this.doesLocalListExist(listName)) {
|
||||
throw Error(`A local list with the name '${listName}' already exists`);
|
||||
}
|
||||
|
||||
const config: DbConfig = cloneDbConfig(this.config);
|
||||
config.databases.local.lists.push({
|
||||
name: listName,
|
||||
databases: [],
|
||||
});
|
||||
|
||||
await this.writeConfig(config);
|
||||
}
|
||||
|
||||
public async addRemoteList(listName: string): Promise<void> {
|
||||
if (!this.config) {
|
||||
throw Error("Cannot add remote list if config is not loaded");
|
||||
|
||||
@@ -104,8 +104,8 @@ export class DbManager {
|
||||
): Promise<void> {
|
||||
switch (listKind) {
|
||||
case DbListKind.Local:
|
||||
// Adding a local list is not supported yet.
|
||||
throw Error("Cannot add a local list");
|
||||
await this.dbConfigStore.addLocalList(listName);
|
||||
break;
|
||||
case DbListKind.Remote:
|
||||
await this.dbConfigStore.addRemoteList(listName);
|
||||
break;
|
||||
|
||||
@@ -23,6 +23,10 @@ export interface RemoteDatabaseQuickPickItem extends QuickPickItem {
|
||||
kind: string;
|
||||
}
|
||||
|
||||
export interface AddListQuickPickItem extends QuickPickItem {
|
||||
kind: DbListKind;
|
||||
}
|
||||
|
||||
export class DbPanel extends DisposableObject {
|
||||
private readonly dataProvider: DbTreeDataProvider;
|
||||
private readonly treeView: TreeView<DbTreeViewItem>;
|
||||
@@ -179,6 +183,8 @@ export class DbPanel extends DisposableObject {
|
||||
}
|
||||
|
||||
private async addNewList(): Promise<void> {
|
||||
const listKind = await this.getAddNewListKind();
|
||||
|
||||
const listName = await window.showInputBox({
|
||||
prompt: "Enter a name for the new list",
|
||||
placeHolder: "example-list",
|
||||
@@ -187,17 +193,6 @@ export class DbPanel extends DisposableObject {
|
||||
return;
|
||||
}
|
||||
|
||||
const highlightedItem = await this.getHighlightedDbItem();
|
||||
|
||||
// For now: we only support adding remote lists, so if no item is highlighted,
|
||||
// we default to the "RootRemote" kind.
|
||||
// In future: if the highlighted item is undefined, we'll show a quick pick where
|
||||
// a user can select whether to add a remote or local list.
|
||||
const highlightedItemKind = highlightedItem?.kind || DbItemKind.RootRemote;
|
||||
const listKind = remoteDbKinds.includes(highlightedItemKind)
|
||||
? DbListKind.Remote
|
||||
: DbListKind.Local;
|
||||
|
||||
if (this.dbManager.doesListExist(listKind, listName)) {
|
||||
void showAndLogErrorMessage(`The list '${listName}' already exists`);
|
||||
return;
|
||||
@@ -206,6 +201,47 @@ export class DbPanel extends DisposableObject {
|
||||
await this.dbManager.addNewList(listKind, listName);
|
||||
}
|
||||
|
||||
private async getAddNewListKind(): Promise<DbListKind> {
|
||||
const highlightedItem = await this.getHighlightedDbItem();
|
||||
if (highlightedItem) {
|
||||
return remoteDbKinds.includes(highlightedItem.kind)
|
||||
? DbListKind.Remote
|
||||
: DbListKind.Local;
|
||||
} else {
|
||||
const quickPickItems = [
|
||||
{
|
||||
label: "$(cloud) Remote",
|
||||
detail: "Add a remote database from GitHub",
|
||||
alwaysShow: true,
|
||||
kind: DbListKind.Remote,
|
||||
},
|
||||
{
|
||||
label: "$(database) Local",
|
||||
detail: "Import a database from the cloud or a local file",
|
||||
alwaysShow: true,
|
||||
kind: DbListKind.Local,
|
||||
},
|
||||
];
|
||||
const selectedOption = await window.showQuickPick<AddListQuickPickItem>(
|
||||
quickPickItems,
|
||||
{
|
||||
title: "Add a new database",
|
||||
ignoreFocusOut: true,
|
||||
},
|
||||
);
|
||||
if (!selectedOption) {
|
||||
// We don't need to display a warning pop-up in this case, since the user just escaped out of the operation.
|
||||
// We set 'true' to make this a silent exception.
|
||||
throw new UserCancellationException(
|
||||
"No database list kind selected",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
return selectedOption.kind;
|
||||
}
|
||||
}
|
||||
|
||||
private async setSelectedItem(treeViewItem: DbTreeViewItem): Promise<void> {
|
||||
if (treeViewItem.dbItem === undefined) {
|
||||
throw new Error(
|
||||
|
||||
@@ -4,7 +4,11 @@ import { CodeQLExtensionInterface } from "../../../extension";
|
||||
import { readJson } from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { DbConfig } from "../../../databases/config/db-config";
|
||||
import { RemoteDatabaseQuickPickItem } from "../../../databases/ui/db-panel";
|
||||
import {
|
||||
AddListQuickPickItem,
|
||||
RemoteDatabaseQuickPickItem,
|
||||
} from "../../../databases/ui/db-panel";
|
||||
import { DbListKind } from "../../../databases/db-item";
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
@@ -25,6 +29,9 @@ describe("Db panel UI commands", () => {
|
||||
|
||||
it("should add new remote db list", async () => {
|
||||
// Add db list
|
||||
jest.spyOn(window, "showQuickPick").mockResolvedValue({
|
||||
kind: DbListKind.Remote,
|
||||
} as AddListQuickPickItem);
|
||||
jest.spyOn(window, "showInputBox").mockResolvedValue("my-list-1");
|
||||
await commands.executeCommand("codeQLDatabasesExperimental.addNewList");
|
||||
|
||||
@@ -35,6 +42,21 @@ describe("Db panel UI commands", () => {
|
||||
expect(dbConfig.databases.remote.repositoryLists[0].name).toBe("my-list-1");
|
||||
});
|
||||
|
||||
it("should add new local db list", async () => {
|
||||
// Add db list
|
||||
jest.spyOn(window, "showQuickPick").mockResolvedValue({
|
||||
kind: DbListKind.Local,
|
||||
} as AddListQuickPickItem);
|
||||
jest.spyOn(window, "showInputBox").mockResolvedValue("my-list-1");
|
||||
await commands.executeCommand("codeQLDatabasesExperimental.addNewList");
|
||||
|
||||
// Check db config
|
||||
const dbConfigFilePath = path.join(storagePath, "workspace-databases.json");
|
||||
const dbConfig: DbConfig = await readJson(dbConfigFilePath);
|
||||
expect(dbConfig.databases.local.lists).toHaveLength(1);
|
||||
expect(dbConfig.databases.local.lists[0].name).toBe("my-list-1");
|
||||
});
|
||||
|
||||
it("should add new remote repository", async () => {
|
||||
// Add db
|
||||
jest.spyOn(window, "showQuickPick").mockResolvedValue({
|
||||
|
||||
@@ -478,10 +478,7 @@ describe("db panel", () => {
|
||||
|
||||
await dbManager.addNewRemoteRepo("owner2/repo2");
|
||||
|
||||
// Read the workspace databases JSON file directly to check that the new repo has been added.
|
||||
// We can't use the dbConfigStore's `read` function here because it depends on the file watcher
|
||||
// picking up changes, and we don't control the timing of that.
|
||||
const dbConfigFileContents = await readJSON(dbConfigFilePath);
|
||||
const dbConfigFileContents = await readDbConfigDirectly();
|
||||
expect(dbConfigFileContents.databases.remote.repositories.length).toBe(2);
|
||||
expect(dbConfigFileContents.databases.remote.repositories[1]).toEqual(
|
||||
"owner2/repo2",
|
||||
@@ -572,10 +569,7 @@ describe("db panel", () => {
|
||||
|
||||
await dbManager.addNewList(DbListKind.Remote, "my-list-2");
|
||||
|
||||
// Read the workspace databases JSON file directly to check that the new list has been added.
|
||||
// We can't use the dbConfigStore's `read` function here because it depends on the file watcher
|
||||
// picking up changes, and we don't control the timing of that.
|
||||
const dbConfigFileContents = await readJSON(dbConfigFilePath);
|
||||
const dbConfigFileContents = await readDbConfigDirectly();
|
||||
expect(dbConfigFileContents.databases.remote.repositoryLists.length).toBe(
|
||||
2,
|
||||
);
|
||||
@@ -586,12 +580,42 @@ describe("db panel", () => {
|
||||
});
|
||||
|
||||
it("should throw error when adding a new list to a local node", async () => {
|
||||
const dbConfig: DbConfig = createDbConfig();
|
||||
const dbConfig: DbConfig = createDbConfig({
|
||||
localLists: [
|
||||
{
|
||||
name: "my-list-1",
|
||||
databases: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
await saveDbConfig(dbConfig);
|
||||
|
||||
await expect(dbManager.addNewList(DbListKind.Local, "")).rejects.toThrow(
|
||||
new Error("Cannot add a local list"),
|
||||
const dbTreeItems = await dbTreeDataProvider.getChildren();
|
||||
|
||||
expect(dbTreeItems).toBeTruthy();
|
||||
const items = dbTreeItems!;
|
||||
|
||||
const localRootNode = items[1];
|
||||
const localUserDefinedLists = localRootNode.children.filter(
|
||||
(c) => c.dbItem?.kind === DbItemKind.LocalList,
|
||||
);
|
||||
const list1 = localRootNode.children.find(
|
||||
(c) =>
|
||||
c.dbItem?.kind === DbItemKind.LocalList &&
|
||||
c.dbItem?.listName === "my-list-1",
|
||||
);
|
||||
|
||||
expect(localUserDefinedLists.length).toBe(1);
|
||||
expect(localUserDefinedLists[0]).toBe(list1);
|
||||
|
||||
await dbManager.addNewList(DbListKind.Local, "my-list-2");
|
||||
|
||||
const dbConfigFileContents = await readDbConfigDirectly();
|
||||
expect(dbConfigFileContents.databases.local.lists.length).toBe(2);
|
||||
expect(dbConfigFileContents.databases.local.lists[1]).toEqual({
|
||||
name: "my-list-2",
|
||||
databases: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -838,4 +862,11 @@ describe("db panel", () => {
|
||||
SELECTED_DB_ITEM_RESOURCE_URI && treeViewItem.contextValue === undefined
|
||||
);
|
||||
}
|
||||
|
||||
async function readDbConfigDirectly(): Promise<DbConfig> {
|
||||
// Read the workspace databases JSON file directly to check that the new list has been added.
|
||||
// We can't use the dbConfigStore's `read` function here because it depends on the file watcher
|
||||
// picking up changes, and we don't control the timing of that.
|
||||
return (await readJSON(dbConfigFilePath)) as DbConfig;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user