Agent feature usage metrics (#3346)

* agent feature usage metrics

* generalized feature telemetry via dialogs

* renamed eventName property to dialogName

* made dialogName an optional field
This commit is contained in:
Aditya Bist
2018-12-05 11:01:46 -08:00
committed by GitHub
parent 8f817ce689
commit c21611661b
20 changed files with 133 additions and 52 deletions

View File

@@ -20,6 +20,9 @@ export abstract class AgentDialog<T extends IAgentDialogData> {
public readonly onSuccess: vscode.Event<T> = this._onSuccess.event; public readonly onSuccess: vscode.Event<T> = this._onSuccess.event;
public dialog: sqlops.window.modelviewdialog.Dialog; public dialog: sqlops.window.modelviewdialog.Dialog;
// Dialog Name for Telemetry
public dialogName: string;
constructor(public ownerUri: string, public model: T, public title: string) { constructor(public ownerUri: string, public model: T, public title: string) {
} }
@@ -31,8 +34,9 @@ export abstract class AgentDialog<T extends IAgentDialogData> {
protected abstract async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog); protected abstract async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog);
public async openDialog() { public async openDialog(dialogName?: string) {
this.dialog = sqlops.window.modelviewdialog.createDialog(this.title); let event = dialogName ? dialogName : null;
this.dialog = sqlops.window.modelviewdialog.createDialog(this.title, event);
await this.model.initialize(); await this.model.initialize();

View File

@@ -116,6 +116,10 @@ export class AlertDialog extends AgentDialog<AlertData> {
private static readonly DelayMinutesTextBoxLabel: string = localize('alertDialog.DelayMinutes', 'Delay Minutes'); private static readonly DelayMinutesTextBoxLabel: string = localize('alertDialog.DelayMinutes', 'Delay Minutes');
private static readonly DelaySecondsTextBoxLabel: string = localize('alertDialog.DelaySeconds', 'Delay Seconds'); private static readonly DelaySecondsTextBoxLabel: string = localize('alertDialog.DelaySeconds', 'Delay Seconds');
// Event Name strings
private readonly NewAlertDialog = 'NewAlertDialogOpen';
private readonly EditAlertDialog = 'EditAlertDialogOpened';
// UI Components // UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab; private generalTab: sqlops.window.modelviewdialog.DialogTab;
private responseTab: sqlops.window.modelviewdialog.DialogTab; private responseTab: sqlops.window.modelviewdialog.DialogTab;
@@ -149,6 +153,7 @@ export class AlertDialog extends AgentDialog<AlertData> {
private delayMinutesTextBox: sqlops.InputBoxComponent; private delayMinutesTextBox: sqlops.InputBoxComponent;
private delaySecondsTextBox: sqlops.InputBoxComponent; private delaySecondsTextBox: sqlops.InputBoxComponent;
private isEdit: boolean = false;
private databases: string[]; private databases: string[];
private jobModel: JobData; private jobModel: JobData;
public jobId: string; public jobId: string;
@@ -166,6 +171,8 @@ export class AlertDialog extends AgentDialog<AlertData> {
this.jobModel = jobModel; this.jobModel = jobModel;
this.jobId = this.jobId ? this.jobId : this.jobModel.jobId; this.jobId = this.jobId ? this.jobId : this.jobModel.jobId;
this.jobName = this.jobName ? this.jobName : this.jobModel.name; 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) { protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {

View File

@@ -67,6 +67,10 @@ export class JobDialog extends AgentDialog<JobData> {
private readonly AlertEnabledLabelString: string = localize('jobDialog.alertEnabledLabel', 'Enabled'); private readonly AlertEnabledLabelString: string = localize('jobDialog.alertEnabledLabel', 'Enabled');
private readonly AlertTypeLabelString: string = localize('jobDialog.alertTypeLabel', 'Type'); private readonly AlertTypeLabelString: string = localize('jobDialog.alertTypeLabel', 'Type');
// Event Name strings
private readonly NewJobDialogEvent: string = 'NewJobDialogOpened';
private readonly EditJobDialogEvent: string = 'EditJobDialogOpened';
// UI Components // UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab; private generalTab: sqlops.window.modelviewdialog.DialogTab;
private stepsTab: sqlops.window.modelviewdialog.DialogTab; private stepsTab: sqlops.window.modelviewdialog.DialogTab;
@@ -125,6 +129,7 @@ export class JobDialog extends AgentDialog<JobData> {
this.schedules = this.model.jobSchedules ? this.model.jobSchedules : []; this.schedules = this.model.jobSchedules ? this.model.jobSchedules : [];
this.alerts = this.model.alerts ? this.model.alerts : []; this.alerts = this.model.alerts ? this.model.alerts : [];
this.isEdit = jobInfo ? true : false; this.isEdit = jobInfo ? true : false;
this.dialogName = this.isEdit ? this.EditJobDialogEvent : this.NewJobDialogEvent;
} }
protected async initializeDialog() { protected async initializeDialog() {

View File

@@ -67,6 +67,9 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
private readonly QuitJobReportingSuccess: string = localize('jobStepDialog.quitJobSuccess', 'Quit the job reporting success'); private readonly QuitJobReportingSuccess: string = localize('jobStepDialog.quitJobSuccess', 'Quit the job reporting success');
private readonly QuitJobReportingFailure: string = localize('jobStepDialog.quitJobFailure', 'Quit the job reporting failure'); 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 // UI Components
// Dialogs // Dialogs
@@ -131,6 +134,7 @@ export class JobStepDialog extends AgentDialog<JobStepData> {
this.jobModel = jobModel; this.jobModel = jobModel;
this.jobName = this.jobName ? this.jobName : this.jobModel.name; this.jobName = this.jobName ? this.jobName : this.jobModel.name;
this.server = server; this.server = server;
this.dialogName = this.isEdit ? this.EditStepDialog : this.NewStepDialog;
} }
private initializeUIComponents() { private initializeUIComponents() {

View File

@@ -43,6 +43,10 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
private static readonly AlertEmailColumnLabel: string = localize('createOperator.AlertEmailColumnLabel', 'E-mail'); private static readonly AlertEmailColumnLabel: string = localize('createOperator.AlertEmailColumnLabel', 'E-mail');
private static readonly AlertPagerColumnLabel: string = localize('createOperator.AlertPagerColumnLabel', 'Pager'); private static readonly AlertPagerColumnLabel: string = localize('createOperator.AlertPagerColumnLabel', 'Pager');
// Event strings
private readonly NewOperatorDialog = 'NewOperatorDialogOpened';
private readonly EditOperatorDialog = 'EditOperatorDialogOpened';
// UI Components // UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab; private generalTab: sqlops.window.modelviewdialog.DialogTab;
private notificationsTab: sqlops.window.modelviewdialog.DialogTab; private notificationsTab: sqlops.window.modelviewdialog.DialogTab;
@@ -68,12 +72,15 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
// Notification tab controls // Notification tab controls
private alertsTable: sqlops.TableComponent; private alertsTable: sqlops.TableComponent;
private isEdit: boolean = false;
constructor(ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo = undefined) { constructor(ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo = undefined) {
super( super(
ownerUri, ownerUri,
new OperatorData(ownerUri, operatorInfo), new OperatorData(ownerUri, operatorInfo),
operatorInfo ? OperatorDialog.EditDialogTitle : OperatorDialog.CreateDialogTitle); 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) { protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {

View File

@@ -36,6 +36,9 @@ export class ProxyDialog extends AgentDialog<ProxyData> {
private static readonly PowerShellLabel: string = localize('createProxy.PowerShell', 'PowerShell'); private static readonly PowerShellLabel: string = localize('createProxy.PowerShell', 'PowerShell');
private static readonly SubSystemHeadingLabel: string = localize('createProxy.subSystemHeading', 'Active to the following subsytems'); private static readonly SubSystemHeadingLabel: string = localize('createProxy.subSystemHeading', 'Active to the following subsytems');
private readonly NewProxyDialog = 'NewProxyDialogOpened';
private readonly EditProxyDialog = 'EditProxyDialogOpened';
// UI Components // UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab; private generalTab: sqlops.window.modelviewdialog.DialogTab;
@@ -56,6 +59,7 @@ export class ProxyDialog extends AgentDialog<ProxyData> {
private powershellCheckBox: sqlops.CheckBoxComponent; private powershellCheckBox: sqlops.CheckBoxComponent;
private credentials: sqlops.CredentialInfo[]; private credentials: sqlops.CredentialInfo[];
private isEdit: boolean = false;
constructor(ownerUri: string, proxyInfo: sqlops.AgentProxyInfo = undefined, credentials: sqlops.CredentialInfo[]) { constructor(ownerUri: string, proxyInfo: sqlops.AgentProxyInfo = undefined, credentials: sqlops.CredentialInfo[]) {
super( super(
@@ -63,6 +67,8 @@ export class ProxyDialog extends AgentDialog<ProxyData> {
new ProxyData(ownerUri, proxyInfo), new ProxyData(ownerUri, proxyInfo),
proxyInfo ? ProxyDialog.EditDialogTitle : ProxyDialog.CreateDialogTitle); proxyInfo ? ProxyDialog.EditDialogTitle : ProxyDialog.CreateDialogTitle);
this.credentials = credentials; this.credentials = credentials;
this.isEdit = proxyInfo ? true : false;
this.dialogName = this.isEdit ? this.EditProxyDialog : this.NewProxyDialog;
} }
protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) { protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {

View File

@@ -40,13 +40,13 @@ export class MainController {
public activate(): void { public activate(): void {
vscode.commands.registerCommand('agent.openJobDialog', (ownerUri: string, jobInfo: sqlops.AgentJobInfo) => { vscode.commands.registerCommand('agent.openJobDialog', (ownerUri: string, jobInfo: sqlops.AgentJobInfo) => {
let dialog = new JobDialog(ownerUri, jobInfo); 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) => { vscode.commands.registerCommand('agent.openNewStepDialog', (ownerUri: string, server: string, jobInfo: sqlops.AgentJobInfo, jobStepInfo: sqlops.AgentJobStepInfo) => {
AgentUtils.getAgentService().then((agentService) => { AgentUtils.getAgentService().then((agentService) => {
let jobData: JobData = new JobData(ownerUri, jobInfo, agentService); let jobData: JobData = new JobData(ownerUri, jobInfo, agentService);
let dialog = new JobStepDialog(ownerUri, server, jobData, jobStepInfo, false); 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) => { vscode.commands.registerCommand('agent.openPickScheduleDialog', (ownerUri: string, jobName: string) => {
@@ -57,17 +57,16 @@ export class MainController {
AgentUtils.getAgentService().then((agentService) => { AgentUtils.getAgentService().then((agentService) => {
let jobData: JobData = new JobData(ownerUri, jobInfo, agentService); let jobData: JobData = new JobData(ownerUri, jobInfo, agentService);
let dialog = new AlertDialog(ownerUri, jobData, alertInfo, false); 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) => { vscode.commands.registerCommand('agent.openOperatorDialog', (ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo) => {
let dialog = new OperatorDialog(ownerUri, operatorInfo); 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[]) => { vscode.commands.registerCommand('agent.openProxyDialog', (ownerUri: string, proxyInfo: sqlops.AgentProxyInfo, credentials: sqlops.CredentialInfo[]) => {
let dialog = new ProxyDialog(ownerUri, proxyInfo, credentials); let dialog = new ProxyDialog(ownerUri, proxyInfo, credentials);
dialog.openDialog(); dialog.dialogName ? dialog.openDialog(dialog.dialogName) : dialog.openDialog();
MainController.showNotYetImplemented();
}); });
} }

View File

@@ -25,7 +25,6 @@ export const NewQuery = 'NewQuery';
export const FirewallRuleRequested = 'FirewallRuleCreated'; export const FirewallRuleRequested = 'FirewallRuleCreated';
export const DashboardNavigated = 'DashboardNavigated'; export const DashboardNavigated = 'DashboardNavigated';
// Telemetry Properties // Telemetry Properties
// Modal Dialogs: // Modal Dialogs:
@@ -42,3 +41,21 @@ export const Accounts = 'Accounts';
export const FireWallRule = 'FirewallRule'; export const FireWallRule = 'FirewallRule';
export const AutoOAuth = 'AutoOAuth'; export const AutoOAuth = 'AutoOAuth';
export const AddNewDashboardTab = 'AddNewDashboardTab'; 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';

View File

@@ -4,31 +4,10 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
'use strict'; 'use strict';
import * as crypto from 'crypto';
import * as os from 'os';
import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { warn } from 'sql/base/common/log'; 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<string> {
return new Promise<string>(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 { export interface IConnectionTelemetryData extends ITelemetryData {
provider?: string; provider?: string;
} }

View File

@@ -17,6 +17,9 @@ import { AlertsViewComponent } from 'sql/parts/jobManagement/views/alertsView.co
import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsView.component'; import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsView.component';
import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component'; import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; 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 { export enum JobActions {
Run = 'run', Run = 'run',
@@ -80,7 +83,8 @@ export class RunJobAction extends Action {
constructor( constructor(
@INotificationService private notificationService: INotificationService, @INotificationService private notificationService: INotificationService,
@IJobManagementService private jobManagementService: IJobManagementService, @IJobManagementService private jobManagementService: IJobManagementService,
@IInstantiationService private instantationService: IInstantiationService @IInstantiationService private instantationService: IInstantiationService,
@ITelemetryService private telemetryService: ITelemetryService
) { ) {
super(RunJobAction.ID, RunJobAction.LABEL, 'runJobIcon'); super(RunJobAction.ID, RunJobAction.LABEL, 'runJobIcon');
} }
@@ -89,6 +93,7 @@ export class RunJobAction extends Action {
let jobName = context.agentJobInfo.name; let jobName = context.agentJobInfo.name;
let ownerUri = context.ownerUri; let ownerUri = context.ownerUri;
let refreshAction = this.instantationService.createInstance(JobsRefreshAction); let refreshAction = this.instantationService.createInstance(JobsRefreshAction);
this.telemetryService.publicLog(TelemetryKeys.RunAgentJob);
return new TPromise<boolean>((resolve, reject) => { return new TPromise<boolean>((resolve, reject) => {
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(result => { this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(result => {
if (result.success) { if (result.success) {
@@ -118,7 +123,8 @@ export class StopJobAction extends Action {
constructor( constructor(
@INotificationService private notificationService: INotificationService, @INotificationService private notificationService: INotificationService,
@IJobManagementService private jobManagementService: IJobManagementService, @IJobManagementService private jobManagementService: IJobManagementService,
@IInstantiationService private instantationService: IInstantiationService @IInstantiationService private instantationService: IInstantiationService,
@ITelemetryService private telemetryService: ITelemetryService
) { ) {
super(StopJobAction.ID, StopJobAction.LABEL, 'stopJobIcon'); super(StopJobAction.ID, StopJobAction.LABEL, 'stopJobIcon');
} }
@@ -127,6 +133,7 @@ export class StopJobAction extends Action {
let jobName = context.agentJobInfo.name; let jobName = context.agentJobInfo.name;
let ownerUri = context.ownerUri; let ownerUri = context.ownerUri;
let refreshAction = this.instantationService.createInstance(JobsRefreshAction); let refreshAction = this.instantationService.createInstance(JobsRefreshAction);
this.telemetryService.publicLog(TelemetryKeys.StopAgentJob);
return new TPromise<boolean>((resolve, reject) => { return new TPromise<boolean>((resolve, reject) => {
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(result => { this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(result => {
if (result.success) { if (result.success) {
@@ -174,7 +181,8 @@ export class DeleteJobAction extends Action {
constructor( constructor(
@INotificationService private _notificationService: INotificationService, @INotificationService private _notificationService: INotificationService,
@IJobManagementService private _jobService: IJobManagementService @IJobManagementService private _jobService: IJobManagementService,
@ITelemetryService private _telemetryService: ITelemetryService
) { ) {
super(DeleteJobAction.ID, DeleteJobAction.LABEL); super(DeleteJobAction.ID, DeleteJobAction.LABEL);
} }
@@ -188,6 +196,7 @@ export class DeleteJobAction extends Action {
[{ [{
label: DeleteJobAction.LABEL, label: DeleteJobAction.LABEL,
run: () => { run: () => {
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentJob);
self._jobService.deleteJob(actionInfo.ownerUri, actionInfo.targetObject).then(result => { self._jobService.deleteJob(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
if (!result || !result.success) { if (!result || !result.success) {
let errorMessage = nls.localize("jobaction.failedToDeleteJob", "Could not delete job '{0}'.\nError: {1}", let errorMessage = nls.localize("jobaction.failedToDeleteJob", "Could not delete job '{0}'.\nError: {1}",
@@ -234,7 +243,8 @@ export class DeleteStepAction extends Action {
constructor( constructor(
@INotificationService private _notificationService: INotificationService, @INotificationService private _notificationService: INotificationService,
@IJobManagementService private _jobService: IJobManagementService, @IJobManagementService private _jobService: IJobManagementService,
@IInstantiationService private instantationService: IInstantiationService @IInstantiationService private instantationService: IInstantiationService,
@ITelemetryService private _telemetryService: ITelemetryService
) { ) {
super(DeleteStepAction.ID, DeleteStepAction.LABEL); super(DeleteStepAction.ID, DeleteStepAction.LABEL);
} }
@@ -249,6 +259,7 @@ export class DeleteStepAction extends Action {
[{ [{
label: DeleteStepAction.LABEL, label: DeleteStepAction.LABEL,
run: () => { run: () => {
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentJobStep);
self._jobService.deleteJobStep(actionInfo.ownerUri, actionInfo.targetObject).then(result => { self._jobService.deleteJobStep(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
if (!result || !result.success) { if (!result || !result.success) {
let errorMessage = nls.localize("jobaction.failedToDeleteStep", "Could not delete step '{0}'.\nError: {1}", let errorMessage = nls.localize("jobaction.failedToDeleteStep", "Could not delete step '{0}'.\nError: {1}",
@@ -318,7 +329,8 @@ export class DeleteAlertAction extends Action {
constructor( constructor(
@INotificationService private _notificationService: INotificationService, @INotificationService private _notificationService: INotificationService,
@IJobManagementService private _jobService: IJobManagementService @IJobManagementService private _jobService: IJobManagementService,
@ITelemetryService private _telemetryService: ITelemetryService
) { ) {
super(DeleteAlertAction.ID, DeleteAlertAction.LABEL); super(DeleteAlertAction.ID, DeleteAlertAction.LABEL);
} }
@@ -332,6 +344,7 @@ export class DeleteAlertAction extends Action {
[{ [{
label: DeleteAlertAction.LABEL, label: DeleteAlertAction.LABEL,
run: () => { run: () => {
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentAlert);
self._jobService.deleteAlert(actionInfo.ownerUri, actionInfo.targetObject).then(result => { self._jobService.deleteAlert(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
if (!result || !result.success) { if (!result || !result.success) {
let errorMessage = nls.localize("jobaction.failedToDeleteAlert", "Could not delete alert '{0}'.\nError: {1}", let errorMessage = nls.localize("jobaction.failedToDeleteAlert", "Could not delete alert '{0}'.\nError: {1}",
@@ -397,7 +410,8 @@ export class DeleteOperatorAction extends Action {
constructor( constructor(
@INotificationService private _notificationService: INotificationService, @INotificationService private _notificationService: INotificationService,
@IJobManagementService private _jobService: IJobManagementService @IJobManagementService private _jobService: IJobManagementService,
@ITelemetryService private _telemetryService: ITelemetryService
) { ) {
super(DeleteOperatorAction.ID, DeleteOperatorAction.LABEL); super(DeleteOperatorAction.ID, DeleteOperatorAction.LABEL);
} }
@@ -411,6 +425,7 @@ export class DeleteOperatorAction extends Action {
[{ [{
label: DeleteOperatorAction.LABEL, label: DeleteOperatorAction.LABEL,
run: () => { run: () => {
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentOperator);
self._jobService.deleteOperator(actionInfo.ownerUri, actionInfo.targetObject).then(result => { self._jobService.deleteOperator(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
if (!result || !result.success) { if (!result || !result.success) {
let errorMessage = nls.localize("jobaction.failedToDeleteOperator", "Could not delete operator '{0}'.\nError: {1}", let errorMessage = nls.localize("jobaction.failedToDeleteOperator", "Could not delete operator '{0}'.\nError: {1}",
@@ -477,7 +492,8 @@ export class DeleteProxyAction extends Action {
constructor( constructor(
@INotificationService private _notificationService: INotificationService, @INotificationService private _notificationService: INotificationService,
@IJobManagementService private _jobService: IJobManagementService @IJobManagementService private _jobService: IJobManagementService,
@ITelemetryService private _telemetryService: ITelemetryService
) { ) {
super(DeleteProxyAction.ID, DeleteProxyAction.LABEL); super(DeleteProxyAction.ID, DeleteProxyAction.LABEL);
} }
@@ -491,6 +507,7 @@ export class DeleteProxyAction extends Action {
[{ [{
label: DeleteProxyAction.LABEL, label: DeleteProxyAction.LABEL,
run: () => { run: () => {
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentProxy);
self._jobService.deleteProxy(actionInfo.ownerUri, actionInfo.targetObject).then(result => { self._jobService.deleteProxy(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
if (!result || !result.success) { if (!result || !result.success) {
let errorMessage = nls.localize("jobaction.failedToDeleteProxy", "Could not delete proxy '{0}'.\nError: {1}", let errorMessage = nls.localize("jobaction.failedToDeleteProxy", "Could not delete proxy '{0}'.\nError: {1}",

View File

@@ -29,6 +29,8 @@ import { JobManagementView } from 'sql/parts/jobManagement/views/jobManagementVi
import { TabChild } from 'sql/base/browser/ui/panel/tab.component'; import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService'; import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; 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'; export const DASHBOARD_SELECTOR: string = 'jobhistory-component';
@@ -76,7 +78,8 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
@Inject(IContextMenuService) private contextMenuService: IContextMenuService, @Inject(IContextMenuService) private contextMenuService: IContextMenuService,
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService, @Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
@Inject(IKeybindingService) keybindingService: IKeybindingService, @Inject(IKeybindingService) keybindingService: IKeybindingService,
@Inject(IDashboardService) dashboardService: IDashboardService @Inject(IDashboardService) dashboardService: IDashboardService,
@Inject(ITelemetryService) private _telemetryService: ITelemetryService
) { ) {
super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService); super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService);
this._treeController = new JobHistoryController(); this._treeController = new JobHistoryController();
@@ -142,6 +145,7 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
this._register(attachListStyler(this._tree, this.themeService)); this._register(attachListStyler(this._tree, this.themeService));
this._tree.layout(dom.getContentHeight(this._tableContainer.nativeElement)); this._tree.layout(dom.getContentHeight(this._tableContainer.nativeElement));
this.initActionBar(); this.initActionBar();
this._telemetryService.publicLog(TelemetryKeys.JobHistoryView);
} }
private loadHistory() { private loadHistory() {

View File

@@ -21,6 +21,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component'; 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'; export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component';
@@ -48,7 +50,8 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit,
@Inject(IInstantiationService) instantiationService: IInstantiationService, @Inject(IInstantiationService) instantiationService: IInstantiationService,
@Inject(IContextMenuService) contextMenuService: IContextMenuService, @Inject(IContextMenuService) contextMenuService: IContextMenuService,
@Inject(IKeybindingService) keybindingService: IKeybindingService, @Inject(IKeybindingService) keybindingService: IKeybindingService,
@Inject(IDashboardService) dashboardService: IDashboardService @Inject(IDashboardService) dashboardService: IDashboardService,
@Inject(ITelemetryService) private _telemetryService: ITelemetryService
) { ) {
super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService); super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService);
} }
@@ -72,6 +75,7 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit,
}, {verticalScrollMode: ScrollbarVisibility.Visible, horizontalScrollMode: ScrollbarVisibility.Visible }); }, {verticalScrollMode: ScrollbarVisibility.Visible, horizontalScrollMode: ScrollbarVisibility.Visible });
this.layout(); this.layout();
this._register(attachListStyler(this._tree, this.themeService)); this._register(attachListStyler(this._tree, this.themeService));
this._telemetryService.publicLog(TelemetryKeys.JobStepsView);
} }
public onFirstVisible() { public onFirstVisible() {

View File

@@ -37,6 +37,8 @@ import { IDashboardService } from 'sql/services/dashboard/common/dashboardServic
import { escape } from 'sql/base/common/strings'; import { escape } from 'sql/base/common/strings';
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { tableBackground, cellBackground, cellBorderColor } from 'sql/common/theme/colors'; 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 JOBSVIEW_SELECTOR: string = 'jobsview-component';
export const ROW_HEIGHT: number = 45; export const ROW_HEIGHT: number = 45;
@@ -106,7 +108,8 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe
@Inject(IInstantiationService) instantiationService: IInstantiationService, @Inject(IInstantiationService) instantiationService: IInstantiationService,
@Inject(IContextMenuService) contextMenuService: IContextMenuService, @Inject(IContextMenuService) contextMenuService: IContextMenuService,
@Inject(IKeybindingService) keybindingService: IKeybindingService, @Inject(IKeybindingService) keybindingService: IKeybindingService,
@Inject(IDashboardService) _dashboardService: IDashboardService @Inject(IDashboardService) _dashboardService: IDashboardService,
@Inject(ITelemetryService) private _telemetryService: ITelemetryService
) { ) {
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService); super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService);
this._didTabChange = false; this._didTabChange = false;
@@ -127,6 +130,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe
this._visibilityElement = this._gridEl; this._visibilityElement = this._gridEl;
this._parentComponent = this._agentViewComponent; this._parentComponent = this._agentViewComponent;
this._register(this._themeService.onDidColorThemeChange(e => this.updateTheme(e))); this._register(this._themeService.onDidColorThemeChange(e => this.updateTheme(e)));
this._telemetryService.publicLog(TelemetryKeys.JobsView);
} }
ngOnDestroy() { ngOnDestroy() {

View File

@@ -22,8 +22,9 @@ export class CustomDialogService {
constructor( @IInstantiationService private _instantiationService: IInstantiationService) { } constructor( @IInstantiationService private _instantiationService: IInstantiationService) { }
public showDialog(dialog: Dialog, options?: IModalOptions): void { public showDialog(dialog: Dialog, dialogName?: string, options?: IModalOptions): void {
let dialogModal = this._instantiationService.createInstance(DialogModal, dialog, 'CustomDialog', options || defaultOptions); let name = dialogName ? dialogName : 'CustomDialog';
let dialogModal = this._instantiationService.createInstance(DialogModal, dialog, name, options || defaultOptions);
this._dialogModals.set(dialog, dialogModal); this._dialogModals.set(dialog, dialogModal);
dialogModal.render(); dialogModal.render();
dialogModal.open(); dialogModal.open();

View File

@@ -839,7 +839,7 @@ declare module 'sqlops' {
* Create a dialog with the given title * Create a dialog with the given title
* @param title The title of the dialog, displayed at the top * @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 * 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; 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 * 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 * one callback can be registered at once, so each registration call will clear

View File

@@ -16,6 +16,8 @@ import * as sqlops from 'sqlops';
import { SqlMainContext, ExtHostModelViewDialogShape, MainThreadModelViewDialogShape, ExtHostModelViewShape, ExtHostBackgroundTaskManagementShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { SqlMainContext, ExtHostModelViewDialogShape, MainThreadModelViewDialogShape, ExtHostModelViewShape, ExtHostBackgroundTaskManagementShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes'; 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 DONE_LABEL = nls.localize('dialogDoneLabel', 'Done');
const CANCEL_LABEL = nls.localize('dialogCancelLabel', 'Cancel'); 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 _message: sqlops.window.modelviewdialog.DialogMessage;
private _closeValidator: () => boolean | Thenable<boolean>; private _closeValidator: () => boolean | Thenable<boolean>;
private _operationHandler: BackgroundOperationHandler; private _operationHandler: BackgroundOperationHandler;
private _dialogName: string;
constructor(extHostModelViewDialog: ExtHostModelViewDialog, constructor(extHostModelViewDialog: ExtHostModelViewDialog,
extHostModelView: ExtHostModelViewShape, extHostModelView: ExtHostModelViewShape,
@@ -157,6 +160,14 @@ class DialogImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdi
this._extHostModelViewDialog.updateDialogContent(this); this._extHostModelViewDialog.updateDialogContent(this);
} }
public get dialogName(): string {
return this._dialogName;
}
public set dialogName(value: string) {
this._dialogName = value;
}
public registerCloseValidator(validator: () => boolean | Thenable<boolean>): void { public registerCloseValidator(validator: () => boolean | Thenable<boolean>): void {
this._closeValidator = validator; this._closeValidator = validator;
} }
@@ -503,7 +514,8 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
public openDialog(dialog: sqlops.window.modelviewdialog.Dialog): void { public openDialog(dialog: sqlops.window.modelviewdialog.Dialog): void {
let handle = this.getHandle(dialog); let handle = this.getHandle(dialog);
this.updateDialogContent(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 { public closeDialog(dialog: sqlops.window.modelviewdialog.Dialog): void {
@@ -560,8 +572,11 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
this._onClickCallbacks.set(handle, callback); 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); let dialog = new DialogImpl(this, this._extHostModelView, this._extHostTaskManagement, extensionLocation);
if (dialogName) {
dialog.dialogName = dialogName;
}
dialog.title = title; dialog.title = title;
dialog.handle = this.getHandle(dialog); dialog.handle = this.getHandle(dialog);
return dialog; return dialog;

View File

@@ -19,6 +19,7 @@ import { ModelViewInput, ModelViewInputModel, ModeViewSaveHandler } from 'sql/pa
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@extHostNamedCustomer(SqlMainContext.MainThreadModelViewDialog) @extHostNamedCustomer(SqlMainContext.MainThreadModelViewDialog)
export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape { export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape {
@@ -35,7 +36,8 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
constructor( constructor(
context: IExtHostContext, context: IExtHostContext,
@IInstantiationService private _instatiationService: IInstantiationService, @IInstantiationService private _instatiationService: IInstantiationService,
@IEditorService private _editorService: IEditorService @IEditorService private _editorService: IEditorService,
@ITelemetryService private _telemetryService: ITelemetryService
) { ) {
this._proxy = context.getProxy(SqlExtHostContext.ExtHostModelViewDialog); this._proxy = context.getProxy(SqlExtHostContext.ExtHostModelViewDialog);
this._dialogService = new CustomDialogService(_instatiationService); this._dialogService = new CustomDialogService(_instatiationService);
@@ -68,9 +70,9 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
return this._proxy.$handleSave(handle); return this._proxy.$handleSave(handle);
} }
public $openDialog(handle: number): Thenable<void> { public $openDialog(handle: number, dialogName?: string): Thenable<void> {
let dialog = this.getDialog(handle); let dialog = this.getDialog(handle);
this._dialogService.showDialog(dialog); dialogName ? this._dialogService.showDialog(dialog, dialogName) : this._dialogService.showDialog(dialog);
return Promise.resolve(); return Promise.resolve();
} }

View File

@@ -348,8 +348,8 @@ export function createApiFactory(
}; };
const modelViewDialog: typeof sqlops.window.modelviewdialog = { const modelViewDialog: typeof sqlops.window.modelviewdialog = {
createDialog(title: string): sqlops.window.modelviewdialog.Dialog { createDialog(title: string, dialogName?: string): sqlops.window.modelviewdialog.Dialog {
return extHostModelViewDialog.createDialog(title, extension.extensionLocation); return extHostModelViewDialog.createDialog(title, dialogName, extension.extensionLocation);
}, },
createTab(title: string): sqlops.window.modelviewdialog.DialogTab { createTab(title: string): sqlops.window.modelviewdialog.DialogTab {
return extHostModelViewDialog.createTab(title, extension.extensionLocation); return extHostModelViewDialog.createTab(title, extension.extensionLocation);

View File

@@ -716,7 +716,7 @@ export interface ExtHostModelViewDialogShape {
export interface MainThreadModelViewDialogShape extends IDisposable { export interface MainThreadModelViewDialogShape extends IDisposable {
$openEditor(handle: number, modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void>; $openEditor(handle: number, modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void>;
$openDialog(handle: number): Thenable<void>; $openDialog(handle: number, dialogName?: string): Thenable<void>;
$closeDialog(handle: number): Thenable<void>; $closeDialog(handle: number): Thenable<void>;
$setDialogDetails(handle: number, details: IModelViewDialogDetails): Thenable<void>; $setDialogDetails(handle: number, details: IModelViewDialogDetails): Thenable<void>;
$setTabDetails(handle: number, details: IModelViewTabDetails): Thenable<void>; $setTabDetails(handle: number, details: IModelViewTabDetails): Thenable<void>;

View File

@@ -66,7 +66,7 @@ suite('MainThreadModelViewDialog Tests', () => {
let extHostContext = <IExtHostContext>{ let extHostContext = <IExtHostContext>{
getProxy: proxyType => mockExtHostModelViewDialog.object getProxy: proxyType => mockExtHostModelViewDialog.object
}; };
mainThreadModelViewDialog = new MainThreadModelViewDialog(extHostContext, undefined, undefined); mainThreadModelViewDialog = new MainThreadModelViewDialog(extHostContext, undefined, undefined, undefined);
// Set up the mock dialog service // Set up the mock dialog service
mockDialogService = Mock.ofType(CustomDialogService, undefined, undefined); mockDialogService = Mock.ofType(CustomDialogService, undefined, undefined);