Merge pull request #1926 from github/charisk/db-config-store-rename

Add db and list rename functionality to db config store
This commit is contained in:
Charis Kyriakou
2023-01-04 17:38:44 +00:00
committed by GitHub
4 changed files with 757 additions and 2 deletions

View File

@@ -1,6 +1,13 @@
import { pathExists, outputJSON, readJSON, readJSONSync } from "fs-extra";
import { join } from "path";
import { cloneDbConfig, DbConfig, SelectedDbItem } from "./db-config";
import {
cloneDbConfig,
DbConfig,
renameLocalDb,
renameLocalList,
renameRemoteList,
SelectedDbItem,
} from "./db-config";
import * as chokidar from "chokidar";
import { DisposableObject, DisposeHandler } from "../../pure/disposable-object";
import { DbConfigValidator } from "./db-config-validator";
@@ -11,6 +18,11 @@ import {
DbConfigValidationErrorKind,
} from "../db-validation-errors";
import { ValueResult } from "../../common/value-result";
import {
LocalDatabaseDbItem,
LocalListDbItem,
RemoteUserDefinedListDbItem,
} from "../db-item";
export class DbConfigStore extends DisposableObject {
public readonly onDidChangeConfig: AppEvent<void>;
@@ -161,6 +173,65 @@ export class DbConfigStore extends DisposableObject {
await this.writeConfig(config);
}
public async renameLocalList(
currentDbItem: LocalListDbItem,
newName: string,
) {
if (!this.config) {
throw Error("Cannot rename local list if config is not loaded");
}
this.validateLocalListName(newName);
const updatedConfig = renameLocalList(
this.config,
currentDbItem.listName,
newName,
);
await this.writeConfig(updatedConfig);
}
public async renameRemoteList(
currentDbItem: RemoteUserDefinedListDbItem,
newName: string,
) {
if (!this.config) {
throw Error("Cannot rename remote list if config is not loaded");
}
this.validateRemoteListName(newName);
const updatedConfig = renameRemoteList(
this.config,
currentDbItem.listName,
newName,
);
await this.writeConfig(updatedConfig);
}
public async renameLocalDb(
currentDbItem: LocalDatabaseDbItem,
newName: string,
parentListName?: string,
): Promise<void> {
if (!this.config) {
throw Error("Cannot rename local db if config is not loaded");
}
this.validateLocalDbName(newName);
const updatedConfig = renameLocalDb(
this.config,
currentDbItem.databaseName,
newName,
parentListName,
);
await this.writeConfig(updatedConfig);
}
public doesRemoteListExist(listName: string): boolean {
if (!this.config) {
throw Error("Cannot check remote list existence if config is not loaded");
@@ -179,6 +250,23 @@ export class DbConfigStore extends DisposableObject {
return this.config.databases.local.lists.some((l) => l.name === listName);
}
public doesLocalDbExist(dbName: string, listName?: string): boolean {
if (!this.config) {
throw Error(
"Cannot check remote database existence if config is not loaded",
);
}
if (listName) {
return this.config.databases.local.lists.some(
(l) =>
l.name === listName && l.databases.some((d) => d.name === dbName),
);
}
return this.config.databases.local.databases.some((d) => d.name === dbName);
}
public doesRemoteDbExist(dbName: string, listName?: string): boolean {
if (!this.config) {
throw Error(
@@ -344,4 +432,14 @@ export class DbConfigStore extends DisposableObject {
throw Error(`A remote list with the name '${listName}' already exists`);
}
}
private validateLocalDbName(dbName: string): void {
if (dbName === "") {
throw Error("Database name cannot be empty");
}
if (this.doesLocalDbExist(dbName)) {
throw Error(`A local database with the name '${dbName}' already exists`);
}
}
}

View File

@@ -114,6 +114,102 @@ export function cloneDbConfig(config: DbConfig): DbConfig {
};
}
export function renameLocalList(
originalConfig: DbConfig,
currentListName: string,
newListName: string,
): DbConfig {
const config = cloneDbConfig(originalConfig);
const list = config.databases.local.lists.find(
(l) => l.name === currentListName,
);
if (!list) {
throw Error(`Cannot find list '${currentListName}' to rename`);
}
list.name = newListName;
if (
config.selected?.kind === SelectedDbItemKind.LocalUserDefinedList ||
config.selected?.kind === SelectedDbItemKind.LocalDatabase
) {
if (config.selected.listName === currentListName) {
config.selected.listName = newListName;
}
}
return config;
}
export function renameRemoteList(
originalConfig: DbConfig,
currentListName: string,
newListName: string,
): DbConfig {
const config = cloneDbConfig(originalConfig);
const list = config.databases.remote.repositoryLists.find(
(l) => l.name === currentListName,
);
if (!list) {
throw Error(`Cannot find list '${currentListName}' to rename`);
}
list.name = newListName;
if (
config.selected?.kind === SelectedDbItemKind.RemoteUserDefinedList ||
config.selected?.kind === SelectedDbItemKind.RemoteRepository
) {
if (config.selected.listName === currentListName) {
config.selected.listName = newListName;
}
}
return config;
}
export function renameLocalDb(
originalConfig: DbConfig,
currentDbName: string,
newDbName: string,
parentListName?: string,
): DbConfig {
const config = cloneDbConfig(originalConfig);
if (parentListName) {
const list = config.databases.local.lists.find(
(l) => l.name === parentListName,
);
if (!list) {
throw Error(`Cannot find parent list '${parentListName}'`);
}
const dbIndex = list.databases.findIndex((db) => db.name === currentDbName);
if (dbIndex === -1) {
throw Error(
`Cannot find database '${currentDbName}' in list '${parentListName}'`,
);
}
list.databases[dbIndex].name = newDbName;
} else {
const dbIndex = config.databases.local.databases.findIndex(
(db) => db.name === currentDbName,
);
if (dbIndex === -1) {
throw Error(`Cannot find database '${currentDbName}' in local databases`);
}
config.databases.local.databases[dbIndex].name = newDbName;
}
if (
config.selected?.kind === SelectedDbItemKind.LocalDatabase &&
config.selected.databaseName === currentDbName
) {
config.selected.databaseName = newDbName;
}
return config;
}
function cloneDbConfigSelectedItem(selected: SelectedDbItem): SelectedDbItem {
switch (selected.kind) {
case SelectedDbItemKind.LocalUserDefinedList:

View File

@@ -1,6 +1,20 @@
import { ensureDir, remove, pathExists } from "fs-extra";
import { ensureDir, remove, pathExists, writeJSON, readJSON } from "fs-extra";
import { join } from "path";
import { App } from "../../../../src/common/app";
import {
DbConfig,
SelectedDbItemKind,
} from "../../../../src/databases/config/db-config";
import { DbConfigStore } from "../../../../src/databases/config/db-config-store";
import {
createDbConfig,
createLocalDbConfigItem,
} from "../../../factories/db-config-factories";
import {
createLocalDatabaseDbItem,
createLocalListDbItem,
createRemoteUserDefinedListDbItem,
} from "../../../factories/db-item-factories";
import { createMockApp } from "../../../__mocks__/appMock";
describe("db config store", () => {
@@ -167,4 +181,188 @@ describe("db config store", () => {
configStore.dispose();
});
});
describe("db and list renaming", () => {
let app: App;
let configPath: string;
beforeEach(async () => {
app = createMockApp({
extensionPath,
workspaceStoragePath: tempWorkspaceStoragePath,
});
configPath = join(tempWorkspaceStoragePath, "workspace-databases.json");
});
it("should allow renaming a remote list", async () => {
// Initial set up
const dbConfig = createDbConfig({
remoteLists: [
{
name: "list1",
repositories: ["owner/repo1", "owner/repo2"],
},
],
selected: {
kind: SelectedDbItemKind.RemoteRepository,
repositoryName: "owner/repo2",
listName: "list1",
},
});
await writeJSON(configPath, dbConfig);
const configStore = new DbConfigStore(app);
await configStore.initialize();
// Rename
const currentDbItem = createRemoteUserDefinedListDbItem({
listName: "list1",
});
await configStore.renameRemoteList(currentDbItem, "listRenamed");
// 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(1);
expect(updatedRemoteDbs.repositoryLists[0].name).toEqual("listRenamed");
expect(updatedDbConfig.selected).toEqual({
kind: SelectedDbItemKind.RemoteRepository,
repositoryName: "owner/repo2",
listName: "listRenamed",
});
configStore.dispose();
});
it("should allow renaming a local list", async () => {
// Initial set up
const dbConfig = createDbConfig({
localLists: [
{
name: "list1",
databases: [
createLocalDbConfigItem(),
createLocalDbConfigItem(),
createLocalDbConfigItem(),
],
},
],
selected: {
kind: SelectedDbItemKind.LocalUserDefinedList,
listName: "list1",
},
});
await writeJSON(configPath, dbConfig);
const configStore = new DbConfigStore(app);
await configStore.initialize();
// Rename
const currentDbItem = createLocalListDbItem({
listName: "list1",
});
await configStore.renameLocalList(currentDbItem, "listRenamed");
// Read the config file
const updatedDbConfig = (await readJSON(configPath)) as DbConfig;
// Check that the config file has been updated
const updatedLocalDbs = updatedDbConfig.databases.local;
expect(updatedLocalDbs.lists).toHaveLength(1);
expect(updatedLocalDbs.lists[0].name).toEqual("listRenamed");
expect(updatedDbConfig.selected).toEqual({
kind: SelectedDbItemKind.LocalUserDefinedList,
listName: "listRenamed",
});
configStore.dispose();
});
it("should allow renaming of a local db", async () => {
// Initial set up
const dbConfig = createDbConfig({
localLists: [
{
name: "list1",
databases: [
createLocalDbConfigItem({ name: "db1" }),
createLocalDbConfigItem({ name: "db2" }),
createLocalDbConfigItem({ name: "db3" }),
],
},
],
selected: {
kind: SelectedDbItemKind.LocalDatabase,
databaseName: "db1",
listName: "list1",
},
});
await writeJSON(configPath, dbConfig);
const configStore = new DbConfigStore(app);
await configStore.initialize();
// Rename
const currentDbItem = createLocalDatabaseDbItem({
databaseName: "db1",
});
await configStore.renameLocalDb(currentDbItem, "dbRenamed", "list1");
// Read the config file
const updatedDbConfig = (await readJSON(configPath)) as DbConfig;
// Check that the config file has been updated
const updatedLocalDbs = updatedDbConfig.databases.local;
expect(updatedLocalDbs.lists).toHaveLength(1);
expect(updatedLocalDbs.lists[0].name).toEqual("list1");
expect(updatedLocalDbs.lists[0].databases.length).toEqual(3);
expect(updatedLocalDbs.lists[0].databases[0].name).toEqual("dbRenamed");
expect(updatedDbConfig.selected).toEqual({
kind: SelectedDbItemKind.LocalDatabase,
databaseName: "dbRenamed",
listName: "list1",
});
configStore.dispose();
});
it("should throw if the name of a list is taken", async () => {
// Initial set up
const dbConfig = createDbConfig({
remoteLists: [
{
name: "list1",
repositories: ["owner/repo1", "owner/repo2"],
},
{
name: "list2",
repositories: ["owner/repo1", "owner/repo2"],
},
],
});
await writeJSON(configPath, dbConfig);
const configStore = new DbConfigStore(app);
await configStore.initialize();
// Rename
const currentDbItem = createRemoteUserDefinedListDbItem({
listName: "list1",
});
await expect(
configStore.renameRemoteList(currentDbItem, "list2"),
).rejects.toThrow(`A remote list with the name 'list2' already exists`);
configStore.dispose();
});
});
});

View File

@@ -0,0 +1,363 @@
import {
LocalList,
renameLocalDb,
renameLocalList,
renameRemoteList,
SelectedDbItemKind,
} from "../../../../src/databases/config/db-config";
import {
createDbConfig,
createLocalDbConfigItem,
} from "../../../factories/db-config-factories";
describe("db config", () => {
describe("renameLocalList", () => {
it("should rename a local list", () => {
const originalConfig = createDbConfig({
localLists: [
{
name: "list1",
databases: [],
},
{
name: "list2",
databases: [],
},
],
});
const updatedConfig = renameLocalList(
originalConfig,
"list1",
"listRenamed",
);
expect(updatedConfig.databases.local.lists).toEqual([
{
name: "listRenamed",
databases: [],
},
{
name: "list2",
databases: [],
},
]);
});
it("should rename a selected local list", () => {
const originalConfig = createDbConfig({
localLists: [
{
name: "list1",
databases: [],
},
{
name: "list2",
databases: [],
},
],
selected: {
kind: SelectedDbItemKind.LocalUserDefinedList,
listName: "list1",
},
});
const updatedConfig = renameLocalList(
originalConfig,
"list1",
"listRenamed",
);
expect(updatedConfig.databases.local.lists).toEqual([
{
name: "listRenamed",
databases: [],
},
{
name: "list2",
databases: [],
},
]);
expect(updatedConfig.selected).toEqual({
kind: SelectedDbItemKind.LocalUserDefinedList,
listName: "listRenamed",
});
});
it("should rename a local list with a db that is selected", () => {
const selectedLocalDb = createLocalDbConfigItem();
const list1: LocalList = {
name: "list1",
databases: [
createLocalDbConfigItem(),
selectedLocalDb,
createLocalDbConfigItem(),
],
};
const list2: LocalList = {
name: "list2",
databases: [],
};
const originalConfig = createDbConfig({
localLists: [list1, list2],
selected: {
kind: SelectedDbItemKind.LocalDatabase,
databaseName: selectedLocalDb.name,
listName: list1.name,
},
});
const updatedConfig = renameLocalList(
originalConfig,
list1.name,
"listRenamed",
);
expect(updatedConfig.databases.local.lists.length).toEqual(2);
expect(updatedConfig.databases.local.lists[0]).toEqual({
...list1,
name: "listRenamed",
});
expect(updatedConfig.databases.local.lists[1]).toEqual(list2);
expect(updatedConfig.selected).toEqual({
kind: SelectedDbItemKind.LocalDatabase,
databaseName: selectedLocalDb.name,
listName: "listRenamed",
});
});
});
describe("renameRemoteList", () => {
it("should rename a remote list", () => {
const originalConfig = createDbConfig({
remoteLists: [
{
name: "list1",
repositories: [],
},
{
name: "list2",
repositories: [],
},
],
});
const updatedConfig = renameRemoteList(
originalConfig,
"list1",
"listRenamed",
);
expect(updatedConfig.databases.remote.repositoryLists).toEqual([
{
name: "listRenamed",
repositories: [],
},
{
name: "list2",
repositories: [],
},
]);
});
it("should rename a selected remote list", () => {
const originalConfig = createDbConfig({
remoteLists: [
{
name: "list1",
repositories: [],
},
{
name: "list2",
repositories: [],
},
],
selected: {
kind: SelectedDbItemKind.RemoteUserDefinedList,
listName: "list1",
},
});
const updatedConfig = renameRemoteList(
originalConfig,
"list1",
"listRenamed",
);
expect(updatedConfig.databases.remote.repositoryLists).toEqual([
{
name: "listRenamed",
repositories: [],
},
{
name: "list2",
repositories: [],
},
]);
expect(updatedConfig.selected).toEqual({
kind: SelectedDbItemKind.RemoteUserDefinedList,
listName: "listRenamed",
});
});
it("should rename a remote list with a db that is selected", () => {
const selectedRemoteRepo = "owner/repo2";
const originalConfig = createDbConfig({
remoteLists: [
{
name: "list1",
repositories: ["owner1/repo1", selectedRemoteRepo, "owner1/repo3"],
},
{
name: "list2",
repositories: [],
},
],
selected: {
kind: SelectedDbItemKind.RemoteRepository,
repositoryName: selectedRemoteRepo,
listName: "list1",
},
});
const updatedConfig = renameRemoteList(
originalConfig,
"list1",
"listRenamed",
);
const updatedRepositoryLists =
updatedConfig.databases.remote.repositoryLists;
expect(updatedRepositoryLists.length).toEqual(2);
expect(updatedRepositoryLists[0]).toEqual({
...originalConfig.databases.remote.repositoryLists[0],
name: "listRenamed",
});
expect(updatedRepositoryLists[1]).toEqual(
originalConfig.databases.remote.repositoryLists[1],
);
expect(updatedConfig.selected).toEqual({
kind: SelectedDbItemKind.RemoteRepository,
repositoryName: selectedRemoteRepo,
listName: "listRenamed",
});
});
});
describe("renameLocalDb", () => {
it("should rename a local db", () => {
const db1 = createLocalDbConfigItem({ name: "db1" });
const db2 = createLocalDbConfigItem({ name: "db2" });
const originalConfig = createDbConfig({
localLists: [
{
name: "list1",
databases: [
createLocalDbConfigItem({ name: "db1" }),
createLocalDbConfigItem({ name: "db2" }),
],
},
],
localDbs: [db1, db2],
});
const updatedConfig = renameLocalDb(originalConfig, "db1", "dbRenamed");
const updatedLocalDbs = updatedConfig.databases.local;
const originalLocalDbs = originalConfig.databases.local;
expect(updatedLocalDbs.lists).toEqual(originalLocalDbs.lists);
expect(updatedLocalDbs.databases.length).toEqual(2);
expect(updatedLocalDbs.databases[0]).toEqual({
...db1,
name: "dbRenamed",
});
expect(updatedLocalDbs.databases[1]).toEqual(db2);
});
it("should rename a local db inside a list", () => {
const db1List1 = createLocalDbConfigItem({ name: "db1" });
const db2List1 = createLocalDbConfigItem({ name: "db2" });
const originalConfig = createDbConfig({
localLists: [
{
name: "list1",
databases: [db1List1, db2List1],
},
{
name: "list2",
databases: [
createLocalDbConfigItem({ name: "db1" }),
createLocalDbConfigItem({ name: "db2" }),
],
},
],
localDbs: [
createLocalDbConfigItem({ name: "db1" }),
createLocalDbConfigItem({ name: "db2" }),
],
});
const updatedConfig = renameLocalDb(
originalConfig,
db1List1.name,
"dbRenamed",
"list1",
);
const updatedLocalDbs = updatedConfig.databases.local;
const originalLocalDbs = originalConfig.databases.local;
expect(updatedLocalDbs.databases).toEqual(originalLocalDbs.databases);
expect(updatedLocalDbs.lists.length).toEqual(2);
expect(updatedLocalDbs.lists[0].databases.length).toEqual(2);
expect(updatedLocalDbs.lists[0].databases[0]).toEqual({
...db1List1,
name: "dbRenamed",
});
expect(updatedLocalDbs.lists[0].databases[1]).toEqual(db2List1);
expect(updatedLocalDbs.lists[1]).toEqual(originalLocalDbs.lists[1]);
});
it("should rename a local db that is selected", () => {
const db1 = createLocalDbConfigItem({ name: "db1" });
const db2 = createLocalDbConfigItem({ name: "db2" });
const originalConfig = createDbConfig({
localLists: [
{
name: "list1",
databases: [
createLocalDbConfigItem({ name: "db1" }),
createLocalDbConfigItem({ name: "db2" }),
],
},
],
localDbs: [db1, db2],
selected: {
kind: SelectedDbItemKind.LocalDatabase,
databaseName: "db1",
},
});
const updatedConfig = renameLocalDb(originalConfig, "db1", "dbRenamed");
const updatedLocalDbs = updatedConfig.databases.local;
const originalLocalDbs = originalConfig.databases.local;
expect(updatedLocalDbs.lists).toEqual(originalLocalDbs.lists);
expect(updatedLocalDbs.databases.length).toEqual(2);
expect(updatedLocalDbs.databases[0]).toEqual({
...db1,
name: "dbRenamed",
});
expect(updatedLocalDbs.databases[1]).toEqual(db2);
});
});
});