Skip to content

Commit b83ef4e

Browse files
committed
Split up listing of databases to separate function
1 parent 9dd061b commit b83ef4e

5 files changed

Lines changed: 471 additions & 52 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { RequestError } from "@octokit/request-error";
2+
import { Octokit } from "@octokit/rest";
3+
import { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
4+
import { showNeverAskAgainDialog } from "../common/vscode/dialog";
5+
import { GitHubDatabaseConfig } from "../config";
6+
import { Credentials } from "../common/authentication";
7+
import { AppOctokit } from "../common/octokit";
8+
9+
export type CodeqlDatabase =
10+
RestEndpointMethodTypes["codeScanning"]["listCodeqlDatabases"]["response"]["data"][number];
11+
12+
/**
13+
* Ask the user if they want to connect to GitHub to download CodeQL databases.
14+
* This should be used when the user does not have an access token and should
15+
* be followed by an access token prompt.
16+
*/
17+
async function askForGitHubConnect(
18+
config: GitHubDatabaseConfig,
19+
): Promise<boolean> {
20+
const answer = await showNeverAskAgainDialog(
21+
"This repository has an origin (GitHub) that may have one or more CodeQL databases. Connect to GitHub and download any existing databases?",
22+
false,
23+
"Connect",
24+
"Not now",
25+
"Never",
26+
);
27+
28+
if (answer === "Not now" || answer === undefined) {
29+
return false;
30+
}
31+
32+
if (answer === "Never") {
33+
await config.setDownload("never");
34+
return false;
35+
}
36+
37+
return true;
38+
}
39+
40+
export type ListDatabasesResult = {
41+
/**
42+
* Whether the user has been prompted for credentials. This can be used to determine
43+
* follow-up actions based on whether the user has already had any feedback.
44+
*/
45+
promptedForCredentials: boolean;
46+
databases: CodeqlDatabase[];
47+
octokit: Octokit;
48+
};
49+
50+
/**
51+
* List CodeQL databases for a GitHub repository.
52+
*
53+
* This will first try to fetch the CodeQL databases for the repository with
54+
* existing credentials (or none if there are none). If that fails, it will
55+
* prompt the user to connect to GitHub and try again.
56+
*
57+
* If the user does not want to connect to GitHub, this will return `undefined`.
58+
*/
59+
export async function listDatabases(
60+
owner: string,
61+
repo: string,
62+
credentials: Credentials,
63+
config: GitHubDatabaseConfig,
64+
): Promise<ListDatabasesResult | undefined> {
65+
const hasAccessToken = !!(await credentials.getExistingAccessToken());
66+
67+
let octokit = hasAccessToken
68+
? await credentials.getOctokit()
69+
: new AppOctokit();
70+
71+
let promptedForCredentials = false;
72+
73+
let databases: CodeqlDatabase[];
74+
try {
75+
const response = await octokit.rest.codeScanning.listCodeqlDatabases({
76+
owner,
77+
repo,
78+
});
79+
databases = response.data;
80+
} catch (e) {
81+
// If we get a 404 when we don't have an access token, it might be because
82+
// the repository is private/internal. Therefore, we should ask the user
83+
// whether they want to connect to GitHub and try again.
84+
if (e instanceof RequestError && e.status === 404 && !hasAccessToken) {
85+
// Check whether the user wants to connect to GitHub
86+
if (!(await askForGitHubConnect(config))) {
87+
return;
88+
}
89+
90+
// Prompt for credentials
91+
octokit = await credentials.getOctokit();
92+
93+
promptedForCredentials = true;
94+
95+
const response = await octokit.rest.codeScanning.listCodeqlDatabases({
96+
owner,
97+
repo,
98+
});
99+
databases = response.data;
100+
} else {
101+
throw e;
102+
}
103+
}
104+
105+
return {
106+
promptedForCredentials,
107+
databases,
108+
octokit,
109+
};
110+
}

extensions/ql-vscode/src/databases/github-database-download.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { window } from "vscode";
2-
import { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
32
import { Octokit } from "@octokit/rest";
43
import { showNeverAskAgainDialog } from "../common/vscode/dialog";
54
import { getLanguageDisplayName } from "../common/query-language";
@@ -12,22 +11,7 @@ import { DatabaseManager } from "./local-databases";
1211
import { CodeQLCliServer } from "../codeql-cli/cli";
1312
import { AppCommandManager } from "../common/commands";
1413
import { GitHubDatabaseConfig } from "../config";
15-
16-
export type CodeqlDatabase =
17-
RestEndpointMethodTypes["codeScanning"]["listCodeqlDatabases"]["response"]["data"][number];
18-
19-
export async function findGitHubDatabasesForRepository(
20-
octokit: Octokit,
21-
owner: string,
22-
repo: string,
23-
): Promise<CodeqlDatabase[]> {
24-
const response = await octokit.rest.codeScanning.listCodeqlDatabases({
25-
owner,
26-
repo,
27-
});
28-
29-
return response.data;
30-
}
14+
import type { CodeqlDatabase } from "./github-database-api";
3115

3216
/**
3317
* Ask whether the user wants to download a database from GitHub.

extensions/ql-vscode/src/databases/github-database-module.ts

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@ import { redactableError } from "../common/errors";
66
import { asError, getErrorMessage } from "../common/helpers-pure";
77
import {
88
askForGitHubDatabaseDownload,
9-
CodeqlDatabase,
109
downloadDatabaseFromGitHub,
11-
findGitHubDatabasesForRepository,
1210
} from "./github-database-download";
1311
import { GitHubDatabaseConfig, GitHubDatabaseConfigListener } from "../config";
1412
import { DatabaseManager } from "./local-databases";
1513
import { CodeQLCliServer } from "../codeql-cli/cli";
16-
import { showNeverAskAgainDialog } from "../common/vscode/dialog";
14+
import { listDatabases, ListDatabasesResult } from "./github-database-api";
1715

1816
export class GithubDatabaseModule extends DisposableObject {
1917
private readonly config: GitHubDatabaseConfig;
@@ -91,38 +89,13 @@ export class GithubDatabaseModule extends DisposableObject {
9189
return;
9290
}
9391

94-
const credentials = this.app.credentials;
95-
96-
const hasAccessToken = !!(await credentials.getExistingAccessToken());
97-
98-
// If the user does not have an access token, ask whether they want to connect.
99-
if (!hasAccessToken) {
100-
const answer = await showNeverAskAgainDialog(
101-
"This repository has an origin (GitHub) that may have one or more CodeQL databases. Connect to GitHub and download any existing databases?",
102-
false,
103-
"Connect",
104-
"Not now",
105-
"Never",
106-
);
107-
108-
if (answer === "Not now" || answer === undefined) {
109-
return;
110-
}
111-
112-
if (answer === "Never") {
113-
await this.config.setDownload("never");
114-
return;
115-
}
116-
}
117-
118-
const octokit = await credentials.getOctokit();
119-
120-
let databases: CodeqlDatabase[];
92+
let result: ListDatabasesResult | undefined;
12193
try {
122-
databases = await findGitHubDatabasesForRepository(
123-
octokit,
94+
result = await listDatabases(
12495
githubRepository.owner,
12596
githubRepository.name,
97+
this.app.credentials,
98+
this.config,
12699
);
127100
} catch (e) {
128101
this.app.telemetry?.sendError(
@@ -138,10 +111,17 @@ export class GithubDatabaseModule extends DisposableObject {
138111
return;
139112
}
140113

114+
// This means the user didn't want to connect, so we can just return.
115+
if (result === undefined) {
116+
return;
117+
}
118+
119+
const { databases, promptedForCredentials, octokit } = result;
120+
141121
if (databases.length === 0) {
142122
// If the user didn't have an access token, they have already been prompted,
143123
// so we should give feedback.
144-
if (!hasAccessToken) {
124+
if (promptedForCredentials) {
145125
void window.showInformationMessage(
146126
"The GitHub repository does not have any CodeQL databases.",
147127
);
@@ -151,7 +131,7 @@ export class GithubDatabaseModule extends DisposableObject {
151131
}
152132

153133
// If the user already had an access token, first ask if they even want to download the DB.
154-
if (hasAccessToken) {
134+
if (!promptedForCredentials) {
155135
if (!(await askForGitHubDatabaseDownload(databases, this.config))) {
156136
return;
157137
}

0 commit comments

Comments
 (0)