From 26cc3759837e629e9ec32c4e7127f6b47eacc15c Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 16 Jan 2019 21:08:57 -0800 Subject: [PATCH 1/3] x --- src/client/telemetry/index.ts | 66 +++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index cf54a8198337..3d88ee657b22 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -8,6 +8,7 @@ import * as stackTrace from 'stack-trace'; import TelemetryReporter from 'vscode-extension-telemetry'; import { EXTENSION_ROOT_DIR, isTestExecution, PVSC_EXTENSION_ID } from '../common/constants'; import { StopWatch } from '../common/utils/stopWatch'; +import { EventName } from './constants'; import { TelemetryProperties } from './types'; /** @@ -44,15 +45,20 @@ function getTelemetryReporter() { // tslint:disable-next-line:no-require-imports const reporter = require('vscode-extension-telemetry').default as typeof TelemetryReporter; - return telemetryReporter = new reporter(extensionId, extensionVersion, aiKey); + return (telemetryReporter = new reporter(extensionId, extensionVersion, aiKey)); } -export function sendTelemetryEvent(eventName: string, durationMs?: { [key: string]: number } | number, properties?: TelemetryProperties, ex?: Error) { +export function sendTelemetryEvent( + eventName: string, + durationMs?: { [key: string]: number } | number, + properties?: TelemetryProperties, + ex?: Error +) { if (isTestExecution() || !isTelemetrySupported()) { return; } const reporter = getTelemetryReporter(); - const measures = typeof durationMs === 'number' ? { duration: durationMs } : (durationMs ? durationMs : undefined); + const measures = typeof durationMs === 'number' ? { duration: durationMs } : durationMs ? durationMs : undefined; // tslint:disable-next-line:no-any const customProperties: { [key: string]: string } = {}; @@ -79,16 +85,16 @@ export function sendTelemetryEvent(eventName: string, durationMs?: { [key: strin // tslint:disable-next-line:no-any function-name export function captureTelemetry( - eventName: string, + eventName: EventName, properties?: TelemetryProperties, captureDuration: boolean = true, - failureEventName?: string + failureEventName?: EventName ) { // tslint:disable-next-line:no-function-expression no-any - return function (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) { + return function(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) { const originalMethod = descriptor.value; // tslint:disable-next-line:no-function-expression no-any - descriptor.value = function (...args: any[]) { + descriptor.value = function(...args: any[]) { if (!captureDuration) { sendTelemetryEvent(eventName, undefined, properties); // tslint:disable-next-line:no-invalid-this @@ -113,7 +119,12 @@ export function captureTelemetry( // tslint:disable-next-line:no-any properties = properties || {}; (properties as any).failed = true; - sendTelemetryEvent(failureEventName ? failureEventName : eventName, stopWatch.elapsedTime, properties, ex); + sendTelemetryEvent( + failureEventName ? failureEventName : eventName, + stopWatch.elapsedTime, + properties, + ex + ); }); } else { sendTelemetryEvent(eventName, stopWatch.elapsedTime, properties); @@ -127,22 +138,28 @@ export function captureTelemetry( } // tslint:disable-next-line:no-any function-name -export function sendTelemetryWhenDone(eventName: string, promise: Promise | Thenable, - stopWatch?: StopWatch, properties?: TelemetryProperties) { +export function sendTelemetryWhenDone( + eventName: EventName, + promise: Promise | Thenable, + stopWatch?: StopWatch, + properties?: TelemetryProperties +) { stopWatch = stopWatch ? stopWatch : new StopWatch(); if (typeof promise.then === 'function') { // tslint:disable-next-line:prefer-type-cast no-any - (promise as Promise) - .then(data => { + (promise as Promise).then( + data => { // tslint:disable-next-line:no-non-null-assertion sendTelemetryEvent(eventName, stopWatch!.elapsedTime, properties); return data; // tslint:disable-next-line:promise-function-async - }, ex => { + }, + ex => { // tslint:disable-next-line:no-non-null-assertion sendTelemetryEvent(eventName, stopWatch!.elapsedTime, properties, ex); return Promise.reject(ex); - }); + } + ); } else { throw new Error('Method is neither a Promise nor a Theneable'); } @@ -199,3 +216,24 @@ function getCallsite(frame: stackTrace.StackFrame) { } return parts.map(sanitizeName).join('.'); } + +interface IEventNamePropertyMapping { + [EventName.COMPLETION]: { x: number }; + [EventName.COMPLETION_ADD_BRACKETS]: { y: string }; +} + +interface IDSMappings extends IMappings { + [EventName.DEBUGGER]: { abc: number }; + [EventName.DEFINITION]: { hello: string }; +} + +function doThat(eventName: K, properties: IMappings[K]) { + console.log(''); +} + +function doThat2(eventName: K, properties: T[K]) { + console.log(''); +} + +doThat(EventName.COMPLETION_ADD_BRACKETS, { y: '1' }); +doThat2(EventName.DEBUGGER, { abc: 1 }); From 1d53e6a8269041a0074350f1acc5ef17d710672e Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 17 Jan 2019 00:36:44 -0800 Subject: [PATCH 2/3] Strongly typed telemetry properties --- src/client/application/diagnostics/base.ts | 3 +- src/client/application/diagnostics/types.ts | 3 +- src/client/common/logger.ts | 28 ++-- src/client/common/terminal/helper.ts | 2 +- src/client/datascience/constants.ts | 54 +++--- src/client/datascience/history.ts | 8 +- .../datascience/historycommandlistener.ts | 6 +- src/client/telemetry/index.ts | 157 ++++++++++++++---- src/client/telemetry/types.ts | 30 +--- .../applicationDiagnostics.unit.test.ts | 8 +- .../checks/envPathVariable.unit.test.ts | 4 +- .../invalidPythonPathInDebugger.unit.test.ts | 4 +- .../checks/lsNotSupported.unit.test.ts | 3 +- .../checks/powerShellActivation.unit.test.ts | 4 +- .../checks/pythonInterpreter.unit.test.ts | 2 +- .../diagnostics/commands/ignore.unit.test.ts | 4 +- .../diagnostics/promptHandler.unit.test.ts | 8 +- src/test/telemetry/index.unit.test.ts | 14 +- 18 files changed, 206 insertions(+), 136 deletions(-) diff --git a/src/client/application/diagnostics/base.ts b/src/client/application/diagnostics/base.ts index f67ce3e10b36..f0b33192d971 100644 --- a/src/client/application/diagnostics/base.ts +++ b/src/client/application/diagnostics/base.ts @@ -8,11 +8,12 @@ import { DiagnosticSeverity } from 'vscode'; import { IServiceContainer } from '../../ioc/types'; import { sendTelemetryEvent } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; +import { DiagnosticCodes } from './constants'; import { DiagnosticScope, IDiagnostic, IDiagnosticFilterService, IDiagnosticsService } from './types'; @injectable() export abstract class BaseDiagnostic implements IDiagnostic { - constructor(public readonly code: string, public readonly message: string, + constructor(public readonly code: DiagnosticCodes, public readonly message: string, public readonly severity: DiagnosticSeverity, public readonly scope: DiagnosticScope) { } } diff --git a/src/client/application/diagnostics/types.ts b/src/client/application/diagnostics/types.ts index 8daf816366c4..984aa84bf0f6 100644 --- a/src/client/application/diagnostics/types.ts +++ b/src/client/application/diagnostics/types.ts @@ -4,6 +4,7 @@ 'use strict'; import { DiagnosticSeverity, Uri } from 'vscode'; +import { DiagnosticCodes } from './constants'; export enum DiagnosticScope { Global = 'Global', @@ -11,7 +12,7 @@ export enum DiagnosticScope { } export interface IDiagnostic { - readonly code: string; + readonly code: DiagnosticCodes; readonly message: string; readonly severity: DiagnosticSeverity; readonly scope: DiagnosticScope; diff --git a/src/client/common/logger.ts b/src/client/common/logger.ts index 0b0d7173d505..5483b6b97446 100644 --- a/src/client/common/logger.ts +++ b/src/client/common/logger.ts @@ -1,4 +1,4 @@ -// tslint:disable:no-console +// tslint:disable:no-console no-any import { injectable } from 'inversify'; import { sendTelemetryEvent } from '../telemetry'; @@ -56,16 +56,18 @@ export enum LogOptions { // tslint:disable-next-line:no-any function argsToLogString(args: any[]): string { try { - return (args || []).map((item, index) => { - try { - if (item.fsPath) { - return `Arg ${index + 1}: `; + return (args || []) + .map((item, index) => { + try { + if (item.fsPath) { + return `Arg ${index + 1}: `; + } + return `Arg ${index + 1}: ${JSON.stringify(item)}`; + } catch { + return `Arg ${index + 1}: UNABLE TO DETERMINE VALUE`; } - return `Arg ${index + 1}: ${JSON.stringify(item)}`; - } catch { - return `Arg ${index + 1}: UNABLE TO DETERMINE VALUE`; - } - }).join(', '); + }) + .join(', '); } catch { return ''; } @@ -113,10 +115,10 @@ export namespace traceDecorators { } function trace(message: string, options: LogOptions = LogOptions.None, logLevel?: LogLevel) { // tslint:disable-next-line:no-function-expression no-any - return function (_: Object, __: string, descriptor: TypedPropertyDescriptor) { + return function(_: Object, __: string, descriptor: TypedPropertyDescriptor) { const originalMethod = descriptor.value; // tslint:disable-next-line:no-function-expression no-any - descriptor.value = function (...args: any[]) { + descriptor.value = function(...args: any[]) { const className = _ && _.constructor ? _.constructor.name : ''; // tslint:disable-next-line:no-any function writeSuccess(returnValue?: any) { @@ -140,7 +142,7 @@ function trace(message: string, options: LogOptions = LogOptions.None, logLevel? } if (ex) { new Logger().logError(messagesToLog.join(', '), ex); - sendTelemetryEvent('ERROR', undefined, undefined, ex); + sendTelemetryEvent('ERROR' as any, undefined, undefined, ex); } else { new Logger().logInformation(messagesToLog.join(', ')); } diff --git a/src/client/common/terminal/helper.ts b/src/client/common/terminal/helper.ts index 4dfd987d2fd9..d3c82546d05d 100644 --- a/src/client/common/terminal/helper.ts +++ b/src/client/common/terminal/helper.ts @@ -120,7 +120,7 @@ export class TerminalHelper implements ITerminalHelper { return promise; } @traceDecorators.error('Failed to capture telemetry') - protected async sendTelemetry(resource: Resource, terminalShellType: TerminalShellType, eventName: string, promise: Promise): Promise { + protected async sendTelemetry(resource: Resource, terminalShellType: TerminalShellType, eventName: EventName, promise: Promise): Promise { let hasCommands = false; const interpreter = await this.interpreterService.getActiveInterpreter(resource); let failed = false; diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index ef4be7e592b9..44d55fff0f35 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -59,33 +59,33 @@ export namespace HistoryMessages { export const UpdateSettings = 'update_settings'; } -export namespace Telemetry { - export const ImportNotebook = 'DATASCIENCE.IMPORT_NOTEBOOK'; - export const RunCell = 'DATASCIENCE.RUN_CELL'; - export const RunCurrentCell = 'DATASCIENCE.RUN_CURRENT_CELL'; - export const RunCurrentCellAndAdvance = 'DATASCIENCE.RUN_CURRENT_CELL_AND_ADVANCE'; - export const RunAllCells = 'DATASCIENCE.RUN_ALL_CELLS'; - export const DeleteAllCells = 'DATASCIENCE.DELETE_ALL_CELLS'; - export const DeleteCell = 'DATASCIENCE.DELETE_CELL'; - export const GotoSourceCode = 'DATASCIENCE.GOTO_SOURCE'; - export const RestartKernel = 'DATASCIENCE.RESTART_KERNEL'; - export const ExportNotebook = 'DATASCIENCE.EXPORT_NOTEBOOK'; - export const Undo = 'DATASCIENCE.UNDO'; - export const Redo = 'DATASCIENCE.REDO'; - export const ShowHistoryPane = 'DATASCIENCE.SHOW_HISTORY_PANE'; - export const ExpandAll = 'DATASCIENCE.EXPAND_ALL'; - export const CollapseAll = 'DATASCIENCE.COLLAPSE_ALL'; - export const SelectJupyterURI = 'DATASCIENCE.SELECT_JUPYTER_URI'; - export const SetJupyterURIToLocal = 'DATASCIENCE.SET_JUPYTER_URI_LOCAL'; - export const SetJupyterURIToUserSpecified = 'DATASCIENCE.SET_JUPYTER_URI_USER_SPECIFIED'; - export const Interrupt = 'DATASCIENCE.INTERRUPT'; - export const ExportPythonFile = 'DATASCIENCE.EXPORT_PYTHON_FILE'; - export const ExportPythonFileAndOutput = 'DATASCIENCE.EXPORT_PYTHON_FILE_AND_OUTPUT'; - export const StartJupyter = 'DATASCIENCE.JUPYTERSTARTUPCOST'; - export const SubmitCellThroughInput = 'DATASCIENCE.SUBMITCELLFROMREPL'; - export const ConnectLocalJupyter = 'DATASCIENCE.CONNECTLOCALJUPYTER'; - export const ConnectRemoteJupyter = 'DATASCIENCE.CONNECTREMOTEJUPYTER'; - export const ConnectFailedJupyter = 'DATASCIENCE.CONNECTFAILEDJUPYTER'; +export enum Telemetry { + ImportNotebook = 'DATASCIENCE.IMPORT_NOTEBOOK', + RunCell = 'DATASCIENCE.RUN_CELL', + RunCurrentCell = 'DATASCIENCE.RUN_CURRENT_CELL', + RunCurrentCellAndAdvance = 'DATASCIENCE.RUN_CURRENT_CELL_AND_ADVANCE', + RunAllCells = 'DATASCIENCE.RUN_ALL_CELLS', + DeleteAllCells = 'DATASCIENCE.DELETE_ALL_CELLS', + DeleteCell = 'DATASCIENCE.DELETE_CELL', + GotoSourceCode = 'DATASCIENCE.GOTO_SOURCE', + RestartKernel = 'DATASCIENCE.RESTART_KERNEL', + ExportNotebook = 'DATASCIENCE.EXPORT_NOTEBOOK', + Undo = 'DATASCIENCE.UNDO', + Redo = 'DATASCIENCE.REDO', + ShowHistoryPane = 'DATASCIENCE.SHOW_HISTORY_PANE', + ExpandAll = 'DATASCIENCE.EXPAND_ALL', + CollapseAll = 'DATASCIENCE.COLLAPSE_ALL', + SelectJupyterURI = 'DATASCIENCE.SELECT_JUPYTER_URI', + SetJupyterURIToLocal = 'DATASCIENCE.SET_JUPYTER_URI_LOCAL', + SetJupyterURIToUserSpecified = 'DATASCIENCE.SET_JUPYTER_URI_USER_SPECIFIED', + Interrupt = 'DATASCIENCE.INTERRUPT', + ExportPythonFile = 'DATASCIENCE.EXPORT_PYTHON_FILE', + ExportPythonFileAndOutput = 'DATASCIENCE.EXPORT_PYTHON_FILE_AND_OUTPUT', + StartJupyter = 'DATASCIENCE.JUPYTERSTARTUPCOST', + SubmitCellThroughInput = 'DATASCIENCE.SUBMITCELLFROMREPL', + ConnectLocalJupyter = 'DATASCIENCE.CONNECTLOCALJUPYTER', + ConnectRemoteJupyter = 'DATASCIENCE.CONNECTREMOTEJUPYTER', + ConnectFailedJupyter = 'DATASCIENCE.CONNECTFAILEDJUPYTER' } export namespace HelpLinks { diff --git a/src/client/datascience/history.ts b/src/client/datascience/history.ts index b7a0c957398b..cdf44090ca93 100644 --- a/src/client/datascience/history.ts +++ b/src/client/datascience/history.ts @@ -405,7 +405,7 @@ export class History implements IWebPanelMessageListener, IHistory { } } - @captureTelemetry(Telemetry.SubmitCellThroughInput, {}, false) + @captureTelemetry(Telemetry.SubmitCellThroughInput, undefined, false) // tslint:disable-next-line:no-any private submitNewCell(payload?: any) { // If there's any payload, it's the code @@ -422,7 +422,7 @@ export class History implements IWebPanelMessageListener, IHistory { return result; } - private logTelemetry = (event : string) => { + private logTelemetry = (event : Telemetry) => { sendTelemetryEvent(event); } @@ -503,7 +503,7 @@ export class History implements IWebPanelMessageListener, IHistory { this.loadPromise = this.loadJupyterServer(true); } - @captureTelemetry(Telemetry.GotoSourceCode, {}, false) + @captureTelemetry(Telemetry.GotoSourceCode, undefined, false) private gotoCode(file: string, line: number) { this.gotoCodeInternal(file, line).catch(err => { this.applicationShell.showErrorMessage(err); @@ -530,7 +530,7 @@ export class History implements IWebPanelMessageListener, IHistory { } } - @captureTelemetry(Telemetry.ExportNotebook, {}, false) + @captureTelemetry(Telemetry.ExportNotebook, undefined, false) // tslint:disable-next-line: no-any no-empty private export (payload: any) { if (payload.contents) { diff --git a/src/client/datascience/historycommandlistener.ts b/src/client/datascience/historycommandlistener.ts index 08fa3a3d17fc..81a275e60a3a 100644 --- a/src/client/datascience/historycommandlistener.ts +++ b/src/client/datascience/historycommandlistener.ts @@ -114,7 +114,7 @@ export class HistoryCommandListener implements IDataScienceCommandListener { } } - @captureTelemetry(Telemetry.ExportPythonFile, {}, false) + @captureTelemetry(Telemetry.ExportPythonFile, undefined, false) private async exportFile(file: string): Promise { if (file && file.length > 0) { // If the current file is the active editor, then generate cells from the document. @@ -154,7 +154,7 @@ export class HistoryCommandListener implements IDataScienceCommandListener { } } - @captureTelemetry(Telemetry.ExportPythonFileAndOutput, {}, false) + @captureTelemetry(Telemetry.ExportPythonFileAndOutput, undefined, false) private async exportFileAndOutput(file: string): Promise { if (file && file.length > 0 && this.jupyterExecution.isNotebookSupported()) { // If the current file is the active editor, then generate cells from the document. @@ -333,7 +333,7 @@ export class HistoryCommandListener implements IDataScienceCommandListener { } - @captureTelemetry(Telemetry.ShowHistoryPane, {}, false) + @captureTelemetry(Telemetry.ShowHistoryPane, undefined, false) private showHistoryPane() : Promise{ const active = this.historyProvider.getOrCreateActive(); return active.show(); diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 3d88ee657b22..94c204aec8ed 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1,15 +1,39 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// tslint:disable:no-reference no-any import-name +// tslint:disable:no-reference no-any import-name no-any function-name /// import { basename as pathBasename, sep as pathSep } from 'path'; import * as stackTrace from 'stack-trace'; import TelemetryReporter from 'vscode-extension-telemetry'; import { EXTENSION_ROOT_DIR, isTestExecution, PVSC_EXTENSION_ID } from '../common/constants'; import { StopWatch } from '../common/utils/stopWatch'; +import { Telemetry } from '../datascience/constants'; import { EventName } from './constants'; -import { TelemetryProperties } from './types'; +import { + CodeExecutionTelemetry, + DebuggerConfigurationPromtpsTelemetry, + DebuggerTelemetry, + DiagnosticsAction, + DiagnosticsMessages, + EditorLoadTelemetry, + FormatTelemetry, + InterpreterActivation, + InterpreterActivationEnvironmentVariables, + InterpreterAutoSelection, + InterpreterDiscovery, + LanguageServePlatformSupported, + LanguageServerErrorTelemetry, + LanguageServerVersionTelemetry, + LinterInstallPromptTelemetry, + LinterSelectionTelemetry, + LintingTelemetry, + Platform, + PythonInterpreterTelemetry, + TerminalTelemetry, + TestDiscoverytTelemetry, + TestRunTelemetry +} from './types'; /** * Checks whether telemetry is supported. @@ -48,10 +72,10 @@ function getTelemetryReporter() { return (telemetryReporter = new reporter(extensionId, extensionVersion, aiKey)); } -export function sendTelemetryEvent( - eventName: string, +export function sendTelemetryEvent

( + eventName: E, durationMs?: { [key: string]: number } | number, - properties?: TelemetryProperties, + properties?: P[E], ex?: Error ) { if (isTestExecution() || !isTelemetrySupported()) { @@ -76,19 +100,19 @@ export function sendTelemetryEvent( if (ex) { customProperties.stackTrace = getStackTrace(ex); } - if (ex && eventName !== 'ERROR') { - customProperties.originalEventName = eventName; + if (ex && (eventName as any) !== 'ERROR') { + customProperties.originalEventName = eventName as any as string; reporter.sendTelemetryEvent('ERROR', customProperties, measures); } - reporter.sendTelemetryEvent(eventName, customProperties, measures); + reporter.sendTelemetryEvent((eventName as any) as string, customProperties, measures); } // tslint:disable-next-line:no-any function-name -export function captureTelemetry( - eventName: EventName, - properties?: TelemetryProperties, +export function captureTelemetry

( + eventName: E, + properties?: P[E], captureDuration: boolean = true, - failureEventName?: EventName + failureEventName?: E ) { // tslint:disable-next-line:no-function-expression no-any return function(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) { @@ -117,7 +141,7 @@ export function captureTelemetry( // tslint:disable-next-line:promise-function-async .catch(ex => { // tslint:disable-next-line:no-any - properties = properties || {}; + properties = properties || ({} as any); (properties as any).failed = true; sendTelemetryEvent( failureEventName ? failureEventName : eventName, @@ -137,12 +161,12 @@ export function captureTelemetry( }; } -// tslint:disable-next-line:no-any function-name -export function sendTelemetryWhenDone( - eventName: EventName, +// function sendTelemetryWhenDone(eventName: K, properties?: T[K]); +export function sendTelemetryWhenDone

( + eventName: E, promise: Promise | Thenable, stopWatch?: StopWatch, - properties?: TelemetryProperties + properties?: P[E] ) { stopWatch = stopWatch ? stopWatch : new StopWatch(); if (typeof promise.then === 'function') { @@ -217,23 +241,86 @@ function getCallsite(frame: stackTrace.StackFrame) { return parts.map(sanitizeName).join('.'); } +// Map all events to their properties interface IEventNamePropertyMapping { - [EventName.COMPLETION]: { x: number }; - [EventName.COMPLETION_ADD_BRACKETS]: { y: string }; -} - -interface IDSMappings extends IMappings { - [EventName.DEBUGGER]: { abc: number }; - [EventName.DEFINITION]: { hello: string }; -} - -function doThat(eventName: K, properties: IMappings[K]) { - console.log(''); -} - -function doThat2(eventName: K, properties: T[K]) { - console.log(''); + [EventName.COMPLETION]: never | undefined; + [EventName.COMPLETION_ADD_BRACKETS]: { enabled: boolean }; + [EventName.DEBUGGER]: DebuggerTelemetry; + [EventName.DEBUGGER_ATTACH_TO_CHILD_PROCESS]: never | undefined; + [EventName.DEBUGGER_CONFIGURATION_PROMPTS]: DebuggerConfigurationPromtpsTelemetry; + [EventName.DEBUGGER_PERFORMANCE]: any; + [EventName.DEFINITION]: never | undefined; + [EventName.DIAGNOSTICS_ACTION]: DiagnosticsAction; + [EventName.DIAGNOSTICS_MESSAGE]: DiagnosticsMessages; + [EventName.EDITOR_LOAD]: EditorLoadTelemetry; + [EventName.EXECUTION_CODE]: CodeExecutionTelemetry; + [EventName.EXECUTION_DJANGO]: CodeExecutionTelemetry; + [EventName.FORMAT]: FormatTelemetry; + [EventName.FORMAT_ON_TYPE]: { enabled: boolean }; + [EventName.FORMAT_SORT_IMPORTS]: never | undefined; + [EventName.GO_TO_OBJECT_DEFINITION]: never | undefined; + [EventName.HOVER_DEFINITION]: never | undefined; + [EventName.LINTER_NOT_INSTALLED_PROMPT]: LinterInstallPromptTelemetry; + [EventName.LINTING]: LintingTelemetry; + [EventName.PLATFORM_INFO]: Platform; + [EventName.PYTHON_INTERPRETER]: PythonInterpreterTelemetry; + [EventName.PYTHON_INTERPRETER_ACTIVATION_ENVIRONMENT_VARIABLES]: InterpreterActivationEnvironmentVariables; + [EventName.PYTHON_INTERPRETER_ACTIVATION_FOR_RUNNING_CODE]: InterpreterActivation; + [EventName.PYTHON_INTERPRETER_ACTIVATION_FOR_TERMINAL]: InterpreterActivation; + [EventName.PYTHON_INTERPRETER_AUTO_SELECTION]: InterpreterAutoSelection; + [EventName.PYTHON_INTERPRETER_DISCOVERY]: InterpreterDiscovery; + [EventName.PYTHON_LANGUAGE_SERVER_ANALYSISTIME]: { success: boolean }; + [EventName.PYTHON_LANGUAGE_SERVER_DOWNLOADED]: LanguageServerVersionTelemetry; + [EventName.PYTHON_LANGUAGE_SERVER_ENABLED]: never | undefined; + [EventName.PYTHON_LANGUAGE_SERVER_ERROR]: LanguageServerErrorTelemetry; + [EventName.PYTHON_LANGUAGE_SERVER_EXTRACTED]: LanguageServerVersionTelemetry; + [EventName.PYTHON_LANGUAGE_SERVER_LIST_BLOB_STORE_PACKAGES]: never | undefined; + [EventName.PYTHON_LANGUAGE_SERVER_PLATFORM_NOT_SUPPORTED]: never | undefined; + [EventName.PYTHON_LANGUAGE_SERVER_PLATFORM_SUPPORTED]: LanguageServePlatformSupported; + [EventName.PYTHON_LANGUAGE_SERVER_READY]: never | undefined; + [EventName.PYTHON_LANGUAGE_SERVER_STARTUP]: never | undefined; + [EventName.PYTHON_LANGUAGE_SERVER_TELEMETRY]: any; + [EventName.REFACTOR_EXTRACT_FUNCTION]: never | undefined; + [EventName.REFACTOR_EXTRACT_VAR]: never | undefined; + [EventName.REFACTOR_RENAME]: never | undefined; + [EventName.REFERENCE]: never | undefined; + [EventName.REPL]: never | undefined; + [EventName.SELECT_LINTER]: LinterSelectionTelemetry; + [EventName.SIGNATURE]: never | undefined; + [EventName.SYMBOL]: never | undefined; + [EventName.TERMINAL_CREATE]: TerminalTelemetry; + [EventName.UNITTEST_DISCOVER]: TestDiscoverytTelemetry; + [EventName.UNITTEST_RUN]: TestRunTelemetry; + [EventName.UNITTEST_STOP]: never | undefined; + [EventName.UNITTEST_VIEW_OUTPUT]: never | undefined; + [EventName.UPDATE_PYSPARK_LIBRARY]: never | undefined; + [EventName.WORKSPACE_SYMBOLS_BUILD]: never | undefined; + [EventName.WORKSPACE_SYMBOLS_GO_TO]: never | undefined; + // Data Science + [Telemetry.CollapseAll]: never | undefined; + [Telemetry.ConnectFailedJupyter]: never | undefined; + [Telemetry.ConnectLocalJupyter]: never | undefined; + [Telemetry.ConnectRemoteJupyter]: never | undefined; + [Telemetry.DeleteAllCells]: never | undefined; + [Telemetry.DeleteCell]: never | undefined; + [Telemetry.ExpandAll]: never | undefined; + [Telemetry.ExportNotebook]: never | undefined; + [Telemetry.ExportPythonFile]: never | undefined; + [Telemetry.ExportPythonFileAndOutput]: never | undefined; + [Telemetry.GotoSourceCode]: never | undefined; + [Telemetry.ImportNotebook]: { scope: 'command' | 'file' }; + [Telemetry.Interrupt]: never | undefined; + [Telemetry.Redo]: never | undefined; + [Telemetry.RestartKernel]: never | undefined; + [Telemetry.RunAllCells]: never | undefined; + [Telemetry.RunCell]: never | undefined; + [Telemetry.RunCurrentCell]: never | undefined; + [Telemetry.RunCurrentCellAndAdvance]: never | undefined; + [Telemetry.SelectJupyterURI]: never | undefined; + [Telemetry.SetJupyterURIToLocal]: never | undefined; + [Telemetry.SetJupyterURIToUserSpecified]: never | undefined; + [Telemetry.ShowHistoryPane]: never | undefined; + [Telemetry.StartJupyter]: never | undefined; + [Telemetry.SubmitCellThroughInput]: never | undefined; + [Telemetry.Undo]: never | undefined; } - -doThat(EventName.COMPLETION_ADD_BRACKETS, { y: '1' }); -doThat2(EventName.DEBUGGER, { abc: 1 }); diff --git a/src/client/telemetry/types.ts b/src/client/telemetry/types.ts index 9759928ce685..b31c0b658c2a 100644 --- a/src/client/telemetry/types.ts +++ b/src/client/telemetry/types.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. 'use strict'; +import { DiagnosticCodes } from '../application/diagnostics/constants'; import { TerminalShellType } from '../common/terminal/types'; import { DebugConfigurationType } from '../debugger/extension/types'; import { AutoSelectionRule } from '../interpreter/autoSelection/types'; @@ -145,7 +146,7 @@ export type DiagnosticsMessages = { * Code of diagnostics message detected and displayed. * @type {string} */ - code: string; + code: DiagnosticCodes; }; export type ImportNotebook = { scope: 'command'; @@ -176,30 +177,5 @@ export type InterpreterActivation = { failed?: boolean; terminal: TerminalShellType; pythonVersion?: string; + interpreterType: InterpreterType; }; - -export type TelemetryProperties = FormatTelemetry - | LanguageServerVersionTelemetry - | LanguageServerErrorTelemetry - | LintingTelemetry - | LinterInstallPromptTelemetry - | LinterSelectionTelemetry - | EditorLoadTelemetry - | PythonInterpreterTelemetry - | CodeExecutionTelemetry - | TestRunTelemetry - | TestDiscoverytTelemetry - | FeedbackTelemetry - | TerminalTelemetry - | DebuggerTelemetry - | SettingsTelemetry - | DiagnosticsAction - | DiagnosticsMessages - | ImportNotebook - | Platform - | LanguageServePlatformSupported - | DebuggerConfigurationPromtpsTelemetry - | InterpreterAutoSelection - | InterpreterDiscovery - | InterpreterActivationEnvironmentVariables - | InterpreterActivation; diff --git a/src/test/application/diagnostics/applicationDiagnostics.unit.test.ts b/src/test/application/diagnostics/applicationDiagnostics.unit.test.ts index c4c3a36f694a..248934dfb1ed 100644 --- a/src/test/application/diagnostics/applicationDiagnostics.unit.test.ts +++ b/src/test/application/diagnostics/applicationDiagnostics.unit.test.ts @@ -3,7 +3,7 @@ 'use strict'; -// tslint:disable:insecure-random +// tslint:disable:insecure-random no-any import * as typemoq from 'typemoq'; import { DiagnosticSeverity } from 'vscode'; @@ -87,7 +87,7 @@ suite('Application Diagnostics - ApplicationDiagnostics', () => { const diagnostics: IDiagnostic[] = []; for (let i = 0; i <= (Math.random() * 10); i += 1) { const diagnostic: IDiagnostic = { - code: `Error${i}`, + code: `Error${i}` as any, message: `Error${i}`, scope: i % 2 === 0 ? DiagnosticScope.Global : DiagnosticScope.WorkspaceFolder, severity: DiagnosticSeverity.Error @@ -96,7 +96,7 @@ suite('Application Diagnostics - ApplicationDiagnostics', () => { } for (let i = 0; i <= (Math.random() * 10); i += 1) { const diagnostic: IDiagnostic = { - code: `Warning${i}`, + code: `Warning${i}` as any, message: `Warning${i}`, scope: i % 2 === 0 ? DiagnosticScope.Global : DiagnosticScope.WorkspaceFolder, severity: DiagnosticSeverity.Warning @@ -105,7 +105,7 @@ suite('Application Diagnostics - ApplicationDiagnostics', () => { } for (let i = 0; i <= (Math.random() * 10); i += 1) { const diagnostic: IDiagnostic = { - code: `Info${i}`, + code: `Info${i}` as any, message: `Info${i}`, scope: i % 2 === 0 ? DiagnosticScope.Global : DiagnosticScope.WorkspaceFolder, severity: DiagnosticSeverity.Information diff --git a/src/test/application/diagnostics/checks/envPathVariable.unit.test.ts b/src/test/application/diagnostics/checks/envPathVariable.unit.test.ts index a2f678f1f142..1100fd4a4e71 100644 --- a/src/test/application/diagnostics/checks/envPathVariable.unit.test.ts +++ b/src/test/application/diagnostics/checks/envPathVariable.unit.test.ts @@ -18,7 +18,7 @@ import { ICurrentProcess, IPathUtils } from '../../../../client/common/types'; import { EnvironmentVariables } from '../../../../client/common/variables/types'; import { IServiceContainer } from '../../../../client/ioc/types'; -// tslint:disable-next-line:max-func-body-length +// tslint:disable:max-func-body-length no-any suite('Application Diagnostics - Checks Env Path Variable', () => { let diagnosticService: IDiagnosticsService; let platformService: typemoq.IMock; @@ -81,7 +81,7 @@ suite('Application Diagnostics - Checks Env Path Variable', () => { test('Can not handle non-EnvPathVariable diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic.setup(d => d.code) - .returns(() => 'Something Else') + .returns(() => 'Something Else' as any) .verifiable(typemoq.Times.atLeastOnce()); const canHandle = await diagnosticService.canHandle(diagnostic.object); diff --git a/src/test/application/diagnostics/checks/invalidPythonPathInDebugger.unit.test.ts b/src/test/application/diagnostics/checks/invalidPythonPathInDebugger.unit.test.ts index fbac5631306f..aca1bdf3995e 100644 --- a/src/test/application/diagnostics/checks/invalidPythonPathInDebugger.unit.test.ts +++ b/src/test/application/diagnostics/checks/invalidPythonPathInDebugger.unit.test.ts @@ -3,7 +3,7 @@ 'use strict'; -// tslint:disable:no-invalid-template-strings max-func-body-length +// tslint:disable:no-invalid-template-strings max-func-body-length no-any import { expect } from 'chai'; import * as path from 'path'; @@ -65,7 +65,7 @@ suite('Application Diagnostics - Checks Python Path in debugger', () => { test('Can not handle non-InvalidPythonPathInDebugger diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic.setup(d => d.code) - .returns(() => 'Something Else') + .returns(() => 'Something Else' as any) .verifiable(typemoq.Times.atLeastOnce()); const canHandle = await diagnosticService.canHandle(diagnostic.object); diff --git a/src/test/application/diagnostics/checks/lsNotSupported.unit.test.ts b/src/test/application/diagnostics/checks/lsNotSupported.unit.test.ts index 179032c7acdc..f2fd01e3d797 100644 --- a/src/test/application/diagnostics/checks/lsNotSupported.unit.test.ts +++ b/src/test/application/diagnostics/checks/lsNotSupported.unit.test.ts @@ -13,6 +13,7 @@ import { DiagnosticCommandPromptHandlerServiceId, MessageCommandPrompt } from '. import { DiagnosticScope, IDiagnostic, IDiagnosticCommand, IDiagnosticFilterService, IDiagnosticHandlerService, IDiagnosticsService } from '../../../../client/application/diagnostics/types'; import { IServiceContainer } from '../../../../client/ioc/types'; +// tslint:disable:max-func-body-length no-any suite('Application Diagnostics - Checks LS not supported', () => { let serviceContainer: TypeMoq.IMock; let diagnosticService: IDiagnosticsService; @@ -95,7 +96,7 @@ suite('Application Diagnostics - Checks LS not supported', () => { test('LSNotSupportedDiagnosticService can not handle non-LSNotSupported diagnostics', async () => { const diagnostic = TypeMoq.Mock.ofType(); diagnostic.setup(d => d.code) - .returns(() => 'Something Else') + .returns(() => 'Something Else' as any) .verifiable(TypeMoq.Times.atLeastOnce()); const canHandle = await diagnosticService.canHandle(diagnostic.object); expect(canHandle).to.be.equal(false, 'Invalid value'); diff --git a/src/test/application/diagnostics/checks/powerShellActivation.unit.test.ts b/src/test/application/diagnostics/checks/powerShellActivation.unit.test.ts index f0d63aa587b0..9a9fc0b13e07 100644 --- a/src/test/application/diagnostics/checks/powerShellActivation.unit.test.ts +++ b/src/test/application/diagnostics/checks/powerShellActivation.unit.test.ts @@ -16,7 +16,7 @@ import { ICurrentProcess, IPathUtils } from '../../../../client/common/types'; import { EnvironmentVariables } from '../../../../client/common/variables/types'; import { IServiceContainer } from '../../../../client/ioc/types'; -// tslint:disable-next-line:max-func-body-length +// tslint:disable:max-func-body-length no-any suite('Application Diagnostics - PowerShell Activation', () => { let diagnosticService: IDiagnosticsService; let platformService: typemoq.IMock; @@ -79,7 +79,7 @@ suite('Application Diagnostics - PowerShell Activation', () => { test('Can not handle non-EnvPathVariable diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic.setup(d => d.code) - .returns(() => 'Something Else') + .returns(() => 'Something Else' as any) .verifiable(typemoq.Times.atLeastOnce()); const canHandle = await diagnosticService.canHandle(diagnostic.object); diff --git a/src/test/application/diagnostics/checks/pythonInterpreter.unit.test.ts b/src/test/application/diagnostics/checks/pythonInterpreter.unit.test.ts index 590a5f6a98a1..231a713e8be9 100644 --- a/src/test/application/diagnostics/checks/pythonInterpreter.unit.test.ts +++ b/src/test/application/diagnostics/checks/pythonInterpreter.unit.test.ts @@ -80,7 +80,7 @@ suite('xApplication Diagnostics - Checks Python Interpreter', () => { test('Can not handle non-InvalidPythonPathInterpreter diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic.setup(d => d.code) - .returns(() => 'Something Else') + .returns(() => 'Something Else' as any) .verifiable(typemoq.Times.atLeastOnce()); const canHandle = await diagnosticService.canHandle(diagnostic.object); diff --git a/src/test/application/diagnostics/commands/ignore.unit.test.ts b/src/test/application/diagnostics/commands/ignore.unit.test.ts index 586071b9abe0..0a68368cbd78 100644 --- a/src/test/application/diagnostics/commands/ignore.unit.test.ts +++ b/src/test/application/diagnostics/commands/ignore.unit.test.ts @@ -8,6 +8,8 @@ import { IgnoreDiagnosticCommand } from '../../../../client/application/diagnost import { DiagnosticScope, IDiagnostic, IDiagnosticCommand, IDiagnosticFilterService } from '../../../../client/application/diagnostics/types'; import { IServiceContainer } from '../../../../client/ioc/types'; +// tslint:disable:no-any + suite('Application Diagnostics - Commands Ignore', () => { let ignoreCommand: IDiagnosticCommand; let serviceContainer: typemoq.IMock; @@ -24,7 +26,7 @@ suite('Application Diagnostics - Commands Ignore', () => { serviceContainer.setup(s => s.get(typemoq.It.isValue(IDiagnosticFilterService))) .returns(() => filterService.object) .verifiable(typemoq.Times.once()); - diagnostic.setup(d => d.code).returns(() => 'xyz') + diagnostic.setup(d => d.code).returns(() => 'xyz' as any) .verifiable(typemoq.Times.once()); filterService.setup(s => s.ignoreDiagnostic(typemoq.It.isValue('xyz'), typemoq.It.isValue(DiagnosticScope.Global))) .verifiable(typemoq.Times.once()); diff --git a/src/test/application/diagnostics/promptHandler.unit.test.ts b/src/test/application/diagnostics/promptHandler.unit.test.ts index 4867af1e51f8..f53cdc201d9f 100644 --- a/src/test/application/diagnostics/promptHandler.unit.test.ts +++ b/src/test/application/diagnostics/promptHandler.unit.test.ts @@ -3,7 +3,7 @@ 'use strict'; -// tslint:disable:insecure-random max-func-body-length +// tslint:disable:insecure-random max-func-body-length no-any import * as typemoq from 'typemoq'; import { DiagnosticSeverity } from 'vscode'; @@ -30,7 +30,7 @@ suite('Application Diagnostics - PromptHandler', () => { getNamesAndValues(DiagnosticSeverity).forEach(severity => { test(`Handling a diagnositic of severity '${severity.name}' should display a message without any buttons`, async () => { - const diagnostic: IDiagnostic = { code: '1', message: 'one', scope: DiagnosticScope.Global, severity: severity.value }; + const diagnostic: IDiagnostic = { code: '1' as any, message: 'one', scope: DiagnosticScope.Global, severity: severity.value }; switch (severity.value) { case DiagnosticSeverity.Error: { appShell.setup(a => a.showErrorMessage(typemoq.It.isValue(diagnostic.message))) @@ -53,7 +53,7 @@ suite('Application Diagnostics - PromptHandler', () => { appShell.verifyAll(); }); test(`Handling a diagnositic of severity '${severity.name}' should display a custom message with buttons`, async () => { - const diagnostic: IDiagnostic = { code: '1', message: 'one', scope: DiagnosticScope.Global, severity: severity.value }; + const diagnostic: IDiagnostic = { code: '1' as any, message: 'one', scope: DiagnosticScope.Global, severity: severity.value }; const options: MessageCommandPrompt = { commandPrompts: [ { prompt: 'Yes' }, @@ -87,7 +87,7 @@ suite('Application Diagnostics - PromptHandler', () => { appShell.verifyAll(); }); test(`Handling a diagnositic of severity '${severity.name}' should display a custom message with buttons and invoke selected command`, async () => { - const diagnostic: IDiagnostic = { code: '1', message: 'one', scope: DiagnosticScope.Global, severity: severity.value }; + const diagnostic: IDiagnostic = { code: '1' as any, message: 'one', scope: DiagnosticScope.Global, severity: severity.value }; const command = typemoq.Mock.ofType(); const options: MessageCommandPrompt = { commandPrompts: [ diff --git a/src/test/telemetry/index.unit.test.ts b/src/test/telemetry/index.unit.test.ts index 902289548e6b..e25756fae78a 100644 --- a/src/test/telemetry/index.unit.test.ts +++ b/src/test/telemetry/index.unit.test.ts @@ -3,7 +3,7 @@ 'use strict'; -//tslint:disable:max-func-body-length match-default-export-name +//tslint:disable:max-func-body-length match-default-export-name no-any import { expect } from 'chai'; import rewiremock from 'rewiremock'; @@ -42,7 +42,7 @@ suite('Telemetry', () => { const measuers = { start: 123, end: 987 }; // tslint:disable-next-line:no-any - sendTelemetryEvent(eventName, measuers, properties as any); + sendTelemetryEvent(eventName as any, measuers, properties as any); expect(Reporter.eventName).to.equal(eventName); expect(Reporter.measures).to.deep.equal(measuers); @@ -54,7 +54,7 @@ suite('Telemetry', () => { const eventName = 'Testing'; - sendTelemetryEvent(eventName); + sendTelemetryEvent(eventName as any); expect(Reporter.eventName).to.equal(eventName); expect(Reporter.measures).to.equal(undefined, 'Measures should be empty'); @@ -70,7 +70,7 @@ suite('Telemetry', () => { const measuers = { start: 123, end: 987 }; // tslint:disable-next-line:no-any - sendTelemetryEvent(eventName, measuers, properties as any, error); + sendTelemetryEvent(eventName as any, measuers, properties as any, error); const stackTrace = Reporter.properties.stackTrace; delete Reporter.properties.stackTrace; @@ -108,7 +108,7 @@ at processImmediate [as _immediateCallback] (timers.js:722:5)`; const measuers = { start: 123, end: 987 }; // tslint:disable-next-line:no-any - sendTelemetryEvent(eventName, measuers, properties as any, error); + sendTelemetryEvent(eventName as any, measuers, properties as any, error); const stackTrace = Reporter.properties.stackTrace; delete Reporter.properties.stackTrace; @@ -155,7 +155,7 @@ at Immediate. (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js const measuers = { start: 123, end: 987 }; // tslint:disable-next-line:no-any - sendTelemetryEvent(eventName, measuers, properties as any, error); + sendTelemetryEvent(eventName as any, measuers, properties as any, error); const stackTrace = Reporter.properties.stackTrace; delete Reporter.properties.stackTrace; @@ -190,7 +190,7 @@ at Immediate. (${EXTENSION_ROOT_DIR}/node_modules/mocha/lib/runner.js const measuers = { start: 123, end: 987 }; // tslint:disable-next-line:no-any - sendTelemetryEvent(eventName, measuers, properties as any, error); + sendTelemetryEvent(eventName as any, measuers, properties as any, error); const stackTrace = Reporter.properties.stackTrace; delete Reporter.properties.stackTrace; From 06e37ae1428ef1936e82cf81fb82b743809fa063 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 17 Jan 2019 11:16:59 -0800 Subject: [PATCH 3/3] Fixes to tests --- .../diagnostics/checks/macPythonInterpreter.unit.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts b/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts index 9f20ebeee276..a77f62147934 100644 --- a/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts +++ b/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts @@ -88,7 +88,7 @@ suite('Application Diagnostics - Checks Python Interpreter', () => { test('Can not handle non-InvalidPythonPathInterpreter diagnostics', async () => { const diagnostic = typemoq.Mock.ofType(); diagnostic.setup(d => d.code) - .returns(() => 'Something Else') + .returns(() => 'Something Else' as any) .verifiable(typemoq.Times.atLeastOnce()); const canHandle = await diagnosticService.canHandle(diagnostic.object);