mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-01 09:35:41 -05:00
Jobs - New step (WIP) (#1711)
* added jobs view toolbar * create job command and dialog stub * add tab content and wire up the provider * fix the steps tab error * create job dialog 6/15 changes * general tab done * success action and retries completed * added failure action dropdown * add notification tab checkbox events * added AgentJobStepInfo objects in sqlops * create job dialog - 0618 update 1 * added model save function * width for controls and initial state for notification tab controls * refresh master and changes to work with latest code * fixed next and prev button positions * new step dialog ui finished * implemented parse button * fix package file * add validation and sub-items collections * hook up the step creation dialog - step 1 * merged master
This commit is contained in:
@@ -216,4 +216,19 @@
|
||||
|
||||
.vs .icon.unpin {
|
||||
background: url('unpin.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.small {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.medium {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.large {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
@@ -238,4 +238,31 @@ table.jobprevruns {
|
||||
|
||||
table.jobprevruns > tbody {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.jobs-view-toolbar{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
padding: 15px 10px 15px 30px;
|
||||
font-size: 16px;
|
||||
border-bottom: 3px solid #f4f4f4;
|
||||
}
|
||||
|
||||
.jobs-view-toolbar .vs-dark {
|
||||
border-bottom: 3px solid #444444;
|
||||
}
|
||||
|
||||
.jobs-view-toolbar > div{
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: flex-start;
|
||||
margin-right: 25px;
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.jobs-view-toolbar span{
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
1
src/sql/parts/jobManagement/common/media/new.svg
Normal file
1
src/sql/parts/jobManagement/common/media/new.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#231f20;}.cls-2{fill:#212121;}</style></defs><title>new_16x16</title><polygon class="cls-1" points="13.59 2.21 13.58 2.22 13.58 2.2 13.59 2.21"/><path class="cls-2" d="M16,7.5v1H8.5V16h-1V8.5H0v-1H7.5V0h1V7.5Z"/></svg>
|
||||
|
After Width: | Height: | Size: 336 B |
1
src/sql/parts/jobManagement/common/media/new_inverse.svg
Normal file
1
src/sql/parts/jobManagement/common/media/new_inverse.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>new_inverse_16x16</title><polygon class="cls-1" points="13.59 2.21 13.58 2.22 13.58 2.2 13.59 2.21"/><path class="cls-1" d="M16,7.5v1H8.5V16h-1V8.5H0v-1H7.5V0h1V7.5Z"/></svg>
|
||||
|
After Width: | Height: | Size: 320 B |
@@ -10,11 +10,15 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component';
|
||||
import { IJobManagementService } from '../common/interfaces';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConnectionManagementService } from '../../connection/common/connectionManagement';
|
||||
|
||||
export enum JobHistoryActions {
|
||||
export enum JobActions {
|
||||
Run = 'run',
|
||||
Stop = 'stop',
|
||||
NewStep = 'newStep'
|
||||
}
|
||||
|
||||
export class RunJobAction extends Action {
|
||||
public static ID = 'jobaction.runJob';
|
||||
public static LABEL = nls.localize('jobaction.run', "Run");
|
||||
@@ -30,7 +34,7 @@ export class RunJobAction extends Action {
|
||||
let jobName = context.agentJobInfo.name;
|
||||
let ownerUri = context.ownerUri;
|
||||
return new TPromise<boolean>((resolve, reject) => {
|
||||
this.jobManagementService.jobAction(ownerUri, jobName, JobHistoryActions.Run).then(result => {
|
||||
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(result => {
|
||||
if (result.success) {
|
||||
var startMsg = nls.localize('jobSuccessfullyStarted', ': The job was successfully started.');
|
||||
this.notificationService.notify({
|
||||
@@ -65,7 +69,7 @@ export class StopJobAction extends Action {
|
||||
let jobName = context.agentJobInfo.name;
|
||||
let ownerUri = context.ownerUri;
|
||||
return new TPromise<boolean>((resolve, reject) => {
|
||||
this.jobManagementService.jobAction(ownerUri, jobName, JobHistoryActions.Stop).then(result => {
|
||||
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(result => {
|
||||
if (result.success) {
|
||||
var stopMsg = nls.localize('jobSuccessfullyStopped', ': The job was successfully stopped.');
|
||||
this.notificationService.notify({
|
||||
@@ -83,4 +87,26 @@ export class StopJobAction extends Action {
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class NewStepAction extends Action {
|
||||
public static ID = 'jobaction.newStep';
|
||||
public static LABEL = nls.localize('jobaction.newStep', "New Step");
|
||||
|
||||
constructor(
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@ICommandService private _commandService: ICommandService,
|
||||
@IConnectionManagementService private _connectionService
|
||||
) {
|
||||
super(NewStepAction.ID, NewStepAction.LABEL, 'newStepIcon');
|
||||
}
|
||||
|
||||
public run(context: JobHistoryComponent): TPromise<boolean> {
|
||||
let ownerUri = context.ownerUri;
|
||||
let jobId = context.agentJobInfo.jobId;
|
||||
let server = context.serverName;
|
||||
return new TPromise<boolean>((resolve, reject) => {
|
||||
resolve(this._commandService.executeCommand('agent.openNewStepDialog', ownerUri, jobId, server));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,9 @@
|
||||
import 'vs/css!./jobHistory';
|
||||
import 'vs/css!sql/media/icons/common-icons';
|
||||
import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
||||
import { AgentJobHistoryInfo, AgentJobInfo } from 'sqlops';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { RunJobAction, StopJobAction } from 'sql/parts/jobManagement/views/jobHistoryActions';
|
||||
import * as sqlops from 'sqlops';
|
||||
import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { RunJobAction, StopJobAction, NewStepAction } from 'sql/parts/jobManagement/views/jobActions';
|
||||
import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService';
|
||||
import { AgentJobUtilities } from '../common/agentJobUtilities';
|
||||
import { IJobManagementService } from '../common/interfaces';
|
||||
@@ -46,9 +46,9 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
||||
@ViewChild('table') private _tableContainer: ElementRef;
|
||||
@ViewChild('actionbarContainer') private _actionbarContainer: ElementRef;
|
||||
|
||||
@Input() public agentJobInfo: AgentJobInfo = undefined;
|
||||
@Input() public agentJobHistories: AgentJobHistoryInfo[] = undefined;
|
||||
public agentJobHistoryInfo: AgentJobHistoryInfo = undefined;
|
||||
@Input() public agentJobInfo: sqlops.AgentJobInfo = undefined;
|
||||
@Input() public agentJobHistories: sqlops.AgentJobHistoryInfo[] = undefined;
|
||||
public agentJobHistoryInfo: sqlops.AgentJobHistoryInfo = undefined;
|
||||
|
||||
private _isVisible: boolean = false;
|
||||
private _stepRows: JobStepsViewRow[] = [];
|
||||
@@ -56,8 +56,9 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
||||
private _showPreviousRuns: boolean = undefined;
|
||||
private _runStatus: string = undefined;
|
||||
private _jobCacheObject: JobCacheObject;
|
||||
private _agentJobInfo: AgentJobInfo;
|
||||
private _agentJobInfo: sqlops.AgentJobInfo;
|
||||
private _noJobsAvailable: boolean = false;
|
||||
private _serverName: string;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
|
||||
@@ -76,14 +77,14 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
||||
this._treeRenderer = new JobHistoryRenderer();
|
||||
this._treeFilter = new JobHistoryFilter();
|
||||
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
||||
let serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||
let jobCache = jobCacheObjectMap[serverName];
|
||||
this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||
let jobCache = jobCacheObjectMap[this._serverName];
|
||||
if (jobCache) {
|
||||
this._jobCacheObject = jobCache;
|
||||
} else {
|
||||
this._jobCacheObject = new JobCacheObject();
|
||||
this._jobCacheObject.serverName = serverName;
|
||||
this._jobManagementService.addToCache(serverName, this._jobCacheObject);
|
||||
this._jobCacheObject.serverName = this._serverName;
|
||||
this._jobManagementService.addToCache(this._serverName, this._jobCacheObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +211,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
private buildHistoryTree(self: any, jobHistories: AgentJobHistoryInfo[]) {
|
||||
private buildHistoryTree(self: any, jobHistories: sqlops.AgentJobHistoryInfo[]) {
|
||||
self._treeController.jobHistories = jobHistories;
|
||||
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, jobHistories);
|
||||
let jobHistoryRows = this._treeController.jobHistories.map(job => self.convertToJobHistoryRow(job));
|
||||
@@ -237,7 +238,7 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
||||
this._agentViewComponent.showHistory = false;
|
||||
}
|
||||
|
||||
private convertToJobHistoryRow(historyInfo: AgentJobHistoryInfo): JobHistoryRow {
|
||||
private convertToJobHistoryRow(historyInfo: sqlops.AgentJobHistoryInfo): JobHistoryRow {
|
||||
let jobHistoryRow = new JobHistoryRow();
|
||||
jobHistoryRow.runDate = this.formatTime(historyInfo.runDate);
|
||||
jobHistoryRow.runStatus = AgentJobUtilities.convertToStatusString(historyInfo.runStatus);
|
||||
@@ -263,12 +264,14 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
||||
private _initActionBar() {
|
||||
let runJobAction = this.instantiationService.createInstance(RunJobAction);
|
||||
let stopJobAction = this.instantiationService.createInstance(StopJobAction);
|
||||
let newStepAction = this.instantiationService.createInstance(NewStepAction);
|
||||
let taskbar = <HTMLElement>this._actionbarContainer.nativeElement;
|
||||
this._actionBar = new Taskbar(taskbar, this.contextMenuService);
|
||||
this._actionBar.context = this;
|
||||
this._actionBar.setContent([
|
||||
{ action: runJobAction },
|
||||
{ action: stopJobAction }
|
||||
{ action: stopJobAction },
|
||||
{ action: newStepAction }
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -286,6 +289,10 @@ export class JobHistoryComponent extends Disposable implements OnInit {
|
||||
return this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
||||
}
|
||||
|
||||
public get serverName(): string {
|
||||
return this._serverName;
|
||||
}
|
||||
|
||||
/** SETTERS */
|
||||
|
||||
public set showSteps(value: boolean) {
|
||||
|
||||
@@ -122,6 +122,15 @@ input#accordion:checked ~ .accordion-content {
|
||||
background-image: url('../common/media/stop.svg');
|
||||
}
|
||||
|
||||
.vs .action-label.icon.newStepIcon {
|
||||
background-image: url('../common/media/new.svg');
|
||||
}
|
||||
|
||||
.vs-dark .action-label.icon.newStepIcon,
|
||||
.hc-black .action-label.icon.newStepIcon {
|
||||
background-image: url('../common/media/new_inverse.svg');
|
||||
}
|
||||
|
||||
a.action-label.icon.runJobIcon.non-runnable {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
|
||||
@@ -9,5 +9,9 @@
|
||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Jobs Available</h1>
|
||||
<div class="icon in-progress" *ngIf="_showProgressWheel === true"></div>
|
||||
</div>
|
||||
<div class="jobs-view-toolbar">
|
||||
<div (click)="refreshJobs()" tabindex="0"><div class="small icon refresh"></div><span>{{RefreshText}}</span></div>
|
||||
<div (click)="openCreateJobDialog()" tabindex="0"><div class="small icon new"></div><span>{{NewJobText}}</span></div>
|
||||
</div>
|
||||
|
||||
<div #jobsgrid class="jobview-grid"></div>
|
||||
@@ -27,7 +27,7 @@ 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 JOBSVIEW_SELECTOR: string = 'jobsview-component';
|
||||
export const ROW_HEIGHT: number = 45;
|
||||
|
||||
@@ -44,16 +44,16 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
private _disposables = new Array<vscode.Disposable>();
|
||||
|
||||
private columns: Array<Slick.Column<any>> = [
|
||||
{ name: nls.localize('jobColumns.name','Name'), field: 'name', formatter: (row, cell, value, columnDef, dataContext) => this.renderName(row, cell, value, columnDef, dataContext), width: 200 , id: 'name' },
|
||||
{ name: nls.localize('jobColumns.lastRun','Last Run'), field: 'lastRun', width: 120, id: 'lastRun' },
|
||||
{ name: nls.localize('jobColumns.nextRun','Next Run'), field: 'nextRun', width: 120, id: 'nextRun' },
|
||||
{ name: nls.localize('jobColumns.enabled','Enabled'), field: 'enabled', width: 50, id: 'enabled' },
|
||||
{ name: nls.localize('jobColumns.status','Status'), field: 'currentExecutionStatus', width: 60, id: 'currentExecutionStatus' },
|
||||
{ name: nls.localize('jobColumns.category','Category'), field: 'category', width: 120, id: 'category' },
|
||||
{ name: nls.localize('jobColumns.runnable','Runnable'), field: 'runnable', width: 70, id: 'runnable' },
|
||||
{ name: nls.localize('jobColumns.schedule','Schedule'), field: 'hasSchedule', width: 60, id: 'hasSchedule' },
|
||||
{ name: nls.localize('jobColumns.name', 'Name'), field: 'name', formatter: (row, cell, value, columnDef, dataContext) => this.renderName(row, cell, value, columnDef, dataContext), width: 200, id: 'name' },
|
||||
{ name: nls.localize('jobColumns.lastRun', 'Last Run'), field: 'lastRun', width: 120, id: 'lastRun' },
|
||||
{ name: nls.localize('jobColumns.nextRun', 'Next Run'), field: 'nextRun', width: 120, id: 'nextRun' },
|
||||
{ name: nls.localize('jobColumns.enabled', 'Enabled'), field: 'enabled', width: 50, id: 'enabled' },
|
||||
{ name: nls.localize('jobColumns.status', 'Status'), field: 'currentExecutionStatus', width: 60, id: 'currentExecutionStatus' },
|
||||
{ name: nls.localize('jobColumns.category', 'Category'), field: 'category', width: 120, id: 'category' },
|
||||
{ name: nls.localize('jobColumns.runnable', 'Runnable'), field: 'runnable', width: 70, id: 'runnable' },
|
||||
{ name: nls.localize('jobColumns.schedule', 'Schedule'), field: 'hasSchedule', width: 60, id: 'hasSchedule' },
|
||||
{ name: nls.localize('jobColumns.lastRunOutcome', 'Last Run Outcome'), field: 'lastRunOutcome', width: 120, id: 'lastRunOutcome' },
|
||||
{ name: nls.localize('jobColumns.previousRuns', 'Previous Runs'), formatter: this.renderChartsPostHistory, field: 'previousRuns', width: 80, id: 'previousRuns'}
|
||||
{ name: nls.localize('jobColumns.previousRuns', 'Previous Runs'), formatter: this.renderChartsPostHistory, field: 'previousRuns', width: 80, id: 'previousRuns' }
|
||||
];
|
||||
|
||||
|
||||
@@ -80,18 +80,22 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
private _isCloud: boolean;
|
||||
private _showProgressWheel: boolean;
|
||||
private _tabHeight: number;
|
||||
private filterStylingMap: { [columnName: string]: [any] ;} = {};
|
||||
private filterStylingMap: { [columnName: string]: [any]; } = {};
|
||||
private filterStack = ['start'];
|
||||
private filterValueMap: { [columnName: string]: string[] ;} = {};
|
||||
private filterValueMap: { [columnName: string]: string[]; } = {};
|
||||
private sortingStylingMap: { [columnName: string]: any; } = {};
|
||||
|
||||
private NewJobText: string = nls.localize("jobsToolbar-NewJob", "New job");
|
||||
private RefreshText: string = nls.localize("jobsToolbar-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(IThemeService) private _themeService: IThemeService,
|
||||
@Inject(ICommandService) private _commandService: ICommandService
|
||||
) {
|
||||
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
||||
this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||
@@ -241,9 +245,9 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
}
|
||||
// apply the previous filter styling
|
||||
let currentItems = this.dataView.getFilteredItems();
|
||||
let styledItems = this.filterValueMap[this.filterStack[this.filterStack.length-1]][1];
|
||||
let styledItems = this.filterValueMap[this.filterStack[this.filterStack.length - 1]][1];
|
||||
if (styledItems === currentItems) {
|
||||
let lastColStyle = this.filterStylingMap[this.filterStack[this.filterStack.length-1]];
|
||||
let lastColStyle = this.filterStylingMap[this.filterStack[this.filterStack.length - 1]];
|
||||
for (let i = 0; i < lastColStyle.length; i++) {
|
||||
this._table.grid.setCellCssStyles(lastColStyle[i][0], lastColStyle[i][1]);
|
||||
}
|
||||
@@ -251,7 +255,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
// style it all over again
|
||||
let seenJobs = 0;
|
||||
for (let i = 0; i < currentItems.length; i++) {
|
||||
this._table.grid.removeCellCssStyles('error-row'+i.toString());
|
||||
this._table.grid.removeCellCssStyles('error-row' + i.toString());
|
||||
let item = this.dataView.getFilteredItems()[i];
|
||||
if (item.lastRunOutcome === 'Failed') {
|
||||
this.addToStyleHash(seenJobs, false, this.filterStylingMap, args.column.name);
|
||||
@@ -261,7 +265,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
}
|
||||
// one expansion for the row and one for
|
||||
// the error detail
|
||||
seenJobs ++;
|
||||
seenJobs++;
|
||||
i++;
|
||||
}
|
||||
seenJobs++;
|
||||
@@ -277,7 +281,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
} else {
|
||||
let seenJobs = 0;
|
||||
for (let i = 0; i < this.jobs.length; i++) {
|
||||
this._table.grid.removeCellCssStyles('error-row'+i.toString());
|
||||
this._table.grid.removeCellCssStyles('error-row' + i.toString());
|
||||
let item = this.dataView.getItemByIdx(i);
|
||||
// current filter
|
||||
if (_.contains(filterValues, item[args.column.field])) {
|
||||
@@ -291,7 +295,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
}
|
||||
// one expansion for the row and one for
|
||||
// the error detail
|
||||
seenJobs ++;
|
||||
seenJobs++;
|
||||
i++;
|
||||
}
|
||||
seenJobs++;
|
||||
@@ -359,7 +363,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
self.jobs.forEach(job => {
|
||||
let jobId = job.jobId;
|
||||
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
||||
let previousRuns = jobHistories.slice(jobHistories.length-5, jobHistories.length);
|
||||
let previousRuns = jobHistories.slice(jobHistories.length - 5, jobHistories.length);
|
||||
self.createJobChart(job.jobId, previousRuns);
|
||||
});
|
||||
});
|
||||
@@ -427,27 +431,27 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
}
|
||||
|
||||
private addToStyleHash(row: number, start: boolean, map: any, columnName: string) {
|
||||
let hash : {
|
||||
let hash: {
|
||||
[index: number]: {
|
||||
[id: string]: string;
|
||||
}
|
||||
} = {};
|
||||
hash = this.setRowWithErrorClass(hash, row, 'job-with-error');
|
||||
hash = this.setRowWithErrorClass(hash, row+1, 'error-row');
|
||||
hash = this.setRowWithErrorClass(hash, row + 1, 'error-row');
|
||||
if (start) {
|
||||
if (map['start']) {
|
||||
map['start'].push(['error-row'+row.toString(), hash]);
|
||||
map['start'].push(['error-row' + row.toString(), hash]);
|
||||
} else {
|
||||
map['start'] = [['error-row'+row.toString(), hash]];
|
||||
map['start'] = [['error-row' + row.toString(), hash]];
|
||||
}
|
||||
} else {
|
||||
if (map[columnName]) {
|
||||
map[columnName].push(['error-row'+row.toString(), hash]);
|
||||
map[columnName].push(['error-row' + row.toString(), hash]);
|
||||
} else {
|
||||
map[columnName] = [['error-row'+row.toString(), hash]];
|
||||
map[columnName] = [['error-row' + row.toString(), hash]];
|
||||
}
|
||||
}
|
||||
this._table.grid.setCellCssStyles('error-row'+row.toString(), hash);
|
||||
this._table.grid.setCellCssStyles('error-row' + row.toString(), hash);
|
||||
}
|
||||
|
||||
private renderName(row, cell, value, columnDef, dataContext) {
|
||||
@@ -557,13 +561,13 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
self.jobHistories[job.jobId] = result.jobs;
|
||||
self._jobCacheObject.setJobHistory(job.jobId, result.jobs);
|
||||
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
||||
let previousRuns = jobHistories.slice(jobHistories.length-5, jobHistories.length);
|
||||
let previousRuns = jobHistories.slice(jobHistories.length - 5, jobHistories.length);
|
||||
self.createJobChart(job.jobId, previousRuns);
|
||||
if (self._agentViewComponent.expanded.has(job.jobId)) {
|
||||
let lastJobHistory = jobHistories[result.jobs.length-1];
|
||||
let lastJobHistory = jobHistories[result.jobs.length - 1];
|
||||
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;
|
||||
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);
|
||||
@@ -576,7 +580,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
private createJobChart(jobId: string, jobHistories: sqlops.AgentJobHistoryInfo[]): void {
|
||||
let chartHeights = this.getChartHeights(jobHistories);
|
||||
for (let i = 0; i < jobHistories.length; i++) {
|
||||
let runGraph = $(`table#${jobId}.jobprevruns > tbody > tr > td > div.bar${i+1}`);
|
||||
let runGraph = $(`table#${jobId}.jobprevruns > tbody > tr > td > div.bar${i + 1}`);
|
||||
if (jobHistories && jobHistories.length > 0) {
|
||||
runGraph.css('height', chartHeights[i]);
|
||||
let bgColor = jobHistories[i].runStatus === 0 ? 'red' : 'green';
|
||||
@@ -599,11 +603,11 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
// chart height normalization logic
|
||||
private getChartHeights(jobHistories: sqlops.AgentJobHistoryInfo[]): string[] {
|
||||
if (!jobHistories || jobHistories.length === 0) {
|
||||
return ['5px','5px','5px','5px','5px'];
|
||||
return ['5px', '5px', '5px', '5px', '5px'];
|
||||
}
|
||||
let maxDuration: number = 0;
|
||||
jobHistories.forEach(history => {
|
||||
let historyDuration = AgentJobUtilities.convertDurationToSeconds(history.runDuration) ;
|
||||
let historyDuration = AgentJobUtilities.convertDurationToSeconds(history.runDuration);
|
||||
if (historyDuration > maxDuration) {
|
||||
maxDuration = historyDuration;
|
||||
}
|
||||
@@ -613,7 +617,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
let chartHeights = [];
|
||||
for (let i = 0; i < jobHistories.length; i++) {
|
||||
let duration = jobHistories[i].runDuration;
|
||||
let chartHeight = (maxBarHeight * AgentJobUtilities.convertDurationToSeconds(duration))/maxDuration;
|
||||
let chartHeight = (maxBarHeight * AgentJobUtilities.convertDurationToSeconds(duration)) / maxDuration;
|
||||
chartHeights.push(`${chartHeight}px`);
|
||||
}
|
||||
return chartHeights;
|
||||
@@ -625,14 +629,14 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
}
|
||||
let expandedJobs = this._agentViewComponent.expanded;
|
||||
let expansions = 0;
|
||||
for (let i = 0; i < this.jobs.length; i++){
|
||||
for (let i = 0; i < this.jobs.length; i++) {
|
||||
let job = this.jobs[i];
|
||||
if (job.lastRunOutcome === 0 && !expandedJobs.get(job.jobId)) {
|
||||
this.expandJobRowDetails(i+expandedJobs.size);
|
||||
this.addToStyleHash(i+expandedJobs.size, start, this.filterStylingMap, undefined);
|
||||
this.expandJobRowDetails(i + expandedJobs.size);
|
||||
this.addToStyleHash(i + expandedJobs.size, start, this.filterStylingMap, undefined);
|
||||
this._agentViewComponent.setExpanded(job.jobId, 'Loading Error...');
|
||||
} else if (job.lastRunOutcome === 0 && expandedJobs.get(job.jobId)) {
|
||||
this.addToStyleHash(i+expansions, start, this.filterStylingMap, undefined);
|
||||
this.addToStyleHash(i + expansions, start, this.filterStylingMap, undefined);
|
||||
expansions++;
|
||||
}
|
||||
}
|
||||
@@ -648,7 +652,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
if (item._parent) {
|
||||
value = value && _.contains(filterValues, item._parent[col.field]);
|
||||
} else {
|
||||
value = value && _.contains(filterValues, item[col.field]);
|
||||
value = value && _.contains(filterValues, item[col.field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -661,8 +665,8 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
let jobItems = items.filter(x => x._parent === undefined);
|
||||
let errorItems = items.filter(x => x._parent !== undefined);
|
||||
this.sortingStylingMap[column] = items;
|
||||
switch(column) {
|
||||
case('Name'): {
|
||||
switch (column) {
|
||||
case ('Name'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => {
|
||||
@@ -670,13 +674,13 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
}, isAscending);
|
||||
break;
|
||||
}
|
||||
case('Last Run'): {
|
||||
case ('Last Run'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, true), isAscending);
|
||||
break;
|
||||
}
|
||||
case ('Next Run') : {
|
||||
case ('Next Run'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, false), isAscending);
|
||||
@@ -737,7 +741,7 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
let item = jobItems[i];
|
||||
if (item._child) {
|
||||
let child = errorItems.find(error => error === item._child);
|
||||
jobItems.splice(i+1, 0, child);
|
||||
jobItems.splice(i + 1, 0, child);
|
||||
jobItemsLength++;
|
||||
}
|
||||
}
|
||||
@@ -751,12 +755,12 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this.jobs.length; i++) {
|
||||
this._table.grid.removeCellCssStyles('error-row'+i.toString());
|
||||
this._table.grid.removeCellCssStyles('error-row' + i.toString());
|
||||
}
|
||||
}
|
||||
// add new style to the items back again
|
||||
items = this.filterStack.length > 1 ? this.dataView.getFilteredItems() : this.dataView.getItems();
|
||||
for (let i = 0; i < items.length; i ++) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let item = items[i];
|
||||
if (item.lastRunOutcome === 'Failed') {
|
||||
this.addToStyleHash(i, false, this.sortingStylingMap, column);
|
||||
@@ -784,4 +788,13 @@ export class JobsViewComponent implements AfterContentChecked {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private openCreateJobDialog() {
|
||||
let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri;
|
||||
this._commandService.executeCommand("agent.openCreateJobDialog", ownerUri);
|
||||
}
|
||||
|
||||
private refreshJobs() {
|
||||
this._agentViewComponent.refresh = true;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ export interface IQueryManagementService {
|
||||
runQueryStatement(ownerUri: string, line: number, column: number): Thenable<void>;
|
||||
runQueryString(ownerUri: string, queryString: string): Thenable<void>;
|
||||
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<sqlops.SimpleExecuteResult>;
|
||||
parseSyntax(ownerUri:string, query: string): Thenable<sqlops.SyntaxParseResult>;
|
||||
getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult>;
|
||||
disposeQuery(ownerUri: string): Thenable<void>;
|
||||
saveResults(requestParams: sqlops.SaveResultsRequestParams): Thenable<sqlops.SaveResultRequestResult>;
|
||||
@@ -63,6 +64,7 @@ export interface IQueryRequestHandler {
|
||||
runQueryStatement(ownerUri: string, line: number, column: number): Thenable<void>;
|
||||
runQueryString(ownerUri: string, queryString: string): Thenable<void>;
|
||||
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<sqlops.SimpleExecuteResult>;
|
||||
parseSyntax(ownerUri:string, query: string): Thenable<sqlops.SyntaxParseResult>;
|
||||
getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult>;
|
||||
disposeQuery(ownerUri: string): Thenable<void>;
|
||||
saveResults(requestParams: sqlops.SaveResultsRequestParams): Thenable<sqlops.SaveResultRequestResult>;
|
||||
@@ -197,6 +199,11 @@ export class QueryManagementService implements IQueryManagementService {
|
||||
return runner.runQueryAndReturn(ownerUri, queryString);
|
||||
});
|
||||
}
|
||||
public parseSyntax(ownerUri: string, query: string): Thenable<sqlops.SyntaxParseResult> {
|
||||
return this._runAction(ownerUri, (runner) => {
|
||||
return runner.parseSyntax(ownerUri, query);
|
||||
});
|
||||
}
|
||||
public getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult> {
|
||||
return this._runAction(rowData.ownerUri, (runner) => {
|
||||
return runner.getQueryRows(rowData);
|
||||
|
||||
71
src/sql/sqlops.d.ts
vendored
71
src/sql/sqlops.d.ts
vendored
@@ -635,6 +635,7 @@ declare module 'sqlops' {
|
||||
runQueryStatement(ownerUri: string, line: number, column: number): Thenable<void>;
|
||||
runQueryString(ownerUri: string, queryString: string): Thenable<void>;
|
||||
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<SimpleExecuteResult>;
|
||||
parseSyntax(ownerUri: string, query: string): Thenable<SyntaxParseResult>;
|
||||
getQueryRows(rowData: QueryExecuteSubsetParams): Thenable<QueryExecuteSubsetResult>;
|
||||
disposeQuery(ownerUri: string): Thenable<void>;
|
||||
saveResults(requestParams: SaveResultsRequestParams): Thenable<SaveResultRequestResult>;
|
||||
@@ -769,6 +770,11 @@ declare module 'sqlops' {
|
||||
rows: DbCellValue[][];
|
||||
}
|
||||
|
||||
export interface SyntaxParseResult {
|
||||
parseable: boolean;
|
||||
errorMessages: string[];
|
||||
}
|
||||
|
||||
// Query Batch Notification -----------------------------------------------------------------------
|
||||
export interface QueryExecuteBatchNotificationParams {
|
||||
batchSummary: BatchSummary;
|
||||
@@ -1054,8 +1060,17 @@ declare module 'sqlops' {
|
||||
wmiEvent = 4
|
||||
}
|
||||
|
||||
export enum JobCompletionActionCondition{
|
||||
Never = 0,
|
||||
OnSuccess = 1,
|
||||
OnFailure = 2,
|
||||
Always = 3
|
||||
}
|
||||
|
||||
export interface AgentJobInfo {
|
||||
name: string;
|
||||
owner: string;
|
||||
description: string;
|
||||
currentExecutionStatus: number;
|
||||
lastRunOutcome: number;
|
||||
currentExecutionStep: string;
|
||||
@@ -1070,9 +1085,22 @@ declare module 'sqlops' {
|
||||
lastRun: string;
|
||||
nextRun: string;
|
||||
jobId: string;
|
||||
EmailLevel: JobCompletionActionCondition;
|
||||
PageLevel: JobCompletionActionCondition;
|
||||
EventLogLevel: JobCompletionActionCondition;
|
||||
DeleteLevel: JobCompletionActionCondition;
|
||||
OperatorToEmail: string;
|
||||
OperatorToPage: string;
|
||||
JobSteps: AgentJobStepInfo[];
|
||||
JobSchedules: AgentJobScheduleInfo[];
|
||||
Alerts: AgentAlertInfo[];
|
||||
}
|
||||
|
||||
export interface AgentJobStepInfo {
|
||||
export interface AgentJobScheduleInfo {
|
||||
|
||||
}
|
||||
|
||||
export interface AgentJobStep {
|
||||
jobId: string;
|
||||
stepId: string;
|
||||
stepName: string;
|
||||
@@ -1081,6 +1109,33 @@ declare module 'sqlops' {
|
||||
runStatus: number;
|
||||
}
|
||||
|
||||
export interface AgentJobStepInfo {
|
||||
jobId: string;
|
||||
jobName: string;
|
||||
script: string;
|
||||
scriptName: string;
|
||||
stepName: string;
|
||||
subSystem: string;
|
||||
id: number;
|
||||
failureAction: string;
|
||||
successAction: string;
|
||||
failStepId: number;
|
||||
successStepId: number;
|
||||
command: string;
|
||||
commandExecutionSuccessCode: number;
|
||||
databaseName: string;
|
||||
databaseUserName: string;
|
||||
server: string;
|
||||
outputFileName: string;
|
||||
appendToLogFile: boolean;
|
||||
appendToStepHist: boolean;
|
||||
writeLogToTable: boolean;
|
||||
appendLogToTable: boolean;
|
||||
retryAttempts: number;
|
||||
retryInterval: number;
|
||||
proxyName: string;
|
||||
}
|
||||
|
||||
export interface AgentJobHistoryInfo {
|
||||
instanceId: number;
|
||||
sqlMessageId: string;
|
||||
@@ -1098,7 +1153,7 @@ declare module 'sqlops' {
|
||||
operatorPaged: string;
|
||||
retriesAttempted: string;
|
||||
server: string;
|
||||
steps: AgentJobStepInfo[];
|
||||
steps: AgentJobStep[];
|
||||
}
|
||||
|
||||
export interface AgentProxyInfo {
|
||||
@@ -1173,7 +1228,7 @@ declare module 'sqlops' {
|
||||
job: AgentJobInfo;
|
||||
}
|
||||
|
||||
export interface UpdateAgentJobResult extends ResultStatus {
|
||||
export interface UpdateAgentJobResult extends ResultStatus {
|
||||
job: AgentJobInfo;
|
||||
}
|
||||
|
||||
@@ -1181,7 +1236,7 @@ declare module 'sqlops' {
|
||||
step: AgentJobStepInfo;
|
||||
}
|
||||
|
||||
export interface UpdateAgentJobStepResult extends ResultStatus {
|
||||
export interface UpdateAgentJobStepResult extends ResultStatus {
|
||||
step: AgentJobStepInfo;
|
||||
}
|
||||
|
||||
@@ -1189,7 +1244,7 @@ declare module 'sqlops' {
|
||||
step: AgentJobStepInfo;
|
||||
}
|
||||
|
||||
export interface UpdateAgentProxyResult extends ResultStatus {
|
||||
export interface UpdateAgentProxyResult extends ResultStatus {
|
||||
step: AgentJobStepInfo;
|
||||
}
|
||||
|
||||
@@ -1201,7 +1256,7 @@ declare module 'sqlops' {
|
||||
alert: AgentJobStepInfo;
|
||||
}
|
||||
|
||||
export interface UpdateAgentAlertResult extends ResultStatus {
|
||||
export interface UpdateAgentAlertResult extends ResultStatus {
|
||||
alert: AgentJobStepInfo;
|
||||
}
|
||||
|
||||
@@ -1213,7 +1268,7 @@ declare module 'sqlops' {
|
||||
operator: AgentOperatorInfo;
|
||||
}
|
||||
|
||||
export interface UpdateAgentOperatorResult extends ResultStatus {
|
||||
export interface UpdateAgentOperatorResult extends ResultStatus {
|
||||
operator: AgentOperatorInfo;
|
||||
}
|
||||
|
||||
@@ -1225,7 +1280,7 @@ declare module 'sqlops' {
|
||||
operator: AgentOperatorInfo;
|
||||
}
|
||||
|
||||
export interface UpdateAgentProxyResult extends ResultStatus {
|
||||
export interface UpdateAgentProxyResult extends ResultStatus {
|
||||
operator: AgentOperatorInfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,13 @@ export enum NotifyMethods
|
||||
notifyAll = 7
|
||||
}
|
||||
|
||||
export enum JobCompletionActionCondition{
|
||||
Never = 0,
|
||||
OnSuccess = 1,
|
||||
OnFailure = 2,
|
||||
Always = 3
|
||||
}
|
||||
|
||||
export enum AlertType
|
||||
{
|
||||
sqlServerEvent = 1,
|
||||
|
||||
@@ -224,6 +224,10 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
||||
return this._resolveProvider<sqlops.QueryProvider>(handle).runQueryAndReturn(ownerUri, queryString);
|
||||
}
|
||||
|
||||
$parseSyntax(handle: number, ownerUri: string, query: string): Thenable<sqlops.SyntaxParseResult> {
|
||||
return this._resolveProvider<sqlops.QueryProvider>(handle).parseSyntax(ownerUri, query);
|
||||
}
|
||||
|
||||
$getQueryRows(handle: number, rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult> {
|
||||
return this._resolveProvider<sqlops.QueryProvider>(handle).getQueryRows(rowData);
|
||||
}
|
||||
|
||||
@@ -114,6 +114,9 @@ export class MainThreadDataProtocol implements MainThreadDataProtocolShape {
|
||||
runQueryAndReturn(ownerUri: string, queryString: string): Thenable<sqlops.SimpleExecuteResult> {
|
||||
return self._proxy.$runQueryAndReturn(handle, ownerUri, queryString);
|
||||
},
|
||||
parseSyntax(ownerUri: string, query: string): Thenable<sqlops.SyntaxParseResult> {
|
||||
return self._proxy.$parseSyntax(handle, ownerUri, query);
|
||||
},
|
||||
getQueryRows(rowData: sqlops.QueryExecuteSubsetParams): Thenable<sqlops.QueryExecuteSubsetResult> {
|
||||
return self._proxy.$getQueryRows(handle, rowData);
|
||||
},
|
||||
|
||||
@@ -384,6 +384,7 @@ export function createApiFactory(
|
||||
ScriptOperation: sqlExtHostTypes.ScriptOperation,
|
||||
WeekDays: sqlExtHostTypes.WeekDays,
|
||||
NotifyMethods: sqlExtHostTypes.NotifyMethods,
|
||||
JobCompletionActionCondition: sqlExtHostTypes.JobCompletionActionCondition,
|
||||
AlertType: sqlExtHostTypes.AlertType,
|
||||
window,
|
||||
tasks,
|
||||
|
||||
@@ -140,6 +140,10 @@ export abstract class ExtHostDataProtocolShape {
|
||||
* Runs a query for a provided query and returns result
|
||||
*/
|
||||
$runQueryAndReturn(handle: number, ownerUri: string, queryString: string): Thenable<sqlops.SimpleExecuteResult> { throw ni(); }
|
||||
/**
|
||||
* Parses a T-SQL string without actually executing it
|
||||
*/
|
||||
$parseSyntax(handle: number, ownerUri: string, query: string): Thenable<sqlops.SyntaxParseResult> { throw ni(); }
|
||||
/**
|
||||
* Gets a subset of rows in a result set in order to display in the UI
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user