mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Feature/agent finishes (#1237)
* added icons for steps and fixed bugs in jobs view page * put svgs in common folder * added steps header logo * added full path for import * changed codes to text * removed cat id, changed bools to yes/no and fixed steps theme * localized the strings * set the jobs table column widths * added indicators for failure, unknown and canceled jobs in jobs view * fixed jobs panel style and jobs view scrolling * fixed jobs view page styling * fixed job history tree size rows * made error messages copy-able * made job history tree work with keyboard * fixed previous runs header * added space between date and status in job history list * fixed history list min width and set scrolling for jobs history page * added scrolling when app is resized * added scrollbars and tooltip for name * added error handling, tab handling and other issues
This commit is contained in:
committed by
Karl Burtram
parent
f199c7a63c
commit
3b90530717
@@ -269,10 +269,22 @@ export class RowDetailView {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getErrorItem(parent, offset) {
|
||||||
|
let item: any = {};
|
||||||
|
item.id = parent.id + '.' + offset;
|
||||||
|
item._collapsed = true;
|
||||||
|
item._isPadding = false;
|
||||||
|
item._parent = parent;
|
||||||
|
item._offset = offset;
|
||||||
|
item.jobId = parent.jobId;
|
||||||
|
item.name = parent.message ? parent.message : 'Error';
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
//create the detail ctr node. this belongs to the dev & can be custom-styled as per
|
//create the detail ctr node. this belongs to the dev & can be custom-styled as per
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
public applyTemplateNewLineHeight(item) {
|
public applyTemplateNewLineHeight(item, showError = false) {
|
||||||
// the height seems to be calculated by the template row count (how many line of items does the template have)
|
// the height seems to be calculated by the template row count (how many line of items does the template have)
|
||||||
let rowCount = this._options.panelRows;
|
let rowCount = this._options.panelRows;
|
||||||
|
|
||||||
@@ -284,10 +296,13 @@ export class RowDetailView {
|
|||||||
|
|
||||||
let idxParent = this._dataView.getIdxById(item.id);
|
let idxParent = this._dataView.getIdxById(item.id);
|
||||||
for (let idx = 1; idx <= item._sizePadding; idx++) {
|
for (let idx = 1; idx <= item._sizePadding; idx++) {
|
||||||
|
if (showError) {
|
||||||
|
this._dataView.insertItem(idxParent + idx, this.getErrorItem(item, 'error'));
|
||||||
|
} else {
|
||||||
this._dataView.insertItem(idxParent + idx, this.getPaddingItem(item, idx));
|
this._dataView.insertItem(idxParent + idx, this.getPaddingItem(item, idx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getColumnDefinition() {
|
public getColumnDefinition() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export class AgentViewComponent {
|
|||||||
private _jobId: string = null;
|
private _jobId: string = null;
|
||||||
private _agentJobInfo: AgentJobInfo = null;
|
private _agentJobInfo: AgentJobInfo = null;
|
||||||
private _refresh: boolean = undefined;
|
private _refresh: boolean = undefined;
|
||||||
|
private _expanded: Map<string, string>;
|
||||||
|
|
||||||
public jobsIconClass: string = 'jobsview-icon';
|
public jobsIconClass: string = 'jobsview-icon';
|
||||||
|
|
||||||
@@ -50,6 +51,7 @@ export class AgentViewComponent {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef){
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef){
|
||||||
|
this._expanded = new Map<string, string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,6 +73,10 @@ export class AgentViewComponent {
|
|||||||
return this._refresh;
|
return this._refresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get expanded(): Map<string, string> {
|
||||||
|
return this._expanded;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public Setters
|
* Public Setters
|
||||||
*/
|
*/
|
||||||
@@ -94,4 +100,8 @@ export class AgentViewComponent {
|
|||||||
this._refresh = value;
|
this._refresh = value;
|
||||||
this._cd.detectChanges();
|
this._cd.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setExpanded(jobId: string, errorMessage: string) {
|
||||||
|
this._expanded.set(jobId, errorMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,15 @@ jobhistory-component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#jobsDiv .jobview-grid {
|
#jobsDiv .jobview-grid {
|
||||||
padding-top: 15px;
|
height: 96%;
|
||||||
height: 100%;
|
|
||||||
width : 100%;
|
width : 100%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#jobsDiv .jobview-grid > .monaco-table > div[class^="slickgrid_"] {
|
||||||
|
overflow: scroll !important;
|
||||||
|
}
|
||||||
|
|
||||||
.vs-dark #jobsDiv .slick-header-column {
|
.vs-dark #jobsDiv .slick-header-column {
|
||||||
background: #333333 !important;
|
background: #333333 !important;
|
||||||
}
|
}
|
||||||
@@ -54,7 +57,6 @@ jobhistory-component {
|
|||||||
border-right: transparent !important;
|
border-right: transparent !important;
|
||||||
border-left: transparent !important;
|
border-left: transparent !important;
|
||||||
line-height: 33px !important;
|
line-height: 33px !important;
|
||||||
vertical-align: middle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#jobsDiv .jobview-joblist {
|
#jobsDiv .jobview-joblist {
|
||||||
@@ -92,6 +94,25 @@ jobhistory-component {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#jobsDiv .job-with-error {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#jobsDiv .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.error-row {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.error-row {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
#jobsDiv .jobview-splitter {
|
#jobsDiv .jobview-splitter {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -156,3 +177,9 @@ agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.ev
|
|||||||
agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.odd > .slick-cell {
|
agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.odd > .slick-cell {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jobsview-component .jobview-grid > .monaco-table .slick-viewport > .grid-canvas
|
||||||
|
|
||||||
|
.vs-dark .jobview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
|
||||||
|
border-left: 1px dotted white;
|
||||||
|
}
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
<!-- Job History details -->
|
<!-- Job History details -->
|
||||||
<div class='history-details'>
|
<div class='history-details'>
|
||||||
<!-- Previous run list -->
|
<!-- Previous run list -->
|
||||||
<div style="width: 20%; overflow-y: scroll">
|
<div style="min-width: 275px">
|
||||||
<table *ngIf="_showPreviousRuns === true">
|
<table *ngIf="_showPreviousRuns === true">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="date-column">
|
<td class="date-column">
|
||||||
@@ -93,11 +93,11 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<h3 *ngIf="_showPreviousRuns === false">No Previous Runs Available</h3>
|
<h3 *ngIf="_showPreviousRuns === false" style="text-align: center">No Previous Runs Available</h3>
|
||||||
<div #table class="step-table prev-run-list" style="position: relative; height: 100%; width: 100%"></div>
|
<div #table class="step-table prev-run-list" style="position: relative; height: 100%; width: 100%"></div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Job Steps -->
|
<!-- Job Steps -->
|
||||||
<div class="job-steps">
|
<div class="job-steps" id="job-steps">
|
||||||
<h1 class="job-heading">
|
<h1 class="job-heading">
|
||||||
{{agentJobHistoryInfo?.runDate}}
|
{{agentJobHistoryInfo?.runDate}}
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import 'vs/css!./jobHistory';
|
import 'vs/css!./jobHistory';
|
||||||
|
|
||||||
import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
||||||
import { AgentJobHistoryInfo, AgentJobInfo } from 'sqlops';
|
import { AgentJobHistoryInfo, AgentJobInfo } from 'sqlops';
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
@@ -27,6 +26,8 @@ import { JobStepsViewComponent } from 'sql/parts/jobManagement/views/jobStepsVie
|
|||||||
import { JobStepsViewRow } from './jobStepsViewTree';
|
import { JobStepsViewRow } from './jobStepsViewTree';
|
||||||
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
|
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
|
||||||
import { AgentJobUtilities } from '../common/agentJobUtilities';
|
import { AgentJobUtilities } from '../common/agentJobUtilities';
|
||||||
|
import { ITreeOptions } from 'vs/base/parts/tree/browser/tree';
|
||||||
|
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||||
|
|
||||||
export const DASHBOARD_SELECTOR: string = 'jobhistory-component';
|
export const DASHBOARD_SELECTOR: string = 'jobhistory-component';
|
||||||
|
|
||||||
@@ -84,7 +85,9 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
this._jobCacheObject.serverName = serverName;
|
this._jobCacheObject.serverName = serverName;
|
||||||
this._jobManagementService.addToCache(serverName, this._jobCacheObject);
|
this._jobManagementService.addToCache(serverName, this._jobCacheObject);
|
||||||
}
|
}
|
||||||
|
$('#accordion').keypress(e => {
|
||||||
|
let meme = e;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -112,15 +115,17 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
this._treeController.onKeyDown = (tree, event) => {
|
this._treeController.onKeyDown = (tree, event) => {
|
||||||
this._treeController.onKeyDownWrapper(tree, event);
|
this._treeController.onKeyDownWrapper(tree, event);
|
||||||
let element = tree.getFocus();
|
let element = tree.getFocus();
|
||||||
|
if (element) {
|
||||||
self.setStepsTree(element);
|
self.setStepsTree(element);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
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,
|
||||||
filter: this._treeFilter,
|
filter: this._treeFilter,
|
||||||
renderer: this._treeRenderer
|
renderer: this._treeRenderer
|
||||||
});
|
}, {verticalScrollMode: ScrollbarVisibility.Visible});
|
||||||
this._register(attachListStyler(this._tree, this.bootstrapService.themeService));
|
this._register(attachListStyler(this._tree, this.bootstrapService.themeService));
|
||||||
this._tree.layout(1024);
|
this._tree.layout(1024);
|
||||||
}
|
}
|
||||||
@@ -154,7 +159,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadHistory() {
|
private loadHistory() {
|
||||||
const self = this;
|
const self = this;
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
||||||
this._jobManagementService.getJobHistory(ownerUri, this._agentViewComponent.jobId).then((result) => {
|
this._jobManagementService.getJobHistory(ownerUri, this._agentViewComponent.jobId).then((result) => {
|
||||||
@@ -172,7 +177,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
|||||||
} else {
|
} else {
|
||||||
self._showPreviousRuns = false;
|
self._showPreviousRuns = false;
|
||||||
self._showSteps = false;
|
self._showSteps = false;
|
||||||
this._cd.detectChanges();
|
self._cd.detectChanges();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ table.step-list tr.step-row td {
|
|||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-details > .job-steps {
|
.history-details > .job-steps {
|
||||||
@@ -236,3 +237,7 @@ table.step-list tr.step-row td {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
min-height: 40px !important;
|
min-height: 40px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jobhistory-component .history-details .step-table.prev-run-list .monaco-scrollable-element {
|
||||||
|
overflow-y: scroll !important;
|
||||||
|
}
|
||||||
@@ -143,7 +143,7 @@ export class JobHistoryRenderer implements tree.IRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public renderElement(tree: tree.ITree, element: JobHistoryRow, templateId: string, templateData: IListTemplate): void {
|
public renderElement(tree: tree.ITree, element: JobHistoryRow, templateId: string, templateData: IListTemplate): void {
|
||||||
templateData.label.innerText = element.runDate + '\t\t\t' + 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 = ' job-passed';
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { AgentJobHistoryInfo } from 'sqlops';
|
|||||||
import { JobStepsViewController, JobStepsViewDataSource, JobStepsViewFilter,
|
import { JobStepsViewController, JobStepsViewDataSource, JobStepsViewFilter,
|
||||||
JobStepsViewRenderer, JobStepsViewRow, JobStepsViewModel} from 'sql/parts/jobManagement/views/jobStepsViewTree';
|
JobStepsViewRenderer, JobStepsViewRow, 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 { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||||
|
|
||||||
export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component';
|
export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component';
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo
|
|||||||
dataSource: this._treeDataSource,
|
dataSource: this._treeDataSource,
|
||||||
filter: this._treeFilter,
|
filter: this._treeFilter,
|
||||||
renderer: this._treeRenderer
|
renderer: this._treeRenderer
|
||||||
});
|
}, {verticalScrollMode: ScrollbarVisibility.Visible});
|
||||||
this._register(attachListStyler(this._tree, this.bootstrapService.themeService));
|
this._register(attachListStyler(this._tree, this.bootstrapService.themeService));
|
||||||
}
|
}
|
||||||
this._tree.layout(JobStepsViewComponent._pageSize);
|
this._tree.layout(JobStepsViewComponent._pageSize);
|
||||||
@@ -74,7 +75,7 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo
|
|||||||
dataSource: this._treeDataSource,
|
dataSource: this._treeDataSource,
|
||||||
filter: this._treeFilter,
|
filter: this._treeFilter,
|
||||||
renderer: this._treeRenderer
|
renderer: this._treeRenderer
|
||||||
});
|
}, {verticalScrollMode: ScrollbarVisibility.Visible});
|
||||||
this._register(attachListStyler(this._tree, this.bootstrapService.themeService));
|
this._register(attachListStyler(this._tree, this.bootstrapService.themeService));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import * as tree from 'vs/base/parts/tree/browser/tree';
|
|||||||
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
|
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
|
||||||
import { Promise, TPromise } from 'vs/base/common/winjs.base';
|
import { Promise, TPromise } from 'vs/base/common/winjs.base';
|
||||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
|
||||||
import { IAction } from 'vs/base/common/actions';
|
import { IAction } from 'vs/base/common/actions';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
-->
|
-->
|
||||||
<div class="job-heading-container">
|
<div class="job-heading-container">
|
||||||
<h1 class="job-heading">Jobs</h1>
|
<h1 class="job-heading" *ngIf="_isCloud === false">Jobs</h1>
|
||||||
|
<h1 class="job-heading" *ngIf="_isCloud === true">No Jobs Available</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div #jobsgrid class="jobview-grid"></div>
|
<div #jobsgrid class="jobview-grid"></div>
|
||||||
@@ -9,7 +9,6 @@ import 'vs/css!sql/parts/grid/media/styles';
|
|||||||
import 'vs/css!sql/parts/grid/media/slick.grid';
|
import 'vs/css!sql/parts/grid/media/slick.grid';
|
||||||
import 'vs/css!sql/parts/grid/media/slickGrid';
|
import 'vs/css!sql/parts/grid/media/slickGrid';
|
||||||
import 'vs/css!../common/media/jobs';
|
import 'vs/css!../common/media/jobs';
|
||||||
import 'vs/css!../common/media/detailview';
|
|
||||||
|
|
||||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core';
|
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, AfterContentChecked } from '@angular/core';
|
||||||
import * as Utils from 'sql/parts/connection/common/utils';
|
import * as Utils from 'sql/parts/connection/common/utils';
|
||||||
@@ -51,19 +50,19 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
private _disposables = new Array<vscode.Disposable>();
|
private _disposables = new Array<vscode.Disposable>();
|
||||||
|
|
||||||
private columns: Array<Slick.Column<any>> = [
|
private columns: Array<Slick.Column<any>> = [
|
||||||
{ name: nls.localize('jobColumns.name','Name'), field: 'name', formatter: this.renderName, width: 200 },
|
{ name: nls.localize('jobColumns.name','Name'), field: 'name', formatter: this.renderName, width: 200, id: 'name' },
|
||||||
{ name: nls.localize('jobColumns.lastRun','Last Run'), field: 'lastRun', minWidth: 150 },
|
{ name: nls.localize('jobColumns.lastRun','Last Run'), field: 'lastRun', minWidth: 150, id: 'lastRun' },
|
||||||
{ name: nls.localize('jobColumns.nextRun','Next Run'), field: 'nextRun', minWidth: 150 },
|
{ name: nls.localize('jobColumns.nextRun','Next Run'), field: 'nextRun', minWidth: 150, id: 'nextRun' },
|
||||||
{ name: nls.localize('jobColumns.enabled','Enabled'), field: 'enabled', minWidth: 70 },
|
{ name: nls.localize('jobColumns.enabled','Enabled'), field: 'enabled', minWidth: 70, id: 'enabled' },
|
||||||
{ name: nls.localize('jobColumns.status','Status'), field: 'currentExecutionStatus', minWidth: 60 },
|
{ name: nls.localize('jobColumns.status','Status'), field: 'currentExecutionStatus', minWidth: 60, id: 'currentExecutionStatus' },
|
||||||
{ name: nls.localize('jobColumns.category','Category'), field: 'category', minWidth: 150 },
|
{ name: nls.localize('jobColumns.category','Category'), field: 'category', minWidth: 150, id: 'category' },
|
||||||
{ name: nls.localize('jobColumns.runnable','Runnable'), field: 'runnable', minWidth: 50 },
|
{ name: nls.localize('jobColumns.runnable','Runnable'), field: 'runnable', minWidth: 50, id: 'runnable' },
|
||||||
{ name: nls.localize('jobColumns.schedule','Schedule'), field: 'hasSchedule', minWidth: 50 },
|
{ name: nls.localize('jobColumns.schedule','Schedule'), field: 'hasSchedule', minWidth: 50, id: 'hasSchedule' },
|
||||||
{ name: nls.localize('jobColumns.lastRunOutcome', 'Last Run Outcome'), field: 'lastRunOutcome', minWidth: 150 },
|
{ name: nls.localize('jobColumns.lastRunOutcome', 'Last Run Outcome'), field: 'lastRunOutcome', minWidth: 150, id: 'lastRunOutcome' },
|
||||||
];
|
];
|
||||||
|
|
||||||
private rowDetail: any;
|
private rowDetail: RowDetailView;
|
||||||
private dataView: any;
|
private dataView: Slick.Data.DataView<any>;
|
||||||
|
|
||||||
@ViewChild('jobsgrid') _gridEl: ElementRef;
|
@ViewChild('jobsgrid') _gridEl: ElementRef;
|
||||||
private isVisible: boolean = false;
|
private isVisible: boolean = false;
|
||||||
@@ -72,6 +71,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
public jobs: sqlops.AgentJobInfo[];
|
public jobs: sqlops.AgentJobInfo[];
|
||||||
public jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = Object.create(null);
|
public jobHistories: { [jobId: string]: sqlops.AgentJobHistoryInfo[]; } = Object.create(null);
|
||||||
private _serverName: string;
|
private _serverName: string;
|
||||||
|
private _isCloud: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(BOOTSTRAP_SERVICE_ID) private bootstrapService: IBootstrapService,
|
@Inject(BOOTSTRAP_SERVICE_ID) private bootstrapService: IBootstrapService,
|
||||||
@@ -91,6 +91,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
this._jobCacheObject.serverName = this._serverName;
|
this._jobCacheObject.serverName = this._serverName;
|
||||||
this._jobManagementService.addToCache(this._serverName, this._jobCacheObject);
|
this._jobManagementService.addToCache(this._serverName, this._jobCacheObject);
|
||||||
}
|
}
|
||||||
|
this._isCloud = this._dashboardService.connectionManagementService.connectionInfo.serverInfo.isCloud;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentChecked() {
|
ngAfterContentChecked() {
|
||||||
@@ -126,14 +127,23 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
syncColumnCellResize: true,
|
syncColumnCellResize: true,
|
||||||
enableColumnReorder: false,
|
enableColumnReorder: false,
|
||||||
rowHeight: 45,
|
rowHeight: 45,
|
||||||
enableCellNavigation: true,
|
enableCellNavigation: true
|
||||||
autoHeight: false,
|
|
||||||
forceFitColumns: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dataView = new Slick.Data.DataView({ inlineFilters: false });
|
this.dataView = new Slick.Data.DataView({ inlineFilters: false });
|
||||||
|
|
||||||
this.rowDetail = new RowDetailView({});
|
let rowDetail = new RowDetailView({
|
||||||
|
cssClass: '_detail_selector',
|
||||||
|
process: (job) => {
|
||||||
|
(<any>rowDetail).onAsyncResponse.notify({
|
||||||
|
'itemDetail': job
|
||||||
|
}, undefined, this);
|
||||||
|
},
|
||||||
|
useRowClick: false,
|
||||||
|
panelRows: 1
|
||||||
|
});
|
||||||
|
this.rowDetail = rowDetail;
|
||||||
|
|
||||||
columns.unshift(this.rowDetail.getColumnDefinition());
|
columns.unshift(this.rowDetail.getColumnDefinition());
|
||||||
this._table = new Table(this._gridEl.nativeElement, undefined, columns, options);
|
this._table = new Table(this._gridEl.nativeElement, undefined, columns, options);
|
||||||
this._table.grid.setData(this.dataView, true);
|
this._table.grid.setData(this.dataView, true);
|
||||||
@@ -159,7 +169,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onJobsAvailable(jobs: sqlops.AgentJobInfo[]) {
|
private onJobsAvailable(jobs: sqlops.AgentJobInfo[]) {
|
||||||
let jobViews = jobs.map((job) => {
|
let jobViews = jobs.map((job) => {
|
||||||
return {
|
return {
|
||||||
id: job.jobId,
|
id: job.jobId,
|
||||||
@@ -175,6 +185,14 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
lastRunOutcome: AgentJobUtilities.convertToStatusString(job.lastRunOutcome)
|
lastRunOutcome: AgentJobUtilities.convertToStatusString(job.lastRunOutcome)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
this._table.registerPlugin(<any>this.rowDetail);
|
||||||
|
|
||||||
|
this.rowDetail.onBeforeRowDetailToggle.subscribe(function(e, args) {
|
||||||
|
});
|
||||||
|
this.rowDetail.onAfterRowDetailToggle.subscribe(function(e, args) {
|
||||||
|
});
|
||||||
|
this.rowDetail.onAsyncEndUpdate.subscribe(function(e, args) {
|
||||||
|
});
|
||||||
|
|
||||||
this.dataView.beginUpdate();
|
this.dataView.beginUpdate();
|
||||||
this.dataView.setItems(jobViews);
|
this.dataView.setItems(jobViews);
|
||||||
@@ -182,14 +200,57 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
|
|
||||||
this._table.resizeCanvas();
|
this._table.resizeCanvas();
|
||||||
this._table.autosizeColumns();
|
this._table.autosizeColumns();
|
||||||
|
let expandedJobs = this._agentViewComponent.expanded;
|
||||||
|
let expansions = 0;
|
||||||
|
for (let i = 0; i < jobs.length; i++){
|
||||||
|
let job = jobs[i];
|
||||||
|
if (job.lastRunOutcome === 0 && !expandedJobs.get(job.jobId)) {
|
||||||
|
this.expandJobRowDetails(i+expandedJobs.size);
|
||||||
|
this.addToStyleHash(i+expandedJobs.size);
|
||||||
|
this._agentViewComponent.setExpanded(job.jobId, 'temp');
|
||||||
|
} else if (job.lastRunOutcome === 0 && expandedJobs.get(job.jobId)) {
|
||||||
|
this.expandJobRowDetails(i+expansions);
|
||||||
|
this.addToStyleHash(i+expansions);
|
||||||
|
expansions++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.jobview-jobnamerow').hover(e => {
|
||||||
|
let currentTarget = e.currentTarget;
|
||||||
|
currentTarget.title = currentTarget.innerText;
|
||||||
|
});
|
||||||
this.loadJobHistories();
|
this.loadJobHistories();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadingTemplate() {
|
private setRowWithErrorClass(hash: {[index: number]: {[id: string]: string;}}, row: number, errorClass: string) {
|
||||||
return '<div class="preload">Loading...</div>';
|
hash[row] = {
|
||||||
|
'_detail_selector': errorClass,
|
||||||
|
'id': errorClass,
|
||||||
|
'jobId': errorClass,
|
||||||
|
'name': errorClass,
|
||||||
|
'lastRun': errorClass,
|
||||||
|
'nextRun': errorClass,
|
||||||
|
'enabled': errorClass,
|
||||||
|
'currentExecutionStatus': errorClass,
|
||||||
|
'category': errorClass,
|
||||||
|
'runnable': errorClass,
|
||||||
|
'hasSchedule': errorClass,
|
||||||
|
'lastRunOutcome': errorClass
|
||||||
|
};
|
||||||
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderName(row, cell, value, columnDef, dataContext) {
|
private addToStyleHash(row: number) {
|
||||||
|
let hash : {
|
||||||
|
[index: number]: {
|
||||||
|
[id: string]: string;
|
||||||
|
}} = {};
|
||||||
|
hash = this.setRowWithErrorClass(hash, row, 'job-with-error');
|
||||||
|
hash = this.setRowWithErrorClass(hash, row+1, 'error-row');
|
||||||
|
this._table.grid.setCellCssStyles('error-row'+row.toString(), hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderName(row, cell, value, columnDef, dataContext) {
|
||||||
let resultIndicatorClass: string;
|
let resultIndicatorClass: string;
|
||||||
switch (dataContext.lastRunOutcome) {
|
switch (dataContext.lastRunOutcome) {
|
||||||
case ('Succeeded'):
|
case ('Succeeded'):
|
||||||
@@ -205,7 +266,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
resultIndicatorClass = 'jobview-jobnameindicatorunknown';
|
resultIndicatorClass = 'jobview-jobnameindicatorunknown';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
resultIndicatorClass = 'jobview-jobnameindicatorunknown';
|
resultIndicatorClass = 'jobview-jobnameindicatorfailure';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,23 +276,49 @@ export class JobsViewComponent implements AfterContentChecked {
|
|||||||
'</tr></table>';
|
'</tr></table>';
|
||||||
}
|
}
|
||||||
|
|
||||||
loadJobHistories() {
|
private expandJobRowDetails(rowIdx: number, message?: string): void {
|
||||||
|
let item = this.dataView.getItemByIdx(rowIdx);
|
||||||
|
item.message = this._agentViewComponent.expanded.get(item.jobId);
|
||||||
|
this.rowDetail.applyTemplateNewLineHeight(item, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadJobHistories() {
|
||||||
|
const self = this;
|
||||||
if (this.jobs) {
|
if (this.jobs) {
|
||||||
this.jobs.forEach((job) => {
|
let erroredJobs = 0;
|
||||||
|
for (let i = 0; i < this.jobs.length; i++) {
|
||||||
|
let job = this.jobs[i];
|
||||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
||||||
this._jobManagementService.getJobHistory(ownerUri, job.jobId).then((result) => {
|
this._jobManagementService.getJobHistory(ownerUri, job.jobId).then((result) => {
|
||||||
if (result.jobs) {
|
if (result.jobs) {
|
||||||
this.jobHistories[job.jobId] = result.jobs;
|
self.jobHistories[job.jobId] = result.jobs;
|
||||||
this._jobCacheObject.setJobHistory(job.jobId, result.jobs);
|
self._jobCacheObject.setJobHistory(job.jobId, result.jobs);
|
||||||
|
if (self._agentViewComponent.expanded.has(job.jobId)) {
|
||||||
|
let jobHistory = self._jobCacheObject.getJobHistory(job.jobId)[0];
|
||||||
|
let item = self.dataView.getItemById(job.jobId + '.error');
|
||||||
|
let noStepsMessage = nls.localize('jobsView.noSteps', 'No Steps available for this job.');
|
||||||
|
let errorMessage = jobHistory ? jobHistory.message: noStepsMessage;
|
||||||
|
item['name'] = item['name'] + ': ' + errorMessage;
|
||||||
|
self._agentViewComponent.setExpanded(job.jobId, errorMessage);
|
||||||
|
self.dataView.updateItem(job.jobId + '.error', item);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private isErrorRow(jobName: string) {
|
||||||
|
return jobName.includes('Error');
|
||||||
|
}
|
||||||
|
|
||||||
private getJob(args: Slick.OnClickEventArgs<any>): sqlops.AgentJobInfo {
|
private getJob(args: Slick.OnClickEventArgs<any>): sqlops.AgentJobInfo {
|
||||||
let row = args.row;
|
let row = args.row;
|
||||||
let jobName = args.grid.getCellNode(row, 1).innerText.trim();
|
let jobName = args.grid.getCellNode(row, 1).innerText.trim();
|
||||||
|
if (this.isErrorRow(jobName)) {
|
||||||
|
jobName = args.grid.getCellNode(row-1, 1).innerText.trim();
|
||||||
|
}
|
||||||
let job = this.jobs.filter(job => job.name === jobName)[0];
|
let job = this.jobs.filter(job => job.name === jobName)[0];
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user