diff --git a/extensions/agent/src/dialogs/agentDialog.ts b/extensions/agent/src/dialogs/agentDialog.ts index 7368904402..e42f4620c8 100644 --- a/extensions/agent/src/dialogs/agentDialog.ts +++ b/extensions/agent/src/dialogs/agentDialog.ts @@ -17,6 +17,7 @@ export abstract class AgentDialog { private static readonly CancelButtonText: string = localize('agentDialog.Cancel', 'Cancel'); protected _onSuccess: vscode.EventEmitter = new vscode.EventEmitter(); + protected _isOpen: boolean = false; public readonly onSuccess: vscode.Event = this._onSuccess.event; public dialog: azdata.window.Dialog; @@ -35,29 +36,34 @@ export abstract class AgentDialog { protected abstract async initializeDialog(dialog: azdata.window.Dialog); public async openDialog(dialogName?: string) { - let event = dialogName ? dialogName : null; - this.dialog = azdata.window.createModelViewDialog(this.title, event); + if (!this._isOpen) { + this._isOpen = true; + let event = dialogName ? dialogName : null; + this.dialog = azdata.window.createModelViewDialog(this.title, event); - await this.model.initialize(); + await this.model.initialize(); - await this.initializeDialog(this.dialog); + await this.initializeDialog(this.dialog); - this.dialog.okButton.label = AgentDialog.OkButtonText; - this.dialog.okButton.onClick(async () => await this.execute()); + 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()); + this.dialog.cancelButton.label = AgentDialog.CancelButtonText; + this.dialog.cancelButton.onClick(async () => await this.cancel()); - azdata.window.openDialog(this.dialog); + azdata.window.openDialog(this.dialog); + } } protected async execute() { this.updateModel(); await this.model.save(); + this._isOpen = false; this._onSuccess.fire(this.model); } protected async cancel() { + this._isOpen = false; } protected getActualConditionValue(checkbox: azdata.CheckBoxComponent, dropdown: azdata.DropDownComponent): azdata.JobCompletionActionCondition { @@ -77,4 +83,8 @@ export abstract class AgentDialog { } } } + + public get isOpen(): boolean { + return this._isOpen; + } } \ No newline at end of file diff --git a/extensions/agent/src/dialogs/jobDialog.ts b/extensions/agent/src/dialogs/jobDialog.ts index ca7fc661b9..d6dbb08efa 100644 --- a/extensions/agent/src/dialogs/jobDialog.ts +++ b/extensions/agent/src/dialogs/jobDialog.ts @@ -229,7 +229,7 @@ export class JobDialog extends AgentDialog { this.StepsTable_FailureColumnString ], data: data, - height: 650 + height: 500 }).component(); this.startStepDropdown = view.modelBuilder.dropDown().withProperties({ width: 180 }).component(); diff --git a/src/sql/platform/jobManagement/common/jobActions.ts b/src/sql/platform/jobManagement/common/jobActions.ts index 57943ccd59..e0e31d09b4 100644 --- a/src/sql/platform/jobManagement/common/jobActions.ts +++ b/src/sql/platform/jobManagement/common/jobActions.ts @@ -97,16 +97,16 @@ export class RunJobAction extends Action { } public run(context: IJobActionInfo): Promise { - let jobName = context.targetObject.name; + let jobName = context.targetObject.job.name; let ownerUri = context.ownerUri; let refreshAction = this.instantationService.createInstance(JobsRefreshAction); this.telemetryService.publicLog(TelemetryKeys.RunAgentJob); return new Promise((resolve, reject) => { - this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(result => { + this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(async (result) => { if (result.success) { let startMsg = nls.localize('jobSuccessfullyStarted', ': The job was successfully started.'); this.notificationService.info(jobName + startMsg); - refreshAction.run(context); + await refreshAction.run(context); resolve(true); } else { this.errorMessageService.showDialog(Severity.Error, errorLabel, result.errorMessage); @@ -137,9 +137,9 @@ export class StopJobAction extends Action { let refreshAction = this.instantationService.createInstance(JobsRefreshAction); this.telemetryService.publicLog(TelemetryKeys.StopAgentJob); return new Promise((resolve, reject) => { - this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(result => { + this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(async (result) => { if (result.success) { - refreshAction.run(context); + await refreshAction.run(context); let stopMsg = nls.localize('jobSuccessfullyStopped', ': The job was successfully stopped.'); this.notificationService.info(jobName + stopMsg); resolve(true); @@ -261,12 +261,12 @@ export class DeleteStepAction extends Action { label: DeleteStepAction.LABEL, run: () => { this._telemetryService.publicLog(TelemetryKeys.DeleteAgentJobStep); - self._jobService.deleteJobStep(actionInfo.ownerUri, actionInfo.targetObject).then(result => { + self._jobService.deleteJobStep(actionInfo.ownerUri, actionInfo.targetObject).then(async (result) => { if (!result || !result.success) { let errorMessage = nls.localize('jobaction.failedToDeleteStep', "Could not delete step '{0}'.\nError: {1}", step.stepName, result.errorMessage ? result.errorMessage : 'Unknown error'); self._errorMessageService.showDialog(Severity.Error, errorLabel, errorMessage); - refreshAction.run(actionInfo); + await refreshAction.run(actionInfo); } else { let successMessage = nls.localize('jobaction.deletedStep', 'The job step was successfully deleted'); self._notificationService.info(successMessage); diff --git a/src/sql/workbench/parts/jobManagement/electron-browser/alertsView.component.ts b/src/sql/workbench/parts/jobManagement/electron-browser/alertsView.component.ts index d455e924a0..f1fcad81e3 100644 --- a/src/sql/workbench/parts/jobManagement/electron-browser/alertsView.component.ts +++ b/src/sql/workbench/parts/jobManagement/electron-browser/alertsView.component.ts @@ -219,8 +219,8 @@ export class AlertsViewComponent extends JobManagementView implements OnInit, On ''; } - public openCreateAlertDialog() { + public async openCreateAlertDialog() { let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri; - this._commandService.executeCommand('agent.openAlertDialog', ownerUri, null, null); + await this._commandService.executeCommand('agent.openAlertDialog', ownerUri, null, null); } } diff --git a/src/sql/workbench/parts/jobManagement/electron-browser/jobHistory.component.ts b/src/sql/workbench/parts/jobManagement/electron-browser/jobHistory.component.ts index 441d396ced..1c886cee83 100644 --- a/src/sql/workbench/parts/jobManagement/electron-browser/jobHistory.component.ts +++ b/src/sql/workbench/parts/jobManagement/electron-browser/jobHistory.component.ts @@ -27,7 +27,7 @@ import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { JobManagementView } from 'sql/workbench/parts/jobManagement/electron-browser/jobManagementView'; +import { JobManagementView, JobActionContext } from 'sql/workbench/parts/jobManagement/electron-browser/jobManagementView'; import { TabChild } from 'sql/base/electron-browser/ui/panel/tab.component'; import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -67,6 +67,12 @@ export class JobHistoryComponent extends JobManagementView implements OnInit { private _agentJobInfo: azdata.AgentJobInfo; private _noJobsAvailable: boolean = false; + // Job Actions + private _editJobAction: EditJobAction; + private _runJobAction: RunJobAction; + private _stopJobAction: StopJobAction; + private _refreshAction: JobsRefreshAction; + private static readonly HEADING_HEIGHT: number = 24; constructor( @@ -102,9 +108,8 @@ export class JobHistoryComponent extends JobManagementView implements OnInit { // set base class elements this._visibilityElement = this._tableContainer; this._parentComponent = this._agentViewComponent; - - let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri; this._agentJobInfo = this._agentViewComponent.agentJobInfo; + this.initActionBar(); const self = this; this._treeController.onClick = (tree, element, event, origin = 'mouse') => { const payload = { origin: origin }; @@ -146,7 +151,6 @@ export class JobHistoryComponent extends JobManagementView implements OnInit { this._register(attachListStyler(this._tree, this.themeService)); this._tree.layout(dom.getContentHeight(this._tableContainer.nativeElement)); this._telemetryService.publicLog(TelemetryKeys.JobHistoryView); - this.initActionBar(); } private loadHistory() { @@ -165,21 +169,29 @@ export class JobHistoryComponent extends JobManagementView implements OnInit { this._agentViewComponent.agentJobInfo.alerts = this._jobCacheObject.getJobAlerts(jobId); this._agentJobInfo = this._agentViewComponent.agentJobInfo; if (result.histories.length > 0) { + self._noJobsAvailable = false; self._showPreviousRuns = true; self.buildHistoryTree(self, result.histories); - if (self._agentViewComponent.showHistory) { - self._cd.detectChanges(); - } } else { self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, result.histories); + self._noJobsAvailable = true; self._showPreviousRuns = false; } } else { + self._noJobsAvailable = true; self._showPreviousRuns = false; self._showSteps = false; - if (self._agentViewComponent.showHistory) { - self._cd.detectChanges(); - } + } + this._actionBar.context = { targetObject: { canEdit: true, job: this._agentJobInfo }, ownerUri: this.ownerUri, component: this }; + this._editJobAction.enabled = true; + this._actionBar.setContent([ + { action: this._runJobAction }, + { action: this._stopJobAction }, + { action: this._refreshAction }, + { action: this._editJobAction } + ]); + if (self._agentViewComponent.showHistory) { + self._cd.detectChanges(); } }); } @@ -234,8 +246,13 @@ export class JobHistoryComponent extends JobManagementView implements OnInit { private buildHistoryTree(self: any, jobHistories: azdata.AgentJobHistoryInfo[]) { self._treeController.jobHistories = jobHistories; - let jobHistoryRows = this._treeController.jobHistories.map(job => self.convertToJobHistoryRow(job)); - self._treeDataSource.data = jobHistoryRows; + let jobHistoryRows: JobHistoryRow[] = this._treeController.jobHistories.map(job => self.convertToJobHistoryRow(job)); + let sortedRows = jobHistoryRows.sort((row1, row2) => { + let date1 = new Date(row1.runDate).getTime(); + let date2 = new Date(row2.runDate).getTime(); + return date2 - date1; + }); + self._treeDataSource.data = sortedRows; self._tree.setInput(new JobHistoryModel()); self.agentJobHistoryInfo = self._treeController.jobHistories[0]; if (self.agentJobHistoryInfo) { @@ -294,23 +311,32 @@ export class JobHistoryComponent extends JobManagementView implements OnInit { } 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._agentViewComponent.agentJobInfo.jobSteps = this._jobCacheObject.getJobSteps(this._agentJobInfo.jobId); - this._agentViewComponent.agentJobInfo.jobSchedules = this._jobCacheObject.getJobSchedules(this._agentJobInfo.jobId); - this._agentViewComponent.agentJobInfo.alerts = this._jobCacheObject.getJobAlerts(this._agentJobInfo.jobId); - this._agentJobInfo = this._agentViewComponent.agentJobInfo; - this.buildHistoryTree(self, jobHistories); - this._actionBar.context = { targetObject: this._agentJobInfo, ownerUri: this.ownerUri, jobHistoryComponent: this }; - this._cd.detectChanges(); + if (jobHistories) { + if (jobHistories.length > 0) { + const self = this; + this._noJobsAvailable = false; + if (this._jobCacheObject.prevJobID === this._agentViewComponent.jobId || jobHistories[0].jobId === this._agentViewComponent.jobId) { + this._showPreviousRuns = true; + this._agentViewComponent.agentJobInfo.jobSteps = this._jobCacheObject.getJobSteps(this._agentJobInfo.jobId); + this._agentViewComponent.agentJobInfo.jobSchedules = this._jobCacheObject.getJobSchedules(this._agentJobInfo.jobId); + this._agentViewComponent.agentJobInfo.alerts = this._jobCacheObject.getJobAlerts(this._agentJobInfo.jobId); + this._agentJobInfo = this._agentViewComponent.agentJobInfo; + this.buildHistoryTree(self, jobHistories); + } + } else if (jobHistories.length === 0) { + this._showPreviousRuns = false; + this._showSteps = false; + this._noJobsAvailable = true; } - } else if (jobHistories && jobHistories.length === 0) { - this._showPreviousRuns = false; - this._showSteps = false; - this._noJobsAvailable = true; + this._editJobAction.enabled = true; + this._actionBar.setContent([ + { action: this._runJobAction }, + { action: this._stopJobAction }, + { action: this._refreshAction }, + { action: this._editJobAction } + ]); this._cd.detectChanges(); + } else { this.loadHistory(); } @@ -339,18 +365,20 @@ export class JobHistoryComponent extends JobManagementView implements OnInit { } protected initActionBar() { - let runJobAction = this.instantiationService.createInstance(RunJobAction); - let stopJobAction = this.instantiationService.createInstance(StopJobAction); - let editJobAction = this.instantiationService.createInstance(EditJobAction); - let refreshAction = this.instantiationService.createInstance(JobsRefreshAction); + this._runJobAction = this.instantiationService.createInstance(RunJobAction); + this._stopJobAction = this.instantiationService.createInstance(StopJobAction); + this._editJobAction = this.instantiationService.createInstance(EditJobAction); + this._refreshAction = this.instantiationService.createInstance(JobsRefreshAction); let taskbar = this.actionBarContainer.nativeElement; this._actionBar = new Taskbar(taskbar); - this._actionBar.context = { targetObject: this._agentJobInfo, ownerUri: this.ownerUri, component: this }; + this._editJobAction.enabled = !this.showProgressWheel(); + let targetObject: JobActionContext = { canEdit: !this.showProgressWheel(), job: this._agentJobInfo }; + this._actionBar.context = { targetObject: targetObject, ownerUri: this.ownerUri, component: this }; this._actionBar.setContent([ - { action: runJobAction }, - { action: stopJobAction }, - { action: refreshAction }, - { action: editJobAction } + { action: this._runJobAction }, + { action: this._stopJobAction }, + { action: this._refreshAction }, + { action: this._editJobAction } ]); } diff --git a/src/sql/workbench/parts/jobManagement/electron-browser/jobManagementView.ts b/src/sql/workbench/parts/jobManagement/electron-browser/jobManagementView.ts index 784c8eb0d6..6cf84ab929 100644 --- a/src/sql/workbench/parts/jobManagement/electron-browser/jobManagementView.ts +++ b/src/sql/workbench/parts/jobManagement/electron-browser/jobManagementView.ts @@ -79,10 +79,10 @@ export abstract class JobManagementView extends TabChild implements AfterContent let actions = this.getTableActions(targetObject); if (actions) { let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri; - let actionContext = { + let actionContext: IJobActionInfo = { ownerUri: ownerUri, - targetObject: targetObject - + targetObject: targetObject, + component: this }; let anchor = { x: event.pageX + 1, y: event.pageY }; @@ -117,7 +117,7 @@ export abstract class JobManagementView extends TabChild implements AfterContent { action: refreshAction }, { action: newAction } ]); - let context: IJobActionInfo = { component: this }; + let context: IJobActionInfo = { component: this, ownerUri: this._commonService.connectionManagementService.connectionInfo.ownerUri }; this._actionBar.context = context; } diff --git a/src/sql/workbench/parts/jobManagement/electron-browser/jobsView.component.ts b/src/sql/workbench/parts/jobManagement/electron-browser/jobsView.component.ts index 1108c7e1b4..be7e3c1518 100644 --- a/src/sql/workbench/parts/jobManagement/electron-browser/jobsView.component.ts +++ b/src/sql/workbench/parts/jobManagement/electron-browser/jobsView.component.ts @@ -605,9 +605,11 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe let item = self.dataView.getItemById(job.jobId + '.error'); let noStepsMessage = nls.localize('jobsView.noSteps', 'No Steps available for this job.'); let errorMessage = lastJobHistory ? lastJobHistory.message : noStepsMessage; - item['name'] = nls.localize('jobsView.error', 'Error: ') + errorMessage; - self._agentViewComponent.setExpanded(job.jobId, item['name']); - self.dataView.updateItem(job.jobId + '.error', item); + if (item) { + item['name'] = nls.localize('jobsView.error', 'Error: ') + errorMessage; + self._agentViewComponent.setExpanded(job.jobId, item['name']); + self.dataView.updateItem(job.jobId + '.error', item); + } } } } diff --git a/src/sql/workbench/parts/jobManagement/electron-browser/operatorsView.component.ts b/src/sql/workbench/parts/jobManagement/electron-browser/operatorsView.component.ts index 2d32667162..e1623a1ea3 100644 --- a/src/sql/workbench/parts/jobManagement/electron-browser/operatorsView.component.ts +++ b/src/sql/workbench/parts/jobManagement/electron-browser/operatorsView.component.ts @@ -215,8 +215,8 @@ export class OperatorsViewComponent extends JobManagementView implements OnInit, ''; } - public openCreateOperatorDialog() { + public async openCreateOperatorDialog() { let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri; - this._commandService.executeCommand('agent.openOperatorDialog', ownerUri); + await this._commandService.executeCommand('agent.openOperatorDialog', ownerUri); } } diff --git a/src/sql/workbench/parts/jobManagement/electron-browser/proxiesView.component.ts b/src/sql/workbench/parts/jobManagement/electron-browser/proxiesView.component.ts index e0819fb92e..401dba540e 100644 --- a/src/sql/workbench/parts/jobManagement/electron-browser/proxiesView.component.ts +++ b/src/sql/workbench/parts/jobManagement/electron-browser/proxiesView.component.ts @@ -221,9 +221,9 @@ export class ProxiesViewComponent extends JobManagementView implements OnInit, O public openCreateProxyDialog() { let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri; - this._jobManagementService.getCredentials(ownerUri).then((result) => { + this._jobManagementService.getCredentials(ownerUri).then(async (result) => { if (result && result.credentials) { - this._commandService.executeCommand('agent.openProxyDialog', ownerUri, undefined, result.credentials); + await this._commandService.executeCommand('agent.openProxyDialog', ownerUri, undefined, result.credentials); } }); }