Skip to content

Commit 2493b0f

Browse files
authored
Handle db validation errors gracefully (#1895)
1 parent dbdb4ba commit 2493b0f

5 files changed

Lines changed: 101 additions & 43 deletions

File tree

extensions/ql-vscode/src/databases/config/db-config-store.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ export class DbConfigStore extends DisposableObject {
9999
throw Error("Cannot add remote repo if config is not loaded");
100100
}
101101

102+
if (repoNwo === "") {
103+
throw Error("Repository name cannot be empty");
104+
}
105+
102106
if (this.doesRemoteDbExist(repoNwo)) {
103107
throw Error(
104108
`A remote repository with the name '${repoNwo}' already exists`,
@@ -116,6 +120,10 @@ export class DbConfigStore extends DisposableObject {
116120
throw Error("Cannot add remote owner if config is not loaded");
117121
}
118122

123+
if (owner === "") {
124+
throw Error("Owner name cannot be empty");
125+
}
126+
119127
if (this.doesRemoteOwnerExist(owner)) {
120128
throw Error(`A remote owner with the name '${owner}' already exists`);
121129
}
@@ -131,6 +139,10 @@ export class DbConfigStore extends DisposableObject {
131139
throw Error("Cannot add remote list if config is not loaded");
132140
}
133141

142+
if (listName === "") {
143+
throw Error("List name cannot be empty");
144+
}
145+
134146
if (this.doesRemoteListExist(listName)) {
135147
throw Error(`A remote list with the name '${listName}' already exists`);
136148
}
@@ -154,6 +166,14 @@ export class DbConfigStore extends DisposableObject {
154166
);
155167
}
156168

169+
public doesLocalListExist(listName: string): boolean {
170+
if (!this.config) {
171+
throw Error("Cannot check local list existence if config is not loaded");
172+
}
173+
174+
return this.config.databases.local.lists.some((l) => l.name === listName);
175+
}
176+
157177
public doesRemoteDbExist(dbName: string, listName?: string): boolean {
158178
if (!this.config) {
159179
throw Error(

extensions/ql-vscode/src/databases/db-item.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ export const localDbKinds = [
2525
DbItemKind.LocalDatabase,
2626
];
2727

28+
export enum DbListKind {
29+
Local = "Local",
30+
Remote = "Remote",
31+
}
32+
2833
export interface RootLocalDbItem {
2934
kind: DbItemKind.RootLocal;
3035
expanded: boolean;

extensions/ql-vscode/src/databases/db-manager.ts

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { App } from "../common/app";
22
import { AppEvent, AppEventEmitter } from "../common/events";
33
import { ValueResult } from "../common/value-result";
44
import { DbConfigStore } from "./config/db-config-store";
5-
import { DbItem, DbItemKind, remoteDbKinds } from "./db-item";
5+
import { DbItem, DbListKind } from "./db-item";
66
import { calculateNewExpandedState } from "./db-item-expansion";
77
import {
88
getSelectedDbItem,
@@ -76,39 +76,45 @@ export class DbManager {
7676
}
7777

7878
public async addNewRemoteRepo(nwo: string): Promise<void> {
79-
if (nwo === "") {
80-
throw new Error("Repository name cannot be empty");
81-
}
82-
if (this.dbConfigStore.doesRemoteDbExist(nwo)) {
83-
throw new Error(`The repository '${nwo}' already exists`);
84-
}
85-
8679
await this.dbConfigStore.addRemoteRepo(nwo);
8780
}
8881

8982
public async addNewRemoteOwner(owner: string): Promise<void> {
90-
if (owner === "") {
91-
throw Error("Owner name cannot be empty");
83+
await this.dbConfigStore.addRemoteOwner(owner);
84+
}
85+
86+
public async addNewList(
87+
listKind: DbListKind,
88+
listName: string,
89+
): Promise<void> {
90+
switch (listKind) {
91+
case DbListKind.Local:
92+
// Adding a local list is not supported yet.
93+
throw Error("Cannot add a local list");
94+
case DbListKind.Remote:
95+
await this.dbConfigStore.addRemoteList(listName);
96+
break;
97+
default:
98+
throw Error(`Unknown list kind '${listKind}'`);
9299
}
93-
if (this.dbConfigStore.doesRemoteOwnerExist(owner)) {
94-
throw Error(`The owner '${owner}' already exists`);
100+
}
101+
102+
public doesListExist(listKind: DbListKind, listName: string): boolean {
103+
switch (listKind) {
104+
case DbListKind.Local:
105+
return this.dbConfigStore.doesLocalListExist(listName);
106+
case DbListKind.Remote:
107+
return this.dbConfigStore.doesRemoteListExist(listName);
108+
default:
109+
throw Error(`Unknown list kind '${listKind}'`);
95110
}
111+
}
96112

97-
await this.dbConfigStore.addRemoteOwner(owner);
113+
public doesRemoteOwnerExist(owner: string): boolean {
114+
return this.dbConfigStore.doesRemoteOwnerExist(owner);
98115
}
99116

100-
public async addNewList(kind: DbItemKind, listName: string): Promise<void> {
101-
if (remoteDbKinds.includes(kind)) {
102-
if (listName === "") {
103-
throw Error("List name cannot be empty");
104-
}
105-
if (this.dbConfigStore.doesRemoteListExist(listName)) {
106-
throw Error(`A list with the name '${listName}' already exists`);
107-
}
108-
109-
await this.dbConfigStore.addRemoteList(listName);
110-
} else {
111-
throw Error("Cannot add a local list");
112-
}
117+
public doesRemoteRepoExist(nwo: string, listName?: string): boolean {
118+
return this.dbConfigStore.doesRemoteDbExist(nwo, listName);
113119
}
114120
}

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

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import {
1212
getOwnerFromGitHubUrl,
1313
isValidGitHubOwner,
1414
} from "../../common/github-url-identifier-helper";
15+
import { showAndLogErrorMessage } from "../../helpers";
1516
import { DisposableObject } from "../../pure/disposable-object";
16-
import { DbItem, DbItemKind } from "../db-item";
17+
import { DbItem, DbItemKind, DbListKind, remoteDbKinds } from "../db-item";
1718
import { DbManager } from "../db-manager";
1819
import { DbTreeDataProvider } from "./db-tree-data-provider";
1920
import { DbTreeViewItem } from "./db-tree-view-item";
@@ -126,7 +127,13 @@ export class DbPanel extends DisposableObject {
126127

127128
const nwo = getNwoFromGitHubUrl(repoName) || repoName;
128129
if (!isValidGitHubNwo(nwo)) {
129-
throw new Error(`Invalid GitHub repository: ${repoName}`);
130+
void showAndLogErrorMessage(`Invalid GitHub repository: ${repoName}`);
131+
return;
132+
}
133+
134+
if (this.dbManager.doesRemoteRepoExist(nwo)) {
135+
void showAndLogErrorMessage(`The repository '${nwo}' already exists`);
136+
return;
130137
}
131138

132139
await this.dbManager.addNewRemoteRepo(nwo);
@@ -145,7 +152,13 @@ export class DbPanel extends DisposableObject {
145152

146153
const owner = getOwnerFromGitHubUrl(ownerName) || ownerName;
147154
if (!isValidGitHubOwner(owner)) {
148-
throw new Error(`Invalid user or organization: ${owner}`);
155+
void showAndLogErrorMessage(`Invalid user or organization: ${owner}`);
156+
return;
157+
}
158+
159+
if (this.dbManager.doesRemoteOwnerExist(owner)) {
160+
void showAndLogErrorMessage(`The owner '${owner}' already exists`);
161+
return;
149162
}
150163

151164
await this.dbManager.addNewRemoteOwner(owner);
@@ -156,7 +169,7 @@ export class DbPanel extends DisposableObject {
156169
prompt: "Enter a name for the new list",
157170
placeHolder: "example-list",
158171
});
159-
if (listName === undefined) {
172+
if (listName === undefined || listName === "") {
160173
return;
161174
}
162175

@@ -166,7 +179,15 @@ export class DbPanel extends DisposableObject {
166179
// we default to the "RootRemote" kind.
167180
// In future: if the highlighted item is undefined, we'll show a quick pick where
168181
// a user can select whether to add a remote or local list.
169-
const listKind = highlightedItem?.kind || DbItemKind.RootRemote;
182+
const highlightedItemKind = highlightedItem?.kind || DbItemKind.RootRemote;
183+
const listKind = remoteDbKinds.includes(highlightedItemKind)
184+
? DbListKind.Remote
185+
: DbListKind.Local;
186+
187+
if (this.dbManager.doesListExist(listKind, listName)) {
188+
void showAndLogErrorMessage(`The list '${listName}' already exists`);
189+
return;
190+
}
170191

171192
await this.dbManager.addNewList(listKind, listName);
172193
}

extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases/db-panel.test.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import {
88
import { DbManager } from "../../../databases/db-manager";
99
import { DbConfigStore } from "../../../databases/config/db-config-store";
1010
import { DbTreeDataProvider } from "../../../databases/ui/db-tree-data-provider";
11-
import { DbItemKind, LocalDatabaseDbItem } from "../../../databases/db-item";
11+
import {
12+
DbItemKind,
13+
DbListKind,
14+
LocalDatabaseDbItem,
15+
} from "../../../databases/db-item";
1216
import { DbTreeViewItem } from "../../../databases/ui/db-tree-view-item";
1317
import { ExtensionApp } from "../../../common/vscode/vscode-app";
1418
import { createMockExtensionContext } from "../../factories/extension-context";
@@ -478,7 +482,7 @@ describe("db panel", () => {
478482
expect(remoteUserDefinedLists.length).toBe(1);
479483
expect(remoteUserDefinedLists[0]).toBe(list1);
480484

481-
await dbManager.addNewList(DbItemKind.RootRemote, "my-list-2");
485+
await dbManager.addNewList(DbListKind.Remote, "my-list-2");
482486

483487
// Read the workspace databases JSON file directly to check that the new list has been added.
484488
// We can't use the dbConfigStore's `read` function here because it depends on the file watcher
@@ -497,9 +501,9 @@ describe("db panel", () => {
497501
const dbConfig: DbConfig = createDbConfig();
498502
await saveDbConfig(dbConfig);
499503

500-
await expect(
501-
dbManager.addNewList(DbItemKind.RootLocal, ""),
502-
).rejects.toThrow(new Error("Cannot add a local list"));
504+
await expect(dbManager.addNewList(DbListKind.Local, "")).rejects.toThrow(
505+
new Error("Cannot add a local list"),
506+
);
503507
});
504508
});
505509

@@ -566,9 +570,9 @@ describe("db panel", () => {
566570

567571
await saveDbConfig(dbConfig);
568572

569-
await expect(
570-
dbManager.addNewList(DbItemKind.RootRemote, ""),
571-
).rejects.toThrow(new Error("List name cannot be empty"));
573+
await expect(dbManager.addNewList(DbListKind.Remote, "")).rejects.toThrow(
574+
new Error("List name cannot be empty"),
575+
);
572576
});
573577

574578
it("should not allow adding a list with duplicate name", async () => {
@@ -584,9 +588,9 @@ describe("db panel", () => {
584588
await saveDbConfig(dbConfig);
585589

586590
await expect(
587-
dbManager.addNewList(DbItemKind.RootRemote, "my-list-1"),
591+
dbManager.addNewList(DbListKind.Remote, "my-list-1"),
588592
).rejects.toThrow(
589-
new Error("A list with the name 'my-list-1' already exists"),
593+
new Error("A remote list with the name 'my-list-1' already exists"),
590594
);
591595
});
592596

@@ -608,7 +612,9 @@ describe("db panel", () => {
608612
await saveDbConfig(dbConfig);
609613

610614
await expect(dbManager.addNewRemoteRepo("owner1/repo1")).rejects.toThrow(
611-
new Error("The repository 'owner1/repo1' already exists"),
615+
new Error(
616+
"A remote repository with the name 'owner1/repo1' already exists",
617+
),
612618
);
613619
});
614620

@@ -630,7 +636,7 @@ describe("db panel", () => {
630636
await saveDbConfig(dbConfig);
631637

632638
await expect(dbManager.addNewRemoteOwner("owner1")).rejects.toThrow(
633-
new Error("The owner 'owner1' already exists"),
639+
new Error("A remote owner with the name 'owner1' already exists"),
634640
);
635641
});
636642
});

0 commit comments

Comments
 (0)