diff --git a/extensions/agent/client/src/data/createAlertData.ts b/extensions/agent/client/src/data/createAlertData.ts new file mode 100644 index 0000000000..ae36fe575d --- /dev/null +++ b/extensions/agent/client/src/data/createAlertData.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +export class CreateAlertData { + public ownerUri: string; + private _alert: sqlops.AgentAlertInfo; + + constructor(ownerUri:string) { + this.ownerUri = ownerUri; + } + + public async initialize() { + let agentService = await AgentUtils.getAgentService(); + + } + + public async save() { + } +} diff --git a/extensions/agent/client/src/data/createScheduleData.ts b/extensions/agent/client/src/data/createScheduleData.ts new file mode 100644 index 0000000000..16d8fc3a8f --- /dev/null +++ b/extensions/agent/client/src/data/createScheduleData.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +export class CreateScheduleData { + public ownerUri: string; + public schedules: sqlops.AgentJobScheduleInfo[]; + public selectedSchedule: sqlops.AgentJobScheduleInfo; + + constructor(ownerUri:string) { + this.ownerUri = ownerUri; + } + + public async initialize() { + let agentService = await AgentUtils.getAgentService(); + let result = await agentService.getJobSchedules(this.ownerUri); + if (result && result.success) { + this.schedules = result.schedules; + } + } + + public async save() { + } +} diff --git a/extensions/agent/client/src/dialogs/createAlertDialog.ts b/extensions/agent/client/src/dialogs/createAlertDialog.ts new file mode 100644 index 0000000000..231d3de83e --- /dev/null +++ b/extensions/agent/client/src/dialogs/createAlertDialog.ts @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------------------------- + * 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 = new vscode.EventEmitter(); + public readonly onSuccess: vscode.Event = 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() { + } +} diff --git a/extensions/agent/client/src/dialogs/createJobDialog.ts b/extensions/agent/client/src/dialogs/createJobDialog.ts index c53de2c596..bd5a39c6e0 100644 --- a/extensions/agent/client/src/dialogs/createJobDialog.ts +++ b/extensions/agent/client/src/dialogs/createJobDialog.ts @@ -7,12 +7,12 @@ import * as sqlops from 'sqlops'; import { CreateJobData } from '../data/createJobData'; import { CreateStepDialog } from './createStepDialog'; import { PickScheduleDialog } from './pickScheduleDialog'; +import { CreateAlertDialog } from './createAlertDialog'; export class CreateJobDialog { // TODO: localize // Top level - // private readonly DialogTitle: string = 'New Job'; private readonly OkButtonText: string = 'OK'; private readonly CancelButtonText: string = 'Cancel'; @@ -23,7 +23,6 @@ export class CreateJobDialog { private readonly NotificationsTabText: string = 'Notifications'; // General tab strings - // private readonly NameTextBoxLabel: string = 'Name'; private readonly OwnerTextBoxLabel: string = 'Owner'; private readonly CategoryDropdownLabel: string = 'Category'; @@ -43,7 +42,6 @@ export class CreateJobDialog { private readonly DeleteStepButtonString: string = 'Delete'; // Notifications tab strings - // private readonly NotificationsTabTopLabelString: string = 'Actions to perform when the job completes'; private readonly EmailCheckBoxString: string = 'Email'; private readonly PagerCheckBoxString: string = 'Page'; @@ -51,10 +49,14 @@ export class CreateJobDialog { private readonly DeleteJobCheckBoxString: string = 'Automatically delete job'; // Schedules tab strings + private readonly SchedulesTopLabelString: string = 'Schedules list'; private readonly PickScheduleButtonString: string = 'Pick Schedule'; + // Alerts tab strings + private readonly AlertsTopLabelString: string = 'Alerts list'; + private readonly NewAlertButtonString: string = 'New Alert'; + // UI Components - // private dialog: sqlops.window.modelviewdialog.Dialog; private generalTab: sqlops.window.modelviewdialog.DialogTab; private stepsTab: sqlops.window.modelviewdialog.DialogTab; @@ -63,7 +65,6 @@ export class CreateJobDialog { private notificationsTab: sqlops.window.modelviewdialog.DialogTab; // General tab controls - // private nameTextBox: sqlops.InputBoxComponent; private ownerTextBox: sqlops.InputBoxComponent; private categoryDropdown: sqlops.DropDownComponent; @@ -94,6 +95,10 @@ export class CreateJobDialog { private schedulesTable: sqlops.TableComponent; private pickScheduleButton: sqlops.ButtonComponent; + // Alert tab controls + private alertsTable: sqlops.TableComponent; + private newAlertButton: sqlops.ButtonComponent; + private model: CreateJobData; constructor(ownerUri: string) { @@ -225,6 +230,38 @@ export class CreateJobDialog { } private initializeAlertsTab() { + this.alertsTab.registerContent(async view => { + this.alertsTable = view.modelBuilder.table() + .withProperties({ + columns: [ + 'Alert Name' + ], + data: [], + height: 600, + width: 400 + }).component(); + + this.newAlertButton = view.modelBuilder.button().withProperties({ + label: this.NewAlertButtonString, + width: 80 + }).component(); + + this.newAlertButton.onDidClick((e)=>{ + let alertDialog = new CreateAlertDialog(this.model.ownerUri); + alertDialog.onSuccess((dialogModel) => { + }); + alertDialog.showDialog(); + }); + + let formModel = view.modelBuilder.formContainer() + .withFormItems([{ + component: this.alertsTable, + title: this.AlertsTopLabelString, + actions: [this.newAlertButton] + }]).withLayout({ width: '100%' }).component(); + + await view.initializeModel(formModel); + }); } private initializeSchedulesTab() { @@ -259,7 +296,7 @@ export class CreateJobDialog { let formModel = view.modelBuilder.formContainer() .withFormItems([{ component: this.schedulesTable, - title: this.JobStepsTopLabelString, + title: this.SchedulesTopLabelString, actions: [this.pickScheduleButton] }]).withLayout({ width: '100%' }).component(); diff --git a/extensions/agent/client/src/dialogs/createScheduleDialog.ts b/extensions/agent/client/src/dialogs/createScheduleDialog.ts new file mode 100644 index 0000000000..5f1994b3f9 --- /dev/null +++ b/extensions/agent/client/src/dialogs/createScheduleDialog.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as sqlops from 'sqlops'; +import * as vscode from 'vscode'; +import { CreateScheduleData } from '../data/createScheduleData'; + +export class CreateScheduleDialog { + + // Top level + private readonly DialogTitle: string = 'New Schedule'; + private readonly OkButtonText: string = 'OK'; + private readonly CancelButtonText: string = 'Cancel'; + + // UI Components + private dialog: sqlops.window.modelviewdialog.Dialog; + private schedulesTable: sqlops.TableComponent; + + private model: CreateScheduleData; + + private _onSuccess: vscode.EventEmitter = new vscode.EventEmitter(); + public readonly onSuccess: vscode.Event = this._onSuccess.event; + + constructor(ownerUri: string) { + this.model = new CreateScheduleData(ownerUri); + } + + public async showDialog() { + await this.model.initialize(); + this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle); + this.initializeContent(); + 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 initializeContent() { + this.dialog.registerContent(async view => { + this.schedulesTable = view.modelBuilder.table() + .withProperties({ + columns: [ + 'Schedule Name' + ], + data: [], + height: 600, + width: 400 + }).component(); + + let formModel = view.modelBuilder.formContainer() + .withFormItems([{ + component: this.schedulesTable, + title: 'Schedules' + }]).withLayout({ width: '100%' }).component(); + + await view.initializeModel(formModel); + + if (this.model.schedules) { + let data: any[][] = []; + for (let i = 0; i < this.model.schedules.length; ++i) { + let schedule = this.model.schedules[i]; + data[i] = [ schedule.name ]; + } + this.schedulesTable.data = data; + } + }); + } + + private async execute() { + this.updateModel(); + await this.model.save(); + this._onSuccess.fire(this.model); + } + + private async cancel() { + } + + private updateModel() { + let selectedRows = this.schedulesTable.selectedRows; + if (selectedRows && selectedRows.length > 0) { + let selectedRow = selectedRows[0]; + this.model.selectedSchedule = this.model.schedules[selectedRow]; + } + } +} \ No newline at end of file diff --git a/extensions/agent/client/src/dialogs/pickScheduleDialog.ts b/extensions/agent/client/src/dialogs/pickScheduleDialog.ts index c070ea9153..98d2cf4e9c 100644 --- a/extensions/agent/client/src/dialogs/pickScheduleDialog.ts +++ b/extensions/agent/client/src/dialogs/pickScheduleDialog.ts @@ -19,7 +19,6 @@ export class PickScheduleDialog { // UI Components private dialog: sqlops.window.modelviewdialog.Dialog; - private scheduleTab: sqlops.window.modelviewdialog.DialogTab; private schedulesTable: sqlops.TableComponent; private model: PickScheduleData; @@ -34,7 +33,6 @@ export class PickScheduleDialog { public async showDialog() { await this.model.initialize(); this.dialog = sqlops.window.modelviewdialog.createDialog(this.DialogTitle); - this.scheduleTab = sqlops.window.modelviewdialog.createTab(this.SchedulesTabText); this.initializeContent(); this.dialog.okButton.onClick(async () => await this.execute()); this.dialog.cancelButton.onClick(async () => await this.cancel()); diff --git a/extensions/agent/client/src/main.ts b/extensions/agent/client/src/main.ts index 8160e56b2d..c4105a8e60 100644 --- a/extensions/agent/client/src/main.ts +++ b/extensions/agent/client/src/main.ts @@ -6,12 +6,10 @@ import vscode = require('vscode'); import { MainController } from './mainController'; -import { ApiWrapper } from './apiWrapper'; export let controller: MainController; export function activate(context: vscode.ExtensionContext) { - let apiWrapper = new ApiWrapper(); - controller = new MainController(context, apiWrapper); + controller = new MainController(context); controller.activate(); } diff --git a/extensions/agent/client/src/mainController.ts b/extensions/agent/client/src/mainController.ts index 6009c2cb8e..12288ad75c 100644 --- a/extensions/agent/client/src/mainController.ts +++ b/extensions/agent/client/src/mainController.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; import * as vscode from 'vscode'; -import { ApiWrapper } from './apiWrapper'; +import { CreateAlertDialog } from './dialogs/createAlertDialog'; import { CreateJobDialog } from './dialogs/createJobDialog'; import { CreateStepDialog } from './dialogs/createStepDialog'; import { PickScheduleDialog } from './dialogs/pickScheduleDialog'; @@ -13,21 +13,16 @@ import { PickScheduleDialog } from './dialogs/pickScheduleDialog'; * The main controller class that initializes the extension */ export class MainController { - protected _apiWrapper: ApiWrapper; protected _context: vscode.ExtensionContext; // PUBLIC METHODS ////////////////////////////////////////////////////// - public constructor(context: vscode.ExtensionContext, apiWrapper?: ApiWrapper) { - this._apiWrapper = apiWrapper || new ApiWrapper(); + public constructor(context: vscode.ExtensionContext) { this._context = context; } /** - * Deactivates the extension + * Activates the extension */ - public deactivate(): void { - } - public activate(): void { vscode.commands.registerCommand('agent.openCreateJobDialog', (ownerUri: string) => { let dialog = new CreateJobDialog(ownerUri); @@ -41,9 +36,19 @@ export class MainController { 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) => { + }); + } - private updateJobStepDialog() { - - } + /** + * Deactivates the extension + */ + public deactivate(): void { + } } diff --git a/extensions/mssql/src/config.json b/extensions/mssql/src/config.json index e140c888d2..6f121c7fc1 100644 --- a/extensions/mssql/src/config.json +++ b/extensions/mssql/src/config.json @@ -1,6 +1,6 @@ { "downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}", - "version": "1.5.0-alpha.1", + "version": "1.5.0-alpha.2", "downloadFileNames": { "Windows_86": "win-x86-netcoreapp2.1.zip", "Windows_64": "win-x64-netcoreapp2.1.zip", diff --git a/extensions/mssql/src/features.ts b/extensions/mssql/src/features.ts index aa166b9781..d90b078cb2 100644 --- a/extensions/mssql/src/features.ts +++ b/extensions/mssql/src/features.ts @@ -409,7 +409,7 @@ export class AgentServicesFeature extends SqlOpsFeature { ); }; - let updateJobSchedule= (ownerUri: string, originalScheduleName: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable => { + let updateJobSchedule = (ownerUri: string, originalScheduleName: string, scheduleInfo: sqlops.AgentJobScheduleInfo): Thenable => { let params: contracts.UpdateAgentJobScheduleParams = { ownerUri: ownerUri, originalScheduleName: originalScheduleName, diff --git a/src/sql/parts/dashboard/dashboard.module.ts b/src/sql/parts/dashboard/dashboard.module.ts index 44c7940136..26b3f24d50 100644 --- a/src/sql/parts/dashboard/dashboard.module.ts +++ b/src/sql/parts/dashboard/dashboard.module.ts @@ -51,7 +51,10 @@ import { ControlHostContent } from 'sql/parts/dashboard/contents/controlHostCont import { DashboardControlHostContainer } from 'sql/parts/dashboard/containers/dashboardControlHostContainer.component'; import { JobsViewComponent } from 'sql/parts/jobManagement/views/jobsView.component'; import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component'; +import { AlertsViewComponent } from 'sql/parts/jobManagement/views/alertsView.component'; import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component'; +import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsView.component'; +import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component'; import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component'; import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component'; import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component'; @@ -59,9 +62,8 @@ import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component'; let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer, DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent, ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer, - JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, DashboardModelViewContainer, ModelComponentWrapper, Checkbox, - SelectBox, - InputBox,]; + JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, AlertsViewComponent, ProxiesViewComponent, OperatorsViewComponent, + DashboardModelViewContainer, ModelComponentWrapper, Checkbox, SelectBox, InputBox,]; /* Panel */ import { PanelModule } from 'sql/base/browser/ui/panel/panel.module'; diff --git a/src/sql/parts/jobManagement/agent/agentView.component.html b/src/sql/parts/jobManagement/agent/agentView.component.html index 94f8922702..5cee6c4897 100644 --- a/src/sql/parts/jobManagement/agent/agentView.component.html +++ b/src/sql/parts/jobManagement/agent/agentView.component.html @@ -4,17 +4,42 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ --> - - - - -
- -
-
- -
-
-
-
+
+ + + +
+ +
+
+ +
+
+
+ + +
+ +
+
+
+ + +
+ +
+
+
+ + +
+ +
+
+
+
+
\ No newline at end of file diff --git a/src/sql/parts/jobManagement/agent/agentView.component.ts b/src/sql/parts/jobManagement/agent/agentView.component.ts index 909573b517..74e5f654d0 100644 --- a/src/sql/parts/jobManagement/agent/agentView.component.ts +++ b/src/sql/parts/jobManagement/agent/agentView.component.ts @@ -33,6 +33,10 @@ export class AgentViewComponent { // tslint:disable:no-unused-variable private readonly jobsComponentTitle: string = nls.localize('jobview.Jobs', "Jobs"); + private readonly alertsComponentTitle: string = nls.localize('jobview.Alerts', "Alerts"); + private readonly proxiesComponentTitle: string = nls.localize('jobview.Proxies', "Proxies"); + private readonly operatorsComponentTitle: string = nls.localize('jobview.Operators', "Operators"); + private _showHistory: boolean = false; private _jobId: string = null; private _agentJobInfo: AgentJobInfo = null; @@ -40,6 +44,9 @@ export class AgentViewComponent { private _expanded: Map; public jobsIconClass: string = 'jobsview-icon'; + public alertsIconClass: string = 'alertsview-icon'; + public proxiesIconClass: string = 'proxiesview-icon'; + public operatorsIconClass: string = 'operatorsview-icon'; // tslint:disable-next-line:no-unused-variable private readonly panelOpt: IPanelOptions = { diff --git a/src/sql/parts/jobManagement/common/interfaces.ts b/src/sql/parts/jobManagement/common/interfaces.ts index 28568b1b92..2d502e7ece 100644 --- a/src/sql/parts/jobManagement/common/interfaces.ts +++ b/src/sql/parts/jobManagement/common/interfaces.ts @@ -7,7 +7,6 @@ import * as sqlops from 'sqlops'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Table } from 'sql/base/browser/ui/table/table'; import { JobCacheObject } from './jobManagementService'; export const SERVICE_ID = 'jobManagementService'; @@ -21,6 +20,12 @@ export interface IJobManagementService { getJobs(connectionUri: string): Thenable; + getAlerts(connectionUri: string): Thenable; + + getOperators(connectionUri: string): Thenable; + + getProxies(connectionUri: string): Thenable; + getJobHistory(connectionUri: string, jobID: string): Thenable; jobAction(connectionUri: string, jobName: string, action: string): Thenable; diff --git a/src/sql/parts/jobManagement/common/jobManagementService.ts b/src/sql/parts/jobManagement/common/jobManagementService.ts index b5837047c8..0b714ff0bb 100644 --- a/src/sql/parts/jobManagement/common/jobManagementService.ts +++ b/src/sql/parts/jobManagement/common/jobManagementService.ts @@ -29,6 +29,25 @@ export class JobManagementService implements IJobManagementService { }); } + public getAlerts(connectionUri: string): Thenable { + return this._runAction(connectionUri, (runner) => { + return runner.getAlerts(connectionUri); + }); + } + + public getOperators(connectionUri: string): Thenable { + return this._runAction(connectionUri, (runner) => { + return runner.getOperators(connectionUri); + }); + } + + public getProxies(connectionUri: string): Thenable { + return this._runAction(connectionUri, (runner) => { + return runner.getProxies(connectionUri); + }); + } + + public getJobHistory(connectionUri: string, jobID: string): Thenable { return this._runAction(connectionUri, (runner) => { return runner.getJobHistory(connectionUri, jobID); diff --git a/src/sql/parts/jobManagement/common/media/alert.svg b/src/sql/parts/jobManagement/common/media/alert.svg new file mode 100644 index 0000000000..d0e8ce7861 --- /dev/null +++ b/src/sql/parts/jobManagement/common/media/alert.svg @@ -0,0 +1 @@ +blocker \ No newline at end of file diff --git a/src/sql/parts/jobManagement/common/media/alert_inverse.svg b/src/sql/parts/jobManagement/common/media/alert_inverse.svg new file mode 100644 index 0000000000..060f8ab73b --- /dev/null +++ b/src/sql/parts/jobManagement/common/media/alert_inverse.svg @@ -0,0 +1 @@ +blocker_inverse \ No newline at end of file diff --git a/src/sql/parts/jobManagement/common/media/jobs.css b/src/sql/parts/jobManagement/common/media/jobs.css index 3d16d92502..5a1571892f 100644 --- a/src/sql/parts/jobManagement/common/media/jobs.css +++ b/src/sql/parts/jobManagement/common/media/jobs.css @@ -31,7 +31,7 @@ jobhistory-component { border-bottom: 3px solid #444444; } -#jobsDiv .jobview-grid { +.jobview-grid { height: 94.7%; width : 100%; display: block; @@ -49,6 +49,18 @@ jobhistory-component { font-size: larger; } +.vs-dark #agentViewDiv .slick-header-column { + background: #333333 !important; +} + +#agentViewDiv .slick-header-column { + background-color: transparent !important; + background: white !important; + border: 0px !important; + font-weight: bold; + font-size: larger; +} + .vs-dark #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell { background:#333333; } @@ -107,14 +119,14 @@ jobhistory-component { border-bottom: none; } -#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.l1.r1.error-row { +.jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.l1.r1.error-row { width: 100%; opacity: 1; font-weight: 700; color: orangered; } -#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.error-row { +.jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.error-row { opacity: 1; } @@ -178,6 +190,30 @@ jobhistory-component { background-image: url('./job_inverse.svg'); } +.alertsview-icon { + background-image: url('./alert.svg'); +} + +.vs-dark .alertsview-icon { + background-image: url('./alert_inverse.svg'); +} + +.proxiesview-icon { + background-image: url('./proxy.svg'); +} + +.vs-dark .proxiesview-icon { + background-image: url('./proxy_inverse.svg'); +} + +.operatorsview-icon { + background-image: url('./operator.svg'); +} + +.vs-dark .operatorsview-icon { + background-image: url('./operator_inverse.svg'); +} + agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.even > .slick-cell, agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.odd > .slick-cell { cursor: pointer; @@ -259,4 +295,23 @@ table.jobprevruns > tbody { .jobs-view-toolbar span{ padding-left: 5px; -} \ No newline at end of file +} + + +#alertsDiv .jobalertsview-grid { + height: 94.7%; + width : 100%; + display: block; +} + +#operatorsDiv .joboperatorsview-grid { + height: 94.7%; + width : 100%; + display: block; +} + +#proxiesDiv .jobproxiesview-grid { + height: 94.7%; + width : 100%; + display: block; +} diff --git a/src/sql/parts/jobManagement/common/media/operator.svg b/src/sql/parts/jobManagement/common/media/operator.svg new file mode 100644 index 0000000000..0666e82d3a --- /dev/null +++ b/src/sql/parts/jobManagement/common/media/operator.svg @@ -0,0 +1 @@ +security \ No newline at end of file diff --git a/src/sql/parts/jobManagement/common/media/operator_inverse.svg b/src/sql/parts/jobManagement/common/media/operator_inverse.svg new file mode 100644 index 0000000000..6380ef9d8f --- /dev/null +++ b/src/sql/parts/jobManagement/common/media/operator_inverse.svg @@ -0,0 +1 @@ +security_inverse \ No newline at end of file diff --git a/src/sql/parts/jobManagement/common/media/proxy.svg b/src/sql/parts/jobManagement/common/media/proxy.svg new file mode 100644 index 0000000000..8978eceb79 --- /dev/null +++ b/src/sql/parts/jobManagement/common/media/proxy.svg @@ -0,0 +1 @@ +health \ No newline at end of file diff --git a/src/sql/parts/jobManagement/common/media/proxy_inverse.svg b/src/sql/parts/jobManagement/common/media/proxy_inverse.svg new file mode 100644 index 0000000000..d47d10bab2 --- /dev/null +++ b/src/sql/parts/jobManagement/common/media/proxy_inverse.svg @@ -0,0 +1 @@ +health_inverse \ No newline at end of file diff --git a/src/sql/parts/jobManagement/views/alertsView.component.html b/src/sql/parts/jobManagement/views/alertsView.component.html new file mode 100644 index 0000000000..dc73981084 --- /dev/null +++ b/src/sql/parts/jobManagement/views/alertsView.component.html @@ -0,0 +1,17 @@ + +
+

Alerts

+

No Alerts Available

+
+
+
+
{{RefreshText}}
+
{{NewAlertText}}
+
+ +
diff --git a/src/sql/parts/jobManagement/views/alertsView.component.ts b/src/sql/parts/jobManagement/views/alertsView.component.ts new file mode 100644 index 0000000000..d1391e62bb --- /dev/null +++ b/src/sql/parts/jobManagement/views/alertsView.component.ts @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!sql/parts/grid/media/slickColorTheme'; +import 'vs/css!sql/parts/grid/media/flexbox'; +import 'vs/css!sql/parts/grid/media/styles'; +import 'vs/css!sql/parts/grid/media/slick.grid'; +import 'vs/css!sql/parts/grid/media/slickGrid'; +import 'vs/css!../common/media/jobs'; +import 'vs/css!sql/media/icons/common-icons'; +import 'vs/css!sql/base/browser/ui/table/media/table'; + +import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core'; +import * as sqlops from 'sqlops'; +import * as nls from 'vs/nls'; +import { Table } from 'sql/base/browser/ui/table/table'; +import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component'; +import * as dom from 'vs/base/browser/dom'; +import { IJobManagementService } from '../common/interfaces'; +import { 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'; +export const VIEW_SELECTOR: string = 'jobalertsview-component'; +export const ROW_HEIGHT: number = 45; + +@Component({ + selector: VIEW_SELECTOR, + templateUrl: decodeURI(require.toUrl('./alertsView.component.html')), + providers: [{ provide: TabChild, useExisting: forwardRef(() => AlertsViewComponent) }], +}) +export class AlertsViewComponent implements AfterContentChecked { + + private columns: Array> = [ + { name: nls.localize('jobAlertColumns.name', 'Name'), field: 'name', width: 200, id: 'name' }, + { name: nls.localize('jobAlertColumns.lastOccurrenceDate', 'Last Occurrence'), field: 'lastOccurrenceDate', width: 200, id: 'lastOccurrenceDate' }, + { name: nls.localize('jobAlertColumns.enabled', 'Enabled'), field: 'enabled', width: 200, id: 'enabled' }, + { name: nls.localize('jobAlertColumns.databaseName', 'Database Name'), field: 'databaseName', width: 200, id: 'databaseName' }, + { name: nls.localize('jobAlertColumns.categoryName', 'Category Name'), field: 'categoryName', width: 200, id: 'categoryName' }, + ]; + + private options: Slick.GridOptions = { + syncColumnCellResize: true, + enableColumnReorder: false, + rowHeight: 45, + enableCellNavigation: true, + editable: false + }; + + private dataView: any; + + @ViewChild('jobalertsgrid') _gridEl: ElementRef; + private isVisible: boolean = false; + private isInitialized: boolean = false; + private isRefreshing: boolean = false; + private _table: Table; + public alerts: sqlops.AgentAlertInfo[]; + private _serverName: string; + private _isCloud: boolean; + private _showProgressWheel: boolean; + + private NewAlertText: string = nls.localize('jobAlertToolbar-NewJob', "New Alert"); + private RefreshText: string = nls.localize('jobAlertToolbar-Refresh', "Refresh"); + + constructor( + @Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface, + @Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef, + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent, + @Inject(IJobManagementService) private _jobManagementService: IJobManagementService, + @Inject(IThemeService) private _themeService: IThemeService, + @Inject(ICommandService) private _commandService: ICommandService + ) { + this._isCloud = this._dashboardService.connectionManagementService.connectionInfo.serverInfo.isCloud; + } + + public layout() { + this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement))); + } + + ngAfterContentChecked() { + if (this.isVisible === false && this._gridEl.nativeElement.offsetParent !== null) { + this.isVisible = true; + if (!this.isInitialized) { + this._showProgressWheel = true; + this.onFirstVisible(false); + this.isInitialized = true; + } + } else if (this.isVisible === true && this._agentViewComponent.refresh === true) { + this._showProgressWheel = true; + this.onFirstVisible(false); + this.isRefreshing = true; + this._agentViewComponent.refresh = false; + } else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) { + this.isVisible = false; + } + } + + onFirstVisible(cached?: boolean) { + let self = this; + let columns = this.columns.map((column) => { + column.rerenderOnResize = true; + return column; + }); + let options = >{ + syncColumnCellResize: true, + enableColumnReorder: false, + rowHeight: ROW_HEIGHT, + enableCellNavigation: true, + forceFitColumns: true + }; + + this.dataView = new Slick.Data.DataView(); + + $(this._gridEl.nativeElement).empty(); + this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options); + this._table.grid.setData(this.dataView, true); + + let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; + this._jobManagementService.getAlerts(ownerUri).then((result) => { + if (result && result.alerts) { + self.alerts = result.alerts; + self.onAlertsAvailable(result.alerts); + } + }); + } + + private onAlertsAvailable(alerts: sqlops.AgentAlertInfo[]) { + let items: any = alerts.map((item) => { + return { + id: item.id, + name: item.name, + lastOccurrenceDate: item.lastOccurrenceDate, + enabled: item.isEnabled, + databaseName: item.databaseName, + categoryName: item.categoryName + }; + }); + + this.dataView.beginUpdate(); + this.dataView.setItems(items); + this.dataView.endUpdate(); + this._table.autosizeColumns(); + this._table.resizeCanvas(); + + this._showProgressWheel = false; + this._cd.detectChanges(); + } + + private openCreateJobDialog() { + let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; + this._commandService.executeCommand('agent.openCreateAlertDialog', ownerUri); + } + + private refreshJobs() { + this._agentViewComponent.refresh = true; + } +} \ No newline at end of file diff --git a/src/sql/parts/jobManagement/views/jobsView.component.ts b/src/sql/parts/jobManagement/views/jobsView.component.ts index 92bbccf852..435ddb5872 100644 --- a/src/sql/parts/jobManagement/views/jobsView.component.ts +++ b/src/sql/parts/jobManagement/views/jobsView.component.ts @@ -96,7 +96,7 @@ export class JobsViewComponent implements AfterContentChecked { private filterValueMap: { [columnName: string]: string[]; } = {}; private sortingStylingMap: { [columnName: string]: any; } = {}; - private NewJobText: string = nls.localize("jobsToolbar-NewJob", "New job"); + private NewJobText: string = nls.localize("jobsToolbar-NewJob", "New Job"); private RefreshText: string = nls.localize("jobsToolbar-Refresh", "Refresh"); constructor( @@ -145,13 +145,13 @@ export class JobsViewComponent implements AfterContentChecked { this.onFirstVisible(false); this.isRefreshing = true; this._agentViewComponent.refresh = false; - } else if (this.isVisible === true && this._agentViewComponent.refresh === false) { + } /*else if (this.isVisible === true && this._agentViewComponent.refresh === false) { if (!this.isRefreshing) { this._showProgressWheel = true; this.onFirstVisible(true); } this.isRefreshing = false; - } else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) { + }*/ else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) { this.isVisible = false; } } @@ -346,7 +346,11 @@ export class JobsViewComponent implements AfterContentChecked { currentTarget.title = currentTarget.innerText; }); this._showProgressWheel = false; - this._cd.detectChanges(); + + if (this.isVisible) { + this._cd.detectChanges(); + } + const self = this; $(window).resize(() => { let jobsViewToolbar = $('jobsview-component .jobs-view-toolbar').get(0); @@ -827,7 +831,7 @@ export class JobsViewComponent implements AfterContentChecked { private openCreateJobDialog() { let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; - this._commandService.executeCommand("agent.openCreateJobDialog", ownerUri); + this._commandService.executeCommand('agent.openCreateJobDialog', ownerUri); } private refreshJobs() { diff --git a/src/sql/parts/jobManagement/views/operatorsView.component.html b/src/sql/parts/jobManagement/views/operatorsView.component.html new file mode 100644 index 0000000000..e36ca67a7c --- /dev/null +++ b/src/sql/parts/jobManagement/views/operatorsView.component.html @@ -0,0 +1,17 @@ + +
+

Operators

+

No Operators Available

+
+
+
+
{{RefreshText}}
+
{{NewOperatorText}}
+
+ +
diff --git a/src/sql/parts/jobManagement/views/operatorsView.component.ts b/src/sql/parts/jobManagement/views/operatorsView.component.ts new file mode 100644 index 0000000000..02eef8263a --- /dev/null +++ b/src/sql/parts/jobManagement/views/operatorsView.component.ts @@ -0,0 +1,157 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!sql/parts/grid/media/slickColorTheme'; +import 'vs/css!sql/parts/grid/media/flexbox'; +import 'vs/css!sql/parts/grid/media/styles'; +import 'vs/css!sql/parts/grid/media/slick.grid'; +import 'vs/css!sql/parts/grid/media/slickGrid'; +import 'vs/css!../common/media/jobs'; +import 'vs/css!sql/media/icons/common-icons'; +import 'vs/css!sql/base/browser/ui/table/media/table'; + +import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core'; +import * as sqlops from 'sqlops'; +import * as nls from 'vs/nls'; +import { Table } from 'sql/base/browser/ui/table/table'; +import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component'; +import * as dom from 'vs/base/browser/dom'; +import { IJobManagementService } from '../common/interfaces'; +import { 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'; +export const VIEW_SELECTOR: string = 'joboperatorsview-component'; +export const ROW_HEIGHT: number = 45; + +@Component({ + selector: VIEW_SELECTOR, + templateUrl: decodeURI(require.toUrl('./operatorsView.component.html')), + providers: [{ provide: TabChild, useExisting: forwardRef(() => OperatorsViewComponent) }], +}) + +export class OperatorsViewComponent implements AfterContentChecked { + + private columns: Array> = [ + { name: nls.localize('jobOperatorsView.name', 'Name'), field: 'name', width: 200, id: 'name' }, + { name: nls.localize('jobOperatorsView.emailAddress', 'Email Address'), field: 'emailAddress', width: 200, id: 'emailAddress' }, + { name: nls.localize('jobOperatorsView.enabled', 'Enabled'), field: 'enabled', width: 200, id: 'enabled' }, + ]; + + private options: Slick.GridOptions = { + syncColumnCellResize: true, + enableColumnReorder: false, + rowHeight: 45, + enableCellNavigation: true, + editable: false + }; + + private dataView: any; + + @ViewChild('operatorsgrid') _gridEl: ElementRef; + private isVisible: boolean = false; + private isInitialized: boolean = false; + private isRefreshing: boolean = false; + private _table: Table; + public operators: sqlops.AgentOperatorInfo[]; + private _serverName: string; + private _isCloud: boolean; + private _showProgressWheel: boolean; + + private NewOperatorText: string = nls.localize('jobOperatorToolbar-NewItem', "New Operator"); + private RefreshText: string = nls.localize('jobOperatorToolbar-Refresh', "Refresh"); + + constructor( + @Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface, + @Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef, + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent, + @Inject(IJobManagementService) private _jobManagementService: IJobManagementService, + @Inject(IThemeService) private _themeService: IThemeService, + @Inject(ICommandService) private _commandService: ICommandService + ) { + this._isCloud = this._dashboardService.connectionManagementService.connectionInfo.serverInfo.isCloud; + } + + public layout() { + this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement))); + } + + ngAfterContentChecked() { + if (this.isVisible === false && this._gridEl.nativeElement.offsetParent !== null) { + this.isVisible = true; + if (!this.isInitialized) { + this._showProgressWheel = true; + this.onFirstVisible(false); + this.isInitialized = true; + } + } else if (this.isVisible === true && this._agentViewComponent.refresh === true) { + this._showProgressWheel = true; + this.onFirstVisible(false); + this.isRefreshing = true; + this._agentViewComponent.refresh = false; + } else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) { + this.isVisible = false; + } + } + + onFirstVisible(cached?: boolean) { + let self = this; + let columns = this.columns.map((column) => { + column.rerenderOnResize = true; + return column; + }); + let options = >{ + syncColumnCellResize: true, + enableColumnReorder: false, + rowHeight: ROW_HEIGHT, + enableCellNavigation: true, + forceFitColumns: true + }; + + this.dataView = new Slick.Data.DataView(); + + $(this._gridEl.nativeElement).empty(); + this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options); + this._table.grid.setData(this.dataView, true); + + let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; + this._jobManagementService.getOperators(ownerUri).then((result) => { + if (result && result.operators) { + self.operators = result.operators; + self.onOperatorsAvailable(result.operators); + } + }); + } + + private onOperatorsAvailable(operators: sqlops.AgentOperatorInfo[]) { + let items: any = operators.map((item) => { + return { + id: item.id, + name: item.name, + emailAddress: item.emailAddress, + enabled: item.enabled + }; + }); + + this.dataView.beginUpdate(); + this.dataView.setItems(items); + this.dataView.endUpdate(); + this._table.autosizeColumns(); + this._table.resizeCanvas(); + + this._showProgressWheel = false; + this._cd.detectChanges(); + } + + private openCreateOperatorDialog() { + let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; + this._commandService.executeCommand('agent.openCreateOperatorDialog', ownerUri); + } + + private refreshJobs() { + this._agentViewComponent.refresh = true; + } +} \ No newline at end of file diff --git a/src/sql/parts/jobManagement/views/proxiesView.component.html b/src/sql/parts/jobManagement/views/proxiesView.component.html new file mode 100644 index 0000000000..ffb7542964 --- /dev/null +++ b/src/sql/parts/jobManagement/views/proxiesView.component.html @@ -0,0 +1,17 @@ + +
+

