Skip to content

Commit 25dd679

Browse files
Merge pull request #1892 from github/robertbrignull/undefined_credentials
Simplify the credentials class, and clear up impossible error cases
2 parents be79d68 + 6285ba7 commit 25dd679

14 files changed

Lines changed: 500 additions & 674 deletions

extensions/ql-vscode/src/authentication.ts

Lines changed: 24 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -13,102 +13,59 @@ const SCOPES = ["repo", "gist"];
1313
* Handles authentication to GitHub, using the VS Code [authentication API](https://code.visualstudio.com/api/references/vscode-api#authentication).
1414
*/
1515
export class Credentials {
16+
/**
17+
* A specific octokit to return, otherwise a new authenticated octokit will be created when needed.
18+
*/
1619
private octokit: Octokit.Octokit | undefined;
1720

1821
// Explicitly make the constructor private, so that we can't accidentally call the constructor from outside the class
1922
// without also initializing the class.
20-
// eslint-disable-next-line @typescript-eslint/no-empty-function
21-
private constructor() {}
23+
private constructor(octokit?: Octokit.Octokit) {
24+
this.octokit = octokit;
25+
}
2226

2327
/**
24-
* Initializes an instance of credentials with an octokit instance.
28+
* Initializes a Credentials instance. This will generate octokit instances
29+
* authenticated as the user. If there is not already an authenticated GitHub
30+
* session available then the user will be prompted to log in.
2531
*
26-
* Do not call this method until you know you actually need an instance of credentials.
27-
* since calling this method will require the user to log in.
28-
*
29-
* @param context The extension context.
3032
* @returns An instance of credentials.
3133
*/
32-
static async initialize(
33-
context: vscode.ExtensionContext,
34-
): Promise<Credentials> {
35-
const c = new Credentials();
36-
c.registerListeners(context);
37-
c.octokit = await c.createOctokit(false);
38-
return c;
34+
static async initialize(): Promise<Credentials> {
35+
return new Credentials();
3936
}
4037

4138
/**
4239
* Initializes an instance of credentials with an octokit instance using
43-
* a token from the user's GitHub account. This method is meant to be
44-
* used non-interactive environments such as tests.
40+
* a specific known token. This method is meant to be used in
41+
* non-interactive environments such as tests.
4542
*
4643
* @param overrideToken The GitHub token to use for authentication.
4744
* @returns An instance of credentials.
4845
*/
4946
static async initializeWithToken(overrideToken: string) {
50-
const c = new Credentials();
51-
c.octokit = await c.createOctokit(false, overrideToken);
52-
return c;
53-
}
54-
55-
private async createOctokit(
56-
createIfNone: boolean,
57-
overrideToken?: string,
58-
): Promise<Octokit.Octokit | undefined> {
59-
if (overrideToken) {
60-
return new Octokit.Octokit({ auth: overrideToken, retry });
61-
}
62-
63-
const session = await vscode.authentication.getSession(
64-
GITHUB_AUTH_PROVIDER_ID,
65-
SCOPES,
66-
{ createIfNone },
67-
);
68-
69-
if (session) {
70-
return new Octokit.Octokit({
71-
auth: session.accessToken,
72-
retry,
73-
});
74-
} else {
75-
return undefined;
76-
}
77-
}
78-
79-
registerListeners(context: vscode.ExtensionContext): void {
80-
// Sessions are changed when a user logs in or logs out.
81-
context.subscriptions.push(
82-
vscode.authentication.onDidChangeSessions(async (e) => {
83-
if (e.provider.id === GITHUB_AUTH_PROVIDER_ID) {
84-
this.octokit = await this.createOctokit(false);
85-
}
86-
}),
87-
);
47+
return new Credentials(new Octokit.Octokit({ auth: overrideToken, retry }));
8848
}
8949

9050
/**
9151
* Creates or returns an instance of Octokit.
9252
*
93-
* @param requireAuthentication Whether the Octokit instance needs to be authenticated as user.
9453
* @returns An instance of Octokit.
9554
*/
96-
async getOctokit(requireAuthentication = true): Promise<Octokit.Octokit> {
55+
async getOctokit(): Promise<Octokit.Octokit> {
9756
if (this.octokit) {
9857
return this.octokit;
9958
}
10059

101-
this.octokit = await this.createOctokit(requireAuthentication);
102-
103-
if (!this.octokit) {
104-
if (requireAuthentication) {
105-
throw new Error("Did not initialize Octokit.");
106-
}
60+
const session = await vscode.authentication.getSession(
61+
GITHUB_AUTH_PROVIDER_ID,
62+
SCOPES,
63+
{ createIfNone: true },
64+
);
10765

108-
// We don't want to set this in this.octokit because that would prevent
109-
// authenticating when requireCredentials is true.
110-
return new Octokit.Octokit({ retry });
111-
}
112-
return this.octokit;
66+
return new Octokit.Octokit({
67+
auth: session.accessToken,
68+
retry,
69+
});
11370
}
11471
}

extensions/ql-vscode/src/databaseFetcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export async function promptImportGithubDatabase(
106106
}
107107

108108
const octokit = credentials
109-
? await credentials.getOctokit(true)
109+
? await credentials.getOctokit()
110110
: new Octokit.Octokit({ retry });
111111

112112
const result = await convertGithubNwoToDatabaseUrl(nwo, octokit, progress);

extensions/ql-vscode/src/extension.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ async function activateWithInstalledDistribution(
591591
qs,
592592
getContextStoragePath(ctx),
593593
ctx.extensionPath,
594-
() => Credentials.initialize(ctx),
594+
() => Credentials.initialize(),
595595
);
596596
databaseUI.init();
597597
ctx.subscriptions.push(databaseUI);
@@ -1236,7 +1236,7 @@ async function activateWithInstalledDistribution(
12361236
commandRunner(
12371237
"codeQL.exportRemoteQueryResults",
12381238
async (queryId: string) => {
1239-
await exportRemoteQueryResults(qhm, rqm, ctx, queryId);
1239+
await exportRemoteQueryResults(qhm, rqm, queryId);
12401240
},
12411241
),
12421242
);
@@ -1251,7 +1251,6 @@ async function activateWithInstalledDistribution(
12511251
filterSort?: RepositoriesFilterSortStateWithIds,
12521252
) => {
12531253
await exportVariantAnalysisResults(
1254-
ctx,
12551254
variantAnalysisManager,
12561255
variantAnalysisId,
12571256
filterSort,
@@ -1356,7 +1355,7 @@ async function activateWithInstalledDistribution(
13561355
"codeQL.chooseDatabaseGithub",
13571356
async (progress: ProgressCallback, token: CancellationToken) => {
13581357
const credentials = isCanary()
1359-
? await Credentials.initialize(ctx)
1358+
? await Credentials.initialize()
13601359
: undefined;
13611360
await databaseUI.handleChooseDatabaseGithub(
13621361
credentials,
@@ -1411,7 +1410,7 @@ async function activateWithInstalledDistribution(
14111410
* Credentials for authenticating to GitHub.
14121411
* These are used when making API calls.
14131412
*/
1414-
const credentials = await Credentials.initialize(ctx);
1413+
const credentials = await Credentials.initialize();
14151414
const octokit = await credentials.getOctokit();
14161415
const userInfo = await octokit.users.getAuthenticated();
14171416
void showAndLogInformationMessage(

extensions/ql-vscode/src/query-history.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ export class QueryHistoryManager extends DisposableObject {
397397
private readonly variantAnalysisManager: VariantAnalysisManager,
398398
private readonly evalLogViewer: EvalLogViewer,
399399
private readonly queryStorageDir: string,
400-
private readonly ctx: ExtensionContext,
400+
ctx: ExtensionContext,
401401
private readonly queryHistoryConfigListener: QueryHistoryConfig,
402402
private readonly labelProvider: HistoryItemLabelProvider,
403403
private readonly doCompareCallback: (
@@ -633,7 +633,7 @@ export class QueryHistoryManager extends DisposableObject {
633633
}
634634

635635
private getCredentials() {
636-
return Credentials.initialize(this.ctx);
636+
return Credentials.initialize();
637637
}
638638

639639
/**

extensions/ql-vscode/src/remote-queries/analyses-results-manager.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { pathExists } from "fs-extra";
22
import { EOL } from "os";
33
import { extname } from "path";
4-
import { CancellationToken, ExtensionContext } from "vscode";
4+
import { CancellationToken } from "vscode";
55

66
import { Credentials } from "../authentication";
77
import { Logger } from "../common";
@@ -26,7 +26,6 @@ export class AnalysesResultsManager {
2626
private readonly analysesResults: Map<string, AnalysisResults[]>;
2727

2828
constructor(
29-
private readonly ctx: ExtensionContext,
3029
private readonly cliServer: CodeQLCliServer,
3130
readonly storagePath: string,
3231
private readonly logger: Logger,
@@ -43,7 +42,7 @@ export class AnalysesResultsManager {
4342
return;
4443
}
4544

46-
const credentials = await Credentials.initialize(this.ctx);
45+
const credentials = await Credentials.initialize();
4746

4847
void this.logger.log(
4948
`Downloading and processing results for ${analysisSummary.nwo}`,
@@ -77,7 +76,7 @@ export class AnalysesResultsManager {
7776
(x) => !this.isAnalysisInMemory(x),
7877
);
7978

80-
const credentials = await Credentials.initialize(this.ctx);
79+
const credentials = await Credentials.initialize();
8180

8281
void this.logger.log("Downloading and processing analyses results");
8382

extensions/ql-vscode/src/remote-queries/export-results.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { ensureDir, writeFile } from "fs-extra";
44
import {
55
commands,
66
CancellationToken,
7-
ExtensionContext,
87
Uri,
98
ViewColumn,
109
window,
@@ -74,7 +73,6 @@ export async function exportSelectedRemoteQueryResults(
7473
export async function exportRemoteQueryResults(
7574
queryHistoryManager: QueryHistoryManager,
7675
remoteQueriesManager: RemoteQueriesManager,
77-
ctx: ExtensionContext,
7876
queryId: string,
7977
): Promise<void> {
8078
const queryHistoryItem = queryHistoryManager.getRemoteQueryById(queryId);
@@ -107,7 +105,6 @@ export async function exportRemoteQueryResults(
107105
const exportedResultsDirectory = join(exportDirectory, "exported-results");
108106

109107
await exportRemoteQueryAnalysisResults(
110-
ctx,
111108
exportedResultsDirectory,
112109
query,
113110
analysesResults,
@@ -116,7 +113,6 @@ export async function exportRemoteQueryResults(
116113
}
117114

118115
export async function exportRemoteQueryAnalysisResults(
119-
ctx: ExtensionContext,
120116
exportedResultsPath: string,
121117
query: RemoteQuery,
122118
analysesResults: AnalysisResults[],
@@ -126,7 +122,6 @@ export async function exportRemoteQueryAnalysisResults(
126122
const markdownFiles = generateMarkdown(query, analysesResults, exportFormat);
127123

128124
await exportResults(
129-
ctx,
130125
exportedResultsPath,
131126
description,
132127
markdownFiles,
@@ -141,7 +136,6 @@ const MAX_VARIANT_ANALYSIS_EXPORT_PROGRESS_STEPS = 2;
141136
* The user is prompted to select the export format.
142137
*/
143138
export async function exportVariantAnalysisResults(
144-
ctx: ExtensionContext,
145139
variantAnalysisManager: VariantAnalysisManager,
146140
variantAnalysisId: number,
147141
filterSort: RepositoriesFilterSortStateWithIds | undefined,
@@ -239,7 +233,6 @@ export async function exportVariantAnalysisResults(
239233
);
240234

241235
await exportVariantAnalysisAnalysisResults(
242-
ctx,
243236
exportedResultsDirectory,
244237
variantAnalysis,
245238
getAnalysesResults(),
@@ -251,7 +244,6 @@ export async function exportVariantAnalysisResults(
251244
}
252245

253246
export async function exportVariantAnalysisAnalysisResults(
254-
ctx: ExtensionContext,
255247
exportedResultsPath: string,
256248
variantAnalysis: VariantAnalysis,
257249
analysesResults: AsyncIterable<
@@ -284,7 +276,6 @@ export async function exportVariantAnalysisAnalysisResults(
284276
);
285277

286278
await exportResults(
287-
ctx,
288279
exportedResultsPath,
289280
description,
290281
markdownFiles,
@@ -328,7 +319,6 @@ async function determineExportFormat(): Promise<"gist" | "local" | undefined> {
328319
}
329320

330321
export async function exportResults(
331-
ctx: ExtensionContext,
332322
exportedResultsPath: string,
333323
description: string,
334324
markdownFiles: MarkdownFile[],
@@ -341,7 +331,7 @@ export async function exportResults(
341331
}
342332

343333
if (exportFormat === "gist") {
344-
await exportToGist(ctx, description, markdownFiles, progress, token);
334+
await exportToGist(description, markdownFiles, progress, token);
345335
} else if (exportFormat === "local") {
346336
await exportToLocalMarkdown(
347337
exportedResultsPath,
@@ -353,7 +343,6 @@ export async function exportResults(
353343
}
354344

355345
export async function exportToGist(
356-
ctx: ExtensionContext,
357346
description: string,
358347
markdownFiles: MarkdownFile[],
359348
progress?: ProgressCallback,
@@ -365,7 +354,7 @@ export async function exportToGist(
365354
message: "Creating Gist",
366355
});
367356

368-
const credentials = await Credentials.initialize(ctx);
357+
const credentials = await Credentials.initialize();
369358

370359
if (token?.isCancellationRequested) {
371360
throw new UserCancellationException("Cancelled");

extensions/ql-vscode/src/remote-queries/remote-queries-manager.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,19 @@ export class RemoteQueriesManager extends DisposableObject {
8181
private readonly view: RemoteQueriesView;
8282

8383
constructor(
84-
private readonly ctx: ExtensionContext,
84+
ctx: ExtensionContext,
8585
private readonly cliServer: CodeQLCliServer,
8686
private readonly storagePath: string,
8787
logger: Logger,
8888
) {
8989
super();
9090
this.analysesResultsManager = new AnalysesResultsManager(
91-
ctx,
9291
cliServer,
9392
storagePath,
9493
logger,
9594
);
9695
this.view = new RemoteQueriesView(ctx, logger, this.analysesResultsManager);
97-
this.remoteQueriesMonitor = new RemoteQueriesMonitor(ctx, logger);
96+
this.remoteQueriesMonitor = new RemoteQueriesMonitor(logger);
9897

9998
this.remoteQueryAddedEventEmitter = this.push(
10099
new EventEmitter<NewQueryEvent>(),
@@ -160,7 +159,7 @@ export class RemoteQueriesManager extends DisposableObject {
160159
progress: ProgressCallback,
161160
token: CancellationToken,
162161
): Promise<void> {
163-
const credentials = await Credentials.initialize(this.ctx);
162+
const credentials = await Credentials.initialize();
164163

165164
const {
166165
actionBranch,
@@ -218,7 +217,7 @@ export class RemoteQueriesManager extends DisposableObject {
218217
remoteQuery: RemoteQuery,
219218
cancellationToken: CancellationToken,
220219
): Promise<void> {
221-
const credentials = await Credentials.initialize(this.ctx);
220+
const credentials = await Credentials.initialize();
222221

223222
const queryWorkflowResult = await this.remoteQueriesMonitor.monitorQuery(
224223
remoteQuery,

0 commit comments

Comments
 (0)