Compare commits

...

75 Commits

Author SHA1 Message Date
Leila Lali
6680be6a73 adding task integration with wizard and dialog framework (#1929)
* adding task integration with wizard and dialog framework
2018-07-18 16:28:36 -07:00
Karl Burtram
e026ab85a7 Escape the aria string for Edit Data grid (#1958) 2018-07-17 18:45:10 -07:00
Karl Burtram
e53c903205 Update tools service to 1.5.0-alpha.12 2018-07-17 18:44:45 -07:00
Matt Irvine
03dbe8565f Set element text instead of HTML where possible (#1956) 2018-07-17 16:48:38 -07:00
Karl Burtram
708793cb23 Update tools service to 1.5.0-alpha.11 2018-07-17 16:47:31 -07:00
Aditya Bist
43ae4fb0aa Fixed 2 bugs in Agent Steps page. (#1953)
* fixed bug where steps werent being shown

* fixed customer reported issue
2018-07-17 15:49:38 -07:00
Aditya Bist
6b1d552277 Agent/step finishes (#1948)
* misc fixes in dialogs

* removed unused import

* disabled more advanced options
2018-07-17 10:51:31 -07:00
Madeline MacDonald
f24f576b72 Profiler display fixes (#1949)
* Fixing details tab, window resizing, and having profiler options in object explorer

* Fixing displaying connection names

* spacing

* Removing unnecessary code
2018-07-16 17:20:13 -07:00
Matt Irvine
c23328564f Hide correct element when hiding buttons (#1945) 2018-07-16 17:09:44 -07:00
Karl Burtram
cd6dd3dafa Bump Tools Service to 1.5.0-alpha.10 (#1947) 2018-07-16 15:40:41 -07:00
Aditya Bist
4081e15bef misc fixes in dialogs (#1942)
* misc fixes in dialogs

* removed unused import
2018-07-16 15:25:11 -07:00
Karl Burtram
b05e3813d1 Bump product version to 0.31.4 from July Public Preview (#1944) 2018-07-16 15:13:10 -07:00
Karl Burtram
3048311f40 Bump agent extension version to 0.31.4 (#1943) 2018-07-16 15:12:54 -07:00
Madeline MacDonald
1045392d91 Escaping profiler text (#1940) 2018-07-16 13:20:56 -07:00
Madeline MacDonald
7b23ca8ee7 Improving profiler controls and toolbar (#1931)
* Profiler toolbar improvements

* Fixing formatting issues
2018-07-16 11:44:14 -07:00
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
Anthony Dresser
1f3e59c9f9 added default config for timeSeries since it is not the same (#1852) 2018-07-06 13:21:12 -07:00
Karl Burtram
1956078c8c Update config.json 2018-07-06 12:50:06 -07:00
Leila Lali
11230f59fc vbump sqlops version (#1862) 2018-07-06 09:43:06 -07:00
Karl Burtram
21bad7a01f Refresh agent dashboard panel after create\update\delete operations (#1861)
* Edit alert WIP

* A couple alert edit bugs

* Hook up dashboard refresh notification

* Hook onchange event to other agent service calls

* Switch update handler to scalar value

* Add null check on handler callback
2018-07-06 08:57:30 -07:00
Aditya Bist
6f9a27ecc7 Agent - UI changes (#1859)
* fixed regressions, added separator

* finished operator dialog UI
2018-07-05 20:31:54 -07:00
Matt Irvine
c504113d13 Update card layout to give more icon space (#1858) 2018-07-05 14:58:46 -07:00
Karl Burtram
c92ff60592 Update Agent extension package-lock.json (#1857)
* Update Agent extension package-lock.json

* Try removing the pacakge-lock file
2018-07-05 14:30:55 -07:00
Karl Burtram
e9013d1a2a Bump SQL Tools Service to 1.5.0-alpha.3 2018-07-05 13:34:27 -07:00
Matt Irvine
9c4580fe40 Add grouping feature for model view forms (#1853) 2018-07-05 11:36:29 -07:00
Anthony Dresser
cb060cb5db add row status on status bar for queries (#1841) 2018-07-05 10:43:24 -07:00
Karl Burtram
6c3d85cc45 New Operator, Alert and Proxy request handlers (#1846)
* Add agent dialog class

* Rename agent dialog data classes

* Alert dialog data updates

* Create operator and proxy handlers
2018-07-05 08:26:03 -07:00
Aditya Bist
14ae89e87c added common action bar with context based actions for all pages (#1842) 2018-07-03 20:32:04 -07:00
Karl Burtram
24c48f025d Add Delete Alert action implementation (#1840)
* Delete alert WIP 1

* Add agent delete methods

* Add Delete implementation for Jobs, Operators, and Proxies
2018-07-03 19:58:02 -07:00
Anthony Dresser
f0a556f004 add quote to string escape (#1838) 2018-07-03 16:37:44 -07:00
Karl Burtram
fd4d6abb4d Update CHANGELOG.md 2018-07-03 14:01:52 -07:00
Anthony Dresser
41cc839380 add themeing to profiler (#1826) 2018-07-03 13:34:20 -07:00
Karl Burtram
c2a4380b96 Rename Agent dialog classes to remove "Create" (#1837)
* Remove Create from agent dialog names

* Move actions class into common directory
2018-07-03 13:30:16 -07:00
Aditya Bist
6f402ac79f Agent : New Step dialog (#1834)
* added file browser tree to API and dialog

* added callbacks for selected files

* added file browser to step dialog

* remove commented code

* fixed file name bug
2018-07-03 13:07:02 -07:00
Karl Burtram
bf7c1306b1 Agent Tab panel visibility check base class (#1829)
* Agent Tab panel visibility check base class

* Add context menu to tab panel tables for Edit and Delete
2018-07-03 12:42:37 -07:00
Matt Irvine
c1509cf09d Update button icon when icon path changes (#1833) 2018-07-03 11:58:43 -07:00
Anthony Dresser
014bca031c add logic to clean up providers when appropriate (#1824) 2018-07-03 11:49:57 -07:00
Anthony Dresser
4f864fd5bd add escape formatting (#1825) 2018-07-03 11:49:17 -07:00
JASM2007
2da67567e4 Update mssql.JSON (#1803)
I modified lots of snippets to include the database name so that the user can additionally tab through the database name. I made some quality of life change for the user.  I normalized and standardized snippets to look more like one user created the file rather than each script taking on various flavors from the contributors. I tried to write the comments in the snippets in the same style at the majority of the snippets.  I added highlighting in key areas that were missing it or were not replacing the comments. Additionally there was no snippets for sqlCreateIndex and sqlCreateTempTable which I find standard for SQL users.  Let me know your thoughts! No trying to offend anyone if I changed your code.
2018-07-03 08:42:56 -07:00
Karl Burtram
5b19d2b1fc Fill out controls for Alert, Operator and Proxy dialogs (#1827)
* Clean up alert dialog

* Fill in controls for operator and proxy dialogs
2018-07-03 07:34:39 -07:00
Karl Burtram
a6837dcd40 Remove "client" folder in Agent extension (#1820)
* Remove "client" folder in Agent extension

* Alert dialog cleanups
2018-07-02 14:47:00 -07:00
Matt Irvine
af80751a1f Fix linked account error message typo (#1823) 2018-07-02 14:14:10 -07:00
Matt Irvine
dd02597c3b Fix display problems with model view icon components (#1822) 2018-07-02 14:03:22 -07:00
Matt Irvine
2926a3cbd8 Update query editor connection display when connecting via API (#1819) 2018-07-02 13:10:19 -07:00
Matt
b02bb3bfd4 Fixes implicit any type - issue #1814 (#1815) 2018-07-02 09:09:40 -07:00
Karl Burtram
67a4683bb1 Bump Agent and Profiler extension versions (#1812) 2018-06-30 23:02:00 -07:00
192 changed files with 6875 additions and 9281 deletions

View File

@@ -22,7 +22,7 @@ The May release is focused on stabilization and bug fixes leading up to the Buil
* Announcing **Redgate SQL Search** extension available in Extension Manager
* Community Localization available for 10 languages: **German, Spanish, French, Italian, Japanese, Korean, Portuguese, Russian, Simplified Chinese and Traditional Chinese!**
* **GDPR-compliant** build has reduced telemetry collection, improved [opt-out](https://github.com/Microsoft/sqlopsstudio/wiki/How-to-Disable-Telemetry-Reporting) experience and in-product links to [Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement)
* Reduced telemetry collection, improved [opt-out](https://github.com/Microsoft/sqlopsstudio/wiki/How-to-Disable-Telemetry-Reporting) experience and in-product links to [Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement)
* Extension Manager has improved Marketplace experience to easily discover community extensions
* SQL Agent extension Jobs and Job History view improvement
* Updates for **whoisactive** and **Server Reports** extensions

View File

@@ -1,149 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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 * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { CreateAlertData } from '../data/createAlertData';
export class CreateAlertDialog {
// Top level
private readonly DialogTitle: string = 'Create Alert';
private readonly OkButtonText: string = 'OK';
private readonly CancelButtonText: string = 'Cancel';
private readonly GeneralTabText: string = 'Response';
private readonly ResponseTabText: string = 'Steps';
private readonly OptionsTabText: string = 'Options';
private readonly HistoryTabText: string = 'History';
// General tab strings
private readonly NameTextBoxLabel: string = 'Name';
// Response tab strings
private readonly ExecuteJobTextBoxLabel: string = 'Execute Job';
// Options tab strings
private readonly AdditionalMessageTextBoxLabel: string = 'Additional notification message to send';
// History tab strings
private readonly ResetCountTextBoxLabel: string = 'Reset Count';
// UI Components
private dialog: sqlops.window.modelviewdialog.Dialog;
private generalTab: sqlops.window.modelviewdialog.DialogTab;
private responseTab: sqlops.window.modelviewdialog.DialogTab;
private optionsTab: sqlops.window.modelviewdialog.DialogTab;
private historyTab: sqlops.window.modelviewdialog.DialogTab;
private schedulesTable: sqlops.TableComponent;
// General tab controls
private nameTextBox: sqlops.InputBoxComponent;
// Response tab controls
private executeJobTextBox: sqlops.InputBoxComponent;
// Options tab controls
private additionalMessageTextBox: sqlops.InputBoxComponent;
// History tab controls
private resetCountTextBox: sqlops.InputBoxComponent;
private model: CreateAlertData;
private _onSuccess: vscode.EventEmitter<CreateAlertData> = new vscode.EventEmitter<CreateAlertData>();
public readonly onSuccess: vscode.Event<CreateAlertData> = this._onSuccess.event;
constructor(ownerUri: string) {
this.model = new CreateAlertData(ownerUri);
}
public async showDialog() {
await this.model.initialize();
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
this.generalTab = sqlops.window.modelviewdialog.createTab(this.GeneralTabText);
this.responseTab = sqlops.window.modelviewdialog.createTab(this.ResponseTabText);
this.optionsTab = sqlops.window.modelviewdialog.createTab(this.OptionsTabText);
this.historyTab = sqlops.window.modelviewdialog.createTab(this.HistoryTabText);
this.initializeGeneralTab();
this.initializeResponseTab();
this.initializeOptionsTab();
this.initializeHistoryTab();
this.dialog.content = [this.generalTab, this.responseTab, this.optionsTab, this.historyTab];
this.dialog.okButton.onClick(async () => await this.execute());
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);
}
private initializeGeneralTab() {
this.generalTab.registerContent(async view => {
this.nameTextBox = view.modelBuilder.inputBox().component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.nameTextBox,
title: this.NameTextBoxLabel
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
private initializeResponseTab() {
this.responseTab.registerContent(async view => {
this.executeJobTextBox = view.modelBuilder.inputBox().component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.executeJobTextBox,
title: this.ExecuteJobTextBoxLabel
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
private initializeOptionsTab() {
this.optionsTab.registerContent(async view => {
this.additionalMessageTextBox = view.modelBuilder.inputBox().component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.additionalMessageTextBox,
title: this.AdditionalMessageTextBoxLabel
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
private initializeHistoryTab() {
this.historyTab.registerContent(async view => {
this.resetCountTextBox = view.modelBuilder.inputBox().component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.resetCountTextBox,
title: this.ResetCountTextBoxLabel
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
private async execute() {
this.updateModel();
await this.model.save();
this._onSuccess.fire(this.model);
}
private async cancel() {
}
private updateModel() {
}
}

View File

@@ -1,421 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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 * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { CreateStepData } from '../data/createStepData';
import { AgentUtils } from '../agentUtils';
import { CreateJobData } from '../data/createJobData';
export class CreateStepDialog {
// TODO: localize
// Top level
//
private static readonly DialogTitle: string = 'New Job Step';
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';
// 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';
// UI Components
//
private dialog: sqlops.window.modelviewdialog.Dialog;
private generalTab: sqlops.window.modelviewdialog.DialogTab;
private advancedTab: sqlops.window.modelviewdialog.DialogTab;
private nameTextBox: sqlops.InputBoxComponent;
private typeDropdown: sqlops.DropDownComponent;
private runAsDropdown: sqlops.DropDownComponent;
private databaseDropdown: sqlops.DropDownComponent;
private successActionDropdown: sqlops.DropDownComponent;
private failureActionDropdown: sqlops.DropDownComponent;
private commandTextBox: sqlops.InputBoxComponent;
private openButton: sqlops.ButtonComponent;
private parseButton: sqlops.ButtonComponent;
private nextButton: sqlops.ButtonComponent;
private previousButton: sqlops.ButtonComponent;
private retryAttemptsBox: sqlops.InputBoxComponent;
private retryIntervalBox: sqlops.InputBoxComponent;
private appendToExistingFileCheckbox: sqlops.CheckBoxComponent;
private logToTableCheckbox: sqlops.CheckBoxComponent;
private outputFileNameBox: sqlops.InputBoxComponent;
private outputFileBrowserButton: sqlops.ButtonComponent;
private model: CreateStepData;
private ownerUri: string;
private jobName: string;
private server: string;
private stepId: number;
private jobModel: CreateJobData;
constructor(
ownerUri: string,
jobName: string,
server: string,
stepId: number,
jobModel?: CreateJobData
) {
this.model = new CreateStepData(ownerUri);
this.stepId = stepId;
this.ownerUri = ownerUri;
this.jobName = jobName;
this.server = server;
this.jobModel = jobModel;
}
private initializeUIComponents() {
this.dialog = sqlops.window.modelviewdialog.createDialog(CreateStepDialog.DialogTitle);
this.generalTab = sqlops.window.modelviewdialog.createTab(CreateStepDialog.GeneralTabText);
this.advancedTab = sqlops.window.modelviewdialog.createTab(CreateStepDialog.AdvancedTabText);
this.dialog.content = [this.generalTab, this.advancedTab];
this.dialog.okButton.onClick(async () => await this.execute());
this.dialog.okButton.label = CreateStepDialog.OkButtonText;
this.dialog.cancelButton.label = CreateStepDialog.CancelButtonText;
}
private createCommands(view, queryProvider: sqlops.QueryProvider) {
this.openButton = view.modelBuilder.button()
.withProperties({
label: CreateStepDialog.OpenCommandText,
width: '80px'
}).component();
this.parseButton = view.modelBuilder.button()
.withProperties({
label: CreateStepDialog.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};
} else if (result && !result.parseable) {
this.dialog.message = { text: 'The command failed' };
}
});
}
});
this.commandTextBox = view.modelBuilder.inputBox()
.withProperties({
height: 300,
width: 400,
multiline: true,
inputType: 'text'
})
.component();
this.nextButton = view.modelBuilder.button()
.withProperties({
label: CreateStepDialog.NextButtonText,
enabled: false,
width: '80px'
}).component();
this.previousButton = view.modelBuilder.button()
.withProperties({
label: CreateStepDialog.PreviousButtonText,
enabled: false,
width: '80px'
}).component();
}
private createGeneralTab(databases: string[], queryProvider: sqlops.QueryProvider) {
this.generalTab.registerContent(async (view) => {
this.nameTextBox = view.modelBuilder.inputBox()
.withProperties({
}).component();
this.nameTextBox.required = true;
this.typeDropdown = view.modelBuilder.dropDown()
.withProperties({
value: CreateStepDialog.TSQLScript,
values: [CreateStepDialog.TSQLScript]
})
.component();
this.runAsDropdown = view.modelBuilder.dropDown()
.withProperties({
value: '',
values: ['']
})
.component();
this.runAsDropdown.enabled = false;
this.typeDropdown.onValueChanged((type) => {
if (type.selected !== CreateStepDialog.TSQLScript) {
this.runAsDropdown.value = CreateStepDialog.AgentServiceAccount;
this.runAsDropdown.values = [this.runAsDropdown.value];
} else {
this.runAsDropdown.value = '';
this.runAsDropdown.values = [''];
}
});
this.databaseDropdown = view.modelBuilder.dropDown()
.withProperties({
value: databases[0],
values: databases
}).component();
// create the commands section
this.createCommands(view, queryProvider);
let buttonContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
justifyContent: 'space-between',
width: 420
}).withItems([this.openButton, this.parseButton, this.previousButton, this.nextButton], {
flex: '1 1 50%'
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.nameTextBox,
title: 'Step name'
}, {
component: this.typeDropdown,
title: 'Type'
}, {
component: this.runAsDropdown,
title: 'Run as'
}, {
component: this.databaseDropdown,
title: 'Database'
}, {
component: this.commandTextBox,
title: 'Command',
actions: [buttonContainer]
}], {
horizontal: false,
componentWidth: 420
}).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
await view.initializeModel(formWrapper);
});
}
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: CreateStepDialog.NextStep,
values: [CreateStepDialog.NextStep, CreateStepDialog.QuitJobReportingSuccess, CreateStepDialog.QuitJobReportingFailure]
})
.component();
let retryFlexContainer = this.createRetryCounters(view);
this.failureActionDropdown = view.modelBuilder.dropDown()
.withProperties({
value: CreateStepDialog.QuitJobReportingFailure,
values: [CreateStepDialog.QuitJobReportingFailure, CreateStepDialog.NextStep, CreateStepDialog.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'
}).component();
let appendToExistingEntryInTableCheckbox = view.modelBuilder.checkBox()
.withProperties({ label: 'Append output to existing entry in table' }).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();
let logStepOutputHistoryCheckbox = view.modelBuilder.checkBox()
.withProperties({ label: 'Include step output in history' }).component();
let runAsUserOptions = this.createRunAsUserOptions(view);
let formModel = view.modelBuilder.formContainer()
.withFormItems(
[{
component: this.successActionDropdown,
title: CreateStepDialog.SuccessAction
}, {
component: retryFlexContainer,
title: ''
}, {
component: this.failureActionDropdown,
title: CreateStepDialog.FailureAction
}, {
component: optionsGroup,
title: 'Transact-SQL script (T-SQL)'
}, {
component: logToTableContainer,
title: ''
}, {
component: appendCheckboxContainer,
title: ' '
}, {
component: logStepOutputHistoryCheckbox,
title: ''
}, {
component: runAsUserOptions,
title: ''
}], {
componentWidth: 400
}).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
view.initializeModel(formWrapper);
});
}
private createRetryCounters(view) {
this.retryAttemptsBox = view.modelBuilder.inputBox()
.withValidation(component => component.value >= 0)
.withProperties({
inputType: 'number'
})
.component();
this.retryIntervalBox = view.modelBuilder.inputBox()
.withValidation(component => component.value >= 0)
.withProperties({
inputType: 'number'
}).component();
let retryAttemptsContainer = view.modelBuilder.formContainer()
.withFormItems(
[{
component: this.retryAttemptsBox,
title: 'Retry Attempts'
}], {
horizontal: false
})
.component();
let retryIntervalContainer = view.modelBuilder.formContainer()
.withFormItems(
[{
component: this.retryIntervalBox,
title: 'Retry Interval (minutes)'
}], {
horizontal: false
})
.component();
let retryFlexContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
}).withItems([retryAttemptsContainer, retryIntervalContainer]).component();
return retryFlexContainer;
}
private createTSQLOptions(view) {
this.outputFileBrowserButton = view.modelBuilder.button()
.withProperties({ width: '20px', label: '...' }).component();
this.outputFileNameBox = view.modelBuilder.inputBox()
.withProperties({
width: '100px',
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();
let outputFlexBox = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
width: 350
}).withItems([this.outputFileNameBox, outputButtonContainer], {
flex: '1 1 50%'
}).component();
this.appendToExistingFileCheckbox = view.modelBuilder.checkBox()
.withProperties({
label: 'Append output to existing file'
}).component();
this.appendToExistingFileCheckbox.enabled = false;
this.outputFileNameBox.onTextChanged((input) => {
if (input !== '') {
this.appendToExistingFileCheckbox.enabled = true;
} else {
this.appendToExistingFileCheckbox.enabled = false;
}
});
let outputFileForm = view.modelBuilder.formContainer()
.withFormItems([{
component: outputFlexBox,
title: 'Output file'
}, {
component: this.appendToExistingFileCheckbox,
title: ''
}], { horizontal: true, componentWidth: 200 }).component();
return outputFileForm;
}
private async execute() {
this.model.jobName = this.jobName;
this.model.id = this.stepId;
this.model.server = this.server;
this.model.stepName = this.nameTextBox.value;
this.model.subSystem = this.typeDropdown.value as string;
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.failureAction = this.failureActionDropdown.value as string;
this.model.outputFileName = this.outputFileNameBox.value;
await this.model.save();
}
public async openNewStepDialog() {
let databases = await AgentUtils.getDatabases(this.ownerUri);
let queryProvider = await AgentUtils.getQueryProvider();
this.initializeUIComponents();
this.createGeneralTab(databases, queryProvider);
this.createAdvancedTab();
sqlops.window.modelviewdialog.openDialog(this.dialog);
}
}

View File

@@ -1,54 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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 * as vscode from 'vscode';
import { CreateAlertDialog } from './dialogs/createAlertDialog';
import { CreateJobDialog } from './dialogs/createJobDialog';
import { CreateStepDialog } from './dialogs/createStepDialog';
import { PickScheduleDialog } from './dialogs/pickScheduleDialog';
/**
* The main controller class that initializes the extension
*/
export class MainController {
protected _context: vscode.ExtensionContext;
// PUBLIC METHODS //////////////////////////////////////////////////////
public constructor(context: vscode.ExtensionContext) {
this._context = context;
}
/**
* Activates the extension
*/
public activate(): void {
vscode.commands.registerCommand('agent.openCreateJobDialog', (ownerUri: string) => {
let dialog = new CreateJobDialog(ownerUri);
dialog.showDialog();
});
vscode.commands.registerCommand('agent.openNewStepDialog', (ownerUri: string, jobId: string, server: string, stepId: number) => {
let dialog = new CreateStepDialog(ownerUri, jobId, server, stepId);
dialog.openNewStepDialog();
});
vscode.commands.registerCommand('agent.openPickScheduleDialog', (ownerUri: string) => {
let dialog = new PickScheduleDialog(ownerUri);
dialog.showDialog();
});
vscode.commands.registerCommand('agent.openCreateAlertDialog', (ownerUri: string) => {
let dialog = new CreateAlertDialog(ownerUri);
dialog.showDialog();
});
vscode.commands.registerCommand('agent.openCreateOperatorDialog', (ownerUri: string) => {
});
vscode.commands.registerCommand('agent.openCreateProxyDialog', (ownerUri: string) => {
});
}
/**
* Deactivates the extension
*/
public deactivate(): void {
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
{
"name": "agent",
"displayName": "SQL Server Agent",
"description": "Manage and troubleshoot SQL Server Agent jobs (early preview)",
"version": "0.29.0",
"description": "Manage and troubleshoot SQL Server Agent jobs",
"version": "0.31.4",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
@@ -14,7 +14,7 @@
"activationEvents": [
"*"
],
"main": "./client/out/main",
"main": "./out/main",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/sqlopsstudio.git"
@@ -46,5 +46,12 @@
}
}
]
}
},
"dependencies": {
"vscode-nls": "^3.2.1"
},
"devDependencies": {
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7"
}
}

View File

@@ -0,0 +1,132 @@
/*---------------------------------------------------------------------------------------------
* 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 * as nls from 'vscode-nls';
import * as vscode from 'vscode';
import * as sqlops from 'sqlops';
import { AgentUtils } from '../agentUtils';
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;
name: string;
originalName: string;
delayBetweenResponses: number;
eventDescriptionKeyword: string;
eventSource: string;
hasNotification: number;
includeEventDescription: string;
isEnabled: boolean = true;
jobId: string;
jobName: string;
lastOccurrenceDate: string;
lastResponseDate: string;
messageId: number;
notificationMessage: string;
occurrenceCount: number;
performanceCondition: string;
severity: number;
databaseName: string;
countResetDate: string;
categoryName: string;
alertType: string = AlertData.DefaultAlertTypeString;
wmiEventNamespace: string;
wmiEventQuery: string;
constructor(ownerUri:string, alertInfo: sqlops.AgentAlertInfo) {
this.ownerUri = ownerUri;
if (alertInfo) {
this.dialogMode = AgentDialogMode.EDIT;
this.id = alertInfo.id;
this.name = alertInfo.name;
this.originalName = alertInfo.name;
this.delayBetweenResponses = alertInfo.delayBetweenResponses;
this.eventDescriptionKeyword = alertInfo.eventDescriptionKeyword;
this.eventSource = alertInfo.eventSource;
this.hasNotification = alertInfo.hasNotification;
this.includeEventDescription = alertInfo.includeEventDescription.toString();
this.isEnabled = alertInfo.isEnabled;
this.jobId = alertInfo.jobId;
this.jobName = alertInfo.jobName;
this.lastOccurrenceDate = alertInfo.lastOccurrenceDate;
this.lastResponseDate = alertInfo.lastResponseDate;
this.messageId = alertInfo.messageId;
this.notificationMessage = alertInfo.notificationMessage;
this.occurrenceCount = alertInfo.occurrenceCount;
this.performanceCondition = alertInfo.performanceCondition;
this.severity = alertInfo.severity;
this.databaseName = alertInfo.databaseName;
this.countResetDate = alertInfo.countResetDate;
this.categoryName = alertInfo.categoryName;
this.alertType = alertInfo.alertType.toString();
this.wmiEventNamespace = alertInfo.wmiEventNamespace;
this.wmiEventQuery = alertInfo.wmiEventQuery;
}
}
public async initialize() {
}
public async save() {
let agentService = await AgentUtils.getAgentService();
let result = this.dialogMode === AgentDialogMode.CREATE
? await agentService.createAlert(this.ownerUri, this.toAgentAlertInfo())
: await agentService.updateAlert(this.ownerUri, this.originalName, this.toAgentAlertInfo());
if (!result || !result.success) {
vscode.window.showErrorMessage(
localize('alertData.saveErrorMessage', "Alert update failed '{0}'", result.errorMessage ? result.errorMessage : 'Unknown'));
}
}
public toAgentAlertInfo(): sqlops.AgentAlertInfo {
return {
id: this.id,
name: this.name,
delayBetweenResponses: this.delayBetweenResponses,
eventDescriptionKeyword: this.eventDescriptionKeyword,
eventSource: this.eventSource,
hasNotification: this.hasNotification,
includeEventDescription: sqlops.NotifyMethods.none, // this.includeEventDescription,
isEnabled: this.isEnabled,
jobId: this.jobId,
jobName: this.jobName,
lastOccurrenceDate: this.lastOccurrenceDate,
lastResponseDate: this.lastResponseDate,
messageId: this.messageId,
notificationMessage: this.notificationMessage,
occurrenceCount: this.occurrenceCount,
performanceCondition: this.performanceCondition,
severity: this.severity,
databaseName: this.databaseName,
countResetDate: this.countResetDate,
categoryName: this.categoryName,
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,26 +4,32 @@
*--------------------------------------------------------------------------------------------*/
'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';
export class CreateJobData {
const localize = nls.loadMessageBundle();
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';
export class JobData implements IAgentDialogData {
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[];
private _operators: string[];
private _agentService: sqlops.AgentServicesProvider;
private _defaultOwner: string;
private _jobCompletionActionConditions: sqlops.CategoryValue[];
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
public name: string;
public originalName: string;
public enabled: boolean = true;
public description: string;
public category: string;
@@ -39,8 +45,21 @@ export class CreateJobData {
public jobSchedules: sqlops.AgentJobScheduleInfo[];
public alerts: sqlops.AgentAlertInfo[];
constructor(ownerUri: string) {
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[] {
@@ -91,7 +110,39 @@ export class CreateJobData {
}
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,
@@ -121,30 +172,6 @@ export class CreateJobData {
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

@@ -5,8 +5,10 @@
'use strict';
import { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
export class CreateStepData {
export class JobStepData implements IAgentDialogData {
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
public ownerUri: string;
public jobId: string; //
public jobName: string;
@@ -37,6 +39,9 @@ export class CreateStepData {
this.ownerUri = ownerUri;
}
public async initialize() {
}
public async save() {
let agentService = await AgentUtils.getAgentService();
agentService.createJobStep(this.ownerUri, {

View File

@@ -0,0 +1,74 @@
/*---------------------------------------------------------------------------------------------
* 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 * as sqlops from 'sqlops';
import { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
export class OperatorData implements IAgentDialogData {
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
ownerUri: string;
name: string;
id: number;
emailAddress: string;
enabled: boolean;
lastEmailDate: string;
lastNetSendDate: string;
lastPagerDate: string;
pagerAddress: string;
categoryName: string;
pagerDays: string;
saturdayPagerEndTime: string;
saturdayPagerStartTime: string;
sundayPagerEndTime: string;
sundayPagerStartTime: string;
netSendAddress: string;
weekdayPagerStartTime: string;
weekdayPagerEndTime: 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() {
}
public async save() {
let agentService = await AgentUtils.getAgentService();
let result = await agentService.createOperator(this.ownerUri, this.toAgentOperatorInfo());
if (!result || !result.success) {
// TODO handle error here
}
}
public toAgentOperatorInfo(): sqlops.AgentOperatorInfo {
return {
name: this.name,
id: this.id,
emailAddress: this.emailAddress,
enabled: this.enabled,
lastEmailDate: this.lastEmailDate,
lastNetSendDate: this.lastNetSendDate,
lastPagerDate: this.lastPagerDate,
pagerAddress: this.pagerAddress,
categoryName: this.categoryName,
pagerDays: sqlops.WeekDays.weekDays, //this.pagerDays,
saturdayPagerEndTime: this.saturdayPagerEndTime,
saturdayPagerStartTime: this.saturdayPagerStartTime,
sundayPagerEndTime: this.sundayPagerEndTime,
sundayPagerStartTime: this.sundayPagerStartTime,
netSendAddress: this.netSendAddress,
weekdayPagerStartTime: this.weekdayPagerStartTime,
weekdayPagerEndTime: this.weekdayPagerEndTime
};
}
}

View File

@@ -6,8 +6,10 @@
import * as sqlops from 'sqlops';
import { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
export class PickScheduleData {
export class PickScheduleData implements IAgentDialogData {
public dialogMode: AgentDialogMode = AgentDialogMode.VIEW;
public ownerUri: string;
public schedules: sqlops.AgentJobScheduleInfo[];
public selectedSchedule: sqlops.AgentJobScheduleInfo;

View File

@@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* 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 * as sqlops from 'sqlops';
import { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
export class ProxyData implements IAgentDialogData {
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
ownerUri: string;
id: number;
accountName: string;
description: string;
credentialName: string;
credentialIdentity: string;
credentialId: number;
isEnabled: boolean;
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() {
}
public async save() {
let agentService = await AgentUtils.getAgentService();
let result = await agentService.createProxy(this.ownerUri, this.toAgentProxyInfo());
if (!result || !result.success) {
// TODO handle error here
}
}
public toAgentProxyInfo(): sqlops.AgentProxyInfo {
return {
id: this.id,
accountName: this.accountName,
description: this.description,
credentialName: this.credentialName,
credentialIdentity: this.credentialIdentity,
credentialId: this.credentialId,
isEnabled: this.isEnabled
};
}
}

View File

@@ -6,8 +6,10 @@
import * as sqlops from 'sqlops';
import { AgentUtils } from '../agentUtils';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
export class CreateScheduleData {
export class ScheduleData implements IAgentDialogData {
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
public ownerUri: string;
public schedules: sqlops.AgentJobScheduleInfo[];
public selectedSchedule: sqlops.AgentJobScheduleInfo;

View File

@@ -0,0 +1,78 @@
/*---------------------------------------------------------------------------------------------
* 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 * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { IAgentDialogData, AgentDialogMode } from '../interfaces';
const localize = nls.loadMessageBundle();
export abstract class AgentDialog<T extends IAgentDialogData> {
private static readonly OkButtonText: string = localize('agentDialog.OK', 'OK');
private static readonly CancelButtonText: string = localize('agentDialog.Cancel', 'Cancel');
protected _onSuccess: vscode.EventEmitter<T> = new vscode.EventEmitter<T>();
public readonly onSuccess: vscode.Event<T> = this._onSuccess.event;
public dialog: sqlops.window.modelviewdialog.Dialog;
constructor(public ownerUri: string, public model: T, public title: string) {
}
public get dialogMode(): AgentDialogMode {
return this.model.dialogMode;
}
protected abstract async updateModel();
protected abstract async initializeDialog(dialog: sqlops.window.modelviewdialog.Dialog);
public async openDialog() {
this.dialog = sqlops.window.modelviewdialog.createDialog(this.title);
await this.model.initialize();
await this.initializeDialog(this.dialog);
this.dialog.okButton.label = AgentDialog.OkButtonText;
this.dialog.okButton.onClick(async () => await this.execute());
this.dialog.cancelButton.label = AgentDialog.CancelButtonText;
this.dialog.cancelButton.onClick(async () => await this.cancel());
sqlops.window.modelviewdialog.openDialog(this.dialog);
}
protected async execute() {
this.updateModel();
let success = await this.model.save();
if (success) {
this._onSuccess.fire(this.model);
}
}
protected async cancel() {
}
protected getActualConditionValue(checkbox: sqlops.CheckBoxComponent, dropdown: sqlops.DropDownComponent): sqlops.JobCompletionActionCondition {
return checkbox.checked ? Number(this.getDropdownValue(dropdown)) : sqlops.JobCompletionActionCondition.Never;
}
protected getDropdownValue(dropdown: sqlops.DropDownComponent): string {
return (typeof dropdown.value === 'string') ? dropdown.value : dropdown.value.name;
}
protected setConditionDropdownSelectedValue(dropdown: sqlops.DropDownComponent, selectedValue: number) {
let idx: number = 0;
for (idx = 0; idx < dropdown.values.length; idx++) {
if (Number((<sqlops.CategoryValue>dropdown.values[idx]).name) === selectedValue) {
dropdown.value = dropdown.values[idx];
break;
}
}
}
}

View File

@@ -0,0 +1,528 @@
/*---------------------------------------------------------------------------------------------
* 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 * as nls from 'vscode-nls';
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();
export class AlertDialog extends AgentDialog<AlertData> {
// Top level
private static readonly CreateDialogTitle: string = localize('alertDialog.createAlert', 'Create Alert');
private static readonly EditDialogTitle: string = localize('alertDialog.editAlert', 'Edit Alert');
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');
private static readonly TypeLabel: string = localize('alertDialog.Type', 'Type');
private static readonly EnabledCheckboxLabel: string = localize('alertDialog.Enabled', 'Enabled');
private static readonly DatabaseLabel: string = localize('alertDialog.DatabaseName', 'Database name');
private static readonly ErrorNumberLabel: string = localize('alertDialog.ErrorNumber', 'Error number');
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 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');
private static readonly AlertSeverity004Label: string = localize('alertDialog.Severity004', '004 - Reserved');
private static readonly AlertSeverity005Label: string = localize('alertDialog.Severity005', '005 - Reserved');
private static readonly AlertSeverity006Label: string = localize('alertDialog.Severity006', '006 - Reserved');
private static readonly AlertSeverity007Label: string = localize('alertDialog.Severity007', '007 - Notification: Status Information');
private static readonly AlertSeverity008Label: string = localize('alertDialog.Severity008', '008 - Notification: User Intervention Required');
private static readonly AlertSeverity009Label: string = localize('alertDialog.Severity009', '009 - User Defined');
private static readonly AlertSeverity010Label: string = localize('alertDialog.Severity010', '010 - Information');
private static readonly AlertSeverity011Label: string = localize('alertDialog.Severity011', '011 - Specified Database Object Not Found');
private static readonly AlertSeverity012Label: string = localize('alertDialog.Severity012', '012 - Unused');
private static readonly AlertSeverity013Label: string = localize('alertDialog.Severity013', '013 - User Transaction Syntax Error');
private static readonly AlertSeverity014Label: string = localize('alertDialog.Severity014', '014 - Insufficient Permission');
private static readonly AlertSeverity015Label: string = localize('alertDialog.Severity015', '015 - Syntax Error in SQL Statements');
private static readonly AlertSeverity016Label: string = localize('alertDialog.Severity016', '016 - Miscellaneous User Error');
private static readonly AlertSeverity017Label: string = localize('alertDialog.Severity017', '017 - Insufficient Resources');
private static readonly AlertSeverity018Label: string = localize('alertDialog.Severity018', '018 - Nonfatal Internal Error');
private static readonly AlertSeverity019Label: string = localize('alertDialog.Severity019', '019 - Fatal Error in Resource');
private static readonly AlertSeverity020Label: string = localize('alertDialog.Severity020', '020 - Fatal Error in Current Process');
private static readonly AlertSeverity021Label: string = localize('alertDialog.Severity021', '021 - Fatal Error in Database Processes');
private static readonly AlertSeverity022Label: string = localize('alertDialog.Severity022', '022 - Fatal Error: Table Integrity Suspect');
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[] = [
AlertData.AlertTypeSqlServerEventString,
// Disabled until next release
// AlertData.AlertTypePerformanceConditionString,
// AlertData.AlertTypeWmiEventString
];
private static readonly AlertSeverities: string[] = [
AlertDialog.AlertSeverity001Label,
AlertDialog.AlertSeverity002Label,
AlertDialog.AlertSeverity003Label,
AlertDialog.AlertSeverity004Label,
AlertDialog.AlertSeverity005Label,
AlertDialog.AlertSeverity006Label,
AlertDialog.AlertSeverity007Label,
AlertDialog.AlertSeverity008Label,
AlertDialog.AlertSeverity009Label,
AlertDialog.AlertSeverity010Label,
AlertDialog.AlertSeverity011Label,
AlertDialog.AlertSeverity012Label,
AlertDialog.AlertSeverity013Label,
AlertDialog.AlertSeverity014Label,
AlertDialog.AlertSeverity015Label,
AlertDialog.AlertSeverity016Label,
AlertDialog.AlertSeverity017Label,
AlertDialog.AlertSeverity018Label,
AlertDialog.AlertSeverity019Label,
AlertDialog.AlertSeverity020Label,
AlertDialog.AlertSeverity021Label,
AlertDialog.AlertSeverity022Label,
AlertDialog.AlertSeverity023Label,
AlertDialog.AlertSeverity024Label,
AlertDialog.AlertSeverity025Label
];
// Response tab strings
private static readonly ExecuteJobCheckBoxLabel: string = localize('alertDialog.ExecuteJob', 'Execute Job');
private static readonly ExecuteJobTextBoxLabel: string = localize('alertDialog.ExecuteJobName', 'Job Name');
private static readonly NotifyOperatorsTextBoxLabel: string = localize('alertDialog.NotifyOperators', 'Notify Operators');
private static readonly NewJobButtonLabel: string = localize('alertDialog.NewJob', 'New Job');
private static readonly OperatorListLabel: string = localize('alertDialog.OperatorList', 'Operator List');
private static readonly OperatorNameColumnLabel: string = localize('alertDialog.OperatorName', 'Operator');
private static readonly OperatorEmailColumnLabel: string = localize('alertDialog.OperatorEmail', 'E-mail');
private static readonly OperatorPagerColumnLabel: string = localize('alertDialog.OperatorPager', 'Pager');
private static readonly NewOperatorButtonLabel: string = localize('alertDialog.NewOperator', 'New Operator');
// Options tab strings
private static readonly IncludeErrorInEmailCheckBoxLabel: string = localize('alertDialog.IncludeErrorInEmail', 'Include alert error text in e-mail');
private static readonly IncludeErrorInPagerCheckBoxLabel: string = localize('alertDialog.IncludeErrorInPager', 'Include alert error text in pager');
private static readonly AdditionalMessageTextBoxLabel: string = localize('alertDialog.AdditionalNotification', 'Additional notification message to send');
private static readonly DelayBetweenResponsesTextBoxLabel: string = localize('alertDialog.DelayBetweenResponse', 'Delay between responses');
private static readonly DelayMinutesTextBoxLabel: string = localize('alertDialog.DelayMinutes', 'Delay Minutes');
private static readonly DelaySecondsTextBoxLabel: string = localize('alertDialog.DelaySeconds', 'Delay Seconds');
// UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab;
private responseTab: sqlops.window.modelviewdialog.DialogTab;
private optionsTab: sqlops.window.modelviewdialog.DialogTab;
// General tab controls
private nameTextBox: sqlops.InputBoxComponent;
private typeDropDown: sqlops.DropDownComponent;
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;
// Response tab controls
private executeJobTextBox: sqlops.InputBoxComponent;
private executeJobCheckBox: sqlops.CheckBoxComponent;
private newJobButton: sqlops.ButtonComponent;
private notifyOperatorsCheckBox: sqlops.CheckBoxComponent;
private operatorsTable: sqlops.TableComponent;
private newOperatorButton: sqlops.ButtonComponent;
// Options tab controls
private additionalMessageTextBox: sqlops.InputBoxComponent;
private includeErrorInEmailTextBox: sqlops.CheckBoxComponent;
private includeErrorInPagerTextBox: sqlops.CheckBoxComponent;
private delayMinutesTextBox: sqlops.InputBoxComponent;
private delaySecondsTextBox: sqlops.InputBoxComponent;
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) {
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(this.databases, dialog);
this.initializeResponseTab();
this.initializeOptionsTab();
dialog.content = [this.generalTab, this.responseTab, this.optionsTab];
}
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,
width: '100%'
}).component();
this.typeDropDown = view.modelBuilder.dropDown()
.withProperties({
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,
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([{
component: this.nameTextBox,
title: AlertDialog.NameLabel
}, {
component: this.enabledCheckBox,
title: ''
}, {
component: this.typeDropDown,
title: AlertDialog.TypeLabel
}, {
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];
}
}
});
}
private initializeResponseTab() {
this.responseTab.registerContent(async view => {
this.executeJobCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.ExecuteJobCheckBoxLabel
}).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({
label: AlertDialog.NotifyOperatorsTextBoxLabel
}).component();
this.operatorsTable = view.modelBuilder.table()
.withProperties({
columns: [
AlertDialog.OperatorNameColumnLabel,
AlertDialog.OperatorEmailColumnLabel,
AlertDialog.OperatorPagerColumnLabel
],
data: [],
height: 500,
width: 375
}).component();
this.newOperatorButton = view.modelBuilder.button().withProperties({
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: executeJobContainer,
title: ''
}, {
component: this.notifyOperatorsCheckBox,
title: ''
}, {
component: notifyOperatorContainer,
title: ''
}])
.withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
private initializeOptionsTab() {
this.optionsTab.registerContent(async view => {
this.includeErrorInEmailTextBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.IncludeErrorInEmailCheckBoxLabel
}).component();
this.includeErrorInPagerTextBox = view.modelBuilder.checkBox()
.withProperties({
label: AlertDialog.IncludeErrorInPagerCheckBoxLabel
}).component();
this.additionalMessageTextBox = view.modelBuilder.inputBox().component();
this.delayMinutesTextBox = view.modelBuilder.inputBox()
.withProperties({
inputType: 'number',
placeHolder: 0
})
.component();
this.delaySecondsTextBox = view.modelBuilder.inputBox()
.withProperties({
inputType: 'number',
placeHolder: 0
})
.component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.includeErrorInEmailTextBox,
title: ''
}, {
component: this.includeErrorInPagerTextBox,
title: ''
}, {
component: this.additionalMessageTextBox,
title: AlertDialog.AdditionalMessageTextBoxLabel
}, {
component: this.delayMinutesTextBox,
title: AlertDialog.DelayMinutesTextBoxLabel
}, {
component: this.delaySecondsTextBox,
title: AlertDialog.DelaySecondsTextBoxLabel
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
private getSeverityNumber(): number {
let selected = this.getDropdownValue(this.severityDropDown);
let severityNumber: number = 0;
if (selected) {
let index = AlertDialog.AlertSeverities.indexOf(selected);
if (index >= 0) {
severityNumber = index + 1;
}
}
return severityNumber;
}
protected updateModel() {
this.model.name = this.nameTextBox.value;
this.model.isEnabled = this.enabledCheckBox.checked;
this.model.alertType = this.getDropdownValue(this.typeDropDown);
let databaseName = this.getDropdownValue(this.databaseDropDown);
this.model.databaseName = (databaseName !== AlertDialog.AllDatabases) ? databaseName : undefined;
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,61 +3,67 @@
* 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 { CreateJobData } from '../data/createJobData';
import { CreateStepDialog } from './createStepDialog';
import { JobData } from '../data/jobData';
import { JobStepDialog } from './jobStepDialog';
import { PickScheduleDialog } from './pickScheduleDialog';
import { CreateAlertDialog } from './createAlertDialog';
import { AlertDialog } from './alertDialog';
import { AgentDialog } from './agentDialog';
export class CreateJobDialog {
const localize = nls.loadMessageBundle();
export class JobDialog extends AgentDialog<JobData> {
// TODO: localize
// Top level
private readonly DialogTitle: string = 'New Job';
private readonly OkButtonText: string = 'OK';
private readonly CancelButtonText: string = 'Cancel';
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 dialog: sqlops.window.modelviewdialog.Dialog;
private generalTab: sqlops.window.modelviewdialog.DialogTab;
private stepsTab: sqlops.window.modelviewdialog.DialogTab;
private alertsTab: sqlops.window.modelviewdialog.DialogTab;
@@ -74,7 +80,8 @@ export class CreateJobDialog {
// 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;
@@ -99,15 +106,14 @@ export class CreateJobDialog {
private alertsTable: sqlops.TableComponent;
private newAlertButton: sqlops.ButtonComponent;
private model: CreateJobData;
constructor(ownerUri: string) {
this.model = new CreateJobData(ownerUri);
constructor(ownerUri: string, jobInfo: sqlops.AgentJobInfo = undefined) {
super(
ownerUri,
new JobData(ownerUri, jobInfo),
jobInfo ? JobDialog.EditDialogTitle : JobDialog.CreateDialogTitle);
}
public async showDialog() {
await this.model.initialize();
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
protected async initializeDialog() {
this.generalTab = sqlops.window.modelviewdialog.createTab(this.GeneralTabText);
this.stepsTab = sqlops.window.modelviewdialog.createTab(this.StepsTabText);
this.alertsTab = sqlops.window.modelviewdialog.createTab(this.AlertsTabText);
@@ -119,10 +125,6 @@ export class CreateJobDialog {
this.initializeSchedulesTab();
this.initializeNotificationsTab();
this.dialog.content = [this.generalTab, this.stepsTab, this.schedulesTab, this.alertsTab, this.notificationsTab];
this.dialog.okButton.onClick(async () => await this.execute());
this.dialog.cancelButton.onClick(async () => await this.cancel());
this.dialog.okButton.label = this.OkButtonText;
this.dialog.cancelButton.label = this.CancelButtonText;
this.dialog.registerCloseValidator(() => {
this.updateModel();
@@ -134,13 +136,17 @@ export class CreateJobDialog {
return validationResult.valid;
});
sqlops.window.modelviewdialog.openDialog(this.dialog);
}
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({
@@ -171,11 +177,18 @@ export class CreateJobDialog {
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;
});
}
@@ -191,24 +204,38 @@ export class CreateJobDialog {
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.moveStepUpButton.enabled = false;
this.moveStepDownButton.enabled = false;
this.newStepButton = view.modelBuilder.button().withProperties({
label: this.NewStepButtonString,
width: 80
}).component();
this.newStepButton.onDidClick((e)=>{
let stepDialog = new CreateStepDialog(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
@@ -223,7 +250,7 @@ export class CreateJobDialog {
.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);
});
@@ -234,10 +261,10 @@ export class CreateJobDialog {
this.alertsTable = view.modelBuilder.table()
.withProperties({
columns: [
'Alert Name'
this.AlertNameLabelString
],
data: [],
height: 600,
height: 430,
width: 400
}).component();
@@ -247,10 +274,10 @@ export class CreateJobDialog {
}).component();
this.newAlertButton.onDidClick((e)=>{
let alertDialog = new CreateAlertDialog(this.model.ownerUri);
let alertDialog = new AlertDialog(this.model.ownerUri, null, []);
alertDialog.onSuccess((dialogModel) => {
});
alertDialog.showDialog();
alertDialog.openDialog();
});
let formModel = view.modelBuilder.formContainer()
@@ -269,11 +296,11 @@ export class CreateJobDialog {
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({
@@ -373,21 +400,23 @@ export class CreateJobDialog {
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;
@@ -421,34 +450,7 @@ export class CreateJobDialog {
});
}
private async execute() {
this.updateModel();
await this.model.save();
}
private async cancel() {
}
private getActualConditionValue(checkbox: sqlops.CheckBoxComponent, dropdown: sqlops.DropDownComponent): sqlops.JobCompletionActionCondition {
return checkbox.checked ? Number(this.getDropdownValue(dropdown)) : sqlops.JobCompletionActionCondition.Never;
}
private getDropdownValue(dropdown: sqlops.DropDownComponent): string {
return (typeof dropdown.value === 'string') ? dropdown.value : dropdown.value.name;
}
private setConditionDropdownSelectedValue(dropdown: sqlops.DropDownComponent, selectedValue: number) {
let idx: number = 0;
for (idx = 0; idx < dropdown.values.length; idx++) {
if (Number((<sqlops.CategoryValue>dropdown.values[idx]).name) === selectedValue) {
dropdown.value = dropdown.values[idx];
break;
}
}
}
private updateModel() {
protected updateModel() {
this.model.name = this.nameTextBox.value;
this.model.owner = this.ownerTextBox.value;
this.model.enabled = this.enabledCheckBox.checked;

View File

@@ -0,0 +1,507 @@
/*---------------------------------------------------------------------------------------------
* 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 * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { JobStepData } from '../data/jobStepData';
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 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.');
private readonly BlankStepNameErrorText: string = localize('jobStepDialog.blankStepName', 'The step name cannot be left blank');
// 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 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
// Dialogs
private dialog: sqlops.window.modelviewdialog.Dialog;
private fileBrowserDialog: sqlops.window.modelviewdialog.Dialog;
// Dialog tabs
private generalTab: sqlops.window.modelviewdialog.DialogTab;
private advancedTab: sqlops.window.modelviewdialog.DialogTab;
//Input boxes
private nameTextBox: sqlops.InputBoxComponent;
private commandTextBox: sqlops.InputBoxComponent;
private selectedPathTextBox: sqlops.InputBoxComponent;
private retryAttemptsBox: sqlops.InputBoxComponent;
private retryIntervalBox: sqlops.InputBoxComponent;
private outputFileNameBox: sqlops.InputBoxComponent;
private fileBrowserNameBox: sqlops.InputBoxComponent;
private userInputBox: sqlops.InputBoxComponent;
// Dropdowns
private typeDropdown: sqlops.DropDownComponent;
private runAsDropdown: sqlops.DropDownComponent;
private databaseDropdown: sqlops.DropDownComponent;
private successActionDropdown: sqlops.DropDownComponent;
private failureActionDropdown: sqlops.DropDownComponent;
private fileTypeDropdown: sqlops.DropDownComponent;
// Buttons
private openButton: sqlops.ButtonComponent;
private parseButton: sqlops.ButtonComponent;
private nextButton: sqlops.ButtonComponent;
private previousButton: sqlops.ButtonComponent;
private outputFileBrowserButton: sqlops.ButtonComponent;
// Checkbox
private appendToExistingFileCheckbox: sqlops.CheckBoxComponent;
private logToTableCheckbox: sqlops.CheckBoxComponent;
private fileBrowserTree: sqlops.FileBrowserTreeComponent;
private jobModel: JobData;
private model: JobStepData;
private ownerUri: string;
private jobName: string;
private server: string;
private stepId: number;
constructor(
ownerUri: string,
jobName: string,
server: string,
stepId: number,
jobModel?: JobData
) {
this.model = new JobStepData(ownerUri);
this.stepId = stepId;
this.ownerUri = ownerUri;
this.jobName = jobName;
this.server = server;
this.jobModel = jobModel;
}
private initializeUIComponents() {
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 = this.OkButtonText;
this.dialog.cancelButton.label = this.CancelButtonText;
}
private createCommands(view, queryProvider: sqlops.QueryProvider) {
this.openButton = view.modelBuilder.button()
.withProperties({
label: this.OpenCommandText,
width: '80px',
isFile: true
}).component();
this.openButton.enabled = false;
this.parseButton = view.modelBuilder.button()
.withProperties({
label: this.ParseCommandText,
width: '80px',
isFile: false
}).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: this.SuccessfulParseText, level: 2};
} else if (result && !result.parseable) {
this.dialog.message = { text: this.FailureParseText };
}
});
}
});
this.commandTextBox = view.modelBuilder.inputBox()
.withProperties({
height: 300,
width: 400,
multiline: true,
inputType: 'text'
})
.component();
this.nextButton = view.modelBuilder.button()
.withProperties({
label: this.NextButtonText,
enabled: false,
width: '80px'
}).component();
this.previousButton = view.modelBuilder.button()
.withProperties({
label: this.PreviousButtonText,
enabled: false,
width: '80px'
}).component();
}
private createGeneralTab(databases: string[], queryProvider: sqlops.QueryProvider) {
this.generalTab.registerContent(async (view) => {
this.nameTextBox = view.modelBuilder.inputBox()
.withProperties({
}).component();
this.nameTextBox.required = true;
this.nameTextBox.onTextChanged(() => {
if (this.nameTextBox.value.length > 0) {
this.dialog.message = null;
}
});
this.typeDropdown = view.modelBuilder.dropDown()
.withProperties({
value: this.TSQLScript,
values: [this.TSQLScript]
})
.component();
this.runAsDropdown = view.modelBuilder.dropDown()
.withProperties({
value: '',
values: ['']
})
.component();
this.runAsDropdown.enabled = false;
this.typeDropdown.onValueChanged((type) => {
if (type.selected !== this.TSQLScript) {
this.runAsDropdown.value = this.AgentServiceAccount;
this.runAsDropdown.values = [this.runAsDropdown.value];
} else {
this.runAsDropdown.value = '';
this.runAsDropdown.values = [''];
}
});
this.databaseDropdown = view.modelBuilder.dropDown()
.withProperties({
value: databases[0],
values: databases
}).component();
// create the commands section
this.createCommands(view, queryProvider);
let buttonContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
justifyContent: 'space-between',
width: 420
}).withItems([this.openButton, this.parseButton, this.previousButton, this.nextButton], {
flex: '1 1 50%'
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.nameTextBox,
title: this.StepNameLabelString
}, {
component: this.typeDropdown,
title: this.TypeLabelString
}, {
component: this.runAsDropdown,
title: this.RunAsLabelString
}, {
component: this.databaseDropdown,
title: this.DatabaseLabelString
}, {
component: this.commandTextBox,
title: this.CommandLabelString,
actions: [buttonContainer]
}], {
horizontal: false,
componentWidth: 420
}).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
await view.initializeModel(formWrapper);
});
}
private createAdvancedTab() {
this.advancedTab.registerContent(async (view) => {
this.successActionDropdown = view.modelBuilder.dropDown()
.withProperties({
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: this.QuitJobReportingFailure,
values: [this.QuitJobReportingFailure, this.NextStep, this.QuitJobReportingSuccess]
})
.component();
let optionsGroup = this.createTSQLOptions(view);
this.logToTableCheckbox = view.modelBuilder.checkBox()
.withProperties({
label: this.LogToTableLabel
}).component();
let appendToExistingEntryInTableCheckbox = view.modelBuilder.checkBox()
.withProperties({ label: this.AppendExistingTableEntryLabel }).component();
appendToExistingEntryInTableCheckbox.enabled = false;
this.logToTableCheckbox.onChanged(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]).component();
let logStepOutputHistoryCheckbox = view.modelBuilder.checkBox()
.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: this.SuccessActionLabel
}, {
component: retryFlexContainer,
title: ''
}, {
component: this.failureActionDropdown,
title: this.FailureActionLabel
}, {
component: optionsGroup,
title: this.TSQLScript
}, {
component: logToTableContainer,
title: ''
}, {
component: appendCheckboxContainer,
title: ' '
}, {
component: logStepOutputHistoryCheckbox,
title: ''
}, {
component: this.userInputBox,
title: this.RunAsUserLabel
}], {
componentWidth: 400
}).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
view.initializeModel(formWrapper);
});
}
private createRetryCounters(view) {
this.retryAttemptsBox = view.modelBuilder.inputBox()
.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',
width: '100%',
placeHolder: '0'
}).component();
let retryAttemptsContainer = view.modelBuilder.formContainer()
.withFormItems(
[{
component: this.retryAttemptsBox,
title: this.RetryAttemptsLabel
}], {
horizontal: false,
componentWidth: '100%'
})
.component();
let retryIntervalContainer = view.modelBuilder.formContainer()
.withFormItems(
[{
component: this.retryIntervalBox,
title: this.RetryIntervalLabel
}], {
horizontal: false
})
.component();
let retryFlexContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
}).withItems([retryAttemptsContainer, retryIntervalContainer]).component();
return retryFlexContainer;
}
private openFileBrowserDialog() {
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];
fileBrowserTab.registerContent(async (view) => {
this.fileBrowserTree = view.modelBuilder.fileBrowserTree()
.withProperties({ ownerUri: this.ownerUri, width: 420, height: 700 })
.component();
this.selectedPathTextBox = view.modelBuilder.inputBox()
.withProperties({ inputType: 'text'})
.component();
this.fileBrowserTree.onDidChange((args) => {
this.selectedPathTextBox.value = args.fullPath;
this.fileBrowserNameBox.value = args.isFile ? path.win32.basename(args.fullPath) : '';
});
this.fileTypeDropdown = view.modelBuilder.dropDown()
.withProperties({
value: this.AllFilesLabelString,
values: [this.AllFilesLabelString]
})
.component();
this.fileBrowserNameBox = view.modelBuilder.inputBox()
.withProperties({})
.component();
let fileBrowserContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.fileBrowserTree,
title: ''
}, {
component: this.selectedPathTextBox,
title: this.SelectedPathLabelString
}, {
component: this.fileTypeDropdown,
title: this.FilesOfTypeLabelString
}, {
component: this.fileBrowserNameBox,
title: this.FileNameLabelString
}
]).component();
view.initializeModel(fileBrowserContainer);
});
this.fileBrowserDialog.okButton.onClick(() => {
this.outputFileNameBox.value = path.join(path.dirname(this.selectedPathTextBox.value), this.fileBrowserNameBox.value);
});
this.fileBrowserDialog.okButton.label = this.OkButtonText;
this.fileBrowserDialog.cancelButton.label = this.CancelButtonText;
sqlops.window.modelviewdialog.openDialog(this.fileBrowserDialog);
}
private createTSQLOptions(view) {
this.outputFileBrowserButton = view.modelBuilder.button()
.withProperties({ width: '20px', label: '...' }).component();
this.outputFileBrowserButton.onDidClick(() => this.openFileBrowserDialog());
this.outputFileNameBox = view.modelBuilder.inputBox()
.withProperties({
width: 250,
inputType: 'text'
}).component();
let outputButtonContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
textAlign: 'right',
width: '100%'
}).withItems([this.outputFileBrowserButton], { flex: '1 1 50%' }).component();
let outputFlexBox = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
width: 350
}).withItems([this.outputFileNameBox, outputButtonContainer], {
flex: '1 1 50%'
}).component();
this.appendToExistingFileCheckbox = view.modelBuilder.checkBox()
.withProperties({
label: this.AppendOutputToFileLabel
}).component();
this.appendToExistingFileCheckbox.enabled = false;
this.outputFileNameBox.onTextChanged((input) => {
if (input !== '') {
this.appendToExistingFileCheckbox.enabled = true;
} else {
this.appendToExistingFileCheckbox.enabled = false;
}
});
let outputFileForm = view.modelBuilder.formContainer()
.withFormItems([{
component: outputFlexBox,
title: this.OutputFileNameLabel
}, {
component: this.appendToExistingFileCheckbox,
title: ''
}], { horizontal: false, componentWidth: 200 }).component();
return outputFileForm;
}
protected execute() {
this.model.stepName = this.nameTextBox.value;
if (!this.model.stepName || this.model.stepName.length === 0) {
this.dialog.message = this.dialog.message = { text: this.BlankStepNameErrorText };
return;
}
this.model.jobName = this.jobName;
this.model.id = this.stepId;
this.model.server = this.server;
this.model.stepName = this.nameTextBox.value;
this.model.subSystem = this.typeDropdown.value as string;
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.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;
}
public async openNewStepDialog() {
let databases = await AgentUtils.getDatabases(this.ownerUri);
let queryProvider = await AgentUtils.getQueryProvider();
this.initializeUIComponents();
this.createGeneralTab(databases, queryProvider);
this.createAdvancedTab();
sqlops.window.modelviewdialog.openDialog(this.dialog);
}
}

View File

@@ -0,0 +1,404 @@
/*---------------------------------------------------------------------------------------------
* 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 * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { OperatorData } from '../data/operatorData';
import * as nls from 'vscode-nls';
import { AgentDialog } from './agentDialog';
const localize = nls.loadMessageBundle();
export class OperatorDialog extends AgentDialog<OperatorData> {
// Top level
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');
// General tab strings
private static readonly NameLabel: string = localize('createOperator.Name', 'Name');
private static readonly EnabledCheckboxLabel: string = localize('createOperator.Enabled', 'Enabled');
private static readonly EmailNameTextLabel: string = localize('createOperator.EmailName', 'E-mail Name');
private static readonly PagerEmailNameTextLabel: string = localize('createOperator.PagerEmailName', 'Pager E-mail Name');
private static readonly PagerMondayCheckBoxLabel: string = localize('createOperator.PagerMondayCheckBox', 'Monday');
private static readonly PagerTuesdayCheckBoxLabel: string = localize('createOperator.PagerTuesdayCheckBox', 'Tuesday');
private static readonly PagerWednesdayCheckBoxLabel: string = localize('createOperator.PagerWednesdayCheckBox', 'Wednesday');
private static readonly PagerThursdayCheckBoxLabel: string = localize('createOperator.PagerThursdayCheckBox', 'Thursday');
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');
private static readonly AlertNameColumnLabel: string = localize('createOperator.AlertNameColumnLabel', 'Alert name');
private static readonly AlertEmailColumnLabel: string = localize('createOperator.AlertEmailColumnLabel', 'E-mail');
private static readonly AlertPagerColumnLabel: string = localize('createOperator.AlertPagerColumnLabel', 'Pager');
// UI Components
private generalTab: sqlops.window.modelviewdialog.DialogTab;
private notificationsTab: sqlops.window.modelviewdialog.DialogTab;
// General tab controls
private nameTextBox: sqlops.InputBoxComponent;
private enabledCheckBox: sqlops.CheckBoxComponent;
private emailNameTextBox: sqlops.InputBoxComponent;
private pagerEmailNameTextBox: sqlops.InputBoxComponent;
private pagerMondayCheckBox: sqlops.CheckBoxComponent;
private pagerTuesdayCheckBox: sqlops.CheckBoxComponent;
private pagerWednesdayCheckBox: sqlops.CheckBoxComponent;
private pagerThursdayCheckBox: sqlops.CheckBoxComponent;
private pagerFridayCheckBox: sqlops.CheckBoxComponent;
private pagerSaturdayCheckBox: sqlops.CheckBoxComponent;
private pagerSundayCheckBox: sqlops.CheckBoxComponent;
private weekdayPagerStartTimeInput: sqlops.InputBoxComponent;
private weekdayPagerEndTimeInput: sqlops.InputBoxComponent;
private saturdayPagerStartTimeInput: sqlops.InputBoxComponent;
private saturdayPagerEndTimeInput: sqlops.InputBoxComponent;
private sundayPagerStartTimeInput: sqlops.InputBoxComponent;
private sundayPagerEndTimeInput: sqlops.InputBoxComponent;
// Notification tab controls
private alertsTable: sqlops.TableComponent;
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) {
this.generalTab = sqlops.window.modelviewdialog.createTab(OperatorDialog.GeneralTabText);
this.notificationsTab = sqlops.window.modelviewdialog.createTab(OperatorDialog.NotificationsTabText);
this.initializeGeneralTab();
this.initializeNotificationTab();
this.dialog.content = [this.generalTab, this.notificationsTab];
}
private initializeGeneralTab() {
this.generalTab.registerContent(async view => {
this.nameTextBox = view.modelBuilder.inputBox().component();
this.enabledCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.EnabledCheckboxLabel
}).component();
this.enabledCheckBox.checked = true;
this.emailNameTextBox = view.modelBuilder.inputBox().component();
this.pagerEmailNameTextBox = view.modelBuilder.inputBox().component();
this.enabledCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.EnabledCheckboxLabel
}).component();
this.pagerMondayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerMondayCheckBoxLabel
}).component();
this.pagerMondayCheckBox.onChanged(() => {
if (this.pagerMondayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = true;
this.weekdayPagerEndTimeInput.enabled = true;
} else {
if (!this.pagerTuesdayCheckBox.checked && !this.pagerWednesdayCheckBox.checked &&
!this.pagerThursdayCheckBox.checked && !this.pagerFridayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = false;
this.weekdayPagerEndTimeInput.enabled = false;
}
}
});
this.pagerTuesdayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerTuesdayCheckBoxLabel
}).component();
this.pagerTuesdayCheckBox.onChanged(() => {
if (this.pagerTuesdayCheckBox .checked) {
this.weekdayPagerStartTimeInput.enabled = true;
this.weekdayPagerEndTimeInput.enabled = true;
} else {
if (!this.pagerMondayCheckBox.checked && !this.pagerWednesdayCheckBox.checked &&
!this.pagerThursdayCheckBox.checked && !this.pagerFridayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = false;
this.weekdayPagerEndTimeInput.enabled = false;
}
}
});
this.pagerWednesdayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerWednesdayCheckBoxLabel
}).component();
this.pagerWednesdayCheckBox.onChanged(() => {
if (this.pagerWednesdayCheckBox .checked) {
this.weekdayPagerStartTimeInput.enabled = true;
this.weekdayPagerEndTimeInput.enabled = true;
} else {
if (!this.pagerMondayCheckBox.checked && !this.pagerTuesdayCheckBox.checked &&
!this.pagerThursdayCheckBox.checked && !this.pagerFridayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = false;
this.weekdayPagerEndTimeInput.enabled = false;
}
}
});
this.pagerThursdayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerThursdayCheckBoxLabel
}).component();
this.pagerThursdayCheckBox.onChanged(() => {
if (this.pagerThursdayCheckBox .checked) {
this.weekdayPagerStartTimeInput.enabled = true;
this.weekdayPagerEndTimeInput.enabled = true;
} else {
if (!this.pagerMondayCheckBox.checked && !this.pagerWednesdayCheckBox.checked &&
!this.pagerTuesdayCheckBox.checked && !this.pagerFridayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = false;
this.weekdayPagerEndTimeInput.enabled = false;
}
}
});
this.weekdayPagerStartTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '08:00:00',
}).component();
this.weekdayPagerStartTimeInput.enabled = false;
let weekdayStartInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.weekdayPagerStartTimeInput,
title: OperatorDialog.WorkdayBeginLabel
}]).component();
this.weekdayPagerEndTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '06:00:00'
}).component();
this.weekdayPagerEndTimeInput.enabled = false;
let weekdayEndInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.weekdayPagerEndTimeInput,
title: OperatorDialog.WorkdayEndLabel
}]).component();
this.pagerFridayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerFridayCheckBoxLabel,
width: 80
}).component();
this.pagerFridayCheckBox.onChanged(() => {
if (this.pagerFridayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = true;
this.weekdayPagerEndTimeInput.enabled = true;
} else {
if (!this.pagerMondayCheckBox.checked && !this.pagerWednesdayCheckBox.checked &&
!this.pagerThursdayCheckBox.checked && !this.pagerTuesdayCheckBox.checked) {
this.weekdayPagerStartTimeInput.enabled = false;
this.weekdayPagerEndTimeInput.enabled = false;
}
}
});
let pagerFridayCheckboxContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
alignItems: 'baseline',
width: '100%'
}).withItems([this.pagerFridayCheckBox, weekdayStartInputContainer, weekdayEndInputContainer])
.component();
this.pagerSaturdayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerSaturdayCheckBoxLabel,
width: 80
}).component();
this.pagerSaturdayCheckBox.onChanged(() => {
if (this.pagerSaturdayCheckBox.checked) {
this.saturdayPagerStartTimeInput.enabled = true;
this.saturdayPagerEndTimeInput.enabled = true;
} else {
this.saturdayPagerStartTimeInput.enabled = false;
this.saturdayPagerEndTimeInput.enabled = false;
}
});
this.saturdayPagerStartTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '08:00:00'
}).component();
this.saturdayPagerStartTimeInput.enabled = false;
let saturdayStartInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.saturdayPagerStartTimeInput,
title: OperatorDialog.WorkdayBeginLabel
}]).component();
this.saturdayPagerEndTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '06:00:00'
}).component();
this.saturdayPagerEndTimeInput.enabled = false;
let saturdayEndInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.saturdayPagerEndTimeInput,
title: OperatorDialog.WorkdayEndLabel
}]).component();
let pagerSaturdayCheckboxContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
alignItems: 'baseline'
}).withItems([this.pagerSaturdayCheckBox, saturdayStartInputContainer, saturdayEndInputContainer])
.component();
this.pagerSundayCheckBox = view.modelBuilder.checkBox()
.withProperties({
label: OperatorDialog.PagerSundayCheckBoxLabel,
width: 80
}).component();
this.pagerSundayCheckBox.onChanged(() => {
if (this.pagerSundayCheckBox.checked) {
this.sundayPagerStartTimeInput.enabled = true;
this.sundayPagerEndTimeInput.enabled = true;
} else {
this.sundayPagerStartTimeInput.enabled = false;
this.sundayPagerEndTimeInput.enabled = false;
}
});
this.sundayPagerStartTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '08:00:00'
}).component();
this.sundayPagerStartTimeInput.enabled = false;
let sundayStartInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.sundayPagerStartTimeInput,
title: OperatorDialog.WorkdayBeginLabel
}]).component();
this.sundayPagerEndTimeInput = view.modelBuilder.inputBox()
.withProperties({
inputType: 'time',
placeHolder: '06:00:00'
}).component();
this.sundayPagerEndTimeInput.enabled = false;
let sundayEndInputContainer = view.modelBuilder.formContainer()
.withFormItems([{
component: this.sundayPagerEndTimeInput,
title: OperatorDialog.WorkdayEndLabel
}]).component();
let pagerSundayCheckboxContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'row',
alignItems: 'baseline'
}).withItems([this.pagerSundayCheckBox, sundayStartInputContainer, sundayEndInputContainer])
.component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.nameTextBox,
title: OperatorDialog.NameLabel
}, {
component: this.enabledCheckBox,
title: ''
}, {
component: this.emailNameTextBox,
title: OperatorDialog.EmailNameTextLabel
}, {
component: this.pagerEmailNameTextBox,
title: OperatorDialog.PagerEmailNameTextLabel
}, {
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);
});
}
private initializeNotificationTab() {
this.notificationsTab.registerContent(async view => {
this.alertsTable = view.modelBuilder.table()
.withProperties({
columns: [
OperatorDialog.AlertNameColumnLabel,
OperatorDialog.AlertEmailColumnLabel,
OperatorDialog.AlertPagerColumnLabel
],
data: [],
height: 500
}).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.alertsTable,
title: OperatorDialog.AlertsTableLabel
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);
});
}
protected updateModel() {
this.model.name = this.nameTextBox.value;
this.model.enabled = this.enabledCheckBox.checked;
this.model.emailAddress = this.emailNameTextBox.value;
this.model.pagerAddress = this.pagerEmailNameTextBox.value;
this.model.weekdayPagerStartTime = this.weekdayPagerStartTimeInput.value;
this.model.weekdayPagerEndTime = this.weekdayPagerEndTimeInput.value;
this.model.saturdayPagerStartTime = this.saturdayPagerStartTimeInput.value;
this.model.saturdayPagerEndTime = this.saturdayPagerEndTimeInput.value;
this.model.sundayPagerStartTime = this.sundayPagerStartTimeInput.value;
this.model.sundayPagerEndTime = this.sundayPagerEndTimeInput.value;
}
}

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

@@ -0,0 +1,219 @@
/*---------------------------------------------------------------------------------------------
* 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 * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import { AgentDialog } from './agentDialog';
import { ProxyData } from '../data/proxyData';
const localize = nls.loadMessageBundle();
export class ProxyDialog extends AgentDialog<ProxyData> {
// Top level
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 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 credentialNameDropDown: sqlops.DropDownComponent;
private descriptionTextBox: sqlops.InputBoxComponent;
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;
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];
}
private initializeGeneralTab() {
this.generalTab.registerContent(async view => {
this.proxyNameTextBox = view.modelBuilder.inputBox()
.withProperties({width: 420})
.component();
this.credentialNameDropDown = view.modelBuilder.dropDown()
.withProperties({
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.credentialNameDropDown,
title: ProxyDialog.CredentialNameTextBoxLabel
}, {
component: this.descriptionTextBox,
title: ProxyDialog.DescriptionTextBoxLabel
}]).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.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,29 +4,33 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { CreateScheduleData } from '../data/createScheduleData';
import { ScheduleData } from '../data/scheduleData';
export class CreateScheduleDialog {
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;
private schedulesTable: sqlops.TableComponent;
private model: CreateScheduleData;
private model: ScheduleData;
private _onSuccess: vscode.EventEmitter<CreateScheduleData> = new vscode.EventEmitter<CreateScheduleData>();
public readonly onSuccess: vscode.Event<CreateScheduleData> = this._onSuccess.event;
private _onSuccess: vscode.EventEmitter<ScheduleData> = new vscode.EventEmitter<ScheduleData>();
public readonly onSuccess: vscode.Event<ScheduleData> = this._onSuccess.event;
constructor(ownerUri: string) {
this.model = new CreateScheduleData(ownerUri);
this.model = new ScheduleData(ownerUri);
}
public async showDialog() {
@@ -46,7 +50,7 @@ export class CreateScheduleDialog {
this.schedulesTable = view.modelBuilder.table()
.withProperties({
columns: [
'Schedule Name'
this.ScheduleNameText
],
data: [],
height: 600,
@@ -56,7 +60,7 @@ export class CreateScheduleDialog {
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: this.schedulesTable,
title: 'Schedules'
title: this.SchedulesLabelText
}]).withLayout({ width: '100%' }).component();
await view.initializeModel(formModel);

View File

@@ -4,22 +4,14 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import { AgentUtils } from '../agentUtils';
export class CreateAlertData {
public ownerUri: string;
private _alert: sqlops.AgentAlertInfo;
constructor(ownerUri:string) {
this.ownerUri = ownerUri;
}
public async initialize() {
let agentService = await AgentUtils.getAgentService();
}
public async save() {
}
export enum AgentDialogMode {
CREATE = 1,
EDIT = 2,
VIEW = 3
}
export interface IAgentDialogData {
dialogMode: AgentDialogMode;
initialize(): void;
save(): void;
}

View File

@@ -0,0 +1,72 @@
/*---------------------------------------------------------------------------------------------
* 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 * as nls from 'vscode-nls';
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
import { AlertDialog } from './dialogs/alertDialog';
import { JobDialog } from './dialogs/jobDialog';
import { OperatorDialog } from './dialogs/operatorDialog';
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
*/
export class MainController {
protected _context: vscode.ExtensionContext;
// PUBLIC METHODS //////////////////////////////////////////////////////
public constructor(context: vscode.ExtensionContext) {
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.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) => {
let dialog = new JobStepDialog(ownerUri, jobId, server, stepId);
dialog.openNewStepDialog();
});
vscode.commands.registerCommand('agent.openPickScheduleDialog', (ownerUri: string) => {
let dialog = new PickScheduleDialog(ownerUri);
dialog.showDialog();
});
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.openOperatorDialog', (ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo) => {
let dialog = new OperatorDialog(ownerUri, operatorInfo);
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();
});
}
/**
* Deactivates the extension
*/
public deactivate(): void {
}
}

View File

@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import 'mocha';
import * as vscode from 'vscode';
import * as sqlops from 'sqlops';
import { JobData } from '../data/jobData';
import { TestAgentService } from './testAgentService';
const testOwnerUri = 'agent://testuri';
suite('Agent extension', () => {
test('Create Job Data', async () => {
let testAgentService = new TestAgentService();
let data = new JobData(testOwnerUri, undefined, testAgentService);
data.save();
});
});

View File

@@ -0,0 +1,110 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as sqlops from 'sqlops';
export class TestAgentService implements sqlops.AgentServicesProvider {
handle?: number;
readonly providerId: string = 'Test Provider';
// Job management methods
getJobs(ownerUri: string): Thenable<sqlops.AgentJobsResult> {
return undefined;
}
getJobHistory(ownerUri: string, jobId: string): Thenable<sqlops.AgentJobHistoryResult> {
return undefined;
}
jobAction(ownerUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus> {
return undefined;
}
createJob(ownerUri: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.CreateAgentJobResult> {
return undefined;
}
updateJob(ownerUri: string, originalJobName: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.UpdateAgentJobResult> {
return undefined;
}
deleteJob(ownerUri: string, jobInfo: sqlops.AgentJobInfo): Thenable<sqlops.ResultStatus> {
return undefined;
}
getJobDefaults(ownerUri: string): Thenable<sqlops.AgentJobDefaultsResult> {
return undefined;
}
// Job Step management methods
createJobStep(ownerUri: string, jobInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.CreateAgentJobStepResult> {
return undefined;
}
updateJobStep(ownerUri: string, originalJobStepName: string, jobInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.UpdateAgentJobStepResult> {
return undefined;
}
deleteJobStep(ownerUri: string, jobInfo: sqlops.AgentJobStepInfo): Thenable<sqlops.ResultStatus> {
return undefined;
}
// Alert management methods
getAlerts(ownerUri: string): Thenable<sqlops.AgentAlertsResult> {
return undefined;
}
createAlert(ownerUri: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.CreateAgentAlertResult> {
return undefined;
}
updateAlert(ownerUri: string, originalAlertName: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.UpdateAgentAlertResult> {
return undefined;
}
deleteAlert(ownerUri: string, alertInfo: sqlops.AgentAlertInfo): Thenable<sqlops.ResultStatus> {
return undefined;
}
// Operator management methods
getOperators(ownerUri: string): Thenable<sqlops.AgentOperatorsResult> {
return undefined;
}
createOperator(ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.CreateAgentOperatorResult> {
return undefined;
}
updateOperator(ownerUri: string, originalOperatorName: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.UpdateAgentOperatorResult> {
return undefined;
}
deleteOperator(ownerUri: string, operatorInfo: sqlops.AgentOperatorInfo): Thenable<sqlops.ResultStatus> {
return undefined;
}
// Proxy management methods
getProxies(ownerUri: string): Thenable<sqlops.AgentProxiesResult> {
return undefined;
}
createProxy(ownerUri: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.CreateAgentOperatorResult> {
return undefined;
}
updateProxy(ownerUri: string, originalProxyName: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.UpdateAgentOperatorResult> {
return undefined;
}
deleteProxy(ownerUri: string, proxyInfo: sqlops.AgentProxyInfo): Thenable<sqlops.ResultStatus> {
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;
}
createJobSchedule(ownerUri: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.CreateAgentJobScheduleResult> {
return undefined;
}
updateJobSchedule(ownerUri: string, originalScheduleName: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.UpdateAgentJobScheduleResult> {
return undefined;
}
deleteJobSchedule(ownerUri: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable<sqlops.ResultStatus> {
return undefined;
}
registerOnUpdated(handler: () => any): void {
}
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../../src/sql/sqlops.d.ts'/>
/// <reference path='../../../../../src/sql/sqlops.proposed.d.ts'/>
/// <reference path='../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../src/sql/sqlops.d.ts'/>
/// <reference path='../../../../src/sql/sqlops.proposed.d.ts'/>
/// <reference types='@types/node'/>

File diff suppressed because it is too large Load Diff

View File

@@ -658,7 +658,7 @@
}
},
"dependencies": {
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.9",
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.0",
"opener": "^1.4.3",
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
"vscode-extension-telemetry": "^0.0.15"

View File

@@ -8,9 +8,9 @@
"GO",
"-- Create the new database if it does not exist already",
"IF NOT EXISTS (",
"\tSELECT name",
"\tSELECT [name]",
"\t\tFROM sys.databases",
"\t\tWHERE name = N'${1:DatabaseName}'",
"\t\tWHERE [name] = N'${1:DatabaseName}'",
")",
"CREATE DATABASE ${1:DatabaseName}",
"GO"
@@ -29,9 +29,9 @@
"-- ALTER DATABASE ${1:DatabaseName} SET SINGLE_USER WITH ROLLBACK IMMEDIATE;",
"-- Drop the database if it exists",
"IF EXISTS (",
" SELECT name",
" SELECT [name]",
" FROM sys.databases",
" WHERE name = N'${1:DatabaseName}'",
" WHERE [name] = N'${1:DatabaseName}'",
")",
"DROP DATABASE ${1:DatabaseName}",
"GO"
@@ -42,38 +42,33 @@
"Create a new Table": {
"prefix": "sqlCreateTable",
"body": [
"-- Create a new table called '${1:TableName}' in schema '${2:SchemaName}'",
"-- Create a new table called '[${1:TableName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"-- Drop the table if it already exists",
"IF OBJECT_ID('${2:SchemaName}.${1:TableName}', 'U') IS NOT NULL",
"DROP TABLE ${2:SchemaName}.${1:TableName}",
"IF OBJECT_ID('[${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]', 'U') IS NOT NULL",
"DROP TABLE [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
"GO",
"-- Create the table in the specified schema",
"CREATE TABLE ${2:SchemaName}.${1:TableName}",
"-- Create the table in the specified database and schema",
"CREATE TABLE [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
"(",
"\t${1:TableName}Id INT NOT NULL PRIMARY KEY, -- primary key column",
"\t$3Column1 [NVARCHAR](50) NOT NULL,",
"\t$4Column2 [NVARCHAR](50) NOT NULL",
"\t-- specify more columns here",
"\t[${4:ColumnName}]Id INT NOT NULL PRIMARY KEY, -- Primary Key column",
"\t[${5:ColumnName1}] [NVARCHAR](50) NOT NULL,",
"\t[${6:ColumnName2}] [NVARCHAR](50) NOT NULL",
"\t-- Specify more columns here",
");",
"GO"
],
"description": "Create a new Table"
},
"Drop a Table": {
"prefix": "sqlDropTable",
"body": [
"-- Drop the table '${1:TableName}' in schema '${2:SchemaName}'",
"IF EXISTS (",
"\tSELECT *",
"\t\tFROM sys.tables",
"\t\tJOIN sys.schemas",
"\t\t\tON sys.tables.schema_id = sys.schemas.schema_id",
"\tWHERE sys.schemas.name = N'${2:SchemaName}'",
"\t\tAND sys.tables.name = N'${1:TableName}'",
")",
"\tDROP TABLE ${2:SchemaName}.${1:TableName}",
"GO"
"-- Drop a table called '${3:TableName}' in schema '${2:SchemaName}' in Database '${1:DatabaseName}'",
"-- Drop the table if it already exists",
"IF OBJECT_ID('[${1:DatabaseName}].[${2:SchemaName}].[${3:TableName}]', 'U') IS NOT NULL",
"DROP TABLE [${1:DatabaseName}].[${2:SchemaName}].[${3:TableName}]",
"GO"
],
"description": "Drop a Table"
},
@@ -81,9 +76,9 @@
"Add a new column to a Table": {
"prefix": "sqlAddColumn",
"body": [
"-- Add a new column '${1:NewColumnName}' to table '${2:TableName}' in schema '${3:SchemaName}'",
"ALTER TABLE ${3:SchemaName}.${2:TableName}",
"\tADD ${1:NewColumnName} /*new_column_name*/ int /*new_column_datatype*/ NULL /*new_column_nullability*/",
"-- Add a new column '[${1:NewColumnName}]' to table '[${2:TableName}]' in schema '[${3:SchemaName}]' in database '[${4:DatabaseName}]'",
"ALTER TABLE [${4:DatabaseName}].[${3:SchemaName}].[${2:TableName}]",
"\tADD [${1:NewColumnName}] /*new_column_name*/ ${5:int} /*new_column_datatype*/ ${6:NULL} /*new_column_nullability*/",
"GO"
],
"description": "Add a new column to a Table"
@@ -92,9 +87,9 @@
"Drop a column from a Table": {
"prefix": "sqlDropColumn",
"body": [
"-- Drop '${1:ColumnName}' from table '${2:TableName}' in schema '${3:SchemaName}'",
"ALTER TABLE ${3:SchemaName}.${2:TableName}",
"\tDROP COLUMN ${1:ColumnName}",
"-- Drop '[${1:ColumnName}]' from table '[${2:TableName}]' in schema '[${3:SchemaName}]' in database '[${4:DatabaseName}]'",
"ALTER TABLE [${4:DatabaseName}].[${3:SchemaName}].[${2:TableName}]",
"\tDROP COLUMN [${1:ColumnName}]",
"GO"
],
"description": "Add a new column to a Table"
@@ -103,9 +98,9 @@
"Select rows from a Table or a View": {
"prefix": "sqlSelect",
"body": [
"-- Select rows from a Table or View '${1:TableOrViewName}' in schema '${2:SchemaName}'",
"SELECT * FROM ${2:SchemaName}.${1:TableOrViewName}",
"WHERE $3\t/* add search conditions here */",
"-- Select rows from a Table or View '[${1:TableOrViewName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"SELECT * FROM [${3:DatabaseName}].[${2:SchemaName}].[${1:TableOrViewName}]",
"WHERE ${4:/* add search conditions here */}",
"GO"
],
"description": "Select rows from a Table or a View"
@@ -114,19 +109,19 @@
"Insert rows into a Table": {
"prefix": "sqlInsertRows",
"body": [
"-- Insert rows into table '${1:TableName}'",
"INSERT INTO ${1:TableName}",
"( -- columns to insert data into",
" $2[Column1], [Column2], [Column3]",
"-- Insert rows into table '${1:TableName}' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"INSERT INTO [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
"( -- Columns to insert data into",
" ${4:[ColumnName1], [ColumnName2], [ColumnName3]}",
")",
"VALUES",
"( -- first row: values for the columns in the list above",
" $3Column1_Value, Column2_Value, Column3_Value",
"( -- First row: values for the columns in the list above",
" ${5:ColumnValue1, ColumnValue2, ColumnValue3}",
"),",
"( -- second row: values for the columns in the list above",
" $4Column1_Value, Column2_Value, Column3_Value",
"( -- Second row: values for the columns in the list above",
" ${6:ColumnValue1, ColumnValue2, ColumnValue3}",
")",
"-- add more rows here",
"-- Add more rows here",
"GO"
],
"description": "Insert rows into a Table"
@@ -135,9 +130,9 @@
"Delete rows from a Table": {
"prefix": "sqlDeleteRows",
"body": [
"-- Delete rows from table '${1:TableName}'",
"DELETE FROM ${1:TableName}",
"WHERE $2\t/* add search conditions here */",
"-- Delete rows from table '[${1:TableName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"DELETE FROM [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
"WHERE ${4:/* add search conditions here */}",
"GO"
],
"description": "Delete rows from a Table"
@@ -146,13 +141,13 @@
"Update rows in a Table": {
"prefix": "sqlUpdateRows",
"body": [
"-- Update rows in table '${1:TableName}'",
"UPDATE ${1:TableName}",
"-- Update rows in table '[${1:TableName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"UPDATE [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
"SET",
"\t$2[Colum1] = Colum1_Value,",
"\t$3[Colum2] = Colum2_Value",
"\t-- add more columns and values here",
"WHERE $4\t/* add search conditions here */",
"\t[${4:ColumnName1}] = ${5:ColumnValue1},",
"\t[${6:ColumnName2}] = ${7:ColumnValue2}",
"\t-- Add more columns and values here",
"WHERE ${8:/* add search conditions here */}",
"GO"
],
"description": "Update rows in a Table"
@@ -207,8 +202,8 @@
"prefix": "sqlListTablesAndViews",
"body": [
"-- Get a list of tables and views in the current database",
"SELECT table_catalog [database], table_schema [schema], table_name name, table_type type",
"FROM information_schema.tables",
"SELECT table_catalog [database], table_schema [schema], table_name [name], table_type [type]",
"FROM INFORMATION_SCHEMA.TABLES",
"GO"
],
"description": "List tables and vies in the current database"
@@ -218,7 +213,7 @@
"prefix": "sqlListDatabases",
"body": [
"-- Get a list of databases",
"SELECT name FROM sys.databases",
"SELECT [name] FROM sys.databases",
"GO"
],
"description": "List databases"
@@ -232,8 +227,8 @@
"\tTableName = tbl.table_schema + '.' + tbl.table_name, ",
"\tColumnName = col.column_name, ",
"\tColumnDataType = col.data_type",
"FROM information_schema.tables tbl",
"INNER JOIN information_schema.columns col ",
"FROM INFORMATION_SCHEMA.TABLES tbl",
"INNER JOIN INFORMATION_SCHEMA.COLUMNS col ",
"\tON col.table_name = tbl.table_name",
"\tAND col.table_schema = tbl.table_schema",
"",
@@ -247,20 +242,20 @@
"prefix": "sqlCursor",
"body": [
"-- Declare a cursor for a Table or a View '${1:TableOrViewName}' in schema '${2:SchemaName}'",
"DECLARE @Column1 NVARCHAR(50), @Column2 NVARCHAR(50)",
"DECLARE @ColumnName1 NVARCHAR(50), @ColumnName2 NVARCHAR(50)",
"",
"DECLARE db_cursor CURSOR FOR",
"SELECT Column1, Column2",
"SELECT ColumnName1, Column2",
"FROM $2.$1",
"",
"OPEN db_cursor",
"FETCH NEXT FROM db_cursor INTO @Column1, @Column2",
"FETCH NEXT FROM db_cursor INTO @ColumnName1, @ColumnName2",
"",
"WHILE @@FETCH_STATUS = 0",
"BEGIN",
"\t-- add instructions to be executed for every row",
"\t$3",
"\tFETCH NEXT FROM db_cursor INTO @Column1, @Column2",
"\tFETCH NEXT FROM db_cursor INTO @ColumnName1, @ColumnName2",
"END",
"",
"CLOSE db_cursor",
@@ -303,5 +298,34 @@
"GO"
],
"description": "Get Space Used by Tables"
}
},
"Create a new Index": {
"prefix": "sqlCreateIndex",
"body": [
"-- Create a nonclustered index with or without a unique constraint",
"-- Or create a clustered index on table '[${1:TableName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
"CREATE ${5:/*UNIQUE or CLUSTERED*/} INDEX IX_${4:IndexName} ON [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}] ([${6:ColumnName1}] DESC /*Change sort order as needed*/",
"GO"
],
"description": "Create a new Index"
},
"Create a new Temporary Table": {
"prefix": "sqlCreateTempTable",
"body": [
"-- Drop a temporary table called '#${1:TableName}'",
"-- Drop the table if it already exists",
"IF OBJECT_ID('tempDB..#${1:TableName}', 'U') IS NOT NULL",
"DROP TABLE #${1:TableName}",
"GO",
"-- Create the temporary table from a physical table called '${4:TableName}' in schema '${3:SchemaName}' in database '${2:DatabaseName}'",
"SELECT *",
"INTO #${1:TableName}",
"FROM [${2:DatabaseName}].[${3:[SchemaName}].[${4:TableName}]",
"WHERE ${5:/* add search conditions here */}"
],
"description": "Create a new Temporary Table"
},
}

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.2",
"version": "1.5.0-alpha.12",
"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

@@ -35,6 +35,8 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
contracts.AgentJobActionRequest.type
];
private onUpdatedHandler: () => any;
constructor(client: SqlOpsDataClient) {
super(client, AgentServicesFeature.messagesTypes);
}
@@ -53,6 +55,18 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
protected registerProvider(options: undefined): Disposable {
const client = this._client;
let self = this;
// On updated registration
let registerOnUpdated = (handler: () => any): void => {
self.onUpdatedHandler = handler;
};
let fireOnUpdated = (): void => {
if (self.onUpdatedHandler) {
self.onUpdatedHandler();
}
};
// Job management methods
let getJobs = (ownerUri: string): Thenable<sqlops.AgentJobsResult> => {
@@ -96,7 +110,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.CreateAgentJobRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -112,7 +129,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.UpdateAgentJobRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -127,7 +147,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.DeleteAgentJobRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -157,7 +180,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.CreateAgentJobStepRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -173,7 +199,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.UpdateAgentJobStepRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -188,7 +217,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.DeleteAgentJobStepRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -218,7 +250,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.CreateAgentAlertRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -234,7 +269,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.UpdateAgentAlertRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -249,7 +287,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.DeleteAgentAlertRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -279,7 +320,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.CreateAgentOperatorRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -295,7 +339,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.UpdateAgentOperatorRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -310,7 +357,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.DeleteAgentOperatorRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -340,7 +390,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.CreateAgentProxyRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -356,7 +409,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.UpdateAgentProxyRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -370,6 +426,24 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
proxy: proxyInfo
};
let requestType = contracts.DeleteAgentProxyRequest.type;
return client.sendRequest(requestType, params).then(
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(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 => {
@@ -379,6 +453,7 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
);
};
// Job Schedule management methods
let getJobSchedules = (ownerUri: string): Thenable<sqlops.AgentJobSchedulesResult> => {
let params: contracts.AgentJobScheduleParams = {
@@ -401,7 +476,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.CreateAgentJobScheduleRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -417,7 +495,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.UpdateAgentJobScheduleRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -432,7 +513,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
};
let requestType = contracts.DeleteAgentJobScheduleRequest.type;
return client.sendRequest(requestType, params).then(
r => r,
r => {
fireOnUpdated();
return r;
},
e => {
client.logFailedRequest(requestType, e);
return Promise.resolve(undefined);
@@ -464,10 +548,12 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
createProxy,
updateProxy,
deleteProxy,
getCredentials,
getJobSchedules,
createJobSchedule,
updateJobSchedule,
deleteJobSchedule
deleteJobSchedule,
registerOnUpdated
});
}
}

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

@@ -49,9 +49,9 @@ core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.1.9":
version "0.1.9"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/a1a79895cb79658b75d78aa5cfd745855019148d"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.0":
version "0.2.0"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/c7a691c7fc5ad54a147090784a7f11efda301f4b"
dependencies:
vscode-languageclient "3.5.0"

View File

@@ -2,7 +2,7 @@
"name": "profiler",
"displayName": "SQL Server Profiler",
"description": "SQL Server Profiler for SQL Operations Studio",
"version": "0.1.1",
"version": "0.1.2",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",

View File

@@ -1,6 +1,6 @@
{
"name": "sqlops",
"version": "0.31.1",
"version": "0.31.4",
"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

@@ -69,10 +69,10 @@ export default class MainController implements vscode.Disposable {
private async getTabContent(view: sqlops.ModelView, customButton1: sqlops.window.modelviewdialog.Button, customButton2: sqlops.window.modelviewdialog.Button, componentWidth: number | string): Promise<void> {
let inputBox = view.modelBuilder.inputBox()
.withProperties({
multiline: true,
height: 100
}).component();
.withProperties({
multiline: true,
height: 100
}).component();
let inputBoxWrapper = view.modelBuilder.loadingComponent().withItem(inputBox).component();
inputBoxWrapper.loading = false;
customButton1.onClick(() => {
@@ -145,8 +145,8 @@ export default class MainController implements vscode.Disposable {
component: inputBox4,
title: 'inputBox4'
}], {
horizontal: true
}).component();
horizontal: true
}).component();
let groupModel1 = view.modelBuilder.groupContainer()
.withLayout({
}).withItems([
@@ -178,8 +178,8 @@ export default class MainController implements vscode.Disposable {
}).component();
let declarativeTable = view.modelBuilder.declarativeTable()
.withProperties({
columns: [{
.withProperties({
columns: [{
displayName: 'Column 1',
valueType: sqlops.DeclarativeDataType.string,
width: '20px',
@@ -204,12 +204,12 @@ export default class MainController implements vscode.Disposable {
{ name: 'options2', displayName: 'option 2' }
]
}
],
data: [
['Data00', 'Data01', false, 'options2'],
['Data10', 'Data11', true, 'options1']
]
}).component();
],
data: [
['Data00', 'Data01', false, 'options2'],
['Data10', 'Data11', true, 'options1']
]
}).component();
declarativeTable.onDataChanged(e => {
inputBox2.value = e.row.toString() + ' ' + e.column.toString() + ' ' + e.value.toString();
@@ -223,7 +223,7 @@ export default class MainController implements vscode.Disposable {
height: 150
}).withItems([
radioButton, groupModel1, radioButton2]
, { flex: '1 1 50%' }).component();
, { flex: '1 1 50%' }).component();
let formModel = view.modelBuilder.formContainer()
.withFormItems([{
component: inputBoxWrapper,
@@ -254,9 +254,9 @@ export default class MainController implements vscode.Disposable {
component: listBox,
title: 'List Box'
}], {
horizontal: false,
componentWidth: componentWidth
}).component();
horizontal: false,
componentWidth: componentWidth
}).component();
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
formWrapper.loading = false;
customButton2.onClick(() => {
@@ -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);
}
@@ -302,6 +301,17 @@ export default class MainController implements vscode.Disposable {
page1.registerContent(async (view) => {
await this.getTabContent(view, customButton1, customButton2, 800);
});
wizard.registerOperation({
displayName: 'test task',
description: 'task description',
isCancelable: true
}, op => {
op.updateStatus(sqlops.TaskStatus.InProgress);
op.updateStatus(sqlops.TaskStatus.InProgress, 'Task is running');
setTimeout(() => {
op.updateStatus(sqlops.TaskStatus.Succeeded);
}, 5000);
});
wizard.pages = [page1, page2];
wizard.open();
}
@@ -379,13 +389,14 @@ export default class MainController implements vscode.Disposable {
let monitorLightPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg'));
let monitorIcon = {
light: monitorLightPath,
dark: path.join(__dirname, '..', 'media', 'monitor_inverse.svg') };
dark: path.join(__dirname, '..', 'media', 'monitor_inverse.svg')
};
let monitorButton = view.modelBuilder.button()
.withProperties({
label: 'Monitor',
iconPath: monitorIcon
}).component();
.withProperties({
label: 'Monitor',
iconPath: monitorIcon
}).component();
let toolbarModel = view.modelBuilder.toolbarContainer()
.withToolbarItems([{
component: inputBox,

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) => {
@@ -33,7 +33,7 @@ export function appendRowLink(container: Builder, label: string, labelClass: str
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

@@ -150,8 +150,8 @@ export class OptionsDialog extends Modal {
private onOptionLinkClicked(optionName: string): void {
var option = this._optionElements[optionName].option;
this._optionTitle.innerHtml(option.displayName);
this._optionDescription.innerHtml(option.description);
this._optionTitle.text(option.displayName);
this._optionDescription.text(option.description);
}
private fillInOptions(container: Builder, options: sqlops.ServiceOption[]): void {

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

@@ -62,7 +62,7 @@ export class TabHeaderComponent extends Disposable implements AfterContentInit,
tabLabelcontainer.classList.add(this.tab.iconClass);
} else {
tabLabelcontainer.className = 'tabLabel';
tabLabelcontainer.innerHTML = this.tab.title;
tabLabelcontainer.textContent = this.tab.title;
}
tabLabelcontainer.title = this.tab.title;
}

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

@@ -6,6 +6,7 @@ import { mixin } from 'vs/base/common/objects';
import { SlickGrid } from 'angular2-slickgrid';
import { Button } from '../../button/button';
import { attachButtonStyler } from 'sql/common/theme/styler';
import { escape } from 'sql/base/common/strings';
import { IThemeService } from 'vs/platform/theme/common/themeService';
export class HeaderFilter {
@@ -174,7 +175,7 @@ export class HeaderFilter {
if (filterItems[i] && filterItems[i].indexOf('Error:') < 0) {
filterOptions += '<label><input type="checkbox" value="' + i + '"'
+ (filtered ? ' checked="checked"' : '')
+ '/>' + filterItems[i] + '</label>';
+ '/>' + escape(filterItems[i]) + '</label>';
}
}
let $filter = $('<div class="filter">')

View File

@@ -1,5 +1,6 @@
// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowdetailview.js
// heavily modified
import { escape } from 'sql/base/common/strings';
import { mixin } from 'vs/base/common/objects';
import * as nls from 'vs/nls';
@@ -354,7 +355,7 @@ export class RowDetailView {
html.push("style='height:", dataContext._height, "px;"); //set total height of padding
html.push("top:", rowHeight, "px'>"); //shift detail below 1st row
html.push("<div id='detailViewContainer_", dataContext.id, "' class='detail-container' style='max-height:" + (dataContext._height - rowHeight + bottomMargin) + "px'>"); //sub ctr for custom styling
html.push("<div id='innerDetailView_", dataContext.id, "'>", dataContext._detailContent, "</div></div>");
html.push("<div id='innerDetailView_", dataContext.id, "'>", escape(dataContext._detailContent), "</div></div>");
//&omit a final closing detail container </div> that would come next
return html.join('');

View File

@@ -82,7 +82,7 @@ export class Taskbar {
public static createTaskbarText(inputText: string): HTMLElement {
let element = document.createElement('div');
element.className = 'taskbarTextSeparator';
element.innerHTML = inputText;
element.textContent = inputText;
return element;
}

View File

@@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
/**
* Converts HTML characters inside the string to use entities instead. Makes the string safe from
* being used e.g. in HTMLElement.innerHTML.
*/
export function escape(html: string): string {
return html.replace(/[<|>|&|"]/g, function (match) {
switch (match) {
case '<': return '&lt;';
case '>': return '&gt;';
case '&': return '&amp;';
case '"': return '&quot;';
default: return match;
}
});
}

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

@@ -130,8 +130,8 @@ 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 acount.');
noAccountTitle.innerHTML = noAccountLabel;
let noAccountLabel = localize('accountDialog.noAccountLabel', 'There is no linked account. Please add an account.');
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

@@ -100,7 +100,7 @@ export class AutoOAuthDialog extends Modal {
let inputBox: InputBox;
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(label);
labelContainer.text(label);
});
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {

View File

@@ -145,7 +145,7 @@ export class FirewallRuleDialog extends Modal {
subnetIPRangeSection = subnetIPRangeContainer.getHTMLElement();
subnetIPRangeContainer.div({ 'class': 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(LocalizedStrings.FROM);
labelContainer.text(LocalizedStrings.FROM);
});
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
@@ -155,7 +155,7 @@ export class FirewallRuleDialog extends Modal {
});
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(LocalizedStrings.TO);
labelContainer.text(LocalizedStrings.TO);
});
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
@@ -234,7 +234,7 @@ export class FirewallRuleDialog extends Modal {
className += ' header';
}
container.div({ 'class': className }, (labelContainer) => {
labelContainer.innerHtml(content);
labelContainer.text(content);
});
}

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;
@@ -320,12 +320,13 @@ export enum MetadataType {
}
export enum TaskStatus {
notStarted = 0,
inProgress = 1,
succeeded = 2,
succeededWithWarning = 3,
failed = 4,
canceled = 5
NotStarted = 0,
InProgress = 1,
Succeeded = 2,
SucceededWithWarning = 3,
Failed = 4,
Canceled = 5,
Canceling = 6
}
export interface IConnectionParams {

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

@@ -280,26 +280,23 @@ export class ConnectionDialogService implements IConnectionDialogService {
return new Promise<void>((resolve, reject) => {
// only create the provider maps first time the dialog gets called
let capabilitiesPromise: Promise<void> = Promise.resolve();
if (this._providerTypes.length === 0) {
entries(this._capabilitiesService.providers).forEach(p => {
this._providerTypes.push(p[1].connection.displayName);
this._providerNameToDisplayNameMap[p[0]] = p[1].connection.displayName;
});
}
capabilitiesPromise.then(s => {
this.updateModelServerCapabilities(model);
// If connecting from a query editor set "save connection" to false
if (params && params.input && params.connectionType === ConnectionType.editor) {
this._model.saveProfile = false;
}
this.updateModelServerCapabilities(model);
// If connecting from a query editor set "save connection" to false
if (params && params.input && params.connectionType === ConnectionType.editor) {
this._model.saveProfile = false;
}
resolve(this.showDialogWithModel().then(() => {
if (connectionResult && connectionResult.errorMessage) {
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
}
}));
}, e => reject(e));
resolve(this.showDialogWithModel().then(() => {
if (connectionResult && connectionResult.errorMessage) {
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
}
}));
});
}
@@ -342,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

@@ -262,7 +262,7 @@ export class ConnectionDialogWidget extends Modal {
let recentHistoryLabel = localize('recentHistory', 'Recent history');
recentConnectionContainer.div({ class: 'recent-titles-container' }, (container) => {
container.div({ class: 'connection-history-label' }, (recentTitle) => {
recentTitle.innerHtml(recentHistoryLabel);
recentTitle.text(recentHistoryLabel);
});
container.div({ class: 'connection-history-actions' }, (actionsContainer) => {
this._actionbar = this._register(new ActionBar(actionsContainer.getHTMLElement(), { animated: false }));
@@ -303,7 +303,7 @@ export class ConnectionDialogWidget extends Modal {
this._noRecentConnectionBuilder.div({ class: 'connection-recent-content' }, (noRecentConnectionContainer) => {
let noRecentHistoryLabel = localize('noRecentConnections', 'No recent connection');
noRecentConnectionContainer.div({ class: 'no-recent-connections' }, (noRecentTitle) => {
noRecentTitle.innerHtml(noRecentHistoryLabel);
noRecentTitle.text(noRecentHistoryLabel);
});
});
}
@@ -335,7 +335,7 @@ export class ConnectionDialogWidget extends Modal {
this._noSavedConnectionBuilder.div({ class: 'connection-saved-content' }, (noSavedConnectionContainer) => {
let noSavedConnectionLabel = localize('noSavedConnections', 'No saved connection');
noSavedConnectionContainer.div({ class: 'no-saved-connections' }, (titleContainer) => {
titleContainer.innerHtml(noSavedConnectionLabel);
titleContainer.text(noSavedConnectionLabel);
});
});
}

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

@@ -163,7 +163,7 @@ export class NewDashboardTabDialog extends Modal {
this._noExtensionViewContainer = DOM.$('.no-extension-view');
let noExtensionTitle = DOM.append(this._noExtensionViewContainer, DOM.$('.no-extensionTab-label'));
let noExtensionLabel = localize('newdashboardTabDialog.noExtensionLabel', 'No dashboard extensions are installed at this time. Go to Extension Manager to explore recommended extensions.');
noExtensionTitle.innerHTML = noExtensionLabel;
noExtensionTitle.textContent = noExtensionLabel;
DOM.append(container, this._noExtensionViewContainer);
}

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

@@ -179,8 +179,8 @@ export class RestoreDialogController implements IRestoreDialogController {
private isSuccessfulRestore(response: TaskNode): boolean {
return (response.taskName === this._restoreTaskName &&
response.message === this._restoreCompleted &&
(response.status === TaskStatus.succeeded ||
response.status === TaskStatus.succeededWithWarning) &&
(response.status === TaskStatus.Succeeded ||
response.status === TaskStatus.SucceededWithWarning) &&
(response.taskExecutionMode === TaskExecutionMode.execute ||
response.taskExecutionMode === TaskExecutionMode.executeAndScript));
}

View File

@@ -226,7 +226,7 @@ export class RestoreDialog extends Modal {
destinationContainer.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(LocalizedStrings.TARGETDATABASE);
labelContainer.text(LocalizedStrings.TARGETDATABASE);
});
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
@@ -471,7 +471,7 @@ export class RestoreDialog extends Modal {
className += ' header';
}
container.div({ class: className }, (labelContainer) => {
labelContainer.innerHtml(content);
labelContainer.text(content);
});
}
@@ -535,7 +535,7 @@ export class RestoreDialog extends Modal {
let selectBox: SelectBox;
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
labelContainer.innerHtml(label);
labelContainer.text(label);
});
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {

View File

@@ -23,7 +23,7 @@ import { ITree } from 'vs/base/parts/tree/browser/tree';
/**
* Implements tree view for file browser
*/
export class FileBrowserTreeView {
export class FileBrowserTreeView implements IDisposable {
private _tree: ITree;
private _toDispose: IDisposable[] = [];

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as Strings from 'vs/base/common/strings';
import { escape } from 'sql/base/common/strings';
export class DBCellValue {
displayValue: string;
@@ -25,7 +25,7 @@ export function hyperLinkFormatter(row: number, cell: any, value: any, columnDef
valueToDisplay = 'NULL';
if (!value.isNull) {
cellClasses += ' xmlLink';
valueToDisplay = Strings.escape(value.displayValue);
valueToDisplay = escape(value.displayValue);
return `<a class="${cellClasses}" href="#" >${valueToDisplay}</a>`;
} else {
cellClasses += ' missing-value';
@@ -44,12 +44,12 @@ export function textFormatter(row: number, cell: any, value: any, columnDef: any
if (DBCellValue.isDBCellValue(value)) {
valueToDisplay = 'NULL';
if (!value.isNull) {
valueToDisplay = Strings.escape(value.displayValue.replace(/(\r\n|\n|\r)/g, ' '));
valueToDisplay = escape(value.displayValue.replace(/(\r\n|\n|\r)/g, ' '));
} else {
cellClasses += ' missing-value';
}
} else if (typeof value === 'string') {
valueToDisplay = Strings.escape(value);
valueToDisplay = escape(value);
}
return `<span title="${valueToDisplay}" class="${cellClasses}">${valueToDisplay}</span>`;

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';
@@ -197,7 +198,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
rowIndex++;
return {
values: row.cells.map(c => {
return mixin({ ariaLabel: c.displayValue }, c);
return mixin({ ariaLabel: escape(c.displayValue) }, c);
}), row: row.id
};
});
@@ -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

@@ -5,13 +5,12 @@
import 'vs/css!sql/parts/grid/views/query/chartViewer';
import {
Component, Inject, ViewContainerRef, forwardRef, OnInit,
ComponentFactoryResolver, ViewChild, OnDestroy, Input, ElementRef, ChangeDetectorRef
Component, Inject, forwardRef, OnInit, ComponentFactoryResolver, ViewChild,
OnDestroy, Input, ElementRef, ChangeDetectorRef
} from '@angular/core';
import { NgGridItemConfig } from 'angular2-grid';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
import { IInsightData, IInsightsView, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces';
@@ -32,7 +31,6 @@ import {
} from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { IDisposable } from 'vs/base/common/lifecycle';
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
import Severity from 'vs/base/common/severity';
import URI from 'vs/base/common/uri';
import * as nls from 'vs/nls';
@@ -87,21 +85,17 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
private _saveAction: SaveImageAction;
private _chartConfig: ILineConfig;
private _disposables: Array<IDisposable> = [];
private _dataSet: IGridDataSet;
private _executeResult: IInsightData;
private _chartComponent: ChartInsight;
private localizedStrings = LocalizedStrings;
private insightRegistry = insightRegistry;
protected localizedStrings = LocalizedStrings;
protected insightRegistry = insightRegistry;
@ViewChild(ComponentHostDirective) private componentHost: ComponentHostDirective;
@ViewChild('taskbarContainer', { read: ElementRef }) private taskbarContainer;
@ViewChild('chartTypesContainer', { read: ElementRef }) private chartTypesElement;
@ViewChild('legendContainer', { read: ElementRef }) private legendElement;
constructor(
@Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver,
@Inject(forwardRef(() => ViewContainerRef)) private _viewContainerRef: ViewContainerRef,
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
@Inject(INotificationService) private notificationService: INotificationService,
@@ -123,15 +117,25 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
}
private setDefaultChartConfig() {
this._chartConfig = <ILineConfig>{
dataDirection: 'vertical',
dataType: 'number',
legendPosition: 'none',
labelFirstColumn: false
};
let defaultChart = this.getDefaultChartType();
if (defaultChart === 'timeSeries') {
this._chartConfig = <ILineConfig>{
dataDirection: 'vertical',
dataType: 'point',
legendPosition: 'none',
labelFirstColumn: false
};
} else {
this._chartConfig = <ILineConfig>{
dataDirection: 'vertical',
dataType: 'number',
legendPosition: 'none',
labelFirstColumn: false
};
}
}
private getDefaultChartType(): string {
protected getDefaultChartType(): string {
let defaultChartType = Constants.chartTypeHorizontalBar;
if (this.configurationService) {
let chartSettings = WorkbenchUtils.getSqlConfigSection(this.configurationService, 'chart');
@@ -300,22 +304,18 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
return undefined;
}
private get showDataDirection(): boolean {
protected get showDataDirection(): boolean {
return ['pie', 'horizontalBar', 'bar', 'doughnut'].some(item => item === this.chartTypesSelectBox.value) || (this.chartTypesSelectBox.value === 'line' && this.dataType === 'number');
}
private get showLabelFirstColumn(): boolean {
protected get showLabelFirstColumn(): boolean {
return this.dataDirection === 'horizontal' && this.dataType !== 'point';
}
private get showColumnsAsLabels(): boolean {
protected get showColumnsAsLabels(): boolean {
return this.dataDirection === 'vertical' && this.dataType !== 'point';
}
private get showDataType(): boolean {
return this.chartTypesSelectBox.value === 'line';
}
public get dataDirection(): DataDirection {
return this._chartConfig.dataDirection;
}
@@ -326,7 +326,6 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
@Input() set dataSet(dataSet: IGridDataSet) {
// Setup the execute result
this._dataSet = dataSet;
this._executeResult = <IInsightData>{};
this._executeResult.columns = dataSet.columnDefinitions.map(def => def.name);
this._executeResult.rows = dataSet.dataRows.getRange(0, dataSet.dataRows.getLength()).map(gridRow => {
@@ -356,4 +355,4 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
ngOnDestroy() {
this._disposables.forEach(i => i.dispose());
}
}
}

View File

@@ -27,8 +27,9 @@ import { error } from 'sql/base/common/log';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { clone, mixin } from 'sql/base/common/objects';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { escape } from 'sql/base/common/strings';
import * as strings from 'vs/base/common/strings';
import { format } from 'vs/base/common/strings';
import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
@@ -60,7 +61,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
// create a function alias to use inside query.component
// tslint:disable-next-line:no-unused-variable
private stringsFormat: any = strings.format;
private stringsFormat: any = format;
// tslint:disable-next-line:no-unused-variable
private dataIcons: IGridIcon[] = [
@@ -302,7 +303,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
// Push row values onto end of gridData for slickgrid
gridData.push({
values: rows.rows[row].map(c => {
return mixin({ ariaLabel: c.displayValue }, c);
return mixin({ ariaLabel: escape(c.displayValue) }, c);
})
});
}
@@ -347,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,17 +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';
@@ -31,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;
@@ -48,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,
@@ -56,8 +49,16 @@ export class AgentViewComponent {
};
constructor(
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef) {
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
@Inject(IJobManagementService) jobManagementService: IJobManagementService,
@Inject(IDashboardService) dashboardService: IDashboardService,) {
this._expanded = new Map<string, string>();
let self = this;
jobManagementService.onDidChange((args) => {
self.refresh = true;
self._cd.detectChanges();
});
}
/**

View File

@@ -8,6 +8,7 @@
import * as sqlops from 'sqlops';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { JobCacheObject } from './jobManagementService';
import { Event } from 'vs/base/common/event';
export const SERVICE_ID = 'jobManagementService';
@@ -15,22 +16,27 @@ export const IJobManagementService = createDecorator<IJobManagementService>(SERV
export interface IJobManagementService {
_serviceBrand: any;
onDidChange: Event<void>;
registerProvider(providerId: string, provider: sqlops.AgentServicesProvider): void;
fireOnDidChange(): void;
getJobs(connectionUri: string): Thenable<sqlops.AgentJobsResult>;
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult>;
deleteJob(connectionUri: string, job: sqlops.AgentJobInfo): Thenable<sqlops.ResultStatus>;
getAlerts(connectionUri: string): Thenable<sqlops.AgentAlertsResult>;
deleteAlert(connectionUri: string, alert: sqlops.AgentAlertInfo): Thenable<sqlops.ResultStatus>;
getOperators(connectionUri: string): Thenable<sqlops.AgentOperatorsResult>;
deleteOperator(connectionUri: string, operator: sqlops.AgentOperatorInfo): Thenable<sqlops.ResultStatus>;
getProxies(connectionUri: string): Thenable<sqlops.AgentProxiesResult>;
deleteProxy(connectionUri: string, proxy: sqlops.AgentProxyInfo): Thenable<sqlops.ResultStatus>;
getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult>;
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

@@ -0,0 +1,463 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import * as nls from 'vs/nls';
import * as sqlops from 'sqlops';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
import { IJobManagementService } from '../common/interfaces';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { JobsViewComponent } from '../views/jobsView.component';
import { AlertsViewComponent } from 'sql/parts/jobManagement/views/alertsView.component';
import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsView.component';
import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component';
export enum JobActions {
Run = 'run',
Stop = 'stop'
}
export interface IJobActionInfo {
ownerUri: string;
targetObject: any;
}
// Job actions
export class JobsRefreshAction extends Action {
public static ID = 'jobaction.refresh';
public static LABEL = nls.localize('jobaction.refresh', "Refresh");
constructor(
) {
super(JobsRefreshAction.ID, JobsRefreshAction.LABEL, 'refreshIcon');
}
public run(context: JobsViewComponent): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
if (context) {
context.refreshJobs();
resolve(true);
} else {
reject(false);
}
});
}
}
export class NewJobAction extends Action {
public static ID = 'jobaction.newJob';
public static LABEL = nls.localize('jobaction.newJob', "New Job");
constructor(
) {
super(NewJobAction.ID, NewJobAction.LABEL, 'newStepIcon');
}
public run(context: JobsViewComponent): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
try {
context.openCreateJobDialog();
resolve(true);
} catch (e) {
reject(e);
}
});
}
}
export class RunJobAction extends Action {
public static ID = 'jobaction.runJob';
public static LABEL = nls.localize('jobaction.run', "Run");
constructor(
@INotificationService private notificationService: INotificationService,
@IJobManagementService private jobManagementService: IJobManagementService
) {
super(RunJobAction.ID, RunJobAction.LABEL, 'runJobIcon');
}
public run(context: JobHistoryComponent): TPromise<boolean> {
let jobName = context.agentJobInfo.name;
let ownerUri = context.ownerUri;
return new TPromise<boolean>((resolve, reject) => {
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(result => {
if (result.success) {
var startMsg = nls.localize('jobSuccessfullyStarted', ': The job was successfully started.');
this.notificationService.notify({
severity: Severity.Info,
message: jobName+ startMsg
});
resolve(true);
} else {
this.notificationService.notify({
severity: Severity.Error,
message: result.errorMessage
});
resolve(false);
}
});
});
}
}
export class StopJobAction extends Action {
public static ID = 'jobaction.stopJob';
public static LABEL = nls.localize('jobaction.stop', "Stop");
constructor(
@INotificationService private notificationService: INotificationService,
@IJobManagementService private jobManagementService: IJobManagementService
) {
super(StopJobAction.ID, StopJobAction.LABEL, 'stopJobIcon');
}
public run(context: JobHistoryComponent): TPromise<boolean> {
let jobName = context.agentJobInfo.name;
let ownerUri = context.ownerUri;
return new TPromise<boolean>((resolve, reject) => {
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(result => {
if (result.success) {
var stopMsg = nls.localize('jobSuccessfullyStopped', ': The job was successfully stopped.');
this.notificationService.notify({
severity: Severity.Info,
message: jobName+ stopMsg
});
resolve(true);
} else {
this.notificationService.notify({
severity: Severity.Error,
message: result.errorMessage
});
resolve(false);
}
});
});
}
}
export class EditJobAction extends Action {
public static ID = 'jobaction.editJob';
public static LABEL = nls.localize('jobaction.editJob', "Edit Job");
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);
}
}
export class DeleteJobAction extends Action {
public static ID = 'jobaction.deleteJob';
public static LABEL = nls.localize('jobaction.deleteJob', "Delete Job");
constructor(
@INotificationService private _notificationService: INotificationService,
@IJobManagementService private _jobService: IJobManagementService
) {
super(DeleteJobAction.ID, DeleteJobAction.LABEL);
}
public run(actionInfo: IJobActionInfo): TPromise<boolean> {
let self = this;
let job = actionInfo.targetObject as sqlops.AgentJobInfo;
self._notificationService.prompt(
Severity.Info,
nls.localize('jobaction.deleteJobConfirm,', "Are you sure you'd like to delete the job '{0}'?", job.name),
[{
label: DeleteJobAction.LABEL,
run: () => {
self._jobService.deleteJob(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
if (!result || !result.success) {
let errorMessage = nls.localize("jobaction.failedToDeleteJob", "Could not delete job '{0}'.\nError: {1}",
job.name, result.errorMessage ? result.errorMessage : 'Unknown error');
self._notificationService.error(errorMessage);
}
});
}
}, {
label: DeleteAlertAction.CancelLabel,
run: () => { }
}]
);
return TPromise.as(true);
}
}
// Step Actions
export class NewStepAction extends Action {
public static ID = 'jobaction.newStep';
public static LABEL = nls.localize('jobaction.newStep', "New Step");
constructor(
@ICommandService private _commandService: ICommandService
) {
super(NewStepAction.ID, NewStepAction.LABEL, 'newStepIcon');
}
public run(context: JobHistoryComponent): TPromise<boolean> {
let ownerUri = context.ownerUri;
let jobName = context.agentJobInfo.name;
let server = context.serverName;
let stepId = 0;
if (context.agentJobHistoryInfo && context.agentJobHistoryInfo.steps) {
stepId = context.agentJobHistoryInfo.steps.length + 1;
}
return new TPromise<boolean>((resolve, reject) => {
resolve(this._commandService.executeCommand('agent.openNewStepDialog', ownerUri, jobName, server, stepId));
});
}
}
// Alert Actions
export class NewAlertAction extends Action {
public static ID = 'jobaction.newAlert';
public static LABEL = nls.localize('jobaction.newAlert', "New Alert");
constructor(
) {
super(NewAlertAction.ID, NewAlertAction.LABEL, 'newStepIcon');
}
public run(context: AlertsViewComponent): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
try {
context.openCreateAlertDialog();
resolve(true);
} catch (e) {
reject(e);
}
});
}
}
export class EditAlertAction extends Action {
public static ID = 'jobaction.editAlert';
public static LABEL = nls.localize('jobaction.editAlert', "Edit Alert");
constructor(
@ICommandService private _commandService: ICommandService
) {
super(EditAlertAction.ID, EditAlertAction.LABEL);
}
public run(actionInfo: IJobActionInfo): TPromise<boolean> {
this._commandService.executeCommand(
'agent.openAlertDialog',
actionInfo.ownerUri,
actionInfo.targetObject);
return TPromise.as(true);
}
}
export class DeleteAlertAction extends Action {
public static ID = 'jobaction.deleteAlert';
public static LABEL = nls.localize('jobaction.deleteAlert', "Delete Alert");
public static CancelLabel = nls.localize('jobaction.Cancel', "Cancel");
constructor(
@INotificationService private _notificationService: INotificationService,
@IJobManagementService private _jobService: IJobManagementService
) {
super(DeleteAlertAction.ID, DeleteAlertAction.LABEL);
}
public run(actionInfo: IJobActionInfo): TPromise<boolean> {
let self = this;
let alert = actionInfo.targetObject as sqlops.AgentAlertInfo;
self._notificationService.prompt(
Severity.Info,
nls.localize('jobaction.deleteAlertConfirm,', "Are you sure you'd like to delete the alert '{0}'?", alert.name),
[{
label: DeleteAlertAction.LABEL,
run: () => {
self._jobService.deleteAlert(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
if (!result || !result.success) {
let errorMessage = nls.localize("jobaction.failedToDeleteAlert", "Could not delete alert '{0}'.\nError: {1}",
alert.name, result.errorMessage ? result.errorMessage : 'Unknown error');
self._notificationService.error(errorMessage);
}
});
}
}, {
label: DeleteAlertAction.CancelLabel,
run: () => { }
}]
);
return TPromise.as(true);
}
}
// Operator Actions
export class NewOperatorAction extends Action {
public static ID = 'jobaction.newOperator';
public static LABEL = nls.localize('jobaction.newOperator', "New Operator");
constructor(
) {
super(NewOperatorAction.ID, NewOperatorAction.LABEL, 'newStepIcon');
}
public run(context: OperatorsViewComponent): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
try {
context.openCreateOperatorDialog();
resolve(true);
} catch (e) {
reject(e);
}
});
}
}
export class EditOperatorAction extends Action {
public static ID = 'jobaction.editAlert';
public static LABEL = nls.localize('jobaction.editOperator', "Edit Operator");
constructor(
@ICommandService private _commandService: ICommandService
) {
super(EditOperatorAction.ID, EditOperatorAction.LABEL);
}
public run(actionInfo: IJobActionInfo): TPromise<boolean> {
this._commandService.executeCommand(
'agent.openOperatorDialog',
actionInfo.ownerUri,
actionInfo.targetObject);
return TPromise.as(true);
}
}
export class DeleteOperatorAction extends Action {
public static ID = 'jobaction.deleteOperator';
public static LABEL = nls.localize('jobaction.deleteOperator', "Delete Operator");
constructor(
@INotificationService private _notificationService: INotificationService,
@IJobManagementService private _jobService: IJobManagementService
) {
super(DeleteOperatorAction.ID, DeleteOperatorAction.LABEL);
}
public run(actionInfo: IJobActionInfo): TPromise<boolean> {
let self = this;
let operator = actionInfo.targetObject as sqlops.AgentOperatorInfo;
self._notificationService.prompt(
Severity.Info,
nls.localize('jobaction.deleteOperatorConfirm,', "Are you sure you'd like to delete the operator '{0}'?", operator.name),
[{
label: DeleteOperatorAction.LABEL,
run: () => {
self._jobService.deleteOperator(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
if (!result || !result.success) {
let errorMessage = nls.localize("jobaction.failedToDeleteOperator", "Could not delete operator '{0}'.\nError: {1}",
operator.name, result.errorMessage ? result.errorMessage : 'Unknown error');
self._notificationService.error(errorMessage);
}
});
}
}, {
label: DeleteAlertAction.CancelLabel,
run: () => { }
}]
);
return TPromise.as(true);
}
}
// Proxy Actions
export class NewProxyAction extends Action {
public static ID = 'jobaction.newProxy';
public static LABEL = nls.localize('jobaction.newProxy', "New Proxy");
constructor(
) {
super(NewProxyAction.ID, NewProxyAction.LABEL, 'newStepIcon');
}
public run(context: ProxiesViewComponent): TPromise<boolean> {
return new TPromise<boolean>((resolve, reject) => {
try {
context.openCreateProxyDialog();
resolve(true);
} catch (e) {
reject(e);
}
});
}
}
export class EditProxyAction extends Action {
public static ID = 'jobaction.editProxy';
public static LABEL = nls.localize('jobaction.editProxy', "Edit Proxy");
constructor(
@ICommandService private _commandService: ICommandService
) {
super(EditProxyAction.ID, EditProxyAction.LABEL);
}
public run(actionInfo: IJobActionInfo): TPromise<boolean> {
this._commandService.executeCommand(
'agent.openProxyDialog',
actionInfo.ownerUri,
actionInfo.targetObject);
return TPromise.as(true);
}
}
export class DeleteProxyAction extends Action {
public static ID = 'jobaction.deleteProxy';
public static LABEL = nls.localize('jobaction.deleteProxy', "Delete Proxy");
constructor(
@INotificationService private _notificationService: INotificationService,
@IJobManagementService private _jobService: IJobManagementService
) {
super(DeleteProxyAction.ID, DeleteProxyAction.LABEL);
}
public run(actionInfo: IJobActionInfo): TPromise<boolean> {
let self = this;
let proxy = actionInfo.targetObject as sqlops.AgentProxyInfo;
self._notificationService.prompt(
Severity.Info,
nls.localize('jobaction.deleteProxyConfirm,', "Are you sure you'd like to delete the proxy '{0}'?", proxy.accountName),
[{
label: DeleteProxyAction.LABEL,
run: () => {
self._jobService.deleteProxy(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
if (!result || !result.success) {
let errorMessage = nls.localize("jobaction.failedToDeleteProxy", "Could not delete proxy '{0}'.\nError: {1}",
proxy.accountName, result.errorMessage ? result.errorMessage : 'Unknown error');
self._notificationService.error(errorMessage);
}
});
}
}, {
label: DeleteAlertAction.CancelLabel,
run: () => { }
}]
);
return TPromise.as(true);
}
}

View File

@@ -10,11 +10,14 @@ import * as sqlops from 'sqlops';
import { TPromise } from 'vs/base/common/winjs.base';
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { Event, Emitter } from 'vs/base/common/event';
export class JobManagementService implements IJobManagementService {
_serviceBrand: any;
private _onDidChange = new Emitter<void>();
public readonly onDidChange: Event<void> = this._onDidChange.event;
private _providers: { [handle: string]: sqlops.AgentServicesProvider; } = Object.create(null);
private _jobCacheObject : {[server: string]: JobCacheObject; } = {};
@@ -23,30 +26,63 @@ export class JobManagementService implements IJobManagementService {
) {
}
public fireOnDidChange(): void {
this._onDidChange.fire(void 0);
}
public getJobs(connectionUri: string): Thenable<sqlops.AgentJobsResult> {
return this._runAction(connectionUri, (runner) => {
return runner.getJobs(connectionUri);
});
}
public deleteJob(connectionUri: string, job: sqlops.AgentJobInfo): Thenable<sqlops.ResultStatus> {
return this._runAction(connectionUri, (runner) => {
return runner.deleteJob(connectionUri, job);
});
}
public getAlerts(connectionUri: string): Thenable<sqlops.AgentAlertsResult> {
return this._runAction(connectionUri, (runner) => {
return runner.getAlerts(connectionUri);
});
}
public deleteAlert(connectionUri: string, alert: sqlops.AgentAlertInfo): Thenable<sqlops.ResultStatus> {
return this._runAction(connectionUri, (runner) => {
return runner.deleteAlert(connectionUri, alert);
});
}
public getOperators(connectionUri: string): Thenable<sqlops.AgentOperatorsResult> {
return this._runAction(connectionUri, (runner) => {
return runner.getOperators(connectionUri);
});
}
public deleteOperator(connectionUri: string, operator: sqlops.AgentOperatorInfo): Thenable<sqlops.ResultStatus> {
return this._runAction(connectionUri, (runner) => {
return runner.deleteOperator(connectionUri, operator);
});
}
public getProxies(connectionUri: string): Thenable<sqlops.AgentProxiesResult> {
return this._runAction(connectionUri, (runner) => {
return runner.getProxies(connectionUri);
});
}
public deleteProxy(connectionUri: string, proxy: sqlops.AgentProxyInfo): Thenable<sqlops.ResultStatus> {
return this._runAction(connectionUri, (runner) => {
return runner.deleteProxy(connectionUri, proxy);
});
}
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) => {
@@ -60,7 +96,6 @@ export class JobManagementService implements IJobManagementService {
});
}
private _runAction<T>(uri: string, action: (handler: sqlops.AgentServicesProvider) => Thenable<T>): Thenable<T> {
let providerId: string = this._connectionService.getProviderIdFromUri(uri);

View File

@@ -7,7 +7,7 @@
import * as nls from 'vs/nls';
export class AgentJobUtilities {
export class JobManagementUtilities {
public static startIconClass: string = 'action-label icon runJobIcon';
public static stopIconClass: string = 'action-label icon stopJobIcon';
@@ -63,8 +63,8 @@ export class AgentJobUtilities {
}
public static getActionIconClassName(startIcon: HTMLElement, stopIcon: HTMLElement, executionStatus: number) {
this.setRunnable(startIcon, AgentJobUtilities.startIconClass.length);
this.setRunnable(stopIcon, AgentJobUtilities.stopIconClass.length);
this.setRunnable(startIcon, JobManagementUtilities.startIconClass.length);
this.setRunnable(stopIcon, JobManagementUtilities.stopIconClass.length);
switch (executionStatus) {
case(1): // executing
startIcon.className += ' non-runnable';

View File

@@ -1 +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:#212121;}</style></defs><title>blocker</title><polygon class="cls-1" points="0.99 3.99 -0.01 3.99 -0.01 0.03 3.98 0.03 3.98 1.03 0.99 1.03 0.99 3.99"/><polygon class="cls-1" points="16.01 3.99 15.01 3.99 15.01 1.03 12.02 1.03 12.02 0.03 16.01 0.03 16.01 3.99"/><polygon class="cls-1" points="16.01 15.97 12.02 15.97 12.02 14.97 15.01 14.97 15.01 12.01 16.01 12.01 16.01 15.97"/><polygon class="cls-1" points="4 15.97 0.01 15.97 0.01 12.01 1.01 12.01 1.01 14.97 4 14.97 4 15.97"/><path class="cls-1" d="M8.41,3.18A4.82,4.82,0,1,0,13.23,8,4.83,4.83,0,0,0,8.41,3.18Zm0,.74A4.08,4.08,0,0,1,12.49,8a4,4,0,0,1-.85,2.47L5.69,5A4,4,0,0,1,8.41,3.93Zm0,8.15A4.08,4.08,0,0,1,4.34,8a4,4,0,0,1,.85-2.47L11.14,11A4,4,0,0,1,8.41,12.07Z"/></svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>jobalert</title><path d="M16,2.24V7.58A5.38,5.38,0,0,0,15,6.5V4.3L12.8,5.39l-.64-.11a5,5,0,0,0-.65,0l-.36,0-.36,0,4.11-2.05H1.12L7.87,6.61,7.49,7a4.7,4.7,0,0,0-.34.39L1,4.3v6.94H6a4.64,4.64,0,0,0,.07.5c0,.17.07.33.11.5H0v-10Zm-4.5,4a4.35,4.35,0,0,1,1.75.36A4.53,4.53,0,0,1,15.64,9a4.49,4.49,0,0,1,0,3.5,4.53,4.53,0,0,1-2.39,2.39,4.49,4.49,0,0,1-3.5,0,4.53,4.53,0,0,1-2.39-2.39,4.49,4.49,0,0,1,0-3.5A4.53,4.53,0,0,1,9.75,6.59,4.35,4.35,0,0,1,11.5,6.24Zm0,8A3.38,3.38,0,0,0,12.86,14a3.53,3.53,0,0,0,1.86-1.86,3.49,3.49,0,0,0,0-2.73,3.53,3.53,0,0,0-1.86-1.86,3.49,3.49,0,0,0-2.73,0A3.53,3.53,0,0,0,8.28,9.37a3.49,3.49,0,0,0,0,2.73A3.53,3.53,0,0,0,10.14,14,3.38,3.38,0,0,0,11.5,14.24Zm-.5-6h1v3H11Zm0,4h1v1H11Z"/></svg>

Before

Width:  |  Height:  |  Size: 847 B

After

Width:  |  Height:  |  Size: 815 B

View File

@@ -1 +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>blocker_inverse</title><polygon class="cls-1" points="0.99 3.99 -0.01 3.99 -0.01 0.03 3.98 0.03 3.98 1.03 0.99 1.03 0.99 3.99"/><polygon class="cls-1" points="16.01 3.99 15.01 3.99 15.01 1.03 12.02 1.03 12.02 0.03 16.01 0.03 16.01 3.99"/><polygon class="cls-1" points="16.01 15.97 12.02 15.97 12.02 14.97 15.01 14.97 15.01 12.01 16.01 12.01 16.01 15.97"/><polygon class="cls-1" points="4 15.97 0.01 15.97 0.01 12.01 1.01 12.01 1.01 14.97 4 14.97 4 15.97"/><path class="cls-1" d="M8.41,3.18A4.82,4.82,0,1,0,13.23,8,4.83,4.83,0,0,0,8.41,3.18Zm0,.74A4.08,4.08,0,0,1,12.49,8a4,4,0,0,1-.85,2.47L5.69,5A4,4,0,0,1,8.41,3.93Zm0,8.15A4.08,4.08,0,0,1,4.34,8a4,4,0,0,1,.85-2.47L11.14,11A4,4,0,0,1,8.41,12.07Z"/></svg>
<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>jobalert_inverse</title><path class="cls-1" d="M16,2.24V7.58A5.38,5.38,0,0,0,15,6.5V4.3L12.8,5.39l-.64-.11a5,5,0,0,0-.65,0l-.36,0-.36,0,4.11-2.05H1.12L7.87,6.61,7.49,7a4.7,4.7,0,0,0-.34.39L1,4.3v6.94H6a4.64,4.64,0,0,0,.07.5c0,.17.07.33.11.5H0v-10Zm-4.5,4a4.35,4.35,0,0,1,1.75.36A4.53,4.53,0,0,1,15.64,9a4.49,4.49,0,0,1,0,3.5,4.53,4.53,0,0,1-2.39,2.39,4.49,4.49,0,0,1-3.5,0,4.53,4.53,0,0,1-2.39-2.39,4.49,4.49,0,0,1,0-3.5A4.53,4.53,0,0,1,9.75,6.59,4.35,4.35,0,0,1,11.5,6.24Zm0,8A3.38,3.38,0,0,0,12.86,14a3.53,3.53,0,0,0,1.86-1.86,3.49,3.49,0,0,0,0-2.73,3.53,3.53,0,0,0-1.86-1.86,3.49,3.49,0,0,0-2.73,0A3.53,3.53,0,0,0,8.28,9.37a3.49,3.49,0,0,0,0,2.73A3.53,3.53,0,0,0,10.14,14,3.38,3.38,0,0,0,11.5,14.24Zm-.5-6h1v3H11Zm0,4h1v1H11Z"/></svg>

Before

Width:  |  Height:  |  Size: 852 B

After

Width:  |  Height:  |  Size: 883 B

View File

@@ -22,7 +22,7 @@ jobhistory-component {
}
.job-heading-container {
height: 49px;
height: 50px;
border-bottom: 3px solid #f4f4f4;
display: -webkit-box;
}
@@ -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,138 @@ 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: calc(100% - 75px);
width : 100%;
display: block;
}
#proxiesDiv .jobproxiesview-grid {
height: 94.7%;
width : 100%;
display: block;
.vs .action-label.icon.refreshIcon {
background-image: url('refresh.svg');
}
.vs-dark .action-label.icon.refreshIcon,
.hc-black .action-label.icon.refreshIcon {
background-image: url('refresh_inverse.svg');
}
.actionbar-container .monaco-action-bar > ul.actions-container {
padding-top: 10px;
}
jobsview-component .actionbar-container {
height: 40px;
}
.actionbar-container .monaco-action-bar > ul.actions-container > li.action-item {
padding-left: 20px;
}
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

@@ -1 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>security</title><path d="M6,8a4.88,4.88,0,0,0-1.33.18,5.11,5.11,0,0,0-1.2.5,5,5,0,0,0-1.79,1.79,5.11,5.11,0,0,0-.5,1.2A4.88,4.88,0,0,0,1,13H0a5.9,5.9,0,0,1,.28-1.79,6.12,6.12,0,0,1,2-2.94,5.33,5.33,0,0,1,1.58-.88,4.18,4.18,0,0,1-.79-.65,4,4,0,0,1-.59-.8A4.05,4.05,0,0,1,2.13,5a4,4,0,0,1,.18-2.57A4,4,0,0,1,4.44.31a4,4,0,0,1,3.12,0A4,4,0,0,1,9.69,2.44,4,4,0,0,1,9.87,5a4.05,4.05,0,0,1-.37.93,4,4,0,0,1-.59.8,4.18,4.18,0,0,1-.79.65,6.14,6.14,0,0,1,1,.5,5.73,5.73,0,0,1,.91.69l-.68.74a5,5,0,0,0-1.57-1A4.93,4.93,0,0,0,6,8ZM3,4a2.92,2.92,0,0,0,.23,1.17,3,3,0,0,0,1.6,1.6,3,3,0,0,0,2.33,0,3,3,0,0,0,1.6-1.6,3,3,0,0,0,0-2.33,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.92,2.92,0,0,0,3,4Zm12,8a2.45,2.45,0,0,1,0,1l1,.4-.38.93-1-.41a2.59,2.59,0,0,1-.67.67l.41,1-.93.38L13,15a2.45,2.45,0,0,1-1,0l-.4,1-.93-.38.41-1a2.59,2.59,0,0,1-.67-.67l-1,.41-.38-.93,1-.4a2.45,2.45,0,0,1,0-1l-1-.4.38-.93,1,.41a2.59,2.59,0,0,1,.67-.67l-.41-1,.93-.38.4,1a2.45,2.45,0,0,1,1,0l.4-1,.93.38-.41,1a2.59,2.59,0,0,1,.67.67l1-.41.38.93ZM12.5,14a1.47,1.47,0,0,0,.59-.12,1.49,1.49,0,0,0,.8-.8,1.52,1.52,0,0,0,0-1.17,1.49,1.49,0,0,0-.8-.8,1.52,1.52,0,0,0-1.17,0,1.49,1.49,0,0,0-.8.8,1.52,1.52,0,0,0,0,1.17,1.49,1.49,0,0,0,.8.8A1.47,1.47,0,0,0,12.5,14Z"/></svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>operator</title><path d="M10.36,9.41a7.54,7.54,0,0,1,1.9,1.05A7,7,0,0,1,13.73,12,6.9,6.9,0,0,1,15,16H14a5.79,5.79,0,0,0-.47-2.33,6.07,6.07,0,0,0-3.2-3.2A5.79,5.79,0,0,0,8,10a5.79,5.79,0,0,0-2.33.47,6.07,6.07,0,0,0-3.2,3.2A5.79,5.79,0,0,0,2,16H1a6.89,6.89,0,0,1,2.74-5.54,7.54,7.54,0,0,1,1.9-1.05A5.07,5.07,0,0,1,4,8a4.24,4.24,0,0,1-.73-.06,1.92,1.92,0,0,1-.64-.23,1.25,1.25,0,0,1-.45-.46A1.48,1.48,0,0,1,2,6.5v-3a1.47,1.47,0,0,1,.12-.59,1.49,1.49,0,0,1,.8-.8A1.47,1.47,0,0,1,3.5,2a1.4,1.4,0,0,1,.45.07,4.88,4.88,0,0,1,.8-.87,5.21,5.21,0,0,1,1-.65A4.95,4.95,0,0,1,8,0,4.88,4.88,0,0,1,9.95.39a5,5,0,0,1,2.66,2.66A4.88,4.88,0,0,1,13,5a4.93,4.93,0,0,1-.18,1.34,5,5,0,0,1-.53,1.23,5.12,5.12,0,0,1-.83,1A4.73,4.73,0,0,1,10.36,9.41ZM3,6.5a.51.51,0,0,0,.5.5H4V3.5a.5.5,0,0,0-.85-.35A.48.48,0,0,0,3,3.5ZM5.35,8a3.92,3.92,0,0,0,1.23.74A4,4,0,0,0,8,9a3.85,3.85,0,0,0,1.55-.32,4.05,4.05,0,0,0,2.13-2.13A3.85,3.85,0,0,0,12,5a3.85,3.85,0,0,0-.32-1.55A4.05,4.05,0,0,0,9.55,1.32,3.85,3.85,0,0,0,8,1a4,4,0,0,0-1.83.45A4,4,0,0,0,4.75,2.67a1.47,1.47,0,0,1,.18.51A5.75,5.75,0,0,1,5,3.91q0,.41,0,.85t0,.87q0,.42,0,.78T5,7H7.5a.5.5,0,0,1,0,1Z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1 +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>security_inverse</title><path class="cls-1" d="M6,8a4.88,4.88,0,0,0-1.33.18,5.11,5.11,0,0,0-1.2.5,5,5,0,0,0-1.79,1.79,5.11,5.11,0,0,0-.5,1.2A4.88,4.88,0,0,0,1,13H0a5.9,5.9,0,0,1,.28-1.79,6.12,6.12,0,0,1,2-2.94,5.33,5.33,0,0,1,1.58-.88,4.18,4.18,0,0,1-.79-.65,4,4,0,0,1-.59-.8A4.05,4.05,0,0,1,2.13,5a4,4,0,0,1,.18-2.57A4,4,0,0,1,4.44.31a4,4,0,0,1,3.12,0A4,4,0,0,1,9.69,2.44,4,4,0,0,1,9.87,5a4.05,4.05,0,0,1-.37.93,4,4,0,0,1-.59.8,4.18,4.18,0,0,1-.79.65,6.14,6.14,0,0,1,1,.5,5.73,5.73,0,0,1,.91.69l-.68.74a5,5,0,0,0-1.57-1A4.93,4.93,0,0,0,6,8ZM3,4a2.92,2.92,0,0,0,.23,1.17,3,3,0,0,0,1.6,1.6,3,3,0,0,0,2.33,0,3,3,0,0,0,1.6-1.6,3,3,0,0,0,0-2.33,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.92,2.92,0,0,0,3,4Zm12,8a2.45,2.45,0,0,1,0,1l1,.4-.38.93-1-.41a2.59,2.59,0,0,1-.67.67l.41,1-.93.38L13,15a2.45,2.45,0,0,1-1,0l-.4,1-.93-.38.41-1a2.59,2.59,0,0,1-.67-.67l-1,.41-.38-.93,1-.4a2.45,2.45,0,0,1,0-1l-1-.4.38-.93,1,.41a2.59,2.59,0,0,1,.67-.67l-.41-1,.93-.38.4,1a2.45,2.45,0,0,1,1,0l.4-1,.93.38-.41,1a2.59,2.59,0,0,1,.67.67l1-.41.38.93ZM12.5,14a1.47,1.47,0,0,0,.59-.12,1.49,1.49,0,0,0,.8-.8,1.52,1.52,0,0,0,0-1.17,1.49,1.49,0,0,0-.8-.8,1.52,1.52,0,0,0-1.17,0,1.49,1.49,0,0,0-.8.8,1.52,1.52,0,0,0,0,1.17,1.49,1.49,0,0,0,.8.8A1.47,1.47,0,0,0,12.5,14Z"/></svg>
<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>operator_inverse</title><path class="cls-1" d="M10.36,9.41a7.54,7.54,0,0,1,1.9,1.05A7,7,0,0,1,13.73,12,6.9,6.9,0,0,1,15,16H14a5.79,5.79,0,0,0-.47-2.33,6.07,6.07,0,0,0-3.2-3.2A5.79,5.79,0,0,0,8,10a5.79,5.79,0,0,0-2.33.47,6.07,6.07,0,0,0-3.2,3.2A5.79,5.79,0,0,0,2,16H1a6.89,6.89,0,0,1,2.74-5.54,7.54,7.54,0,0,1,1.9-1.05A5.07,5.07,0,0,1,4,8a4.24,4.24,0,0,1-.73-.06,1.92,1.92,0,0,1-.64-.23,1.25,1.25,0,0,1-.45-.46A1.48,1.48,0,0,1,2,6.5v-3a1.47,1.47,0,0,1,.12-.59,1.49,1.49,0,0,1,.8-.8A1.47,1.47,0,0,1,3.5,2a1.4,1.4,0,0,1,.45.07,4.88,4.88,0,0,1,.8-.87,5.21,5.21,0,0,1,1-.65A4.95,4.95,0,0,1,8,0,4.88,4.88,0,0,1,9.95.39a5,5,0,0,1,2.66,2.66A4.88,4.88,0,0,1,13,5a4.93,4.93,0,0,1-.18,1.34,5,5,0,0,1-.53,1.23,5.12,5.12,0,0,1-.83,1A4.73,4.73,0,0,1,10.36,9.41ZM3,6.5a.51.51,0,0,0,.5.5H4V3.5a.5.5,0,0,0-.85-.35A.48.48,0,0,0,3,3.5ZM5.35,8a3.92,3.92,0,0,0,1.23.74A4,4,0,0,0,8,9a3.85,3.85,0,0,0,1.55-.32,4.05,4.05,0,0,0,2.13-2.13A3.85,3.85,0,0,0,12,5a3.85,3.85,0,0,0-.32-1.55A4.05,4.05,0,0,0,9.55,1.32,3.85,3.85,0,0,0,8,1a4,4,0,0,0-1.83.45A4,4,0,0,0,4.75,2.67a1.47,1.47,0,0,1,.18.51A5.75,5.75,0,0,1,5,3.91q0,.41,0,.85t0,.87q0,.42,0,.78T5,7H7.5a.5.5,0,0,1,0,1Z"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +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:#101e23;}.cls-2{fill:#4bb8d1;}.cls-3{fill:#0c1011;}</style></defs><title>health</title><path class="cls-1" d="M12.58,1.51A6.36,6.36,0,0,0,8,3.9,6.32,6.32,0,0,0,3.41,1.51,3.81,3.81,0,0,0,0,5.35,5.7,5.7,0,0,0,.64,7.88h.72l0-.08A5.18,5.18,0,0,1,.64,5.39c.07-1.25.87-3.14,2.8-3.23h.12A5.81,5.81,0,0,1,7.73,4.63L8,5.06l.27-.43a5.72,5.72,0,0,1,4.28-2.47c1.93.09,2.73,2,2.8,3.23a5.15,5.15,0,0,1-.64,2.34l0,0a2.38,2.38,0,0,1-.34.68,19.45,19.45,0,0,1-6.57,6.06,11.11,11.11,0,0,1-1.25-.81c-.34-.25-.66-.52-1-.8h0a22.83,22.83,0,0,1-2.76-3H2a18.68,18.68,0,0,0,5.76,5.29h0l0,0h0c3.49-1.63,7-5.73,7.49-7.18V8A5.85,5.85,0,0,0,16,5.35,3.81,3.81,0,0,0,12.58,1.51Z"/><path class="cls-1" d="M1.41,8l-.1-.15h0Z"/><path class="cls-1" d="M7.79,15.22v0h0Z"/><path class="cls-1" d="M7.76,15.23h0v0Z"/><path class="cls-1" d="M14.72,7.73l0,0a.13.13,0,0,0,0,0Z"/><path class="cls-2" d="M12.62,8.7v.12a.48.48,0,0,1-.48.48H8.66l0,.07L7.38,12.65h0A.72.72,0,0,1,6,12.53V9.44H6V6.6L5,9.05H5a.56.56,0,0,1-.52.37H.92V8.36H4.13l0-.07L5.41,5h0a.72.72,0,0,1,1.42.12V8.22h0v2.84L7.77,8.6h0a.56.56,0,0,1,.52-.37h3.84A.48.48,0,0,1,12.62,8.7Z"/><path class="cls-3" d="M12.62,8.7v.12a.48.48,0,0,1-.48.48H8.66l0,.07L7.38,12.65h0A.72.72,0,0,1,6,12.53V9.44H6V6.6L5,9.05H5a.56.56,0,0,1-.52.37H.92V8.36H4.13l0-.07L5.41,5h0a.72.72,0,0,1,1.42.12V8.22h0v2.84L7.77,8.6h0a.56.56,0,0,1,.52-.37h3.84A.48.48,0,0,1,12.62,8.7Z"/></svg>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>proxy_account</title><path d="M12.23,7a3,3,0,0,0-3.39-.77,3,3,0,0,0-1.6,1.6A2.93,2.93,0,0,0,7,9a3,3,0,0,1-1.23,2.41,4.31,4.31,0,0,1,.66.42,4.2,4.2,0,0,1,.57.53V15a2.93,2.93,0,0,0-.23-1.16,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.93,2.93,0,0,0,1,15H0a3.92,3.92,0,0,1,.16-1.11,4.11,4.11,0,0,1,.45-1,3.87,3.87,0,0,1,.7-.84,4.2,4.2,0,0,1,.92-.63,3,3,0,0,1-1-3.58,3,3,0,0,1,1.6-1.6A2.92,2.92,0,0,1,4,6,3,3,0,0,1,6.41,7.23,4.12,4.12,0,0,1,8.23,5.41,3,3,0,0,1,7,3a2.93,2.93,0,0,1,.23-1.16A3,3,0,0,1,8.83.23a3,3,0,0,1,2.33,0,3,3,0,0,1,1.6,1.6,3,3,0,0,1-.09,2.52,2.94,2.94,0,0,1-.9,1.06,4.07,4.07,0,0,1,1,.67,4,4,0,0,1,.73.92ZM4,11a1.94,1.94,0,0,0,.78-.16A2,2,0,0,0,5.84,9.78a2,2,0,0,0,0-1.55A2,2,0,0,0,4.78,7.16a2,2,0,0,0-1.55,0A2,2,0,0,0,2.16,8.22a2,2,0,0,0,0,1.55,2,2,0,0,0,1.07,1.07A1.94,1.94,0,0,0,4,11ZM8,3a1.94,1.94,0,0,0,.16.78A2,2,0,0,0,9.22,4.84a2,2,0,0,0,1.55,0,2,2,0,0,0,1.07-1.07,2,2,0,0,0,0-1.55,2,2,0,0,0-1.07-1.07,2,2,0,0,0-1.55,0A2,2,0,0,0,8.16,2.22,1.94,1.94,0,0,0,8,3Zm8,7v6H8V10h2V8h4v2Zm-1,1H9v1h6Zm0,2H14v1H13V13H11v1H10V13H9v2h6Zm-4-3h2V9H11Z"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1 +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;}.cls-2{fill:#101e23;}.cls-3{fill:#4bb8d1;}</style></defs><title>health_inverse</title><path class="cls-1" d="M12.58,1.51A6.36,6.36,0,0,0,8,3.9,6.32,6.32,0,0,0,3.41,1.51,3.81,3.81,0,0,0,0,5.35,5.7,5.7,0,0,0,.64,7.88h.72l0-.08A5.18,5.18,0,0,1,.64,5.39c.07-1.25.87-3.14,2.8-3.23h.12A5.81,5.81,0,0,1,7.73,4.63L8,5.06l.27-.43a5.72,5.72,0,0,1,4.28-2.47c1.93.09,2.73,2,2.8,3.23a5.15,5.15,0,0,1-.64,2.34l0,0a2.38,2.38,0,0,1-.34.68,19.45,19.45,0,0,1-6.57,6.06,11.11,11.11,0,0,1-1.25-.81c-.34-.25-.66-.52-1-.8h0a22.83,22.83,0,0,1-2.76-3H2a18.68,18.68,0,0,0,5.76,5.29h0l0,0h0c3.49-1.63,7-5.73,7.49-7.18V8A5.85,5.85,0,0,0,16,5.35,3.81,3.81,0,0,0,12.58,1.51Z"/><path class="cls-2" d="M1.41,8l-.1-.15h0Z"/><path class="cls-2" d="M7.79,15.22v0h0Z"/><path class="cls-2" d="M7.76,15.23h0v0Z"/><path class="cls-2" d="M14.72,7.73l0,0a.13.13,0,0,0,0,0Z"/><path class="cls-3" d="M12.62,8.7v.12a.48.48,0,0,1-.48.48H8.66l0,.07L7.38,12.65h0A.72.72,0,0,1,6,12.53V9.44H6V6.6L5,9.05H5a.56.56,0,0,1-.52.37H.92V8.36H4.13l0-.07L5.41,5h0a.72.72,0,0,1,1.42.12V8.22h0v2.84L7.77,8.6h0a.56.56,0,0,1,.52-.37h3.84A.48.48,0,0,1,12.62,8.7Z"/><path class="cls-1" d="M12.62,8.7v.12a.48.48,0,0,1-.48.48H8.66l0,.07L7.38,12.65h0A.72.72,0,0,1,6,12.53V9.44H6V6.6L5,9.05H5a.56.56,0,0,1-.52.37H.92V8.36H4.13l0-.07L5.41,5h0a.72.72,0,0,1,1.42.12V8.22h0v2.84L7.77,8.6h0a.56.56,0,0,1,.52-.37h3.84A.48.48,0,0,1,12.62,8.7Z"/></svg>
<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>proxy_account_inverse</title><path class="cls-1" d="M12.23,7a3,3,0,0,0-3.39-.77,3,3,0,0,0-1.6,1.6A2.93,2.93,0,0,0,7,9a3,3,0,0,1-1.23,2.41,4.31,4.31,0,0,1,.66.42,4.2,4.2,0,0,1,.57.53V15a2.93,2.93,0,0,0-.23-1.16,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.93,2.93,0,0,0,1,15H0a3.92,3.92,0,0,1,.16-1.11,4.11,4.11,0,0,1,.45-1A3.87,3.87,0,0,1,1.3,12a4.2,4.2,0,0,1,.92-.63,3,3,0,0,1-1-3.58,3,3,0,0,1,1.6-1.6A2.92,2.92,0,0,1,4,6,3,3,0,0,1,6.41,7.23,4.12,4.12,0,0,1,8.23,5.41,3,3,0,0,1,7,3a2.93,2.93,0,0,1,.23-1.16A3,3,0,0,1,8.83.23a3,3,0,0,1,2.33,0,3,3,0,0,1,1.6,1.6,3,3,0,0,1-.09,2.52,2.94,2.94,0,0,1-.9,1.06,4.07,4.07,0,0,1,1,.67,4,4,0,0,1,.73.92ZM4,11a1.94,1.94,0,0,0,.78-.16A2,2,0,0,0,5.84,9.78a2,2,0,0,0,0-1.55A2,2,0,0,0,4.78,7.16a2,2,0,0,0-1.55,0A2,2,0,0,0,2.16,8.22a2,2,0,0,0,0,1.55,2,2,0,0,0,1.07,1.07A1.94,1.94,0,0,0,4,11ZM8,3a1.94,1.94,0,0,0,.16.78A2,2,0,0,0,9.22,4.84a2,2,0,0,0,1.55,0,2,2,0,0,0,1.07-1.07,2,2,0,0,0,0-1.55,2,2,0,0,0-1.07-1.07,2,2,0,0,0-1.55,0A2,2,0,0,0,8.16,2.22,1.94,1.94,0,0,0,8,3Zm8,7v6H8V10h2V8h4v2Zm-1,1H9v1h6Zm0,2H14v1H13V13H11v1H10V13H9v2h6Zm-4-3h2V9H11Z"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.2 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:#212121;}</style></defs><title>refresh</title><path class="cls-1" d="M12.51,1.59a8.06,8.06,0,0,1,3.06,4A7.83,7.83,0,0,1,16,8.2a7.91,7.91,0,0,1-.29,2.12,8.13,8.13,0,0,1-.8,1.91A8,8,0,0,1,12,15.11a8.1,8.1,0,0,1-1.91.8,8.06,8.06,0,0,1-4.25,0A8.08,8.08,0,0,1,4,15.11a8,8,0,0,1-2.87-2.87,8.07,8.07,0,0,1-.8-1.91,8,8,0,0,1,0-4.25,8.11,8.11,0,0,1,.82-1.94,7.86,7.86,0,0,1,1.3-1.66A8,8,0,0,1,4.14,1.2H2V.2H6v4H5V1.88A7,7,0,0,0,1.28,6.24a7,7,0,0,0,0,3.82,7,7,0,0,0,1.8,3.09A7,7,0,0,0,6.14,15a7,7,0,0,0,3.71,0,7,7,0,0,0,1.67-.71,7,7,0,0,0,3.22-4.18,7,7,0,0,0-.13-4.12,7.07,7.07,0,0,0-2.68-3.52,6.78,6.78,0,0,0-2.07-1l.27-1A7.67,7.67,0,0,1,12.51,1.59Z"/></svg>

After

Width:  |  Height:  |  Size: 767 B

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>refresh_inverse</title><path class="cls-1" d="M12.51,1.59a8.06,8.06,0,0,1,3.06,4A7.83,7.83,0,0,1,16,8.2a7.91,7.91,0,0,1-.29,2.12,8.13,8.13,0,0,1-.8,1.91A8,8,0,0,1,12,15.11a8.1,8.1,0,0,1-1.91.8,8.06,8.06,0,0,1-4.25,0A8.08,8.08,0,0,1,4,15.11a8,8,0,0,1-2.87-2.87,8.07,8.07,0,0,1-.8-1.91,8,8,0,0,1,0-4.25,8.11,8.11,0,0,1,.82-1.94,7.86,7.86,0,0,1,1.3-1.66A8,8,0,0,1,4.14,1.2H2V.2H6v4H5V1.88A7,7,0,0,0,1.28,6.24a7,7,0,0,0,0,3.82,7,7,0,0,0,1.8,3.09A7,7,0,0,0,6.14,15a7,7,0,0,0,3.71,0,7,7,0,0,0,1.67-.71,7,7,0,0,0,3.22-4.18,7,7,0,0,0-.13-4.12,7.07,7.07,0,0,0-2.68-3.52,6.78,6.78,0,0,0-2.07-1l.27-1A7.67,7.67,0,0,1,12.51,1.59Z"/></svg>

After

Width:  |  Height:  |  Size: 772 B

View File

@@ -9,9 +9,7 @@
<h1 class="job-heading" *ngIf="_isCloud === true">No Alerts Available</h1>
<div class="icon in-progress" *ngIf="_showProgressWheel === true"></div>
</div>
<div class="jobs-view-toolbar">
<div (click)="refreshJobs()" tabindex="0"><div class="small icon refresh"></div><span>{{RefreshText}}</span></div>
<div (click)="openCreateJobDialog()" tabindex="0"><div class="small icon new"></div><span>{{NewAlertText}}</span></div>
</div>
<div #jobalertsgrid class="jobview-grid"></div>
<div #actionbarContainer class="actionbar-container"></div>
<div #jobalertsgrid class="jobalertsview-grid"></div>

View File

@@ -12,26 +12,34 @@ import 'vs/css!../common/media/jobs';
import 'vs/css!sql/media/icons/common-icons';
import 'vs/css!sql/base/browser/ui/table/media/table';
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core';
import * as sqlops from 'sqlops';
import * as dom from 'vs/base/browser/dom';
import * as nls from 'vs/nls';
import * as sqlops from 'sqlops';
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnInit } from '@angular/core';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { Table } from 'sql/base/browser/ui/table/table';
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
import * as dom from 'vs/base/browser/dom';
import { IJobManagementService } from '../common/interfaces';
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
import { EditAlertAction, DeleteAlertAction, NewAlertAction } from 'sql/parts/jobManagement/common/jobActions';
import { JobManagementView } from 'sql/parts/jobManagement/views/jobManagementView';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
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,
templateUrl: decodeURI(require.toUrl('./alertsView.component.html')),
providers: [{ provide: TabChild, useExisting: forwardRef(() => AlertsViewComponent) }],
})
export class AlertsViewComponent implements AfterContentChecked {
export class AlertsViewComponent extends JobManagementView implements OnInit {
private columns: Array<Slick.Column<any>> = [
{ name: nls.localize('jobAlertColumns.name', 'Name'), field: 'name', width: 200, id: 'name' },
@@ -44,85 +52,82 @@ export class AlertsViewComponent implements AfterContentChecked {
private options: Slick.GridOptions<any> = {
syncColumnCellResize: true,
enableColumnReorder: false,
rowHeight: 45,
rowHeight: ROW_HEIGHT,
enableCellNavigation: true,
editable: false
};
private dataView: any;
private _isCloud: boolean;
@ViewChild('jobalertsgrid') _gridEl: ElementRef;
private isVisible: boolean = false;
private isInitialized: boolean = false;
private isRefreshing: boolean = false;
private _table: Table<any>;
public alerts: sqlops.AgentAlertInfo[];
private _serverName: string;
private _isCloud: boolean;
private _showProgressWheel: boolean;
private NewAlertText: string = nls.localize('jobAlertToolbar-NewJob', "New Alert");
private RefreshText: string = nls.localize('jobAlertToolbar-Refresh', "Refresh");
public alerts: sqlops.AgentAlertInfo[];
public contextAction = NewAlertAction;
constructor(
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
@Inject(IThemeService) private _themeService: IThemeService,
@Inject(ICommandService) private _commandService: ICommandService
) {
this._isCloud = this._dashboardService.connectionManagementService.connectionInfo.serverInfo.isCloud;
@Inject(ICommandService) private _commandService: ICommandService,
@Inject(IInstantiationService) instantiationService: IInstantiationService,
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
@Inject(IKeybindingService) keybindingService: IKeybindingService,
@Inject(IDashboardService) _dashboardService: IDashboardService) {
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService);
this._isCloud = commonService.connectionManagementService.connectionInfo.serverInfo.isCloud;
}
ngOnInit(){
// set base class elements
this._visibilityElement = this._gridEl;
this._parentComponent = this._agentViewComponent;
}
public layout() {
this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement)));
}
ngAfterContentChecked() {
if (this.isVisible === false && this._gridEl.nativeElement.offsetParent !== null) {
this.isVisible = true;
if (!this.isInitialized) {
this._showProgressWheel = true;
this.onFirstVisible(false);
this.isInitialized = true;
}
} else if (this.isVisible === true && this._agentViewComponent.refresh === true) {
this._showProgressWheel = true;
this.onFirstVisible(false);
this.isRefreshing = true;
this._agentViewComponent.refresh = false;
} else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) {
this.isVisible = false;
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(cached?: boolean) {
onFirstVisible() {
let self = this;
let columns = this.columns.map((column) => {
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();
$(this._gridEl.nativeElement).empty();
$(this.actionBarContainer.nativeElement).empty();
this.initActionBar();
this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options);
this._table.grid.setData(this.dataView, true);
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
this._register(this._table.onContextMenu((e: DOMEvent, data: Slick.OnContextMenuEventArgs<any>) => {
self.openContextMenu(e);
}));
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
this._jobManagementService.getAlerts(ownerUri).then((result) => {
if (result && result.alerts) {
self.alerts = result.alerts;
self.onAlertsAvailable(result.alerts);
} else {
// TODO: handle error
}
this._showProgressWheel = false;
if (this.isVisible) {
this._cd.detectChanges();
}
});
}
@@ -144,14 +149,34 @@ export class AlertsViewComponent implements AfterContentChecked {
this.dataView.endUpdate();
this._table.autosizeColumns();
this._table.resizeCanvas();
this._showProgressWheel = false;
this._cd.detectChanges();
}
private openCreateJobDialog() {
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
this._commandService.executeCommand('agent.openCreateAlertDialog', ownerUri);
protected getTableActions(): TPromise<IAction[]> {
let actions: IAction[] = [];
actions.push(this._instantiationService.createInstance(EditAlertAction));
actions.push(this._instantiationService.createInstance(DeleteAlertAction));
return TPromise.as(actions);
}
protected getCurrentTableObject(rowIndex: number): any {
return (this.alerts && this.alerts.length >= rowIndex)
? this.alerts[rowIndex]
: undefined;
}
public openCreateAlertDialog() {
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.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

@@ -1,116 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import * as nls from 'vs/nls';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
import { IJobManagementService } from '../common/interfaces';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IConnectionManagementService } from '../../connection/common/connectionManagement';
export enum JobActions {
Run = 'run',
Stop = 'stop',
NewStep = 'newStep'
}
export class RunJobAction extends Action {
public static ID = 'jobaction.runJob';
public static LABEL = nls.localize('jobaction.run', "Run");
constructor(
@INotificationService private notificationService: INotificationService,
@IJobManagementService private jobManagementService: IJobManagementService
) {
super(RunJobAction.ID, RunJobAction.LABEL, 'runJobIcon');
}
public run(context: JobHistoryComponent): TPromise<boolean> {
let jobName = context.agentJobInfo.name;
let ownerUri = context.ownerUri;
return new TPromise<boolean>((resolve, reject) => {
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(result => {
if (result.success) {
var startMsg = nls.localize('jobSuccessfullyStarted', ': The job was successfully started.');
this.notificationService.notify({
severity: Severity.Info,
message: jobName+ startMsg
});
resolve(true);
} else {
this.notificationService.notify({
severity: Severity.Error,
message: result.errorMessage
});
resolve(false);
}
});
});
}
}
export class StopJobAction extends Action {
public static ID = 'jobaction.stopJob';
public static LABEL = nls.localize('jobaction.stop', "Stop");
constructor(
@INotificationService private notificationService: INotificationService,
@IJobManagementService private jobManagementService: IJobManagementService
) {
super(StopJobAction.ID, StopJobAction.LABEL, 'stopJobIcon');
}
public run(context: JobHistoryComponent): TPromise<boolean> {
let jobName = context.agentJobInfo.name;
let ownerUri = context.ownerUri;
return new TPromise<boolean>((resolve, reject) => {
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(result => {
if (result.success) {
var stopMsg = nls.localize('jobSuccessfullyStopped', ': The job was successfully stopped.');
this.notificationService.notify({
severity: Severity.Info,
message: jobName+ stopMsg
});
resolve(true);
} else {
this.notificationService.notify({
severity: Severity.Error,
message: result.errorMessage
});
resolve(false);
}
});
});
}
}
export class NewStepAction extends Action {
public static ID = 'jobaction.newStep';
public static LABEL = nls.localize('jobaction.newStep', "New Step");
constructor(
@INotificationService private notificationService: INotificationService,
@ICommandService private _commandService: ICommandService,
@IConnectionManagementService private _connectionService
) {
super(NewStepAction.ID, NewStepAction.LABEL, 'newStepIcon');
}
public run(context: JobHistoryComponent): TPromise<boolean> {
let ownerUri = context.ownerUri;
let jobName = context.agentJobInfo.name;
let server = context.serverName;
let stepId = 0;
if (context.agentJobHistoryInfo && context.agentJobHistoryInfo.steps) {
stepId = context.agentJobHistoryInfo.steps.length + 1;
}
return new TPromise<boolean>((resolve, reject) => {
resolve(this._commandService.executeCommand('agent.openNewStepDialog', ownerUri, jobName, server, stepId));
});
}
}

View File

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

View File

@@ -5,46 +5,51 @@
import 'vs/css!./jobHistory';
import 'vs/css!sql/media/icons/common-icons';
import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
import * as sqlops from 'sqlops';
import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
import { RunJobAction, StopJobAction, NewStepAction } from 'sql/parts/jobManagement/views/jobActions';
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
import { AgentJobUtilities } from '../common/agentJobUtilities';
import { IJobManagementService } from '../common/interfaces';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
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';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { RunJobAction, StopJobAction, NewStepAction } from 'sql/parts/jobManagement/common/jobActions';
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
import { JobManagementUtilities } from 'sql/parts/jobManagement/common/jobManagementUtilities';
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
import { JobHistoryController, JobHistoryDataSource,
JobHistoryRenderer, JobHistoryFilter, JobHistoryModel, JobHistoryRow } from 'sql/parts/jobManagement/views/jobHistoryTree';
import { JobStepsViewRow } from './jobStepsViewTree';
import { JobStepsViewRow } from 'sql/parts/jobManagement/views/jobStepsViewTree';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
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;
@@ -66,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;
@@ -92,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 };
@@ -129,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) {
@@ -206,11 +174,13 @@ export class JobHistoryComponent extends Disposable implements OnInit {
if (self.agentJobHistoryInfo) {
self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate);
if (self.agentJobHistoryInfo.steps) {
let jobStepStatus = this.didJobFail(self.agentJobHistoryInfo);
self._stepRows = self.agentJobHistoryInfo.steps.map(step => {
let stepViewRow = new JobStepsViewRow();
stepViewRow.message = step.message;
stepViewRow.runStatus = AgentJobUtilities.convertToStatusString(step.runStatus);
self._runStatus = AgentJobUtilities.convertToStatusString(self.agentJobHistoryInfo.runStatus);
stepViewRow.runStatus = jobStepStatus ? JobManagementUtilities.convertToStatusString(0) :
JobManagementUtilities.convertToStatusString(step.runStatus);
self._runStatus = JobManagementUtilities.convertToStatusString(self.agentJobHistoryInfo.runStatus);
stepViewRow.stepName = step.stepName;
stepViewRow.stepID = step.stepId.toString();
return stepViewRow;
@@ -223,6 +193,15 @@ export class JobHistoryComponent extends Disposable implements OnInit {
}
}
private didJobFail(job: sqlops.AgentJobHistoryInfo): boolean {
for (let i = 0; i < job.steps.length; i++) {
if (job.steps[i].runStatus === 0) {
return true;
}
}
return false;
}
private buildHistoryTree(self: any, jobHistories: sqlops.AgentJobHistoryInfo[]) {
self._treeController.jobHistories = jobHistories;
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, jobHistories);
@@ -253,7 +232,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
private convertToJobHistoryRow(historyInfo: sqlops.AgentJobHistoryInfo): JobHistoryRow {
let jobHistoryRow = new JobHistoryRow();
jobHistoryRow.runDate = this.formatTime(historyInfo.runDate);
jobHistoryRow.runStatus = AgentJobUtilities.convertToStatusString(historyInfo.runStatus);
jobHistoryRow.runStatus = JobManagementUtilities.convertToStatusString(historyInfo.runStatus);
jobHistoryRow.instanceID = historyInfo.instanceId;
return jobHistoryRow;
}
@@ -269,15 +248,74 @@ export class JobHistoryComponent extends Disposable implements OnInit {
private setActions(): void {
let startIcon: HTMLElement = $('.action-label.icon.runJobIcon').get(0);
let stopIcon: HTMLElement = $('.action-label.icon.stopJobIcon').get(0);
AgentJobUtilities.getActionIconClassName(startIcon, stopIcon, this.agentJobInfo.currentExecutionStatus);
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([
@@ -298,7 +336,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 {
@@ -243,7 +245,6 @@ jobhistory-component > .jobhistory-heading-container > .icon.in-progress {
jobhistory-component > .actionbar-container .monaco-action-bar > ul.actions-container {
border-top: 3px solid #f4f4f4;
padding-top: 10px;
}
.vs-dark jobhistory-component > .actionbar-container .monaco-action-bar > ul.actions-container {

View File

@@ -104,7 +104,6 @@ export interface IListTemplate {
}
export class JobHistoryRenderer implements tree.IRenderer {
private _statusIcon: HTMLElement;
public getHeight(tree: tree.ITree, element: JobHistoryRow): number {
return 30;
@@ -121,11 +120,10 @@ export class JobHistoryRenderer implements tree.IRenderer {
public renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement): IListTemplate {
let row = DOM.$('.list-row');
let label = DOM.$('.label');
this._statusIcon = this.createStatusIcon();
row.appendChild(this._statusIcon);
let statusIcon = this.createStatusIcon();
row.appendChild(statusIcon);
row.appendChild(label);
container.appendChild(row);
let statusIcon = this._statusIcon;
return { statusIcon, label };
}
@@ -133,13 +131,13 @@ export class JobHistoryRenderer implements tree.IRenderer {
templateData.label.innerHTML = element.runDate + '&nbsp;&nbsp;' + element.runStatus;
let statusClass: string;
if (element.runStatus === 'Succeeded') {
statusClass = ' job-passed';
statusClass = 'status-icon job-passed';
} else if (element.runStatus === 'Failed') {
statusClass = ' job-failed';
statusClass = 'status-icon job-failed';
} else {
statusClass = ' job-unknown';
statusClass = 'status-icon job-unknown';
}
this._statusIcon.className += statusClass;
templateData.statusIcon.className = statusClass;
}
public disposeTemplate(tree: tree.ITree, templateId: string, templateData: IListTemplate): void {
@@ -148,7 +146,6 @@ export class JobHistoryRenderer implements tree.IRenderer {
private createStatusIcon(): HTMLElement {
let statusIcon: HTMLElement = DOM.$('div');
statusIcon.className += ' status-icon';
return statusIcon;
}
}

View File

@@ -0,0 +1,119 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ElementRef, AfterContentChecked, ViewChild } from '@angular/core';
import { Table } from 'sql/base/browser/ui/table/table';
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { IAction, Action } from 'vs/base/common/actions';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
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 TabChild implements AfterContentChecked {
protected isVisible: boolean = false;
protected isInitialized: boolean = false;
protected isRefreshing: boolean = false;
protected _showProgressWheel: boolean;
protected _visibilityElement: ElementRef;
protected _parentComponent: AgentViewComponent;
protected _table: Table<any>;
protected _actionBar: Taskbar;
public contextAction: any;
@ViewChild('actionbarContainer') protected actionBarContainer: ElementRef;
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() {
if (this._visibilityElement && this._parentComponent) {
if (this.isVisible === false && this._visibilityElement.nativeElement.offsetParent !== null) {
this.isVisible = true;
if (!this.isInitialized) {
this._showProgressWheel = true;
this.onFirstVisible();
this.isInitialized = true;
}
} else if (this.isVisible === true && this._parentComponent.refresh === true) {
this._showProgressWheel = true;
this.isRefreshing = true;
this.onFirstVisible();
this.layout();
this._parentComponent.refresh = false;
} else if (this.isVisible === true && this._visibilityElement.nativeElement.offsetParent === null) {
this.isVisible = false;
}
}
}
abstract onFirstVisible();
protected openContextMenu(event): void {
let grid = this._table.grid;
let rowIndex = grid.getCellFromEvent(event).row;
let targetObject = this.getCurrentTableObject(rowIndex);
let actions = this.getTableActions();
if (actions) {
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
let actionContext= {
ownerUri: ownerUri,
targetObject: targetObject
};
let anchor = { x: event.pageX + 1, y: event.pageY };
this._contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => actions,
getKeyBinding: (action) => this._keybindingFor(action),
getActionsContext: () => (actionContext)
});
}
}
protected _keybindingFor(action: IAction): ResolvedKeybinding {
var [kb] = this._keybindingService.lookupKeybindings(action.id);
return kb;
}
protected getTableActions(): TPromise<IAction[]> {
return undefined;
}
protected getCurrentTableObject(rowIndex: number): any {
return undefined;
}
protected initActionBar() {
let refreshAction = this._instantiationService.createInstance(JobsRefreshAction);
let newAction: Action = this._instantiationService.createInstance(this.contextAction);
let taskbar = <HTMLElement>this.actionBarContainer.nativeElement;
this._actionBar = new Taskbar(taskbar, this._contextMenuService);
this._actionBar.context = this;
this._actionBar.setContent([
{ action: refreshAction },
{ action: newAction }
]);
}
}

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,11 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo
}, {verticalScrollMode: ScrollbarVisibility.Visible});
this._register(attachListStyler(this._tree, this.themeService));
}
public onFirstVisible() {
}
public layout() {
}
}

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