Proxies

+

No Proxies Available

+
+
+
+
{{RefreshText}}
+
{{NewProxyText}}
+
+ +
diff --git a/src/sql/parts/jobManagement/views/proxiesView.component.ts b/src/sql/parts/jobManagement/views/proxiesView.component.ts new file mode 100644 index 0000000000..7536131f8d --- /dev/null +++ b/src/sql/parts/jobManagement/views/proxiesView.component.ts @@ -0,0 +1,155 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!sql/parts/grid/media/slickColorTheme'; +import 'vs/css!sql/parts/grid/media/flexbox'; +import 'vs/css!sql/parts/grid/media/styles'; +import 'vs/css!sql/parts/grid/media/slick.grid'; +import 'vs/css!sql/parts/grid/media/slickGrid'; +import 'vs/css!../common/media/jobs'; +import 'vs/css!sql/media/icons/common-icons'; +import 'vs/css!sql/base/browser/ui/table/media/table'; + +import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core'; +import * as sqlops from 'sqlops'; +import * as nls from 'vs/nls'; +import { Table } from 'sql/base/browser/ui/table/table'; +import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component'; +import * as dom from 'vs/base/browser/dom'; +import { IJobManagementService } from '../common/interfaces'; +import { 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'; +export const VIEW_SELECTOR: string = 'jobproxiesview-component'; +export const ROW_HEIGHT: number = 45; + +@Component({ + selector: VIEW_SELECTOR, + templateUrl: decodeURI(require.toUrl('./proxiesView.component.html')), + providers: [{ provide: TabChild, useExisting: forwardRef(() => ProxiesViewComponent) }], +}) + +export class ProxiesViewComponent implements AfterContentChecked { + + private columns: Array> = [ + { name: nls.localize('jobProxiesView.accountName', 'Account Name'), field: 'accountName', width: 200, id: 'accountName' }, + { name: nls.localize('jobProxiesView.credentialName', 'Credential Name'), field: 'credentialName', width: 200, id: 'credentialName' }, + ]; + + private options: Slick.GridOptions = { + syncColumnCellResize: true, + enableColumnReorder: false, + rowHeight: 45, + enableCellNavigation: true, + editable: false + }; + + private dataView: any; + + @ViewChild('proxiesgrid') _gridEl: ElementRef; + private isVisible: boolean = false; + private isInitialized: boolean = false; + private isRefreshing: boolean = false; + private _table: Table; + public proxies: sqlops.AgentProxyInfo[]; + private _serverName: string; + private _isCloud: boolean; + private _showProgressWheel: boolean; + + private NewProxyText: string = nls.localize('jobProxyToolbar-NewItem', "New Proxy"); + private RefreshText: string = nls.localize('jobProxyToolbar-Refresh', "Refresh"); + + constructor( + @Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface, + @Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef, + @Inject(forwardRef(() => ElementRef)) private _el: ElementRef, + @Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent, + @Inject(IJobManagementService) private _jobManagementService: IJobManagementService, + @Inject(IThemeService) private _themeService: IThemeService, + @Inject(ICommandService) private _commandService: ICommandService + ) { + this._isCloud = this._dashboardService.connectionManagementService.connectionInfo.serverInfo.isCloud; + } + + public layout() { + this._table.layout(new dom.Dimension(dom.getContentWidth(this._gridEl.nativeElement), dom.getContentHeight(this._gridEl.nativeElement))); + } + + ngAfterContentChecked() { + if (this.isVisible === false && this._gridEl.nativeElement.offsetParent !== null) { + this.isVisible = true; + if (!this.isInitialized) { + this._showProgressWheel = true; + this.onFirstVisible(false); + this.isInitialized = true; + } + } else if (this.isVisible === true && this._agentViewComponent.refresh === true) { + this._showProgressWheel = true; + this.onFirstVisible(false); + this.isRefreshing = true; + this._agentViewComponent.refresh = false; + } else if (this.isVisible === true && this._gridEl.nativeElement.offsetParent === null) { + this.isVisible = false; + } + } + + onFirstVisible(cached?: boolean) { + let self = this; + let columns = this.columns.map((column) => { + column.rerenderOnResize = true; + return column; + }); + let options = >{ + syncColumnCellResize: true, + enableColumnReorder: false, + rowHeight: ROW_HEIGHT, + enableCellNavigation: true, + forceFitColumns: true + }; + + this.dataView = new Slick.Data.DataView(); + + $(this._gridEl.nativeElement).empty(); + this._table = new Table(this._gridEl.nativeElement, undefined, columns, this.options); + this._table.grid.setData(this.dataView, true); + + let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; + this._jobManagementService.getProxies(ownerUri).then((result) => { + if (result && result.proxies) { + self.proxies = result.proxies; + self.onProxiesAvailable(result.proxies); + } + }); + } + + private onProxiesAvailable(proxies: sqlops.AgentProxyInfo[]) { + let items: any = proxies.map((item) => { + return { + id: item.id, + accountName: item.accountName, + credentialName: item.credentialName + }; + }); + + this.dataView.beginUpdate(); + this.dataView.setItems(items); + this.dataView.endUpdate(); + this._table.autosizeColumns(); + this._table.resizeCanvas(); + + this._showProgressWheel = false; + this._cd.detectChanges(); + } + + private openCreateProxyDialog() { + let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; + this._commandService.executeCommand('agent.openCreateProxyDialog', ownerUri); + } + + private refreshJobs() { + this._agentViewComponent.refresh = true; + } +} \ No newline at end of file diff --git a/src/sql/sqlops.d.ts b/src/sql/sqlops.d.ts index e1fa291f33..746d1c98dd 100644 --- a/src/sql/sqlops.d.ts +++ b/src/sql/sqlops.d.ts @@ -1211,6 +1211,7 @@ declare module 'sqlops' { export interface AgentAlertInfo { id: number; + name: string; delayBetweenResponses: number; eventDescriptionKeyword: string; eventSource: string; @@ -1327,15 +1328,15 @@ declare module 'sqlops' { } export interface AgentProxiesResult extends ResultStatus { - operators: AgentOperatorInfo[]; + proxies: AgentProxyInfo[]; } export interface CreateAgentProxyResult extends ResultStatus { - operator: AgentOperatorInfo; + proxy: AgentProxyInfo; } export interface UpdateAgentProxyResult extends ResultStatus { - operator: AgentOperatorInfo; + proxy: AgentProxyInfo; } export interface AgentJobSchedulesResult extends ResultStatus { diff --git a/src/sql/workbench/api/node/extHostDataProtocol.ts b/src/sql/workbench/api/node/extHostDataProtocol.ts index 6bbe831698..1d0e94e7cc 100644 --- a/src/sql/workbench/api/node/extHostDataProtocol.ts +++ b/src/sql/workbench/api/node/extHostDataProtocol.ts @@ -554,4 +554,25 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape { public $jobAction(handle: number, ownerUri: string, jobName: string, action: string): Thenable { return this._resolveProvider(handle).jobAction(ownerUri, jobName, action); } + + /** + * Get Agent Alerts list + */ + $getAlerts(handle: number, ownerUri: string): Thenable { + return this._resolveProvider(handle).getAlerts(ownerUri); + } + + /** + * Get Agent Oeprators list + */ + $getOperators(handle: number, ownerUri: string): Thenable { + return this._resolveProvider(handle).getOperators(ownerUri); + } + + /** + * Get Agent Proxies list + */ + $getProxies(handle: number, ownerUri: string): Thenable { + return this._resolveProvider(handle).getProxies(ownerUri); + } } diff --git a/src/sql/workbench/api/node/mainThreadDataProtocol.ts b/src/sql/workbench/api/node/mainThreadDataProtocol.ts index ba85edbec7..022207ddcf 100644 --- a/src/sql/workbench/api/node/mainThreadDataProtocol.ts +++ b/src/sql/workbench/api/node/mainThreadDataProtocol.ts @@ -344,6 +344,15 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape { }, jobAction(connectionUri: string, jobName: string, action: string): Thenable { return self._proxy.$jobAction(handle, connectionUri, jobName, action); + }, + getAlerts(connectionUri: string): Thenable { + return self._proxy.$getAlerts(handle, connectionUri); + }, + getOperators(connectionUri: string): Thenable { + return self._proxy.$getOperators(handle, connectionUri); + }, + getProxies(connectionUri: string): Thenable { + return self._proxy.$getProxies(handle, connectionUri); } }); diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts index 36830feb56..03d43f0456 100644 --- a/src/sql/workbench/api/node/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -337,6 +337,21 @@ export abstract class ExtHostDataProtocolShape { * Run an action on a Job */ $jobAction(handle: number, ownerUri: string, jobName: string, action: string): Thenable { throw ni(); } + + /** + * Get Agent Alerts list + */ + $getAlerts(handle: number, connectionUri: string): Thenable { throw ni(); } + + /** + * Get Agent Oeprators list + */ + $getOperators(handle: number, connectionUri: string): Thenable { throw ni(); } + + /** + * Get Agent Proxies list + */ + $getProxies(handle: number, connectionUri: string): Thenable { throw ni(); } } /**