From 334795d2da3849c7b6e8944e663f48ab40c23b56 Mon Sep 17 00:00:00 2001 From: Prashant Cholachagudda Date: Thu, 23 Jun 2022 16:58:30 +0530 Subject: [PATCH 1/4] Add VSCode session id to gallery extension query --- .../extensionManagement/common/extensionGalleryService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 6f04a76edb65e..cfea8e58f809d 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -923,12 +923,14 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi const commonHeaders = await this.commonHeadersPromise; const data = JSON.stringify(query.raw); + const { sessionId } = await this.telemetryService.getTelemetryInfo(); const headers = { ...commonHeaders, 'Content-Type': 'application/json', 'Accept': 'application/json;api-version=3.0-preview.1', 'Accept-Encoding': 'gzip', - 'Content-Length': String(data.length) + 'Content-Length': String(data.length), + 'VSCode-SessionId': sessionId }; const startTime = new Date().getTime(); From d9ccb7eff15945c823c72a7e8c96a1c5e5e0214f Mon Sep 17 00:00:00 2001 From: Prashant Cholachagudda Date: Mon, 27 Jun 2022 15:46:18 +0530 Subject: [PATCH 2/4] assign 'VSCode-SessionId' header in marketplace.ts --- .../common/extensionGalleryService.ts | 11 +++- .../common/extensionGalleryService.test.ts | 59 ++++++++++++++++++- .../externalServices/common/marketplace.ts | 13 +++- .../platform/windows/electron-main/window.ts | 9 ++- 4 files changed, 83 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index cfea8e58f809d..3515dea44640b 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -584,7 +584,14 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi const config = productService.extensionsGallery; this.extensionsGalleryUrl = config && config.serviceUrl; this.extensionsControlUrl = config && config.controlUrl; - this.commonHeadersPromise = resolveMarketplaceHeaders(productService.version, productService, this.environmentService, this.configurationService, this.fileService, storageService); + this.commonHeadersPromise = resolveMarketplaceHeaders( + productService.version, + productService, + this.environmentService, + this.configurationService, + this.fileService, + storageService, + this.telemetryService); } private api(path = ''): string { @@ -923,14 +930,12 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi const commonHeaders = await this.commonHeadersPromise; const data = JSON.stringify(query.raw); - const { sessionId } = await this.telemetryService.getTelemetryInfo(); const headers = { ...commonHeaders, 'Content-Type': 'application/json', 'Accept': 'application/json;api-version=3.0-preview.1', 'Accept-Encoding': 'gzip', 'Content-Length': String(data.length), - 'VSCode-SessionId': sessionId }; const startTime = new Date().getTime(); diff --git a/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts b/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts index 4312de805f8a0..4db659c6fc8a6 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts @@ -21,8 +21,11 @@ import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { resolveMarketplaceHeaders } from 'vs/platform/externalServices/common/marketplace'; import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/common/storage'; -import { TelemetryConfiguration, TELEMETRY_SETTING_ID } from 'vs/platform/telemetry/common/telemetry'; +import { ITelemetryInfo, ITelemetryService, TelemetryConfiguration, TelemetryLevel, TELEMETRY_SETTING_ID } from 'vs/platform/telemetry/common/telemetry'; import { TargetPlatform } from 'vs/platform/extensions/common/extensions'; +import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; +import { staticObservableValue } from 'vs/base/common/observableValue'; +import { Emitter, Event } from 'vs/base/common/event'; class EnvironmentServiceMock extends mock() { override readonly serviceMachineIdResource: URI; @@ -36,6 +39,7 @@ class EnvironmentServiceMock extends mock() { suite('Extension Gallery Service', () => { const disposables: DisposableStore = new DisposableStore(); let fileService: IFileService, environmentService: IEnvironmentService, storageService: IStorageService, productService: IProductService, configurationService: IConfigurationService; + let telemetryService: ITelemetryService; setup(() => { const serviceMachineIdResource = joinPath(URI.file('tests').with({ scheme: 'vscode-tests' }), 'machineid'); @@ -47,14 +51,15 @@ suite('Extension Gallery Service', () => { configurationService = new TestConfigurationService({ [TELEMETRY_SETTING_ID]: TelemetryConfiguration.ON }); configurationService.updateValue(TELEMETRY_SETTING_ID, TelemetryConfiguration.ON); productService = { _serviceBrand: undefined, ...product, enableTelemetry: true }; + telemetryService = new MockTelemetryService(); }); teardown(() => disposables.clear()); test('marketplace machine id', async () => { - const headers = await resolveMarketplaceHeaders(product.version, productService, environmentService, configurationService, fileService, storageService); + const headers = await resolveMarketplaceHeaders(product.version, productService, environmentService, configurationService, fileService, storageService, telemetryService); assert.ok(isUUID(headers['X-Market-User-Id'])); - const headers2 = await resolveMarketplaceHeaders(product.version, productService, environmentService, configurationService, fileService, storageService); + const headers2 = await resolveMarketplaceHeaders(product.version, productService, environmentService, configurationService, fileService, storageService, telemetryService); assert.strictEqual(headers['X-Market-User-Id'], headers2['X-Market-User-Id']); }); @@ -153,3 +158,51 @@ suite('Extension Gallery Service', () => { return { version, targetPlatform } as IRawGalleryExtensionVersion; } }); + +class MockTelemetryService implements ITelemetryService { + public _serviceBrand: undefined; + public telemetryLevel = staticObservableValue(TelemetryLevel.USAGE); + public sendErrorTelemetry = true; + + public events: any[] = []; + + private readonly emitter = new Emitter(); + + public get eventLogged(): Event { + return this.emitter.event; + } + + public setEnabled(value: boolean): void { + } + + public setExperimentProperty(name: string, value: string): void { + } + + public publicLog(eventName: string, data?: any): Promise { + const event = { name: eventName, data: data }; + this.events.push(event); + this.emitter.fire(event); + return Promise.resolve(); + } + + public publicLog2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyCheck) { + return this.publicLog(eventName, data as any); + } + + public publicLogError(eventName: string, data?: any): Promise { + return this.publicLog(eventName, data); + } + + public publicLogError2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyCheck) { + return this.publicLogError(eventName, data as any); + } + + public getTelemetryInfo(): Promise { + return Promise.resolve({ + instanceId: 'someValue.instanceId', + sessionId: 'someValue.sessionId', + machineId: 'someValue.machineId', + firstSessionDate: 'someValue.firstSessionDate' + }); + } +} diff --git a/src/vs/platform/externalServices/common/marketplace.ts b/src/vs/platform/externalServices/common/marketplace.ts index 6d2c25ab86d3c..5923e1cd8e880 100644 --- a/src/vs/platform/externalServices/common/marketplace.ts +++ b/src/vs/platform/externalServices/common/marketplace.ts @@ -10,17 +10,26 @@ import { getServiceMachineId } from 'vs/platform/externalServices/common/service import { IFileService } from 'vs/platform/files/common/files'; import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; +import { ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; -export async function resolveMarketplaceHeaders(version: string, productService: IProductService, environmentService: IEnvironmentService, configurationService: IConfigurationService, fileService: IFileService, storageService: IStorageService | undefined): Promise { +export async function resolveMarketplaceHeaders(version: string, + productService: IProductService, + environmentService: IEnvironmentService, + configurationService: IConfigurationService, + fileService: IFileService, + storageService: IStorageService | undefined, + telemetryService: ITelemetryService): Promise { const headers: IHeaders = { 'X-Market-Client-Id': `VSCode ${version}`, 'User-Agent': `VSCode ${version} (${productService.nameShort})` }; const uuid = await getServiceMachineId(environmentService, fileService, storageService); + const { sessionId } = await telemetryService.getTelemetryInfo(); + if (supportsTelemetry(productService, environmentService) && getTelemetryLevel(configurationService) === TelemetryLevel.USAGE) { headers['X-Market-User-Id'] = uuid; + headers['VSCode-SessionId'] = sessionId; } return headers; } diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 02524b2235bf5..fcc8c93ee47ee 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -542,7 +542,14 @@ export class CodeWindow extends Disposable implements ICodeWindow { private marketplaceHeadersPromise: Promise | undefined; private getMarketplaceHeaders(): Promise { if (!this.marketplaceHeadersPromise) { - this.marketplaceHeadersPromise = resolveMarketplaceHeaders(this.productService.version, this.productService, this.environmentMainService, this.configurationService, this.fileService, this.applicationStorageMainService); + this.marketplaceHeadersPromise = resolveMarketplaceHeaders( + this.productService.version, + this.productService, + this.environmentMainService, + this.configurationService, + this.fileService, + this.applicationStorageMainService, + this.telemetryService); } return this.marketplaceHeadersPromise; From ae617b7c7b67738516e3a94656825409e7f10bc2 Mon Sep 17 00:00:00 2001 From: Prashant Cholachagudda Date: Tue, 28 Jun 2022 13:27:31 +0530 Subject: [PATCH 3/4] Using NullTelemetryService instead of mock --- .../common/extensionGalleryService.test.ts | 56 +------------------ 1 file changed, 3 insertions(+), 53 deletions(-) diff --git a/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts b/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts index 4db659c6fc8a6..803066d7790b2 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts @@ -21,11 +21,9 @@ import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { resolveMarketplaceHeaders } from 'vs/platform/externalServices/common/marketplace'; import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/common/storage'; -import { ITelemetryInfo, ITelemetryService, TelemetryConfiguration, TelemetryLevel, TELEMETRY_SETTING_ID } from 'vs/platform/telemetry/common/telemetry'; +import { ITelemetryService, TelemetryConfiguration, TELEMETRY_SETTING_ID } from 'vs/platform/telemetry/common/telemetry'; import { TargetPlatform } from 'vs/platform/extensions/common/extensions'; -import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; -import { staticObservableValue } from 'vs/base/common/observableValue'; -import { Emitter, Event } from 'vs/base/common/event'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; class EnvironmentServiceMock extends mock() { override readonly serviceMachineIdResource: URI; @@ -51,7 +49,7 @@ suite('Extension Gallery Service', () => { configurationService = new TestConfigurationService({ [TELEMETRY_SETTING_ID]: TelemetryConfiguration.ON }); configurationService.updateValue(TELEMETRY_SETTING_ID, TelemetryConfiguration.ON); productService = { _serviceBrand: undefined, ...product, enableTelemetry: true }; - telemetryService = new MockTelemetryService(); + telemetryService = NullTelemetryService; }); teardown(() => disposables.clear()); @@ -158,51 +156,3 @@ suite('Extension Gallery Service', () => { return { version, targetPlatform } as IRawGalleryExtensionVersion; } }); - -class MockTelemetryService implements ITelemetryService { - public _serviceBrand: undefined; - public telemetryLevel = staticObservableValue(TelemetryLevel.USAGE); - public sendErrorTelemetry = true; - - public events: any[] = []; - - private readonly emitter = new Emitter(); - - public get eventLogged(): Event { - return this.emitter.event; - } - - public setEnabled(value: boolean): void { - } - - public setExperimentProperty(name: string, value: string): void { - } - - public publicLog(eventName: string, data?: any): Promise { - const event = { name: eventName, data: data }; - this.events.push(event); - this.emitter.fire(event); - return Promise.resolve(); - } - - public publicLog2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyCheck) { - return this.publicLog(eventName, data as any); - } - - public publicLogError(eventName: string, data?: any): Promise { - return this.publicLog(eventName, data); - } - - public publicLogError2 = never, T extends GDPRClassification = never>(eventName: string, data?: StrictPropertyCheck) { - return this.publicLogError(eventName, data as any); - } - - public getTelemetryInfo(): Promise { - return Promise.resolve({ - instanceId: 'someValue.instanceId', - sessionId: 'someValue.sessionId', - machineId: 'someValue.machineId', - firstSessionDate: 'someValue.firstSessionDate' - }); - } -} From 44a89e5e8ee701e3275aebe8edc2c61451be826a Mon Sep 17 00:00:00 2001 From: Prashant Cholachagudda Date: Tue, 28 Jun 2022 13:32:51 +0530 Subject: [PATCH 4/4] remove redundant telemetryService variable --- .../test/common/extensionGalleryService.test.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts b/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts index 803066d7790b2..7dcb41d56bf50 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionGalleryService.test.ts @@ -21,7 +21,7 @@ import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { resolveMarketplaceHeaders } from 'vs/platform/externalServices/common/marketplace'; import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/common/storage'; -import { ITelemetryService, TelemetryConfiguration, TELEMETRY_SETTING_ID } from 'vs/platform/telemetry/common/telemetry'; +import { TelemetryConfiguration, TELEMETRY_SETTING_ID } from 'vs/platform/telemetry/common/telemetry'; import { TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -37,7 +37,6 @@ class EnvironmentServiceMock extends mock() { suite('Extension Gallery Service', () => { const disposables: DisposableStore = new DisposableStore(); let fileService: IFileService, environmentService: IEnvironmentService, storageService: IStorageService, productService: IProductService, configurationService: IConfigurationService; - let telemetryService: ITelemetryService; setup(() => { const serviceMachineIdResource = joinPath(URI.file('tests').with({ scheme: 'vscode-tests' }), 'machineid'); @@ -49,15 +48,14 @@ suite('Extension Gallery Service', () => { configurationService = new TestConfigurationService({ [TELEMETRY_SETTING_ID]: TelemetryConfiguration.ON }); configurationService.updateValue(TELEMETRY_SETTING_ID, TelemetryConfiguration.ON); productService = { _serviceBrand: undefined, ...product, enableTelemetry: true }; - telemetryService = NullTelemetryService; }); teardown(() => disposables.clear()); test('marketplace machine id', async () => { - const headers = await resolveMarketplaceHeaders(product.version, productService, environmentService, configurationService, fileService, storageService, telemetryService); + const headers = await resolveMarketplaceHeaders(product.version, productService, environmentService, configurationService, fileService, storageService, NullTelemetryService); assert.ok(isUUID(headers['X-Market-User-Id'])); - const headers2 = await resolveMarketplaceHeaders(product.version, productService, environmentService, configurationService, fileService, storageService, telemetryService); + const headers2 = await resolveMarketplaceHeaders(product.version, productService, environmentService, configurationService, fileService, storageService, NullTelemetryService); assert.strictEqual(headers['X-Market-User-Id'], headers2['X-Market-User-Id']); });