Archived
1
0

chore(vscode): update to 1.54.2

This commit is contained in:
Joe Previte
2021-03-11 10:27:10 -07:00
1459 changed files with 53404 additions and 51004 deletions

View File

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Disposable } from 'vscode';
import { EventEmitter, Event, Disposable } from 'vscode';
export function filterEvent<T>(event: Event<T>, filter: (e: T) => boolean): Event<T> {
return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
@ -47,27 +47,54 @@ const passthrough = (value: any, resolve: (value?: any) => void) => resolve(valu
* @param adapter controls resolution of the returned promise
* @returns a promise that resolves or rejects as specified by the adapter
*/
export async function promiseFromEvent<T, U>(
export function promiseFromEvent<T, U>(
event: Event<T>,
adapter: PromiseAdapter<T, U> = passthrough): Promise<U> {
adapter: PromiseAdapter<T, U> = passthrough): { promise: Promise<U>, cancel: EventEmitter<void> } {
let subscription: Disposable;
return new Promise<U>((resolve, reject) =>
subscription = event((value: T) => {
try {
Promise.resolve(adapter(value, resolve, reject))
.catch(reject);
} catch (error) {
reject(error);
let cancel = new EventEmitter<void>();
return {
promise: new Promise<U>((resolve, reject) => {
cancel.event(_ => reject());
subscription = event((value: T) => {
try {
Promise.resolve(adapter(value, resolve, reject))
.catch(reject);
} catch (error) {
reject(error);
}
});
}).then(
(result: U) => {
subscription.dispose();
return result;
},
error => {
subscription.dispose();
throw error;
}
})
).then(
(result: U) => {
subscription.dispose();
return result;
},
error => {
subscription.dispose();
throw error;
}
);
),
cancel
};
}
export function arrayEquals<T>(one: ReadonlyArray<T> | undefined, other: ReadonlyArray<T> | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean {
if (one === other) {
return true;
}
if (!one || !other) {
return false;
}
if (one.length !== other.length) {
return false;
}
for (let i = 0, len = one.length; i < len; i++) {
if (!itemEquals(one[i], other[i])) {
return false;
}
}
return true;
}

View File

@ -14,7 +14,7 @@ export async function activate(context: vscode.ExtensionContext) {
const telemetryReporter = new TelemetryReporter(name, version, aiKey);
context.subscriptions.push(vscode.window.registerUriHandler(uriHandler));
const loginService = new GitHubAuthenticationProvider(context);
const loginService = new GitHubAuthenticationProvider(context, telemetryReporter);
await loginService.initialize(context);
@ -24,17 +24,17 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.authentication.registerAuthenticationProvider('github', 'GitHub', {
onDidChangeSessions: onDidChangeSessions.event,
getSessions: () => Promise.resolve(loginService.sessions),
login: async (scopeList: string[]) => {
getSessions: (scopes?: string[]) => loginService.getSessions(scopes),
createSession: async (scopeList: string[]) => {
try {
/* __GDPR__
"login" : { }
*/
telemetryReporter.sendTelemetryEvent('login');
const session = await loginService.login(scopeList.sort().join(' '));
const session = await loginService.createSession(scopeList.sort().join(' '));
Logger.info('Login success!');
onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] });
onDidChangeSessions.fire({ added: [session], removed: [], changed: [] });
return session;
} catch (e) {
// If login was cancelled, do not notify user.
@ -56,15 +56,17 @@ export async function activate(context: vscode.ExtensionContext) {
throw e;
}
},
logout: async (id: string) => {
removeSession: async (id: string) => {
try {
/* __GDPR__
"logout" : { }
*/
telemetryReporter.sendTelemetryEvent('logout');
await loginService.logout(id);
onDidChangeSessions.fire({ added: [], removed: [id], changed: [] });
const session = await loginService.removeSession(id);
if (session) {
onDidChangeSessions.fire({ added: [], removed: [session], changed: [] });
}
} catch (e) {
/* __GDPR__
"logoutFailed" : { }

View File

@ -8,6 +8,8 @@ import { v4 as uuid } from 'uuid';
import { Keychain } from './common/keychain';
import { GitHubServer, NETWORK_ERROR } from './githubServer';
import Logger from './common/logger';
import { arrayEquals } from './common/utils';
import TelemetryReporter from 'vscode-extension-telemetry';
export const onDidChangeSessions = new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
@ -24,12 +26,13 @@ interface SessionData {
export class GitHubAuthenticationProvider {
private _sessions: vscode.AuthenticationSession[] = [];
private _githubServer = new GitHubServer();
private _githubServer: GitHubServer;
private _keychain: Keychain;
constructor(context: vscode.ExtensionContext) {
constructor(context: vscode.ExtensionContext, telemetryReporter: TelemetryReporter) {
this._keychain = new Keychain(context);
this._githubServer = new GitHubServer(telemetryReporter);
}
public async initialize(context: vscode.ExtensionContext): Promise<void> {
@ -43,11 +46,18 @@ export class GitHubAuthenticationProvider {
context.subscriptions.push(context.secrets.onDidChange(() => this.checkForUpdates()));
}
async getSessions(scopes?: string[]): Promise<vscode.AuthenticationSession[]> {
return scopes
? this._sessions.filter(session => arrayEquals(session.scopes, scopes))
: this._sessions;
}
private async verifySessions(): Promise<void> {
const verifiedSessions: vscode.AuthenticationSession[] = [];
const verificationPromises = this._sessions.map(async session => {
try {
await this._githubServer.getUserInfo(session.accessToken);
this._githubServer.checkIsEdu(session.accessToken);
verifiedSessions.push(session);
} catch (e) {
// Remove sessions that return unauthorized response
@ -74,8 +84,8 @@ export class GitHubAuthenticationProvider {
return;
}
const added: string[] = [];
const removed: string[] = [];
const added: vscode.AuthenticationSession[] = [];
const removed: vscode.AuthenticationSession[] = [];
storedSessions.forEach(session => {
const matchesExisting = this._sessions.some(s => s.id === session.id);
@ -83,7 +93,7 @@ export class GitHubAuthenticationProvider {
if (!matchesExisting) {
Logger.info('Adding session found in keychain');
this._sessions.push(session);
added.push(session.id);
added.push(session);
}
});
@ -97,7 +107,7 @@ export class GitHubAuthenticationProvider {
this._sessions.splice(sessionIndex, 1);
}
removed.push(session.id);
removed.push(session);
}
});
@ -153,9 +163,10 @@ export class GitHubAuthenticationProvider {
return this._sessions;
}
public async login(scopes: string): Promise<vscode.AuthenticationSession> {
public async createSession(scopes: string): Promise<vscode.AuthenticationSession> {
const token = await this._githubServer.login(scopes);
const session = await this.tokenToSession(token, scopes.split(' '));
this._githubServer.checkIsEdu(token);
await this.setToken(session);
return session;
}
@ -185,15 +196,18 @@ export class GitHubAuthenticationProvider {
await this.storeSessions();
}
public async logout(id: string) {
public async removeSession(id: string): Promise<vscode.AuthenticationSession | undefined> {
Logger.info(`Logging out of ${id}`);
const sessionIndex = this._sessions.findIndex(session => session.id === id);
let session: vscode.AuthenticationSession | undefined;
if (sessionIndex > -1) {
session = this._sessions[sessionIndex];
this._sessions.splice(sessionIndex, 1);
} else {
Logger.error('Session not found');
}
await this.storeSessions();
return session;
}
}

View File

@ -9,6 +9,7 @@ import fetch, { Response } from 'node-fetch';
import { v4 as uuid } from 'uuid';
import { PromiseAdapter, promiseFromEvent } from './common/utils';
import Logger from './common/logger';
import TelemetryReporter from 'vscode-extension-telemetry';
const localize = nls.loadMessageBundle();
@ -39,7 +40,9 @@ export class GitHubServer {
private _statusBarItem: vscode.StatusBarItem | undefined;
private _pendingStates = new Map<string, string[]>();
private _codeExchangePromises = new Map<string, Promise<string>>();
private _codeExchangePromises = new Map<string, { promise: Promise<string>, cancel: vscode.EventEmitter<void> }>();
constructor(private readonly telemetryReporter: TelemetryReporter) { }
private isTestEnvironment(url: vscode.Uri): boolean {
return url.authority === 'vscode-web-test-playground.azurewebsites.net' || url.authority.startsWith('localhost:');
@ -83,17 +86,21 @@ export class GitHubServer {
// Register a single listener for the URI callback, in case the user starts the login process multiple times
// before completing it.
let existingPromise = this._codeExchangePromises.get(scopes);
if (!existingPromise) {
existingPromise = promiseFromEvent(uriHandler.event, this.exchangeCodeForToken(scopes));
this._codeExchangePromises.set(scopes, existingPromise);
let codeExchangePromise = this._codeExchangePromises.get(scopes);
if (!codeExchangePromise) {
codeExchangePromise = promiseFromEvent(uriHandler.event, this.exchangeCodeForToken(scopes));
this._codeExchangePromises.set(scopes, codeExchangePromise);
}
return Promise.race([
existingPromise,
promiseFromEvent<string | undefined, string>(onDidManuallyProvideToken.event, (token: string | undefined): string => { if (!token) { throw new Error('Cancelled'); } return token; })
codeExchangePromise.promise,
promiseFromEvent<string | undefined, string>(onDidManuallyProvideToken.event, (token: string | undefined): string => {
if (!token) { throw new Error('Cancelled'); }
return token;
}).promise
]).finally(() => {
this._pendingStates.delete(scopes);
codeExchangePromise?.cancel.fire();
this._codeExchangePromises.delete(scopes);
this.updateStatusBarItem(false);
});
@ -153,7 +160,7 @@ export class GitHubServer {
}
try {
const uri = vscode.Uri.parse(uriOrToken);
const uri = vscode.Uri.parse(uriOrToken.trim());
if (!uri.scheme || uri.scheme === 'file') { throw new Error; }
uriHandler.handleUri(uri);
} catch (e) {
@ -210,4 +217,36 @@ export class GitHubServer {
throw new Error(result.statusText);
}
}
public async checkIsEdu(token: string): Promise<void> {
try {
const result = await fetch('https://education.github.com/api/user', {
headers: {
Authorization: `token ${token}`,
'faculty-check-preview': 'true',
'User-Agent': 'Visual-Studio-Code'
}
});
if (result.ok) {
const json: { student: boolean, faculty: boolean } = await result.json();
/* __GDPR__
"session" : {
"isEdu": { "classification": "NonIdentifiableDemographicInfo", "purpose": "FeatureInsight" }
}
*/
this.telemetryReporter.sendTelemetryEvent('session', {
isEdu: json.student
? 'student'
: json.faculty
? 'faculty'
: 'none'
});
}
} catch (e) {
// No-op
}
}
}