Agent: Edit Job improvements (#2721)

* fixed right click context menu bug in jobs view

* added stepInfo and edit job WIP

* show jobs in job edit

* added schedule description on select schedule

* fetch schedules during history and show in edit job

* added alerts to job histories and show in edit

* made history calls async

* filter menus now close when esc is pressed

* fixed bug where clicking on error row wouldnt populate job details
This commit is contained in:
Aditya Bist
2018-10-04 13:52:25 -07:00
committed by GitHub
parent 0693080630
commit b097b54792
10 changed files with 217 additions and 43 deletions

View File

@@ -39,16 +39,26 @@ export class HeaderFilter {
this.handler.subscribe(this.grid.onHeaderCellRendered, (e, args) => this.handleHeaderCellRendered(e , args))
.subscribe(this.grid.onBeforeHeaderCellDestroy, (e, args) => this.handleBeforeHeaderCellDestroy(e, args))
.subscribe(this.grid.onClick, (e) => this.handleBodyMouseDown)
.subscribe(this.grid.onColumnsResized, () => this.columnsResized());
.subscribe(this.grid.onColumnsResized, () => this.columnsResized())
.subscribe(this.grid.onKeyDown, (e) => this.handleKeyDown);
this.grid.setColumns(this.grid.getColumns());
$(document.body).bind('mousedown', this.handleBodyMouseDown);
$(document.body).bind('keydown', this.handleKeyDown);
}
public destroy() {
this.handler.unsubscribeAll();
$(document.body).unbind('mousedown', this.handleBodyMouseDown);
$(document.body).unbind('keydown', this.handleKeyDown);
}
private handleKeyDown = (e) => {
if (this.$menu && (e.key === 'Escape' || e.keyCode === 27)) {
this.hideMenu();
e.preventDefault();
e.stopPropagation();
}
}
private handleBodyMouseDown = (e) => {

View File

@@ -17,7 +17,9 @@ export class JobManagementUtilities {
switch(status) {
case(0): return nls.localize('agentUtilities.failed','Failed');
case(1): return nls.localize('agentUtilities.succeeded', 'Succeeded');
case(3): return nls.localize('agentUtilities.canceled', 'Canceled');
case(2): return nls.localize('agentUtilities.retry', 'Retry');
case(3): return nls.localize('agentUtilities.canceled', 'Cancelled');
case(4): return nls.localize('agentUtilities.inProgress', 'In Progress');
case(5): return nls.localize('agentUtilities.statusUnknown', 'Status Unknown');
default: return nls.localize('agentUtilities.statusUnknown', 'Status Unknown');
}

View File

@@ -185,8 +185,8 @@ export class JobHistoryComponent extends JobManagementView implements OnInit {
stepViewRow.runStatus = jobStepStatus ? JobManagementUtilities.convertToStatusString(0) :
JobManagementUtilities.convertToStatusString(step.runStatus);
self._runStatus = JobManagementUtilities.convertToStatusString(self.agentJobHistoryInfo.runStatus);
stepViewRow.stepName = step.stepName;
stepViewRow.stepID = step.stepId.toString();
stepViewRow.stepName = step.stepDetails.stepName;
stepViewRow.stepId = step.stepDetails.id.toString();
return stepViewRow;
});
this._showSteps = self._stepRows.length > 0;

View File

@@ -66,7 +66,7 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit,
}, { verticalScrollMode: ScrollbarVisibility.Visible });
this._register(attachListStyler(this._tree, this.themeService));
}
this._tree.layout(JobStepsViewComponent._pageSize);
this._tree.layout(500);
this._tree.setInput(new JobStepsViewModel());
$('jobstepsview-component .steps-tree .monaco-tree').attr('tabIndex', '-1');
$('jobstepsview-component .steps-tree .monaco-tree-row').attr('tabIndex', '0');
@@ -74,8 +74,6 @@ export class JobStepsViewComponent extends JobManagementView implements OnInit,
}
ngOnInit() {
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
this._tree = new Tree(this._tableContainer.nativeElement, {
controller: this._treeController,
dataSource: this._treeDataSource,

View File

@@ -14,7 +14,7 @@ import { AgentJobHistoryInfo } from 'sqlops';
import { JobManagementUtilities } from 'sql/parts/jobManagement/common/jobManagementUtilities';
export class JobStepsViewRow {
public stepID: string;
public stepId: string;
public stepName: string;
public message: string;
public rowID: string = generateUuid();
@@ -121,7 +121,7 @@ export class JobStepsViewRenderer implements tree.IRenderer {
public renderElement(tree: tree.ITree, element: JobStepsViewRow, templateId: string, templateData: IListTemplate): void {
let stepIdCol: HTMLElement = DOM.$('div');
stepIdCol.className = 'tree-id-col';
stepIdCol.innerText = element.stepID;
stepIdCol.innerText = element.stepId;
let stepNameCol: HTMLElement = DOM.$('div');
stepNameCol.className = 'tree-name-col';
stepNameCol.innerText = element.stepName;

View File

@@ -37,6 +37,7 @@ import { IDashboardService } from 'sql/services/dashboard/common/dashboardServic
import { escape } from 'sql/base/common/strings';
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { tableBackground, cellBackground, tableHoverBackground, jobsHeadingBackground, cellBorderColor } from 'sql/common/theme/colors';
import { JobStepsViewRow } from 'sql/parts/jobManagement/views/jobStepsViewTree';
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
export const ROW_HEIGHT: number = 45;
@@ -171,7 +172,6 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
});
this.rowDetail = rowDetail;
columns.unshift(this.rowDetail.getColumnDefinition());
let filterPlugin = new HeaderFilter({}, this._themeService);
this.filterPlugin = filterPlugin;
$(this._gridEl.nativeElement).empty();
@@ -475,7 +475,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
case ('Failed'):
resultIndicatorClass = 'jobview-jobnameindicatorfailure';
break;
case ('Canceled'):
case ('Cancelled'):
resultIndicatorClass = 'jobview-jobnameindicatorcancel';
break;
case ('Status Unknown'):
@@ -523,17 +523,15 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
this.rowDetail.applyTemplateNewLineHeight(item, true);
}
private loadJobHistories(): void {
private async loadJobHistories() {
if (this.jobs) {
let erroredJobs = 0;
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
let separatedJobs = this.separateFailingJobs();
// grab histories of the failing jobs first
// so they can be expanded quicker
let failing = separatedJobs[0];
this.curateJobHistory(failing, ownerUri);
let passing = separatedJobs[1];
this.curateJobHistory(passing, ownerUri);
Promise.all([this.curateJobHistory(failing, ownerUri), this.curateJobHistory(passing, ownerUri)]);
}
}
@@ -578,11 +576,10 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
return job;
}
private curateJobHistory(jobs: sqlops.AgentJobInfo[], ownerUri: string) {
private async curateJobHistory(jobs: sqlops.AgentJobInfo[], ownerUri: string) {
const self = this;
for (let i = 0; i < jobs.length; i++) {
let job = jobs[i];
this._jobManagementService.getJobHistory(ownerUri, job.jobId).then((result) => {
jobs.forEach(async (job) => {
await this._jobManagementService.getJobHistory(ownerUri, job.jobId).then((result) => {
if (result && result.jobs) {
self.jobHistories[job.jobId] = result.jobs;
self._jobCacheObject.setJobHistory(job.jobId, result.jobs);
@@ -600,7 +597,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
}
}
});
}
});
}
private createJobChart(jobId: string, jobHistories: sqlops.AgentJobHistoryInfo[]): void {
@@ -848,6 +845,40 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
return TPromise.as(actions);
}
protected convertStepsToStepInfos(steps: sqlops.AgentJobStep[], job: sqlops.AgentJobInfo): sqlops.AgentJobStepInfo[] {
let result = [];
steps.forEach(step => {
let stepInfo: sqlops.AgentJobStepInfo = {
jobId: job.jobId,
jobName: job.name,
script: null,
scriptName: null,
stepName: step.stepName,
subSystem: null,
id: +step.stepId,
failureAction: null,
successAction: null,
failStepId: null,
successStepId: null,
command: null,
commandExecutionSuccessCode: null,
databaseName: null,
databaseUserName: null,
server: null,
outputFileName: null,
appendToLogFile: null,
appendToStepHist: null,
writeLogToTable: null,
appendLogToTable: null,
retryAttempts: null,
retryInterval: null,
proxyName: null
};
result.push(stepInfo);
});
return result;
}
protected getCurrentTableObject(rowIndex: number): any {
let data = this._table.grid.getData();
if (!data || rowIndex >= data.getLength()) {
@@ -855,10 +886,60 @@ export class JobsViewComponent extends JobManagementView implements OnInit {
}
let jobId = data.getItem(rowIndex).jobId;
let job = this.jobs.filter(job => {
if (!jobId) {
// if we couldn't find the ID, check if it's an
// error row
let isErrorRow: boolean = data.getItem(rowIndex).id.indexOf('error') >= 0;
if (isErrorRow) {
jobId = data.getItem(rowIndex - 1).jobId;
}
}
let job: sqlops.AgentJobInfo[] = this.jobs.filter(job => {
return job.jobId === jobId;
});
let jobHistories = this.jobHistories[jobId];
let steps: sqlops.AgentJobStep[] = undefined;
let schedules: sqlops.AgentJobScheduleInfo[] = undefined;
let alerts: sqlops.AgentAlertInfo[] = undefined;
if (jobHistories && jobHistories[jobHistories.length-1]) {
// add steps
steps = jobHistories[jobHistories.length-1].steps;
if (steps && steps.length > 0) {
if (!job[0].JobSteps) {
job[0].JobSteps = [];
}
if (job[0].JobSteps.length !== steps.length) {
job[0].JobSteps = [];
steps.forEach(step => {
job[0].JobSteps.push(step.stepDetails);
});
}
}
// add schedules
schedules = jobHistories[jobHistories.length-1].schedules;
if (schedules && schedules.length > 0) {
if (!job[0].JobSchedules) {
job[0].JobSchedules = [];
}
if (job[0].JobSchedules.length !== schedules.length) {
job[0].JobSchedules = [];
schedules.forEach(schedule => {
job[0].JobSchedules.push(schedule);
});
}
}
// add alerts
alerts = jobHistories[jobHistories.length-1].alerts;
if (!job[0].Alerts) {
job[0].Alerts = [];
}
if (job[0].Alerts.length !== alerts.length) {
job[0].Alerts = [];
alerts.forEach(alert => {
job[0].Alerts.push(alert);
});
}
}
return job && job.length > 0 ? job[0] : undefined;
}

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

@@ -1313,6 +1313,7 @@ declare module 'sqlops' {
jobCount: number;
activeEndDate: string;
scheduleUid: string;
description: string;
}
export interface AgentJobStep {
@@ -1322,6 +1323,7 @@ declare module 'sqlops' {
message: string;
runDate: string;
runStatus: number;
stepDetails: AgentJobStepInfo;
}
export interface AgentJobStepInfo {
@@ -1369,6 +1371,8 @@ declare module 'sqlops' {
retriesAttempted: string;
server: string;
steps: AgentJobStep[];
schedules: AgentJobScheduleInfo[];
alerts: AgentAlertInfo[];
}
export interface AgentProxyInfo {