Compare commits

..

30 Commits

Author SHA1 Message Date
Karl Burtram
4d67eca8bb Dashboard agent tab style updates (#1934) 2018-07-15 10:51:20 -07:00
Aditya Bist
74c4b7311e Agent/proxy ui (#1880)
* finished basic proxy ui

* finished proxy UI and logic

* made changes to accomodate toolsservice side changes
2018-07-14 10:43:31 -07:00
Karl Burtram
713c74adfd Update version to 0.31.3 2018-07-14 10:41:07 -07:00
Karl Burtram
408a8a6f19 Edit Agent Job dialog updates (#1925)
* Fill in job name and description on edit

* Hook up update job request call

* Clean up table styles
2018-07-13 22:57:09 -07:00
Karl Burtram
e1485e49d3 Pick up 1.5.0-alpha.9 2018-07-13 22:56:13 -07:00
Matt Irvine
30b66934cd Enable custom delimiters when saving as CSV (#1928)
* Support custom delimiters for csv

* Run tsfmt
2018-07-13 18:12:57 -07:00
Matt Irvine
fd49c081c2 Fix uses of innerHtml when we could just set element text (#1919) 2018-07-13 15:24:24 -07:00
Madeline MacDonald
1327120024 Profiler view templates (#1915)
* Initial view template framework

* Removing some templates, reordering drop down

* Fixing comments and formatting

* Adding issue reference for commented code
2018-07-13 14:24:49 -07:00
Matt Irvine
d2b5043972 Render column titles as text not html in query results (#1923) 2018-07-13 14:23:50 -07:00
Karl Burtram
a0e55ea3fd Update config.json 2018-07-13 12:12:08 -07:00
Matt Irvine
1f32de29c1 Style SQL input box correctly when enabled/disabled (#1920) 2018-07-13 09:03:22 -07:00
Aditya Bist
12be06d682 Agent: dialog finishes (#1913)
* fixed crashes from job dialog and new step dialog group options UI

* added placeholder for retry counters

* fixed alert general UI

* fixed misc dialog errors

* localized all strings

* fixed create operator UI
2018-07-12 19:43:59 -07:00
Kevin Cunnane
27ca9b13f8 Fix #1916 Object explorer context object doesn't include database name (#1917) 2018-07-12 17:21:57 -07:00
Matt Irvine
be45905830 Add wizard sidebar navigation (#1911) 2018-07-12 11:15:56 -07:00
Karl Burtram
05d0a89655 Fix Job History scroll and resize issues (#1912) 2018-07-12 09:32:43 -07:00
Aditya Bist
3ba575dcd0 Agent - dialog finishes (#1910)
* fixed crashes from job dialog and new step dialog group options UI

* added placeholder for retry counters

* fixed alert general UI

* fixed misc dialog errors

* localized all strings
2018-07-11 23:08:23 -07:00
Karl Burtram
3e200b7f0f Handle resize message in Agent dashboard tab (#1908)
* Resize related changes WIP

* Resize table control better on resize

* Update DashboardTab to use inherited Disposable

* Set forceFitColumns to false
2018-07-11 13:50:58 -07:00
Aditya Bist
cbce1f7008 added parse syntax params to sqlops (#1906) 2018-07-11 13:26:51 -07:00
Kevin Cunnane
e99101447e Fixes #1856 Object Explorer needs Icons field for nodes separate from… (#1901)
* Fixes #1856 Object Explorer needs Icons field for nodes separate from type/subtype
- Adds in the concept of a themeable icon path which matches VSCode's implementation. This should help support theme-based overrides in the future
2018-07-11 11:24:35 -07:00
Karl Burtram
0ddb326e44 Update SQL Tools Service to 1.5.0-alpha.6 (#1897) 2018-07-11 10:25:18 -07:00
Leila Lali
460446a15c updated the icon for form container help (#1892) 2018-07-10 15:53:01 -07:00
Aditya Bist
4eea24997f Agent: Updated Alerts dialog UI (#1874)
* finished alert dialog UI

* removed unused import
2018-07-10 14:21:58 -07:00
Matt Irvine
0b1e9c7c66 Update form layout defaults to match design (#1878) 2018-07-10 13:45:36 -07:00
Kevin Cunnane
d51a7a9eb7 Extensibility: Context menu support in Object Explorer (#1883)
- Fixes #1867 context menu should be extensible
- Added context keys to support "when" conditions on the new extensions
- Fixes issue where actions like New Query, scripting show up even if these are not valid for the provider type or object type
- Fixed node expansion bug where rapid connect / expand / disconnect could break the app (fix in ObjectExplorerService.onNodeExpanded)
- Major change to how internal actions work. These cannot assume the context has non-serializable objects. Opened up some APIs to make this easier to handle.
- Fixed a number of existing bugs in internal actions.
  - Notably, DisconnectAction was adding a listener on each right-click on an active connection and never getting it disposed. This wasn't needed at all due to design changes.
  - Another bug fix is that the Manage action now correctly navigates to the DB dashboard for database-level connections. Before this it went to the server-level dashboard.

* Define API for context info
2018-07-10 12:23:47 -07:00
Karl Burtram
0f0b959e14 Update config.json 2018-07-10 09:53:00 -07:00
Leila Lali
b2ceb09e4d fixed some issues in table component and added tests (#1873)
* fixed some issues in table component  and added tests

* fixed the enter key handling in text area component
2018-07-09 14:40:21 -07:00
Anthony Dresser
53953f5cda bump slickgrid to fix focus issue (#1875) 2018-07-09 13:30:16 -07:00
Karl Burtram
fbd5e819a2 Edit Agent Alert updates (#1872)
* Set database name and severity when editing alert

* Add Operator and Proxy edit

* Add edit job hookup

* Edit WIP

* Additional edit alert updates

* Remove unused method
2018-07-09 10:47:50 -07:00
Matt Irvine
bdc391d376 Fix enter button behavior for wizards and dialogs (#1868) 2018-07-06 17:35:28 -07:00
Madeline MacDonald
6b618fb121 Updating keybindings (#1839)
* Changing key combos, and behavior for starting/stopping

* Updating keybindings

* Fixing mac keybindings

* Clear data when starting profiler from keyboard shortcut
2018-07-06 14:53:31 -07:00
110 changed files with 2619 additions and 1172 deletions

View File

@@ -13,6 +13,11 @@ import { IAgentDialogData, AgentDialogMode } from '../interfaces';
const localize = nls.loadMessageBundle();
export class AlertData implements IAgentDialogData {
public static readonly AlertTypeSqlServerEventString: string = localize('alertData.DefaultAlertTypString', 'SQL Server event alert');
public static readonly AlertTypePerformanceConditionString: string = localize('alertDialog.PerformanceCondition', 'SQL Server performance condition alert');
public static readonly AlertTypeWmiEventString: string = localize('alertDialog.WmiEvent', 'WMI event alert');
public static readonly DefaultAlertTypeString: string = AlertData.AlertTypeSqlServerEventString;
ownerUri: string;
dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
id: number;
@@ -23,7 +28,7 @@ export class AlertData implements IAgentDialogData {
eventSource: string;
hasNotification: number;
includeEventDescription: string;
isEnabled: boolean;
isEnabled: boolean = true;
jobId: string;
jobName: string;
lastOccurrenceDate: string;
@@ -36,7 +41,7 @@ export class AlertData implements IAgentDialogData {
databaseName: string;
countResetDate: string;
categoryName: string;
alertType: string;
alertType: string = AlertData.DefaultAlertTypeString;
wmiEventNamespace: string;
wmiEventQuery: string;
@@ -109,9 +114,19 @@ export class AlertData implements IAgentDialogData {
databaseName: this.databaseName,
countResetDate: this.countResetDate,
categoryName: this.categoryName,
alertType: sqlops.AlertType.sqlServerEvent, //this.alertType,
alertType: AlertData.getAlertTypeFromString(this.alertType),
wmiEventNamespace: this.wmiEventNamespace,
wmiEventQuery: this.wmiEventQuery
};
}
private static getAlertTypeFromString(alertTypeString: string): sqlops.AlertType {
if (alertTypeString === AlertData.AlertTypePerformanceConditionString) {
return sqlops.AlertType.sqlServerPerformanceCondition;
} else if (alertTypeString === AlertData.AlertTypeWmiEventString) {
return sqlops.AlertType.wmiEvent;
} else {
return sqlops.AlertType.sqlServerEvent;
}
}
}

View File

@@ -4,18 +4,22 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
const localize = nls.loadMessageBundle();
export class JobData implements IAgentDialogData {
private readonly JobCompletionActionCondition_Always: string = 'When the job completes';
private readonly JobCompletionActionCondition_OnFailure: string = 'When the job fails';
private readonly JobCompletionActionCondition_OnSuccess: string = 'When the job succeeds';
private readonly JobCompletionActionCondition_Always: string = localize('jobData.whenJobCompletes', 'When the job completes');
private readonly JobCompletionActionCondition_OnFailure: string = localize('jobData.whenJobFails', 'When the job fails');
private readonly JobCompletionActionCondition_OnSuccess: string = localize('jobData.whenJobSucceeds', 'When the job succeeds');
// Error Messages
private readonly CreateJobErrorMessage_NameIsEmpty = 'Job name must be provided';
private readonly CreateJobErrorMessage_NameIsEmpty = localize('jobData.jobNameRequired', 'Job name must be provided');
private _ownerUri: string;
private _jobCategories: string[];
@@ -25,6 +29,7 @@ export class JobData implements IAgentDialogData {
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
public name: string;
public originalName: string;
public enabled: boolean = true;
public description: string;
public category: string;
@@ -40,8 +45,21 @@ export class JobData implements IAgentDialogData {
public jobSchedules: sqlops.AgentJobScheduleInfo[];
public alerts: sqlops.AgentAlertInfo[];
constructor(ownerUri: string, private _agentService: sqlops.AgentServicesProvider = null) {
constructor(
ownerUri: string,
jobInfo: sqlops.AgentJobInfo = undefined,
private _agentService: sqlops.AgentServicesProvider = undefined) {
this._ownerUri = ownerUri;
if (jobInfo) {
this.dialogMode = AgentDialogMode.EDIT;
this.name = jobInfo.name;
this.originalName = jobInfo.name;
this.owner = jobInfo.owner;
this.category = jobInfo.category;
this.description = jobInfo.description;
this.enabled = jobInfo.enabled;
}
}
public get jobCategories(): string[] {
@@ -92,7 +110,39 @@ export class JobData implements IAgentDialogData {
}
public async save() {
await this._agentService.createJob(this.ownerUri, {
let jobInfo: sqlops.AgentJobInfo = this.toAgentJobInfo();
let result = this.dialogMode === AgentDialogMode.CREATE
? await this._agentService.createJob(this.ownerUri, jobInfo)
: await this._agentService.updateJob(this.ownerUri, this.originalName, jobInfo);
if (!result || !result.success) {
vscode.window.showErrorMessage(
localize('jobData.saveErrorMessage', "Job update failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
}
}
public validate(): { valid: boolean, errorMessages: string[] } {
let validationErrors: string[] = [];
if (!(this.name && this.name.trim())) {
validationErrors.push(this.CreateJobErrorMessage_NameIsEmpty);
}
return {
valid: validationErrors.length === 0,
errorMessages: validationErrors
};
}
public addJobSchedule(schedule: sqlops.AgentJobScheduleInfo) {
let existingSchedule = this.jobSchedules.find(item => item.name === schedule.name);
if (!existingSchedule) {
this.jobSchedules.push(schedule);
}
}
public toAgentJobInfo(): sqlops.AgentJobInfo {
return {
name: this.name,
owner: this.owner,
description: this.description,
@@ -122,30 +172,6 @@ export class JobData implements IAgentDialogData {
lastRun: '',
nextRun: '',
jobId: ''
}).then(result => {
if (!result.success) {
console.info(result.errorMessage);
}
});
}
public validate(): { valid: boolean, errorMessages: string[] } {
let validationErrors: string[] = [];
if (!(this.name && this.name.trim())) {
validationErrors.push(this.CreateJobErrorMessage_NameIsEmpty);
}
return {
valid: validationErrors.length === 0,
errorMessages: validationErrors
};
}
public addJobSchedule(schedule: sqlops.AgentJobScheduleInfo) {
let existingSchedule = this.jobSchedules.find(item => item.name === schedule.name);
if (!existingSchedule) {
this.jobSchedules.push(schedule);
}
}
}

View File

@@ -29,8 +29,14 @@ export class OperatorData implements IAgentDialogData {
weekdayPagerStartTime: string;
weekdayPagerEndTime: string;
constructor(ownerUri:string) {
constructor(ownerUri:string, operatorInfo: sqlops.AgentOperatorInfo) {
this.ownerUri = ownerUri;
if (operatorInfo) {
this.dialogMode = AgentDialogMode.EDIT;
this.name = operatorInfo.name;
this.enabled = operatorInfo.enabled;
}
}
public async initialize() {

View File

@@ -19,8 +19,14 @@ export class ProxyData implements IAgentDialogData {
credentialId: number;
isEnabled: boolean;
constructor(ownerUri:string) {
constructor(ownerUri:string, proxyInfo: sqlops.AgentProxyInfo) {
this.ownerUri = ownerUri;
if (proxyInfo) {
this.accountName = proxyInfo.accountName;
this.credentialName = proxyInfo.credentialName;
this.description = proxyInfo.description;
}
}
public async initialize() {

View File

@@ -10,6 +10,8 @@ import * as sqlops from 'sqlops';
import { AgentDialog } from './agentDialog';
import { AgentUtils } from '../agentUtils';
import { AlertData } from '../data/alertData';
import { OperatorDialog } from './operatorDialog';
import { JobDialog } from './jobDialog';
const localize = nls.loadMessageBundle();
@@ -21,6 +23,7 @@ export class AlertDialog extends AgentDialog<AlertData> {
private static readonly GeneralTabText: string = localize('alertDialog.General', 'General');
private static readonly ResponseTabText: string = localize('alertDialog.Response', 'Response');
private static readonly OptionsTabText: string = localize('alertDialog.Options', 'Options');
private static readonly EventAlertText: string = localize('alertDialog.eventAlert', 'Event alert definition');
// General tab strings
private static readonly NameLabel: string = localize('alertDialog.Name', 'Name');
@@ -31,9 +34,6 @@ export class AlertDialog extends AgentDialog<AlertData> {
private static readonly SeverityLabel: string = localize('alertDialog.Severity', 'Severity');
private static readonly RaiseIfMessageContainsLabel: string = localize('alertDialog.RaiseAlertContains', 'Raise alert when message contains');
private static readonly MessageTextLabel: string = localize('alertDialog.MessageText', 'Message text');
private static readonly AlertTypeSqlServerEventString: string = localize('alertDialog.SqlServerEventAlert', 'SQL Server event alert');
private static readonly AlertTypePerformanceConditionString: string = localize('alertDialog.PerformanceCondition', 'SQL Server performance condition alert');
private static readonly AlertTypeWmiEventString: string = localize('alertDialog.WmiEvent', 'WMI event alert');
private static readonly AlertSeverity001Label: string = localize('alertDialog.Severity001', '001 - Miscellaneous System Information');
private static readonly AlertSeverity002Label: string = localize('alertDialog.Severity002', '002 - Reserved');
private static readonly AlertSeverity003Label: string = localize('alertDialog.Severity003', '003 - Reserved');
@@ -59,11 +59,12 @@ export class AlertDialog extends AgentDialog<AlertData> {
private static readonly AlertSeverity023Label: string = localize('alertDialog.Severity023', '023 - Fatal Error: Database Integrity Suspect');
private static readonly AlertSeverity024Label: string = localize('alertDialog.Severity024', '024 - Fatal Error: Hardware Error');
private static readonly AlertSeverity025Label: string = localize('alertDialog.Severity025', '025 - Fatal Error');
private static readonly AllDatabases: string = localize('alertDialog.AllDatabases', '<all databases>');
private static readonly AlertTypes: string[] = [
AlertDialog.AlertTypeSqlServerEventString,
AlertDialog.AlertTypePerformanceConditionString,
AlertDialog.AlertTypeWmiEventString
AlertData.AlertTypeSqlServerEventString,
AlertData.AlertTypePerformanceConditionString,
AlertData.AlertTypeWmiEventString
];
private static readonly AlertSeverities: string[] = [
@@ -124,6 +125,10 @@ export class AlertDialog extends AgentDialog<AlertData> {
private severityDropDown: sqlops.DropDownComponent;
private databaseDropDown: sqlops.DropDownComponent;
private enabledCheckBox: sqlops.CheckBoxComponent;
private errorNumberRadioButton: sqlops.RadioButtonComponent;
private severityRadioButton: sqlops.RadioButtonComponent;
private errorNumberTextBox: sqlops.InputBoxComponent;
private raiseAlertMessageCheckBox: sqlops.CheckBoxComponent;
private raiseAlertMessageTextBox: sqlops.InputBoxComponent;
@@ -142,58 +147,115 @@ export class AlertDialog extends AgentDialog<AlertData> {
private delayMinutesTextBox: sqlops.InputBoxComponent;
private delaySecondsTextBox: sqlops.InputBoxComponent;
constructor(ownerUri: string, alertInfo: sqlops.AgentAlertInfo = null) {
private jobs: string[];
private databases: string[];
constructor(ownerUri: string, alertInfo: sqlops.AgentAlertInfo = undefined, jobs: string[]) {
super(ownerUri,
new AlertData(ownerUri, alertInfo),
alertInfo ? AlertDialog.EditDialogTitle : AlertDialog.CreateDialogTitle);
this.jobs = jobs;
}
protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
let databases = await AgentUtils.getDatabases(this.ownerUri);
this.databases = await AgentUtils.getDatabases(this.ownerUri);
this.databases.unshift(AlertDialog.AllDatabases);
this.generalTab = sqlops.window.modelviewdialog.createTab(AlertDialog.GeneralTabText);
this.responseTab = sqlops.window.modelviewdialog.createTab(AlertDialog.ResponseTabText);
this.optionsTab = sqlops.window.modelviewdialog.createTab(AlertDialog.OptionsTabText);
this.initializeGeneralTab(databases);
this.initializeGeneralTab(this.databases, dialog);
this.initializeResponseTab();
this.initializeOptionsTab();
dialog.content = [this.generalTab, this.responseTab, this.optionsTab];
}
private initializeGeneralTab(databases: string[]) {
private initializeGeneralTab(databases: string[], dialog: sqlops.window.modelviewdialog.Dialog) {
this.generalTab.registerContent(async view => {
// create controls
this.nameTextBox = view.modelBuilder.inputBox().component();
this.nameTextBox.required = true;
this.nameTextBox.onTextChanged(() => {
if (this.nameTextBox.value.length > 0) {
dialog.okButton.enabled = true;
} else {
dialog.okButton.enabled = false;
}
});
this.enabledCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.EnabledCheckboxLabel
}).component();
this.enabledCheckBox.checked = true;
this.databaseDropDown = view.modelBuilder.dropDown()
.withProperties({
value: databases[0],
values: databases
values: databases,
width: '100%'
}).component();
this.typeDropDown = view.modelBuilder.dropDown()
.withProperties({
value: AlertDialog.AlertTypes[0],
values: AlertDialog.AlertTypes
value: '',
values: AlertDialog.AlertTypes,
width: '100%'
}).component();
this.severityRadioButton = view.modelBuilder.radioButton()
.withProperties({
value: 'serverity',
name: 'alertTypeOptions',
label: AlertDialog.SeverityLabel,
checked: true
}).component();
this.severityRadioButton.checked = true;
this.severityDropDown = view.modelBuilder.dropDown()
.withProperties({
value: AlertDialog.AlertSeverities[0],
values: AlertDialog.AlertSeverities
values: AlertDialog.AlertSeverities,
width: '100%'
}).component();
this.errorNumberRadioButton = view.modelBuilder.radioButton()
.withProperties({
value: 'errorNumber',
name: 'alertTypeOptions',
label: AlertDialog.ErrorNumberLabel
}).component();
this.errorNumberTextBox = view.modelBuilder.inputBox()
.withProperties({
width: '100%'
})
.component();
this.errorNumberTextBox.enabled = false;
this.errorNumberRadioButton.onDidClick(() => {
this.errorNumberTextBox.enabled = true;
this.severityDropDown.enabled = false;
});
this.severityRadioButton.onDidClick(() => {
this.errorNumberTextBox.enabled = false;
this.severityDropDown.enabled = true;
});
this.raiseAlertMessageCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.RaiseIfMessageContainsLabel
}).component();
this.raiseAlertMessageTextBox = view.modelBuilder.inputBox().component();
this.raiseAlertMessageTextBox.enabled = false;
this.raiseAlertMessageCheckBox.onChanged(() => {
this.raiseAlertMessageTextBox.enabled = this.raiseAlertMessageCheckBox.checked;
});
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
@@ -206,24 +268,61 @@ export class AlertDialog extends AgentDialog<AlertData> {
component: this.typeDropDown,
title: AlertDialog.TypeLabel
}, {
component: this.databaseDropDown,
title: AlertDialog.DatabaseLabel
}, {
component: this.severityDropDown,
title: AlertDialog.SeverityLabel
}, {
component: this.raiseAlertMessageCheckBox,
title: ''
}, {
component: this.raiseAlertMessageTextBox,
title: AlertDialog.MessageTextLabel
components: [{
component: this.databaseDropDown,
title: AlertDialog.DatabaseLabel
},
{
component: this.severityRadioButton,
title: ''
},
{
component: this.severityDropDown,
title: ''
},
{
component: this.errorNumberRadioButton,
title: ''
},
{
component: this.errorNumberTextBox,
title: ''
},
{
component: this.raiseAlertMessageCheckBox,
title: ''
}, {
component: this.raiseAlertMessageTextBox,
title: AlertDialog.MessageTextLabel
}],
title: AlertDialog.EventAlertText
}
]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
// initialize control values
this.nameTextBox.value = this.model.name;
this.raiseAlertMessageTextBox.value = this.model.eventDescriptionKeyword;
this.typeDropDown.value = this.model.alertType;
this.enabledCheckBox.checked = this.model.isEnabled;
if (this.model.messageId > 0) {
this.errorNumberRadioButton.checked = true;
this.errorNumberTextBox.value = this.model.messageId.toString();
}
if (this.model.severity > 0) {
this.severityRadioButton.checked = true;
this.severityDropDown.value = this.severityDropDown.values[this.model.severity-1];
}
if (this.model.databaseName) {
let idx = this.databases.indexOf(this.model.databaseName);
if (idx >= 0) {
this.databaseDropDown.value = this.databases[idx];
}
}
});
}
@@ -234,12 +333,38 @@ export class AlertDialog extends AgentDialog<AlertData> {
label: AlertDialog.ExecuteJobCheckBoxLabel
}).component();
this.executeJobTextBox = view.modelBuilder.inputBox().component();
this.executeJobTextBox = view.modelBuilder.inputBox()
.withProperties({ width: 375 })
.component();
this.executeJobTextBox.enabled = false;
this.newJobButton = view.modelBuilder.button().withProperties({
label: AlertDialog.NewJobButtonLabel,
width: 80
}).component();
this.newJobButton.enabled = false;
this.newJobButton.onDidClick(() => {
let jobDialog = new JobDialog(this.ownerUri);
jobDialog.openDialog();
});
this.executeJobCheckBox.onChanged(() => {
if (this.executeJobCheckBox.checked) {
this.executeJobTextBox.enabled = true;
this.newJobButton.enabled = true;
} else {
this.executeJobTextBox.enabled = false;
this.newJobButton.enabled = false;
}
});
let executeJobContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.executeJobTextBox,
title: AlertDialog.ExecuteJobTextBoxLabel
}, {
component: this.newJobButton,
title: AlertDialog.NewJobButtonLabel
}], { componentWidth: '100%'}).component();
this.notifyOperatorsCheckBox = view.modelBuilder.checkBox()
.withProperties({
@@ -254,32 +379,57 @@ export class AlertDialog extends AgentDialog<AlertData> {
AlertDialog.OperatorPagerColumnLabel
],
data: [],
height: 500
height: 500,
width: 375
}).component();
this.newOperatorButton = view.modelBuilder.button().withProperties({
label: this.newOperatorButton,
label: AlertDialog.NewOperatorButtonLabel,
width: 80
}).component();
this.operatorsTable.enabled = false;
this.newOperatorButton.enabled = false;
this.newOperatorButton.onDidClick(() => {
let operatorDialog = new OperatorDialog(this.ownerUri);
operatorDialog.openDialog();
});
this.notifyOperatorsCheckBox.onChanged(() => {
if (this.notifyOperatorsCheckBox.checked) {
this.operatorsTable.enabled = true;
this.newOperatorButton.enabled = true;
} else {
this.operatorsTable.enabled = false;
this.newOperatorButton.enabled = false;
}
});
let notifyOperatorContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.operatorsTable,
title: AlertDialog.OperatorListLabel
}, {
component: this.newOperatorButton,
title: ''
}], { componentWidth: '100%'}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.executeJobCheckBox,
title: ''
}, {
component: this.executeJobTextBox,
title: AlertDialog.ExecuteJobTextBoxLabel
}, {
component: this.newJobButton,
title: AlertDialog.NewJobButtonLabel
component: executeJobContainer,
title: ''
}, {
component: this.notifyOperatorsCheckBox,
title: ''
}, {
component: this.operatorsTable,
title: AlertDialog.OperatorListLabel,
actions: [this.newOperatorButton]
}]).withLayout({ width: '100%' }).component();
component: notifyOperatorContainer,
title: ''
}])
.withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
@@ -300,9 +450,19 @@ export class AlertDialog extends AgentDialog<AlertData> {
this.additionalMessageTextBox = view.modelBuilder.inputBox().component();
this.delayMinutesTextBox = view.modelBuilder.inputBox().component();
this.delayMinutesTextBox = view.modelBuilder.inputBox()
.withProperties({
inputType: 'number',
placeHolder: 0
})
.component();
this.delaySecondsTextBox = view.modelBuilder.inputBox().component();
this.delaySecondsTextBox = view.modelBuilder.inputBox()
.withProperties({
inputType: 'number',
placeHolder: 0
})
.component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
@@ -343,13 +503,25 @@ export class AlertDialog extends AgentDialog<AlertData> {
this.model.isEnabled = this.enabledCheckBox.checked;
this.model.alertType = this.getDropdownValue(this.typeDropDown);
this.model.databaseName = this.getDropdownValue(this.databaseDropDown);
this.model.severity = this.getSeverityNumber();
this.model.messageId = undefined;
let databaseName = this.getDropdownValue(this.databaseDropDown);
this.model.databaseName = (databaseName !== AlertDialog.AllDatabases) ? databaseName : undefined;
let raiseIfError = this.raiseAlertMessageCheckBox.checked;
if (raiseIfError) {
let messageText = this.raiseAlertMessageTextBox.value;
if (this.severityRadioButton.checked) {
this.model.severity = this.getSeverityNumber();
this.model.messageId = 0;
} else {
this.model.severity = 0;
this.model.messageId = +this.errorNumberTextBox.value;
}
if (this.raiseAlertMessageCheckBox.checked) {
this.model.eventDescriptionKeyword = this.raiseAlertMessageTextBox.value;
} else {
this.model.eventDescriptionKeyword = '';
}
let minutes = this.delayMinutesTextBox.value ? +this.delayMinutesTextBox.value : 0;
let seconds = this.delaySecondsTextBox.value ? +this.delaySecondsTextBox : 0;
this.model.delayBetweenResponses = minutes + seconds;
}
}

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import { JobData } from '../data/jobData';
import { JobStepDialog } from './jobStepDialog';
@@ -10,50 +11,57 @@ import { PickScheduleDialog } from './pickScheduleDialog';
import { AlertDialog } from './alertDialog';
import { AgentDialog } from './agentDialog';
const localize = nls.loadMessageBundle();
export class JobDialog extends AgentDialog<JobData> {
// TODO: localize
// Top level
private static readonly DialogTitle: string = 'New Job';
private readonly GeneralTabText: string = 'General';
private readonly StepsTabText: string = 'Steps';
private readonly SchedulesTabText: string = 'Schedules';
private readonly AlertsTabText: string = 'Alerts';
private readonly NotificationsTabText: string = 'Notifications';
private static readonly CreateDialogTitle: string = localize('jobDialog.newJob', 'New Job');
private static readonly EditDialogTitle: string = localize('jobDialog.editJob', 'Edit Job');
private readonly GeneralTabText: string = localize('jobDialog.general', 'General');
private readonly StepsTabText: string = localize('jobDialog.steps', 'Steps');
private readonly SchedulesTabText: string = localize('jobDialog.schedules', 'Schedules');
private readonly AlertsTabText: string = localize('jobDialog.alerts', 'Alerts');
private readonly NotificationsTabText: string = localize('jobDialog.notifications', 'Notifications');
private readonly BlankJobNameErrorText: string = localize('jobDialog.blankJobNameError', 'The name of the job cannot be blank.');
// General tab strings
private readonly NameTextBoxLabel: string = 'Name';
private readonly OwnerTextBoxLabel: string = 'Owner';
private readonly CategoryDropdownLabel: string = 'Category';
private readonly DescriptionTextBoxLabel: string = 'Description';
private readonly EnabledCheckboxLabel: string = 'Enabled';
private readonly NameTextBoxLabel: string = localize('jobDialog.name', 'Name');
private readonly OwnerTextBoxLabel: string = localize('jobDialog.owner', 'Owner');
private readonly CategoryDropdownLabel: string = localize('jobDialog.category', 'Category');
private readonly DescriptionTextBoxLabel: string = localize('jobDialog.description', 'Description');
private readonly EnabledCheckboxLabel: string = localize('jobDialog.enabled', 'Enabled');
// Steps tab strings
private readonly JobStepsTopLabelString: string = 'Job step list';
private readonly StepsTable_StepColumnString: string = 'Step';
private readonly StepsTable_NameColumnString: string = 'Name';
private readonly StepsTable_TypeColumnString: string = 'Type';
private readonly StepsTable_SuccessColumnString: string = 'On Success';
private readonly StepsTable_FailureColumnString: string = 'On Failure';
private readonly NewStepButtonString: string = 'New...';
private readonly InsertStepButtonString: string = 'Insert...';
private readonly EditStepButtonString: string = 'Edit';
private readonly DeleteStepButtonString: string = 'Delete';
private readonly JobStepsTopLabelString: string = localize('jobDialog.jobStepList', 'Job step list');
private readonly StepsTable_StepColumnString: string = localize('jobDialog.step', 'Step');
private readonly StepsTable_NameColumnString: string = localize('jobDialog.name', 'Name');
private readonly StepsTable_TypeColumnString: string = localize('jobDialog.type', 'Type');
private readonly StepsTable_SuccessColumnString: string = localize('jobDialog.onSuccess', 'On Success');
private readonly StepsTable_FailureColumnString: string = localize('jobDialog.onFailure', 'On Failure');
private readonly NewStepButtonString: string = localize('jobDialog.new', 'New...');
private readonly EditStepButtonString: string = localize('jobDialog.edit', 'Edit');
private readonly DeleteStepButtonString: string = localize('jobDialog.delete', 'Delete');
private readonly MoveStepUpButtonString: string = localize('jobDialog.moveUp', 'Move Step Up');
private readonly MoveStepDownButtonString: string = localize('jobDialog.moveDown', 'Move Step Up');
// Notifications tab strings
private readonly NotificationsTabTopLabelString: string = 'Actions to perform when the job completes';
private readonly EmailCheckBoxString: string = 'Email';
private readonly PagerCheckBoxString: string = 'Page';
private readonly EventLogCheckBoxString: string = 'Write to the Windows Application event log';
private readonly DeleteJobCheckBoxString: string = 'Automatically delete job';
private readonly NotificationsTabTopLabelString: string = localize('jobDialog.notificationsTabTop', 'Actions to perform when the job completes');
private readonly EmailCheckBoxString: string = localize('jobDialog.email', 'Email');
private readonly PagerCheckBoxString: string = localize('jobDialog.page', 'Page');
private readonly EventLogCheckBoxString: string = localize('jobDialog.eventLogCheckBoxLabel', 'Write to the Windows Application event log');
private readonly DeleteJobCheckBoxString: string = localize('jobDialog.deleteJobLabel', 'Automatically delete job');
// Schedules tab strings
private readonly SchedulesTopLabelString: string = 'Schedules list';
private readonly PickScheduleButtonString: string = 'Pick Schedule';
private readonly SchedulesTopLabelString: string = localize('jobDialog.schedulesaLabel', 'Schedules list');
private readonly PickScheduleButtonString: string = localize('jobDialog.pickSchedule', 'Pick Schedule');
private readonly ScheduleNameLabelString: string = localize('jobDialog.scheduleNameLabel', 'Schedule Name');
// Alerts tab strings
private readonly AlertsTopLabelString: string = 'Alerts list';
private readonly NewAlertButtonString: string = 'New Alert';
private readonly AlertsTopLabelString: string = localize('jobDialog.alertsList', 'Alerts list');
private readonly NewAlertButtonString: string = localize('jobDialog.newAlert', 'New Alert');
private readonly AlertNameLabelString: string = localize('jobDialog.alertNameLabel', 'Alert Name');
// UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab;
@@ -72,7 +80,8 @@ export class JobDialog extends AgentDialog<JobData> {
// Steps tab controls
private stepsTable: sqlops.TableComponent;
private newStepButton: sqlops.ButtonComponent;
private insertStepButton: sqlops.ButtonComponent;
private moveStepUpButton: sqlops.ButtonComponent;
private moveStepDownButton: sqlops.ButtonComponent;
private editStepButton: sqlops.ButtonComponent;
private deleteStepButton: sqlops.ButtonComponent;
@@ -97,8 +106,11 @@ export class JobDialog extends AgentDialog<JobData> {
private alertsTable: sqlops.TableComponent;
private newAlertButton: sqlops.ButtonComponent;
constructor(ownerUri: string) {
super(ownerUri, new JobData(ownerUri), JobDialog.DialogTitle);
constructor(ownerUri: string, jobInfo: sqlops.AgentJobInfo = undefined) {
super(
ownerUri,
new JobData(ownerUri, jobInfo),
jobInfo ? JobDialog.EditDialogTitle : JobDialog.CreateDialogTitle);
}
protected async initializeDialog() {
@@ -129,6 +141,12 @@ export class JobDialog extends AgentDialog<JobData> {
private initializeGeneralTab() {
this.generalTab.registerContent(async view => {
this.nameTextBox = view.modelBuilder.inputBox().component();
this.nameTextBox.required = true;
this.nameTextBox.onTextChanged(() => {
if (this.nameTextBox.value && this.nameTextBox.value.length > 0) {
this.dialog.message = null;
}
});
this.ownerTextBox = view.modelBuilder.inputBox().component();
this.categoryDropdown = view.modelBuilder.dropDown().component();
this.descriptionTextBox = view.modelBuilder.inputBox().withProperties({
@@ -159,11 +177,18 @@ export class JobDialog extends AgentDialog<JobData> {
await view.initializeModel(formModel);
this.nameTextBox.value = this.model.name;
this.ownerTextBox.value = this.model.defaultOwner;
this.categoryDropdown.values = this.model.jobCategories;
this.categoryDropdown.value = this.model.jobCategories[0];
let idx: number = undefined;
if (this.model.category && this.model.category !== '') {
idx = this.model.jobCategories.indexOf(this.model.category);
}
this.categoryDropdown.value = this.model.jobCategories[idx > 0 ? idx : 0];
this.enabledCheckBox.checked = this.model.enabled;
this.descriptionTextBox.value = '';
this.descriptionTextBox.value = this.model.description;
});
}
@@ -179,7 +204,19 @@ export class JobDialog extends AgentDialog<JobData> {
this.StepsTable_FailureColumnString
],
data: [],
height: 800
height: 430
}).component();
this.moveStepUpButton = view.modelBuilder.button()
.withProperties({
label: this.MoveStepUpButtonString,
width: 80
}).component();
this.moveStepDownButton = view.modelBuilder.button()
.withProperties({
label: this.MoveStepDownButtonString,
width: 80
}).component();
this.newStepButton = view.modelBuilder.button().withProperties({
@@ -188,15 +225,14 @@ export class JobDialog extends AgentDialog<JobData> {
}).component();
this.newStepButton.onDidClick((e)=>{
let stepDialog = new JobStepDialog(this.model.ownerUri, '', '', 1, this.model);
stepDialog.openNewStepDialog();
if (this.nameTextBox.value && this.nameTextBox.value.length > 0) {
let stepDialog = new JobStepDialog(this.model.ownerUri, this.nameTextBox.value, '' , 1, this.model);
stepDialog.openNewStepDialog();
} else {
this.dialog.message = { text: this.BlankJobNameErrorText };
}
});
this.insertStepButton = view.modelBuilder.button().withProperties({
label: this.InsertStepButtonString,
width: 80
}).component();
this.editStepButton = view.modelBuilder.button().withProperties({
label: this.EditStepButtonString,
width: 80
@@ -211,7 +247,7 @@ export class JobDialog extends AgentDialog<JobData> {
.withFormItems([{
component: this.stepsTable,
title: this.JobStepsTopLabelString,
actions: [this.newStepButton, this.insertStepButton, this.editStepButton, this.deleteStepButton]
actions: [this.moveStepUpButton, this.moveStepDownButton, this.newStepButton, this.editStepButton, this.deleteStepButton]
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
@@ -222,10 +258,10 @@ export class JobDialog extends AgentDialog<JobData> {
this.alertsTable = view.modelBuilder.table()
.withProperties({
columns: [
'Alert Name'
this.AlertNameLabelString
],
data: [],
height: 600,
height: 430,
width: 400
}).component();
@@ -235,7 +271,7 @@ export class JobDialog extends AgentDialog<JobData> {
}).component();
this.newAlertButton.onDidClick((e)=>{
let alertDialog = new AlertDialog(this.model.ownerUri);
let alertDialog = new AlertDialog(this.model.ownerUri, null, []);
alertDialog.onSuccess((dialogModel) => {
});
alertDialog.openDialog();
@@ -257,11 +293,11 @@ export class JobDialog extends AgentDialog<JobData> {
this.schedulesTable = view.modelBuilder.table()
.withProperties({
columns: [
'Schedule Name'
this.ScheduleNameLabelString
],
data: [],
height: 600,
width: 400
height: 430,
width: 420
}).component();
this.pickScheduleButton = view.modelBuilder.button().withProperties({
@@ -361,21 +397,23 @@ export class JobDialog extends AgentDialog<JobData> {
let formModel = view.modelBuilder.formContainer().withFormItems([
{
component: this.notificationsTabTopLabel,
title: ''
}, {
component: emailContainer,
title: ''
}, {
component: pagerContainer,
title: ''
}, {
component: eventLogContainer,
title: ''
}, {
component: deleteJobContainer,
title: ''
}]).withLayout({ width: '100%' }).component();
components:
[{
component: emailContainer,
title: ''
},
{
component: pagerContainer,
title: ''
},
{
component: eventLogContainer,
title: ''
},
{
component: deleteJobContainer,
title: ''
}], title: this.NotificationsTabTopLabelString}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
this.emailConditionDropdown.values = this.model.JobCompletionActionConditions;

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { JobStepData } from '../data/jobStepData';
@@ -10,31 +11,57 @@ import { AgentUtils } from '../agentUtils';
import { JobData } from '../data/jobData';
const path = require('path');
const localize = nls.loadMessageBundle();
export class JobStepDialog {
// TODO: localize
// Top level
//
private static readonly DialogTitle: string = 'New Job Step';
private static readonly FileBrowserDialogTitle: string = 'Locate Database Files - ';
private static readonly OkButtonText: string = 'OK';
private static readonly CancelButtonText: string = 'Cancel';
private static readonly GeneralTabText: string = 'General';
private static readonly AdvancedTabText: string = 'Advanced';
private static readonly OpenCommandText: string = 'Open...';
private static readonly ParseCommandText: string = 'Parse';
private static readonly NextButtonText: string = 'Next';
private static readonly PreviousButtonText: string = 'Previous';
private static readonly SuccessAction: string = 'On success action';
private static readonly FailureAction: string = 'On failure action';
private readonly DialogTitle: string = localize('jobStepDialog.newJobStep', 'New Job Step');
private readonly FileBrowserDialogTitle: string = localize('jobStepDialog.fileBrowserTitle', 'Locate Database Files - ');
private readonly OkButtonText: string = localize('jobStepDialog.ok', 'OK');
private readonly CancelButtonText: string = localize('jobStepDialog.cancel', 'Cancel');
private readonly GeneralTabText: string = localize('jobStepDialog.general', 'General');
private readonly AdvancedTabText: string = localize('jobStepDialog.advanced', 'Advanced');
private readonly OpenCommandText: string = localize('jobStepDialog.open', 'Open...');
private readonly ParseCommandText: string = localize('jobStepDialog.parse','Parse');
private readonly NextButtonText: string = localize('jobStepDialog.next', 'Next');
private readonly PreviousButtonText: string = localize('jobStepDialog.previous','Previous');
private readonly SuccessfulParseText: string = localize('jobStepDialog.successParse', 'The command was successfully parsed.');
private readonly FailureParseText: string = localize('jobStepDialog.failParse', 'The command failed.');
// General Control Titles
private readonly StepNameLabelString: string = localize('jobStepDialog.stepNameLabel', 'Step Name');
private readonly TypeLabelString: string = localize('jobStepDialog.typeLabel', 'Type');
private readonly RunAsLabelString: string = localize('jobStepDialog.runAsLabel', 'Run as');
private readonly DatabaseLabelString: string = localize('jobStepDialog.databaseLabel', 'Database');
private readonly CommandLabelString: string = localize('jobStepDialog.commandLabel', 'Command');
// Advanced Control Titles
private readonly SuccessActionLabel: string = localize('jobStepDialog.successAction', 'On success action');
private readonly FailureActionLabel: string = localize('jobStepDialog.failureAction', 'On failure action');
private readonly RunAsUserLabel: string = localize('jobStepDialog.runAsUser', 'Run as user');
private readonly RetryAttemptsLabel: string = localize('jobStepDialog.retryAttempts', 'Retry Attempts');
private readonly RetryIntervalLabel: string = localize('jobStepDialog.retryInterval', 'Retry Interval (minutes)');
private readonly LogToTableLabel: string = localize('jobStepDialog.logToTable', 'Log to table');
private readonly AppendExistingTableEntryLabel: string = localize('jobStepDialog.appendExistingTableEntry', 'Append output to exisiting entry in table');
private readonly IncludeStepOutputHistoryLabel: string = localize('jobStepDialog.includeStepOutputHistory', 'Include step output in history');
private readonly OutputFileNameLabel: string = localize('jobStepDialog.outputFile', 'Output File');
private readonly AppendOutputToFileLabel: string = localize('jobStepDialog.appendOutputToFile', 'Append output to existing file');
// File Browser Control Titles
private readonly SelectedPathLabelString: string = localize('jobStepDialog.selectedPath', 'Selected path');
private readonly FilesOfTypeLabelString: string = localize('jobStepDialog.filesOfType', 'Files of type');
private readonly FileNameLabelString: string = localize('jobStepDialog.fileName', 'File name');
private readonly AllFilesLabelString: string = localize('jobStepDialog.allFiles', 'All Files (*)');
// Dropdown options
private static readonly TSQLScript: string = 'Transact-SQL script (T-SQL)';
private static readonly AgentServiceAccount: string = 'SQL Server Agent Service Account';
private static readonly NextStep: string = 'Go to the next step';
private static readonly QuitJobReportingSuccess: string = 'Quit the job reporting success';
private static readonly QuitJobReportingFailure: string = 'Quit the job reporting failure';
private readonly TSQLScript: string = localize('jobStepDialog.TSQL', 'Transact-SQL script (T-SQL)');
private readonly AgentServiceAccount: string = localize('jobStepDialog.agentServiceAccount', 'SQL Server Agent Service Account');
private readonly NextStep: string = localize('jobStepDialog.nextStep', 'Go to the next step');
private readonly QuitJobReportingSuccess: string = localize('jobStepDialog.quitJobSuccess', 'Quit the job reporting success');
private readonly QuitJobReportingFailure: string = localize('jobStepDialog.quitJobFailure', 'Quit the job reporting failure');
// UI Components
@@ -54,6 +81,7 @@ export class JobStepDialog {
private retryIntervalBox: sqlops.InputBoxComponent;
private outputFileNameBox: sqlops.InputBoxComponent;
private fileBrowserNameBox: sqlops.InputBoxComponent;
private userInputBox: sqlops.InputBoxComponent;
// Dropdowns
private typeDropdown: sqlops.DropDownComponent;
@@ -98,33 +126,33 @@ export class JobStepDialog {
}
private initializeUIComponents() {
this.dialog = sqlops.window.modelviewdialog.createDialog(JobStepDialog.DialogTitle);
this.generalTab = sqlops.window.modelviewdialog.createTab(JobStepDialog.GeneralTabText);
this.advancedTab = sqlops.window.modelviewdialog.createTab(JobStepDialog.AdvancedTabText);
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
this.generalTab = sqlops.window.modelviewdialog.createTab(this.GeneralTabText);
this.advancedTab = sqlops.window.modelviewdialog.createTab(this.AdvancedTabText);
this.dialog.content = [this.generalTab, this.advancedTab];
this.dialog.okButton.onClick(async () => await this.execute());
this.dialog.okButton.label = JobStepDialog.OkButtonText;
this.dialog.cancelButton.label = JobStepDialog.CancelButtonText;
this.dialog.okButton.label = this.OkButtonText;
this.dialog.cancelButton.label = this.CancelButtonText;
}
private createCommands(view, queryProvider: sqlops.QueryProvider) {
this.openButton = view.modelBuilder.button()
.withProperties({
label: JobStepDialog.OpenCommandText,
label: this.OpenCommandText,
width: '80px'
}).component();
this.parseButton = view.modelBuilder.button()
.withProperties({
label: JobStepDialog.ParseCommandText,
label: this.ParseCommandText,
width: '80px'
}).component();
this.parseButton.onDidClick(e => {
if (this.commandTextBox.value) {
queryProvider.parseSyntax(this.ownerUri, this.commandTextBox.value).then(result => {
if (result && result.parseable) {
this.dialog.message = { text: 'The command was successfully parsed.', level: 2};
this.dialog.message = { text: this.SuccessfulParseText, level: 2};
} else if (result && !result.parseable) {
this.dialog.message = { text: 'The command failed' };
this.dialog.message = { text: this.FailureParseText };
}
});
}
@@ -139,13 +167,13 @@ export class JobStepDialog {
.component();
this.nextButton = view.modelBuilder.button()
.withProperties({
label: JobStepDialog.NextButtonText,
label: this.NextButtonText,
enabled: false,
width: '80px'
}).component();
this.previousButton = view.modelBuilder.button()
.withProperties({
label: JobStepDialog.PreviousButtonText,
label: this.PreviousButtonText,
enabled: false,
width: '80px'
}).component();
@@ -159,8 +187,8 @@ export class JobStepDialog {
this.nameTextBox.required = true;
this.typeDropdown = view.modelBuilder.dropDown()
.withProperties({
value: JobStepDialog.TSQLScript,
values: [JobStepDialog.TSQLScript]
value: this.TSQLScript,
values: [this.TSQLScript]
})
.component();
this.runAsDropdown = view.modelBuilder.dropDown()
@@ -171,8 +199,8 @@ export class JobStepDialog {
.component();
this.runAsDropdown.enabled = false;
this.typeDropdown.onValueChanged((type) => {
if (type.selected !== JobStepDialog.TSQLScript) {
this.runAsDropdown.value = JobStepDialog.AgentServiceAccount;
if (type.selected !== this.TSQLScript) {
this.runAsDropdown.value = this.AgentServiceAccount;
this.runAsDropdown.values = [this.runAsDropdown.value];
} else {
this.runAsDropdown.value = '';
@@ -200,19 +228,19 @@ export class JobStepDialog {
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.nameTextBox,
title: 'Step name'
title: this.StepNameLabelString
}, {
component: this.typeDropdown,
title: 'Type'
title: this.TypeLabelString
}, {
component: this.runAsDropdown,
title: 'Run as'
title: this.RunAsLabelString
}, {
component: this.databaseDropdown,
title: 'Database'
title: this.DatabaseLabelString
}, {
component: this.commandTextBox,
title: 'Command',
title: this.CommandLabelString,
actions: [buttonContainer]
}], {
horizontal: false,
@@ -224,81 +252,57 @@ export class JobStepDialog {
});
}
private createRunAsUserOptions(view) {
let userInputBox = view.modelBuilder.inputBox()
.withProperties({ inputType: 'text', width: '100px' }).component();
let viewButton = view.modelBuilder.button()
.withProperties({ label: '...', width: '20px' }).component();
let viewButtonContainer = view.modelBuilder.flexContainer()
.withLayout({ width: 100, textAlign: 'right' })
.withItems([viewButton], { flex: '1 1 50%' }).component();
let userInputBoxContainer = view.modelBuilder.flexContainer()
.withLayout({ width: 200, textAlign: 'left' })
.withItems([userInputBox], { flex: '1 1 50%' }).component();
let runAsUserContainer = view.modelBuilder.flexContainer()
.withLayout({ width: 200 })
.withItems([userInputBoxContainer, viewButtonContainer], { flex: '1 1 50%' })
.component();
let runAsUserForm = view.modelBuilder.formContainer()
.withFormItems([{
component: runAsUserContainer,
title: 'Run as user'
}], { horizontal: true, componentWidth: 200 }).component();
return runAsUserForm;
}
private createAdvancedTab() {
this.advancedTab.registerContent(async (view) => {
this.successActionDropdown = view.modelBuilder.dropDown()
.withProperties({
value: JobStepDialog.NextStep,
values: [JobStepDialog.NextStep, JobStepDialog.QuitJobReportingSuccess, JobStepDialog.QuitJobReportingFailure]
width: '100%',
value: this.NextStep,
values: [this.NextStep, this.QuitJobReportingSuccess, this.QuitJobReportingFailure]
})
.component();
let retryFlexContainer = this.createRetryCounters(view);
this.failureActionDropdown = view.modelBuilder.dropDown()
.withProperties({
value: JobStepDialog.QuitJobReportingFailure,
values: [JobStepDialog.QuitJobReportingFailure, JobStepDialog.NextStep, JobStepDialog.QuitJobReportingSuccess]
value: this.QuitJobReportingFailure,
values: [this.QuitJobReportingFailure, this.NextStep, this.QuitJobReportingSuccess]
})
.component();
let optionsGroup = this.createTSQLOptions(view);
let viewButton = view.modelBuilder.button()
.withProperties({ label: 'View', width: '50px' }).component();
viewButton.enabled = false;
this.logToTableCheckbox = view.modelBuilder.checkBox()
.withProperties({
label: 'Log to table'
label: this.LogToTableLabel
}).component();
let appendToExistingEntryInTableCheckbox = view.modelBuilder.checkBox()
.withProperties({ label: 'Append output to existing entry in table' }).component();
.withProperties({ label: this.AppendExistingTableEntryLabel }).component();
appendToExistingEntryInTableCheckbox.enabled = false;
this.logToTableCheckbox.onChanged(e => {
viewButton.enabled = e;
appendToExistingEntryInTableCheckbox.enabled = e;
});
let appendCheckboxContainer = view.modelBuilder.groupContainer()
.withItems([appendToExistingEntryInTableCheckbox]).component();
let logToTableContainer = view.modelBuilder.flexContainer()
.withLayout({ flexFlow: 'row', justifyContent: 'space-between', width: 300 })
.withItems([this.logToTableCheckbox, viewButton]).component();
.withItems([this.logToTableCheckbox]).component();
let logStepOutputHistoryCheckbox = view.modelBuilder.checkBox()
.withProperties({ label: 'Include step output in history' }).component();
let runAsUserOptions = this.createRunAsUserOptions(view);
.withProperties({ label: this.IncludeStepOutputHistoryLabel }).component();
this.userInputBox = view.modelBuilder.inputBox()
.withProperties({ inputType: 'text', width: '100%' }).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems(
[{
component: this.successActionDropdown,
title: JobStepDialog.SuccessAction
title: this.SuccessActionLabel
}, {
component: retryFlexContainer,
title: ''
}, {
component: this.failureActionDropdown,
title: JobStepDialog.FailureAction
title: this.FailureActionLabel
}, {
component: optionsGroup,
title: 'Transact-SQL script (T-SQL)'
title: this.TSQLScript
}, {
component: logToTableContainer,
title: ''
@@ -309,8 +313,8 @@ export class JobStepDialog {
component: logStepOutputHistoryCheckbox,
title: ''
}, {
component: runAsUserOptions,
title: ''
component: this.userInputBox,
title: this.RunAsUserLabel
}], {
componentWidth: 400
}).component();
@@ -323,32 +327,37 @@ export class JobStepDialog {
private createRetryCounters(view) {
this.retryAttemptsBox = view.modelBuilder.inputBox()
.withValidation(component => component.value >= 0)
.withProperties({
inputType: 'number'
})
.component();
.withValidation(component => component.value >= 0)
.withProperties({
inputType: 'number',
width: '100%',
placeHolder: '0'
})
.component();
this.retryIntervalBox = view.modelBuilder.inputBox()
.withValidation(component => component.value >= 0)
.withProperties({
inputType: 'number'
inputType: 'number',
width: '100%',
placeHolder: '0'
}).component();
let retryAttemptsContainer = view.modelBuilder.formContainer()
.withFormItems(
[{
component: this.retryAttemptsBox,
title: 'Retry Attempts'
}], {
horizontal: false
})
[{
component: this.retryAttemptsBox,
title: this.RetryAttemptsLabel
}], {
horizontal: false,
componentWidth: '100%'
})
.component();
let retryIntervalContainer = view.modelBuilder.formContainer()
.withFormItems(
[{
component: this.retryIntervalBox,
title: 'Retry Interval (minutes)'
title: this.RetryIntervalLabel
}], {
horizontal: false
})
@@ -362,7 +371,7 @@ export class JobStepDialog {
}
private openFileBrowserDialog() {
let fileBrowserTitle = JobStepDialog.FileBrowserDialogTitle + `${this.server}`;
let fileBrowserTitle = this.FileBrowserDialogTitle + `${this.server}`;
this.fileBrowserDialog = sqlops.window.modelviewdialog.createDialog(fileBrowserTitle);
let fileBrowserTab = sqlops.window.modelviewdialog.createTab('File Browser');
this.fileBrowserDialog.content = [fileBrowserTab];
@@ -379,8 +388,8 @@ export class JobStepDialog {
});
this.fileTypeDropdown = view.modelBuilder.dropDown()
.withProperties({
value: 'All Files (*)',
values: ['All Files (*)']
value: this.AllFilesLabelString,
values: [this.AllFilesLabelString]
})
.component();
this.fileBrowserNameBox = view.modelBuilder.inputBox()
@@ -392,13 +401,13 @@ export class JobStepDialog {
title: ''
}, {
component: this.selectedPathTextBox,
title: 'Selected path:'
title: this.SelectedPathLabelString
}, {
component: this.fileTypeDropdown,
title: 'Files of type:'
title: this.FilesOfTypeLabelString
}, {
component: this.fileBrowserNameBox,
title: 'File name:'
title: this.FileNameLabelString
}
]).component();
view.initializeModel(fileBrowserContainer);
@@ -406,8 +415,8 @@ export class JobStepDialog {
this.fileBrowserDialog.okButton.onClick(() => {
this.outputFileNameBox.value = path.join(path.dirname(this.selectedPathTextBox.value), this.fileBrowserNameBox.value);
});
this.fileBrowserDialog.okButton.label = JobStepDialog.OkButtonText;
this.fileBrowserDialog.cancelButton.label = JobStepDialog.CancelButtonText;
this.fileBrowserDialog.okButton.label = this.OkButtonText;
this.fileBrowserDialog.cancelButton.label = this.CancelButtonText;
sqlops.window.modelviewdialog.openDialog(this.fileBrowserDialog);
}
@@ -417,21 +426,15 @@ export class JobStepDialog {
this.outputFileBrowserButton.onDidClick(() => this.openFileBrowserDialog());
this.outputFileNameBox = view.modelBuilder.inputBox()
.withProperties({
width: '150px',
width: 250,
inputType: 'text'
}).component();
let outputViewButton = view.modelBuilder.button()
.withProperties({
width: '50px',
label: 'View'
}).component();
outputViewButton.enabled = false;
let outputButtonContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
textAlign: 'right',
width: 120
}).withItems([this.outputFileBrowserButton, outputViewButton], { flex: '1 1 50%' }).component();
width: '100%'
}).withItems([this.outputFileBrowserButton], { flex: '1 1 50%' }).component();
let outputFlexBox = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
@@ -441,7 +444,7 @@ export class JobStepDialog {
}).component();
this.appendToExistingFileCheckbox = view.modelBuilder.checkBox()
.withProperties({
label: 'Append output to existing file'
label: this.AppendOutputToFileLabel
}).component();
this.appendToExistingFileCheckbox.enabled = false;
this.outputFileNameBox.onTextChanged((input) => {
@@ -454,11 +457,11 @@ export class JobStepDialog {
let outputFileForm = view.modelBuilder.formContainer()
.withFormItems([{
component: outputFlexBox,
title: 'Output file'
title: this.OutputFileNameLabel
}, {
component: this.appendToExistingFileCheckbox,
title: ''
}], { horizontal: true, componentWidth: 200 }).component();
}], { horizontal: false, componentWidth: 200 }).component();
return outputFileForm;
}
@@ -471,8 +474,8 @@ export class JobStepDialog {
this.model.databaseName = this.databaseDropdown.value as string;
this.model.script = this.commandTextBox.value;
this.model.successAction = this.successActionDropdown.value as string;
this.model.retryAttempts = +this.retryAttemptsBox.value;
this.model.retryInterval = +this.retryIntervalBox.value;
this.model.retryAttempts = this.retryAttemptsBox.value ? +this.retryAttemptsBox.value : 0;
this.model.retryInterval = +this.retryIntervalBox.value ? +this.retryIntervalBox.value : 0;
this.model.failureAction = this.failureActionDropdown.value as string;
this.model.outputFileName = this.outputFileNameBox.value;
this.model.appendToLogFile = this.appendToExistingFileCheckbox.checked;

View File

@@ -16,7 +16,8 @@ const localize = nls.loadMessageBundle();
export class OperatorDialog extends AgentDialog<OperatorData> {
// Top level
private static readonly DialogTitle: string = localize('createOperator.createOperator', 'Create Operator');
private static readonly CreateDialogTitle: string = localize('createOperator.createOperator', 'Create Operator');
private static readonly EditDialogTitle: string = localize('createOperator.editOperator', 'Edit Operator');
private static readonly GeneralTabText: string = localize('createOperator.General', 'General');
private static readonly NotificationsTabText: string = localize('createOperator.Notifications', 'Notifications');
@@ -32,6 +33,9 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
private static readonly PagerFridayCheckBoxLabel: string = localize('createOperator.PagerFridayCheckBox', 'Friday ');
private static readonly PagerSaturdayCheckBoxLabel: string = localize('createOperator.PagerSaturdayCheckBox', 'Saturday');
private static readonly PagerSundayCheckBoxLabel: string = localize('createOperator.PagerSundayCheckBox', 'Sunday');
private static readonly WorkdayBeginLabel: string = localize('createOperator.workdayBegin', 'Workday begin');
private static readonly WorkdayEndLabel: string = localize('createOperator.workdayEnd', 'Workday end');
private static readonly PagerDutyScheduleLabel: string = localize('createOperator.PagerDutySchedule', 'Pager on duty schdule');
// Notifications tab strings
private static readonly AlertsTableLabel: string = localize('createOperator.AlertListHeading', 'Alert list');
@@ -65,8 +69,11 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
// Notification tab controls
private alertsTable: sqlops.TableComponent;
constructor(ownerUri: string) {
super(ownerUri, new OperatorData(ownerUri), OperatorDialog.DialogTitle);
constructor(ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo = undefined) {
super(
ownerUri,
new OperatorData(ownerUri, operatorInfo),
operatorInfo ? OperatorDialog.EditDialogTitle : OperatorDialog.CreateDialogTitle);
}
protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
@@ -174,13 +181,13 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
this.weekdayPagerStartTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '08:00:00'
placeHolder: '08:00:00',
}).component();
this.weekdayPagerStartTimeInput.enabled = false;
let weekdayStartInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.weekdayPagerStartTimeInput,
title: 'Workday begin'
title: OperatorDialog.WorkdayBeginLabel
}]).component();
this.weekdayPagerEndTimeInput = view.modelBuilder.inputBox()
@@ -192,7 +199,7 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
let weekdayEndInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.weekdayPagerEndTimeInput,
title: 'Workday end'
title: OperatorDialog.WorkdayEndLabel
}]).component();
this.pagerFridayCheckBox = view.modelBuilder.checkBox()
@@ -216,7 +223,8 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
let pagerFridayCheckboxContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
alignItems: 'baseline'
alignItems: 'baseline',
width: '100%'
}).withItems([this.pagerFridayCheckBox, weekdayStartInputContainer, weekdayEndInputContainer])
.component();
@@ -245,7 +253,7 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
let saturdayStartInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.saturdayPagerStartTimeInput,
title: 'Workday begin'
title: OperatorDialog.WorkdayBeginLabel
}]).component();
this.saturdayPagerEndTimeInput = view.modelBuilder.inputBox()
@@ -257,7 +265,7 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
let saturdayEndInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.saturdayPagerEndTimeInput,
title: 'Workday end'
title: OperatorDialog.WorkdayEndLabel
}]).component();
let pagerSaturdayCheckboxContainer = view.modelBuilder.flexContainer()
@@ -292,7 +300,7 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
let sundayStartInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.sundayPagerStartTimeInput,
title: 'Workday begin'
title: OperatorDialog.WorkdayBeginLabel
}]).component();
this.sundayPagerEndTimeInput = view.modelBuilder.inputBox()
@@ -304,7 +312,7 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
let sundayEndInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.sundayPagerEndTimeInput,
title: 'Workday end'
title: OperatorDialog.WorkdayEndLabel
}]).component();
let pagerSundayCheckboxContainer = view.modelBuilder.flexContainer()
@@ -314,36 +322,6 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
}).withItems([this.pagerSundayCheckBox, sundayStartInputContainer, sundayEndInputContainer])
.component();
let checkBoxContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.pagerMondayCheckBox,
title: ''
}, {
component: this.pagerTuesdayCheckBox,
title: ''
}, {
component: this.pagerWednesdayCheckBox,
title: ''
}, {
component: this.pagerThursdayCheckBox,
title: ''
}, {
component: pagerFridayCheckboxContainer,
title: ''
}, {
component: pagerSaturdayCheckboxContainer,
title: ''
}, {
component: pagerSundayCheckboxContainer,
title: ''
}]).component();
let pagerContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row'
}).withItems([checkBoxContainer])
.component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.nameTextBox,
@@ -358,8 +336,29 @@ export class OperatorDialog extends AgentDialog<OperatorData> {
component: this.pagerEmailNameTextBox,
title: OperatorDialog.PagerEmailNameTextLabel
}, {
component: pagerContainer,
title: ''
components: [{
component: this.pagerMondayCheckBox,
title: ''
}, {
component: this.pagerTuesdayCheckBox,
title: ''
}, {
component: this.pagerWednesdayCheckBox,
title: ''
}, {
component: this.pagerThursdayCheckBox,
title: ''
}, {
component: pagerFridayCheckboxContainer,
title: ''
}, {
component: pagerSaturdayCheckboxContainer,
title: ''
}, {
component: pagerSundayCheckboxContainer,
title: ''
}] ,
title: OperatorDialog.PagerDutyScheduleLabel
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);

View File

@@ -4,18 +4,22 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { PickScheduleData } from '../data/pickScheduleData';
const localize = nls.loadMessageBundle();
export class PickScheduleDialog {
// TODO: localize
// Top level
private readonly DialogTitle: string = 'Job Schedules';
private readonly OkButtonText: string = 'OK';
private readonly CancelButtonText: string = 'Cancel';
private readonly SchedulesTabText: string = 'Schedules';
private readonly DialogTitle: string = localize('pickSchedule.jobSchedules', 'Job Schedules');
private readonly OkButtonText: string = localize('pickSchedule.ok', 'OK');
private readonly CancelButtonText: string = localize('pickSchedule.cancel', 'Cancel');
private readonly ScheduleNameLabelText: string = localize('pickSchedule.scheduleName', 'Schedule Name');
private readonly SchedulesLabelText: string = localize('pickSchedule.schedules', 'Schedules');
// UI Components
private dialog: sqlops.window.modelviewdialog.Dialog;
@@ -38,7 +42,6 @@ export class PickScheduleDialog {
this.dialog.cancelButton.onClick(async () => await this.cancel());
this.dialog.okButton.label = this.OkButtonText;
this.dialog.cancelButton.label = this.CancelButtonText;
sqlops.window.modelviewdialog.openDialog(this.dialog);
}
@@ -47,17 +50,17 @@ export class PickScheduleDialog {
this.schedulesTable = view.modelBuilder.table()
.withProperties({
columns: [
'Schedule Name'
this.ScheduleNameLabelText
],
data: [],
height: 600,
width: 400
height: '80em',
width: '40em'
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.schedulesTable,
title: 'Schedules'
title: this.SchedulesLabelText
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);

View File

@@ -15,32 +15,60 @@ const localize = nls.loadMessageBundle();
export class ProxyDialog extends AgentDialog<ProxyData> {
// Top level
private static readonly DialogTitle: string = localize('createProxy.createAlert', 'Create Alert');
private static readonly CreateDialogTitle: string = localize('createProxy.createProxy', 'Create Proxy');
private static readonly EditDialogTitle: string = localize('createProxy.editProxy', 'Edit Proxy');
private static readonly GeneralTabText: string = localize('createProxy.General', 'General');
// General tab strings
private static readonly ProxyNameTextBoxLabel: string = localize('createProxy.ProxyName', 'Proxy name');
private static readonly CredentialNameTextBoxLabel: string = localize('createProxy.CredentialName', 'Credential name');
private static readonly DescriptionTextBoxLabel: string = localize('createProxy.Description', 'Description');
private static readonly SubsystemsTableLabel: string = localize('createProxy.Subsystems', 'Subsystems');
private static readonly SubsystemNameColumnLabel: string = localize('createProxy.SubsystemName', 'Subsystem');
private static readonly SubsystemLabel: string = localize('createProxy.SubsystemName', 'Subsystem');
private static readonly OperatingSystemLabel: string = localize('createProxy.OperatingSystem', 'Operating system (CmdExec)');
private static readonly ReplicationSnapshotLabel: string = localize('createProxy.ReplicationSnapshot', 'Replication Snapshot');
private static readonly ReplicationTransactionLogLabel: string = localize('createProxy.ReplicationTransactionLog', 'Replication Transaction-Log Reader');
private static readonly ReplicationDistributorLabel: string = localize('createProxy.ReplicationDistributor', 'Replication Distributor');
private static readonly ReplicationMergeLabel: string = localize('createProxy.ReplicationMerge', 'Replication Merge');
private static readonly ReplicationQueueReaderLabel: string = localize('createProxy.ReplicationQueueReader', 'Replication Queue Reader');
private static readonly SSASQueryLabel: string = localize('createProxy.SSASQueryLabel', 'SQL Server Analysis Services Query');
private static readonly SSASCommandLabel: string = localize('createProxy.SSASCommandLabel', 'SQL Server Analysis Services Command');
private static readonly SSISPackageLabel: string = localize('createProxy.SSISPackage', 'SQL Server Integration Services Package');
private static readonly PowerShellLabel: string = localize('createProxy.PowerShell', 'PowerShell');
private static readonly SubSystemHeadingLabel: string = localize('createProxy.subSystemHeading', 'Active to the following subsytems');
// UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab;
// General tab controls
private proxyNameTextBox: sqlops.InputBoxComponent;
private credentialNameTextBox: sqlops.InputBoxComponent;
private credentialNameDropDown: sqlops.DropDownComponent;
private descriptionTextBox: sqlops.InputBoxComponent;
private subsystemsTable: sqlops.TableComponent;
private subsystemCheckBox: sqlops.CheckBoxComponent;
private operatingSystemCheckBox: sqlops.CheckBoxComponent;
private replicationSnapshotCheckBox: sqlops.CheckBoxComponent;
private replicationTransactionLogCheckBox: sqlops.CheckBoxComponent;
private replicationDistributorCheckBox: sqlops.CheckBoxComponent;
private replicationMergeCheckbox: sqlops.CheckBoxComponent;
private replicationQueueReaderCheckbox: sqlops.CheckBoxComponent;
private sqlQueryCheckBox: sqlops.CheckBoxComponent;
private sqlCommandCheckBox: sqlops.CheckBoxComponent;
private sqlIntegrationServicesPackageCheckbox: sqlops.CheckBoxComponent;
private powershellCheckBox: sqlops.CheckBoxComponent;
constructor(ownerUri: string) {
super(ownerUri, new ProxyData(ownerUri), ProxyDialog.DialogTitle);
private credentials: sqlops.CredentialInfo[];
constructor(ownerUri: string, proxyInfo: sqlops.AgentProxyInfo = undefined, credentials: sqlops.CredentialInfo[]) {
super(
ownerUri,
new ProxyData(ownerUri, proxyInfo),
proxyInfo ? ProxyDialog.EditDialogTitle : ProxyDialog.CreateDialogTitle);
this.credentials = credentials;
}
protected async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog) {
this.generalTab = sqlops.window.modelviewdialog.createTab(ProxyDialog.GeneralTabText);
this.initializeGeneralTab();
this.dialog.content = [this.generalTab];
@@ -49,43 +77,143 @@ export class ProxyDialog extends AgentDialog<ProxyData> {
private initializeGeneralTab() {
this.generalTab.registerContent(async view => {
this.proxyNameTextBox = view.modelBuilder.inputBox().component();
this.proxyNameTextBox = view.modelBuilder.inputBox()
.withProperties({width: 420})
.component();
this.credentialNameTextBox = view.modelBuilder.inputBox().component();
this.descriptionTextBox = view.modelBuilder.inputBox().component();
this.subsystemsTable = view.modelBuilder.table()
this.credentialNameDropDown = view.modelBuilder.dropDown()
.withProperties({
columns: [
ProxyDialog.SubsystemNameColumnLabel
],
data: [],
height: 500
width: 432,
value: '',
editable: true,
values: this.credentials.length > 0 ? this.credentials.map(c => c.name) : ['']
})
.component();
this.descriptionTextBox = view.modelBuilder.inputBox()
.withProperties({
width: 420,
multiline: true,
height: 300
})
.component();
this.subsystemCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: ProxyDialog.SubsystemLabel
}).component();
this.subsystemCheckBox.onChanged(() => {
if (this.subsystemCheckBox.checked) {
this.operatingSystemCheckBox.checked = true;
this.replicationSnapshotCheckBox.checked = true;
this.replicationTransactionLogCheckBox.checked = true;
this.replicationDistributorCheckBox.checked = true;
this.replicationMergeCheckbox.checked = true;
this.replicationQueueReaderCheckbox.checked = true;
this.sqlQueryCheckBox.checked = true;
this.sqlCommandCheckBox.checked = true;
this.sqlIntegrationServicesPackageCheckbox.checked = true;
this.powershellCheckBox.checked = true;
} else {
this.operatingSystemCheckBox.checked = false;
this.replicationSnapshotCheckBox.checked = false;
this.replicationTransactionLogCheckBox.checked = false;
this.replicationDistributorCheckBox.checked = false;
this.replicationMergeCheckbox.checked = false;
this.replicationQueueReaderCheckbox.checked = false;
this.sqlQueryCheckBox.checked = false;
this.sqlCommandCheckBox.checked = false;
this.sqlIntegrationServicesPackageCheckbox.checked = false;
this.powershellCheckBox.checked = false;
}
});
this.operatingSystemCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: ProxyDialog.OperatingSystemLabel
}).component();
this.replicationSnapshotCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: ProxyDialog.ReplicationSnapshotLabel
}).component();
this.replicationTransactionLogCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: ProxyDialog.ReplicationTransactionLogLabel
}).component();
this.replicationDistributorCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: ProxyDialog.ReplicationDistributorLabel
}).component();
this.replicationMergeCheckbox = view.modelBuilder.checkBox()
.withProperties({
label: ProxyDialog.ReplicationMergeLabel
}).component();
this.replicationQueueReaderCheckbox = view.modelBuilder.checkBox()
.withProperties({
label: ProxyDialog.ReplicationQueueReaderLabel
}).component();
this.sqlQueryCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: ProxyDialog.SSASQueryLabel
}).component();
this.sqlCommandCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: ProxyDialog.SSASCommandLabel
}).component();
this.sqlIntegrationServicesPackageCheckbox = view.modelBuilder.checkBox()
.withProperties({
label: ProxyDialog.SSISPackageLabel
}).component();
this.powershellCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: ProxyDialog.PowerShellLabel
}).component();
let checkBoxContainer = view.modelBuilder.groupContainer()
.withItems([this.operatingSystemCheckBox, this.replicationSnapshotCheckBox,
this.replicationTransactionLogCheckBox, this.replicationDistributorCheckBox, this.replicationMergeCheckbox,
this.replicationQueueReaderCheckbox, this.sqlQueryCheckBox, this.sqlCommandCheckBox, this.sqlIntegrationServicesPackageCheckbox,
this.powershellCheckBox])
.component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.proxyNameTextBox,
title: ProxyDialog.ProxyNameTextBoxLabel
}, {
component: this.credentialNameTextBox,
component: this.credentialNameDropDown,
title: ProxyDialog.CredentialNameTextBoxLabel
}, {
component: this.descriptionTextBox,
title: ProxyDialog.DescriptionTextBoxLabel
}, {
component: this.subsystemsTable,
title: ProxyDialog.SubsystemsTableLabel
}]).withLayout({ width: '100%' }).component();
}]).withLayout({ width: 420 }).component();
await view.initializeModel(formModel);
this.proxyNameTextBox.value = this.model.accountName;
this.credentialNameDropDown.value = this.model.credentialName;
this.descriptionTextBox.value = this.model.description;
});
}
protected updateModel() {
this.model.accountName = this.proxyNameTextBox.value;
this.model.credentialName = this.credentialNameTextBox.value;
this.model.credentialName = this.credentialNameDropDown.value as string;
this.model.credentialId = this.credentials.find(
c => c.name === this.model.credentialName).id;
this.model.credentialIdentity = this.credentials.find(
c => c.name === this.model.credentialName).identity;
this.model.description = this.descriptionTextBox.value;
}
}

View File

@@ -4,17 +4,21 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { ScheduleData } from '../data/scheduleData';
const localize = nls.loadMessageBundle();
export class ScheduleDialog {
// Top level
private readonly DialogTitle: string = 'New Schedule';
private readonly OkButtonText: string = 'OK';
private readonly CancelButtonText: string = 'Cancel';
private readonly DialogTitle: string = localize('scheduleDialog.newSchedule', 'New Schedule');
private readonly OkButtonText: string = localize('scheduleDialog.ok', 'OK');
private readonly CancelButtonText: string = localize('scheduleDialog.cancel', 'Cancel');
private readonly ScheduleNameText: string = localize('scheduleDialog.scheduleName', 'Schedule Name');
private readonly SchedulesLabelText: string = localize('scheduleDialog.schedules', 'Schedules');
// UI Components
private dialog: sqlops.window.modelviewdialog.Dialog;
@@ -46,7 +50,7 @@ export class ScheduleDialog {
this.schedulesTable = view.modelBuilder.table()
.withProperties({
columns: [
'Schedule Name'
this.ScheduleNameText
],
data: [],
height: 600,
@@ -56,7 +60,7 @@ export class ScheduleDialog {
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.schedulesTable,
title: 'Schedules'
title: this.SchedulesLabelText
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);

View File

@@ -3,6 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { AlertDialog } from './dialogs/alertDialog';
@@ -12,6 +14,8 @@ import { ProxyDialog } from './dialogs/proxyDialog';
import { JobStepDialog } from './dialogs/jobStepDialog';
import { PickScheduleDialog } from './dialogs/pickScheduleDialog';
const localize = nls.loadMessageBundle();
/**
* The main controller class that initializes the extension
*/
@@ -23,12 +27,17 @@ export class MainController {
this._context = context;
}
public static showNotYetImplemented(): void {
vscode.window.showInformationMessage(
localize('mainController.notImplemented', "This feature is under development. Check-out the latest insiders build if you'd like to try out the most recent changes!"));
}
/**
* Activates the extension
*/
public activate(): void {
vscode.commands.registerCommand('agent.openCreateJobDialog', (ownerUri: string) => {
let dialog = new JobDialog(ownerUri);
vscode.commands.registerCommand('agent.openJobDialog', (ownerUri: string, jobInfo: sqlops.AgentJobInfo) => {
let dialog = new JobDialog(ownerUri, jobInfo);
dialog.openDialog();
});
vscode.commands.registerCommand('agent.openNewStepDialog', (ownerUri: string, jobId: string, server: string, stepId: number) => {
@@ -39,17 +48,19 @@ export class MainController {
let dialog = new PickScheduleDialog(ownerUri);
dialog.showDialog();
});
vscode.commands.registerCommand('agent.openAlertDialog', (ownerUri: string, alertInfo: sqlops.AgentAlertInfo) => {
let dialog = new AlertDialog(ownerUri, alertInfo);
vscode.commands.registerCommand('agent.openAlertDialog', (ownerUri: string, alertInfo: sqlops.AgentAlertInfo, jobs: string[]) => {
let dialog = new AlertDialog(ownerUri, alertInfo, jobs);
dialog.openDialog();
});
vscode.commands.registerCommand('agent.openCreateOperatorDialog', (ownerUri: string) => {
let dialog = new OperatorDialog(ownerUri);
vscode.commands.registerCommand('agent.openOperatorDialog', (ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo) => {
let dialog = new OperatorDialog(ownerUri, operatorInfo);
dialog.openDialog();
});
vscode.commands.registerCommand('agent.openCreateProxyDialog', (ownerUri: string) => {
let dialog = new ProxyDialog(ownerUri);
dialog.openDialog();
vscode.commands.registerCommand('agent.openProxyDialog', (ownerUri: string, proxyInfo: sqlops.AgentProxyInfo, credentials: sqlops.CredentialInfo[]) => {
//@TODO: reenable create proxy after snapping July release (7/14/18)
// let dialog = new ProxyDialog(ownerUri, proxyInfo, credentials);
// dialog.openDialog();
MainController.showNotYetImplemented();
});
}

View File

@@ -15,7 +15,7 @@ const testOwnerUri = 'agent://testuri';
suite('Agent extension', () => {
test('Create Job Data', async () => {
let testAgentService = new TestAgentService();
let data = new JobData(testOwnerUri, testAgentService);
let data = new JobData(testOwnerUri, undefined, testAgentService);
data.save();
});
});

View File

@@ -86,6 +86,11 @@ export class TestAgentService implements sqlops.AgentServicesProvider {
return undefined;
}
// Agent Credential method
getCredentials(ownerUri: string): Thenable<sqlops.GetCredentialsResult> {
return undefined;
}
// Job Schedule management methods
getJobSchedules(ownerUri: string): Thenable<sqlops.AgentJobSchedulesResult> {
return undefined;

View File

@@ -1,6 +1,6 @@
{
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "1.5.0-alpha.4",
"version": "1.5.0-alpha.9",
"downloadFileNames": {
"Windows_86": "win-x86-netcoreapp2.1.zip",
"Windows_64": "win-x64-netcoreapp2.1.zip",

View File

@@ -148,6 +148,11 @@ export interface DeleteAgentProxyParams {
proxy: sqlops.AgentProxyInfo;
}
// Agent Credentials parameters
export interface GetCredentialsParams {
ownerUri: string;
}
// Job Schedule management parameters
export interface AgentJobScheduleParams {
ownerUri: string;
@@ -262,6 +267,11 @@ export namespace DeleteAgentProxyRequest {
export const type = new RequestType<DeleteAgentProxyParams, sqlops.ResultStatus, void, void>('agent/deleteproxy');
}
// Agent Credentials request
export namespace AgentCredentialsRequest {
export const type = new RequestType<GetCredentialsParams, sqlops.GetCredentialsResult, void, void>('security/credentials');
}
// Job Schedules requests
export namespace AgentJobSchedulesRequest {
export const type = new RequestType<AgentJobScheduleParams, sqlops.AgentJobSchedulesResult, void, void>('agent/schedules');

View File

@@ -438,6 +438,22 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
);
};
// Agent Credential Method
let getCredentials = (ownerUri: string): Thenable<sqlops.GetCredentialsResult> => {
let params: contracts.GetCredentialsParams = {
ownerUri: ownerUri
};
let requestType = contracts.AgentCredentialsRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
}
);
};
// Job Schedule management methods
let getJobSchedules = (ownerUri: string): Thenable<sqlops.AgentJobSchedulesResult> => {
let params: contracts.AgentJobScheduleParams = {
@@ -532,6 +548,7 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
createProxy,
updateProxy,
deleteProxy,
getCredentials,
getJobSchedules,
createJobSchedule,
updateJobSchedule,

View File

@@ -44,22 +44,6 @@ export class Telemetry {
private static platformInformation: PlatformInformation;
private static disabled: boolean;
// Get the unique ID for the current user of the extension
public static getUserId(): Promise<string> {
return new Promise<string>(resolve => {
// Generate the user id if it has not been created already
if (typeof this.userId === 'undefined') {
let id = Utils.generateUserId();
id.then(newId => {
this.userId = newId;
resolve(this.userId);
});
} else {
resolve(this.userId);
}
});
}
public static getPlatformInformation(): Promise<PlatformInformation> {
if (this.platformInformation) {
return Promise.resolve(this.platformInformation);
@@ -143,8 +127,7 @@ export class Telemetry {
}
// Augment the properties structure with additional common properties before sending
Promise.all([this.getUserId(), this.getPlatformInformation()]).then(() => {
properties['userId'] = this.userId;
Promise.all([this.getPlatformInformation()]).then(() => {
properties['distribution'] = (this.platformInformation && this.platformInformation.distribution) ?
`${this.platformInformation.distribution.name}, ${this.platformInformation.distribution.version}` : '';

View File

@@ -1,6 +1,6 @@
{
"name": "sqlops",
"version": "0.31.2",
"version": "0.31.3",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": {
"name": "Microsoft Corporation"
@@ -60,7 +60,7 @@
"reflect-metadata": "^0.1.8",
"rxjs": "5.4.0",
"semver": "4.3.6",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.22",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.23",
"spdlog": "0.6.0",
"sudo-prompt": "^8.0.0",
"svg.js": "^2.2.5",

View File

@@ -285,7 +285,6 @@ export default class MainController implements vscode.Disposable {
tab1.registerContent(async (view) => {
await this.getTabContent(view, customButton1, customButton2, 400);
});
sqlops.window.modelviewdialog.openDialog(dialog);
}

View File

@@ -63,13 +63,13 @@ export class InputBox extends vsInputBox {
this.enabledInputBorder = this.inputBorder;
this.disabledInputBackground = styles.disabledInputBackground;
this.disabledInputForeground = styles.disabledInputForeground;
this.updateInputEnabledDisabledColors();
this.applyStyles();
}
public enable(): void {
super.enable();
this.inputBackground = this.enabledInputBackground;
this.inputForeground = this.enabledInputForeground;
this.inputBorder = this.enabledInputBorder;
this.updateInputEnabledDisabledColors();
this.applyStyles();
}
@@ -89,9 +89,7 @@ export class InputBox extends vsInputBox {
public disable(): void {
super.disable();
this.inputBackground = this.disabledInputBackground;
this.inputForeground = this.disabledInputForeground;
this.inputBorder = this.disabledInputBorder;
this.updateInputEnabledDisabledColors();
this.applyStyles();
}
@@ -121,4 +119,11 @@ export class InputBox extends vsInputBox {
super.showMessage(message, force);
}
}
private updateInputEnabledDisabledColors(): void {
let enabled = this.isEnabled();
this.inputBackground = enabled ? this.enabledInputBackground : this.disabledInputBackground;
this.inputForeground = enabled ? this.enabledInputForeground : this.disabledInputForeground;
this.inputBorder = enabled ? this.enabledInputBorder : this.disabledInputBorder;
}
}

View File

@@ -17,7 +17,7 @@ export function appendRow(container: Builder, label: string, labelClass: string,
container.element('tr', {}, (rowContainer) => {
rowContainer.element('td', { class: labelClass }, (labelCellContainer) => {
labelCellContainer.div({}, (labelContainer) => {
labelContainer.innerHtml(label);
labelContainer.text(label);
});
});
rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => {

View File

@@ -170,7 +170,7 @@ export abstract class Modal extends Disposable implements IThemable {
}
modalHeader.div({ class: 'modal-title' }, (modalTitle) => {
this._modalTitle = modalTitle;
modalTitle.innerHtml(this._title);
modalTitle.text(this._title);
});
});
parts.push(this._modalHeaderSection.getHTMLElement());
@@ -433,7 +433,7 @@ export abstract class Modal extends Disposable implements IThemable {
*/
protected set title(title: string) {
if (this._title !== undefined) {
this._modalTitle.innerHtml(title);
this._modalTitle.text(title);
}
}

View File

@@ -5,8 +5,9 @@
import { Component, Input, ContentChild, OnDestroy, TemplateRef, ChangeDetectorRef, forwardRef, Inject } from '@angular/core';
import { Action } from 'vs/base/common/actions';
import { Disposable } from 'vs/base/common/lifecycle';
export abstract class TabChild {
export abstract class TabChild extends Disposable {
public abstract layout(): void;
}
@@ -19,7 +20,7 @@ export abstract class TabChild {
`
})
export class TabComponent implements OnDestroy {
@ContentChild(TabChild) private _child: TabChild;
private _child: TabChild;
@ContentChild(TemplateRef) templateRef;
@Input() public title: string;
@Input() public canClose: boolean;
@@ -29,19 +30,30 @@ export class TabComponent implements OnDestroy {
@Input() public identifier: string;
@Input() private visibilityType: 'if' | 'visibility' = 'if';
private rendered = false;
private destroyed: boolean = false;
@ContentChild(TabChild) private set child(tab: TabChild) {
this._child = tab;
if (this.active && this._child) {
this._child.layout();
}
}
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef
) { }
public set active(val: boolean) {
this._active = val;
if (this.active) {
this.rendered = true;
}
this._cd.detectChanges();
if (this.active && this._child) {
this._child.layout();
if (!this.destroyed) {
this._active = val;
if (this.active) {
this.rendered = true;
}
this._cd.detectChanges();
if (this.active && this._child) {
this._child.layout();
}
}
}
@@ -50,6 +62,7 @@ export class TabComponent implements OnDestroy {
}
ngOnDestroy() {
this.destroyed = true;
if (this.actions && this.actions.length > 0) {
this.actions.forEach((action) => action.dispose());
}
@@ -74,6 +87,8 @@ export class TabComponent implements OnDestroy {
}
public layout() {
this._child.layout();
if (this._child) {
this._child.layout();
}
}
}

View File

@@ -84,7 +84,7 @@
{
border: 1px solid #BFBDBD;
font-size: 8pt;
height: 400px;
height: 250px;
margin-top: 6px;
overflow: scroll;
padding: 4px;

View File

@@ -13,3 +13,6 @@ export const SerializationDisabled = 'Saving results into different format disab
*/
export const RestoreFeatureName = 'restore';
export const BackupFeatureName = 'backup';
export const MssqlProviderId = 'MSSQL';

View File

@@ -81,6 +81,15 @@
content: url("status_info.svg");
}
.vs .icon.help {
content: url("help.svg");
}
.vs-dark .icon.help,
.hc-black .icon.help {
content: url("help_inverse.svg");
}
.vs .icon.success,
.vs-dark .icon.success,
.hc-black .icon.success {

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>help_16x16</title><path d="M10.13.45A8.14,8.14,0,0,1,12,1.23a8,8,0,0,1,1.62,1.21A7.8,7.8,0,0,1,14.91,4a7.59,7.59,0,0,1,.81,1.85,7.6,7.6,0,0,1,0,4.12,7.59,7.59,0,0,1-.81,1.85,7.8,7.8,0,0,1-1.25,1.57A8,8,0,0,1,12,14.62a8.2,8.2,0,0,1-8.08,0,8,8,0,0,1-1.62-1.21,7.8,7.8,0,0,1-1.25-1.57A7.63,7.63,0,0,1,.28,10a7.6,7.6,0,0,1,0-4.12A7.63,7.63,0,0,1,1.09,4,7.8,7.8,0,0,1,2.34,2.44,8,8,0,0,1,4,1.23,8.26,8.26,0,0,1,10.13.45Zm-.27,14a7.17,7.17,0,0,0,1.67-.69,7,7,0,0,0,1.41-1.06,6.75,6.75,0,0,0,1.8-3,6.59,6.59,0,0,0,0-3.6,6.75,6.75,0,0,0-1.8-3,7,7,0,0,0-1.41-1.06,7.17,7.17,0,0,0-1.67-.69,7.21,7.21,0,0,0-3.72,0,7.17,7.17,0,0,0-1.67.69A7,7,0,0,0,3.05,3.13,6.82,6.82,0,0,0,2,4.5a6.72,6.72,0,0,0-.71,1.62,6.59,6.59,0,0,0,0,3.6A6.72,6.72,0,0,0,2,11.35a6.82,6.82,0,0,0,1.09,1.37,7,7,0,0,0,1.41,1.06,7.17,7.17,0,0,0,1.67.69,7.21,7.21,0,0,0,3.72,0ZM9,4a2.75,2.75,0,0,1,.85.56,2.61,2.61,0,0,1,.57.82,2.44,2.44,0,0,1,.21,1,2.08,2.08,0,0,1-.16.85,2.82,2.82,0,0,1-.4.65,4.28,4.28,0,0,1-.51.53c-.18.16-.36.32-.51.48a2.55,2.55,0,0,0-.4.51,1.19,1.19,0,0,0-.16.61v.52H7.46V10a2.09,2.09,0,0,1,.16-.85A2.86,2.86,0,0,1,8,8.5,4.28,4.28,0,0,1,8.54,8c.18-.16.36-.32.51-.48A2.51,2.51,0,0,0,9.45,7a1.18,1.18,0,0,0,.16-.61,1.49,1.49,0,0,0-.13-.61,1.56,1.56,0,0,0-.34-.49A1.63,1.63,0,0,0,8,4.81a1.63,1.63,0,0,0-1.14.45,1.57,1.57,0,0,0-.34.49,1.49,1.49,0,0,0-.13.61H5.32a2.41,2.41,0,0,1,.21-1,2.68,2.68,0,0,1,.58-.82A2.77,2.77,0,0,1,7,4a2.62,2.62,0,0,1,1-.21A2.65,2.65,0,0,1,9,4ZM8.54,12.6H7.46v-1H8.54Z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>help_inverse_16x16</title><path class="cls-1" d="M10.13.45A8.14,8.14,0,0,1,12,1.23a8,8,0,0,1,1.62,1.21A7.8,7.8,0,0,1,14.91,4a7.59,7.59,0,0,1,.81,1.85,7.6,7.6,0,0,1,0,4.12,7.59,7.59,0,0,1-.81,1.85,7.8,7.8,0,0,1-1.25,1.57A8,8,0,0,1,12,14.62a8.2,8.2,0,0,1-8.08,0,8,8,0,0,1-1.62-1.21,7.8,7.8,0,0,1-1.25-1.57A7.63,7.63,0,0,1,.28,10a7.6,7.6,0,0,1,0-4.12A7.63,7.63,0,0,1,1.09,4,7.8,7.8,0,0,1,2.34,2.44,8,8,0,0,1,4,1.23,8.26,8.26,0,0,1,10.13.45Zm-.27,14a7.17,7.17,0,0,0,1.67-.69,7,7,0,0,0,1.41-1.06,6.75,6.75,0,0,0,1.8-3,6.59,6.59,0,0,0,0-3.6,6.75,6.75,0,0,0-1.8-3,7,7,0,0,0-1.41-1.06,7.17,7.17,0,0,0-1.67-.69,7.21,7.21,0,0,0-3.72,0,7.17,7.17,0,0,0-1.67.69A7,7,0,0,0,3.05,3.13,6.82,6.82,0,0,0,2,4.5a6.72,6.72,0,0,0-.71,1.62,6.59,6.59,0,0,0,0,3.6A6.72,6.72,0,0,0,2,11.35a6.82,6.82,0,0,0,1.09,1.37,7,7,0,0,0,1.41,1.06,7.17,7.17,0,0,0,1.67.69,7.21,7.21,0,0,0,3.72,0ZM9,4a2.75,2.75,0,0,1,.85.56,2.61,2.61,0,0,1,.57.82,2.44,2.44,0,0,1,.21,1,2.08,2.08,0,0,1-.16.85,2.82,2.82,0,0,1-.4.65,4.28,4.28,0,0,1-.51.53c-.18.16-.36.32-.51.48a2.55,2.55,0,0,0-.4.51,1.19,1.19,0,0,0-.16.61v.52H7.46V10a2.09,2.09,0,0,1,.16-.85A2.86,2.86,0,0,1,8,8.5,4.28,4.28,0,0,1,8.54,8c.18-.16.36-.32.51-.48A2.51,2.51,0,0,0,9.45,7a1.18,1.18,0,0,0,.16-.61,1.49,1.49,0,0,0-.13-.61,1.56,1.56,0,0,0-.34-.49A1.63,1.63,0,0,0,8,4.81a1.63,1.63,0,0,0-1.14.45,1.57,1.57,0,0,0-.34.49,1.49,1.49,0,0,0-.13.61H5.32a2.41,2.41,0,0,1,.21-1,2.68,2.68,0,0,1,.58-.82A2.77,2.77,0,0,1,7,4a2.62,2.62,0,0,1,1-.21A2.65,2.65,0,0,1,9,4ZM8.54,12.6H7.46v-1H8.54Z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -131,7 +131,7 @@ export class AccountDialog extends Modal {
this._noaccountViewContainer = DOM.$('div.no-account-view');
let noAccountTitle = DOM.append(this._noaccountViewContainer, DOM.$('.no-account-view-label'));
let noAccountLabel = localize('accountDialog.noAccountLabel', 'There is no linked account. Please add an account.');
noAccountTitle.innerHTML = noAccountLabel;
noAccountTitle.innerText = noAccountLabel;
// Show the add account button for the first provider
// Todo: If we have more than 1 provider, need to show all add account buttons for all providers

View File

@@ -8,7 +8,7 @@
import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IConnectionProfile } from 'sqlops';
export class ConnectionContextkey implements IContextKey<IConnectionProfile> {
export class ConnectionContextKey implements IContextKey<IConnectionProfile> {
static Provider = new RawContextKey<string>('connectionProvider', undefined);
static Server = new RawContextKey<string>('serverName', undefined);
@@ -23,10 +23,10 @@ export class ConnectionContextkey implements IContextKey<IConnectionProfile> {
constructor(
@IContextKeyService contextKeyService: IContextKeyService
) {
this._providerKey = ConnectionContextkey.Provider.bindTo(contextKeyService);
this._serverKey = ConnectionContextkey.Server.bindTo(contextKeyService);
this._databaseKey = ConnectionContextkey.Database.bindTo(contextKeyService);
this._connectionKey = ConnectionContextkey.Connection.bindTo(contextKeyService);
this._providerKey = ConnectionContextKey.Provider.bindTo(contextKeyService);
this._serverKey = ConnectionContextKey.Server.bindTo(contextKeyService);
this._databaseKey = ConnectionContextKey.Database.bindTo(contextKeyService);
this._connectionKey = ConnectionContextKey.Connection.bindTo(contextKeyService);
}
set(value: IConnectionProfile) {

View File

@@ -174,7 +174,7 @@ export interface IConnectionManagementService {
disconnectEditor(owner: IConnectableInput, force?: boolean): Promise<boolean>;
disconnect(connection: ConnectionProfile): Promise<void>;
disconnect(connection: IConnectionProfile): Promise<void>;
disconnect(ownerUri: string): Promise<void>;
@@ -208,7 +208,7 @@ export interface IConnectionManagementService {
*/
cancelEditorConnection(owner: IConnectableInput): Thenable<boolean>;
showDashboard(connection: ConnectionProfile): Thenable<boolean>;
showDashboard(connection: IConnectionProfile): Thenable<boolean>;
closeDashboard(uri: string): void;
@@ -216,7 +216,7 @@ export interface IConnectionManagementService {
hasRegisteredServers(): boolean;
canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean;
canChangeConnectionConfig(profile: IConnectionProfile, newGroupID: string): boolean;
getTabColorForUri(uri: string): string;

View File

@@ -553,7 +553,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
});
}
public showDashboard(connection: ConnectionProfile): Thenable<boolean> {
public showDashboard(connection: IConnectionProfile): Thenable<boolean> {
return this.showDashboardForConnectionManagementInfo(connection);
}

View File

@@ -240,7 +240,7 @@ export class ConnectionStore {
return connectionProfile;
} else {
return undefined;
};
}
});
}
@@ -352,7 +352,7 @@ export class ConnectionStore {
list.unshift(savedProfile);
let newList = list.map(c => {
let connectionProfile = c ? c.toIConnectionProfile() : undefined;;
let connectionProfile = c ? c.toIConnectionProfile() : undefined;
return connectionProfile;
});
return newList.filter(n => n !== undefined);
@@ -372,7 +372,7 @@ export class ConnectionStore {
});
let newList = list.map(c => {
let connectionProfile = c ? c.toIConnectionProfile() : undefined;;
let connectionProfile = c ? c.toIConnectionProfile() : undefined;
return connectionProfile;
});
return newList.filter(n => n !== undefined);

View File

@@ -339,9 +339,9 @@ export class ConnectionDialogService implements IConnectionDialogService {
if (!platform.isWindows && types.isString(message) && message.toLowerCase().includes('kerberos') && message.toLowerCase().includes('kinit')) {
message = [
localize('kerberosErrorStart', "Connection failed due to Kerberos error."),
localize('kerberosHelpLink', "&nbsp;Help configuring Kerberos is available at ") + helpLink,
localize('kerberosKinit', "&nbsp;If you have previously connected you may need to re-run kinit.")
].join('<br/>');
localize('kerberosHelpLink', "Help configuring Kerberos is available at {0}", helpLink),
localize('kerberosKinit', "If you have previously connected you may need to re-run kinit.")
].join('\r\n');
actions.push(new Action('Kinit', 'Run kinit', null, true, () => {
this._connectionDialog.close();
this._clipboardService.writeText('kinit\r');

View File

@@ -32,22 +32,10 @@ export abstract class DashboardTab extends TabChild implements OnDestroy {
public enableEdit(): void {
// no op
}
private _toDispose: IDisposable[] = [];
constructor() {
super();
}
public dispose(): void {
this._toDispose = dispose(this._toDispose);
}
protected _register<T extends IDisposable>(t: T): T {
this._toDispose.push(t);
return t;
}
ngOnDestroy() {
this.dispose();
}

View File

@@ -40,7 +40,7 @@ export class DashboardErrorContainer extends DashboardTab implements AfterViewIn
ngAfterViewInit() {
let errorMessage = this._errorMessageContainer.nativeElement as HTMLElement;
errorMessage.innerHTML = nls.localize('dashboardNavSection_loadTabError', 'The "{0}" section has invalid content. Please contact extension owner.', this.tab.title);
errorMessage.innerText = nls.localize('dashboardNavSection_loadTabError', 'The "{0}" section has invalid content. Please contact extension owner.', this.tab.title);
}
public get id(): string {

View File

@@ -18,7 +18,7 @@ import { DashboardModule } from './dashboard.module';
import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component';
import { ConnectionContextkey } from 'sql/parts/connection/common/connectionContextKey';
import { ConnectionContextKey } from 'sql/parts/connection/common/connectionContextKey';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
@@ -71,6 +71,7 @@ export class DashboardEditor extends BaseEditor {
* To be called when the container of this editor changes size.
*/
public layout(dimension: DOM.Dimension): void {
this._dashboardService.layout(dimension);
}
public setInput(input: DashboardInput, options: EditorOptions): TPromise<void> {
@@ -110,7 +111,7 @@ export class DashboardEditor extends BaseEditor {
let serverInfo = this._connMan.getConnectionInfo(this.input.uri).serverInfo;
this._dashboardService.changeToDashboard({ profile, serverInfo });
let scopedContextService = this._contextKeyService.createScoped(input.container);
let connectionContextKey = new ConnectionContextkey(scopedContextService);
let connectionContextKey = new ConnectionContextKey(scopedContextService);
connectionContextKey.set(input.connectionProfile);
let params: IDashboardComponentParams = {

View File

@@ -15,7 +15,6 @@ import {
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import * as Constants from 'sql/parts/connection/common/constants';
import { OEAction } from 'sql/parts/objectExplorer/viewlet/objectExplorerActions';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { IScriptingService } from 'sql/services/scripting/scriptingService';

View File

@@ -24,6 +24,7 @@ import { error } from 'sql/base/common/log';
import { clone, mixin } from 'sql/base/common/objects';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { escape } from 'sql/base/common/strings';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
@@ -367,11 +368,12 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
columnDefinitions: resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson;
let linkType = c.isXml ? 'xml' : 'json';
return {
id: i.toString(),
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan'
: c.columnName,
: escape(c.columnName),
type: self.stringToFieldType('string'),
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined,

View File

@@ -348,11 +348,12 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
columnDefinitions: resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson;
let linkType = c.isXml ? 'xml' : 'json';
return {
id: i.toString(),
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan'
: c.columnName,
: escape(c.columnName),
type: self.stringToFieldType('string'),
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined

View File

@@ -7,18 +7,11 @@ import 'vs/css!../common/media/jobs';
import 'sql/parts/dashboard/common/dashboardPanelStyles';
import * as nls from 'vs/nls';
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable } from '@angular/core';
import * as Utils from 'sql/parts/connection/common/utils';
import { RefreshWidgetAction, EditDashboardAction } from 'sql/parts/dashboard/common/actions';
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as themeColors from 'vs/workbench/common/theme';
import { DashboardPage } from 'sql/parts/dashboard/common/dashboardPage.component';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { AgentJobInfo, AgentJobHistoryInfo } from 'sqlops';
import { Component, Inject, forwardRef, ChangeDetectorRef, ViewChild, Injectable } from '@angular/core';
import { AgentJobInfo } from 'sqlops';
import { PanelComponent, IPanelOptions, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component';
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
export const DASHBOARD_SELECTOR: string = 'agentview-component';
@@ -32,12 +25,6 @@ export class AgentViewComponent {
@ViewChild(PanelComponent) private _panel: PanelComponent;
// tslint:disable:no-unused-variable
private readonly jobsComponentTitle: string = nls.localize('jobview.Jobs', "Jobs");
private readonly alertsComponentTitle: string = nls.localize('jobview.Alerts', "Alerts");
private readonly proxiesComponentTitle: string = nls.localize('jobview.Proxies', "Proxies");
private readonly operatorsComponentTitle: string = nls.localize('jobview.Operators', "Operators");
private _showHistory: boolean = false;
private _jobId: string = null;
private _agentJobInfo: AgentJobInfo = null;
@@ -49,6 +36,11 @@ export class AgentViewComponent {
public proxiesIconClass: string = 'proxiesview-icon';
public operatorsIconClass: string = 'operatorsview-icon';
private readonly jobsComponentTitle: string = nls.localize('jobview.Jobs', "Jobs");
private readonly alertsComponentTitle: string = nls.localize('jobview.Alerts', "Alerts");
private readonly proxiesComponentTitle: string = nls.localize('jobview.Proxies', "Proxies");
private readonly operatorsComponentTitle: string = nls.localize('jobview.Operators', "Operators");
// tslint:disable-next-line:no-unused-variable
private readonly panelOpt: IPanelOptions = {
showTabsWhenOne: true,
@@ -58,7 +50,8 @@ export class AgentViewComponent {
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
@Inject(IJobManagementService) jobManagementService: IJobManagementService) {
@Inject(IJobManagementService) jobManagementService: IJobManagementService,
@Inject(IDashboardService) dashboardService: IDashboardService,) {
this._expanded = new Map<string, string>();
let self = this;

View File

@@ -34,6 +34,8 @@ export interface IJobManagementService {
getProxies(connectionUri: string): Thenable<sqlops.AgentProxiesResult>;
deleteProxy(connectionUri: string, proxy: sqlops.AgentProxyInfo): Thenable<sqlops.ResultStatus>;
getCredentials(connectionUri: string): Thenable<sqlops.GetCredentialsResult>;
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus>;
addToCache(server: string, cache: JobCacheObject);
jobCacheObjectMap: { [server: string]: JobCacheObject; };

View File

@@ -145,11 +145,17 @@ export class EditJobAction extends Action {
public static ID = 'jobaction.editJob';
public static LABEL = nls.localize('jobaction.editJob', "Edit Job");
constructor() {
constructor(
@ICommandService private _commandService: ICommandService
) {
super(EditJobAction.ID, EditJobAction.LABEL);
}
public run(actionInfo: IJobActionInfo): TPromise<boolean> {
this._commandService.executeCommand(
'agent.openJobDialog',
actionInfo.ownerUri,
actionInfo.targetObject);
return TPromise.as(true);
}
}
@@ -324,11 +330,17 @@ export class EditOperatorAction extends Action {
public static ID = 'jobaction.editAlert';
public static LABEL = nls.localize('jobaction.editOperator', "Edit Operator");
constructor() {
constructor(
@ICommandService private _commandService: ICommandService
) {
super(EditOperatorAction.ID, EditOperatorAction.LABEL);
}
public run(info: any): TPromise<boolean> {
public run(actionInfo: IJobActionInfo): TPromise<boolean> {
this._commandService.executeCommand(
'agent.openOperatorDialog',
actionInfo.ownerUri,
actionInfo.targetObject);
return TPromise.as(true);
}
}
@@ -398,11 +410,17 @@ export class EditProxyAction extends Action {
public static ID = 'jobaction.editProxy';
public static LABEL = nls.localize('jobaction.editProxy', "Edit Proxy");
constructor() {
constructor(
@ICommandService private _commandService: ICommandService
) {
super(EditProxyAction.ID, EditProxyAction.LABEL);
}
public run(info: any): TPromise<boolean> {
public run(actionInfo: IJobActionInfo): TPromise<boolean> {
this._commandService.executeCommand(
'agent.openProxyDialog',
actionInfo.ownerUri,
actionInfo.targetObject);
return TPromise.as(true);
}
}

View File

@@ -78,6 +78,12 @@ export class JobManagementService implements IJobManagementService {
});
}
public getCredentials(connectionUri: string): Thenable<sqlops.GetCredentialsResult> {
return this._runAction(connectionUri, (runner) => {
return runner.getCredentials(connectionUri);
});
}
public getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
return this._runAction(connectionUri, (runner) => {
return runner.getJobHistory(connectionUri, jobID);

View File

@@ -32,23 +32,11 @@ jobhistory-component {
}
.jobview-grid {
height: 94.7%;
height: calc(100% - 75px);
width : 100%;
display: block;
}
.vs-dark #jobsDiv .slick-header-column {
background: #333333 !important;
}
#jobsDiv .slick-header-column {
background-color: transparent !important;
background: white !important;
border: 0px !important;
font-weight: bold;
font-size: larger;
}
.vs-dark #agentViewDiv .slick-header-column {
background: #333333 !important;
}
@@ -58,7 +46,6 @@ jobhistory-component {
background: white !important;
border: 0px !important;
font-weight: bold;
font-size: larger;
}
.vs-dark #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
@@ -164,7 +151,7 @@ jobhistory-component {
}
#jobsDiv .preload {
font-size: 18px;
font-size: 13px;
}
#jobsDiv .dynamic-cell-detail > :first-child {
@@ -299,19 +286,20 @@ table.jobprevruns > tbody {
#alertsDiv .jobalertsview-grid {
height: 94.7%;
height: calc(100% - 75px);
width : 100%;
display: block;
}
#operatorsDiv .joboperatorsview-grid {
height: 94.7%;
height: calc(100% - 75px);
width : 100%;
display: block;
overflow: scroll;
}
#proxiesDiv .jobproxiesview-grid {
height: 94.7%;
height: calc(100% - 75px);
width : 100%;
display: block;
}
@@ -339,4 +327,97 @@ jobsview-component .actionbar-container {
jobsview-component .jobview-grid .slick-cell.error-row {
opacity: 0;
}
}
.vs-dark #alertsDiv jobalertsview-component .jobalertsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background:#333333;
}
#alertsDiv jobalertsview-component .jobalertsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background: white;
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
}
#alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
#alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
#alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
background: #dcdcdc !important;
}
.vs-dark #alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.vs-dark #alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.vs-dark #alertsDiv .jobalertsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
background: #444444 !important;
}
.vs-dark .jobalertsview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
border-left: 1px dotted white;
}
.jobalertsview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
border-left: 1px dotted #444444;
}
.vs-dark #operatorsDiv joboperatorsview-component .joboperatorsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background:#333333;
}
#operatorsDiv joboperatorsview-component .joboperatorsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background: white;
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
}
#operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
#operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
#operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
background: #dcdcdc !important;
}
.vs-dark #operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.vs-dark #operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.vs-dark #operatorsDiv .joboperatorsview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
background: #444444 !important;
}
.vs-dark .joboperatorsview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
border-left: 1px dotted white;
}
.joboperatorsview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
border-left: 1px dotted #444444;
}
.vs-dark #proxiesDiv jobproxiesview-component .jobproxiesview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background:#333333;
}
#proxiesDiv jobproxiesview-component .jobproxiesview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
background: white;
border-right: transparent !important;
border-left: transparent !important;
line-height: 33px !important;
}
#proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
#proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
#proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
background: #dcdcdc !important;
}
.vs-dark #proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
.vs-dark #proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
.vs-dark #proxiesDiv .jobproxiesview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
background: #444444 !important;
}
.vs-dark .jobproxiesview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
border-left: 1px dotted white;
}
.jobproxiesview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
border-left: 1px dotted #444444;
}

View File

@@ -12,4 +12,4 @@
<div #actionbarContainer class="actionbar-container"></div>
<div #jobalertsgrid class="jobview-grid"></div>
<div #jobalertsgrid class="jobalertsview-grid"></div>

View File

@@ -29,9 +29,10 @@ import { IAction } from 'vs/base/common/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
export const VIEW_SELECTOR: string = 'jobalertsview-component';
export const ROW_HEIGHT: number = 45;
export const ROW_HEIGHT: number = 30;
@Component({
selector: VIEW_SELECTOR,
@@ -73,8 +74,9 @@ export class AlertsViewComponent extends JobManagementView implements OnInit {
@Inject(IInstantiationService) instantiationService: IInstantiationService,
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
@Inject(IKeybindingService) keybindingService: IKeybindingService) {
super(commonService, contextMenuService, keybindingService, instantiationService);
@Inject(IKeybindingService) keybindingService: IKeybindingService,
@Inject(IDashboardService) _dashboardService: IDashboardService) {
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService);
this._isCloud = commonService.connectionManagementService.connectionInfo.serverInfo.isCloud;
}
@@ -85,7 +87,14 @@ export class AlertsViewComponent extends JobManagementView implements OnInit {
}
public layout() {
this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement)));
let height = dom.getContentHeight(this._gridEl.nativeElement) - 10;
if (height < 0) {
height = 0;
}
this._table.layout(new dom.Dimension(
dom.getContentWidth(this._gridEl.nativeElement),
height));
}
onFirstVisible() {
@@ -94,13 +103,6 @@ export class AlertsViewComponent extends JobManagementView implements OnInit {
column.rerenderOnResize = true;
return column;
});
let options = <Slick.GridOptions<any>>{
syncColumnCellResize: true,
enableColumnReorder: false,
rowHeight: ROW_HEIGHT,
enableCellNavigation: true,
forceFitColumns: true
};
this.dataView = new Slick.Data.DataView();
@@ -164,7 +166,17 @@ export class AlertsViewComponent extends JobManagementView implements OnInit {
public openCreateAlertDialog() {
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
this._commandService.executeCommand('agent.openAlertDialog', ownerUri);
this._jobManagementService.getJobs(ownerUri).then((result) => {
if (result && result.jobs.length > 0) {
let jobs = [];
result.jobs.forEach(job => {
jobs.push(job.name);
});
this._commandService.executeCommand('agent.openAlertDialog', ownerUri, null, jobs);
} else {
this._commandService.executeCommand('agent.openAlertDialog', ownerUri, null, null);
}
});
}
private refreshJobs() {

View File

@@ -154,8 +154,9 @@
</td>
</tr>
</table>
<jobstepsview-component *ngIf="showSteps === true"></jobstepsview-component>
<div #jobsteps>
<jobstepsview-component *ngIf="showSteps === true"></jobstepsview-component>
</div>
<h3 *ngIf="showSteps === false">No Steps Available</h3>
</div>
</div>

View File

@@ -7,6 +7,7 @@ import 'vs/css!./jobHistory';
import 'vs/css!sql/media/icons/common-icons';
import * as sqlops from 'sqlops';
import * as dom from 'vs/base/browser/dom';
import { OnInit, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
@@ -22,30 +23,33 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Disposable } from 'vs/base/common/lifecycle';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { JobManagementView } from 'sql/parts/jobManagement/views/jobManagementView';
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';
export const DASHBOARD_SELECTOR: string = 'jobhistory-component';
@Component({
selector: DASHBOARD_SELECTOR,
templateUrl: decodeURI(require.toUrl('./jobHistory.component.html')),
providers: [{ provide: TabChild, useExisting: forwardRef(() => JobHistoryComponent) }],
changeDetection: ChangeDetectionStrategy.OnPush
})
@Injectable()
export class JobHistoryComponent extends Disposable implements OnInit {
export class JobHistoryComponent extends JobManagementView implements OnInit {
private _tree: Tree;
private _treeController: JobHistoryController;
private _treeDataSource: JobHistoryDataSource;
private _treeRenderer: JobHistoryRenderer;
private _treeFilter: JobHistoryFilter;
private _actionBar: Taskbar;
@ViewChild('table') private _tableContainer: ElementRef;
@ViewChild('actionbarContainer') private _actionbarContainer: ElementRef;
@ViewChild('jobsteps') private _jobStepsView: ElementRef;
@Input() public agentJobInfo: sqlops.AgentJobInfo = undefined;
@Input() public agentJobHistories: sqlops.AgentJobHistoryInfo[] = undefined;
@@ -67,21 +71,23 @@ export class JobHistoryComponent extends Disposable implements OnInit {
constructor(
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
@Inject(INotificationService) private _notificationService: INotificationService,
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
@Inject(IKeybindingService) keybindingService: IKeybindingService,
@Inject(IDashboardService) dashboardService: IDashboardService
) {
super();
super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService);
this._treeController = new JobHistoryController();
this._treeDataSource = new JobHistoryDataSource();
this._treeRenderer = new JobHistoryRenderer();
this._treeFilter = new JobHistoryFilter();
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
this._serverName = commonService.connectionManagementService.connectionInfo.connectionProfile.serverName;
let jobCache = jobCacheObjectMap[this._serverName];
if (jobCache) {
this._jobCacheObject = jobCache;
@@ -93,7 +99,11 @@ export class JobHistoryComponent extends Disposable implements OnInit {
}
ngOnInit() {
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
// set base class elements
this._visibilityElement = this._tableContainer;
this._parentComponent = this._agentViewComponent;
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
const self = this;
this._treeController.onClick = (tree, element, event, origin = 'mouse') => {
const payload = { origin: origin };
@@ -130,56 +140,13 @@ export class JobHistoryComponent extends Disposable implements OnInit {
}, {verticalScrollMode: ScrollbarVisibility.Visible});
this._register(attachListStyler(this._tree, this.themeService));
this._tree.layout(JobHistoryComponent.INITIAL_TREE_HEIGHT);
this._initActionBar();
$(window).resize(() => {
let historyDetails = $('.overview-container').get(0);
let statusBar = $('.part.statusbar').get(0);
if (historyDetails && statusBar) {
let historyBottom = historyDetails.getBoundingClientRect().bottom;
let statusTop = statusBar.getBoundingClientRect().top;
this._tree.layout(statusTop - historyBottom - JobHistoryComponent.HEADING_HEIGHT);
}
});
}
this.initActionBar();
ngAfterContentChecked() {
this._agentJobInfo = this._agentViewComponent.agentJobInfo;
if (!this.agentJobInfo) {
this.agentJobInfo = this._agentJobInfo;
this.setActions();
}
if (this._isVisible === false && this._tableContainer.nativeElement.offsetParent !== null) {
this._isVisible = true;
let jobHistories = this._jobCacheObject.jobHistories[this._agentViewComponent.jobId];
if (jobHistories && jobHistories.length > 0) {
const self = this;
if (this._jobCacheObject.prevJobID === this._agentViewComponent.jobId || jobHistories[0].jobId === this._agentViewComponent.jobId) {
this._showPreviousRuns = true;
this.buildHistoryTree(self, jobHistories);
$('jobhistory-component .history-details .prev-run-list .monaco-tree').attr('tabIndex', '-1');
$('jobhistory-component .history-details .prev-run-list .monaco-tree-row').attr('tabIndex', '0');
this._cd.detectChanges();
}
} else if (jobHistories && jobHistories.length === 0 ){
this._showPreviousRuns = false;
this._showSteps = false;
this._noJobsAvailable = true;
this._cd.detectChanges();
} else {
this.loadHistory();
}
this._jobCacheObject.prevJobID = this._agentViewComponent.jobId;
} else if (this._isVisible === true && this._agentViewComponent.refresh) {
this.loadHistory();
this._agentViewComponent.refresh = false;
} else if (this._isVisible === true && this._tableContainer.nativeElement.offsetParent === null) {
this._isVisible = false;
}
}
private loadHistory() {
const self = this;
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
this._jobManagementService.getJobHistory(ownerUri, this._agentViewComponent.jobId).then((result) => {
if (result && result.jobs) {
if (result.jobs.length > 0) {
@@ -273,12 +240,71 @@ export class JobHistoryComponent extends Disposable implements OnInit {
JobManagementUtilities.getActionIconClassName(startIcon, stopIcon, this.agentJobInfo.currentExecutionStatus);
}
public onFirstVisible() {
this._agentJobInfo = this._agentViewComponent.agentJobInfo;
if (!this.agentJobInfo) {
this.agentJobInfo = this._agentJobInfo;
this.setActions();
}
private _initActionBar() {
if (this.isRefreshing ) {
this.loadHistory();
return;
}
let jobHistories = this._jobCacheObject.jobHistories[this._agentViewComponent.jobId];
if (jobHistories && jobHistories.length > 0) {
const self = this;
if (this._jobCacheObject.prevJobID === this._agentViewComponent.jobId || jobHistories[0].jobId === this._agentViewComponent.jobId) {
this._showPreviousRuns = true;
this.buildHistoryTree(self, jobHistories);
$('jobhistory-component .history-details .prev-run-list .monaco-tree').attr('tabIndex', '-1');
$('jobhistory-component .history-details .prev-run-list .monaco-tree-row').attr('tabIndex', '0');
this._cd.detectChanges();
}
} else if (jobHistories && jobHistories.length === 0 ){
this._showPreviousRuns = false;
this._showSteps = false;
this._noJobsAvailable = true;
this._cd.detectChanges();
} else {
this.loadHistory();
}
this._jobCacheObject.prevJobID = this._agentViewComponent.jobId;
}
public layout() {
let historyDetails = $('.overview-container').get(0);
let statusBar = $('.part.statusbar').get(0);
if (historyDetails && statusBar) {
let historyBottom = historyDetails.getBoundingClientRect().bottom;
let statusTop = statusBar.getBoundingClientRect().top;
let height: number = statusTop - historyBottom - JobHistoryComponent.HEADING_HEIGHT;
if (this._table) {
this._table.layout(new dom.Dimension(
dom.getContentWidth(this._tableContainer.nativeElement),
height));
}
if (this._tree) {
this._tree.layout(height);
}
if (this._jobStepsView) {
let element = this._jobStepsView.nativeElement as HTMLElement;
if (element) {
element.style.height = height + 'px';
}
}
}
}
protected initActionBar() {
let runJobAction = this.instantiationService.createInstance(RunJobAction);
let stopJobAction = this.instantiationService.createInstance(StopJobAction);
let newStepAction = this.instantiationService.createInstance(NewStepAction);
let taskbar = <HTMLElement>this._actionbarContainer.nativeElement;
let taskbar = <HTMLElement>this.actionBarContainer.nativeElement;
this._actionBar = new Taskbar(taskbar, this.contextMenuService);
this._actionBar.context = this;
this._actionBar.setContent([
@@ -299,7 +325,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
}
public get ownerUri(): string {
return this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
return this._commonService.connectionManagementService.connectionInfo.ownerUri;
}
public get serverName(): string {

View File

@@ -167,7 +167,7 @@ table.step-list tr.step-row td {
}
.vs-dark .history-details > .job-steps {
display: inline-block;
display: block;
border-left: 3px solid #444444;
padding-left: 10px;
height: 100%;
@@ -176,10 +176,12 @@ table.step-list tr.step-row td {
}
.history-details > .job-steps {
display: inline-block;
display: block;
border-left: 3px solid #f4f4f4;
padding-left: 10px;
height: 100%;
width: 90%;
overflow-y: scroll;
}
.history-details > .job-steps > .step-list {

View File

@@ -16,8 +16,10 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Taskbar } from '../../../base/browser/ui/taskbar/taskbar';
import { JobsRefreshAction } from 'sql/parts/jobManagement/common/jobActions';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
export abstract class JobManagementView extends Disposable implements AfterContentChecked {
export abstract class JobManagementView extends TabChild implements AfterContentChecked {
protected isVisible: boolean = false;
protected isInitialized: boolean = false;
protected isRefreshing: boolean = false;
@@ -32,10 +34,16 @@ export abstract class JobManagementView extends Disposable implements AfterConte
constructor(
protected _commonService: CommonServiceInterface,
protected _dashboardService: IDashboardService,
protected _contextMenuService: IContextMenuService,
protected _keybindingService: IKeybindingService,
protected _instantiationService: IInstantiationService) {
super();
let self = this;
this._dashboardService.onLayout((d) => {
self.layout();
});
}
ngAfterContentChecked() {
@@ -49,8 +57,9 @@ export abstract class JobManagementView extends Disposable implements AfterConte
}
} else if (this.isVisible === true && this._parentComponent.refresh === true) {
this._showProgressWheel = true;
this.onFirstVisible();
this.isRefreshing = true;
this.onFirstVisible();
this.layout();
this._parentComponent.refresh = false;
} else if (this.isVisible === true && this._visibilityElement.nativeElement.offsetParent === null) {
this.isVisible = false;

View File

@@ -8,21 +8,28 @@ import 'vs/css!./jobStepsView';
import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable, AfterContentChecked } from '@angular/core';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { Disposable } from 'vs/base/common/lifecycle';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { JobStepsViewController, JobStepsViewDataSource, JobStepsViewFilter,
JobStepsViewRenderer, JobStepsViewModel} from 'sql/parts/jobManagement/views/jobStepsViewTree';
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
import { JobManagementView } from 'sql/parts/jobManagement/views/jobManagementView';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
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 * as dom from 'vs/base/browser/dom';
export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component';
@Component({
selector: JOBSTEPSVIEW_SELECTOR,
templateUrl: decodeURI(require.toUrl('./jobStepsView.component.html'))
templateUrl: decodeURI(require.toUrl('./jobStepsView.component.html')),
providers: [{ provide: TabChild, useExisting: forwardRef(() => JobStepsViewComponent) }],
})
export class JobStepsViewComponent extends Disposable implements OnInit, AfterContentChecked {
export class JobStepsViewComponent extends JobManagementView implements OnInit, AfterContentChecked {
private _tree: Tree;
private _treeController = new JobStepsViewController();
@@ -33,15 +40,18 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo
@ViewChild('table') private _tableContainer: ElementRef;
constructor(
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
@Inject(forwardRef(() => JobHistoryComponent)) private _jobHistoryComponent: JobHistoryComponent,
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
@Inject(IInstantiationService) instantiationService: IInstantiationService,
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
@Inject(IKeybindingService) keybindingService: IKeybindingService,
@Inject(IDashboardService) dashboardService: IDashboardService
) {
super();
super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService);
}
ngAfterContentChecked() {
@@ -64,7 +74,7 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo
}
ngOnInit() {
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
this._tree = new Tree(this._tableContainer.nativeElement, {
controller: this._treeController,
dataSource: this._treeDataSource,
@@ -73,5 +83,20 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo
}, {verticalScrollMode: ScrollbarVisibility.Visible});
this._register(attachListStyler(this._tree, this.themeService));
}
public onFirstVisible() {
}
public layout() {
let jobsViewToolbar = $('jobhistory-component .actionbar-container').get(0);
let statusBar = $('.part.statusbar').get(0);
if (jobsViewToolbar && statusBar) {
let toolbarBottom = jobsViewToolbar.getBoundingClientRect().bottom;
let statusTop = statusBar.getBoundingClientRect().top;
this._table.layout(new dom.Dimension(
dom.getContentWidth(this._tableContainer.nativeElement),
statusTop - toolbarBottom));
}
}
}

View File

@@ -34,6 +34,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { TPromise } from 'vs/base/common/winjs.base';
import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
export const ROW_HEIGHT: number = 45;
@@ -99,8 +100,9 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
@Inject(IInstantiationService) instantiationService: IInstantiationService,
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
@Inject(IKeybindingService) keybindingService: IKeybindingService,
@Inject(IDashboardService) _dashboardService: IDashboardService
) {
super(commonService, contextMenuService, keybindingService, instantiationService);
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService);
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
this._serverName = commonService.connectionManagementService.connectionInfo.connectionProfile.serverName;
let jobCache = jobCacheObjectMap[this._serverName];
@@ -121,7 +123,15 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
}
public layout() {
this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement)));
let jobsViewToolbar = $('jobsview-component .actionbar-container').get(0);
let statusBar = $('.part.statusbar').get(0);
if (jobsViewToolbar && statusBar) {
let toolbarBottom = jobsViewToolbar.getBoundingClientRect().bottom;
let statusTop = statusBar.getBoundingClientRect().top;
this._table.layout(new dom.Dimension(
dom.getContentWidth(this._gridEl.nativeElement),
statusTop - toolbarBottom));
}
}
onFirstVisible() {
@@ -141,7 +151,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
enableColumnReorder: false,
rowHeight: ROW_HEIGHT,
enableCellNavigation: true,
forceFitColumns: true
forceFitColumns: false
};
this.dataView = new Slick.Data.DataView({ inlineFilters: false });
@@ -328,7 +338,6 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
this.dataView.beginUpdate();
this.dataView.setItems(jobViews);
this.dataView.setFilter((item) => this.filter(item));
this.dataView.endUpdate();
this._table.autosizeColumns();
this._table.resizeCanvas();
@@ -341,17 +350,6 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
});
const self = this;
$(window).resize(() => {
let jobsViewToolbar = $('jobsview-component .actionbar-container').get(0);
let statusBar = $('.part.statusbar').get(0);
if (jobsViewToolbar && statusBar) {
let toolbarBottom = jobsViewToolbar.getBoundingClientRect().bottom;
let statusTop = statusBar.getBoundingClientRect().top;
$('agentview-component #jobsDiv .jobview-grid').css('height', statusTop - toolbarBottom);
self._table.resizeCanvas();
}
});
this._table.grid.onColumnsResized.subscribe((e, data: any) => {
let nameWidth: number = data.grid.getColumnWidths()[1];
// adjust job name when resized
@@ -844,7 +842,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
public openCreateJobDialog() {
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
this._commandService.executeCommand('agent.openCreateJobDialog', ownerUri);
this._commandService.executeCommand('agent.openJobDialog', ownerUri);
}
public refreshJobs() {

View File

@@ -29,9 +29,10 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { TPromise } from 'vs/base/common/winjs.base';
import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
export const VIEW_SELECTOR: string = 'joboperatorsview-component';
export const ROW_HEIGHT: number = 45;
export const ROW_HEIGHT: number = 30;
@Component({
selector: VIEW_SELECTOR,
@@ -73,9 +74,10 @@ export class OperatorsViewComponent extends JobManagementView implements OnInit
@Inject(IInstantiationService) instantiationService: IInstantiationService,
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
@Inject(IKeybindingService) keybindingService: IKeybindingService
@Inject(IKeybindingService) keybindingService: IKeybindingService,
@Inject(IDashboardService) _dashboardService: IDashboardService
) {
super(commonService, contextMenuService, keybindingService, instantiationService);
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService);
this._isCloud = commonService.connectionManagementService.connectionInfo.serverInfo.isCloud;
}
@@ -86,7 +88,14 @@ export class OperatorsViewComponent extends JobManagementView implements OnInit
}
public layout() {
this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement)));
let height = dom.getContentHeight(this._gridEl.nativeElement) - 10;
if (height < 0) {
height = 0;
}
this._table.layout(new dom.Dimension(
dom.getContentWidth(this._gridEl.nativeElement),
height));
}
onFirstVisible() {
@@ -95,13 +104,6 @@ export class OperatorsViewComponent extends JobManagementView implements OnInit
column.rerenderOnResize = true;
return column;
});
let options = <Slick.GridOptions<any>>{
syncColumnCellResize: true,
enableColumnReorder: false,
rowHeight: ROW_HEIGHT,
enableCellNavigation: true,
forceFitColumns: true
};
this.dataView = new Slick.Data.DataView();
@@ -163,7 +165,7 @@ export class OperatorsViewComponent extends JobManagementView implements OnInit
public openCreateOperatorDialog() {
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
this._commandService.executeCommand('agent.openCreateOperatorDialog', ownerUri);
this._commandService.executeCommand('agent.openOperatorDialog', ownerUri);
}
private refreshJobs() {

View File

@@ -29,9 +29,10 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IAction } from 'vs/base/common/actions';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
export const VIEW_SELECTOR: string = 'jobproxiesview-component';
export const ROW_HEIGHT: number = 45;
export const ROW_HEIGHT: number = 30;
@Component({
selector: VIEW_SELECTOR,
@@ -75,9 +76,10 @@ export class ProxiesViewComponent extends JobManagementView implements OnInit {
@Inject(IInstantiationService) instantiationService: IInstantiationService,
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
@Inject(IKeybindingService) keybindingService: IKeybindingService
@Inject(IKeybindingService) keybindingService: IKeybindingService,
@Inject(IDashboardService) _dashboardService: IDashboardService
) {
super(commonService, contextMenuService, keybindingService, instantiationService);
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService);
this._isCloud = commonService.connectionManagementService.connectionInfo.serverInfo.isCloud;
}
@@ -88,7 +90,14 @@ export class ProxiesViewComponent extends JobManagementView implements OnInit {
}
public layout() {
this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement)));
let height = dom.getContentHeight(this._gridEl.nativeElement) - 10;
if (height < 0) {
height = 0;
}
this._table.layout(new dom.Dimension(
dom.getContentWidth(this._gridEl.nativeElement),
height));
}
onFirstVisible() {
@@ -97,13 +106,6 @@ export class ProxiesViewComponent extends JobManagementView implements OnInit {
column.rerenderOnResize = true;
return column;
});
let options = <Slick.GridOptions<any>>{
syncColumnCellResize: true,
enableColumnReorder: false,
rowHeight: ROW_HEIGHT,
enableCellNavigation: true,
forceFitColumns: true
};
this.dataView = new Slick.Data.DataView();
@@ -164,7 +166,11 @@ export class ProxiesViewComponent extends JobManagementView implements OnInit {
public openCreateProxyDialog() {
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
this._commandService.executeCommand('agent.openCreateProxyDialog', ownerUri);
this._jobManagementService.getCredentials(ownerUri).then((result) => {
if (result && result.credentials) {
this._commandService.executeCommand('agent.openProxyDialog', ownerUri, undefined, result.credentials);
}
});
}
private refreshJobs() {

View File

@@ -130,11 +130,14 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
this.setPropertyFromUI<sqlops.ComponentProperties, number | string>((props, value) => props.width = value, newValue);
}
protected convertSizeToNumber(size: number | string): number {
public convertSizeToNumber(size: number | string): number {
if (size && typeof (size) === 'string') {
if (size.toLowerCase().endsWith('px')) {
return +size.replace('px', '');
} else if (size.toLowerCase().endsWith('em')) {
return +size.replace('em', '') * 11;
}
} else if (!size) {
return 0;
}
return +size;
@@ -148,7 +151,7 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
return this.height ? this.convertSize(this.height) : '';
}
protected convertSize(size: number | string, defaultValue?: string): string {
public convertSize(size: number | string, defaultValue?: string): string {
defaultValue = defaultValue || '';
if (types.isUndefinedOrNull(size)) {
return defaultValue;

View File

@@ -187,7 +187,7 @@ export default class DropDownComponent extends ComponentBase implements ICompone
}
private get values(): string[] | sqlops.CategoryValue[] {
return this.getPropertyOrDefault<sqlops.DropDownProperties, string[] | sqlops.CategoryValue[]>((props) => props.values, undefined);
return this.getPropertyOrDefault<sqlops.DropDownProperties, string[] | sqlops.CategoryValue[]>((props) => props.values, []);
}
private set values(newValue: string[] | sqlops.CategoryValue[]) {

View File

@@ -45,7 +45,7 @@ class FormItem {
template: `
<div #container *ngIf="items" class="form-table" [style.padding]="getFormPadding()" [style.width]="getFormWidth()" [style.height]="getFormHeight()">
<ng-container *ngFor="let item of items">
<div class="form-row" *ngIf="isGroupLabel(item)">
<div class="form-row" *ngIf="isGroupLabel(item)" [style.font-size]="getItemTitleFontSize(item)">
<div class="form-item-row form-group-label">
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
</model-component-wrapper>
@@ -56,7 +56,7 @@ class FormItem {
<ng-container *ngIf="isHorizontal(item)">
<div class="form-cell" [style.font-size]="getItemTitleFontSize(item)" [ngClass]="{'form-group-item': isInGroup(item)}">
{{getItemTitle(item)}}<span class="form-required" *ngIf="isItemRequired(item)">*</span>
<span class="icon info form-info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span>
<span class="icon help form-info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span>
</div>
<div class="form-cell">
<div class="form-component-container">
@@ -76,7 +76,7 @@ class FormItem {
<div class="form-vertical-container" *ngIf="isVertical(item)" [style.height]="getRowHeight(item)" [ngClass]="{'form-group-item': isInGroup(item)}">
<div class="form-item-row" [style.font-size]="getItemTitleFontSize(item)">
{{getItemTitle(item)}}<span class="form-required" *ngIf="isItemRequired(item)">*</span>
<span class="icon info form-info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span>
<span class="icon help form-info" *ngIf="itemHasInfo(item)" [title]="getItemInfo(item)"></span>
</div>
<div class="form-item-row" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore" [style.width]="getComponentWidth(item)" [style.height]="getRowHeight(item)">
@@ -179,8 +179,12 @@ export default class FormContainer extends ContainerBase<FormItemLayout> impleme
}
private getItemTitleFontSize(item: FormItem): string {
let defaultFontSize = '14px';
if (this.isInGroup(item)) {
defaultFontSize = '12px';
}
let itemConfig = item.config;
return itemConfig && itemConfig.titleFontSize ? this.convertSize(itemConfig.titleFontSize, '11px') : '11px';
return itemConfig && itemConfig.titleFontSize ? this.convertSize(itemConfig.titleFontSize, defaultFontSize) : defaultFontSize;
}
private getActionComponents(item: FormItem): FormItem[] {

View File

@@ -18,7 +18,7 @@
}
.form-vertical-container {
padding-bottom: 15px;
padding-bottom: 5px;
width: 100%;
}
@@ -57,11 +57,8 @@
display: table-cell;
}
.form-group-item .form-item-row,
.form-group-item.form-cell {
padding-left: 30px;
}
.form-group-label {
padding-top: 3px;
padding-bottom: 0px;
}
font-weight: bold;
}

View File

@@ -12,14 +12,17 @@ import * as sqlops from 'sqlops';
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import { attachInputBoxStyler } from 'sql/common/theme/styler';
import { IInputOptions, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { attachInputBoxStyler, attachListStyler } from 'vs/platform/theme/common/styler';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import * as nls from 'vs/nls';
import { inputBackground, inputBorder } from 'vs/platform/theme/common/colorRegistry';
import * as DomUtils from 'vs/base/browser/dom';
import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
@Component({
selector: 'modelview-inputBox',
@@ -73,11 +76,31 @@ export default class InputBoxComponent extends ComponentBase implements ICompone
if (this._textareaContainer) {
let textAreaInputOptions = Object.assign({}, inputOptions, { flexibleHeight: true, type: 'textarea' });
this._textAreaInput = new InputBox(this._textareaContainer.nativeElement, this.contextViewService, textAreaInputOptions);
this.onkeydown(this._textAreaInput.inputElement, (e: StandardKeyboardEvent) => {
if (this.tryHandleKeyEvent(e)) {
e.stopPropagation();
}
// Else assume that keybinding service handles routing this to a command
});
this.registerInput(this._textAreaInput, () => this.multiline);
}
this.inputElement.hideErrors = true;
}
private onkeydown(domNode: HTMLElement, listener: (e: IKeyboardEvent) => void): void {
this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.KEY_DOWN, (e: KeyboardEvent) => listener(new StandardKeyboardEvent(e))));
}
private tryHandleKeyEvent(e: StandardKeyboardEvent): boolean {
let handled: boolean = false;
if (this.multiline && e.keyCode === KeyCode.Enter) {
handled = true;
}
return handled;
}
private get inputElement(): InputBox {
return this.multiline ? this._textAreaInput : this._input;
}

View File

@@ -74,15 +74,21 @@ export default class TableComponent extends ComponentBase implements IComponent,
}
}
transformData(rows: string[][], columns: any[]): { [key: string]: string }[] {
return rows.map(row => {
let object: { [key: string]: string } = {};
row.forEach((val, index) => {
let columnName: string = (columns[index].value) ? columns[index].value : <string>columns[index];
object[columnName] = val;
public static transformData(rows: string[][], columns: any[]): { [key: string]: string }[] {
if (rows && columns) {
return rows.map(row => {
let object: { [key: string]: string } = {};
if (row.forEach) {
row.forEach((val, index) => {
let columnName: string = (columns[index].value) ? columns[index].value : <string>columns[index];
object[columnName] = val;
});
}
return object;
});
return object;
});
} else {
return [];
}
}
ngAfterViewInit(): void {
@@ -147,7 +153,7 @@ export default class TableComponent extends ComponentBase implements IComponent,
public setProperties(properties: { [key: string]: any; }): void {
super.setProperties(properties);
this._tableData.clear();
this._tableData.push(this.transformData(this.data, this.columns));
this._tableData.push(TableComponent.transformData(this.data, this.columns));
this._tableColumns = this.transformColumns(this.columns);
this._table.columns = this._tableColumns;
this._table.setData(this._tableData);

View File

@@ -95,3 +95,7 @@ export class NodeType {
public static ColumnMasterKey = 'ColumnMasterKey';
public static ColumnEncryptionKey = 'ColumnEncryptionKey';
}
export interface SqlThemeIcon {
readonly id: string;
}

View File

@@ -5,7 +5,7 @@
'use strict';
import { NodeType } from 'sql/parts/objectExplorer/common/nodeType';
import { TreeNode, TreeItemCollapsibleState, ObjectExplorerCallbacks } from 'sql/parts/objectExplorer/common/treeNode';
import { TreeNode, TreeItemCollapsibleState } from 'sql/parts/objectExplorer/common/treeNode';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
@@ -20,7 +20,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { warn, error } from 'sql/base/common/log';
import { ServerTreeView } from 'sql/parts/objectExplorer/viewlet/serverTreeView';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import * as vscode from 'vscode';
export const SERVICE_ID = 'ObjectExplorerService';
@@ -162,10 +161,16 @@ export class ObjectExplorerService implements IObjectExplorerService {
error(expandResponse.errorMessage);
}
let nodeStatus = this._sessions[expandResponse.sessionId].nodes[expandResponse.nodePath];
if (nodeStatus && nodeStatus.expandEmitter) {
nodeStatus.expandEmitter.fire(expandResponse);
} else {
let sessionStatus = this._sessions[expandResponse.sessionId];
let foundSession = false;
if (sessionStatus) {
let nodeStatus = this._sessions[expandResponse.sessionId].nodes[expandResponse.nodePath];
foundSession = !!nodeStatus;
if (foundSession && nodeStatus.expandEmitter) {
nodeStatus.expandEmitter.fire(expandResponse);
}
}
if (!foundSession) {
warn(`Cannot find node status for session: ${expandResponse.sessionId} and node path: ${expandResponse.nodePath}`);
}
}
@@ -396,7 +401,7 @@ export class ObjectExplorerService implements IObjectExplorerService {
}
return new TreeNode(nodeInfo.nodeType, nodeInfo.label, isLeaf, nodeInfo.nodePath,
nodeInfo.nodeSubType, nodeInfo.nodeStatus, parent, nodeInfo.metadata, {
nodeInfo.nodeSubType, nodeInfo.nodeStatus, parent, nodeInfo.metadata, nodeInfo.iconType, {
getChildren: treeNode => this.getChildren(treeNode),
isExpanded: treeNode => this.isExpanded(treeNode),
setNodeExpandedState: (treeNode, expandedState) => this.setNodeExpandedState(treeNode, expandedState),

View File

@@ -6,7 +6,7 @@
'use strict';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { NodeType } from 'sql/parts/objectExplorer/common/nodeType';
import { NodeType, SqlThemeIcon } from 'sql/parts/objectExplorer/common/nodeType';
import * as sqlops from 'sqlops';
import * as UUID from 'vs/base/common/uuid';
@@ -83,6 +83,23 @@ export class TreeNode {
public metadata: sqlops.ObjectMetadata;
public iconType: string | SqlThemeIcon;
constructor(nodeTypeId: string, label: string, isAlwaysLeaf: boolean, nodePath: string,
nodeSubType: string, nodeStatus: string, parent: TreeNode, metadata: sqlops.ObjectMetadata,
iconType: string | SqlThemeIcon,
private _objectExplorerCallbacks: ObjectExplorerCallbacks) {
this.nodeTypeId = nodeTypeId;
this.label = label;
this.isAlwaysLeaf = isAlwaysLeaf;
this.nodePath = nodePath;
this.parent = parent;
this.metadata = metadata;
this.iconType = iconType;
this.id = UUID.generateUuid();
this.nodeSubType = nodeSubType;
this.nodeStatus = nodeStatus;
}
public getConnectionProfile(): ConnectionProfile {
var currentNode: TreeNode = this;
while (!currentNode.connection && currentNode.parent) {
@@ -149,18 +166,4 @@ export class TreeNode {
public setSelected(selected: boolean, clearOtherSelections?: boolean): Thenable<void> {
return this._objectExplorerCallbacks.setNodeSelected(this, selected, clearOtherSelections);
}
constructor(nodeTypeId: string, label: string, isAlwaysLeaf: boolean, nodePath: string,
nodeSubType: string, nodeStatus: string, parent: TreeNode, metadata: sqlops.ObjectMetadata,
private _objectExplorerCallbacks: ObjectExplorerCallbacks) {
this.nodeTypeId = nodeTypeId;
this.label = label;
this.isAlwaysLeaf = isAlwaysLeaf;
this.nodePath = nodePath;
this.parent = parent;
this.metadata = metadata;
this.id = UUID.generateUuid();
this.nodeSubType = nodeSubType;
this.nodeStatus = nodeStatus;
}
}

View File

@@ -33,7 +33,7 @@ export class RefreshAction extends Action {
id: string,
label: string,
tree: ITree,
private element: ConnectionProfile | TreeNode,
private element: IConnectionProfile | TreeNode,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IErrorMessageService private _errorMessageService: IErrorMessageService
@@ -88,80 +88,31 @@ export class DisconnectConnectionAction extends Action {
public static ID = 'objectExplorer.disconnect';
public static LABEL = localize('DisconnectAction', 'Disconnect');
private _disposables: IDisposable[] = [];
private _connectionProfile: ConnectionProfile;
private _container: HTMLElement;
constructor(
id: string,
label: string,
private _connectionProfile: ConnectionProfile,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IErrorMessageService private _errorMessageService: IErrorMessageService
) {
super(id, label);
const self = this;
this._disposables.push(this._connectionManagementService.onConnect(() => {
self.setLabel();
})
);
this._disposables.push(this._connectionManagementService.onDisconnect((disconnectParams) => {
if (this._connectionProfile) {
this._connectionProfile.isDisconnecting = false;
}
self.setLabel();
self._connectionManagementService.closeDashboard(disconnectParams.connectionUri);
})
);
if (this._objectExplorerService && this._objectExplorerService.onUpdateObjectExplorerNodes) {
this._disposables.push(this._objectExplorerService.onUpdateObjectExplorerNodes((args) => {
self.removeSpinning(args.connection);
if (args.errorMessage !== undefined) {
self.showError(args.errorMessage);
}
})
);
}
}
private showError(errorMessage: string) {
if (this._errorMessageService) {
this._errorMessageService.showDialog(Severity.Error, '', errorMessage);
}
}
private setLabel(): void {
if (!this._connectionProfile) {
this.label = 'Connect';
return;
}
this.label = this._connectionManagementService.isProfileConnected(this._connectionProfile) ? 'Disconnect' : 'Connect';
}
private removeSpinning(connection: IConnectionProfile): void {
if (this._connectionProfile) {
if (connection.id === this._connectionProfile.id && this._container) {
ObjectExplorerActionUtilities.hideLoadingIcon(this._container, ObjectExplorerActionUtilities.connectionElementClass);
}
}
}
run(actionContext: ObjectExplorerActionsContext): TPromise<any> {
return new TPromise<boolean>((resolve, reject) => {
if (actionContext instanceof ObjectExplorerActionsContext) {
//set objectExplorerTreeNode for context menu clicks
this._connectionProfile = actionContext.connectionProfile;
this._container = actionContext.container;
resolve(true);
}
if (!this._connectionProfile) {
resolve(true);
}
if (this._connectionManagementService.isProfileConnected(this._connectionProfile)) {
this._connectionProfile.isDisconnecting = true;
let profileImpl = this._connectionProfile as ConnectionProfile;
if (profileImpl) {
profileImpl.isDisconnecting = true;
}
this._connectionManagementService.disconnect(this._connectionProfile).then((value) => {
if (profileImpl) {
profileImpl.isDisconnecting = false;
}
resolve(true);
}
).catch(disconnectError => {
@@ -172,11 +123,6 @@ export class DisconnectConnectionAction extends Action {
}
});
}
dispose(): void {
super.dispose();
this._disposables = dispose(this._disposables);
}
}
@@ -362,11 +308,11 @@ export class RecentConnectionsFilterAction extends Action {
export class NewQueryAction extends Action {
public static ID = 'registeredServers.newQuery';
public static LABEL = localize('registeredServers.newQuery', 'New Query');
private _connectionProfile: ConnectionProfile;
get connectionProfile(): ConnectionProfile {
private _connectionProfile: IConnectionProfile;
get connectionProfile(): IConnectionProfile {
return this._connectionProfile;
}
set connectionProfile(profile: ConnectionProfile) {
set connectionProfile(profile: IConnectionProfile) {
this._connectionProfile = profile;
}
@@ -403,7 +349,7 @@ export class DeleteConnectionAction extends Action {
constructor(
id: string,
label: string,
private element: ConnectionProfile | ConnectionProfileGroup,
private element: IConnectionProfile | ConnectionProfileGroup,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
) {
super(id, label);
@@ -413,7 +359,6 @@ export class DeleteConnectionAction extends Action {
}
if (element instanceof ConnectionProfile) {
element = <ConnectionProfile>element;
let parent: ConnectionProfileGroup = element.parent;
if (parent && parent.id === Constants.unsavedGroupId) {
this.enabled = false;

View File

@@ -8,11 +8,14 @@ import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { ExecuteCommandAction } from 'vs/platform/actions/common/actions';
import { ICommandService } from 'vs/platform/commands/common/commands';
import * as sqlops from 'sqlops';
import { IConnectionManagementService, IConnectionCompletionOptions, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import {
NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction,
ScriptSelectAction, EditDataAction, ScriptCreateAction,
ScriptExecuteAction, ScriptDeleteAction, ScriptAlterAction
} from 'sql/workbench/common/actions';
import { NodeType } from 'sql/parts/objectExplorer/common/nodeType';
@@ -23,41 +26,51 @@ import { IScriptingService } from 'sql/services/scripting/scriptingService';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService';
import * as Constants from 'sql/parts/connection/common/constants';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ExecuteCommandAction } from 'vs/platform/actions/common/actions';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
export class ObjectExplorerActionsContext {
public treeNode: TreeNode;
public connectionProfile: ConnectionProfile;
public container: HTMLElement;
public tree: ITree;
export class ObjectExplorerActionsContext implements sqlops.ObjectExplorerContext {
public connectionProfile: IConnectionProfile;
public nodeInfo: sqlops.NodeInfo;
public isConnectionNode: boolean = false;
}
async function getTreeNode(context: ObjectExplorerActionsContext, objectExplorerService: IObjectExplorerService): TPromise<TreeNode> {
if (context.isConnectionNode) {
return Promise.resolve(undefined);
}
return await objectExplorerService.getTreeNode(context.connectionProfile.id, context.nodeInfo.nodePath);
}
export class OEAction extends ExecuteCommandAction {
private _objectExplorerTreeNode: TreeNode;
private _container: HTMLElement;
private _treeSelectionHandler: TreeSelectionHandler;
constructor(
id: string, label: string,
@IInstantiationService private _instantiationService: IInstantiationService,
@ICommandService commandService: ICommandService,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService
) {
super(id, label, commandService);
}
public run(actionContext: any): TPromise<boolean> {
public async run(actionContext: any): TPromise<boolean> {
this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler);
let profile: IConnectionProfile;
if (actionContext.connectionProfile) {
profile = actionContext.connectionProfile;
} else {
profile = TreeUpdateUtils.getConnectionProfile(<TreeNode>actionContext.treeNode);
if (actionContext instanceof ObjectExplorerActionsContext) {
if (actionContext.isConnectionNode) {
profile = actionContext.connectionProfile;
} else {
// Get the "correct" version from the tree
let treeNode = await getTreeNode(actionContext, this._objectExplorerService);
profile = TreeUpdateUtils.getConnectionProfile(treeNode);
}
}
this._treeSelectionHandler.onTreeActionStateChange(true);
@@ -72,18 +85,16 @@ export class ManageConnectionAction extends Action {
public static ID = 'objectExplorer.manage';
public static LABEL = localize('ManageAction', 'Manage');
private _connectionProfile: ConnectionProfile;
private _objectExplorerTreeNode: TreeNode;
private _treeSelectionHandler: TreeSelectionHandler;
protected _container: HTMLElement;
constructor(
id: string,
label: string,
private _tree: ITree,
@IConnectionManagementService protected _connectionManagementService: IConnectionManagementService,
@ICapabilitiesService protected _capabilitiesService: ICapabilitiesService,
@IInstantiationService private _instantiationService: IInstantiationService,
@IObjectExplorerService private _objectExplorerService?: IObjectExplorerService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService
) {
super(id, label);
}
@@ -91,33 +102,37 @@ export class ManageConnectionAction extends Action {
run(actionContext: ObjectExplorerActionsContext): TPromise<any> {
this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler);
this._treeSelectionHandler.onTreeActionStateChange(true);
let self = this;
let promise = new TPromise<boolean>((resolve, reject) => {
this.doManage(actionContext).then((success) => {
this.done();
self.doManage(actionContext).then((success) => {
self.done();
resolve(success);
}, error => {
this.done();
self.done();
reject(error);
});
});
return promise;
}
private doManage(actionContext: ObjectExplorerActionsContext): Thenable<boolean> {
private async doManage(actionContext: ObjectExplorerActionsContext): TPromise<boolean> {
let treeNode: TreeNode = undefined;
let connectionProfile: IConnectionProfile = undefined;
if (actionContext instanceof ObjectExplorerActionsContext) {
//set objectExplorerTreeNode for context menu clicks
this._connectionProfile = actionContext.connectionProfile;
this._objectExplorerTreeNode = actionContext.treeNode;
if (this._connectionProfile === undefined && TreeUpdateUtils.isDatabaseNode(this._objectExplorerTreeNode)) {
this._connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
// Must use a real connection profile for this action due to lookup
connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, actionContext.connectionProfile);
if (!actionContext.isConnectionNode) {
treeNode = await getTreeNode(actionContext, this._objectExplorerService);
if (TreeUpdateUtils.isDatabaseNode(treeNode)) {
connectionProfile = TreeUpdateUtils.getConnectionProfile(treeNode);
}
}
this._container = actionContext.container;
}
if (!this._connectionProfile) {
if (!connectionProfile) {
// This should never happen. There should be always a valid connection if the manage action is called for
// an OE node or a database node
return TPromise.wrap(true);
return true;
}
let options: IConnectionCompletionOptions = {
@@ -130,10 +145,10 @@ export class ManageConnectionAction extends Action {
// If it's a database node just open a database connection and open dashboard,
// the node is already from an open OE session we don't need to create new session
if (TreeUpdateUtils.isAvailableDatabaseNode(this._objectExplorerTreeNode)) {
return this._connectionManagementService.showDashboard(this._connectionProfile);
if (TreeUpdateUtils.isAvailableDatabaseNode(treeNode)) {
return this._connectionManagementService.showDashboard(connectionProfile);
} else {
return TreeUpdateUtils.connectAndCreateOeSession(this._connectionProfile, options, this._connectionManagementService, this._objectExplorerService, actionContext.tree);
return TreeUpdateUtils.connectAndCreateOeSession(connectionProfile, options, this._connectionManagementService, this._objectExplorerService, this._tree);
}
}
@@ -149,7 +164,6 @@ export class ManageConnectionAction extends Action {
export class OEScriptSelectAction extends ScriptSelectAction {
public static ID = 'objectExplorer.' + ScriptSelectAction.ID;
private _objectExplorerTreeNode: TreeNode;
private _container: HTMLElement;
private _treeSelectionHandler: TreeSelectionHandler;
constructor(
@@ -157,23 +171,23 @@ export class OEScriptSelectAction extends ScriptSelectAction {
@IQueryEditorService protected _queryEditorService: IQueryEditorService,
@IConnectionManagementService protected _connectionManagementService: IConnectionManagementService,
@IScriptingService protected _scriptingService: IScriptingService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IInstantiationService private _instantiationService: IInstantiationService
) {
super(id, label, _queryEditorService, _connectionManagementService, _scriptingService);
}
public run(actionContext: any): TPromise<boolean> {
public async run(actionContext: any): TPromise<boolean> {
this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler);
if (actionContext instanceof ObjectExplorerActionsContext) {
//set objectExplorerTreeNode for context menu clicks
this._objectExplorerTreeNode = actionContext.treeNode;
this._container = actionContext.container;
this._objectExplorerTreeNode = await getTreeNode(actionContext, this._objectExplorerService);
}
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(this._objectExplorerTreeNode);
var ownerUri = this._connectionManagementService.getConnectionId(connectionProfile);
ownerUri = this._connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
var metadata = (<TreeNode>this._objectExplorerTreeNode).metadata;
var metadata = this._objectExplorerTreeNode.metadata;
return super.run({ profile: connectionProfile, object: metadata }).then((result) => {
this._treeSelectionHandler.onTreeActionStateChange(false);
@@ -185,7 +199,6 @@ export class OEScriptSelectAction extends ScriptSelectAction {
export class OEEditDataAction extends EditDataAction {
public static ID = 'objectExplorer.' + EditDataAction.ID;
private _objectExplorerTreeNode: TreeNode;
private _container: HTMLElement;
private _treeSelectionHandler: TreeSelectionHandler;
constructor(
@@ -193,17 +206,17 @@ export class OEEditDataAction extends EditDataAction {
@IQueryEditorService protected _queryEditorService: IQueryEditorService,
@IConnectionManagementService protected _connectionManagementService: IConnectionManagementService,
@IScriptingService protected _scriptingService: IScriptingService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IInstantiationService private _instantiationService: IInstantiationService
) {
super(id, label, _queryEditorService, _connectionManagementService, _scriptingService);
}
public run(actionContext: any): TPromise<boolean> {
public async run(actionContext: any): TPromise<boolean> {
this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler);
if (actionContext instanceof ObjectExplorerActionsContext) {
//set objectExplorerTreeNode for context menu clicks
this._objectExplorerTreeNode = actionContext.treeNode;
this._container = actionContext.container;
this._objectExplorerTreeNode = await getTreeNode(actionContext, this._objectExplorerService);
}
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
@@ -219,7 +232,6 @@ export class OEEditDataAction extends EditDataAction {
export class OEScriptCreateAction extends ScriptCreateAction {
public static ID = 'objectExplorer.' + ScriptCreateAction.ID;
private _objectExplorerTreeNode: TreeNode;
private _container: HTMLElement;
private _treeSelectionHandler: TreeSelectionHandler;
constructor(
@@ -227,18 +239,18 @@ export class OEScriptCreateAction extends ScriptCreateAction {
@IQueryEditorService protected _queryEditorService: IQueryEditorService,
@IConnectionManagementService protected _connectionManagementService: IConnectionManagementService,
@IScriptingService protected _scriptingService: IScriptingService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IInstantiationService private _instantiationService: IInstantiationService,
@IErrorMessageService protected _errorMessageService: IErrorMessageService
) {
super(id, label, _queryEditorService, _connectionManagementService, _scriptingService, _errorMessageService);
}
public run(actionContext: any): TPromise<boolean> {
public async run(actionContext: any): TPromise<boolean> {
this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler);
if (actionContext instanceof ObjectExplorerActionsContext) {
//set objectExplorerTreeNode for context menu clicks
this._objectExplorerTreeNode = actionContext.treeNode;
this._container = actionContext.container;
this._objectExplorerTreeNode = await getTreeNode(actionContext, this._objectExplorerService);
}
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
@@ -256,7 +268,6 @@ export class OEScriptCreateAction extends ScriptCreateAction {
export class OEScriptExecuteAction extends ScriptExecuteAction {
public static ID = 'objectExplorer.' + ScriptExecuteAction.ID;
private _objectExplorerTreeNode: TreeNode;
private _container: HTMLElement;
private _treeSelectionHandler: TreeSelectionHandler;
constructor(
@@ -264,18 +275,18 @@ export class OEScriptExecuteAction extends ScriptExecuteAction {
@IQueryEditorService protected _queryEditorService: IQueryEditorService,
@IConnectionManagementService protected _connectionManagementService: IConnectionManagementService,
@IScriptingService protected _scriptingService: IScriptingService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IInstantiationService private _instantiationService: IInstantiationService,
@IErrorMessageService protected _errorMessageService: IErrorMessageService
) {
super(id, label, _queryEditorService, _connectionManagementService, _scriptingService, _errorMessageService);
}
public run(actionContext: any): TPromise<boolean> {
public async run(actionContext: any): TPromise<boolean> {
this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler);
if (actionContext instanceof ObjectExplorerActionsContext) {
//set objectExplorerTreeNode for context menu clicks
this._objectExplorerTreeNode = actionContext.treeNode;
this._container = actionContext.container;
this._objectExplorerTreeNode = await getTreeNode(actionContext, this._objectExplorerService);
}
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
@@ -293,7 +304,6 @@ export class OEScriptExecuteAction extends ScriptExecuteAction {
export class OEScriptAlterAction extends ScriptAlterAction {
public static ID = 'objectExplorer.' + ScriptAlterAction.ID;
private _objectExplorerTreeNode: TreeNode;
private _container: HTMLElement;
private _treeSelectionHandler: TreeSelectionHandler;
constructor(
@@ -301,18 +311,18 @@ export class OEScriptAlterAction extends ScriptAlterAction {
@IQueryEditorService protected _queryEditorService: IQueryEditorService,
@IConnectionManagementService protected _connectionManagementService: IConnectionManagementService,
@IScriptingService protected _scriptingService: IScriptingService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IInstantiationService private _instantiationService: IInstantiationService,
@IErrorMessageService protected _errorMessageService: IErrorMessageService
) {
super(id, label, _queryEditorService, _connectionManagementService, _scriptingService, _errorMessageService);
}
public run(actionContext: any): TPromise<boolean> {
public async run(actionContext: any): TPromise<boolean> {
this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler);
if (actionContext instanceof ObjectExplorerActionsContext) {
//set objectExplorerTreeNode for context menu clicks
this._objectExplorerTreeNode = actionContext.treeNode;
this._container = actionContext.container;
this._objectExplorerTreeNode = await getTreeNode(actionContext, this._objectExplorerService);
}
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
@@ -330,7 +340,6 @@ export class OEScriptAlterAction extends ScriptAlterAction {
export class OEScriptDeleteAction extends ScriptDeleteAction {
public static ID = 'objectExplorer.' + ScriptDeleteAction.ID;
private _objectExplorerTreeNode: TreeNode;
private _container: HTMLElement;
private _treeSelectionHandler: TreeSelectionHandler;
constructor(
@@ -338,18 +347,18 @@ export class OEScriptDeleteAction extends ScriptDeleteAction {
@IQueryEditorService protected _queryEditorService: IQueryEditorService,
@IConnectionManagementService protected _connectionManagementService: IConnectionManagementService,
@IScriptingService protected _scriptingService: IScriptingService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IInstantiationService private _instantiationService: IInstantiationService,
@IErrorMessageService protected _errorMessageService: IErrorMessageService
) {
super(id, label, _queryEditorService, _connectionManagementService, _scriptingService, _errorMessageService);
}
public run(actionContext: any): TPromise<boolean> {
public async run(actionContext: any): TPromise<boolean> {
this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler);
if (actionContext instanceof ObjectExplorerActionsContext) {
//set objectExplorerTreeNode for context menu clicks
this._objectExplorerTreeNode = actionContext.treeNode;
this._container = actionContext.container;
this._objectExplorerTreeNode = await getTreeNode(actionContext, this._objectExplorerService);
}
this._treeSelectionHandler.onTreeActionStateChange(true);
var connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>this._objectExplorerTreeNode);
@@ -364,77 +373,11 @@ export class OEScriptDeleteAction extends ScriptDeleteAction {
}
}
export class DisconnectAction extends Action {
public static ID = 'objectExplorer.disconnect';
public static LABEL = localize('objectExplorAction.disconnect', 'Disconnect');
private _objectExplorerTreeNode: TreeNode;
private _container: HTMLElement;
private _treeSelectionHandler: TreeSelectionHandler;
constructor(
id: string,
label: string,
@IConnectionManagementService private connectionManagementService: IConnectionManagementService,
@IInstantiationService private _instantiationService: IInstantiationService
) {
super(id, label);
}
public run(actionContext: any): TPromise<boolean> {
this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler);
if (actionContext instanceof ObjectExplorerActionsContext) {
//set objectExplorerTreeNode for context menu clicks
this._objectExplorerTreeNode = actionContext.treeNode;
this._container = actionContext.container;
}
var connectionProfile = (<TreeNode>this._objectExplorerTreeNode).getConnectionProfile();
if (this.connectionManagementService.isProfileConnected(connectionProfile)) {
this._treeSelectionHandler.onTreeActionStateChange(true);
this.connectionManagementService.disconnect(connectionProfile).then(() => {
this._treeSelectionHandler.onTreeActionStateChange(false);
});
}
return TPromise.as(true);
}
}
export class ObjectExplorerActionUtilities {
public static readonly objectExplorerElementClass = 'object-element-group';
public static readonly connectionElementClass = 'connection-tile';
private static getGroupContainer(container: HTMLElement, elementName: string): HTMLElement {
var element = container;
while (element && element.className !== elementName) {
element = element.parentElement;
}
return element ? element.parentElement : undefined;
}
public static showLoadingIcon(container: HTMLElement, elementName: string): void {
if (container) {
let groupContainer = this.getGroupContainer(container, elementName);
if (groupContainer) {
groupContainer.classList.add('icon');
groupContainer.classList.add('in-progress');
}
}
}
public static hideLoadingIcon(container: HTMLElement, elementName: string): void {
if (container) {
let element = this.getGroupContainer(container, elementName);
if (element && element.classList) {
element.classList.remove('icon');
element.classList.remove('in-progress');
}
}
}
public static getScriptMap(treeNode: TreeNode): Map<NodeType, any[]> {
let scriptMap = new Map<NodeType, any[]>();

View File

@@ -9,6 +9,8 @@ import { ITree } from 'vs/base/parts/tree/browser/tree';
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
import {
DisconnectConnectionAction, AddServerAction,
@@ -16,9 +18,7 @@ import {
}
from 'sql/parts/objectExplorer/viewlet/connectionTreeAction';
import {
DisconnectAction, ObjectExplorerActionUtilities,
ManageConnectionAction,
OEAction
ObjectExplorerActionUtilities, ManageConnectionAction, OEAction
} from 'sql/parts/objectExplorer/viewlet/objectExplorerActions';
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode';
import { NodeType } from 'sql/parts/objectExplorer/common/nodeType';
@@ -27,8 +27,14 @@ import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile
import { NewProfilerAction } from 'sql/parts/profiler/contrib/profilerActions';
import { TreeUpdateUtils } from 'sql/parts/objectExplorer/viewlet/treeUpdateUtils';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ExecuteCommandAction } from 'vs/platform/actions/common/actions';
import { MenuId, IMenuService } from 'vs/platform/actions/common/actions';
import { NewQueryAction } from 'sql/workbench/common/actions';
import { ConnectionContextKey } from 'sql/parts/connection/common/connectionContextKey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { TreeNodeContextKey } from './treeNodeContextKey';
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
import { IScriptingService } from 'sql/services/scripting/scriptingService';
import * as constants from 'sql/common/constants';
/**
* Provides actions for the server tree elements
@@ -37,7 +43,12 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IQueryManagementService private _queryManagementService: IQueryManagementService,
@IScriptingService private _scriptingService: IScriptingService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IMenuService private menuService: IMenuService,
@IContextKeyService private _contextKeyService: IContextKeyService
) {
super();
}
@@ -57,8 +68,11 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
return TPromise.as(this.getConnectionProfileGroupActions(tree, element));
}
if (element instanceof TreeNode) {
var treeNode = <TreeNode>element;
return TPromise.as(this.getObjectExplorerNodeActions(tree, treeNode));
return TPromise.as(this.getObjectExplorerNodeActions({
tree: tree,
profile: element.getConnectionProfile(),
treeNode: element
}));
}
return TPromise.as([]);
@@ -75,23 +89,58 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
/**
* Return actions for connection elements
*/
public getConnectionActions(tree: ITree, element: ConnectionProfile): IAction[] {
let actions: IAction[] = [];
actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL));
actions.push(this._instantiationService.createInstance(OEAction, NewQueryAction.ID, NewQueryAction.LABEL));
if (this._connectionManagementService.isProfileConnected(element)) {
actions.push(this._instantiationService.createInstance(DisconnectConnectionAction, DisconnectConnectionAction.ID, DisconnectConnectionAction.LABEL));
}
actions.push(this._instantiationService.createInstance(DeleteConnectionAction, DeleteConnectionAction.ID, DeleteConnectionAction.DELETE_CONNECTION_LABEL, element));
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, tree, element));
public getConnectionActions(tree: ITree, profile: ConnectionProfile): IAction[] {
return this.getAllActions({
tree: tree,
profile: profile
}, (context) => this.getBuiltinConnectionActions(context));
}
if (process.env['VSCODE_DEV']) {
private getAllActions(context: ObjectExplorerContext, getDefaultActions: (ObjectExplorerContext) => IAction[]) {
// Create metadata needed to get a useful set of actions
let scopedContextService = this.getContextKeyService(context);
let menu = this.menuService.createMenu(MenuId.ObjectExplorerItemContext, scopedContextService);
// Fill in all actions
let actions = getDefaultActions(context);
fillInActions(menu, { arg: undefined, shouldForwardArgs: true }, actions, this.contextMenuService);
// Cleanup
scopedContextService.dispose();
menu.dispose();
return actions;
}
private getBuiltinConnectionActions(context: ObjectExplorerContext): IAction[] {
let actions: IAction[] = [];
actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL, context.tree));
this.addNewQueryAction(context, actions);
if (this._connectionManagementService.isProfileConnected(context.profile)) {
actions.push(this._instantiationService.createInstance(DisconnectConnectionAction, DisconnectConnectionAction.ID, DisconnectConnectionAction.LABEL, context.profile));
}
actions.push(this._instantiationService.createInstance(DeleteConnectionAction, DeleteConnectionAction.ID, DeleteConnectionAction.DELETE_CONNECTION_LABEL, context.profile));
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, context.tree, context.profile));
if (process.env['VSCODE_DEV'] && constants.MssqlProviderId === context.profile.providerName) {
actions.push(this._instantiationService.createInstance(OEAction, NewProfilerAction.ID, NewProfilerAction.LABEL));
}
return actions;
}
private getContextKeyService(context: ObjectExplorerContext): IContextKeyService {
let scopedContextService = this._contextKeyService.createScoped();
let connectionContextKey = new ConnectionContextKey(scopedContextService);
connectionContextKey.set(context.profile);
let treeNodeContextKey = new TreeNodeContextKey(scopedContextService);
if (context.treeNode) {
treeNodeContextKey.set(context.treeNode);
}
return scopedContextService;
}
/**
* Return actions for connection group elements
*/
@@ -106,32 +155,50 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
/**
* Return actions for OE elements
*/
public getObjectExplorerNodeActions(tree: ITree, treeNode: TreeNode): IAction[] {
let actions = [];
private getObjectExplorerNodeActions(context: ObjectExplorerContext): IAction[] {
return this.getAllActions(context, (context) => this.getBuiltInNodeActions(context));
}
private getBuiltInNodeActions(context: ObjectExplorerContext): IAction[] {
let actions: IAction[] = [];
let treeNode = context.treeNode;
if (TreeUpdateUtils.isDatabaseNode(treeNode)) {
if (TreeUpdateUtils.isAvailableDatabaseNode(treeNode)) {
actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL));
actions.push(this._instantiationService.createInstance(ManageConnectionAction, ManageConnectionAction.ID, ManageConnectionAction.LABEL, context.tree));
} else {
return actions;
}
}
actions.push(this._instantiationService.createInstance(OEAction, NewQueryAction.ID, NewQueryAction.LABEL));
let scriptMap: Map<NodeType, any[]> = ObjectExplorerActionUtilities.getScriptMap(treeNode);
let supportedActions = scriptMap.get(treeNode.nodeTypeId);
let self = this;
if (supportedActions !== null && supportedActions !== undefined) {
supportedActions.forEach(action => {
actions.push(self._instantiationService.createInstance(action, action.ID, action.LABEL));
});
}
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, tree, treeNode));
if (treeNode.isTopLevel()) {
actions.push(this._instantiationService.createInstance(DisconnectAction, DisconnectAction.ID, DisconnectAction.LABEL));
}
this.addNewQueryAction(context, actions);
this.addScriptingActions(context, actions);
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, context.tree, treeNode));
return actions;
}
private addNewQueryAction(context: ObjectExplorerContext, actions: IAction[]): void {
if (this._queryManagementService.isProviderRegistered(context.profile.providerName)) {
actions.push(this._instantiationService.createInstance(OEAction, NewQueryAction.ID, NewQueryAction.LABEL));
}
}
private addScriptingActions(context: ObjectExplorerContext, actions: IAction[]): void {
if (this._scriptingService.isProviderRegistered(context.profile.providerName)) {
let scriptMap: Map<NodeType, any[]> = ObjectExplorerActionUtilities.getScriptMap(context.treeNode);
let supportedActions = scriptMap.get(context.treeNode.nodeTypeId);
let self = this;
if (supportedActions !== null && supportedActions !== undefined) {
supportedActions.forEach(action => {
actions.push(self._instantiationService.createInstance(action, action.ID, action.LABEL));
});
}
}
}
}
interface ObjectExplorerContext {
tree: ITree;
profile: ConnectionProfile;
treeNode?: TreeNode;
}

View File

@@ -18,6 +18,7 @@ import { ServerTreeActionProvider } from 'sql/parts/objectExplorer/viewlet/serve
import { ObjectExplorerActionsContext } from 'sql/parts/objectExplorer/viewlet/objectExplorerActions';
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode';
import { OpenMode } from 'vs/base/parts/tree/browser/treeDefaults';
import { TreeUpdateUtils } from 'sql/parts/objectExplorer/viewlet/treeUpdateUtils';
/**
* Extends the tree controller to handle clicks on the tree elements
@@ -73,26 +74,24 @@ export class ServerTreeController extends treedefaults.DefaultController {
event.stopPropagation();
tree.setFocus(element);
let parent: ConnectionProfileGroup = undefined;
if (element instanceof ConnectionProfileGroup) {
parent = <ConnectionProfileGroup>element;
}
else if (element instanceof ConnectionProfile) {
parent = (<ConnectionProfile>element).parent;
}
var actionContext: any;
if (element instanceof TreeNode) {
actionContext = new ObjectExplorerActionsContext();
actionContext.container = event.target;
actionContext.treeNode = <TreeNode>element;
actionContext.tree = tree;
let context = new ObjectExplorerActionsContext();
context.nodeInfo = element.toNodeInfo();
// Note: getting DB name before, but intentionally not using treeUpdateUtils.getConnectionProfile as it replaces
// the connection ID with a new one. This breaks a number of internal tasks
context.connectionProfile = element.getConnectionProfile().toIConnectionProfile();
context.connectionProfile.databaseName = element.getDatabaseName();
actionContext = context;
} else if (element instanceof ConnectionProfile) {
actionContext = new ObjectExplorerActionsContext();
actionContext.container = event.target;
actionContext.connectionProfile = <ConnectionProfile>element;
actionContext.tree = tree;
let context = new ObjectExplorerActionsContext();
context.connectionProfile = element.toIConnectionProfile();
context.isConnectionNode = true;
actionContext = context;
} else {
// TODO: because the connection group is used as a context object and isn't serializable,
// the Group-level context menu is not currently extensible
actionContext = element;
}

View File

@@ -31,12 +31,10 @@ export class ServerTreeDataSource implements IDataSource {
* No more than one element may use a given identifier.
*/
public getId(tree: ITree, element: any): string {
if (element instanceof ConnectionProfile) {
return (<ConnectionProfile>element).id;
} else if (element instanceof ConnectionProfileGroup) {
return (<ConnectionProfileGroup>element).id;
} else if (element instanceof TreeNode) {
return (<TreeNode>element).id;
if (element instanceof ConnectionProfile
|| element instanceof ConnectionProfileGroup
|| element instanceof TreeNode) {
return element.id;
} else {
return undefined;
}
@@ -49,9 +47,9 @@ export class ServerTreeDataSource implements IDataSource {
if (element instanceof ConnectionProfile) {
return true;
} else if (element instanceof ConnectionProfileGroup) {
return (<ConnectionProfileGroup>element).hasChildren();
return element.hasChildren();
} else if (element instanceof TreeNode) {
return !(<TreeNode>element).isAlwaysLeaf;
return !element.isAlwaysLeaf;
}
return false;
}
@@ -70,7 +68,7 @@ export class ServerTreeDataSource implements IDataSource {
} else if (element instanceof ConnectionProfileGroup) {
resolve((<ConnectionProfileGroup>element).getChildren());
} else if (element instanceof TreeNode) {
var node = <TreeNode>element;
var node = element;
if (node.children) {
resolve(node.children);
} else {
@@ -92,11 +90,11 @@ export class ServerTreeDataSource implements IDataSource {
*/
public getParent(tree: ITree, element: any): TPromise<any> {
if (element instanceof ConnectionProfile) {
return TPromise.as((<ConnectionProfile>element).getParent());
return TPromise.as(element.getParent());
} else if (element instanceof ConnectionProfileGroup) {
return TPromise.as((<ConnectionProfileGroup>element).getParent());
return TPromise.as(element.getParent());
} else if (element instanceof TreeNode) {
return TPromise.as(TreeUpdateUtils.getObjectExplorerParent(<TreeNode>element, this._connectionManagementService));
return TPromise.as(TreeUpdateUtils.getObjectExplorerParent(element, this._connectionManagementService));
} else {
return TPromise.as(null);
}

View File

@@ -105,38 +105,45 @@ export class ServerTreeRenderer implements IRenderer {
*/
public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
if (templateId === ServerTreeRenderer.CONNECTION_TEMPLATE_ID) {
this.renderConnection(tree, element, templateData);
this.renderConnection(element, templateData);
} else if (templateId === ServerTreeRenderer.CONNECTION_GROUP_TEMPLATE_ID) {
this.renderConnectionProfileGroup(tree, element, templateData);
this.renderConnectionProfileGroup(element, templateData);
} else {
this.renderObjectExplorer(tree, element, templateData);
this.renderObjectExplorer(element, templateData);
}
}
private renderObjectExplorer(tree: ITree, treeNode: TreeNode, templateData: IObjectExplorerTemplateData): void {
var iconName = treeNode.nodeTypeId;
if (treeNode.nodeStatus) {
iconName = treeNode.nodeTypeId + '_' + treeNode.nodeStatus;
}
if (treeNode.nodeSubType) {
iconName = treeNode.nodeTypeId + '_' + treeNode.nodeSubType;
private renderObjectExplorer(treeNode: TreeNode, templateData: IObjectExplorerTemplateData): void {
// Use an explicitly defined iconType first. If not defined, fall back to using nodeType and
// other compount indicators instead.
let iconName: string = undefined;
if (treeNode.iconType) {
iconName = (typeof treeNode.iconType === 'string') ? treeNode.iconType : treeNode.iconType.id;
} else {
iconName = treeNode.nodeTypeId;
if (treeNode.nodeStatus) {
iconName = treeNode.nodeTypeId + '_' + treeNode.nodeStatus;
}
if (treeNode.nodeSubType) {
iconName = treeNode.nodeTypeId + '_' + treeNode.nodeSubType;
}
}
let tokens: string[] = [];
for (var index = 1; index < templateData.icon.classList.length; index++) {
for (let index = 1; index < templateData.icon.classList.length; index++) {
tokens.push(templateData.icon.classList.item(index));
}
templateData.icon.classList.remove(...tokens);
templateData.icon.classList.add('icon');
let iconLoweCaseName = iconName.toLocaleLowerCase();
templateData.icon.classList.add(iconLoweCaseName);
let iconLowerCaseName = iconName.toLocaleLowerCase();
templateData.icon.classList.add(iconLowerCaseName);
templateData.label.textContent = treeNode.label;
templateData.root.title = treeNode.label;
}
private renderConnection(tree: ITree, connection: ConnectionProfile, templateData: IConnectionTemplateData): void {
private renderConnection(connection: ConnectionProfile, templateData: IConnectionTemplateData): void {
if (!this._isCompact) {
if (this._connectionManagementService.isConnected(undefined, connection)) {
templateData.icon.classList.remove('disconnected');
@@ -157,9 +164,9 @@ export class ServerTreeRenderer implements IRenderer {
templateData.connectionProfile = connection;
}
private renderConnectionProfileGroup(tree: ITree, connectionProfileGroup: ConnectionProfileGroup, templateData: IConnectionProfileGroupTemplateData): void {
private renderConnectionProfileGroup(connectionProfileGroup: ConnectionProfileGroup, templateData: IConnectionProfileGroupTemplateData): void {
var rowElement = this.findParentElement(templateData.root, 'monaco-tree-row');
let rowElement = this.findParentElement(templateData.root, 'monaco-tree-row');
if (rowElement) {
if (connectionProfileGroup.color) {
rowElement.style.background = connectionProfileGroup.color;
@@ -179,7 +186,7 @@ export class ServerTreeRenderer implements IRenderer {
* Returns the first parent which contains the className
*/
private findParentElement(container: HTMLElement, className: string): HTMLElement {
var currentElement = container;
let currentElement = container;
while (currentElement) {
if (currentElement.className.includes(className)) {
break;

View File

@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IConnectionProfile } from 'sqlops';
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode';
export class TreeNodeContextKey implements IContextKey<TreeNode> {
static NodeType = new RawContextKey<string>('nodeType', undefined);
static SubType = new RawContextKey<string>('nodeSubType', undefined);
static Status = new RawContextKey<string>('nodeStatus', undefined);
static TreeNode = new RawContextKey<TreeNode>('treeNode', undefined);
private _nodeTypeKey: IContextKey<string>;
private _subTypeKey: IContextKey<string>;
private _statusKey: IContextKey<string>;
private _treeNodeKey: IContextKey<TreeNode>;
constructor(
@IContextKeyService contextKeyService: IContextKeyService
) {
this._nodeTypeKey = TreeNodeContextKey.NodeType.bindTo(contextKeyService);
this._subTypeKey = TreeNodeContextKey.SubType.bindTo(contextKeyService);
this._statusKey = TreeNodeContextKey.Status.bindTo(contextKeyService);
this._treeNodeKey = TreeNodeContextKey.TreeNode.bindTo(contextKeyService);
}
set(value: TreeNode) {
this._treeNodeKey.set(value);
this._nodeTypeKey.set(value && value.nodeTypeId);
this._subTypeKey.set(value && value.nodeSubType);
this._statusKey.set(value && value.nodeStatus);
}
reset(): void {
this._nodeTypeKey.reset();
this._subTypeKey.reset();
this._statusKey.reset();
this._treeNodeKey.reset();
}
public get(): TreeNode {
return this._treeNodeKey.get();
}
}

View File

@@ -104,7 +104,7 @@ export class TreeSelectionHandler {
});
}
} else if (isDoubleClick && selection && selection.length > 0 && (selection[0] instanceof TreeNode)) {
let treeNode = <TreeNode>selection[0];
let treeNode = selection[0];
if (TreeUpdateUtils.isAvailableDatabaseNode(treeNode)) {
connectionProfile = TreeUpdateUtils.getConnectionProfile(treeNode);
if (connectionProfile) {

View File

@@ -13,6 +13,7 @@ import { NodeType } from 'sql/parts/objectExplorer/common/nodeType';
import { TPromise } from 'vs/base/common/winjs.base';
import { TreeNode } from 'sql/parts/objectExplorer/common/treeNode';
import errors = require('vs/base/common/errors');
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
export class TreeUpdateUtils {
@@ -113,7 +114,7 @@ export class TreeUpdateUtils {
}
public static connectIfNotConnected(
connection: ConnectionProfile,
connection: IConnectionProfile,
options: IConnectionCompletionOptions,
connectionManagementService: IConnectionManagementService,
tree: ITree): TPromise<ConnectionProfile> {
@@ -172,7 +173,7 @@ export class TreeUpdateUtils {
* @param connectionManagementService Connection management service instance
* @param objectExplorerService Object explorer service instance
*/
public static connectAndCreateOeSession(connection: ConnectionProfile, options: IConnectionCompletionOptions,
public static connectAndCreateOeSession(connection: IConnectionProfile, options: IConnectionCompletionOptions,
connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, tree: ITree): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
TreeUpdateUtils.connectIfNotConnected(connection, options, connectionManagementService, tree).then(connectedConnection => {

View File

@@ -13,7 +13,7 @@ import * as nls from 'vs/nls';
import { ProfilerInput } from 'sql/parts/profiler/editor/profilerInput';
import { ProfilerEditor } from 'sql/parts/profiler/editor/profilerEditor';
import { PROFILER_SESSION_TEMPLATE_SETTINGS, IProfilerSessionTemplate } from 'sql/parts/profiler/service/interfaces';
import { PROFILER_VIEW_TEMPLATE_SETTINGS, IProfilerViewTemplate } from 'sql/parts/profiler/service/interfaces';
const profilerDescriptor = new EditorDescriptor(
ProfilerEditor,
@@ -24,84 +24,213 @@ const profilerDescriptor = new EditorDescriptor(
Registry.as<IEditorRegistry>(EditorExtensions.Editors)
.registerEditor(profilerDescriptor, [new SyncDescriptor(ProfilerInput)]);
const profilerSessionTemplateSchema: IJSONSchema = {
description: nls.localize('profiler.settings.sessionTemplates', "Specifies session templates"),
type: 'array',
items: <IJSONSchema>{
type: 'object',
properties: {
name: {
type: 'string'
}
}
},
default: <Array<IProfilerSessionTemplate>>[
{
name: 'Standard',
events: [
{
name: 'Audit Login',
optionalColumns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'ClientProcessID', 'SPID', 'StartTime', 'BinaryData']
},
{
name: 'Audit Logout',
optionalColumns: ['ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime']
},
{
name: 'ExistingConnection',
optionalColumns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime', 'BinaryData']
},
{
name: 'RPC:Completed',
optionalColumns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime', 'BinaryData']
},
{
name: 'SQL:BatchCompleted',
optionalColumns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime', 'BinaryData']
},
{
name: 'SQL:BatchStarting',
optionalColumns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'ClientProcessID', 'SPID', 'StartTime']
const profilerViewTemplateSchema: IJSONSchema = {
description: nls.localize('profiler.settings.viewTemplates', "Specifies view templates"),
type: 'array',
items: <IJSONSchema>{
type: 'object',
properties: {
name: {
type: 'string'
}
],
view: {
events: [
}
},
default: <Array<IProfilerViewTemplate>>[
{
name: 'Standard View',
columns: [
{
name: 'Audit Login',
columns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'ClientProcessID', 'SPID', 'StartTime']
name: 'EventClass',
eventsMapped: ['name']
},
{
name: 'Audit Logout',
columns: ['ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime']
name: 'TextData',
eventsMapped: ['options_text', 'batch_text']
},
{
name: 'ExistingConnection',
columns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'ClientProcessID', 'SPID', 'StartTime']
name: 'ApplicationName',
width: '1',
eventsMapped: ['client_app_name']
},
{
name: 'RPC:Completed',
columns: ['ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime', 'BinaryData']
name: 'NTUserName',
eventsMapped: ['nt_username']
},
{
name: 'SQL:BatchCompleted',
columns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'CPU', 'Reads', 'Writes', 'Duration', 'ClientProcessID', 'SPID', 'StartTime', 'EndTime', 'BinaryData']
name: 'LoginName',
eventsMapped: ['server_principal_name']
},
{
name: 'SQL:BatchStarting',
columns: ['TextData', 'ApplicationName', 'NTUserName', 'LoginName', 'ClientProcessID', 'SPID', 'StartTime']
name: 'ClientProcessID',
eventsMapped: ['client_pid']
},
{
name: 'SPID',
eventsMapped: ['session_id']
},
{
name: 'StartTime',
eventsMapped: ['timestamp']
},
{
name: 'CPU',
eventsMapped: ['cpu_time']
},
{
name: 'Reads',
eventsMapped: ['logical_reads']
},
{
name: 'Writes',
eventsMapped: ['writes']
},
{
name: 'Duration',
eventsMapped: ['duration']
}
]
},
{
name: 'TSQL View',
columns: [
{
name: 'EventClass',
eventsMapped: ['name']
},
{
name: 'TextData',
eventsMapped: ['options_text', 'batch_text']
},
{
name: 'SPID',
eventsMapped: ['session_id']
},
{
name: 'StartTime',
eventsMapped: ['timestamp']
}
]
},
{
name: 'Tuning View',
columns: [
{
name: 'EventClass',
eventsMapped: ['name']
},
{
name: 'TextData',
eventsMapped: ['options_text', 'batch_text']
},
{
name: 'Duration',
eventsMapped: ['duration']
},
{
name: 'SPID',
eventsMapped: ['session_id']
},
{
name: 'DatabaseID',
eventsMapped: ['database_id']
},
{
name: 'DatabaseName',
eventsMapped: ['database_name']
},
{
name: 'ObjectType',
eventsMapped: ['object_type']
},
{
name: 'LoginName',
eventsMapped: ['server_principal_name']
}
]
},
{
name: 'TSQL_Locks View',
columns: [
{
name: 'EventClass',
eventsMapped: ['name']
},
{
name: 'TextData',
eventsMapped: ['options_text', 'batch_text']
},
{
name: 'ApplicationName',
eventsMapped: ['client_app_name']
},
{
name: 'NTUserName',
eventsMapped: ['nt_username']
},
{
name: 'LoginName',
eventsMapped: ['server_principal_name']
},
{
name: 'ClientProcessID',
eventsMapped: ['client_pid']
},
{
name: 'SPID',
eventsMapped: ['session_id']
},
{
name: 'StartTime',
eventsMapped: ['timestamp']
},
{
name: 'CPU',
eventsMapped: ['cpu_time']
},
{
name: 'Reads',
eventsMapped: ['logical_reads']
},
{
name: 'Writes',
eventsMapped: ['writes']
},
{
name: 'Duration',
eventsMapped: ['duration']
}
]
},
{
name: 'TSQL_Duration View',
columns: [
{
name: 'EventClass',
eventsMapped: ['name']
},
{
name: 'Duration',
eventsMapped: ['duration']
},
{
name: 'TextData',
eventsMapped: ['options_text', 'batch_text']
},
{
name: 'SPID',
eventsMapped: ['session_id']
}
]
}
}
]
};
]
};
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
const dashboardConfig: IConfigurationNode = {
id: 'Profiler',
type: 'object',
properties: {
[PROFILER_SESSION_TEMPLATE_SETTINGS]: profilerSessionTemplateSchema
[PROFILER_VIEW_TEMPLATE_SETTINGS]: profilerViewTemplateSchema
}
};

View File

@@ -55,13 +55,17 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'profiler.newProfiler',
weight: KeybindingsRegistry.WEIGHT.builtinExtension(),
when: undefined,
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_P,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_P },
primary: KeyMod.Alt | KeyCode.KEY_P,
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.KEY_P },
handler: CommandsRegistry.getCommand('profiler.newProfiler').handler
});
CommandsRegistry.registerCommand({
id: 'profiler.start',
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'profiler.toggleStartStop',
weight: KeybindingsRegistry.WEIGHT.editorContrib(),
when: undefined,
primary: KeyMod.Alt | KeyCode.KEY_S,
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.KEY_S },
handler: (accessor: ServicesAccessor) => {
let profilerService: IProfilerService = accessor.get(IProfilerService);
let editorService: IWorkbenchEditorService = accessor.get(IWorkbenchEditorService);
@@ -69,23 +73,14 @@ CommandsRegistry.registerCommand({
let activeEditor = editorService.getActiveEditor();
if (activeEditor instanceof ProfilerEditor) {
let profilerInput = activeEditor.input;
return profilerService.startSession(profilerInput.id);
if (profilerInput.state.isRunning){
return profilerService.stopSession(profilerInput.id);
} else {
// clear data when profiler is started
profilerInput.data.clear();
return profilerService.startSession(profilerInput.id);
}
}
return TPromise.as(false);
}
});
CommandsRegistry.registerCommand({
id: 'profiler.stop',
handler: (accessor: ServicesAccessor) => {
let profilerService: IProfilerService = accessor.get(IProfilerService);
let editorService: IWorkbenchEditorService = accessor.get(IWorkbenchEditorService);
let activeEditor = editorService.getActiveEditor();
if (activeEditor instanceof ProfilerEditor) {
let profilerInput = activeEditor.input;
return profilerService.stopSession(profilerInput.id);
}
return TPromise.as(false);
}
});

View File

@@ -360,7 +360,10 @@ export class ProfilerColumnEditorDialog extends Modal {
super.onAccept(e);
}
// currently not used, this dialog is a work in progress
// tracked in issue #1545 https://github.com/Microsoft/sqlopsstudio/issues/1545
private _updateInput(): void {
/*
this._element.getUnsortedChildren().forEach(e => {
let origEvent = this._input.sessionTemplate.view.events.find(i => i.name === e.id);
if (e.indeterminate) {
@@ -387,9 +390,13 @@ export class ProfilerColumnEditorDialog extends Modal {
}, []);
newColumns.unshift('EventClass');
this._input.setColumns(newColumns);
*/
}
// currently not used, this dialog is a work in progress
// tracked in issue #1545 https://github.com/Microsoft/sqlopsstudio/issues/1545
private _updateList(): void {
/*
this._element = new SessionItem(this._input.sessionTemplate.name, this._selectedValue === 0 ? 'event' : 'column');
this._input.sessionTemplate.events.forEach(item => {
let event = new EventItem(item.name, this._element);
@@ -402,6 +409,7 @@ export class ProfilerColumnEditorDialog extends Modal {
});
this._tree.setInput(this._element);
this._tree.layout(DOM.getTotalHeight(this._treeContainer));
*/
}
protected layout(height?: number): void {

View File

@@ -8,7 +8,7 @@ import { ProfilerInput } from './profilerInput';
import { TabbedPanel } from 'sql/base/browser/ui/panel/panel';
import { Table } from 'sql/base/browser/ui/table/table';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { IProfilerService, IProfilerSessionTemplate } from 'sql/parts/profiler/service/interfaces';
import { IProfilerService, IProfilerViewTemplate } from 'sql/parts/profiler/service/interfaces';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { attachTableStyler } from 'sql/common/theme/styler';
import { IProfilerStateChangedEvent } from './profilerState';
@@ -114,8 +114,8 @@ export class ProfilerEditor extends BaseEditor {
private _profilerEditorContextKey: IContextKey<boolean>;
private _sessionTemplateSelector: SelectBox;
private _sessionTemplates: Array<IProfilerSessionTemplate>;
private _viewTemplateSelector: SelectBox;
private _viewTemplates: Array<IProfilerViewTemplate>;
// Actions
private _connectAction: Actions.ProfilerConnect;
@@ -125,6 +125,7 @@ export class ProfilerEditor extends BaseEditor {
private _autoscrollAction: Actions.ProfilerAutoScroll;
private _collapsedPanelAction: Actions.ProfilerCollapsablePanelAction;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IWorkbenchThemeService themeService: IWorkbenchThemeService,
@@ -189,27 +190,27 @@ export class ProfilerEditor extends BaseEditor {
this._connectAction = this._instantiationService.createInstance(Actions.ProfilerConnect, Actions.ProfilerConnect.ID, Actions.ProfilerConnect.LABEL);
this._autoscrollAction = this._instantiationService.createInstance(Actions.ProfilerAutoScroll, Actions.ProfilerAutoScroll.ID, Actions.ProfilerAutoScroll.LABEL);
this._sessionTemplates = this._profilerService.getSessionTemplates();
this._sessionTemplateSelector = new SelectBox(this._sessionTemplates.map(i => i.name), 'Standard', this._contextViewService);
this._register(this._sessionTemplateSelector.onDidSelect(e => {
this._viewTemplates = this._profilerService.getViewTemplates();
this._viewTemplateSelector = new SelectBox(this._viewTemplates.map(i => i.name), 'Standard View', this._contextViewService);
this._register(this._viewTemplateSelector.onDidSelect(e => {
if (this.input) {
this.input.sessionTemplate = this._sessionTemplates.find(i => i.name === e.selected);
this.input.viewTemplate = this._viewTemplates.find(i => i.name === e.selected);
}
}));
let dropdownContainer = document.createElement('div');
dropdownContainer.style.width = '150px';
this._sessionTemplateSelector.render(dropdownContainer);
this._viewTemplateSelector.render(dropdownContainer);
this._register(attachSelectBoxStyler(this._sessionTemplateSelector, this.themeService));
this._register(attachSelectBoxStyler(this._viewTemplateSelector, this.themeService));
this._actionBar.setContent([
{ action: this._startAction },
{ action: this._stopAction },
{ element: dropdownContainer },
{ element: Taskbar.createTaskbarSeparator() },
{ action: this._pauseAction },
{ action: this._autoscrollAction },
{ action: this._instantiationService.createInstance(Actions.ProfilerClear, Actions.ProfilerClear.ID, Actions.ProfilerClear.LABEL) }
{ action: this._instantiationService.createInstance(Actions.ProfilerClear, Actions.ProfilerClear.ID, Actions.ProfilerClear.LABEL) },
{ element: dropdownContainer },
]);
}
@@ -340,10 +341,10 @@ export class ProfilerEditor extends BaseEditor {
return super.setInput(input, options).then(() => {
this._profilerTableEditor.setInput(input);
if (input.sessionTemplate) {
this._sessionTemplateSelector.selectWithOptionName(input.sessionTemplate.name);
if (input.viewTemplate) {
this._viewTemplateSelector.selectWithOptionName(input.viewTemplate.name);
} else {
input.sessionTemplate = this._sessionTemplates.find(i => i.name === 'Standard');
input.viewTemplate = this._viewTemplates.find(i => i.name === 'Standard View');
}
this._actionBar.context = input;
@@ -401,10 +402,7 @@ export class ProfilerEditor extends BaseEditor {
if (e.isConnected) {
this._connectAction.connected = this.input.state.isConnected;
if (this.input.state.isConnected) {
this._sessionTemplateSelector.disable();
} else {
this._sessionTemplateSelector.enable();
if (!this.input.state.isConnected) {
this._startAction.enabled = this.input.state.isConnected;
this._stopAction.enabled = false;
this._pauseAction.enabled = false;

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { IProfilerSession, IProfilerService, ProfilerSessionID, IProfilerSessionTemplate } from 'sql/parts/profiler/service/interfaces';
import { IProfilerSession, IProfilerService, ProfilerSessionID, IProfilerViewTemplate } from 'sql/parts/profiler/service/interfaces';
import { ProfilerState } from './profilerState';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
@@ -28,7 +28,10 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
private _id: ProfilerSessionID;
private _state: ProfilerState;
private _columns: string[] = [];
private _sessionTemplate: IProfilerSessionTemplate;
private _viewTemplate: IProfilerViewTemplate;
// mapping of event categories to what column they display under
// used for coallescing multiple events with different names to the same column
private _columnMapping: { [event: string]: string } = {};
private _onColumnsChanged = new Emitter<Slick.Column<Slick.SlickData>[]>();
public onColumnsChanged: Event<Slick.Column<Slick.SlickData>[]> = this._onColumnsChanged.event;
@@ -63,22 +66,26 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
this._data = new TableDataView<Slick.SlickData>(undefined, searchFn);
}
public set sessionTemplate(template: IProfilerSessionTemplate) {
this._sessionTemplate = template;
let newColumns = this.sessionTemplate.view.events.reduce<Array<string>>((p, e) => {
e.columns.forEach(c => {
if (!p.includes(c)) {
p.push(c);
}
});
public set viewTemplate(template: IProfilerViewTemplate) {
this._data.clear();
this._viewTemplate = template;
let newColumns = this._viewTemplate.columns.reduce<Array<string>>((p, e) => {
p.push(e.name);
return p;
}, []);
newColumns.unshift('EventClass');
this.setColumns(newColumns);
let newMapping: { [event: string]: string } = {};
this._viewTemplate.columns.forEach(c => {
c.eventsMapped.forEach(e => {
newMapping[e] = c.name;
});
});
this.setColumnMapping(newColumns, newMapping);
}
public get sessionTemplate(): IProfilerSessionTemplate {
return this._sessionTemplate;
public get viewTemplate(): IProfilerViewTemplate {
return this._viewTemplate;
}
public getTypeId(): string {
@@ -117,6 +124,12 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
this._onColumnsChanged.fire(this.columns);
}
public setColumnMapping(columns: Array<string>, mapping: { [event: string]: string }) {
this._columns = columns;
this._columnMapping = mapping;
this._onColumnsChanged.fire(this.columns);
}
public get id(): ProfilerSessionID {
return this._id;
}
@@ -140,51 +153,22 @@ export class ProfilerInput extends EditorInput implements IProfilerSession {
}
public onMoreRows(eventMessage: sqlops.ProfilerSessionEvents) {
if (eventMessage.eventsLost){
if (eventMessage.eventsLost) {
this._notificationService.warn(nls.localize("profiler.eventsLost", "The XEvent Profiler session for {0} has lost events.", this._connection.serverName));
}
for (let i: number = 0; i < eventMessage.events.length && i < 500; ++i) {
for (let i: number = 0; i < eventMessage.events.length && i < 500; ++i) {
let e: sqlops.ProfilerEvent = eventMessage.events[i];
let data = {};
data['EventClass'] = e.name;
data['EventClass'] = e.name;
data['StartTime'] = e.timestamp;
const columns = [
'TextData',
'ApplicationName',
'NTUserName',
'LoginName',
'CPU',
'Reads',
'Writes',
'Duration',
'ClientProcessID',
'SPID',
'StartTime',
'EndTime',
'BinaryData'
];
let columnNameMap: Map<string, string> = new Map<string, string>();
columnNameMap['client_app_name'] = 'ApplicationName';
columnNameMap['nt_username'] = 'NTUserName';
columnNameMap['options_text'] = 'TextData';
columnNameMap['server_principal_name'] = 'LoginName';
columnNameMap['session_id'] = 'SPID';
columnNameMap['batch_text'] = 'TextData';
columnNameMap['cpu_time'] = 'CPU';
columnNameMap['duration'] = 'Duration';
columnNameMap['logical_reads'] = 'Reads';
columnNameMap['event_sequence'] = 'EventSequence';
columnNameMap['client_pid'] = 'ClientProcessID';
columnNameMap['writes'] = 'Writes';
// Using ' ' instead of '' fixed the error where clicking through events
// with empty text fields causes future text panes to be highlighted.
// This is a temporary fix, and should be changed before the July release
// This is a temporary fix
data['TextData'] = ' ';
for (let key in e.values) {
let columnName = columnNameMap[key];
let columnName = this._columnMapping[key];
if (columnName) {
let value = e.values[key];
data[columnName] = value;

View File

@@ -15,7 +15,7 @@ export const IProfilerService = createDecorator<IProfilerService>(PROFILER_SERVI
export type ProfilerSessionID = string;
export const PROFILER_SESSION_TEMPLATE_SETTINGS = 'profiler.sessionTemplates';
export const PROFILER_VIEW_TEMPLATE_SETTINGS = 'profiler.viewTemplates';
export const PROFILER_SETTINGS = 'profiler';
/**
@@ -84,7 +84,7 @@ export interface IProfilerService {
* @returns An array of session templates that match the provider passed, if passed, and generic ones (no provider specified),
* otherwise returns all session templates
*/
getSessionTemplates(providerId?: string): Array<IProfilerSessionTemplate>;
getViewTemplates(providerId?: string): Array<IProfilerViewTemplate>;
/**
* Launches the dialog for editing the view columns of a profiler session template for the given input
* @param input input object that contains the necessary information which will be modified based on used input
@@ -93,25 +93,16 @@ export interface IProfilerService {
}
export interface IProfilerSettings {
sessionTemplates: Array<IProfilerSessionTemplate>;
viewTemplates: Array<IProfilerViewTemplate>;
}
export interface IEventTemplate {
export interface IColumnViewTemplate {
name: string;
optionalColumns: Array<string>;
width: string;
eventsMapped: Array<string>;
}
export interface IEventViewTemplate {
export interface IProfilerViewTemplate {
name: string;
columns: Array<string>;
}
export interface ISessionViewTemplate {
events: Array<IEventViewTemplate>;
}
export interface IProfilerSessionTemplate {
name: string;
events: Array<IEventTemplate>;
view: ISessionViewTemplate;
columns: Array<IColumnViewTemplate>;
}

View File

@@ -5,7 +5,7 @@
import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionType, RunQueryOnConnectionMode } from 'sql/parts/connection/common/connectionManagement';
import {
ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerSessionTemplate,
ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerViewTemplate,
PROFILER_SETTINGS, IProfilerSettings
} from './interfaces';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
@@ -128,13 +128,13 @@ export class ProfilerService implements IProfilerService {
}
}
public getSessionTemplates(provider?: string): Array<IProfilerSessionTemplate> {
public getViewTemplates(provider?: string): Array<IProfilerViewTemplate> {
let config = <IProfilerSettings>this._configurationService.getValue(PROFILER_SETTINGS);
if (provider) {
return config.sessionTemplates;
return config.viewTemplates;
} else {
return config.sessionTemplates;
return config.viewTemplates;
}
}

View File

@@ -275,6 +275,11 @@ let registryProperties = {
'description': localize('sql.saveAsCsv.includeHeaders', '[Optional] When true, column headers are included when saving results as CSV'),
'default': true
},
'sql.saveAsCsv.delimiter': {
'type': 'string',
'description': localize('sql.saveAsCsv.delimiter', '[Optional] The custom delimiter to use between values when saving as CSV'),
'default': ','
},
'sql.copyIncludeHeaders': {
'type': 'boolean',
'description': localize('sql.copyIncludeHeaders', '[Optional] Configuration options for copying results from the Results View'),

View File

@@ -21,6 +21,7 @@ export interface IQueryManagementService {
_serviceBrand: any;
addQueryRequestHandler(queryType: string, runner: IQueryRequestHandler): IDisposable;
isProviderRegistered(providerId: string): boolean;
registerRunner(runner: QueryRunner, uri: string): void;
cancelQuery(ownerUri: string): Thenable<sqlops.QueryCancelResult>;
@@ -82,7 +83,6 @@ export interface IQueryRequestHandler {
}
export class QueryManagementService implements IQueryManagementService {
public static readonly DefaultQueryType = 'MSSQL';
public _serviceBrand: any;
private _requestHandlers = new Map<string, IQueryRequestHandler>();
@@ -143,6 +143,11 @@ export class QueryManagementService implements IQueryManagementService {
};
}
public isProviderRegistered(providerId: string): boolean {
let handler = this._requestHandlers.get(providerId);
return !!handler;
}
private addTelemetry(eventName: string, ownerUri: string, runOptions?: sqlops.ExecutionPlanOptions): void {
let providerId: string = this._connectionService.getProviderIdFromUri(ownerUri);
let data: TelemetryUtils.IConnectionTelemetryData = {

View File

@@ -186,7 +186,11 @@ export class ResultSerializer {
if (saveConfig.includeHeaders !== undefined) {
saveResultsParams.includeHeaders = saveConfig.includeHeaders;
}
if (saveConfig.delimiter !== undefined) {
saveResultsParams.delimiter = saveConfig.delimiter;
}
}
return saveResultsParams;
}
@@ -202,6 +206,7 @@ export class ResultSerializer {
// and we want to have just 1 setting that lists this.
let config = this.getConfigForCsv();
config.resultFormat = SaveFormat.EXCEL;
config.delimiter = undefined;
return config;
}

View File

@@ -13,6 +13,7 @@ import { CommonModule, APP_BASE_HREF } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { DialogContainer } from 'sql/platform/dialog/dialogContainer.component';
import { WizardNavigation } from 'sql/platform/dialog/wizardNavigation.component';
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.component';
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
@@ -37,12 +38,13 @@ export const DialogModule = (params, selector: string, instantiationService: IIn
SelectBox,
InputBox,
DialogContainer,
WizardNavigation,
ModelViewContent,
ModelComponentWrapper,
ComponentHostDirective,
...extensionComponents
],
entryComponents: [DialogContainer, ...extensionComponents],
entryComponents: [DialogContainer, WizardNavigation, ...extensionComponents],
imports: [
FormsModule,
CommonModule,
@@ -65,7 +67,8 @@ export const DialogModule = (params, selector: string, instantiationService: IIn
}
ngDoBootstrap(appRef: ApplicationRef) {
const factoryWrapper: any = this._resolver.resolveComponentFactory(DialogContainer);
let componentClass = this.selector.startsWith(WizardNavigation.SELECTOR) ? WizardNavigation : DialogContainer;
const factoryWrapper: any = this._resolver.resolveComponentFactory<WizardNavigation | DialogContainer>(componentClass);
factoryWrapper.factory.selector = this.selector;
appRef.bootstrap(factoryWrapper);
}

View File

@@ -124,7 +124,7 @@ export class DialogModal extends Modal {
}
public async done(): Promise<void> {
if (this._dialog.okButton.enabled) {
if (this._doneButton.enabled) {
if (await this._dialog.validateClose()) {
this._onDone.fire();
this.dispose();

View File

@@ -5,7 +5,7 @@
.dialogModal-body {
display: flex;
flex-direction: column;
flex-direction: row;
height: 100%;
width: 100%;
min-width: 500px;

View File

@@ -0,0 +1,94 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.wizardNavigation-container {
display: flex;
flex-direction: column;
width: 80px;
height: calc(100% + 25px);
margin-top: -25px;
}
.hc-black .wizardNavigation-container {
border-right-color: #2b56f2;
border-right-style: solid;
border-right-width: 1px;
background-color: unset;
}
.wizardNavigation-pageNumber {
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-height: 130px;
}
.wizardNavigation-pageNumber a {
text-decoration: none;
}
.wizardNavigation-dot {
height: 30px;
width: 30px;
background-color: rgb(200, 200, 200);
color: white;
border-radius: 50%;
border-style: none;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
}
.hc-black .wizardNavigation-dot {
flex-grow: 1;
background-color: unset;
border-style: solid;
border-width: 1px;
border-color: white;
}
.wizardNavigation-connector {
width: 3px;
display: inline-block;
flex-grow: 1;
background-color: rgb(200, 200, 200);
}
.hc-black .wizardNavigation-connector {
display: none;
}
.wizardNavigation-connector.active,
.wizardNavigation-dot.active {
background-color: rgb(9, 109, 201);
}
.hc-black .wizardNavigation-dot.active {
border-color: #2b56f2;
background-color: unset;
border-style: solid;
}
.hc-black .wizardNavigation-dot.active:hover,
.hc-black .wizardNavigation-dot.active.currentPage:hover {
border-color: #F38518;
border-style: dashed;
}
.wizardNavigation-dot.active.currentPage {
border-style: double;
}
.hc-black .wizardNavigation-dot.active.currentPage {
border-style: solid;
border-color: #F38518;
}
.wizardNavigation-connector.invisible {
visibility: hidden;
}

View File

@@ -8,23 +8,22 @@
import 'vs/css!./media/dialogModal';
import { Modal, IModalOptions } from 'sql/base/browser/ui/modal/modal';
import { attachModalDialogStyler } from 'sql/common/theme/styler';
import { Wizard, Dialog, DialogButton, WizardPage } from 'sql/platform/dialog/dialogTypes';
import { Wizard, DialogButton, WizardPage } from 'sql/platform/dialog/dialogTypes';
import { DialogPane } from 'sql/platform/dialog/dialogPane';
import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes';
import { DialogModule } from 'sql/platform/dialog/dialog.module';
import { Button } from 'vs/base/browser/ui/button/button';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { Builder } from 'vs/base/browser/builder';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { localize } from 'vs/nls';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Emitter } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { DialogMessage, MessageLevel } from '../../workbench/api/common/sqlExtHostTypes';
export class WizardModal extends Modal {
private _dialogPanes = new Map<WizardPage, DialogPane>();
@@ -128,6 +127,8 @@ export class WizardModal extends Modal {
this._body = bodyBuilder.getHTMLElement();
});
this.initializeNavigation(this._body);
this._wizard.pages.forEach(page => {
this.registerPage(page);
});
@@ -201,13 +202,29 @@ export class WizardModal extends Modal {
}
}
/**
* Bootstrap angular for the wizard's left nav bar
*/
private initializeNavigation(bodyContainer: HTMLElement) {
bootstrapAngular(this._instantiationService,
DialogModule,
bodyContainer,
'wizard-navigation',
{
wizard: this._wizard,
navigationHandler: (index: number) => this.showPage(index, index > this._wizard.currentPage)
},
undefined,
() => undefined);
}
public open(): void {
this.showPage(0, false);
this.show();
}
public async done(validate: boolean = true): Promise<void> {
if (this._wizard.doneButton.enabled) {
if (this._doneButton.enabled) {
if (validate && !await this._wizard.validateNavigation(undefined)) {
return;
}
@@ -242,7 +259,13 @@ export class WizardModal extends Modal {
* Overridable to change behavior of enter key
*/
protected onAccept(e: StandardKeyboardEvent) {
this.done();
if (this._wizard.currentPage === this._wizard.pages.length - 1) {
this.done();
} else {
if (this._nextButton.enabled) {
this.showPage(this._wizard.currentPage + 1);
}
}
}
public dispose(): void {

View File

@@ -0,0 +1,91 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/wizardNavigation';
import { Component, Inject, forwardRef, ElementRef, AfterViewInit, ChangeDetectorRef, ViewChild } from '@angular/core';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { Event, Emitter } from 'vs/base/common/event';
import { Wizard } from './dialogTypes';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { attachStyler } from 'vs/platform/theme/common/styler';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
export class WizardNavigationParams implements IBootstrapParams {
wizard: Wizard;
navigationHandler: (index: number) => void;
}
@Component({
selector: WizardNavigation.SELECTOR,
providers: [],
template: `
<div #container class="wizardNavigation-container">
<ng-container *ngFor="let item of _params.wizard.pages; let i = index">
<div class="wizardNavigation-pageNumber">
<div class="wizardNavigation-connector" [ngClass]="{'invisible': !hasTopConnector(i), 'active': isActive(i)}"></div>
<a [attr.href]="isActive(i) ? '' : null" [title]="item.title">
<span class="wizardNavigation-dot" [ngClass]="{'active': isActive(i), 'currentPage': isCurrentPage(i)}" (click)="navigate(i)">{{i+1}}</span>
</a>
<div class="wizardNavigation-connector" [ngClass]="{'invisible': !hasBottomConnector(i), 'active': isActive(i)}"></div>
</div>
</ng-container>
</div>
`
})
export class WizardNavigation implements AfterViewInit {
public static readonly SELECTOR = 'wizard-navigation';
private _onResize = new Emitter<void>();
public readonly onResize: Event<void> = this._onResize.event;
@ViewChild('container', { read: ElementRef }) private _container: ElementRef;
constructor(
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
@Inject(IBootstrapParams) private _params: WizardNavigationParams,
@Inject(IWorkbenchThemeService) private _themeService: IWorkbenchThemeService) {
}
ngAfterViewInit() {
this._themeService.onThemeChange(() => this.style());
this.style();
this._params.wizard.onPageChanged(() => this._changeRef.detectChanges());
}
hasTopConnector(index: number): boolean {
return index > 0;
}
hasBottomConnector(index: number): boolean {
return index + 1 !== this._params.wizard.pages.length;
}
isActive(index: number): boolean {
return index <= this._params.wizard.currentPage;
}
isCurrentPage(index: number): boolean {
return index === this._params.wizard.currentPage;
}
navigate(index: number): void {
if (this.isActive(index)) {
this._params.navigationHandler(index);
}
}
private style(): void {
let theme = this._themeService.getTheme();
let navigationBackgroundColor = theme.getColor(SIDE_BAR_BACKGROUND);
if (theme.type === 'light') {
navigationBackgroundColor = navigationBackgroundColor.lighten(0.03);
} else if (theme.type === 'dark') {
navigationBackgroundColor = navigationBackgroundColor.darken(0.03);
}
(this._container.nativeElement as HTMLElement).style.backgroundColor = navigationBackgroundColor.toString();
}
}

View File

@@ -6,7 +6,7 @@
import { DataService } from 'sql/parts/grid/services/dataService';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ConnectionContextkey } from 'sql/parts/connection/common/connectionContextKey';
import { ConnectionContextKey } from 'sql/parts/connection/common/connectionContextKey';
import { IBootstrapParams } from './bootstrapService';
export interface IQueryComponentParams extends IBootstrapParams {
@@ -21,7 +21,7 @@ export interface IDefaultComponentParams extends IBootstrapParams {
connection: IConnectionProfile;
ownerUri: string;
scopedContextService: IContextKeyService;
connectionContextKey: ConnectionContextkey;
connectionContextKey: ConnectionContextKey;
}
export interface IDashboardComponentParams extends IDefaultComponentParams {

View File

@@ -17,7 +17,7 @@ import { IAdminService } from 'sql/parts/admin/common/adminService';
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { ConnectionContextkey } from 'sql/parts/connection/common/connectionContextKey';
import { ConnectionContextKey } from 'sql/parts/connection/common/connectionContextKey';
import { ProviderMetadata, DatabaseInfo, SimpleExecuteResult } from 'sqlops';
@@ -47,7 +47,7 @@ export class SingleConnectionManagementService {
constructor(
private _connectionService: IConnectionManagementService,
private _uri: string,
private _contextKey: ConnectionContextkey
private _contextKey: ConnectionContextKey
) { }
public changeDatabase(name: string): Thenable<boolean> {
@@ -104,7 +104,7 @@ export class CommonServiceInterface extends AngularDisposable {
protected _singleQueryManagementService: SingleQueryManagementService;
public scopedContextKeyService: IContextKeyService;
protected _connectionContextKey: ConnectionContextkey;
protected _connectionContextKey: ConnectionContextKey;
constructor(
@Inject(IBootstrapParams) protected _params: IDefaultComponentParams,

View File

@@ -6,7 +6,7 @@
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import * as DOM from 'vs/base/browser/dom';
import * as sqlops from 'sqlops';
export const IDashboardService = createDecorator<IDashboardService>('dashboardService');
@@ -16,8 +16,11 @@ export interface IDashboardService {
_serviceBrand: any;
readonly onDidOpenDashboard: Event<sqlops.DashboardDocument>;
readonly onDidChangeToDashboard: Event<sqlops.DashboardDocument>;
readonly onLayout: Event<DOM.Dimension>;
openDashboard(document: sqlops.DashboardDocument): void;
changeToDashboard(document: sqlops.DashboardDocument): void;
layout(dimension: DOM.Dimension): void;
}

View File

@@ -5,9 +5,8 @@
'use strict';
import { IDashboardService } from './dashboardService';
import { Event, Emitter } from 'vs/base/common/event';
import * as DOM from 'vs/base/browser/dom';
import * as sqlops from 'sqlops';
export class DashboardService implements IDashboardService {
@@ -18,6 +17,9 @@ export class DashboardService implements IDashboardService {
private _onDidChangeToDashboard = new Emitter<sqlops.DashboardDocument>();
public readonly onDidChangeToDashboard: Event<sqlops.DashboardDocument> = this._onDidChangeToDashboard.event;
private _onLayout = new Emitter<DOM.Dimension>();
public readonly onLayout: Event<DOM.Dimension> = this._onLayout.event;
public openDashboard(document: sqlops.DashboardDocument): void {
this._onDidOpenDashboard.fire(document);
}
@@ -25,4 +27,8 @@ export class DashboardService implements IDashboardService {
public changeToDashboard(document: sqlops.DashboardDocument): void {
this._onDidChangeToDashboard.fire(document);
}
public layout(dimension: DOM.Dimension): void {
this._onLayout.fire(dimension);
}
}

View File

@@ -25,6 +25,11 @@ export interface IScriptingService {
*/
registerProvider(providerId: string, provider: sqlops.ScriptingProvider): void;
/**
* Specifies whether a provider with a given ID has been registered or not
*/
isProviderRegistered(providerId: string): boolean;
/**
* Callback method for when scripting is complete
*/
@@ -99,6 +104,11 @@ export class ScriptingService implements IScriptingService {
this._providers[providerId] = provider;
}
public isProviderRegistered(providerId: string): boolean {
let provider = this._providers[providerId];
return !!provider;
}
public dispose(): void {
this.disposables = dispose(this.disposables);
}

144
src/sql/sqlops.d.ts vendored
View File

@@ -770,6 +770,11 @@ declare module 'sqlops' {
rows: DbCellValue[][];
}
export interface SyntaxParseParams {
ownerUri: string;
query: string;
}
export interface SyntaxParseResult {
parseable: boolean;
errorMessages: string[];
@@ -835,6 +840,7 @@ declare module 'sqlops' {
columnStartIndex: number;
columnEndIndex: number;
includeHeaders?: boolean;
delimiter?: string;
}
export interface SaveResultRequestResult {
@@ -928,6 +934,10 @@ declare module 'sqlops' {
subset: EditRow[];
}
/**
* A NodeInfo object represents an element in the Object Explorer tree under
* a connection.
*/
export interface NodeInfo {
nodePath: string;
nodeType: string;
@@ -937,6 +947,116 @@ declare module 'sqlops' {
isLeaf: boolean;
metadata: ObjectMetadata;
errorMessage: string;
/**
* Optional iconType for the object in the tree. Currently this only supports
* an icon name or SqlThemeIcon name, rather than a path to an icon.
* If not defined, the nodeType + nodeStatus / nodeSubType values
* will be used instead.
*/
iconType?: string | SqlThemeIcon;
}
/**
* A reference to a named icon. Currently only a subset of the SQL icons are available.
* Using a theme icon is preferred over a custom icon as it gives theme authors the possibility to change the icons.
*/
export class SqlThemeIcon {
static readonly Folder: SqlThemeIcon;
static readonly Root: SqlThemeIcon;
static readonly Database: SqlThemeIcon;
static readonly Server: SqlThemeIcon;
static readonly ScalarValuedFunction: SqlThemeIcon;
static readonly TableValuedFunction: SqlThemeIcon;
static readonly AggregateFunction: SqlThemeIcon;
static readonly FileGroup: SqlThemeIcon;
static readonly StoredProcedure: SqlThemeIcon;
static readonly UserDefinedTableType: SqlThemeIcon;
static readonly View: SqlThemeIcon;
static readonly Table: SqlThemeIcon;
static readonly HistoryTable: SqlThemeIcon;
static readonly ServerLevelLinkedServerLogin: SqlThemeIcon;
static readonly ServerLevelServerAudit: SqlThemeIcon;
static readonly ServerLevelCryptographicProvider: SqlThemeIcon;
static readonly ServerLevelCredential: SqlThemeIcon;
static readonly ServerLevelServerRole: SqlThemeIcon;
static readonly ServerLevelLogin: SqlThemeIcon;
static readonly ServerLevelServerAuditSpecification: SqlThemeIcon;
static readonly ServerLevelServerTrigger: SqlThemeIcon;
static readonly ServerLevelLinkedServer: SqlThemeIcon;
static readonly ServerLevelEndpoint: SqlThemeIcon;
static readonly Synonym: SqlThemeIcon;
static readonly DatabaseTrigger: SqlThemeIcon;
static readonly Assembly: SqlThemeIcon;
static readonly MessageType: SqlThemeIcon;
static readonly Contract: SqlThemeIcon;
static readonly Queue: SqlThemeIcon;
static readonly Service: SqlThemeIcon;
static readonly Route: SqlThemeIcon;
static readonly DatabaseAndQueueEventNotification: SqlThemeIcon;
static readonly RemoteServiceBinding: SqlThemeIcon;
static readonly BrokerPriority: SqlThemeIcon;
static readonly FullTextCatalog: SqlThemeIcon;
static readonly FullTextStopList: SqlThemeIcon;
static readonly SqlLogFile: SqlThemeIcon;
static readonly PartitionFunction: SqlThemeIcon;
static readonly PartitionScheme: SqlThemeIcon;
static readonly SearchPropertyList: SqlThemeIcon;
static readonly User: SqlThemeIcon;
static readonly Schema: SqlThemeIcon;
static readonly AsymmetricKey: SqlThemeIcon;
static readonly Certificate: SqlThemeIcon;
static readonly SymmetricKey: SqlThemeIcon;
static readonly DatabaseEncryptionKey: SqlThemeIcon;
static readonly MasterKey: SqlThemeIcon;
static readonly DatabaseAuditSpecification: SqlThemeIcon;
static readonly Column: SqlThemeIcon;
static readonly Key: SqlThemeIcon;
static readonly Constraint: SqlThemeIcon;
static readonly Trigger: SqlThemeIcon;
static readonly Index: SqlThemeIcon;
static readonly Statistic: SqlThemeIcon;
static readonly UserDefinedDataType: SqlThemeIcon;
static readonly UserDefinedType: SqlThemeIcon;
static readonly XmlSchemaCollection: SqlThemeIcon;
static readonly SystemExactNumeric: SqlThemeIcon;
static readonly SystemApproximateNumeric: SqlThemeIcon;
static readonly SystemDateAndTime: SqlThemeIcon;
static readonly SystemCharacterString: SqlThemeIcon;
static readonly SystemUnicodeCharacterString: SqlThemeIcon;
static readonly SystemBinaryString: SqlThemeIcon;
static readonly SystemOtherDataType: SqlThemeIcon;
static readonly SystemClrDataType: SqlThemeIcon;
static readonly SystemSpatialDataType: SqlThemeIcon;
static readonly UserDefinedTableTypeColumn: SqlThemeIcon;
static readonly UserDefinedTableTypeKey: SqlThemeIcon;
static readonly UserDefinedTableTypeConstraint: SqlThemeIcon;
static readonly StoredProcedureParameter: SqlThemeIcon;
static readonly TableValuedFunctionParameter: SqlThemeIcon;
static readonly ScalarValuedFunctionParameter: SqlThemeIcon;
static readonly AggregateFunctionParameter: SqlThemeIcon;
static readonly DatabaseRole: SqlThemeIcon;
static readonly ApplicationRole: SqlThemeIcon;
static readonly FileGroupFile: SqlThemeIcon;
static readonly SystemMessageType: SqlThemeIcon;
static readonly SystemContract: SqlThemeIcon;
static readonly SystemService: SqlThemeIcon;
static readonly SystemQueue: SqlThemeIcon;
static readonly Sequence: SqlThemeIcon;
static readonly SecurityPolicy: SqlThemeIcon;
static readonly DatabaseScopedCredential: SqlThemeIcon;
static readonly ExternalResource: SqlThemeIcon;
static readonly ExternalDataSource: SqlThemeIcon;
static readonly ExternalFileFormat: SqlThemeIcon;
static readonly ExternalTable: SqlThemeIcon;
static readonly ColumnMasterKey: SqlThemeIcon;
static readonly ColumnEncryptionKey: SqlThemeIcon;
private constructor(id: string);
/**
* Gets the ID for the theme icon for help in cases where string comparison is needed
*/
public readonly id: string;
}
// Object Explorer interfaces -----------------------------------------------------------------------
@@ -1068,7 +1188,7 @@ declare module 'sqlops' {
}
export enum FrequencyTypes {
Unknown ,
Unknown,
OneTime = 1 << 1,
Daily = 1 << 2,
Weekly = 1 << 3,
@@ -1276,15 +1396,14 @@ declare module 'sqlops' {
job: AgentJobInfo;
}
export interface AgentJobCategory
{
export interface AgentJobCategory {
id: string;
name: string;
}
export interface AgentJobDefaultsResult extends ResultStatus {
owner: string;
categories: AgentJobCategory[];
categories: AgentJobCategory[];
}
export interface CreateAgentJobStepResult extends ResultStatus {
@@ -1384,6 +1503,9 @@ declare module 'sqlops' {
updateProxy(ownerUri: string, originalProxyName: string, proxyInfo: AgentProxyInfo): Thenable<UpdateAgentOperatorResult>;
deleteProxy(ownerUri: string, proxyInfo: AgentProxyInfo): Thenable<ResultStatus>;
// Credential method
getCredentials(ownerUri: string): Thenable<GetCredentialsResult>;
// Job Schedule management methods
getJobSchedules(ownerUri: string): Thenable<AgentJobSchedulesResult>;
createJobSchedule(ownerUri: string, scheduleInfo: AgentJobScheduleInfo): Thenable<CreateAgentJobScheduleResult>;
@@ -1393,6 +1515,20 @@ declare module 'sqlops' {
registerOnUpdated(handler: () => any): void;
}
// Security service interfaces ------------------------------------------------------------------------
export interface CredentialInfo {
id: number;
identity: string;
name: string;
dateLastModified: string;
createDate: string;
providerName: string;
}
export interface GetCredentialsResult extends ResultStatus {
credentials: CredentialInfo[];
}
// Task service interfaces ----------------------------------------------------------------------------
export enum TaskStatus {
notStarted = 0,

View File

@@ -973,4 +973,31 @@ declare module 'sqlops' {
*/
export function getProvidersByType<T extends DataProvider>(providerType: DataProviderType): T[];
}
/**
* Context object passed as an argument to command callbacks.
* Defines the key properties required to identify a node in the object
* explorer tree and take action against it.
*/
export interface ObjectExplorerContext {
/**
* The connection information for the selected object.
* Note that the connection is not guaranteed to be in a connected
* state on click.
*/
connectionProfile: IConnectionProfile;
/**
* Defines whether this is a Connection-level object.
* If not, the object is expected to be a child object underneath
* one of the connections.
*/
isConnectionNode: boolean;
/**
* Node info for objects below a specific connection. This
* may be null for a Connection-level object
*/
nodeInfo: NodeInfo;
}
}

View File

@@ -277,4 +277,102 @@ export enum DeclarativeDataType {
export enum CardType {
VerticalButton = 'VerticalButton',
Details = 'Details'
}
}
export class SqlThemeIcon {
static readonly Folder = new SqlThemeIcon('Folder');
static readonly Root = new SqlThemeIcon('root');
static readonly Database = new SqlThemeIcon('Database');
static readonly Server = new SqlThemeIcon('Server');
static readonly ScalarValuedFunction = new SqlThemeIcon('ScalarValuedFunction');
static readonly TableValuedFunction = new SqlThemeIcon('TableValuedFunction');
static readonly AggregateFunction = new SqlThemeIcon('AggregateFunction');
static readonly FileGroup = new SqlThemeIcon('FileGroup');
static readonly StoredProcedure = new SqlThemeIcon('StoredProcedure');
static readonly UserDefinedTableType = new SqlThemeIcon('UserDefinedTableType');
static readonly View = new SqlThemeIcon('View');
static readonly Table = new SqlThemeIcon('Table');
static readonly HistoryTable = new SqlThemeIcon('HistoryTable');
static readonly ServerLevelLinkedServerLogin = new SqlThemeIcon('ServerLevelLinkedServerLogin');
static readonly ServerLevelServerAudit = new SqlThemeIcon('ServerLevelServerAudit');
static readonly ServerLevelCryptographicProvider = new SqlThemeIcon('ServerLevelCryptographicProvider');
static readonly ServerLevelCredential = new SqlThemeIcon('ServerLevelCredential');
static readonly ServerLevelServerRole = new SqlThemeIcon('ServerLevelServerRole');
static readonly ServerLevelLogin = new SqlThemeIcon('ServerLevelLogin');
static readonly ServerLevelServerAuditSpecification = new SqlThemeIcon('ServerLevelServerAuditSpecification');
static readonly ServerLevelServerTrigger = new SqlThemeIcon('ServerLevelServerTrigger');
static readonly ServerLevelLinkedServer = new SqlThemeIcon('ServerLevelLinkedServer');
static readonly ServerLevelEndpoint = new SqlThemeIcon('ServerLevelEndpoint');
static readonly Synonym = new SqlThemeIcon('Synonym');
static readonly DatabaseTrigger = new SqlThemeIcon('DatabaseTrigger');
static readonly Assembly = new SqlThemeIcon('Assembly');
static readonly MessageType = new SqlThemeIcon('MessageType');
static readonly Contract = new SqlThemeIcon('Contract');
static readonly Queue = new SqlThemeIcon('Queue');
static readonly Service = new SqlThemeIcon('Service');
static readonly Route = new SqlThemeIcon('Route');
static readonly DatabaseAndQueueEventNotification = new SqlThemeIcon('DatabaseAndQueueEventNotification');
static readonly RemoteServiceBinding = new SqlThemeIcon('RemoteServiceBinding');
static readonly BrokerPriority = new SqlThemeIcon('BrokerPriority');
static readonly FullTextCatalog = new SqlThemeIcon('FullTextCatalog');
static readonly FullTextStopList = new SqlThemeIcon('FullTextStopList');
static readonly SqlLogFile = new SqlThemeIcon('SqlLogFile');
static readonly PartitionFunction = new SqlThemeIcon('PartitionFunction');
static readonly PartitionScheme = new SqlThemeIcon('PartitionScheme');
static readonly SearchPropertyList = new SqlThemeIcon('SearchPropertyList');
static readonly User = new SqlThemeIcon('User');
static readonly Schema = new SqlThemeIcon('Schema');
static readonly AsymmetricKey = new SqlThemeIcon('AsymmetricKey');
static readonly Certificate = new SqlThemeIcon('Certificate');
static readonly SymmetricKey = new SqlThemeIcon('SymmetricKey');
static readonly DatabaseEncryptionKey = new SqlThemeIcon('DatabaseEncryptionKey');
static readonly MasterKey = new SqlThemeIcon('MasterKey');
static readonly DatabaseAuditSpecification = new SqlThemeIcon('DatabaseAuditSpecification');
static readonly Column = new SqlThemeIcon('Column');
static readonly Key = new SqlThemeIcon('Key');
static readonly Constraint = new SqlThemeIcon('Constraint');
static readonly Trigger = new SqlThemeIcon('Trigger');
static readonly Index = new SqlThemeIcon('Index');
static readonly Statistic = new SqlThemeIcon('Statistic');
static readonly UserDefinedDataType = new SqlThemeIcon('UserDefinedDataType');
static readonly UserDefinedType = new SqlThemeIcon('UserDefinedType');
static readonly XmlSchemaCollection = new SqlThemeIcon('XmlSchemaCollection');
static readonly SystemExactNumeric = new SqlThemeIcon('SystemExactNumeric');
static readonly SystemApproximateNumeric = new SqlThemeIcon('SystemApproximateNumeric');
static readonly SystemDateAndTime = new SqlThemeIcon('SystemDateAndTime');
static readonly SystemCharacterString = new SqlThemeIcon('SystemCharacterString');
static readonly SystemUnicodeCharacterString = new SqlThemeIcon('SystemUnicodeCharacterString');
static readonly SystemBinaryString = new SqlThemeIcon('SystemBinaryString');
static readonly SystemOtherDataType = new SqlThemeIcon('SystemOtherDataType');
static readonly SystemClrDataType = new SqlThemeIcon('SystemClrDataType');
static readonly SystemSpatialDataType = new SqlThemeIcon('SystemSpatialDataType');
static readonly UserDefinedTableTypeColumn = new SqlThemeIcon('UserDefinedTableTypeColumn');
static readonly UserDefinedTableTypeKey = new SqlThemeIcon('UserDefinedTableTypeKey');
static readonly UserDefinedTableTypeConstraint = new SqlThemeIcon('UserDefinedTableTypeConstraint');
static readonly StoredProcedureParameter = new SqlThemeIcon('StoredProcedureParameter');
static readonly TableValuedFunctionParameter = new SqlThemeIcon('TableValuedFunctionParameter');
static readonly ScalarValuedFunctionParameter = new SqlThemeIcon('ScalarValuedFunctionParameter');
static readonly AggregateFunctionParameter = new SqlThemeIcon('AggregateFunctionParameter');
static readonly DatabaseRole = new SqlThemeIcon('DatabaseRole');
static readonly ApplicationRole = new SqlThemeIcon('ApplicationRole');
static readonly FileGroupFile = new SqlThemeIcon('FileGroupFile');
static readonly SystemMessageType = new SqlThemeIcon('SystemMessageType');
static readonly SystemContract = new SqlThemeIcon('SystemContract');
static readonly SystemService = new SqlThemeIcon('SystemService');
static readonly SystemQueue = new SqlThemeIcon('SystemQueue');
static readonly Sequence = new SqlThemeIcon('Sequence');
static readonly SecurityPolicy = new SqlThemeIcon('SecurityPolicy');
static readonly DatabaseScopedCredential = new SqlThemeIcon('DatabaseScopedCredential');
static readonly ExternalResource = new SqlThemeIcon('ExternalResource');
static readonly ExternalDataSource = new SqlThemeIcon('ExternalDataSource');
static readonly ExternalFileFormat = new SqlThemeIcon('ExternalFileFormat');
static readonly ExternalTable = new SqlThemeIcon('ExternalTable');
static readonly ColumnMasterKey = new SqlThemeIcon('ColumnMasterKey');
static readonly ColumnEncryptionKey = new SqlThemeIcon('ColumnEncryptionKey');
public readonly id: string;
private constructor(id: string) {
this.id = id;
}
}

View File

@@ -604,6 +604,13 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
return this._resolveProvider<sqlops.AgentServicesProvider>(handle).deleteProxy(ownerUri, proxy);
}
/**
* Gets Agent Credentials from server
*/
$getCredentials(handle: number, ownerUri: string): Thenable<sqlops.GetCredentialsResult> {
return this._resolveProvider<sqlops.AgentServicesProvider>(handle).getCredentials(ownerUri);
}
/**
* SQL Agent job data update notification
*/

View File

@@ -366,6 +366,9 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
deleteProxy(connectionUri: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.ResultStatus> {
return self._proxy.$deleteProxy(handle, connectionUri, proxyInfo);
},
getCredentials(connectionUri: string): Thenable<sqlops.GetCredentialsResult> {
return self._proxy.$getCredentials(handle, connectionUri);
}
});
return undefined;

Some files were not shown because too many files have changed in this diff Show More