diff --git a/extensions/agent/src/dialogs/agentDialog.ts b/extensions/agent/src/dialogs/agentDialog.ts index 63d5d240d2..8ce3487b86 100644 --- a/extensions/agent/src/dialogs/agentDialog.ts +++ b/extensions/agent/src/dialogs/agentDialog.ts @@ -20,6 +20,9 @@ export abstract class AgentDialog { public readonly onSuccess: vscode.Event = this._onSuccess.event; public dialog: sqlops.window.modelviewdialog.Dialog; + // Dialog Name for Telemetry + public dialogName: string; + constructor(public ownerUri: string, public model: T, public title: string) { } @@ -31,8 +34,9 @@ export abstract class AgentDialog { protected abstract async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog); - public async openDialog() { - this.dialog = sqlops.window.modelviewdialog.createDialog(this.title); + public async openDialog(dialogName?: string) { + let event = dialogName ? dialogName : null; + this.dialog = sqlops.window.modelviewdialog.createDialog(this.title, event); await this.model.initialize(); diff --git a/extensions/agent/src/dialogs/alertDialog.ts b/extensions/agent/src/dialogs/alertDialog.ts index b10ba8e40e..031d58c6bb 100644 --- a/extensions/agent/src/dialogs/alertDialog.ts +++ b/extensions/agent/src/dialogs/alertDialog.ts @@ -116,6 +116,10 @@ export class AlertDialog extends AgentDialog { private static readonly DelayMinutesTextBoxLabel: string = localize('alertDialog.DelayMinutes', 'Delay Minutes'); private static readonly DelaySecondsTextBoxLabel: string = localize('alertDialog.DelaySeconds', 'Delay Seconds'); + // Event Name strings + private readonly NewAlertDialog = 'NewAlertDialogOpen'; + private readonly EditAlertDialog = 'EditAlertDialogOpened'; + // UI Components private generalTab: sqlops.window.modelviewdialog.DialogTab; private responseTab: sqlops.window.modelviewdialog.DialogTab; @@ -149,6 +153,7 @@ export class AlertDialog extends AgentDialog { private delayMinutesTextBox: sqlops.InputBoxComponent; private delaySecondsTextBox: sqlops.InputBoxComponent; + private isEdit: boolean = false; private databases: string[]; private jobModel: JobData; public jobId: string; @@ -166,6 +171,8 @@ export class AlertDialog extends AgentDialog { this.jobModel = jobModel; this.jobId = this.jobId ? this.jobId : this.jobModel.jobId; this.jobName = this.jobName ? this.jobName : this.jobModel.name; + this.isEdit = alertInfo ? true : false; + this.dialogName = this.isEdit ? this.EditAlertDialog : this.NewAlertDialog; } protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) { diff --git a/extensions/agent/src/dialogs/jobDialog.ts b/extensions/agent/src/dialogs/jobDialog.ts index ad263cd690..6111eacae4 100644 --- a/extensions/agent/src/dialogs/jobDialog.ts +++ b/extensions/agent/src/dialogs/jobDialog.ts @@ -67,6 +67,10 @@ export class JobDialog extends AgentDialog { private readonly AlertEnabledLabelString: string = localize('jobDialog.alertEnabledLabel', 'Enabled'); private readonly AlertTypeLabelString: string = localize('jobDialog.alertTypeLabel', 'Type'); + // Event Name strings + private readonly NewJobDialogEvent: string = 'NewJobDialogOpened'; + private readonly EditJobDialogEvent: string = 'EditJobDialogOpened'; + // UI Components private generalTab: sqlops.window.modelviewdialog.DialogTab; private stepsTab: sqlops.window.modelviewdialog.DialogTab; @@ -125,6 +129,7 @@ export class JobDialog extends AgentDialog { this.schedules = this.model.jobSchedules ? this.model.jobSchedules : []; this.alerts = this.model.alerts ? this.model.alerts : []; this.isEdit = jobInfo ? true : false; + this.dialogName = this.isEdit ? this.EditJobDialogEvent : this.NewJobDialogEvent; } protected async initializeDialog() { diff --git a/extensions/agent/src/dialogs/jobStepDialog.ts b/extensions/agent/src/dialogs/jobStepDialog.ts index 7e9990f03c..7fede4c2e1 100644 --- a/extensions/agent/src/dialogs/jobStepDialog.ts +++ b/extensions/agent/src/dialogs/jobStepDialog.ts @@ -67,6 +67,9 @@ export class JobStepDialog extends AgentDialog { private readonly QuitJobReportingSuccess: string = localize('jobStepDialog.quitJobSuccess', 'Quit the job reporting success'); private readonly QuitJobReportingFailure: string = localize('jobStepDialog.quitJobFailure', 'Quit the job reporting failure'); + // Event Name strings + private readonly NewStepDialog = 'NewStepDialogOpened'; + private readonly EditStepDialog = 'EditStepDialogOpened'; // UI Components // Dialogs @@ -131,6 +134,7 @@ export class JobStepDialog extends AgentDialog { this.jobModel = jobModel; this.jobName = this.jobName ? this.jobName : this.jobModel.name; this.server = server; + this.dialogName = this.isEdit ? this.EditStepDialog : this.NewStepDialog; } private initializeUIComponents() { diff --git a/extensions/agent/src/dialogs/operatorDialog.ts b/extensions/agent/src/dialogs/operatorDialog.ts index 3c87be610a..d04361e3b0 100644 --- a/extensions/agent/src/dialogs/operatorDialog.ts +++ b/extensions/agent/src/dialogs/operatorDialog.ts @@ -43,6 +43,10 @@ export class OperatorDialog extends AgentDialog { private static readonly AlertEmailColumnLabel: string = localize('createOperator.AlertEmailColumnLabel', 'E-mail'); private static readonly AlertPagerColumnLabel: string = localize('createOperator.AlertPagerColumnLabel', 'Pager'); + // Event strings + private readonly NewOperatorDialog = 'NewOperatorDialogOpened'; + private readonly EditOperatorDialog = 'EditOperatorDialogOpened'; + // UI Components private generalTab: sqlops.window.modelviewdialog.DialogTab; private notificationsTab: sqlops.window.modelviewdialog.DialogTab; @@ -68,12 +72,15 @@ export class OperatorDialog extends AgentDialog { // Notification tab controls private alertsTable: sqlops.TableComponent; + private isEdit: boolean = false; constructor(ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo = undefined) { super( ownerUri, new OperatorData(ownerUri, operatorInfo), operatorInfo ? OperatorDialog.EditDialogTitle : OperatorDialog.CreateDialogTitle); + this.isEdit = operatorInfo ? true : false; + this.dialogName = this.isEdit ? this.EditOperatorDialog : this.NewOperatorDialog; } protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) { diff --git a/extensions/agent/src/dialogs/proxyDialog.ts b/extensions/agent/src/dialogs/proxyDialog.ts index 453c698160..de5f76a3d4 100644 --- a/extensions/agent/src/dialogs/proxyDialog.ts +++ b/extensions/agent/src/dialogs/proxyDialog.ts @@ -36,6 +36,9 @@ export class ProxyDialog extends AgentDialog { private static readonly PowerShellLabel: string = localize('createProxy.PowerShell', 'PowerShell'); private static readonly SubSystemHeadingLabel: string = localize('createProxy.subSystemHeading', 'Active to the following subsytems'); + private readonly NewProxyDialog = 'NewProxyDialogOpened'; + private readonly EditProxyDialog = 'EditProxyDialogOpened'; + // UI Components private generalTab: sqlops.window.modelviewdialog.DialogTab; @@ -56,6 +59,7 @@ export class ProxyDialog extends AgentDialog { private powershellCheckBox: sqlops.CheckBoxComponent; private credentials: sqlops.CredentialInfo[]; + private isEdit: boolean = false; constructor(ownerUri: string, proxyInfo: sqlops.AgentProxyInfo = undefined, credentials: sqlops.CredentialInfo[]) { super( @@ -63,6 +67,8 @@ export class ProxyDialog extends AgentDialog { new ProxyData(ownerUri, proxyInfo), proxyInfo ? ProxyDialog.EditDialogTitle : ProxyDialog.CreateDialogTitle); this.credentials = credentials; + this.isEdit = proxyInfo ? true : false; + this.dialogName = this.isEdit ? this.EditProxyDialog : this.NewProxyDialog; } protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) { diff --git a/extensions/agent/src/mainController.ts b/extensions/agent/src/mainController.ts index ce1ff93608..d8a35b7ca9 100644 --- a/extensions/agent/src/mainController.ts +++ b/extensions/agent/src/mainController.ts @@ -40,13 +40,13 @@ export class MainController { public activate(): void { vscode.commands.registerCommand('agent.openJobDialog', (ownerUri: string, jobInfo: sqlops.AgentJobInfo) => { let dialog = new JobDialog(ownerUri, jobInfo); - dialog.openDialog(); + dialog.dialogName ? dialog.openDialog(dialog.dialogName) : dialog.openDialog(); }); vscode.commands.registerCommand('agent.openNewStepDialog', (ownerUri: string, server: string, jobInfo: sqlops.AgentJobInfo, jobStepInfo: sqlops.AgentJobStepInfo) => { AgentUtils.getAgentService().then((agentService) => { let jobData: JobData = new JobData(ownerUri, jobInfo, agentService); let dialog = new JobStepDialog(ownerUri, server, jobData, jobStepInfo, false); - dialog.openDialog(); + dialog.dialogName ? dialog.openDialog(dialog.dialogName) : dialog.openDialog(); }); }); vscode.commands.registerCommand('agent.openPickScheduleDialog', (ownerUri: string, jobName: string) => { @@ -57,17 +57,16 @@ export class MainController { AgentUtils.getAgentService().then((agentService) => { let jobData: JobData = new JobData(ownerUri, jobInfo, agentService); let dialog = new AlertDialog(ownerUri, jobData, alertInfo, false); - dialog.openDialog(); + dialog.dialogName ? dialog.openDialog(dialog.dialogName) : dialog.openDialog(); }); }); vscode.commands.registerCommand('agent.openOperatorDialog', (ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo) => { let dialog = new OperatorDialog(ownerUri, operatorInfo); - dialog.openDialog(); + dialog.dialogName ? dialog.openDialog(dialog.dialogName) : dialog.openDialog(); }); vscode.commands.registerCommand('agent.openProxyDialog', (ownerUri: string, proxyInfo: sqlops.AgentProxyInfo, credentials: sqlops.CredentialInfo[]) => { let dialog = new ProxyDialog(ownerUri, proxyInfo, credentials); - dialog.openDialog(); - MainController.showNotYetImplemented(); + dialog.dialogName ? dialog.openDialog(dialog.dialogName) : dialog.openDialog(); }); } diff --git a/src/sql/common/telemetryKeys.ts b/src/sql/common/telemetryKeys.ts index 44cc3896f3..53e954b5ed 100644 --- a/src/sql/common/telemetryKeys.ts +++ b/src/sql/common/telemetryKeys.ts @@ -25,7 +25,6 @@ export const NewQuery = 'NewQuery'; export const FirewallRuleRequested = 'FirewallRuleCreated'; export const DashboardNavigated = 'DashboardNavigated'; - // Telemetry Properties // Modal Dialogs: @@ -42,3 +41,21 @@ export const Accounts = 'Accounts'; export const FireWallRule = 'FirewallRule'; export const AutoOAuth = 'AutoOAuth'; export const AddNewDashboardTab = 'AddNewDashboardTab'; + +// SQL Agent Events: + +// Views +export const JobsView = 'JobsViewOpened'; +export const JobHistoryView = 'JobHistoryViewOpened'; +export const JobStepsView = 'JobStepsViewOpened'; + +// Actions +export const RunAgentJob = 'RunAgentJob'; +export const StopAgentJob = 'StopAgentJob'; +export const DeleteAgentJob = 'DeleteAgentJob'; +export const DeleteAgentJobStep = 'DeleteAgentJobStep'; +export const DeleteAgentAlert = 'DeleteAgentAlert'; +export const DeleteAgentOperator = 'DeleteAgentOperator'; +export const DeleteAgentProxy = 'DeleteAgentProxy'; + + diff --git a/src/sql/common/telemetryUtilities.ts b/src/sql/common/telemetryUtilities.ts index fbeebdc5f6..cabc0d2d19 100644 --- a/src/sql/common/telemetryUtilities.ts +++ b/src/sql/common/telemetryUtilities.ts @@ -4,31 +4,10 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as crypto from 'crypto'; -import * as os from 'os'; import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { warn } from 'sql/base/common/log'; -import { generateUuid } from 'vs/base/common/uuid'; - -// Generate a unique, deterministic ID for the current user of the extension -export function generateUserId(): Promise { - return new Promise(resolve => { - try { - getmac.getMac((error, macAddress) => { - if (!error) { - resolve(crypto.createHash('sha256').update(macAddress + os.homedir(), 'utf8').digest('hex')); - } else { - resolve(generateUuid()); // fallback - } - }); - } catch (err) { - resolve(generateUuid()); // fallback - } - }); -} - export interface IConnectionTelemetryData extends ITelemetryData { provider?: string; } diff --git a/src/sql/parts/jobManagement/common/jobActions.ts b/src/sql/parts/jobManagement/common/jobActions.ts index 1ec5c54fd9..a065b87cf0 100644 --- a/src/sql/parts/jobManagement/common/jobActions.ts +++ b/src/sql/parts/jobManagement/common/jobActions.ts @@ -17,6 +17,9 @@ import { AlertsViewComponent } from 'sql/parts/jobManagement/views/alertsView.co import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsView.component'; import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; +import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils'; export enum JobActions { Run = 'run', @@ -80,7 +83,8 @@ export class RunJobAction extends Action { constructor( @INotificationService private notificationService: INotificationService, @IJobManagementService private jobManagementService: IJobManagementService, - @IInstantiationService private instantationService: IInstantiationService + @IInstantiationService private instantationService: IInstantiationService, + @ITelemetryService private telemetryService: ITelemetryService ) { super(RunJobAction.ID, RunJobAction.LABEL, 'runJobIcon'); } @@ -89,6 +93,7 @@ export class RunJobAction extends Action { let jobName = context.agentJobInfo.name; let ownerUri = context.ownerUri; let refreshAction = this.instantationService.createInstance(JobsRefreshAction); + this.telemetryService.publicLog(TelemetryKeys.RunAgentJob); return new TPromise((resolve, reject) => { this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(result => { if (result.success) { @@ -118,7 +123,8 @@ export class StopJobAction extends Action { constructor( @INotificationService private notificationService: INotificationService, @IJobManagementService private jobManagementService: IJobManagementService, - @IInstantiationService private instantationService: IInstantiationService + @IInstantiationService private instantationService: IInstantiationService, + @ITelemetryService private telemetryService: ITelemetryService ) { super(StopJobAction.ID, StopJobAction.LABEL, 'stopJobIcon'); } @@ -127,6 +133,7 @@ export class StopJobAction extends Action { let jobName = context.agentJobInfo.name; let ownerUri = context.ownerUri; let refreshAction = this.instantationService.createInstance(JobsRefreshAction); + this.telemetryService.publicLog(TelemetryKeys.StopAgentJob); return new TPromise((resolve, reject) => { this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(result => { if (result.success) { @@ -174,7 +181,8 @@ export class DeleteJobAction extends Action { constructor( @INotificationService private _notificationService: INotificationService, - @IJobManagementService private _jobService: IJobManagementService + @IJobManagementService private _jobService: IJobManagementService, + @ITelemetryService private _telemetryService: ITelemetryService ) { super(DeleteJobAction.ID, DeleteJobAction.LABEL); } @@ -188,6 +196,7 @@ export class DeleteJobAction extends Action { [{ label: DeleteJobAction.LABEL, run: () => { + this._telemetryService.publicLog(TelemetryKeys.DeleteAgentJob); self._jobService.deleteJob(actionInfo.ownerUri, actionInfo.targetObject).then(result => { if (!result || !result.success) { let errorMessage = nls.localize("jobaction.failedToDeleteJob", "Could not delete job '{0}'.\nError: {1}", @@ -234,7 +243,8 @@ export class DeleteStepAction extends Action { constructor( @INotificationService private _notificationService: INotificationService, @IJobManagementService private _jobService: IJobManagementService, - @IInstantiationService private instantationService: IInstantiationService + @IInstantiationService private instantationService: IInstantiationService, + @ITelemetryService private _telemetryService: ITelemetryService ) { super(DeleteStepAction.ID, DeleteStepAction.LABEL); } @@ -249,6 +259,7 @@ export class DeleteStepAction extends Action { [{ label: DeleteStepAction.LABEL, run: () => { + this._telemetryService.publicLog(TelemetryKeys.DeleteAgentJobStep); self._jobService.deleteJobStep(actionInfo.ownerUri, actionInfo.targetObject).then(result => { if (!result || !result.success) { let errorMessage = nls.localize("jobaction.failedToDeleteStep", "Could not delete step '{0}'.\nError: {1}", @@ -318,7 +329,8 @@ export class DeleteAlertAction extends Action { constructor( @INotificationService private _notificationService: INotificationService, - @IJobManagementService private _jobService: IJobManagementService + @IJobManagementService private _jobService: IJobManagementService, + @ITelemetryService private _telemetryService: ITelemetryService ) { super(DeleteAlertAction.ID, DeleteAlertAction.LABEL); } @@ -332,6 +344,7 @@ export class DeleteAlertAction extends Action { [{ label: DeleteAlertAction.LABEL, run: () => { + this._telemetryService.publicLog(TelemetryKeys.DeleteAgentAlert); self._jobService.deleteAlert(actionInfo.ownerUri, actionInfo.targetObject).then(result => { if (!result || !result.success) { let errorMessage = nls.localize("jobaction.failedToDeleteAlert", "Could not delete alert '{0}'.\nError: {1}", @@ -397,7 +410,8 @@ export class DeleteOperatorAction extends Action { constructor( @INotificationService private _notificationService: INotificationService, - @IJobManagementService private _jobService: IJobManagementService + @IJobManagementService private _jobService: IJobManagementService, + @ITelemetryService private _telemetryService: ITelemetryService ) { super(DeleteOperatorAction.ID, DeleteOperatorAction.LABEL); } @@ -411,6 +425,7 @@ export class DeleteOperatorAction extends Action { [{ label: DeleteOperatorAction.LABEL, run: () => { + this._telemetryService.publicLog(TelemetryKeys.DeleteAgentOperator); self._jobService.deleteOperator(actionInfo.ownerUri, actionInfo.targetObject).then(result => { if (!result || !result.success) { let errorMessage = nls.localize("jobaction.failedToDeleteOperator", "Could not delete operator '{0}'.\nError: {1}", @@ -477,7 +492,8 @@ export class DeleteProxyAction extends Action { constructor( @INotificationService private _notificationService: INotificationService, - @IJobManagementService private _jobService: IJobManagementService + @IJobManagementService private _jobService: IJobManagementService, + @ITelemetryService private _telemetryService: ITelemetryService ) { super(DeleteProxyAction.ID, DeleteProxyAction.LABEL); } @@ -491,6 +507,7 @@ export class DeleteProxyAction extends Action { [{ label: DeleteProxyAction.LABEL, run: () => { + this._telemetryService.publicLog(TelemetryKeys.DeleteAgentProxy); self._jobService.deleteProxy(actionInfo.ownerUri, actionInfo.targetObject).then(result => { if (!result || !result.success) { let errorMessage = nls.localize("jobaction.failedToDeleteProxy", "Could not delete proxy '{0}'.\nError: {1}", diff --git a/src/sql/parts/jobManagement/views/jobHistory.component.ts b/src/sql/parts/jobManagement/views/jobHistory.component.ts index 1d2aef7f52..46a3d8d0b3 100644 --- a/src/sql/parts/jobManagement/views/jobHistory.component.ts +++ b/src/sql/parts/jobManagement/views/jobHistory.component.ts @@ -29,6 +29,8 @@ import { JobManagementView } from 'sql/parts/jobManagement/views/jobManagementVi import { TabChild } from 'sql/base/browser/ui/panel/tab.component'; import { IDashboardService } from 'sql/services/dashboard/common/dashboardService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; export const DASHBOARD_SELECTOR: string = 'jobhistory-component'; @@ -76,7 +78,8 @@ export class JobHistoryComponent extends JobManagementView implements OnInit { @Inject(IContextMenuService) private contextMenuService: IContextMenuService, @Inject(IJobManagementService) private _jobManagementService: IJobManagementService, @Inject(IKeybindingService) keybindingService: IKeybindingService, - @Inject(IDashboardService) dashboardService: IDashboardService + @Inject(IDashboardService) dashboardService: IDashboardService, + @Inject(ITelemetryService) private _telemetryService: ITelemetryService ) { super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService); this._treeController = new JobHistoryController(); @@ -142,6 +145,7 @@ export class JobHistoryComponent extends JobManagementView implements OnInit { this._register(attachListStyler(this._tree, this.themeService)); this._tree.layout(dom.getContentHeight(this._tableContainer.nativeElement)); this.initActionBar(); + this._telemetryService.publicLog(TelemetryKeys.JobHistoryView); } private loadHistory() { diff --git a/src/sql/parts/jobManagement/views/jobStepsView.component.ts b/src/sql/parts/jobManagement/views/jobStepsView.component.ts index bcf2b6597d..d51862585f 100644 --- a/src/sql/parts/jobManagement/views/jobStepsView.component.ts +++ b/src/sql/parts/jobManagement/views/jobStepsView.component.ts @@ -21,6 +21,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TabChild } from 'sql/base/browser/ui/panel/tab.component'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component'; @@ -48,7 +50,8 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit, @Inject(IInstantiationService) instantiationService: IInstantiationService, @Inject(IContextMenuService) contextMenuService: IContextMenuService, @Inject(IKeybindingService) keybindingService: IKeybindingService, - @Inject(IDashboardService) dashboardService: IDashboardService + @Inject(IDashboardService) dashboardService: IDashboardService, + @Inject(ITelemetryService) private _telemetryService: ITelemetryService ) { super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService); } @@ -72,6 +75,7 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit, }, {verticalScrollMode: ScrollbarVisibility.Visible, horizontalScrollMode: ScrollbarVisibility.Visible }); this.layout(); this._register(attachListStyler(this._tree, this.themeService)); + this._telemetryService.publicLog(TelemetryKeys.JobStepsView); } public onFirstVisible() { diff --git a/src/sql/parts/jobManagement/views/jobsView.component.ts b/src/sql/parts/jobManagement/views/jobsView.component.ts index a945c13540..882cad38a8 100644 --- a/src/sql/parts/jobManagement/views/jobsView.component.ts +++ b/src/sql/parts/jobManagement/views/jobsView.component.ts @@ -37,6 +37,8 @@ import { IDashboardService } from 'sql/services/dashboard/common/dashboardServic import { escape } from 'sql/base/common/strings'; import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { tableBackground, cellBackground, cellBorderColor } from 'sql/common/theme/colors'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import * as TelemetryKeys from 'sql/common/telemetryKeys'; export const JOBSVIEW_SELECTOR: string = 'jobsview-component'; export const ROW_HEIGHT: number = 45; @@ -106,7 +108,8 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe @Inject(IInstantiationService) instantiationService: IInstantiationService, @Inject(IContextMenuService) contextMenuService: IContextMenuService, @Inject(IKeybindingService) keybindingService: IKeybindingService, - @Inject(IDashboardService) _dashboardService: IDashboardService + @Inject(IDashboardService) _dashboardService: IDashboardService, + @Inject(ITelemetryService) private _telemetryService: ITelemetryService ) { super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService); this._didTabChange = false; @@ -127,6 +130,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe this._visibilityElement = this._gridEl; this._parentComponent = this._agentViewComponent; this._register(this._themeService.onDidColorThemeChange(e => this.updateTheme(e))); + this._telemetryService.publicLog(TelemetryKeys.JobsView); } ngOnDestroy() { diff --git a/src/sql/platform/dialog/customDialogService.ts b/src/sql/platform/dialog/customDialogService.ts index 76c9bd8371..c374f35a53 100644 --- a/src/sql/platform/dialog/customDialogService.ts +++ b/src/sql/platform/dialog/customDialogService.ts @@ -22,8 +22,9 @@ export class CustomDialogService { constructor( @IInstantiationService private _instantiationService: IInstantiationService) { } - public showDialog(dialog: Dialog, options?: IModalOptions): void { - let dialogModal = this._instantiationService.createInstance(DialogModal, dialog, 'CustomDialog', options || defaultOptions); + public showDialog(dialog: Dialog, dialogName?: string, options?: IModalOptions): void { + let name = dialogName ? dialogName : 'CustomDialog'; + let dialogModal = this._instantiationService.createInstance(DialogModal, dialog, name, options || defaultOptions); this._dialogModals.set(dialog, dialogModal); dialogModal.render(); dialogModal.open(); diff --git a/src/sql/sqlops.proposed.d.ts b/src/sql/sqlops.proposed.d.ts index db00f795a5..bb80b97897 100644 --- a/src/sql/sqlops.proposed.d.ts +++ b/src/sql/sqlops.proposed.d.ts @@ -839,7 +839,7 @@ declare module 'sqlops' { * Create a dialog with the given title * @param title The title of the dialog, displayed at the top */ - export function createDialog(title: string): Dialog; + export function createDialog(title: string, dialogName?: string): Dialog; /** * Create a dialog tab which can be included as part of the content of a dialog @@ -951,6 +951,12 @@ declare module 'sqlops' { */ message: DialogMessage; + /** + * Set the dialog name when opening + * the dialog for telemetry + */ + dialogName?: string; + /** * Register a callback that will be called when the user tries to click done. Only * one callback can be registered at once, so each registration call will clear diff --git a/src/sql/workbench/api/node/extHostModelViewDialog.ts b/src/sql/workbench/api/node/extHostModelViewDialog.ts index 29fbb39b07..b4894c07e5 100644 --- a/src/sql/workbench/api/node/extHostModelViewDialog.ts +++ b/src/sql/workbench/api/node/extHostModelViewDialog.ts @@ -16,6 +16,8 @@ import * as sqlops from 'sqlops'; import { SqlMainContext, ExtHostModelViewDialogShape, MainThreadModelViewDialogShape, ExtHostModelViewShape, ExtHostBackgroundTaskManagementShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Inject } from '@angular/core'; const DONE_LABEL = nls.localize('dialogDoneLabel', 'Done'); const CANCEL_LABEL = nls.localize('dialogCancelLabel', 'Cancel'); @@ -125,6 +127,7 @@ class DialogImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdi private _message: sqlops.window.modelviewdialog.DialogMessage; private _closeValidator: () => boolean | Thenable; private _operationHandler: BackgroundOperationHandler; + private _dialogName: string; constructor(extHostModelViewDialog: ExtHostModelViewDialog, extHostModelView: ExtHostModelViewShape, @@ -157,6 +160,14 @@ class DialogImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdi this._extHostModelViewDialog.updateDialogContent(this); } + public get dialogName(): string { + return this._dialogName; + } + + public set dialogName(value: string) { + this._dialogName = value; + } + public registerCloseValidator(validator: () => boolean | Thenable): void { this._closeValidator = validator; } @@ -503,7 +514,8 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape { public openDialog(dialog: sqlops.window.modelviewdialog.Dialog): void { let handle = this.getHandle(dialog); this.updateDialogContent(dialog); - this._proxy.$openDialog(handle); + dialog.dialogName ? this._proxy.$openDialog(handle, dialog.dialogName) : + this._proxy.$openDialog(handle); } public closeDialog(dialog: sqlops.window.modelviewdialog.Dialog): void { @@ -560,8 +572,11 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape { this._onClickCallbacks.set(handle, callback); } - public createDialog(title: string, extensionLocation?: URI): sqlops.window.modelviewdialog.Dialog { + public createDialog(title: string, dialogName?: string, extensionLocation?: URI): sqlops.window.modelviewdialog.Dialog { let dialog = new DialogImpl(this, this._extHostModelView, this._extHostTaskManagement, extensionLocation); + if (dialogName) { + dialog.dialogName = dialogName; + } dialog.title = title; dialog.handle = this.getHandle(dialog); return dialog; diff --git a/src/sql/workbench/api/node/mainThreadModelViewDialog.ts b/src/sql/workbench/api/node/mainThreadModelViewDialog.ts index 68ff5d16f0..98a3d5c3b8 100644 --- a/src/sql/workbench/api/node/mainThreadModelViewDialog.ts +++ b/src/sql/workbench/api/node/mainThreadModelViewDialog.ts @@ -19,6 +19,7 @@ import { ModelViewInput, ModelViewInputModel, ModeViewSaveHandler } from 'sql/pa import * as vscode from 'vscode'; import * as sqlops from 'sqlops'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @extHostNamedCustomer(SqlMainContext.MainThreadModelViewDialog) export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape { @@ -35,7 +36,8 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape constructor( context: IExtHostContext, @IInstantiationService private _instatiationService: IInstantiationService, - @IEditorService private _editorService: IEditorService + @IEditorService private _editorService: IEditorService, + @ITelemetryService private _telemetryService: ITelemetryService ) { this._proxy = context.getProxy(SqlExtHostContext.ExtHostModelViewDialog); this._dialogService = new CustomDialogService(_instatiationService); @@ -68,9 +70,9 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape return this._proxy.$handleSave(handle); } - public $openDialog(handle: number): Thenable { + public $openDialog(handle: number, dialogName?: string): Thenable { let dialog = this.getDialog(handle); - this._dialogService.showDialog(dialog); + dialogName ? this._dialogService.showDialog(dialog, dialogName) : this._dialogService.showDialog(dialog); return Promise.resolve(); } diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts index 051de9b857..4862fb0b24 100644 --- a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -348,8 +348,8 @@ export function createApiFactory( }; const modelViewDialog: typeof sqlops.window.modelviewdialog = { - createDialog(title: string): sqlops.window.modelviewdialog.Dialog { - return extHostModelViewDialog.createDialog(title, extension.extensionLocation); + createDialog(title: string, dialogName?: string): sqlops.window.modelviewdialog.Dialog { + return extHostModelViewDialog.createDialog(title, dialogName, extension.extensionLocation); }, createTab(title: string): sqlops.window.modelviewdialog.DialogTab { return extHostModelViewDialog.createTab(title, extension.extensionLocation); diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts index 0a5e5443e7..2ea39eaf6f 100644 --- a/src/sql/workbench/api/node/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -716,7 +716,7 @@ export interface ExtHostModelViewDialogShape { export interface MainThreadModelViewDialogShape extends IDisposable { $openEditor(handle: number, modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable; - $openDialog(handle: number): Thenable; + $openDialog(handle: number, dialogName?: string): Thenable; $closeDialog(handle: number): Thenable; $setDialogDetails(handle: number, details: IModelViewDialogDetails): Thenable; $setTabDetails(handle: number, details: IModelViewTabDetails): Thenable; diff --git a/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts b/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts index 1f13a972ac..cd13a915f9 100644 --- a/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts +++ b/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts @@ -66,7 +66,7 @@ suite('MainThreadModelViewDialog Tests', () => { let extHostContext = { getProxy: proxyType => mockExtHostModelViewDialog.object }; - mainThreadModelViewDialog = new MainThreadModelViewDialog(extHostContext, undefined, undefined); + mainThreadModelViewDialog = new MainThreadModelViewDialog(extHostContext, undefined, undefined, undefined); // Set up the mock dialog service mockDialogService = Mock.ofType(CustomDialogService, undefined, undefined);