diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index ceb66e6b18d..9c23f5dc192 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -57,7 +57,8 @@ "node-fetch": "2.6.1", "uuid": "8.1.0", "vscode-extension-telemetry": "0.1.7", - "vscode-nls": "^4.1.2" + "vscode-nls": "^4.1.2", + "vscode-tas-client": "^0.1.22" }, "devDependencies": { "@types/node": "^12.19.9", diff --git a/extensions/github-authentication/src/experimentationService.ts b/extensions/github-authentication/src/experimentationService.ts new file mode 100644 index 00000000000..7772436a7ec --- /dev/null +++ b/extensions/github-authentication/src/experimentationService.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from 'path'; +import * as vscode from 'vscode'; +import TelemetryReporter from 'vscode-extension-telemetry'; +import { getExperimentationService, IExperimentationService, IExperimentationTelemetry, TargetPopulation } from 'vscode-tas-client'; + +export class ExperimentationTelemetry implements IExperimentationTelemetry { + private sharedProperties: Record = {}; + + constructor(private baseReporter: TelemetryReporter) { } + + sendTelemetryEvent(eventName: string, properties?: Record, measurements?: Record) { + this.baseReporter.sendTelemetryEvent( + eventName, + { + ...this.sharedProperties, + ...properties, + }, + measurements, + ); + } + + sendTelemetryErrorEvent( + eventName: string, + properties?: Record, + _measurements?: Record, + ) { + this.baseReporter.sendTelemetryErrorEvent(eventName, { + ...this.sharedProperties, + ...properties, + }); + } + + setSharedProperty(name: string, value: string): void { + this.sharedProperties[name] = value; + } + + postEvent(eventName: string, props: Map): void { + const event: Record = {}; + for (const [key, value] of props) { + event[key] = value; + } + this.sendTelemetryEvent(eventName, event); + } + + dispose(): Promise { + return this.baseReporter.dispose(); + } +} + +interface ProductConfiguration { + quality?: 'stable' | 'insider' | 'exploration'; +} + +async function getProductConfig(appRoot: string): Promise { + const raw = await vscode.workspace.fs.readFile(vscode.Uri.file(path.join(appRoot, 'product.json'))); + return JSON.parse(raw.toString()); +} + +function getTargetPopulation(product: ProductConfiguration): TargetPopulation { + switch (product.quality) { + case 'stable': + return TargetPopulation.Public; + case 'insider': + return TargetPopulation.Insiders; + case 'exploration': + return TargetPopulation.Internal; + case undefined: + return TargetPopulation.Team; + default: + return TargetPopulation.Public; + } +} + +export async function createExperimentationService(context: vscode.ExtensionContext, telemetry: ExperimentationTelemetry): Promise { + const id = context.extension.id; + const version = context.extension.packageJSON.version; + const product = await getProductConfig(vscode.env.appRoot); + const targetPopulation = getTargetPopulation(product); + return getExperimentationService(id, version, targetPopulation, telemetry, context.globalState); +} diff --git a/extensions/github-authentication/src/extension.ts b/extensions/github-authentication/src/extension.ts index 7ae1e1d2110..253f3aec579 100644 --- a/extensions/github-authentication/src/extension.ts +++ b/extensions/github-authentication/src/extension.ts @@ -8,10 +8,14 @@ import { GitHubAuthenticationProvider, onDidChangeSessions } from './github'; import { uriHandler } from './githubServer'; import Logger from './common/logger'; import TelemetryReporter from 'vscode-extension-telemetry'; +import { createExperimentationService, ExperimentationTelemetry } from './experimentationService'; export async function activate(context: vscode.ExtensionContext) { const { name, version, aiKey } = require('../package.json') as { name: string, version: string, aiKey: string }; - const telemetryReporter = new TelemetryReporter(name, version, aiKey); + const telemetryReporter = new ExperimentationTelemetry(new TelemetryReporter(name, version, aiKey)); + + const experimentationService = await createExperimentationService(context, telemetryReporter); + await experimentationService.initialFetch; context.subscriptions.push(vscode.window.registerUriHandler(uriHandler)); const loginService = new GitHubAuthenticationProvider(context, telemetryReporter); diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 595d13147a1..4d3573a7e64 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -9,7 +9,7 @@ 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'; +import { ExperimentationTelemetry } from './experimentationService'; export const onDidChangeSessions = new vscode.EventEmitter(); @@ -30,7 +30,7 @@ export class GitHubAuthenticationProvider { private _keychain: Keychain; - constructor(context: vscode.ExtensionContext, telemetryReporter: TelemetryReporter) { + constructor(context: vscode.ExtensionContext, telemetryReporter: ExperimentationTelemetry) { this._keychain = new Keychain(context); this._githubServer = new GitHubServer(telemetryReporter); } diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index a6b4566d125..be9a8ce60d9 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -9,7 +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'; +import { ExperimentationTelemetry } from './experimentationService'; const localize = nls.loadMessageBundle(); @@ -42,7 +42,7 @@ export class GitHubServer { private _pendingStates = new Map(); private _codeExchangePromises = new Map, cancel: vscode.EventEmitter }>(); - constructor(private readonly telemetryReporter: TelemetryReporter) { } + constructor(private readonly telemetryReporter: ExperimentationTelemetry) { } private isTestEnvironment(url: vscode.Uri): boolean { return /\.azurewebsites\.net$/.test(url.authority) || url.authority.startsWith('localhost:'); diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock index 4ba712691d1..8095e574523 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -55,6 +55,13 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= +axios@^0.21.1: + version "0.21.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" + integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== + dependencies: + follow-redirects "^1.10.0" + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -103,6 +110,11 @@ emitter-listener@^1.0.1, emitter-listener@^1.1.1: dependencies: shimmer "^1.2.0" +follow-redirects@^1.10.0: + version "1.13.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" + integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== + form-data@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" @@ -144,6 +156,13 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU= +tas-client@0.1.21: + version "0.1.21" + resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.1.21.tgz#62275d5f75266eaae408f7463364748cb92f220d" + integrity sha512-7UuIwOXarCYoCTrQHY5n7M+63XuwMC0sVUdbPQzxqDB9wMjIW0JF39dnp3yoJnxr4jJUVhPtvkkXZbAD0BxCcA== + dependencies: + axios "^0.21.1" + uuid@8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d" @@ -160,3 +179,10 @@ vscode-nls@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== + +vscode-tas-client@^0.1.22: + version "0.1.22" + resolved "https://registry.yarnpkg.com/vscode-tas-client/-/vscode-tas-client-0.1.22.tgz#2dd674b21a94ff4e97db2b6545d9efda8b5f07c3" + integrity sha512-1sYH73nhiSRVQgfZkLQNJW7VzhKM9qNbCe8QyXgiKkLhH4GflDXRPAK4yy4P41jUgula+Fc9G7i5imj1dlKfaw== + dependencies: + tas-client "0.1.21"