Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6680be6a73 | ||
|
|
e026ab85a7 | ||
|
|
e53c903205 | ||
|
|
03dbe8565f | ||
|
|
708793cb23 | ||
|
|
43ae4fb0aa | ||
|
|
6b1d552277 | ||
|
|
f24f576b72 | ||
|
|
c23328564f | ||
|
|
cd6dd3dafa | ||
|
|
4081e15bef | ||
|
|
b05e3813d1 | ||
|
|
3048311f40 | ||
|
|
1045392d91 | ||
|
|
7b23ca8ee7 | ||
|
|
4d67eca8bb | ||
|
|
74c4b7311e | ||
|
|
713c74adfd | ||
|
|
408a8a6f19 | ||
|
|
e1485e49d3 | ||
|
|
30b66934cd | ||
|
|
fd49c081c2 | ||
|
|
1327120024 | ||
|
|
d2b5043972 | ||
|
|
a0e55ea3fd | ||
|
|
1f32de29c1 | ||
|
|
12be06d682 | ||
|
|
27ca9b13f8 | ||
|
|
be45905830 | ||
|
|
05d0a89655 | ||
|
|
3ba575dcd0 | ||
|
|
3e200b7f0f | ||
|
|
cbce1f7008 | ||
|
|
e99101447e | ||
|
|
0ddb326e44 | ||
|
|
460446a15c | ||
|
|
4eea24997f | ||
|
|
0b1e9c7c66 | ||
|
|
d51a7a9eb7 | ||
|
|
0f0b959e14 | ||
|
|
b2ceb09e4d | ||
|
|
53953f5cda | ||
|
|
fbd5e819a2 | ||
|
|
bdc391d376 | ||
|
|
6b618fb121 | ||
|
|
1f3e59c9f9 | ||
|
|
1956078c8c | ||
|
|
11230f59fc | ||
|
|
21bad7a01f | ||
|
|
6f9a27ecc7 | ||
|
|
c504113d13 | ||
|
|
c92ff60592 | ||
|
|
e9013d1a2a | ||
|
|
9c4580fe40 | ||
|
|
cb060cb5db | ||
|
|
6c3d85cc45 | ||
|
|
14ae89e87c | ||
|
|
24c48f025d | ||
|
|
f0a556f004 | ||
|
|
fd4d6abb4d | ||
|
|
41cc839380 | ||
|
|
c2a4380b96 | ||
|
|
6f402ac79f | ||
|
|
bf7c1306b1 | ||
|
|
c1509cf09d | ||
|
|
014bca031c | ||
|
|
4f864fd5bd | ||
|
|
2da67567e4 | ||
|
|
5b19d2b1fc | ||
|
|
a6837dcd40 | ||
|
|
af80751a1f | ||
|
|
dd02597c3b | ||
|
|
2926a3cbd8 | ||
|
|
b02bb3bfd4 | ||
|
|
67a4683bb1 |
@@ -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
|
* 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!**
|
* 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
|
* Extension Manager has improved Marketplace experience to easily discover community extensions
|
||||||
* SQL Agent extension Jobs and Job History view improvement
|
* SQL Agent extension Jobs and Job History view improvement
|
||||||
* Updates for **whoisactive** and **Server Reports** extensions
|
* Updates for **whoisactive** and **Server Reports** extensions
|
||||||
|
|||||||
@@ -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() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
3234
extensions/agent/package-lock.json
generated
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "agent",
|
"name": "agent",
|
||||||
"displayName": "SQL Server Agent",
|
"displayName": "SQL Server Agent",
|
||||||
"description": "Manage and troubleshoot SQL Server Agent jobs (early preview)",
|
"description": "Manage and troubleshoot SQL Server Agent jobs",
|
||||||
"version": "0.29.0",
|
"version": "0.31.4",
|
||||||
"publisher": "Microsoft",
|
"publisher": "Microsoft",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
"*"
|
"*"
|
||||||
],
|
],
|
||||||
"main": "./client/out/main",
|
"main": "./out/main",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Microsoft/sqlopsstudio.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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
132
extensions/agent/src/data/alertData.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,26 +4,32 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
import { AgentUtils } from '../agentUtils';
|
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';
|
export class JobData implements IAgentDialogData {
|
||||||
private readonly JobCompletionActionCondition_OnFailure: string = 'When the job fails';
|
|
||||||
private readonly JobCompletionActionCondition_OnSuccess: string = 'When the job succeeds';
|
private readonly JobCompletionActionCondition_Always: string = localize('jobData.whenJobCompletes', 'When the job completes');
|
||||||
|
private readonly JobCompletionActionCondition_OnFailure: string = localize('jobData.whenJobFails', 'When the job fails');
|
||||||
|
private readonly JobCompletionActionCondition_OnSuccess: string = localize('jobData.whenJobSucceeds', 'When the job succeeds');
|
||||||
|
|
||||||
// Error Messages
|
// 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 _ownerUri: string;
|
||||||
private _jobCategories: string[];
|
private _jobCategories: string[];
|
||||||
private _operators: string[];
|
private _operators: string[];
|
||||||
private _agentService: sqlops.AgentServicesProvider;
|
|
||||||
private _defaultOwner: string;
|
private _defaultOwner: string;
|
||||||
private _jobCompletionActionConditions: sqlops.CategoryValue[];
|
private _jobCompletionActionConditions: sqlops.CategoryValue[];
|
||||||
|
|
||||||
|
public dialogMode: AgentDialogMode = AgentDialogMode.CREATE;
|
||||||
public name: string;
|
public name: string;
|
||||||
|
public originalName: string;
|
||||||
public enabled: boolean = true;
|
public enabled: boolean = true;
|
||||||
public description: string;
|
public description: string;
|
||||||
public category: string;
|
public category: string;
|
||||||
@@ -39,8 +45,21 @@ export class CreateJobData {
|
|||||||
public jobSchedules: sqlops.AgentJobScheduleInfo[];
|
public jobSchedules: sqlops.AgentJobScheduleInfo[];
|
||||||
public alerts: sqlops.AgentAlertInfo[];
|
public alerts: sqlops.AgentAlertInfo[];
|
||||||
|
|
||||||
constructor(ownerUri: string) {
|
constructor(
|
||||||
|
ownerUri: string,
|
||||||
|
jobInfo: sqlops.AgentJobInfo = undefined,
|
||||||
|
private _agentService: sqlops.AgentServicesProvider = undefined) {
|
||||||
|
|
||||||
this._ownerUri = ownerUri;
|
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[] {
|
public get jobCategories(): string[] {
|
||||||
@@ -91,7 +110,39 @@ export class CreateJobData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async save() {
|
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,
|
name: this.name,
|
||||||
owner: this.owner,
|
owner: this.owner,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
@@ -121,30 +172,6 @@ export class CreateJobData {
|
|||||||
lastRun: '',
|
lastRun: '',
|
||||||
nextRun: '',
|
nextRun: '',
|
||||||
jobId: ''
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { AgentUtils } from '../agentUtils';
|
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 ownerUri: string;
|
||||||
public jobId: string; //
|
public jobId: string; //
|
||||||
public jobName: string;
|
public jobName: string;
|
||||||
@@ -37,6 +39,9 @@ export class CreateStepData {
|
|||||||
this.ownerUri = ownerUri;
|
this.ownerUri = ownerUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async initialize() {
|
||||||
|
}
|
||||||
|
|
||||||
public async save() {
|
public async save() {
|
||||||
let agentService = await AgentUtils.getAgentService();
|
let agentService = await AgentUtils.getAgentService();
|
||||||
agentService.createJobStep(this.ownerUri, {
|
agentService.createJobStep(this.ownerUri, {
|
||||||
74
extensions/agent/src/data/operatorData.ts
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { AgentUtils } from '../agentUtils';
|
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 ownerUri: string;
|
||||||
public schedules: sqlops.AgentJobScheduleInfo[];
|
public schedules: sqlops.AgentJobScheduleInfo[];
|
||||||
public selectedSchedule: sqlops.AgentJobScheduleInfo;
|
public selectedSchedule: sqlops.AgentJobScheduleInfo;
|
||||||
54
extensions/agent/src/data/proxyData.ts
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { AgentUtils } from '../agentUtils';
|
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 ownerUri: string;
|
||||||
public schedules: sqlops.AgentJobScheduleInfo[];
|
public schedules: sqlops.AgentJobScheduleInfo[];
|
||||||
public selectedSchedule: sqlops.AgentJobScheduleInfo;
|
public selectedSchedule: sqlops.AgentJobScheduleInfo;
|
||||||
78
extensions/agent/src/dialogs/agentDialog.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
528
extensions/agent/src/dialogs/alertDialog.ts
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,61 +3,67 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { CreateJobData } from '../data/createJobData';
|
import { JobData } from '../data/jobData';
|
||||||
import { CreateStepDialog } from './createStepDialog';
|
import { JobStepDialog } from './jobStepDialog';
|
||||||
import { PickScheduleDialog } from './pickScheduleDialog';
|
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
|
// TODO: localize
|
||||||
// Top level
|
// Top level
|
||||||
private readonly DialogTitle: string = 'New Job';
|
private static readonly CreateDialogTitle: string = localize('jobDialog.newJob', 'New Job');
|
||||||
private readonly OkButtonText: string = 'OK';
|
private static readonly EditDialogTitle: string = localize('jobDialog.editJob', 'Edit Job');
|
||||||
private readonly CancelButtonText: string = 'Cancel';
|
private readonly GeneralTabText: string = localize('jobDialog.general', 'General');
|
||||||
private readonly GeneralTabText: string = 'General';
|
private readonly StepsTabText: string = localize('jobDialog.steps', 'Steps');
|
||||||
private readonly StepsTabText: string = 'Steps';
|
private readonly SchedulesTabText: string = localize('jobDialog.schedules', 'Schedules');
|
||||||
private readonly SchedulesTabText: string = 'Schedules';
|
private readonly AlertsTabText: string = localize('jobDialog.alerts', 'Alerts');
|
||||||
private readonly AlertsTabText: string = 'Alerts';
|
private readonly NotificationsTabText: string = localize('jobDialog.notifications', 'Notifications');
|
||||||
private readonly NotificationsTabText: string = 'Notifications';
|
private readonly BlankJobNameErrorText: string = localize('jobDialog.blankJobNameError', 'The name of the job cannot be blank.');
|
||||||
|
|
||||||
// General tab strings
|
// General tab strings
|
||||||
private readonly NameTextBoxLabel: string = 'Name';
|
private readonly NameTextBoxLabel: string = localize('jobDialog.name', 'Name');
|
||||||
private readonly OwnerTextBoxLabel: string = 'Owner';
|
private readonly OwnerTextBoxLabel: string = localize('jobDialog.owner', 'Owner');
|
||||||
private readonly CategoryDropdownLabel: string = 'Category';
|
private readonly CategoryDropdownLabel: string = localize('jobDialog.category', 'Category');
|
||||||
private readonly DescriptionTextBoxLabel: string = 'Description';
|
private readonly DescriptionTextBoxLabel: string = localize('jobDialog.description', 'Description');
|
||||||
private readonly EnabledCheckboxLabel: string = 'Enabled';
|
private readonly EnabledCheckboxLabel: string = localize('jobDialog.enabled', 'Enabled');
|
||||||
|
|
||||||
// Steps tab strings
|
// Steps tab strings
|
||||||
private readonly JobStepsTopLabelString: string = 'Job step list';
|
private readonly JobStepsTopLabelString: string = localize('jobDialog.jobStepList', 'Job step list');
|
||||||
private readonly StepsTable_StepColumnString: string = 'Step';
|
private readonly StepsTable_StepColumnString: string = localize('jobDialog.step', 'Step');
|
||||||
private readonly StepsTable_NameColumnString: string = 'Name';
|
private readonly StepsTable_NameColumnString: string = localize('jobDialog.name', 'Name');
|
||||||
private readonly StepsTable_TypeColumnString: string = 'Type';
|
private readonly StepsTable_TypeColumnString: string = localize('jobDialog.type', 'Type');
|
||||||
private readonly StepsTable_SuccessColumnString: string = 'On Success';
|
private readonly StepsTable_SuccessColumnString: string = localize('jobDialog.onSuccess', 'On Success');
|
||||||
private readonly StepsTable_FailureColumnString: string = 'On Failure';
|
private readonly StepsTable_FailureColumnString: string = localize('jobDialog.onFailure', 'On Failure');
|
||||||
private readonly NewStepButtonString: string = 'New...';
|
private readonly NewStepButtonString: string = localize('jobDialog.new', 'New...');
|
||||||
private readonly InsertStepButtonString: string = 'Insert...';
|
private readonly EditStepButtonString: string = localize('jobDialog.edit', 'Edit');
|
||||||
private readonly EditStepButtonString: string = 'Edit';
|
private readonly DeleteStepButtonString: string = localize('jobDialog.delete', 'Delete');
|
||||||
private readonly DeleteStepButtonString: string = 'Delete';
|
private readonly MoveStepUpButtonString: string = localize('jobDialog.moveUp', 'Move Step Up');
|
||||||
|
private readonly MoveStepDownButtonString: string = localize('jobDialog.moveDown', 'Move Step Up');
|
||||||
|
|
||||||
// Notifications tab strings
|
// Notifications tab strings
|
||||||
private readonly NotificationsTabTopLabelString: string = 'Actions to perform when the job completes';
|
private readonly NotificationsTabTopLabelString: string = localize('jobDialog.notificationsTabTop', 'Actions to perform when the job completes');
|
||||||
private readonly EmailCheckBoxString: string = 'Email';
|
private readonly EmailCheckBoxString: string = localize('jobDialog.email', 'Email');
|
||||||
private readonly PagerCheckBoxString: string = 'Page';
|
private readonly PagerCheckBoxString: string = localize('jobDialog.page', 'Page');
|
||||||
private readonly EventLogCheckBoxString: string = 'Write to the Windows Application event log';
|
private readonly EventLogCheckBoxString: string = localize('jobDialog.eventLogCheckBoxLabel', 'Write to the Windows Application event log');
|
||||||
private readonly DeleteJobCheckBoxString: string = 'Automatically delete job';
|
private readonly DeleteJobCheckBoxString: string = localize('jobDialog.deleteJobLabel', 'Automatically delete job');
|
||||||
|
|
||||||
// Schedules tab strings
|
// Schedules tab strings
|
||||||
private readonly SchedulesTopLabelString: string = 'Schedules list';
|
private readonly SchedulesTopLabelString: string = localize('jobDialog.schedulesaLabel', 'Schedules list');
|
||||||
private readonly PickScheduleButtonString: string = 'Pick Schedule';
|
private readonly PickScheduleButtonString: string = localize('jobDialog.pickSchedule', 'Pick Schedule');
|
||||||
|
private readonly ScheduleNameLabelString: string = localize('jobDialog.scheduleNameLabel', 'Schedule Name');
|
||||||
|
|
||||||
// Alerts tab strings
|
// Alerts tab strings
|
||||||
private readonly AlertsTopLabelString: string = 'Alerts list';
|
private readonly AlertsTopLabelString: string = localize('jobDialog.alertsList', 'Alerts list');
|
||||||
private readonly NewAlertButtonString: string = 'New Alert';
|
private readonly NewAlertButtonString: string = localize('jobDialog.newAlert', 'New Alert');
|
||||||
|
private readonly AlertNameLabelString: string = localize('jobDialog.alertNameLabel', 'Alert Name');
|
||||||
|
|
||||||
// UI Components
|
// UI Components
|
||||||
private dialog: sqlops.window.modelviewdialog.Dialog;
|
|
||||||
private generalTab: sqlops.window.modelviewdialog.DialogTab;
|
private generalTab: sqlops.window.modelviewdialog.DialogTab;
|
||||||
private stepsTab: sqlops.window.modelviewdialog.DialogTab;
|
private stepsTab: sqlops.window.modelviewdialog.DialogTab;
|
||||||
private alertsTab: sqlops.window.modelviewdialog.DialogTab;
|
private alertsTab: sqlops.window.modelviewdialog.DialogTab;
|
||||||
@@ -74,7 +80,8 @@ export class CreateJobDialog {
|
|||||||
// Steps tab controls
|
// Steps tab controls
|
||||||
private stepsTable: sqlops.TableComponent;
|
private stepsTable: sqlops.TableComponent;
|
||||||
private newStepButton: sqlops.ButtonComponent;
|
private newStepButton: sqlops.ButtonComponent;
|
||||||
private insertStepButton: sqlops.ButtonComponent;
|
private moveStepUpButton: sqlops.ButtonComponent;
|
||||||
|
private moveStepDownButton: sqlops.ButtonComponent;
|
||||||
private editStepButton: sqlops.ButtonComponent;
|
private editStepButton: sqlops.ButtonComponent;
|
||||||
private deleteStepButton: sqlops.ButtonComponent;
|
private deleteStepButton: sqlops.ButtonComponent;
|
||||||
|
|
||||||
@@ -99,15 +106,14 @@ export class CreateJobDialog {
|
|||||||
private alertsTable: sqlops.TableComponent;
|
private alertsTable: sqlops.TableComponent;
|
||||||
private newAlertButton: sqlops.ButtonComponent;
|
private newAlertButton: sqlops.ButtonComponent;
|
||||||
|
|
||||||
private model: CreateJobData;
|
constructor(ownerUri: string, jobInfo: sqlops.AgentJobInfo = undefined) {
|
||||||
|
super(
|
||||||
constructor(ownerUri: string) {
|
ownerUri,
|
||||||
this.model = new CreateJobData(ownerUri);
|
new JobData(ownerUri, jobInfo),
|
||||||
|
jobInfo ? JobDialog.EditDialogTitle : JobDialog.CreateDialogTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async showDialog() {
|
protected async initializeDialog() {
|
||||||
await this.model.initialize();
|
|
||||||
this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle);
|
|
||||||
this.generalTab = sqlops.window.modelviewdialog.createTab(this.GeneralTabText);
|
this.generalTab = sqlops.window.modelviewdialog.createTab(this.GeneralTabText);
|
||||||
this.stepsTab = sqlops.window.modelviewdialog.createTab(this.StepsTabText);
|
this.stepsTab = sqlops.window.modelviewdialog.createTab(this.StepsTabText);
|
||||||
this.alertsTab = sqlops.window.modelviewdialog.createTab(this.AlertsTabText);
|
this.alertsTab = sqlops.window.modelviewdialog.createTab(this.AlertsTabText);
|
||||||
@@ -119,10 +125,6 @@ export class CreateJobDialog {
|
|||||||
this.initializeSchedulesTab();
|
this.initializeSchedulesTab();
|
||||||
this.initializeNotificationsTab();
|
this.initializeNotificationsTab();
|
||||||
this.dialog.content = [this.generalTab, this.stepsTab, this.schedulesTab, this.alertsTab, this.notificationsTab];
|
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.dialog.registerCloseValidator(() => {
|
||||||
this.updateModel();
|
this.updateModel();
|
||||||
@@ -134,13 +136,17 @@ export class CreateJobDialog {
|
|||||||
|
|
||||||
return validationResult.valid;
|
return validationResult.valid;
|
||||||
});
|
});
|
||||||
|
|
||||||
sqlops.window.modelviewdialog.openDialog(this.dialog);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeGeneralTab() {
|
private initializeGeneralTab() {
|
||||||
this.generalTab.registerContent(async view => {
|
this.generalTab.registerContent(async view => {
|
||||||
this.nameTextBox = view.modelBuilder.inputBox().component();
|
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.ownerTextBox = view.modelBuilder.inputBox().component();
|
||||||
this.categoryDropdown = view.modelBuilder.dropDown().component();
|
this.categoryDropdown = view.modelBuilder.dropDown().component();
|
||||||
this.descriptionTextBox = view.modelBuilder.inputBox().withProperties({
|
this.descriptionTextBox = view.modelBuilder.inputBox().withProperties({
|
||||||
@@ -171,11 +177,18 @@ export class CreateJobDialog {
|
|||||||
|
|
||||||
await view.initializeModel(formModel);
|
await view.initializeModel(formModel);
|
||||||
|
|
||||||
|
this.nameTextBox.value = this.model.name;
|
||||||
this.ownerTextBox.value = this.model.defaultOwner;
|
this.ownerTextBox.value = this.model.defaultOwner;
|
||||||
this.categoryDropdown.values = this.model.jobCategories;
|
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.enabledCheckBox.checked = this.model.enabled;
|
||||||
this.descriptionTextBox.value = '';
|
this.descriptionTextBox.value = this.model.description;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,24 +204,38 @@ export class CreateJobDialog {
|
|||||||
this.StepsTable_FailureColumnString
|
this.StepsTable_FailureColumnString
|
||||||
],
|
],
|
||||||
data: [],
|
data: [],
|
||||||
height: 800
|
height: 430
|
||||||
}).component();
|
}).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({
|
this.newStepButton = view.modelBuilder.button().withProperties({
|
||||||
label: this.NewStepButtonString,
|
label: this.NewStepButtonString,
|
||||||
width: 80
|
width: 80
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.newStepButton.onDidClick((e)=>{
|
this.newStepButton.onDidClick((e)=>{
|
||||||
let stepDialog = new CreateStepDialog(this.model.ownerUri, '', '', 1, this.model);
|
if (this.nameTextBox.value && this.nameTextBox.value.length > 0) {
|
||||||
stepDialog.openNewStepDialog();
|
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({
|
this.editStepButton = view.modelBuilder.button().withProperties({
|
||||||
label: this.EditStepButtonString,
|
label: this.EditStepButtonString,
|
||||||
width: 80
|
width: 80
|
||||||
@@ -223,7 +250,7 @@ export class CreateJobDialog {
|
|||||||
.withFormItems([{
|
.withFormItems([{
|
||||||
component: this.stepsTable,
|
component: this.stepsTable,
|
||||||
title: this.JobStepsTopLabelString,
|
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();
|
}]).withLayout({ width: '100%' }).component();
|
||||||
await view.initializeModel(formModel);
|
await view.initializeModel(formModel);
|
||||||
});
|
});
|
||||||
@@ -234,10 +261,10 @@ export class CreateJobDialog {
|
|||||||
this.alertsTable = view.modelBuilder.table()
|
this.alertsTable = view.modelBuilder.table()
|
||||||
.withProperties({
|
.withProperties({
|
||||||
columns: [
|
columns: [
|
||||||
'Alert Name'
|
this.AlertNameLabelString
|
||||||
],
|
],
|
||||||
data: [],
|
data: [],
|
||||||
height: 600,
|
height: 430,
|
||||||
width: 400
|
width: 400
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
@@ -247,10 +274,10 @@ export class CreateJobDialog {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.newAlertButton.onDidClick((e)=>{
|
this.newAlertButton.onDidClick((e)=>{
|
||||||
let alertDialog = new CreateAlertDialog(this.model.ownerUri);
|
let alertDialog = new AlertDialog(this.model.ownerUri, null, []);
|
||||||
alertDialog.onSuccess((dialogModel) => {
|
alertDialog.onSuccess((dialogModel) => {
|
||||||
});
|
});
|
||||||
alertDialog.showDialog();
|
alertDialog.openDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
let formModel = view.modelBuilder.formContainer()
|
||||||
@@ -269,11 +296,11 @@ export class CreateJobDialog {
|
|||||||
this.schedulesTable = view.modelBuilder.table()
|
this.schedulesTable = view.modelBuilder.table()
|
||||||
.withProperties({
|
.withProperties({
|
||||||
columns: [
|
columns: [
|
||||||
'Schedule Name'
|
this.ScheduleNameLabelString
|
||||||
],
|
],
|
||||||
data: [],
|
data: [],
|
||||||
height: 600,
|
height: 430,
|
||||||
width: 400
|
width: 420
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this.pickScheduleButton = view.modelBuilder.button().withProperties({
|
this.pickScheduleButton = view.modelBuilder.button().withProperties({
|
||||||
@@ -373,21 +400,23 @@ export class CreateJobDialog {
|
|||||||
|
|
||||||
let formModel = view.modelBuilder.formContainer().withFormItems([
|
let formModel = view.modelBuilder.formContainer().withFormItems([
|
||||||
{
|
{
|
||||||
component: this.notificationsTabTopLabel,
|
components:
|
||||||
title: ''
|
[{
|
||||||
}, {
|
component: emailContainer,
|
||||||
component: emailContainer,
|
title: ''
|
||||||
title: ''
|
},
|
||||||
}, {
|
{
|
||||||
component: pagerContainer,
|
component: pagerContainer,
|
||||||
title: ''
|
title: ''
|
||||||
}, {
|
},
|
||||||
component: eventLogContainer,
|
{
|
||||||
title: ''
|
component: eventLogContainer,
|
||||||
}, {
|
title: ''
|
||||||
component: deleteJobContainer,
|
},
|
||||||
title: ''
|
{
|
||||||
}]).withLayout({ width: '100%' }).component();
|
component: deleteJobContainer,
|
||||||
|
title: ''
|
||||||
|
}], title: this.NotificationsTabTopLabelString}]).withLayout({ width: '100%' }).component();
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
await view.initializeModel(formModel);
|
||||||
this.emailConditionDropdown.values = this.model.JobCompletionActionConditions;
|
this.emailConditionDropdown.values = this.model.JobCompletionActionConditions;
|
||||||
@@ -421,34 +450,7 @@ export class CreateJobDialog {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async execute() {
|
protected updateModel() {
|
||||||
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() {
|
|
||||||
this.model.name = this.nameTextBox.value;
|
this.model.name = this.nameTextBox.value;
|
||||||
this.model.owner = this.ownerTextBox.value;
|
this.model.owner = this.ownerTextBox.value;
|
||||||
this.model.enabled = this.enabledCheckBox.checked;
|
this.model.enabled = this.enabledCheckBox.checked;
|
||||||
507
extensions/agent/src/dialogs/jobStepDialog.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
404
extensions/agent/src/dialogs/operatorDialog.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,18 +4,22 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { PickScheduleData } from '../data/pickScheduleData';
|
import { PickScheduleData } from '../data/pickScheduleData';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export class PickScheduleDialog {
|
export class PickScheduleDialog {
|
||||||
|
|
||||||
// TODO: localize
|
// TODO: localize
|
||||||
// Top level
|
// Top level
|
||||||
private readonly DialogTitle: string = 'Job Schedules';
|
private readonly DialogTitle: string = localize('pickSchedule.jobSchedules', 'Job Schedules');
|
||||||
private readonly OkButtonText: string = 'OK';
|
private readonly OkButtonText: string = localize('pickSchedule.ok', 'OK');
|
||||||
private readonly CancelButtonText: string = 'Cancel';
|
private readonly CancelButtonText: string = localize('pickSchedule.cancel', 'Cancel');
|
||||||
private readonly SchedulesTabText: string = 'Schedules';
|
private readonly ScheduleNameLabelText: string = localize('pickSchedule.scheduleName', 'Schedule Name');
|
||||||
|
private readonly SchedulesLabelText: string = localize('pickSchedule.schedules', 'Schedules');
|
||||||
|
|
||||||
// UI Components
|
// UI Components
|
||||||
private dialog: sqlops.window.modelviewdialog.Dialog;
|
private dialog: sqlops.window.modelviewdialog.Dialog;
|
||||||
@@ -38,7 +42,6 @@ export class PickScheduleDialog {
|
|||||||
this.dialog.cancelButton.onClick(async () => await this.cancel());
|
this.dialog.cancelButton.onClick(async () => await this.cancel());
|
||||||
this.dialog.okButton.label = this.OkButtonText;
|
this.dialog.okButton.label = this.OkButtonText;
|
||||||
this.dialog.cancelButton.label = this.CancelButtonText;
|
this.dialog.cancelButton.label = this.CancelButtonText;
|
||||||
|
|
||||||
sqlops.window.modelviewdialog.openDialog(this.dialog);
|
sqlops.window.modelviewdialog.openDialog(this.dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,17 +50,17 @@ export class PickScheduleDialog {
|
|||||||
this.schedulesTable = view.modelBuilder.table()
|
this.schedulesTable = view.modelBuilder.table()
|
||||||
.withProperties({
|
.withProperties({
|
||||||
columns: [
|
columns: [
|
||||||
'Schedule Name'
|
this.ScheduleNameLabelText
|
||||||
],
|
],
|
||||||
data: [],
|
data: [],
|
||||||
height: 600,
|
height: '80em',
|
||||||
width: 400
|
width: '40em'
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
let formModel = view.modelBuilder.formContainer()
|
let formModel = view.modelBuilder.formContainer()
|
||||||
.withFormItems([{
|
.withFormItems([{
|
||||||
component: this.schedulesTable,
|
component: this.schedulesTable,
|
||||||
title: 'Schedules'
|
title: this.SchedulesLabelText
|
||||||
}]).withLayout({ width: '100%' }).component();
|
}]).withLayout({ width: '100%' }).component();
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
await view.initializeModel(formModel);
|
||||||
219
extensions/agent/src/dialogs/proxyDialog.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,29 +4,33 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import * as vscode from 'vscode';
|
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
|
// Top level
|
||||||
private readonly DialogTitle: string = 'New Schedule';
|
private readonly DialogTitle: string = localize('scheduleDialog.newSchedule', 'New Schedule');
|
||||||
private readonly OkButtonText: string = 'OK';
|
private readonly OkButtonText: string = localize('scheduleDialog.ok', 'OK');
|
||||||
private readonly CancelButtonText: string = 'Cancel';
|
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
|
// UI Components
|
||||||
private dialog: sqlops.window.modelviewdialog.Dialog;
|
private dialog: sqlops.window.modelviewdialog.Dialog;
|
||||||
private schedulesTable: sqlops.TableComponent;
|
private schedulesTable: sqlops.TableComponent;
|
||||||
|
|
||||||
private model: CreateScheduleData;
|
private model: ScheduleData;
|
||||||
|
|
||||||
private _onSuccess: vscode.EventEmitter<CreateScheduleData> = new vscode.EventEmitter<CreateScheduleData>();
|
private _onSuccess: vscode.EventEmitter<ScheduleData> = new vscode.EventEmitter<ScheduleData>();
|
||||||
public readonly onSuccess: vscode.Event<CreateScheduleData> = this._onSuccess.event;
|
public readonly onSuccess: vscode.Event<ScheduleData> = this._onSuccess.event;
|
||||||
|
|
||||||
constructor(ownerUri: string) {
|
constructor(ownerUri: string) {
|
||||||
this.model = new CreateScheduleData(ownerUri);
|
this.model = new ScheduleData(ownerUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async showDialog() {
|
public async showDialog() {
|
||||||
@@ -46,7 +50,7 @@ export class CreateScheduleDialog {
|
|||||||
this.schedulesTable = view.modelBuilder.table()
|
this.schedulesTable = view.modelBuilder.table()
|
||||||
.withProperties({
|
.withProperties({
|
||||||
columns: [
|
columns: [
|
||||||
'Schedule Name'
|
this.ScheduleNameText
|
||||||
],
|
],
|
||||||
data: [],
|
data: [],
|
||||||
height: 600,
|
height: 600,
|
||||||
@@ -56,7 +60,7 @@ export class CreateScheduleDialog {
|
|||||||
let formModel = view.modelBuilder.formContainer()
|
let formModel = view.modelBuilder.formContainer()
|
||||||
.withFormItems([{
|
.withFormItems([{
|
||||||
component: this.schedulesTable,
|
component: this.schedulesTable,
|
||||||
title: 'Schedules'
|
title: this.SchedulesLabelText
|
||||||
}]).withLayout({ width: '100%' }).component();
|
}]).withLayout({ width: '100%' }).component();
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
await view.initializeModel(formModel);
|
||||||
@@ -4,22 +4,14 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
export enum AgentDialogMode {
|
||||||
import { AgentUtils } from '../agentUtils';
|
CREATE = 1,
|
||||||
|
EDIT = 2,
|
||||||
export class CreateAlertData {
|
VIEW = 3
|
||||||
public ownerUri: string;
|
}
|
||||||
private _alert: sqlops.AgentAlertInfo;
|
|
||||||
|
export interface IAgentDialogData {
|
||||||
constructor(ownerUri:string) {
|
dialogMode: AgentDialogMode;
|
||||||
this.ownerUri = ownerUri;
|
initialize(): void;
|
||||||
}
|
save(): void;
|
||||||
|
|
||||||
public async initialize() {
|
|
||||||
let agentService = await AgentUtils.getAgentService();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public async save() {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
72
extensions/agent/src/mainController.ts
Normal 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 {
|
||||||
|
}
|
||||||
|
}
|
||||||
21
extensions/agent/src/test/agent.test.ts
Normal 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
110
extensions/agent/src/test/testAgentService.ts
Normal 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 {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* 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/vs/vscode.d.ts'/>
|
||||||
/// <reference path='../../../../../src/sql/sqlops.d.ts'/>
|
/// <reference path='../../../../src/sql/sqlops.d.ts'/>
|
||||||
/// <reference path='../../../../../src/sql/sqlops.proposed.d.ts'/>
|
/// <reference path='../../../../src/sql/sqlops.proposed.d.ts'/>
|
||||||
/// <reference types='@types/node'/>
|
/// <reference types='@types/node'/>
|
||||||
@@ -658,7 +658,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.1.9",
|
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.0",
|
||||||
"opener": "^1.4.3",
|
"opener": "^1.4.3",
|
||||||
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
|
"service-downloader": "github:anthonydresser/service-downloader#0.1.2",
|
||||||
"vscode-extension-telemetry": "^0.0.15"
|
"vscode-extension-telemetry": "^0.0.15"
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
"GO",
|
"GO",
|
||||||
"-- Create the new database if it does not exist already",
|
"-- Create the new database if it does not exist already",
|
||||||
"IF NOT EXISTS (",
|
"IF NOT EXISTS (",
|
||||||
"\tSELECT name",
|
"\tSELECT [name]",
|
||||||
"\t\tFROM sys.databases",
|
"\t\tFROM sys.databases",
|
||||||
"\t\tWHERE name = N'${1:DatabaseName}'",
|
"\t\tWHERE [name] = N'${1:DatabaseName}'",
|
||||||
")",
|
")",
|
||||||
"CREATE DATABASE ${1:DatabaseName}",
|
"CREATE DATABASE ${1:DatabaseName}",
|
||||||
"GO"
|
"GO"
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
"-- ALTER DATABASE ${1:DatabaseName} SET SINGLE_USER WITH ROLLBACK IMMEDIATE;",
|
"-- ALTER DATABASE ${1:DatabaseName} SET SINGLE_USER WITH ROLLBACK IMMEDIATE;",
|
||||||
"-- Drop the database if it exists",
|
"-- Drop the database if it exists",
|
||||||
"IF EXISTS (",
|
"IF EXISTS (",
|
||||||
" SELECT name",
|
" SELECT [name]",
|
||||||
" FROM sys.databases",
|
" FROM sys.databases",
|
||||||
" WHERE name = N'${1:DatabaseName}'",
|
" WHERE [name] = N'${1:DatabaseName}'",
|
||||||
")",
|
")",
|
||||||
"DROP DATABASE ${1:DatabaseName}",
|
"DROP DATABASE ${1:DatabaseName}",
|
||||||
"GO"
|
"GO"
|
||||||
@@ -42,38 +42,33 @@
|
|||||||
"Create a new Table": {
|
"Create a new Table": {
|
||||||
"prefix": "sqlCreateTable",
|
"prefix": "sqlCreateTable",
|
||||||
"body": [
|
"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",
|
"-- Drop the table if it already exists",
|
||||||
"IF OBJECT_ID('${2:SchemaName}.${1:TableName}', 'U') IS NOT NULL",
|
"IF OBJECT_ID('[${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]', 'U') IS NOT NULL",
|
||||||
"DROP TABLE ${2:SchemaName}.${1:TableName}",
|
"DROP TABLE [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
|
||||||
"GO",
|
"GO",
|
||||||
"-- Create the table in the specified schema",
|
"-- Create the table in the specified database and schema",
|
||||||
"CREATE TABLE ${2:SchemaName}.${1:TableName}",
|
"CREATE TABLE [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
|
||||||
"(",
|
"(",
|
||||||
"\t${1:TableName}Id INT NOT NULL PRIMARY KEY, -- primary key column",
|
"\t[${4:ColumnName}]Id INT NOT NULL PRIMARY KEY, -- Primary Key column",
|
||||||
"\t$3Column1 [NVARCHAR](50) NOT NULL,",
|
"\t[${5:ColumnName1}] [NVARCHAR](50) NOT NULL,",
|
||||||
"\t$4Column2 [NVARCHAR](50) NOT NULL",
|
"\t[${6:ColumnName2}] [NVARCHAR](50) NOT NULL",
|
||||||
"\t-- specify more columns here",
|
"\t-- Specify more columns here",
|
||||||
");",
|
");",
|
||||||
"GO"
|
"GO"
|
||||||
],
|
],
|
||||||
"description": "Create a new Table"
|
"description": "Create a new Table"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
"Drop a Table": {
|
"Drop a Table": {
|
||||||
"prefix": "sqlDropTable",
|
"prefix": "sqlDropTable",
|
||||||
"body": [
|
"body": [
|
||||||
"-- Drop the table '${1:TableName}' in schema '${2:SchemaName}'",
|
"-- Drop a table called '${3:TableName}' in schema '${2:SchemaName}' in Database '${1:DatabaseName}'",
|
||||||
"IF EXISTS (",
|
"-- Drop the table if it already exists",
|
||||||
"\tSELECT *",
|
"IF OBJECT_ID('[${1:DatabaseName}].[${2:SchemaName}].[${3:TableName}]', 'U') IS NOT NULL",
|
||||||
"\t\tFROM sys.tables",
|
"DROP TABLE [${1:DatabaseName}].[${2:SchemaName}].[${3:TableName}]",
|
||||||
"\t\tJOIN sys.schemas",
|
"GO"
|
||||||
"\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"
|
|
||||||
],
|
],
|
||||||
"description": "Drop a Table"
|
"description": "Drop a Table"
|
||||||
},
|
},
|
||||||
@@ -81,9 +76,9 @@
|
|||||||
"Add a new column to a Table": {
|
"Add a new column to a Table": {
|
||||||
"prefix": "sqlAddColumn",
|
"prefix": "sqlAddColumn",
|
||||||
"body": [
|
"body": [
|
||||||
"-- Add a new column '${1:NewColumnName}' to table '${2:TableName}' in schema '${3:SchemaName}'",
|
"-- Add a new column '[${1:NewColumnName}]' to table '[${2:TableName}]' in schema '[${3:SchemaName}]' in database '[${4:DatabaseName}]'",
|
||||||
"ALTER TABLE ${3:SchemaName}.${2:TableName}",
|
"ALTER TABLE [${4:DatabaseName}].[${3:SchemaName}].[${2:TableName}]",
|
||||||
"\tADD ${1:NewColumnName} /*new_column_name*/ int /*new_column_datatype*/ NULL /*new_column_nullability*/",
|
"\tADD [${1:NewColumnName}] /*new_column_name*/ ${5:int} /*new_column_datatype*/ ${6:NULL} /*new_column_nullability*/",
|
||||||
"GO"
|
"GO"
|
||||||
],
|
],
|
||||||
"description": "Add a new column to a Table"
|
"description": "Add a new column to a Table"
|
||||||
@@ -92,9 +87,9 @@
|
|||||||
"Drop a column from a Table": {
|
"Drop a column from a Table": {
|
||||||
"prefix": "sqlDropColumn",
|
"prefix": "sqlDropColumn",
|
||||||
"body": [
|
"body": [
|
||||||
"-- Drop '${1:ColumnName}' from table '${2:TableName}' in schema '${3:SchemaName}'",
|
"-- Drop '[${1:ColumnName}]' from table '[${2:TableName}]' in schema '[${3:SchemaName}]' in database '[${4:DatabaseName}]'",
|
||||||
"ALTER TABLE ${3:SchemaName}.${2:TableName}",
|
"ALTER TABLE [${4:DatabaseName}].[${3:SchemaName}].[${2:TableName}]",
|
||||||
"\tDROP COLUMN ${1:ColumnName}",
|
"\tDROP COLUMN [${1:ColumnName}]",
|
||||||
"GO"
|
"GO"
|
||||||
],
|
],
|
||||||
"description": "Add a new column to a Table"
|
"description": "Add a new column to a Table"
|
||||||
@@ -103,9 +98,9 @@
|
|||||||
"Select rows from a Table or a View": {
|
"Select rows from a Table or a View": {
|
||||||
"prefix": "sqlSelect",
|
"prefix": "sqlSelect",
|
||||||
"body": [
|
"body": [
|
||||||
"-- Select rows from a Table or View '${1:TableOrViewName}' in schema '${2:SchemaName}'",
|
"-- Select rows from a Table or View '[${1:TableOrViewName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
|
||||||
"SELECT * FROM ${2:SchemaName}.${1:TableOrViewName}",
|
"SELECT * FROM [${3:DatabaseName}].[${2:SchemaName}].[${1:TableOrViewName}]",
|
||||||
"WHERE $3\t/* add search conditions here */",
|
"WHERE ${4:/* add search conditions here */}",
|
||||||
"GO"
|
"GO"
|
||||||
],
|
],
|
||||||
"description": "Select rows from a Table or a View"
|
"description": "Select rows from a Table or a View"
|
||||||
@@ -114,19 +109,19 @@
|
|||||||
"Insert rows into a Table": {
|
"Insert rows into a Table": {
|
||||||
"prefix": "sqlInsertRows",
|
"prefix": "sqlInsertRows",
|
||||||
"body": [
|
"body": [
|
||||||
"-- Insert rows into table '${1:TableName}'",
|
"-- Insert rows into table '${1:TableName}' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
|
||||||
"INSERT INTO ${1:TableName}",
|
"INSERT INTO [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
|
||||||
"( -- columns to insert data into",
|
"( -- Columns to insert data into",
|
||||||
" $2[Column1], [Column2], [Column3]",
|
" ${4:[ColumnName1], [ColumnName2], [ColumnName3]}",
|
||||||
")",
|
")",
|
||||||
"VALUES",
|
"VALUES",
|
||||||
"( -- first row: values for the columns in the list above",
|
"( -- First row: values for the columns in the list above",
|
||||||
" $3Column1_Value, Column2_Value, Column3_Value",
|
" ${5:ColumnValue1, ColumnValue2, ColumnValue3}",
|
||||||
"),",
|
"),",
|
||||||
"( -- second row: values for the columns in the list above",
|
"( -- Second row: values for the columns in the list above",
|
||||||
" $4Column1_Value, Column2_Value, Column3_Value",
|
" ${6:ColumnValue1, ColumnValue2, ColumnValue3}",
|
||||||
")",
|
")",
|
||||||
"-- add more rows here",
|
"-- Add more rows here",
|
||||||
"GO"
|
"GO"
|
||||||
],
|
],
|
||||||
"description": "Insert rows into a Table"
|
"description": "Insert rows into a Table"
|
||||||
@@ -135,9 +130,9 @@
|
|||||||
"Delete rows from a Table": {
|
"Delete rows from a Table": {
|
||||||
"prefix": "sqlDeleteRows",
|
"prefix": "sqlDeleteRows",
|
||||||
"body": [
|
"body": [
|
||||||
"-- Delete rows from table '${1:TableName}'",
|
"-- Delete rows from table '[${1:TableName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
|
||||||
"DELETE FROM ${1:TableName}",
|
"DELETE FROM [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
|
||||||
"WHERE $2\t/* add search conditions here */",
|
"WHERE ${4:/* add search conditions here */}",
|
||||||
"GO"
|
"GO"
|
||||||
],
|
],
|
||||||
"description": "Delete rows from a Table"
|
"description": "Delete rows from a Table"
|
||||||
@@ -146,13 +141,13 @@
|
|||||||
"Update rows in a Table": {
|
"Update rows in a Table": {
|
||||||
"prefix": "sqlUpdateRows",
|
"prefix": "sqlUpdateRows",
|
||||||
"body": [
|
"body": [
|
||||||
"-- Update rows in table '${1:TableName}'",
|
"-- Update rows in table '[${1:TableName}]' in schema '[${2:SchemaName}]' in database '[${3:DatabaseName}]'",
|
||||||
"UPDATE ${1:TableName}",
|
"UPDATE [${3:DatabaseName}].[${2:SchemaName}].[${1:TableName}]",
|
||||||
"SET",
|
"SET",
|
||||||
"\t$2[Colum1] = Colum1_Value,",
|
"\t[${4:ColumnName1}] = ${5:ColumnValue1},",
|
||||||
"\t$3[Colum2] = Colum2_Value",
|
"\t[${6:ColumnName2}] = ${7:ColumnValue2}",
|
||||||
"\t-- add more columns and values here",
|
"\t-- Add more columns and values here",
|
||||||
"WHERE $4\t/* add search conditions here */",
|
"WHERE ${8:/* add search conditions here */}",
|
||||||
"GO"
|
"GO"
|
||||||
],
|
],
|
||||||
"description": "Update rows in a Table"
|
"description": "Update rows in a Table"
|
||||||
@@ -207,8 +202,8 @@
|
|||||||
"prefix": "sqlListTablesAndViews",
|
"prefix": "sqlListTablesAndViews",
|
||||||
"body": [
|
"body": [
|
||||||
"-- Get a list of tables and views in the current database",
|
"-- Get a list of tables and views in the current database",
|
||||||
"SELECT table_catalog [database], table_schema [schema], table_name name, table_type type",
|
"SELECT table_catalog [database], table_schema [schema], table_name [name], table_type [type]",
|
||||||
"FROM information_schema.tables",
|
"FROM INFORMATION_SCHEMA.TABLES",
|
||||||
"GO"
|
"GO"
|
||||||
],
|
],
|
||||||
"description": "List tables and vies in the current database"
|
"description": "List tables and vies in the current database"
|
||||||
@@ -218,7 +213,7 @@
|
|||||||
"prefix": "sqlListDatabases",
|
"prefix": "sqlListDatabases",
|
||||||
"body": [
|
"body": [
|
||||||
"-- Get a list of databases",
|
"-- Get a list of databases",
|
||||||
"SELECT name FROM sys.databases",
|
"SELECT [name] FROM sys.databases",
|
||||||
"GO"
|
"GO"
|
||||||
],
|
],
|
||||||
"description": "List databases"
|
"description": "List databases"
|
||||||
@@ -232,8 +227,8 @@
|
|||||||
"\tTableName = tbl.table_schema + '.' + tbl.table_name, ",
|
"\tTableName = tbl.table_schema + '.' + tbl.table_name, ",
|
||||||
"\tColumnName = col.column_name, ",
|
"\tColumnName = col.column_name, ",
|
||||||
"\tColumnDataType = col.data_type",
|
"\tColumnDataType = col.data_type",
|
||||||
"FROM information_schema.tables tbl",
|
"FROM INFORMATION_SCHEMA.TABLES tbl",
|
||||||
"INNER JOIN information_schema.columns col ",
|
"INNER JOIN INFORMATION_SCHEMA.COLUMNS col ",
|
||||||
"\tON col.table_name = tbl.table_name",
|
"\tON col.table_name = tbl.table_name",
|
||||||
"\tAND col.table_schema = tbl.table_schema",
|
"\tAND col.table_schema = tbl.table_schema",
|
||||||
"",
|
"",
|
||||||
@@ -247,20 +242,20 @@
|
|||||||
"prefix": "sqlCursor",
|
"prefix": "sqlCursor",
|
||||||
"body": [
|
"body": [
|
||||||
"-- Declare a cursor for a Table or a View '${1:TableOrViewName}' in schema '${2:SchemaName}'",
|
"-- 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",
|
"DECLARE db_cursor CURSOR FOR",
|
||||||
"SELECT Column1, Column2",
|
"SELECT ColumnName1, Column2",
|
||||||
"FROM $2.$1",
|
"FROM $2.$1",
|
||||||
"",
|
"",
|
||||||
"OPEN db_cursor",
|
"OPEN db_cursor",
|
||||||
"FETCH NEXT FROM db_cursor INTO @Column1, @Column2",
|
"FETCH NEXT FROM db_cursor INTO @ColumnName1, @ColumnName2",
|
||||||
"",
|
"",
|
||||||
"WHILE @@FETCH_STATUS = 0",
|
"WHILE @@FETCH_STATUS = 0",
|
||||||
"BEGIN",
|
"BEGIN",
|
||||||
"\t-- add instructions to be executed for every row",
|
"\t-- add instructions to be executed for every row",
|
||||||
"\t$3",
|
"\t$3",
|
||||||
"\tFETCH NEXT FROM db_cursor INTO @Column1, @Column2",
|
"\tFETCH NEXT FROM db_cursor INTO @ColumnName1, @ColumnName2",
|
||||||
"END",
|
"END",
|
||||||
"",
|
"",
|
||||||
"CLOSE db_cursor",
|
"CLOSE db_cursor",
|
||||||
@@ -303,5 +298,34 @@
|
|||||||
"GO"
|
"GO"
|
||||||
],
|
],
|
||||||
"description": "Get Space Used by Tables"
|
"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"
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
"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": {
|
"downloadFileNames": {
|
||||||
"Windows_86": "win-x86-netcoreapp2.1.zip",
|
"Windows_86": "win-x86-netcoreapp2.1.zip",
|
||||||
"Windows_64": "win-x64-netcoreapp2.1.zip",
|
"Windows_64": "win-x64-netcoreapp2.1.zip",
|
||||||
|
|||||||
@@ -148,6 +148,11 @@ export interface DeleteAgentProxyParams {
|
|||||||
proxy: sqlops.AgentProxyInfo;
|
proxy: sqlops.AgentProxyInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Agent Credentials parameters
|
||||||
|
export interface GetCredentialsParams {
|
||||||
|
ownerUri: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Job Schedule management parameters
|
// Job Schedule management parameters
|
||||||
export interface AgentJobScheduleParams {
|
export interface AgentJobScheduleParams {
|
||||||
ownerUri: string;
|
ownerUri: string;
|
||||||
@@ -262,6 +267,11 @@ export namespace DeleteAgentProxyRequest {
|
|||||||
export const type = new RequestType<DeleteAgentProxyParams, sqlops.ResultStatus, void, void>('agent/deleteproxy');
|
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
|
// Job Schedules requests
|
||||||
export namespace AgentJobSchedulesRequest {
|
export namespace AgentJobSchedulesRequest {
|
||||||
export const type = new RequestType<AgentJobScheduleParams, sqlops.AgentJobSchedulesResult, void, void>('agent/schedules');
|
export const type = new RequestType<AgentJobScheduleParams, sqlops.AgentJobSchedulesResult, void, void>('agent/schedules');
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
contracts.AgentJobActionRequest.type
|
contracts.AgentJobActionRequest.type
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private onUpdatedHandler: () => any;
|
||||||
|
|
||||||
constructor(client: SqlOpsDataClient) {
|
constructor(client: SqlOpsDataClient) {
|
||||||
super(client, AgentServicesFeature.messagesTypes);
|
super(client, AgentServicesFeature.messagesTypes);
|
||||||
}
|
}
|
||||||
@@ -53,6 +55,18 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
|
|
||||||
protected registerProvider(options: undefined): Disposable {
|
protected registerProvider(options: undefined): Disposable {
|
||||||
const client = this._client;
|
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
|
// Job management methods
|
||||||
let getJobs = (ownerUri: string): Thenable<sqlops.AgentJobsResult> => {
|
let getJobs = (ownerUri: string): Thenable<sqlops.AgentJobsResult> => {
|
||||||
@@ -96,7 +110,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.CreateAgentJobRequest.type;
|
let requestType = contracts.CreateAgentJobRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -112,7 +129,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.UpdateAgentJobRequest.type;
|
let requestType = contracts.UpdateAgentJobRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -127,7 +147,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.DeleteAgentJobRequest.type;
|
let requestType = contracts.DeleteAgentJobRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -157,7 +180,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.CreateAgentJobStepRequest.type;
|
let requestType = contracts.CreateAgentJobStepRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -173,7 +199,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.UpdateAgentJobStepRequest.type;
|
let requestType = contracts.UpdateAgentJobStepRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -188,7 +217,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.DeleteAgentJobStepRequest.type;
|
let requestType = contracts.DeleteAgentJobStepRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -218,7 +250,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.CreateAgentAlertRequest.type;
|
let requestType = contracts.CreateAgentAlertRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -234,7 +269,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.UpdateAgentAlertRequest.type;
|
let requestType = contracts.UpdateAgentAlertRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -249,7 +287,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.DeleteAgentAlertRequest.type;
|
let requestType = contracts.DeleteAgentAlertRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -279,7 +320,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.CreateAgentOperatorRequest.type;
|
let requestType = contracts.CreateAgentOperatorRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -295,7 +339,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.UpdateAgentOperatorRequest.type;
|
let requestType = contracts.UpdateAgentOperatorRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -310,7 +357,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.DeleteAgentOperatorRequest.type;
|
let requestType = contracts.DeleteAgentOperatorRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -340,7 +390,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.CreateAgentProxyRequest.type;
|
let requestType = contracts.CreateAgentProxyRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -356,7 +409,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.UpdateAgentProxyRequest.type;
|
let requestType = contracts.UpdateAgentProxyRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -370,6 +426,24 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
proxy: proxyInfo
|
proxy: proxyInfo
|
||||||
};
|
};
|
||||||
let requestType = contracts.DeleteAgentProxyRequest.type;
|
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(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => r,
|
||||||
e => {
|
e => {
|
||||||
@@ -379,6 +453,7 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Job Schedule management methods
|
// Job Schedule management methods
|
||||||
let getJobSchedules = (ownerUri: string): Thenable<sqlops.AgentJobSchedulesResult> => {
|
let getJobSchedules = (ownerUri: string): Thenable<sqlops.AgentJobSchedulesResult> => {
|
||||||
let params: contracts.AgentJobScheduleParams = {
|
let params: contracts.AgentJobScheduleParams = {
|
||||||
@@ -401,7 +476,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.CreateAgentJobScheduleRequest.type;
|
let requestType = contracts.CreateAgentJobScheduleRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -417,7 +495,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.UpdateAgentJobScheduleRequest.type;
|
let requestType = contracts.UpdateAgentJobScheduleRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -432,7 +513,10 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
};
|
};
|
||||||
let requestType = contracts.DeleteAgentJobScheduleRequest.type;
|
let requestType = contracts.DeleteAgentJobScheduleRequest.type;
|
||||||
return client.sendRequest(requestType, params).then(
|
return client.sendRequest(requestType, params).then(
|
||||||
r => r,
|
r => {
|
||||||
|
fireOnUpdated();
|
||||||
|
return r;
|
||||||
|
},
|
||||||
e => {
|
e => {
|
||||||
client.logFailedRequest(requestType, e);
|
client.logFailedRequest(requestType, e);
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
@@ -464,10 +548,12 @@ export class AgentServicesFeature extends SqlOpsFeature<undefined> {
|
|||||||
createProxy,
|
createProxy,
|
||||||
updateProxy,
|
updateProxy,
|
||||||
deleteProxy,
|
deleteProxy,
|
||||||
|
getCredentials,
|
||||||
getJobSchedules,
|
getJobSchedules,
|
||||||
createJobSchedule,
|
createJobSchedule,
|
||||||
updateJobSchedule,
|
updateJobSchedule,
|
||||||
deleteJobSchedule
|
deleteJobSchedule,
|
||||||
|
registerOnUpdated
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,22 +44,6 @@ export class Telemetry {
|
|||||||
private static platformInformation: PlatformInformation;
|
private static platformInformation: PlatformInformation;
|
||||||
private static disabled: boolean;
|
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> {
|
public static getPlatformInformation(): Promise<PlatformInformation> {
|
||||||
if (this.platformInformation) {
|
if (this.platformInformation) {
|
||||||
return Promise.resolve(this.platformInformation);
|
return Promise.resolve(this.platformInformation);
|
||||||
@@ -143,8 +127,7 @@ export class Telemetry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Augment the properties structure with additional common properties before sending
|
// Augment the properties structure with additional common properties before sending
|
||||||
Promise.all([this.getUserId(), this.getPlatformInformation()]).then(() => {
|
Promise.all([this.getPlatformInformation()]).then(() => {
|
||||||
properties['userId'] = this.userId;
|
|
||||||
properties['distribution'] = (this.platformInformation && this.platformInformation.distribution) ?
|
properties['distribution'] = (this.platformInformation && this.platformInformation.distribution) ?
|
||||||
`${this.platformInformation.distribution.name}, ${this.platformInformation.distribution.version}` : '';
|
`${this.platformInformation.distribution.name}, ${this.platformInformation.distribution.version}` : '';
|
||||||
|
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ core-util-is@~1.0.0:
|
|||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
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":
|
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.0":
|
||||||
version "0.1.9"
|
version "0.2.0"
|
||||||
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/a1a79895cb79658b75d78aa5cfd745855019148d"
|
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/c7a691c7fc5ad54a147090784a7f11efda301f4b"
|
||||||
dependencies:
|
dependencies:
|
||||||
vscode-languageclient "3.5.0"
|
vscode-languageclient "3.5.0"
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "profiler",
|
"name": "profiler",
|
||||||
"displayName": "SQL Server Profiler",
|
"displayName": "SQL Server Profiler",
|
||||||
"description": "SQL Server Profiler for SQL Operations Studio",
|
"description": "SQL Server Profiler for SQL Operations Studio",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"publisher": "Microsoft",
|
"publisher": "Microsoft",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
"license": "https://raw.githubusercontent.com/Microsoft/sqlopsstudio/master/LICENSE.txt",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sqlops",
|
"name": "sqlops",
|
||||||
"version": "0.31.1",
|
"version": "0.31.4",
|
||||||
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Microsoft Corporation"
|
"name": "Microsoft Corporation"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"reflect-metadata": "^0.1.8",
|
"reflect-metadata": "^0.1.8",
|
||||||
"rxjs": "5.4.0",
|
"rxjs": "5.4.0",
|
||||||
"semver": "4.3.6",
|
"semver": "4.3.6",
|
||||||
"slickgrid": "github:anthonydresser/SlickGrid#2.3.22",
|
"slickgrid": "github:anthonydresser/SlickGrid#2.3.23",
|
||||||
"spdlog": "0.6.0",
|
"spdlog": "0.6.0",
|
||||||
"sudo-prompt": "^8.0.0",
|
"sudo-prompt": "^8.0.0",
|
||||||
"svg.js": "^2.2.5",
|
"svg.js": "^2.2.5",
|
||||||
|
|||||||
@@ -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> {
|
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()
|
let inputBox = view.modelBuilder.inputBox()
|
||||||
.withProperties({
|
.withProperties({
|
||||||
multiline: true,
|
multiline: true,
|
||||||
height: 100
|
height: 100
|
||||||
}).component();
|
}).component();
|
||||||
let inputBoxWrapper = view.modelBuilder.loadingComponent().withItem(inputBox).component();
|
let inputBoxWrapper = view.modelBuilder.loadingComponent().withItem(inputBox).component();
|
||||||
inputBoxWrapper.loading = false;
|
inputBoxWrapper.loading = false;
|
||||||
customButton1.onClick(() => {
|
customButton1.onClick(() => {
|
||||||
@@ -145,8 +145,8 @@ export default class MainController implements vscode.Disposable {
|
|||||||
component: inputBox4,
|
component: inputBox4,
|
||||||
title: 'inputBox4'
|
title: 'inputBox4'
|
||||||
}], {
|
}], {
|
||||||
horizontal: true
|
horizontal: true
|
||||||
}).component();
|
}).component();
|
||||||
let groupModel1 = view.modelBuilder.groupContainer()
|
let groupModel1 = view.modelBuilder.groupContainer()
|
||||||
.withLayout({
|
.withLayout({
|
||||||
}).withItems([
|
}).withItems([
|
||||||
@@ -178,8 +178,8 @@ export default class MainController implements vscode.Disposable {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
let declarativeTable = view.modelBuilder.declarativeTable()
|
let declarativeTable = view.modelBuilder.declarativeTable()
|
||||||
.withProperties({
|
.withProperties({
|
||||||
columns: [{
|
columns: [{
|
||||||
displayName: 'Column 1',
|
displayName: 'Column 1',
|
||||||
valueType: sqlops.DeclarativeDataType.string,
|
valueType: sqlops.DeclarativeDataType.string,
|
||||||
width: '20px',
|
width: '20px',
|
||||||
@@ -204,12 +204,12 @@ export default class MainController implements vscode.Disposable {
|
|||||||
{ name: 'options2', displayName: 'option 2' }
|
{ name: 'options2', displayName: 'option 2' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
data: [
|
data: [
|
||||||
['Data00', 'Data01', false, 'options2'],
|
['Data00', 'Data01', false, 'options2'],
|
||||||
['Data10', 'Data11', true, 'options1']
|
['Data10', 'Data11', true, 'options1']
|
||||||
]
|
]
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
declarativeTable.onDataChanged(e => {
|
declarativeTable.onDataChanged(e => {
|
||||||
inputBox2.value = e.row.toString() + ' ' + e.column.toString() + ' ' + e.value.toString();
|
inputBox2.value = e.row.toString() + ' ' + e.column.toString() + ' ' + e.value.toString();
|
||||||
@@ -223,7 +223,7 @@ export default class MainController implements vscode.Disposable {
|
|||||||
height: 150
|
height: 150
|
||||||
}).withItems([
|
}).withItems([
|
||||||
radioButton, groupModel1, radioButton2]
|
radioButton, groupModel1, radioButton2]
|
||||||
, { flex: '1 1 50%' }).component();
|
, { flex: '1 1 50%' }).component();
|
||||||
let formModel = view.modelBuilder.formContainer()
|
let formModel = view.modelBuilder.formContainer()
|
||||||
.withFormItems([{
|
.withFormItems([{
|
||||||
component: inputBoxWrapper,
|
component: inputBoxWrapper,
|
||||||
@@ -254,9 +254,9 @@ export default class MainController implements vscode.Disposable {
|
|||||||
component: listBox,
|
component: listBox,
|
||||||
title: 'List Box'
|
title: 'List Box'
|
||||||
}], {
|
}], {
|
||||||
horizontal: false,
|
horizontal: false,
|
||||||
componentWidth: componentWidth
|
componentWidth: componentWidth
|
||||||
}).component();
|
}).component();
|
||||||
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
||||||
formWrapper.loading = false;
|
formWrapper.loading = false;
|
||||||
customButton2.onClick(() => {
|
customButton2.onClick(() => {
|
||||||
@@ -285,7 +285,6 @@ export default class MainController implements vscode.Disposable {
|
|||||||
tab1.registerContent(async (view) => {
|
tab1.registerContent(async (view) => {
|
||||||
await this.getTabContent(view, customButton1, customButton2, 400);
|
await this.getTabContent(view, customButton1, customButton2, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
sqlops.window.modelviewdialog.openDialog(dialog);
|
sqlops.window.modelviewdialog.openDialog(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,6 +301,17 @@ export default class MainController implements vscode.Disposable {
|
|||||||
page1.registerContent(async (view) => {
|
page1.registerContent(async (view) => {
|
||||||
await this.getTabContent(view, customButton1, customButton2, 800);
|
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.pages = [page1, page2];
|
||||||
wizard.open();
|
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 monitorLightPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg'));
|
||||||
let monitorIcon = {
|
let monitorIcon = {
|
||||||
light: monitorLightPath,
|
light: monitorLightPath,
|
||||||
dark: path.join(__dirname, '..', 'media', 'monitor_inverse.svg') };
|
dark: path.join(__dirname, '..', 'media', 'monitor_inverse.svg')
|
||||||
|
};
|
||||||
|
|
||||||
let monitorButton = view.modelBuilder.button()
|
let monitorButton = view.modelBuilder.button()
|
||||||
.withProperties({
|
.withProperties({
|
||||||
label: 'Monitor',
|
label: 'Monitor',
|
||||||
iconPath: monitorIcon
|
iconPath: monitorIcon
|
||||||
}).component();
|
}).component();
|
||||||
let toolbarModel = view.modelBuilder.toolbarContainer()
|
let toolbarModel = view.modelBuilder.toolbarContainer()
|
||||||
.withToolbarItems([{
|
.withToolbarItems([{
|
||||||
component: inputBox,
|
component: inputBox,
|
||||||
|
|||||||
@@ -63,13 +63,13 @@ export class InputBox extends vsInputBox {
|
|||||||
this.enabledInputBorder = this.inputBorder;
|
this.enabledInputBorder = this.inputBorder;
|
||||||
this.disabledInputBackground = styles.disabledInputBackground;
|
this.disabledInputBackground = styles.disabledInputBackground;
|
||||||
this.disabledInputForeground = styles.disabledInputForeground;
|
this.disabledInputForeground = styles.disabledInputForeground;
|
||||||
|
this.updateInputEnabledDisabledColors();
|
||||||
|
this.applyStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public enable(): void {
|
public enable(): void {
|
||||||
super.enable();
|
super.enable();
|
||||||
this.inputBackground = this.enabledInputBackground;
|
this.updateInputEnabledDisabledColors();
|
||||||
this.inputForeground = this.enabledInputForeground;
|
|
||||||
this.inputBorder = this.enabledInputBorder;
|
|
||||||
this.applyStyles();
|
this.applyStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,9 +89,7 @@ export class InputBox extends vsInputBox {
|
|||||||
|
|
||||||
public disable(): void {
|
public disable(): void {
|
||||||
super.disable();
|
super.disable();
|
||||||
this.inputBackground = this.disabledInputBackground;
|
this.updateInputEnabledDisabledColors();
|
||||||
this.inputForeground = this.disabledInputForeground;
|
|
||||||
this.inputBorder = this.disabledInputBorder;
|
|
||||||
this.applyStyles();
|
this.applyStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,4 +119,11 @@ export class InputBox extends vsInputBox {
|
|||||||
super.showMessage(message, force);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ export function appendRow(container: Builder, label: string, labelClass: string,
|
|||||||
container.element('tr', {}, (rowContainer) => {
|
container.element('tr', {}, (rowContainer) => {
|
||||||
rowContainer.element('td', { class: labelClass }, (labelCellContainer) => {
|
rowContainer.element('td', { class: labelClass }, (labelCellContainer) => {
|
||||||
labelCellContainer.div({}, (labelContainer) => {
|
labelCellContainer.div({}, (labelContainer) => {
|
||||||
labelContainer.innerHtml(label);
|
labelContainer.text(label);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => {
|
rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => {
|
||||||
@@ -33,7 +33,7 @@ export function appendRowLink(container: Builder, label: string, labelClass: str
|
|||||||
container.element('tr', {}, (rowContainer) => {
|
container.element('tr', {}, (rowContainer) => {
|
||||||
rowContainer.element('td', { class: labelClass }, (labelCellContainer) => {
|
rowContainer.element('td', { class: labelClass }, (labelCellContainer) => {
|
||||||
labelCellContainer.div({}, (labelContainer) => {
|
labelCellContainer.div({}, (labelContainer) => {
|
||||||
labelContainer.innerHtml(label);
|
labelContainer.text(label);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => {
|
rowContainer.element('td', { class: cellContainerClass }, (inputCellContainer) => {
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ export abstract class Modal extends Disposable implements IThemable {
|
|||||||
}
|
}
|
||||||
modalHeader.div({ class: 'modal-title' }, (modalTitle) => {
|
modalHeader.div({ class: 'modal-title' }, (modalTitle) => {
|
||||||
this._modalTitle = modalTitle;
|
this._modalTitle = modalTitle;
|
||||||
modalTitle.innerHtml(this._title);
|
modalTitle.text(this._title);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
parts.push(this._modalHeaderSection.getHTMLElement());
|
parts.push(this._modalHeaderSection.getHTMLElement());
|
||||||
@@ -433,7 +433,7 @@ export abstract class Modal extends Disposable implements IThemable {
|
|||||||
*/
|
*/
|
||||||
protected set title(title: string) {
|
protected set title(title: string) {
|
||||||
if (this._title !== undefined) {
|
if (this._title !== undefined) {
|
||||||
this._modalTitle.innerHtml(title);
|
this._modalTitle.text(title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -150,8 +150,8 @@ export class OptionsDialog extends Modal {
|
|||||||
|
|
||||||
private onOptionLinkClicked(optionName: string): void {
|
private onOptionLinkClicked(optionName: string): void {
|
||||||
var option = this._optionElements[optionName].option;
|
var option = this._optionElements[optionName].option;
|
||||||
this._optionTitle.innerHtml(option.displayName);
|
this._optionTitle.text(option.displayName);
|
||||||
this._optionDescription.innerHtml(option.description);
|
this._optionDescription.text(option.description);
|
||||||
}
|
}
|
||||||
|
|
||||||
private fillInOptions(container: Builder, options: sqlops.ServiceOption[]): void {
|
private fillInOptions(container: Builder, options: sqlops.ServiceOption[]): void {
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
import { Component, Input, ContentChild, OnDestroy, TemplateRef, ChangeDetectorRef, forwardRef, Inject } from '@angular/core';
|
import { Component, Input, ContentChild, OnDestroy, TemplateRef, ChangeDetectorRef, forwardRef, Inject } from '@angular/core';
|
||||||
|
|
||||||
import { Action } from 'vs/base/common/actions';
|
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;
|
public abstract layout(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ export abstract class TabChild {
|
|||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class TabComponent implements OnDestroy {
|
export class TabComponent implements OnDestroy {
|
||||||
@ContentChild(TabChild) private _child: TabChild;
|
private _child: TabChild;
|
||||||
@ContentChild(TemplateRef) templateRef;
|
@ContentChild(TemplateRef) templateRef;
|
||||||
@Input() public title: string;
|
@Input() public title: string;
|
||||||
@Input() public canClose: boolean;
|
@Input() public canClose: boolean;
|
||||||
@@ -29,19 +30,30 @@ export class TabComponent implements OnDestroy {
|
|||||||
@Input() public identifier: string;
|
@Input() public identifier: string;
|
||||||
@Input() private visibilityType: 'if' | 'visibility' = 'if';
|
@Input() private visibilityType: 'if' | 'visibility' = 'if';
|
||||||
private rendered = false;
|
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(
|
constructor(
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public set active(val: boolean) {
|
public set active(val: boolean) {
|
||||||
this._active = val;
|
if (!this.destroyed) {
|
||||||
if (this.active) {
|
this._active = val;
|
||||||
this.rendered = true;
|
if (this.active) {
|
||||||
}
|
this.rendered = true;
|
||||||
this._cd.detectChanges();
|
}
|
||||||
if (this.active && this._child) {
|
this._cd.detectChanges();
|
||||||
this._child.layout();
|
if (this.active && this._child) {
|
||||||
|
this._child.layout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +62,7 @@ export class TabComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
this.destroyed = true;
|
||||||
if (this.actions && this.actions.length > 0) {
|
if (this.actions && this.actions.length > 0) {
|
||||||
this.actions.forEach((action) => action.dispose());
|
this.actions.forEach((action) => action.dispose());
|
||||||
}
|
}
|
||||||
@@ -74,6 +87,8 @@ export class TabComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public layout() {
|
public layout() {
|
||||||
this._child.layout();
|
if (this._child) {
|
||||||
|
this._child.layout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export class TabHeaderComponent extends Disposable implements AfterContentInit,
|
|||||||
tabLabelcontainer.classList.add(this.tab.iconClass);
|
tabLabelcontainer.classList.add(this.tab.iconClass);
|
||||||
} else {
|
} else {
|
||||||
tabLabelcontainer.className = 'tabLabel';
|
tabLabelcontainer.className = 'tabLabel';
|
||||||
tabLabelcontainer.innerHTML = this.tab.title;
|
tabLabelcontainer.textContent = this.tab.title;
|
||||||
}
|
}
|
||||||
tabLabelcontainer.title = this.tab.title;
|
tabLabelcontainer.title = this.tab.title;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
{
|
{
|
||||||
border: 1px solid #BFBDBD;
|
border: 1px solid #BFBDBD;
|
||||||
font-size: 8pt;
|
font-size: 8pt;
|
||||||
height: 400px;
|
height: 250px;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { mixin } from 'vs/base/common/objects';
|
|||||||
import { SlickGrid } from 'angular2-slickgrid';
|
import { SlickGrid } from 'angular2-slickgrid';
|
||||||
import { Button } from '../../button/button';
|
import { Button } from '../../button/button';
|
||||||
import { attachButtonStyler } from 'sql/common/theme/styler';
|
import { attachButtonStyler } from 'sql/common/theme/styler';
|
||||||
|
import { escape } from 'sql/base/common/strings';
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
|
|
||||||
export class HeaderFilter {
|
export class HeaderFilter {
|
||||||
@@ -174,7 +175,7 @@ export class HeaderFilter {
|
|||||||
if (filterItems[i] && filterItems[i].indexOf('Error:') < 0) {
|
if (filterItems[i] && filterItems[i].indexOf('Error:') < 0) {
|
||||||
filterOptions += '<label><input type="checkbox" value="' + i + '"'
|
filterOptions += '<label><input type="checkbox" value="' + i + '"'
|
||||||
+ (filtered ? ' checked="checked"' : '')
|
+ (filtered ? ' checked="checked"' : '')
|
||||||
+ '/>' + filterItems[i] + '</label>';
|
+ '/>' + escape(filterItems[i]) + '</label>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let $filter = $('<div class="filter">')
|
let $filter = $('<div class="filter">')
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowdetailview.js
|
// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowdetailview.js
|
||||||
// heavily modified
|
// heavily modified
|
||||||
|
import { escape } from 'sql/base/common/strings';
|
||||||
import { mixin } from 'vs/base/common/objects';
|
import { mixin } from 'vs/base/common/objects';
|
||||||
import * as nls from 'vs/nls';
|
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("style='height:", dataContext._height, "px;"); //set total height of padding
|
||||||
html.push("top:", rowHeight, "px'>"); //shift detail below 1st row
|
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='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
|
//&omit a final closing detail container </div> that would come next
|
||||||
|
|
||||||
return html.join('');
|
return html.join('');
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export class Taskbar {
|
|||||||
public static createTaskbarText(inputText: string): HTMLElement {
|
public static createTaskbarText(inputText: string): HTMLElement {
|
||||||
let element = document.createElement('div');
|
let element = document.createElement('div');
|
||||||
element.className = 'taskbarTextSeparator';
|
element.className = 'taskbarTextSeparator';
|
||||||
element.innerHTML = inputText;
|
element.textContent = inputText;
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
21
src/sql/base/common/strings.ts
Normal 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 '<';
|
||||||
|
case '>': return '>';
|
||||||
|
case '&': return '&';
|
||||||
|
case '"': return '"';
|
||||||
|
default: return match;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -13,3 +13,6 @@ export const SerializationDisabled = 'Saving results into different format disab
|
|||||||
*/
|
*/
|
||||||
export const RestoreFeatureName = 'restore';
|
export const RestoreFeatureName = 'restore';
|
||||||
export const BackupFeatureName = 'backup';
|
export const BackupFeatureName = 'backup';
|
||||||
|
|
||||||
|
export const MssqlProviderId = 'MSSQL';
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,15 @@
|
|||||||
content: url("status_info.svg");
|
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 .icon.success,
|
||||||
.vs-dark .icon.success,
|
.vs-dark .icon.success,
|
||||||
.hc-black .icon.success {
|
.hc-black .icon.success {
|
||||||
|
|||||||
1
src/sql/media/icons/help.svg
Normal 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 |
1
src/sql/media/icons/help_inverse.svg
Normal 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 |
@@ -130,8 +130,8 @@ export class AccountDialog extends Modal {
|
|||||||
|
|
||||||
this._noaccountViewContainer = DOM.$('div.no-account-view');
|
this._noaccountViewContainer = DOM.$('div.no-account-view');
|
||||||
let noAccountTitle = DOM.append(this._noaccountViewContainer, DOM.$('.no-account-view-label'));
|
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.');
|
let noAccountLabel = localize('accountDialog.noAccountLabel', 'There is no linked account. Please add an account.');
|
||||||
noAccountTitle.innerHTML = noAccountLabel;
|
noAccountTitle.innerText = noAccountLabel;
|
||||||
|
|
||||||
// Show the add account button for the first provider
|
// 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
|
// Todo: If we have more than 1 provider, need to show all add account buttons for all providers
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export class AutoOAuthDialog extends Modal {
|
|||||||
let inputBox: InputBox;
|
let inputBox: InputBox;
|
||||||
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
|
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
|
||||||
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
|
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
|
||||||
labelContainer.innerHtml(label);
|
labelContainer.text(label);
|
||||||
});
|
});
|
||||||
|
|
||||||
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
|
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export class FirewallRuleDialog extends Modal {
|
|||||||
subnetIPRangeSection = subnetIPRangeContainer.getHTMLElement();
|
subnetIPRangeSection = subnetIPRangeContainer.getHTMLElement();
|
||||||
subnetIPRangeContainer.div({ 'class': 'dialog-input-section' }, (inputContainer) => {
|
subnetIPRangeContainer.div({ 'class': 'dialog-input-section' }, (inputContainer) => {
|
||||||
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
|
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
|
||||||
labelContainer.innerHtml(LocalizedStrings.FROM);
|
labelContainer.text(LocalizedStrings.FROM);
|
||||||
});
|
});
|
||||||
|
|
||||||
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
|
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
|
||||||
@@ -155,7 +155,7 @@ export class FirewallRuleDialog extends Modal {
|
|||||||
});
|
});
|
||||||
|
|
||||||
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
|
inputContainer.div({ 'class': 'dialog-label' }, (labelContainer) => {
|
||||||
labelContainer.innerHtml(LocalizedStrings.TO);
|
labelContainer.text(LocalizedStrings.TO);
|
||||||
});
|
});
|
||||||
|
|
||||||
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
|
inputContainer.div({ 'class': 'dialog-input' }, (inputCellContainer) => {
|
||||||
@@ -234,7 +234,7 @@ export class FirewallRuleDialog extends Modal {
|
|||||||
className += ' header';
|
className += ' header';
|
||||||
}
|
}
|
||||||
container.div({ 'class': className }, (labelContainer) => {
|
container.div({ 'class': className }, (labelContainer) => {
|
||||||
labelContainer.innerHtml(content);
|
labelContainer.text(content);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||||
import { IConnectionProfile } from 'sqlops';
|
import { IConnectionProfile } from 'sqlops';
|
||||||
|
|
||||||
export class ConnectionContextkey implements IContextKey<IConnectionProfile> {
|
export class ConnectionContextKey implements IContextKey<IConnectionProfile> {
|
||||||
|
|
||||||
static Provider = new RawContextKey<string>('connectionProvider', undefined);
|
static Provider = new RawContextKey<string>('connectionProvider', undefined);
|
||||||
static Server = new RawContextKey<string>('serverName', undefined);
|
static Server = new RawContextKey<string>('serverName', undefined);
|
||||||
@@ -23,10 +23,10 @@ export class ConnectionContextkey implements IContextKey<IConnectionProfile> {
|
|||||||
constructor(
|
constructor(
|
||||||
@IContextKeyService contextKeyService: IContextKeyService
|
@IContextKeyService contextKeyService: IContextKeyService
|
||||||
) {
|
) {
|
||||||
this._providerKey = ConnectionContextkey.Provider.bindTo(contextKeyService);
|
this._providerKey = ConnectionContextKey.Provider.bindTo(contextKeyService);
|
||||||
this._serverKey = ConnectionContextkey.Server.bindTo(contextKeyService);
|
this._serverKey = ConnectionContextKey.Server.bindTo(contextKeyService);
|
||||||
this._databaseKey = ConnectionContextkey.Database.bindTo(contextKeyService);
|
this._databaseKey = ConnectionContextKey.Database.bindTo(contextKeyService);
|
||||||
this._connectionKey = ConnectionContextkey.Connection.bindTo(contextKeyService);
|
this._connectionKey = ConnectionContextKey.Connection.bindTo(contextKeyService);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(value: IConnectionProfile) {
|
set(value: IConnectionProfile) {
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ export interface IConnectionManagementService {
|
|||||||
|
|
||||||
disconnectEditor(owner: IConnectableInput, force?: boolean): Promise<boolean>;
|
disconnectEditor(owner: IConnectableInput, force?: boolean): Promise<boolean>;
|
||||||
|
|
||||||
disconnect(connection: ConnectionProfile): Promise<void>;
|
disconnect(connection: IConnectionProfile): Promise<void>;
|
||||||
|
|
||||||
disconnect(ownerUri: string): Promise<void>;
|
disconnect(ownerUri: string): Promise<void>;
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ export interface IConnectionManagementService {
|
|||||||
*/
|
*/
|
||||||
cancelEditorConnection(owner: IConnectableInput): Thenable<boolean>;
|
cancelEditorConnection(owner: IConnectableInput): Thenable<boolean>;
|
||||||
|
|
||||||
showDashboard(connection: ConnectionProfile): Thenable<boolean>;
|
showDashboard(connection: IConnectionProfile): Thenable<boolean>;
|
||||||
|
|
||||||
closeDashboard(uri: string): void;
|
closeDashboard(uri: string): void;
|
||||||
|
|
||||||
@@ -216,7 +216,7 @@ export interface IConnectionManagementService {
|
|||||||
|
|
||||||
hasRegisteredServers(): boolean;
|
hasRegisteredServers(): boolean;
|
||||||
|
|
||||||
canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean;
|
canChangeConnectionConfig(profile: IConnectionProfile, newGroupID: string): boolean;
|
||||||
|
|
||||||
getTabColorForUri(uri: string): string;
|
getTabColorForUri(uri: string): string;
|
||||||
|
|
||||||
@@ -320,12 +320,13 @@ export enum MetadataType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum TaskStatus {
|
export enum TaskStatus {
|
||||||
notStarted = 0,
|
NotStarted = 0,
|
||||||
inProgress = 1,
|
InProgress = 1,
|
||||||
succeeded = 2,
|
Succeeded = 2,
|
||||||
succeededWithWarning = 3,
|
SucceededWithWarning = 3,
|
||||||
failed = 4,
|
Failed = 4,
|
||||||
canceled = 5
|
Canceled = 5,
|
||||||
|
Canceling = 6
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IConnectionParams {
|
export interface IConnectionParams {
|
||||||
|
|||||||
@@ -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);
|
return this.showDashboardForConnectionManagementInfo(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ export class ConnectionStore {
|
|||||||
return connectionProfile;
|
return connectionProfile;
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +352,7 @@ export class ConnectionStore {
|
|||||||
list.unshift(savedProfile);
|
list.unshift(savedProfile);
|
||||||
|
|
||||||
let newList = list.map(c => {
|
let newList = list.map(c => {
|
||||||
let connectionProfile = c ? c.toIConnectionProfile() : undefined;;
|
let connectionProfile = c ? c.toIConnectionProfile() : undefined;
|
||||||
return connectionProfile;
|
return connectionProfile;
|
||||||
});
|
});
|
||||||
return newList.filter(n => n !== undefined);
|
return newList.filter(n => n !== undefined);
|
||||||
@@ -372,7 +372,7 @@ export class ConnectionStore {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let newList = list.map(c => {
|
let newList = list.map(c => {
|
||||||
let connectionProfile = c ? c.toIConnectionProfile() : undefined;;
|
let connectionProfile = c ? c.toIConnectionProfile() : undefined;
|
||||||
return connectionProfile;
|
return connectionProfile;
|
||||||
});
|
});
|
||||||
return newList.filter(n => n !== undefined);
|
return newList.filter(n => n !== undefined);
|
||||||
|
|||||||
@@ -280,26 +280,23 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
|||||||
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
// only create the provider maps first time the dialog gets called
|
// only create the provider maps first time the dialog gets called
|
||||||
let capabilitiesPromise: Promise<void> = Promise.resolve();
|
|
||||||
if (this._providerTypes.length === 0) {
|
if (this._providerTypes.length === 0) {
|
||||||
entries(this._capabilitiesService.providers).forEach(p => {
|
entries(this._capabilitiesService.providers).forEach(p => {
|
||||||
this._providerTypes.push(p[1].connection.displayName);
|
this._providerTypes.push(p[1].connection.displayName);
|
||||||
this._providerNameToDisplayNameMap[p[0]] = p[1].connection.displayName;
|
this._providerNameToDisplayNameMap[p[0]] = p[1].connection.displayName;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
capabilitiesPromise.then(s => {
|
this.updateModelServerCapabilities(model);
|
||||||
this.updateModelServerCapabilities(model);
|
// If connecting from a query editor set "save connection" to false
|
||||||
// If connecting from a query editor set "save connection" to false
|
if (params && params.input && params.connectionType === ConnectionType.editor) {
|
||||||
if (params && params.input && params.connectionType === ConnectionType.editor) {
|
this._model.saveProfile = false;
|
||||||
this._model.saveProfile = false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
resolve(this.showDialogWithModel().then(() => {
|
resolve(this.showDialogWithModel().then(() => {
|
||||||
if (connectionResult && connectionResult.errorMessage) {
|
if (connectionResult && connectionResult.errorMessage) {
|
||||||
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
|
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}, e => reject(e));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,9 +339,9 @@ export class ConnectionDialogService implements IConnectionDialogService {
|
|||||||
if (!platform.isWindows && types.isString(message) && message.toLowerCase().includes('kerberos') && message.toLowerCase().includes('kinit')) {
|
if (!platform.isWindows && types.isString(message) && message.toLowerCase().includes('kerberos') && message.toLowerCase().includes('kinit')) {
|
||||||
message = [
|
message = [
|
||||||
localize('kerberosErrorStart', "Connection failed due to Kerberos error."),
|
localize('kerberosErrorStart', "Connection failed due to Kerberos error."),
|
||||||
localize('kerberosHelpLink', " Help configuring Kerberos is available at ") + helpLink,
|
localize('kerberosHelpLink', "Help configuring Kerberos is available at {0}", helpLink),
|
||||||
localize('kerberosKinit', " If you have previously connected you may need to re-run kinit.")
|
localize('kerberosKinit', "If you have previously connected you may need to re-run kinit.")
|
||||||
].join('<br/>');
|
].join('\r\n');
|
||||||
actions.push(new Action('Kinit', 'Run kinit', null, true, () => {
|
actions.push(new Action('Kinit', 'Run kinit', null, true, () => {
|
||||||
this._connectionDialog.close();
|
this._connectionDialog.close();
|
||||||
this._clipboardService.writeText('kinit\r');
|
this._clipboardService.writeText('kinit\r');
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ export class ConnectionDialogWidget extends Modal {
|
|||||||
let recentHistoryLabel = localize('recentHistory', 'Recent history');
|
let recentHistoryLabel = localize('recentHistory', 'Recent history');
|
||||||
recentConnectionContainer.div({ class: 'recent-titles-container' }, (container) => {
|
recentConnectionContainer.div({ class: 'recent-titles-container' }, (container) => {
|
||||||
container.div({ class: 'connection-history-label' }, (recentTitle) => {
|
container.div({ class: 'connection-history-label' }, (recentTitle) => {
|
||||||
recentTitle.innerHtml(recentHistoryLabel);
|
recentTitle.text(recentHistoryLabel);
|
||||||
});
|
});
|
||||||
container.div({ class: 'connection-history-actions' }, (actionsContainer) => {
|
container.div({ class: 'connection-history-actions' }, (actionsContainer) => {
|
||||||
this._actionbar = this._register(new ActionBar(actionsContainer.getHTMLElement(), { animated: false }));
|
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) => {
|
this._noRecentConnectionBuilder.div({ class: 'connection-recent-content' }, (noRecentConnectionContainer) => {
|
||||||
let noRecentHistoryLabel = localize('noRecentConnections', 'No recent connection');
|
let noRecentHistoryLabel = localize('noRecentConnections', 'No recent connection');
|
||||||
noRecentConnectionContainer.div({ class: 'no-recent-connections' }, (noRecentTitle) => {
|
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) => {
|
this._noSavedConnectionBuilder.div({ class: 'connection-saved-content' }, (noSavedConnectionContainer) => {
|
||||||
let noSavedConnectionLabel = localize('noSavedConnections', 'No saved connection');
|
let noSavedConnectionLabel = localize('noSavedConnections', 'No saved connection');
|
||||||
noSavedConnectionContainer.div({ class: 'no-saved-connections' }, (titleContainer) => {
|
noSavedConnectionContainer.div({ class: 'no-saved-connections' }, (titleContainer) => {
|
||||||
titleContainer.innerHtml(noSavedConnectionLabel);
|
titleContainer.text(noSavedConnectionLabel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,22 +32,10 @@ export abstract class DashboardTab extends TabChild implements OnDestroy {
|
|||||||
public enableEdit(): void {
|
public enableEdit(): void {
|
||||||
// no op
|
// no op
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toDispose: IDisposable[] = [];
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose(): void {
|
|
||||||
this._toDispose = dispose(this._toDispose);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _register<T extends IDisposable>(t: T): T {
|
|
||||||
this._toDispose.push(t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.dispose();
|
this.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export class DashboardErrorContainer extends DashboardTab implements AfterViewIn
|
|||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
let errorMessage = this._errorMessageContainer.nativeElement as HTMLElement;
|
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 {
|
public get id(): string {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { DashboardModule } from './dashboard.module';
|
|||||||
import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
|
import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
|
||||||
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
|
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
|
||||||
import { DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component';
|
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 { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
|
||||||
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
|
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
|
||||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
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.
|
* To be called when the container of this editor changes size.
|
||||||
*/
|
*/
|
||||||
public layout(dimension: DOM.Dimension): void {
|
public layout(dimension: DOM.Dimension): void {
|
||||||
|
this._dashboardService.layout(dimension);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setInput(input: DashboardInput, options: EditorOptions): TPromise<void> {
|
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;
|
let serverInfo = this._connMan.getConnectionInfo(this.input.uri).serverInfo;
|
||||||
this._dashboardService.changeToDashboard({ profile, serverInfo });
|
this._dashboardService.changeToDashboard({ profile, serverInfo });
|
||||||
let scopedContextService = this._contextKeyService.createScoped(input.container);
|
let scopedContextService = this._contextKeyService.createScoped(input.container);
|
||||||
let connectionContextKey = new ConnectionContextkey(scopedContextService);
|
let connectionContextKey = new ConnectionContextKey(scopedContextService);
|
||||||
connectionContextKey.set(input.connectionProfile);
|
connectionContextKey.set(input.connectionProfile);
|
||||||
|
|
||||||
let params: IDashboardComponentParams = {
|
let params: IDashboardComponentParams = {
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ export class NewDashboardTabDialog extends Modal {
|
|||||||
this._noExtensionViewContainer = DOM.$('.no-extension-view');
|
this._noExtensionViewContainer = DOM.$('.no-extension-view');
|
||||||
let noExtensionTitle = DOM.append(this._noExtensionViewContainer, DOM.$('.no-extensionTab-label'));
|
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.');
|
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);
|
DOM.append(container, this._noExtensionViewContainer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
|
||||||
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
|
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
|
||||||
import * as Constants from 'sql/parts/connection/common/constants';
|
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 { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
||||||
import { IScriptingService } from 'sql/services/scripting/scriptingService';
|
import { IScriptingService } from 'sql/services/scripting/scriptingService';
|
||||||
|
|
||||||
|
|||||||
@@ -179,8 +179,8 @@ export class RestoreDialogController implements IRestoreDialogController {
|
|||||||
private isSuccessfulRestore(response: TaskNode): boolean {
|
private isSuccessfulRestore(response: TaskNode): boolean {
|
||||||
return (response.taskName === this._restoreTaskName &&
|
return (response.taskName === this._restoreTaskName &&
|
||||||
response.message === this._restoreCompleted &&
|
response.message === this._restoreCompleted &&
|
||||||
(response.status === TaskStatus.succeeded ||
|
(response.status === TaskStatus.Succeeded ||
|
||||||
response.status === TaskStatus.succeededWithWarning) &&
|
response.status === TaskStatus.SucceededWithWarning) &&
|
||||||
(response.taskExecutionMode === TaskExecutionMode.execute ||
|
(response.taskExecutionMode === TaskExecutionMode.execute ||
|
||||||
response.taskExecutionMode === TaskExecutionMode.executeAndScript));
|
response.taskExecutionMode === TaskExecutionMode.executeAndScript));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ export class RestoreDialog extends Modal {
|
|||||||
|
|
||||||
destinationContainer.div({ class: 'dialog-input-section' }, (inputContainer) => {
|
destinationContainer.div({ class: 'dialog-input-section' }, (inputContainer) => {
|
||||||
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
|
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
|
||||||
labelContainer.innerHtml(LocalizedStrings.TARGETDATABASE);
|
labelContainer.text(LocalizedStrings.TARGETDATABASE);
|
||||||
});
|
});
|
||||||
|
|
||||||
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
|
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
|
||||||
@@ -471,7 +471,7 @@ export class RestoreDialog extends Modal {
|
|||||||
className += ' header';
|
className += ' header';
|
||||||
}
|
}
|
||||||
container.div({ class: className }, (labelContainer) => {
|
container.div({ class: className }, (labelContainer) => {
|
||||||
labelContainer.innerHtml(content);
|
labelContainer.text(content);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,7 +535,7 @@ export class RestoreDialog extends Modal {
|
|||||||
let selectBox: SelectBox;
|
let selectBox: SelectBox;
|
||||||
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
|
container.div({ class: 'dialog-input-section' }, (inputContainer) => {
|
||||||
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
|
inputContainer.div({ class: 'dialog-label' }, (labelContainer) => {
|
||||||
labelContainer.innerHtml(label);
|
labelContainer.text(label);
|
||||||
});
|
});
|
||||||
|
|
||||||
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
|
inputContainer.div({ class: 'dialog-input' }, (inputCellContainer) => {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { ITree } from 'vs/base/parts/tree/browser/tree';
|
|||||||
/**
|
/**
|
||||||
* Implements tree view for file browser
|
* Implements tree view for file browser
|
||||||
*/
|
*/
|
||||||
export class FileBrowserTreeView {
|
export class FileBrowserTreeView implements IDisposable {
|
||||||
private _tree: ITree;
|
private _tree: ITree;
|
||||||
private _toDispose: IDisposable[] = [];
|
private _toDispose: IDisposable[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* 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 {
|
export class DBCellValue {
|
||||||
displayValue: string;
|
displayValue: string;
|
||||||
@@ -25,7 +25,7 @@ export function hyperLinkFormatter(row: number, cell: any, value: any, columnDef
|
|||||||
valueToDisplay = 'NULL';
|
valueToDisplay = 'NULL';
|
||||||
if (!value.isNull) {
|
if (!value.isNull) {
|
||||||
cellClasses += ' xmlLink';
|
cellClasses += ' xmlLink';
|
||||||
valueToDisplay = Strings.escape(value.displayValue);
|
valueToDisplay = escape(value.displayValue);
|
||||||
return `<a class="${cellClasses}" href="#" >${valueToDisplay}</a>`;
|
return `<a class="${cellClasses}" href="#" >${valueToDisplay}</a>`;
|
||||||
} else {
|
} else {
|
||||||
cellClasses += ' missing-value';
|
cellClasses += ' missing-value';
|
||||||
@@ -44,12 +44,12 @@ export function textFormatter(row: number, cell: any, value: any, columnDef: any
|
|||||||
if (DBCellValue.isDBCellValue(value)) {
|
if (DBCellValue.isDBCellValue(value)) {
|
||||||
valueToDisplay = 'NULL';
|
valueToDisplay = 'NULL';
|
||||||
if (!value.isNull) {
|
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 {
|
} else {
|
||||||
cellClasses += ' missing-value';
|
cellClasses += ' missing-value';
|
||||||
}
|
}
|
||||||
} else if (typeof value === 'string') {
|
} else if (typeof value === 'string') {
|
||||||
valueToDisplay = Strings.escape(value);
|
valueToDisplay = escape(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<span title="${valueToDisplay}" class="${cellClasses}">${valueToDisplay}</span>`;
|
return `<span title="${valueToDisplay}" class="${cellClasses}">${valueToDisplay}</span>`;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { error } from 'sql/base/common/log';
|
|||||||
import { clone, mixin } from 'sql/base/common/objects';
|
import { clone, mixin } from 'sql/base/common/objects';
|
||||||
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
||||||
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
|
||||||
|
import { escape } from 'sql/base/common/strings';
|
||||||
|
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
import Severity from 'vs/base/common/severity';
|
import Severity from 'vs/base/common/severity';
|
||||||
@@ -197,7 +198,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
|||||||
rowIndex++;
|
rowIndex++;
|
||||||
return {
|
return {
|
||||||
values: row.cells.map(c => {
|
values: row.cells.map(c => {
|
||||||
return mixin({ ariaLabel: c.displayValue }, c);
|
return mixin({ ariaLabel: escape(c.displayValue) }, c);
|
||||||
}), row: row.id
|
}), row: row.id
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -367,11 +368,12 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
|
|||||||
columnDefinitions: resultSet.columnInfo.map((c, i) => {
|
columnDefinitions: resultSet.columnInfo.map((c, i) => {
|
||||||
let isLinked = c.isXml || c.isJson;
|
let isLinked = c.isXml || c.isJson;
|
||||||
let linkType = c.isXml ? 'xml' : 'json';
|
let linkType = c.isXml ? 'xml' : 'json';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: i.toString(),
|
id: i.toString(),
|
||||||
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
|
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
|
||||||
? 'XML Showplan'
|
? 'XML Showplan'
|
||||||
: c.columnName,
|
: escape(c.columnName),
|
||||||
type: self.stringToFieldType('string'),
|
type: self.stringToFieldType('string'),
|
||||||
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
|
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
|
||||||
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined,
|
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined,
|
||||||
|
|||||||
@@ -5,13 +5,12 @@
|
|||||||
import 'vs/css!sql/parts/grid/views/query/chartViewer';
|
import 'vs/css!sql/parts/grid/views/query/chartViewer';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Component, Inject, ViewContainerRef, forwardRef, OnInit,
|
Component, Inject, forwardRef, OnInit, ComponentFactoryResolver, ViewChild,
|
||||||
ComponentFactoryResolver, ViewChild, OnDestroy, Input, ElementRef, ChangeDetectorRef
|
OnDestroy, Input, ElementRef, ChangeDetectorRef
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { NgGridItemConfig } from 'angular2-grid';
|
import { NgGridItemConfig } from 'angular2-grid';
|
||||||
|
|
||||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
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 { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
|
||||||
import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
|
import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
|
||||||
import { IInsightData, IInsightsView, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/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';
|
} from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||||
|
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
|
||||||
import Severity from 'vs/base/common/severity';
|
import Severity from 'vs/base/common/severity';
|
||||||
import URI from 'vs/base/common/uri';
|
import URI from 'vs/base/common/uri';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
@@ -87,21 +85,17 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
|
|||||||
private _saveAction: SaveImageAction;
|
private _saveAction: SaveImageAction;
|
||||||
private _chartConfig: ILineConfig;
|
private _chartConfig: ILineConfig;
|
||||||
private _disposables: Array<IDisposable> = [];
|
private _disposables: Array<IDisposable> = [];
|
||||||
private _dataSet: IGridDataSet;
|
|
||||||
private _executeResult: IInsightData;
|
private _executeResult: IInsightData;
|
||||||
private _chartComponent: ChartInsight;
|
private _chartComponent: ChartInsight;
|
||||||
|
|
||||||
private localizedStrings = LocalizedStrings;
|
protected localizedStrings = LocalizedStrings;
|
||||||
private insightRegistry = insightRegistry;
|
protected insightRegistry = insightRegistry;
|
||||||
|
|
||||||
@ViewChild(ComponentHostDirective) private componentHost: ComponentHostDirective;
|
@ViewChild(ComponentHostDirective) private componentHost: ComponentHostDirective;
|
||||||
@ViewChild('taskbarContainer', { read: ElementRef }) private taskbarContainer;
|
@ViewChild('taskbarContainer', { read: ElementRef }) private taskbarContainer;
|
||||||
@ViewChild('chartTypesContainer', { read: ElementRef }) private chartTypesElement;
|
|
||||||
@ViewChild('legendContainer', { read: ElementRef }) private legendElement;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver,
|
@Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver,
|
||||||
@Inject(forwardRef(() => ViewContainerRef)) private _viewContainerRef: ViewContainerRef,
|
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||||
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
|
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
|
||||||
@Inject(INotificationService) private notificationService: INotificationService,
|
@Inject(INotificationService) private notificationService: INotificationService,
|
||||||
@@ -123,15 +117,25 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
|
|||||||
}
|
}
|
||||||
|
|
||||||
private setDefaultChartConfig() {
|
private setDefaultChartConfig() {
|
||||||
this._chartConfig = <ILineConfig>{
|
let defaultChart = this.getDefaultChartType();
|
||||||
dataDirection: 'vertical',
|
if (defaultChart === 'timeSeries') {
|
||||||
dataType: 'number',
|
this._chartConfig = <ILineConfig>{
|
||||||
legendPosition: 'none',
|
dataDirection: 'vertical',
|
||||||
labelFirstColumn: false
|
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;
|
let defaultChartType = Constants.chartTypeHorizontalBar;
|
||||||
if (this.configurationService) {
|
if (this.configurationService) {
|
||||||
let chartSettings = WorkbenchUtils.getSqlConfigSection(this.configurationService, 'chart');
|
let chartSettings = WorkbenchUtils.getSqlConfigSection(this.configurationService, 'chart');
|
||||||
@@ -300,22 +304,18 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
|
|||||||
return undefined;
|
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');
|
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';
|
return this.dataDirection === 'horizontal' && this.dataType !== 'point';
|
||||||
}
|
}
|
||||||
|
|
||||||
private get showColumnsAsLabels(): boolean {
|
protected get showColumnsAsLabels(): boolean {
|
||||||
return this.dataDirection === 'vertical' && this.dataType !== 'point';
|
return this.dataDirection === 'vertical' && this.dataType !== 'point';
|
||||||
}
|
}
|
||||||
|
|
||||||
private get showDataType(): boolean {
|
|
||||||
return this.chartTypesSelectBox.value === 'line';
|
|
||||||
}
|
|
||||||
|
|
||||||
public get dataDirection(): DataDirection {
|
public get dataDirection(): DataDirection {
|
||||||
return this._chartConfig.dataDirection;
|
return this._chartConfig.dataDirection;
|
||||||
}
|
}
|
||||||
@@ -326,7 +326,6 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
|
|||||||
|
|
||||||
@Input() set dataSet(dataSet: IGridDataSet) {
|
@Input() set dataSet(dataSet: IGridDataSet) {
|
||||||
// Setup the execute result
|
// Setup the execute result
|
||||||
this._dataSet = dataSet;
|
|
||||||
this._executeResult = <IInsightData>{};
|
this._executeResult = <IInsightData>{};
|
||||||
this._executeResult.columns = dataSet.columnDefinitions.map(def => def.name);
|
this._executeResult.columns = dataSet.columnDefinitions.map(def => def.name);
|
||||||
this._executeResult.rows = dataSet.dataRows.getRange(0, dataSet.dataRows.getLength()).map(gridRow => {
|
this._executeResult.rows = dataSet.dataRows.getRange(0, dataSet.dataRows.getLength()).map(gridRow => {
|
||||||
@@ -356,4 +355,4 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
|
|||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this._disposables.forEach(i => i.dispose());
|
this._disposables.forEach(i => i.dispose());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,9 @@ import { error } from 'sql/base/common/log';
|
|||||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||||
import { clone, mixin } from 'sql/base/common/objects';
|
import { clone, mixin } from 'sql/base/common/objects';
|
||||||
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
|
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 * as DOM from 'vs/base/browser/dom';
|
||||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
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
|
// create a function alias to use inside query.component
|
||||||
// tslint:disable-next-line:no-unused-variable
|
// tslint:disable-next-line:no-unused-variable
|
||||||
private stringsFormat: any = strings.format;
|
private stringsFormat: any = format;
|
||||||
|
|
||||||
// tslint:disable-next-line:no-unused-variable
|
// tslint:disable-next-line:no-unused-variable
|
||||||
private dataIcons: IGridIcon[] = [
|
private dataIcons: IGridIcon[] = [
|
||||||
@@ -302,7 +303,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
|
|||||||
// Push row values onto end of gridData for slickgrid
|
// Push row values onto end of gridData for slickgrid
|
||||||
gridData.push({
|
gridData.push({
|
||||||
values: rows.rows[row].map(c => {
|
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) => {
|
columnDefinitions: resultSet.columnInfo.map((c, i) => {
|
||||||
let isLinked = c.isXml || c.isJson;
|
let isLinked = c.isXml || c.isJson;
|
||||||
let linkType = c.isXml ? 'xml' : 'json';
|
let linkType = c.isXml ? 'xml' : 'json';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: i.toString(),
|
id: i.toString(),
|
||||||
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
|
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
|
||||||
? 'XML Showplan'
|
? 'XML Showplan'
|
||||||
: c.columnName,
|
: escape(c.columnName),
|
||||||
type: self.stringToFieldType('string'),
|
type: self.stringToFieldType('string'),
|
||||||
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
|
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
|
||||||
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined
|
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined
|
||||||
|
|||||||
@@ -7,17 +7,11 @@ import 'vs/css!../common/media/jobs';
|
|||||||
import 'sql/parts/dashboard/common/dashboardPanelStyles';
|
import 'sql/parts/dashboard/common/dashboardPanelStyles';
|
||||||
|
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable } from '@angular/core';
|
import { Component, Inject, forwardRef, ChangeDetectorRef, ViewChild, Injectable } from '@angular/core';
|
||||||
import * as Utils from 'sql/parts/connection/common/utils';
|
import { AgentJobInfo } from 'sqlops';
|
||||||
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 { PanelComponent, IPanelOptions, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component';
|
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';
|
export const DASHBOARD_SELECTOR: string = 'agentview-component';
|
||||||
@@ -31,12 +25,6 @@ export class AgentViewComponent {
|
|||||||
|
|
||||||
@ViewChild(PanelComponent) private _panel: PanelComponent;
|
@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 _showHistory: boolean = false;
|
||||||
private _jobId: string = null;
|
private _jobId: string = null;
|
||||||
private _agentJobInfo: AgentJobInfo = null;
|
private _agentJobInfo: AgentJobInfo = null;
|
||||||
@@ -48,6 +36,11 @@ export class AgentViewComponent {
|
|||||||
public proxiesIconClass: string = 'proxiesview-icon';
|
public proxiesIconClass: string = 'proxiesview-icon';
|
||||||
public operatorsIconClass: string = 'operatorsview-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
|
// tslint:disable-next-line:no-unused-variable
|
||||||
private readonly panelOpt: IPanelOptions = {
|
private readonly panelOpt: IPanelOptions = {
|
||||||
showTabsWhenOne: true,
|
showTabsWhenOne: true,
|
||||||
@@ -56,8 +49,16 @@ export class AgentViewComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
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>();
|
this._expanded = new Map<string, string>();
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
jobManagementService.onDidChange((args) => {
|
||||||
|
self.refresh = true;
|
||||||
|
self._cd.detectChanges();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { JobCacheObject } from './jobManagementService';
|
import { JobCacheObject } from './jobManagementService';
|
||||||
|
import { Event } from 'vs/base/common/event';
|
||||||
|
|
||||||
export const SERVICE_ID = 'jobManagementService';
|
export const SERVICE_ID = 'jobManagementService';
|
||||||
|
|
||||||
@@ -15,22 +16,27 @@ export const IJobManagementService = createDecorator<IJobManagementService>(SERV
|
|||||||
|
|
||||||
export interface IJobManagementService {
|
export interface IJobManagementService {
|
||||||
_serviceBrand: any;
|
_serviceBrand: any;
|
||||||
|
onDidChange: Event<void>;
|
||||||
|
|
||||||
registerProvider(providerId: string, provider: sqlops.AgentServicesProvider): void;
|
registerProvider(providerId: string, provider: sqlops.AgentServicesProvider): void;
|
||||||
|
fireOnDidChange(): void;
|
||||||
|
|
||||||
getJobs(connectionUri: string): Thenable<sqlops.AgentJobsResult>;
|
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>;
|
getAlerts(connectionUri: string): Thenable<sqlops.AgentAlertsResult>;
|
||||||
|
deleteAlert(connectionUri: string, alert: sqlops.AgentAlertInfo): Thenable<sqlops.ResultStatus>;
|
||||||
|
|
||||||
getOperators(connectionUri: string): Thenable<sqlops.AgentOperatorsResult>;
|
getOperators(connectionUri: string): Thenable<sqlops.AgentOperatorsResult>;
|
||||||
|
deleteOperator(connectionUri: string, operator: sqlops.AgentOperatorInfo): Thenable<sqlops.ResultStatus>;
|
||||||
|
|
||||||
getProxies(connectionUri: string): Thenable<sqlops.AgentProxiesResult>;
|
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>;
|
jobAction(connectionUri: string, jobName: string, action: string): Thenable<sqlops.ResultStatus>;
|
||||||
|
|
||||||
addToCache(server: string, cache: JobCacheObject);
|
addToCache(server: string, cache: JobCacheObject);
|
||||||
|
|
||||||
jobCacheObjectMap: { [server: string]: JobCacheObject; };
|
jobCacheObjectMap: { [server: string]: JobCacheObject; };
|
||||||
}
|
}
|
||||||
463
src/sql/parts/jobManagement/common/jobActions.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,11 +10,14 @@ import * as sqlops from 'sqlops';
|
|||||||
import { TPromise } from 'vs/base/common/winjs.base';
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
|
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
|
||||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||||
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
export class JobManagementService implements IJobManagementService {
|
export class JobManagementService implements IJobManagementService {
|
||||||
_serviceBrand: any;
|
_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 _providers: { [handle: string]: sqlops.AgentServicesProvider; } = Object.create(null);
|
||||||
private _jobCacheObject : {[server: string]: JobCacheObject; } = {};
|
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> {
|
public getJobs(connectionUri: string): Thenable<sqlops.AgentJobsResult> {
|
||||||
return this._runAction(connectionUri, (runner) => {
|
return this._runAction(connectionUri, (runner) => {
|
||||||
return runner.getJobs(connectionUri);
|
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> {
|
public getAlerts(connectionUri: string): Thenable<sqlops.AgentAlertsResult> {
|
||||||
return this._runAction(connectionUri, (runner) => {
|
return this._runAction(connectionUri, (runner) => {
|
||||||
return runner.getAlerts(connectionUri);
|
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> {
|
public getOperators(connectionUri: string): Thenable<sqlops.AgentOperatorsResult> {
|
||||||
return this._runAction(connectionUri, (runner) => {
|
return this._runAction(connectionUri, (runner) => {
|
||||||
return runner.getOperators(connectionUri);
|
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> {
|
public getProxies(connectionUri: string): Thenable<sqlops.AgentProxiesResult> {
|
||||||
return this._runAction(connectionUri, (runner) => {
|
return this._runAction(connectionUri, (runner) => {
|
||||||
return runner.getProxies(connectionUri);
|
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> {
|
public getJobHistory(connectionUri: string, jobID: string): Thenable<sqlops.AgentJobHistoryResult> {
|
||||||
return this._runAction(connectionUri, (runner) => {
|
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> {
|
private _runAction<T>(uri: string, action: (handler: sqlops.AgentServicesProvider) => Thenable<T>): Thenable<T> {
|
||||||
let providerId: string = this._connectionService.getProviderIdFromUri(uri);
|
let providerId: string = this._connectionService.getProviderIdFromUri(uri);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
|
|
||||||
export class AgentJobUtilities {
|
export class JobManagementUtilities {
|
||||||
|
|
||||||
public static startIconClass: string = 'action-label icon runJobIcon';
|
public static startIconClass: string = 'action-label icon runJobIcon';
|
||||||
public static stopIconClass: string = 'action-label icon stopJobIcon';
|
public static stopIconClass: string = 'action-label icon stopJobIcon';
|
||||||
@@ -63,8 +63,8 @@ export class AgentJobUtilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static getActionIconClassName(startIcon: HTMLElement, stopIcon: HTMLElement, executionStatus: number) {
|
public static getActionIconClassName(startIcon: HTMLElement, stopIcon: HTMLElement, executionStatus: number) {
|
||||||
this.setRunnable(startIcon, AgentJobUtilities.startIconClass.length);
|
this.setRunnable(startIcon, JobManagementUtilities.startIconClass.length);
|
||||||
this.setRunnable(stopIcon, AgentJobUtilities.stopIconClass.length);
|
this.setRunnable(stopIcon, JobManagementUtilities.stopIconClass.length);
|
||||||
switch (executionStatus) {
|
switch (executionStatus) {
|
||||||
case(1): // executing
|
case(1): // executing
|
||||||
startIcon.className += ' non-runnable';
|
startIcon.className += ' non-runnable';
|
||||||
@@ -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 |
@@ -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 |
@@ -22,7 +22,7 @@ jobhistory-component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.job-heading-container {
|
.job-heading-container {
|
||||||
height: 49px;
|
height: 50px;
|
||||||
border-bottom: 3px solid #f4f4f4;
|
border-bottom: 3px solid #f4f4f4;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
}
|
}
|
||||||
@@ -32,23 +32,11 @@ jobhistory-component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.jobview-grid {
|
.jobview-grid {
|
||||||
height: 94.7%;
|
height: calc(100% - 75px);
|
||||||
width : 100%;
|
width : 100%;
|
||||||
display: block;
|
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 {
|
.vs-dark #agentViewDiv .slick-header-column {
|
||||||
background: #333333 !important;
|
background: #333333 !important;
|
||||||
}
|
}
|
||||||
@@ -58,7 +46,6 @@ jobhistory-component {
|
|||||||
background: white !important;
|
background: white !important;
|
||||||
border: 0px !important;
|
border: 0px !important;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: larger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vs-dark #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
.vs-dark #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
||||||
@@ -164,7 +151,7 @@ jobhistory-component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#jobsDiv .preload {
|
#jobsDiv .preload {
|
||||||
font-size: 18px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#jobsDiv .dynamic-cell-detail > :first-child {
|
#jobsDiv .dynamic-cell-detail > :first-child {
|
||||||
@@ -299,19 +286,138 @@ table.jobprevruns > tbody {
|
|||||||
|
|
||||||
|
|
||||||
#alertsDiv .jobalertsview-grid {
|
#alertsDiv .jobalertsview-grid {
|
||||||
height: 94.7%;
|
height: calc(100% - 75px);
|
||||||
width : 100%;
|
width : 100%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#operatorsDiv .joboperatorsview-grid {
|
#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%;
|
width : 100%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#proxiesDiv .jobproxiesview-grid {
|
.vs .action-label.icon.refreshIcon {
|
||||||
height: 94.7%;
|
background-image: url('refresh.svg');
|
||||||
width : 100%;
|
}
|
||||||
display: block;
|
|
||||||
|
.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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
1
src/sql/parts/jobManagement/common/media/refresh.svg
Normal 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 |
@@ -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 |
@@ -9,9 +9,7 @@
|
|||||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Alerts Available</h1>
|
<h1 class="job-heading" *ngIf="_isCloud === true">No Alerts Available</h1>
|
||||||
<div class="icon in-progress" *ngIf="_showProgressWheel === true"></div>
|
<div class="icon in-progress" *ngIf="_showProgressWheel === true"></div>
|
||||||
</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>
|
||||||
|
|||||||
@@ -12,26 +12,34 @@ import 'vs/css!../common/media/jobs';
|
|||||||
import 'vs/css!sql/media/icons/common-icons';
|
import 'vs/css!sql/media/icons/common-icons';
|
||||||
import 'vs/css!sql/base/browser/ui/table/media/table';
|
import 'vs/css!sql/base/browser/ui/table/media/table';
|
||||||
|
|
||||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core';
|
import * as dom from 'vs/base/browser/dom';
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
import * as nls from 'vs/nls';
|
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 { Table } from 'sql/base/browser/ui/table/table';
|
||||||
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
||||||
import * as dom from 'vs/base/browser/dom';
|
import { IJobManagementService } from 'sql/parts/jobManagement/common/interfaces';
|
||||||
import { IJobManagementService } from '../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 { 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 { 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 VIEW_SELECTOR: string = 'jobalertsview-component';
|
||||||
export const ROW_HEIGHT: number = 45;
|
export const ROW_HEIGHT: number = 30;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: VIEW_SELECTOR,
|
selector: VIEW_SELECTOR,
|
||||||
templateUrl: decodeURI(require.toUrl('./alertsView.component.html')),
|
templateUrl: decodeURI(require.toUrl('./alertsView.component.html')),
|
||||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => AlertsViewComponent) }],
|
providers: [{ provide: TabChild, useExisting: forwardRef(() => AlertsViewComponent) }],
|
||||||
})
|
})
|
||||||
export class AlertsViewComponent implements AfterContentChecked {
|
export class AlertsViewComponent extends JobManagementView implements OnInit {
|
||||||
|
|
||||||
private columns: Array<Slick.Column<any>> = [
|
private columns: Array<Slick.Column<any>> = [
|
||||||
{ name: nls.localize('jobAlertColumns.name', 'Name'), field: 'name', width: 200, id: 'name' },
|
{ 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> = {
|
private options: Slick.GridOptions<any> = {
|
||||||
syncColumnCellResize: true,
|
syncColumnCellResize: true,
|
||||||
enableColumnReorder: false,
|
enableColumnReorder: false,
|
||||||
rowHeight: 45,
|
rowHeight: ROW_HEIGHT,
|
||||||
enableCellNavigation: true,
|
enableCellNavigation: true,
|
||||||
editable: false
|
editable: false
|
||||||
};
|
};
|
||||||
|
|
||||||
private dataView: any;
|
private dataView: any;
|
||||||
|
private _isCloud: boolean;
|
||||||
|
|
||||||
@ViewChild('jobalertsgrid') _gridEl: ElementRef;
|
@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");
|
public alerts: sqlops.AgentAlertInfo[];
|
||||||
private RefreshText: string = nls.localize('jobAlertToolbar-Refresh', "Refresh");
|
public contextAction = NewAlertAction;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
|
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
||||||
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
|
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
|
||||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||||
@Inject(IThemeService) private _themeService: IThemeService,
|
@Inject(ICommandService) private _commandService: ICommandService,
|
||||||
@Inject(ICommandService) private _commandService: ICommandService
|
@Inject(IInstantiationService) instantiationService: IInstantiationService,
|
||||||
) {
|
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||||
this._isCloud = this._dashboardService.connectionManagementService.connectionInfo.serverInfo.isCloud;
|
@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() {
|
public layout() {
|
||||||
this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement)));
|
let height = dom.getContentHeight(this._gridEl.nativeElement) - 10;
|
||||||
}
|
if (height < 0) {
|
||||||
|
height = 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._table.layout(new dom.Dimension(
|
||||||
|
dom.getContentWidth(this._gridEl.nativeElement),
|
||||||
|
height));
|
||||||
}
|
}
|
||||||
|
|
||||||
onFirstVisible(cached?: boolean) {
|
onFirstVisible() {
|
||||||
let self = this;
|
let self = this;
|
||||||
let columns = this.columns.map((column) => {
|
let columns = this.columns.map((column) => {
|
||||||
column.rerenderOnResize = true;
|
column.rerenderOnResize = true;
|
||||||
return column;
|
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.dataView = new Slick.Data.DataView();
|
||||||
|
|
||||||
$(this._gridEl.nativeElement).empty();
|
$(this._gridEl.nativeElement).empty();
|
||||||
|
$(this.actionBarContainer.nativeElement).empty();
|
||||||
|
this.initActionBar();
|
||||||
this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options);
|
this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options);
|
||||||
this._table.grid.setData(this.dataView, true);
|
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) => {
|
this._jobManagementService.getAlerts(ownerUri).then((result) => {
|
||||||
if (result && result.alerts) {
|
if (result && result.alerts) {
|
||||||
self.alerts = result.alerts;
|
self.alerts = result.alerts;
|
||||||
self.onAlertsAvailable(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.dataView.endUpdate();
|
||||||
this._table.autosizeColumns();
|
this._table.autosizeColumns();
|
||||||
this._table.resizeCanvas();
|
this._table.resizeCanvas();
|
||||||
|
|
||||||
this._showProgressWheel = false;
|
|
||||||
this._cd.detectChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private openCreateJobDialog() {
|
protected getTableActions(): TPromise<IAction[]> {
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
let actions: IAction[] = [];
|
||||||
this._commandService.executeCommand('agent.openCreateAlertDialog', ownerUri);
|
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() {
|
private refreshJobs() {
|
||||||
|
|||||||
@@ -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));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -154,8 +154,9 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</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>
|
<h3 *ngIf="showSteps === false">No Steps Available</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -5,46 +5,51 @@
|
|||||||
|
|
||||||
import 'vs/css!./jobHistory';
|
import 'vs/css!./jobHistory';
|
||||||
import 'vs/css!sql/media/icons/common-icons';
|
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 * as sqlops from 'sqlops';
|
||||||
import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
|
import * as dom from 'vs/base/browser/dom';
|
||||||
import { RunJobAction, StopJobAction, NewStepAction } from 'sql/parts/jobManagement/views/jobActions';
|
import { OnInit, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
||||||
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
|
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||||
import { AgentJobUtilities } from '../common/agentJobUtilities';
|
|
||||||
import { IJobManagementService } from '../common/interfaces';
|
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
|
||||||
import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component';
|
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,
|
import { JobHistoryController, JobHistoryDataSource,
|
||||||
JobHistoryRenderer, JobHistoryFilter, JobHistoryModel, JobHistoryRow } from 'sql/parts/jobManagement/views/jobHistoryTree';
|
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 { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
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';
|
export const DASHBOARD_SELECTOR: string = 'jobhistory-component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: DASHBOARD_SELECTOR,
|
selector: DASHBOARD_SELECTOR,
|
||||||
templateUrl: decodeURI(require.toUrl('./jobHistory.component.html')),
|
templateUrl: decodeURI(require.toUrl('./jobHistory.component.html')),
|
||||||
|
providers: [{ provide: TabChild, useExisting: forwardRef(() => JobHistoryComponent) }],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class JobHistoryComponent extends Disposable implements OnInit {
|
export class JobHistoryComponent extends JobManagementView implements OnInit {
|
||||||
|
|
||||||
private _tree: Tree;
|
private _tree: Tree;
|
||||||
private _treeController: JobHistoryController;
|
private _treeController: JobHistoryController;
|
||||||
private _treeDataSource: JobHistoryDataSource;
|
private _treeDataSource: JobHistoryDataSource;
|
||||||
private _treeRenderer: JobHistoryRenderer;
|
private _treeRenderer: JobHistoryRenderer;
|
||||||
private _treeFilter: JobHistoryFilter;
|
private _treeFilter: JobHistoryFilter;
|
||||||
private _actionBar: Taskbar;
|
|
||||||
|
|
||||||
@ViewChild('table') private _tableContainer: ElementRef;
|
@ViewChild('table') private _tableContainer: ElementRef;
|
||||||
@ViewChild('actionbarContainer') private _actionbarContainer: ElementRef;
|
@ViewChild('jobsteps') private _jobStepsView: ElementRef;
|
||||||
|
|
||||||
@Input() public agentJobInfo: sqlops.AgentJobInfo = undefined;
|
@Input() public agentJobInfo: sqlops.AgentJobInfo = undefined;
|
||||||
@Input() public agentJobHistories: sqlops.AgentJobHistoryInfo[] = undefined;
|
@Input() public agentJobHistories: sqlops.AgentJobHistoryInfo[] = undefined;
|
||||||
@@ -66,21 +71,23 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||||
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
|
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||||
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
|
@Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent,
|
||||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||||
@Inject(INotificationService) private _notificationService: INotificationService,
|
@Inject(INotificationService) private _notificationService: INotificationService,
|
||||||
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
|
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
|
||||||
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
|
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
|
||||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService
|
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||||
|
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||||
|
@Inject(IDashboardService) dashboardService: IDashboardService
|
||||||
) {
|
) {
|
||||||
super();
|
super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService);
|
||||||
this._treeController = new JobHistoryController();
|
this._treeController = new JobHistoryController();
|
||||||
this._treeDataSource = new JobHistoryDataSource();
|
this._treeDataSource = new JobHistoryDataSource();
|
||||||
this._treeRenderer = new JobHistoryRenderer();
|
this._treeRenderer = new JobHistoryRenderer();
|
||||||
this._treeFilter = new JobHistoryFilter();
|
this._treeFilter = new JobHistoryFilter();
|
||||||
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
||||||
this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
this._serverName = commonService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||||
let jobCache = jobCacheObjectMap[this._serverName];
|
let jobCache = jobCacheObjectMap[this._serverName];
|
||||||
if (jobCache) {
|
if (jobCache) {
|
||||||
this._jobCacheObject = jobCache;
|
this._jobCacheObject = jobCache;
|
||||||
@@ -92,7 +99,11 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
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;
|
const self = this;
|
||||||
this._treeController.onClick = (tree, element, event, origin = 'mouse') => {
|
this._treeController.onClick = (tree, element, event, origin = 'mouse') => {
|
||||||
const payload = { origin: origin };
|
const payload = { origin: origin };
|
||||||
@@ -129,56 +140,13 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
}, {verticalScrollMode: ScrollbarVisibility.Visible});
|
}, {verticalScrollMode: ScrollbarVisibility.Visible});
|
||||||
this._register(attachListStyler(this._tree, this.themeService));
|
this._register(attachListStyler(this._tree, this.themeService));
|
||||||
this._tree.layout(JobHistoryComponent.INITIAL_TREE_HEIGHT);
|
this._tree.layout(JobHistoryComponent.INITIAL_TREE_HEIGHT);
|
||||||
this._initActionBar();
|
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
private loadHistory() {
|
||||||
const self = this;
|
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) => {
|
this._jobManagementService.getJobHistory(ownerUri, this._agentViewComponent.jobId).then((result) => {
|
||||||
if (result && result.jobs) {
|
if (result && result.jobs) {
|
||||||
if (result.jobs.length > 0) {
|
if (result.jobs.length > 0) {
|
||||||
@@ -206,11 +174,13 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
if (self.agentJobHistoryInfo) {
|
if (self.agentJobHistoryInfo) {
|
||||||
self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate);
|
self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate);
|
||||||
if (self.agentJobHistoryInfo.steps) {
|
if (self.agentJobHistoryInfo.steps) {
|
||||||
|
let jobStepStatus = this.didJobFail(self.agentJobHistoryInfo);
|
||||||
self._stepRows = self.agentJobHistoryInfo.steps.map(step => {
|
self._stepRows = self.agentJobHistoryInfo.steps.map(step => {
|
||||||
let stepViewRow = new JobStepsViewRow();
|
let stepViewRow = new JobStepsViewRow();
|
||||||
stepViewRow.message = step.message;
|
stepViewRow.message = step.message;
|
||||||
stepViewRow.runStatus = AgentJobUtilities.convertToStatusString(step.runStatus);
|
stepViewRow.runStatus = jobStepStatus ? JobManagementUtilities.convertToStatusString(0) :
|
||||||
self._runStatus = AgentJobUtilities.convertToStatusString(self.agentJobHistoryInfo.runStatus);
|
JobManagementUtilities.convertToStatusString(step.runStatus);
|
||||||
|
self._runStatus = JobManagementUtilities.convertToStatusString(self.agentJobHistoryInfo.runStatus);
|
||||||
stepViewRow.stepName = step.stepName;
|
stepViewRow.stepName = step.stepName;
|
||||||
stepViewRow.stepID = step.stepId.toString();
|
stepViewRow.stepID = step.stepId.toString();
|
||||||
return stepViewRow;
|
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[]) {
|
private buildHistoryTree(self: any, jobHistories: sqlops.AgentJobHistoryInfo[]) {
|
||||||
self._treeController.jobHistories = jobHistories;
|
self._treeController.jobHistories = jobHistories;
|
||||||
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, 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 {
|
private convertToJobHistoryRow(historyInfo: sqlops.AgentJobHistoryInfo): JobHistoryRow {
|
||||||
let jobHistoryRow = new JobHistoryRow();
|
let jobHistoryRow = new JobHistoryRow();
|
||||||
jobHistoryRow.runDate = this.formatTime(historyInfo.runDate);
|
jobHistoryRow.runDate = this.formatTime(historyInfo.runDate);
|
||||||
jobHistoryRow.runStatus = AgentJobUtilities.convertToStatusString(historyInfo.runStatus);
|
jobHistoryRow.runStatus = JobManagementUtilities.convertToStatusString(historyInfo.runStatus);
|
||||||
jobHistoryRow.instanceID = historyInfo.instanceId;
|
jobHistoryRow.instanceID = historyInfo.instanceId;
|
||||||
return jobHistoryRow;
|
return jobHistoryRow;
|
||||||
}
|
}
|
||||||
@@ -269,15 +248,74 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
private setActions(): void {
|
private setActions(): void {
|
||||||
let startIcon: HTMLElement = $('.action-label.icon.runJobIcon').get(0);
|
let startIcon: HTMLElement = $('.action-label.icon.runJobIcon').get(0);
|
||||||
let stopIcon: HTMLElement = $('.action-label.icon.stopJobIcon').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 runJobAction = this.instantiationService.createInstance(RunJobAction);
|
||||||
let stopJobAction = this.instantiationService.createInstance(StopJobAction);
|
let stopJobAction = this.instantiationService.createInstance(StopJobAction);
|
||||||
let newStepAction = this.instantiationService.createInstance(NewStepAction);
|
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 = new Taskbar(taskbar, this.contextMenuService);
|
||||||
this._actionBar.context = this;
|
this._actionBar.context = this;
|
||||||
this._actionBar.setContent([
|
this._actionBar.setContent([
|
||||||
@@ -298,7 +336,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get ownerUri(): string {
|
public get ownerUri(): string {
|
||||||
return this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
return this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get serverName(): string {
|
public get serverName(): string {
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ table.step-list tr.step-row td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.vs-dark .history-details > .job-steps {
|
.vs-dark .history-details > .job-steps {
|
||||||
display: inline-block;
|
display: block;
|
||||||
border-left: 3px solid #444444;
|
border-left: 3px solid #444444;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -176,10 +176,12 @@ table.step-list tr.step-row td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.history-details > .job-steps {
|
.history-details > .job-steps {
|
||||||
display: inline-block;
|
display: block;
|
||||||
border-left: 3px solid #f4f4f4;
|
border-left: 3px solid #f4f4f4;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: 90%;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-details > .job-steps > .step-list {
|
.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 {
|
jobhistory-component > .actionbar-container .monaco-action-bar > ul.actions-container {
|
||||||
border-top: 3px solid #f4f4f4;
|
border-top: 3px solid #f4f4f4;
|
||||||
padding-top: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vs-dark jobhistory-component > .actionbar-container .monaco-action-bar > ul.actions-container {
|
.vs-dark jobhistory-component > .actionbar-container .monaco-action-bar > ul.actions-container {
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ export interface IListTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class JobHistoryRenderer implements tree.IRenderer {
|
export class JobHistoryRenderer implements tree.IRenderer {
|
||||||
private _statusIcon: HTMLElement;
|
|
||||||
|
|
||||||
public getHeight(tree: tree.ITree, element: JobHistoryRow): number {
|
public getHeight(tree: tree.ITree, element: JobHistoryRow): number {
|
||||||
return 30;
|
return 30;
|
||||||
@@ -121,11 +120,10 @@ export class JobHistoryRenderer implements tree.IRenderer {
|
|||||||
public renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement): IListTemplate {
|
public renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement): IListTemplate {
|
||||||
let row = DOM.$('.list-row');
|
let row = DOM.$('.list-row');
|
||||||
let label = DOM.$('.label');
|
let label = DOM.$('.label');
|
||||||
this._statusIcon = this.createStatusIcon();
|
let statusIcon = this.createStatusIcon();
|
||||||
row.appendChild(this._statusIcon);
|
row.appendChild(statusIcon);
|
||||||
row.appendChild(label);
|
row.appendChild(label);
|
||||||
container.appendChild(row);
|
container.appendChild(row);
|
||||||
let statusIcon = this._statusIcon;
|
|
||||||
return { statusIcon, label };
|
return { statusIcon, label };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,13 +131,13 @@ export class JobHistoryRenderer implements tree.IRenderer {
|
|||||||
templateData.label.innerHTML = element.runDate + ' ' + element.runStatus;
|
templateData.label.innerHTML = element.runDate + ' ' + element.runStatus;
|
||||||
let statusClass: string;
|
let statusClass: string;
|
||||||
if (element.runStatus === 'Succeeded') {
|
if (element.runStatus === 'Succeeded') {
|
||||||
statusClass = ' job-passed';
|
statusClass = 'status-icon job-passed';
|
||||||
} else if (element.runStatus === 'Failed') {
|
} else if (element.runStatus === 'Failed') {
|
||||||
statusClass = ' job-failed';
|
statusClass = 'status-icon job-failed';
|
||||||
} else {
|
} 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 {
|
public disposeTemplate(tree: tree.ITree, templateId: string, templateData: IListTemplate): void {
|
||||||
@@ -148,7 +146,6 @@ export class JobHistoryRenderer implements tree.IRenderer {
|
|||||||
|
|
||||||
private createStatusIcon(): HTMLElement {
|
private createStatusIcon(): HTMLElement {
|
||||||
let statusIcon: HTMLElement = DOM.$('div');
|
let statusIcon: HTMLElement = DOM.$('div');
|
||||||
statusIcon.className += ' status-icon';
|
|
||||||
return statusIcon;
|
return statusIcon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
119
src/sql/parts/jobManagement/views/jobManagementView.ts
Normal 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 }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,21 +8,28 @@ import 'vs/css!./jobStepsView';
|
|||||||
import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable, AfterContentChecked } from '@angular/core';
|
import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable, AfterContentChecked } from '@angular/core';
|
||||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
|
||||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { JobStepsViewController, JobStepsViewDataSource, JobStepsViewFilter,
|
import { JobStepsViewController, JobStepsViewDataSource, JobStepsViewFilter,
|
||||||
JobStepsViewRenderer, JobStepsViewModel} from 'sql/parts/jobManagement/views/jobStepsViewTree';
|
JobStepsViewRenderer, JobStepsViewModel} from 'sql/parts/jobManagement/views/jobStepsViewTree';
|
||||||
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
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';
|
export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: JOBSTEPSVIEW_SELECTOR,
|
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 _tree: Tree;
|
||||||
private _treeController = new JobStepsViewController();
|
private _treeController = new JobStepsViewController();
|
||||||
@@ -33,15 +40,18 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo
|
|||||||
|
|
||||||
@ViewChild('table') private _tableContainer: ElementRef;
|
@ViewChild('table') private _tableContainer: ElementRef;
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||||
@Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface,
|
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||||
@Inject(forwardRef(() => JobHistoryComponent)) private _jobHistoryComponent: JobHistoryComponent,
|
@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() {
|
ngAfterContentChecked() {
|
||||||
@@ -64,7 +74,7 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||||
this._tree = new Tree(this._tableContainer.nativeElement, {
|
this._tree = new Tree(this._tableContainer.nativeElement, {
|
||||||
controller: this._treeController,
|
controller: this._treeController,
|
||||||
dataSource: this._treeDataSource,
|
dataSource: this._treeDataSource,
|
||||||
@@ -73,5 +83,11 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo
|
|||||||
}, {verticalScrollMode: ScrollbarVisibility.Visible});
|
}, {verticalScrollMode: ScrollbarVisibility.Visible});
|
||||||
this._register(attachListStyler(this._tree, this.themeService));
|
this._register(attachListStyler(this._tree, this.themeService));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onFirstVisible() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public layout() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||