Skip to content

Commit 952faf5

Browse files
authored
Merge pull request #1889 from github/shati-nora/add-remote-repositories
Create "Add database" button in DB panel
2 parents 5a32486 + eb3ba1e commit 952faf5

7 files changed

Lines changed: 150 additions & 13 deletions

File tree

extensions/ql-vscode/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"onCommand:codeQLDatabases.chooseDatabase",
6161
"onCommand:codeQLDatabases.setCurrentDatabase",
6262
"onCommand:codeQLDatabasesExperimental.openConfigFile",
63+
"onCommand:codeQLDatabasesExperimental.addNewDatabase",
6364
"onCommand:codeQLDatabasesExperimental.addNewList",
6465
"onCommand:codeQLDatabasesExperimental.setSelectedItem",
6566
"onCommand:codeQL.quickQuery",
@@ -362,6 +363,11 @@
362363
"title": "Open Database Configuration File",
363364
"icon": "$(edit)"
364365
},
366+
{
367+
"command": "codeQLDatabasesExperimental.addNewDatabase",
368+
"title": "Add new database",
369+
"icon": "$(add)"
370+
},
365371
{
366372
"command": "codeQLDatabasesExperimental.addNewList",
367373
"title": "Add new list",
@@ -758,6 +764,11 @@
758764
"when": "view == codeQLDatabasesExperimental",
759765
"group": "navigation"
760766
},
767+
{
768+
"command": "codeQLDatabasesExperimental.addNewDatabase",
769+
"when": "view == codeQLDatabasesExperimental && codeQLDatabasesExperimental.configError == false",
770+
"group": "navigation"
771+
},
761772
{
762773
"command": "codeQLDatabasesExperimental.addNewList",
763774
"when": "view == codeQLDatabasesExperimental && codeQLDatabasesExperimental.configError == false",
@@ -981,6 +992,10 @@
981992
"command": "codeQLDatabasesExperimental.openConfigFile",
982993
"when": "false"
983994
},
995+
{
996+
"command": "codeQLDatabasesExperimental.addNewDatabase",
997+
"when": "false"
998+
},
984999
{
9851000
"command": "codeQLDatabasesExperimental.addNewList",
9861001
"when": "false"

extensions/ql-vscode/src/common/github-url-identifier-helper.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { OWNER_REGEX, REPO_REGEX } from "../pure/helpers-pure";
55
* @param identifier The GitHub NWO
66
* @returns
77
*/
8-
export function validGitHubNwo(identifier: string): boolean {
8+
export function isValidGitHubNwo(identifier: string): boolean {
99
return validGitHubNwoOrOwner(identifier, "nwo");
1010
}
1111

@@ -14,7 +14,7 @@ export function validGitHubNwo(identifier: string): boolean {
1414
* @param identifier The GitHub owner
1515
* @returns
1616
*/
17-
export function validGitHubOwner(identifier: string): boolean {
17+
export function isValidGitHubOwner(identifier: string): boolean {
1818
return validGitHubNwoOrOwner(identifier, "owner");
1919
}
2020

@@ -28,7 +28,7 @@ function validGitHubNwoOrOwner(
2828
}
2929

3030
/**
31-
* Extracts an NOW from a GitHub URL.
31+
* Extracts an NWO from a GitHub URL.
3232
* @param githubUrl The GitHub repository URL
3333
* @return The corresponding NWO, or undefined if the URL is not valid
3434
*/

extensions/ql-vscode/src/databaseFetcher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { Credentials } from "./authentication";
2424
import { getErrorMessage } from "./pure/helpers-pure";
2525
import {
2626
getNwoFromGitHubUrl,
27-
validGitHubNwo,
27+
isValidGitHubNwo,
2828
} from "./common/github-url-identifier-helper";
2929

3030
/**
@@ -101,7 +101,7 @@ export async function promptImportGithubDatabase(
101101
}
102102

103103
const nwo = getNwoFromGitHubUrl(githubRepo) || githubRepo;
104-
if (!validGitHubNwo(nwo)) {
104+
if (!isValidGitHubNwo(nwo)) {
105105
throw new Error(`Invalid GitHub repository: ${githubRepo}`);
106106
}
107107

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,28 @@ export class DbConfigStore extends DisposableObject {
9494
await this.writeConfig(config);
9595
}
9696

97+
public async addRemoteRepo(repoNwo: string): Promise<void> {
98+
if (!this.config) {
99+
throw Error("Cannot add remote repo if config is not loaded");
100+
}
101+
102+
const config: DbConfig = cloneDbConfig(this.config);
103+
config.databases.remote.repositories.push(repoNwo);
104+
105+
await this.writeConfig(config);
106+
}
107+
108+
public async addRemoteOwner(owner: string): Promise<void> {
109+
if (!this.config) {
110+
throw Error("Cannot add remote owner if config is not loaded");
111+
}
112+
113+
const config: DbConfig = cloneDbConfig(this.config);
114+
config.databases.remote.owners.push(owner);
115+
116+
await this.writeConfig(config);
117+
}
118+
97119
public async addRemoteList(listName: string): Promise<void> {
98120
if (!this.config) {
99121
throw Error("Cannot add remote list if config is not loaded");

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ export class DbManager {
7575
await this.dbConfigStore.updateExpandedState(newExpandedItems);
7676
}
7777

78+
public async addNewRemoteRepo(nwo: string): Promise<void> {
79+
await this.dbConfigStore.addRemoteRepo(nwo);
80+
}
81+
82+
public async addNewRemoteOwner(owner: string): Promise<void> {
83+
await this.dbConfigStore.addRemoteOwner(owner);
84+
}
85+
7886
public async addNewList(kind: DbItemKind, listName: string): Promise<void> {
7987
if (remoteDbKinds.includes(kind)) {
8088
if (listName === "") {

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

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
1-
import { TreeView, TreeViewExpansionEvent, window, workspace } from "vscode";
2-
import { commandRunner } from "../../commandRunner";
1+
import {
2+
QuickPickItem,
3+
TreeView,
4+
TreeViewExpansionEvent,
5+
window,
6+
workspace,
7+
} from "vscode";
8+
import { commandRunner, UserCancellationException } from "../../commandRunner";
9+
import {
10+
getNwoFromGitHubUrl,
11+
isValidGitHubNwo,
12+
getOwnerFromGitHubUrl,
13+
isValidGitHubOwner,
14+
} from "../../common/github-url-identifier-helper";
315
import { DisposableObject } from "../../pure/disposable-object";
416
import { DbItem, DbItemKind } from "../db-item";
517
import { DbManager } from "../db-manager";
618
import { DbTreeDataProvider } from "./db-tree-data-provider";
719
import { DbTreeViewItem } from "./db-tree-view-item";
820

21+
interface RemoteDatabaseQuickPickItem extends QuickPickItem {
22+
kind: string;
23+
}
24+
925
export class DbPanel extends DisposableObject {
1026
private readonly dataProvider: DbTreeDataProvider;
1127
private readonly treeView: TreeView<DbTreeViewItem>;
@@ -40,6 +56,11 @@ export class DbPanel extends DisposableObject {
4056
this.openConfigFile(),
4157
),
4258
);
59+
this.push(
60+
commandRunner("codeQLDatabasesExperimental.addNewDatabase", () =>
61+
this.addNewRemoteDatabase(),
62+
),
63+
);
4364
this.push(
4465
commandRunner("codeQLDatabasesExperimental.addNewList", () =>
4566
this.addNewRemoteList(),
@@ -59,6 +80,77 @@ export class DbPanel extends DisposableObject {
5980
await window.showTextDocument(document);
6081
}
6182

83+
private async addNewRemoteDatabase(): Promise<void> {
84+
const quickPickItems = [
85+
{
86+
label: "$(repo) From a GitHub repository",
87+
detail: "Add a remote repository from GitHub",
88+
alwaysShow: true,
89+
kind: "repo",
90+
},
91+
{
92+
label: "$(organization) All repositories of a GitHub org or owner",
93+
detail:
94+
"Add a remote list of repositories from a GitHub organization/owner",
95+
alwaysShow: true,
96+
kind: "owner",
97+
},
98+
];
99+
const databaseKind =
100+
await window.showQuickPick<RemoteDatabaseQuickPickItem>(quickPickItems, {
101+
title: "Add a remote repository",
102+
placeHolder: "Select an option",
103+
ignoreFocusOut: true,
104+
});
105+
if (!databaseKind) {
106+
// We don't need to display a warning pop-up in this case, since the user just escaped out of the operation.
107+
// We set 'true' to make this a silent exception.
108+
throw new UserCancellationException("No repository selected", true);
109+
}
110+
if (databaseKind.kind === "repo") {
111+
await this.addNewRemoteRepo();
112+
} else if (databaseKind.kind === "owner") {
113+
await this.addNewRemoteOwner();
114+
}
115+
}
116+
117+
private async addNewRemoteRepo(): Promise<void> {
118+
const repoName = await window.showInputBox({
119+
title: "Add a remote repository",
120+
prompt: "Insert a GitHub repository URL or name with owner",
121+
placeHolder: "github.com/<owner>/<repo> or <owner>/<repo>",
122+
});
123+
if (!repoName) {
124+
return;
125+
}
126+
127+
const nwo = getNwoFromGitHubUrl(repoName) || repoName;
128+
if (!isValidGitHubNwo(nwo)) {
129+
throw new Error(`Invalid GitHub repository: ${repoName}`);
130+
}
131+
132+
await this.dbManager.addNewRemoteRepo(nwo);
133+
}
134+
135+
private async addNewRemoteOwner(): Promise<void> {
136+
const ownerName = await window.showInputBox({
137+
title: "Add all repositories of a GitHub org or owner",
138+
prompt: "Insert a GitHub organization or owner name",
139+
placeHolder: "github.com/<owner> or <owner>",
140+
});
141+
142+
if (!ownerName) {
143+
return;
144+
}
145+
146+
const owner = getOwnerFromGitHubUrl(ownerName) || ownerName;
147+
if (!isValidGitHubOwner(owner)) {
148+
throw new Error(`Invalid user or organization: ${owner}`);
149+
}
150+
151+
await this.dbManager.addNewRemoteOwner(owner);
152+
}
153+
62154
private async addNewRemoteList(): Promise<void> {
63155
const listName = await window.showInputBox({
64156
prompt: "Enter a name for the new list",

extensions/ql-vscode/test/pure-tests/common/github-url-identifier-helper.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
import {
22
getNwoFromGitHubUrl,
33
getOwnerFromGitHubUrl,
4-
validGitHubNwo,
5-
validGitHubOwner,
4+
isValidGitHubNwo,
5+
isValidGitHubOwner,
66
} from "../../../src/common/github-url-identifier-helper";
77

88
describe("github url identifier helper", () => {
99
describe("valid GitHub Nwo Or Owner method", () => {
1010
it("should return true for valid owner", () => {
11-
expect(validGitHubOwner("github")).toBe(true);
11+
expect(isValidGitHubOwner("github")).toBe(true);
1212
});
1313
it("should return true for valid NWO", () => {
14-
expect(validGitHubNwo("github/codeql")).toBe(true);
14+
expect(isValidGitHubNwo("github/codeql")).toBe(true);
1515
});
1616
it("should return false for invalid owner", () => {
17-
expect(validGitHubOwner("github/codeql")).toBe(false);
17+
expect(isValidGitHubOwner("github/codeql")).toBe(false);
1818
});
1919
it("should return false for invalid NWO", () => {
20-
expect(validGitHubNwo("githubl")).toBe(false);
20+
expect(isValidGitHubNwo("githubl")).toBe(false);
2121
});
2222
});
2323

0 commit comments

Comments
 (0)